michael@0: /* michael@0: * Copyright 2013 Mozilla Foundation michael@0: * michael@0: * Licensed under the Apache License, Version 2.0 (the "License"); michael@0: * you may not use this file except in compliance with the License. michael@0: * You may obtain a copy of the License at michael@0: * michael@0: * http://www.apache.org/licenses/LICENSE-2.0 michael@0: * michael@0: * Unless required by applicable law or agreed to in writing, software michael@0: * distributed under the License is distributed on an "AS IS" BASIS, michael@0: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. michael@0: * See the License for the specific language governing permissions and michael@0: * limitations under the License. michael@0: */ michael@0: michael@0: // This file is automatically generated michael@0: michael@0: (function (global) { michael@0: if (global.DataView) michael@0: return; michael@0: if (!global.ArrayBuffer) michael@0: fail('ArrayBuffer not supported'); michael@0: if (!Object.defineProperties) michael@0: fail('This module requires ECMAScript 5'); michael@0: var nativele = new Int8Array(new Int32Array([ michael@0: 1 michael@0: ]).buffer)[0] === 1; michael@0: var temp = new Uint8Array(8); michael@0: global.DataView = function DataView(buffer, offset, length) { michael@0: if (!(buffer instanceof ArrayBuffer)) michael@0: fail('Bad ArrayBuffer'); michael@0: offset = offset || 0; michael@0: length = length || buffer.byteLength - offset; michael@0: if (offset < 0 || length < 0 || offset + length > buffer.byteLength) michael@0: fail('Illegal offset and/or length'); michael@0: Object.defineProperties(this, { michael@0: buffer: { michael@0: value: buffer, michael@0: enumerable: false, michael@0: writable: false, michael@0: configurable: false michael@0: }, michael@0: byteOffset: { michael@0: value: offset, michael@0: enumerable: false, michael@0: writable: false, michael@0: configurable: false michael@0: }, michael@0: byteLength: { michael@0: value: length, michael@0: enumerable: false, michael@0: writable: false, michael@0: configurable: false michael@0: }, michael@0: _bytes: { michael@0: value: new Uint8Array(buffer, offset, length), michael@0: enumerable: false, michael@0: writable: false, michael@0: configurable: false michael@0: } michael@0: }); michael@0: }; michael@0: global.DataView.prototype = { michael@0: constructor: DataView, michael@0: getInt8: function getInt8(offset) { michael@0: return get(this, Int8Array, 1, offset); michael@0: }, michael@0: getUint8: function getUint8(offset) { michael@0: return get(this, Uint8Array, 1, offset); michael@0: }, michael@0: getInt16: function getInt16(offset, le) { michael@0: return get(this, Int16Array, 2, offset, le); michael@0: }, michael@0: getUint16: function getUint16(offset, le) { michael@0: return get(this, Uint16Array, 2, offset, le); michael@0: }, michael@0: getInt32: function getInt32(offset, le) { michael@0: return get(this, Int32Array, 4, offset, le); michael@0: }, michael@0: getUint32: function getUint32(offset, le) { michael@0: return get(this, Uint32Array, 4, offset, le); michael@0: }, michael@0: getFloat32: function getFloat32(offset, le) { michael@0: return get(this, Float32Array, 4, offset, le); michael@0: }, michael@0: getFloat64: function getFloat32(offset, le) { michael@0: return get(this, Float64Array, 8, offset, le); michael@0: }, michael@0: setInt8: function setInt8(offset, value) { michael@0: set(this, Int8Array, 1, offset, value); michael@0: }, michael@0: setUint8: function setUint8(offset, value) { michael@0: set(this, Uint8Array, 1, offset, value); michael@0: }, michael@0: setInt16: function setInt16(offset, value, le) { michael@0: set(this, Int16Array, 2, offset, value, le); michael@0: }, michael@0: setUint16: function setUint16(offset, value, le) { michael@0: set(this, Uint16Array, 2, offset, value, le); michael@0: }, michael@0: setInt32: function setInt32(offset, value, le) { michael@0: set(this, Int32Array, 4, offset, value, le); michael@0: }, michael@0: setUint32: function setUint32(offset, value, le) { michael@0: set(this, Uint32Array, 4, offset, value, le); michael@0: }, michael@0: setFloat32: function setFloat32(offset, value, le) { michael@0: set(this, Float32Array, 4, offset, value, le); michael@0: }, michael@0: setFloat64: function setFloat64(offset, value, le) { michael@0: set(this, Float64Array, 8, offset, value, le); michael@0: } michael@0: }; michael@0: function get(view, type, size, offset, le) { michael@0: if (offset === undefined) michael@0: fail('Missing required offset argument'); michael@0: if (offset < 0 || offset + size > view.byteLength) michael@0: fail('Invalid index: ' + offset); michael@0: if (size === 1 || !(!le) === nativele) { michael@0: if ((view.byteOffset + offset) % size === 0) michael@0: return new type(view.buffer, view.byteOffset + offset, 1)[0]; michael@0: else { michael@0: for (var i = 0; i < size; i++) michael@0: temp[i] = view._bytes[offset + i]; michael@0: return new type(temp.buffer)[0]; michael@0: } michael@0: } else { michael@0: for (var i = 0; i < size; i++) michael@0: temp[size - i - 1] = view._bytes[offset + i]; michael@0: return new type(temp.buffer)[0]; michael@0: } michael@0: } michael@0: function set(view, type, size, offset, value, le) { michael@0: if (offset === undefined) michael@0: fail('Missing required offset argument'); michael@0: if (value === undefined) michael@0: fail('Missing required value argument'); michael@0: if (offset < 0 || offset + size > view.byteLength) michael@0: fail('Invalid index: ' + offset); michael@0: if (size === 1 || !(!le) === nativele) { michael@0: if ((view.byteOffset + offset) % size === 0) { michael@0: new type(view.buffer, view.byteOffset + offset, 1)[0] = value; michael@0: } else { michael@0: new type(temp.buffer)[0] = value; michael@0: for (var i = 0; i < size; i++) michael@0: view._bytes[i + offset] = temp[i]; michael@0: } michael@0: } else { michael@0: new type(temp.buffer)[0] = value; michael@0: for (var i = 0; i < size; i++) michael@0: view._bytes[offset + i] = temp[size - 1 - i]; michael@0: } michael@0: } michael@0: function fail(msg) { michael@0: throw new Error(msg); michael@0: } michael@0: }(this)); michael@0: var create = Object.create; michael@0: var defineProperty = Object.defineProperty; michael@0: var keys = Object.keys; michael@0: var isArray = Array.isArray; michael@0: var fromCharCode = String.fromCharCode; michael@0: var logE = Math.log; michael@0: var max = Math.max; michael@0: var min = Math.min; michael@0: var pow = Math.pow; michael@0: var push = Array.prototype.push; michael@0: var slice = Array.prototype.slice; michael@0: var splice = Array.prototype.splice; michael@0: function fail(msg, context) { michael@0: throw new Error((context ? context + ': ' : '') + msg); michael@0: } michael@0: function assert(cond, msg, context) { michael@0: if (!cond) michael@0: fail(msg, context); michael@0: } michael@0: function scriptProperties(namespace, props) { michael@0: return props.reduce(function (o, p) { michael@0: o[p] = namespace + ' ' + p; michael@0: return o; michael@0: }, {}); michael@0: } michael@0: function cloneObject(obj) { michael@0: var clone = Object.create(null); michael@0: for (var prop in obj) michael@0: clone[prop] = obj[prop]; michael@0: return clone; michael@0: } michael@0: function sortNumeric(a, b) { michael@0: return a - b; michael@0: } michael@0: function sortByZindex(a, b) { michael@0: return a._zindex - b._zindex; michael@0: } michael@0: function rgbaObjToStr(color) { michael@0: return 'rgba(' + color.red + ',' + color.green + ',' + color.blue + ',' + color.alpha / 255 + ')'; michael@0: } michael@0: function rgbIntAlphaToStr(color, alpha) { michael@0: color |= 0; michael@0: if (alpha >= 1) { michael@0: var colorStr = color.toString(16); michael@0: while (colorStr.length < 6) { michael@0: colorStr = '0' + colorStr; michael@0: } michael@0: return '#' + colorStr; michael@0: } michael@0: var red = color >> 16 & 255; michael@0: var green = color >> 8 & 255; michael@0: var blue = color & 255; michael@0: return 'rgba(' + red + ',' + green + ',' + blue + ',' + alpha + ')'; michael@0: } michael@0: function argbUintToStr(argb) { michael@0: return 'rgba(' + (argb >>> 16 & 255) + ',' + (argb >>> 8 & 255) + ',' + (argb & 255) + ',' + (argb >>> 24 & 255) / 255 + ')'; michael@0: } michael@0: (function functionNameSupport() { michael@0: if (eval('function t() {} t.name === \'t\'')) { michael@0: return; michael@0: } michael@0: Object.defineProperty(Function.prototype, 'name', { michael@0: get: function () { michael@0: if (this.__name) { michael@0: return this.__name; michael@0: } michael@0: var m = /function\s([^\(]+)/.exec(this.toString()); michael@0: var name = m && m[1] !== 'anonymous' ? m[1] : null; michael@0: this.__name = name; michael@0: return name; michael@0: }, michael@0: configurable: true, michael@0: enumerable: false michael@0: }); michael@0: }()); michael@0: var randomStyleCache; michael@0: var nextStyle = 0; michael@0: function randomStyle() { michael@0: if (!randomStyleCache) { michael@0: randomStyleCache = [ michael@0: '#ff5e3a', michael@0: '#ff9500', michael@0: '#ffdb4c', michael@0: '#87fc70', michael@0: '#52edc7', michael@0: '#1ad6fd', michael@0: '#c644fc', michael@0: '#ef4db6', michael@0: '#4a4a4a', michael@0: '#dbddde', michael@0: '#ff3b30', michael@0: '#ff9500', michael@0: '#ffcc00', michael@0: '#4cd964', michael@0: '#34aadc', michael@0: '#007aff', michael@0: '#5856d6', michael@0: '#ff2d55', michael@0: '#8e8e93', michael@0: '#c7c7cc', michael@0: '#5ad427', michael@0: '#c86edf', michael@0: '#d1eefc', michael@0: '#e0f8d8', michael@0: '#fb2b69', michael@0: '#f7f7f7', michael@0: '#1d77ef', michael@0: '#d6cec3', michael@0: '#55efcb', michael@0: '#ff4981', michael@0: '#ffd3e0', michael@0: '#f7f7f7', michael@0: '#ff1300', michael@0: '#1f1f21', michael@0: '#bdbec2', michael@0: '#ff3a2d' michael@0: ]; michael@0: } michael@0: return randomStyleCache[nextStyle++ % randomStyleCache.length]; michael@0: } michael@0: (function PromiseClosure() { michael@0: var global = Function('return this')(); michael@0: if (global.Promise) { michael@0: if (typeof global.Promise.all !== 'function') { michael@0: global.Promise.all = function (iterable) { michael@0: var count = 0, results = [], resolve, reject; michael@0: var promise = new global.Promise(function (resolve_, reject_) { michael@0: resolve = resolve_; michael@0: reject = reject_; michael@0: }); michael@0: iterable.forEach(function (p, i) { michael@0: count++; michael@0: p.then(function (result) { michael@0: results[i] = result; michael@0: count--; michael@0: if (count === 0) { michael@0: resolve(results); michael@0: } michael@0: }, reject); michael@0: }); michael@0: if (count === 0) { michael@0: resolve(results); michael@0: } michael@0: return promise; michael@0: }; michael@0: } michael@0: if (typeof global.Promise.resolve !== 'function') { michael@0: global.Promise.resolve = function (x) { michael@0: return new global.Promise(function (resolve) { michael@0: resolve(x); michael@0: }); michael@0: }; michael@0: } michael@0: return; michael@0: } michael@0: function getDeferred(C) { michael@0: if (typeof C !== 'function') { michael@0: throw new TypeError('Invalid deferred constructor'); michael@0: } michael@0: var resolver = createDeferredConstructionFunctions(); michael@0: var promise = new C(resolver); michael@0: var resolve = resolver.resolve; michael@0: if (typeof resolve !== 'function') { michael@0: throw new TypeError('Invalid resolve construction function'); michael@0: } michael@0: var reject = resolver.reject; michael@0: if (typeof reject !== 'function') { michael@0: throw new TypeError('Invalid reject construction function'); michael@0: } michael@0: return { michael@0: promise: promise, michael@0: resolve: resolve, michael@0: reject: reject michael@0: }; michael@0: } michael@0: function updateDeferredFromPotentialThenable(x, deferred) { michael@0: if (typeof x !== 'object' || x === null) { michael@0: return false; michael@0: } michael@0: try { michael@0: var then = x.then; michael@0: if (typeof then !== 'function') { michael@0: return false; michael@0: } michael@0: var thenCallResult = then.call(x, deferred.resolve, deferred.reject); michael@0: } catch (e) { michael@0: var reject = deferred.reject; michael@0: reject(e); michael@0: } michael@0: return true; michael@0: } michael@0: function isPromise(x) { michael@0: return typeof x === 'object' && x !== null && typeof x.promiseStatus !== 'undefined'; michael@0: } michael@0: function rejectPromise(promise, reason) { michael@0: if (promise.promiseStatus !== 'unresolved') { michael@0: return; michael@0: } michael@0: var reactions = promise.rejectReactions; michael@0: promise.result = reason; michael@0: promise.resolveReactions = undefined; michael@0: promise.rejectReactions = undefined; michael@0: promise.promiseStatus = 'has-rejection'; michael@0: triggerPromiseReactions(reactions, reason); michael@0: } michael@0: function resolvePromise(promise, resolution) { michael@0: if (promise.promiseStatus !== 'unresolved') { michael@0: return; michael@0: } michael@0: var reactions = promise.resolveReactions; michael@0: promise.result = resolution; michael@0: promise.resolveReactions = undefined; michael@0: promise.rejectReactions = undefined; michael@0: promise.promiseStatus = 'has-resolution'; michael@0: triggerPromiseReactions(reactions, resolution); michael@0: } michael@0: function triggerPromiseReactions(reactions, argument) { michael@0: for (var i = 0; i < reactions.length; i++) { michael@0: queueMicrotask({ michael@0: reaction: reactions[i], michael@0: argument: argument michael@0: }); michael@0: } michael@0: } michael@0: function queueMicrotask(task) { michael@0: if (microtasksQueue.length === 0) { michael@0: setTimeout(handleMicrotasksQueue, 0); michael@0: } michael@0: microtasksQueue.push(task); michael@0: } michael@0: function executePromiseReaction(reaction, argument) { michael@0: var deferred = reaction.deferred; michael@0: var handler = reaction.handler; michael@0: var handlerResult, updateResult; michael@0: try { michael@0: handlerResult = handler(argument); michael@0: } catch (e) { michael@0: var reject = deferred.reject; michael@0: return reject(e); michael@0: } michael@0: if (handlerResult === deferred.promise) { michael@0: var reject = deferred.reject; michael@0: return reject(new TypeError('Self resolution')); michael@0: } michael@0: try { michael@0: updateResult = updateDeferredFromPotentialThenable(handlerResult, deferred); michael@0: if (!updateResult) { michael@0: var resolve = deferred.resolve; michael@0: return resolve(handlerResult); michael@0: } michael@0: } catch (e) { michael@0: var reject = deferred.reject; michael@0: return reject(e); michael@0: } michael@0: } michael@0: var microtasksQueue = []; michael@0: function handleMicrotasksQueue() { michael@0: while (microtasksQueue.length > 0) { michael@0: var task = microtasksQueue[0]; michael@0: try { michael@0: executePromiseReaction(task.reaction, task.argument); michael@0: } catch (e) { michael@0: if (typeof Promise.onerror === 'function') { michael@0: Promise.onerror(e); michael@0: } michael@0: } michael@0: microtasksQueue.shift(); michael@0: } michael@0: } michael@0: function throwerFunction(e) { michael@0: throw e; michael@0: } michael@0: function identityFunction(x) { michael@0: return x; michael@0: } michael@0: function createRejectPromiseFunction(promise) { michael@0: return function (reason) { michael@0: rejectPromise(promise, reason); michael@0: }; michael@0: } michael@0: function createResolvePromiseFunction(promise) { michael@0: return function (resolution) { michael@0: resolvePromise(promise, resolution); michael@0: }; michael@0: } michael@0: function createDeferredConstructionFunctions() { michael@0: var fn = function (resolve, reject) { michael@0: fn.resolve = resolve; michael@0: fn.reject = reject; michael@0: }; michael@0: return fn; michael@0: } michael@0: function createPromiseResolutionHandlerFunctions(promise, fulfillmentHandler, rejectionHandler) { michael@0: return function (x) { michael@0: if (x === promise) { michael@0: return rejectionHandler(new TypeError('Self resolution')); michael@0: } michael@0: var cstr = promise.promiseConstructor; michael@0: if (isPromise(x)) { michael@0: var xConstructor = x.promiseConstructor; michael@0: if (xConstructor === cstr) { michael@0: return x.then(fulfillmentHandler, rejectionHandler); michael@0: } michael@0: } michael@0: var deferred = getDeferred(cstr); michael@0: var updateResult = updateDeferredFromPotentialThenable(x, deferred); michael@0: if (updateResult) { michael@0: var deferredPromise = deferred.promise; michael@0: return deferredPromise.then(fulfillmentHandler, rejectionHandler); michael@0: } michael@0: return fulfillmentHandler(x); michael@0: }; michael@0: } michael@0: function createPromiseAllCountdownFunction(index, values, deferred, countdownHolder) { michael@0: return function (x) { michael@0: values[index] = x; michael@0: countdownHolder.countdown--; michael@0: if (countdownHolder.countdown === 0) { michael@0: deferred.resolve(values); michael@0: } michael@0: }; michael@0: } michael@0: function Promise(resolver) { michael@0: if (typeof resolver !== 'function') { michael@0: throw new TypeError('resolver is not a function'); michael@0: } michael@0: var promise = this; michael@0: if (typeof promise !== 'object') { michael@0: throw new TypeError('Promise to initialize is not an object'); michael@0: } michael@0: promise.promiseStatus = 'unresolved'; michael@0: promise.resolveReactions = []; michael@0: promise.rejectReactions = []; michael@0: promise.result = undefined; michael@0: var resolve = createResolvePromiseFunction(promise); michael@0: var reject = createRejectPromiseFunction(promise); michael@0: try { michael@0: var result = resolver(resolve, reject); michael@0: } catch (e) { michael@0: rejectPromise(promise, e); michael@0: } michael@0: promise.promiseConstructor = Promise; michael@0: return promise; michael@0: } michael@0: Promise.all = function (iterable) { michael@0: var deferred = getDeferred(this); michael@0: var values = []; michael@0: var countdownHolder = { michael@0: countdown: 0 michael@0: }; michael@0: var index = 0; michael@0: iterable.forEach(function (nextValue) { michael@0: var nextPromise = this.cast(nextValue); michael@0: var fn = createPromiseAllCountdownFunction(index, values, deferred, countdownHolder); michael@0: nextPromise.then(fn, deferred.reject); michael@0: index++; michael@0: countdownHolder.countdown++; michael@0: }, this); michael@0: if (index === 0) { michael@0: deferred.resolve(values); michael@0: } michael@0: return deferred.promise; michael@0: }; michael@0: Promise.cast = function (x) { michael@0: if (isPromise(x)) { michael@0: return x; michael@0: } michael@0: var deferred = getDeferred(this); michael@0: deferred.resolve(x); michael@0: return deferred.promise; michael@0: }; michael@0: Promise.reject = function (r) { michael@0: var deferred = getDeferred(this); michael@0: var rejectResult = deferred.reject(r); michael@0: return deferred.promise; michael@0: }; michael@0: Promise.resolve = function (x) { michael@0: var deferred = getDeferred(this); michael@0: var rejectResult = deferred.resolve(x); michael@0: return deferred.promise; michael@0: }; michael@0: Promise.prototype = { michael@0: 'catch': function (onRejected) { michael@0: this.then(undefined, onRejected); michael@0: }, michael@0: then: function (onFulfilled, onRejected) { michael@0: var promise = this; michael@0: if (!isPromise(promise)) { michael@0: throw new TypeError('this is not a Promises'); michael@0: } michael@0: var cstr = promise.promiseConstructor; michael@0: var deferred = getDeferred(cstr); michael@0: var rejectionHandler = typeof onRejected === 'function' ? onRejected : throwerFunction; michael@0: var fulfillmentHandler = typeof onFulfilled === 'function' ? onFulfilled : identityFunction; michael@0: var resolutionHandler = createPromiseResolutionHandlerFunctions(promise, fulfillmentHandler, rejectionHandler); michael@0: var resolveReaction = { michael@0: deferred: deferred, michael@0: handler: resolutionHandler michael@0: }; michael@0: var rejectReaction = { michael@0: deferred: deferred, michael@0: handler: rejectionHandler michael@0: }; michael@0: switch (promise.promiseStatus) { michael@0: case 'unresolved': michael@0: promise.resolveReactions.push(resolveReaction); michael@0: promise.rejectReactions.push(rejectReaction); michael@0: break; michael@0: case 'has-resolution': michael@0: var resolution = promise.result; michael@0: queueMicrotask({ michael@0: reaction: resolveReaction, michael@0: argument: resolution michael@0: }); michael@0: break; michael@0: case 'has-rejection': michael@0: var rejection = promise.result; michael@0: queueMicrotask({ michael@0: reaction: rejectReaction, michael@0: argument: rejection michael@0: }); michael@0: break; michael@0: } michael@0: return deferred.promise; michael@0: } michael@0: }; michael@0: global.Promise = Promise; michael@0: }()); michael@0: var QuadTree = function (x, y, width, height, parent) { michael@0: this.x = x | 0; michael@0: this.y = y | 0; michael@0: this.width = width | 0; michael@0: this.height = height | 0; michael@0: if (parent) { michael@0: this.root = parent.root; michael@0: this.parent = parent; michael@0: this.level = parent.level + 1; michael@0: } else { michael@0: this.root = this; michael@0: this.parent = null; michael@0: this.level = 0; michael@0: } michael@0: this.reset(); michael@0: }; michael@0: QuadTree.prototype.reset = function () { michael@0: this.stuckObjects = null; michael@0: this.objects = null; michael@0: this.nodes = []; michael@0: }; michael@0: QuadTree.prototype._findIndex = function (xMin, xMax, yMin, yMax) { michael@0: var midX = this.x + (this.width / 2 | 0); michael@0: var midY = this.y + (this.height / 2 | 0); michael@0: var top = yMin < midY && yMax < midY; michael@0: var bottom = yMin > midY; michael@0: if (xMin < midX && xMax < midX) { michael@0: if (top) { michael@0: return 1; michael@0: } else if (bottom) { michael@0: return 2; michael@0: } michael@0: } else if (xMin > midX) { michael@0: if (top) { michael@0: return 0; michael@0: } else if (bottom) { michael@0: return 3; michael@0: } michael@0: } michael@0: return -1; michael@0: }; michael@0: QuadTree.prototype.insert = function (obj) { michael@0: var nodes = this.nodes; michael@0: if (nodes.length) { michael@0: var index = this._findIndex(obj.xMin, obj.xMax, obj.yMin, obj.yMax); michael@0: if (index > -1) { michael@0: nodes[index].insert(obj); michael@0: } else { michael@0: obj.prev = null; michael@0: if (this.stuckObjects) { michael@0: obj.next = this.stuckObjects; michael@0: this.stuckObjects.prev = obj; michael@0: } else { michael@0: obj.next = null; michael@0: } michael@0: this.stuckObjects = obj; michael@0: obj.parent = this; michael@0: } michael@0: return; michael@0: } michael@0: var numChildren = 1; michael@0: var item = this.objects; michael@0: if (!item) { michael@0: obj.prev = null; michael@0: obj.next = null; michael@0: this.objects = obj; michael@0: } else { michael@0: while (item.next) { michael@0: numChildren++; michael@0: item = item.next; michael@0: } michael@0: obj.prev = item; michael@0: obj.next = null; michael@0: item.next = obj; michael@0: } michael@0: if (numChildren > 4 && this.level < 10) { michael@0: this._subdivide(); michael@0: item = this.objects; michael@0: while (item) { michael@0: var next = item.next; michael@0: this.insert(item); michael@0: item = next; michael@0: } michael@0: this.objects = null; michael@0: return; michael@0: } michael@0: obj.parent = this; michael@0: }; michael@0: QuadTree.prototype.update = function (obj) { michael@0: var node = obj.parent; michael@0: if (node) { michael@0: if (obj.xMin >= node.x && obj.xMax <= node.x + node.width && obj.yMin >= node.y && obj.yMax <= node.y + node.height) { michael@0: if (node.nodes.length) { michael@0: var index = this._findIndex(obj.xMin, obj.xMax, obj.yMin, obj.yMax); michael@0: if (index > -1) { michael@0: node.remove(obj); michael@0: node = this.nodes[index]; michael@0: node.insert(obj); michael@0: } michael@0: } else { michael@0: node.remove(obj); michael@0: node.insert(obj); michael@0: } michael@0: return; michael@0: } michael@0: node.remove(obj); michael@0: } michael@0: this.root.insert(obj); michael@0: }; michael@0: QuadTree.prototype.remove = function (obj) { michael@0: var prev = obj.prev; michael@0: var next = obj.next; michael@0: if (prev) { michael@0: prev.next = next; michael@0: obj.prev = null; michael@0: } else { michael@0: var node = obj.parent; michael@0: if (node.objects === obj) { michael@0: node.objects = next; michael@0: } else if (node.stuckObjects === obj) { michael@0: node.stuckObjects = next; michael@0: } michael@0: } michael@0: if (next) { michael@0: next.prev = prev; michael@0: obj.next = null; michael@0: } michael@0: obj.parent = null; michael@0: }; michael@0: QuadTree.prototype.retrieve = function (xMin, xMax, yMin, yMax) { michael@0: var stack = []; michael@0: var out = []; michael@0: var node = this; michael@0: do { michael@0: if (node.nodes.length) { michael@0: var index = node._findIndex(xMin, xMax, yMin, yMax); michael@0: if (index > -1) { michael@0: stack.push(node.nodes[index]); michael@0: } else { michael@0: stack.push.apply(stack, node.nodes); michael@0: } michael@0: } michael@0: var item = node.objects; michael@0: for (var i = 0; i < 2; i++) { michael@0: while (item) { michael@0: if (!(item.xMin > xMax || item.xMax < xMin || item.yMin > yMax || item.yMax < yMin)) { michael@0: out.push(item); michael@0: } michael@0: item = item.next; michael@0: } michael@0: item = node.stuckObjects; michael@0: } michael@0: node = stack.pop(); michael@0: } while (node); michael@0: return out; michael@0: }; michael@0: QuadTree.prototype._subdivide = function () { michael@0: var halfWidth = this.width / 2 | 0; michael@0: var halfHeight = this.height / 2 | 0; michael@0: var midX = this.x + halfWidth; michael@0: var midY = this.y + halfHeight; michael@0: this.nodes[0] = new QuadTree(midX, this.y, halfWidth, halfHeight, this); michael@0: this.nodes[1] = new QuadTree(this.x, this.y, halfWidth, halfHeight, this); michael@0: this.nodes[2] = new QuadTree(this.x, midY, halfWidth, halfHeight, this); michael@0: this.nodes[3] = new QuadTree(midX, midY, halfWidth, halfHeight, this); michael@0: }; michael@0: var RegionCluster = function () { michael@0: this.regions = []; michael@0: }; michael@0: RegionCluster.prototype.reset = function () { michael@0: this.regions.length = 0; michael@0: }; michael@0: RegionCluster.prototype.insert = function (region) { michael@0: var regions = this.regions; michael@0: if (regions.length < 3) { michael@0: regions.push({ michael@0: xMin: region.xMin, michael@0: xMax: region.xMax, michael@0: yMin: region.yMin, michael@0: yMax: region.yMax michael@0: }); michael@0: return; michael@0: } michael@0: var a = region; michael@0: var b = regions[0]; michael@0: var c = regions[1]; michael@0: var d = regions[2]; michael@0: var ab = (max(a.xMax, b.xMax) - min(a.xMin, b.xMin)) * (max(a.yMax, b.yMax) - min(a.yMin, b.yMin)); michael@0: var rb = regions[0]; michael@0: var ac = (max(a.xMax, c.xMax) - min(a.xMin, c.xMin)) * (max(a.yMax, c.yMax) - min(a.yMin, c.yMin)); michael@0: var ad = (max(a.xMax, d.xMax) - min(a.xMin, d.xMin)) * (max(a.yMax, d.yMax) - min(a.yMin, d.yMin)); michael@0: if (ac < ab) { michael@0: ab = ac; michael@0: rb = c; michael@0: } michael@0: if (ad < ab) { michael@0: ab = ad; michael@0: rb = d; michael@0: } michael@0: var bc = (max(b.xMax, c.xMax) - min(b.xMin, c.xMin)) * (max(b.yMax, c.yMax) - min(b.yMin, c.yMin)); michael@0: var bd = (max(b.xMax, d.xMax) - min(b.xMin, d.xMin)) * (max(b.yMax, d.yMax) - min(b.yMin, d.yMin)); michael@0: var cd = (max(c.xMax, d.xMax) - min(c.xMin, d.xMin)) * (max(c.yMax, d.yMax) - min(c.yMin, d.yMin)); michael@0: if (ab < bc && ab < bd && ab < cd) { michael@0: if (a.xMin < rb.xMin) { michael@0: rb.xMin = a.xMin; michael@0: } michael@0: if (a.xMax > rb.xMax) { michael@0: rb.xMax = a.xMax; michael@0: } michael@0: if (a.yMin < rb.yMin) { michael@0: rb.yMin = a.yMin; michael@0: } michael@0: if (a.yMax > rb.yMax) { michael@0: rb.yMax = a.yMax; michael@0: } michael@0: return; michael@0: } michael@0: rb = regions[0]; michael@0: var rc = regions[1]; michael@0: if (bd < bc) { michael@0: bc = bd; michael@0: rc = regions[2]; michael@0: } michael@0: if (cd < bc) { michael@0: rb = regions[1]; michael@0: rc = regions[2]; michael@0: } michael@0: if (rc.xMin < rb.xMin) { michael@0: rb.xMin = rc.xMin; michael@0: } michael@0: if (rc.xMax > rb.xMax) { michael@0: rb.xMax = rc.xMax; michael@0: } michael@0: if (rc.yMin < rb.yMin) { michael@0: rb.yMin = rc.yMin; michael@0: } michael@0: if (rc.yMax > rb.yMax) { michael@0: rb.yMax = rc.yMax; michael@0: } michael@0: rc.xMin = a.xMin; michael@0: rc.xMax = a.xMax; michael@0: rc.yMin = a.yMin; michael@0: rc.yMax = a.yMax; michael@0: }; michael@0: RegionCluster.prototype.retrieve = function () { michael@0: return this.regions; michael@0: }; michael@0: var EXTERNAL_INTERFACE_FEATURE = 1; michael@0: var CLIPBOARD_FEATURE = 2; michael@0: var SHAREDOBJECT_FEATURE = 3; michael@0: var VIDEO_FEATURE = 4; michael@0: var SOUND_FEATURE = 5; michael@0: var NETCONNECTION_FEATURE = 6; michael@0: if (!this.performance) { michael@0: this.performance = {}; michael@0: } michael@0: if (!this.performance.now) { michael@0: this.performance.now = Date.now; michael@0: } michael@0: var SWF_TAG_CODE_CSM_TEXT_SETTINGS = 74; michael@0: var SWF_TAG_CODE_DEFINE_BINARY_DATA = 87; michael@0: var SWF_TAG_CODE_DEFINE_BITS = 6; michael@0: var SWF_TAG_CODE_DEFINE_BITS_JPEG2 = 21; michael@0: var SWF_TAG_CODE_DEFINE_BITS_JPEG3 = 35; michael@0: var SWF_TAG_CODE_DEFINE_BITS_JPEG4 = 90; michael@0: var SWF_TAG_CODE_DEFINE_BITS_LOSSLESS = 20; michael@0: var SWF_TAG_CODE_DEFINE_BITS_LOSSLESS2 = 36; michael@0: var SWF_TAG_CODE_DEFINE_BUTTON = 7; michael@0: var SWF_TAG_CODE_DEFINE_BUTTON2 = 34; michael@0: var SWF_TAG_CODE_DEFINE_BUTTON_CXFORM = 23; michael@0: var SWF_TAG_CODE_DEFINE_BUTTON_SOUND = 17; michael@0: var SWF_TAG_CODE_DEFINE_EDIT_TEXT = 37; michael@0: var SWF_TAG_CODE_DEFINE_FONT = 10; michael@0: var SWF_TAG_CODE_DEFINE_FONT2 = 48; michael@0: var SWF_TAG_CODE_DEFINE_FONT3 = 75; michael@0: var SWF_TAG_CODE_DEFINE_FONT4 = 91; michael@0: var SWF_TAG_CODE_DEFINE_FONT_ALIGN_ZONES = 73; michael@0: var SWF_TAG_CODE_DEFINE_FONT_INFO = 13; michael@0: var SWF_TAG_CODE_DEFINE_FONT_INFO2 = 62; michael@0: var SWF_TAG_CODE_DEFINE_FONT_NAME = 88; michael@0: var SWF_TAG_CODE_DEFINE_MORPH_SHAPE = 46; michael@0: var SWF_TAG_CODE_DEFINE_MORPH_SHAPE2 = 84; michael@0: var SWF_TAG_CODE_DEFINE_SCALING_GRID = 78; michael@0: var SWF_TAG_CODE_DEFINE_SCENE_AND_FRAME_LABEL_DATA = 86; michael@0: var SWF_TAG_CODE_DEFINE_SHAPE = 2; michael@0: var SWF_TAG_CODE_DEFINE_SHAPE2 = 22; michael@0: var SWF_TAG_CODE_DEFINE_SHAPE3 = 32; michael@0: var SWF_TAG_CODE_DEFINE_SHAPE4 = 83; michael@0: var SWF_TAG_CODE_DEFINE_SOUND = 14; michael@0: var SWF_TAG_CODE_DEFINE_SPRITE = 39; michael@0: var SWF_TAG_CODE_DEFINE_TEXT = 11; michael@0: var SWF_TAG_CODE_DEFINE_TEXT2 = 33; michael@0: var SWF_TAG_CODE_DEFINE_VIDEO_STREAM = 60; michael@0: var SWF_TAG_CODE_DO_ABC = 82; michael@0: var SWF_TAG_CODE_DO_ABC_ = 72; michael@0: var SWF_TAG_CODE_DO_ACTION = 12; michael@0: var SWF_TAG_CODE_DO_INIT_ACTION = 59; michael@0: var SWF_TAG_CODE_ENABLE_DEBUGGER = 58; michael@0: var SWF_TAG_CODE_ENABLE_DEBUGGER2 = 64; michael@0: var SWF_TAG_CODE_END = 0; michael@0: var SWF_TAG_CODE_EXPORT_ASSETS = 56; michael@0: var SWF_TAG_CODE_FILE_ATTRIBUTES = 69; michael@0: var SWF_TAG_CODE_FRAME_LABEL = 43; michael@0: var SWF_TAG_CODE_IMPORT_ASSETS = 57; michael@0: var SWF_TAG_CODE_IMPORT_ASSETS2 = 71; michael@0: var SWF_TAG_CODE_JPEG_TABLES = 8; michael@0: var SWF_TAG_CODE_METADATA = 77; michael@0: var SWF_TAG_CODE_PLACE_OBJECT = 4; michael@0: var SWF_TAG_CODE_PLACE_OBJECT2 = 26; michael@0: var SWF_TAG_CODE_PLACE_OBJECT3 = 70; michael@0: var SWF_TAG_CODE_PROTECT = 24; michael@0: var SWF_TAG_CODE_REMOVE_OBJECT = 5; michael@0: var SWF_TAG_CODE_REMOVE_OBJECT2 = 28; michael@0: var SWF_TAG_CODE_SCRIPT_LIMITS = 65; michael@0: var SWF_TAG_CODE_SET_BACKGROUND_COLOR = 9; michael@0: var SWF_TAG_CODE_SET_TAB_INDEX = 66; michael@0: var SWF_TAG_CODE_SHOW_FRAME = 1; michael@0: var SWF_TAG_CODE_SOUND_STREAM_BLOCK = 19; michael@0: var SWF_TAG_CODE_SOUND_STREAM_HEAD = 18; michael@0: var SWF_TAG_CODE_SOUND_STREAM_HEAD2 = 45; michael@0: var SWF_TAG_CODE_START_SOUND = 15; michael@0: var SWF_TAG_CODE_START_SOUND2 = 89; michael@0: var SWF_TAG_CODE_SYMBOL_CLASS = 76; michael@0: var SWF_TAG_CODE_VIDEO_FRAME = 61; michael@0: self.SWF = {}; michael@0: var FORMAT_COLORMAPPED = 3; michael@0: var FORMAT_15BPP = 4; michael@0: var FORMAT_24BPP = 5; michael@0: var FACTOR_5BBP = 255 / 31; michael@0: var crcTable = []; michael@0: for (var i = 0; i < 256; i++) { michael@0: var c = i; michael@0: for (var h = 0; h < 8; h++) { michael@0: if (c & 1) michael@0: c = 3988292384 ^ c >> 1 & 2147483647; michael@0: else michael@0: c = c >> 1 & 2147483647; michael@0: } michael@0: crcTable[i] = c; michael@0: } michael@0: function crc32(data, start, end) { michael@0: var crc = -1; michael@0: for (var i = start; i < end; i++) { michael@0: var a = (crc ^ data[i]) & 255; michael@0: var b = crcTable[a]; michael@0: crc = crc >>> 8 ^ b; michael@0: } michael@0: return crc ^ -1; michael@0: } michael@0: function createPngChunk(type, data) { michael@0: var chunk = new Uint8Array(12 + data.length); michael@0: var p = 0; michael@0: var len = data.length; michael@0: chunk[p] = len >> 24 & 255; michael@0: chunk[p + 1] = len >> 16 & 255; michael@0: chunk[p + 2] = len >> 8 & 255; michael@0: chunk[p + 3] = len & 255; michael@0: chunk[p + 4] = type.charCodeAt(0) & 255; michael@0: chunk[p + 5] = type.charCodeAt(1) & 255; michael@0: chunk[p + 6] = type.charCodeAt(2) & 255; michael@0: chunk[p + 7] = type.charCodeAt(3) & 255; michael@0: if (data instanceof Uint8Array) michael@0: chunk.set(data, 8); michael@0: p = 8 + len; michael@0: var crc = crc32(chunk, 4, p); michael@0: chunk[p] = crc >> 24 & 255; michael@0: chunk[p + 1] = crc >> 16 & 255; michael@0: chunk[p + 2] = crc >> 8 & 255; michael@0: chunk[p + 3] = crc & 255; michael@0: return chunk; michael@0: } michael@0: function adler32(data, start, end) { michael@0: var a = 1; michael@0: var b = 0; michael@0: for (var i = start; i < end; ++i) { michael@0: a = (a + (data[i] & 255)) % 65521; michael@0: b = (b + a) % 65521; michael@0: } michael@0: return b << 16 | a; michael@0: } michael@0: function defineBitmap(tag) { michael@0: var width = tag.width; michael@0: var height = tag.height; michael@0: var hasAlpha = tag.hasAlpha; michael@0: var plte = ''; michael@0: var trns = ''; michael@0: var literals; michael@0: var bmpData = tag.bmpData; michael@0: switch (tag.format) { michael@0: case FORMAT_COLORMAPPED: michael@0: var colorType = 3; michael@0: var bytesPerLine = width + 3 & ~3; michael@0: var colorTableSize = tag.colorTableSize + 1; michael@0: var paletteSize = colorTableSize * (tag.hasAlpha ? 4 : 3); michael@0: var datalen = paletteSize + bytesPerLine * height; michael@0: var stream = createInflatedStream(bmpData, datalen); michael@0: var bytes = stream.bytes; michael@0: var pos = 0; michael@0: stream.ensure(paletteSize); michael@0: if (hasAlpha) { michael@0: var palette = new Uint8Array(paletteSize / 4 * 3); michael@0: var pp = 0; michael@0: var alphaValues = new Uint8Array(paletteSize / 4); michael@0: var pa = 0; michael@0: while (pos < paletteSize) { michael@0: palette[pp++] = bytes[pos]; michael@0: palette[pp++] = bytes[pos + 1]; michael@0: palette[pp++] = bytes[pos + 2]; michael@0: alphaValues[pa++] = bytes[pos + 3]; michael@0: pos += 4; michael@0: } michael@0: plte = createPngChunk('PLTE', palette); michael@0: trns = createPngChunk('tRNS', alphaValues); michael@0: } else { michael@0: plte = createPngChunk('PLTE', bytes.subarray(pos, pos + paletteSize)); michael@0: pos += paletteSize; michael@0: } michael@0: literals = new Uint8Array(width * height + height); michael@0: var pl = 0; michael@0: while (pos < datalen) { michael@0: stream.ensure(bytesPerLine); michael@0: var begin = pos; michael@0: var end = begin + width; michael@0: pl++; michael@0: literals.set(bytes.subarray(begin, end), pl); michael@0: pl += end - begin; michael@0: stream.pos = pos += bytesPerLine; michael@0: } michael@0: break; michael@0: case FORMAT_15BPP: michael@0: var colorType = 2; michael@0: var bytesPerLine = width * 2 + 3 & ~3; michael@0: var stream = createInflatedStream(bmpData, bytesPerLine * height); michael@0: var pos = 0; michael@0: literals = new Uint8Array(width * height * 3 + height); michael@0: var pl = 0; michael@0: for (var y = 0; y < height; ++y) { michael@0: pl++; michael@0: stream.ensure(bytesPerLine); michael@0: for (var x = 0; x < width; ++x) { michael@0: var word = stream.getUint16(pos); michael@0: pos += 2; michael@0: literals[pl++] = 0 | FACTOR_5BBP * (word >> 10 & 31); michael@0: literals[pl++] = 0 | FACTOR_5BBP * (word >> 5 & 31); michael@0: literals[pl++] = 0 | FACTOR_5BBP * (word & 31); michael@0: } michael@0: stream.pos = pos += bytesPerLine; michael@0: } michael@0: break; michael@0: case FORMAT_24BPP: michael@0: var padding; michael@0: if (hasAlpha) { michael@0: var colorType = 6; michael@0: padding = 0; michael@0: literals = new Uint8Array(width * height * 4 + height); michael@0: } else { michael@0: var colorType = 2; michael@0: padding = 1; michael@0: literals = new Uint8Array(width * height * 3 + height); michael@0: } michael@0: var bytesPerLine = width * 4; michael@0: var stream = createInflatedStream(bmpData, bytesPerLine * height); michael@0: var bytes = stream.bytes; michael@0: var pos = 0; michael@0: var pl = 0; michael@0: for (var y = 0; y < height; ++y) { michael@0: stream.ensure(bytesPerLine); michael@0: pl++; michael@0: for (var x = 0; x < width; ++x) { michael@0: pos += padding; michael@0: if (hasAlpha) { michael@0: var alpha = bytes[pos]; michael@0: if (alpha) { michael@0: var opacity = alpha / 255; michael@0: literals[pl++] = 0 | bytes[pos + 1] / opacity; michael@0: literals[pl++] = 0 | bytes[pos + 2] / opacity; michael@0: literals[pl++] = 0 | bytes[pos + 3] / opacity; michael@0: literals[pl++] = alpha; michael@0: } else { michael@0: pl += 4; michael@0: } michael@0: } else { michael@0: literals[pl++] = bytes[pos]; michael@0: literals[pl++] = bytes[pos + 1]; michael@0: literals[pl++] = bytes[pos + 2]; michael@0: } michael@0: pos += 4 - padding; michael@0: } michael@0: stream.pos = pos; michael@0: } michael@0: break; michael@0: default: michael@0: fail('invalid format', 'bitmap'); michael@0: } michael@0: var ihdr = new Uint8Array([ michael@0: width >> 24 & 255, michael@0: width >> 16 & 255, michael@0: width >> 8 & 255, michael@0: width & 255, michael@0: height >> 24 & 255, michael@0: height >> 16 & 255, michael@0: height >> 8 & 255, michael@0: height & 255, michael@0: 8, michael@0: colorType, michael@0: 0, michael@0: 0, michael@0: 0 michael@0: ]); michael@0: var len = literals.length; michael@0: var maxBlockLength = 65535; michael@0: var idat = new Uint8Array(2 + len + Math.ceil(len / maxBlockLength) * 5 + 4); michael@0: var pi = 0; michael@0: idat[pi++] = 120; michael@0: idat[pi++] = 156; michael@0: var pos = 0; michael@0: while (len > maxBlockLength) { michael@0: idat[pi++] = 0; michael@0: idat[pi++] = 255; michael@0: idat[pi++] = 255; michael@0: idat[pi++] = 0; michael@0: idat[pi++] = 0; michael@0: idat.set(literals.subarray(pos, pos + maxBlockLength), pi); michael@0: pi += maxBlockLength; michael@0: pos += maxBlockLength; michael@0: len -= maxBlockLength; michael@0: } michael@0: idat[pi++] = 1; michael@0: idat[pi++] = len & 255; michael@0: idat[pi++] = len >> 8 & 255; michael@0: idat[pi++] = ~len & 65535 & 255; michael@0: idat[pi++] = (~len & 65535) >> 8 & 255; michael@0: idat.set(literals.subarray(pos), pi); michael@0: pi += literals.length - pos; michael@0: var adler = adler32(literals, 0, literals.length); michael@0: idat[pi++] = adler >> 24 & 255; michael@0: idat[pi++] = adler >> 16 & 255; michael@0: idat[pi++] = adler >> 8 & 255; michael@0: idat[pi++] = adler & 255; michael@0: var chunks = [ michael@0: new Uint8Array([ michael@0: 137, michael@0: 80, michael@0: 78, michael@0: 71, michael@0: 13, michael@0: 10, michael@0: 26, michael@0: 10 michael@0: ]), michael@0: createPngChunk('IHDR', ihdr), michael@0: plte, michael@0: trns, michael@0: createPngChunk('IDAT', idat), michael@0: createPngChunk('IEND', '') michael@0: ]; michael@0: return { michael@0: type: 'image', michael@0: id: tag.id, michael@0: width: width, michael@0: height: height, michael@0: mimeType: 'image/png', michael@0: data: new Blob(chunks, { michael@0: type: 'image/png' michael@0: }) michael@0: }; michael@0: } michael@0: function defineButton(tag, dictionary) { michael@0: var characters = tag.characters; michael@0: var states = { michael@0: up: {}, michael@0: over: {}, michael@0: down: {}, michael@0: hitTest: {} michael@0: }; michael@0: var i = 0, character; michael@0: while (character = characters[i++]) { michael@0: if (character.eob) michael@0: break; michael@0: var characterItem = dictionary[character.symbolId]; michael@0: var entry = { michael@0: symbolId: characterItem.id, michael@0: hasMatrix: !(!character.matrix), michael@0: matrix: character.matrix michael@0: }; michael@0: if (character.stateUp) michael@0: states.up[character.depth] = entry; michael@0: if (character.stateOver) michael@0: states.over[character.depth] = entry; michael@0: if (character.stateDown) michael@0: states.down[character.depth] = entry; michael@0: if (character.stateHitTest) michael@0: states.hitTest[character.depth] = entry; michael@0: } michael@0: var button = { michael@0: type: 'button', michael@0: id: tag.id, michael@0: buttonActions: tag.buttonActions, michael@0: states: states michael@0: }; michael@0: return button; michael@0: } michael@0: var nextFontId = 1; michael@0: function maxPower2(num) { michael@0: var maxPower = 0; michael@0: var val = num; michael@0: while (val >= 2) { michael@0: val /= 2; michael@0: ++maxPower; michael@0: } michael@0: return pow(2, maxPower); michael@0: } michael@0: function toString16(val) { michael@0: return fromCharCode(val >> 8 & 255, val & 255); michael@0: } michael@0: function toString32(val) { michael@0: return toString16(val >> 16) + toString16(val); michael@0: } michael@0: function defineFont(tag, dictionary) { michael@0: var tables = {}; michael@0: var codes = []; michael@0: var glyphIndex = {}; michael@0: var ranges = []; michael@0: var glyphs = tag.glyphs; michael@0: var glyphCount = glyphs.length; michael@0: if (tag.codes) { michael@0: codes = codes.concat(tag.codes); michael@0: for (var i = 0, code; code = codes[i]; ++i) michael@0: glyphIndex[code] = i; michael@0: codes.sort(function (a, b) { michael@0: return a - b; michael@0: }); michael@0: var i = 0; michael@0: var code; michael@0: while (code = codes[i++]) { michael@0: var start = code; michael@0: var end = start; michael@0: var indices = [ michael@0: i - 1 michael@0: ]; michael@0: while ((code = codes[i]) && end + 1 === code) { michael@0: ++end; michael@0: indices.push(i); michael@0: ++i; michael@0: } michael@0: ranges.push([ michael@0: start, michael@0: end, michael@0: indices michael@0: ]); michael@0: } michael@0: } else { michael@0: var indices = []; michael@0: var UAC_OFFSET = 57344; michael@0: for (var i = 0; i < glyphCount; i++) { michael@0: var code = UAC_OFFSET + i; michael@0: codes.push(code); michael@0: glyphIndex[code] = i; michael@0: indices.push(i); michael@0: } michael@0: ranges.push([ michael@0: UAC_OFFSET, michael@0: UAC_OFFSET + glyphCount - 1, michael@0: indices michael@0: ]); michael@0: } michael@0: var resolution = tag.resolution || 1; michael@0: var ascent = Math.ceil(tag.ascent / resolution) || 1024; michael@0: var descent = -Math.ceil(tag.descent / resolution) | 0; michael@0: var leading = tag.leading / resolution | 0; michael@0: tables['OS/2'] = '\0\x01\0\0' + toString16(tag.bold ? 700 : 400) + '\0\x05' + '\0\0' + '\0\0' + '\0\0' + '\0\0' + '\0\0' + '\0\0' + '\0\0' + '\0\0' + '\0\0' + '\0\0' + '\0\0' + '\0\0' + '\0\0\0\0\0\0\0\0\0\0' + '\0\0\0\0' + '\0\0\0\0' + '\0\0\0\0' + '\0\0\0\0' + 'ALF ' + toString16((tag.italic ? 1 : 0) | (tag.bold ? 32 : 0)) + toString16(codes[0]) + toString16(codes[codes.length - 1]) + toString16(ascent) + toString16(descent) + toString16(leading) + toString16(ascent) + toString16(-descent) + '\0\0\0\0' + '\0\0\0\0'; michael@0: ; michael@0: var startCount = ''; michael@0: var endCount = ''; michael@0: var idDelta = ''; michael@0: var idRangeOffset = ''; michael@0: var i = 0; michael@0: var range; michael@0: while (range = ranges[i++]) { michael@0: var start = range[0]; michael@0: var end = range[1]; michael@0: var code = range[2][0]; michael@0: startCount += toString16(start); michael@0: endCount += toString16(end); michael@0: idDelta += toString16(code - start + 1 & 65535); michael@0: idRangeOffset += toString16(0); michael@0: } michael@0: endCount += '\xff\xff'; michael@0: startCount += '\xff\xff'; michael@0: idDelta += '\0\x01'; michael@0: idRangeOffset += '\0\0'; michael@0: var segCount = ranges.length + 1; michael@0: var searchRange = maxPower2(segCount) * 2; michael@0: var rangeShift = 2 * segCount - searchRange; michael@0: var format314 = '\0\0' + toString16(segCount * 2) + toString16(searchRange) + toString16(logE(segCount) / logE(2)) + toString16(rangeShift) + endCount + '\0\0' + startCount + idDelta + idRangeOffset; michael@0: ; michael@0: tables['cmap'] = '\0\0\0\x01\0\x03\0\x01\0\0\0\f\0\x04' + toString16(format314.length + 4) + format314; michael@0: ; michael@0: var glyf = '\0\x01\0\0\0\0\0\0\0\0\0\0\0\x001\0'; michael@0: var loca = '\0\0'; michael@0: var offset = 16; michael@0: var maxPoints = 0; michael@0: var xMins = []; michael@0: var xMaxs = []; michael@0: var yMins = []; michael@0: var yMaxs = []; michael@0: var maxContours = 0; michael@0: var i = 0; michael@0: var code; michael@0: while (code = codes[i++]) { michael@0: var glyph = glyphs[glyphIndex[code]]; michael@0: var records = glyph.records; michael@0: var numberOfContours = 1; michael@0: var endPoint = 0; michael@0: var endPtsOfContours = ''; michael@0: var flags = ''; michael@0: var xCoordinates = ''; michael@0: var yCoordinates = ''; michael@0: var x = 0; michael@0: var y = 0; michael@0: var xMin = 1024; michael@0: var xMax = -1024; michael@0: var yMin = 1024; michael@0: var yMax = -1024; michael@0: for (var j = 0, record; record = records[j]; ++j) { michael@0: if (record.type) { michael@0: if (record.isStraight) { michael@0: if (record.isGeneral) { michael@0: flags += '\x01'; michael@0: var dx = record.deltaX / resolution; michael@0: var dy = -record.deltaY / resolution; michael@0: xCoordinates += toString16(dx); michael@0: yCoordinates += toString16(dy); michael@0: x += dx; michael@0: y += dy; michael@0: } else if (record.isVertical) { michael@0: flags += '\x11'; michael@0: var dy = -record.deltaY / resolution; michael@0: yCoordinates += toString16(dy); michael@0: y += dy; michael@0: } else { michael@0: flags += '!'; michael@0: var dx = record.deltaX / resolution; michael@0: xCoordinates += toString16(dx); michael@0: x += dx; michael@0: } michael@0: } else { michael@0: flags += '\0'; michael@0: var cx = record.controlDeltaX / resolution; michael@0: var cy = -record.controlDeltaY / resolution; michael@0: xCoordinates += toString16(cx); michael@0: yCoordinates += toString16(cy); michael@0: flags += '\x01'; michael@0: var dx = record.anchorDeltaX / resolution; michael@0: var dy = -record.anchorDeltaY / resolution; michael@0: xCoordinates += toString16(dx); michael@0: yCoordinates += toString16(dy); michael@0: ++endPoint; michael@0: x += cx + dx; michael@0: y += cy + dy; michael@0: } michael@0: if (x < xMin) michael@0: xMin = x; michael@0: if (x > xMax) michael@0: xMax = x; michael@0: if (y < yMin) michael@0: yMin = y; michael@0: if (y > yMax) michael@0: yMax = y; michael@0: ++endPoint; michael@0: } else { michael@0: if (record.eos) michael@0: break; michael@0: if (record.move) { michael@0: if (endPoint) { michael@0: ++numberOfContours; michael@0: endPtsOfContours += toString16(endPoint - 1); michael@0: } michael@0: flags += '\x01'; michael@0: var moveX = record.moveX / resolution; michael@0: var moveY = -record.moveY / resolution; michael@0: var dx = moveX - x; michael@0: var dy = moveY - y; michael@0: xCoordinates += toString16(dx); michael@0: yCoordinates += toString16(dy); michael@0: x = moveX; michael@0: y = moveY; michael@0: if (endPoint > maxPoints) michael@0: maxPoints = endPoint; michael@0: if (x < xMin) michael@0: xMin = x; michael@0: if (x > xMax) michael@0: xMax = x; michael@0: if (y < yMin) michael@0: yMin = y; michael@0: if (y > yMax) michael@0: yMax = y; michael@0: ++endPoint; michael@0: } michael@0: } michael@0: } michael@0: endPtsOfContours += toString16((endPoint || 1) - 1); michael@0: if (!j) { michael@0: xMin = xMax = yMin = yMax = 0; michael@0: flags += '1'; michael@0: } michael@0: var entry = toString16(numberOfContours) + toString16(xMin) + toString16(yMin) + toString16(xMax) + toString16(yMax) + endPtsOfContours + '\0\0' + flags + xCoordinates + yCoordinates; michael@0: ; michael@0: if (entry.length & 1) michael@0: entry += '\0'; michael@0: glyf += entry; michael@0: loca += toString16(offset / 2); michael@0: offset += entry.length; michael@0: xMins.push(xMin); michael@0: xMaxs.push(xMax); michael@0: yMins.push(yMin); michael@0: yMaxs.push(yMax); michael@0: if (numberOfContours > maxContours) michael@0: maxContours = numberOfContours; michael@0: if (endPoint > maxPoints) michael@0: maxPoints = endPoint; michael@0: } michael@0: loca += toString16(offset / 2); michael@0: tables['glyf'] = glyf; michael@0: tables['head'] = '\0\x01\0\0\0\x01\0\0\0\0\0\0_\x0f<\xf5\0\v\x04\0\0\0\0\0' + toString32(Date.now()) + '\0\0\0\0' + toString32(Date.now()) + toString16(min.apply(null, xMins)) + toString16(min.apply(null, yMins)) + toString16(max.apply(null, xMaxs)) + toString16(max.apply(null, yMaxs)) + toString16((tag.italic ? 2 : 0) | (tag.bold ? 1 : 0)) + '\0\b' + '\0\x02' + '\0\0' + '\0\0'; michael@0: ; michael@0: var advance = tag.advance; michael@0: tables['hhea'] = '\0\x01\0\0' + toString16(ascent) + toString16(descent) + toString16(leading) + toString16(advance ? max.apply(null, advance) : 1024) + '\0\0' + '\0\0' + '\x03\xb8' + '\0\x01' + '\0\0' + '\0\0' + '\0\0' + '\0\0' + '\0\0' + '\0\0' + '\0\0' + toString16(glyphCount + 1); michael@0: ; michael@0: var hmtx = '\0\0\0\0'; michael@0: for (var i = 0; i < glyphCount; ++i) michael@0: hmtx += toString16(advance ? advance[i] / resolution : 1024) + '\0\0'; michael@0: tables['hmtx'] = hmtx; michael@0: if (tag.kerning) { michael@0: var kerning = tag.kerning; michael@0: var nPairs = kerning.length; michael@0: var searchRange = maxPower2(nPairs) * 2; michael@0: var kern = '\0\0\0\x01\0\0' + toString16(14 + nPairs * 6) + '\0\x01' + toString16(nPairs) + toString16(searchRange) + toString16(logE(nPairs) / logE(2)) + toString16(2 * nPairs - searchRange); michael@0: ; michael@0: var i = 0; michael@0: var record; michael@0: while (record = kerning[i++]) { michael@0: kern += toString16(glyphIndex[record.code1]) + toString16(glyphIndex[record.code2]) + toString16(record.adjustment); michael@0: ; michael@0: } michael@0: tables['kern'] = kern; michael@0: } michael@0: tables['loca'] = loca; michael@0: tables['maxp'] = '\0\x01\0\0' + toString16(glyphCount + 1) + toString16(maxPoints) + toString16(maxContours) + '\0\0' + '\0\0' + '\0\0' + '\0\0' + '\0\0' + '\0\0' + '\0\0' + '\0\0' + '\0\0' + '\0\0' + '\0\0'; michael@0: ; michael@0: var uniqueId = 'swf-font-' + nextFontId++; michael@0: var fontName = tag.name || uniqueId; michael@0: var psName = fontName.replace(/ /g, ''); michael@0: var strings = [ michael@0: tag.copyright || 'Original licence', michael@0: fontName, michael@0: 'Unknown', michael@0: uniqueId, michael@0: fontName, michael@0: '1.0', michael@0: psName, michael@0: 'Unknown', michael@0: 'Unknown', michael@0: 'Unknown' michael@0: ]; michael@0: var count = strings.length; michael@0: var name = '\0\0' + toString16(count) + toString16(count * 12 + 6); michael@0: var offset = 0; michael@0: var i = 0; michael@0: var str; michael@0: while (str = strings[i++]) { michael@0: name += '\0\x01\0\0\0\0' + toString16(i - 1) + toString16(str.length) + toString16(offset); michael@0: offset += str.length; michael@0: } michael@0: tables['name'] = name + strings.join(''); michael@0: tables['post'] = '\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'; michael@0: ; michael@0: var names = keys(tables); michael@0: var numTables = names.length; michael@0: var header = '\0\x01\0\0' + toString16(numTables) + '\0\x80' + '\0\x03' + '\0 '; michael@0: ; michael@0: var data = ''; michael@0: var offset = numTables * 16 + header.length; michael@0: var i = 0; michael@0: var name; michael@0: while (name = names[i++]) { michael@0: var table = tables[name]; michael@0: var length = table.length; michael@0: header += name + '\0\0\0\0' + toString32(offset) + toString32(length); michael@0: ; michael@0: while (length & 3) { michael@0: table += '\0'; michael@0: ++length; michael@0: } michael@0: data += table; michael@0: while (offset & 3) michael@0: ++offset; michael@0: offset += length; michael@0: } michael@0: var otf = header + data; michael@0: var unitPerEm = 1024; michael@0: var metrics = { michael@0: ascent: ascent / unitPerEm, michael@0: descent: -descent / unitPerEm, michael@0: leading: leading / unitPerEm michael@0: }; michael@0: return { michael@0: type: 'font', michael@0: id: tag.id, michael@0: name: fontName, michael@0: uniqueName: psName + uniqueId, michael@0: codes: codes, michael@0: metrics: metrics, michael@0: bold: tag.bold === 1, michael@0: italic: tag.italic === 1, michael@0: data: otf michael@0: }; michael@0: } michael@0: function getUint16(buff, pos) { michael@0: return buff[pos] << 8 | buff[pos + 1]; michael@0: } michael@0: function parseJpegChunks(imgDef, bytes) { michael@0: var i = 0; michael@0: var n = bytes.length; michael@0: var chunks = []; michael@0: var code; michael@0: do { michael@0: var begin = i; michael@0: while (i < n && bytes[i] !== 255) michael@0: ++i; michael@0: while (i < n && bytes[i] === 255) michael@0: ++i; michael@0: code = bytes[i++]; michael@0: if (code === 218) { michael@0: i = n; michael@0: } else if (code === 217) { michael@0: i += 2; michael@0: continue; michael@0: } else if (code < 208 || code > 216) { michael@0: var length = getUint16(bytes, i); michael@0: if (code >= 192 && code <= 195) { michael@0: imgDef.height = getUint16(bytes, i + 3); michael@0: imgDef.width = getUint16(bytes, i + 5); michael@0: } michael@0: i += length; michael@0: } michael@0: chunks.push(bytes.subarray(begin, i)); michael@0: } while (i < n); michael@0: return chunks; michael@0: } michael@0: function defineImage(tag, dictionary) { michael@0: var img = { michael@0: type: 'image', michael@0: id: tag.id, michael@0: mimeType: tag.mimeType michael@0: }; michael@0: var imgData = tag.imgData; michael@0: var chunks; michael@0: if (tag.mimeType === 'image/jpeg') { michael@0: chunks = parseJpegChunks(img, imgData); michael@0: var alphaData = tag.alphaData; michael@0: if (alphaData) { michael@0: img.mask = createInflatedStream(alphaData, img.width * img.height).bytes; michael@0: } michael@0: if (tag.incomplete) { michael@0: var tables = dictionary[0]; michael@0: var header = tables.data; michael@0: if (header && header.size) { michael@0: chunks[0] = chunks[0].subarray(2); michael@0: chunks.unshift(header.slice(0, header.size - 2)); michael@0: } michael@0: } michael@0: } else { michael@0: chunks = [ michael@0: imgData michael@0: ]; michael@0: } michael@0: img.data = new Blob(chunks, { michael@0: type: tag.mimeType michael@0: }); michael@0: return img; michael@0: } michael@0: function defineLabel(tag, dictionary) { michael@0: var records = tag.records; michael@0: var m = tag.matrix; michael@0: var cmds = [ michael@0: 'c.save()', michael@0: 'c.transform(' + [ michael@0: m.a, michael@0: m.b, michael@0: m.c, michael@0: m.d, michael@0: m.tx / 20, michael@0: m.ty / 20 michael@0: ].join(',') + ')', michael@0: 'c.scale(0.05, 0.05)' michael@0: ]; michael@0: var dependencies = []; michael@0: var x = 0; michael@0: var y = 0; michael@0: var i = 0; michael@0: var record; michael@0: var codes; michael@0: while (record = records[i++]) { michael@0: if (record.eot) michael@0: break; michael@0: if (record.hasFont) { michael@0: var font = dictionary[record.fontId]; michael@0: codes = font.codes; michael@0: cmds.push('c.font="' + record.fontHeight + 'px \'' + font.uniqueName + '\'"'); michael@0: dependencies.push(font.id); michael@0: } michael@0: if (record.hasColor) { michael@0: cmds.push('ct.setFillStyle(c,"' + rgbaObjToStr(record.color) + '")'); michael@0: cmds.push('ct.setAlpha(c)'); michael@0: } else { michael@0: cmds.push('ct.setAlpha(c,true)'); michael@0: } michael@0: if (record.hasMoveX) michael@0: x = record.moveX; michael@0: if (record.hasMoveY) michael@0: y = record.moveY; michael@0: var entries = record.entries; michael@0: var j = 0; michael@0: var entry; michael@0: while (entry = entries[j++]) { michael@0: var code = codes[entry.glyphIndex]; michael@0: var text = code >= 32 && code != 34 && code != 92 ? fromCharCode(code) : '\\u' + (code + 65536).toString(16).substring(1); michael@0: cmds.push('c.fillText("' + text + '",' + x + ',' + y + ')'); michael@0: x += entry.advance; michael@0: } michael@0: } michael@0: cmds.push('c.restore()'); michael@0: var label = { michael@0: type: 'label', michael@0: id: tag.id, michael@0: bbox: tag.bbox, michael@0: data: cmds.join('\n') michael@0: }; michael@0: if (dependencies.length) michael@0: label.require = dependencies; michael@0: return label; michael@0: } michael@0: var GRAPHICS_FILL_CLIPPED_BITMAP = 65; michael@0: var GRAPHICS_FILL_FOCAL_RADIAL_GRADIENT = 19; michael@0: var GRAPHICS_FILL_LINEAR_GRADIENT = 16; michael@0: var GRAPHICS_FILL_NONSMOOTHED_CLIPPED_BITMAP = 67; michael@0: var GRAPHICS_FILL_NONSMOOTHED_REPEATING_BITMAP = 66; michael@0: var GRAPHICS_FILL_RADIAL_GRADIENT = 18; michael@0: var GRAPHICS_FILL_REPEATING_BITMAP = 64; michael@0: var GRAPHICS_FILL_SOLID = 0; michael@0: function applySegmentToStyles(segment, styles, linePaths, fillPaths, isMorph) { michael@0: if (!segment) { michael@0: return; michael@0: } michael@0: var commands = segment.commands; michael@0: var data = segment.data; michael@0: var morphData = segment.morphData; michael@0: if (morphData) { michael@0: } michael@0: var path; michael@0: var targetSegment; michael@0: var command; michael@0: var i; michael@0: if (styles.fill0) { michael@0: path = fillPaths[styles.fill0 - 1]; michael@0: if (!(styles.fill1 || styles.line)) { michael@0: targetSegment = path.head(); michael@0: targetSegment.commands = []; michael@0: targetSegment.data = []; michael@0: targetSegment.morphData = isMorph ? [] : null; michael@0: } else { michael@0: targetSegment = path.addSegment([], [], isMorph ? [] : null); michael@0: } michael@0: var targetCommands = targetSegment.commands; michael@0: var targetData = targetSegment.data; michael@0: var targetMorphData = targetSegment.morphData; michael@0: targetCommands.push(SHAPE_MOVE_TO); michael@0: var j = data.length - 2; michael@0: targetData.push(data[j], data[j + 1]); michael@0: if (isMorph) { michael@0: targetMorphData.push(morphData[j], morphData[j + 1]); michael@0: } michael@0: for (i = commands.length; i-- > 1; j -= 2) { michael@0: command = commands[i]; michael@0: targetCommands.push(command); michael@0: targetData.push(data[j - 2], data[j - 1]); michael@0: if (isMorph) { michael@0: targetMorphData.push(morphData[j - 2], morphData[j - 1]); michael@0: } michael@0: if (command === SHAPE_CURVE_TO) { michael@0: targetData.push(data[j - 4], data[j - 3]); michael@0: if (isMorph) { michael@0: targetMorphData.push(morphData[j - 4], morphData[j - 3]); michael@0: } michael@0: j -= 2; michael@0: } michael@0: } michael@0: if (isMorph) { michael@0: } michael@0: } michael@0: if (styles.line && styles.fill1) { michael@0: path = linePaths[styles.line - 1]; michael@0: path.addSegment(commands, data, morphData); michael@0: } michael@0: } michael@0: function convertRecordsToStyledPaths(records, fillPaths, linePaths, dictionary, dependencies, recordsMorph, transferables) { michael@0: var isMorph = recordsMorph !== null; michael@0: var styles = { michael@0: fill0: 0, michael@0: fill1: 0, michael@0: line: 0 michael@0: }; michael@0: var segment = null; michael@0: var allPaths; michael@0: var defaultPath; michael@0: var numRecords = records.length - 1; michael@0: var x = 0; michael@0: var y = 0; michael@0: var morphX = 0; michael@0: var morphY = 0; michael@0: var path; michael@0: for (var i = 0, j = 0; i < numRecords; i++) { michael@0: var record = records[i]; michael@0: var morphRecord; michael@0: if (isMorph) { michael@0: morphRecord = recordsMorph[j++]; michael@0: } michael@0: if (record.type === 0) { michael@0: if (segment) { michael@0: applySegmentToStyles(segment, styles, linePaths, fillPaths, isMorph); michael@0: } michael@0: if (record.hasNewStyles) { michael@0: if (!allPaths) { michael@0: allPaths = []; michael@0: } michael@0: push.apply(allPaths, fillPaths); michael@0: fillPaths = createPathsList(record.fillStyles, false, dictionary, dependencies); michael@0: push.apply(allPaths, linePaths); michael@0: linePaths = createPathsList(record.lineStyles, true, dictionary, dependencies); michael@0: if (defaultPath) { michael@0: allPaths.push(defaultPath); michael@0: defaultPath = null; michael@0: } michael@0: styles = { michael@0: fill0: 0, michael@0: fill1: 0, michael@0: line: 0 michael@0: }; michael@0: } michael@0: if (record.hasFillStyle0) { michael@0: styles.fill0 = record.fillStyle0; michael@0: } michael@0: if (record.hasFillStyle1) { michael@0: styles.fill1 = record.fillStyle1; michael@0: } michael@0: if (record.hasLineStyle) { michael@0: styles.line = record.lineStyle; michael@0: } michael@0: if (styles.fill1) { michael@0: path = fillPaths[styles.fill1 - 1]; michael@0: } else if (styles.line) { michael@0: path = linePaths[styles.line - 1]; michael@0: } else if (styles.fill0) { michael@0: path = fillPaths[styles.fill0 - 1]; michael@0: } michael@0: if (record.move) { michael@0: x = record.moveX | 0; michael@0: y = record.moveY | 0; michael@0: } michael@0: if (path) { michael@0: segment = path.addSegment([], [], isMorph ? [] : null); michael@0: segment.commands.push(SHAPE_MOVE_TO); michael@0: segment.data.push(x, y); michael@0: if (isMorph) { michael@0: if (morphRecord.type === 0) { michael@0: morphX = morphRecord.moveX | 0; michael@0: morphY = morphRecord.moveY | 0; michael@0: } else { michael@0: morphX = x; michael@0: morphY = y; michael@0: j--; michael@0: } michael@0: segment.morphData.push(morphX, morphY); michael@0: } michael@0: } michael@0: } else { michael@0: if (!segment) { michael@0: if (!defaultPath) { michael@0: var style = { michael@0: color: { michael@0: red: 0, michael@0: green: 0, michael@0: blue: 0, michael@0: alpha: 255 michael@0: }, michael@0: width: 20 michael@0: }; michael@0: defaultPath = new SegmentedPath(null, processStyle(style, true)); michael@0: } michael@0: segment = defaultPath.addSegment([], [], isMorph ? [] : null); michael@0: segment.commands.push(SHAPE_MOVE_TO); michael@0: segment.data.push(x, y); michael@0: if (isMorph) { michael@0: segment.morphData.push(morphX, morphY); michael@0: } michael@0: } michael@0: if (isMorph) { michael@0: while (morphRecord && morphRecord.type === 0) { michael@0: morphRecord = recordsMorph[j++]; michael@0: } michael@0: if (!morphRecord) { michael@0: morphRecord = record; michael@0: } michael@0: } michael@0: if (record.isStraight && (!isMorph || morphRecord.isStraight)) { michael@0: x += record.deltaX | 0; michael@0: y += record.deltaY | 0; michael@0: segment.commands.push(SHAPE_LINE_TO); michael@0: segment.data.push(x, y); michael@0: if (isMorph) { michael@0: morphX += morphRecord.deltaX | 0; michael@0: morphY += morphRecord.deltaY | 0; michael@0: segment.morphData.push(morphX, morphY); michael@0: } michael@0: } else { michael@0: var cx, cy; michael@0: var deltaX, deltaY; michael@0: if (!record.isStraight) { michael@0: cx = x + record.controlDeltaX | 0; michael@0: cy = y + record.controlDeltaY | 0; michael@0: x = cx + record.anchorDeltaX | 0; michael@0: y = cy + record.anchorDeltaY | 0; michael@0: } else { michael@0: deltaX = record.deltaX | 0; michael@0: deltaY = record.deltaY | 0; michael@0: cx = x + (deltaX >> 1); michael@0: cy = y + (deltaY >> 1); michael@0: x += deltaX; michael@0: y += deltaY; michael@0: } michael@0: segment.commands.push(SHAPE_CURVE_TO); michael@0: segment.data.push(cx, cy, x, y); michael@0: if (isMorph) { michael@0: if (!morphRecord.isStraight) { michael@0: cx = morphX + morphRecord.controlDeltaX | 0; michael@0: cy = morphY + morphRecord.controlDeltaY | 0; michael@0: morphX = cx + morphRecord.anchorDeltaX | 0; michael@0: morphY = cy + morphRecord.anchorDeltaY | 0; michael@0: } else { michael@0: deltaX = morphRecord.deltaX | 0; michael@0: deltaY = morphRecord.deltaY | 0; michael@0: cx = morphX + (deltaX >> 1); michael@0: cy = morphY + (deltaY >> 1); michael@0: morphX += deltaX; michael@0: morphY += deltaY; michael@0: } michael@0: segment.morphData.push(cx, cy, morphX, morphY); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: applySegmentToStyles(segment, styles, linePaths, fillPaths, isMorph); michael@0: if (allPaths) { michael@0: push.apply(allPaths, fillPaths); michael@0: } else { michael@0: allPaths = fillPaths; michael@0: } michael@0: push.apply(allPaths, linePaths); michael@0: if (defaultPath) { michael@0: allPaths.push(defaultPath); michael@0: } michael@0: var removeCount = 0; michael@0: for (i = 0; i < allPaths.length; i++) { michael@0: path = allPaths[i]; michael@0: if (!path.head()) { michael@0: removeCount++; michael@0: continue; michael@0: } michael@0: allPaths[i - removeCount] = segmentedPathToShapePath(path, isMorph, transferables); michael@0: } michael@0: allPaths.length -= removeCount; michael@0: return allPaths; michael@0: } michael@0: function segmentedPathToShapePath(path, isMorph, transferables) { michael@0: var start = path.head(); michael@0: var end = start; michael@0: var finalRoot = null; michael@0: var finalHead = null; michael@0: var skippedMoves = 0; michael@0: var current = start.prev; michael@0: while (start) { michael@0: while (current) { michael@0: if (path.segmentsConnect(current, start)) { michael@0: if (current.next !== start) { michael@0: path.removeSegment(current); michael@0: path.insertSegment(current, start); michael@0: } michael@0: start = current; michael@0: current = start.prev; michael@0: skippedMoves++; michael@0: continue; michael@0: } michael@0: if (path.segmentsConnect(end, current)) { michael@0: path.removeSegment(current); michael@0: end.next = current; michael@0: current = current.prev; michael@0: end.next.prev = end; michael@0: end.next.next = null; michael@0: end = end.next; michael@0: skippedMoves++; michael@0: continue; michael@0: } michael@0: current = current.prev; michael@0: } michael@0: current = start.prev; michael@0: if (!finalRoot) { michael@0: finalRoot = start; michael@0: finalHead = end; michael@0: } else { michael@0: finalHead.next = start; michael@0: start.prev = finalHead; michael@0: finalHead = end; michael@0: finalHead.next = null; michael@0: } michael@0: if (!current) { michael@0: break; michael@0: } michael@0: start = end = current; michael@0: current = start.prev; michael@0: } michael@0: var totalCommandsLength = -skippedMoves; michael@0: var totalDataLength = -skippedMoves << 1; michael@0: current = finalRoot; michael@0: while (current) { michael@0: totalCommandsLength += current.commands.length; michael@0: totalDataLength += current.data.length; michael@0: current = current.next; michael@0: } michael@0: var shape = new ShapePath(path.fillStyle, path.lineStyle, totalCommandsLength, totalDataLength, isMorph, transferables); michael@0: var allCommands = shape.commands; michael@0: var allData = shape.data; michael@0: var allMorphData = shape.morphData; michael@0: var commandsIndex = 0; michael@0: var dataIndex = 0; michael@0: current = finalRoot; michael@0: while (current) { michael@0: var commands = current.commands; michael@0: var data = current.data; michael@0: var morphData = current.morphData; michael@0: var offset = +(data[0] === allData[dataIndex - 2] && data[1] === allData[dataIndex - 1]); michael@0: for (var i = offset; i < commands.length; i++, commandsIndex++) { michael@0: allCommands[commandsIndex] = commands[i]; michael@0: } michael@0: for (i = offset << 1; i < data.length; i++, dataIndex++) { michael@0: allData[dataIndex] = data[i]; michael@0: if (isMorph) { michael@0: allMorphData[dataIndex] = morphData[i]; michael@0: } michael@0: } michael@0: current = current.next; michael@0: } michael@0: return shape; michael@0: } michael@0: var CAPS_STYLE_TYPES = [ michael@0: 'round', michael@0: 'none', michael@0: 'square' michael@0: ]; michael@0: var JOIN_STYLE_TYPES = [ michael@0: 'round', michael@0: 'bevel', michael@0: 'miter' michael@0: ]; michael@0: function processStyle(style, isLineStyle, dictionary, dependencies) { michael@0: if (isLineStyle) { michael@0: style.lineCap = CAPS_STYLE_TYPES[style.endCapStyle | 0]; michael@0: style.lineJoin = JOIN_STYLE_TYPES[style.joinStyle | 0]; michael@0: style.miterLimit = (style.miterLimitFactor || 1.5) * 2; michael@0: if (!style.color && style.hasFill) { michael@0: var fillStyle = processStyle(style.fillStyle, false, dictionary, dependencies); michael@0: style.style = fillStyle.style; michael@0: style.type = fillStyle.type; michael@0: style.transform = fillStyle.transform; michael@0: style.records = fillStyle.records; michael@0: style.focalPoint = fillStyle.focalPoint; michael@0: style.bitmapId = fillStyle.bitmapId; michael@0: style.repeat = fillStyle.repeat; michael@0: style.fillStyle = null; michael@0: return style; michael@0: } michael@0: } michael@0: var color; michael@0: if (style.type === undefined || style.type === GRAPHICS_FILL_SOLID) { michael@0: color = style.color; michael@0: style.style = 'rgba(' + color.red + ',' + color.green + ',' + color.blue + ',' + color.alpha / 255 + ')'; michael@0: style.color = null; michael@0: return style; michael@0: } michael@0: var scale; michael@0: switch (style.type) { michael@0: case GRAPHICS_FILL_LINEAR_GRADIENT: michael@0: case GRAPHICS_FILL_RADIAL_GRADIENT: michael@0: case GRAPHICS_FILL_FOCAL_RADIAL_GRADIENT: michael@0: scale = 819.2; michael@0: break; michael@0: case GRAPHICS_FILL_REPEATING_BITMAP: michael@0: case GRAPHICS_FILL_CLIPPED_BITMAP: michael@0: case GRAPHICS_FILL_NONSMOOTHED_REPEATING_BITMAP: michael@0: case GRAPHICS_FILL_NONSMOOTHED_CLIPPED_BITMAP: michael@0: if (dictionary[style.bitmapId]) { michael@0: dependencies.push(dictionary[style.bitmapId].id); michael@0: scale = 0.05; michael@0: } michael@0: break; michael@0: default: michael@0: fail('invalid fill style', 'shape'); michael@0: } michael@0: if (!style.matrix) { michael@0: return style; michael@0: } michael@0: var matrix = style.matrix; michael@0: style.transform = { michael@0: a: matrix.a * scale, michael@0: b: matrix.b * scale, michael@0: c: matrix.c * scale, michael@0: d: matrix.d * scale, michael@0: e: matrix.tx, michael@0: f: matrix.ty michael@0: }; michael@0: style.matrix = null; michael@0: return style; michael@0: } michael@0: function createPathsList(styles, isLineStyle, dictionary, dependencies) { michael@0: var paths = []; michael@0: for (var i = 0; i < styles.length; i++) { michael@0: var style = processStyle(styles[i], isLineStyle, dictionary, dependencies); michael@0: if (!isLineStyle) { michael@0: paths[i] = new SegmentedPath(style, null); michael@0: } else { michael@0: paths[i] = new SegmentedPath(null, style); michael@0: } michael@0: } michael@0: return paths; michael@0: } michael@0: function defineShape(tag, dictionary) { michael@0: var dependencies = []; michael@0: var transferables = []; michael@0: var fillPaths = createPathsList(tag.fillStyles, false, dictionary, dependencies); michael@0: var linePaths = createPathsList(tag.lineStyles, true, dictionary, dependencies); michael@0: var paths = convertRecordsToStyledPaths(tag.records, fillPaths, linePaths, dictionary, dependencies, tag.recordsMorph || null, transferables); michael@0: if (tag.bboxMorph) { michael@0: var mbox = tag.bboxMorph; michael@0: extendBoundsByPoint(tag.bbox, mbox.xMin, mbox.yMin); michael@0: extendBoundsByPoint(tag.bbox, mbox.xMax, mbox.yMax); michael@0: mbox = tag.strokeBboxMorph; michael@0: if (mbox) { michael@0: extendBoundsByPoint(tag.strokeBbox, mbox.xMin, mbox.yMin); michael@0: extendBoundsByPoint(tag.strokeBbox, mbox.xMax, mbox.yMax); michael@0: } michael@0: } michael@0: return { michael@0: type: 'shape', michael@0: id: tag.id, michael@0: strokeBbox: tag.strokeBbox, michael@0: strokeBboxMorph: tag.strokeBboxMorph, michael@0: bbox: tag.bbox, michael@0: bboxMorph: tag.bboxMorph, michael@0: isMorph: tag.isMorph, michael@0: paths: paths, michael@0: require: dependencies.length ? dependencies : null, michael@0: transferables: transferables michael@0: }; michael@0: } michael@0: function logShape(paths, bbox) { michael@0: var output = '{"bounds":' + JSON.stringify(bbox) + ',"paths":[' + paths.map(function (path) { michael@0: return path.serialize(); michael@0: }).join() + ']}'; michael@0: console.log(output); michael@0: } michael@0: function SegmentedPath(fillStyle, lineStyle) { michael@0: this.fillStyle = fillStyle; michael@0: this.lineStyle = lineStyle; michael@0: this._head = null; michael@0: } michael@0: SegmentedPath.prototype = { michael@0: addSegment: function (commands, data, morphData) { michael@0: var segment = { michael@0: commands: commands, michael@0: data: data, michael@0: morphData: morphData, michael@0: prev: this._head, michael@0: next: null michael@0: }; michael@0: if (this._head) { michael@0: this._head.next = segment; michael@0: } michael@0: this._head = segment; michael@0: return segment; michael@0: }, michael@0: removeSegment: function (segment) { michael@0: if (segment.prev) { michael@0: segment.prev.next = segment.next; michael@0: } michael@0: if (segment.next) { michael@0: segment.next.prev = segment.prev; michael@0: } michael@0: }, michael@0: insertSegment: function (segment, next) { michael@0: var prev = next.prev; michael@0: segment.prev = prev; michael@0: segment.next = next; michael@0: if (prev) { michael@0: prev.next = segment; michael@0: } michael@0: next.prev = segment; michael@0: }, michael@0: head: function () { michael@0: return this._head; michael@0: }, michael@0: segmentsConnect: function (first, second) { michael@0: var firstLength = first.data.length; michael@0: return first.data[firstLength - 2] === second.data[0] && first.data[firstLength - 1] === second.data[1]; michael@0: } michael@0: }; michael@0: var SHAPE_MOVE_TO = 1; michael@0: var SHAPE_LINE_TO = 2; michael@0: var SHAPE_CURVE_TO = 3; michael@0: var SHAPE_WIDE_MOVE_TO = 4; michael@0: var SHAPE_WIDE_LINE_TO = 5; michael@0: var SHAPE_CUBIC_CURVE_TO = 6; michael@0: var SHAPE_CIRCLE = 7; michael@0: var SHAPE_ELLIPSE = 8; michael@0: function ShapePath(fillStyle, lineStyle, commandsCount, dataLength, isMorph, transferables) { michael@0: this.fillStyle = fillStyle; michael@0: this.lineStyle = lineStyle; michael@0: if (commandsCount) { michael@0: this.commands = new Uint8Array(commandsCount); michael@0: this.data = new Int32Array(dataLength); michael@0: this.morphData = isMorph ? new Int32Array(dataLength) : null; michael@0: } else { michael@0: this.commands = []; michael@0: this.data = []; michael@0: } michael@0: this.bounds = null; michael@0: this.strokeBounds = null; michael@0: this.isMorph = !(!isMorph); michael@0: this.fullyInitialized = false; michael@0: if (inWorker) { michael@0: this.buffers = [ michael@0: this.commands.buffer, michael@0: this.data.buffer michael@0: ]; michael@0: transferables.push(this.commands.buffer, this.data.buffer); michael@0: if (isMorph) { michael@0: this.buffers.push(this.morphData.buffer); michael@0: transferables.push(this.morphData.buffer); michael@0: } michael@0: } else { michael@0: this.buffers = null; michael@0: } michael@0: } michael@0: ShapePath.prototype = { michael@0: get isEmpty() { michael@0: return this.commands.length === 0; michael@0: }, michael@0: moveTo: function (x, y) { michael@0: if (this.commands[this.commands.length - 1] === SHAPE_MOVE_TO) { michael@0: this.data[this.data.length - 2] = x; michael@0: this.data[this.data.length - 1] = y; michael@0: return; michael@0: } michael@0: this.commands.push(SHAPE_MOVE_TO); michael@0: this.data.push(x, y); michael@0: }, michael@0: lineTo: function (x, y) { michael@0: this.commands.push(SHAPE_LINE_TO); michael@0: this.data.push(x, y); michael@0: }, michael@0: curveTo: function (controlX, controlY, anchorX, anchorY) { michael@0: this.commands.push(SHAPE_CURVE_TO); michael@0: this.data.push(controlX, controlY, anchorX, anchorY); michael@0: }, michael@0: cubicCurveTo: function (control1X, control1Y, control2X, control2Y, anchorX, anchorY) { michael@0: this.commands.push(SHAPE_CUBIC_CURVE_TO); michael@0: this.data.push(control1X, control1Y, control2X, control2Y, anchorX, anchorY); michael@0: }, michael@0: rect: function (x, y, w, h) { michael@0: var x2 = x + w; michael@0: var y2 = y + h; michael@0: this.commands.push(SHAPE_MOVE_TO, SHAPE_LINE_TO, SHAPE_LINE_TO, SHAPE_LINE_TO, SHAPE_LINE_TO); michael@0: this.data.push(x, y, x2, y, x2, y2, x, y2, x, y); michael@0: }, michael@0: circle: function (x, y, radius) { michael@0: this.commands.push(SHAPE_CIRCLE); michael@0: this.data.push(x, y, radius); michael@0: }, michael@0: ellipse: function (x, y, radiusX, radiusY) { michael@0: this.commands.push(SHAPE_ELLIPSE); michael@0: this.data.push(x, y, radiusX, radiusY); michael@0: }, michael@0: draw: function (ctx, clip, ratio, colorTransform) { michael@0: if (clip && !this.fillStyle) { michael@0: return; michael@0: } michael@0: ctx.beginPath(); michael@0: var commands = this.commands; michael@0: var data = this.data; michael@0: var morphData = this.morphData; michael@0: var formOpen = false; michael@0: var formOpenX = 0; michael@0: var formOpenY = 0; michael@0: if (!this.isMorph) { michael@0: for (var j = 0, k = 0; j < commands.length; j++) { michael@0: switch (commands[j]) { michael@0: case SHAPE_MOVE_TO: michael@0: formOpen = true; michael@0: formOpenX = data[k++] / 20; michael@0: formOpenY = data[k++] / 20; michael@0: ctx.moveTo(formOpenX, formOpenY); michael@0: break; michael@0: case SHAPE_WIDE_MOVE_TO: michael@0: ctx.moveTo(data[k++] / 20, data[k++] / 20); michael@0: k += 2; michael@0: break; michael@0: case SHAPE_LINE_TO: michael@0: ctx.lineTo(data[k++] / 20, data[k++] / 20); michael@0: break; michael@0: case SHAPE_WIDE_LINE_TO: michael@0: ctx.lineTo(data[k++] / 20, data[k++] / 20); michael@0: k += 2; michael@0: break; michael@0: case SHAPE_CURVE_TO: michael@0: ctx.quadraticCurveTo(data[k++] / 20, data[k++] / 20, data[k++] / 20, data[k++] / 20); michael@0: break; michael@0: case SHAPE_CUBIC_CURVE_TO: michael@0: ctx.bezierCurveTo(data[k++] / 20, data[k++] / 20, data[k++] / 20, data[k++] / 20, data[k++] / 20, data[k++] / 20); michael@0: break; michael@0: case SHAPE_CIRCLE: michael@0: if (formOpen) { michael@0: ctx.lineTo(formOpenX, formOpenY); michael@0: formOpen = false; michael@0: } michael@0: ctx.moveTo((data[k] + data[k + 2]) / 20, data[k + 1] / 20); michael@0: ctx.arc(data[k++] / 20, data[k++] / 20, data[k++] / 20, 0, Math.PI * 2, false); michael@0: break; michael@0: case SHAPE_ELLIPSE: michael@0: if (formOpen) { michael@0: ctx.lineTo(formOpenX, formOpenY); michael@0: formOpen = false; michael@0: } michael@0: var x = data[k++]; michael@0: var y = data[k++]; michael@0: var rX = data[k++]; michael@0: var rY = data[k++]; michael@0: var radius; michael@0: if (rX !== rY) { michael@0: ctx.save(); michael@0: var ellipseScale; michael@0: if (rX > rY) { michael@0: ellipseScale = rX / rY; michael@0: radius = rY; michael@0: x /= ellipseScale; michael@0: ctx.scale(ellipseScale, 1); michael@0: } else { michael@0: ellipseScale = rY / rX; michael@0: radius = rX; michael@0: y /= ellipseScale; michael@0: ctx.scale(1, ellipseScale); michael@0: } michael@0: } michael@0: ctx.moveTo((x + radius) / 20, y / 20); michael@0: ctx.arc(x / 20, y / 20, radius / 20, 0, Math.PI * 2, false); michael@0: if (rX !== rY) { michael@0: ctx.restore(); michael@0: } michael@0: break; michael@0: default: michael@0: if (commands[j] === 0 && j === commands.length - 1) { michael@0: break; michael@0: } michael@0: console.warn('Unknown drawing command encountered: ' + commands[j]); michael@0: } michael@0: } michael@0: } else { michael@0: for (var j = 0, k = 0; j < commands.length; j++) { michael@0: switch (commands[j]) { michael@0: case SHAPE_MOVE_TO: michael@0: ctx.moveTo(morph(data[k] / 20, morphData[k++] / 20, ratio), morph(data[k] / 20, morphData[k++] / 20, ratio)); michael@0: break; michael@0: case SHAPE_LINE_TO: michael@0: ctx.lineTo(morph(data[k] / 20, morphData[k++] / 20, ratio), morph(data[k] / 20, morphData[k++] / 20, ratio)); michael@0: break; michael@0: case SHAPE_CURVE_TO: michael@0: ctx.quadraticCurveTo(morph(data[k] / 20, morphData[k++] / 20, ratio), morph(data[k] / 20, morphData[k++] / 20, ratio), morph(data[k] / 20, morphData[k++] / 20, ratio), morph(data[k] / 20, morphData[k++] / 20, ratio)); michael@0: break; michael@0: default: michael@0: console.warn('Drawing command not supported for morph shapes: ' + commands[j]); michael@0: } michael@0: } michael@0: } michael@0: if (!clip) { michael@0: var fillStyle = this.fillStyle; michael@0: if (fillStyle) { michael@0: colorTransform.setFillStyle(ctx, fillStyle.style); michael@0: ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = fillStyle.smooth; michael@0: var m = fillStyle.transform; michael@0: ctx.save(); michael@0: colorTransform.setAlpha(ctx); michael@0: if (m) { michael@0: ctx.transform(m.a, m.b, m.c, m.d, m.e / 20, m.f / 20); michael@0: } michael@0: ctx.fill(); michael@0: ctx.restore(); michael@0: } michael@0: var lineStyle = this.lineStyle; michael@0: if (lineStyle) { michael@0: colorTransform.setStrokeStyle(ctx, lineStyle.style); michael@0: ctx.save(); michael@0: colorTransform.setAlpha(ctx); michael@0: ctx.lineWidth = Math.max(lineStyle.width / 20, 1); michael@0: ctx.lineCap = lineStyle.lineCap; michael@0: ctx.lineJoin = lineStyle.lineJoin; michael@0: ctx.miterLimit = lineStyle.miterLimit; michael@0: ctx.stroke(); michael@0: ctx.restore(); michael@0: } michael@0: } else { michael@0: ctx.fill(); michael@0: } michael@0: ctx.closePath(); michael@0: }, michael@0: isPointInPath: function (x, y) { michael@0: if (!(this.fillStyle || this.lineStyle)) { michael@0: return false; michael@0: } michael@0: var bounds = this.strokeBounds || this.bounds || this._calculateBounds(); michael@0: if (x < bounds.xMin || x > bounds.xMax || y < bounds.yMin || y > bounds.yMax) { michael@0: return false; michael@0: } michael@0: if (this.fillStyle && this.isPointInFill(x, y)) { michael@0: return true; michael@0: } michael@0: return this.lineStyle && this.lineStyle.width !== undefined && this.isPointInStroke(x, y); michael@0: }, michael@0: isPointInFill: function (x, y) { michael@0: var commands = this.commands; michael@0: var data = this.data; michael@0: var length = commands.length; michael@0: var inside = false; michael@0: var fromX = 0; michael@0: var fromY = 0; michael@0: var toX = 0; michael@0: var toY = 0; michael@0: var localX; michael@0: var localY; michael@0: var cpX; michael@0: var cpY; michael@0: var rX; michael@0: var rY; michael@0: var formOpen = false; michael@0: var formOpenX = 0; michael@0: var formOpenY = 0; michael@0: for (var commandIndex = 0, dataIndex = 0; commandIndex < length; commandIndex++) { michael@0: switch (commands[commandIndex]) { michael@0: case SHAPE_WIDE_MOVE_TO: michael@0: dataIndex += 2; michael@0: case SHAPE_MOVE_TO: michael@0: toX = data[dataIndex++]; michael@0: toY = data[dataIndex++]; michael@0: if (formOpen && intersectsLine(x, y, fromX, fromY, formOpenX, formOpenY)) { michael@0: inside = !inside; michael@0: } michael@0: formOpen = true; michael@0: formOpenX = toX; michael@0: formOpenY = toY; michael@0: break; michael@0: case SHAPE_WIDE_LINE_TO: michael@0: dataIndex += 2; michael@0: case SHAPE_LINE_TO: michael@0: toX = data[dataIndex++]; michael@0: toY = data[dataIndex++]; michael@0: if (intersectsLine(x, y, fromX, fromY, toX, toY)) { michael@0: inside = !inside; michael@0: } michael@0: break; michael@0: case SHAPE_CURVE_TO: michael@0: cpX = data[dataIndex++]; michael@0: cpY = data[dataIndex++]; michael@0: toX = data[dataIndex++]; michael@0: toY = data[dataIndex++]; michael@0: if (cpY > y === fromY > y && toY > y === fromY > y) { michael@0: break; michael@0: } michael@0: if (fromX >= x && cpX >= x && toX >= x) { michael@0: inside = !inside; michael@0: break; michael@0: } michael@0: var a = fromY - 2 * cpY + toY; michael@0: var c = fromY - y; michael@0: var b = 2 * (cpY - fromY); michael@0: var d = b * b - 4 * a * c; michael@0: if (d < 0) { michael@0: break; michael@0: } michael@0: d = Math.sqrt(d); michael@0: a = 1 / (a + a); michael@0: var t1 = (d - b) * a; michael@0: var t2 = (-b - d) * a; michael@0: if (t1 >= 0 && t1 <= 1 && quadraticBezier(fromX, cpX, toX, t1) > x) { michael@0: inside = !inside; michael@0: } michael@0: if (t2 >= 0 && t2 <= 1 && quadraticBezier(fromX, cpX, toX, t2) > x) { michael@0: inside = !inside; michael@0: } michael@0: break; michael@0: case SHAPE_CUBIC_CURVE_TO: michael@0: cpX = data[dataIndex++]; michael@0: cpY = data[dataIndex++]; michael@0: var cp2X = data[dataIndex++]; michael@0: var cp2Y = data[dataIndex++]; michael@0: toX = data[dataIndex++]; michael@0: toY = data[dataIndex++]; michael@0: if (cpY > y === fromY > y && cp2Y > y === fromY > y && toY > y === fromY > y) { michael@0: break; michael@0: } michael@0: if (fromX >= x && cpX >= x && cp2X >= x && toX >= x) { michael@0: inside = !inside; michael@0: break; michael@0: } michael@0: var roots = cubicXAtY(fromX, fromY, cpX, cpY, cp2X, cp2Y, toX, toY, y); michael@0: for (var i = roots.length; i--;) { michael@0: if (roots[i] >= x) { michael@0: inside = !inside; michael@0: } michael@0: } michael@0: break; michael@0: case SHAPE_CIRCLE: michael@0: toX = data[dataIndex++]; michael@0: toY = data[dataIndex++]; michael@0: var r = data[dataIndex++]; michael@0: localX = x - toX; michael@0: localY = y - toY; michael@0: if (localX * localX + localY * localY < r * r) { michael@0: inside = !inside; michael@0: } michael@0: toX += r; michael@0: break; michael@0: case SHAPE_ELLIPSE: michael@0: cpX = data[dataIndex++]; michael@0: cpY = data[dataIndex++]; michael@0: rX = data[dataIndex++]; michael@0: rY = data[dataIndex++]; michael@0: localX = x - cpX; michael@0: localY = y - cpY; michael@0: if (localX * localX / (rX * rX) + localY * localY / (rY * rY) <= 1) { michael@0: inside = !inside; michael@0: } michael@0: toX = cpX + rX; michael@0: toY = cpY; michael@0: break; michael@0: default: michael@0: if (!inWorker) { michael@0: console.warn('Drawing command not handled in isPointInPath: ' + commands[commandIndex]); michael@0: } michael@0: } michael@0: fromX = toX; michael@0: fromY = toY; michael@0: } michael@0: if (formOpen && intersectsLine(x, y, fromX, fromY, formOpenX, formOpenY)) { michael@0: inside = !inside; michael@0: } michael@0: return inside; michael@0: }, michael@0: isPointInStroke: function (x, y) { michael@0: var commands = this.commands; michael@0: var data = this.data; michael@0: var length = commands.length; michael@0: var width = this.lineStyle.width; michael@0: var halfWidth = width / 2; michael@0: var halfWidthSq = halfWidth * halfWidth; michael@0: var minX = x - halfWidth; michael@0: var maxX = x + halfWidth; michael@0: var minY = y - halfWidth; michael@0: var maxY = y + halfWidth; michael@0: var fromX = 0; michael@0: var fromY = 0; michael@0: var toX = 0; michael@0: var toY = 0; michael@0: var localX; michael@0: var localY; michael@0: var cpX; michael@0: var cpY; michael@0: var rX; michael@0: var rY; michael@0: var curveX; michael@0: var curveY; michael@0: var t; michael@0: for (var commandIndex = 0, dataIndex = 0; commandIndex < length; commandIndex++) { michael@0: switch (commands[commandIndex]) { michael@0: case SHAPE_WIDE_MOVE_TO: michael@0: dataIndex += 2; michael@0: case SHAPE_MOVE_TO: michael@0: toX = data[dataIndex++]; michael@0: toY = data[dataIndex++]; michael@0: break; michael@0: case SHAPE_WIDE_LINE_TO: michael@0: dataIndex += 2; michael@0: case SHAPE_LINE_TO: michael@0: toX = data[dataIndex++]; michael@0: toY = data[dataIndex++]; michael@0: if (fromX === toX && fromY === toY) { michael@0: break; michael@0: } michael@0: if (maxX < fromX && maxX < toX || minX > fromX && minX > toX || maxY < fromY && maxY < toY || minY > fromY && minY > toY) { michael@0: break; michael@0: } michael@0: if (toX === fromX || toY === fromY) { michael@0: return true; michael@0: } michael@0: t = ((x - fromX) * (toX - fromX) + (y - fromY) * (toY - fromY)) / distanceSq(fromX, fromY, toX, toY); michael@0: if (t < 0) { michael@0: if (distanceSq(x, y, fromX, fromY) <= halfWidthSq) { michael@0: return true; michael@0: } michael@0: break; michael@0: } michael@0: if (t > 1) { michael@0: if (distanceSq(x, y, toX, toY) <= halfWidthSq) { michael@0: return true; michael@0: } michael@0: break; michael@0: } michael@0: if (distanceSq(x, y, fromX + t * (toX - fromX), fromY + t * (toY - fromY)) <= halfWidthSq) { michael@0: return true; michael@0: } michael@0: break; michael@0: case SHAPE_CURVE_TO: michael@0: cpX = data[dataIndex++]; michael@0: cpY = data[dataIndex++]; michael@0: toX = data[dataIndex++]; michael@0: toY = data[dataIndex++]; michael@0: var extremeX = quadraticBezierExtreme(fromX, cpX, toX); michael@0: if (maxX < fromX && maxX < extremeX && maxX < toX || minX > fromX && minX > extremeX && minX > toX) { michael@0: break; michael@0: } michael@0: var extremeY = quadraticBezierExtreme(fromY, cpY, toY); michael@0: if (maxY < fromY && maxY < extremeY && maxY < toY || minY > fromY && minY > extremeY && minY > toY) { michael@0: break; michael@0: } michael@0: for (t = 0; t < 1; t += 0.02) { michael@0: curveX = quadraticBezier(fromX, cpX, toX, t); michael@0: if (curveX < minX || curveX > maxX) { michael@0: continue; michael@0: } michael@0: curveY = quadraticBezier(fromY, cpY, toY, t); michael@0: if (curveY < minY || curveY > maxY) { michael@0: continue; michael@0: } michael@0: if ((x - curveX) * (x - curveX) + (y - curveY) * (y - curveY) < halfWidthSq) { michael@0: return true; michael@0: } michael@0: } michael@0: break; michael@0: case SHAPE_CUBIC_CURVE_TO: michael@0: cpX = data[dataIndex++]; michael@0: cpY = data[dataIndex++]; michael@0: var cp2X = data[dataIndex++]; michael@0: var cp2Y = data[dataIndex++]; michael@0: toX = data[dataIndex++]; michael@0: toY = data[dataIndex++]; michael@0: var extremesX = cubicBezierExtremes(fromX, cpX, cp2X, toX); michael@0: while (extremesX.length < 2) { michael@0: extremesX.push(toX); michael@0: } michael@0: if (maxX < fromX && maxX < toX && maxX < extremesX[0] && maxX < extremesX[1] || minX > fromX && minX > toX && minX > extremesX[0] && minX > extremesX[1]) { michael@0: break; michael@0: } michael@0: var extremesY = cubicBezierExtremes(fromY, cpY, cp2Y, toY); michael@0: while (extremesY.length < 2) { michael@0: extremesY.push(toY); michael@0: } michael@0: if (maxY < fromY && maxY < toY && maxY < extremesY[0] && maxY < extremesY[1] || minY > fromY && minY > toY && minY > extremesY[0] && minY > extremesY[1]) { michael@0: break; michael@0: } michael@0: for (t = 0; t < 1; t += 0.02) { michael@0: curveX = cubicBezier(fromX, cpX, cp2X, toX, t); michael@0: if (curveX < minX || curveX > maxX) { michael@0: continue; michael@0: } michael@0: curveY = cubicBezier(fromY, cpY, cp2Y, toY, t); michael@0: if (curveY < minY || curveY > maxY) { michael@0: continue; michael@0: } michael@0: if ((x - curveX) * (x - curveX) + (y - curveY) * (y - curveY) < halfWidthSq) { michael@0: return true; michael@0: } michael@0: } michael@0: break; michael@0: case SHAPE_CIRCLE: michael@0: cpX = data[dataIndex++]; michael@0: cpY = data[dataIndex++]; michael@0: var r = data[dataIndex++]; michael@0: toX = cpX + r; michael@0: toY = cpY; michael@0: if (maxX < cpX - r || minX > cpX + r || maxY < cpY - r || minY > cpY + r) { michael@0: break; michael@0: } michael@0: localX = x - cpX; michael@0: localY = y - cpY; michael@0: var rMin = r - halfWidth; michael@0: var rMax = r + halfWidth; michael@0: var distSq = localX * localX + localY * localY; michael@0: if (distSq >= rMin * rMin && distSq <= rMax * rMax) { michael@0: return true; michael@0: } michael@0: break; michael@0: case SHAPE_ELLIPSE: michael@0: cpX = data[dataIndex++]; michael@0: cpY = data[dataIndex++]; michael@0: rX = data[dataIndex++]; michael@0: rY = data[dataIndex++]; michael@0: toX = cpX + rX; michael@0: toY = cpY; michael@0: localX = Math.abs(x - cpX); michael@0: localY = Math.abs(y - cpY); michael@0: localX -= halfWidth; michael@0: localY -= halfWidth; michael@0: if (localX * localX / (rX * rX) + localY * localY / (rY * rY) > 1) { michael@0: break; michael@0: } michael@0: localX += width; michael@0: localY += width; michael@0: if (localX * localX / (rX * rX) + localY * localY / (rY * rY) > 1) { michael@0: return true; michael@0: } michael@0: break; michael@0: default: michael@0: if (!inWorker) { michael@0: console.warn('Drawing command not handled in isPointInPath: ' + commands[commandIndex]); michael@0: } michael@0: } michael@0: fromX = toX; michael@0: fromY = toY; michael@0: } michael@0: return false; michael@0: }, michael@0: getBounds: function (includeStroke) { michael@0: var bounds = includeStroke ? this.strokeBounds : this.bounds; michael@0: if (!bounds) { michael@0: this._calculateBounds(); michael@0: bounds = includeStroke ? this.strokeBounds : this.bounds; michael@0: } michael@0: return bounds; michael@0: }, michael@0: _calculateBounds: function () { michael@0: var commands = this.commands; michael@0: var data = this.data; michael@0: var length = commands.length; michael@0: var bounds; michael@0: if (commands[0] === SHAPE_MOVE_TO || commands[0] > SHAPE_CUBIC_CURVE_TO) { michael@0: bounds = { michael@0: xMin: data[0], michael@0: yMin: data[1] michael@0: }; michael@0: } else { michael@0: bounds = { michael@0: xMin: 0, michael@0: yMin: 0 michael@0: }; michael@0: } michael@0: bounds.xMax = bounds.xMin; michael@0: bounds.yMax = bounds.yMin; michael@0: var fromX = bounds.xMin; michael@0: var fromY = bounds.yMin; michael@0: for (var commandIndex = 0, dataIndex = 0; commandIndex < length; commandIndex++) { michael@0: var toX; michael@0: var toY; michael@0: var cpX; michael@0: var cpY; michael@0: switch (commands[commandIndex]) { michael@0: case SHAPE_WIDE_MOVE_TO: michael@0: dataIndex += 2; michael@0: case SHAPE_MOVE_TO: michael@0: toX = data[dataIndex++]; michael@0: toY = data[dataIndex++]; michael@0: extendBoundsByPoint(bounds, toX, toY); michael@0: break; michael@0: case SHAPE_WIDE_LINE_TO: michael@0: dataIndex += 2; michael@0: case SHAPE_LINE_TO: michael@0: toX = data[dataIndex++]; michael@0: toY = data[dataIndex++]; michael@0: extendBoundsByPoint(bounds, toX, toY); michael@0: break; michael@0: case SHAPE_CURVE_TO: michael@0: cpX = data[dataIndex++]; michael@0: cpY = data[dataIndex++]; michael@0: toX = data[dataIndex++]; michael@0: toY = data[dataIndex++]; michael@0: extendBoundsByPoint(bounds, toX, toY); michael@0: if (cpX < fromX || cpX > toX) { michael@0: extendBoundsByX(bounds, quadraticBezierExtreme(fromX, cpX, toX)); michael@0: } michael@0: if (cpY < fromY || cpY > toY) { michael@0: extendBoundsByY(bounds, quadraticBezierExtreme(fromY, cpY, toY)); michael@0: } michael@0: break; michael@0: case SHAPE_CUBIC_CURVE_TO: michael@0: cpX = data[dataIndex++]; michael@0: cpY = data[dataIndex++]; michael@0: var cp2X = data[dataIndex++]; michael@0: var cp2Y = data[dataIndex++]; michael@0: toX = data[dataIndex++]; michael@0: toY = data[dataIndex++]; michael@0: extendBoundsByPoint(bounds, toX, toY); michael@0: var extremes; michael@0: var i; michael@0: if (cpX < fromX || cp2X < fromX || cpX > toX || cp2X > toX) { michael@0: extremes = cubicBezierExtremes(fromX, cpX, cp2X, toX); michael@0: for (i = extremes.length; i--;) { michael@0: extendBoundsByX(bounds, extremes[i]); michael@0: } michael@0: } michael@0: if (cpY < fromY || cp2Y < fromY || cpY > toY || cp2Y > toY) { michael@0: extremes = cubicBezierExtremes(fromY, cpY, cp2Y, toY); michael@0: for (i = extremes.length; i--;) { michael@0: extendBoundsByY(bounds, extremes[i]); michael@0: } michael@0: } michael@0: break; michael@0: case SHAPE_CIRCLE: michael@0: toX = data[dataIndex++]; michael@0: toY = data[dataIndex++]; michael@0: var radius = data[dataIndex++]; michael@0: extendBoundsByPoint(bounds, toX - radius, toY - radius); michael@0: extendBoundsByPoint(bounds, toX + radius, toY + radius); michael@0: toX += radius; michael@0: break; michael@0: case SHAPE_ELLIPSE: michael@0: toX = data[dataIndex++]; michael@0: toY = data[dataIndex++]; michael@0: var radiusX = data[dataIndex++]; michael@0: var radiusY = data[dataIndex++]; michael@0: extendBoundsByPoint(bounds, toX - radiusX, toY - radiusY); michael@0: extendBoundsByPoint(bounds, toX + radiusX, toY + radiusY); michael@0: toX += radiusX; michael@0: break; michael@0: default: michael@0: if (!inWorker) { michael@0: console.warn('Drawing command not handled in bounds calculation: ' + commands[commandIndex]); michael@0: } michael@0: } michael@0: fromX = toX; michael@0: fromY = toY; michael@0: } michael@0: this.bounds = bounds; michael@0: if (this.lineStyle) { michael@0: var halfLineWidth = this.lineStyle.width / 2; michael@0: this.strokeBounds = { michael@0: xMin: bounds.xMin - halfLineWidth, michael@0: yMin: bounds.yMin - halfLineWidth, michael@0: xMax: bounds.xMax + halfLineWidth, michael@0: yMax: bounds.yMax + halfLineWidth michael@0: }; michael@0: return this.strokeBounds; michael@0: } else { michael@0: this.strokeBounds = bounds; michael@0: } michael@0: return bounds; michael@0: }, michael@0: serialize: function () { michael@0: var output = '{'; michael@0: if (this.fillStyle) { michael@0: output += '"fill":' + JSON.stringify(this.fillStyle) + ','; michael@0: } michael@0: if (this.lineStyle) { michael@0: output += '"stroke":' + JSON.stringify(this.lineStyle) + ','; michael@0: } michael@0: output += '"commands":[' + Array.apply([], this.commands).join() + '],'; michael@0: output += '"data":[' + Array.apply([], this.data).join() + ']'; michael@0: return output + '}'; michael@0: } michael@0: }; michael@0: ShapePath.fromPlainObject = function (obj) { michael@0: var path = new ShapePath(obj.fill || null, obj.stroke || null); michael@0: path.commands = new Uint8Array(obj.commands); michael@0: path.data = new Int32Array(obj.data); michael@0: if (!inWorker) { michael@0: finishShapePath(path); michael@0: } michael@0: return path; michael@0: }; michael@0: function distanceSq(x1, y1, x2, y2) { michael@0: var dX = x2 - x1; michael@0: var dY = y2 - y1; michael@0: return dX * dX + dY * dY; michael@0: } michael@0: function intersectsLine(x, y, x1, y1, x2, y2) { michael@0: return y2 > y !== y1 > y && x < (x1 - x2) * (y - y2) / (y1 - y2) + x2; michael@0: } michael@0: function quadraticBezier(from, cp, to, t) { michael@0: var inverseT = 1 - t; michael@0: return from * inverseT * inverseT + 2 * cp * inverseT * t + to * t * t; michael@0: } michael@0: function quadraticBezierExtreme(from, cp, to) { michael@0: var t = (from - cp) / (from - 2 * cp + to); michael@0: if (t < 0) { michael@0: return from; michael@0: } michael@0: if (t > 1) { michael@0: return to; michael@0: } michael@0: return quadraticBezier(from, cp, to, t); michael@0: } michael@0: function cubicBezier(from, cp, cp2, to, t) { michael@0: var tSq = t * t; michael@0: var inverseT = 1 - t; michael@0: var inverseTSq = inverseT * inverseT; michael@0: return from * inverseT * inverseTSq + 3 * cp * t * inverseTSq + 3 * cp2 * inverseT * tSq + to * t * tSq; michael@0: } michael@0: function cubicBezierExtremes(from, cp, cp2, to) { michael@0: var d1 = cp - from; michael@0: var d2 = cp2 - cp; michael@0: d2 *= 2; michael@0: var d3 = to - cp2; michael@0: if (d1 + d3 === d2) { michael@0: d3 *= 1.0001; michael@0: } michael@0: var fHead = 2 * d1 - d2; michael@0: var part1 = d2 - 2 * d1; michael@0: var fCenter = Math.sqrt(part1 * part1 - 4 * d1 * (d1 - d2 + d3)); michael@0: var fTail = 2 * (d1 - d2 + d3); michael@0: var t1 = (fHead + fCenter) / fTail; michael@0: var t2 = (fHead - fCenter) / fTail; michael@0: var result = []; michael@0: if (t1 >= 0 && t1 <= 1) { michael@0: result.push(cubicBezier(from, cp, cp2, to, t1)); michael@0: } michael@0: if (t2 >= 0 && t2 <= 1) { michael@0: result.push(cubicBezier(from, cp, cp2, to, t2)); michael@0: } michael@0: return result; michael@0: } michael@0: function cubicXAtY(x0, y0, cx, cy, cx1, cy1, x1, y1, y) { michael@0: var dX = 3 * (cx - x0); michael@0: var dY = 3 * (cy - y0); michael@0: var bX = 3 * (cx1 - cx) - dX; michael@0: var bY = 3 * (cy1 - cy) - dY; michael@0: var c3X = x1 - x0 - dX - bX; michael@0: var c3Y = y1 - y0 - dY - bY; michael@0: function f(t) { michael@0: return t * (dY + t * (bY + t * c3Y)) + y0 - y; michael@0: } michael@0: function pointAt(t) { michael@0: if (t < 0) { michael@0: t = 0; michael@0: } else if (t > 1) { michael@0: t = 1; michael@0: } michael@0: return x0 + t * (dX + t * (bX + t * c3X)); michael@0: } michael@0: function bisectCubicBezierRange(f, l, r, limit) { michael@0: if (Math.abs(r - l) <= limit) { michael@0: return; michael@0: } michael@0: var middle = 0.5 * (l + r); michael@0: if (f(l) * f(r) <= 0) { michael@0: left = l; michael@0: right = r; michael@0: return; michael@0: } michael@0: bisectCubicBezierRange(f, l, middle, limit); michael@0: bisectCubicBezierRange(f, middle, r, limit); michael@0: } michael@0: var left = 0; michael@0: var right = 1; michael@0: bisectCubicBezierRange(f, 0, 1, 0.05); michael@0: var t0 = findRoot(left, right, f, 50, 0.000001); michael@0: var evalResult = Math.abs(f(t0)); michael@0: if (evalResult > 0.00001) { michael@0: return []; michael@0: } michael@0: var result = []; michael@0: if (t0 <= 1) { michael@0: result.push(pointAt(t0)); michael@0: } michael@0: var a = c3Y; michael@0: var b = t0 * a + bY; michael@0: var c = t0 * b + dY; michael@0: var d = b * b - 4 * a * c; michael@0: if (d < 0) { michael@0: return result; michael@0: } michael@0: d = Math.sqrt(d); michael@0: a = 1 / (a + a); michael@0: var t1 = (d - b) * a; michael@0: var t2 = (-b - d) * a; michael@0: if (t1 >= 0 && t1 <= 1) { michael@0: result.push(pointAt(t1)); michael@0: } michael@0: if (t2 >= 0 && t2 <= 1) { michael@0: result.push(pointAt(t2)); michael@0: } michael@0: return result; michael@0: } michael@0: function findRoot(x0, x2, f, maxIterations, epsilon) { michael@0: var x1; michael@0: var y0; michael@0: var y1; michael@0: var y2; michael@0: var b; michael@0: var c; michael@0: var y10; michael@0: var y20; michael@0: var y21; michael@0: var xm; michael@0: var ym; michael@0: var temp; michael@0: var xmlast = x0; michael@0: y0 = f(x0); michael@0: if (y0 === 0) { michael@0: return x0; michael@0: } michael@0: y2 = f(x2); michael@0: if (y2 === 0) { michael@0: return x2; michael@0: } michael@0: if (y2 * y0 > 0) { michael@0: return x0; michael@0: } michael@0: var __iter = 0; michael@0: for (var i = 0; i < maxIterations; ++i) { michael@0: __iter++; michael@0: x1 = 0.5 * (x2 + x0); michael@0: y1 = f(x1); michael@0: if (y1 === 0) { michael@0: return x1; michael@0: } michael@0: if (Math.abs(x1 - x0) < epsilon) { michael@0: return x1; michael@0: } michael@0: if (y1 * y0 > 0) { michael@0: temp = x0; michael@0: x0 = x2; michael@0: x2 = temp; michael@0: temp = y0; michael@0: y0 = y2; michael@0: y2 = temp; michael@0: } michael@0: y10 = y1 - y0; michael@0: y21 = y2 - y1; michael@0: y20 = y2 - y0; michael@0: if (y2 * y20 < 2 * y1 * y10) { michael@0: x2 = x1; michael@0: y2 = y1; michael@0: } else { michael@0: b = (x1 - x0) / y10; michael@0: c = (y10 - y21) / (y21 * y20); michael@0: xm = x0 - b * y0 * (1 - c * y1); michael@0: ym = f(xm); michael@0: if (ym === 0) { michael@0: return xm; michael@0: } michael@0: if (Math.abs(xm - xmlast) < epsilon) { michael@0: return xm; michael@0: } michael@0: xmlast = xm; michael@0: if (ym * y0 < 0) { michael@0: x2 = xm; michael@0: y2 = ym; michael@0: } else { michael@0: x0 = xm; michael@0: y0 = ym; michael@0: x2 = x1; michael@0: y2 = y1; michael@0: } michael@0: } michael@0: } michael@0: return x1; michael@0: } michael@0: function extendBoundsByPoint(bounds, x, y) { michael@0: if (x < bounds.xMin) { michael@0: bounds.xMin = x; michael@0: } else if (x > bounds.xMax) { michael@0: bounds.xMax = x; michael@0: } michael@0: if (y < bounds.yMin) { michael@0: bounds.yMin = y; michael@0: } else if (y > bounds.yMax) { michael@0: bounds.yMax = y; michael@0: } michael@0: } michael@0: function extendBoundsByX(bounds, x) { michael@0: if (x < bounds.xMin) { michael@0: bounds.xMin = x; michael@0: } else if (x > bounds.xMax) { michael@0: bounds.xMax = x; michael@0: } michael@0: } michael@0: function extendBoundsByY(bounds, y) { michael@0: if (y < bounds.yMin) { michael@0: bounds.yMin = y; michael@0: } else if (y > bounds.yMax) { michael@0: bounds.yMax = y; michael@0: } michael@0: } michael@0: function morph(start, end, ratio) { michael@0: return start + (end - start) * ratio; michael@0: } michael@0: function finishShapePath(path, dictionaryResolved) { michael@0: if (path.fullyInitialized) { michael@0: return path; michael@0: } michael@0: if (!(path instanceof ShapePath)) { michael@0: var untypedPath = path; michael@0: path = new ShapePath(path.fillStyle, path.lineStyle, 0, 0, path.isMorph); michael@0: path.commands = new Uint8Array(untypedPath.buffers[0]); michael@0: path.data = new Int32Array(untypedPath.buffers[1]); michael@0: if (untypedPath.isMorph) { michael@0: path.morphData = new Int32Array(untypedPath.buffers[2]); michael@0: } michael@0: path.buffers = null; michael@0: } michael@0: path.fillStyle && initStyle(path.fillStyle, dictionaryResolved); michael@0: path.lineStyle && initStyle(path.lineStyle, dictionaryResolved); michael@0: path.fullyInitialized = true; michael@0: return path; michael@0: } michael@0: var inWorker = typeof window === 'undefined'; michael@0: var factoryCtx = !inWorker ? document.createElement('canvas').getContext('2d') : null; michael@0: function buildLinearGradientFactory(colorStops) { michael@0: var defaultGradient = factoryCtx.createLinearGradient(-1, 0, 1, 0); michael@0: for (var i = 0; i < colorStops.length; i++) { michael@0: defaultGradient.addColorStop(colorStops[i].ratio, colorStops[i].color); michael@0: } michael@0: var fn = function createLinearGradient(ctx, colorTransform) { michael@0: var gradient = ctx.createLinearGradient(-1, 0, 1, 0); michael@0: for (var i = 0; i < colorStops.length; i++) { michael@0: colorTransform.addGradientColorStop(gradient, colorStops[i].ratio, colorStops[i].color); michael@0: } michael@0: return gradient; michael@0: }; michael@0: fn.defaultFillStyle = defaultGradient; michael@0: return fn; michael@0: } michael@0: function buildRadialGradientFactory(focalPoint, colorStops) { michael@0: var defaultGradient = factoryCtx.createRadialGradient(focalPoint, 0, 0, 0, 0, 1); michael@0: for (var i = 0; i < colorStops.length; i++) { michael@0: defaultGradient.addColorStop(colorStops[i].ratio, colorStops[i].color); michael@0: } michael@0: var fn = function createRadialGradient(ctx, colorTransform) { michael@0: var gradient = ctx.createRadialGradient(focalPoint, 0, 0, 0, 0, 1); michael@0: for (var i = 0; i < colorStops.length; i++) { michael@0: colorTransform.addGradientColorStop(gradient, colorStops[i].ratio, colorStops[i].color); michael@0: } michael@0: return gradient; michael@0: }; michael@0: fn.defaultFillStyle = defaultGradient; michael@0: return fn; michael@0: } michael@0: function buildBitmapPatternFactory(img, repeat) { michael@0: var defaultPattern = factoryCtx.createPattern(img, repeat); michael@0: var cachedTransform, cachedTransformKey; michael@0: var fn = function createBitmapPattern(ctx, colorTransform) { michael@0: if (!colorTransform.mode) { michael@0: return defaultPattern; michael@0: } michael@0: var key = colorTransform.getTransformFingerprint(); michael@0: if (key === cachedTransformKey) { michael@0: return cachedTransform; michael@0: } michael@0: var canvas = document.createElement('canvas'); michael@0: canvas.width = img.width; michael@0: canvas.height = img.height; michael@0: var ctx = canvas.getContext('2d'); michael@0: colorTransform.setAlpha(ctx, true); michael@0: ctx.drawImage(img, 0, 0); michael@0: cachedTransform = ctx.createPattern(canvas, repeat); michael@0: cachedTransformKey = key; michael@0: return cachedTransform; michael@0: }; michael@0: fn.defaultFillStyle = defaultPattern; michael@0: return fn; michael@0: } michael@0: function initStyle(style, dictionaryResolved) { michael@0: if (style.type === undefined) { michael@0: return; michael@0: } michael@0: switch (style.type) { michael@0: case GRAPHICS_FILL_SOLID: michael@0: break; michael@0: case GRAPHICS_FILL_LINEAR_GRADIENT: michael@0: case GRAPHICS_FILL_RADIAL_GRADIENT: michael@0: case GRAPHICS_FILL_FOCAL_RADIAL_GRADIENT: michael@0: var records = style.records, colorStops = []; michael@0: for (var j = 0, n = records.length; j < n; j++) { michael@0: var record = records[j]; michael@0: var colorStr = rgbaObjToStr(record.color); michael@0: colorStops.push({ michael@0: ratio: record.ratio / 255, michael@0: color: colorStr michael@0: }); michael@0: } michael@0: var gradientConstructor; michael@0: var isLinear = style.type === GRAPHICS_FILL_LINEAR_GRADIENT; michael@0: if (isLinear) { michael@0: gradientConstructor = buildLinearGradientFactory(colorStops); michael@0: } else { michael@0: gradientConstructor = buildRadialGradientFactory((style.focalPoint | 0) / 20, colorStops); michael@0: } michael@0: style.style = gradientConstructor; michael@0: break; michael@0: case GRAPHICS_FILL_REPEATING_BITMAP: michael@0: case GRAPHICS_FILL_CLIPPED_BITMAP: michael@0: case GRAPHICS_FILL_NONSMOOTHED_REPEATING_BITMAP: michael@0: case GRAPHICS_FILL_NONSMOOTHED_CLIPPED_BITMAP: michael@0: var bitmap = dictionaryResolved[style.bitmapId]; michael@0: var repeat = style.type === GRAPHICS_FILL_REPEATING_BITMAP || style.type === GRAPHICS_FILL_NONSMOOTHED_REPEATING_BITMAP; michael@0: style.style = buildBitmapPatternFactory(bitmap.props.img, repeat ? 'repeat' : 'no-repeat'); michael@0: break; michael@0: default: michael@0: fail('invalid fill style', 'shape'); michael@0: } michael@0: } michael@0: var SOUND_SIZE_8_BIT = 0; michael@0: var SOUND_SIZE_16_BIT = 1; michael@0: var SOUND_TYPE_MONO = 0; michael@0: var SOUND_TYPE_STEREO = 1; michael@0: var SOUND_FORMAT_PCM_BE = 0; michael@0: var SOUND_FORMAT_ADPCM = 1; michael@0: var SOUND_FORMAT_MP3 = 2; michael@0: var SOUND_FORMAT_PCM_LE = 3; michael@0: var SOUND_FORMAT_NELLYMOSER_16 = 4; michael@0: var SOUND_FORMAT_NELLYMOSER_8 = 5; michael@0: var SOUND_FORMAT_NELLYMOSER = 6; michael@0: var SOUND_FORMAT_SPEEX = 11; michael@0: var SOUND_RATES = [ michael@0: 5512, michael@0: 11250, michael@0: 22500, michael@0: 44100 michael@0: ]; michael@0: var WaveHeader = new Uint8Array([ michael@0: 82, michael@0: 73, michael@0: 70, michael@0: 70, michael@0: 0, michael@0: 0, michael@0: 0, michael@0: 0, michael@0: 87, michael@0: 65, michael@0: 86, michael@0: 69, michael@0: 102, michael@0: 109, michael@0: 116, michael@0: 32, michael@0: 16, michael@0: 0, michael@0: 0, michael@0: 0, michael@0: 1, michael@0: 0, michael@0: 2, michael@0: 0, michael@0: 68, michael@0: 172, michael@0: 0, michael@0: 0, michael@0: 16, michael@0: 177, michael@0: 2, michael@0: 0, michael@0: 4, michael@0: 0, michael@0: 16, michael@0: 0, michael@0: 100, michael@0: 97, michael@0: 116, michael@0: 97, michael@0: 0, michael@0: 0, michael@0: 0, michael@0: 0 michael@0: ]); michael@0: function packageWave(data, sampleRate, channels, size, swapBytes) { michael@0: var sizeInBytes = size >> 3; michael@0: var sizePerSecond = channels * sampleRate * sizeInBytes; michael@0: var sizePerSample = channels * sizeInBytes; michael@0: var dataLength = data.length + (data.length & 1); michael@0: var buffer = new ArrayBuffer(WaveHeader.length + dataLength); michael@0: var bytes = new Uint8Array(buffer); michael@0: bytes.set(WaveHeader); michael@0: if (swapBytes) { michael@0: for (var i = 0, j = WaveHeader.length; i < data.length; i += 2, j += 2) { michael@0: bytes[j] = data[i + 1]; michael@0: bytes[j + 1] = data[i]; michael@0: } michael@0: } else { michael@0: bytes.set(data, WaveHeader.length); michael@0: } michael@0: var view = new DataView(buffer); michael@0: view.setUint32(4, dataLength + 36, true); michael@0: view.setUint16(22, channels, true); michael@0: view.setUint32(24, sampleRate, true); michael@0: view.setUint32(28, sizePerSecond, true); michael@0: view.setUint16(32, sizePerSample, true); michael@0: view.setUint16(34, size, true); michael@0: view.setUint32(40, dataLength, true); michael@0: return { michael@0: data: bytes, michael@0: mimeType: 'audio/wav' michael@0: }; michael@0: } michael@0: function defineSound(tag, dictionary) { michael@0: var channels = tag.soundType == SOUND_TYPE_STEREO ? 2 : 1; michael@0: var samplesCount = tag.samplesCount; michael@0: var sampleRate = SOUND_RATES[tag.soundRate]; michael@0: var data = tag.soundData; michael@0: var pcm, packaged; michael@0: switch (tag.soundFormat) { michael@0: case SOUND_FORMAT_PCM_BE: michael@0: pcm = new Float32Array(samplesCount * channels); michael@0: if (tag.soundSize == SOUND_SIZE_16_BIT) { michael@0: for (var i = 0, j = 0; i < pcm.length; i++, j += 2) michael@0: pcm[i] = (data[j] << 24 | data[j + 1] << 16) / 2147483648; michael@0: packaged = packageWave(data, sampleRate, channels, 16, true); michael@0: } else { michael@0: for (var i = 0; i < pcm.length; i++) michael@0: pcm[i] = (data[i] - 128) / 128; michael@0: packaged = packageWave(data, sampleRate, channels, 8, false); michael@0: } michael@0: break; michael@0: case SOUND_FORMAT_PCM_LE: michael@0: pcm = new Float32Array(samplesCount * channels); michael@0: if (tag.soundSize == SOUND_SIZE_16_BIT) { michael@0: for (var i = 0, j = 0; i < pcm.length; i++, j += 2) michael@0: pcm[i] = (data[j + 1] << 24 | data[j] << 16) / 2147483648; michael@0: packaged = packageWave(data, sampleRate, channels, 16, false); michael@0: } else { michael@0: for (var i = 0; i < pcm.length; i++) michael@0: pcm[i] = (data[i] - 128) / 128; michael@0: packaged = packageWave(data, sampleRate, channels, 8, false); michael@0: } michael@0: break; michael@0: case SOUND_FORMAT_MP3: michael@0: packaged = { michael@0: data: new Uint8Array(data.subarray(2)), michael@0: mimeType: 'audio/mpeg' michael@0: }; michael@0: break; michael@0: case SOUND_FORMAT_ADPCM: michael@0: var pcm16 = new Int16Array(samplesCount * channels); michael@0: decodeACPCMSoundData(data, pcm16, channels); michael@0: pcm = new Float32Array(samplesCount * channels); michael@0: for (var i = 0; i < pcm.length; i++) michael@0: pcm[i] = pcm16[i] / 32768; michael@0: packaged = packageWave(new Uint8Array(pcm16.buffer), sampleRate, channels, 16, !new Uint8Array(new Uint16Array([ michael@0: 1 michael@0: ]).buffer)[0]); michael@0: break; michael@0: default: michael@0: throw new Error('Unsupported audio format: ' + tag.soundFormat); michael@0: } michael@0: var sound = { michael@0: type: 'sound', michael@0: id: tag.id, michael@0: sampleRate: sampleRate, michael@0: channels: channels, michael@0: pcm: pcm michael@0: }; michael@0: if (packaged) michael@0: sound.packaged = packaged; michael@0: return sound; michael@0: } michael@0: var ACPCMIndexTables = [ michael@0: [ michael@0: -1, michael@0: 2 michael@0: ], michael@0: [ michael@0: -1, michael@0: -1, michael@0: 2, michael@0: 4 michael@0: ], michael@0: [ michael@0: -1, michael@0: -1, michael@0: -1, michael@0: -1, michael@0: 2, michael@0: 4, michael@0: 6, michael@0: 8 michael@0: ], michael@0: [ michael@0: -1, michael@0: -1, michael@0: -1, michael@0: -1, michael@0: -1, michael@0: -1, michael@0: -1, michael@0: -1, michael@0: 1, michael@0: 2, michael@0: 4, michael@0: 6, michael@0: 8, michael@0: 10, michael@0: 13, michael@0: 16 michael@0: ] michael@0: ]; michael@0: var ACPCMStepSizeTable = [ michael@0: 7, michael@0: 8, michael@0: 9, michael@0: 10, michael@0: 11, michael@0: 12, michael@0: 13, michael@0: 14, michael@0: 16, michael@0: 17, michael@0: 19, michael@0: 21, michael@0: 23, michael@0: 25, michael@0: 28, michael@0: 31, michael@0: 34, michael@0: 37, michael@0: 41, michael@0: 45, michael@0: 50, michael@0: 55, michael@0: 60, michael@0: 66, michael@0: 73, michael@0: 80, michael@0: 88, michael@0: 97, michael@0: 107, michael@0: 118, michael@0: 130, michael@0: 143, michael@0: 157, michael@0: 173, michael@0: 190, michael@0: 209, michael@0: 230, michael@0: 253, michael@0: 279, michael@0: 307, michael@0: 337, michael@0: 371, michael@0: 408, michael@0: 449, michael@0: 494, michael@0: 544, michael@0: 598, michael@0: 658, michael@0: 724, michael@0: 796, michael@0: 876, michael@0: 963, michael@0: 1060, michael@0: 1166, michael@0: 1282, michael@0: 1411, michael@0: 1552, michael@0: 1707, michael@0: 1878, michael@0: 2066, michael@0: 2272, michael@0: 2499, michael@0: 2749, michael@0: 3024, michael@0: 3327, michael@0: 3660, michael@0: 4026, michael@0: 4428, michael@0: 4871, michael@0: 5358, michael@0: 5894, michael@0: 6484, michael@0: 7132, michael@0: 7845, michael@0: 8630, michael@0: 9493, michael@0: 10442, michael@0: 11487, michael@0: 12635, michael@0: 13899, michael@0: 15289, michael@0: 16818, michael@0: 18500, michael@0: 20350, michael@0: 22385, michael@0: 24623, michael@0: 27086, michael@0: 29794, michael@0: 32767 michael@0: ]; michael@0: function decodeACPCMSoundData(data, pcm16, channels) { michael@0: function readBits(n, signed) { michael@0: while (dataBufferLength < n) { michael@0: dataBuffer = dataBuffer << 8 | data[dataPosition++]; michael@0: dataBufferLength += 8; michael@0: } michael@0: dataBufferLength -= n; michael@0: return dataBuffer >>> dataBufferLength & (1 << n) - 1; michael@0: } michael@0: var dataPosition = 0; michael@0: var dataBuffer = 0; michael@0: var dataBufferLength = 0; michael@0: var pcmPosition = 0; michael@0: var codeSize = readBits(2); michael@0: var indexTable = ACPCMIndexTables[codeSize]; michael@0: while (pcmPosition < pcm16.length) { michael@0: var x = pcm16[pcmPosition++] = readBits(16) << 16 >> 16, x2; michael@0: var stepIndex = readBits(6), stepIndex2; michael@0: if (channels > 1) { michael@0: x2 = pcm16[pcmPosition++] = readBits(16) << 16 >> 16; michael@0: stepIndex2 = readBits(6); michael@0: } michael@0: var signMask = 1 << codeSize + 1; michael@0: for (var i = 0; i < 4095; i++) { michael@0: var nibble = readBits(codeSize + 2); michael@0: var step = ACPCMStepSizeTable[stepIndex]; michael@0: var sum = 0; michael@0: for (var currentBit = signMask >> 1; currentBit; currentBit >>= 1, step >>= 1) { michael@0: if (nibble & currentBit) michael@0: sum += step; michael@0: } michael@0: x += (nibble & signMask ? -1 : 1) * (sum + step); michael@0: pcm16[pcmPosition++] = x = x < -32768 ? -32768 : x > 32767 ? 32767 : x; michael@0: stepIndex += indexTable[nibble & ~signMask]; michael@0: stepIndex = stepIndex < 0 ? 0 : stepIndex > 88 ? 88 : stepIndex; michael@0: if (channels > 1) { michael@0: nibble = readBits(codeSize + 2); michael@0: step = ACPCMStepSizeTable[stepIndex2]; michael@0: sum = 0; michael@0: for (var currentBit = signMask >> 1; currentBit; currentBit >>= 1, step >>= 1) { michael@0: if (nibble & currentBit) michael@0: sum += step; michael@0: } michael@0: x2 += (nibble & signMask ? -1 : 1) * (sum + step); michael@0: pcm16[pcmPosition++] = x2 = x2 < -32768 ? -32768 : x2 > 32767 ? 32767 : x2; michael@0: stepIndex2 += indexTable[nibble & ~signMask]; michael@0: stepIndex2 = stepIndex2 < 0 ? 0 : stepIndex2 > 88 ? 88 : stepIndex2; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: var nextSoundStreamId = 0; michael@0: function SwfSoundStream(samplesCount, sampleRate, channels) { michael@0: this.streamId = nextSoundStreamId++; michael@0: this.samplesCount = samplesCount; michael@0: this.sampleRate = sampleRate; michael@0: this.channels = channels; michael@0: this.format = null; michael@0: this.currentSample = 0; michael@0: } michael@0: SwfSoundStream.prototype = { michael@0: get info() { michael@0: return { michael@0: samplesCount: this.samplesCount, michael@0: sampleRate: this.sampleRate, michael@0: channels: this.channels, michael@0: format: this.format, michael@0: streamId: this.streamId michael@0: }; michael@0: }, michael@0: decode: function (data) { michael@0: throw new Error('SwfSoundStream.decode: not implemented'); michael@0: } michael@0: }; michael@0: function SwfSoundStream_decode_PCM(data) { michael@0: var pcm = new Float32Array(data.length); michael@0: for (var i = 0; i < pcm.length; i++) michael@0: pcm[i] = (data[i] - 128) / 128; michael@0: this.currentSample += pcm.length / this.channels; michael@0: return { michael@0: streamId: this.streamId, michael@0: samplesCount: pcm.length / this.channels, michael@0: pcm: pcm michael@0: }; michael@0: } michael@0: function SwfSoundStream_decode_PCM_be(data) { michael@0: var pcm = new Float32Array(data.length / 2); michael@0: for (var i = 0, j = 0; i < pcm.length; i++, j += 2) michael@0: pcm[i] = (data[j] << 24 | data[j + 1] << 16) / 2147483648; michael@0: this.currentSample += pcm.length / this.channels; michael@0: return { michael@0: streamId: this.streamId, michael@0: samplesCount: pcm.length / this.channels, michael@0: pcm: pcm michael@0: }; michael@0: } michael@0: function SwfSoundStream_decode_PCM_le(data) { michael@0: var pcm = new Float32Array(data.length / 2); michael@0: for (var i = 0, j = 0; i < pcm.length; i++, j += 2) michael@0: pcm[i] = (data[j + 1] << 24 | data[j] << 16) / 2147483648; michael@0: this.currentSample += pcm.length / this.channels; michael@0: return { michael@0: streamId: this.streamId, michael@0: samplesCount: pcm.length / this.channels, michael@0: pcm: pcm michael@0: }; michael@0: } michael@0: function SwfSoundStream_decode_MP3(data) { michael@0: var samplesCount = data[1] << 8 | data[0]; michael@0: var seek = data[3] << 8 | data[2]; michael@0: this.currentSample += samplesCount; michael@0: return { michael@0: streamId: this.streamId, michael@0: samplesCount: samplesCount, michael@0: data: new Uint8Array(data.subarray(4)), michael@0: seek: seek michael@0: }; michael@0: } michael@0: function createSoundStream(tag) { michael@0: var channels = tag.streamType == SOUND_TYPE_STEREO ? 2 : 1; michael@0: var samplesCount = tag.samplesCount; michael@0: var sampleRate = SOUND_RATES[tag.streamRate]; michael@0: var stream = new SwfSoundStream(samplesCount, sampleRate, channels); michael@0: switch (tag.streamCompression) { michael@0: case SOUND_FORMAT_PCM_BE: michael@0: stream.format = 'wave'; michael@0: if (tag.soundSize == SOUND_SIZE_16_BIT) { michael@0: stream.decode = SwfSoundStream_decode_PCM_be; michael@0: } else { michael@0: stream.decode = SwfSoundStream_decode_PCM; michael@0: } michael@0: break; michael@0: case SOUND_FORMAT_PCM_LE: michael@0: stream.format = 'wave'; michael@0: if (tag.soundSize == SOUND_SIZE_16_BIT) { michael@0: stream.decode = SwfSoundStream_decode_PCM_le; michael@0: } else { michael@0: stream.decode = SwfSoundStream_decode_PCM; michael@0: } michael@0: break; michael@0: case SOUND_FORMAT_MP3: michael@0: stream.format = 'mp3'; michael@0: stream.decode = SwfSoundStream_decode_MP3; michael@0: break; michael@0: default: michael@0: throw new Error('Unsupported audio format: ' + tag.soundFormat); michael@0: } michael@0: return stream; michael@0: } michael@0: function defineText(tag, dictionary) { michael@0: var dependencies = []; michael@0: if (tag.hasFont) { michael@0: var font = dictionary[tag.fontId]; michael@0: tag.font = font.uniqueName; michael@0: dependencies.push(font.id); michael@0: } michael@0: var props = { michael@0: type: 'text', michael@0: id: tag.id, michael@0: variableName: tag.variableName, michael@0: tag: tag michael@0: }; michael@0: if (dependencies.length) michael@0: props.require = dependencies; michael@0: return props; michael@0: } michael@0: var $RELEASE = false; michael@0: var isWorker = typeof window === 'undefined'; michael@0: if (isWorker && !true) { michael@0: importScripts.apply(null, [ michael@0: '../../lib/DataView.js/DataView.js', michael@0: '../flash/util.js', michael@0: 'config.js', michael@0: 'swf.js', michael@0: 'types.js', michael@0: 'structs.js', michael@0: 'tags.js', michael@0: 'inflate.js', michael@0: 'stream.js', michael@0: 'templates.js', michael@0: 'generator.js', michael@0: 'handlers.js', michael@0: 'parser.js', michael@0: 'bitmap.js', michael@0: 'button.js', michael@0: 'font.js', michael@0: 'image.js', michael@0: 'label.js', michael@0: 'shape.js', michael@0: 'sound.js', michael@0: 'text.js' michael@0: ]); michael@0: } michael@0: function defineSymbol(swfTag, symbols) { michael@0: var symbol; michael@0: switch (swfTag.code) { michael@0: case SWF_TAG_CODE_DEFINE_BITS: michael@0: case SWF_TAG_CODE_DEFINE_BITS_JPEG2: michael@0: case SWF_TAG_CODE_DEFINE_BITS_JPEG3: michael@0: case SWF_TAG_CODE_DEFINE_BITS_JPEG4: michael@0: case SWF_TAG_CODE_JPEG_TABLES: michael@0: symbol = defineImage(swfTag, symbols); michael@0: break; michael@0: case SWF_TAG_CODE_DEFINE_BITS_LOSSLESS: michael@0: case SWF_TAG_CODE_DEFINE_BITS_LOSSLESS2: michael@0: symbol = defineBitmap(swfTag); michael@0: break; michael@0: case SWF_TAG_CODE_DEFINE_BUTTON: michael@0: case SWF_TAG_CODE_DEFINE_BUTTON2: michael@0: symbol = defineButton(swfTag, symbols); michael@0: break; michael@0: case SWF_TAG_CODE_DEFINE_EDIT_TEXT: michael@0: symbol = defineText(swfTag, symbols); michael@0: break; michael@0: case SWF_TAG_CODE_DEFINE_FONT: michael@0: case SWF_TAG_CODE_DEFINE_FONT2: michael@0: case SWF_TAG_CODE_DEFINE_FONT3: michael@0: case SWF_TAG_CODE_DEFINE_FONT4: michael@0: symbol = defineFont(swfTag, symbols); michael@0: break; michael@0: case SWF_TAG_CODE_DEFINE_MORPH_SHAPE: michael@0: case SWF_TAG_CODE_DEFINE_MORPH_SHAPE2: michael@0: case SWF_TAG_CODE_DEFINE_SHAPE: michael@0: case SWF_TAG_CODE_DEFINE_SHAPE2: michael@0: case SWF_TAG_CODE_DEFINE_SHAPE3: michael@0: case SWF_TAG_CODE_DEFINE_SHAPE4: michael@0: symbol = defineShape(swfTag, symbols); michael@0: break; michael@0: case SWF_TAG_CODE_DEFINE_SOUND: michael@0: symbol = defineSound(swfTag, symbols); michael@0: break; michael@0: case SWF_TAG_CODE_DEFINE_BINARY_DATA: michael@0: symbol = { michael@0: type: 'binary', michael@0: id: swfTag.id, michael@0: data: swfTag.data michael@0: }; michael@0: break; michael@0: case SWF_TAG_CODE_DEFINE_SPRITE: michael@0: var depths = {}; michael@0: var frame = { michael@0: type: 'frame' michael@0: }; michael@0: var frames = []; michael@0: var tags = swfTag.tags; michael@0: var frameScripts = null; michael@0: var frameIndex = 0; michael@0: var soundStream = null; michael@0: for (var i = 0, n = tags.length; i < n; i++) { michael@0: var tag = tags[i]; michael@0: switch (tag.code) { michael@0: case SWF_TAG_CODE_DO_ACTION: michael@0: if (!frameScripts) michael@0: frameScripts = []; michael@0: frameScripts.push(frameIndex); michael@0: frameScripts.push(tag.actionsData); michael@0: break; michael@0: case SWF_TAG_CODE_START_SOUND: michael@0: var startSounds = frame.startSounds || (frame.startSounds = []); michael@0: startSounds.push(tag); michael@0: break; michael@0: case SWF_TAG_CODE_SOUND_STREAM_HEAD: michael@0: try { michael@0: soundStream = createSoundStream(tag); michael@0: frame.soundStream = soundStream.info; michael@0: } catch (e) { michael@0: } michael@0: break; michael@0: case SWF_TAG_CODE_SOUND_STREAM_BLOCK: michael@0: if (soundStream) { michael@0: frame.soundStreamBlock = soundStream.decode(tag.data); michael@0: } michael@0: break; michael@0: case SWF_TAG_CODE_FRAME_LABEL: michael@0: frame.labelName = tag.name; michael@0: break; michael@0: case SWF_TAG_CODE_PLACE_OBJECT: michael@0: case SWF_TAG_CODE_PLACE_OBJECT2: michael@0: case SWF_TAG_CODE_PLACE_OBJECT3: michael@0: depths[tag.depth] = tag; michael@0: break; michael@0: case SWF_TAG_CODE_REMOVE_OBJECT: michael@0: case SWF_TAG_CODE_REMOVE_OBJECT2: michael@0: depths[tag.depth] = null; michael@0: break; michael@0: case SWF_TAG_CODE_SHOW_FRAME: michael@0: frameIndex += tag.repeat; michael@0: frame.repeat = tag.repeat; michael@0: frame.depths = depths; michael@0: frames.push(frame); michael@0: depths = {}; michael@0: frame = { michael@0: type: 'frame' michael@0: }; michael@0: break; michael@0: } michael@0: } michael@0: symbol = { michael@0: type: 'sprite', michael@0: id: swfTag.id, michael@0: frameCount: swfTag.frameCount, michael@0: frames: frames, michael@0: frameScripts: frameScripts michael@0: }; michael@0: break; michael@0: case SWF_TAG_CODE_DEFINE_TEXT: michael@0: case SWF_TAG_CODE_DEFINE_TEXT2: michael@0: symbol = defineLabel(swfTag, symbols); michael@0: break; michael@0: } michael@0: if (!symbol) { michael@0: return { michael@0: command: 'error', michael@0: message: 'unknown symbol type: ' + swfTag.code michael@0: }; michael@0: } michael@0: symbol.isSymbol = true; michael@0: symbols[swfTag.id] = symbol; michael@0: return symbol; michael@0: } michael@0: function createParsingContext(commitData) { michael@0: var depths = {}; michael@0: var symbols = {}; michael@0: var frame = { michael@0: type: 'frame' michael@0: }; michael@0: var tagsProcessed = 0; michael@0: var soundStream = null; michael@0: var lastProgressSent = 0; michael@0: return { michael@0: onstart: function (result) { michael@0: commitData({ michael@0: command: 'init', michael@0: result: result michael@0: }); michael@0: }, michael@0: onprogress: function (result) { michael@0: if (Date.now() - lastProgressSent > 1000 / 24 || result.bytesLoaded === result.bytesTotal) { michael@0: commitData({ michael@0: command: 'progress', michael@0: result: { michael@0: bytesLoaded: result.bytesLoaded, michael@0: bytesTotal: result.bytesTotal michael@0: } michael@0: }); michael@0: lastProgressSent = Date.now(); michael@0: } michael@0: var tags = result.tags; michael@0: for (var n = tags.length; tagsProcessed < n; tagsProcessed++) { michael@0: var tag = tags[tagsProcessed]; michael@0: if ('id' in tag) { michael@0: var symbol = defineSymbol(tag, symbols); michael@0: commitData(symbol, symbol.transferables); michael@0: continue; michael@0: } michael@0: switch (tag.code) { michael@0: case SWF_TAG_CODE_DEFINE_SCENE_AND_FRAME_LABEL_DATA: michael@0: frame.sceneData = tag; michael@0: break; michael@0: case SWF_TAG_CODE_DEFINE_SCALING_GRID: michael@0: var symbolUpdate = { michael@0: isSymbol: true, michael@0: id: tag.symbolId, michael@0: updates: { michael@0: scale9Grid: tag.splitter michael@0: } michael@0: }; michael@0: commitData(symbolUpdate); michael@0: break; michael@0: case SWF_TAG_CODE_DO_ABC: michael@0: case SWF_TAG_CODE_DO_ABC_: michael@0: var abcBlocks = frame.abcBlocks; michael@0: if (abcBlocks) michael@0: abcBlocks.push({ michael@0: data: tag.data, michael@0: flags: tag.flags michael@0: }); michael@0: else michael@0: frame.abcBlocks = [ michael@0: { michael@0: data: tag.data, michael@0: flags: tag.flags michael@0: } michael@0: ]; michael@0: break; michael@0: case SWF_TAG_CODE_DO_ACTION: michael@0: var actionBlocks = frame.actionBlocks; michael@0: if (actionBlocks) michael@0: actionBlocks.push(tag.actionsData); michael@0: else michael@0: frame.actionBlocks = [ michael@0: tag.actionsData michael@0: ]; michael@0: break; michael@0: case SWF_TAG_CODE_DO_INIT_ACTION: michael@0: var initActionBlocks = frame.initActionBlocks || (frame.initActionBlocks = []); michael@0: initActionBlocks.push({ michael@0: spriteId: tag.spriteId, michael@0: actionsData: tag.actionsData michael@0: }); michael@0: break; michael@0: case SWF_TAG_CODE_START_SOUND: michael@0: var startSounds = frame.startSounds; michael@0: if (!startSounds) michael@0: frame.startSounds = startSounds = []; michael@0: startSounds.push(tag); michael@0: break; michael@0: case SWF_TAG_CODE_SOUND_STREAM_HEAD: michael@0: try { michael@0: soundStream = createSoundStream(tag); michael@0: frame.soundStream = soundStream.info; michael@0: } catch (e) { michael@0: } michael@0: break; michael@0: case SWF_TAG_CODE_SOUND_STREAM_BLOCK: michael@0: if (soundStream) { michael@0: frame.soundStreamBlock = soundStream.decode(tag.data); michael@0: } michael@0: break; michael@0: case SWF_TAG_CODE_EXPORT_ASSETS: michael@0: var exports = frame.exports; michael@0: if (exports) michael@0: frame.exports = exports.concat(tag.exports); michael@0: else michael@0: frame.exports = tag.exports.slice(0); michael@0: break; michael@0: case SWF_TAG_CODE_SYMBOL_CLASS: michael@0: var symbolClasses = frame.symbolClasses; michael@0: if (symbolClasses) michael@0: frame.symbolClasses = symbolClasses.concat(tag.exports); michael@0: else michael@0: frame.symbolClasses = tag.exports.slice(0); michael@0: break; michael@0: case SWF_TAG_CODE_FRAME_LABEL: michael@0: frame.labelName = tag.name; michael@0: break; michael@0: case SWF_TAG_CODE_PLACE_OBJECT: michael@0: case SWF_TAG_CODE_PLACE_OBJECT2: michael@0: case SWF_TAG_CODE_PLACE_OBJECT3: michael@0: depths[tag.depth] = tag; michael@0: break; michael@0: case SWF_TAG_CODE_REMOVE_OBJECT: michael@0: case SWF_TAG_CODE_REMOVE_OBJECT2: michael@0: depths[tag.depth] = null; michael@0: break; michael@0: case SWF_TAG_CODE_SET_BACKGROUND_COLOR: michael@0: frame.bgcolor = tag.color; michael@0: break; michael@0: case SWF_TAG_CODE_SHOW_FRAME: michael@0: frame.repeat = tag.repeat; michael@0: frame.depths = depths; michael@0: frame.complete = !(!tag.finalTag); michael@0: commitData(frame); michael@0: depths = {}; michael@0: frame = { michael@0: type: 'frame' michael@0: }; michael@0: break; michael@0: } michael@0: } michael@0: }, michael@0: oncomplete: function (result) { michael@0: commitData(result); michael@0: var stats; michael@0: if (typeof result.swfVersion === 'number') { michael@0: var bbox = result.bbox; michael@0: stats = { michael@0: topic: 'parseInfo', michael@0: parseTime: result.parseTime, michael@0: bytesTotal: result.bytesTotal, michael@0: swfVersion: result.swfVersion, michael@0: frameRate: result.frameRate, michael@0: width: (bbox.xMax - bbox.xMin) / 20, michael@0: height: (bbox.yMax - bbox.yMin) / 20, michael@0: isAvm2: !(!result.fileAttributes.doAbc) michael@0: }; michael@0: } michael@0: commitData({ michael@0: command: 'complete', michael@0: stats: stats michael@0: }); michael@0: }, michael@0: onexception: function (e) { michael@0: commitData({ michael@0: type: 'exception', michael@0: message: e.message, michael@0: stack: e.stack michael@0: }); michael@0: } michael@0: }; michael@0: } michael@0: function parseBytes(bytes, commitData) { michael@0: SWF.parse(bytes, createParsingContext(commitData)); michael@0: } michael@0: function ResourceLoader(scope) { michael@0: this.subscription = null; michael@0: var self = this; michael@0: if (!isWorker) { michael@0: this.messenger = { michael@0: postMessage: function (data) { michael@0: self.onmessage({ michael@0: data: data michael@0: }); michael@0: } michael@0: }; michael@0: } else { michael@0: this.messenger = scope; michael@0: scope.onmessage = function (event) { michael@0: self.listener(event.data); michael@0: }; michael@0: } michael@0: } michael@0: ResourceLoader.prototype = { michael@0: terminate: function () { michael@0: this.messenger = null; michael@0: this.listener = null; michael@0: }, michael@0: onmessage: function (event) { michael@0: this.listener(event.data); michael@0: }, michael@0: postMessage: function (data) { michael@0: this.listener && this.listener(data); michael@0: }, michael@0: listener: function (data) { michael@0: if (this.subscription) { michael@0: this.subscription.callback(data.data, data.progress); michael@0: } else if (data === 'pipe:') { michael@0: this.subscription = { michael@0: subscribe: function (callback) { michael@0: this.callback = callback; michael@0: } michael@0: }; michael@0: this.parseLoadedData(this.messenger, this.subscription); michael@0: } else { michael@0: this.parseLoadedData(this.messenger, data); michael@0: } michael@0: }, michael@0: parseLoadedData: function (loader, request, context) { michael@0: function commitData(data, transferables) { michael@0: try { michael@0: loader.postMessage(data, transferables); michael@0: } catch (ex) { michael@0: if (ex != 'DataCloneError') { michael@0: throw ex; michael@0: } michael@0: loader.postMessage(data); michael@0: } michael@0: } michael@0: if (request instanceof ArrayBuffer) { michael@0: parseBytes(request, commitData); michael@0: } else if ('subscribe' in request) { michael@0: var pipe = SWF.parseAsync(createParsingContext(commitData)); michael@0: request.subscribe(function (data, progress) { michael@0: if (data) { michael@0: pipe.push(data, progress); michael@0: } else { michael@0: pipe.close(); michael@0: } michael@0: }); michael@0: } else if (typeof FileReaderSync !== 'undefined') { michael@0: var reader = new FileReaderSync(); michael@0: var buffer = reader.readAsArrayBuffer(request); michael@0: parseBytes(buffer, commitData); michael@0: } else { michael@0: var reader = new FileReader(); michael@0: reader.onload = function () { michael@0: parseBytes(this.result, commitData); michael@0: }; michael@0: reader.readAsArrayBuffer(request); michael@0: } michael@0: } michael@0: }; michael@0: if (isWorker) { michael@0: var loader = new ResourceLoader(this); michael@0: } michael@0: var codeLengthOrder = [ michael@0: 16, michael@0: 17, michael@0: 18, michael@0: 0, michael@0: 8, michael@0: 7, michael@0: 9, michael@0: 6, michael@0: 10, michael@0: 5, michael@0: 11, michael@0: 4, michael@0: 12, michael@0: 3, michael@0: 13, michael@0: 2, michael@0: 14, michael@0: 1, michael@0: 15 michael@0: ]; michael@0: var distanceCodes = []; michael@0: var distanceExtraBits = []; michael@0: for (var i = 0, j = 0, code = 1; i < 30; ++i) { michael@0: distanceCodes[i] = code; michael@0: code += 1 << (distanceExtraBits[i] = ~(~((j += i > 2 ? 1 : 0) / 2))); michael@0: } michael@0: var bitLengths = []; michael@0: for (var i = 0; i < 32; ++i) michael@0: bitLengths[i] = 5; michael@0: var fixedDistanceTable = makeHuffmanTable(bitLengths); michael@0: var lengthCodes = []; michael@0: var lengthExtraBits = []; michael@0: for (var i = 0, j = 0, code = 3; i < 29; ++i) { michael@0: lengthCodes[i] = code - (i == 28 ? 1 : 0); michael@0: code += 1 << (lengthExtraBits[i] = ~(~((j += i > 4 ? 1 : 0) / 4 % 6))); michael@0: } michael@0: for (var i = 0; i < 288; ++i) michael@0: bitLengths[i] = i < 144 || i > 279 ? 8 : i < 256 ? 9 : 7; michael@0: var fixedLiteralTable = makeHuffmanTable(bitLengths); michael@0: function makeHuffmanTable(bitLengths) { michael@0: var maxBits = Math.max.apply(null, bitLengths); michael@0: var numLengths = bitLengths.length; michael@0: var size = 1 << maxBits; michael@0: var codes = new Uint32Array(size); michael@0: for (var code = 0, len = 1, skip = 2; len <= maxBits; code <<= 1, ++len, skip <<= 1) { michael@0: for (var val = 0; val < numLengths; ++val) { michael@0: if (bitLengths[val] === len) { michael@0: var lsb = 0; michael@0: for (var i = 0; i < len; ++i) michael@0: lsb = lsb * 2 + (code >> i & 1); michael@0: for (var i = lsb; i < size; i += skip) michael@0: codes[i] = len << 16 | val; michael@0: ++code; michael@0: } michael@0: } michael@0: } michael@0: return { michael@0: codes: codes, michael@0: maxBits: maxBits michael@0: }; michael@0: } michael@0: function verifyDeflateHeader(bytes) { michael@0: var header = bytes[0] << 8 | bytes[1]; michael@0: } michael@0: function createInflatedStream(bytes, outputLength) { michael@0: verifyDeflateHeader(bytes); michael@0: var stream = new Stream(bytes, 2); michael@0: var output = { michael@0: data: new Uint8Array(outputLength), michael@0: available: 0, michael@0: completed: false michael@0: }; michael@0: var state = { michael@0: header: null, michael@0: distanceTable: null, michael@0: literalTable: null, michael@0: sym: null, michael@0: len: null, michael@0: sym2: null michael@0: }; michael@0: do { michael@0: inflateBlock(stream, output, state); michael@0: } while (!output.completed && stream.pos < stream.end); michael@0: return new Stream(output.data, 0, output.available); michael@0: } michael@0: var InflateNoDataError = {}; michael@0: function inflateBlock(stream, output, state) { michael@0: var header = state.header !== null ? state.header : state.header = readBits(stream.bytes, stream, 3); michael@0: switch (header >> 1) { michael@0: case 0: michael@0: stream.align(); michael@0: var pos = stream.pos; michael@0: if (stream.end - pos < 4) { michael@0: throw InflateNoDataError; michael@0: } michael@0: var len = stream.getUint16(pos, true); michael@0: var nlen = stream.getUint16(pos + 2, true); michael@0: if (stream.end - pos < 4 + len) { michael@0: throw InflateNoDataError; michael@0: } michael@0: var begin = pos + 4; michael@0: var end = stream.pos = begin + len; michael@0: var sbytes = stream.bytes, dbytes = output.data; michael@0: dbytes.set(sbytes.subarray(begin, end), output.available); michael@0: output.available += len; michael@0: break; michael@0: case 1: michael@0: inflate(stream, output, fixedLiteralTable, fixedDistanceTable, state); michael@0: break; michael@0: case 2: michael@0: var distanceTable, literalTable; michael@0: if (state.distanceTable !== null) { michael@0: distanceTable = state.distanceTable; michael@0: literalTable = state.literalTable; michael@0: } else { michael@0: var sbytes = stream.bytes; michael@0: var savedBufferPos = stream.pos; michael@0: var savedBitBuffer = stream.bitBuffer; michael@0: var savedBitLength = stream.bitLength; michael@0: var bitLengths = []; michael@0: var numLiteralCodes, numDistanceCodes; michael@0: try { michael@0: numLiteralCodes = readBits(sbytes, stream, 5) + 257; michael@0: numDistanceCodes = readBits(sbytes, stream, 5) + 1; michael@0: var numCodes = numLiteralCodes + numDistanceCodes; michael@0: var numLengthCodes = readBits(sbytes, stream, 4) + 4; michael@0: for (var i = 0; i < 19; ++i) michael@0: bitLengths[codeLengthOrder[i]] = i < numLengthCodes ? readBits(sbytes, stream, 3) : 0; michael@0: var codeLengthTable = makeHuffmanTable(bitLengths); michael@0: bitLengths = []; michael@0: var i = 0; michael@0: var prev = 0; michael@0: while (i < numCodes) { michael@0: var j = 1; michael@0: var sym = readCode(sbytes, stream, codeLengthTable); michael@0: switch (sym) { michael@0: case 16: michael@0: j = readBits(sbytes, stream, 2) + 3; michael@0: sym = prev; michael@0: break; michael@0: case 17: michael@0: j = readBits(sbytes, stream, 3) + 3; michael@0: sym = 0; michael@0: break; michael@0: case 18: michael@0: j = readBits(sbytes, stream, 7) + 11; michael@0: sym = 0; michael@0: break; michael@0: default: michael@0: prev = sym; michael@0: } michael@0: while (j--) michael@0: bitLengths[i++] = sym; michael@0: } michael@0: } catch (e) { michael@0: stream.pos = savedBufferPos; michael@0: stream.bitBuffer = savedBitBuffer; michael@0: stream.bitLength = savedBitLength; michael@0: throw e; michael@0: } michael@0: distanceTable = state.distanceTable = makeHuffmanTable(bitLengths.splice(numLiteralCodes, numDistanceCodes)); michael@0: literalTable = state.literalTable = makeHuffmanTable(bitLengths); michael@0: } michael@0: inflate(stream, output, literalTable, distanceTable, state); michael@0: state.distanceTable = null; michael@0: state.literalTable = null; michael@0: break; michael@0: default: michael@0: fail('unknown block type', 'inflate'); michael@0: } michael@0: state.header = null; michael@0: output.completed = !(!(header & 1)); michael@0: } michael@0: function readBits(bytes, stream, size) { michael@0: var bitBuffer = stream.bitBuffer; michael@0: var bitLength = stream.bitLength; michael@0: if (size > bitLength) { michael@0: var pos = stream.pos; michael@0: var end = stream.end; michael@0: do { michael@0: if (pos >= end) { michael@0: stream.pos = pos; michael@0: stream.bitBuffer = bitBuffer; michael@0: stream.bitLength = bitLength; michael@0: throw InflateNoDataError; michael@0: } michael@0: bitBuffer |= bytes[pos++] << bitLength; michael@0: bitLength += 8; michael@0: } while (size > bitLength); michael@0: stream.pos = pos; michael@0: } michael@0: stream.bitBuffer = bitBuffer >>> size; michael@0: stream.bitLength = bitLength - size; michael@0: return bitBuffer & (1 << size) - 1; michael@0: } michael@0: function inflate(stream, output, literalTable, distanceTable, state) { michael@0: var pos = output.available; michael@0: var dbytes = output.data; michael@0: var sbytes = stream.bytes; michael@0: var sym = state.sym !== null ? state.sym : readCode(sbytes, stream, literalTable); michael@0: while (sym !== 256) { michael@0: if (sym < 256) { michael@0: dbytes[pos++] = sym; michael@0: } else { michael@0: state.sym = sym; michael@0: sym -= 257; michael@0: var len = state.len !== null ? state.len : state.len = lengthCodes[sym] + readBits(sbytes, stream, lengthExtraBits[sym]); michael@0: var sym2 = state.sym2 !== null ? state.sym2 : state.sym2 = readCode(sbytes, stream, distanceTable); michael@0: var distance = distanceCodes[sym2] + readBits(sbytes, stream, distanceExtraBits[sym2]); michael@0: var i = pos - distance; michael@0: while (len--) michael@0: dbytes[pos++] = dbytes[i++]; michael@0: state.sym2 = null; michael@0: state.len = null; michael@0: state.sym = null; michael@0: } michael@0: output.available = pos; michael@0: sym = readCode(sbytes, stream, literalTable); michael@0: } michael@0: } michael@0: function readCode(bytes, stream, codeTable) { michael@0: var bitBuffer = stream.bitBuffer; michael@0: var bitLength = stream.bitLength; michael@0: var maxBits = codeTable.maxBits; michael@0: if (maxBits > bitLength) { michael@0: var pos = stream.pos; michael@0: var end = stream.end; michael@0: do { michael@0: if (pos >= end) { michael@0: stream.pos = pos; michael@0: stream.bitBuffer = bitBuffer; michael@0: stream.bitLength = bitLength; michael@0: throw InflateNoDataError; michael@0: } michael@0: bitBuffer |= bytes[pos++] << bitLength; michael@0: bitLength += 8; michael@0: } while (maxBits > bitLength); michael@0: stream.pos = pos; michael@0: } michael@0: var code = codeTable.codes[bitBuffer & (1 << maxBits) - 1]; michael@0: var len = code >> 16; michael@0: stream.bitBuffer = bitBuffer >>> len; michael@0: stream.bitLength = bitLength - len; michael@0: return code & 65535; michael@0: } michael@0: (function (global) { michael@0: global['createInflatedStream'] = createInflatedStream; michael@0: }(this)); michael@0: var StreamNoDataError = {}; michael@0: var Stream = function StreamClosure() { michael@0: function Stream_align() { michael@0: this.bitBuffer = this.bitLength = 0; michael@0: } michael@0: function Stream_ensure(size) { michael@0: if (this.pos + size > this.end) { michael@0: throw StreamNoDataError; michael@0: } michael@0: } michael@0: function Stream_remaining() { michael@0: return this.end - this.pos; michael@0: } michael@0: function Stream_substream(begin, end) { michael@0: var stream = new Stream(this.bytes); michael@0: stream.pos = begin; michael@0: stream.end = end; michael@0: return stream; michael@0: } michael@0: function Stream_push(data) { michael@0: var bytes = this.bytes; michael@0: var newBytesLength = this.end + data.length; michael@0: if (newBytesLength > bytes.length) { michael@0: throw 'stream buffer overfow'; michael@0: } michael@0: bytes.set(data, this.end); michael@0: this.end = newBytesLength; michael@0: } michael@0: function Stream(buffer, offset, length, maxLength) { michael@0: if (offset === undefined) michael@0: offset = 0; michael@0: if (buffer.buffer instanceof ArrayBuffer) { michael@0: offset += buffer.byteOffset; michael@0: buffer = buffer.buffer; michael@0: } michael@0: if (length === undefined) michael@0: length = buffer.byteLength - offset; michael@0: if (maxLength === undefined) michael@0: maxLength = length; michael@0: var bytes = new Uint8Array(buffer, offset, maxLength); michael@0: var stream = new DataView(buffer, offset, maxLength); michael@0: stream.bytes = bytes; michael@0: stream.pos = 0; michael@0: stream.end = length; michael@0: stream.bitBuffer = 0; michael@0: stream.bitLength = 0; michael@0: stream.align = Stream_align; michael@0: stream.ensure = Stream_ensure; michael@0: stream.remaining = Stream_remaining; michael@0: stream.substream = Stream_substream; michael@0: stream.push = Stream_push; michael@0: return stream; michael@0: } michael@0: return Stream; michael@0: }(); michael@0: (function (global) { michael@0: global['Stream'] = Stream; michael@0: }(this)); michael@0: function readSi8($bytes, $stream) { michael@0: return $stream.getInt8($stream.pos++); michael@0: } michael@0: function readSi16($bytes, $stream) { michael@0: return $stream.getInt16($stream.pos, $stream.pos += 2); michael@0: } michael@0: function readSi32($bytes, $stream) { michael@0: return $stream.getInt32($stream.pos, $stream.pos += 4); michael@0: } michael@0: function readUi8($bytes, $stream) { michael@0: return $bytes[$stream.pos++]; michael@0: } michael@0: function readUi16($bytes, $stream) { michael@0: return $stream.getUint16($stream.pos, $stream.pos += 2); michael@0: } michael@0: function readUi32($bytes, $stream) { michael@0: return $stream.getUint32($stream.pos, $stream.pos += 4); michael@0: } michael@0: function readFixed($bytes, $stream) { michael@0: return $stream.getInt32($stream.pos, $stream.pos += 4) / 65536; michael@0: } michael@0: function readFixed8($bytes, $stream) { michael@0: return $stream.getInt16($stream.pos, $stream.pos += 2) / 256; michael@0: } michael@0: function readFloat16($bytes, $stream) { michael@0: var ui16 = $stream.getUint16($stream.pos); michael@0: $stream.pos += 2; michael@0: var sign = ui16 >> 15 ? -1 : 1; michael@0: var exponent = (ui16 & 31744) >> 10; michael@0: var fraction = ui16 & 1023; michael@0: if (!exponent) michael@0: return sign * pow(2, -14) * (fraction / 1024); michael@0: if (exponent === 31) michael@0: return fraction ? NaN : sign * Infinity; michael@0: return sign * pow(2, exponent - 15) * (1 + fraction / 1024); michael@0: } michael@0: function readFloat($bytes, $stream) { michael@0: return $stream.getFloat32($stream.pos, $stream.pos += 4); michael@0: } michael@0: function readDouble($bytes, $stream) { michael@0: return $stream.getFloat64($stream.pos, $stream.pos += 8); michael@0: } michael@0: function readEncodedU32($bytes, $stream) { michael@0: var val = $bytes[$stream.pos++]; michael@0: if (!(val & 128)) michael@0: return val; michael@0: val |= $bytes[$stream.pos++] << 7; michael@0: if (!(val & 16384)) michael@0: return val; michael@0: val |= $bytes[$stream.pos++] << 14; michael@0: if (!(val & 2097152)) michael@0: return val; michael@0: val |= $bytes[$stream.pos++] << 21; michael@0: if (!(val & 268435456)) michael@0: return val; michael@0: return val | $bytes[$stream.pos++] << 28; michael@0: } michael@0: function readBool($bytes, $stream) { michael@0: return !(!$bytes[$stream.pos++]); michael@0: } michael@0: function align($bytes, $stream) { michael@0: $stream.align(); michael@0: } michael@0: function readSb($bytes, $stream, size) { michael@0: return readUb($bytes, $stream, size) << 32 - size >> 32 - size; michael@0: } michael@0: var masks = new Uint32Array(33); michael@0: for (var i = 1, mask = 0; i <= 32; ++i) michael@0: masks[i] = mask = mask << 1 | 1; michael@0: function readUb($bytes, $stream, size) { michael@0: var buffer = $stream.bitBuffer; michael@0: var bitlen = $stream.bitLength; michael@0: while (size > bitlen) { michael@0: buffer = buffer << 8 | $bytes[$stream.pos++]; michael@0: bitlen += 8; michael@0: } michael@0: bitlen -= size; michael@0: var val = buffer >>> bitlen & masks[size]; michael@0: $stream.bitBuffer = buffer; michael@0: $stream.bitLength = bitlen; michael@0: return val; michael@0: } michael@0: function readFb($bytes, $stream, size) { michael@0: return readSb($bytes, $stream, size) / 65536; michael@0: } michael@0: function readString($bytes, $stream, length) { michael@0: var codes = []; michael@0: var pos = $stream.pos; michael@0: if (length) { michael@0: codes = slice.call($bytes, pos, pos += length); michael@0: } else { michael@0: length = 0; michael@0: for (var code; code = $bytes[pos++]; length++) michael@0: codes[length] = code; michael@0: } michael@0: $stream.pos = pos; michael@0: var numChunks = length / 65536; michael@0: var str = ''; michael@0: for (var i = 0; i < numChunks; ++i) { michael@0: var begin = i * 65536; michael@0: var end = begin + 65536; michael@0: var chunk = codes.slice(begin, end); michael@0: str += fromCharCode.apply(null, chunk); michael@0: } michael@0: return decodeURIComponent(escape(str.replace('\0', '', 'g'))); michael@0: } michael@0: function readBinary($bytes, $stream, size) { michael@0: return $bytes.subarray($stream.pos, $stream.pos = size ? $stream.pos + size : $stream.end); michael@0: } michael@0: (function (global) { michael@0: global['readSi8'] = readSi8; michael@0: global['readUi16'] = readUi16; michael@0: global['readUi32'] = readUi32; michael@0: }(this)); michael@0: var tagHandler = function (global) { michael@0: function defineShape($bytes, $stream, $, swfVersion, tagCode) { michael@0: $ || ($ = {}); michael@0: $.id = readUi16($bytes, $stream); michael@0: var $0 = $.bbox = {}; michael@0: bbox($bytes, $stream, $0, swfVersion, tagCode); michael@0: var isMorph = $.isMorph = tagCode === 46 || tagCode === 84; michael@0: if (isMorph) { michael@0: var $1 = $.bboxMorph = {}; michael@0: bbox($bytes, $stream, $1, swfVersion, tagCode); michael@0: } michael@0: var hasStrokes = $.hasStrokes = tagCode === 83 || tagCode === 84; michael@0: if (hasStrokes) { michael@0: var $2 = $.strokeBbox = {}; michael@0: bbox($bytes, $stream, $2, swfVersion, tagCode); michael@0: if (isMorph) { michael@0: var $3 = $.strokeBboxMorph = {}; michael@0: bbox($bytes, $stream, $3, swfVersion, tagCode); michael@0: } michael@0: var reserved = readUb($bytes, $stream, 5); michael@0: $.fillWinding = readUb($bytes, $stream, 1); michael@0: $.nonScalingStrokes = readUb($bytes, $stream, 1); michael@0: $.scalingStrokes = readUb($bytes, $stream, 1); michael@0: } michael@0: if (isMorph) { michael@0: $.offsetMorph = readUi32($bytes, $stream); michael@0: morphShapeWithStyle($bytes, $stream, $, swfVersion, tagCode, isMorph, hasStrokes); michael@0: } else { michael@0: shapeWithStyle($bytes, $stream, $, swfVersion, tagCode, isMorph, hasStrokes); michael@0: } michael@0: return $; michael@0: } michael@0: function placeObject($bytes, $stream, $, swfVersion, tagCode) { michael@0: var flags, hasEvents, clip, hasName, hasRatio, hasCxform, hasMatrix, place; michael@0: var move, hasBackgroundColor, hasVisibility, hasImage, hasClassName, cache; michael@0: var blend, hasFilters, eoe; michael@0: $ || ($ = {}); michael@0: if (tagCode > 4) { michael@0: if (tagCode > 26) { michael@0: flags = readUi16($bytes, $stream); michael@0: } else { michael@0: flags = readUi8($bytes, $stream); michael@0: } michael@0: hasEvents = $.hasEvents = flags >> 7 & 1; michael@0: clip = $.clip = flags >> 6 & 1; michael@0: hasName = $.hasName = flags >> 5 & 1; michael@0: hasRatio = $.hasRatio = flags >> 4 & 1; michael@0: hasCxform = $.hasCxform = flags >> 3 & 1; michael@0: hasMatrix = $.hasMatrix = flags >> 2 & 1; michael@0: place = $.place = flags >> 1 & 1; michael@0: move = $.move = flags & 1; michael@0: if (tagCode === 70) { michael@0: hasBackgroundColor = $.hasBackgroundColor = flags >> 15 & 1; michael@0: hasVisibility = $.hasVisibility = flags >> 14 & 1; michael@0: hasImage = $.hasImage = flags >> 12 & 1; michael@0: hasClassName = $.hasClassName = flags >> 11 & 1; michael@0: cache = $.cache = flags >> 10 & 1; michael@0: blend = $.blend = flags >> 9 & 1; michael@0: hasFilters = $.hasFilters = flags >> 8 & 1; michael@0: } else { michael@0: cache = $.cache = 0; michael@0: blend = $.blend = 0; michael@0: hasFilters = $.hasFilters = 0; michael@0: } michael@0: $.depth = readUi16($bytes, $stream); michael@0: if (hasClassName) { michael@0: $.className = readString($bytes, $stream, 0); michael@0: } michael@0: if (place) { michael@0: $.symbolId = readUi16($bytes, $stream); michael@0: } michael@0: if (hasMatrix) { michael@0: var $0 = $.matrix = {}; michael@0: matrix($bytes, $stream, $0, swfVersion, tagCode); michael@0: } michael@0: if (hasCxform) { michael@0: var $1 = $.cxform = {}; michael@0: cxform($bytes, $stream, $1, swfVersion, tagCode); michael@0: } michael@0: if (hasRatio) { michael@0: $.ratio = readUi16($bytes, $stream); michael@0: } michael@0: if (hasName) { michael@0: $.name = readString($bytes, $stream, 0); michael@0: } michael@0: if (clip) { michael@0: $.clipDepth = readUi16($bytes, $stream); michael@0: } michael@0: if (hasFilters) { michael@0: var count = readUi8($bytes, $stream); michael@0: var $2 = $.filters = []; michael@0: var $3 = count; michael@0: while ($3--) { michael@0: var $4 = {}; michael@0: anyFilter($bytes, $stream, $4, swfVersion, tagCode); michael@0: $2.push($4); michael@0: } michael@0: } michael@0: if (blend) { michael@0: $.blendMode = readUi8($bytes, $stream); michael@0: } michael@0: if (cache) { michael@0: $.bmpCache = readUi8($bytes, $stream); michael@0: } michael@0: if (hasEvents) { michael@0: var reserved = readUi16($bytes, $stream); michael@0: if (swfVersion >= 6) { michael@0: var allFlags = readUi32($bytes, $stream); michael@0: } else { michael@0: var allFlags = readUi16($bytes, $stream); michael@0: } michael@0: var $28 = $.events = []; michael@0: do { michael@0: var $29 = {}; michael@0: var temp = events($bytes, $stream, $29, swfVersion, tagCode); michael@0: eoe = temp.eoe; michael@0: $28.push($29); michael@0: } while (!eoe); michael@0: } michael@0: if (hasBackgroundColor) { michael@0: var $126 = $.backgroundColor = {}; michael@0: argb($bytes, $stream, $126, swfVersion, tagCode); michael@0: } michael@0: if (hasVisibility) { michael@0: $.visibility = readUi8($bytes, $stream); michael@0: } michael@0: } else { michael@0: $.place = 1; michael@0: $.symbolId = readUi16($bytes, $stream); michael@0: $.depth = readUi16($bytes, $stream); michael@0: $.hasMatrix = 1; michael@0: var $30 = $.matrix = {}; michael@0: matrix($bytes, $stream, $30, swfVersion, tagCode); michael@0: if ($stream.remaining()) { michael@0: $.hasCxform = 1; michael@0: var $31 = $.cxform = {}; michael@0: cxform($bytes, $stream, $31, swfVersion, tagCode); michael@0: } michael@0: } michael@0: return $; michael@0: } michael@0: function removeObject($bytes, $stream, $, swfVersion, tagCode) { michael@0: $ || ($ = {}); michael@0: if (tagCode === 5) { michael@0: $.symbolId = readUi16($bytes, $stream); michael@0: } michael@0: $.depth = readUi16($bytes, $stream); michael@0: return $; michael@0: } michael@0: function defineImage($bytes, $stream, $, swfVersion, tagCode) { michael@0: var imgData; michael@0: $ || ($ = {}); michael@0: $.id = readUi16($bytes, $stream); michael@0: if (tagCode > 21) { michael@0: var alphaDataOffset = readUi32($bytes, $stream); michael@0: if (tagCode === 90) { michael@0: $.deblock = readFixed8($bytes, $stream); michael@0: } michael@0: imgData = $.imgData = readBinary($bytes, $stream, alphaDataOffset); michael@0: $.alphaData = readBinary($bytes, $stream, 0); michael@0: } else { michael@0: imgData = $.imgData = readBinary($bytes, $stream, 0); michael@0: } michael@0: switch (imgData[0] << 8 | imgData[1]) { michael@0: case 65496: michael@0: case 65497: michael@0: $.mimeType = 'image/jpeg'; michael@0: break; michael@0: case 35152: michael@0: $.mimeType = 'image/png'; michael@0: break; michael@0: case 18249: michael@0: $.mimeType = 'image/gif'; michael@0: break; michael@0: default: michael@0: $.mimeType = 'application/octet-stream'; michael@0: } michael@0: if (tagCode === 6) { michael@0: $.incomplete = 1; michael@0: } michael@0: return $; michael@0: } michael@0: function defineButton($bytes, $stream, $, swfVersion, tagCode) { michael@0: var eob, hasFilters, count, blend; michael@0: $ || ($ = {}); michael@0: $.id = readUi16($bytes, $stream); michael@0: if (tagCode == 7) { michael@0: var $0 = $.characters = []; michael@0: do { michael@0: var $1 = {}; michael@0: var temp = button($bytes, $stream, $1, swfVersion, tagCode); michael@0: eob = temp.eob; michael@0: $0.push($1); michael@0: } while (!eob); michael@0: $.actionsData = readBinary($bytes, $stream, 0); michael@0: } else { michael@0: var trackFlags = readUi8($bytes, $stream); michael@0: $.trackAsMenu = trackFlags >> 7 & 1; michael@0: var actionOffset = readUi16($bytes, $stream); michael@0: var $28 = $.characters = []; michael@0: do { michael@0: var $29 = {}; michael@0: var flags = readUi8($bytes, $stream); michael@0: var eob = $29.eob = !flags; michael@0: if (swfVersion >= 8) { michael@0: blend = $29.blend = flags >> 5 & 1; michael@0: hasFilters = $29.hasFilters = flags >> 4 & 1; michael@0: } else { michael@0: blend = $29.blend = 0; michael@0: hasFilters = $29.hasFilters = 0; michael@0: } michael@0: $29.stateHitTest = flags >> 3 & 1; michael@0: $29.stateDown = flags >> 2 & 1; michael@0: $29.stateOver = flags >> 1 & 1; michael@0: $29.stateUp = flags & 1; michael@0: if (!eob) { michael@0: $29.symbolId = readUi16($bytes, $stream); michael@0: $29.depth = readUi16($bytes, $stream); michael@0: var $30 = $29.matrix = {}; michael@0: matrix($bytes, $stream, $30, swfVersion, tagCode); michael@0: if (tagCode === 34) { michael@0: var $31 = $29.cxform = {}; michael@0: cxform($bytes, $stream, $31, swfVersion, tagCode); michael@0: } michael@0: if (hasFilters) { michael@0: var count = readUi8($bytes, $stream); michael@0: var $2 = $.filters = []; michael@0: var $3 = count; michael@0: while ($3--) { michael@0: var $4 = {}; michael@0: anyFilter($bytes, $stream, $4, swfVersion, tagCode); michael@0: $2.push($4); michael@0: } michael@0: } michael@0: if (blend) { michael@0: $29.blendMode = readUi8($bytes, $stream); michael@0: } michael@0: } michael@0: $28.push($29); michael@0: } while (!eob); michael@0: if (!(!actionOffset)) { michael@0: var $56 = $.buttonActions = []; michael@0: do { michael@0: var $57 = {}; michael@0: buttonCondAction($bytes, $stream, $57, swfVersion, tagCode); michael@0: $56.push($57); michael@0: } while ($stream.remaining() > 0); michael@0: } michael@0: } michael@0: return $; michael@0: } michael@0: function defineJPEGTables($bytes, $stream, $, swfVersion, tagCode) { michael@0: $ || ($ = {}); michael@0: $.id = 0; michael@0: $.imgData = readBinary($bytes, $stream, 0); michael@0: $.mimeType = 'application/octet-stream'; michael@0: return $; michael@0: } michael@0: function setBackgroundColor($bytes, $stream, $, swfVersion, tagCode) { michael@0: $ || ($ = {}); michael@0: var $0 = $.color = {}; michael@0: rgb($bytes, $stream, $0, swfVersion, tagCode); michael@0: return $; michael@0: } michael@0: function defineBinaryData($bytes, $stream, $, swfVersion, tagCode) { michael@0: $ || ($ = {}); michael@0: $.id = readUi16($bytes, $stream); michael@0: var reserved = readUi32($bytes, $stream); michael@0: $.data = readBinary($bytes, $stream, 0); michael@0: return $; michael@0: } michael@0: function defineFont($bytes, $stream, $, swfVersion, tagCode) { michael@0: $ || ($ = {}); michael@0: $.id = readUi16($bytes, $stream); michael@0: var firstOffset = readUi16($bytes, $stream); michael@0: var glyphCount = $.glyphCount = firstOffset / 2; michael@0: var restOffsets = []; michael@0: var $0 = glyphCount - 1; michael@0: while ($0--) { michael@0: restOffsets.push(readUi16($bytes, $stream)); michael@0: } michael@0: $.offsets = [ michael@0: firstOffset michael@0: ].concat(restOffsets); michael@0: var $1 = $.glyphs = []; michael@0: var $2 = glyphCount; michael@0: while ($2--) { michael@0: var $3 = {}; michael@0: shape($bytes, $stream, $3, swfVersion, tagCode); michael@0: $1.push($3); michael@0: } michael@0: return $; michael@0: } michael@0: function defineLabel($bytes, $stream, $, swfVersion, tagCode) { michael@0: var eot; michael@0: $ || ($ = {}); michael@0: $.id = readUi16($bytes, $stream); michael@0: var $0 = $.bbox = {}; michael@0: bbox($bytes, $stream, $0, swfVersion, tagCode); michael@0: var $1 = $.matrix = {}; michael@0: matrix($bytes, $stream, $1, swfVersion, tagCode); michael@0: var glyphBits = $.glyphBits = readUi8($bytes, $stream); michael@0: var advanceBits = $.advanceBits = readUi8($bytes, $stream); michael@0: var $2 = $.records = []; michael@0: do { michael@0: var $3 = {}; michael@0: var temp = textRecord($bytes, $stream, $3, swfVersion, tagCode, glyphBits, advanceBits); michael@0: eot = temp.eot; michael@0: $2.push($3); michael@0: } while (!eot); michael@0: return $; michael@0: } michael@0: function doAction($bytes, $stream, $, swfVersion, tagCode) { michael@0: $ || ($ = {}); michael@0: if (tagCode === 59) { michael@0: $.spriteId = readUi16($bytes, $stream); michael@0: } michael@0: $.actionsData = readBinary($bytes, $stream, 0); michael@0: return $; michael@0: } michael@0: function defineSound($bytes, $stream, $, swfVersion, tagCode) { michael@0: $ || ($ = {}); michael@0: $.id = readUi16($bytes, $stream); michael@0: var soundFlags = readUi8($bytes, $stream); michael@0: $.soundFormat = soundFlags >> 4 & 15; michael@0: $.soundRate = soundFlags >> 2 & 3; michael@0: $.soundSize = soundFlags >> 1 & 1; michael@0: $.soundType = soundFlags & 1; michael@0: $.samplesCount = readUi32($bytes, $stream); michael@0: $.soundData = readBinary($bytes, $stream, 0); michael@0: return $; michael@0: } michael@0: function startSound($bytes, $stream, $, swfVersion, tagCode) { michael@0: $ || ($ = {}); michael@0: if (tagCode == 15) { michael@0: $.soundId = readUi16($bytes, $stream); michael@0: } michael@0: if (tagCode == 89) { michael@0: $.soundClassName = readString($bytes, $stream, 0); michael@0: } michael@0: var $0 = $.soundInfo = {}; michael@0: soundInfo($bytes, $stream, $0, swfVersion, tagCode); michael@0: return $; michael@0: } michael@0: function soundStreamHead($bytes, $stream, $, swfVersion, tagCode) { michael@0: $ || ($ = {}); michael@0: var playbackFlags = readUi8($bytes, $stream); michael@0: $.playbackRate = playbackFlags >> 2 & 3; michael@0: $.playbackSize = playbackFlags >> 1 & 1; michael@0: $.playbackType = playbackFlags & 1; michael@0: var streamFlags = readUi8($bytes, $stream); michael@0: var streamCompression = $.streamCompression = streamFlags >> 4 & 15; michael@0: $.streamRate = streamFlags >> 2 & 3; michael@0: $.streamSize = streamFlags >> 1 & 1; michael@0: $.streamType = streamFlags & 1; michael@0: $.samplesCount = readUi32($bytes, $stream); michael@0: if (streamCompression == 2) { michael@0: $.latencySeek = readSi16($bytes, $stream); michael@0: } michael@0: return $; michael@0: } michael@0: function soundStreamBlock($bytes, $stream, $, swfVersion, tagCode) { michael@0: $ || ($ = {}); michael@0: $.data = readBinary($bytes, $stream, 0); michael@0: return $; michael@0: } michael@0: function defineBitmap($bytes, $stream, $, swfVersion, tagCode) { michael@0: $ || ($ = {}); michael@0: $.id = readUi16($bytes, $stream); michael@0: var format = $.format = readUi8($bytes, $stream); michael@0: $.width = readUi16($bytes, $stream); michael@0: $.height = readUi16($bytes, $stream); michael@0: $.hasAlpha = tagCode === 36; michael@0: if (format === 3) { michael@0: $.colorTableSize = readUi8($bytes, $stream); michael@0: } michael@0: $.bmpData = readBinary($bytes, $stream, 0); michael@0: return $; michael@0: } michael@0: function defineText($bytes, $stream, $, swfVersion, tagCode) { michael@0: $ || ($ = {}); michael@0: $.id = readUi16($bytes, $stream); michael@0: var $0 = $.bbox = {}; michael@0: bbox($bytes, $stream, $0, swfVersion, tagCode); michael@0: var flags = readUi16($bytes, $stream); michael@0: var hasText = $.hasText = flags >> 7 & 1; michael@0: $.wordWrap = flags >> 6 & 1; michael@0: $.multiline = flags >> 5 & 1; michael@0: $.password = flags >> 4 & 1; michael@0: $.readonly = flags >> 3 & 1; michael@0: var hasColor = $.hasColor = flags >> 2 & 1; michael@0: var hasMaxLength = $.hasMaxLength = flags >> 1 & 1; michael@0: var hasFont = $.hasFont = flags & 1; michael@0: var hasFontClass = $.hasFontClass = flags >> 15 & 1; michael@0: $.autoSize = flags >> 14 & 1; michael@0: var hasLayout = $.hasLayout = flags >> 13 & 1; michael@0: $.noSelect = flags >> 12 & 1; michael@0: $.border = flags >> 11 & 1; michael@0: $.wasStatic = flags >> 10 & 1; michael@0: $.html = flags >> 9 & 1; michael@0: $.useOutlines = flags >> 8 & 1; michael@0: if (hasFont) { michael@0: $.fontId = readUi16($bytes, $stream); michael@0: } michael@0: if (hasFontClass) { michael@0: $.fontClass = readString($bytes, $stream, 0); michael@0: } michael@0: if (hasFont) { michael@0: $.fontHeight = readUi16($bytes, $stream); michael@0: } michael@0: if (hasColor) { michael@0: var $1 = $.color = {}; michael@0: rgba($bytes, $stream, $1, swfVersion, tagCode); michael@0: } michael@0: if (hasMaxLength) { michael@0: $.maxLength = readUi16($bytes, $stream); michael@0: } michael@0: if (hasLayout) { michael@0: $.align = readUi8($bytes, $stream); michael@0: $.leftMargin = readUi16($bytes, $stream); michael@0: $.rightMargin = readUi16($bytes, $stream); michael@0: $.indent = readSi16($bytes, $stream); michael@0: $.leading = readSi16($bytes, $stream); michael@0: } michael@0: $.variableName = readString($bytes, $stream, 0); michael@0: if (hasText) { michael@0: $.initialText = readString($bytes, $stream, 0); michael@0: } michael@0: return $; michael@0: } michael@0: function frameLabel($bytes, $stream, $, swfVersion, tagCode) { michael@0: $ || ($ = {}); michael@0: $.name = readString($bytes, $stream, 0); michael@0: return $; michael@0: } michael@0: function defineFont2($bytes, $stream, $, swfVersion, tagCode) { michael@0: $ || ($ = {}); michael@0: $.id = readUi16($bytes, $stream); michael@0: var hasLayout = $.hasLayout = readUb($bytes, $stream, 1); michael@0: if (swfVersion > 5) { michael@0: $.shiftJis = readUb($bytes, $stream, 1); michael@0: } else { michael@0: var reserved = readUb($bytes, $stream, 1); michael@0: } michael@0: $.smallText = readUb($bytes, $stream, 1); michael@0: $.ansi = readUb($bytes, $stream, 1); michael@0: var wideOffset = $.wideOffset = readUb($bytes, $stream, 1); michael@0: var wide = $.wide = readUb($bytes, $stream, 1); michael@0: $.italic = readUb($bytes, $stream, 1); michael@0: $.bold = readUb($bytes, $stream, 1); michael@0: if (swfVersion > 5) { michael@0: $.language = readUi8($bytes, $stream); michael@0: } else { michael@0: var reserved = readUi8($bytes, $stream); michael@0: $.language = 0; michael@0: } michael@0: var nameLength = readUi8($bytes, $stream); michael@0: $.name = readString($bytes, $stream, nameLength); michael@0: if (tagCode === 75) { michael@0: $.resolution = 20; michael@0: } michael@0: var glyphCount = $.glyphCount = readUi16($bytes, $stream); michael@0: if (wideOffset) { michael@0: var $0 = $.offsets = []; michael@0: var $1 = glyphCount; michael@0: while ($1--) { michael@0: $0.push(readUi32($bytes, $stream)); michael@0: } michael@0: $.mapOffset = readUi32($bytes, $stream); michael@0: } else { michael@0: var $2 = $.offsets = []; michael@0: var $3 = glyphCount; michael@0: while ($3--) { michael@0: $2.push(readUi16($bytes, $stream)); michael@0: } michael@0: $.mapOffset = readUi16($bytes, $stream); michael@0: } michael@0: var $4 = $.glyphs = []; michael@0: var $5 = glyphCount; michael@0: while ($5--) { michael@0: var $6 = {}; michael@0: shape($bytes, $stream, $6, swfVersion, tagCode); michael@0: $4.push($6); michael@0: } michael@0: if (wide) { michael@0: var $47 = $.codes = []; michael@0: var $48 = glyphCount; michael@0: while ($48--) { michael@0: $47.push(readUi16($bytes, $stream)); michael@0: } michael@0: } else { michael@0: var $49 = $.codes = []; michael@0: var $50 = glyphCount; michael@0: while ($50--) { michael@0: $49.push(readUi8($bytes, $stream)); michael@0: } michael@0: } michael@0: if (hasLayout) { michael@0: $.ascent = readUi16($bytes, $stream); michael@0: $.descent = readUi16($bytes, $stream); michael@0: $.leading = readSi16($bytes, $stream); michael@0: var $51 = $.advance = []; michael@0: var $52 = glyphCount; michael@0: while ($52--) { michael@0: $51.push(readSi16($bytes, $stream)); michael@0: } michael@0: var $53 = $.bbox = []; michael@0: var $54 = glyphCount; michael@0: while ($54--) { michael@0: var $55 = {}; michael@0: bbox($bytes, $stream, $55, swfVersion, tagCode); michael@0: $53.push($55); michael@0: } michael@0: var kerningCount = readUi16($bytes, $stream); michael@0: var $56 = $.kerning = []; michael@0: var $57 = kerningCount; michael@0: while ($57--) { michael@0: var $58 = {}; michael@0: kerning($bytes, $stream, $58, swfVersion, tagCode, wide); michael@0: $56.push($58); michael@0: } michael@0: } michael@0: return $; michael@0: } michael@0: function fileAttributes($bytes, $stream, $, swfVersion, tagCode) { michael@0: $ || ($ = {}); michael@0: var reserved = readUb($bytes, $stream, 1); michael@0: $.useDirectBlit = readUb($bytes, $stream, 1); michael@0: $.useGpu = readUb($bytes, $stream, 1); michael@0: $.hasMetadata = readUb($bytes, $stream, 1); michael@0: $.doAbc = readUb($bytes, $stream, 1); michael@0: $.noCrossDomainCaching = readUb($bytes, $stream, 1); michael@0: $.relativeUrls = readUb($bytes, $stream, 1); michael@0: $.network = readUb($bytes, $stream, 1); michael@0: var pad = readUb($bytes, $stream, 24); michael@0: return $; michael@0: } michael@0: function doABC($bytes, $stream, $, swfVersion, tagCode) { michael@0: $ || ($ = {}); michael@0: if (tagCode === 82) { michael@0: $.flags = readUi32($bytes, $stream); michael@0: } else { michael@0: $.flags = 0; michael@0: } michael@0: if (tagCode === 82) { michael@0: $.name = readString($bytes, $stream, 0); michael@0: } else { michael@0: $.name = ''; michael@0: } michael@0: $.data = readBinary($bytes, $stream, 0); michael@0: return $; michael@0: } michael@0: function exportAssets($bytes, $stream, $, swfVersion, tagCode) { michael@0: $ || ($ = {}); michael@0: var exportsCount = readUi16($bytes, $stream); michael@0: var $0 = $.exports = []; michael@0: var $1 = exportsCount; michael@0: while ($1--) { michael@0: var $2 = {}; michael@0: $2.symbolId = readUi16($bytes, $stream); michael@0: $2.className = readString($bytes, $stream, 0); michael@0: $0.push($2); michael@0: } michael@0: return $; michael@0: } michael@0: function symbolClass($bytes, $stream, $, swfVersion, tagCode) { michael@0: $ || ($ = {}); michael@0: var symbolCount = readUi16($bytes, $stream); michael@0: var $0 = $.exports = []; michael@0: var $1 = symbolCount; michael@0: while ($1--) { michael@0: var $2 = {}; michael@0: $2.symbolId = readUi16($bytes, $stream); michael@0: $2.className = readString($bytes, $stream, 0); michael@0: $0.push($2); michael@0: } michael@0: return $; michael@0: } michael@0: function defineScalingGrid($bytes, $stream, $, swfVersion, tagCode) { michael@0: $ || ($ = {}); michael@0: $.symbolId = readUi16($bytes, $stream); michael@0: var $0 = $.splitter = {}; michael@0: bbox($bytes, $stream, $0, swfVersion, tagCode); michael@0: return $; michael@0: } michael@0: function defineScene($bytes, $stream, $, swfVersion, tagCode) { michael@0: $ || ($ = {}); michael@0: var sceneCount = readEncodedU32($bytes, $stream); michael@0: var $0 = $.scenes = []; michael@0: var $1 = sceneCount; michael@0: while ($1--) { michael@0: var $2 = {}; michael@0: $2.offset = readEncodedU32($bytes, $stream); michael@0: $2.name = readString($bytes, $stream, 0); michael@0: $0.push($2); michael@0: } michael@0: var labelCount = readEncodedU32($bytes, $stream); michael@0: var $3 = $.labels = []; michael@0: var $4 = labelCount; michael@0: while ($4--) { michael@0: var $5 = {}; michael@0: $5.frame = readEncodedU32($bytes, $stream); michael@0: $5.name = readString($bytes, $stream, 0); michael@0: $3.push($5); michael@0: } michael@0: return $; michael@0: } michael@0: function bbox($bytes, $stream, $, swfVersion, tagCode) { michael@0: align($bytes, $stream); michael@0: var bits = readUb($bytes, $stream, 5); michael@0: var xMin = readSb($bytes, $stream, bits); michael@0: var xMax = readSb($bytes, $stream, bits); michael@0: var yMin = readSb($bytes, $stream, bits); michael@0: var yMax = readSb($bytes, $stream, bits); michael@0: $.xMin = xMin; michael@0: $.xMax = xMax; michael@0: $.yMin = yMin; michael@0: $.yMax = yMax; michael@0: align($bytes, $stream); michael@0: } michael@0: function rgb($bytes, $stream, $, swfVersion, tagCode) { michael@0: $.red = readUi8($bytes, $stream); michael@0: $.green = readUi8($bytes, $stream); michael@0: $.blue = readUi8($bytes, $stream); michael@0: $.alpha = 255; michael@0: return; michael@0: } michael@0: function rgba($bytes, $stream, $, swfVersion, tagCode) { michael@0: $.red = readUi8($bytes, $stream); michael@0: $.green = readUi8($bytes, $stream); michael@0: $.blue = readUi8($bytes, $stream); michael@0: $.alpha = readUi8($bytes, $stream); michael@0: return; michael@0: } michael@0: function argb($bytes, $stream, $, swfVersion, tagCode) { michael@0: $.alpha = readUi8($bytes, $stream); michael@0: $.red = readUi8($bytes, $stream); michael@0: $.green = readUi8($bytes, $stream); michael@0: $.blue = readUi8($bytes, $stream); michael@0: } michael@0: function fillSolid($bytes, $stream, $, swfVersion, tagCode, isMorph) { michael@0: if (tagCode > 22 || isMorph) { michael@0: var $125 = $.color = {}; michael@0: rgba($bytes, $stream, $125, swfVersion, tagCode); michael@0: } else { michael@0: var $126 = $.color = {}; michael@0: rgb($bytes, $stream, $126, swfVersion, tagCode); michael@0: } michael@0: if (isMorph) { michael@0: var $127 = $.colorMorph = {}; michael@0: rgba($bytes, $stream, $127, swfVersion, tagCode); michael@0: } michael@0: return; michael@0: } michael@0: function matrix($bytes, $stream, $, swfVersion, tagCode) { michael@0: align($bytes, $stream); michael@0: var hasScale = readUb($bytes, $stream, 1); michael@0: if (hasScale) { michael@0: var bits = readUb($bytes, $stream, 5); michael@0: $.a = readFb($bytes, $stream, bits); michael@0: $.d = readFb($bytes, $stream, bits); michael@0: } else { michael@0: $.a = 1; michael@0: $.d = 1; michael@0: } michael@0: var hasRotate = readUb($bytes, $stream, 1); michael@0: if (hasRotate) { michael@0: var bits = readUb($bytes, $stream, 5); michael@0: $.b = readFb($bytes, $stream, bits); michael@0: $.c = readFb($bytes, $stream, bits); michael@0: } else { michael@0: $.b = 0; michael@0: $.c = 0; michael@0: } michael@0: var bits = readUb($bytes, $stream, 5); michael@0: var e = readSb($bytes, $stream, bits); michael@0: var f = readSb($bytes, $stream, bits); michael@0: $.tx = e; michael@0: $.ty = f; michael@0: align($bytes, $stream); michael@0: } michael@0: function cxform($bytes, $stream, $, swfVersion, tagCode) { michael@0: align($bytes, $stream); michael@0: var hasOffsets = readUb($bytes, $stream, 1); michael@0: var hasMultipliers = readUb($bytes, $stream, 1); michael@0: var bits = readUb($bytes, $stream, 4); michael@0: if (hasMultipliers) { michael@0: $.redMultiplier = readSb($bytes, $stream, bits); michael@0: $.greenMultiplier = readSb($bytes, $stream, bits); michael@0: $.blueMultiplier = readSb($bytes, $stream, bits); michael@0: if (tagCode > 4) { michael@0: $.alphaMultiplier = readSb($bytes, $stream, bits); michael@0: } else { michael@0: $.alphaMultiplier = 256; michael@0: } michael@0: } else { michael@0: $.redMultiplier = 256; michael@0: $.greenMultiplier = 256; michael@0: $.blueMultiplier = 256; michael@0: $.alphaMultiplier = 256; michael@0: } michael@0: if (hasOffsets) { michael@0: $.redOffset = readSb($bytes, $stream, bits); michael@0: $.greenOffset = readSb($bytes, $stream, bits); michael@0: $.blueOffset = readSb($bytes, $stream, bits); michael@0: if (tagCode > 4) { michael@0: $.alphaOffset = readSb($bytes, $stream, bits); michael@0: } else { michael@0: $.alphaOffset = 0; michael@0: } michael@0: } else { michael@0: $.redOffset = 0; michael@0: $.greenOffset = 0; michael@0: $.blueOffset = 0; michael@0: $.alphaOffset = 0; michael@0: } michael@0: align($bytes, $stream); michael@0: } michael@0: function fillGradient($bytes, $stream, $, swfVersion, tagCode, isMorph, type) { michael@0: var $128 = $.matrix = {}; michael@0: matrix($bytes, $stream, $128, swfVersion, tagCode); michael@0: if (isMorph) { michael@0: var $129 = $.matrixMorph = {}; michael@0: matrix($bytes, $stream, $129, swfVersion, tagCode); michael@0: } michael@0: gradient($bytes, $stream, $, swfVersion, tagCode, isMorph, type); michael@0: } michael@0: function gradient($bytes, $stream, $, swfVersion, tagCode, isMorph, type) { michael@0: if (tagCode === 83) { michael@0: $.spreadMode = readUb($bytes, $stream, 2); michael@0: $.interpolationMode = readUb($bytes, $stream, 2); michael@0: } else { michael@0: var pad = readUb($bytes, $stream, 4); michael@0: } michael@0: var count = $.count = readUb($bytes, $stream, 4); michael@0: var $130 = $.records = []; michael@0: var $131 = count; michael@0: while ($131--) { michael@0: var $132 = {}; michael@0: gradientRecord($bytes, $stream, $132, swfVersion, tagCode, isMorph); michael@0: $130.push($132); michael@0: } michael@0: if (type === 19) { michael@0: $.focalPoint = readFixed8($bytes, $stream); michael@0: if (isMorph) { michael@0: $.focalPointMorph = readFixed8($bytes, $stream); michael@0: } michael@0: } michael@0: } michael@0: function gradientRecord($bytes, $stream, $, swfVersion, tagCode, isMorph) { michael@0: $.ratio = readUi8($bytes, $stream); michael@0: if (tagCode > 22) { michael@0: var $133 = $.color = {}; michael@0: rgba($bytes, $stream, $133, swfVersion, tagCode); michael@0: } else { michael@0: var $134 = $.color = {}; michael@0: rgb($bytes, $stream, $134, swfVersion, tagCode); michael@0: } michael@0: if (isMorph) { michael@0: $.ratioMorph = readUi8($bytes, $stream); michael@0: var $135 = $.colorMorph = {}; michael@0: rgba($bytes, $stream, $135, swfVersion, tagCode); michael@0: } michael@0: } michael@0: function morphShapeWithStyle($bytes, $stream, $, swfVersion, tagCode, isMorph, hasStrokes) { michael@0: var eos, bits; michael@0: var temp = styles($bytes, $stream, $, swfVersion, tagCode, isMorph, hasStrokes); michael@0: var lineBits = temp.lineBits; michael@0: var fillBits = temp.fillBits; michael@0: var $160 = $.records = []; michael@0: do { michael@0: var $161 = {}; michael@0: var temp = shapeRecord($bytes, $stream, $161, swfVersion, tagCode, isMorph, fillBits, lineBits, hasStrokes, bits); michael@0: var eos = temp.eos; michael@0: var flags = temp.flags; michael@0: var type = temp.type; michael@0: var fillBits = temp.fillBits; michael@0: var lineBits = temp.lineBits; michael@0: var bits = temp.bits; michael@0: $160.push($161); michael@0: } while (!eos); michael@0: var temp = styleBits($bytes, $stream, $, swfVersion, tagCode); michael@0: var fillBits = temp.fillBits; michael@0: var lineBits = temp.lineBits; michael@0: var $162 = $.recordsMorph = []; michael@0: do { michael@0: var $163 = {}; michael@0: var temp = shapeRecord($bytes, $stream, $163, swfVersion, tagCode, isMorph, fillBits, lineBits, hasStrokes, bits); michael@0: eos = temp.eos; michael@0: var flags = temp.flags; michael@0: var type = temp.type; michael@0: var fillBits = temp.fillBits; michael@0: var lineBits = temp.lineBits; michael@0: bits = temp.bits; michael@0: $162.push($163); michael@0: } while (!eos); michael@0: } michael@0: function shapeWithStyle($bytes, $stream, $, swfVersion, tagCode, isMorph, hasStrokes) { michael@0: var eos; michael@0: var temp = styles($bytes, $stream, $, swfVersion, tagCode, isMorph, hasStrokes); michael@0: var fillBits = temp.fillBits; michael@0: var lineBits = temp.lineBits; michael@0: var $160 = $.records = []; michael@0: do { michael@0: var $161 = {}; michael@0: var temp = shapeRecord($bytes, $stream, $161, swfVersion, tagCode, isMorph, fillBits, lineBits, hasStrokes, bits); michael@0: eos = temp.eos; michael@0: var flags = temp.flags; michael@0: var type = temp.type; michael@0: var fillBits = temp.fillBits; michael@0: var lineBits = temp.lineBits; michael@0: var bits = temp.bits; michael@0: $160.push($161); michael@0: } while (!eos); michael@0: } michael@0: function shapeRecord($bytes, $stream, $, swfVersion, tagCode, isMorph, fillBits, lineBits, hasStrokes, bits) { michael@0: var type = $.type = readUb($bytes, $stream, 1); michael@0: var flags = readUb($bytes, $stream, 5); michael@0: var eos = $.eos = !(type || flags); michael@0: if (type) { michael@0: var temp = shapeRecordEdge($bytes, $stream, $, swfVersion, tagCode, flags, bits); michael@0: var bits = temp.bits; michael@0: } else { michael@0: var temp = shapeRecordSetup($bytes, $stream, $, swfVersion, tagCode, flags, isMorph, fillBits, lineBits, hasStrokes, bits); michael@0: var fillBits = temp.fillBits; michael@0: var lineBits = temp.lineBits; michael@0: var bits = temp.bits; michael@0: } michael@0: return { michael@0: type: type, michael@0: flags: flags, michael@0: eos: eos, michael@0: fillBits: fillBits, michael@0: lineBits: lineBits, michael@0: bits: bits michael@0: }; michael@0: } michael@0: function shapeRecordEdge($bytes, $stream, $, swfVersion, tagCode, flags, bits) { michael@0: var isStraight = 0, tmp = 0, bits = 0, isGeneral = 0, isVertical = 0; michael@0: isStraight = $.isStraight = flags >> 4; michael@0: tmp = flags & 15; michael@0: bits = tmp + 2; michael@0: if (isStraight) { michael@0: isGeneral = $.isGeneral = readUb($bytes, $stream, 1); michael@0: if (isGeneral) { michael@0: $.deltaX = readSb($bytes, $stream, bits); michael@0: $.deltaY = readSb($bytes, $stream, bits); michael@0: } else { michael@0: isVertical = $.isVertical = readUb($bytes, $stream, 1); michael@0: if (isVertical) { michael@0: $.deltaY = readSb($bytes, $stream, bits); michael@0: } else { michael@0: $.deltaX = readSb($bytes, $stream, bits); michael@0: } michael@0: } michael@0: } else { michael@0: $.controlDeltaX = readSb($bytes, $stream, bits); michael@0: $.controlDeltaY = readSb($bytes, $stream, bits); michael@0: $.anchorDeltaX = readSb($bytes, $stream, bits); michael@0: $.anchorDeltaY = readSb($bytes, $stream, bits); michael@0: } michael@0: return { michael@0: bits: bits michael@0: }; michael@0: } michael@0: function shapeRecordSetup($bytes, $stream, $, swfVersion, tagCode, flags, isMorph, fillBits, lineBits, hasStrokes, bits) { michael@0: var hasNewStyles = 0, hasLineStyle = 0, hasFillStyle1 = 0; michael@0: var hasFillStyle0 = 0, move = 0; michael@0: if (tagCode > 2) { michael@0: hasNewStyles = $.hasNewStyles = flags >> 4; michael@0: } else { michael@0: hasNewStyles = $.hasNewStyles = 0; michael@0: } michael@0: hasLineStyle = $.hasLineStyle = flags >> 3 & 1; michael@0: hasFillStyle1 = $.hasFillStyle1 = flags >> 2 & 1; michael@0: hasFillStyle0 = $.hasFillStyle0 = flags >> 1 & 1; michael@0: move = $.move = flags & 1; michael@0: if (move) { michael@0: bits = readUb($bytes, $stream, 5); michael@0: $.moveX = readSb($bytes, $stream, bits); michael@0: $.moveY = readSb($bytes, $stream, bits); michael@0: } michael@0: if (hasFillStyle0) { michael@0: $.fillStyle0 = readUb($bytes, $stream, fillBits); michael@0: } michael@0: if (hasFillStyle1) { michael@0: $.fillStyle1 = readUb($bytes, $stream, fillBits); michael@0: } michael@0: if (hasLineStyle) { michael@0: $.lineStyle = readUb($bytes, $stream, lineBits); michael@0: } michael@0: if (hasNewStyles) { michael@0: var temp = styles($bytes, $stream, $, swfVersion, tagCode, isMorph, hasStrokes); michael@0: var lineBits = temp.lineBits; michael@0: var fillBits = temp.fillBits; michael@0: } michael@0: return { michael@0: lineBits: lineBits, michael@0: fillBits: fillBits, michael@0: bits: bits michael@0: }; michael@0: } michael@0: function styles($bytes, $stream, $, swfVersion, tagCode, isMorph, hasStrokes) { michael@0: fillStyleArray($bytes, $stream, $, swfVersion, tagCode, isMorph); michael@0: lineStyleArray($bytes, $stream, $, swfVersion, tagCode, isMorph, hasStrokes); michael@0: var temp = styleBits($bytes, $stream, $, swfVersion, tagCode); michael@0: var fillBits = temp.fillBits; michael@0: var lineBits = temp.lineBits; michael@0: return { michael@0: fillBits: fillBits, michael@0: lineBits: lineBits michael@0: }; michael@0: } michael@0: function fillStyleArray($bytes, $stream, $, swfVersion, tagCode, isMorph) { michael@0: var count; michael@0: var tmp = readUi8($bytes, $stream); michael@0: if (tagCode > 2 && tmp === 255) { michael@0: count = readUi16($bytes, $stream); michael@0: } else { michael@0: count = tmp; michael@0: } michael@0: var $4 = $.fillStyles = []; michael@0: var $5 = count; michael@0: while ($5--) { michael@0: var $6 = {}; michael@0: fillStyle($bytes, $stream, $6, swfVersion, tagCode, isMorph); michael@0: $4.push($6); michael@0: } michael@0: } michael@0: function lineStyleArray($bytes, $stream, $, swfVersion, tagCode, isMorph, hasStrokes) { michael@0: var count; michael@0: var tmp = readUi8($bytes, $stream); michael@0: if (tagCode > 2 && tmp === 255) { michael@0: count = readUi16($bytes, $stream); michael@0: } else { michael@0: count = tmp; michael@0: } michael@0: var $138 = $.lineStyles = []; michael@0: var $139 = count; michael@0: while ($139--) { michael@0: var $140 = {}; michael@0: lineStyle($bytes, $stream, $140, swfVersion, tagCode, isMorph, hasStrokes); michael@0: $138.push($140); michael@0: } michael@0: } michael@0: function styleBits($bytes, $stream, $, swfVersion, tagCode) { michael@0: align($bytes, $stream); michael@0: var fillBits = readUb($bytes, $stream, 4); michael@0: var lineBits = readUb($bytes, $stream, 4); michael@0: return { michael@0: fillBits: fillBits, michael@0: lineBits: lineBits michael@0: }; michael@0: } michael@0: function fillStyle($bytes, $stream, $, swfVersion, tagCode, isMorph) { michael@0: var type = $.type = readUi8($bytes, $stream); michael@0: switch (type) { michael@0: case 0: michael@0: fillSolid($bytes, $stream, $, swfVersion, tagCode, isMorph); michael@0: break; michael@0: case 16: michael@0: case 18: michael@0: case 19: michael@0: fillGradient($bytes, $stream, $, swfVersion, tagCode, isMorph, type); michael@0: break; michael@0: case 64: michael@0: case 65: michael@0: case 66: michael@0: case 67: michael@0: fillBitmap($bytes, $stream, $, swfVersion, tagCode, isMorph, type); michael@0: break; michael@0: default: michael@0: } michael@0: } michael@0: function lineStyle($bytes, $stream, $, swfVersion, tagCode, isMorph, hasStrokes) { michael@0: $.width = readUi16($bytes, $stream); michael@0: if (isMorph) { michael@0: $.widthMorph = readUi16($bytes, $stream); michael@0: } michael@0: if (hasStrokes) { michael@0: align($bytes, $stream); michael@0: $.startCapStyle = readUb($bytes, $stream, 2); michael@0: var joinStyle = $.joinStyle = readUb($bytes, $stream, 2); michael@0: var hasFill = $.hasFill = readUb($bytes, $stream, 1); michael@0: $.noHscale = readUb($bytes, $stream, 1); michael@0: $.noVscale = readUb($bytes, $stream, 1); michael@0: $.pixelHinting = readUb($bytes, $stream, 1); michael@0: var reserved = readUb($bytes, $stream, 5); michael@0: $.noClose = readUb($bytes, $stream, 1); michael@0: $.endCapStyle = readUb($bytes, $stream, 2); michael@0: if (joinStyle === 2) { michael@0: $.miterLimitFactor = readFixed8($bytes, $stream); michael@0: } michael@0: if (hasFill) { michael@0: var $141 = $.fillStyle = {}; michael@0: fillStyle($bytes, $stream, $141, swfVersion, tagCode, isMorph); michael@0: } else { michael@0: var $155 = $.color = {}; michael@0: rgba($bytes, $stream, $155, swfVersion, tagCode); michael@0: if (isMorph) { michael@0: var $156 = $.colorMorph = {}; michael@0: rgba($bytes, $stream, $156, swfVersion, tagCode); michael@0: } michael@0: } michael@0: } else { michael@0: if (tagCode > 22) { michael@0: var $157 = $.color = {}; michael@0: rgba($bytes, $stream, $157, swfVersion, tagCode); michael@0: } else { michael@0: var $158 = $.color = {}; michael@0: rgb($bytes, $stream, $158, swfVersion, tagCode); michael@0: } michael@0: if (isMorph) { michael@0: var $159 = $.colorMorph = {}; michael@0: rgba($bytes, $stream, $159, swfVersion, tagCode); michael@0: } michael@0: } michael@0: } michael@0: function fillBitmap($bytes, $stream, $, swfVersion, tagCode, isMorph, type) { michael@0: $.bitmapId = readUi16($bytes, $stream); michael@0: var $18 = $.matrix = {}; michael@0: matrix($bytes, $stream, $18, swfVersion, tagCode); michael@0: if (isMorph) { michael@0: var $19 = $.matrixMorph = {}; michael@0: matrix($bytes, $stream, $19, swfVersion, tagCode); michael@0: } michael@0: $.condition = type === 64 || type === 67; michael@0: } michael@0: function filterGlow($bytes, $stream, $, swfVersion, tagCode, type) { michael@0: var count; michael@0: if (type === 4 || type === 7) { michael@0: count = readUi8($bytes, $stream); michael@0: } else { michael@0: count = 1; michael@0: } michael@0: var $5 = $.colors = []; michael@0: var $6 = count; michael@0: while ($6--) { michael@0: var $7 = {}; michael@0: rgba($bytes, $stream, $7, swfVersion, tagCode); michael@0: $5.push($7); michael@0: } michael@0: if (type === 3) { michael@0: var $8 = $.higlightColor = {}; michael@0: rgba($bytes, $stream, $8, swfVersion, tagCode); michael@0: } michael@0: if (type === 4 || type === 7) { michael@0: var $9 = $.ratios = []; michael@0: var $10 = count; michael@0: while ($10--) { michael@0: $9.push(readUi8($bytes, $stream)); michael@0: } michael@0: } michael@0: $.blurX = readFixed($bytes, $stream); michael@0: $.blurY = readFixed($bytes, $stream); michael@0: if (type !== 2) { michael@0: $.angle = readFixed($bytes, $stream); michael@0: $.distance = readFixed($bytes, $stream); michael@0: } michael@0: $.strength = readFixed8($bytes, $stream); michael@0: $.innerShadow = readUb($bytes, $stream, 1); michael@0: $.knockout = readUb($bytes, $stream, 1); michael@0: $.compositeSource = readUb($bytes, $stream, 1); michael@0: if (type === 3) { michael@0: $.onTop = readUb($bytes, $stream, 1); michael@0: } else { michael@0: var reserved = readUb($bytes, $stream, 1); michael@0: } michael@0: if (type === 4 || type === 7) { michael@0: $.passes = readUb($bytes, $stream, 4); michael@0: } else { michael@0: var reserved = readUb($bytes, $stream, 4); michael@0: } michael@0: } michael@0: function filterBlur($bytes, $stream, $, swfVersion, tagCode) { michael@0: $.blurX = readFixed($bytes, $stream); michael@0: $.blurY = readFixed($bytes, $stream); michael@0: $.passes = readUb($bytes, $stream, 5); michael@0: var reserved = readUb($bytes, $stream, 3); michael@0: } michael@0: function filterConvolution($bytes, $stream, $, swfVersion, tagCode) { michael@0: var columns = $.columns = readUi8($bytes, $stream); michael@0: var rows = $.rows = readUi8($bytes, $stream); michael@0: $.divisor = readFloat($bytes, $stream); michael@0: $.bias = readFloat($bytes, $stream); michael@0: var $17 = $.weights = []; michael@0: var $18 = columns * rows; michael@0: while ($18--) { michael@0: $17.push(readFloat($bytes, $stream)); michael@0: } michael@0: var $19 = $.defaultColor = {}; michael@0: rgba($bytes, $stream, $19, swfVersion, tagCode); michael@0: var reserved = readUb($bytes, $stream, 6); michael@0: $.clamp = readUb($bytes, $stream, 1); michael@0: $.preserveAlpha = readUb($bytes, $stream, 1); michael@0: } michael@0: function filterColorMatrix($bytes, $stream, $, swfVersion, tagCode) { michael@0: var $20 = $.matrix = []; michael@0: var $21 = 20; michael@0: while ($21--) { michael@0: $20.push(readFloat($bytes, $stream)); michael@0: } michael@0: } michael@0: function anyFilter($bytes, $stream, $, swfVersion, tagCode) { michael@0: var type = $.type = readUi8($bytes, $stream); michael@0: switch (type) { michael@0: case 0: michael@0: case 2: michael@0: case 3: michael@0: case 4: michael@0: case 7: michael@0: filterGlow($bytes, $stream, $, swfVersion, tagCode, type); michael@0: break; michael@0: case 1: michael@0: filterBlur($bytes, $stream, $, swfVersion, tagCode); michael@0: break; michael@0: case 5: michael@0: filterConvolution($bytes, $stream, $, swfVersion, tagCode); michael@0: break; michael@0: case 6: michael@0: filterColorMatrix($bytes, $stream, $, swfVersion, tagCode); michael@0: break; michael@0: default: michael@0: } michael@0: } michael@0: function events($bytes, $stream, $, swfVersion, tagCode) { michael@0: var flags, keyPress; michael@0: if (swfVersion >= 6) { michael@0: flags = readUi32($bytes, $stream); michael@0: } else { michael@0: flags = readUi16($bytes, $stream); michael@0: } michael@0: var eoe = $.eoe = !flags; michael@0: $.onKeyUp = flags >> 7 & 1; michael@0: $.onKeyDown = flags >> 6 & 1; michael@0: $.onMouseUp = flags >> 5 & 1; michael@0: $.onMouseDown = flags >> 4 & 1; michael@0: $.onMouseMove = flags >> 3 & 1; michael@0: $.onUnload = flags >> 2 & 1; michael@0: $.onEnterFrame = flags >> 1 & 1; michael@0: $.onLoad = flags & 1; michael@0: if (swfVersion >= 6) { michael@0: $.onDragOver = flags >> 15 & 1; michael@0: $.onRollOut = flags >> 14 & 1; michael@0: $.onRollOver = flags >> 13 & 1; michael@0: $.onReleaseOutside = flags >> 12 & 1; michael@0: $.onRelease = flags >> 11 & 1; michael@0: $.onPress = flags >> 10 & 1; michael@0: $.onInitialize = flags >> 9 & 1; michael@0: $.onData = flags >> 8 & 1; michael@0: if (swfVersion >= 7) { michael@0: $.onConstruct = flags >> 18 & 1; michael@0: } else { michael@0: $.onConstruct = 0; michael@0: } michael@0: keyPress = $.keyPress = flags >> 17 & 1; michael@0: $.onDragOut = flags >> 16 & 1; michael@0: } michael@0: if (!eoe) { michael@0: var length = $.length = readUi32($bytes, $stream); michael@0: if (keyPress) { michael@0: $.keyCode = readUi8($bytes, $stream); michael@0: } michael@0: $.actionsData = readBinary($bytes, $stream, length - (keyPress ? 1 : 0)); michael@0: } michael@0: return { michael@0: eoe: eoe michael@0: }; michael@0: } michael@0: function kerning($bytes, $stream, $, swfVersion, tagCode, wide) { michael@0: if (wide) { michael@0: $.code1 = readUi16($bytes, $stream); michael@0: $.code2 = readUi16($bytes, $stream); michael@0: } else { michael@0: $.code1 = readUi8($bytes, $stream); michael@0: $.code2 = readUi8($bytes, $stream); michael@0: } michael@0: $.adjustment = readUi16($bytes, $stream); michael@0: } michael@0: function textEntry($bytes, $stream, $, swfVersion, tagCode, glyphBits, advanceBits) { michael@0: $.glyphIndex = readUb($bytes, $stream, glyphBits); michael@0: $.advance = readSb($bytes, $stream, advanceBits); michael@0: } michael@0: function textRecordSetup($bytes, $stream, $, swfVersion, tagCode, flags) { michael@0: var hasFont = $.hasFont = flags >> 3 & 1; michael@0: var hasColor = $.hasColor = flags >> 2 & 1; michael@0: var hasMoveY = $.hasMoveY = flags >> 1 & 1; michael@0: var hasMoveX = $.hasMoveX = flags & 1; michael@0: if (hasFont) { michael@0: $.fontId = readUi16($bytes, $stream); michael@0: } michael@0: if (hasColor) { michael@0: if (tagCode === 33) { michael@0: var $4 = $.color = {}; michael@0: rgba($bytes, $stream, $4, swfVersion, tagCode); michael@0: } else { michael@0: var $5 = $.color = {}; michael@0: rgb($bytes, $stream, $5, swfVersion, tagCode); michael@0: } michael@0: } michael@0: if (hasMoveX) { michael@0: $.moveX = readSi16($bytes, $stream); michael@0: } michael@0: if (hasMoveY) { michael@0: $.moveY = readSi16($bytes, $stream); michael@0: } michael@0: if (hasFont) { michael@0: $.fontHeight = readUi16($bytes, $stream); michael@0: } michael@0: } michael@0: function textRecord($bytes, $stream, $, swfVersion, tagCode, glyphBits, advanceBits) { michael@0: var glyphCount; michael@0: align($bytes, $stream); michael@0: var flags = readUb($bytes, $stream, 8); michael@0: var eot = $.eot = !flags; michael@0: textRecordSetup($bytes, $stream, $, swfVersion, tagCode, flags); michael@0: if (!eot) { michael@0: var tmp = readUi8($bytes, $stream); michael@0: if (swfVersion > 6) { michael@0: glyphCount = $.glyphCount = tmp; michael@0: } else { michael@0: glyphCount = $.glyphCount = tmp & 127; michael@0: } michael@0: var $6 = $.entries = []; michael@0: var $7 = glyphCount; michael@0: while ($7--) { michael@0: var $8 = {}; michael@0: textEntry($bytes, $stream, $8, swfVersion, tagCode, glyphBits, advanceBits); michael@0: $6.push($8); michael@0: } michael@0: } michael@0: return { michael@0: eot: eot michael@0: }; michael@0: } michael@0: function soundEnvelope($bytes, $stream, $, swfVersion, tagCode) { michael@0: $.pos44 = readUi32($bytes, $stream); michael@0: $.volumeLeft = readUi16($bytes, $stream); michael@0: $.volumeRight = readUi16($bytes, $stream); michael@0: } michael@0: function soundInfo($bytes, $stream, $, swfVersion, tagCode) { michael@0: var reserved = readUb($bytes, $stream, 2); michael@0: $.stop = readUb($bytes, $stream, 1); michael@0: $.noMultiple = readUb($bytes, $stream, 1); michael@0: var hasEnvelope = $.hasEnvelope = readUb($bytes, $stream, 1); michael@0: var hasLoops = $.hasLoops = readUb($bytes, $stream, 1); michael@0: var hasOutPoint = $.hasOutPoint = readUb($bytes, $stream, 1); michael@0: var hasInPoint = $.hasInPoint = readUb($bytes, $stream, 1); michael@0: if (hasInPoint) { michael@0: $.inPoint = readUi32($bytes, $stream); michael@0: } michael@0: if (hasOutPoint) { michael@0: $.outPoint = readUi32($bytes, $stream); michael@0: } michael@0: if (hasLoops) { michael@0: $.loopCount = readUi16($bytes, $stream); michael@0: } michael@0: if (hasEnvelope) { michael@0: var envelopeCount = $.envelopeCount = readUi8($bytes, $stream); michael@0: var $1 = $.envelopes = []; michael@0: var $2 = envelopeCount; michael@0: while ($2--) { michael@0: var $3 = {}; michael@0: soundEnvelope($bytes, $stream, $3, swfVersion, tagCode); michael@0: $1.push($3); michael@0: } michael@0: } michael@0: } michael@0: function button($bytes, $stream, $, swfVersion, tagCode) { michael@0: var hasFilters, blend; michael@0: var flags = readUi8($bytes, $stream); michael@0: var eob = $.eob = !flags; michael@0: if (swfVersion >= 8) { michael@0: blend = $.blend = flags >> 5 & 1; michael@0: hasFilters = $.hasFilters = flags >> 4 & 1; michael@0: } else { michael@0: blend = $.blend = 0; michael@0: hasFilters = $.hasFilters = 0; michael@0: } michael@0: $.stateHitTest = flags >> 3 & 1; michael@0: $.stateDown = flags >> 2 & 1; michael@0: $.stateOver = flags >> 1 & 1; michael@0: $.stateUp = flags & 1; michael@0: if (!eob) { michael@0: $.symbolId = readUi16($bytes, $stream); michael@0: $.depth = readUi16($bytes, $stream); michael@0: var $2 = $.matrix = {}; michael@0: matrix($bytes, $stream, $2, swfVersion, tagCode); michael@0: if (tagCode === 34) { michael@0: var $3 = $.cxform = {}; michael@0: cxform($bytes, $stream, $3, swfVersion, tagCode); michael@0: } michael@0: if (hasFilters) { michael@0: $.filterCount = readUi8($bytes, $stream); michael@0: var $4 = $.filters = {}; michael@0: anyFilter($bytes, $stream, $4, swfVersion, tagCode); michael@0: } michael@0: if (blend) { michael@0: $.blendMode = readUi8($bytes, $stream); michael@0: } michael@0: } michael@0: return { michael@0: eob: eob michael@0: }; michael@0: } michael@0: function buttonCondAction($bytes, $stream, $, swfVersion, tagCode) { michael@0: var buttonCondSize = readUi16($bytes, $stream); michael@0: var buttonConditions = readUi16($bytes, $stream); michael@0: $.idleToOverDown = buttonConditions >> 7 & 1; michael@0: $.outDownToIdle = buttonConditions >> 6 & 1; michael@0: $.outDownToOverDown = buttonConditions >> 5 & 1; michael@0: $.overDownToOutDown = buttonConditions >> 4 & 1; michael@0: $.overDownToOverUp = buttonConditions >> 3 & 1; michael@0: $.overUpToOverDown = buttonConditions >> 2 & 1; michael@0: $.overUpToIdle = buttonConditions >> 1 & 1; michael@0: $.idleToOverUp = buttonConditions & 1; michael@0: $.mouseEventFlags = buttonConditions & 511; michael@0: $.keyPress = buttonConditions >> 9 & 127; michael@0: $.overDownToIdle = buttonConditions >> 8 & 1; michael@0: if (!buttonCondSize) { michael@0: $.actionsData = readBinary($bytes, $stream, 0); michael@0: } else { michael@0: $.actionsData = readBinary($bytes, $stream, buttonCondSize - 4); michael@0: } michael@0: } michael@0: function shape($bytes, $stream, $, swfVersion, tagCode) { michael@0: var eos; michael@0: var temp = styleBits($bytes, $stream, $, swfVersion, tagCode); michael@0: var fillBits = temp.fillBits; michael@0: var lineBits = temp.lineBits; michael@0: var $4 = $.records = []; michael@0: do { michael@0: var $5 = {}; michael@0: var isMorph = false; michael@0: var hasStrokes = false; michael@0: var temp = shapeRecord($bytes, $stream, $5, swfVersion, tagCode, isMorph, fillBits, lineBits, hasStrokes, bits); michael@0: eos = temp.eos; michael@0: var fillBits = temp.fillBits; michael@0: var lineBits = temp.lineBits; michael@0: var bits = bits; michael@0: $4.push($5); michael@0: } while (!eos); michael@0: } michael@0: return { michael@0: 0: undefined, michael@0: 1: undefined, michael@0: 2: defineShape, michael@0: 4: placeObject, michael@0: 5: removeObject, michael@0: 6: defineImage, michael@0: 7: defineButton, michael@0: 8: defineJPEGTables, michael@0: 9: setBackgroundColor, michael@0: 10: defineFont, michael@0: 11: defineLabel, michael@0: 12: doAction, michael@0: 13: undefined, michael@0: 14: defineSound, michael@0: 15: startSound, michael@0: 17: undefined, michael@0: 18: soundStreamHead, michael@0: 19: soundStreamBlock, michael@0: 20: defineBitmap, michael@0: 21: defineImage, michael@0: 22: defineShape, michael@0: 23: undefined, michael@0: 24: undefined, michael@0: 26: placeObject, michael@0: 28: removeObject, michael@0: 32: defineShape, michael@0: 33: defineLabel, michael@0: 34: defineButton, michael@0: 35: defineImage, michael@0: 36: defineBitmap, michael@0: 37: defineText, michael@0: 39: undefined, michael@0: 43: frameLabel, michael@0: 45: soundStreamHead, michael@0: 46: defineShape, michael@0: 48: defineFont2, michael@0: 56: exportAssets, michael@0: 57: undefined, michael@0: 58: undefined, michael@0: 59: doAction, michael@0: 60: undefined, michael@0: 61: undefined, michael@0: 62: undefined, michael@0: 64: undefined, michael@0: 65: undefined, michael@0: 66: undefined, michael@0: 69: fileAttributes, michael@0: 70: placeObject, michael@0: 71: undefined, michael@0: 72: doABC, michael@0: 73: undefined, michael@0: 74: undefined, michael@0: 75: defineFont2, michael@0: 76: symbolClass, michael@0: 77: undefined, michael@0: 78: defineScalingGrid, michael@0: 82: doABC, michael@0: 83: defineShape, michael@0: 84: defineShape, michael@0: 86: defineScene, michael@0: 87: defineBinaryData, michael@0: 88: undefined, michael@0: 89: startSound, michael@0: 90: defineImage, michael@0: 91: undefined michael@0: }; michael@0: }(this); michael@0: var readHeader = function readHeader($bytes, $stream, $, swfVersion, tagCode) { michael@0: $ || ($ = {}); michael@0: var $0 = $.bbox = {}; michael@0: align($bytes, $stream); michael@0: var bits = readUb($bytes, $stream, 5); michael@0: var xMin = readSb($bytes, $stream, bits); michael@0: var xMax = readSb($bytes, $stream, bits); michael@0: var yMin = readSb($bytes, $stream, bits); michael@0: var yMax = readSb($bytes, $stream, bits); michael@0: $0.xMin = xMin; michael@0: $0.xMax = xMax; michael@0: $0.yMin = yMin; michael@0: $0.yMax = yMax; michael@0: align($bytes, $stream); michael@0: var frameRateFraction = readUi8($bytes, $stream); michael@0: $.frameRate = readUi8($bytes, $stream) + frameRateFraction / 256; michael@0: $.frameCount = readUi16($bytes, $stream); michael@0: return $; michael@0: }; michael@0: (function (global) { michael@0: global['tagHandler'] = tagHandler; michael@0: global['readHeader'] = readHeader; michael@0: }(this)); michael@0: function readTags(context, stream, swfVersion, final, onprogress, onexception) { michael@0: var tags = context.tags; michael@0: var bytes = stream.bytes; michael@0: var lastSuccessfulPosition; michael@0: var tag = null; michael@0: if (context._readTag) { michael@0: tag = context._readTag; michael@0: context._readTag = null; michael@0: } michael@0: try { michael@0: while (stream.pos < stream.end) { michael@0: lastSuccessfulPosition = stream.pos; michael@0: stream.ensure(2); michael@0: var tagCodeAndLength = readUi16(bytes, stream); michael@0: if (!tagCodeAndLength) { michael@0: final = true; michael@0: break; michael@0: } michael@0: var tagCode = tagCodeAndLength >> 6; michael@0: var length = tagCodeAndLength & 63; michael@0: if (length === 63) { michael@0: stream.ensure(4); michael@0: length = readUi32(bytes, stream); michael@0: } michael@0: if (tag) { michael@0: if (tagCode === 1 && tag.code === 1) { michael@0: tag.repeat++; michael@0: stream.pos += length; michael@0: continue; michael@0: } michael@0: tags.push(tag); michael@0: if (onprogress && tag.id !== undefined) { michael@0: onprogress(context); michael@0: } michael@0: tag = null; michael@0: } michael@0: stream.ensure(length); michael@0: var substream = stream.substream(stream.pos, stream.pos += length); michael@0: var subbytes = substream.bytes; michael@0: var nextTag = { michael@0: code: tagCode michael@0: }; michael@0: if (tagCode === 39) { michael@0: nextTag.type = 'sprite'; michael@0: nextTag.id = readUi16(subbytes, substream); michael@0: nextTag.frameCount = readUi16(subbytes, substream); michael@0: nextTag.tags = []; michael@0: readTags(nextTag, substream, swfVersion, true); michael@0: } else if (tagCode === 1) { michael@0: nextTag.repeat = 1; michael@0: } else { michael@0: var handler = tagHandler[tagCode]; michael@0: if (handler) { michael@0: handler(subbytes, substream, nextTag, swfVersion, tagCode); michael@0: } michael@0: } michael@0: tag = nextTag; michael@0: } michael@0: if (tag && final) { michael@0: tag.finalTag = true; michael@0: tags.push(tag); michael@0: if (onprogress) { michael@0: onprogress(context); michael@0: } michael@0: } else { michael@0: context._readTag = tag; michael@0: } michael@0: } catch (e) { michael@0: if (e !== StreamNoDataError) { michael@0: onexception && onexception(e); michael@0: throw e; michael@0: } michael@0: stream.pos = lastSuccessfulPosition; michael@0: context._readTag = tag; michael@0: } michael@0: } michael@0: function HeadTailBuffer(defaultSize) { michael@0: this.bufferSize = defaultSize || 16; michael@0: this.buffer = new Uint8Array(this.bufferSize); michael@0: this.pos = 0; michael@0: } michael@0: HeadTailBuffer.prototype = { michael@0: push: function (data, need) { michael@0: var bufferLengthNeed = this.pos + data.length; michael@0: if (this.bufferSize < bufferLengthNeed) { michael@0: var newBufferSize = this.bufferSize; michael@0: while (newBufferSize < bufferLengthNeed) { michael@0: newBufferSize <<= 1; michael@0: } michael@0: var newBuffer = new Uint8Array(newBufferSize); michael@0: if (this.bufferSize > 0) { michael@0: newBuffer.set(this.buffer); michael@0: } michael@0: this.buffer = newBuffer; michael@0: this.bufferSize = newBufferSize; michael@0: } michael@0: this.buffer.set(data, this.pos); michael@0: this.pos += data.length; michael@0: if (need) michael@0: return this.pos >= need; michael@0: }, michael@0: getHead: function (size) { michael@0: return this.buffer.subarray(0, size); michael@0: }, michael@0: getTail: function (offset) { michael@0: return this.buffer.subarray(offset, this.pos); michael@0: }, michael@0: removeHead: function (size) { michael@0: var tail = this.getTail(size); michael@0: this.buffer = new Uint8Array(this.bufferSize); michael@0: this.buffer.set(tail); michael@0: this.pos = tail.length; michael@0: }, michael@0: get arrayBuffer() { michael@0: return this.buffer.buffer; michael@0: }, michael@0: get length() { michael@0: return this.pos; michael@0: }, michael@0: createStream: function () { michael@0: return new Stream(this.arrayBuffer, 0, this.length); michael@0: } michael@0: }; michael@0: function CompressedPipe(target, length) { michael@0: this.target = target; michael@0: this.length = length; michael@0: this.initialize = true; michael@0: this.buffer = new HeadTailBuffer(8096); michael@0: this.state = { michael@0: bitBuffer: 0, michael@0: bitLength: 0, michael@0: compression: { michael@0: header: null, michael@0: distanceTable: null, michael@0: literalTable: null, michael@0: sym: null, michael@0: len: null, michael@0: sym2: null michael@0: } michael@0: }; michael@0: this.output = { michael@0: data: new Uint8Array(length), michael@0: available: 0, michael@0: completed: false michael@0: }; michael@0: } michael@0: CompressedPipe.prototype = { michael@0: push: function (data, progressInfo) { michael@0: var buffer = this.buffer; michael@0: if (this.initialize) { michael@0: if (!buffer.push(data, 2)) michael@0: return; michael@0: var headerBytes = buffer.getHead(2); michael@0: verifyDeflateHeader(headerBytes); michael@0: buffer.removeHead(2); michael@0: this.initialize = false; michael@0: } else { michael@0: buffer.push(data); michael@0: } michael@0: var stream = buffer.createStream(); michael@0: stream.bitBuffer = this.state.bitBuffer; michael@0: stream.bitLength = this.state.bitLength; michael@0: var output = this.output; michael@0: var lastAvailable = output.available; michael@0: try { michael@0: do { michael@0: inflateBlock(stream, output, this.state.compression); michael@0: } while (stream.pos < buffer.length && !output.completed); michael@0: } catch (e) { michael@0: if (e !== InflateNoDataError) michael@0: throw e; michael@0: } finally { michael@0: this.state.bitBuffer = stream.bitBuffer; michael@0: this.state.bitLength = stream.bitLength; michael@0: } michael@0: buffer.removeHead(stream.pos); michael@0: this.target.push(output.data.subarray(lastAvailable, output.available), progressInfo); michael@0: } michael@0: }; michael@0: function BodyParser(swfVersion, length, options) { michael@0: this.swf = { michael@0: swfVersion: swfVersion, michael@0: parseTime: 0 michael@0: }; michael@0: this.buffer = new HeadTailBuffer(32768); michael@0: this.initialize = true; michael@0: this.totalRead = 0; michael@0: this.length = length; michael@0: this.options = options; michael@0: } michael@0: BodyParser.prototype = { michael@0: push: function (data, progressInfo) { michael@0: if (data.length === 0) michael@0: return; michael@0: var swf = this.swf; michael@0: var swfVersion = swf.swfVersion; michael@0: var buffer = this.buffer; michael@0: var options = this.options; michael@0: var stream; michael@0: if (this.initialize) { michael@0: var PREFETCH_SIZE = 27; michael@0: if (!buffer.push(data, PREFETCH_SIZE)) michael@0: return; michael@0: stream = buffer.createStream(); michael@0: var bytes = stream.bytes; michael@0: readHeader(bytes, stream, swf); michael@0: var nextTagHeader = readUi16(bytes, stream); michael@0: var FILE_ATTRIBUTES_LENGTH = 4; michael@0: if (nextTagHeader == (SWF_TAG_CODE_FILE_ATTRIBUTES << 6 | FILE_ATTRIBUTES_LENGTH)) { michael@0: stream.ensure(FILE_ATTRIBUTES_LENGTH); michael@0: var substream = stream.substream(stream.pos, stream.pos += FILE_ATTRIBUTES_LENGTH); michael@0: var handler = tagHandler[SWF_TAG_CODE_FILE_ATTRIBUTES]; michael@0: var fileAttributesTag = { michael@0: code: SWF_TAG_CODE_FILE_ATTRIBUTES michael@0: }; michael@0: handler(substream.bytes, substream, fileAttributesTag, swfVersion, SWF_TAG_CODE_FILE_ATTRIBUTES); michael@0: swf.fileAttributes = fileAttributesTag; michael@0: } else { michael@0: stream.pos -= 2; michael@0: swf.fileAttributes = {}; michael@0: } michael@0: if (options.onstart) michael@0: options.onstart(swf); michael@0: swf.tags = []; michael@0: this.initialize = false; michael@0: } else { michael@0: buffer.push(data); michael@0: stream = buffer.createStream(); michael@0: } michael@0: var finalBlock = false; michael@0: if (progressInfo) { michael@0: swf.bytesLoaded = progressInfo.bytesLoaded; michael@0: swf.bytesTotal = progressInfo.bytesTotal; michael@0: finalBlock = progressInfo.bytesLoaded >= progressInfo.bytesTotal; michael@0: } michael@0: var readStartTime = performance.now(); michael@0: readTags(swf, stream, swfVersion, finalBlock, options.onprogress, options.onexception); michael@0: swf.parseTime += performance.now() - readStartTime; michael@0: var read = stream.pos; michael@0: buffer.removeHead(read); michael@0: this.totalRead += read; michael@0: if (options.oncomplete && swf.tags[swf.tags.length - 1].finalTag) { michael@0: options.oncomplete(swf); michael@0: } michael@0: } michael@0: }; michael@0: SWF.parseAsync = function swf_parseAsync(options) { michael@0: var buffer = new HeadTailBuffer(); michael@0: var pipe = { michael@0: push: function (data, progressInfo) { michael@0: if (this.target !== undefined) { michael@0: return this.target.push(data, progressInfo); michael@0: } michael@0: if (!buffer.push(data, 8)) { michael@0: return null; michael@0: } michael@0: var bytes = buffer.getHead(8); michael@0: var magic1 = bytes[0]; michael@0: var magic2 = bytes[1]; michael@0: var magic3 = bytes[2]; michael@0: if ((magic1 === 70 || magic1 === 67) && magic2 === 87 && magic3 === 83) { michael@0: var swfVersion = bytes[3]; michael@0: var compressed = magic1 === 67; michael@0: parseSWF(compressed, swfVersion, progressInfo); michael@0: buffer = null; michael@0: return; michael@0: } michael@0: var isImage = false; michael@0: var imageType; michael@0: if (magic1 === 255 && magic2 === 216 && magic3 === 255) { michael@0: isImage = true; michael@0: imageType = 'image/jpeg'; michael@0: } else if (magic1 === 137 && magic2 === 80 && magic3 === 78) { michael@0: isImage = true; michael@0: imageType = 'image/png'; michael@0: } michael@0: if (isImage) { michael@0: parseImage(data, progressInfo.bytesTotal, imageType); michael@0: } michael@0: buffer = null; michael@0: }, michael@0: close: function () { michael@0: if (buffer) { michael@0: var symbol = { michael@0: command: 'empty', michael@0: data: buffer.buffer.subarray(0, buffer.pos) michael@0: }; michael@0: options.oncomplete && options.oncomplete(symbol); michael@0: } michael@0: if (this.target !== undefined && this.target.close) { michael@0: this.target.close(); michael@0: } michael@0: } michael@0: }; michael@0: function parseSWF(compressed, swfVersion, progressInfo) { michael@0: var stream = buffer.createStream(); michael@0: stream.pos += 4; michael@0: var fileLength = readUi32(null, stream); michael@0: var bodyLength = fileLength - 8; michael@0: var target = new BodyParser(swfVersion, bodyLength, options); michael@0: if (compressed) { michael@0: target = new CompressedPipe(target, bodyLength); michael@0: } michael@0: target.push(buffer.getTail(8), progressInfo); michael@0: pipe['target'] = target; michael@0: } michael@0: function parseImage(data, bytesTotal, type) { michael@0: var buffer = new Uint8Array(bytesTotal); michael@0: buffer.set(data); michael@0: var bufferPos = data.length; michael@0: pipe['target'] = { michael@0: push: function (data) { michael@0: buffer.set(data, bufferPos); michael@0: bufferPos += data.length; michael@0: }, michael@0: close: function () { michael@0: var props = {}; michael@0: var chunks; michael@0: if (type == 'image/jpeg') { michael@0: chunks = parseJpegChunks(props, buffer); michael@0: } else { michael@0: chunks = [ michael@0: buffer michael@0: ]; michael@0: } michael@0: var symbol = { michael@0: type: 'image', michael@0: props: props, michael@0: data: new Blob(chunks, { michael@0: type: type michael@0: }) michael@0: }; michael@0: options.oncomplete && options.oncomplete(symbol); michael@0: } michael@0: }; michael@0: } michael@0: return pipe; michael@0: }; michael@0: SWF.parse = function (buffer, options) { michael@0: if (!options) michael@0: options = {}; michael@0: var pipe = SWF.parseAsync(options); michael@0: var bytes = new Uint8Array(buffer); michael@0: var progressInfo = { michael@0: bytesLoaded: bytes.length, michael@0: bytesTotal: bytes.length michael@0: }; michael@0: pipe.push(bytes, progressInfo); michael@0: pipe.close(); michael@0: }; michael@0: (function (global) { michael@0: global['SWF']['parse'] = SWF.parse; michael@0: global['SWF']['parseAsync'] = SWF.parseAsync; michael@0: }(this));