From e384f7d2e2f3e4beec1f3cbc77b5ba03a7f834f5 Mon Sep 17 00:00:00 2001 From: jdescottes Date: Tue, 14 Apr 2015 23:08:53 +0200 Subject: [PATCH] Added Blob polyfill for Phantom JS (debug mode only) --- Gruntfile.js | 37 +++---- src/js/devtools/lib/Blob.js | 195 ++++++++++++++++++++++++++++++++++++ src/piskel-script-list.js | 3 + 3 files changed, 217 insertions(+), 18 deletions(-) create mode 100644 src/js/devtools/lib/Blob.js diff --git a/Gruntfile.js b/Gruntfile.js index 5ea1d5aa..4b4b85be 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -71,6 +71,23 @@ module.exports = function(grunt) { clean: { before: ['dest'] }, + leadingIndent : { + options: { + indentation : "spaces" + }, + css : ['src/css/**/*.css'] + }, + jscs : { + options : { + "preset": "google", + "maximumLineLength": 120, + "requireCamelCaseOrUpperCaseIdentifiers": "ignoreProperties", + "validateQuoteMarks": { "mark": "'", "escape": true }, + "disallowMultipleVarDecl": "exceptUndefined", + "disallowSpacesInAnonymousFunctionExpression": null + }, + js : [ 'src/js/**/*.js' , '!src/js/**/lib/**/*.js' ] + }, jshint: { options: { undef : true, @@ -84,7 +101,7 @@ module.exports = function(grunt) { 'Gruntfile.js', 'package.json', 'src/js/**/*.js', - '!src/js/lib/**/*.js' // Exclude lib folder (note the leading !) + '!src/js/**/lib/**/*.js' // Exclude lib folder (note the leading !) ] }, express: { @@ -208,23 +225,6 @@ module.exports = function(grunt) { linux64: true }, src: ['./dest/**/*', "./package.json", "!./dest/desktop/"] - }, - leadingIndent : { - options: { - indentation : "spaces" - }, - css : ['src/css/**/*.css'] - }, - jscs : { - options : { - "preset": "google", - "maximumLineLength": 120, - "requireCamelCaseOrUpperCaseIdentifiers": "ignoreProperties", - "validateQuoteMarks": { "mark": "'", "escape": true }, - "disallowMultipleVarDecl": "exceptUndefined", - "disallowSpacesInAnonymousFunctionExpression": null - }, - js : [ 'src/js/**/*.js' , '!src/js/lib/**/*.js' ] } }); @@ -238,6 +238,7 @@ module.exports = function(grunt) { grunt.registerTask('test-travis', ['lint', 'unit-test', 'express:test', 'ghost:travis']); // Validate & Test (faster version) will NOT work on travis !! grunt.registerTask('test-local', ['lint', 'unit-test', 'express:test', 'ghost:local']); + grunt.registerTask('test-local-nolint', ['unit-test', 'express:test', 'ghost:local']); grunt.registerTask('test', ['test-travis']); grunt.registerTask('precommit', ['test-local']); diff --git a/src/js/devtools/lib/Blob.js b/src/js/devtools/lib/Blob.js new file mode 100644 index 00000000..0fdcc10f --- /dev/null +++ b/src/js/devtools/lib/Blob.js @@ -0,0 +1,195 @@ +(function (view) { + "use strict"; + + view.URL = view.URL || view.webkitURL; + + if (view.Blob && view.URL) { + try { + new Blob; + return; + } catch (e) {} + } + + // Internally we use a BlobBuilder implementation to base Blob off of + // in order to support older browsers that only have BlobBuilder + var BlobBuilder = view.BlobBuilder || view.WebKitBlobBuilder || view.MozBlobBuilder || (function(view) { + var + get_class = function(object) { + return Object.prototype.toString.call(object).match(/^\[object\s(.*)\]$/)[1]; + } + , FakeBlobBuilder = function BlobBuilder() { + this.data = []; + } + , FakeBlob = function Blob(data, type, encoding) { + this.data = data; + this.size = data.length; + this.type = type; + this.encoding = encoding; + } + , FBB_proto = FakeBlobBuilder.prototype + , FB_proto = FakeBlob.prototype + , FileReaderSync = view.FileReaderSync + , FileException = function(type) { + this.code = this[this.name = type]; + } + , file_ex_codes = ( + "NOT_FOUND_ERR SECURITY_ERR ABORT_ERR NOT_READABLE_ERR ENCODING_ERR " + + "NO_MODIFICATION_ALLOWED_ERR INVALID_STATE_ERR SYNTAX_ERR" + ).split(" ") + , file_ex_code = file_ex_codes.length + , real_URL = view.URL || view.webkitURL || view + , real_create_object_URL = real_URL.createObjectURL + , real_revoke_object_URL = real_URL.revokeObjectURL + , URL = real_URL + , btoa = view.btoa + , atob = view.atob + + , ArrayBuffer = view.ArrayBuffer + , Uint8Array = view.Uint8Array + + , origin = /^[\w-]+:\/*\[?[\w\.:-]+\]?(?::[0-9]+)?/ + ; + FakeBlob.fake = FB_proto.fake = true; + while (file_ex_code--) { + FileException.prototype[file_ex_codes[file_ex_code]] = file_ex_code + 1; + } + // Polyfill URL + if (!real_URL.createObjectURL) { + URL = view.URL = function(uri) { + var + uri_info = document.createElementNS("http://www.w3.org/1999/xhtml", "a") + , uri_origin + ; + uri_info.href = uri; + if (!("origin" in uri_info)) { + if (uri_info.protocol.toLowerCase() === "data:") { + uri_info.origin = null; + } else { + uri_origin = uri.match(origin); + uri_info.origin = uri_origin && uri_origin[1]; + } + } + return uri_info; + }; + } + URL.createObjectURL = function(blob) { + var + type = blob.type + , data_URI_header + ; + if (type === null) { + type = "application/octet-stream"; + } + if (blob instanceof FakeBlob) { + data_URI_header = "data:" + type; + if (blob.encoding === "base64") { + return data_URI_header + ";base64," + blob.data; + } else if (blob.encoding === "URI") { + return data_URI_header + "," + decodeURIComponent(blob.data); + } if (btoa) { + return data_URI_header + ";base64," + btoa(blob.data); + } else { + return data_URI_header + "," + encodeURIComponent(blob.data); + } + } else if (real_create_object_URL) { + return real_create_object_URL.call(real_URL, blob); + } + }; + URL.revokeObjectURL = function(object_URL) { + if (object_URL.substring(0, 5) !== "data:" && real_revoke_object_URL) { + real_revoke_object_URL.call(real_URL, object_URL); + } + }; + FBB_proto.append = function(data/*, endings*/) { + var bb = this.data; + // decode data to a binary string + if (Uint8Array && (data instanceof ArrayBuffer || data instanceof Uint8Array)) { + var + str = "" + , buf = new Uint8Array(data) + , i = 0 + , buf_len = buf.length + ; + for (; i < buf_len; i++) { + str += String.fromCharCode(buf[i]); + } + bb.push(str); + } else if (get_class(data) === "Blob" || get_class(data) === "File") { + if (FileReaderSync) { + var fr = new FileReaderSync; + bb.push(fr.readAsBinaryString(data)); + } else { + // async FileReader won't work as BlobBuilder is sync + throw new FileException("NOT_READABLE_ERR"); + } + } else if (data instanceof FakeBlob) { + if (data.encoding === "base64" && atob) { + bb.push(atob(data.data)); + } else if (data.encoding === "URI") { + bb.push(decodeURIComponent(data.data)); + } else if (data.encoding === "raw") { + bb.push(data.data); + } + } else { + if (typeof data !== "string") { + data += ""; // convert unsupported types to strings + } + // decode UTF-16 to binary string + bb.push(unescape(encodeURIComponent(data))); + } + }; + FBB_proto.getBlob = function(type) { + if (!arguments.length) { + type = null; + } + return new FakeBlob(this.data.join(""), type, "raw"); + }; + FBB_proto.toString = function() { + return "[object BlobBuilder]"; + }; + FB_proto.slice = function(start, end, type) { + var args = arguments.length; + if (args < 3) { + type = null; + } + return new FakeBlob( + this.data.slice(start, args > 1 ? end : this.data.length) + , type + , this.encoding + ); + }; + FB_proto.toString = function() { + return "[object Blob]"; + }; + FB_proto.close = function() { + this.size = 0; + delete this.data; + }; + return FakeBlobBuilder; + }(view)); + + view.Blob = function(blobParts, options) { + var type = options ? (options.type || "") : ""; + var builder = new BlobBuilder(); + if (blobParts) { + for (var i = 0, len = blobParts.length; i < len; i++) { + if (Uint8Array && blobParts[i] instanceof Uint8Array) { + builder.append(blobParts[i].buffer); + } + else { + builder.append(blobParts[i]); + } + } + } + var blob = builder.getBlob(type); + if (!blob.slice && blob.webkitSlice) { + blob.slice = blob.webkitSlice; + } + return blob; + }; + + var getPrototypeOf = Object.getPrototypeOf || function(object) { + return object.__proto__; + }; + view.Blob.prototype = getPrototypeOf(new view.Blob()); +}(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content || this)); \ No newline at end of file diff --git a/src/piskel-script-list.js b/src/piskel-script-list.js index 2ac9abf9..783a01de 100644 --- a/src/piskel-script-list.js +++ b/src/piskel-script-list.js @@ -4,6 +4,8 @@ // Core libraries "js/lib/jquery-1.8.0.js","js/lib/jquery-ui-1.10.3.custom.js","js/lib/pubsub.js","js/lib/bootstrap/bootstrap.js", + + // Application wide configuration "js/Constants.js", "js/Events.js", @@ -191,6 +193,7 @@ "js/devtools/MouseEvent.js", "js/devtools/TestRecordController.js", "js/devtools/init.js", + "js/devtools/lib/Blob.js", // Workers "js/worker/framecolors/FrameColorsWorker.js",