1/* Blob.js 2 * A Blob implementation. 3 * 2014-07-24 4 * 5 * By Eli Grey, http://eligrey.com 6 * By Devin Samarin, https://github.com/dsamarin 7 * License: X11/MIT 8 * See https://github.com/eligrey/Blob.js/blob/master/LICENSE.md 9 */ 10 11/*global self, unescape */ 12/*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true, 13 plusplus: true */ 14 15/*! @source http://purl.eligrey.com/github/Blob.js/blob/master/Blob.js */ 16 17(function (view) { 18 "use strict"; 19 20 view.URL = view.URL || view.webkitURL; 21 22 if (view.Blob && view.URL) { 23 try { 24 new Blob; 25 return; 26 } catch (e) {} 27 } 28 29 // Internally we use a BlobBuilder implementation to base Blob off of 30 // in order to support older browsers that only have BlobBuilder 31 var BlobBuilder = view.BlobBuilder || view.WebKitBlobBuilder || view.MozBlobBuilder || (function(view) { 32 var 33 get_class = function(object) { 34 return Object.prototype.toString.call(object).match(/^\[object\s(.*)\]$/)[1]; 35 } 36 , FakeBlobBuilder = function BlobBuilder() { 37 this.data = []; 38 } 39 , FakeBlob = function Blob(data, type, encoding) { 40 this.data = data; 41 this.size = data.length; 42 this.type = type; 43 this.encoding = encoding; 44 } 45 , FBB_proto = FakeBlobBuilder.prototype 46 , FB_proto = FakeBlob.prototype 47 , FileReaderSync = view.FileReaderSync 48 , FileException = function(type) { 49 this.code = this[this.name = type]; 50 } 51 , file_ex_codes = ( 52 "NOT_FOUND_ERR SECURITY_ERR ABORT_ERR NOT_READABLE_ERR ENCODING_ERR " 53 + "NO_MODIFICATION_ALLOWED_ERR INVALID_STATE_ERR SYNTAX_ERR" 54 ).split(" ") 55 , file_ex_code = file_ex_codes.length 56 , real_URL = view.URL || view.webkitURL || view 57 , real_create_object_URL = real_URL.createObjectURL 58 , real_revoke_object_URL = real_URL.revokeObjectURL 59 , URL = real_URL 60 , btoa = view.btoa 61 , atob = view.atob 62 63 , ArrayBuffer = view.ArrayBuffer 64 , Uint8Array = view.Uint8Array 65 66 , origin = /^[\w-]+:\/*\[?[\w\.:-]+\]?(?::[0-9]+)?/ 67 ; 68 FakeBlob.fake = FB_proto.fake = true; 69 while (file_ex_code--) { 70 FileException.prototype[file_ex_codes[file_ex_code]] = file_ex_code + 1; 71 } 72 // Polyfill URL 73 if (!real_URL.createObjectURL) { 74 URL = view.URL = function(uri) { 75 var 76 uri_info = document.createElementNS("http://www.w3.org/1999/xhtml", "a") 77 , uri_origin 78 ; 79 uri_info.href = uri; 80 if (!("origin" in uri_info)) { 81 if (uri_info.protocol.toLowerCase() === "data:") { 82 uri_info.origin = null; 83 } else { 84 uri_origin = uri.match(origin); 85 uri_info.origin = uri_origin && uri_origin[1]; 86 } 87 } 88 return uri_info; 89 }; 90 } 91 URL.createObjectURL = function(blob) { 92 var 93 type = blob.type 94 , data_URI_header 95 ; 96 if (type === null) { 97 type = "application/octet-stream"; 98 } 99 if (blob instanceof FakeBlob) { 100 data_URI_header = "data:" + type; 101 if (blob.encoding === "base64") { 102 return data_URI_header + ";base64," + blob.data; 103 } else if (blob.encoding === "URI") { 104 return data_URI_header + "," + decodeURIComponent(blob.data); 105 } if (btoa) { 106 return data_URI_header + ";base64," + btoa(blob.data); 107 } else { 108 return data_URI_header + "," + encodeURIComponent(blob.data); 109 } 110 } else if (real_create_object_URL) { 111 return real_create_object_URL.call(real_URL, blob); 112 } 113 }; 114 URL.revokeObjectURL = function(object_URL) { 115 if (object_URL.substring(0, 5) !== "data:" && real_revoke_object_URL) { 116 real_revoke_object_URL.call(real_URL, object_URL); 117 } 118 }; 119 FBB_proto.append = function(data/*, endings*/) { 120 var bb = this.data; 121 // decode data to a binary string 122 if (Uint8Array && (data instanceof ArrayBuffer || data instanceof Uint8Array)) { 123 var 124 str = "" 125 , buf = new Uint8Array(data) 126 , i = 0 127 , buf_len = buf.length 128 ; 129 for (; i < buf_len; i++) { 130 str += String.fromCharCode(buf[i]); 131 } 132 bb.push(str); 133 } else if (get_class(data) === "Blob" || get_class(data) === "File") { 134 if (FileReaderSync) { 135 var fr = new FileReaderSync; 136 bb.push(fr.readAsBinaryString(data)); 137 } else { 138 // async FileReader won't work as BlobBuilder is sync 139 throw new FileException("NOT_READABLE_ERR"); 140 } 141 } else if (data instanceof FakeBlob) { 142 if (data.encoding === "base64" && atob) { 143 bb.push(atob(data.data)); 144 } else if (data.encoding === "URI") { 145 bb.push(decodeURIComponent(data.data)); 146 } else if (data.encoding === "raw") { 147 bb.push(data.data); 148 } 149 } else { 150 if (typeof data !== "string") { 151 data += ""; // convert unsupported types to strings 152 } 153 // decode UTF-16 to binary string 154 bb.push(unescape(encodeURIComponent(data))); 155 } 156 }; 157 FBB_proto.getBlob = function(type) { 158 if (!arguments.length) { 159 type = null; 160 } 161 return new FakeBlob(this.data.join(""), type, "raw"); 162 }; 163 FBB_proto.toString = function() { 164 return "[object BlobBuilder]"; 165 }; 166 FB_proto.slice = function(start, end, type) { 167 var args = arguments.length; 168 if (args < 3) { 169 type = null; 170 } 171 return new FakeBlob( 172 this.data.slice(start, args > 1 ? end : this.data.length) 173 , type 174 , this.encoding 175 ); 176 }; 177 FB_proto.toString = function() { 178 return "[object Blob]"; 179 }; 180 FB_proto.close = function() { 181 this.size = 0; 182 delete this.data; 183 }; 184 return FakeBlobBuilder; 185 }(view)); 186 187 view.Blob = function(blobParts, options) { 188 var type = options ? (options.type || "") : ""; 189 var builder = new BlobBuilder(); 190 if (blobParts) { 191 for (var i = 0, len = blobParts.length; i < len; i++) { 192 if (Uint8Array && blobParts[i] instanceof Uint8Array) { 193 builder.append(blobParts[i].buffer); 194 } 195 else { 196 builder.append(blobParts[i]); 197 } 198 } 199 } 200 var blob = builder.getBlob(type); 201 if (!blob.slice && blob.webkitSlice) { 202 blob.slice = blob.webkitSlice; 203 } 204 return blob; 205 }; 206 207 var getPrototypeOf = Object.getPrototypeOf || function(object) { 208 return object.__proto__; 209 }; 210 view.Blob.prototype = getPrototypeOf(new view.Blob()); 211}(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content || this));