browser/extensions/shumway/content/shumway-worker.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /*
     2  * Copyright 2013 Mozilla Foundation
     3  *
     4  * Licensed under the Apache License, Version 2.0 (the "License");
     5  * you may not use this file except in compliance with the License.
     6  * You may obtain a copy of the License at
     7  *
     8  *     http://www.apache.org/licenses/LICENSE-2.0
     9  *
    10  * Unless required by applicable law or agreed to in writing, software
    11  * distributed under the License is distributed on an "AS IS" BASIS,
    12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  * See the License for the specific language governing permissions and
    14  * limitations under the License.
    15  */
    17 // This file is automatically generated
    19 (function (global) {
    20   if (global.DataView)
    21     return;
    22   if (!global.ArrayBuffer)
    23     fail('ArrayBuffer not supported');
    24   if (!Object.defineProperties)
    25     fail('This module requires ECMAScript 5');
    26   var nativele = new Int8Array(new Int32Array([
    27       1
    28     ]).buffer)[0] === 1;
    29   var temp = new Uint8Array(8);
    30   global.DataView = function DataView(buffer, offset, length) {
    31     if (!(buffer instanceof ArrayBuffer))
    32       fail('Bad ArrayBuffer');
    33     offset = offset || 0;
    34     length = length || buffer.byteLength - offset;
    35     if (offset < 0 || length < 0 || offset + length > buffer.byteLength)
    36       fail('Illegal offset and/or length');
    37     Object.defineProperties(this, {
    38       buffer: {
    39         value: buffer,
    40         enumerable: false,
    41         writable: false,
    42         configurable: false
    43       },
    44       byteOffset: {
    45         value: offset,
    46         enumerable: false,
    47         writable: false,
    48         configurable: false
    49       },
    50       byteLength: {
    51         value: length,
    52         enumerable: false,
    53         writable: false,
    54         configurable: false
    55       },
    56       _bytes: {
    57         value: new Uint8Array(buffer, offset, length),
    58         enumerable: false,
    59         writable: false,
    60         configurable: false
    61       }
    62     });
    63   };
    64   global.DataView.prototype = {
    65     constructor: DataView,
    66     getInt8: function getInt8(offset) {
    67       return get(this, Int8Array, 1, offset);
    68     },
    69     getUint8: function getUint8(offset) {
    70       return get(this, Uint8Array, 1, offset);
    71     },
    72     getInt16: function getInt16(offset, le) {
    73       return get(this, Int16Array, 2, offset, le);
    74     },
    75     getUint16: function getUint16(offset, le) {
    76       return get(this, Uint16Array, 2, offset, le);
    77     },
    78     getInt32: function getInt32(offset, le) {
    79       return get(this, Int32Array, 4, offset, le);
    80     },
    81     getUint32: function getUint32(offset, le) {
    82       return get(this, Uint32Array, 4, offset, le);
    83     },
    84     getFloat32: function getFloat32(offset, le) {
    85       return get(this, Float32Array, 4, offset, le);
    86     },
    87     getFloat64: function getFloat32(offset, le) {
    88       return get(this, Float64Array, 8, offset, le);
    89     },
    90     setInt8: function setInt8(offset, value) {
    91       set(this, Int8Array, 1, offset, value);
    92     },
    93     setUint8: function setUint8(offset, value) {
    94       set(this, Uint8Array, 1, offset, value);
    95     },
    96     setInt16: function setInt16(offset, value, le) {
    97       set(this, Int16Array, 2, offset, value, le);
    98     },
    99     setUint16: function setUint16(offset, value, le) {
   100       set(this, Uint16Array, 2, offset, value, le);
   101     },
   102     setInt32: function setInt32(offset, value, le) {
   103       set(this, Int32Array, 4, offset, value, le);
   104     },
   105     setUint32: function setUint32(offset, value, le) {
   106       set(this, Uint32Array, 4, offset, value, le);
   107     },
   108     setFloat32: function setFloat32(offset, value, le) {
   109       set(this, Float32Array, 4, offset, value, le);
   110     },
   111     setFloat64: function setFloat64(offset, value, le) {
   112       set(this, Float64Array, 8, offset, value, le);
   113     }
   114   };
   115   function get(view, type, size, offset, le) {
   116     if (offset === undefined)
   117       fail('Missing required offset argument');
   118     if (offset < 0 || offset + size > view.byteLength)
   119       fail('Invalid index: ' + offset);
   120     if (size === 1 || !(!le) === nativele) {
   121       if ((view.byteOffset + offset) % size === 0)
   122         return new type(view.buffer, view.byteOffset + offset, 1)[0];
   123       else {
   124         for (var i = 0; i < size; i++)
   125           temp[i] = view._bytes[offset + i];
   126         return new type(temp.buffer)[0];
   127       }
   128     } else {
   129       for (var i = 0; i < size; i++)
   130         temp[size - i - 1] = view._bytes[offset + i];
   131       return new type(temp.buffer)[0];
   132     }
   133   }
   134   function set(view, type, size, offset, value, le) {
   135     if (offset === undefined)
   136       fail('Missing required offset argument');
   137     if (value === undefined)
   138       fail('Missing required value argument');
   139     if (offset < 0 || offset + size > view.byteLength)
   140       fail('Invalid index: ' + offset);
   141     if (size === 1 || !(!le) === nativele) {
   142       if ((view.byteOffset + offset) % size === 0) {
   143         new type(view.buffer, view.byteOffset + offset, 1)[0] = value;
   144       } else {
   145         new type(temp.buffer)[0] = value;
   146         for (var i = 0; i < size; i++)
   147           view._bytes[i + offset] = temp[i];
   148       }
   149     } else {
   150       new type(temp.buffer)[0] = value;
   151       for (var i = 0; i < size; i++)
   152         view._bytes[offset + i] = temp[size - 1 - i];
   153     }
   154   }
   155   function fail(msg) {
   156     throw new Error(msg);
   157   }
   158 }(this));
   159 var create = Object.create;
   160 var defineProperty = Object.defineProperty;
   161 var keys = Object.keys;
   162 var isArray = Array.isArray;
   163 var fromCharCode = String.fromCharCode;
   164 var logE = Math.log;
   165 var max = Math.max;
   166 var min = Math.min;
   167 var pow = Math.pow;
   168 var push = Array.prototype.push;
   169 var slice = Array.prototype.slice;
   170 var splice = Array.prototype.splice;
   171 function fail(msg, context) {
   172   throw new Error((context ? context + ': ' : '') + msg);
   173 }
   174 function assert(cond, msg, context) {
   175   if (!cond)
   176     fail(msg, context);
   177 }
   178 function scriptProperties(namespace, props) {
   179   return props.reduce(function (o, p) {
   180     o[p] = namespace + ' ' + p;
   181     return o;
   182   }, {});
   183 }
   184 function cloneObject(obj) {
   185   var clone = Object.create(null);
   186   for (var prop in obj)
   187     clone[prop] = obj[prop];
   188   return clone;
   189 }
   190 function sortNumeric(a, b) {
   191   return a - b;
   192 }
   193 function sortByZindex(a, b) {
   194   return a._zindex - b._zindex;
   195 }
   196 function rgbaObjToStr(color) {
   197   return 'rgba(' + color.red + ',' + color.green + ',' + color.blue + ',' + color.alpha / 255 + ')';
   198 }
   199 function rgbIntAlphaToStr(color, alpha) {
   200   color |= 0;
   201   if (alpha >= 1) {
   202     var colorStr = color.toString(16);
   203     while (colorStr.length < 6) {
   204       colorStr = '0' + colorStr;
   205     }
   206     return '#' + colorStr;
   207   }
   208   var red = color >> 16 & 255;
   209   var green = color >> 8 & 255;
   210   var blue = color & 255;
   211   return 'rgba(' + red + ',' + green + ',' + blue + ',' + alpha + ')';
   212 }
   213 function argbUintToStr(argb) {
   214   return 'rgba(' + (argb >>> 16 & 255) + ',' + (argb >>> 8 & 255) + ',' + (argb & 255) + ',' + (argb >>> 24 & 255) / 255 + ')';
   215 }
   216 (function functionNameSupport() {
   217   if (eval('function t() {} t.name === \'t\'')) {
   218     return;
   219   }
   220   Object.defineProperty(Function.prototype, 'name', {
   221     get: function () {
   222       if (this.__name) {
   223         return this.__name;
   224       }
   225       var m = /function\s([^\(]+)/.exec(this.toString());
   226       var name = m && m[1] !== 'anonymous' ? m[1] : null;
   227       this.__name = name;
   228       return name;
   229     },
   230     configurable: true,
   231     enumerable: false
   232   });
   233 }());
   234 var randomStyleCache;
   235 var nextStyle = 0;
   236 function randomStyle() {
   237   if (!randomStyleCache) {
   238     randomStyleCache = [
   239       '#ff5e3a',
   240       '#ff9500',
   241       '#ffdb4c',
   242       '#87fc70',
   243       '#52edc7',
   244       '#1ad6fd',
   245       '#c644fc',
   246       '#ef4db6',
   247       '#4a4a4a',
   248       '#dbddde',
   249       '#ff3b30',
   250       '#ff9500',
   251       '#ffcc00',
   252       '#4cd964',
   253       '#34aadc',
   254       '#007aff',
   255       '#5856d6',
   256       '#ff2d55',
   257       '#8e8e93',
   258       '#c7c7cc',
   259       '#5ad427',
   260       '#c86edf',
   261       '#d1eefc',
   262       '#e0f8d8',
   263       '#fb2b69',
   264       '#f7f7f7',
   265       '#1d77ef',
   266       '#d6cec3',
   267       '#55efcb',
   268       '#ff4981',
   269       '#ffd3e0',
   270       '#f7f7f7',
   271       '#ff1300',
   272       '#1f1f21',
   273       '#bdbec2',
   274       '#ff3a2d'
   275     ];
   276   }
   277   return randomStyleCache[nextStyle++ % randomStyleCache.length];
   278 }
   279 (function PromiseClosure() {
   280   var global = Function('return this')();
   281   if (global.Promise) {
   282     if (typeof global.Promise.all !== 'function') {
   283       global.Promise.all = function (iterable) {
   284         var count = 0, results = [], resolve, reject;
   285         var promise = new global.Promise(function (resolve_, reject_) {
   286             resolve = resolve_;
   287             reject = reject_;
   288           });
   289         iterable.forEach(function (p, i) {
   290           count++;
   291           p.then(function (result) {
   292             results[i] = result;
   293             count--;
   294             if (count === 0) {
   295               resolve(results);
   296             }
   297           }, reject);
   298         });
   299         if (count === 0) {
   300           resolve(results);
   301         }
   302         return promise;
   303       };
   304     }
   305     if (typeof global.Promise.resolve !== 'function') {
   306       global.Promise.resolve = function (x) {
   307         return new global.Promise(function (resolve) {
   308           resolve(x);
   309         });
   310       };
   311     }
   312     return;
   313   }
   314   function getDeferred(C) {
   315     if (typeof C !== 'function') {
   316       throw new TypeError('Invalid deferred constructor');
   317     }
   318     var resolver = createDeferredConstructionFunctions();
   319     var promise = new C(resolver);
   320     var resolve = resolver.resolve;
   321     if (typeof resolve !== 'function') {
   322       throw new TypeError('Invalid resolve construction function');
   323     }
   324     var reject = resolver.reject;
   325     if (typeof reject !== 'function') {
   326       throw new TypeError('Invalid reject construction function');
   327     }
   328     return {
   329       promise: promise,
   330       resolve: resolve,
   331       reject: reject
   332     };
   333   }
   334   function updateDeferredFromPotentialThenable(x, deferred) {
   335     if (typeof x !== 'object' || x === null) {
   336       return false;
   337     }
   338     try {
   339       var then = x.then;
   340       if (typeof then !== 'function') {
   341         return false;
   342       }
   343       var thenCallResult = then.call(x, deferred.resolve, deferred.reject);
   344     } catch (e) {
   345       var reject = deferred.reject;
   346       reject(e);
   347     }
   348     return true;
   349   }
   350   function isPromise(x) {
   351     return typeof x === 'object' && x !== null && typeof x.promiseStatus !== 'undefined';
   352   }
   353   function rejectPromise(promise, reason) {
   354     if (promise.promiseStatus !== 'unresolved') {
   355       return;
   356     }
   357     var reactions = promise.rejectReactions;
   358     promise.result = reason;
   359     promise.resolveReactions = undefined;
   360     promise.rejectReactions = undefined;
   361     promise.promiseStatus = 'has-rejection';
   362     triggerPromiseReactions(reactions, reason);
   363   }
   364   function resolvePromise(promise, resolution) {
   365     if (promise.promiseStatus !== 'unresolved') {
   366       return;
   367     }
   368     var reactions = promise.resolveReactions;
   369     promise.result = resolution;
   370     promise.resolveReactions = undefined;
   371     promise.rejectReactions = undefined;
   372     promise.promiseStatus = 'has-resolution';
   373     triggerPromiseReactions(reactions, resolution);
   374   }
   375   function triggerPromiseReactions(reactions, argument) {
   376     for (var i = 0; i < reactions.length; i++) {
   377       queueMicrotask({
   378         reaction: reactions[i],
   379         argument: argument
   380       });
   381     }
   382   }
   383   function queueMicrotask(task) {
   384     if (microtasksQueue.length === 0) {
   385       setTimeout(handleMicrotasksQueue, 0);
   386     }
   387     microtasksQueue.push(task);
   388   }
   389   function executePromiseReaction(reaction, argument) {
   390     var deferred = reaction.deferred;
   391     var handler = reaction.handler;
   392     var handlerResult, updateResult;
   393     try {
   394       handlerResult = handler(argument);
   395     } catch (e) {
   396       var reject = deferred.reject;
   397       return reject(e);
   398     }
   399     if (handlerResult === deferred.promise) {
   400       var reject = deferred.reject;
   401       return reject(new TypeError('Self resolution'));
   402     }
   403     try {
   404       updateResult = updateDeferredFromPotentialThenable(handlerResult, deferred);
   405       if (!updateResult) {
   406         var resolve = deferred.resolve;
   407         return resolve(handlerResult);
   408       }
   409     } catch (e) {
   410       var reject = deferred.reject;
   411       return reject(e);
   412     }
   413   }
   414   var microtasksQueue = [];
   415   function handleMicrotasksQueue() {
   416     while (microtasksQueue.length > 0) {
   417       var task = microtasksQueue[0];
   418       try {
   419         executePromiseReaction(task.reaction, task.argument);
   420       } catch (e) {
   421         if (typeof Promise.onerror === 'function') {
   422           Promise.onerror(e);
   423         }
   424       }
   425       microtasksQueue.shift();
   426     }
   427   }
   428   function throwerFunction(e) {
   429     throw e;
   430   }
   431   function identityFunction(x) {
   432     return x;
   433   }
   434   function createRejectPromiseFunction(promise) {
   435     return function (reason) {
   436       rejectPromise(promise, reason);
   437     };
   438   }
   439   function createResolvePromiseFunction(promise) {
   440     return function (resolution) {
   441       resolvePromise(promise, resolution);
   442     };
   443   }
   444   function createDeferredConstructionFunctions() {
   445     var fn = function (resolve, reject) {
   446       fn.resolve = resolve;
   447       fn.reject = reject;
   448     };
   449     return fn;
   450   }
   451   function createPromiseResolutionHandlerFunctions(promise, fulfillmentHandler, rejectionHandler) {
   452     return function (x) {
   453       if (x === promise) {
   454         return rejectionHandler(new TypeError('Self resolution'));
   455       }
   456       var cstr = promise.promiseConstructor;
   457       if (isPromise(x)) {
   458         var xConstructor = x.promiseConstructor;
   459         if (xConstructor === cstr) {
   460           return x.then(fulfillmentHandler, rejectionHandler);
   461         }
   462       }
   463       var deferred = getDeferred(cstr);
   464       var updateResult = updateDeferredFromPotentialThenable(x, deferred);
   465       if (updateResult) {
   466         var deferredPromise = deferred.promise;
   467         return deferredPromise.then(fulfillmentHandler, rejectionHandler);
   468       }
   469       return fulfillmentHandler(x);
   470     };
   471   }
   472   function createPromiseAllCountdownFunction(index, values, deferred, countdownHolder) {
   473     return function (x) {
   474       values[index] = x;
   475       countdownHolder.countdown--;
   476       if (countdownHolder.countdown === 0) {
   477         deferred.resolve(values);
   478       }
   479     };
   480   }
   481   function Promise(resolver) {
   482     if (typeof resolver !== 'function') {
   483       throw new TypeError('resolver is not a function');
   484     }
   485     var promise = this;
   486     if (typeof promise !== 'object') {
   487       throw new TypeError('Promise to initialize is not an object');
   488     }
   489     promise.promiseStatus = 'unresolved';
   490     promise.resolveReactions = [];
   491     promise.rejectReactions = [];
   492     promise.result = undefined;
   493     var resolve = createResolvePromiseFunction(promise);
   494     var reject = createRejectPromiseFunction(promise);
   495     try {
   496       var result = resolver(resolve, reject);
   497     } catch (e) {
   498       rejectPromise(promise, e);
   499     }
   500     promise.promiseConstructor = Promise;
   501     return promise;
   502   }
   503   Promise.all = function (iterable) {
   504     var deferred = getDeferred(this);
   505     var values = [];
   506     var countdownHolder = {
   507         countdown: 0
   508       };
   509     var index = 0;
   510     iterable.forEach(function (nextValue) {
   511       var nextPromise = this.cast(nextValue);
   512       var fn = createPromiseAllCountdownFunction(index, values, deferred, countdownHolder);
   513       nextPromise.then(fn, deferred.reject);
   514       index++;
   515       countdownHolder.countdown++;
   516     }, this);
   517     if (index === 0) {
   518       deferred.resolve(values);
   519     }
   520     return deferred.promise;
   521   };
   522   Promise.cast = function (x) {
   523     if (isPromise(x)) {
   524       return x;
   525     }
   526     var deferred = getDeferred(this);
   527     deferred.resolve(x);
   528     return deferred.promise;
   529   };
   530   Promise.reject = function (r) {
   531     var deferred = getDeferred(this);
   532     var rejectResult = deferred.reject(r);
   533     return deferred.promise;
   534   };
   535   Promise.resolve = function (x) {
   536     var deferred = getDeferred(this);
   537     var rejectResult = deferred.resolve(x);
   538     return deferred.promise;
   539   };
   540   Promise.prototype = {
   541     'catch': function (onRejected) {
   542       this.then(undefined, onRejected);
   543     },
   544     then: function (onFulfilled, onRejected) {
   545       var promise = this;
   546       if (!isPromise(promise)) {
   547         throw new TypeError('this is not a Promises');
   548       }
   549       var cstr = promise.promiseConstructor;
   550       var deferred = getDeferred(cstr);
   551       var rejectionHandler = typeof onRejected === 'function' ? onRejected : throwerFunction;
   552       var fulfillmentHandler = typeof onFulfilled === 'function' ? onFulfilled : identityFunction;
   553       var resolutionHandler = createPromiseResolutionHandlerFunctions(promise, fulfillmentHandler, rejectionHandler);
   554       var resolveReaction = {
   555           deferred: deferred,
   556           handler: resolutionHandler
   557         };
   558       var rejectReaction = {
   559           deferred: deferred,
   560           handler: rejectionHandler
   561         };
   562       switch (promise.promiseStatus) {
   563       case 'unresolved':
   564         promise.resolveReactions.push(resolveReaction);
   565         promise.rejectReactions.push(rejectReaction);
   566         break;
   567       case 'has-resolution':
   568         var resolution = promise.result;
   569         queueMicrotask({
   570           reaction: resolveReaction,
   571           argument: resolution
   572         });
   573         break;
   574       case 'has-rejection':
   575         var rejection = promise.result;
   576         queueMicrotask({
   577           reaction: rejectReaction,
   578           argument: rejection
   579         });
   580         break;
   581       }
   582       return deferred.promise;
   583     }
   584   };
   585   global.Promise = Promise;
   586 }());
   587 var QuadTree = function (x, y, width, height, parent) {
   588   this.x = x | 0;
   589   this.y = y | 0;
   590   this.width = width | 0;
   591   this.height = height | 0;
   592   if (parent) {
   593     this.root = parent.root;
   594     this.parent = parent;
   595     this.level = parent.level + 1;
   596   } else {
   597     this.root = this;
   598     this.parent = null;
   599     this.level = 0;
   600   }
   601   this.reset();
   602 };
   603 QuadTree.prototype.reset = function () {
   604   this.stuckObjects = null;
   605   this.objects = null;
   606   this.nodes = [];
   607 };
   608 QuadTree.prototype._findIndex = function (xMin, xMax, yMin, yMax) {
   609   var midX = this.x + (this.width / 2 | 0);
   610   var midY = this.y + (this.height / 2 | 0);
   611   var top = yMin < midY && yMax < midY;
   612   var bottom = yMin > midY;
   613   if (xMin < midX && xMax < midX) {
   614     if (top) {
   615       return 1;
   616     } else if (bottom) {
   617       return 2;
   618     }
   619   } else if (xMin > midX) {
   620     if (top) {
   621       return 0;
   622     } else if (bottom) {
   623       return 3;
   624     }
   625   }
   626   return -1;
   627 };
   628 QuadTree.prototype.insert = function (obj) {
   629   var nodes = this.nodes;
   630   if (nodes.length) {
   631     var index = this._findIndex(obj.xMin, obj.xMax, obj.yMin, obj.yMax);
   632     if (index > -1) {
   633       nodes[index].insert(obj);
   634     } else {
   635       obj.prev = null;
   636       if (this.stuckObjects) {
   637         obj.next = this.stuckObjects;
   638         this.stuckObjects.prev = obj;
   639       } else {
   640         obj.next = null;
   641       }
   642       this.stuckObjects = obj;
   643       obj.parent = this;
   644     }
   645     return;
   646   }
   647   var numChildren = 1;
   648   var item = this.objects;
   649   if (!item) {
   650     obj.prev = null;
   651     obj.next = null;
   652     this.objects = obj;
   653   } else {
   654     while (item.next) {
   655       numChildren++;
   656       item = item.next;
   657     }
   658     obj.prev = item;
   659     obj.next = null;
   660     item.next = obj;
   661   }
   662   if (numChildren > 4 && this.level < 10) {
   663     this._subdivide();
   664     item = this.objects;
   665     while (item) {
   666       var next = item.next;
   667       this.insert(item);
   668       item = next;
   669     }
   670     this.objects = null;
   671     return;
   672   }
   673   obj.parent = this;
   674 };
   675 QuadTree.prototype.update = function (obj) {
   676   var node = obj.parent;
   677   if (node) {
   678     if (obj.xMin >= node.x && obj.xMax <= node.x + node.width && obj.yMin >= node.y && obj.yMax <= node.y + node.height) {
   679       if (node.nodes.length) {
   680         var index = this._findIndex(obj.xMin, obj.xMax, obj.yMin, obj.yMax);
   681         if (index > -1) {
   682           node.remove(obj);
   683           node = this.nodes[index];
   684           node.insert(obj);
   685         }
   686       } else {
   687         node.remove(obj);
   688         node.insert(obj);
   689       }
   690       return;
   691     }
   692     node.remove(obj);
   693   }
   694   this.root.insert(obj);
   695 };
   696 QuadTree.prototype.remove = function (obj) {
   697   var prev = obj.prev;
   698   var next = obj.next;
   699   if (prev) {
   700     prev.next = next;
   701     obj.prev = null;
   702   } else {
   703     var node = obj.parent;
   704     if (node.objects === obj) {
   705       node.objects = next;
   706     } else if (node.stuckObjects === obj) {
   707       node.stuckObjects = next;
   708     }
   709   }
   710   if (next) {
   711     next.prev = prev;
   712     obj.next = null;
   713   }
   714   obj.parent = null;
   715 };
   716 QuadTree.prototype.retrieve = function (xMin, xMax, yMin, yMax) {
   717   var stack = [];
   718   var out = [];
   719   var node = this;
   720   do {
   721     if (node.nodes.length) {
   722       var index = node._findIndex(xMin, xMax, yMin, yMax);
   723       if (index > -1) {
   724         stack.push(node.nodes[index]);
   725       } else {
   726         stack.push.apply(stack, node.nodes);
   727       }
   728     }
   729     var item = node.objects;
   730     for (var i = 0; i < 2; i++) {
   731       while (item) {
   732         if (!(item.xMin > xMax || item.xMax < xMin || item.yMin > yMax || item.yMax < yMin)) {
   733           out.push(item);
   734         }
   735         item = item.next;
   736       }
   737       item = node.stuckObjects;
   738     }
   739     node = stack.pop();
   740   } while (node);
   741   return out;
   742 };
   743 QuadTree.prototype._subdivide = function () {
   744   var halfWidth = this.width / 2 | 0;
   745   var halfHeight = this.height / 2 | 0;
   746   var midX = this.x + halfWidth;
   747   var midY = this.y + halfHeight;
   748   this.nodes[0] = new QuadTree(midX, this.y, halfWidth, halfHeight, this);
   749   this.nodes[1] = new QuadTree(this.x, this.y, halfWidth, halfHeight, this);
   750   this.nodes[2] = new QuadTree(this.x, midY, halfWidth, halfHeight, this);
   751   this.nodes[3] = new QuadTree(midX, midY, halfWidth, halfHeight, this);
   752 };
   753 var RegionCluster = function () {
   754   this.regions = [];
   755 };
   756 RegionCluster.prototype.reset = function () {
   757   this.regions.length = 0;
   758 };
   759 RegionCluster.prototype.insert = function (region) {
   760   var regions = this.regions;
   761   if (regions.length < 3) {
   762     regions.push({
   763       xMin: region.xMin,
   764       xMax: region.xMax,
   765       yMin: region.yMin,
   766       yMax: region.yMax
   767     });
   768     return;
   769   }
   770   var a = region;
   771   var b = regions[0];
   772   var c = regions[1];
   773   var d = regions[2];
   774   var ab = (max(a.xMax, b.xMax) - min(a.xMin, b.xMin)) * (max(a.yMax, b.yMax) - min(a.yMin, b.yMin));
   775   var rb = regions[0];
   776   var ac = (max(a.xMax, c.xMax) - min(a.xMin, c.xMin)) * (max(a.yMax, c.yMax) - min(a.yMin, c.yMin));
   777   var ad = (max(a.xMax, d.xMax) - min(a.xMin, d.xMin)) * (max(a.yMax, d.yMax) - min(a.yMin, d.yMin));
   778   if (ac < ab) {
   779     ab = ac;
   780     rb = c;
   781   }
   782   if (ad < ab) {
   783     ab = ad;
   784     rb = d;
   785   }
   786   var bc = (max(b.xMax, c.xMax) - min(b.xMin, c.xMin)) * (max(b.yMax, c.yMax) - min(b.yMin, c.yMin));
   787   var bd = (max(b.xMax, d.xMax) - min(b.xMin, d.xMin)) * (max(b.yMax, d.yMax) - min(b.yMin, d.yMin));
   788   var cd = (max(c.xMax, d.xMax) - min(c.xMin, d.xMin)) * (max(c.yMax, d.yMax) - min(c.yMin, d.yMin));
   789   if (ab < bc && ab < bd && ab < cd) {
   790     if (a.xMin < rb.xMin) {
   791       rb.xMin = a.xMin;
   792     }
   793     if (a.xMax > rb.xMax) {
   794       rb.xMax = a.xMax;
   795     }
   796     if (a.yMin < rb.yMin) {
   797       rb.yMin = a.yMin;
   798     }
   799     if (a.yMax > rb.yMax) {
   800       rb.yMax = a.yMax;
   801     }
   802     return;
   803   }
   804   rb = regions[0];
   805   var rc = regions[1];
   806   if (bd < bc) {
   807     bc = bd;
   808     rc = regions[2];
   809   }
   810   if (cd < bc) {
   811     rb = regions[1];
   812     rc = regions[2];
   813   }
   814   if (rc.xMin < rb.xMin) {
   815     rb.xMin = rc.xMin;
   816   }
   817   if (rc.xMax > rb.xMax) {
   818     rb.xMax = rc.xMax;
   819   }
   820   if (rc.yMin < rb.yMin) {
   821     rb.yMin = rc.yMin;
   822   }
   823   if (rc.yMax > rb.yMax) {
   824     rb.yMax = rc.yMax;
   825   }
   826   rc.xMin = a.xMin;
   827   rc.xMax = a.xMax;
   828   rc.yMin = a.yMin;
   829   rc.yMax = a.yMax;
   830 };
   831 RegionCluster.prototype.retrieve = function () {
   832   return this.regions;
   833 };
   834 var EXTERNAL_INTERFACE_FEATURE = 1;
   835 var CLIPBOARD_FEATURE = 2;
   836 var SHAREDOBJECT_FEATURE = 3;
   837 var VIDEO_FEATURE = 4;
   838 var SOUND_FEATURE = 5;
   839 var NETCONNECTION_FEATURE = 6;
   840 if (!this.performance) {
   841   this.performance = {};
   842 }
   843 if (!this.performance.now) {
   844   this.performance.now = Date.now;
   845 }
   846 var SWF_TAG_CODE_CSM_TEXT_SETTINGS = 74;
   847 var SWF_TAG_CODE_DEFINE_BINARY_DATA = 87;
   848 var SWF_TAG_CODE_DEFINE_BITS = 6;
   849 var SWF_TAG_CODE_DEFINE_BITS_JPEG2 = 21;
   850 var SWF_TAG_CODE_DEFINE_BITS_JPEG3 = 35;
   851 var SWF_TAG_CODE_DEFINE_BITS_JPEG4 = 90;
   852 var SWF_TAG_CODE_DEFINE_BITS_LOSSLESS = 20;
   853 var SWF_TAG_CODE_DEFINE_BITS_LOSSLESS2 = 36;
   854 var SWF_TAG_CODE_DEFINE_BUTTON = 7;
   855 var SWF_TAG_CODE_DEFINE_BUTTON2 = 34;
   856 var SWF_TAG_CODE_DEFINE_BUTTON_CXFORM = 23;
   857 var SWF_TAG_CODE_DEFINE_BUTTON_SOUND = 17;
   858 var SWF_TAG_CODE_DEFINE_EDIT_TEXT = 37;
   859 var SWF_TAG_CODE_DEFINE_FONT = 10;
   860 var SWF_TAG_CODE_DEFINE_FONT2 = 48;
   861 var SWF_TAG_CODE_DEFINE_FONT3 = 75;
   862 var SWF_TAG_CODE_DEFINE_FONT4 = 91;
   863 var SWF_TAG_CODE_DEFINE_FONT_ALIGN_ZONES = 73;
   864 var SWF_TAG_CODE_DEFINE_FONT_INFO = 13;
   865 var SWF_TAG_CODE_DEFINE_FONT_INFO2 = 62;
   866 var SWF_TAG_CODE_DEFINE_FONT_NAME = 88;
   867 var SWF_TAG_CODE_DEFINE_MORPH_SHAPE = 46;
   868 var SWF_TAG_CODE_DEFINE_MORPH_SHAPE2 = 84;
   869 var SWF_TAG_CODE_DEFINE_SCALING_GRID = 78;
   870 var SWF_TAG_CODE_DEFINE_SCENE_AND_FRAME_LABEL_DATA = 86;
   871 var SWF_TAG_CODE_DEFINE_SHAPE = 2;
   872 var SWF_TAG_CODE_DEFINE_SHAPE2 = 22;
   873 var SWF_TAG_CODE_DEFINE_SHAPE3 = 32;
   874 var SWF_TAG_CODE_DEFINE_SHAPE4 = 83;
   875 var SWF_TAG_CODE_DEFINE_SOUND = 14;
   876 var SWF_TAG_CODE_DEFINE_SPRITE = 39;
   877 var SWF_TAG_CODE_DEFINE_TEXT = 11;
   878 var SWF_TAG_CODE_DEFINE_TEXT2 = 33;
   879 var SWF_TAG_CODE_DEFINE_VIDEO_STREAM = 60;
   880 var SWF_TAG_CODE_DO_ABC = 82;
   881 var SWF_TAG_CODE_DO_ABC_ = 72;
   882 var SWF_TAG_CODE_DO_ACTION = 12;
   883 var SWF_TAG_CODE_DO_INIT_ACTION = 59;
   884 var SWF_TAG_CODE_ENABLE_DEBUGGER = 58;
   885 var SWF_TAG_CODE_ENABLE_DEBUGGER2 = 64;
   886 var SWF_TAG_CODE_END = 0;
   887 var SWF_TAG_CODE_EXPORT_ASSETS = 56;
   888 var SWF_TAG_CODE_FILE_ATTRIBUTES = 69;
   889 var SWF_TAG_CODE_FRAME_LABEL = 43;
   890 var SWF_TAG_CODE_IMPORT_ASSETS = 57;
   891 var SWF_TAG_CODE_IMPORT_ASSETS2 = 71;
   892 var SWF_TAG_CODE_JPEG_TABLES = 8;
   893 var SWF_TAG_CODE_METADATA = 77;
   894 var SWF_TAG_CODE_PLACE_OBJECT = 4;
   895 var SWF_TAG_CODE_PLACE_OBJECT2 = 26;
   896 var SWF_TAG_CODE_PLACE_OBJECT3 = 70;
   897 var SWF_TAG_CODE_PROTECT = 24;
   898 var SWF_TAG_CODE_REMOVE_OBJECT = 5;
   899 var SWF_TAG_CODE_REMOVE_OBJECT2 = 28;
   900 var SWF_TAG_CODE_SCRIPT_LIMITS = 65;
   901 var SWF_TAG_CODE_SET_BACKGROUND_COLOR = 9;
   902 var SWF_TAG_CODE_SET_TAB_INDEX = 66;
   903 var SWF_TAG_CODE_SHOW_FRAME = 1;
   904 var SWF_TAG_CODE_SOUND_STREAM_BLOCK = 19;
   905 var SWF_TAG_CODE_SOUND_STREAM_HEAD = 18;
   906 var SWF_TAG_CODE_SOUND_STREAM_HEAD2 = 45;
   907 var SWF_TAG_CODE_START_SOUND = 15;
   908 var SWF_TAG_CODE_START_SOUND2 = 89;
   909 var SWF_TAG_CODE_SYMBOL_CLASS = 76;
   910 var SWF_TAG_CODE_VIDEO_FRAME = 61;
   911 self.SWF = {};
   912 var FORMAT_COLORMAPPED = 3;
   913 var FORMAT_15BPP = 4;
   914 var FORMAT_24BPP = 5;
   915 var FACTOR_5BBP = 255 / 31;
   916 var crcTable = [];
   917 for (var i = 0; i < 256; i++) {
   918   var c = i;
   919   for (var h = 0; h < 8; h++) {
   920     if (c & 1)
   921       c = 3988292384 ^ c >> 1 & 2147483647;
   922     else
   923       c = c >> 1 & 2147483647;
   924   }
   925   crcTable[i] = c;
   926 }
   927 function crc32(data, start, end) {
   928   var crc = -1;
   929   for (var i = start; i < end; i++) {
   930     var a = (crc ^ data[i]) & 255;
   931     var b = crcTable[a];
   932     crc = crc >>> 8 ^ b;
   933   }
   934   return crc ^ -1;
   935 }
   936 function createPngChunk(type, data) {
   937   var chunk = new Uint8Array(12 + data.length);
   938   var p = 0;
   939   var len = data.length;
   940   chunk[p] = len >> 24 & 255;
   941   chunk[p + 1] = len >> 16 & 255;
   942   chunk[p + 2] = len >> 8 & 255;
   943   chunk[p + 3] = len & 255;
   944   chunk[p + 4] = type.charCodeAt(0) & 255;
   945   chunk[p + 5] = type.charCodeAt(1) & 255;
   946   chunk[p + 6] = type.charCodeAt(2) & 255;
   947   chunk[p + 7] = type.charCodeAt(3) & 255;
   948   if (data instanceof Uint8Array)
   949     chunk.set(data, 8);
   950   p = 8 + len;
   951   var crc = crc32(chunk, 4, p);
   952   chunk[p] = crc >> 24 & 255;
   953   chunk[p + 1] = crc >> 16 & 255;
   954   chunk[p + 2] = crc >> 8 & 255;
   955   chunk[p + 3] = crc & 255;
   956   return chunk;
   957 }
   958 function adler32(data, start, end) {
   959   var a = 1;
   960   var b = 0;
   961   for (var i = start; i < end; ++i) {
   962     a = (a + (data[i] & 255)) % 65521;
   963     b = (b + a) % 65521;
   964   }
   965   return b << 16 | a;
   966 }
   967 function defineBitmap(tag) {
   968   var width = tag.width;
   969   var height = tag.height;
   970   var hasAlpha = tag.hasAlpha;
   971   var plte = '';
   972   var trns = '';
   973   var literals;
   974   var bmpData = tag.bmpData;
   975   switch (tag.format) {
   976   case FORMAT_COLORMAPPED:
   977     var colorType = 3;
   978     var bytesPerLine = width + 3 & ~3;
   979     var colorTableSize = tag.colorTableSize + 1;
   980     var paletteSize = colorTableSize * (tag.hasAlpha ? 4 : 3);
   981     var datalen = paletteSize + bytesPerLine * height;
   982     var stream = createInflatedStream(bmpData, datalen);
   983     var bytes = stream.bytes;
   984     var pos = 0;
   985     stream.ensure(paletteSize);
   986     if (hasAlpha) {
   987       var palette = new Uint8Array(paletteSize / 4 * 3);
   988       var pp = 0;
   989       var alphaValues = new Uint8Array(paletteSize / 4);
   990       var pa = 0;
   991       while (pos < paletteSize) {
   992         palette[pp++] = bytes[pos];
   993         palette[pp++] = bytes[pos + 1];
   994         palette[pp++] = bytes[pos + 2];
   995         alphaValues[pa++] = bytes[pos + 3];
   996         pos += 4;
   997       }
   998       plte = createPngChunk('PLTE', palette);
   999       trns = createPngChunk('tRNS', alphaValues);
  1000     } else {
  1001       plte = createPngChunk('PLTE', bytes.subarray(pos, pos + paletteSize));
  1002       pos += paletteSize;
  1004     literals = new Uint8Array(width * height + height);
  1005     var pl = 0;
  1006     while (pos < datalen) {
  1007       stream.ensure(bytesPerLine);
  1008       var begin = pos;
  1009       var end = begin + width;
  1010       pl++;
  1011       literals.set(bytes.subarray(begin, end), pl);
  1012       pl += end - begin;
  1013       stream.pos = pos += bytesPerLine;
  1015     break;
  1016   case FORMAT_15BPP:
  1017     var colorType = 2;
  1018     var bytesPerLine = width * 2 + 3 & ~3;
  1019     var stream = createInflatedStream(bmpData, bytesPerLine * height);
  1020     var pos = 0;
  1021     literals = new Uint8Array(width * height * 3 + height);
  1022     var pl = 0;
  1023     for (var y = 0; y < height; ++y) {
  1024       pl++;
  1025       stream.ensure(bytesPerLine);
  1026       for (var x = 0; x < width; ++x) {
  1027         var word = stream.getUint16(pos);
  1028         pos += 2;
  1029         literals[pl++] = 0 | FACTOR_5BBP * (word >> 10 & 31);
  1030         literals[pl++] = 0 | FACTOR_5BBP * (word >> 5 & 31);
  1031         literals[pl++] = 0 | FACTOR_5BBP * (word & 31);
  1033       stream.pos = pos += bytesPerLine;
  1035     break;
  1036   case FORMAT_24BPP:
  1037     var padding;
  1038     if (hasAlpha) {
  1039       var colorType = 6;
  1040       padding = 0;
  1041       literals = new Uint8Array(width * height * 4 + height);
  1042     } else {
  1043       var colorType = 2;
  1044       padding = 1;
  1045       literals = new Uint8Array(width * height * 3 + height);
  1047     var bytesPerLine = width * 4;
  1048     var stream = createInflatedStream(bmpData, bytesPerLine * height);
  1049     var bytes = stream.bytes;
  1050     var pos = 0;
  1051     var pl = 0;
  1052     for (var y = 0; y < height; ++y) {
  1053       stream.ensure(bytesPerLine);
  1054       pl++;
  1055       for (var x = 0; x < width; ++x) {
  1056         pos += padding;
  1057         if (hasAlpha) {
  1058           var alpha = bytes[pos];
  1059           if (alpha) {
  1060             var opacity = alpha / 255;
  1061             literals[pl++] = 0 | bytes[pos + 1] / opacity;
  1062             literals[pl++] = 0 | bytes[pos + 2] / opacity;
  1063             literals[pl++] = 0 | bytes[pos + 3] / opacity;
  1064             literals[pl++] = alpha;
  1065           } else {
  1066             pl += 4;
  1068         } else {
  1069           literals[pl++] = bytes[pos];
  1070           literals[pl++] = bytes[pos + 1];
  1071           literals[pl++] = bytes[pos + 2];
  1073         pos += 4 - padding;
  1075       stream.pos = pos;
  1077     break;
  1078   default:
  1079     fail('invalid format', 'bitmap');
  1081   var ihdr = new Uint8Array([
  1082       width >> 24 & 255,
  1083       width >> 16 & 255,
  1084       width >> 8 & 255,
  1085       width & 255,
  1086       height >> 24 & 255,
  1087       height >> 16 & 255,
  1088       height >> 8 & 255,
  1089       height & 255,
  1090       8,
  1091       colorType,
  1092       0,
  1093       0,
  1095     ]);
  1096   var len = literals.length;
  1097   var maxBlockLength = 65535;
  1098   var idat = new Uint8Array(2 + len + Math.ceil(len / maxBlockLength) * 5 + 4);
  1099   var pi = 0;
  1100   idat[pi++] = 120;
  1101   idat[pi++] = 156;
  1102   var pos = 0;
  1103   while (len > maxBlockLength) {
  1104     idat[pi++] = 0;
  1105     idat[pi++] = 255;
  1106     idat[pi++] = 255;
  1107     idat[pi++] = 0;
  1108     idat[pi++] = 0;
  1109     idat.set(literals.subarray(pos, pos + maxBlockLength), pi);
  1110     pi += maxBlockLength;
  1111     pos += maxBlockLength;
  1112     len -= maxBlockLength;
  1114   idat[pi++] = 1;
  1115   idat[pi++] = len & 255;
  1116   idat[pi++] = len >> 8 & 255;
  1117   idat[pi++] = ~len & 65535 & 255;
  1118   idat[pi++] = (~len & 65535) >> 8 & 255;
  1119   idat.set(literals.subarray(pos), pi);
  1120   pi += literals.length - pos;
  1121   var adler = adler32(literals, 0, literals.length);
  1122   idat[pi++] = adler >> 24 & 255;
  1123   idat[pi++] = adler >> 16 & 255;
  1124   idat[pi++] = adler >> 8 & 255;
  1125   idat[pi++] = adler & 255;
  1126   var chunks = [
  1127       new Uint8Array([
  1128         137,
  1129         80,
  1130         78,
  1131         71,
  1132         13,
  1133         10,
  1134         26,
  1135         10
  1136       ]),
  1137       createPngChunk('IHDR', ihdr),
  1138       plte,
  1139       trns,
  1140       createPngChunk('IDAT', idat),
  1141       createPngChunk('IEND', '')
  1142     ];
  1143   return {
  1144     type: 'image',
  1145     id: tag.id,
  1146     width: width,
  1147     height: height,
  1148     mimeType: 'image/png',
  1149     data: new Blob(chunks, {
  1150       type: 'image/png'
  1151     })
  1152   };
  1154 function defineButton(tag, dictionary) {
  1155   var characters = tag.characters;
  1156   var states = {
  1157       up: {},
  1158       over: {},
  1159       down: {},
  1160       hitTest: {}
  1161     };
  1162   var i = 0, character;
  1163   while (character = characters[i++]) {
  1164     if (character.eob)
  1165       break;
  1166     var characterItem = dictionary[character.symbolId];
  1167     var entry = {
  1168         symbolId: characterItem.id,
  1169         hasMatrix: !(!character.matrix),
  1170         matrix: character.matrix
  1171       };
  1172     if (character.stateUp)
  1173       states.up[character.depth] = entry;
  1174     if (character.stateOver)
  1175       states.over[character.depth] = entry;
  1176     if (character.stateDown)
  1177       states.down[character.depth] = entry;
  1178     if (character.stateHitTest)
  1179       states.hitTest[character.depth] = entry;
  1181   var button = {
  1182       type: 'button',
  1183       id: tag.id,
  1184       buttonActions: tag.buttonActions,
  1185       states: states
  1186     };
  1187   return button;
  1189 var nextFontId = 1;
  1190 function maxPower2(num) {
  1191   var maxPower = 0;
  1192   var val = num;
  1193   while (val >= 2) {
  1194     val /= 2;
  1195     ++maxPower;
  1197   return pow(2, maxPower);
  1199 function toString16(val) {
  1200   return fromCharCode(val >> 8 & 255, val & 255);
  1202 function toString32(val) {
  1203   return toString16(val >> 16) + toString16(val);
  1205 function defineFont(tag, dictionary) {
  1206   var tables = {};
  1207   var codes = [];
  1208   var glyphIndex = {};
  1209   var ranges = [];
  1210   var glyphs = tag.glyphs;
  1211   var glyphCount = glyphs.length;
  1212   if (tag.codes) {
  1213     codes = codes.concat(tag.codes);
  1214     for (var i = 0, code; code = codes[i]; ++i)
  1215       glyphIndex[code] = i;
  1216     codes.sort(function (a, b) {
  1217       return a - b;
  1218     });
  1219     var i = 0;
  1220     var code;
  1221     while (code = codes[i++]) {
  1222       var start = code;
  1223       var end = start;
  1224       var indices = [
  1225           i - 1
  1226         ];
  1227       while ((code = codes[i]) && end + 1 === code) {
  1228         ++end;
  1229         indices.push(i);
  1230         ++i;
  1232       ranges.push([
  1233         start,
  1234         end,
  1235         indices
  1236       ]);
  1238   } else {
  1239     var indices = [];
  1240     var UAC_OFFSET = 57344;
  1241     for (var i = 0; i < glyphCount; i++) {
  1242       var code = UAC_OFFSET + i;
  1243       codes.push(code);
  1244       glyphIndex[code] = i;
  1245       indices.push(i);
  1247     ranges.push([
  1248       UAC_OFFSET,
  1249       UAC_OFFSET + glyphCount - 1,
  1250       indices
  1251     ]);
  1253   var resolution = tag.resolution || 1;
  1254   var ascent = Math.ceil(tag.ascent / resolution) || 1024;
  1255   var descent = -Math.ceil(tag.descent / resolution) | 0;
  1256   var leading = tag.leading / resolution | 0;
  1257   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';
  1259   var startCount = '';
  1260   var endCount = '';
  1261   var idDelta = '';
  1262   var idRangeOffset = '';
  1263   var i = 0;
  1264   var range;
  1265   while (range = ranges[i++]) {
  1266     var start = range[0];
  1267     var end = range[1];
  1268     var code = range[2][0];
  1269     startCount += toString16(start);
  1270     endCount += toString16(end);
  1271     idDelta += toString16(code - start + 1 & 65535);
  1272     idRangeOffset += toString16(0);
  1274   endCount += '\xff\xff';
  1275   startCount += '\xff\xff';
  1276   idDelta += '\0\x01';
  1277   idRangeOffset += '\0\0';
  1278   var segCount = ranges.length + 1;
  1279   var searchRange = maxPower2(segCount) * 2;
  1280   var rangeShift = 2 * segCount - searchRange;
  1281   var format314 = '\0\0' + toString16(segCount * 2) + toString16(searchRange) + toString16(logE(segCount) / logE(2)) + toString16(rangeShift) + endCount + '\0\0' + startCount + idDelta + idRangeOffset;
  1283   tables['cmap'] = '\0\0\0\x01\0\x03\0\x01\0\0\0\f\0\x04' + toString16(format314.length + 4) + format314;
  1285   var glyf = '\0\x01\0\0\0\0\0\0\0\0\0\0\0\x001\0';
  1286   var loca = '\0\0';
  1287   var offset = 16;
  1288   var maxPoints = 0;
  1289   var xMins = [];
  1290   var xMaxs = [];
  1291   var yMins = [];
  1292   var yMaxs = [];
  1293   var maxContours = 0;
  1294   var i = 0;
  1295   var code;
  1296   while (code = codes[i++]) {
  1297     var glyph = glyphs[glyphIndex[code]];
  1298     var records = glyph.records;
  1299     var numberOfContours = 1;
  1300     var endPoint = 0;
  1301     var endPtsOfContours = '';
  1302     var flags = '';
  1303     var xCoordinates = '';
  1304     var yCoordinates = '';
  1305     var x = 0;
  1306     var y = 0;
  1307     var xMin = 1024;
  1308     var xMax = -1024;
  1309     var yMin = 1024;
  1310     var yMax = -1024;
  1311     for (var j = 0, record; record = records[j]; ++j) {
  1312       if (record.type) {
  1313         if (record.isStraight) {
  1314           if (record.isGeneral) {
  1315             flags += '\x01';
  1316             var dx = record.deltaX / resolution;
  1317             var dy = -record.deltaY / resolution;
  1318             xCoordinates += toString16(dx);
  1319             yCoordinates += toString16(dy);
  1320             x += dx;
  1321             y += dy;
  1322           } else if (record.isVertical) {
  1323             flags += '\x11';
  1324             var dy = -record.deltaY / resolution;
  1325             yCoordinates += toString16(dy);
  1326             y += dy;
  1327           } else {
  1328             flags += '!';
  1329             var dx = record.deltaX / resolution;
  1330             xCoordinates += toString16(dx);
  1331             x += dx;
  1333         } else {
  1334           flags += '\0';
  1335           var cx = record.controlDeltaX / resolution;
  1336           var cy = -record.controlDeltaY / resolution;
  1337           xCoordinates += toString16(cx);
  1338           yCoordinates += toString16(cy);
  1339           flags += '\x01';
  1340           var dx = record.anchorDeltaX / resolution;
  1341           var dy = -record.anchorDeltaY / resolution;
  1342           xCoordinates += toString16(dx);
  1343           yCoordinates += toString16(dy);
  1344           ++endPoint;
  1345           x += cx + dx;
  1346           y += cy + dy;
  1348         if (x < xMin)
  1349           xMin = x;
  1350         if (x > xMax)
  1351           xMax = x;
  1352         if (y < yMin)
  1353           yMin = y;
  1354         if (y > yMax)
  1355           yMax = y;
  1356         ++endPoint;
  1357       } else {
  1358         if (record.eos)
  1359           break;
  1360         if (record.move) {
  1361           if (endPoint) {
  1362             ++numberOfContours;
  1363             endPtsOfContours += toString16(endPoint - 1);
  1365           flags += '\x01';
  1366           var moveX = record.moveX / resolution;
  1367           var moveY = -record.moveY / resolution;
  1368           var dx = moveX - x;
  1369           var dy = moveY - y;
  1370           xCoordinates += toString16(dx);
  1371           yCoordinates += toString16(dy);
  1372           x = moveX;
  1373           y = moveY;
  1374           if (endPoint > maxPoints)
  1375             maxPoints = endPoint;
  1376           if (x < xMin)
  1377             xMin = x;
  1378           if (x > xMax)
  1379             xMax = x;
  1380           if (y < yMin)
  1381             yMin = y;
  1382           if (y > yMax)
  1383             yMax = y;
  1384           ++endPoint;
  1388     endPtsOfContours += toString16((endPoint || 1) - 1);
  1389     if (!j) {
  1390       xMin = xMax = yMin = yMax = 0;
  1391       flags += '1';
  1393     var entry = toString16(numberOfContours) + toString16(xMin) + toString16(yMin) + toString16(xMax) + toString16(yMax) + endPtsOfContours + '\0\0' + flags + xCoordinates + yCoordinates;
  1395     if (entry.length & 1)
  1396       entry += '\0';
  1397     glyf += entry;
  1398     loca += toString16(offset / 2);
  1399     offset += entry.length;
  1400     xMins.push(xMin);
  1401     xMaxs.push(xMax);
  1402     yMins.push(yMin);
  1403     yMaxs.push(yMax);
  1404     if (numberOfContours > maxContours)
  1405       maxContours = numberOfContours;
  1406     if (endPoint > maxPoints)
  1407       maxPoints = endPoint;
  1409   loca += toString16(offset / 2);
  1410   tables['glyf'] = glyf;
  1411   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';
  1413   var advance = tag.advance;
  1414   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);
  1416   var hmtx = '\0\0\0\0';
  1417   for (var i = 0; i < glyphCount; ++i)
  1418     hmtx += toString16(advance ? advance[i] / resolution : 1024) + '\0\0';
  1419   tables['hmtx'] = hmtx;
  1420   if (tag.kerning) {
  1421     var kerning = tag.kerning;
  1422     var nPairs = kerning.length;
  1423     var searchRange = maxPower2(nPairs) * 2;
  1424     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);
  1426     var i = 0;
  1427     var record;
  1428     while (record = kerning[i++]) {
  1429       kern += toString16(glyphIndex[record.code1]) + toString16(glyphIndex[record.code2]) + toString16(record.adjustment);
  1432     tables['kern'] = kern;
  1434   tables['loca'] = loca;
  1435   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';
  1437   var uniqueId = 'swf-font-' + nextFontId++;
  1438   var fontName = tag.name || uniqueId;
  1439   var psName = fontName.replace(/ /g, '');
  1440   var strings = [
  1441       tag.copyright || 'Original licence',
  1442       fontName,
  1443       'Unknown',
  1444       uniqueId,
  1445       fontName,
  1446       '1.0',
  1447       psName,
  1448       'Unknown',
  1449       'Unknown',
  1450       'Unknown'
  1451     ];
  1452   var count = strings.length;
  1453   var name = '\0\0' + toString16(count) + toString16(count * 12 + 6);
  1454   var offset = 0;
  1455   var i = 0;
  1456   var str;
  1457   while (str = strings[i++]) {
  1458     name += '\0\x01\0\0\0\0' + toString16(i - 1) + toString16(str.length) + toString16(offset);
  1459     offset += str.length;
  1461   tables['name'] = name + strings.join('');
  1462   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';
  1464   var names = keys(tables);
  1465   var numTables = names.length;
  1466   var header = '\0\x01\0\0' + toString16(numTables) + '\0\x80' + '\0\x03' + '\0 ';
  1468   var data = '';
  1469   var offset = numTables * 16 + header.length;
  1470   var i = 0;
  1471   var name;
  1472   while (name = names[i++]) {
  1473     var table = tables[name];
  1474     var length = table.length;
  1475     header += name + '\0\0\0\0' + toString32(offset) + toString32(length);
  1477     while (length & 3) {
  1478       table += '\0';
  1479       ++length;
  1481     data += table;
  1482     while (offset & 3)
  1483       ++offset;
  1484     offset += length;
  1486   var otf = header + data;
  1487   var unitPerEm = 1024;
  1488   var metrics = {
  1489       ascent: ascent / unitPerEm,
  1490       descent: -descent / unitPerEm,
  1491       leading: leading / unitPerEm
  1492     };
  1493   return {
  1494     type: 'font',
  1495     id: tag.id,
  1496     name: fontName,
  1497     uniqueName: psName + uniqueId,
  1498     codes: codes,
  1499     metrics: metrics,
  1500     bold: tag.bold === 1,
  1501     italic: tag.italic === 1,
  1502     data: otf
  1503   };
  1505 function getUint16(buff, pos) {
  1506   return buff[pos] << 8 | buff[pos + 1];
  1508 function parseJpegChunks(imgDef, bytes) {
  1509   var i = 0;
  1510   var n = bytes.length;
  1511   var chunks = [];
  1512   var code;
  1513   do {
  1514     var begin = i;
  1515     while (i < n && bytes[i] !== 255)
  1516       ++i;
  1517     while (i < n && bytes[i] === 255)
  1518       ++i;
  1519     code = bytes[i++];
  1520     if (code === 218) {
  1521       i = n;
  1522     } else if (code === 217) {
  1523       i += 2;
  1524       continue;
  1525     } else if (code < 208 || code > 216) {
  1526       var length = getUint16(bytes, i);
  1527       if (code >= 192 && code <= 195) {
  1528         imgDef.height = getUint16(bytes, i + 3);
  1529         imgDef.width = getUint16(bytes, i + 5);
  1531       i += length;
  1533     chunks.push(bytes.subarray(begin, i));
  1534   } while (i < n);
  1535   return chunks;
  1537 function defineImage(tag, dictionary) {
  1538   var img = {
  1539       type: 'image',
  1540       id: tag.id,
  1541       mimeType: tag.mimeType
  1542     };
  1543   var imgData = tag.imgData;
  1544   var chunks;
  1545   if (tag.mimeType === 'image/jpeg') {
  1546     chunks = parseJpegChunks(img, imgData);
  1547     var alphaData = tag.alphaData;
  1548     if (alphaData) {
  1549       img.mask = createInflatedStream(alphaData, img.width * img.height).bytes;
  1551     if (tag.incomplete) {
  1552       var tables = dictionary[0];
  1553       var header = tables.data;
  1554       if (header && header.size) {
  1555         chunks[0] = chunks[0].subarray(2);
  1556         chunks.unshift(header.slice(0, header.size - 2));
  1559   } else {
  1560     chunks = [
  1561       imgData
  1562     ];
  1564   img.data = new Blob(chunks, {
  1565     type: tag.mimeType
  1566   });
  1567   return img;
  1569 function defineLabel(tag, dictionary) {
  1570   var records = tag.records;
  1571   var m = tag.matrix;
  1572   var cmds = [
  1573       'c.save()',
  1574       'c.transform(' + [
  1575         m.a,
  1576         m.b,
  1577         m.c,
  1578         m.d,
  1579         m.tx / 20,
  1580         m.ty / 20
  1581       ].join(',') + ')',
  1582       'c.scale(0.05, 0.05)'
  1583     ];
  1584   var dependencies = [];
  1585   var x = 0;
  1586   var y = 0;
  1587   var i = 0;
  1588   var record;
  1589   var codes;
  1590   while (record = records[i++]) {
  1591     if (record.eot)
  1592       break;
  1593     if (record.hasFont) {
  1594       var font = dictionary[record.fontId];
  1595       codes = font.codes;
  1596       cmds.push('c.font="' + record.fontHeight + 'px \'' + font.uniqueName + '\'"');
  1597       dependencies.push(font.id);
  1599     if (record.hasColor) {
  1600       cmds.push('ct.setFillStyle(c,"' + rgbaObjToStr(record.color) + '")');
  1601       cmds.push('ct.setAlpha(c)');
  1602     } else {
  1603       cmds.push('ct.setAlpha(c,true)');
  1605     if (record.hasMoveX)
  1606       x = record.moveX;
  1607     if (record.hasMoveY)
  1608       y = record.moveY;
  1609     var entries = record.entries;
  1610     var j = 0;
  1611     var entry;
  1612     while (entry = entries[j++]) {
  1613       var code = codes[entry.glyphIndex];
  1614       var text = code >= 32 && code != 34 && code != 92 ? fromCharCode(code) : '\\u' + (code + 65536).toString(16).substring(1);
  1615       cmds.push('c.fillText("' + text + '",' + x + ',' + y + ')');
  1616       x += entry.advance;
  1619   cmds.push('c.restore()');
  1620   var label = {
  1621       type: 'label',
  1622       id: tag.id,
  1623       bbox: tag.bbox,
  1624       data: cmds.join('\n')
  1625     };
  1626   if (dependencies.length)
  1627     label.require = dependencies;
  1628   return label;
  1630 var GRAPHICS_FILL_CLIPPED_BITMAP = 65;
  1631 var GRAPHICS_FILL_FOCAL_RADIAL_GRADIENT = 19;
  1632 var GRAPHICS_FILL_LINEAR_GRADIENT = 16;
  1633 var GRAPHICS_FILL_NONSMOOTHED_CLIPPED_BITMAP = 67;
  1634 var GRAPHICS_FILL_NONSMOOTHED_REPEATING_BITMAP = 66;
  1635 var GRAPHICS_FILL_RADIAL_GRADIENT = 18;
  1636 var GRAPHICS_FILL_REPEATING_BITMAP = 64;
  1637 var GRAPHICS_FILL_SOLID = 0;
  1638 function applySegmentToStyles(segment, styles, linePaths, fillPaths, isMorph) {
  1639   if (!segment) {
  1640     return;
  1642   var commands = segment.commands;
  1643   var data = segment.data;
  1644   var morphData = segment.morphData;
  1645   if (morphData) {
  1647   var path;
  1648   var targetSegment;
  1649   var command;
  1650   var i;
  1651   if (styles.fill0) {
  1652     path = fillPaths[styles.fill0 - 1];
  1653     if (!(styles.fill1 || styles.line)) {
  1654       targetSegment = path.head();
  1655       targetSegment.commands = [];
  1656       targetSegment.data = [];
  1657       targetSegment.morphData = isMorph ? [] : null;
  1658     } else {
  1659       targetSegment = path.addSegment([], [], isMorph ? [] : null);
  1661     var targetCommands = targetSegment.commands;
  1662     var targetData = targetSegment.data;
  1663     var targetMorphData = targetSegment.morphData;
  1664     targetCommands.push(SHAPE_MOVE_TO);
  1665     var j = data.length - 2;
  1666     targetData.push(data[j], data[j + 1]);
  1667     if (isMorph) {
  1668       targetMorphData.push(morphData[j], morphData[j + 1]);
  1670     for (i = commands.length; i-- > 1; j -= 2) {
  1671       command = commands[i];
  1672       targetCommands.push(command);
  1673       targetData.push(data[j - 2], data[j - 1]);
  1674       if (isMorph) {
  1675         targetMorphData.push(morphData[j - 2], morphData[j - 1]);
  1677       if (command === SHAPE_CURVE_TO) {
  1678         targetData.push(data[j - 4], data[j - 3]);
  1679         if (isMorph) {
  1680           targetMorphData.push(morphData[j - 4], morphData[j - 3]);
  1682         j -= 2;
  1685     if (isMorph) {
  1688   if (styles.line && styles.fill1) {
  1689     path = linePaths[styles.line - 1];
  1690     path.addSegment(commands, data, morphData);
  1693 function convertRecordsToStyledPaths(records, fillPaths, linePaths, dictionary, dependencies, recordsMorph, transferables) {
  1694   var isMorph = recordsMorph !== null;
  1695   var styles = {
  1696       fill0: 0,
  1697       fill1: 0,
  1698       line: 0
  1699     };
  1700   var segment = null;
  1701   var allPaths;
  1702   var defaultPath;
  1703   var numRecords = records.length - 1;
  1704   var x = 0;
  1705   var y = 0;
  1706   var morphX = 0;
  1707   var morphY = 0;
  1708   var path;
  1709   for (var i = 0, j = 0; i < numRecords; i++) {
  1710     var record = records[i];
  1711     var morphRecord;
  1712     if (isMorph) {
  1713       morphRecord = recordsMorph[j++];
  1715     if (record.type === 0) {
  1716       if (segment) {
  1717         applySegmentToStyles(segment, styles, linePaths, fillPaths, isMorph);
  1719       if (record.hasNewStyles) {
  1720         if (!allPaths) {
  1721           allPaths = [];
  1723         push.apply(allPaths, fillPaths);
  1724         fillPaths = createPathsList(record.fillStyles, false, dictionary, dependencies);
  1725         push.apply(allPaths, linePaths);
  1726         linePaths = createPathsList(record.lineStyles, true, dictionary, dependencies);
  1727         if (defaultPath) {
  1728           allPaths.push(defaultPath);
  1729           defaultPath = null;
  1731         styles = {
  1732           fill0: 0,
  1733           fill1: 0,
  1734           line: 0
  1735         };
  1737       if (record.hasFillStyle0) {
  1738         styles.fill0 = record.fillStyle0;
  1740       if (record.hasFillStyle1) {
  1741         styles.fill1 = record.fillStyle1;
  1743       if (record.hasLineStyle) {
  1744         styles.line = record.lineStyle;
  1746       if (styles.fill1) {
  1747         path = fillPaths[styles.fill1 - 1];
  1748       } else if (styles.line) {
  1749         path = linePaths[styles.line - 1];
  1750       } else if (styles.fill0) {
  1751         path = fillPaths[styles.fill0 - 1];
  1753       if (record.move) {
  1754         x = record.moveX | 0;
  1755         y = record.moveY | 0;
  1757       if (path) {
  1758         segment = path.addSegment([], [], isMorph ? [] : null);
  1759         segment.commands.push(SHAPE_MOVE_TO);
  1760         segment.data.push(x, y);
  1761         if (isMorph) {
  1762           if (morphRecord.type === 0) {
  1763             morphX = morphRecord.moveX | 0;
  1764             morphY = morphRecord.moveY | 0;
  1765           } else {
  1766             morphX = x;
  1767             morphY = y;
  1768             j--;
  1770           segment.morphData.push(morphX, morphY);
  1773     } else {
  1774       if (!segment) {
  1775         if (!defaultPath) {
  1776           var style = {
  1777               color: {
  1778                 red: 0,
  1779                 green: 0,
  1780                 blue: 0,
  1781                 alpha: 255
  1782               },
  1783               width: 20
  1784             };
  1785           defaultPath = new SegmentedPath(null, processStyle(style, true));
  1787         segment = defaultPath.addSegment([], [], isMorph ? [] : null);
  1788         segment.commands.push(SHAPE_MOVE_TO);
  1789         segment.data.push(x, y);
  1790         if (isMorph) {
  1791           segment.morphData.push(morphX, morphY);
  1794       if (isMorph) {
  1795         while (morphRecord && morphRecord.type === 0) {
  1796           morphRecord = recordsMorph[j++];
  1798         if (!morphRecord) {
  1799           morphRecord = record;
  1802       if (record.isStraight && (!isMorph || morphRecord.isStraight)) {
  1803         x += record.deltaX | 0;
  1804         y += record.deltaY | 0;
  1805         segment.commands.push(SHAPE_LINE_TO);
  1806         segment.data.push(x, y);
  1807         if (isMorph) {
  1808           morphX += morphRecord.deltaX | 0;
  1809           morphY += morphRecord.deltaY | 0;
  1810           segment.morphData.push(morphX, morphY);
  1812       } else {
  1813         var cx, cy;
  1814         var deltaX, deltaY;
  1815         if (!record.isStraight) {
  1816           cx = x + record.controlDeltaX | 0;
  1817           cy = y + record.controlDeltaY | 0;
  1818           x = cx + record.anchorDeltaX | 0;
  1819           y = cy + record.anchorDeltaY | 0;
  1820         } else {
  1821           deltaX = record.deltaX | 0;
  1822           deltaY = record.deltaY | 0;
  1823           cx = x + (deltaX >> 1);
  1824           cy = y + (deltaY >> 1);
  1825           x += deltaX;
  1826           y += deltaY;
  1828         segment.commands.push(SHAPE_CURVE_TO);
  1829         segment.data.push(cx, cy, x, y);
  1830         if (isMorph) {
  1831           if (!morphRecord.isStraight) {
  1832             cx = morphX + morphRecord.controlDeltaX | 0;
  1833             cy = morphY + morphRecord.controlDeltaY | 0;
  1834             morphX = cx + morphRecord.anchorDeltaX | 0;
  1835             morphY = cy + morphRecord.anchorDeltaY | 0;
  1836           } else {
  1837             deltaX = morphRecord.deltaX | 0;
  1838             deltaY = morphRecord.deltaY | 0;
  1839             cx = morphX + (deltaX >> 1);
  1840             cy = morphY + (deltaY >> 1);
  1841             morphX += deltaX;
  1842             morphY += deltaY;
  1844           segment.morphData.push(cx, cy, morphX, morphY);
  1849   applySegmentToStyles(segment, styles, linePaths, fillPaths, isMorph);
  1850   if (allPaths) {
  1851     push.apply(allPaths, fillPaths);
  1852   } else {
  1853     allPaths = fillPaths;
  1855   push.apply(allPaths, linePaths);
  1856   if (defaultPath) {
  1857     allPaths.push(defaultPath);
  1859   var removeCount = 0;
  1860   for (i = 0; i < allPaths.length; i++) {
  1861     path = allPaths[i];
  1862     if (!path.head()) {
  1863       removeCount++;
  1864       continue;
  1866     allPaths[i - removeCount] = segmentedPathToShapePath(path, isMorph, transferables);
  1868   allPaths.length -= removeCount;
  1869   return allPaths;
  1871 function segmentedPathToShapePath(path, isMorph, transferables) {
  1872   var start = path.head();
  1873   var end = start;
  1874   var finalRoot = null;
  1875   var finalHead = null;
  1876   var skippedMoves = 0;
  1877   var current = start.prev;
  1878   while (start) {
  1879     while (current) {
  1880       if (path.segmentsConnect(current, start)) {
  1881         if (current.next !== start) {
  1882           path.removeSegment(current);
  1883           path.insertSegment(current, start);
  1885         start = current;
  1886         current = start.prev;
  1887         skippedMoves++;
  1888         continue;
  1890       if (path.segmentsConnect(end, current)) {
  1891         path.removeSegment(current);
  1892         end.next = current;
  1893         current = current.prev;
  1894         end.next.prev = end;
  1895         end.next.next = null;
  1896         end = end.next;
  1897         skippedMoves++;
  1898         continue;
  1900       current = current.prev;
  1902     current = start.prev;
  1903     if (!finalRoot) {
  1904       finalRoot = start;
  1905       finalHead = end;
  1906     } else {
  1907       finalHead.next = start;
  1908       start.prev = finalHead;
  1909       finalHead = end;
  1910       finalHead.next = null;
  1912     if (!current) {
  1913       break;
  1915     start = end = current;
  1916     current = start.prev;
  1918   var totalCommandsLength = -skippedMoves;
  1919   var totalDataLength = -skippedMoves << 1;
  1920   current = finalRoot;
  1921   while (current) {
  1922     totalCommandsLength += current.commands.length;
  1923     totalDataLength += current.data.length;
  1924     current = current.next;
  1926   var shape = new ShapePath(path.fillStyle, path.lineStyle, totalCommandsLength, totalDataLength, isMorph, transferables);
  1927   var allCommands = shape.commands;
  1928   var allData = shape.data;
  1929   var allMorphData = shape.morphData;
  1930   var commandsIndex = 0;
  1931   var dataIndex = 0;
  1932   current = finalRoot;
  1933   while (current) {
  1934     var commands = current.commands;
  1935     var data = current.data;
  1936     var morphData = current.morphData;
  1937     var offset = +(data[0] === allData[dataIndex - 2] && data[1] === allData[dataIndex - 1]);
  1938     for (var i = offset; i < commands.length; i++, commandsIndex++) {
  1939       allCommands[commandsIndex] = commands[i];
  1941     for (i = offset << 1; i < data.length; i++, dataIndex++) {
  1942       allData[dataIndex] = data[i];
  1943       if (isMorph) {
  1944         allMorphData[dataIndex] = morphData[i];
  1947     current = current.next;
  1949   return shape;
  1951 var CAPS_STYLE_TYPES = [
  1952     'round',
  1953     'none',
  1954     'square'
  1955   ];
  1956 var JOIN_STYLE_TYPES = [
  1957     'round',
  1958     'bevel',
  1959     'miter'
  1960   ];
  1961 function processStyle(style, isLineStyle, dictionary, dependencies) {
  1962   if (isLineStyle) {
  1963     style.lineCap = CAPS_STYLE_TYPES[style.endCapStyle | 0];
  1964     style.lineJoin = JOIN_STYLE_TYPES[style.joinStyle | 0];
  1965     style.miterLimit = (style.miterLimitFactor || 1.5) * 2;
  1966     if (!style.color && style.hasFill) {
  1967       var fillStyle = processStyle(style.fillStyle, false, dictionary, dependencies);
  1968       style.style = fillStyle.style;
  1969       style.type = fillStyle.type;
  1970       style.transform = fillStyle.transform;
  1971       style.records = fillStyle.records;
  1972       style.focalPoint = fillStyle.focalPoint;
  1973       style.bitmapId = fillStyle.bitmapId;
  1974       style.repeat = fillStyle.repeat;
  1975       style.fillStyle = null;
  1976       return style;
  1979   var color;
  1980   if (style.type === undefined || style.type === GRAPHICS_FILL_SOLID) {
  1981     color = style.color;
  1982     style.style = 'rgba(' + color.red + ',' + color.green + ',' + color.blue + ',' + color.alpha / 255 + ')';
  1983     style.color = null;
  1984     return style;
  1986   var scale;
  1987   switch (style.type) {
  1988   case GRAPHICS_FILL_LINEAR_GRADIENT:
  1989   case GRAPHICS_FILL_RADIAL_GRADIENT:
  1990   case GRAPHICS_FILL_FOCAL_RADIAL_GRADIENT:
  1991     scale = 819.2;
  1992     break;
  1993   case GRAPHICS_FILL_REPEATING_BITMAP:
  1994   case GRAPHICS_FILL_CLIPPED_BITMAP:
  1995   case GRAPHICS_FILL_NONSMOOTHED_REPEATING_BITMAP:
  1996   case GRAPHICS_FILL_NONSMOOTHED_CLIPPED_BITMAP:
  1997     if (dictionary[style.bitmapId]) {
  1998       dependencies.push(dictionary[style.bitmapId].id);
  1999       scale = 0.05;
  2001     break;
  2002   default:
  2003     fail('invalid fill style', 'shape');
  2005   if (!style.matrix) {
  2006     return style;
  2008   var matrix = style.matrix;
  2009   style.transform = {
  2010     a: matrix.a * scale,
  2011     b: matrix.b * scale,
  2012     c: matrix.c * scale,
  2013     d: matrix.d * scale,
  2014     e: matrix.tx,
  2015     f: matrix.ty
  2016   };
  2017   style.matrix = null;
  2018   return style;
  2020 function createPathsList(styles, isLineStyle, dictionary, dependencies) {
  2021   var paths = [];
  2022   for (var i = 0; i < styles.length; i++) {
  2023     var style = processStyle(styles[i], isLineStyle, dictionary, dependencies);
  2024     if (!isLineStyle) {
  2025       paths[i] = new SegmentedPath(style, null);
  2026     } else {
  2027       paths[i] = new SegmentedPath(null, style);
  2030   return paths;
  2032 function defineShape(tag, dictionary) {
  2033   var dependencies = [];
  2034   var transferables = [];
  2035   var fillPaths = createPathsList(tag.fillStyles, false, dictionary, dependencies);
  2036   var linePaths = createPathsList(tag.lineStyles, true, dictionary, dependencies);
  2037   var paths = convertRecordsToStyledPaths(tag.records, fillPaths, linePaths, dictionary, dependencies, tag.recordsMorph || null, transferables);
  2038   if (tag.bboxMorph) {
  2039     var mbox = tag.bboxMorph;
  2040     extendBoundsByPoint(tag.bbox, mbox.xMin, mbox.yMin);
  2041     extendBoundsByPoint(tag.bbox, mbox.xMax, mbox.yMax);
  2042     mbox = tag.strokeBboxMorph;
  2043     if (mbox) {
  2044       extendBoundsByPoint(tag.strokeBbox, mbox.xMin, mbox.yMin);
  2045       extendBoundsByPoint(tag.strokeBbox, mbox.xMax, mbox.yMax);
  2048   return {
  2049     type: 'shape',
  2050     id: tag.id,
  2051     strokeBbox: tag.strokeBbox,
  2052     strokeBboxMorph: tag.strokeBboxMorph,
  2053     bbox: tag.bbox,
  2054     bboxMorph: tag.bboxMorph,
  2055     isMorph: tag.isMorph,
  2056     paths: paths,
  2057     require: dependencies.length ? dependencies : null,
  2058     transferables: transferables
  2059   };
  2061 function logShape(paths, bbox) {
  2062   var output = '{"bounds":' + JSON.stringify(bbox) + ',"paths":[' + paths.map(function (path) {
  2063       return path.serialize();
  2064     }).join() + ']}';
  2065   console.log(output);
  2067 function SegmentedPath(fillStyle, lineStyle) {
  2068   this.fillStyle = fillStyle;
  2069   this.lineStyle = lineStyle;
  2070   this._head = null;
  2072 SegmentedPath.prototype = {
  2073   addSegment: function (commands, data, morphData) {
  2074     var segment = {
  2075         commands: commands,
  2076         data: data,
  2077         morphData: morphData,
  2078         prev: this._head,
  2079         next: null
  2080       };
  2081     if (this._head) {
  2082       this._head.next = segment;
  2084     this._head = segment;
  2085     return segment;
  2086   },
  2087   removeSegment: function (segment) {
  2088     if (segment.prev) {
  2089       segment.prev.next = segment.next;
  2091     if (segment.next) {
  2092       segment.next.prev = segment.prev;
  2094   },
  2095   insertSegment: function (segment, next) {
  2096     var prev = next.prev;
  2097     segment.prev = prev;
  2098     segment.next = next;
  2099     if (prev) {
  2100       prev.next = segment;
  2102     next.prev = segment;
  2103   },
  2104   head: function () {
  2105     return this._head;
  2106   },
  2107   segmentsConnect: function (first, second) {
  2108     var firstLength = first.data.length;
  2109     return first.data[firstLength - 2] === second.data[0] && first.data[firstLength - 1] === second.data[1];
  2111 };
  2112 var SHAPE_MOVE_TO = 1;
  2113 var SHAPE_LINE_TO = 2;
  2114 var SHAPE_CURVE_TO = 3;
  2115 var SHAPE_WIDE_MOVE_TO = 4;
  2116 var SHAPE_WIDE_LINE_TO = 5;
  2117 var SHAPE_CUBIC_CURVE_TO = 6;
  2118 var SHAPE_CIRCLE = 7;
  2119 var SHAPE_ELLIPSE = 8;
  2120 function ShapePath(fillStyle, lineStyle, commandsCount, dataLength, isMorph, transferables) {
  2121   this.fillStyle = fillStyle;
  2122   this.lineStyle = lineStyle;
  2123   if (commandsCount) {
  2124     this.commands = new Uint8Array(commandsCount);
  2125     this.data = new Int32Array(dataLength);
  2126     this.morphData = isMorph ? new Int32Array(dataLength) : null;
  2127   } else {
  2128     this.commands = [];
  2129     this.data = [];
  2131   this.bounds = null;
  2132   this.strokeBounds = null;
  2133   this.isMorph = !(!isMorph);
  2134   this.fullyInitialized = false;
  2135   if (inWorker) {
  2136     this.buffers = [
  2137       this.commands.buffer,
  2138       this.data.buffer
  2139     ];
  2140     transferables.push(this.commands.buffer, this.data.buffer);
  2141     if (isMorph) {
  2142       this.buffers.push(this.morphData.buffer);
  2143       transferables.push(this.morphData.buffer);
  2145   } else {
  2146     this.buffers = null;
  2149 ShapePath.prototype = {
  2150   get isEmpty() {
  2151     return this.commands.length === 0;
  2152   },
  2153   moveTo: function (x, y) {
  2154     if (this.commands[this.commands.length - 1] === SHAPE_MOVE_TO) {
  2155       this.data[this.data.length - 2] = x;
  2156       this.data[this.data.length - 1] = y;
  2157       return;
  2159     this.commands.push(SHAPE_MOVE_TO);
  2160     this.data.push(x, y);
  2161   },
  2162   lineTo: function (x, y) {
  2163     this.commands.push(SHAPE_LINE_TO);
  2164     this.data.push(x, y);
  2165   },
  2166   curveTo: function (controlX, controlY, anchorX, anchorY) {
  2167     this.commands.push(SHAPE_CURVE_TO);
  2168     this.data.push(controlX, controlY, anchorX, anchorY);
  2169   },
  2170   cubicCurveTo: function (control1X, control1Y, control2X, control2Y, anchorX, anchorY) {
  2171     this.commands.push(SHAPE_CUBIC_CURVE_TO);
  2172     this.data.push(control1X, control1Y, control2X, control2Y, anchorX, anchorY);
  2173   },
  2174   rect: function (x, y, w, h) {
  2175     var x2 = x + w;
  2176     var y2 = y + h;
  2177     this.commands.push(SHAPE_MOVE_TO, SHAPE_LINE_TO, SHAPE_LINE_TO, SHAPE_LINE_TO, SHAPE_LINE_TO);
  2178     this.data.push(x, y, x2, y, x2, y2, x, y2, x, y);
  2179   },
  2180   circle: function (x, y, radius) {
  2181     this.commands.push(SHAPE_CIRCLE);
  2182     this.data.push(x, y, radius);
  2183   },
  2184   ellipse: function (x, y, radiusX, radiusY) {
  2185     this.commands.push(SHAPE_ELLIPSE);
  2186     this.data.push(x, y, radiusX, radiusY);
  2187   },
  2188   draw: function (ctx, clip, ratio, colorTransform) {
  2189     if (clip && !this.fillStyle) {
  2190       return;
  2192     ctx.beginPath();
  2193     var commands = this.commands;
  2194     var data = this.data;
  2195     var morphData = this.morphData;
  2196     var formOpen = false;
  2197     var formOpenX = 0;
  2198     var formOpenY = 0;
  2199     if (!this.isMorph) {
  2200       for (var j = 0, k = 0; j < commands.length; j++) {
  2201         switch (commands[j]) {
  2202         case SHAPE_MOVE_TO:
  2203           formOpen = true;
  2204           formOpenX = data[k++] / 20;
  2205           formOpenY = data[k++] / 20;
  2206           ctx.moveTo(formOpenX, formOpenY);
  2207           break;
  2208         case SHAPE_WIDE_MOVE_TO:
  2209           ctx.moveTo(data[k++] / 20, data[k++] / 20);
  2210           k += 2;
  2211           break;
  2212         case SHAPE_LINE_TO:
  2213           ctx.lineTo(data[k++] / 20, data[k++] / 20);
  2214           break;
  2215         case SHAPE_WIDE_LINE_TO:
  2216           ctx.lineTo(data[k++] / 20, data[k++] / 20);
  2217           k += 2;
  2218           break;
  2219         case SHAPE_CURVE_TO:
  2220           ctx.quadraticCurveTo(data[k++] / 20, data[k++] / 20, data[k++] / 20, data[k++] / 20);
  2221           break;
  2222         case SHAPE_CUBIC_CURVE_TO:
  2223           ctx.bezierCurveTo(data[k++] / 20, data[k++] / 20, data[k++] / 20, data[k++] / 20, data[k++] / 20, data[k++] / 20);
  2224           break;
  2225         case SHAPE_CIRCLE:
  2226           if (formOpen) {
  2227             ctx.lineTo(formOpenX, formOpenY);
  2228             formOpen = false;
  2230           ctx.moveTo((data[k] + data[k + 2]) / 20, data[k + 1] / 20);
  2231           ctx.arc(data[k++] / 20, data[k++] / 20, data[k++] / 20, 0, Math.PI * 2, false);
  2232           break;
  2233         case SHAPE_ELLIPSE:
  2234           if (formOpen) {
  2235             ctx.lineTo(formOpenX, formOpenY);
  2236             formOpen = false;
  2238           var x = data[k++];
  2239           var y = data[k++];
  2240           var rX = data[k++];
  2241           var rY = data[k++];
  2242           var radius;
  2243           if (rX !== rY) {
  2244             ctx.save();
  2245             var ellipseScale;
  2246             if (rX > rY) {
  2247               ellipseScale = rX / rY;
  2248               radius = rY;
  2249               x /= ellipseScale;
  2250               ctx.scale(ellipseScale, 1);
  2251             } else {
  2252               ellipseScale = rY / rX;
  2253               radius = rX;
  2254               y /= ellipseScale;
  2255               ctx.scale(1, ellipseScale);
  2258           ctx.moveTo((x + radius) / 20, y / 20);
  2259           ctx.arc(x / 20, y / 20, radius / 20, 0, Math.PI * 2, false);
  2260           if (rX !== rY) {
  2261             ctx.restore();
  2263           break;
  2264         default:
  2265           if (commands[j] === 0 && j === commands.length - 1) {
  2266             break;
  2268           console.warn('Unknown drawing command encountered: ' + commands[j]);
  2271     } else {
  2272       for (var j = 0, k = 0; j < commands.length; j++) {
  2273         switch (commands[j]) {
  2274         case SHAPE_MOVE_TO:
  2275           ctx.moveTo(morph(data[k] / 20, morphData[k++] / 20, ratio), morph(data[k] / 20, morphData[k++] / 20, ratio));
  2276           break;
  2277         case SHAPE_LINE_TO:
  2278           ctx.lineTo(morph(data[k] / 20, morphData[k++] / 20, ratio), morph(data[k] / 20, morphData[k++] / 20, ratio));
  2279           break;
  2280         case SHAPE_CURVE_TO:
  2281           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));
  2282           break;
  2283         default:
  2284           console.warn('Drawing command not supported for morph shapes: ' + commands[j]);
  2288     if (!clip) {
  2289       var fillStyle = this.fillStyle;
  2290       if (fillStyle) {
  2291         colorTransform.setFillStyle(ctx, fillStyle.style);
  2292         ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = fillStyle.smooth;
  2293         var m = fillStyle.transform;
  2294         ctx.save();
  2295         colorTransform.setAlpha(ctx);
  2296         if (m) {
  2297           ctx.transform(m.a, m.b, m.c, m.d, m.e / 20, m.f / 20);
  2299         ctx.fill();
  2300         ctx.restore();
  2302       var lineStyle = this.lineStyle;
  2303       if (lineStyle) {
  2304         colorTransform.setStrokeStyle(ctx, lineStyle.style);
  2305         ctx.save();
  2306         colorTransform.setAlpha(ctx);
  2307         ctx.lineWidth = Math.max(lineStyle.width / 20, 1);
  2308         ctx.lineCap = lineStyle.lineCap;
  2309         ctx.lineJoin = lineStyle.lineJoin;
  2310         ctx.miterLimit = lineStyle.miterLimit;
  2311         ctx.stroke();
  2312         ctx.restore();
  2314     } else {
  2315       ctx.fill();
  2317     ctx.closePath();
  2318   },
  2319   isPointInPath: function (x, y) {
  2320     if (!(this.fillStyle || this.lineStyle)) {
  2321       return false;
  2323     var bounds = this.strokeBounds || this.bounds || this._calculateBounds();
  2324     if (x < bounds.xMin || x > bounds.xMax || y < bounds.yMin || y > bounds.yMax) {
  2325       return false;
  2327     if (this.fillStyle && this.isPointInFill(x, y)) {
  2328       return true;
  2330     return this.lineStyle && this.lineStyle.width !== undefined && this.isPointInStroke(x, y);
  2331   },
  2332   isPointInFill: function (x, y) {
  2333     var commands = this.commands;
  2334     var data = this.data;
  2335     var length = commands.length;
  2336     var inside = false;
  2337     var fromX = 0;
  2338     var fromY = 0;
  2339     var toX = 0;
  2340     var toY = 0;
  2341     var localX;
  2342     var localY;
  2343     var cpX;
  2344     var cpY;
  2345     var rX;
  2346     var rY;
  2347     var formOpen = false;
  2348     var formOpenX = 0;
  2349     var formOpenY = 0;
  2350     for (var commandIndex = 0, dataIndex = 0; commandIndex < length; commandIndex++) {
  2351       switch (commands[commandIndex]) {
  2352       case SHAPE_WIDE_MOVE_TO:
  2353         dataIndex += 2;
  2354       case SHAPE_MOVE_TO:
  2355         toX = data[dataIndex++];
  2356         toY = data[dataIndex++];
  2357         if (formOpen && intersectsLine(x, y, fromX, fromY, formOpenX, formOpenY)) {
  2358           inside = !inside;
  2360         formOpen = true;
  2361         formOpenX = toX;
  2362         formOpenY = toY;
  2363         break;
  2364       case SHAPE_WIDE_LINE_TO:
  2365         dataIndex += 2;
  2366       case SHAPE_LINE_TO:
  2367         toX = data[dataIndex++];
  2368         toY = data[dataIndex++];
  2369         if (intersectsLine(x, y, fromX, fromY, toX, toY)) {
  2370           inside = !inside;
  2372         break;
  2373       case SHAPE_CURVE_TO:
  2374         cpX = data[dataIndex++];
  2375         cpY = data[dataIndex++];
  2376         toX = data[dataIndex++];
  2377         toY = data[dataIndex++];
  2378         if (cpY > y === fromY > y && toY > y === fromY > y) {
  2379           break;
  2381         if (fromX >= x && cpX >= x && toX >= x) {
  2382           inside = !inside;
  2383           break;
  2385         var a = fromY - 2 * cpY + toY;
  2386         var c = fromY - y;
  2387         var b = 2 * (cpY - fromY);
  2388         var d = b * b - 4 * a * c;
  2389         if (d < 0) {
  2390           break;
  2392         d = Math.sqrt(d);
  2393         a = 1 / (a + a);
  2394         var t1 = (d - b) * a;
  2395         var t2 = (-b - d) * a;
  2396         if (t1 >= 0 && t1 <= 1 && quadraticBezier(fromX, cpX, toX, t1) > x) {
  2397           inside = !inside;
  2399         if (t2 >= 0 && t2 <= 1 && quadraticBezier(fromX, cpX, toX, t2) > x) {
  2400           inside = !inside;
  2402         break;
  2403       case SHAPE_CUBIC_CURVE_TO:
  2404         cpX = data[dataIndex++];
  2405         cpY = data[dataIndex++];
  2406         var cp2X = data[dataIndex++];
  2407         var cp2Y = data[dataIndex++];
  2408         toX = data[dataIndex++];
  2409         toY = data[dataIndex++];
  2410         if (cpY > y === fromY > y && cp2Y > y === fromY > y && toY > y === fromY > y) {
  2411           break;
  2413         if (fromX >= x && cpX >= x && cp2X >= x && toX >= x) {
  2414           inside = !inside;
  2415           break;
  2417         var roots = cubicXAtY(fromX, fromY, cpX, cpY, cp2X, cp2Y, toX, toY, y);
  2418         for (var i = roots.length; i--;) {
  2419           if (roots[i] >= x) {
  2420             inside = !inside;
  2423         break;
  2424       case SHAPE_CIRCLE:
  2425         toX = data[dataIndex++];
  2426         toY = data[dataIndex++];
  2427         var r = data[dataIndex++];
  2428         localX = x - toX;
  2429         localY = y - toY;
  2430         if (localX * localX + localY * localY < r * r) {
  2431           inside = !inside;
  2433         toX += r;
  2434         break;
  2435       case SHAPE_ELLIPSE:
  2436         cpX = data[dataIndex++];
  2437         cpY = data[dataIndex++];
  2438         rX = data[dataIndex++];
  2439         rY = data[dataIndex++];
  2440         localX = x - cpX;
  2441         localY = y - cpY;
  2442         if (localX * localX / (rX * rX) + localY * localY / (rY * rY) <= 1) {
  2443           inside = !inside;
  2445         toX = cpX + rX;
  2446         toY = cpY;
  2447         break;
  2448       default:
  2449         if (!inWorker) {
  2450           console.warn('Drawing command not handled in isPointInPath: ' + commands[commandIndex]);
  2453       fromX = toX;
  2454       fromY = toY;
  2456     if (formOpen && intersectsLine(x, y, fromX, fromY, formOpenX, formOpenY)) {
  2457       inside = !inside;
  2459     return inside;
  2460   },
  2461   isPointInStroke: function (x, y) {
  2462     var commands = this.commands;
  2463     var data = this.data;
  2464     var length = commands.length;
  2465     var width = this.lineStyle.width;
  2466     var halfWidth = width / 2;
  2467     var halfWidthSq = halfWidth * halfWidth;
  2468     var minX = x - halfWidth;
  2469     var maxX = x + halfWidth;
  2470     var minY = y - halfWidth;
  2471     var maxY = y + halfWidth;
  2472     var fromX = 0;
  2473     var fromY = 0;
  2474     var toX = 0;
  2475     var toY = 0;
  2476     var localX;
  2477     var localY;
  2478     var cpX;
  2479     var cpY;
  2480     var rX;
  2481     var rY;
  2482     var curveX;
  2483     var curveY;
  2484     var t;
  2485     for (var commandIndex = 0, dataIndex = 0; commandIndex < length; commandIndex++) {
  2486       switch (commands[commandIndex]) {
  2487       case SHAPE_WIDE_MOVE_TO:
  2488         dataIndex += 2;
  2489       case SHAPE_MOVE_TO:
  2490         toX = data[dataIndex++];
  2491         toY = data[dataIndex++];
  2492         break;
  2493       case SHAPE_WIDE_LINE_TO:
  2494         dataIndex += 2;
  2495       case SHAPE_LINE_TO:
  2496         toX = data[dataIndex++];
  2497         toY = data[dataIndex++];
  2498         if (fromX === toX && fromY === toY) {
  2499           break;
  2501         if (maxX < fromX && maxX < toX || minX > fromX && minX > toX || maxY < fromY && maxY < toY || minY > fromY && minY > toY) {
  2502           break;
  2504         if (toX === fromX || toY === fromY) {
  2505           return true;
  2507         t = ((x - fromX) * (toX - fromX) + (y - fromY) * (toY - fromY)) / distanceSq(fromX, fromY, toX, toY);
  2508         if (t < 0) {
  2509           if (distanceSq(x, y, fromX, fromY) <= halfWidthSq) {
  2510             return true;
  2512           break;
  2514         if (t > 1) {
  2515           if (distanceSq(x, y, toX, toY) <= halfWidthSq) {
  2516             return true;
  2518           break;
  2520         if (distanceSq(x, y, fromX + t * (toX - fromX), fromY + t * (toY - fromY)) <= halfWidthSq) {
  2521           return true;
  2523         break;
  2524       case SHAPE_CURVE_TO:
  2525         cpX = data[dataIndex++];
  2526         cpY = data[dataIndex++];
  2527         toX = data[dataIndex++];
  2528         toY = data[dataIndex++];
  2529         var extremeX = quadraticBezierExtreme(fromX, cpX, toX);
  2530         if (maxX < fromX && maxX < extremeX && maxX < toX || minX > fromX && minX > extremeX && minX > toX) {
  2531           break;
  2533         var extremeY = quadraticBezierExtreme(fromY, cpY, toY);
  2534         if (maxY < fromY && maxY < extremeY && maxY < toY || minY > fromY && minY > extremeY && minY > toY) {
  2535           break;
  2537         for (t = 0; t < 1; t += 0.02) {
  2538           curveX = quadraticBezier(fromX, cpX, toX, t);
  2539           if (curveX < minX || curveX > maxX) {
  2540             continue;
  2542           curveY = quadraticBezier(fromY, cpY, toY, t);
  2543           if (curveY < minY || curveY > maxY) {
  2544             continue;
  2546           if ((x - curveX) * (x - curveX) + (y - curveY) * (y - curveY) < halfWidthSq) {
  2547             return true;
  2550         break;
  2551       case SHAPE_CUBIC_CURVE_TO:
  2552         cpX = data[dataIndex++];
  2553         cpY = data[dataIndex++];
  2554         var cp2X = data[dataIndex++];
  2555         var cp2Y = data[dataIndex++];
  2556         toX = data[dataIndex++];
  2557         toY = data[dataIndex++];
  2558         var extremesX = cubicBezierExtremes(fromX, cpX, cp2X, toX);
  2559         while (extremesX.length < 2) {
  2560           extremesX.push(toX);
  2562         if (maxX < fromX && maxX < toX && maxX < extremesX[0] && maxX < extremesX[1] || minX > fromX && minX > toX && minX > extremesX[0] && minX > extremesX[1]) {
  2563           break;
  2565         var extremesY = cubicBezierExtremes(fromY, cpY, cp2Y, toY);
  2566         while (extremesY.length < 2) {
  2567           extremesY.push(toY);
  2569         if (maxY < fromY && maxY < toY && maxY < extremesY[0] && maxY < extremesY[1] || minY > fromY && minY > toY && minY > extremesY[0] && minY > extremesY[1]) {
  2570           break;
  2572         for (t = 0; t < 1; t += 0.02) {
  2573           curveX = cubicBezier(fromX, cpX, cp2X, toX, t);
  2574           if (curveX < minX || curveX > maxX) {
  2575             continue;
  2577           curveY = cubicBezier(fromY, cpY, cp2Y, toY, t);
  2578           if (curveY < minY || curveY > maxY) {
  2579             continue;
  2581           if ((x - curveX) * (x - curveX) + (y - curveY) * (y - curveY) < halfWidthSq) {
  2582             return true;
  2585         break;
  2586       case SHAPE_CIRCLE:
  2587         cpX = data[dataIndex++];
  2588         cpY = data[dataIndex++];
  2589         var r = data[dataIndex++];
  2590         toX = cpX + r;
  2591         toY = cpY;
  2592         if (maxX < cpX - r || minX > cpX + r || maxY < cpY - r || minY > cpY + r) {
  2593           break;
  2595         localX = x - cpX;
  2596         localY = y - cpY;
  2597         var rMin = r - halfWidth;
  2598         var rMax = r + halfWidth;
  2599         var distSq = localX * localX + localY * localY;
  2600         if (distSq >= rMin * rMin && distSq <= rMax * rMax) {
  2601           return true;
  2603         break;
  2604       case SHAPE_ELLIPSE:
  2605         cpX = data[dataIndex++];
  2606         cpY = data[dataIndex++];
  2607         rX = data[dataIndex++];
  2608         rY = data[dataIndex++];
  2609         toX = cpX + rX;
  2610         toY = cpY;
  2611         localX = Math.abs(x - cpX);
  2612         localY = Math.abs(y - cpY);
  2613         localX -= halfWidth;
  2614         localY -= halfWidth;
  2615         if (localX * localX / (rX * rX) + localY * localY / (rY * rY) > 1) {
  2616           break;
  2618         localX += width;
  2619         localY += width;
  2620         if (localX * localX / (rX * rX) + localY * localY / (rY * rY) > 1) {
  2621           return true;
  2623         break;
  2624       default:
  2625         if (!inWorker) {
  2626           console.warn('Drawing command not handled in isPointInPath: ' + commands[commandIndex]);
  2629       fromX = toX;
  2630       fromY = toY;
  2632     return false;
  2633   },
  2634   getBounds: function (includeStroke) {
  2635     var bounds = includeStroke ? this.strokeBounds : this.bounds;
  2636     if (!bounds) {
  2637       this._calculateBounds();
  2638       bounds = includeStroke ? this.strokeBounds : this.bounds;
  2640     return bounds;
  2641   },
  2642   _calculateBounds: function () {
  2643     var commands = this.commands;
  2644     var data = this.data;
  2645     var length = commands.length;
  2646     var bounds;
  2647     if (commands[0] === SHAPE_MOVE_TO || commands[0] > SHAPE_CUBIC_CURVE_TO) {
  2648       bounds = {
  2649         xMin: data[0],
  2650         yMin: data[1]
  2651       };
  2652     } else {
  2653       bounds = {
  2654         xMin: 0,
  2655         yMin: 0
  2656       };
  2658     bounds.xMax = bounds.xMin;
  2659     bounds.yMax = bounds.yMin;
  2660     var fromX = bounds.xMin;
  2661     var fromY = bounds.yMin;
  2662     for (var commandIndex = 0, dataIndex = 0; commandIndex < length; commandIndex++) {
  2663       var toX;
  2664       var toY;
  2665       var cpX;
  2666       var cpY;
  2667       switch (commands[commandIndex]) {
  2668       case SHAPE_WIDE_MOVE_TO:
  2669         dataIndex += 2;
  2670       case SHAPE_MOVE_TO:
  2671         toX = data[dataIndex++];
  2672         toY = data[dataIndex++];
  2673         extendBoundsByPoint(bounds, toX, toY);
  2674         break;
  2675       case SHAPE_WIDE_LINE_TO:
  2676         dataIndex += 2;
  2677       case SHAPE_LINE_TO:
  2678         toX = data[dataIndex++];
  2679         toY = data[dataIndex++];
  2680         extendBoundsByPoint(bounds, toX, toY);
  2681         break;
  2682       case SHAPE_CURVE_TO:
  2683         cpX = data[dataIndex++];
  2684         cpY = data[dataIndex++];
  2685         toX = data[dataIndex++];
  2686         toY = data[dataIndex++];
  2687         extendBoundsByPoint(bounds, toX, toY);
  2688         if (cpX < fromX || cpX > toX) {
  2689           extendBoundsByX(bounds, quadraticBezierExtreme(fromX, cpX, toX));
  2691         if (cpY < fromY || cpY > toY) {
  2692           extendBoundsByY(bounds, quadraticBezierExtreme(fromY, cpY, toY));
  2694         break;
  2695       case SHAPE_CUBIC_CURVE_TO:
  2696         cpX = data[dataIndex++];
  2697         cpY = data[dataIndex++];
  2698         var cp2X = data[dataIndex++];
  2699         var cp2Y = data[dataIndex++];
  2700         toX = data[dataIndex++];
  2701         toY = data[dataIndex++];
  2702         extendBoundsByPoint(bounds, toX, toY);
  2703         var extremes;
  2704         var i;
  2705         if (cpX < fromX || cp2X < fromX || cpX > toX || cp2X > toX) {
  2706           extremes = cubicBezierExtremes(fromX, cpX, cp2X, toX);
  2707           for (i = extremes.length; i--;) {
  2708             extendBoundsByX(bounds, extremes[i]);
  2711         if (cpY < fromY || cp2Y < fromY || cpY > toY || cp2Y > toY) {
  2712           extremes = cubicBezierExtremes(fromY, cpY, cp2Y, toY);
  2713           for (i = extremes.length; i--;) {
  2714             extendBoundsByY(bounds, extremes[i]);
  2717         break;
  2718       case SHAPE_CIRCLE:
  2719         toX = data[dataIndex++];
  2720         toY = data[dataIndex++];
  2721         var radius = data[dataIndex++];
  2722         extendBoundsByPoint(bounds, toX - radius, toY - radius);
  2723         extendBoundsByPoint(bounds, toX + radius, toY + radius);
  2724         toX += radius;
  2725         break;
  2726       case SHAPE_ELLIPSE:
  2727         toX = data[dataIndex++];
  2728         toY = data[dataIndex++];
  2729         var radiusX = data[dataIndex++];
  2730         var radiusY = data[dataIndex++];
  2731         extendBoundsByPoint(bounds, toX - radiusX, toY - radiusY);
  2732         extendBoundsByPoint(bounds, toX + radiusX, toY + radiusY);
  2733         toX += radiusX;
  2734         break;
  2735       default:
  2736         if (!inWorker) {
  2737           console.warn('Drawing command not handled in bounds calculation: ' + commands[commandIndex]);
  2740       fromX = toX;
  2741       fromY = toY;
  2743     this.bounds = bounds;
  2744     if (this.lineStyle) {
  2745       var halfLineWidth = this.lineStyle.width / 2;
  2746       this.strokeBounds = {
  2747         xMin: bounds.xMin - halfLineWidth,
  2748         yMin: bounds.yMin - halfLineWidth,
  2749         xMax: bounds.xMax + halfLineWidth,
  2750         yMax: bounds.yMax + halfLineWidth
  2751       };
  2752       return this.strokeBounds;
  2753     } else {
  2754       this.strokeBounds = bounds;
  2756     return bounds;
  2757   },
  2758   serialize: function () {
  2759     var output = '{';
  2760     if (this.fillStyle) {
  2761       output += '"fill":' + JSON.stringify(this.fillStyle) + ',';
  2763     if (this.lineStyle) {
  2764       output += '"stroke":' + JSON.stringify(this.lineStyle) + ',';
  2766     output += '"commands":[' + Array.apply([], this.commands).join() + '],';
  2767     output += '"data":[' + Array.apply([], this.data).join() + ']';
  2768     return output + '}';
  2770 };
  2771 ShapePath.fromPlainObject = function (obj) {
  2772   var path = new ShapePath(obj.fill || null, obj.stroke || null);
  2773   path.commands = new Uint8Array(obj.commands);
  2774   path.data = new Int32Array(obj.data);
  2775   if (!inWorker) {
  2776     finishShapePath(path);
  2778   return path;
  2779 };
  2780 function distanceSq(x1, y1, x2, y2) {
  2781   var dX = x2 - x1;
  2782   var dY = y2 - y1;
  2783   return dX * dX + dY * dY;
  2785 function intersectsLine(x, y, x1, y1, x2, y2) {
  2786   return y2 > y !== y1 > y && x < (x1 - x2) * (y - y2) / (y1 - y2) + x2;
  2788 function quadraticBezier(from, cp, to, t) {
  2789   var inverseT = 1 - t;
  2790   return from * inverseT * inverseT + 2 * cp * inverseT * t + to * t * t;
  2792 function quadraticBezierExtreme(from, cp, to) {
  2793   var t = (from - cp) / (from - 2 * cp + to);
  2794   if (t < 0) {
  2795     return from;
  2797   if (t > 1) {
  2798     return to;
  2800   return quadraticBezier(from, cp, to, t);
  2802 function cubicBezier(from, cp, cp2, to, t) {
  2803   var tSq = t * t;
  2804   var inverseT = 1 - t;
  2805   var inverseTSq = inverseT * inverseT;
  2806   return from * inverseT * inverseTSq + 3 * cp * t * inverseTSq + 3 * cp2 * inverseT * tSq + to * t * tSq;
  2808 function cubicBezierExtremes(from, cp, cp2, to) {
  2809   var d1 = cp - from;
  2810   var d2 = cp2 - cp;
  2811   d2 *= 2;
  2812   var d3 = to - cp2;
  2813   if (d1 + d3 === d2) {
  2814     d3 *= 1.0001;
  2816   var fHead = 2 * d1 - d2;
  2817   var part1 = d2 - 2 * d1;
  2818   var fCenter = Math.sqrt(part1 * part1 - 4 * d1 * (d1 - d2 + d3));
  2819   var fTail = 2 * (d1 - d2 + d3);
  2820   var t1 = (fHead + fCenter) / fTail;
  2821   var t2 = (fHead - fCenter) / fTail;
  2822   var result = [];
  2823   if (t1 >= 0 && t1 <= 1) {
  2824     result.push(cubicBezier(from, cp, cp2, to, t1));
  2826   if (t2 >= 0 && t2 <= 1) {
  2827     result.push(cubicBezier(from, cp, cp2, to, t2));
  2829   return result;
  2831 function cubicXAtY(x0, y0, cx, cy, cx1, cy1, x1, y1, y) {
  2832   var dX = 3 * (cx - x0);
  2833   var dY = 3 * (cy - y0);
  2834   var bX = 3 * (cx1 - cx) - dX;
  2835   var bY = 3 * (cy1 - cy) - dY;
  2836   var c3X = x1 - x0 - dX - bX;
  2837   var c3Y = y1 - y0 - dY - bY;
  2838   function f(t) {
  2839     return t * (dY + t * (bY + t * c3Y)) + y0 - y;
  2841   function pointAt(t) {
  2842     if (t < 0) {
  2843       t = 0;
  2844     } else if (t > 1) {
  2845       t = 1;
  2847     return x0 + t * (dX + t * (bX + t * c3X));
  2849   function bisectCubicBezierRange(f, l, r, limit) {
  2850     if (Math.abs(r - l) <= limit) {
  2851       return;
  2853     var middle = 0.5 * (l + r);
  2854     if (f(l) * f(r) <= 0) {
  2855       left = l;
  2856       right = r;
  2857       return;
  2859     bisectCubicBezierRange(f, l, middle, limit);
  2860     bisectCubicBezierRange(f, middle, r, limit);
  2862   var left = 0;
  2863   var right = 1;
  2864   bisectCubicBezierRange(f, 0, 1, 0.05);
  2865   var t0 = findRoot(left, right, f, 50, 0.000001);
  2866   var evalResult = Math.abs(f(t0));
  2867   if (evalResult > 0.00001) {
  2868     return [];
  2870   var result = [];
  2871   if (t0 <= 1) {
  2872     result.push(pointAt(t0));
  2874   var a = c3Y;
  2875   var b = t0 * a + bY;
  2876   var c = t0 * b + dY;
  2877   var d = b * b - 4 * a * c;
  2878   if (d < 0) {
  2879     return result;
  2881   d = Math.sqrt(d);
  2882   a = 1 / (a + a);
  2883   var t1 = (d - b) * a;
  2884   var t2 = (-b - d) * a;
  2885   if (t1 >= 0 && t1 <= 1) {
  2886     result.push(pointAt(t1));
  2888   if (t2 >= 0 && t2 <= 1) {
  2889     result.push(pointAt(t2));
  2891   return result;
  2893 function findRoot(x0, x2, f, maxIterations, epsilon) {
  2894   var x1;
  2895   var y0;
  2896   var y1;
  2897   var y2;
  2898   var b;
  2899   var c;
  2900   var y10;
  2901   var y20;
  2902   var y21;
  2903   var xm;
  2904   var ym;
  2905   var temp;
  2906   var xmlast = x0;
  2907   y0 = f(x0);
  2908   if (y0 === 0) {
  2909     return x0;
  2911   y2 = f(x2);
  2912   if (y2 === 0) {
  2913     return x2;
  2915   if (y2 * y0 > 0) {
  2916     return x0;
  2918   var __iter = 0;
  2919   for (var i = 0; i < maxIterations; ++i) {
  2920     __iter++;
  2921     x1 = 0.5 * (x2 + x0);
  2922     y1 = f(x1);
  2923     if (y1 === 0) {
  2924       return x1;
  2926     if (Math.abs(x1 - x0) < epsilon) {
  2927       return x1;
  2929     if (y1 * y0 > 0) {
  2930       temp = x0;
  2931       x0 = x2;
  2932       x2 = temp;
  2933       temp = y0;
  2934       y0 = y2;
  2935       y2 = temp;
  2937     y10 = y1 - y0;
  2938     y21 = y2 - y1;
  2939     y20 = y2 - y0;
  2940     if (y2 * y20 < 2 * y1 * y10) {
  2941       x2 = x1;
  2942       y2 = y1;
  2943     } else {
  2944       b = (x1 - x0) / y10;
  2945       c = (y10 - y21) / (y21 * y20);
  2946       xm = x0 - b * y0 * (1 - c * y1);
  2947       ym = f(xm);
  2948       if (ym === 0) {
  2949         return xm;
  2951       if (Math.abs(xm - xmlast) < epsilon) {
  2952         return xm;
  2954       xmlast = xm;
  2955       if (ym * y0 < 0) {
  2956         x2 = xm;
  2957         y2 = ym;
  2958       } else {
  2959         x0 = xm;
  2960         y0 = ym;
  2961         x2 = x1;
  2962         y2 = y1;
  2966   return x1;
  2968 function extendBoundsByPoint(bounds, x, y) {
  2969   if (x < bounds.xMin) {
  2970     bounds.xMin = x;
  2971   } else if (x > bounds.xMax) {
  2972     bounds.xMax = x;
  2974   if (y < bounds.yMin) {
  2975     bounds.yMin = y;
  2976   } else if (y > bounds.yMax) {
  2977     bounds.yMax = y;
  2980 function extendBoundsByX(bounds, x) {
  2981   if (x < bounds.xMin) {
  2982     bounds.xMin = x;
  2983   } else if (x > bounds.xMax) {
  2984     bounds.xMax = x;
  2987 function extendBoundsByY(bounds, y) {
  2988   if (y < bounds.yMin) {
  2989     bounds.yMin = y;
  2990   } else if (y > bounds.yMax) {
  2991     bounds.yMax = y;
  2994 function morph(start, end, ratio) {
  2995   return start + (end - start) * ratio;
  2997 function finishShapePath(path, dictionaryResolved) {
  2998   if (path.fullyInitialized) {
  2999     return path;
  3001   if (!(path instanceof ShapePath)) {
  3002     var untypedPath = path;
  3003     path = new ShapePath(path.fillStyle, path.lineStyle, 0, 0, path.isMorph);
  3004     path.commands = new Uint8Array(untypedPath.buffers[0]);
  3005     path.data = new Int32Array(untypedPath.buffers[1]);
  3006     if (untypedPath.isMorph) {
  3007       path.morphData = new Int32Array(untypedPath.buffers[2]);
  3009     path.buffers = null;
  3011   path.fillStyle && initStyle(path.fillStyle, dictionaryResolved);
  3012   path.lineStyle && initStyle(path.lineStyle, dictionaryResolved);
  3013   path.fullyInitialized = true;
  3014   return path;
  3016 var inWorker = typeof window === 'undefined';
  3017 var factoryCtx = !inWorker ? document.createElement('canvas').getContext('2d') : null;
  3018 function buildLinearGradientFactory(colorStops) {
  3019   var defaultGradient = factoryCtx.createLinearGradient(-1, 0, 1, 0);
  3020   for (var i = 0; i < colorStops.length; i++) {
  3021     defaultGradient.addColorStop(colorStops[i].ratio, colorStops[i].color);
  3023   var fn = function createLinearGradient(ctx, colorTransform) {
  3024     var gradient = ctx.createLinearGradient(-1, 0, 1, 0);
  3025     for (var i = 0; i < colorStops.length; i++) {
  3026       colorTransform.addGradientColorStop(gradient, colorStops[i].ratio, colorStops[i].color);
  3028     return gradient;
  3029   };
  3030   fn.defaultFillStyle = defaultGradient;
  3031   return fn;
  3033 function buildRadialGradientFactory(focalPoint, colorStops) {
  3034   var defaultGradient = factoryCtx.createRadialGradient(focalPoint, 0, 0, 0, 0, 1);
  3035   for (var i = 0; i < colorStops.length; i++) {
  3036     defaultGradient.addColorStop(colorStops[i].ratio, colorStops[i].color);
  3038   var fn = function createRadialGradient(ctx, colorTransform) {
  3039     var gradient = ctx.createRadialGradient(focalPoint, 0, 0, 0, 0, 1);
  3040     for (var i = 0; i < colorStops.length; i++) {
  3041       colorTransform.addGradientColorStop(gradient, colorStops[i].ratio, colorStops[i].color);
  3043     return gradient;
  3044   };
  3045   fn.defaultFillStyle = defaultGradient;
  3046   return fn;
  3048 function buildBitmapPatternFactory(img, repeat) {
  3049   var defaultPattern = factoryCtx.createPattern(img, repeat);
  3050   var cachedTransform, cachedTransformKey;
  3051   var fn = function createBitmapPattern(ctx, colorTransform) {
  3052     if (!colorTransform.mode) {
  3053       return defaultPattern;
  3055     var key = colorTransform.getTransformFingerprint();
  3056     if (key === cachedTransformKey) {
  3057       return cachedTransform;
  3059     var canvas = document.createElement('canvas');
  3060     canvas.width = img.width;
  3061     canvas.height = img.height;
  3062     var ctx = canvas.getContext('2d');
  3063     colorTransform.setAlpha(ctx, true);
  3064     ctx.drawImage(img, 0, 0);
  3065     cachedTransform = ctx.createPattern(canvas, repeat);
  3066     cachedTransformKey = key;
  3067     return cachedTransform;
  3068   };
  3069   fn.defaultFillStyle = defaultPattern;
  3070   return fn;
  3072 function initStyle(style, dictionaryResolved) {
  3073   if (style.type === undefined) {
  3074     return;
  3076   switch (style.type) {
  3077   case GRAPHICS_FILL_SOLID:
  3078     break;
  3079   case GRAPHICS_FILL_LINEAR_GRADIENT:
  3080   case GRAPHICS_FILL_RADIAL_GRADIENT:
  3081   case GRAPHICS_FILL_FOCAL_RADIAL_GRADIENT:
  3082     var records = style.records, colorStops = [];
  3083     for (var j = 0, n = records.length; j < n; j++) {
  3084       var record = records[j];
  3085       var colorStr = rgbaObjToStr(record.color);
  3086       colorStops.push({
  3087         ratio: record.ratio / 255,
  3088         color: colorStr
  3089       });
  3091     var gradientConstructor;
  3092     var isLinear = style.type === GRAPHICS_FILL_LINEAR_GRADIENT;
  3093     if (isLinear) {
  3094       gradientConstructor = buildLinearGradientFactory(colorStops);
  3095     } else {
  3096       gradientConstructor = buildRadialGradientFactory((style.focalPoint | 0) / 20, colorStops);
  3098     style.style = gradientConstructor;
  3099     break;
  3100   case GRAPHICS_FILL_REPEATING_BITMAP:
  3101   case GRAPHICS_FILL_CLIPPED_BITMAP:
  3102   case GRAPHICS_FILL_NONSMOOTHED_REPEATING_BITMAP:
  3103   case GRAPHICS_FILL_NONSMOOTHED_CLIPPED_BITMAP:
  3104     var bitmap = dictionaryResolved[style.bitmapId];
  3105     var repeat = style.type === GRAPHICS_FILL_REPEATING_BITMAP || style.type === GRAPHICS_FILL_NONSMOOTHED_REPEATING_BITMAP;
  3106     style.style = buildBitmapPatternFactory(bitmap.props.img, repeat ? 'repeat' : 'no-repeat');
  3107     break;
  3108   default:
  3109     fail('invalid fill style', 'shape');
  3112 var SOUND_SIZE_8_BIT = 0;
  3113 var SOUND_SIZE_16_BIT = 1;
  3114 var SOUND_TYPE_MONO = 0;
  3115 var SOUND_TYPE_STEREO = 1;
  3116 var SOUND_FORMAT_PCM_BE = 0;
  3117 var SOUND_FORMAT_ADPCM = 1;
  3118 var SOUND_FORMAT_MP3 = 2;
  3119 var SOUND_FORMAT_PCM_LE = 3;
  3120 var SOUND_FORMAT_NELLYMOSER_16 = 4;
  3121 var SOUND_FORMAT_NELLYMOSER_8 = 5;
  3122 var SOUND_FORMAT_NELLYMOSER = 6;
  3123 var SOUND_FORMAT_SPEEX = 11;
  3124 var SOUND_RATES = [
  3125     5512,
  3126     11250,
  3127     22500,
  3128     44100
  3129   ];
  3130 var WaveHeader = new Uint8Array([
  3131     82,
  3132     73,
  3133     70,
  3134     70,
  3135     0,
  3136     0,
  3137     0,
  3138     0,
  3139     87,
  3140     65,
  3141     86,
  3142     69,
  3143     102,
  3144     109,
  3145     116,
  3146     32,
  3147     16,
  3148     0,
  3149     0,
  3150     0,
  3151     1,
  3152     0,
  3153     2,
  3154     0,
  3155     68,
  3156     172,
  3157     0,
  3158     0,
  3159     16,
  3160     177,
  3161     2,
  3162     0,
  3163     4,
  3164     0,
  3165     16,
  3166     0,
  3167     100,
  3168     97,
  3169     116,
  3170     97,
  3171     0,
  3172     0,
  3173     0,
  3175   ]);
  3176 function packageWave(data, sampleRate, channels, size, swapBytes) {
  3177   var sizeInBytes = size >> 3;
  3178   var sizePerSecond = channels * sampleRate * sizeInBytes;
  3179   var sizePerSample = channels * sizeInBytes;
  3180   var dataLength = data.length + (data.length & 1);
  3181   var buffer = new ArrayBuffer(WaveHeader.length + dataLength);
  3182   var bytes = new Uint8Array(buffer);
  3183   bytes.set(WaveHeader);
  3184   if (swapBytes) {
  3185     for (var i = 0, j = WaveHeader.length; i < data.length; i += 2, j += 2) {
  3186       bytes[j] = data[i + 1];
  3187       bytes[j + 1] = data[i];
  3189   } else {
  3190     bytes.set(data, WaveHeader.length);
  3192   var view = new DataView(buffer);
  3193   view.setUint32(4, dataLength + 36, true);
  3194   view.setUint16(22, channels, true);
  3195   view.setUint32(24, sampleRate, true);
  3196   view.setUint32(28, sizePerSecond, true);
  3197   view.setUint16(32, sizePerSample, true);
  3198   view.setUint16(34, size, true);
  3199   view.setUint32(40, dataLength, true);
  3200   return {
  3201     data: bytes,
  3202     mimeType: 'audio/wav'
  3203   };
  3205 function defineSound(tag, dictionary) {
  3206   var channels = tag.soundType == SOUND_TYPE_STEREO ? 2 : 1;
  3207   var samplesCount = tag.samplesCount;
  3208   var sampleRate = SOUND_RATES[tag.soundRate];
  3209   var data = tag.soundData;
  3210   var pcm, packaged;
  3211   switch (tag.soundFormat) {
  3212   case SOUND_FORMAT_PCM_BE:
  3213     pcm = new Float32Array(samplesCount * channels);
  3214     if (tag.soundSize == SOUND_SIZE_16_BIT) {
  3215       for (var i = 0, j = 0; i < pcm.length; i++, j += 2)
  3216         pcm[i] = (data[j] << 24 | data[j + 1] << 16) / 2147483648;
  3217       packaged = packageWave(data, sampleRate, channels, 16, true);
  3218     } else {
  3219       for (var i = 0; i < pcm.length; i++)
  3220         pcm[i] = (data[i] - 128) / 128;
  3221       packaged = packageWave(data, sampleRate, channels, 8, false);
  3223     break;
  3224   case SOUND_FORMAT_PCM_LE:
  3225     pcm = new Float32Array(samplesCount * channels);
  3226     if (tag.soundSize == SOUND_SIZE_16_BIT) {
  3227       for (var i = 0, j = 0; i < pcm.length; i++, j += 2)
  3228         pcm[i] = (data[j + 1] << 24 | data[j] << 16) / 2147483648;
  3229       packaged = packageWave(data, sampleRate, channels, 16, false);
  3230     } else {
  3231       for (var i = 0; i < pcm.length; i++)
  3232         pcm[i] = (data[i] - 128) / 128;
  3233       packaged = packageWave(data, sampleRate, channels, 8, false);
  3235     break;
  3236   case SOUND_FORMAT_MP3:
  3237     packaged = {
  3238       data: new Uint8Array(data.subarray(2)),
  3239       mimeType: 'audio/mpeg'
  3240     };
  3241     break;
  3242   case SOUND_FORMAT_ADPCM:
  3243     var pcm16 = new Int16Array(samplesCount * channels);
  3244     decodeACPCMSoundData(data, pcm16, channels);
  3245     pcm = new Float32Array(samplesCount * channels);
  3246     for (var i = 0; i < pcm.length; i++)
  3247       pcm[i] = pcm16[i] / 32768;
  3248     packaged = packageWave(new Uint8Array(pcm16.buffer), sampleRate, channels, 16, !new Uint8Array(new Uint16Array([
  3250     ]).buffer)[0]);
  3251     break;
  3252   default:
  3253     throw new Error('Unsupported audio format: ' + tag.soundFormat);
  3255   var sound = {
  3256       type: 'sound',
  3257       id: tag.id,
  3258       sampleRate: sampleRate,
  3259       channels: channels,
  3260       pcm: pcm
  3261     };
  3262   if (packaged)
  3263     sound.packaged = packaged;
  3264   return sound;
  3266 var ACPCMIndexTables = [
  3268       -1,
  3270     ],
  3272       -1,
  3273       -1,
  3274       2,
  3276     ],
  3278       -1,
  3279       -1,
  3280       -1,
  3281       -1,
  3282       2,
  3283       4,
  3284       6,
  3286     ],
  3288       -1,
  3289       -1,
  3290       -1,
  3291       -1,
  3292       -1,
  3293       -1,
  3294       -1,
  3295       -1,
  3296       1,
  3297       2,
  3298       4,
  3299       6,
  3300       8,
  3301       10,
  3302       13,
  3303       16
  3305   ];
  3306 var ACPCMStepSizeTable = [
  3307     7,
  3308     8,
  3309     9,
  3310     10,
  3311     11,
  3312     12,
  3313     13,
  3314     14,
  3315     16,
  3316     17,
  3317     19,
  3318     21,
  3319     23,
  3320     25,
  3321     28,
  3322     31,
  3323     34,
  3324     37,
  3325     41,
  3326     45,
  3327     50,
  3328     55,
  3329     60,
  3330     66,
  3331     73,
  3332     80,
  3333     88,
  3334     97,
  3335     107,
  3336     118,
  3337     130,
  3338     143,
  3339     157,
  3340     173,
  3341     190,
  3342     209,
  3343     230,
  3344     253,
  3345     279,
  3346     307,
  3347     337,
  3348     371,
  3349     408,
  3350     449,
  3351     494,
  3352     544,
  3353     598,
  3354     658,
  3355     724,
  3356     796,
  3357     876,
  3358     963,
  3359     1060,
  3360     1166,
  3361     1282,
  3362     1411,
  3363     1552,
  3364     1707,
  3365     1878,
  3366     2066,
  3367     2272,
  3368     2499,
  3369     2749,
  3370     3024,
  3371     3327,
  3372     3660,
  3373     4026,
  3374     4428,
  3375     4871,
  3376     5358,
  3377     5894,
  3378     6484,
  3379     7132,
  3380     7845,
  3381     8630,
  3382     9493,
  3383     10442,
  3384     11487,
  3385     12635,
  3386     13899,
  3387     15289,
  3388     16818,
  3389     18500,
  3390     20350,
  3391     22385,
  3392     24623,
  3393     27086,
  3394     29794,
  3395     32767
  3396   ];
  3397 function decodeACPCMSoundData(data, pcm16, channels) {
  3398   function readBits(n, signed) {
  3399     while (dataBufferLength < n) {
  3400       dataBuffer = dataBuffer << 8 | data[dataPosition++];
  3401       dataBufferLength += 8;
  3403     dataBufferLength -= n;
  3404     return dataBuffer >>> dataBufferLength & (1 << n) - 1;
  3406   var dataPosition = 0;
  3407   var dataBuffer = 0;
  3408   var dataBufferLength = 0;
  3409   var pcmPosition = 0;
  3410   var codeSize = readBits(2);
  3411   var indexTable = ACPCMIndexTables[codeSize];
  3412   while (pcmPosition < pcm16.length) {
  3413     var x = pcm16[pcmPosition++] = readBits(16) << 16 >> 16, x2;
  3414     var stepIndex = readBits(6), stepIndex2;
  3415     if (channels > 1) {
  3416       x2 = pcm16[pcmPosition++] = readBits(16) << 16 >> 16;
  3417       stepIndex2 = readBits(6);
  3419     var signMask = 1 << codeSize + 1;
  3420     for (var i = 0; i < 4095; i++) {
  3421       var nibble = readBits(codeSize + 2);
  3422       var step = ACPCMStepSizeTable[stepIndex];
  3423       var sum = 0;
  3424       for (var currentBit = signMask >> 1; currentBit; currentBit >>= 1, step >>= 1) {
  3425         if (nibble & currentBit)
  3426           sum += step;
  3428       x += (nibble & signMask ? -1 : 1) * (sum + step);
  3429       pcm16[pcmPosition++] = x = x < -32768 ? -32768 : x > 32767 ? 32767 : x;
  3430       stepIndex += indexTable[nibble & ~signMask];
  3431       stepIndex = stepIndex < 0 ? 0 : stepIndex > 88 ? 88 : stepIndex;
  3432       if (channels > 1) {
  3433         nibble = readBits(codeSize + 2);
  3434         step = ACPCMStepSizeTable[stepIndex2];
  3435         sum = 0;
  3436         for (var currentBit = signMask >> 1; currentBit; currentBit >>= 1, step >>= 1) {
  3437           if (nibble & currentBit)
  3438             sum += step;
  3440         x2 += (nibble & signMask ? -1 : 1) * (sum + step);
  3441         pcm16[pcmPosition++] = x2 = x2 < -32768 ? -32768 : x2 > 32767 ? 32767 : x2;
  3442         stepIndex2 += indexTable[nibble & ~signMask];
  3443         stepIndex2 = stepIndex2 < 0 ? 0 : stepIndex2 > 88 ? 88 : stepIndex2;
  3448 var nextSoundStreamId = 0;
  3449 function SwfSoundStream(samplesCount, sampleRate, channels) {
  3450   this.streamId = nextSoundStreamId++;
  3451   this.samplesCount = samplesCount;
  3452   this.sampleRate = sampleRate;
  3453   this.channels = channels;
  3454   this.format = null;
  3455   this.currentSample = 0;
  3457 SwfSoundStream.prototype = {
  3458   get info() {
  3459     return {
  3460       samplesCount: this.samplesCount,
  3461       sampleRate: this.sampleRate,
  3462       channels: this.channels,
  3463       format: this.format,
  3464       streamId: this.streamId
  3465     };
  3466   },
  3467   decode: function (data) {
  3468     throw new Error('SwfSoundStream.decode: not implemented');
  3470 };
  3471 function SwfSoundStream_decode_PCM(data) {
  3472   var pcm = new Float32Array(data.length);
  3473   for (var i = 0; i < pcm.length; i++)
  3474     pcm[i] = (data[i] - 128) / 128;
  3475   this.currentSample += pcm.length / this.channels;
  3476   return {
  3477     streamId: this.streamId,
  3478     samplesCount: pcm.length / this.channels,
  3479     pcm: pcm
  3480   };
  3482 function SwfSoundStream_decode_PCM_be(data) {
  3483   var pcm = new Float32Array(data.length / 2);
  3484   for (var i = 0, j = 0; i < pcm.length; i++, j += 2)
  3485     pcm[i] = (data[j] << 24 | data[j + 1] << 16) / 2147483648;
  3486   this.currentSample += pcm.length / this.channels;
  3487   return {
  3488     streamId: this.streamId,
  3489     samplesCount: pcm.length / this.channels,
  3490     pcm: pcm
  3491   };
  3493 function SwfSoundStream_decode_PCM_le(data) {
  3494   var pcm = new Float32Array(data.length / 2);
  3495   for (var i = 0, j = 0; i < pcm.length; i++, j += 2)
  3496     pcm[i] = (data[j + 1] << 24 | data[j] << 16) / 2147483648;
  3497   this.currentSample += pcm.length / this.channels;
  3498   return {
  3499     streamId: this.streamId,
  3500     samplesCount: pcm.length / this.channels,
  3501     pcm: pcm
  3502   };
  3504 function SwfSoundStream_decode_MP3(data) {
  3505   var samplesCount = data[1] << 8 | data[0];
  3506   var seek = data[3] << 8 | data[2];
  3507   this.currentSample += samplesCount;
  3508   return {
  3509     streamId: this.streamId,
  3510     samplesCount: samplesCount,
  3511     data: new Uint8Array(data.subarray(4)),
  3512     seek: seek
  3513   };
  3515 function createSoundStream(tag) {
  3516   var channels = tag.streamType == SOUND_TYPE_STEREO ? 2 : 1;
  3517   var samplesCount = tag.samplesCount;
  3518   var sampleRate = SOUND_RATES[tag.streamRate];
  3519   var stream = new SwfSoundStream(samplesCount, sampleRate, channels);
  3520   switch (tag.streamCompression) {
  3521   case SOUND_FORMAT_PCM_BE:
  3522     stream.format = 'wave';
  3523     if (tag.soundSize == SOUND_SIZE_16_BIT) {
  3524       stream.decode = SwfSoundStream_decode_PCM_be;
  3525     } else {
  3526       stream.decode = SwfSoundStream_decode_PCM;
  3528     break;
  3529   case SOUND_FORMAT_PCM_LE:
  3530     stream.format = 'wave';
  3531     if (tag.soundSize == SOUND_SIZE_16_BIT) {
  3532       stream.decode = SwfSoundStream_decode_PCM_le;
  3533     } else {
  3534       stream.decode = SwfSoundStream_decode_PCM;
  3536     break;
  3537   case SOUND_FORMAT_MP3:
  3538     stream.format = 'mp3';
  3539     stream.decode = SwfSoundStream_decode_MP3;
  3540     break;
  3541   default:
  3542     throw new Error('Unsupported audio format: ' + tag.soundFormat);
  3544   return stream;
  3546 function defineText(tag, dictionary) {
  3547   var dependencies = [];
  3548   if (tag.hasFont) {
  3549     var font = dictionary[tag.fontId];
  3550     tag.font = font.uniqueName;
  3551     dependencies.push(font.id);
  3553   var props = {
  3554       type: 'text',
  3555       id: tag.id,
  3556       variableName: tag.variableName,
  3557       tag: tag
  3558     };
  3559   if (dependencies.length)
  3560     props.require = dependencies;
  3561   return props;
  3563 var $RELEASE = false;
  3564 var isWorker = typeof window === 'undefined';
  3565 if (isWorker && !true) {
  3566   importScripts.apply(null, [
  3567     '../../lib/DataView.js/DataView.js',
  3568     '../flash/util.js',
  3569     'config.js',
  3570     'swf.js',
  3571     'types.js',
  3572     'structs.js',
  3573     'tags.js',
  3574     'inflate.js',
  3575     'stream.js',
  3576     'templates.js',
  3577     'generator.js',
  3578     'handlers.js',
  3579     'parser.js',
  3580     'bitmap.js',
  3581     'button.js',
  3582     'font.js',
  3583     'image.js',
  3584     'label.js',
  3585     'shape.js',
  3586     'sound.js',
  3587     'text.js'
  3588   ]);
  3590 function defineSymbol(swfTag, symbols) {
  3591   var symbol;
  3592   switch (swfTag.code) {
  3593   case SWF_TAG_CODE_DEFINE_BITS:
  3594   case SWF_TAG_CODE_DEFINE_BITS_JPEG2:
  3595   case SWF_TAG_CODE_DEFINE_BITS_JPEG3:
  3596   case SWF_TAG_CODE_DEFINE_BITS_JPEG4:
  3597   case SWF_TAG_CODE_JPEG_TABLES:
  3598     symbol = defineImage(swfTag, symbols);
  3599     break;
  3600   case SWF_TAG_CODE_DEFINE_BITS_LOSSLESS:
  3601   case SWF_TAG_CODE_DEFINE_BITS_LOSSLESS2:
  3602     symbol = defineBitmap(swfTag);
  3603     break;
  3604   case SWF_TAG_CODE_DEFINE_BUTTON:
  3605   case SWF_TAG_CODE_DEFINE_BUTTON2:
  3606     symbol = defineButton(swfTag, symbols);
  3607     break;
  3608   case SWF_TAG_CODE_DEFINE_EDIT_TEXT:
  3609     symbol = defineText(swfTag, symbols);
  3610     break;
  3611   case SWF_TAG_CODE_DEFINE_FONT:
  3612   case SWF_TAG_CODE_DEFINE_FONT2:
  3613   case SWF_TAG_CODE_DEFINE_FONT3:
  3614   case SWF_TAG_CODE_DEFINE_FONT4:
  3615     symbol = defineFont(swfTag, symbols);
  3616     break;
  3617   case SWF_TAG_CODE_DEFINE_MORPH_SHAPE:
  3618   case SWF_TAG_CODE_DEFINE_MORPH_SHAPE2:
  3619   case SWF_TAG_CODE_DEFINE_SHAPE:
  3620   case SWF_TAG_CODE_DEFINE_SHAPE2:
  3621   case SWF_TAG_CODE_DEFINE_SHAPE3:
  3622   case SWF_TAG_CODE_DEFINE_SHAPE4:
  3623     symbol = defineShape(swfTag, symbols);
  3624     break;
  3625   case SWF_TAG_CODE_DEFINE_SOUND:
  3626     symbol = defineSound(swfTag, symbols);
  3627     break;
  3628   case SWF_TAG_CODE_DEFINE_BINARY_DATA:
  3629     symbol = {
  3630       type: 'binary',
  3631       id: swfTag.id,
  3632       data: swfTag.data
  3633     };
  3634     break;
  3635   case SWF_TAG_CODE_DEFINE_SPRITE:
  3636     var depths = {};
  3637     var frame = {
  3638         type: 'frame'
  3639       };
  3640     var frames = [];
  3641     var tags = swfTag.tags;
  3642     var frameScripts = null;
  3643     var frameIndex = 0;
  3644     var soundStream = null;
  3645     for (var i = 0, n = tags.length; i < n; i++) {
  3646       var tag = tags[i];
  3647       switch (tag.code) {
  3648       case SWF_TAG_CODE_DO_ACTION:
  3649         if (!frameScripts)
  3650           frameScripts = [];
  3651         frameScripts.push(frameIndex);
  3652         frameScripts.push(tag.actionsData);
  3653         break;
  3654       case SWF_TAG_CODE_START_SOUND:
  3655         var startSounds = frame.startSounds || (frame.startSounds = []);
  3656         startSounds.push(tag);
  3657         break;
  3658       case SWF_TAG_CODE_SOUND_STREAM_HEAD:
  3659         try {
  3660           soundStream = createSoundStream(tag);
  3661           frame.soundStream = soundStream.info;
  3662         } catch (e) {
  3664         break;
  3665       case SWF_TAG_CODE_SOUND_STREAM_BLOCK:
  3666         if (soundStream) {
  3667           frame.soundStreamBlock = soundStream.decode(tag.data);
  3669         break;
  3670       case SWF_TAG_CODE_FRAME_LABEL:
  3671         frame.labelName = tag.name;
  3672         break;
  3673       case SWF_TAG_CODE_PLACE_OBJECT:
  3674       case SWF_TAG_CODE_PLACE_OBJECT2:
  3675       case SWF_TAG_CODE_PLACE_OBJECT3:
  3676         depths[tag.depth] = tag;
  3677         break;
  3678       case SWF_TAG_CODE_REMOVE_OBJECT:
  3679       case SWF_TAG_CODE_REMOVE_OBJECT2:
  3680         depths[tag.depth] = null;
  3681         break;
  3682       case SWF_TAG_CODE_SHOW_FRAME:
  3683         frameIndex += tag.repeat;
  3684         frame.repeat = tag.repeat;
  3685         frame.depths = depths;
  3686         frames.push(frame);
  3687         depths = {};
  3688         frame = {
  3689           type: 'frame'
  3690         };
  3691         break;
  3694     symbol = {
  3695       type: 'sprite',
  3696       id: swfTag.id,
  3697       frameCount: swfTag.frameCount,
  3698       frames: frames,
  3699       frameScripts: frameScripts
  3700     };
  3701     break;
  3702   case SWF_TAG_CODE_DEFINE_TEXT:
  3703   case SWF_TAG_CODE_DEFINE_TEXT2:
  3704     symbol = defineLabel(swfTag, symbols);
  3705     break;
  3707   if (!symbol) {
  3708     return {
  3709       command: 'error',
  3710       message: 'unknown symbol type: ' + swfTag.code
  3711     };
  3713   symbol.isSymbol = true;
  3714   symbols[swfTag.id] = symbol;
  3715   return symbol;
  3717 function createParsingContext(commitData) {
  3718   var depths = {};
  3719   var symbols = {};
  3720   var frame = {
  3721       type: 'frame'
  3722     };
  3723   var tagsProcessed = 0;
  3724   var soundStream = null;
  3725   var lastProgressSent = 0;
  3726   return {
  3727     onstart: function (result) {
  3728       commitData({
  3729         command: 'init',
  3730         result: result
  3731       });
  3732     },
  3733     onprogress: function (result) {
  3734       if (Date.now() - lastProgressSent > 1000 / 24 || result.bytesLoaded === result.bytesTotal) {
  3735         commitData({
  3736           command: 'progress',
  3737           result: {
  3738             bytesLoaded: result.bytesLoaded,
  3739             bytesTotal: result.bytesTotal
  3741         });
  3742         lastProgressSent = Date.now();
  3744       var tags = result.tags;
  3745       for (var n = tags.length; tagsProcessed < n; tagsProcessed++) {
  3746         var tag = tags[tagsProcessed];
  3747         if ('id' in tag) {
  3748           var symbol = defineSymbol(tag, symbols);
  3749           commitData(symbol, symbol.transferables);
  3750           continue;
  3752         switch (tag.code) {
  3753         case SWF_TAG_CODE_DEFINE_SCENE_AND_FRAME_LABEL_DATA:
  3754           frame.sceneData = tag;
  3755           break;
  3756         case SWF_TAG_CODE_DEFINE_SCALING_GRID:
  3757           var symbolUpdate = {
  3758               isSymbol: true,
  3759               id: tag.symbolId,
  3760               updates: {
  3761                 scale9Grid: tag.splitter
  3763             };
  3764           commitData(symbolUpdate);
  3765           break;
  3766         case SWF_TAG_CODE_DO_ABC:
  3767         case SWF_TAG_CODE_DO_ABC_:
  3768           var abcBlocks = frame.abcBlocks;
  3769           if (abcBlocks)
  3770             abcBlocks.push({
  3771               data: tag.data,
  3772               flags: tag.flags
  3773             });
  3774           else
  3775             frame.abcBlocks = [
  3777                 data: tag.data,
  3778                 flags: tag.flags
  3780             ];
  3781           break;
  3782         case SWF_TAG_CODE_DO_ACTION:
  3783           var actionBlocks = frame.actionBlocks;
  3784           if (actionBlocks)
  3785             actionBlocks.push(tag.actionsData);
  3786           else
  3787             frame.actionBlocks = [
  3788               tag.actionsData
  3789             ];
  3790           break;
  3791         case SWF_TAG_CODE_DO_INIT_ACTION:
  3792           var initActionBlocks = frame.initActionBlocks || (frame.initActionBlocks = []);
  3793           initActionBlocks.push({
  3794             spriteId: tag.spriteId,
  3795             actionsData: tag.actionsData
  3796           });
  3797           break;
  3798         case SWF_TAG_CODE_START_SOUND:
  3799           var startSounds = frame.startSounds;
  3800           if (!startSounds)
  3801             frame.startSounds = startSounds = [];
  3802           startSounds.push(tag);
  3803           break;
  3804         case SWF_TAG_CODE_SOUND_STREAM_HEAD:
  3805           try {
  3806             soundStream = createSoundStream(tag);
  3807             frame.soundStream = soundStream.info;
  3808           } catch (e) {
  3810           break;
  3811         case SWF_TAG_CODE_SOUND_STREAM_BLOCK:
  3812           if (soundStream) {
  3813             frame.soundStreamBlock = soundStream.decode(tag.data);
  3815           break;
  3816         case SWF_TAG_CODE_EXPORT_ASSETS:
  3817           var exports = frame.exports;
  3818           if (exports)
  3819             frame.exports = exports.concat(tag.exports);
  3820           else
  3821             frame.exports = tag.exports.slice(0);
  3822           break;
  3823         case SWF_TAG_CODE_SYMBOL_CLASS:
  3824           var symbolClasses = frame.symbolClasses;
  3825           if (symbolClasses)
  3826             frame.symbolClasses = symbolClasses.concat(tag.exports);
  3827           else
  3828             frame.symbolClasses = tag.exports.slice(0);
  3829           break;
  3830         case SWF_TAG_CODE_FRAME_LABEL:
  3831           frame.labelName = tag.name;
  3832           break;
  3833         case SWF_TAG_CODE_PLACE_OBJECT:
  3834         case SWF_TAG_CODE_PLACE_OBJECT2:
  3835         case SWF_TAG_CODE_PLACE_OBJECT3:
  3836           depths[tag.depth] = tag;
  3837           break;
  3838         case SWF_TAG_CODE_REMOVE_OBJECT:
  3839         case SWF_TAG_CODE_REMOVE_OBJECT2:
  3840           depths[tag.depth] = null;
  3841           break;
  3842         case SWF_TAG_CODE_SET_BACKGROUND_COLOR:
  3843           frame.bgcolor = tag.color;
  3844           break;
  3845         case SWF_TAG_CODE_SHOW_FRAME:
  3846           frame.repeat = tag.repeat;
  3847           frame.depths = depths;
  3848           frame.complete = !(!tag.finalTag);
  3849           commitData(frame);
  3850           depths = {};
  3851           frame = {
  3852             type: 'frame'
  3853           };
  3854           break;
  3857     },
  3858     oncomplete: function (result) {
  3859       commitData(result);
  3860       var stats;
  3861       if (typeof result.swfVersion === 'number') {
  3862         var bbox = result.bbox;
  3863         stats = {
  3864           topic: 'parseInfo',
  3865           parseTime: result.parseTime,
  3866           bytesTotal: result.bytesTotal,
  3867           swfVersion: result.swfVersion,
  3868           frameRate: result.frameRate,
  3869           width: (bbox.xMax - bbox.xMin) / 20,
  3870           height: (bbox.yMax - bbox.yMin) / 20,
  3871           isAvm2: !(!result.fileAttributes.doAbc)
  3872         };
  3874       commitData({
  3875         command: 'complete',
  3876         stats: stats
  3877       });
  3878     },
  3879     onexception: function (e) {
  3880       commitData({
  3881         type: 'exception',
  3882         message: e.message,
  3883         stack: e.stack
  3884       });
  3886   };
  3888 function parseBytes(bytes, commitData) {
  3889   SWF.parse(bytes, createParsingContext(commitData));
  3891 function ResourceLoader(scope) {
  3892   this.subscription = null;
  3893   var self = this;
  3894   if (!isWorker) {
  3895     this.messenger = {
  3896       postMessage: function (data) {
  3897         self.onmessage({
  3898           data: data
  3899         });
  3901     };
  3902   } else {
  3903     this.messenger = scope;
  3904     scope.onmessage = function (event) {
  3905       self.listener(event.data);
  3906     };
  3909 ResourceLoader.prototype = {
  3910   terminate: function () {
  3911     this.messenger = null;
  3912     this.listener = null;
  3913   },
  3914   onmessage: function (event) {
  3915     this.listener(event.data);
  3916   },
  3917   postMessage: function (data) {
  3918     this.listener && this.listener(data);
  3919   },
  3920   listener: function (data) {
  3921     if (this.subscription) {
  3922       this.subscription.callback(data.data, data.progress);
  3923     } else if (data === 'pipe:') {
  3924       this.subscription = {
  3925         subscribe: function (callback) {
  3926           this.callback = callback;
  3928       };
  3929       this.parseLoadedData(this.messenger, this.subscription);
  3930     } else {
  3931       this.parseLoadedData(this.messenger, data);
  3933   },
  3934   parseLoadedData: function (loader, request, context) {
  3935     function commitData(data, transferables) {
  3936       try {
  3937         loader.postMessage(data, transferables);
  3938       } catch (ex) {
  3939         if (ex != 'DataCloneError') {
  3940           throw ex;
  3942         loader.postMessage(data);
  3945     if (request instanceof ArrayBuffer) {
  3946       parseBytes(request, commitData);
  3947     } else if ('subscribe' in request) {
  3948       var pipe = SWF.parseAsync(createParsingContext(commitData));
  3949       request.subscribe(function (data, progress) {
  3950         if (data) {
  3951           pipe.push(data, progress);
  3952         } else {
  3953           pipe.close();
  3955       });
  3956     } else if (typeof FileReaderSync !== 'undefined') {
  3957       var reader = new FileReaderSync();
  3958       var buffer = reader.readAsArrayBuffer(request);
  3959       parseBytes(buffer, commitData);
  3960     } else {
  3961       var reader = new FileReader();
  3962       reader.onload = function () {
  3963         parseBytes(this.result, commitData);
  3964       };
  3965       reader.readAsArrayBuffer(request);
  3968 };
  3969 if (isWorker) {
  3970   var loader = new ResourceLoader(this);
  3972 var codeLengthOrder = [
  3973     16,
  3974     17,
  3975     18,
  3976     0,
  3977     8,
  3978     7,
  3979     9,
  3980     6,
  3981     10,
  3982     5,
  3983     11,
  3984     4,
  3985     12,
  3986     3,
  3987     13,
  3988     2,
  3989     14,
  3990     1,
  3991     15
  3992   ];
  3993 var distanceCodes = [];
  3994 var distanceExtraBits = [];
  3995 for (var i = 0, j = 0, code = 1; i < 30; ++i) {
  3996   distanceCodes[i] = code;
  3997   code += 1 << (distanceExtraBits[i] = ~(~((j += i > 2 ? 1 : 0) / 2)));
  3999 var bitLengths = [];
  4000 for (var i = 0; i < 32; ++i)
  4001   bitLengths[i] = 5;
  4002 var fixedDistanceTable = makeHuffmanTable(bitLengths);
  4003 var lengthCodes = [];
  4004 var lengthExtraBits = [];
  4005 for (var i = 0, j = 0, code = 3; i < 29; ++i) {
  4006   lengthCodes[i] = code - (i == 28 ? 1 : 0);
  4007   code += 1 << (lengthExtraBits[i] = ~(~((j += i > 4 ? 1 : 0) / 4 % 6)));
  4009 for (var i = 0; i < 288; ++i)
  4010   bitLengths[i] = i < 144 || i > 279 ? 8 : i < 256 ? 9 : 7;
  4011 var fixedLiteralTable = makeHuffmanTable(bitLengths);
  4012 function makeHuffmanTable(bitLengths) {
  4013   var maxBits = Math.max.apply(null, bitLengths);
  4014   var numLengths = bitLengths.length;
  4015   var size = 1 << maxBits;
  4016   var codes = new Uint32Array(size);
  4017   for (var code = 0, len = 1, skip = 2; len <= maxBits; code <<= 1, ++len, skip <<= 1) {
  4018     for (var val = 0; val < numLengths; ++val) {
  4019       if (bitLengths[val] === len) {
  4020         var lsb = 0;
  4021         for (var i = 0; i < len; ++i)
  4022           lsb = lsb * 2 + (code >> i & 1);
  4023         for (var i = lsb; i < size; i += skip)
  4024           codes[i] = len << 16 | val;
  4025         ++code;
  4029   return {
  4030     codes: codes,
  4031     maxBits: maxBits
  4032   };
  4034 function verifyDeflateHeader(bytes) {
  4035   var header = bytes[0] << 8 | bytes[1];
  4037 function createInflatedStream(bytes, outputLength) {
  4038   verifyDeflateHeader(bytes);
  4039   var stream = new Stream(bytes, 2);
  4040   var output = {
  4041       data: new Uint8Array(outputLength),
  4042       available: 0,
  4043       completed: false
  4044     };
  4045   var state = {
  4046       header: null,
  4047       distanceTable: null,
  4048       literalTable: null,
  4049       sym: null,
  4050       len: null,
  4051       sym2: null
  4052     };
  4053   do {
  4054     inflateBlock(stream, output, state);
  4055   } while (!output.completed && stream.pos < stream.end);
  4056   return new Stream(output.data, 0, output.available);
  4058 var InflateNoDataError = {};
  4059 function inflateBlock(stream, output, state) {
  4060   var header = state.header !== null ? state.header : state.header = readBits(stream.bytes, stream, 3);
  4061   switch (header >> 1) {
  4062   case 0:
  4063     stream.align();
  4064     var pos = stream.pos;
  4065     if (stream.end - pos < 4) {
  4066       throw InflateNoDataError;
  4068     var len = stream.getUint16(pos, true);
  4069     var nlen = stream.getUint16(pos + 2, true);
  4070     if (stream.end - pos < 4 + len) {
  4071       throw InflateNoDataError;
  4073     var begin = pos + 4;
  4074     var end = stream.pos = begin + len;
  4075     var sbytes = stream.bytes, dbytes = output.data;
  4076     dbytes.set(sbytes.subarray(begin, end), output.available);
  4077     output.available += len;
  4078     break;
  4079   case 1:
  4080     inflate(stream, output, fixedLiteralTable, fixedDistanceTable, state);
  4081     break;
  4082   case 2:
  4083     var distanceTable, literalTable;
  4084     if (state.distanceTable !== null) {
  4085       distanceTable = state.distanceTable;
  4086       literalTable = state.literalTable;
  4087     } else {
  4088       var sbytes = stream.bytes;
  4089       var savedBufferPos = stream.pos;
  4090       var savedBitBuffer = stream.bitBuffer;
  4091       var savedBitLength = stream.bitLength;
  4092       var bitLengths = [];
  4093       var numLiteralCodes, numDistanceCodes;
  4094       try {
  4095         numLiteralCodes = readBits(sbytes, stream, 5) + 257;
  4096         numDistanceCodes = readBits(sbytes, stream, 5) + 1;
  4097         var numCodes = numLiteralCodes + numDistanceCodes;
  4098         var numLengthCodes = readBits(sbytes, stream, 4) + 4;
  4099         for (var i = 0; i < 19; ++i)
  4100           bitLengths[codeLengthOrder[i]] = i < numLengthCodes ? readBits(sbytes, stream, 3) : 0;
  4101         var codeLengthTable = makeHuffmanTable(bitLengths);
  4102         bitLengths = [];
  4103         var i = 0;
  4104         var prev = 0;
  4105         while (i < numCodes) {
  4106           var j = 1;
  4107           var sym = readCode(sbytes, stream, codeLengthTable);
  4108           switch (sym) {
  4109           case 16:
  4110             j = readBits(sbytes, stream, 2) + 3;
  4111             sym = prev;
  4112             break;
  4113           case 17:
  4114             j = readBits(sbytes, stream, 3) + 3;
  4115             sym = 0;
  4116             break;
  4117           case 18:
  4118             j = readBits(sbytes, stream, 7) + 11;
  4119             sym = 0;
  4120             break;
  4121           default:
  4122             prev = sym;
  4124           while (j--)
  4125             bitLengths[i++] = sym;
  4127       } catch (e) {
  4128         stream.pos = savedBufferPos;
  4129         stream.bitBuffer = savedBitBuffer;
  4130         stream.bitLength = savedBitLength;
  4131         throw e;
  4133       distanceTable = state.distanceTable = makeHuffmanTable(bitLengths.splice(numLiteralCodes, numDistanceCodes));
  4134       literalTable = state.literalTable = makeHuffmanTable(bitLengths);
  4136     inflate(stream, output, literalTable, distanceTable, state);
  4137     state.distanceTable = null;
  4138     state.literalTable = null;
  4139     break;
  4140   default:
  4141     fail('unknown block type', 'inflate');
  4143   state.header = null;
  4144   output.completed = !(!(header & 1));
  4146 function readBits(bytes, stream, size) {
  4147   var bitBuffer = stream.bitBuffer;
  4148   var bitLength = stream.bitLength;
  4149   if (size > bitLength) {
  4150     var pos = stream.pos;
  4151     var end = stream.end;
  4152     do {
  4153       if (pos >= end) {
  4154         stream.pos = pos;
  4155         stream.bitBuffer = bitBuffer;
  4156         stream.bitLength = bitLength;
  4157         throw InflateNoDataError;
  4159       bitBuffer |= bytes[pos++] << bitLength;
  4160       bitLength += 8;
  4161     } while (size > bitLength);
  4162     stream.pos = pos;
  4164   stream.bitBuffer = bitBuffer >>> size;
  4165   stream.bitLength = bitLength - size;
  4166   return bitBuffer & (1 << size) - 1;
  4168 function inflate(stream, output, literalTable, distanceTable, state) {
  4169   var pos = output.available;
  4170   var dbytes = output.data;
  4171   var sbytes = stream.bytes;
  4172   var sym = state.sym !== null ? state.sym : readCode(sbytes, stream, literalTable);
  4173   while (sym !== 256) {
  4174     if (sym < 256) {
  4175       dbytes[pos++] = sym;
  4176     } else {
  4177       state.sym = sym;
  4178       sym -= 257;
  4179       var len = state.len !== null ? state.len : state.len = lengthCodes[sym] + readBits(sbytes, stream, lengthExtraBits[sym]);
  4180       var sym2 = state.sym2 !== null ? state.sym2 : state.sym2 = readCode(sbytes, stream, distanceTable);
  4181       var distance = distanceCodes[sym2] + readBits(sbytes, stream, distanceExtraBits[sym2]);
  4182       var i = pos - distance;
  4183       while (len--)
  4184         dbytes[pos++] = dbytes[i++];
  4185       state.sym2 = null;
  4186       state.len = null;
  4187       state.sym = null;
  4189     output.available = pos;
  4190     sym = readCode(sbytes, stream, literalTable);
  4193 function readCode(bytes, stream, codeTable) {
  4194   var bitBuffer = stream.bitBuffer;
  4195   var bitLength = stream.bitLength;
  4196   var maxBits = codeTable.maxBits;
  4197   if (maxBits > bitLength) {
  4198     var pos = stream.pos;
  4199     var end = stream.end;
  4200     do {
  4201       if (pos >= end) {
  4202         stream.pos = pos;
  4203         stream.bitBuffer = bitBuffer;
  4204         stream.bitLength = bitLength;
  4205         throw InflateNoDataError;
  4207       bitBuffer |= bytes[pos++] << bitLength;
  4208       bitLength += 8;
  4209     } while (maxBits > bitLength);
  4210     stream.pos = pos;
  4212   var code = codeTable.codes[bitBuffer & (1 << maxBits) - 1];
  4213   var len = code >> 16;
  4214   stream.bitBuffer = bitBuffer >>> len;
  4215   stream.bitLength = bitLength - len;
  4216   return code & 65535;
  4218 (function (global) {
  4219   global['createInflatedStream'] = createInflatedStream;
  4220 }(this));
  4221 var StreamNoDataError = {};
  4222 var Stream = function StreamClosure() {
  4223     function Stream_align() {
  4224       this.bitBuffer = this.bitLength = 0;
  4226     function Stream_ensure(size) {
  4227       if (this.pos + size > this.end) {
  4228         throw StreamNoDataError;
  4231     function Stream_remaining() {
  4232       return this.end - this.pos;
  4234     function Stream_substream(begin, end) {
  4235       var stream = new Stream(this.bytes);
  4236       stream.pos = begin;
  4237       stream.end = end;
  4238       return stream;
  4240     function Stream_push(data) {
  4241       var bytes = this.bytes;
  4242       var newBytesLength = this.end + data.length;
  4243       if (newBytesLength > bytes.length) {
  4244         throw 'stream buffer overfow';
  4246       bytes.set(data, this.end);
  4247       this.end = newBytesLength;
  4249     function Stream(buffer, offset, length, maxLength) {
  4250       if (offset === undefined)
  4251         offset = 0;
  4252       if (buffer.buffer instanceof ArrayBuffer) {
  4253         offset += buffer.byteOffset;
  4254         buffer = buffer.buffer;
  4256       if (length === undefined)
  4257         length = buffer.byteLength - offset;
  4258       if (maxLength === undefined)
  4259         maxLength = length;
  4260       var bytes = new Uint8Array(buffer, offset, maxLength);
  4261       var stream = new DataView(buffer, offset, maxLength);
  4262       stream.bytes = bytes;
  4263       stream.pos = 0;
  4264       stream.end = length;
  4265       stream.bitBuffer = 0;
  4266       stream.bitLength = 0;
  4267       stream.align = Stream_align;
  4268       stream.ensure = Stream_ensure;
  4269       stream.remaining = Stream_remaining;
  4270       stream.substream = Stream_substream;
  4271       stream.push = Stream_push;
  4272       return stream;
  4274     return Stream;
  4275   }();
  4276 (function (global) {
  4277   global['Stream'] = Stream;
  4278 }(this));
  4279 function readSi8($bytes, $stream) {
  4280   return $stream.getInt8($stream.pos++);
  4282 function readSi16($bytes, $stream) {
  4283   return $stream.getInt16($stream.pos, $stream.pos += 2);
  4285 function readSi32($bytes, $stream) {
  4286   return $stream.getInt32($stream.pos, $stream.pos += 4);
  4288 function readUi8($bytes, $stream) {
  4289   return $bytes[$stream.pos++];
  4291 function readUi16($bytes, $stream) {
  4292   return $stream.getUint16($stream.pos, $stream.pos += 2);
  4294 function readUi32($bytes, $stream) {
  4295   return $stream.getUint32($stream.pos, $stream.pos += 4);
  4297 function readFixed($bytes, $stream) {
  4298   return $stream.getInt32($stream.pos, $stream.pos += 4) / 65536;
  4300 function readFixed8($bytes, $stream) {
  4301   return $stream.getInt16($stream.pos, $stream.pos += 2) / 256;
  4303 function readFloat16($bytes, $stream) {
  4304   var ui16 = $stream.getUint16($stream.pos);
  4305   $stream.pos += 2;
  4306   var sign = ui16 >> 15 ? -1 : 1;
  4307   var exponent = (ui16 & 31744) >> 10;
  4308   var fraction = ui16 & 1023;
  4309   if (!exponent)
  4310     return sign * pow(2, -14) * (fraction / 1024);
  4311   if (exponent === 31)
  4312     return fraction ? NaN : sign * Infinity;
  4313   return sign * pow(2, exponent - 15) * (1 + fraction / 1024);
  4315 function readFloat($bytes, $stream) {
  4316   return $stream.getFloat32($stream.pos, $stream.pos += 4);
  4318 function readDouble($bytes, $stream) {
  4319   return $stream.getFloat64($stream.pos, $stream.pos += 8);
  4321 function readEncodedU32($bytes, $stream) {
  4322   var val = $bytes[$stream.pos++];
  4323   if (!(val & 128))
  4324     return val;
  4325   val |= $bytes[$stream.pos++] << 7;
  4326   if (!(val & 16384))
  4327     return val;
  4328   val |= $bytes[$stream.pos++] << 14;
  4329   if (!(val & 2097152))
  4330     return val;
  4331   val |= $bytes[$stream.pos++] << 21;
  4332   if (!(val & 268435456))
  4333     return val;
  4334   return val | $bytes[$stream.pos++] << 28;
  4336 function readBool($bytes, $stream) {
  4337   return !(!$bytes[$stream.pos++]);
  4339 function align($bytes, $stream) {
  4340   $stream.align();
  4342 function readSb($bytes, $stream, size) {
  4343   return readUb($bytes, $stream, size) << 32 - size >> 32 - size;
  4345 var masks = new Uint32Array(33);
  4346 for (var i = 1, mask = 0; i <= 32; ++i)
  4347   masks[i] = mask = mask << 1 | 1;
  4348 function readUb($bytes, $stream, size) {
  4349   var buffer = $stream.bitBuffer;
  4350   var bitlen = $stream.bitLength;
  4351   while (size > bitlen) {
  4352     buffer = buffer << 8 | $bytes[$stream.pos++];
  4353     bitlen += 8;
  4355   bitlen -= size;
  4356   var val = buffer >>> bitlen & masks[size];
  4357   $stream.bitBuffer = buffer;
  4358   $stream.bitLength = bitlen;
  4359   return val;
  4361 function readFb($bytes, $stream, size) {
  4362   return readSb($bytes, $stream, size) / 65536;
  4364 function readString($bytes, $stream, length) {
  4365   var codes = [];
  4366   var pos = $stream.pos;
  4367   if (length) {
  4368     codes = slice.call($bytes, pos, pos += length);
  4369   } else {
  4370     length = 0;
  4371     for (var code; code = $bytes[pos++]; length++)
  4372       codes[length] = code;
  4374   $stream.pos = pos;
  4375   var numChunks = length / 65536;
  4376   var str = '';
  4377   for (var i = 0; i < numChunks; ++i) {
  4378     var begin = i * 65536;
  4379     var end = begin + 65536;
  4380     var chunk = codes.slice(begin, end);
  4381     str += fromCharCode.apply(null, chunk);
  4383   return decodeURIComponent(escape(str.replace('\0', '', 'g')));
  4385 function readBinary($bytes, $stream, size) {
  4386   return $bytes.subarray($stream.pos, $stream.pos = size ? $stream.pos + size : $stream.end);
  4388 (function (global) {
  4389   global['readSi8'] = readSi8;
  4390   global['readUi16'] = readUi16;
  4391   global['readUi32'] = readUi32;
  4392 }(this));
  4393 var tagHandler = function (global) {
  4394     function defineShape($bytes, $stream, $, swfVersion, tagCode) {
  4395       $ || ($ = {});
  4396       $.id = readUi16($bytes, $stream);
  4397       var $0 = $.bbox = {};
  4398       bbox($bytes, $stream, $0, swfVersion, tagCode);
  4399       var isMorph = $.isMorph = tagCode === 46 || tagCode === 84;
  4400       if (isMorph) {
  4401         var $1 = $.bboxMorph = {};
  4402         bbox($bytes, $stream, $1, swfVersion, tagCode);
  4404       var hasStrokes = $.hasStrokes = tagCode === 83 || tagCode === 84;
  4405       if (hasStrokes) {
  4406         var $2 = $.strokeBbox = {};
  4407         bbox($bytes, $stream, $2, swfVersion, tagCode);
  4408         if (isMorph) {
  4409           var $3 = $.strokeBboxMorph = {};
  4410           bbox($bytes, $stream, $3, swfVersion, tagCode);
  4412         var reserved = readUb($bytes, $stream, 5);
  4413         $.fillWinding = readUb($bytes, $stream, 1);
  4414         $.nonScalingStrokes = readUb($bytes, $stream, 1);
  4415         $.scalingStrokes = readUb($bytes, $stream, 1);
  4417       if (isMorph) {
  4418         $.offsetMorph = readUi32($bytes, $stream);
  4419         morphShapeWithStyle($bytes, $stream, $, swfVersion, tagCode, isMorph, hasStrokes);
  4420       } else {
  4421         shapeWithStyle($bytes, $stream, $, swfVersion, tagCode, isMorph, hasStrokes);
  4423       return $;
  4425     function placeObject($bytes, $stream, $, swfVersion, tagCode) {
  4426       var flags, hasEvents, clip, hasName, hasRatio, hasCxform, hasMatrix, place;
  4427       var move, hasBackgroundColor, hasVisibility, hasImage, hasClassName, cache;
  4428       var blend, hasFilters, eoe;
  4429       $ || ($ = {});
  4430       if (tagCode > 4) {
  4431         if (tagCode > 26) {
  4432           flags = readUi16($bytes, $stream);
  4433         } else {
  4434           flags = readUi8($bytes, $stream);
  4436         hasEvents = $.hasEvents = flags >> 7 & 1;
  4437         clip = $.clip = flags >> 6 & 1;
  4438         hasName = $.hasName = flags >> 5 & 1;
  4439         hasRatio = $.hasRatio = flags >> 4 & 1;
  4440         hasCxform = $.hasCxform = flags >> 3 & 1;
  4441         hasMatrix = $.hasMatrix = flags >> 2 & 1;
  4442         place = $.place = flags >> 1 & 1;
  4443         move = $.move = flags & 1;
  4444         if (tagCode === 70) {
  4445           hasBackgroundColor = $.hasBackgroundColor = flags >> 15 & 1;
  4446           hasVisibility = $.hasVisibility = flags >> 14 & 1;
  4447           hasImage = $.hasImage = flags >> 12 & 1;
  4448           hasClassName = $.hasClassName = flags >> 11 & 1;
  4449           cache = $.cache = flags >> 10 & 1;
  4450           blend = $.blend = flags >> 9 & 1;
  4451           hasFilters = $.hasFilters = flags >> 8 & 1;
  4452         } else {
  4453           cache = $.cache = 0;
  4454           blend = $.blend = 0;
  4455           hasFilters = $.hasFilters = 0;
  4457         $.depth = readUi16($bytes, $stream);
  4458         if (hasClassName) {
  4459           $.className = readString($bytes, $stream, 0);
  4461         if (place) {
  4462           $.symbolId = readUi16($bytes, $stream);
  4464         if (hasMatrix) {
  4465           var $0 = $.matrix = {};
  4466           matrix($bytes, $stream, $0, swfVersion, tagCode);
  4468         if (hasCxform) {
  4469           var $1 = $.cxform = {};
  4470           cxform($bytes, $stream, $1, swfVersion, tagCode);
  4472         if (hasRatio) {
  4473           $.ratio = readUi16($bytes, $stream);
  4475         if (hasName) {
  4476           $.name = readString($bytes, $stream, 0);
  4478         if (clip) {
  4479           $.clipDepth = readUi16($bytes, $stream);
  4481         if (hasFilters) {
  4482           var count = readUi8($bytes, $stream);
  4483           var $2 = $.filters = [];
  4484           var $3 = count;
  4485           while ($3--) {
  4486             var $4 = {};
  4487             anyFilter($bytes, $stream, $4, swfVersion, tagCode);
  4488             $2.push($4);
  4491         if (blend) {
  4492           $.blendMode = readUi8($bytes, $stream);
  4494         if (cache) {
  4495           $.bmpCache = readUi8($bytes, $stream);
  4497         if (hasEvents) {
  4498           var reserved = readUi16($bytes, $stream);
  4499           if (swfVersion >= 6) {
  4500             var allFlags = readUi32($bytes, $stream);
  4501           } else {
  4502             var allFlags = readUi16($bytes, $stream);
  4504           var $28 = $.events = [];
  4505           do {
  4506             var $29 = {};
  4507             var temp = events($bytes, $stream, $29, swfVersion, tagCode);
  4508             eoe = temp.eoe;
  4509             $28.push($29);
  4510           } while (!eoe);
  4512         if (hasBackgroundColor) {
  4513           var $126 = $.backgroundColor = {};
  4514           argb($bytes, $stream, $126, swfVersion, tagCode);
  4516         if (hasVisibility) {
  4517           $.visibility = readUi8($bytes, $stream);
  4519       } else {
  4520         $.place = 1;
  4521         $.symbolId = readUi16($bytes, $stream);
  4522         $.depth = readUi16($bytes, $stream);
  4523         $.hasMatrix = 1;
  4524         var $30 = $.matrix = {};
  4525         matrix($bytes, $stream, $30, swfVersion, tagCode);
  4526         if ($stream.remaining()) {
  4527           $.hasCxform = 1;
  4528           var $31 = $.cxform = {};
  4529           cxform($bytes, $stream, $31, swfVersion, tagCode);
  4532       return $;
  4534     function removeObject($bytes, $stream, $, swfVersion, tagCode) {
  4535       $ || ($ = {});
  4536       if (tagCode === 5) {
  4537         $.symbolId = readUi16($bytes, $stream);
  4539       $.depth = readUi16($bytes, $stream);
  4540       return $;
  4542     function defineImage($bytes, $stream, $, swfVersion, tagCode) {
  4543       var imgData;
  4544       $ || ($ = {});
  4545       $.id = readUi16($bytes, $stream);
  4546       if (tagCode > 21) {
  4547         var alphaDataOffset = readUi32($bytes, $stream);
  4548         if (tagCode === 90) {
  4549           $.deblock = readFixed8($bytes, $stream);
  4551         imgData = $.imgData = readBinary($bytes, $stream, alphaDataOffset);
  4552         $.alphaData = readBinary($bytes, $stream, 0);
  4553       } else {
  4554         imgData = $.imgData = readBinary($bytes, $stream, 0);
  4556       switch (imgData[0] << 8 | imgData[1]) {
  4557       case 65496:
  4558       case 65497:
  4559         $.mimeType = 'image/jpeg';
  4560         break;
  4561       case 35152:
  4562         $.mimeType = 'image/png';
  4563         break;
  4564       case 18249:
  4565         $.mimeType = 'image/gif';
  4566         break;
  4567       default:
  4568         $.mimeType = 'application/octet-stream';
  4570       if (tagCode === 6) {
  4571         $.incomplete = 1;
  4573       return $;
  4575     function defineButton($bytes, $stream, $, swfVersion, tagCode) {
  4576       var eob, hasFilters, count, blend;
  4577       $ || ($ = {});
  4578       $.id = readUi16($bytes, $stream);
  4579       if (tagCode == 7) {
  4580         var $0 = $.characters = [];
  4581         do {
  4582           var $1 = {};
  4583           var temp = button($bytes, $stream, $1, swfVersion, tagCode);
  4584           eob = temp.eob;
  4585           $0.push($1);
  4586         } while (!eob);
  4587         $.actionsData = readBinary($bytes, $stream, 0);
  4588       } else {
  4589         var trackFlags = readUi8($bytes, $stream);
  4590         $.trackAsMenu = trackFlags >> 7 & 1;
  4591         var actionOffset = readUi16($bytes, $stream);
  4592         var $28 = $.characters = [];
  4593         do {
  4594           var $29 = {};
  4595           var flags = readUi8($bytes, $stream);
  4596           var eob = $29.eob = !flags;
  4597           if (swfVersion >= 8) {
  4598             blend = $29.blend = flags >> 5 & 1;
  4599             hasFilters = $29.hasFilters = flags >> 4 & 1;
  4600           } else {
  4601             blend = $29.blend = 0;
  4602             hasFilters = $29.hasFilters = 0;
  4604           $29.stateHitTest = flags >> 3 & 1;
  4605           $29.stateDown = flags >> 2 & 1;
  4606           $29.stateOver = flags >> 1 & 1;
  4607           $29.stateUp = flags & 1;
  4608           if (!eob) {
  4609             $29.symbolId = readUi16($bytes, $stream);
  4610             $29.depth = readUi16($bytes, $stream);
  4611             var $30 = $29.matrix = {};
  4612             matrix($bytes, $stream, $30, swfVersion, tagCode);
  4613             if (tagCode === 34) {
  4614               var $31 = $29.cxform = {};
  4615               cxform($bytes, $stream, $31, swfVersion, tagCode);
  4617             if (hasFilters) {
  4618               var count = readUi8($bytes, $stream);
  4619               var $2 = $.filters = [];
  4620               var $3 = count;
  4621               while ($3--) {
  4622                 var $4 = {};
  4623                 anyFilter($bytes, $stream, $4, swfVersion, tagCode);
  4624                 $2.push($4);
  4627             if (blend) {
  4628               $29.blendMode = readUi8($bytes, $stream);
  4631           $28.push($29);
  4632         } while (!eob);
  4633         if (!(!actionOffset)) {
  4634           var $56 = $.buttonActions = [];
  4635           do {
  4636             var $57 = {};
  4637             buttonCondAction($bytes, $stream, $57, swfVersion, tagCode);
  4638             $56.push($57);
  4639           } while ($stream.remaining() > 0);
  4642       return $;
  4644     function defineJPEGTables($bytes, $stream, $, swfVersion, tagCode) {
  4645       $ || ($ = {});
  4646       $.id = 0;
  4647       $.imgData = readBinary($bytes, $stream, 0);
  4648       $.mimeType = 'application/octet-stream';
  4649       return $;
  4651     function setBackgroundColor($bytes, $stream, $, swfVersion, tagCode) {
  4652       $ || ($ = {});
  4653       var $0 = $.color = {};
  4654       rgb($bytes, $stream, $0, swfVersion, tagCode);
  4655       return $;
  4657     function defineBinaryData($bytes, $stream, $, swfVersion, tagCode) {
  4658       $ || ($ = {});
  4659       $.id = readUi16($bytes, $stream);
  4660       var reserved = readUi32($bytes, $stream);
  4661       $.data = readBinary($bytes, $stream, 0);
  4662       return $;
  4664     function defineFont($bytes, $stream, $, swfVersion, tagCode) {
  4665       $ || ($ = {});
  4666       $.id = readUi16($bytes, $stream);
  4667       var firstOffset = readUi16($bytes, $stream);
  4668       var glyphCount = $.glyphCount = firstOffset / 2;
  4669       var restOffsets = [];
  4670       var $0 = glyphCount - 1;
  4671       while ($0--) {
  4672         restOffsets.push(readUi16($bytes, $stream));
  4674       $.offsets = [
  4675         firstOffset
  4676       ].concat(restOffsets);
  4677       var $1 = $.glyphs = [];
  4678       var $2 = glyphCount;
  4679       while ($2--) {
  4680         var $3 = {};
  4681         shape($bytes, $stream, $3, swfVersion, tagCode);
  4682         $1.push($3);
  4684       return $;
  4686     function defineLabel($bytes, $stream, $, swfVersion, tagCode) {
  4687       var eot;
  4688       $ || ($ = {});
  4689       $.id = readUi16($bytes, $stream);
  4690       var $0 = $.bbox = {};
  4691       bbox($bytes, $stream, $0, swfVersion, tagCode);
  4692       var $1 = $.matrix = {};
  4693       matrix($bytes, $stream, $1, swfVersion, tagCode);
  4694       var glyphBits = $.glyphBits = readUi8($bytes, $stream);
  4695       var advanceBits = $.advanceBits = readUi8($bytes, $stream);
  4696       var $2 = $.records = [];
  4697       do {
  4698         var $3 = {};
  4699         var temp = textRecord($bytes, $stream, $3, swfVersion, tagCode, glyphBits, advanceBits);
  4700         eot = temp.eot;
  4701         $2.push($3);
  4702       } while (!eot);
  4703       return $;
  4705     function doAction($bytes, $stream, $, swfVersion, tagCode) {
  4706       $ || ($ = {});
  4707       if (tagCode === 59) {
  4708         $.spriteId = readUi16($bytes, $stream);
  4710       $.actionsData = readBinary($bytes, $stream, 0);
  4711       return $;
  4713     function defineSound($bytes, $stream, $, swfVersion, tagCode) {
  4714       $ || ($ = {});
  4715       $.id = readUi16($bytes, $stream);
  4716       var soundFlags = readUi8($bytes, $stream);
  4717       $.soundFormat = soundFlags >> 4 & 15;
  4718       $.soundRate = soundFlags >> 2 & 3;
  4719       $.soundSize = soundFlags >> 1 & 1;
  4720       $.soundType = soundFlags & 1;
  4721       $.samplesCount = readUi32($bytes, $stream);
  4722       $.soundData = readBinary($bytes, $stream, 0);
  4723       return $;
  4725     function startSound($bytes, $stream, $, swfVersion, tagCode) {
  4726       $ || ($ = {});
  4727       if (tagCode == 15) {
  4728         $.soundId = readUi16($bytes, $stream);
  4730       if (tagCode == 89) {
  4731         $.soundClassName = readString($bytes, $stream, 0);
  4733       var $0 = $.soundInfo = {};
  4734       soundInfo($bytes, $stream, $0, swfVersion, tagCode);
  4735       return $;
  4737     function soundStreamHead($bytes, $stream, $, swfVersion, tagCode) {
  4738       $ || ($ = {});
  4739       var playbackFlags = readUi8($bytes, $stream);
  4740       $.playbackRate = playbackFlags >> 2 & 3;
  4741       $.playbackSize = playbackFlags >> 1 & 1;
  4742       $.playbackType = playbackFlags & 1;
  4743       var streamFlags = readUi8($bytes, $stream);
  4744       var streamCompression = $.streamCompression = streamFlags >> 4 & 15;
  4745       $.streamRate = streamFlags >> 2 & 3;
  4746       $.streamSize = streamFlags >> 1 & 1;
  4747       $.streamType = streamFlags & 1;
  4748       $.samplesCount = readUi32($bytes, $stream);
  4749       if (streamCompression == 2) {
  4750         $.latencySeek = readSi16($bytes, $stream);
  4752       return $;
  4754     function soundStreamBlock($bytes, $stream, $, swfVersion, tagCode) {
  4755       $ || ($ = {});
  4756       $.data = readBinary($bytes, $stream, 0);
  4757       return $;
  4759     function defineBitmap($bytes, $stream, $, swfVersion, tagCode) {
  4760       $ || ($ = {});
  4761       $.id = readUi16($bytes, $stream);
  4762       var format = $.format = readUi8($bytes, $stream);
  4763       $.width = readUi16($bytes, $stream);
  4764       $.height = readUi16($bytes, $stream);
  4765       $.hasAlpha = tagCode === 36;
  4766       if (format === 3) {
  4767         $.colorTableSize = readUi8($bytes, $stream);
  4769       $.bmpData = readBinary($bytes, $stream, 0);
  4770       return $;
  4772     function defineText($bytes, $stream, $, swfVersion, tagCode) {
  4773       $ || ($ = {});
  4774       $.id = readUi16($bytes, $stream);
  4775       var $0 = $.bbox = {};
  4776       bbox($bytes, $stream, $0, swfVersion, tagCode);
  4777       var flags = readUi16($bytes, $stream);
  4778       var hasText = $.hasText = flags >> 7 & 1;
  4779       $.wordWrap = flags >> 6 & 1;
  4780       $.multiline = flags >> 5 & 1;
  4781       $.password = flags >> 4 & 1;
  4782       $.readonly = flags >> 3 & 1;
  4783       var hasColor = $.hasColor = flags >> 2 & 1;
  4784       var hasMaxLength = $.hasMaxLength = flags >> 1 & 1;
  4785       var hasFont = $.hasFont = flags & 1;
  4786       var hasFontClass = $.hasFontClass = flags >> 15 & 1;
  4787       $.autoSize = flags >> 14 & 1;
  4788       var hasLayout = $.hasLayout = flags >> 13 & 1;
  4789       $.noSelect = flags >> 12 & 1;
  4790       $.border = flags >> 11 & 1;
  4791       $.wasStatic = flags >> 10 & 1;
  4792       $.html = flags >> 9 & 1;
  4793       $.useOutlines = flags >> 8 & 1;
  4794       if (hasFont) {
  4795         $.fontId = readUi16($bytes, $stream);
  4797       if (hasFontClass) {
  4798         $.fontClass = readString($bytes, $stream, 0);
  4800       if (hasFont) {
  4801         $.fontHeight = readUi16($bytes, $stream);
  4803       if (hasColor) {
  4804         var $1 = $.color = {};
  4805         rgba($bytes, $stream, $1, swfVersion, tagCode);
  4807       if (hasMaxLength) {
  4808         $.maxLength = readUi16($bytes, $stream);
  4810       if (hasLayout) {
  4811         $.align = readUi8($bytes, $stream);
  4812         $.leftMargin = readUi16($bytes, $stream);
  4813         $.rightMargin = readUi16($bytes, $stream);
  4814         $.indent = readSi16($bytes, $stream);
  4815         $.leading = readSi16($bytes, $stream);
  4817       $.variableName = readString($bytes, $stream, 0);
  4818       if (hasText) {
  4819         $.initialText = readString($bytes, $stream, 0);
  4821       return $;
  4823     function frameLabel($bytes, $stream, $, swfVersion, tagCode) {
  4824       $ || ($ = {});
  4825       $.name = readString($bytes, $stream, 0);
  4826       return $;
  4828     function defineFont2($bytes, $stream, $, swfVersion, tagCode) {
  4829       $ || ($ = {});
  4830       $.id = readUi16($bytes, $stream);
  4831       var hasLayout = $.hasLayout = readUb($bytes, $stream, 1);
  4832       if (swfVersion > 5) {
  4833         $.shiftJis = readUb($bytes, $stream, 1);
  4834       } else {
  4835         var reserved = readUb($bytes, $stream, 1);
  4837       $.smallText = readUb($bytes, $stream, 1);
  4838       $.ansi = readUb($bytes, $stream, 1);
  4839       var wideOffset = $.wideOffset = readUb($bytes, $stream, 1);
  4840       var wide = $.wide = readUb($bytes, $stream, 1);
  4841       $.italic = readUb($bytes, $stream, 1);
  4842       $.bold = readUb($bytes, $stream, 1);
  4843       if (swfVersion > 5) {
  4844         $.language = readUi8($bytes, $stream);
  4845       } else {
  4846         var reserved = readUi8($bytes, $stream);
  4847         $.language = 0;
  4849       var nameLength = readUi8($bytes, $stream);
  4850       $.name = readString($bytes, $stream, nameLength);
  4851       if (tagCode === 75) {
  4852         $.resolution = 20;
  4854       var glyphCount = $.glyphCount = readUi16($bytes, $stream);
  4855       if (wideOffset) {
  4856         var $0 = $.offsets = [];
  4857         var $1 = glyphCount;
  4858         while ($1--) {
  4859           $0.push(readUi32($bytes, $stream));
  4861         $.mapOffset = readUi32($bytes, $stream);
  4862       } else {
  4863         var $2 = $.offsets = [];
  4864         var $3 = glyphCount;
  4865         while ($3--) {
  4866           $2.push(readUi16($bytes, $stream));
  4868         $.mapOffset = readUi16($bytes, $stream);
  4870       var $4 = $.glyphs = [];
  4871       var $5 = glyphCount;
  4872       while ($5--) {
  4873         var $6 = {};
  4874         shape($bytes, $stream, $6, swfVersion, tagCode);
  4875         $4.push($6);
  4877       if (wide) {
  4878         var $47 = $.codes = [];
  4879         var $48 = glyphCount;
  4880         while ($48--) {
  4881           $47.push(readUi16($bytes, $stream));
  4883       } else {
  4884         var $49 = $.codes = [];
  4885         var $50 = glyphCount;
  4886         while ($50--) {
  4887           $49.push(readUi8($bytes, $stream));
  4890       if (hasLayout) {
  4891         $.ascent = readUi16($bytes, $stream);
  4892         $.descent = readUi16($bytes, $stream);
  4893         $.leading = readSi16($bytes, $stream);
  4894         var $51 = $.advance = [];
  4895         var $52 = glyphCount;
  4896         while ($52--) {
  4897           $51.push(readSi16($bytes, $stream));
  4899         var $53 = $.bbox = [];
  4900         var $54 = glyphCount;
  4901         while ($54--) {
  4902           var $55 = {};
  4903           bbox($bytes, $stream, $55, swfVersion, tagCode);
  4904           $53.push($55);
  4906         var kerningCount = readUi16($bytes, $stream);
  4907         var $56 = $.kerning = [];
  4908         var $57 = kerningCount;
  4909         while ($57--) {
  4910           var $58 = {};
  4911           kerning($bytes, $stream, $58, swfVersion, tagCode, wide);
  4912           $56.push($58);
  4915       return $;
  4917     function fileAttributes($bytes, $stream, $, swfVersion, tagCode) {
  4918       $ || ($ = {});
  4919       var reserved = readUb($bytes, $stream, 1);
  4920       $.useDirectBlit = readUb($bytes, $stream, 1);
  4921       $.useGpu = readUb($bytes, $stream, 1);
  4922       $.hasMetadata = readUb($bytes, $stream, 1);
  4923       $.doAbc = readUb($bytes, $stream, 1);
  4924       $.noCrossDomainCaching = readUb($bytes, $stream, 1);
  4925       $.relativeUrls = readUb($bytes, $stream, 1);
  4926       $.network = readUb($bytes, $stream, 1);
  4927       var pad = readUb($bytes, $stream, 24);
  4928       return $;
  4930     function doABC($bytes, $stream, $, swfVersion, tagCode) {
  4931       $ || ($ = {});
  4932       if (tagCode === 82) {
  4933         $.flags = readUi32($bytes, $stream);
  4934       } else {
  4935         $.flags = 0;
  4937       if (tagCode === 82) {
  4938         $.name = readString($bytes, $stream, 0);
  4939       } else {
  4940         $.name = '';
  4942       $.data = readBinary($bytes, $stream, 0);
  4943       return $;
  4945     function exportAssets($bytes, $stream, $, swfVersion, tagCode) {
  4946       $ || ($ = {});
  4947       var exportsCount = readUi16($bytes, $stream);
  4948       var $0 = $.exports = [];
  4949       var $1 = exportsCount;
  4950       while ($1--) {
  4951         var $2 = {};
  4952         $2.symbolId = readUi16($bytes, $stream);
  4953         $2.className = readString($bytes, $stream, 0);
  4954         $0.push($2);
  4956       return $;
  4958     function symbolClass($bytes, $stream, $, swfVersion, tagCode) {
  4959       $ || ($ = {});
  4960       var symbolCount = readUi16($bytes, $stream);
  4961       var $0 = $.exports = [];
  4962       var $1 = symbolCount;
  4963       while ($1--) {
  4964         var $2 = {};
  4965         $2.symbolId = readUi16($bytes, $stream);
  4966         $2.className = readString($bytes, $stream, 0);
  4967         $0.push($2);
  4969       return $;
  4971     function defineScalingGrid($bytes, $stream, $, swfVersion, tagCode) {
  4972       $ || ($ = {});
  4973       $.symbolId = readUi16($bytes, $stream);
  4974       var $0 = $.splitter = {};
  4975       bbox($bytes, $stream, $0, swfVersion, tagCode);
  4976       return $;
  4978     function defineScene($bytes, $stream, $, swfVersion, tagCode) {
  4979       $ || ($ = {});
  4980       var sceneCount = readEncodedU32($bytes, $stream);
  4981       var $0 = $.scenes = [];
  4982       var $1 = sceneCount;
  4983       while ($1--) {
  4984         var $2 = {};
  4985         $2.offset = readEncodedU32($bytes, $stream);
  4986         $2.name = readString($bytes, $stream, 0);
  4987         $0.push($2);
  4989       var labelCount = readEncodedU32($bytes, $stream);
  4990       var $3 = $.labels = [];
  4991       var $4 = labelCount;
  4992       while ($4--) {
  4993         var $5 = {};
  4994         $5.frame = readEncodedU32($bytes, $stream);
  4995         $5.name = readString($bytes, $stream, 0);
  4996         $3.push($5);
  4998       return $;
  5000     function bbox($bytes, $stream, $, swfVersion, tagCode) {
  5001       align($bytes, $stream);
  5002       var bits = readUb($bytes, $stream, 5);
  5003       var xMin = readSb($bytes, $stream, bits);
  5004       var xMax = readSb($bytes, $stream, bits);
  5005       var yMin = readSb($bytes, $stream, bits);
  5006       var yMax = readSb($bytes, $stream, bits);
  5007       $.xMin = xMin;
  5008       $.xMax = xMax;
  5009       $.yMin = yMin;
  5010       $.yMax = yMax;
  5011       align($bytes, $stream);
  5013     function rgb($bytes, $stream, $, swfVersion, tagCode) {
  5014       $.red = readUi8($bytes, $stream);
  5015       $.green = readUi8($bytes, $stream);
  5016       $.blue = readUi8($bytes, $stream);
  5017       $.alpha = 255;
  5018       return;
  5020     function rgba($bytes, $stream, $, swfVersion, tagCode) {
  5021       $.red = readUi8($bytes, $stream);
  5022       $.green = readUi8($bytes, $stream);
  5023       $.blue = readUi8($bytes, $stream);
  5024       $.alpha = readUi8($bytes, $stream);
  5025       return;
  5027     function argb($bytes, $stream, $, swfVersion, tagCode) {
  5028       $.alpha = readUi8($bytes, $stream);
  5029       $.red = readUi8($bytes, $stream);
  5030       $.green = readUi8($bytes, $stream);
  5031       $.blue = readUi8($bytes, $stream);
  5033     function fillSolid($bytes, $stream, $, swfVersion, tagCode, isMorph) {
  5034       if (tagCode > 22 || isMorph) {
  5035         var $125 = $.color = {};
  5036         rgba($bytes, $stream, $125, swfVersion, tagCode);
  5037       } else {
  5038         var $126 = $.color = {};
  5039         rgb($bytes, $stream, $126, swfVersion, tagCode);
  5041       if (isMorph) {
  5042         var $127 = $.colorMorph = {};
  5043         rgba($bytes, $stream, $127, swfVersion, tagCode);
  5045       return;
  5047     function matrix($bytes, $stream, $, swfVersion, tagCode) {
  5048       align($bytes, $stream);
  5049       var hasScale = readUb($bytes, $stream, 1);
  5050       if (hasScale) {
  5051         var bits = readUb($bytes, $stream, 5);
  5052         $.a = readFb($bytes, $stream, bits);
  5053         $.d = readFb($bytes, $stream, bits);
  5054       } else {
  5055         $.a = 1;
  5056         $.d = 1;
  5058       var hasRotate = readUb($bytes, $stream, 1);
  5059       if (hasRotate) {
  5060         var bits = readUb($bytes, $stream, 5);
  5061         $.b = readFb($bytes, $stream, bits);
  5062         $.c = readFb($bytes, $stream, bits);
  5063       } else {
  5064         $.b = 0;
  5065         $.c = 0;
  5067       var bits = readUb($bytes, $stream, 5);
  5068       var e = readSb($bytes, $stream, bits);
  5069       var f = readSb($bytes, $stream, bits);
  5070       $.tx = e;
  5071       $.ty = f;
  5072       align($bytes, $stream);
  5074     function cxform($bytes, $stream, $, swfVersion, tagCode) {
  5075       align($bytes, $stream);
  5076       var hasOffsets = readUb($bytes, $stream, 1);
  5077       var hasMultipliers = readUb($bytes, $stream, 1);
  5078       var bits = readUb($bytes, $stream, 4);
  5079       if (hasMultipliers) {
  5080         $.redMultiplier = readSb($bytes, $stream, bits);
  5081         $.greenMultiplier = readSb($bytes, $stream, bits);
  5082         $.blueMultiplier = readSb($bytes, $stream, bits);
  5083         if (tagCode > 4) {
  5084           $.alphaMultiplier = readSb($bytes, $stream, bits);
  5085         } else {
  5086           $.alphaMultiplier = 256;
  5088       } else {
  5089         $.redMultiplier = 256;
  5090         $.greenMultiplier = 256;
  5091         $.blueMultiplier = 256;
  5092         $.alphaMultiplier = 256;
  5094       if (hasOffsets) {
  5095         $.redOffset = readSb($bytes, $stream, bits);
  5096         $.greenOffset = readSb($bytes, $stream, bits);
  5097         $.blueOffset = readSb($bytes, $stream, bits);
  5098         if (tagCode > 4) {
  5099           $.alphaOffset = readSb($bytes, $stream, bits);
  5100         } else {
  5101           $.alphaOffset = 0;
  5103       } else {
  5104         $.redOffset = 0;
  5105         $.greenOffset = 0;
  5106         $.blueOffset = 0;
  5107         $.alphaOffset = 0;
  5109       align($bytes, $stream);
  5111     function fillGradient($bytes, $stream, $, swfVersion, tagCode, isMorph, type) {
  5112       var $128 = $.matrix = {};
  5113       matrix($bytes, $stream, $128, swfVersion, tagCode);
  5114       if (isMorph) {
  5115         var $129 = $.matrixMorph = {};
  5116         matrix($bytes, $stream, $129, swfVersion, tagCode);
  5118       gradient($bytes, $stream, $, swfVersion, tagCode, isMorph, type);
  5120     function gradient($bytes, $stream, $, swfVersion, tagCode, isMorph, type) {
  5121       if (tagCode === 83) {
  5122         $.spreadMode = readUb($bytes, $stream, 2);
  5123         $.interpolationMode = readUb($bytes, $stream, 2);
  5124       } else {
  5125         var pad = readUb($bytes, $stream, 4);
  5127       var count = $.count = readUb($bytes, $stream, 4);
  5128       var $130 = $.records = [];
  5129       var $131 = count;
  5130       while ($131--) {
  5131         var $132 = {};
  5132         gradientRecord($bytes, $stream, $132, swfVersion, tagCode, isMorph);
  5133         $130.push($132);
  5135       if (type === 19) {
  5136         $.focalPoint = readFixed8($bytes, $stream);
  5137         if (isMorph) {
  5138           $.focalPointMorph = readFixed8($bytes, $stream);
  5142     function gradientRecord($bytes, $stream, $, swfVersion, tagCode, isMorph) {
  5143       $.ratio = readUi8($bytes, $stream);
  5144       if (tagCode > 22) {
  5145         var $133 = $.color = {};
  5146         rgba($bytes, $stream, $133, swfVersion, tagCode);
  5147       } else {
  5148         var $134 = $.color = {};
  5149         rgb($bytes, $stream, $134, swfVersion, tagCode);
  5151       if (isMorph) {
  5152         $.ratioMorph = readUi8($bytes, $stream);
  5153         var $135 = $.colorMorph = {};
  5154         rgba($bytes, $stream, $135, swfVersion, tagCode);
  5157     function morphShapeWithStyle($bytes, $stream, $, swfVersion, tagCode, isMorph, hasStrokes) {
  5158       var eos, bits;
  5159       var temp = styles($bytes, $stream, $, swfVersion, tagCode, isMorph, hasStrokes);
  5160       var lineBits = temp.lineBits;
  5161       var fillBits = temp.fillBits;
  5162       var $160 = $.records = [];
  5163       do {
  5164         var $161 = {};
  5165         var temp = shapeRecord($bytes, $stream, $161, swfVersion, tagCode, isMorph, fillBits, lineBits, hasStrokes, bits);
  5166         var eos = temp.eos;
  5167         var flags = temp.flags;
  5168         var type = temp.type;
  5169         var fillBits = temp.fillBits;
  5170         var lineBits = temp.lineBits;
  5171         var bits = temp.bits;
  5172         $160.push($161);
  5173       } while (!eos);
  5174       var temp = styleBits($bytes, $stream, $, swfVersion, tagCode);
  5175       var fillBits = temp.fillBits;
  5176       var lineBits = temp.lineBits;
  5177       var $162 = $.recordsMorph = [];
  5178       do {
  5179         var $163 = {};
  5180         var temp = shapeRecord($bytes, $stream, $163, swfVersion, tagCode, isMorph, fillBits, lineBits, hasStrokes, bits);
  5181         eos = temp.eos;
  5182         var flags = temp.flags;
  5183         var type = temp.type;
  5184         var fillBits = temp.fillBits;
  5185         var lineBits = temp.lineBits;
  5186         bits = temp.bits;
  5187         $162.push($163);
  5188       } while (!eos);
  5190     function shapeWithStyle($bytes, $stream, $, swfVersion, tagCode, isMorph, hasStrokes) {
  5191       var eos;
  5192       var temp = styles($bytes, $stream, $, swfVersion, tagCode, isMorph, hasStrokes);
  5193       var fillBits = temp.fillBits;
  5194       var lineBits = temp.lineBits;
  5195       var $160 = $.records = [];
  5196       do {
  5197         var $161 = {};
  5198         var temp = shapeRecord($bytes, $stream, $161, swfVersion, tagCode, isMorph, fillBits, lineBits, hasStrokes, bits);
  5199         eos = temp.eos;
  5200         var flags = temp.flags;
  5201         var type = temp.type;
  5202         var fillBits = temp.fillBits;
  5203         var lineBits = temp.lineBits;
  5204         var bits = temp.bits;
  5205         $160.push($161);
  5206       } while (!eos);
  5208     function shapeRecord($bytes, $stream, $, swfVersion, tagCode, isMorph, fillBits, lineBits, hasStrokes, bits) {
  5209       var type = $.type = readUb($bytes, $stream, 1);
  5210       var flags = readUb($bytes, $stream, 5);
  5211       var eos = $.eos = !(type || flags);
  5212       if (type) {
  5213         var temp = shapeRecordEdge($bytes, $stream, $, swfVersion, tagCode, flags, bits);
  5214         var bits = temp.bits;
  5215       } else {
  5216         var temp = shapeRecordSetup($bytes, $stream, $, swfVersion, tagCode, flags, isMorph, fillBits, lineBits, hasStrokes, bits);
  5217         var fillBits = temp.fillBits;
  5218         var lineBits = temp.lineBits;
  5219         var bits = temp.bits;
  5221       return {
  5222         type: type,
  5223         flags: flags,
  5224         eos: eos,
  5225         fillBits: fillBits,
  5226         lineBits: lineBits,
  5227         bits: bits
  5228       };
  5230     function shapeRecordEdge($bytes, $stream, $, swfVersion, tagCode, flags, bits) {
  5231       var isStraight = 0, tmp = 0, bits = 0, isGeneral = 0, isVertical = 0;
  5232       isStraight = $.isStraight = flags >> 4;
  5233       tmp = flags & 15;
  5234       bits = tmp + 2;
  5235       if (isStraight) {
  5236         isGeneral = $.isGeneral = readUb($bytes, $stream, 1);
  5237         if (isGeneral) {
  5238           $.deltaX = readSb($bytes, $stream, bits);
  5239           $.deltaY = readSb($bytes, $stream, bits);
  5240         } else {
  5241           isVertical = $.isVertical = readUb($bytes, $stream, 1);
  5242           if (isVertical) {
  5243             $.deltaY = readSb($bytes, $stream, bits);
  5244           } else {
  5245             $.deltaX = readSb($bytes, $stream, bits);
  5248       } else {
  5249         $.controlDeltaX = readSb($bytes, $stream, bits);
  5250         $.controlDeltaY = readSb($bytes, $stream, bits);
  5251         $.anchorDeltaX = readSb($bytes, $stream, bits);
  5252         $.anchorDeltaY = readSb($bytes, $stream, bits);
  5254       return {
  5255         bits: bits
  5256       };
  5258     function shapeRecordSetup($bytes, $stream, $, swfVersion, tagCode, flags, isMorph, fillBits, lineBits, hasStrokes, bits) {
  5259       var hasNewStyles = 0, hasLineStyle = 0, hasFillStyle1 = 0;
  5260       var hasFillStyle0 = 0, move = 0;
  5261       if (tagCode > 2) {
  5262         hasNewStyles = $.hasNewStyles = flags >> 4;
  5263       } else {
  5264         hasNewStyles = $.hasNewStyles = 0;
  5266       hasLineStyle = $.hasLineStyle = flags >> 3 & 1;
  5267       hasFillStyle1 = $.hasFillStyle1 = flags >> 2 & 1;
  5268       hasFillStyle0 = $.hasFillStyle0 = flags >> 1 & 1;
  5269       move = $.move = flags & 1;
  5270       if (move) {
  5271         bits = readUb($bytes, $stream, 5);
  5272         $.moveX = readSb($bytes, $stream, bits);
  5273         $.moveY = readSb($bytes, $stream, bits);
  5275       if (hasFillStyle0) {
  5276         $.fillStyle0 = readUb($bytes, $stream, fillBits);
  5278       if (hasFillStyle1) {
  5279         $.fillStyle1 = readUb($bytes, $stream, fillBits);
  5281       if (hasLineStyle) {
  5282         $.lineStyle = readUb($bytes, $stream, lineBits);
  5284       if (hasNewStyles) {
  5285         var temp = styles($bytes, $stream, $, swfVersion, tagCode, isMorph, hasStrokes);
  5286         var lineBits = temp.lineBits;
  5287         var fillBits = temp.fillBits;
  5289       return {
  5290         lineBits: lineBits,
  5291         fillBits: fillBits,
  5292         bits: bits
  5293       };
  5295     function styles($bytes, $stream, $, swfVersion, tagCode, isMorph, hasStrokes) {
  5296       fillStyleArray($bytes, $stream, $, swfVersion, tagCode, isMorph);
  5297       lineStyleArray($bytes, $stream, $, swfVersion, tagCode, isMorph, hasStrokes);
  5298       var temp = styleBits($bytes, $stream, $, swfVersion, tagCode);
  5299       var fillBits = temp.fillBits;
  5300       var lineBits = temp.lineBits;
  5301       return {
  5302         fillBits: fillBits,
  5303         lineBits: lineBits
  5304       };
  5306     function fillStyleArray($bytes, $stream, $, swfVersion, tagCode, isMorph) {
  5307       var count;
  5308       var tmp = readUi8($bytes, $stream);
  5309       if (tagCode > 2 && tmp === 255) {
  5310         count = readUi16($bytes, $stream);
  5311       } else {
  5312         count = tmp;
  5314       var $4 = $.fillStyles = [];
  5315       var $5 = count;
  5316       while ($5--) {
  5317         var $6 = {};
  5318         fillStyle($bytes, $stream, $6, swfVersion, tagCode, isMorph);
  5319         $4.push($6);
  5322     function lineStyleArray($bytes, $stream, $, swfVersion, tagCode, isMorph, hasStrokes) {
  5323       var count;
  5324       var tmp = readUi8($bytes, $stream);
  5325       if (tagCode > 2 && tmp === 255) {
  5326         count = readUi16($bytes, $stream);
  5327       } else {
  5328         count = tmp;
  5330       var $138 = $.lineStyles = [];
  5331       var $139 = count;
  5332       while ($139--) {
  5333         var $140 = {};
  5334         lineStyle($bytes, $stream, $140, swfVersion, tagCode, isMorph, hasStrokes);
  5335         $138.push($140);
  5338     function styleBits($bytes, $stream, $, swfVersion, tagCode) {
  5339       align($bytes, $stream);
  5340       var fillBits = readUb($bytes, $stream, 4);
  5341       var lineBits = readUb($bytes, $stream, 4);
  5342       return {
  5343         fillBits: fillBits,
  5344         lineBits: lineBits
  5345       };
  5347     function fillStyle($bytes, $stream, $, swfVersion, tagCode, isMorph) {
  5348       var type = $.type = readUi8($bytes, $stream);
  5349       switch (type) {
  5350       case 0:
  5351         fillSolid($bytes, $stream, $, swfVersion, tagCode, isMorph);
  5352         break;
  5353       case 16:
  5354       case 18:
  5355       case 19:
  5356         fillGradient($bytes, $stream, $, swfVersion, tagCode, isMorph, type);
  5357         break;
  5358       case 64:
  5359       case 65:
  5360       case 66:
  5361       case 67:
  5362         fillBitmap($bytes, $stream, $, swfVersion, tagCode, isMorph, type);
  5363         break;
  5364       default:
  5367     function lineStyle($bytes, $stream, $, swfVersion, tagCode, isMorph, hasStrokes) {
  5368       $.width = readUi16($bytes, $stream);
  5369       if (isMorph) {
  5370         $.widthMorph = readUi16($bytes, $stream);
  5372       if (hasStrokes) {
  5373         align($bytes, $stream);
  5374         $.startCapStyle = readUb($bytes, $stream, 2);
  5375         var joinStyle = $.joinStyle = readUb($bytes, $stream, 2);
  5376         var hasFill = $.hasFill = readUb($bytes, $stream, 1);
  5377         $.noHscale = readUb($bytes, $stream, 1);
  5378         $.noVscale = readUb($bytes, $stream, 1);
  5379         $.pixelHinting = readUb($bytes, $stream, 1);
  5380         var reserved = readUb($bytes, $stream, 5);
  5381         $.noClose = readUb($bytes, $stream, 1);
  5382         $.endCapStyle = readUb($bytes, $stream, 2);
  5383         if (joinStyle === 2) {
  5384           $.miterLimitFactor = readFixed8($bytes, $stream);
  5386         if (hasFill) {
  5387           var $141 = $.fillStyle = {};
  5388           fillStyle($bytes, $stream, $141, swfVersion, tagCode, isMorph);
  5389         } else {
  5390           var $155 = $.color = {};
  5391           rgba($bytes, $stream, $155, swfVersion, tagCode);
  5392           if (isMorph) {
  5393             var $156 = $.colorMorph = {};
  5394             rgba($bytes, $stream, $156, swfVersion, tagCode);
  5397       } else {
  5398         if (tagCode > 22) {
  5399           var $157 = $.color = {};
  5400           rgba($bytes, $stream, $157, swfVersion, tagCode);
  5401         } else {
  5402           var $158 = $.color = {};
  5403           rgb($bytes, $stream, $158, swfVersion, tagCode);
  5405         if (isMorph) {
  5406           var $159 = $.colorMorph = {};
  5407           rgba($bytes, $stream, $159, swfVersion, tagCode);
  5411     function fillBitmap($bytes, $stream, $, swfVersion, tagCode, isMorph, type) {
  5412       $.bitmapId = readUi16($bytes, $stream);
  5413       var $18 = $.matrix = {};
  5414       matrix($bytes, $stream, $18, swfVersion, tagCode);
  5415       if (isMorph) {
  5416         var $19 = $.matrixMorph = {};
  5417         matrix($bytes, $stream, $19, swfVersion, tagCode);
  5419       $.condition = type === 64 || type === 67;
  5421     function filterGlow($bytes, $stream, $, swfVersion, tagCode, type) {
  5422       var count;
  5423       if (type === 4 || type === 7) {
  5424         count = readUi8($bytes, $stream);
  5425       } else {
  5426         count = 1;
  5428       var $5 = $.colors = [];
  5429       var $6 = count;
  5430       while ($6--) {
  5431         var $7 = {};
  5432         rgba($bytes, $stream, $7, swfVersion, tagCode);
  5433         $5.push($7);
  5435       if (type === 3) {
  5436         var $8 = $.higlightColor = {};
  5437         rgba($bytes, $stream, $8, swfVersion, tagCode);
  5439       if (type === 4 || type === 7) {
  5440         var $9 = $.ratios = [];
  5441         var $10 = count;
  5442         while ($10--) {
  5443           $9.push(readUi8($bytes, $stream));
  5446       $.blurX = readFixed($bytes, $stream);
  5447       $.blurY = readFixed($bytes, $stream);
  5448       if (type !== 2) {
  5449         $.angle = readFixed($bytes, $stream);
  5450         $.distance = readFixed($bytes, $stream);
  5452       $.strength = readFixed8($bytes, $stream);
  5453       $.innerShadow = readUb($bytes, $stream, 1);
  5454       $.knockout = readUb($bytes, $stream, 1);
  5455       $.compositeSource = readUb($bytes, $stream, 1);
  5456       if (type === 3) {
  5457         $.onTop = readUb($bytes, $stream, 1);
  5458       } else {
  5459         var reserved = readUb($bytes, $stream, 1);
  5461       if (type === 4 || type === 7) {
  5462         $.passes = readUb($bytes, $stream, 4);
  5463       } else {
  5464         var reserved = readUb($bytes, $stream, 4);
  5467     function filterBlur($bytes, $stream, $, swfVersion, tagCode) {
  5468       $.blurX = readFixed($bytes, $stream);
  5469       $.blurY = readFixed($bytes, $stream);
  5470       $.passes = readUb($bytes, $stream, 5);
  5471       var reserved = readUb($bytes, $stream, 3);
  5473     function filterConvolution($bytes, $stream, $, swfVersion, tagCode) {
  5474       var columns = $.columns = readUi8($bytes, $stream);
  5475       var rows = $.rows = readUi8($bytes, $stream);
  5476       $.divisor = readFloat($bytes, $stream);
  5477       $.bias = readFloat($bytes, $stream);
  5478       var $17 = $.weights = [];
  5479       var $18 = columns * rows;
  5480       while ($18--) {
  5481         $17.push(readFloat($bytes, $stream));
  5483       var $19 = $.defaultColor = {};
  5484       rgba($bytes, $stream, $19, swfVersion, tagCode);
  5485       var reserved = readUb($bytes, $stream, 6);
  5486       $.clamp = readUb($bytes, $stream, 1);
  5487       $.preserveAlpha = readUb($bytes, $stream, 1);
  5489     function filterColorMatrix($bytes, $stream, $, swfVersion, tagCode) {
  5490       var $20 = $.matrix = [];
  5491       var $21 = 20;
  5492       while ($21--) {
  5493         $20.push(readFloat($bytes, $stream));
  5496     function anyFilter($bytes, $stream, $, swfVersion, tagCode) {
  5497       var type = $.type = readUi8($bytes, $stream);
  5498       switch (type) {
  5499       case 0:
  5500       case 2:
  5501       case 3:
  5502       case 4:
  5503       case 7:
  5504         filterGlow($bytes, $stream, $, swfVersion, tagCode, type);
  5505         break;
  5506       case 1:
  5507         filterBlur($bytes, $stream, $, swfVersion, tagCode);
  5508         break;
  5509       case 5:
  5510         filterConvolution($bytes, $stream, $, swfVersion, tagCode);
  5511         break;
  5512       case 6:
  5513         filterColorMatrix($bytes, $stream, $, swfVersion, tagCode);
  5514         break;
  5515       default:
  5518     function events($bytes, $stream, $, swfVersion, tagCode) {
  5519       var flags, keyPress;
  5520       if (swfVersion >= 6) {
  5521         flags = readUi32($bytes, $stream);
  5522       } else {
  5523         flags = readUi16($bytes, $stream);
  5525       var eoe = $.eoe = !flags;
  5526       $.onKeyUp = flags >> 7 & 1;
  5527       $.onKeyDown = flags >> 6 & 1;
  5528       $.onMouseUp = flags >> 5 & 1;
  5529       $.onMouseDown = flags >> 4 & 1;
  5530       $.onMouseMove = flags >> 3 & 1;
  5531       $.onUnload = flags >> 2 & 1;
  5532       $.onEnterFrame = flags >> 1 & 1;
  5533       $.onLoad = flags & 1;
  5534       if (swfVersion >= 6) {
  5535         $.onDragOver = flags >> 15 & 1;
  5536         $.onRollOut = flags >> 14 & 1;
  5537         $.onRollOver = flags >> 13 & 1;
  5538         $.onReleaseOutside = flags >> 12 & 1;
  5539         $.onRelease = flags >> 11 & 1;
  5540         $.onPress = flags >> 10 & 1;
  5541         $.onInitialize = flags >> 9 & 1;
  5542         $.onData = flags >> 8 & 1;
  5543         if (swfVersion >= 7) {
  5544           $.onConstruct = flags >> 18 & 1;
  5545         } else {
  5546           $.onConstruct = 0;
  5548         keyPress = $.keyPress = flags >> 17 & 1;
  5549         $.onDragOut = flags >> 16 & 1;
  5551       if (!eoe) {
  5552         var length = $.length = readUi32($bytes, $stream);
  5553         if (keyPress) {
  5554           $.keyCode = readUi8($bytes, $stream);
  5556         $.actionsData = readBinary($bytes, $stream, length - (keyPress ? 1 : 0));
  5558       return {
  5559         eoe: eoe
  5560       };
  5562     function kerning($bytes, $stream, $, swfVersion, tagCode, wide) {
  5563       if (wide) {
  5564         $.code1 = readUi16($bytes, $stream);
  5565         $.code2 = readUi16($bytes, $stream);
  5566       } else {
  5567         $.code1 = readUi8($bytes, $stream);
  5568         $.code2 = readUi8($bytes, $stream);
  5570       $.adjustment = readUi16($bytes, $stream);
  5572     function textEntry($bytes, $stream, $, swfVersion, tagCode, glyphBits, advanceBits) {
  5573       $.glyphIndex = readUb($bytes, $stream, glyphBits);
  5574       $.advance = readSb($bytes, $stream, advanceBits);
  5576     function textRecordSetup($bytes, $stream, $, swfVersion, tagCode, flags) {
  5577       var hasFont = $.hasFont = flags >> 3 & 1;
  5578       var hasColor = $.hasColor = flags >> 2 & 1;
  5579       var hasMoveY = $.hasMoveY = flags >> 1 & 1;
  5580       var hasMoveX = $.hasMoveX = flags & 1;
  5581       if (hasFont) {
  5582         $.fontId = readUi16($bytes, $stream);
  5584       if (hasColor) {
  5585         if (tagCode === 33) {
  5586           var $4 = $.color = {};
  5587           rgba($bytes, $stream, $4, swfVersion, tagCode);
  5588         } else {
  5589           var $5 = $.color = {};
  5590           rgb($bytes, $stream, $5, swfVersion, tagCode);
  5593       if (hasMoveX) {
  5594         $.moveX = readSi16($bytes, $stream);
  5596       if (hasMoveY) {
  5597         $.moveY = readSi16($bytes, $stream);
  5599       if (hasFont) {
  5600         $.fontHeight = readUi16($bytes, $stream);
  5603     function textRecord($bytes, $stream, $, swfVersion, tagCode, glyphBits, advanceBits) {
  5604       var glyphCount;
  5605       align($bytes, $stream);
  5606       var flags = readUb($bytes, $stream, 8);
  5607       var eot = $.eot = !flags;
  5608       textRecordSetup($bytes, $stream, $, swfVersion, tagCode, flags);
  5609       if (!eot) {
  5610         var tmp = readUi8($bytes, $stream);
  5611         if (swfVersion > 6) {
  5612           glyphCount = $.glyphCount = tmp;
  5613         } else {
  5614           glyphCount = $.glyphCount = tmp & 127;
  5616         var $6 = $.entries = [];
  5617         var $7 = glyphCount;
  5618         while ($7--) {
  5619           var $8 = {};
  5620           textEntry($bytes, $stream, $8, swfVersion, tagCode, glyphBits, advanceBits);
  5621           $6.push($8);
  5624       return {
  5625         eot: eot
  5626       };
  5628     function soundEnvelope($bytes, $stream, $, swfVersion, tagCode) {
  5629       $.pos44 = readUi32($bytes, $stream);
  5630       $.volumeLeft = readUi16($bytes, $stream);
  5631       $.volumeRight = readUi16($bytes, $stream);
  5633     function soundInfo($bytes, $stream, $, swfVersion, tagCode) {
  5634       var reserved = readUb($bytes, $stream, 2);
  5635       $.stop = readUb($bytes, $stream, 1);
  5636       $.noMultiple = readUb($bytes, $stream, 1);
  5637       var hasEnvelope = $.hasEnvelope = readUb($bytes, $stream, 1);
  5638       var hasLoops = $.hasLoops = readUb($bytes, $stream, 1);
  5639       var hasOutPoint = $.hasOutPoint = readUb($bytes, $stream, 1);
  5640       var hasInPoint = $.hasInPoint = readUb($bytes, $stream, 1);
  5641       if (hasInPoint) {
  5642         $.inPoint = readUi32($bytes, $stream);
  5644       if (hasOutPoint) {
  5645         $.outPoint = readUi32($bytes, $stream);
  5647       if (hasLoops) {
  5648         $.loopCount = readUi16($bytes, $stream);
  5650       if (hasEnvelope) {
  5651         var envelopeCount = $.envelopeCount = readUi8($bytes, $stream);
  5652         var $1 = $.envelopes = [];
  5653         var $2 = envelopeCount;
  5654         while ($2--) {
  5655           var $3 = {};
  5656           soundEnvelope($bytes, $stream, $3, swfVersion, tagCode);
  5657           $1.push($3);
  5661     function button($bytes, $stream, $, swfVersion, tagCode) {
  5662       var hasFilters, blend;
  5663       var flags = readUi8($bytes, $stream);
  5664       var eob = $.eob = !flags;
  5665       if (swfVersion >= 8) {
  5666         blend = $.blend = flags >> 5 & 1;
  5667         hasFilters = $.hasFilters = flags >> 4 & 1;
  5668       } else {
  5669         blend = $.blend = 0;
  5670         hasFilters = $.hasFilters = 0;
  5672       $.stateHitTest = flags >> 3 & 1;
  5673       $.stateDown = flags >> 2 & 1;
  5674       $.stateOver = flags >> 1 & 1;
  5675       $.stateUp = flags & 1;
  5676       if (!eob) {
  5677         $.symbolId = readUi16($bytes, $stream);
  5678         $.depth = readUi16($bytes, $stream);
  5679         var $2 = $.matrix = {};
  5680         matrix($bytes, $stream, $2, swfVersion, tagCode);
  5681         if (tagCode === 34) {
  5682           var $3 = $.cxform = {};
  5683           cxform($bytes, $stream, $3, swfVersion, tagCode);
  5685         if (hasFilters) {
  5686           $.filterCount = readUi8($bytes, $stream);
  5687           var $4 = $.filters = {};
  5688           anyFilter($bytes, $stream, $4, swfVersion, tagCode);
  5690         if (blend) {
  5691           $.blendMode = readUi8($bytes, $stream);
  5694       return {
  5695         eob: eob
  5696       };
  5698     function buttonCondAction($bytes, $stream, $, swfVersion, tagCode) {
  5699       var buttonCondSize = readUi16($bytes, $stream);
  5700       var buttonConditions = readUi16($bytes, $stream);
  5701       $.idleToOverDown = buttonConditions >> 7 & 1;
  5702       $.outDownToIdle = buttonConditions >> 6 & 1;
  5703       $.outDownToOverDown = buttonConditions >> 5 & 1;
  5704       $.overDownToOutDown = buttonConditions >> 4 & 1;
  5705       $.overDownToOverUp = buttonConditions >> 3 & 1;
  5706       $.overUpToOverDown = buttonConditions >> 2 & 1;
  5707       $.overUpToIdle = buttonConditions >> 1 & 1;
  5708       $.idleToOverUp = buttonConditions & 1;
  5709       $.mouseEventFlags = buttonConditions & 511;
  5710       $.keyPress = buttonConditions >> 9 & 127;
  5711       $.overDownToIdle = buttonConditions >> 8 & 1;
  5712       if (!buttonCondSize) {
  5713         $.actionsData = readBinary($bytes, $stream, 0);
  5714       } else {
  5715         $.actionsData = readBinary($bytes, $stream, buttonCondSize - 4);
  5718     function shape($bytes, $stream, $, swfVersion, tagCode) {
  5719       var eos;
  5720       var temp = styleBits($bytes, $stream, $, swfVersion, tagCode);
  5721       var fillBits = temp.fillBits;
  5722       var lineBits = temp.lineBits;
  5723       var $4 = $.records = [];
  5724       do {
  5725         var $5 = {};
  5726         var isMorph = false;
  5727         var hasStrokes = false;
  5728         var temp = shapeRecord($bytes, $stream, $5, swfVersion, tagCode, isMorph, fillBits, lineBits, hasStrokes, bits);
  5729         eos = temp.eos;
  5730         var fillBits = temp.fillBits;
  5731         var lineBits = temp.lineBits;
  5732         var bits = bits;
  5733         $4.push($5);
  5734       } while (!eos);
  5736     return {
  5737       0: undefined,
  5738       1: undefined,
  5739       2: defineShape,
  5740       4: placeObject,
  5741       5: removeObject,
  5742       6: defineImage,
  5743       7: defineButton,
  5744       8: defineJPEGTables,
  5745       9: setBackgroundColor,
  5746       10: defineFont,
  5747       11: defineLabel,
  5748       12: doAction,
  5749       13: undefined,
  5750       14: defineSound,
  5751       15: startSound,
  5752       17: undefined,
  5753       18: soundStreamHead,
  5754       19: soundStreamBlock,
  5755       20: defineBitmap,
  5756       21: defineImage,
  5757       22: defineShape,
  5758       23: undefined,
  5759       24: undefined,
  5760       26: placeObject,
  5761       28: removeObject,
  5762       32: defineShape,
  5763       33: defineLabel,
  5764       34: defineButton,
  5765       35: defineImage,
  5766       36: defineBitmap,
  5767       37: defineText,
  5768       39: undefined,
  5769       43: frameLabel,
  5770       45: soundStreamHead,
  5771       46: defineShape,
  5772       48: defineFont2,
  5773       56: exportAssets,
  5774       57: undefined,
  5775       58: undefined,
  5776       59: doAction,
  5777       60: undefined,
  5778       61: undefined,
  5779       62: undefined,
  5780       64: undefined,
  5781       65: undefined,
  5782       66: undefined,
  5783       69: fileAttributes,
  5784       70: placeObject,
  5785       71: undefined,
  5786       72: doABC,
  5787       73: undefined,
  5788       74: undefined,
  5789       75: defineFont2,
  5790       76: symbolClass,
  5791       77: undefined,
  5792       78: defineScalingGrid,
  5793       82: doABC,
  5794       83: defineShape,
  5795       84: defineShape,
  5796       86: defineScene,
  5797       87: defineBinaryData,
  5798       88: undefined,
  5799       89: startSound,
  5800       90: defineImage,
  5801       91: undefined
  5802     };
  5803   }(this);
  5804 var readHeader = function readHeader($bytes, $stream, $, swfVersion, tagCode) {
  5805   $ || ($ = {});
  5806   var $0 = $.bbox = {};
  5807   align($bytes, $stream);
  5808   var bits = readUb($bytes, $stream, 5);
  5809   var xMin = readSb($bytes, $stream, bits);
  5810   var xMax = readSb($bytes, $stream, bits);
  5811   var yMin = readSb($bytes, $stream, bits);
  5812   var yMax = readSb($bytes, $stream, bits);
  5813   $0.xMin = xMin;
  5814   $0.xMax = xMax;
  5815   $0.yMin = yMin;
  5816   $0.yMax = yMax;
  5817   align($bytes, $stream);
  5818   var frameRateFraction = readUi8($bytes, $stream);
  5819   $.frameRate = readUi8($bytes, $stream) + frameRateFraction / 256;
  5820   $.frameCount = readUi16($bytes, $stream);
  5821   return $;
  5822 };
  5823 (function (global) {
  5824   global['tagHandler'] = tagHandler;
  5825   global['readHeader'] = readHeader;
  5826 }(this));
  5827 function readTags(context, stream, swfVersion, final, onprogress, onexception) {
  5828   var tags = context.tags;
  5829   var bytes = stream.bytes;
  5830   var lastSuccessfulPosition;
  5831   var tag = null;
  5832   if (context._readTag) {
  5833     tag = context._readTag;
  5834     context._readTag = null;
  5836   try {
  5837     while (stream.pos < stream.end) {
  5838       lastSuccessfulPosition = stream.pos;
  5839       stream.ensure(2);
  5840       var tagCodeAndLength = readUi16(bytes, stream);
  5841       if (!tagCodeAndLength) {
  5842         final = true;
  5843         break;
  5845       var tagCode = tagCodeAndLength >> 6;
  5846       var length = tagCodeAndLength & 63;
  5847       if (length === 63) {
  5848         stream.ensure(4);
  5849         length = readUi32(bytes, stream);
  5851       if (tag) {
  5852         if (tagCode === 1 && tag.code === 1) {
  5853           tag.repeat++;
  5854           stream.pos += length;
  5855           continue;
  5857         tags.push(tag);
  5858         if (onprogress && tag.id !== undefined) {
  5859           onprogress(context);
  5861         tag = null;
  5863       stream.ensure(length);
  5864       var substream = stream.substream(stream.pos, stream.pos += length);
  5865       var subbytes = substream.bytes;
  5866       var nextTag = {
  5867           code: tagCode
  5868         };
  5869       if (tagCode === 39) {
  5870         nextTag.type = 'sprite';
  5871         nextTag.id = readUi16(subbytes, substream);
  5872         nextTag.frameCount = readUi16(subbytes, substream);
  5873         nextTag.tags = [];
  5874         readTags(nextTag, substream, swfVersion, true);
  5875       } else if (tagCode === 1) {
  5876         nextTag.repeat = 1;
  5877       } else {
  5878         var handler = tagHandler[tagCode];
  5879         if (handler) {
  5880           handler(subbytes, substream, nextTag, swfVersion, tagCode);
  5883       tag = nextTag;
  5885     if (tag && final) {
  5886       tag.finalTag = true;
  5887       tags.push(tag);
  5888       if (onprogress) {
  5889         onprogress(context);
  5891     } else {
  5892       context._readTag = tag;
  5894   } catch (e) {
  5895     if (e !== StreamNoDataError) {
  5896       onexception && onexception(e);
  5897       throw e;
  5899     stream.pos = lastSuccessfulPosition;
  5900     context._readTag = tag;
  5903 function HeadTailBuffer(defaultSize) {
  5904   this.bufferSize = defaultSize || 16;
  5905   this.buffer = new Uint8Array(this.bufferSize);
  5906   this.pos = 0;
  5908 HeadTailBuffer.prototype = {
  5909   push: function (data, need) {
  5910     var bufferLengthNeed = this.pos + data.length;
  5911     if (this.bufferSize < bufferLengthNeed) {
  5912       var newBufferSize = this.bufferSize;
  5913       while (newBufferSize < bufferLengthNeed) {
  5914         newBufferSize <<= 1;
  5916       var newBuffer = new Uint8Array(newBufferSize);
  5917       if (this.bufferSize > 0) {
  5918         newBuffer.set(this.buffer);
  5920       this.buffer = newBuffer;
  5921       this.bufferSize = newBufferSize;
  5923     this.buffer.set(data, this.pos);
  5924     this.pos += data.length;
  5925     if (need)
  5926       return this.pos >= need;
  5927   },
  5928   getHead: function (size) {
  5929     return this.buffer.subarray(0, size);
  5930   },
  5931   getTail: function (offset) {
  5932     return this.buffer.subarray(offset, this.pos);
  5933   },
  5934   removeHead: function (size) {
  5935     var tail = this.getTail(size);
  5936     this.buffer = new Uint8Array(this.bufferSize);
  5937     this.buffer.set(tail);
  5938     this.pos = tail.length;
  5939   },
  5940   get arrayBuffer() {
  5941     return this.buffer.buffer;
  5942   },
  5943   get length() {
  5944     return this.pos;
  5945   },
  5946   createStream: function () {
  5947     return new Stream(this.arrayBuffer, 0, this.length);
  5949 };
  5950 function CompressedPipe(target, length) {
  5951   this.target = target;
  5952   this.length = length;
  5953   this.initialize = true;
  5954   this.buffer = new HeadTailBuffer(8096);
  5955   this.state = {
  5956     bitBuffer: 0,
  5957     bitLength: 0,
  5958     compression: {
  5959       header: null,
  5960       distanceTable: null,
  5961       literalTable: null,
  5962       sym: null,
  5963       len: null,
  5964       sym2: null
  5966   };
  5967   this.output = {
  5968     data: new Uint8Array(length),
  5969     available: 0,
  5970     completed: false
  5971   };
  5973 CompressedPipe.prototype = {
  5974   push: function (data, progressInfo) {
  5975     var buffer = this.buffer;
  5976     if (this.initialize) {
  5977       if (!buffer.push(data, 2))
  5978         return;
  5979       var headerBytes = buffer.getHead(2);
  5980       verifyDeflateHeader(headerBytes);
  5981       buffer.removeHead(2);
  5982       this.initialize = false;
  5983     } else {
  5984       buffer.push(data);
  5986     var stream = buffer.createStream();
  5987     stream.bitBuffer = this.state.bitBuffer;
  5988     stream.bitLength = this.state.bitLength;
  5989     var output = this.output;
  5990     var lastAvailable = output.available;
  5991     try {
  5992       do {
  5993         inflateBlock(stream, output, this.state.compression);
  5994       } while (stream.pos < buffer.length && !output.completed);
  5995     } catch (e) {
  5996       if (e !== InflateNoDataError)
  5997         throw e;
  5998     } finally {
  5999       this.state.bitBuffer = stream.bitBuffer;
  6000       this.state.bitLength = stream.bitLength;
  6002     buffer.removeHead(stream.pos);
  6003     this.target.push(output.data.subarray(lastAvailable, output.available), progressInfo);
  6005 };
  6006 function BodyParser(swfVersion, length, options) {
  6007   this.swf = {
  6008     swfVersion: swfVersion,
  6009     parseTime: 0
  6010   };
  6011   this.buffer = new HeadTailBuffer(32768);
  6012   this.initialize = true;
  6013   this.totalRead = 0;
  6014   this.length = length;
  6015   this.options = options;
  6017 BodyParser.prototype = {
  6018   push: function (data, progressInfo) {
  6019     if (data.length === 0)
  6020       return;
  6021     var swf = this.swf;
  6022     var swfVersion = swf.swfVersion;
  6023     var buffer = this.buffer;
  6024     var options = this.options;
  6025     var stream;
  6026     if (this.initialize) {
  6027       var PREFETCH_SIZE = 27;
  6028       if (!buffer.push(data, PREFETCH_SIZE))
  6029         return;
  6030       stream = buffer.createStream();
  6031       var bytes = stream.bytes;
  6032       readHeader(bytes, stream, swf);
  6033       var nextTagHeader = readUi16(bytes, stream);
  6034       var FILE_ATTRIBUTES_LENGTH = 4;
  6035       if (nextTagHeader == (SWF_TAG_CODE_FILE_ATTRIBUTES << 6 | FILE_ATTRIBUTES_LENGTH)) {
  6036         stream.ensure(FILE_ATTRIBUTES_LENGTH);
  6037         var substream = stream.substream(stream.pos, stream.pos += FILE_ATTRIBUTES_LENGTH);
  6038         var handler = tagHandler[SWF_TAG_CODE_FILE_ATTRIBUTES];
  6039         var fileAttributesTag = {
  6040             code: SWF_TAG_CODE_FILE_ATTRIBUTES
  6041           };
  6042         handler(substream.bytes, substream, fileAttributesTag, swfVersion, SWF_TAG_CODE_FILE_ATTRIBUTES);
  6043         swf.fileAttributes = fileAttributesTag;
  6044       } else {
  6045         stream.pos -= 2;
  6046         swf.fileAttributes = {};
  6048       if (options.onstart)
  6049         options.onstart(swf);
  6050       swf.tags = [];
  6051       this.initialize = false;
  6052     } else {
  6053       buffer.push(data);
  6054       stream = buffer.createStream();
  6056     var finalBlock = false;
  6057     if (progressInfo) {
  6058       swf.bytesLoaded = progressInfo.bytesLoaded;
  6059       swf.bytesTotal = progressInfo.bytesTotal;
  6060       finalBlock = progressInfo.bytesLoaded >= progressInfo.bytesTotal;
  6062     var readStartTime = performance.now();
  6063     readTags(swf, stream, swfVersion, finalBlock, options.onprogress, options.onexception);
  6064     swf.parseTime += performance.now() - readStartTime;
  6065     var read = stream.pos;
  6066     buffer.removeHead(read);
  6067     this.totalRead += read;
  6068     if (options.oncomplete && swf.tags[swf.tags.length - 1].finalTag) {
  6069       options.oncomplete(swf);
  6072 };
  6073 SWF.parseAsync = function swf_parseAsync(options) {
  6074   var buffer = new HeadTailBuffer();
  6075   var pipe = {
  6076       push: function (data, progressInfo) {
  6077         if (this.target !== undefined) {
  6078           return this.target.push(data, progressInfo);
  6080         if (!buffer.push(data, 8)) {
  6081           return null;
  6083         var bytes = buffer.getHead(8);
  6084         var magic1 = bytes[0];
  6085         var magic2 = bytes[1];
  6086         var magic3 = bytes[2];
  6087         if ((magic1 === 70 || magic1 === 67) && magic2 === 87 && magic3 === 83) {
  6088           var swfVersion = bytes[3];
  6089           var compressed = magic1 === 67;
  6090           parseSWF(compressed, swfVersion, progressInfo);
  6091           buffer = null;
  6092           return;
  6094         var isImage = false;
  6095         var imageType;
  6096         if (magic1 === 255 && magic2 === 216 && magic3 === 255) {
  6097           isImage = true;
  6098           imageType = 'image/jpeg';
  6099         } else if (magic1 === 137 && magic2 === 80 && magic3 === 78) {
  6100           isImage = true;
  6101           imageType = 'image/png';
  6103         if (isImage) {
  6104           parseImage(data, progressInfo.bytesTotal, imageType);
  6106         buffer = null;
  6107       },
  6108       close: function () {
  6109         if (buffer) {
  6110           var symbol = {
  6111               command: 'empty',
  6112               data: buffer.buffer.subarray(0, buffer.pos)
  6113             };
  6114           options.oncomplete && options.oncomplete(symbol);
  6116         if (this.target !== undefined && this.target.close) {
  6117           this.target.close();
  6120     };
  6121   function parseSWF(compressed, swfVersion, progressInfo) {
  6122     var stream = buffer.createStream();
  6123     stream.pos += 4;
  6124     var fileLength = readUi32(null, stream);
  6125     var bodyLength = fileLength - 8;
  6126     var target = new BodyParser(swfVersion, bodyLength, options);
  6127     if (compressed) {
  6128       target = new CompressedPipe(target, bodyLength);
  6130     target.push(buffer.getTail(8), progressInfo);
  6131     pipe['target'] = target;
  6133   function parseImage(data, bytesTotal, type) {
  6134     var buffer = new Uint8Array(bytesTotal);
  6135     buffer.set(data);
  6136     var bufferPos = data.length;
  6137     pipe['target'] = {
  6138       push: function (data) {
  6139         buffer.set(data, bufferPos);
  6140         bufferPos += data.length;
  6141       },
  6142       close: function () {
  6143         var props = {};
  6144         var chunks;
  6145         if (type == 'image/jpeg') {
  6146           chunks = parseJpegChunks(props, buffer);
  6147         } else {
  6148           chunks = [
  6149             buffer
  6150           ];
  6152         var symbol = {
  6153             type: 'image',
  6154             props: props,
  6155             data: new Blob(chunks, {
  6156               type: type
  6157             })
  6158           };
  6159         options.oncomplete && options.oncomplete(symbol);
  6161     };
  6163   return pipe;
  6164 };
  6165 SWF.parse = function (buffer, options) {
  6166   if (!options)
  6167     options = {};
  6168   var pipe = SWF.parseAsync(options);
  6169   var bytes = new Uint8Array(buffer);
  6170   var progressInfo = {
  6171       bytesLoaded: bytes.length,
  6172       bytesTotal: bytes.length
  6173     };
  6174   pipe.push(bytes, progressInfo);
  6175   pipe.close();
  6176 };
  6177 (function (global) {
  6178   global['SWF']['parse'] = SWF.parse;
  6179   global['SWF']['parseAsync'] = SWF.parseAsync;
  6180 }(this));

mercurial