IF YOU WOULD LIKE TO GET AN ACCOUNT, please write an email to s dot adaszewski at gmail dot com. User accounts are meant only to report issues and/or generate pull requests. This is a purpose-specific Git hosting for ADARED projects. Thank you for your understanding!
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

297 linhas
9.3KB

  1. /* global chrome location ReadableStream define MessageChannel TransformStream */
  2. ;((name, definition) => {
  3. typeof module !== 'undefined'
  4. ? module.exports = definition()
  5. : typeof define === 'function' && typeof define.amd === 'object'
  6. ? define(definition)
  7. : this[name] = definition()
  8. })('streamSaver', () => {
  9. 'use strict'
  10. let mitmTransporter = null
  11. let supportsTransferable = false
  12. const test = fn => { try { fn() } catch (e) {} }
  13. const ponyfill = window.WebStreamsPolyfill || {}
  14. const isSecureContext = window.isSecureContext
  15. let useBlobFallback = /constructor/i.test(window.HTMLElement) || !!window.safari
  16. const downloadStrategy = isSecureContext || 'MozAppearance' in document.documentElement.style
  17. ? 'iframe'
  18. : 'navigate'
  19. const streamSaver = {
  20. createWriteStream,
  21. WritableStream: window.WritableStream || ponyfill.WritableStream,
  22. supported: true,
  23. version: { full: '2.0.0', major: 2, minor: 0, dot: 0 },
  24. mitm: 'https://jimmywarting.github.io/StreamSaver.js/mitm.html?version=2.0.0'
  25. }
  26. /**
  27. * create a hidden iframe and append it to the DOM (body)
  28. *
  29. * @param {string} src page to load
  30. * @return {HTMLIFrameElement} page to load
  31. */
  32. function makeIframe (src) {
  33. if (!src) throw new Error('meh')
  34. const iframe = document.createElement('iframe')
  35. iframe.hidden = true
  36. iframe.src = src
  37. iframe.loaded = false
  38. iframe.name = 'iframe'
  39. iframe.isIframe = true
  40. iframe.postMessage = (...args) => iframe.contentWindow.postMessage(...args)
  41. iframe.addEventListener('load', () => {
  42. iframe.loaded = true
  43. }, { once: true })
  44. document.body.appendChild(iframe)
  45. return iframe
  46. }
  47. /**
  48. * create a popup that simulates the basic things
  49. * of what a iframe can do
  50. *
  51. * @param {string} src page to load
  52. * @return {object} iframe like object
  53. */
  54. function makePopup (src) {
  55. const options = 'width=200,height=100'
  56. const delegate = document.createDocumentFragment()
  57. const popup = {
  58. frame: window.open(src, 'popup', options),
  59. loaded: false,
  60. isIframe: false,
  61. isPopup: true,
  62. remove () { popup.frame.close() },
  63. addEventListener (...args) { delegate.addEventListener(...args) },
  64. dispatchEvent (...args) { delegate.dispatchEvent(...args) },
  65. removeEventListener (...args) { delegate.removeEventListener(...args) },
  66. postMessage (...args) { popup.frame.postMessage(...args) }
  67. }
  68. const onReady = evt => {
  69. if (evt.source === popup.frame) {
  70. popup.loaded = true
  71. window.removeEventListener('message', onReady)
  72. popup.dispatchEvent(new Event('load'))
  73. }
  74. }
  75. window.addEventListener('message', onReady)
  76. return popup
  77. }
  78. try {
  79. // We can't look for service worker since it may still work on http
  80. new Response(new ReadableStream())
  81. if (isSecureContext && !('serviceWorker' in navigator)) {
  82. useBlobFallback = true
  83. }
  84. } catch (err) {
  85. useBlobFallback = true
  86. }
  87. test(() => {
  88. // Transfariable stream was first enabled in chrome v73 behind a flag
  89. const { readable } = new TransformStream()
  90. const mc = new MessageChannel()
  91. mc.port1.postMessage(readable, [readable])
  92. mc.port1.close()
  93. mc.port2.close()
  94. supportsTransferable = true
  95. // Freeze TransformStream object (can only work with native)
  96. Object.defineProperty(streamSaver, 'TransformStream', {
  97. configurable: false,
  98. writable: false,
  99. value: TransformStream
  100. })
  101. })
  102. function loadTransporter () {
  103. if (!mitmTransporter) {
  104. mitmTransporter = isSecureContext
  105. ? makeIframe(streamSaver.mitm)
  106. : makePopup(streamSaver.mitm)
  107. }
  108. }
  109. /**
  110. * @param {string} filename filename that should be used
  111. * @param {object} options [description]
  112. * @param {number} size depricated
  113. * @return {WritableStream}
  114. */
  115. function createWriteStream (filename, options, size) {
  116. let opts = {
  117. size: null,
  118. pathname: null,
  119. writableStrategy: undefined,
  120. readableStrategy: undefined
  121. }
  122. // normalize arguments
  123. if (Number.isFinite(options)) {
  124. [ size, options ] = [ options, size ]
  125. console.warn('[StreamSaver] Depricated pass an object as 2nd argument when creating a write stream')
  126. opts.size = size
  127. opts.writableStrategy = options
  128. } else if (options && options.highWaterMark) {
  129. console.warn('[StreamSaver] Depricated pass an object as 2nd argument when creating a write stream')
  130. opts.size = size
  131. opts.writableStrategy = options
  132. } else {
  133. opts = options || {}
  134. }
  135. if (!useBlobFallback) {
  136. loadTransporter()
  137. var bytesWritten = 0 // by StreamSaver.js (not the service worker)
  138. var downloadUrl = null
  139. var channel = new MessageChannel()
  140. // Make filename RFC5987 compatible
  141. filename = encodeURIComponent(filename.replace(/\//g, ':'))
  142. .replace(/['()]/g, escape)
  143. .replace(/\*/g, '%2A')
  144. const response = {
  145. transferringReadable: supportsTransferable,
  146. pathname: opts.pathname || Math.random().toString().slice(-6) + '/' + filename,
  147. headers: {
  148. 'Content-Type': (options.contentType ? options.contentType :
  149. 'application/octet-stream; charset=utf-8'),
  150. 'Content-Disposition': (options.inline ? 'inline' :
  151. ("attachment; filename*=UTF-8''" + filename))
  152. }
  153. }
  154. if (opts.size) {
  155. response.headers['Content-Length'] = opts.size
  156. }
  157. const args = [ response, '*', [ channel.port2 ] ]
  158. if (supportsTransferable) {
  159. const transformer = downloadStrategy === 'iframe' ? undefined : {
  160. // This transformer & flush method is only used by insecure context.
  161. transform (chunk, controller) {
  162. bytesWritten += chunk.length
  163. controller.enqueue(chunk)
  164. if (downloadUrl) {
  165. location.href = downloadUrl
  166. downloadUrl = null
  167. }
  168. },
  169. flush () {
  170. if (downloadUrl) {
  171. location.href = downloadUrl
  172. }
  173. }
  174. }
  175. var ts = new streamSaver.TransformStream(
  176. transformer,
  177. opts.writableStrategy,
  178. opts.readableStrategy
  179. )
  180. const readableStream = ts.readable
  181. channel.port1.postMessage({ readableStream }, [ readableStream ])
  182. }
  183. channel.port1.onmessage = evt => {
  184. // Service worker sent us a link that we should open.
  185. if (evt.data.download) {
  186. // Special treatment for popup...
  187. if (downloadStrategy === 'navigate') {
  188. mitmTransporter.remove()
  189. mitmTransporter = null
  190. if (bytesWritten) {
  191. location.href = evt.data.download
  192. } else {
  193. downloadUrl = evt.data.download
  194. }
  195. } else {
  196. if (mitmTransporter.isPopup) {
  197. mitmTransporter.remove()
  198. // Special case for firefox, they can keep sw alive with fetch
  199. if (downloadStrategy === 'iframe') {
  200. makeIframe(streamSaver.mitm)
  201. }
  202. }
  203. // We never remove this iframes b/c it can interrupt saving
  204. makeIframe(evt.data.download)
  205. }
  206. }
  207. }
  208. if (mitmTransporter.loaded) {
  209. mitmTransporter.postMessage(...args)
  210. } else {
  211. mitmTransporter.addEventListener('load', () => {
  212. mitmTransporter.postMessage(...args)
  213. }, { once: true })
  214. }
  215. }
  216. let chunks = []
  217. return (!useBlobFallback && ts && ts.writable) || new streamSaver.WritableStream({
  218. write (chunk) {
  219. if (useBlobFallback) {
  220. // Safari... The new IE6
  221. // https://github.com/jimmywarting/StreamSaver.js/issues/69
  222. //
  223. // even doe it has everything it fails to download anything
  224. // that comes from the service worker..!
  225. chunks.push(chunk)
  226. return
  227. }
  228. // is called when a new chunk of data is ready to be written
  229. // to the underlying sink. It can return a promise to signal
  230. // success or failure of the write operation. The stream
  231. // implementation guarantees that this method will be called
  232. // only after previous writes have succeeded, and never after
  233. // close or abort is called.
  234. // TODO: Kind of important that service worker respond back when
  235. // it has been written. Otherwise we can't handle backpressure
  236. // EDIT: Transfarable streams solvs this...
  237. channel.port1.postMessage(chunk)
  238. bytesWritten += chunk.length
  239. if (downloadUrl) {
  240. location.href = downloadUrl
  241. downloadUrl = null
  242. }
  243. },
  244. close () {
  245. if (useBlobFallback) {
  246. const blob = new Blob(chunks, { type: 'application/octet-stream; charset=utf-8' })
  247. const link = document.createElement('a')
  248. link.href = URL.createObjectURL(blob)
  249. link.download = filename
  250. link.click()
  251. } else {
  252. channel.port1.postMessage('end')
  253. }
  254. },
  255. abort () {
  256. chunks = []
  257. channel.port1.postMessage('abort')
  258. channel.port1.onmessage = null
  259. channel.port1.close()
  260. channel.port2.close()
  261. channel = null
  262. }
  263. }, opts.writableStrategy)
  264. }
  265. return streamSaver
  266. })