Browse Source

Update ComposeModal, fix filter bug on safari

Daniel Supernault 4 years ago
parent
commit
8e3e75864c

+ 1 - 0
resources/assets/js/app.js

@@ -1,3 +1,4 @@
+require('./polyfill');
 window._ = require('lodash');
 window.Popper = require('popper.js').default;
 window.pixelfed = window.pixelfed || {};

+ 7 - 2
resources/assets/js/components/ComposeModal.vue

@@ -996,6 +996,11 @@ export default {
 
 		applyFilterToMedia() {
 			// this is where the magic happens
+			var ua = navigator.userAgent.toLowerCase();
+			if(ua.indexOf('firefox') == -1 && ua.indexOf('chrome') == -1) {
+			 	// swal('Oops!', 'Your browser does not support the filter feature. Please use Chrome or Firefox if you want to apply a filter.', 'error');
+			 	return;
+			}
 
 			let medias = this.media;
 			let media = null;
@@ -1021,8 +1026,8 @@ export default {
 							axios.post('/api/local/compose/media/update/'+media.id, data).then(res => {
 							}).catch(err => {
 							});
-						}, media.mime, 0.9);
-					});
+						});
+					}, media.mime, 0.9);
 					ctx.clearRect(0, 0, image.width, image.height);
 				}
 			}

+ 810 - 0
resources/assets/js/polyfill.js

@@ -0,0 +1,810 @@
+/* Blob.js
+ * A Blob, File, FileReader & URL implementation.
+ * 2019-04-19
+ *
+ * By Eli Grey, http://eligrey.com
+ * By Jimmy Wärting, https://github.com/jimmywarting
+ * License: MIT
+ *   See https://github.com/eligrey/Blob.js/blob/master/LICENSE.md
+ */
+
+;(function () {
+  var global = typeof window === 'object'
+      ? window : typeof self === 'object'
+      ? self : this
+
+  var BlobBuilder = global.BlobBuilder
+    || global.WebKitBlobBuilder
+    || global.MSBlobBuilder
+    || global.MozBlobBuilder
+
+  global.URL = global.URL || global.webkitURL || function (href, a) {
+  	a = document.createElement('a')
+  	a.href = href
+  	return a
+  }
+
+  var origBlob = global.Blob
+  var createObjectURL = URL.createObjectURL
+  var revokeObjectURL = URL.revokeObjectURL
+  var strTag = global.Symbol && global.Symbol.toStringTag
+  var blobSupported = false
+  var blobSupportsArrayBufferView = false
+  var arrayBufferSupported = !!global.ArrayBuffer
+  var blobBuilderSupported = BlobBuilder
+    && BlobBuilder.prototype.append
+    && BlobBuilder.prototype.getBlob
+
+  try {
+    // Check if Blob constructor is supported
+    blobSupported = new Blob(['ä']).size === 2
+
+    // Check if Blob constructor supports ArrayBufferViews
+    // Fails in Safari 6, so we need to map to ArrayBuffers there.
+    blobSupportsArrayBufferView = new Blob([new Uint8Array([1, 2])]).size === 2
+  } catch (e) {}
+
+  /**
+   * Helper function that maps ArrayBufferViews to ArrayBuffers
+   * Used by BlobBuilder constructor and old browsers that didn't
+   * support it in the Blob constructor.
+   */
+  function mapArrayBufferViews (ary) {
+    return ary.map(function (chunk) {
+      if (chunk.buffer instanceof ArrayBuffer) {
+        var buf = chunk.buffer
+
+        // if this is a subarray, make a copy so we only
+        // include the subarray region from the underlying buffer
+        if (chunk.byteLength !== buf.byteLength) {
+          var copy = new Uint8Array(chunk.byteLength)
+          copy.set(new Uint8Array(buf, chunk.byteOffset, chunk.byteLength))
+          buf = copy.buffer
+        }
+
+        return buf
+      }
+
+      return chunk
+    })
+  }
+
+  function BlobBuilderConstructor (ary, options) {
+    options = options || {}
+
+    var bb = new BlobBuilder()
+    mapArrayBufferViews(ary).forEach(function (part) {
+      bb.append(part)
+    })
+
+    return options.type ? bb.getBlob(options.type) : bb.getBlob()
+  }
+
+  function BlobConstructor (ary, options) {
+    return new origBlob(mapArrayBufferViews(ary), options || {})
+  }
+
+  if (global.Blob) {
+    BlobBuilderConstructor.prototype = Blob.prototype
+    BlobConstructor.prototype = Blob.prototype
+  }
+
+
+
+  /********************************************************/
+  /*               String Encoder fallback                */
+  /********************************************************/
+  function stringEncode (string) {
+    var pos = 0
+    var len = string.length
+    var Arr = global.Uint8Array || Array // Use byte array when possible
+
+    var at = 0  // output position
+    var tlen = Math.max(32, len + (len >> 1) + 7)  // 1.5x size
+    var target = new Arr((tlen >> 3) << 3)  // ... but at 8 byte offset
+
+    while (pos < len) {
+      var value = string.charCodeAt(pos++)
+      if (value >= 0xd800 && value <= 0xdbff) {
+        // high surrogate
+        if (pos < len) {
+          var extra = string.charCodeAt(pos)
+          if ((extra & 0xfc00) === 0xdc00) {
+            ++pos
+            value = ((value & 0x3ff) << 10) + (extra & 0x3ff) + 0x10000
+          }
+        }
+        if (value >= 0xd800 && value <= 0xdbff) {
+          continue  // drop lone surrogate
+        }
+      }
+
+      // expand the buffer if we couldn't write 4 bytes
+      if (at + 4 > target.length) {
+        tlen += 8  // minimum extra
+        tlen *= (1.0 + (pos / string.length) * 2)  // take 2x the remaining
+        tlen = (tlen >> 3) << 3  // 8 byte offset
+
+        var update = new Uint8Array(tlen)
+        update.set(target)
+        target = update
+      }
+
+      if ((value & 0xffffff80) === 0) {  // 1-byte
+        target[at++] = value  // ASCII
+        continue
+      } else if ((value & 0xfffff800) === 0) {  // 2-byte
+        target[at++] = ((value >> 6) & 0x1f) | 0xc0
+      } else if ((value & 0xffff0000) === 0) {  // 3-byte
+        target[at++] = ((value >> 12) & 0x0f) | 0xe0
+        target[at++] = ((value >> 6) & 0x3f) | 0x80
+      } else if ((value & 0xffe00000) === 0) {  // 4-byte
+        target[at++] = ((value >> 18) & 0x07) | 0xf0
+        target[at++] = ((value >> 12) & 0x3f) | 0x80
+        target[at++] = ((value >> 6) & 0x3f) | 0x80
+      } else {
+        // FIXME: do we care
+        continue
+      }
+
+      target[at++] = (value & 0x3f) | 0x80
+    }
+
+    return target.slice(0, at)
+  }
+
+  /********************************************************/
+  /*               String Decoder fallback                */
+  /********************************************************/
+  function stringDecode (buf) {
+    var end = buf.length
+    var res = []
+
+    var i = 0
+    while (i < end) {
+      var firstByte = buf[i]
+      var codePoint = null
+      var bytesPerSequence = (firstByte > 0xEF) ? 4
+        : (firstByte > 0xDF) ? 3
+          : (firstByte > 0xBF) ? 2
+            : 1
+
+      if (i + bytesPerSequence <= end) {
+        var secondByte, thirdByte, fourthByte, tempCodePoint
+
+        switch (bytesPerSequence) {
+          case 1:
+            if (firstByte < 0x80) {
+              codePoint = firstByte
+            }
+            break
+          case 2:
+            secondByte = buf[i + 1]
+            if ((secondByte & 0xC0) === 0x80) {
+              tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F)
+              if (tempCodePoint > 0x7F) {
+                codePoint = tempCodePoint
+              }
+            }
+            break
+          case 3:
+            secondByte = buf[i + 1]
+            thirdByte = buf[i + 2]
+            if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) {
+              tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F)
+              if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) {
+                codePoint = tempCodePoint
+              }
+            }
+            break
+          case 4:
+            secondByte = buf[i + 1]
+            thirdByte = buf[i + 2]
+            fourthByte = buf[i + 3]
+            if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) {
+              tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F)
+              if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) {
+                codePoint = tempCodePoint
+              }
+            }
+        }
+      }
+
+      if (codePoint === null) {
+        // we did not generate a valid codePoint so insert a
+        // replacement char (U+FFFD) and advance only 1 byte
+        codePoint = 0xFFFD
+        bytesPerSequence = 1
+      } else if (codePoint > 0xFFFF) {
+        // encode to utf16 (surrogate pair dance)
+        codePoint -= 0x10000
+        res.push(codePoint >>> 10 & 0x3FF | 0xD800)
+        codePoint = 0xDC00 | codePoint & 0x3FF
+      }
+
+      res.push(codePoint)
+      i += bytesPerSequence
+    }
+
+    var len = res.length
+    var str = ''
+    var i = 0
+
+    while (i < len) {
+      str += String.fromCharCode.apply(String, res.slice(i, i += 0x1000))
+    }
+
+    return str
+  }
+
+  // string -> buffer
+  var textEncode = typeof TextEncoder === 'function'
+    ? TextEncoder.prototype.encode.bind(new TextEncoder())
+    : stringEncode
+
+  // buffer -> string
+  var textDecode = typeof TextDecoder === 'function'
+    ? TextDecoder.prototype.decode.bind(new TextDecoder())
+    : stringDecode
+
+  function FakeBlobBuilder () {
+    function isDataView (obj) {
+      return obj && DataView.prototype.isPrototypeOf(obj)
+    }
+    function bufferClone (buf) {
+      var view = new Array(buf.byteLength)
+      var array = new Uint8Array(buf)
+      var i = view.length
+      while (i--) {
+        view[i] = array[i]
+      }
+      return view
+    }
+    function array2base64 (input) {
+      var byteToCharMap = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
+
+      var output = []
+
+      for (var i = 0; i < input.length; i += 3) {
+        var byte1 = input[i]
+        var haveByte2 = i + 1 < input.length
+        var byte2 = haveByte2 ? input[i + 1] : 0
+        var haveByte3 = i + 2 < input.length
+        var byte3 = haveByte3 ? input[i + 2] : 0
+
+        var outByte1 = byte1 >> 2
+        var outByte2 = ((byte1 & 0x03) << 4) | (byte2 >> 4)
+        var outByte3 = ((byte2 & 0x0F) << 2) | (byte3 >> 6)
+        var outByte4 = byte3 & 0x3F
+
+        if (!haveByte3) {
+          outByte4 = 64
+
+          if (!haveByte2) {
+            outByte3 = 64
+          }
+        }
+
+        output.push(
+          byteToCharMap[outByte1], byteToCharMap[outByte2],
+          byteToCharMap[outByte3], byteToCharMap[outByte4]
+        )
+      }
+
+      return output.join('')
+    }
+
+    var create = Object.create || function (a) {
+      function c () {}
+      c.prototype = a
+      return new c()
+    }
+
+    if (arrayBufferSupported) {
+      var viewClasses = [
+        '[object Int8Array]',
+        '[object Uint8Array]',
+        '[object Uint8ClampedArray]',
+        '[object Int16Array]',
+        '[object Uint16Array]',
+        '[object Int32Array]',
+        '[object Uint32Array]',
+        '[object Float32Array]',
+        '[object Float64Array]'
+      ]
+
+      var isArrayBufferView = ArrayBuffer.isView || function (obj) {
+        return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1
+      }
+    }
+
+    function concatTypedarrays (chunks) {
+      var size = 0
+      var i = chunks.length
+      while (i--) { size += chunks[i].length }
+      var b = new Uint8Array(size)
+      var offset = 0
+      for (i = 0, l = chunks.length; i < l; i++) {
+        var chunk = chunks[i]
+        b.set(chunk, offset)
+        offset += chunk.byteLength || chunk.length
+      }
+
+      return b
+    }
+
+    /********************************************************/
+    /*                   Blob constructor                   */
+    /********************************************************/
+    function Blob (chunks, opts) {
+      chunks = chunks || []
+      opts = opts == null ? {} : opts
+      for (var i = 0, len = chunks.length; i < len; i++) {
+        var chunk = chunks[i]
+        if (chunk instanceof Blob) {
+          chunks[i] = chunk._buffer
+        } else if (typeof chunk === 'string') {
+          chunks[i] = textEncode(chunk)
+        } else if (arrayBufferSupported && (ArrayBuffer.prototype.isPrototypeOf(chunk) || isArrayBufferView(chunk))) {
+          chunks[i] = bufferClone(chunk)
+        } else if (arrayBufferSupported && isDataView(chunk)) {
+          chunks[i] = bufferClone(chunk.buffer)
+        } else {
+          chunks[i] = textEncode(String(chunk))
+        }
+      }
+
+      this._buffer = global.Uint8Array
+        ? concatTypedarrays(chunks)
+        : [].concat.apply([], chunks)
+      this.size = this._buffer.length
+
+      this.type = opts.type || ''
+      if (/[^\u0020-\u007E]/.test(this.type)) {
+        this.type = ''
+      } else {
+        this.type = this.type.toLowerCase()
+      }
+    }
+
+    Blob.prototype.arrayBuffer = function () {
+      return Promise.resolve(this._buffer)
+    }
+
+    Blob.prototype.text = function () {
+      return Promise.resolve(textDecode(this._buffer))
+    }
+
+    Blob.prototype.slice = function (start, end, type) {
+      var slice = this._buffer.slice(start || 0, end || this._buffer.length)
+      return new Blob([slice], {type: type})
+    }
+
+    Blob.prototype.toString = function () {
+      return '[object Blob]'
+    }
+
+    /********************************************************/
+    /*                   File constructor                   */
+    /********************************************************/
+    function File (chunks, name, opts) {
+      opts = opts || {}
+      var a = Blob.call(this, chunks, opts) || this
+      a.name = name.replace(/\//g, ':')
+      a.lastModifiedDate = opts.lastModified ? new Date(opts.lastModified) : new Date()
+      a.lastModified = +a.lastModifiedDate
+
+      return a
+    }
+
+    File.prototype = create(Blob.prototype)
+    File.prototype.constructor = File
+
+    if (Object.setPrototypeOf) {
+      Object.setPrototypeOf(File, Blob)
+    } else {
+      try { File.__proto__ = Blob } catch (e) {}
+    }
+
+    File.prototype.toString = function () {
+      return '[object File]'
+    }
+
+    /********************************************************/
+    /*                FileReader constructor                */
+    /********************************************************/
+    function FileReader () {
+    	if (!(this instanceof FileReader)) {
+        throw new TypeError("Failed to construct 'FileReader': Please use the 'new' operator, this DOM object constructor cannot be called as a function.")
+      }
+
+    	var delegate = document.createDocumentFragment()
+    	this.addEventListener = delegate.addEventListener
+    	this.dispatchEvent = function (evt) {
+    		var local = this['on' + evt.type]
+    		if (typeof local === 'function') local(evt)
+    		delegate.dispatchEvent(evt)
+    	}
+    	this.removeEventListener = delegate.removeEventListener
+    }
+
+    function _read (fr, blob, kind) {
+    	if (!(blob instanceof Blob)) {
+        throw new TypeError("Failed to execute '" + kind + "' on 'FileReader': parameter 1 is not of type 'Blob'.")
+      }
+
+    	fr.result = ''
+
+    	setTimeout(function () {
+    		this.readyState = FileReader.LOADING
+    		fr.dispatchEvent(new Event('load'))
+    		fr.dispatchEvent(new Event('loadend'))
+    	})
+    }
+
+    FileReader.EMPTY = 0
+    FileReader.LOADING = 1
+    FileReader.DONE = 2
+    FileReader.prototype.error = null
+    FileReader.prototype.onabort = null
+    FileReader.prototype.onerror = null
+    FileReader.prototype.onload = null
+    FileReader.prototype.onloadend = null
+    FileReader.prototype.onloadstart = null
+    FileReader.prototype.onprogress = null
+
+    FileReader.prototype.readAsDataURL = function (blob) {
+    	_read(this, blob, 'readAsDataURL')
+    	this.result = 'data:' + blob.type + ';base64,' + array2base64(blob._buffer)
+    }
+
+    FileReader.prototype.readAsText = function (blob) {
+    	_read(this, blob, 'readAsText')
+    	this.result = textDecode(blob._buffer)
+    }
+
+    FileReader.prototype.readAsArrayBuffer = function (blob) {
+      _read(this, blob, 'readAsText')
+       // return ArrayBuffer when possible
+      this.result = (blob._buffer.buffer || blob._buffer).slice()
+    }
+
+    FileReader.prototype.abort = function () {}
+
+    /********************************************************/
+    /*                         URL                          */
+    /********************************************************/
+    URL.createObjectURL = function (blob) {
+      return blob instanceof Blob
+        ? 'data:' + blob.type + ';base64,' + array2base64(blob._buffer)
+        : createObjectURL.call(URL, blob)
+    }
+
+    URL.revokeObjectURL = function (url) {
+      revokeObjectURL && revokeObjectURL.call(URL, url)
+    }
+
+    /********************************************************/
+    /*                         XHR                          */
+    /********************************************************/
+    var _send = global.XMLHttpRequest && global.XMLHttpRequest.prototype.send
+    if (_send) {
+      XMLHttpRequest.prototype.send = function (data) {
+        if (data instanceof Blob) {
+          this.setRequestHeader('Content-Type', data.type)
+          _send.call(this, textDecode(data._buffer))
+        } else {
+          _send.call(this, data)
+        }
+      }
+    }
+
+    global.FileReader = FileReader
+    global.File = File
+    global.Blob = Blob
+  }
+
+  function fixFileAndXHR () {
+    var isIE = !!global.ActiveXObject || (
+      '-ms-scroll-limit' in document.documentElement.style &&
+      '-ms-ime-align' in document.documentElement.style
+    )
+
+    // Monkey patched
+    // IE don't set Content-Type header on XHR whose body is a typed Blob
+    // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/6047383
+    var _send = global.XMLHttpRequest && global.XMLHttpRequest.prototype.send
+    if (isIE && _send) {
+      XMLHttpRequest.prototype.send = function (data) {
+        if (data instanceof Blob) {
+          this.setRequestHeader('Content-Type', data.type)
+          _send.call(this, data)
+        } else {
+          _send.call(this, data)
+        }
+      }
+    }
+
+    try {
+      new File([], '')
+    } catch (e) {
+      try {
+        var klass = new Function('class File extends Blob {' +
+          'constructor(chunks, name, opts) {' +
+            'opts = opts || {};' +
+            'super(chunks, opts || {});' +
+            'this.name = name.replace(/\//g, ":");' +
+            'this.lastModifiedDate = opts.lastModified ? new Date(opts.lastModified) : new Date();' +
+            'this.lastModified = +this.lastModifiedDate;' +
+          '}};' +
+          'return new File([], ""), File'
+        )()
+        global.File = klass
+      } catch (e) {
+        var klass = function (b, d, c) {
+          var blob = new Blob(b, c)
+          var t = c && void 0 !== c.lastModified ? new Date(c.lastModified) : new Date()
+
+          blob.name = d.replace(/\//g, ':')
+          blob.lastModifiedDate = t
+          blob.lastModified = +t
+          blob.toString = function () {
+            return '[object File]'
+          }
+
+          if (strTag) {
+            blob[strTag] = 'File'
+          }
+
+          return blob
+        }
+        global.File = klass
+      }
+    }
+  }
+
+  if (blobSupported) {
+    fixFileAndXHR()
+    global.Blob = blobSupportsArrayBufferView ? global.Blob : BlobConstructor
+  } else if (blobBuilderSupported) {
+    fixFileAndXHR()
+    global.Blob = BlobBuilderConstructor
+  } else {
+    FakeBlobBuilder()
+  }
+
+  if (strTag) {
+    File.prototype[strTag] = 'File'
+    Blob.prototype[strTag] = 'Blob'
+    FileReader.prototype[strTag] = 'FileReader'
+  }
+
+  var blob = global.Blob.prototype
+  var stream
+
+  function promisify(obj) {
+    return new Promise(function(resolve, reject) {
+      obj.onload =
+      obj.onerror = function(evt) {
+        obj.onload =
+        obj.onerror = null
+
+        evt.type === 'load'
+          ? resolve(obj.result || obj)
+          : reject(new Error('Failed to read the blob/file'))
+      }
+    })
+  }
+
+
+  try {
+    new ReadableStream({ type: 'bytes' })
+    stream = function stream() {
+      var position = 0
+      var blob = this
+
+      return new ReadableStream({
+        type: 'bytes',
+        autoAllocateChunkSize: 524288,
+
+        pull: function (controller) {
+          var v = controller.byobRequest.view
+          var chunk = blob.slice(position, position + v.byteLength)
+          return chunk.arrayBuffer()
+          .then(function (buffer) {
+            var uint8array = new Uint8Array(buffer)
+            var bytesRead = uint8array.byteLength
+
+            position += bytesRead
+            v.set(uint8array)
+              controller.byobRequest.respond(bytesRead)
+
+            if(position >= blob.size)
+              controller.close()
+          })
+        }
+      })
+    }
+  } catch (e) {
+    try {
+      new ReadableStream({})
+      stream = function stream(blob){
+        var position = 0
+        var blob = this
+
+        return new ReadableStream({
+          pull: function (controller) {
+            var chunk = blob.slice(position, position + 524288)
+
+            return chunk.arrayBuffer().then(function (buffer) {
+              position += buffer.byteLength
+              var uint8array = new Uint8Array(buffer)
+              controller.enqueue(uint8array)
+
+              if (position == blob.size)
+                controller.close()
+            })
+          }
+        })
+      }
+    } catch (e) {
+      try {
+        new Response('').body.getReader().read()
+        stream = function stream() {
+          return (new Response(this)).body
+        }
+      } catch (e) {
+        stream = function stream() {
+          throw new Error('Include https://github.com/MattiasBuelens/web-streams-polyfill')
+        }
+      }
+    }
+  }
+
+
+  if (!blob.arrayBuffer) {
+    blob.arrayBuffer = function arrayBuffer() {
+      var fr = new FileReader()
+      fr.readAsArrayBuffer(this)
+      return promisify(fr)
+    }
+  }
+
+  if (!blob.text) {
+    blob.text = function text() {
+      var fr = new FileReader()
+      fr.readAsText(this)
+      return promisify(fr)
+    }
+  }
+
+  if (!blob.stream) {
+    blob.stream = stream
+  }
+})();
+
+/* canvas-toBlob.js
+ * A canvas.toBlob() implementation.
+ * 2016-05-26
+ * 
+ * By Eli Grey, http://eligrey.com and Devin Samarin, https://github.com/eboyjr
+ * License: MIT
+ *   See https://github.com/eligrey/canvas-toBlob.js/blob/master/LICENSE.md
+ */
+
+/*global self */
+/*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true,
+  plusplus: true */
+
+/*! @source http://purl.eligrey.com/github/canvas-toBlob.js/blob/master/canvas-toBlob.js */
+
+(function(view) {
+"use strict";
+var
+	  Uint8Array = view.Uint8Array
+	, HTMLCanvasElement = view.HTMLCanvasElement
+	, canvas_proto = HTMLCanvasElement && HTMLCanvasElement.prototype
+	, is_base64_regex = /\s*;\s*base64\s*(?:;|$)/i
+	, to_data_url = "toDataURL"
+	, base64_ranks
+	, decode_base64 = function(base64) {
+		var
+			  len = base64.length
+			, buffer = new Uint8Array(len / 4 * 3 | 0)
+			, i = 0
+			, outptr = 0
+			, last = [0, 0]
+			, state = 0
+			, save = 0
+			, rank
+			, code
+			, undef
+		;
+		while (len--) {
+			code = base64.charCodeAt(i++);
+			rank = base64_ranks[code-43];
+			if (rank !== 255 && rank !== undef) {
+				last[1] = last[0];
+				last[0] = code;
+				save = (save << 6) | rank;
+				state++;
+				if (state === 4) {
+					buffer[outptr++] = save >>> 16;
+					if (last[1] !== 61 /* padding character */) {
+						buffer[outptr++] = save >>> 8;
+					}
+					if (last[0] !== 61 /* padding character */) {
+						buffer[outptr++] = save;
+					}
+					state = 0;
+				}
+			}
+		}
+		// 2/3 chance there's going to be some null bytes at the end, but that
+		// doesn't really matter with most image formats.
+		// If it somehow matters for you, truncate the buffer up outptr.
+		return buffer;
+	}
+;
+if (Uint8Array) {
+	base64_ranks = new Uint8Array([
+		  62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1
+		, -1, -1,  0, -1, -1, -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9
+		, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25
+		, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35
+		, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
+	]);
+}
+if (HTMLCanvasElement && (!canvas_proto.toBlob || !canvas_proto.toBlobHD)) {
+	if (!canvas_proto.toBlob)
+	canvas_proto.toBlob = function(callback, type /*, ...args*/) {
+		  if (!type) {
+			type = "image/png";
+		} if (this.mozGetAsFile) {
+			callback(this.mozGetAsFile("canvas", type));
+			return;
+		} if (this.msToBlob && /^\s*image\/png\s*(?:$|;)/i.test(type)) {
+			callback(this.msToBlob());
+			return;
+		}
+
+		var
+			  args = Array.prototype.slice.call(arguments, 1)
+			, dataURI = this[to_data_url].apply(this, args)
+			, header_end = dataURI.indexOf(",")
+			, data = dataURI.substring(header_end + 1)
+			, is_base64 = is_base64_regex.test(dataURI.substring(0, header_end))
+			, blob
+		;
+		if (Blob.fake) {
+			// no reason to decode a data: URI that's just going to become a data URI again
+			blob = new Blob
+			if (is_base64) {
+				blob.encoding = "base64";
+			} else {
+				blob.encoding = "URI";
+			}
+			blob.data = data;
+			blob.size = data.length;
+		} else if (Uint8Array) {
+			if (is_base64) {
+				blob = new Blob([decode_base64(data)], {type: type});
+			} else {
+				blob = new Blob([decodeURIComponent(data)], {type: type});
+			}
+		}
+		callback(blob);
+	};
+
+	if (!canvas_proto.toBlobHD && canvas_proto.toDataURLHD) {
+		canvas_proto.toBlobHD = function() {
+			to_data_url = "toDataURLHD";
+			var blob = this.toBlob();
+			to_data_url = "toDataURL";
+			return blob;
+		}
+	} else {
+		canvas_proto.toBlobHD = canvas_proto.toBlob;
+	}
+}
+}(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content || this));