diff --git a/Makefile b/Makefile index 593e6bc..2f348c1 100755 --- a/Makefile +++ b/Makefile @@ -1,40 +1,15 @@ -# -# build package & update examples -# - build: - @echo "Compiling Handlebars templates..." - @./node_modules/.bin/handlebars templates/*.handlebars -f lib/templates.js @echo "Compiling and minifying javascript..." - @mkdir -p comicbook/js/pixastic - @cat lib/vendor/pixastic/pixastic.js lib/vendor/pixastic/pixastic.effects.js lib/vendor/pixastic/pixastic.worker.js lib/vendor/handlebars.runtime-1.0.rc.1.min.js lib/templates.js lib/ComicBook.js > comicbook/js/comicbook.js - @cp lib/vendor/pixastic/pixastic.js comicbook/js/pixastic - @cp lib/vendor/pixastic/pixastic.effects.js comicbook/js/pixastic - @cp lib/vendor/pixastic/pixastic.worker.js comicbook/js/pixastic - @cp lib/vendor/pixastic/pixastic.worker.control.js comicbook/js/pixastic - @cp lib/vendor/pixastic/license-gpl-3.0.txt comicbook/js/pixastic - @cp lib/vendor/pixastic/license-mpl.txt comicbook/js/pixastic - @./node_modules/.bin/uglifyjs comicbook/js/comicbook.js --compress --mangle --screw-ie8 --output comicbook.min.js --source-map comicbook/js/comicbook.min.js.map --source-map-root ./ + @./node_modules/.bin/uglifyjs dist/js/comicbook.js --compress --mangle --screw-ie8 --output comicbook.min.js --source-map dist/js/comicbook.min.js.map --source-map-root ./ @echo "Compiling CSS..." @cat fonts/icomoon-toolbar/style.css css/reset.css css/styles.css css/toolbar.css > comicbook/comicbook.css - @./node_modules/.bin/cssmin comicbook/comicbook.css > comicbook/comicbook.min.css + @./node_modules/.bin/cssmin dist/comicbook.css > dist/comicbook.min.css @echo "Copying assets..." - @cp -r css/img comicbook/img - @cp -r icons/1_Desktop_Icons/icon_128.png comicbook/img - @cp -r icons/1_Desktop_Icons/icon_196.png comicbook/img - @cp -r fonts/icomoon-toolbar/fonts comicbook - @cp -r fonts/icomoon-toolbar/license.txt comicbook/fonts - @echo "Updating examples" - @cp -r comicbook examples + @cp -r assets/css/img dist/img + @cp -r assets/icons/1_Desktop_Icons/icon_128.png comicbook/img + @cp -r assets/icons/1_Desktop_Icons/icon_196.png comicbook/img + @cp -r assets/fonts/icomoon-toolbar/fonts comicbook + @cp -r assets/fonts/icomoon-toolbar/license.txt comicbook/fonts @echo "Done" - -# -# remove prior builds -# - -clean: - @rm -r comicbook - @rm -r examples/comicbook - @rm lib/templates.js diff --git a/TODO b/TODO deleted file mode 100644 index 378b9f2..0000000 --- a/TODO +++ /dev/null @@ -1,21 +0,0 @@ -Fo sho: - - remember reading position / applied effects (per comic) - - offline mode via cache manifest - - firefox marketplace - - create onclose callback, show close button only if set - - trigger preload if requesting valid but not loaded images (can happen if network was interupted) - - loading and generally hackiness of pointer is buggy, fix. - - check for html5 feature support where used: diveintohtml5.org/everything.html or www.modernizr.com - -Nice 2 have: - - remove image inhancement lag when navigating by pre-applying enhancements to other pages - - jump to page? - - make page draggable with the cursor - - enable menu items via config, allow for custom items - - split out classes into seperate files - - thumbnail browser - - refactor so we are not using all these loose shared variables and other nastyness - - use custom event emitters instead of hacky code - - properly bind 'this' so we don't have to keep using 'self' - - allow toolbar to be hidden on mobile (maybe show a small translucent button that opens the toolbar when clicked) - - enhancement progress bar diff --git a/css/img/loading.gif b/app/css/img/loading.gif similarity index 100% rename from css/img/loading.gif rename to app/css/img/loading.gif diff --git a/css/reset.css b/app/css/reset.css similarity index 100% rename from css/reset.css rename to app/css/reset.css diff --git a/css/styles.css b/app/css/styles.css similarity index 100% rename from css/styles.css rename to app/css/styles.css diff --git a/css/toolbar.css b/app/css/toolbar.css similarity index 100% rename from css/toolbar.css rename to app/css/toolbar.css diff --git a/lib/vendor/pixastic/README b/app/index.js old mode 100755 new mode 100644 similarity index 100% rename from lib/vendor/pixastic/README rename to app/index.js diff --git a/templates/loadingOverlay.handlebars b/app/view/template/loadingOverlay.handlebars similarity index 100% rename from templates/loadingOverlay.handlebars rename to app/view/template/loadingOverlay.handlebars diff --git a/templates/navigateLeft.handlebars b/app/view/template/navigateLeft.handlebars similarity index 100% rename from templates/navigateLeft.handlebars rename to app/view/template/navigateLeft.handlebars diff --git a/templates/navigateRight.handlebars b/app/view/template/navigateRight.handlebars similarity index 100% rename from templates/navigateRight.handlebars rename to app/view/template/navigateRight.handlebars diff --git a/templates/progressbar.handlebars b/app/view/template/progressbar.handlebars similarity index 100% rename from templates/progressbar.handlebars rename to app/view/template/progressbar.handlebars diff --git a/templates/toolbar.handlebars b/app/view/template/toolbar.handlebars similarity index 100% rename from templates/toolbar.handlebars rename to app/view/template/toolbar.handlebars diff --git a/fonts/icomoon-toolbar/Read Me.txt b/assets/fonts/icomoon-toolbar/Read Me.txt similarity index 100% rename from fonts/icomoon-toolbar/Read Me.txt rename to assets/fonts/icomoon-toolbar/Read Me.txt diff --git a/fonts/icomoon-toolbar/fonts/toolbar.dev.svg b/assets/fonts/icomoon-toolbar/fonts/toolbar.dev.svg similarity index 100% rename from fonts/icomoon-toolbar/fonts/toolbar.dev.svg rename to assets/fonts/icomoon-toolbar/fonts/toolbar.dev.svg diff --git a/fonts/icomoon-toolbar/fonts/toolbar.eot b/assets/fonts/icomoon-toolbar/fonts/toolbar.eot similarity index 100% rename from fonts/icomoon-toolbar/fonts/toolbar.eot rename to assets/fonts/icomoon-toolbar/fonts/toolbar.eot diff --git a/fonts/icomoon-toolbar/fonts/toolbar.svg b/assets/fonts/icomoon-toolbar/fonts/toolbar.svg similarity index 100% rename from fonts/icomoon-toolbar/fonts/toolbar.svg rename to assets/fonts/icomoon-toolbar/fonts/toolbar.svg diff --git a/fonts/icomoon-toolbar/fonts/toolbar.ttf b/assets/fonts/icomoon-toolbar/fonts/toolbar.ttf similarity index 100% rename from fonts/icomoon-toolbar/fonts/toolbar.ttf rename to assets/fonts/icomoon-toolbar/fonts/toolbar.ttf diff --git a/fonts/icomoon-toolbar/fonts/toolbar.woff b/assets/fonts/icomoon-toolbar/fonts/toolbar.woff similarity index 100% rename from fonts/icomoon-toolbar/fonts/toolbar.woff rename to assets/fonts/icomoon-toolbar/fonts/toolbar.woff diff --git a/fonts/icomoon-toolbar/index.html b/assets/fonts/icomoon-toolbar/index.html similarity index 100% rename from fonts/icomoon-toolbar/index.html rename to assets/fonts/icomoon-toolbar/index.html diff --git a/fonts/icomoon-toolbar/license.txt b/assets/fonts/icomoon-toolbar/license.txt similarity index 100% rename from fonts/icomoon-toolbar/license.txt rename to assets/fonts/icomoon-toolbar/license.txt diff --git a/fonts/icomoon-toolbar/lte-ie7.js b/assets/fonts/icomoon-toolbar/lte-ie7.js similarity index 100% rename from fonts/icomoon-toolbar/lte-ie7.js rename to assets/fonts/icomoon-toolbar/lte-ie7.js diff --git a/fonts/icomoon-toolbar/style.css b/assets/fonts/icomoon-toolbar/style.css similarity index 100% rename from fonts/icomoon-toolbar/style.css rename to assets/fonts/icomoon-toolbar/style.css diff --git a/icons/1_Desktop_Icons/icon_016.png b/assets/icons/1_Desktop_Icons/icon_016.png similarity index 100% rename from icons/1_Desktop_Icons/icon_016.png rename to assets/icons/1_Desktop_Icons/icon_016.png diff --git a/icons/1_Desktop_Icons/icon_032.png b/assets/icons/1_Desktop_Icons/icon_032.png similarity index 100% rename from icons/1_Desktop_Icons/icon_032.png rename to assets/icons/1_Desktop_Icons/icon_032.png diff --git a/icons/1_Desktop_Icons/icon_048.png b/assets/icons/1_Desktop_Icons/icon_048.png similarity index 100% rename from icons/1_Desktop_Icons/icon_048.png rename to assets/icons/1_Desktop_Icons/icon_048.png diff --git a/icons/1_Desktop_Icons/icon_128.png b/assets/icons/1_Desktop_Icons/icon_128.png similarity index 100% rename from icons/1_Desktop_Icons/icon_128.png rename to assets/icons/1_Desktop_Icons/icon_128.png diff --git a/icons/1_Desktop_Icons/icon_196.png b/assets/icons/1_Desktop_Icons/icon_196.png similarity index 100% rename from icons/1_Desktop_Icons/icon_196.png rename to assets/icons/1_Desktop_Icons/icon_196.png diff --git a/icons/1_Desktop_Icons/icon_512.png b/assets/icons/1_Desktop_Icons/icon_512.png similarity index 100% rename from icons/1_Desktop_Icons/icon_512.png rename to assets/icons/1_Desktop_Icons/icon_512.png diff --git a/icons/2_Android_Icons/icon_036.png b/assets/icons/2_Android_Icons/icon_036.png similarity index 100% rename from icons/2_Android_Icons/icon_036.png rename to assets/icons/2_Android_Icons/icon_036.png diff --git a/icons/2_Android_Icons/icon_048.png b/assets/icons/2_Android_Icons/icon_048.png similarity index 100% rename from icons/2_Android_Icons/icon_048.png rename to assets/icons/2_Android_Icons/icon_048.png diff --git a/icons/2_Android_Icons/icon_072.png b/assets/icons/2_Android_Icons/icon_072.png similarity index 100% rename from icons/2_Android_Icons/icon_072.png rename to assets/icons/2_Android_Icons/icon_072.png diff --git a/icons/3_iPhone_Icons/icon_029.png b/assets/icons/3_iPhone_Icons/icon_029.png similarity index 100% rename from icons/3_iPhone_Icons/icon_029.png rename to assets/icons/3_iPhone_Icons/icon_029.png diff --git a/icons/3_iPhone_Icons/icon_048.png b/assets/icons/3_iPhone_Icons/icon_048.png similarity index 100% rename from icons/3_iPhone_Icons/icon_048.png rename to assets/icons/3_iPhone_Icons/icon_048.png diff --git a/icons/3_iPhone_Icons/icon_057.png b/assets/icons/3_iPhone_Icons/icon_057.png similarity index 100% rename from icons/3_iPhone_Icons/icon_057.png rename to assets/icons/3_iPhone_Icons/icon_057.png diff --git a/icons/3_iPhone_Icons/icon_072.png b/assets/icons/3_iPhone_Icons/icon_072.png similarity index 100% rename from icons/3_iPhone_Icons/icon_072.png rename to assets/icons/3_iPhone_Icons/icon_072.png diff --git a/icons/3_iPhone_Icons/icon_512.png b/assets/icons/3_iPhone_Icons/icon_512.png similarity index 100% rename from icons/3_iPhone_Icons/icon_512.png rename to assets/icons/3_iPhone_Icons/icon_512.png diff --git a/examples/basic.html b/examples/basic.html deleted file mode 100755 index c4958dc..0000000 --- a/examples/basic.html +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - - - - Comic Book Reader - - - - - - - - - - - - - - - diff --git a/examples/bitjs/README.txt b/examples/bitjs/README.txt deleted file mode 100755 index 9de777a..0000000 --- a/examples/bitjs/README.txt +++ /dev/null @@ -1 +0,0 @@ -bitjs: Binary Tools for JavaScript diff --git a/examples/bitjs/archive.js b/examples/bitjs/archive.js deleted file mode 100755 index ae6f1e5..0000000 --- a/examples/bitjs/archive.js +++ /dev/null @@ -1,340 +0,0 @@ -/** - * archive.js - * - * Provides base functionality for unarchiving. - * - * Licensed under the MIT License - * - * Copyright(c) 2011 Google Inc. - */ - -var bitjs = bitjs || {}; -bitjs.archive = bitjs.archive || {}; - -(function() { - -// =========================================================================== -// Stolen from Closure because it's the best way to do Java-like inheritance. -bitjs.base = function(me, opt_methodName, var_args) { - var caller = arguments.callee.caller; - if (caller.superClass_) { - // This is a constructor. Call the superclass constructor. - return caller.superClass_.constructor.apply( - me, Array.prototype.slice.call(arguments, 1)); - } - - var args = Array.prototype.slice.call(arguments, 2); - var foundCaller = false; - for (var ctor = me.constructor; - ctor; ctor = ctor.superClass_ && ctor.superClass_.constructor) { - if (ctor.prototype[opt_methodName] === caller) { - foundCaller = true; - } else if (foundCaller) { - return ctor.prototype[opt_methodName].apply(me, args); - } - } - - // If we did not find the caller in the prototype chain, - // then one of two things happened: - // 1) The caller is an instance method. - // 2) This method was not called by the right caller. - if (me[opt_methodName] === caller) { - return me.constructor.prototype[opt_methodName].apply(me, args); - } else { - throw Error( - 'goog.base called from a method of one name ' + - 'to a method of a different name'); - } -}; -bitjs.inherits = function(childCtor, parentCtor) { - /** @constructor */ - function tempCtor() {}; - tempCtor.prototype = parentCtor.prototype; - childCtor.superClass_ = parentCtor.prototype; - childCtor.prototype = new tempCtor(); - childCtor.prototype.constructor = childCtor; -}; -// =========================================================================== - -/** - * An unarchive event. - * - * @param {string} type The event type. - * @constructor - */ -bitjs.archive.UnarchiveEvent = function(type) { - /** - * The event type. - * - * @type {string} - */ - this.type = type; -}; - -/** - * The UnarchiveEvent types. - */ -bitjs.archive.UnarchiveEvent.Type = { - START: 'start', - PROGRESS: 'progress', - EXTRACT: 'extract', - FINISH: 'finish', - INFO: 'info', - ERROR: 'error' -}; - -/** - * Useful for passing info up to the client (for debugging). - * - * @param {string} msg The info message. - */ -bitjs.archive.UnarchiveInfoEvent = function(msg) { - bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.INFO); - - /** - * The information message. - * - * @type {string} - */ - this.msg = msg; -}; -bitjs.inherits(bitjs.archive.UnarchiveInfoEvent, bitjs.archive.UnarchiveEvent); - -/** - * An unrecoverable error has occured. - * - * @param {string} msg The error message. - */ -bitjs.archive.UnarchiveErrorEvent = function(msg) { - bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.ERROR); - - /** - * The information message. - * - * @type {string} - */ - this.msg = msg; -}; -bitjs.inherits(bitjs.archive.UnarchiveErrorEvent, bitjs.archive.UnarchiveEvent); - -/** - * Start event. - * - * @param {string} msg The info message. - */ -bitjs.archive.UnarchiveStartEvent = function() { - bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.START); -}; -bitjs.inherits(bitjs.archive.UnarchiveStartEvent, bitjs.archive.UnarchiveEvent); - -/** - * Finish event. - * - * @param {string} msg The info message. - */ -bitjs.archive.UnarchiveFinishEvent = function() { - bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.FINISH); -}; -bitjs.inherits(bitjs.archive.UnarchiveFinishEvent, bitjs.archive.UnarchiveEvent); - -/** - * Progress event. - */ -bitjs.archive.UnarchiveProgressEvent = function( - currentFilename, - currentFileNumber, - currentBytesUnarchivedInFile, - currentBytesUnarchived, - totalUncompressedBytesInArchive, - totalFilesInArchive) { - bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.PROGRESS); - - this.currentFilename = currentFilename; - this.currentFileNumber = currentFileNumber; - this.currentBytesUnarchivedInFile = currentBytesUnarchivedInFile; - this.totalFilesInArchive = totalFilesInArchive; - this.currentBytesUnarchived = currentBytesUnarchived; - this.totalUncompressedBytesInArchive = totalUncompressedBytesInArchive; -}; -bitjs.inherits(bitjs.archive.UnarchiveProgressEvent, bitjs.archive.UnarchiveEvent); - -/** - * All extracted files returned by an Unarchiver will implement - * the following interface: - * - * interface UnarchivedFile { - * string filename - * TypedArray fileData - * } - * - */ - -/** - * Extract event. - */ -bitjs.archive.UnarchiveExtractEvent = function(unarchivedFile) { - bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.EXTRACT); - - /** - * @type {UnarchivedFile} - */ - this.unarchivedFile = unarchivedFile; -}; -bitjs.inherits(bitjs.archive.UnarchiveExtractEvent, bitjs.archive.UnarchiveEvent); - - -/** - * Base class for all Unarchivers. - * - * @param {ArrayBuffer} arrayBuffer The Array Buffer. - * @param {string} opt_pathToBitJS Optional string for where the BitJS files are located. - * @constructor - */ -bitjs.archive.Unarchiver = function(arrayBuffer, opt_pathToBitJS) { - /** - * The ArrayBuffer object. - * @type {ArrayBuffer} - * @protected - */ - this.ab = arrayBuffer; - - /** - * The path to the BitJS files. - * @type {string} - * @private - */ - this.pathToBitJS_ = opt_pathToBitJS || ''; - - /** - * A map from event type to an array of listeners. - * @type {Map.} - */ - this.listeners_ = {}; - for (var type in bitjs.archive.UnarchiveEvent.Type) { - this.listeners_[bitjs.archive.UnarchiveEvent.Type[type]] = []; - } -}; - -/** - * Private web worker initialized during start(). - * @type {Worker} - * @private - */ -bitjs.archive.Unarchiver.prototype.worker_ = null; - -/** - * This method must be overridden by the subclass to return the script filename. - * @return {string} The script filename. - * @protected. - */ -bitjs.archive.Unarchiver.prototype.getScriptFileName = function() { - throw 'Subclasses of AbstractUnarchiver must overload getScriptFileName()'; -}; - -/** - * Adds an event listener for UnarchiveEvents. - * - * @param {string} Event type. - * @param {function} An event handler function. - */ -bitjs.archive.Unarchiver.prototype.addEventListener = function(type, listener) { - if (type in this.listeners_) { - if (this.listeners_[type].indexOf(listener) == -1) { - this.listeners_[type].push(listener); - } - } -}; - -/** - * Removes an event listener. - * - * @param {string} Event type. - * @param {EventListener|function} An event listener or handler function. - */ -bitjs.archive.Unarchiver.prototype.removeEventListener = function(type, listener) { - if (type in this.listeners_) { - var index = this.listeners_[type].indexOf(listener); - if (index != -1) { - this.listeners_[type].splice(index, 1); - } - } -}; - -/** - * Receive an event and pass it to the listener functions. - * - * @param {bitjs.archive.UnarchiveEvent} e - * @private - */ -bitjs.archive.Unarchiver.prototype.handleWorkerEvent_ = function(e) { - if ((e instanceof bitjs.archive.UnarchiveEvent || e.type) && - this.listeners_[e.type] instanceof Array) { - this.listeners_[e.type].forEach(function (listener) { listener(e) }); - } else { - console.log(e); - } -}; - -/** - * Starts the unarchive in a separate Web Worker thread and returns immediately. - */ - bitjs.archive.Unarchiver.prototype.start = function() { - var me = this; - var scriptFileName = this.pathToBitJS_ + this.getScriptFileName(); - if (scriptFileName) { - this.worker_ = new Worker(scriptFileName); - - this.worker_.onerror = function(e) { - console.log('Worker error: message = ' + e.message); - throw e; - }; - - this.worker_.onmessage = function(e) { - if (typeof e.data == 'string') { - // Just log any strings the workers pump our way. - console.log(e.data); - } else { - // Assume that it is an UnarchiveEvent. Some browsers preserve the 'type' - // so that instanceof UnarchiveEvent returns true, but others do not. - me.handleWorkerEvent_(e.data); - } - }; - - this.worker_.postMessage({file: this.ab}); - } -} - -/** - * Unzipper - * @extends {bitjs.archive.Unarchiver} - * @constructor - */ -bitjs.archive.Unzipper = function(arrayBuffer, opt_pathToBitJS) { - bitjs.base(this, arrayBuffer, opt_pathToBitJS); -}; -bitjs.inherits(bitjs.archive.Unzipper, bitjs.archive.Unarchiver); -bitjs.archive.Unzipper.prototype.getScriptFileName = function() { return 'unzip.js' }; - -/** - * Unrarrer - * @extends {bitjs.archive.Unarchiver} - * @constructor - */ -bitjs.archive.Unrarrer = function(arrayBuffer, opt_pathToBitJS) { - bitjs.base(this, arrayBuffer, opt_pathToBitJS); -}; -bitjs.inherits(bitjs.archive.Unrarrer, bitjs.archive.Unarchiver); -bitjs.archive.Unrarrer.prototype.getScriptFileName = function() { return 'unrar.js' }; - -/** - * Untarrer - * @extends {bitjs.archive.Unarchiver} - * @constructor - */ -bitjs.archive.Untarrer = function(arrayBuffer, opt_pathToBitJS) { - bitjs.base(this, arrayBuffer, opt_pathToBitJS); -}; -bitjs.inherits(bitjs.archive.Untarrer, bitjs.archive.Unarchiver); -bitjs.archive.Untarrer.prototype.getScriptFileName = function() { return 'untar.js' }; - -})(); \ No newline at end of file diff --git a/examples/bitjs/drive.html b/examples/bitjs/drive.html deleted file mode 100755 index 1ad7b7d..0000000 --- a/examples/bitjs/drive.html +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - - - - - diff --git a/examples/bitjs/io.js b/examples/bitjs/io.js deleted file mode 100755 index 0abd637..0000000 --- a/examples/bitjs/io.js +++ /dev/null @@ -1,310 +0,0 @@ -/* - * io.js - * - * Provides readers for bit/byte streams (reading) and a byte buffer (writing). - * - * Licensed under the MIT License - * - * Copyright(c) 2011 Google Inc. - * Copyright(c) 2011 antimatter15 - */ - -var bitjs = bitjs || {}; -bitjs.io = bitjs.io || {}; - -(function() { - -// mask for getting the Nth bit (zero-based) -bitjs.BIT = [ 0x01, 0x02, 0x04, 0x08, - 0x10, 0x20, 0x40, 0x80, - 0x100, 0x200, 0x400, 0x800, - 0x1000, 0x2000, 0x4000, 0x8000]; - -// mask for getting N number of bits (0-8) -var BITMASK = [0, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF ]; - - -/** - * This bit stream peeks and consumes bits out of a binary stream. - * - * {ArrayBuffer} ab An ArrayBuffer object or a Uint8Array. - * {boolean} rtl Whether the stream reads bits from the byte starting - * from bit 7 to 0 (true) or bit 0 to 7 (false). - * {Number} opt_offset The offset into the ArrayBuffer - * {Number} opt_length The length of this BitStream - */ -bitjs.io.BitStream = function(ab, rtl, opt_offset, opt_length) { - if (!ab || !ab.toString || ab.toString() !== "[object ArrayBuffer]") { - throw "Error! BitArray constructed with an invalid ArrayBuffer object"; - } - - var offset = opt_offset || 0; - var length = opt_length || ab.byteLength; - this.bytes = new Uint8Array(ab, offset, length); - this.bytePtr = 0; // tracks which byte we are on - this.bitPtr = 0; // tracks which bit we are on (can have values 0 through 7) - this.peekBits = rtl ? this.peekBits_rtl : this.peekBits_ltr; -}; - -// byte0 byte1 byte2 byte3 -// 7......0 | 7......0 | 7......0 | 7......0 -// -// The bit pointer starts at bit0 of byte0 and moves left until it reaches -// bit7 of byte0, then jumps to bit0 of byte1, etc. -bitjs.io.BitStream.prototype.peekBits_ltr = function(n, movePointers) { - if (n <= 0 || typeof n != typeof 1) { - return 0; - } - - var movePointers = movePointers || false, - bytePtr = this.bytePtr, - bitPtr = this.bitPtr, - result = 0, - bitsIn = 0, - bytes = this.bytes; - - // keep going until we have no more bits left to peek at - // TODO: Consider putting all bits from bytes we will need into a variable and then - // shifting/masking it to just extract the bits we want. - // This could be considerably faster when reading more than 3 or 4 bits at a time. - while (n > 0) { - if (bytePtr >= bytes.length) { - throw "Error! Overflowed the bit stream! n=" + n + ", bytePtr=" + bytePtr + ", bytes.length=" + - bytes.length + ", bitPtr=" + bitPtr; - return -1; - } - - var numBitsLeftInThisByte = (8 - bitPtr); - if (n >= numBitsLeftInThisByte) { - var mask = (BITMASK[numBitsLeftInThisByte] << bitPtr); - result |= (((bytes[bytePtr] & mask) >> bitPtr) << bitsIn); - - bytePtr++; - bitPtr = 0; - bitsIn += numBitsLeftInThisByte; - n -= numBitsLeftInThisByte; - } - else { - var mask = (BITMASK[n] << bitPtr); - result |= (((bytes[bytePtr] & mask) >> bitPtr) << bitsIn); - - bitPtr += n; - bitsIn += n; - n = 0; - } - } - - if (movePointers) { - this.bitPtr = bitPtr; - this.bytePtr = bytePtr; - } - - return result; -}; - -// byte0 byte1 byte2 byte3 -// 7......0 | 7......0 | 7......0 | 7......0 -// -// The bit pointer starts at bit7 of byte0 and moves right until it reaches -// bit0 of byte0, then goes to bit7 of byte1, etc. -bitjs.io.BitStream.prototype.peekBits_rtl = function(n, movePointers) { - if (n <= 0 || typeof n != typeof 1) { - return 0; - } - - var movePointers = movePointers || false, - bytePtr = this.bytePtr, - bitPtr = this.bitPtr, - result = 0, - bytes = this.bytes; - - // keep going until we have no more bits left to peek at - // TODO: Consider putting all bits from bytes we will need into a variable and then - // shifting/masking it to just extract the bits we want. - // This could be considerably faster when reading more than 3 or 4 bits at a time. - while (n > 0) { - - if (bytePtr >= bytes.length) { - throw "Error! Overflowed the bit stream! n=" + n + ", bytePtr=" + bytePtr + ", bytes.length=" + - bytes.length + ", bitPtr=" + bitPtr; - return -1; - } - - var numBitsLeftInThisByte = (8 - bitPtr); - if (n >= numBitsLeftInThisByte) { - result <<= numBitsLeftInThisByte; - result |= (BITMASK[numBitsLeftInThisByte] & bytes[bytePtr]); - bytePtr++; - bitPtr = 0; - n -= numBitsLeftInThisByte; - } - else { - result <<= n; - result |= ((bytes[bytePtr] & (BITMASK[n] << (8 - n - bitPtr))) >> (8 - n - bitPtr)); - - bitPtr += n; - n = 0; - } - } - - if (movePointers) { - this.bitPtr = bitPtr; - this.bytePtr = bytePtr; - } - - return result; -}; - -//some voodoo magic -bitjs.io.BitStream.prototype.getBits = function() { - return (((((this.bytes[this.bytePtr] & 0xff) << 16) + - ((this.bytes[this.bytePtr+1] & 0xff) << 8) + - ((this.bytes[this.bytePtr+2] & 0xff))) >>> (8-this.bitPtr)) & 0xffff); -}; - -bitjs.io.BitStream.prototype.readBits = function(n) { - return this.peekBits(n, true); -}; - -// This returns n bytes as a sub-array, advancing the pointer if movePointers -// is true. -// Only use this for uncompressed blocks as this throws away remaining bits in -// the current byte. -bitjs.io.BitStream.prototype.peekBytes = function(n, movePointers) { - if (n <= 0 || typeof n != typeof 1) { - return 0; - } - - // from http://tools.ietf.org/html/rfc1951#page-11 - // "Any bits of input up to the next byte boundary are ignored." - while (this.bitPtr != 0) { - this.readBits(1); - } - - var movePointers = movePointers || false; - var bytePtr = this.bytePtr, - bitPtr = this.bitPtr; - - var result = this.bytes.subarray(bytePtr, bytePtr + n); - - if (movePointers) { - this.bytePtr += n; - } - - return result; -}; - -bitjs.io.BitStream.prototype.readBytes = function( n ) { - return this.peekBytes(n, true); -}; - - -/** - * This object allows you to peek and consume bytes as numbers and strings - * out of an ArrayBuffer. - * - * This object is much easier to write than the above BitStream since - * everything is byte-aligned. - * - * {ArrayBuffer} ab The ArrayBuffer object. - * {Number} opt_offset The offset into the ArrayBuffer - * {Number} opt_length The length of this BitStream - */ -bitjs.io.ByteStream = function(ab, opt_offset, opt_length) { - var offset = opt_offset || 0; - var length = opt_length || ab.byteLength; - this.bytes = new Uint8Array(ab, offset, length); - this.ptr = 0; -}; - -// peeks at the next n bytes as an unsigned number but does not advance the pointer -// TODO: This apparently cannot read more than 4 bytes as a number? -bitjs.io.ByteStream.prototype.peekNumber = function( n ) { - // TODO: return error if n would go past the end of the stream? - if (n <= 0 || typeof n != typeof 1) - return -1; - - var result = 0; - // read from last byte to first byte and roll them in - var curByte = this.ptr + n - 1; - while (curByte >= this.ptr) { - result <<= 8; - result |= this.bytes[curByte]; - --curByte; - } - return result; -}; - -// returns the next n bytes as an unsigned number (or -1 on error) -// and advances the stream pointer n bytes -bitjs.io.ByteStream.prototype.readNumber = function( n ) { - var num = this.peekNumber( n ); - this.ptr += n; - return num; -}; - -// This returns n bytes as a sub-array, advancing the pointer if movePointers -// is true. -bitjs.io.ByteStream.prototype.peekBytes = function(n, movePointers) { - if (n <= 0 || typeof n != typeof 1) { - return 0; - } - - var result = this.bytes.subarray(this.ptr, this.ptr + n); - - if (movePointers) { - this.ptr += n; - } - - return result; -}; - -bitjs.io.ByteStream.prototype.readBytes = function( n ) { - return this.peekBytes(n, true); -}; - -// peeks at the next n bytes as a string but does not advance the pointer -bitjs.io.ByteStream.prototype.peekString = function( n ) { - if (n <= 0 || typeof n != typeof 1) { - return 0; - } - - var result = ""; - for (var p = this.ptr, end = this.ptr + n; p < end; ++p) { - result += String.fromCharCode(this.bytes[p]); - } - return result; -}; - -// returns the next n bytes as a string -// and advances the stream pointer n bytes -bitjs.io.ByteStream.prototype.readString = function(n) { - var strToReturn = this.peekString(n); - this.ptr += n; - return strToReturn; -}; - - -/** - * A write-only Byte buffer which uses a Uint8 Typed Array as a backing store. - */ -bitjs.io.ByteBuffer = function(numBytes) { - if (typeof numBytes != typeof 1 || numBytes <= 0) { - throw "Error! ByteBuffer initialized with '" + numBytes + "'"; - } - this.data = new Uint8Array(numBytes); - this.ptr = 0; -}; - -bitjs.io.ByteBuffer.prototype.insertByte = function(b) { - // TODO: throw if byte is invalid? - this.data[this.ptr++] = b; -}; - -bitjs.io.ByteBuffer.prototype.insertBytes = function(bytes) { - // TODO: throw if bytes is invalid? - this.data.set(bytes, this.ptr); - this.ptr += bytes.length; -}; - -})(); diff --git a/examples/bitjs/unrar.js b/examples/bitjs/unrar.js deleted file mode 100755 index e7133dc..0000000 --- a/examples/bitjs/unrar.js +++ /dev/null @@ -1,906 +0,0 @@ -/** - * unrar.js - * - * Copyright(c) 2011 Google Inc. - * Copyright(c) 2011 antimatter15 - * - * Reference Documentation: - * - * http://kthoom.googlecode.com/hg/docs/unrar.html - */ - -// This file expects to be invoked as a Worker (see onmessage below). -importScripts('io.js'); -importScripts('archive.js'); - -// Progress variables. -var currentFilename = ""; -var currentFileNumber = 0; -var currentBytesUnarchivedInFile = 0; -var currentBytesUnarchived = 0; -var totalUncompressedBytesInArchive = 0; -var totalFilesInArchive = 0; - -// Helper functions. -var info = function(str) { - postMessage(new bitjs.archive.UnarchiveInfoEvent(str)); -}; -var err = function(str) { - postMessage(new bitjs.archive.UnarchiveErrorEvent(str)); -}; -var postProgress = function() { - postMessage(new bitjs.archive.UnarchiveProgressEvent( - currentFilename, - currentFileNumber, - currentBytesUnarchivedInFile, - currentBytesUnarchived, - totalUncompressedBytesInArchive, - totalFilesInArchive)); -}; - -// shows a byte value as its hex representation -var nibble = "0123456789ABCDEF"; -var byteValueToHexString = function(num) { - return nibble[num>>4] + nibble[num&0xF]; -}; -var twoByteValueToHexString = function(num) { - return nibble[(num>>12)&0xF] + nibble[(num>>8)&0xF] + nibble[(num>>4)&0xF] + nibble[num&0xF]; -}; - - -// Volume Types -var MARK_HEAD = 0x72, - MAIN_HEAD = 0x73, - FILE_HEAD = 0x74, - COMM_HEAD = 0x75, - AV_HEAD = 0x76, - SUB_HEAD = 0x77, - PROTECT_HEAD = 0x78, - SIGN_HEAD = 0x79, - NEWSUB_HEAD = 0x7a, - ENDARC_HEAD = 0x7b; - -// bstream is a bit stream -var RarVolumeHeader = function(bstream) { - - var headPos = bstream.bytePtr; - // byte 1,2 - info("Rar Volume Header @"+bstream.bytePtr); - - this.crc = bstream.readBits(16); - info(" crc=" + this.crc); - - // byte 3 - this.headType = bstream.readBits(8); - info(" headType=" + this.headType); - - // Get flags - // bytes 4,5 - this.flags = {}; - this.flags.value = bstream.peekBits(16); - - info(" flags=" + twoByteValueToHexString(this.flags.value)); - switch (this.headType) { - case MAIN_HEAD: - this.flags.MHD_VOLUME = !!bstream.readBits(1); - this.flags.MHD_COMMENT = !!bstream.readBits(1); - this.flags.MHD_LOCK = !!bstream.readBits(1); - this.flags.MHD_SOLID = !!bstream.readBits(1); - this.flags.MHD_PACK_COMMENT = !!bstream.readBits(1); - this.flags.MHD_NEWNUMBERING = this.flags.MHD_PACK_COMMENT; - this.flags.MHD_AV = !!bstream.readBits(1); - this.flags.MHD_PROTECT = !!bstream.readBits(1); - this.flags.MHD_PASSWORD = !!bstream.readBits(1); - this.flags.MHD_FIRSTVOLUME = !!bstream.readBits(1); - this.flags.MHD_ENCRYPTVER = !!bstream.readBits(1); - bstream.readBits(6); // unused - break; - case FILE_HEAD: - this.flags.LHD_SPLIT_BEFORE = !!bstream.readBits(1); // 0x0001 - this.flags.LHD_SPLIT_AFTER = !!bstream.readBits(1); // 0x0002 - this.flags.LHD_PASSWORD = !!bstream.readBits(1); // 0x0004 - this.flags.LHD_COMMENT = !!bstream.readBits(1); // 0x0008 - this.flags.LHD_SOLID = !!bstream.readBits(1); // 0x0010 - bstream.readBits(3); // unused - this.flags.LHD_LARGE = !!bstream.readBits(1); // 0x0100 - this.flags.LHD_UNICODE = !!bstream.readBits(1); // 0x0200 - this.flags.LHD_SALT = !!bstream.readBits(1); // 0x0400 - this.flags.LHD_VERSION = !!bstream.readBits(1); // 0x0800 - this.flags.LHD_EXTTIME = !!bstream.readBits(1); // 0x1000 - this.flags.LHD_EXTFLAGS = !!bstream.readBits(1); // 0x2000 - bstream.readBits(2); // unused - info(" LHD_SPLIT_BEFORE = " + this.flags.LHD_SPLIT_BEFORE); - break; - default: - bstream.readBits(16); - } - - // byte 6,7 - this.headSize = bstream.readBits(16); - info(" headSize=" + this.headSize); - switch (this.headType) { - case MAIN_HEAD: - this.highPosAv = bstream.readBits(16); - this.posAv = bstream.readBits(32); - if (this.flags.MHD_ENCRYPTVER) { - this.encryptVer = bstream.readBits(8); - } - info("Found MAIN_HEAD with highPosAv=" + this.highPosAv + ", posAv=" + this.posAv); - break; - case FILE_HEAD: - this.packSize = bstream.readBits(32); - this.unpackedSize = bstream.readBits(32); - this.hostOS = bstream.readBits(8); - this.fileCRC = bstream.readBits(32); - this.fileTime = bstream.readBits(32); - this.unpVer = bstream.readBits(8); - this.method = bstream.readBits(8); - this.nameSize = bstream.readBits(16); - this.fileAttr = bstream.readBits(32); - - if (this.flags.LHD_LARGE) { - info("Warning: Reading in LHD_LARGE 64-bit size values"); - this.HighPackSize = bstream.readBits(32); - this.HighUnpSize = bstream.readBits(32); - } else { - this.HighPackSize = 0; - this.HighUnpSize = 0; - if (this.unpackedSize == 0xffffffff) { - this.HighUnpSize = 0x7fffffff - this.unpackedSize = 0xffffffff; - } - } - this.fullPackSize = 0; - this.fullUnpackSize = 0; - this.fullPackSize |= this.HighPackSize; - this.fullPackSize <<= 32; - this.fullPackSize |= this.packSize; - - // read in filename - - this.filename = bstream.readBytes(this.nameSize); - for (var _i = 0, _s = ''; _i < this.filename.length; _i++) { - _s += String.fromCharCode(this.filename[_i]); - } - - this.filename = _s; - - if (this.flags.LHD_SALT) { - info("Warning: Reading in 64-bit salt value"); - this.salt = bstream.readBits(64); // 8 bytes - } - - if (this.flags.LHD_EXTTIME) { - // 16-bit flags - var extTimeFlags = bstream.readBits(16); - - // this is adapted straight out of arcread.cpp, Archive::ReadHeader() - for (var I = 0; I < 4; ++I) { - var rmode = extTimeFlags >> ((3-I)*4); - if ((rmode & 8)==0) - continue; - if (I!=0) - bstream.readBits(16); - var count = (rmode&3); - for (var J = 0; J < count; ++J) - bstream.readBits(8); - } - } - - if (this.flags.LHD_COMMENT) { - info("Found a LHD_COMMENT"); - } - - - while(headPos + this.headSize > bstream.bytePtr) bstream.readBits(1); - - info("Found FILE_HEAD with packSize=" + this.packSize + ", unpackedSize= " + this.unpackedSize + ", hostOS=" + this.hostOS + ", unpVer=" + this.unpVer + ", method=" + this.method + ", filename=" + this.filename); - - break; - default: - info("Found a header of type 0x" + byteValueToHexString(this.headType)); - // skip the rest of the header bytes (for now) - bstream.readBytes( this.headSize - 7 ); - break; - } -}; - -var BLOCK_LZ = 0, - BLOCK_PPM = 1; - -var rLDecode = [0,1,2,3,4,5,6,7,8,10,12,14,16,20,24,28,32,40,48,56,64,80,96,112,128,160,192,224], - rLBits = [0,0,0,0,0,0,0,0,1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5], - rDBitLengthCounts = [4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,14,0,12], - rSDDecode = [0,4,8,16,32,64,128,192], - rSDBits = [2,2,3, 4, 5, 6, 6, 6]; - -var rDDecode = [0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, - 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024, 1536, 2048, 3072, - 4096, 6144, 8192, 12288, 16384, 24576, 32768, 49152, 65536, 98304, - 131072, 196608, 262144, 327680, 393216, 458752, 524288, 589824, - 655360, 720896, 786432, 851968, 917504, 983040]; - -var rDBits = [0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, - 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, - 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16]; - -var rLOW_DIST_REP_COUNT = 16; - -var rNC = 299, - rDC = 60, - rLDC = 17, - rRC = 28, - rBC = 20, - rHUFF_TABLE_SIZE = (rNC+rDC+rRC+rLDC); - -var UnpBlockType = BLOCK_LZ; -var UnpOldTable = new Array(rHUFF_TABLE_SIZE); - -var BD = { //bitdecode - DecodeLen: new Array(16), - DecodePos: new Array(16), - DecodeNum: new Array(rBC) -}; -var LD = { //litdecode - DecodeLen: new Array(16), - DecodePos: new Array(16), - DecodeNum: new Array(rNC) -}; -var DD = { //distdecode - DecodeLen: new Array(16), - DecodePos: new Array(16), - DecodeNum: new Array(rDC) -}; -var LDD = { //low dist decode - DecodeLen: new Array(16), - DecodePos: new Array(16), - DecodeNum: new Array(rLDC) -}; -var RD = { //rep decode - DecodeLen: new Array(16), - DecodePos: new Array(16), - DecodeNum: new Array(rRC) -}; - -var rBuffer; - -// read in Huffman tables for RAR -function RarReadTables(bstream) { - var BitLength = new Array(rBC), - Table = new Array(rHUFF_TABLE_SIZE); - - // before we start anything we need to get byte-aligned - bstream.readBits( (8 - bstream.bitPtr) & 0x7 ); - - if (bstream.readBits(1)) { - info("Error! PPM not implemented yet"); - return; - } - - if (!bstream.readBits(1)) { //discard old table - for (var i = UnpOldTable.length; i--;) UnpOldTable[i] = 0; - } - - // read in bit lengths - for (var I = 0; I < rBC; ++I) { - - var Length = bstream.readBits(4); - if (Length == 15) { - var ZeroCount = bstream.readBits(4); - if (ZeroCount == 0) { - BitLength[I] = 15; - } - else { - ZeroCount += 2; - while (ZeroCount-- > 0 && I < rBC) - BitLength[I++] = 0; - --I; - } - } - else { - BitLength[I] = Length; - } - } - - // now all 20 bit lengths are obtained, we construct the Huffman Table: - - RarMakeDecodeTables(BitLength, 0, BD, rBC); - - var TableSize = rHUFF_TABLE_SIZE; - //console.log(DecodeLen, DecodePos, DecodeNum); - for (var i = 0; i < TableSize;) { - var num = RarDecodeNumber(bstream, BD); - if (num < 16) { - Table[i] = (num + UnpOldTable[i]) & 0xf; - i++; - } else if(num < 18) { - var N = (num == 16) ? (bstream.readBits(3) + 3) : (bstream.readBits(7) + 11); - - while (N-- > 0 && i < TableSize) { - Table[i] = Table[i - 1]; - i++; - } - } else { - var N = (num == 18) ? (bstream.readBits(3) + 3) : (bstream.readBits(7) + 11); - - while (N-- > 0 && i < TableSize) { - Table[i++] = 0; - } - } - } - - RarMakeDecodeTables(Table, 0, LD, rNC); - RarMakeDecodeTables(Table, rNC, DD, rDC); - RarMakeDecodeTables(Table, rNC + rDC, LDD, rLDC); - RarMakeDecodeTables(Table, rNC + rDC + rLDC, RD, rRC); - - for (var i = UnpOldTable.length; i--;) { - UnpOldTable[i] = Table[i]; - } - return true; -} - - -function RarDecodeNumber(bstream, dec) { - var DecodeLen = dec.DecodeLen, DecodePos = dec.DecodePos, DecodeNum = dec.DecodeNum; - var bitField = bstream.getBits() & 0xfffe; - //some sort of rolled out binary search - var bits = ((bitField < DecodeLen[8])? - ((bitField < DecodeLen[4])? - ((bitField < DecodeLen[2])? - ((bitField < DecodeLen[1])?1:2) - :((bitField < DecodeLen[3])?3:4)) - :(bitField < DecodeLen[6])? - ((bitField < DecodeLen[5])?5:6) - :((bitField < DecodeLen[7])?7:8)) - :((bitField < DecodeLen[12])? - ((bitField < DecodeLen[10])? - ((bitField < DecodeLen[9])?9:10) - :((bitField < DecodeLen[11])?11:12)) - :(bitField < DecodeLen[14])? - ((bitField < DecodeLen[13])?13:14) - :15)); - bstream.readBits(bits); - var N = DecodePos[bits] + ((bitField - DecodeLen[bits -1]) >>> (16 - bits)); - - return DecodeNum[N]; -} - - - -function RarMakeDecodeTables(BitLength, offset, dec, size) { - var DecodeLen = dec.DecodeLen, DecodePos = dec.DecodePos, DecodeNum = dec.DecodeNum; - var LenCount = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], - TmpPos = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], - N = 0, M = 0; - for (var i = DecodeNum.length; i--;) DecodeNum[i] = 0; - for (var i = 0; i < size; i++) { - LenCount[BitLength[i + offset] & 0xF]++; - } - LenCount[0] = 0; - TmpPos[0] = 0; - DecodePos[0] = 0; - DecodeLen[0] = 0; - - for (var I = 1; I < 16; ++I) { - N = 2 * (N+LenCount[I]); - M = (N << (15-I)); - if (M > 0xFFFF) - M = 0xFFFF; - DecodeLen[I] = M; - DecodePos[I] = DecodePos[I-1] + LenCount[I-1]; - TmpPos[I] = DecodePos[I]; - } - for (I = 0; I < size; ++I) - if (BitLength[I + offset] != 0) - DecodeNum[ TmpPos[ BitLength[offset + I] & 0xF ]++] = I; - -} - -// TODO: implement -function Unpack15(bstream, Solid) { - info("ERROR! RAR 1.5 compression not supported"); -} - -function Unpack20(bstream, Solid) { - var destUnpSize = rBuffer.data.length; - var oldDistPtr = 0; - - RarReadTables20(bstream); - while (destUnpSize > rBuffer.ptr) { - var num = RarDecodeNumber(bstream, LD); - if (num < 256) { - rBuffer.insertByte(num); - continue; - } - if (num > 269) { - var Length = rLDecode[num -= 270] + 3; - if ((Bits = rLBits[num]) > 0) { - Length += bstream.readBits(Bits); - } - var DistNumber = RarDecodeNumber(bstream, DD); - var Distance = rDDecode[DistNumber] + 1; - if ((Bits = rDBits[DistNumber]) > 0) { - Distance += bstream.readBits(Bits); - } - if (Distance >= 0x2000) { - Length++; - if(Distance >= 0x40000) Length++; - } - lastLength = Length; - lastDist = rOldDist[oldDistPtr++ & 3] = Distance; - RarCopyString(Length, Distance); - continue; - } - if (num == 269) { - RarReadTables20(bstream); - - RarUpdateProgress() - - continue; - } - if (num == 256) { - lastDist = rOldDist[oldDistPtr++ & 3] = lastDist; - RarCopyString(lastLength, lastDist); - continue; - } - if (num < 261) { - var Distance = rOldDist[(oldDistPtr - (num - 256)) & 3]; - var LengthNumber = RarDecodeNumber(bstream, RD); - var Length = rLDecode[LengthNumber] +2; - if ((Bits = rLBits[LengthNumber]) > 0) { - Length += bstream.readBits(Bits); - } - if (Distance >= 0x101) { - Length++; - if (Distance >= 0x2000) { - Length++ - if (Distance >= 0x40000) Length++; - } - } - lastLength = Length; - lastDist = rOldDist[oldDistPtr++ & 3] = Distance; - RarCopyString(Length, Distance); - continue; - } - if (num < 270) { - var Distance = rSDDecode[num -= 261] + 1; - if ((Bits = rSDBits[num]) > 0) { - Distance += bstream.readBits(Bits); - } - lastLength = 2; - lastDist = rOldDist[oldDistPtr++ & 3] = Distance; - RarCopyString(2, Distance); - continue; - } - - } - RarUpdateProgress() -} - -function RarUpdateProgress() { - var change = rBuffer.ptr - currentBytesUnarchivedInFile; - currentBytesUnarchivedInFile = rBuffer.ptr; - currentBytesUnarchived += change; - postProgress(); -} - - -var rNC20 = 298, - rDC20 = 48, - rRC20 = 28, - rBC20 = 19, - rMC20 = 257; - -var UnpOldTable20 = new Array(rMC20 * 4); - -function RarReadTables20(bstream) { - var BitLength = new Array(rBC20); - var Table = new Array(rMC20 * 4); - var TableSize, N, I; - var AudioBlock = bstream.readBits(1); - if (!bstream.readBits(1)) - for (var i = UnpOldTable20.length; i--;) UnpOldTable20[i] = 0; - TableSize = rNC20 + rDC20 + rRC20; - for (var I = 0; I < rBC20; I++) - BitLength[I] = bstream.readBits(4); - RarMakeDecodeTables(BitLength, 0, BD, rBC20); - I = 0; - while (I < TableSize) { - var num = RarDecodeNumber(bstream, BD); - if (num < 16) { - Table[I] = num + UnpOldTable20[I] & 0xf; - I++; - } else if(num == 16) { - N = bstream.readBits(2) + 3; - while (N-- > 0 && I < TableSize) { - Table[I] = Table[I - 1]; - I++; - } - } else { - if (num == 17) { - N = bstream.readBits(3) + 3; - } else { - N = bstream.readBits(7) + 11; - } - while (N-- > 0 && I < TableSize) { - Table[I++] = 0; - } - } - } - RarMakeDecodeTables(Table, 0, LD, rNC20); - RarMakeDecodeTables(Table, rNC20, DD, rDC20); - RarMakeDecodeTables(Table, rNC20 + rDC20, RD, rRC20); - for (var i = UnpOldTable20.length; i--;) UnpOldTable20[i] = Table[i]; -} - -var lowDistRepCount = 0, prevLowDist = 0; - -var rOldDist = [0,0,0,0]; -var lastDist; -var lastLength; - - -function Unpack29(bstream, Solid) { - // lazy initialize rDDecode and rDBits - - var DDecode = new Array(rDC); - var DBits = new Array(rDC); - - var Dist=0,BitLength=0,Slot=0; - - for (var I = 0; I < rDBitLengthCounts.length; I++,BitLength++) { - for (var J = 0; J < rDBitLengthCounts[I]; J++,Slot++,Dist+=(1<= 271) { - var Length = rLDecode[num -= 271] + 3; - if ((Bits = rLBits[num]) > 0) { - Length += bstream.readBits(Bits); - } - var DistNumber = RarDecodeNumber(bstream, DD); - var Distance = DDecode[DistNumber]+1; - if ((Bits = DBits[DistNumber]) > 0) { - if (DistNumber > 9) { - if (Bits > 4) { - Distance += ((bstream.getBits() >>> (20 - Bits)) << 4); - bstream.readBits(Bits - 4); - //todo: check this - } - if (lowDistRepCount > 0) { - lowDistRepCount--; - Distance += prevLowDist; - } else { - var LowDist = RarDecodeNumber(bstream, LDD); - if (LowDist == 16) { - lowDistRepCount = rLOW_DIST_REP_COUNT - 1; - Distance += prevLowDist; - } else { - Distance += LowDist; - prevLowDist = LowDist; - } - } - } else { - Distance += bstream.readBits(Bits); - } - } - if (Distance >= 0x2000) { - Length++; - if (Distance >= 0x40000) { - Length++; - } - } - RarInsertOldDist(Distance); - RarInsertLastMatch(Length, Distance); - RarCopyString(Length, Distance); - continue; - } - if (num == 256) { - if (!RarReadEndOfBlock(bstream)) break; - - continue; - } - if (num == 257) { - //console.log("READVMCODE"); - if (!RarReadVMCode(bstream)) break; - continue; - } - if (num == 258) { - if (lastLength != 0) { - RarCopyString(lastLength, lastDist); - } - continue; - } - if (num < 263) { - var DistNum = num - 259; - var Distance = rOldDist[DistNum]; - - for (var I = DistNum; I > 0; I--) { - rOldDist[I] = rOldDist[I-1]; - } - rOldDist[0] = Distance; - - var LengthNumber = RarDecodeNumber(bstream, RD); - var Length = rLDecode[LengthNumber] + 2; - if ((Bits = rLBits[LengthNumber]) > 0) { - Length += bstream.readBits(Bits); - } - RarInsertLastMatch(Length, Distance); - RarCopyString(Length, Distance); - continue; - } - if (num < 272) { - var Distance = rSDDecode[num -= 263] + 1; - if ((Bits = rSDBits[num]) > 0) { - Distance += bstream.readBits(Bits); - } - RarInsertOldDist(Distance); - RarInsertLastMatch(2, Distance); - RarCopyString(2, Distance); - continue; - } - - } - RarUpdateProgress() -} - -function RarReadEndOfBlock(bstream) { - - RarUpdateProgress() - - - var NewTable = false, NewFile = false; - if (bstream.readBits(1)) { - NewTable = true; - } else { - NewFile = true; - NewTable = !!bstream.readBits(1); - } - //tablesRead = !NewTable; - return !(NewFile || NewTable && !RarReadTables(bstream)); -} - - -function RarReadVMCode(bstream) { - var FirstByte = bstream.readBits(8); - var Length = (FirstByte & 7) + 1; - if (Length == 7) { - Length = bstream.readBits(8) + 7; - } else if(Length == 8) { - Length = bstream.readBits(16); - } - var vmCode = []; - for(var I = 0; I < Length; I++) { - //do something here with cheking readbuf - vmCode.push(bstream.readBits(8)); - } - return RarAddVMCode(FirstByte, vmCode, Length); -} - -function RarAddVMCode(firstByte, vmCode, length) { - //console.log(vmCode); - if (vmCode.length > 0) { - info("Error! RarVM not supported yet!"); - } - return true; -} - -function RarInsertLastMatch(length, distance) { - lastDist = distance; - lastLength = length; -} - -function RarInsertOldDist(distance) { - rOldDist.splice(3,1); - rOldDist.splice(0,0,distance); -} - -//this is the real function, the other one is for debugging -function RarCopyString(length, distance) { - var destPtr = rBuffer.ptr - distance; - if(destPtr < 0){ - var l = rOldBuffers.length; - while(destPtr < 0){ - destPtr = rOldBuffers[--l].data.length + destPtr - } - //TODO: lets hope that it never needs to read beyond file boundaries - while(length--) rBuffer.insertByte(rOldBuffers[l].data[destPtr++]); - - } - if (length > distance) { - while(length--) rBuffer.insertByte(rBuffer.data[destPtr++]); - } else { - rBuffer.insertBytes(rBuffer.data.subarray(destPtr, destPtr + length)); - } - -} - -var rOldBuffers = [] -// v must be a valid RarVolume -function unpack(v) { - - // TODO: implement what happens when unpVer is < 15 - var Ver = v.header.unpVer <= 15 ? 15 : v.header.unpVer, - Solid = v.header.LHD_SOLID, - bstream = new bitjs.io.BitStream(v.fileData.buffer, true /* rtl */, v.fileData.byteOffset, v.fileData.byteLength ); - - rBuffer = new bitjs.io.ByteBuffer(v.header.unpackedSize); - - info("Unpacking "+v.filename+" RAR v"+Ver); - - switch(Ver) { - case 15: // rar 1.5 compression - Unpack15(bstream, Solid); - break; - case 20: // rar 2.x compression - case 26: // files larger than 2GB - Unpack20(bstream, Solid); - break; - case 29: // rar 3.x compression - case 36: // alternative hash - Unpack29(bstream, Solid); - break; - } // switch(method) - - rOldBuffers.push(rBuffer); - //TODO: clear these old buffers when there's over 4MB of history - return rBuffer.data; -} - -// bstream is a bit stream -var RarLocalFile = function(bstream) { - - this.header = new RarVolumeHeader(bstream); - this.filename = this.header.filename; - - if (this.header.headType != FILE_HEAD && this.header.headType != ENDARC_HEAD) { - this.isValid = false; - info("Error! RAR Volume did not include a FILE_HEAD header "); - } - else { - // read in the compressed data - this.fileData = null; - if (this.header.packSize > 0) { - this.fileData = bstream.readBytes(this.header.packSize); - this.isValid = true; - } - } -}; - -RarLocalFile.prototype.unrar = function() { - - if (!this.header.flags.LHD_SPLIT_BEFORE) { - // unstore file - if (this.header.method == 0x30) { - info("Unstore "+this.filename); - this.isValid = true; - - currentBytesUnarchivedInFile += this.fileData.length; - currentBytesUnarchived += this.fileData.length; - } else { - this.isValid = true; - this.fileData = unpack(this); - } - } -} - -var unrar = function(arrayBuffer) { - currentFilename = ""; - currentFileNumber = 0; - currentBytesUnarchivedInFile = 0; - currentBytesUnarchived = 0; - totalUncompressedBytesInArchive = 0; - totalFilesInArchive = 0; - - postMessage(new bitjs.archive.UnarchiveStartEvent()); - var bstream = new bitjs.io.BitStream(arrayBuffer, false /* rtl */); - - var header = new RarVolumeHeader(bstream); - if (header.crc == 0x6152 && - header.headType == 0x72 && - header.flags.value == 0x1A21 && - header.headSize == 7) { - info("Found RAR signature"); - - var mhead = new RarVolumeHeader(bstream); - if (mhead.headType != MAIN_HEAD) { - info("Error! RAR did not include a MAIN_HEAD header"); - } - else { - var localFiles = [], - localFile = null; - do { - try { - localFile = new RarLocalFile(bstream); - info("RAR localFile isValid=" + localFile.isValid + ", volume packSize=" + localFile.header.packSize); - if (localFile && localFile.isValid && localFile.header.packSize > 0) { - totalUncompressedBytesInArchive += localFile.header.unpackedSize; - localFiles.push(localFile); - } else if (localFile.header.packSize == 0 && localFile.header.unpackedSize == 0) { - localFile.isValid = true; - } - } catch(err) { - break; - } - //info("bstream" + bstream.bytePtr+"/"+bstream.bytes.length); - } while( localFile.isValid ); - totalFilesInArchive = localFiles.length; - - // now we have all information but things are unpacked - // TODO: unpack - localFiles = localFiles.sort(function(a,b) { - // extract the number at the end of both filenames - var aname = a.filename; - var bname = b.filename; - return aname > bname ? 1 : -1; - /* - var aindex = aname.length, bindex = bname.length; - - // Find the last number character from the back of the filename. - while (aname[aindex-1] < '0' || aname[aindex-1] > '9') --aindex; - while (bname[bindex-1] < '0' || bname[bindex-1] > '9') --bindex; - - // Find the first number character from the back of the filename - while (aname[aindex-1] >= '0' && aname[aindex-1] <= '9') --aindex; - while (bname[bindex-1] >= '0' && bname[bindex-1] <= '9') --bindex; - - // parse them into numbers and return comparison - var anum = parseInt(aname.substr(aindex), 10), - bnum = parseInt(bname.substr(bindex), 10); - return bnum - anum;*/ - }); - - info(localFiles.map(function(a){return a.filename}).join(', ')); - for (var i = 0; i < localFiles.length; ++i) { - var localfile = localFiles[i]; - - // update progress - currentFilename = localfile.header.filename; - currentBytesUnarchivedInFile = 0; - - // actually do the unzipping - localfile.unrar(); - - if (localfile.isValid) { - postMessage(new bitjs.archive.UnarchiveExtractEvent(localfile)); - postProgress(); - } - } - - postProgress(); - } - } - else { - err("Invalid RAR file"); - } - postMessage(new bitjs.archive.UnarchiveFinishEvent()); -}; - -// event.data.file has the ArrayBuffer. -onmessage = function(event) { - var ab = event.data.file; - unrar(ab, true); -}; diff --git a/examples/bitjs/untar.js b/examples/bitjs/untar.js deleted file mode 100755 index 3b16764..0000000 --- a/examples/bitjs/untar.js +++ /dev/null @@ -1,182 +0,0 @@ -/** - * untar.js - * - * Copyright(c) 2011 Google Inc. - * - * Reference Documentation: - * - * TAR format: http://www.gnu.org/software/automake/manual/tar/Standard.html - */ - -// This file expects to be invoked as a Worker (see onmessage below). -importScripts('io.js'); -importScripts('archive.js'); - -// Progress variables. -var currentFilename = ""; -var currentFileNumber = 0; -var currentBytesUnarchivedInFile = 0; -var currentBytesUnarchived = 0; -var totalUncompressedBytesInArchive = 0; -var totalFilesInArchive = 0; - -// Helper functions. -var info = function(str) { - postMessage(new bitjs.archive.UnarchiveInfoEvent(str)); -}; -var err = function(str) { - postMessage(new bitjs.archive.UnarchiveErrorEvent(str)); -}; -var postProgress = function() { - postMessage(new bitjs.archive.UnarchiveProgressEvent( - currentFilename, - currentFileNumber, - currentBytesUnarchivedInFile, - currentBytesUnarchived, - totalUncompressedBytesInArchive, - totalFilesInArchive)); -}; - -// Removes all characters from the first zero-byte in the string onwards. -var readCleanString = function(bstr, numBytes) { - var str = bstr.readString(numBytes); - var zIndex = str.indexOf(String.fromCharCode(0)); - return zIndex != -1 ? str.substr(0, zIndex) : str; -}; - -// takes a ByteStream and parses out the local file information -var TarLocalFile = function(bstream) { - this.isValid = false; - - // Read in the header block - this.name = readCleanString(bstream, 100); - this.mode = readCleanString(bstream, 8); - this.uid = readCleanString(bstream, 8); - this.gid = readCleanString(bstream, 8); - this.size = parseInt(readCleanString(bstream, 12), 8); - this.mtime = readCleanString(bstream, 12); - this.chksum = readCleanString(bstream, 8); - this.typeflag = readCleanString(bstream, 1); - this.linkname = readCleanString(bstream, 100); - this.maybeMagic = readCleanString(bstream, 6); - - if (this.maybeMagic == "ustar") { - this.version = readCleanString(bstream, 2); - this.uname = readCleanString(bstream, 32); - this.gname = readCleanString(bstream, 32); - this.devmajor = readCleanString(bstream, 8); - this.devminor = readCleanString(bstream, 8); - this.prefix = readCleanString(bstream, 155); - - if (this.prefix.length) { - this.name = this.prefix + this.name; - } - bstream.readBytes(12); // 512 - 500 - } else { - bstream.readBytes(255); // 512 - 257 - } - - // Done header, now rest of blocks are the file contents. - this.filename = this.name; - this.fileData = null; - - info("Untarring file '" + this.filename + "'"); - info(" size = " + this.size); - info(" typeflag = " + this.typeflag); - - // A regular file. - if (this.typeflag == 0) { - info(" This is a regular file."); - var sizeInBytes = parseInt(this.size); - this.fileData = new Uint8Array(bstream.bytes.buffer, bstream.ptr, this.size); - if (this.name.length > 0 && this.size > 0 && this.fileData && this.fileData.buffer) { - this.isValid = true; - } - - bstream.readBytes(this.size); - - // Round up to 512-byte blocks. - var remaining = 512 - this.size % 512; - if (remaining > 0 && remaining < 512) { - bstream.readBytes(remaining); - } - } else if (this.typeflag == 5) { - info(" This is a directory.") - } -}; - -// Takes an ArrayBuffer of a tar file in -// returns null on error -// returns an array of DecompressedFile objects on success -var untar = function(arrayBuffer) { - currentFilename = ""; - currentFileNumber = 0; - currentBytesUnarchivedInFile = 0; - currentBytesUnarchived = 0; - totalUncompressedBytesInArchive = 0; - totalFilesInArchive = 0; - - postMessage(new bitjs.archive.UnarchiveStartEvent()); - var bstream = new bitjs.io.ByteStream(arrayBuffer); - var localFiles = []; - - // While we don't encounter an empty block, keep making TarLocalFiles. - while (bstream.peekNumber(4) != 0) { - var oneLocalFile = new TarLocalFile(bstream); - if (oneLocalFile && oneLocalFile.isValid) { - localFiles.push(oneLocalFile); - totalUncompressedBytesInArchive += oneLocalFile.size; - } - } - totalFilesInArchive = localFiles.length; - - // got all local files, now sort them - localFiles.sort(function(a,b) { - // extract the number at the end of both filenames - var aname = a.filename; - var bname = b.filename; - var aindex = aname.length, bindex = bname.length; - - // Find the last number character from the back of the filename. - while (aname[aindex-1] < '0' || aname[aindex-1] > '9') --aindex; - while (bname[bindex-1] < '0' || bname[bindex-1] > '9') --bindex; - - // Find the first number character from the back of the filename - while (aname[aindex-1] >= '0' && aname[aindex-1] <= '9') --aindex; - while (bname[bindex-1] >= '0' && bname[bindex-1] <= '9') --bindex; - - // parse them into numbers and return comparison - var anum = parseInt(aname.substr(aindex), 10), - bnum = parseInt(bname.substr(bindex), 10); - return anum - bnum; - }); - - // report # files and total length - if (localFiles.length > 0) { - postProgress(); - } - - // now do the shipping of each file - for (var i = 0; i < localFiles.length; ++i) { - var localfile = localFiles[i]; - info("Sending file '" + localfile.filename + "' up"); - - // update progress - currentFilename = localfile.filename; - currentFileNumber = i; - currentBytesUnarchivedInFile = localfile.size; - currentBytesUnarchived += localfile.size; - postMessage(new bitjs.archive.UnarchiveExtractEvent(localfile)); - postProgress(); - } - - postProgress(); - - postMessage(new bitjs.archive.UnarchiveFinishEvent()); -}; - -// event.data.file has the ArrayBuffer. -onmessage = function(event) { - var ab = event.data.file; - untar(ab); -}; diff --git a/examples/bitjs/unzip.js b/examples/bitjs/unzip.js deleted file mode 100755 index 99e223d..0000000 --- a/examples/bitjs/unzip.js +++ /dev/null @@ -1,631 +0,0 @@ -/** - * unzip.js - * - * Copyright(c) 2011 Google Inc. - * Copyright(c) 2011 antimatter15 - * - * Reference Documentation: - * - * ZIP format: http://www.pkware.com/documents/casestudies/APPNOTE.TXT - * DEFLATE format: http://tools.ietf.org/html/rfc1951 - */ - -// This file expects to be invoked as a Worker (see onmessage below). -importScripts('io.js'); -importScripts('archive.js'); - -// Progress variables. -var currentFilename = ""; -var currentFileNumber = 0; -var currentBytesUnarchivedInFile = 0; -var currentBytesUnarchived = 0; -var totalUncompressedBytesInArchive = 0; -var totalFilesInArchive = 0; - -// Helper functions. -var info = function(str) { - postMessage(new bitjs.archive.UnarchiveInfoEvent(str)); -}; -var err = function(str) { - postMessage(new bitjs.archive.UnarchiveErrorEvent(str)); -}; -var postProgress = function() { - postMessage(new bitjs.archive.UnarchiveProgressEvent( - currentFilename, - currentFileNumber, - currentBytesUnarchivedInFile, - currentBytesUnarchived, - totalUncompressedBytesInArchive, - totalFilesInArchive)); -}; - -var zLocalFileHeaderSignature = 0x04034b50; -var zArchiveExtraDataSignature = 0x08064b50; -var zCentralFileHeaderSignature = 0x02014b50; -var zDigitalSignatureSignature = 0x05054b50; -var zEndOfCentralDirSignature = 0x06064b50; -var zEndOfCentralDirLocatorSignature = 0x07064b50; - -// takes a ByteStream and parses out the local file information -var ZipLocalFile = function(bstream) { - if (typeof bstream != typeof {} || !bstream.readNumber || typeof bstream.readNumber != typeof function(){}) { - return null; - } - - bstream.readNumber(4); // swallow signature - this.version = bstream.readNumber(2); - this.generalPurpose = bstream.readNumber(2); - this.compressionMethod = bstream.readNumber(2); - this.lastModFileTime = bstream.readNumber(2); - this.lastModFileDate = bstream.readNumber(2); - this.crc32 = bstream.readNumber(4); - this.compressedSize = bstream.readNumber(4); - this.uncompressedSize = bstream.readNumber(4); - this.fileNameLength = bstream.readNumber(2); - this.extraFieldLength = bstream.readNumber(2); - - this.filename = null; - if (this.fileNameLength > 0) { - this.filename = bstream.readString(this.fileNameLength); - } - - info("Zip Local File Header:"); - info(" version=" + this.version); - info(" general purpose=" + this.generalPurpose); - info(" compression method=" + this.compressionMethod); - info(" last mod file time=" + this.lastModFileTime); - info(" last mod file date=" + this.lastModFileDate); - info(" crc32=" + this.crc32); - info(" compressed size=" + this.compressedSize); - info(" uncompressed size=" + this.uncompressedSize); - info(" file name length=" + this.fileNameLength); - info(" extra field length=" + this.extraFieldLength); - info(" filename = '" + this.filename + "'"); - - this.extraField = null; - if (this.extraFieldLength > 0) { - this.extraField = bstream.readString(this.extraFieldLength); - info(" extra field=" + this.extraField); - } - - // read in the compressed data - this.fileData = null; - if (this.compressedSize > 0) { - this.fileData = new Uint8Array(bstream.bytes.buffer, bstream.ptr, this.compressedSize); - bstream.ptr += this.compressedSize; - } - - // TODO: deal with data descriptor if present (we currently assume no data descriptor!) - // "This descriptor exists only if bit 3 of the general purpose bit flag is set" - // But how do you figure out how big the file data is if you don't know the compressedSize - // from the header?!? - if ((this.generalPurpose & bitjs.BIT[3]) != 0) { - this.crc32 = bstream.readNumber(4); - this.compressedSize = bstream.readNumber(4); - this.uncompressedSize = bstream.readNumber(4); - } -}; - -// determine what kind of compressed data we have and decompress -ZipLocalFile.prototype.unzip = function() { - - // Zip Version 1.0, no compression (store only) - if (this.compressionMethod == 0 ) { - info("ZIP v"+this.version+", store only: " + this.filename + " (" + this.compressedSize + " bytes)"); - currentBytesUnarchivedInFile = this.compressedSize; - currentBytesUnarchived += this.compressedSize; - } - // version == 20, compression method == 8 (DEFLATE) - else if (this.compressionMethod == 8) { - info("ZIP v2.0, DEFLATE: " + this.filename + " (" + this.compressedSize + " bytes)"); - this.fileData = inflate(this.fileData, this.uncompressedSize); - } - else { - err("UNSUPPORTED VERSION/FORMAT: ZIP v" + this.version + ", compression method=" + this.compressionMethod + ": " + this.filename + " (" + this.compressedSize + " bytes)"); - this.fileData = null; - } -}; - - -// Takes an ArrayBuffer of a zip file in -// returns null on error -// returns an array of DecompressedFile objects on success -var unzip = function(arrayBuffer) { - postMessage(new bitjs.archive.UnarchiveStartEvent()); - - currentFilename = ""; - currentFileNumber = 0; - currentBytesUnarchivedInFile = 0; - currentBytesUnarchived = 0; - totalUncompressedBytesInArchive = 0; - totalFilesInArchive = 0; - currentBytesUnarchived = 0; - - var bstream = new bitjs.io.ByteStream(arrayBuffer); - // detect local file header signature or return null - if (bstream.peekNumber(4) == zLocalFileHeaderSignature) { - var localFiles = []; - // loop until we don't see any more local files - while (bstream.peekNumber(4) == zLocalFileHeaderSignature) { - var oneLocalFile = new ZipLocalFile(bstream); - // this should strip out directories/folders - if (oneLocalFile && oneLocalFile.uncompressedSize > 0 && oneLocalFile.fileData) { - localFiles.push(oneLocalFile); - totalUncompressedBytesInArchive += oneLocalFile.uncompressedSize; - } - } - totalFilesInArchive = localFiles.length; - - // got all local files, now sort them - localFiles.sort(function(a,b) { - // extract the number at the end of both filenames - var aname = a.filename; - var bname = b.filename; - var aindex = aname.length, bindex = bname.length; - - // Find the last number character from the back of the filename. - while (aname[aindex-1] < '0' || aname[aindex-1] > '9') --aindex; - while (bname[bindex-1] < '0' || bname[bindex-1] > '9') --bindex; - - // Find the first number character from the back of the filename - while (aname[aindex-1] >= '0' && aname[aindex-1] <= '9') --aindex; - while (bname[bindex-1] >= '0' && bname[bindex-1] <= '9') --bindex; - - // parse them into numbers and return comparison - var anum = parseInt(aname.substr(aindex), 10), - bnum = parseInt(bname.substr(bindex), 10); - return anum - bnum; - }); - - // archive extra data record - if (bstream.peekNumber(4) == zArchiveExtraDataSignature) { - info(" Found an Archive Extra Data Signature"); - - // skipping this record for now - bstream.readNumber(4); - var archiveExtraFieldLength = bstream.readNumber(4); - bstream.readString(archiveExtraFieldLength); - } - - // central directory structure - // TODO: handle the rest of the structures (Zip64 stuff) - if (bstream.peekNumber(4) == zCentralFileHeaderSignature) { - info(" Found a Central File Header"); - - // read all file headers - while (bstream.peekNumber(4) == zCentralFileHeaderSignature) { - bstream.readNumber(4); // signature - bstream.readNumber(2); // version made by - bstream.readNumber(2); // version needed to extract - bstream.readNumber(2); // general purpose bit flag - bstream.readNumber(2); // compression method - bstream.readNumber(2); // last mod file time - bstream.readNumber(2); // last mod file date - bstream.readNumber(4); // crc32 - bstream.readNumber(4); // compressed size - bstream.readNumber(4); // uncompressed size - var fileNameLength = bstream.readNumber(2); // file name length - var extraFieldLength = bstream.readNumber(2); // extra field length - var fileCommentLength = bstream.readNumber(2); // file comment length - bstream.readNumber(2); // disk number start - bstream.readNumber(2); // internal file attributes - bstream.readNumber(4); // external file attributes - bstream.readNumber(4); // relative offset of local header - - bstream.readString(fileNameLength); // file name - bstream.readString(extraFieldLength); // extra field - bstream.readString(fileCommentLength); // file comment - } - } - - // digital signature - if (bstream.peekNumber(4) == zDigitalSignatureSignature) { - info(" Found a Digital Signature"); - - bstream.readNumber(4); - var sizeOfSignature = bstream.readNumber(2); - bstream.readString(sizeOfSignature); // digital signature data - } - - // report # files and total length - if (localFiles.length > 0) { - postProgress(); - } - - // now do the unzipping of each file - for (var i = 0; i < localFiles.length; ++i) { - var localfile = localFiles[i]; - - // update progress - currentFilename = localfile.filename; - currentFileNumber = i; - currentBytesUnarchivedInFile = 0; - - // actually do the unzipping - localfile.unzip(); - - if (localfile.fileData != null) { - postMessage(new bitjs.archive.UnarchiveExtractEvent(localfile)); - postProgress(); - } - } - postProgress(); - postMessage(new bitjs.archive.UnarchiveFinishEvent()); - } -} - -// returns a table of Huffman codes -// each entry's index is its code and its value is a JavaScript object -// containing {length: 6, symbol: X} -function getHuffmanCodes(bitLengths) { - // ensure bitLengths is an array containing at least one element - if (typeof bitLengths != typeof [] || bitLengths.length < 1) { - err("Error! getHuffmanCodes() called with an invalid array"); - return null; - } - - // Reference: http://tools.ietf.org/html/rfc1951#page-8 - var numLengths = bitLengths.length, - bl_count = [], - MAX_BITS = 1; - - // Step 1: count up how many codes of each length we have - for (var i = 0; i < numLengths; ++i) { - var length = bitLengths[i]; - // test to ensure each bit length is a positive, non-zero number - if (typeof length != typeof 1 || length < 0) { - err("bitLengths contained an invalid number in getHuffmanCodes(): " + length + " of type " + (typeof length)); - return null; - } - // increment the appropriate bitlength count - if (bl_count[length] == undefined) bl_count[length] = 0; - // a length of zero means this symbol is not participating in the huffman coding - if (length > 0) bl_count[length]++; - - if (length > MAX_BITS) MAX_BITS = length; - } - - // Step 2: Find the numerical value of the smallest code for each code length - var next_code = [], - code = 0; - for (var bits = 1; bits <= MAX_BITS; ++bits) { - var length = bits-1; - // ensure undefined lengths are zero - if (bl_count[length] == undefined) bl_count[length] = 0; - code = (code + bl_count[bits-1]) << 1; - next_code[bits] = code; - } - - // Step 3: Assign numerical values to all codes - var table = {}, tableLength = 0; - for (var n = 0; n < numLengths; ++n) { - var len = bitLengths[n]; - if (len != 0) { - table[next_code[len]] = { length: len, symbol: n }; //, bitstring: binaryValueToString(next_code[len],len) }; - tableLength++; - next_code[len]++; - } - } - table.maxLength = tableLength; - - return table; -} - -/* - The Huffman codes for the two alphabets are fixed, and are not - represented explicitly in the data. The Huffman code lengths - for the literal/length alphabet are: - - Lit Value Bits Codes - --------- ---- ----- - 0 - 143 8 00110000 through - 10111111 - 144 - 255 9 110010000 through - 111111111 - 256 - 279 7 0000000 through - 0010111 - 280 - 287 8 11000000 through - 11000111 -*/ -// fixed Huffman codes go from 7-9 bits, so we need an array whose index can hold up to 9 bits -var fixedHCtoLiteral = null; -var fixedHCtoDistance = null; -function getFixedLiteralTable() { - // create once - if (!fixedHCtoLiteral) { - var bitlengths = new Array(288); - for (var i = 0; i <= 143; ++i) bitlengths[i] = 8; - for (i = 144; i <= 255; ++i) bitlengths[i] = 9; - for (i = 256; i <= 279; ++i) bitlengths[i] = 7; - for (i = 280; i <= 287; ++i) bitlengths[i] = 8; - - // get huffman code table - fixedHCtoLiteral = getHuffmanCodes(bitlengths); - } - return fixedHCtoLiteral; -} -function getFixedDistanceTable() { - // create once - if (!fixedHCtoDistance) { - var bitlengths = new Array(32); - for (var i = 0; i < 32; ++i) { bitlengths[i] = 5; } - - // get huffman code table - fixedHCtoDistance = getHuffmanCodes(bitlengths); - } - return fixedHCtoDistance; -} - -// extract one bit at a time until we find a matching Huffman Code -// then return that symbol -function decodeSymbol(bstream, hcTable) { - var code = 0, len = 0; - var match = false; - - // loop until we match - for (;;) { - // read in next bit - var bit = bstream.readBits(1); - code = (code<<1) | bit; - ++len; - - // check against Huffman Code table and break if found - if (hcTable.hasOwnProperty(code) && hcTable[code].length == len) { - - break; - } - if (len > hcTable.maxLength) { - err("Bit stream out of sync, didn't find a Huffman Code, length was " + len + - " and table only max code length of " + hcTable.maxLength); - break; - } - } - return hcTable[code].symbol; -} - - -var CodeLengthCodeOrder = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]; - /* - Extra Extra Extra - Code Bits Length(s) Code Bits Lengths Code Bits Length(s) - ---- ---- ------ ---- ---- ------- ---- ---- ------- - 257 0 3 267 1 15,16 277 4 67-82 - 258 0 4 268 1 17,18 278 4 83-98 - 259 0 5 269 2 19-22 279 4 99-114 - 260 0 6 270 2 23-26 280 4 115-130 - 261 0 7 271 2 27-30 281 5 131-162 - 262 0 8 272 2 31-34 282 5 163-194 - 263 0 9 273 3 35-42 283 5 195-226 - 264 0 10 274 3 43-50 284 5 227-257 - 265 1 11,12 275 3 51-58 285 0 258 - 266 1 13,14 276 3 59-66 - - */ -var LengthLookupTable = [ - [0,3], [0,4], [0,5], [0,6], - [0,7], [0,8], [0,9], [0,10], - [1,11], [1,13], [1,15], [1,17], - [2,19], [2,23], [2,27], [2,31], - [3,35], [3,43], [3,51], [3,59], - [4,67], [4,83], [4,99], [4,115], - [5,131], [5,163], [5,195], [5,227], - [0,258] -]; - /* - Extra Extra Extra - Code Bits Dist Code Bits Dist Code Bits Distance - ---- ---- ---- ---- ---- ------ ---- ---- -------- - 0 0 1 10 4 33-48 20 9 1025-1536 - 1 0 2 11 4 49-64 21 9 1537-2048 - 2 0 3 12 5 65-96 22 10 2049-3072 - 3 0 4 13 5 97-128 23 10 3073-4096 - 4 1 5,6 14 6 129-192 24 11 4097-6144 - 5 1 7,8 15 6 193-256 25 11 6145-8192 - 6 2 9-12 16 7 257-384 26 12 8193-12288 - 7 2 13-16 17 7 385-512 27 12 12289-16384 - 8 3 17-24 18 8 513-768 28 13 16385-24576 - 9 3 25-32 19 8 769-1024 29 13 24577-32768 - */ -var DistLookupTable = [ - [0,1], [0,2], [0,3], [0,4], - [1,5], [1,7], - [2,9], [2,13], - [3,17], [3,25], - [4,33], [4,49], - [5,65], [5,97], - [6,129], [6,193], - [7,257], [7,385], - [8,513], [8,769], - [9,1025], [9,1537], - [10,2049], [10,3073], - [11,4097], [11,6145], - [12,8193], [12,12289], - [13,16385], [13,24577] -]; - -function inflateBlockData(bstream, hcLiteralTable, hcDistanceTable, buffer) { - /* - loop (until end of block code recognized) - decode literal/length value from input stream - if value < 256 - copy value (literal byte) to output stream - otherwise - if value = end of block (256) - break from loop - otherwise (value = 257..285) - decode distance from input stream - - move backwards distance bytes in the output - stream, and copy length bytes from this - position to the output stream. - */ - var numSymbols = 0, blockSize = 0; - for (;;) { - var symbol = decodeSymbol(bstream, hcLiteralTable); - ++numSymbols; - if (symbol < 256) { - // copy literal byte to output - buffer.insertByte(symbol); - blockSize++; - } - else { - // end of block reached - if (symbol == 256) { - break; - } - else { - var lengthLookup = LengthLookupTable[symbol-257], - length = lengthLookup[1] + bstream.readBits(lengthLookup[0]), - distLookup = DistLookupTable[decodeSymbol(bstream, hcDistanceTable)], - distance = distLookup[1] + bstream.readBits(distLookup[0]); - - // now apply length and distance appropriately and copy to output - - // TODO: check that backward distance < data.length? - - // http://tools.ietf.org/html/rfc1951#page-11 - // "Note also that the referenced string may overlap the current - // position; for example, if the last 2 bytes decoded have values - // X and Y, a string reference with - // adds X,Y,X,Y,X to the output stream." - // - // loop for each character - var ch = buffer.ptr - distance; - blockSize += length; - if(length > distance) { - var data = buffer.data; - while (length--) { - buffer.insertByte(data[ch++]); - } - } else { - buffer.insertBytes(buffer.data.subarray(ch, ch + length)) - } - - } // length-distance pair - } // length-distance pair or end-of-block - } // loop until we reach end of block - return blockSize; -} - -// {Uint8Array} compressedData A Uint8Array of the compressed file data. -// compression method 8 -// deflate: http://tools.ietf.org/html/rfc1951 -function inflate(compressedData, numDecompressedBytes) { - // Bit stream representing the compressed data. - var bstream = new bitjs.io.BitStream(compressedData.buffer, - false /* rtl */, - compressedData.byteOffset, - compressedData.byteLength); - var buffer = new bitjs.io.ByteBuffer(numDecompressedBytes); - var numBlocks = 0, blockSize = 0; - - // block format: http://tools.ietf.org/html/rfc1951#page-9 - do { - var bFinal = bstream.readBits(1), - bType = bstream.readBits(2); - blockSize = 0; - ++numBlocks; - // no compression - if (bType == 0) { - // skip remaining bits in this byte - while (bstream.bitPtr != 0) bstream.readBits(1); - var len = bstream.readBits(16), - nlen = bstream.readBits(16); - // TODO: check if nlen is the ones-complement of len? - - if(len > 0) buffer.insertBytes(bstream.readBytes(len)); - blockSize = len; - } - // fixed Huffman codes - else if(bType == 1) { - blockSize = inflateBlockData(bstream, getFixedLiteralTable(), getFixedDistanceTable(), buffer); - } - // dynamic Huffman codes - else if(bType == 2) { - var numLiteralLengthCodes = bstream.readBits(5) + 257; - var numDistanceCodes = bstream.readBits(5) + 1, - numCodeLengthCodes = bstream.readBits(4) + 4; - - // populate the array of code length codes (first de-compaction) - var codeLengthsCodeLengths = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; - for (var i = 0; i < numCodeLengthCodes; ++i) { - codeLengthsCodeLengths[ CodeLengthCodeOrder[i] ] = bstream.readBits(3); - } - - // get the Huffman Codes for the code lengths - var codeLengthsCodes = getHuffmanCodes(codeLengthsCodeLengths); - - // now follow this mapping - /* - 0 - 15: Represent code lengths of 0 - 15 - 16: Copy the previous code length 3 - 6 times. - The next 2 bits indicate repeat length - (0 = 3, ... , 3 = 6) - Example: Codes 8, 16 (+2 bits 11), - 16 (+2 bits 10) will expand to - 12 code lengths of 8 (1 + 6 + 5) - 17: Repeat a code length of 0 for 3 - 10 times. - (3 bits of length) - 18: Repeat a code length of 0 for 11 - 138 times - (7 bits of length) - */ - // to generate the true code lengths of the Huffman Codes for the literal - // and distance tables together - var literalCodeLengths = []; - var prevCodeLength = 0; - while (literalCodeLengths.length < numLiteralLengthCodes + numDistanceCodes) { - var symbol = decodeSymbol(bstream, codeLengthsCodes); - if (symbol <= 15) { - literalCodeLengths.push(symbol); - prevCodeLength = symbol; - } - else if (symbol == 16) { - var repeat = bstream.readBits(2) + 3; - while (repeat--) { - literalCodeLengths.push(prevCodeLength); - } - } - else if (symbol == 17) { - var repeat = bstream.readBits(3) + 3; - while (repeat--) { - literalCodeLengths.push(0); - } - } - else if (symbol == 18) { - var repeat = bstream.readBits(7) + 11; - while (repeat--) { - literalCodeLengths.push(0); - } - } - } - - // now split the distance code lengths out of the literal code array - var distanceCodeLengths = literalCodeLengths.splice(numLiteralLengthCodes, numDistanceCodes); - - // now generate the true Huffman Code tables using these code lengths - var hcLiteralTable = getHuffmanCodes(literalCodeLengths), - hcDistanceTable = getHuffmanCodes(distanceCodeLengths); - blockSize = inflateBlockData(bstream, hcLiteralTable, hcDistanceTable, buffer); - } - // error - else { - err("Error! Encountered deflate block of type 3"); - return null; - } - - // update progress - currentBytesUnarchivedInFile += blockSize; - currentBytesUnarchived += blockSize; - postProgress(); - - } while (bFinal != 1); - // we are done reading blocks if the bFinal bit was set for this block - - // return the buffer data bytes - return buffer.data; -} - -// event.data.file has the ArrayBuffer. -onmessage = function(event) { - unzip(event.data.file, true); -}; diff --git a/examples/dev.html b/examples/dev.html deleted file mode 100755 index 5504794..0000000 --- a/examples/dev.html +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - Comic Book Reader (Dev) - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/file.html b/examples/file.html deleted file mode 100644 index 29c526f..0000000 --- a/examples/file.html +++ /dev/null @@ -1,211 +0,0 @@ - - - - - - - - - - - Comic Book Reader - - - - - - - - - - - - - -
- - - -
- - -
- - - - - - diff --git a/examples/goldenboy.cbz b/examples/goldenboy.cbz deleted file mode 100644 index ec6dba6..0000000 Binary files a/examples/goldenboy.cbz and /dev/null differ diff --git a/examples/goldenboy/goldenboy_00.jpg b/examples/goldenboy/goldenboy_00.jpg deleted file mode 100644 index 348a9ed..0000000 Binary files a/examples/goldenboy/goldenboy_00.jpg and /dev/null differ diff --git a/examples/goldenboy/goldenboy_01.jpg b/examples/goldenboy/goldenboy_01.jpg deleted file mode 100644 index 070e2c8..0000000 Binary files a/examples/goldenboy/goldenboy_01.jpg and /dev/null differ diff --git a/examples/goldenboy/goldenboy_02.jpg b/examples/goldenboy/goldenboy_02.jpg deleted file mode 100644 index 98ed740..0000000 Binary files a/examples/goldenboy/goldenboy_02.jpg and /dev/null differ diff --git a/examples/goldenboy/goldenboy_03.jpg b/examples/goldenboy/goldenboy_03.jpg deleted file mode 100644 index d7657a6..0000000 Binary files a/examples/goldenboy/goldenboy_03.jpg and /dev/null differ diff --git a/examples/goldenboy/goldenboy_04.jpg b/examples/goldenboy/goldenboy_04.jpg deleted file mode 100644 index 70c23b1..0000000 Binary files a/examples/goldenboy/goldenboy_04.jpg and /dev/null differ diff --git a/examples/goldenboy/goldenboy_05.jpg b/examples/goldenboy/goldenboy_05.jpg deleted file mode 100644 index 9856078..0000000 Binary files a/examples/goldenboy/goldenboy_05.jpg and /dev/null differ diff --git a/examples/goldenboy/goldenboy_06.jpg b/examples/goldenboy/goldenboy_06.jpg deleted file mode 100644 index 3d81518..0000000 Binary files a/examples/goldenboy/goldenboy_06.jpg and /dev/null differ diff --git a/examples/goldenboy/goldenboy_07.jpg b/examples/goldenboy/goldenboy_07.jpg deleted file mode 100644 index a413000..0000000 Binary files a/examples/goldenboy/goldenboy_07.jpg and /dev/null differ diff --git a/examples/goldenboy/goldenboy_08.jpg b/examples/goldenboy/goldenboy_08.jpg deleted file mode 100644 index c3b7586..0000000 Binary files a/examples/goldenboy/goldenboy_08.jpg and /dev/null differ diff --git a/examples/goldenboy/goldenboy_09.jpg b/examples/goldenboy/goldenboy_09.jpg deleted file mode 100644 index 5684801..0000000 Binary files a/examples/goldenboy/goldenboy_09.jpg and /dev/null differ diff --git a/examples/goldenboy/goldenboy_10.jpg b/examples/goldenboy/goldenboy_10.jpg deleted file mode 100644 index 3b98022..0000000 Binary files a/examples/goldenboy/goldenboy_10.jpg and /dev/null differ diff --git a/examples/goldenboy/goldenboy_11.jpg b/examples/goldenboy/goldenboy_11.jpg deleted file mode 100644 index 7c18353..0000000 Binary files a/examples/goldenboy/goldenboy_11.jpg and /dev/null differ diff --git a/examples/goldenboy/goldenboy_12.jpg b/examples/goldenboy/goldenboy_12.jpg deleted file mode 100644 index 522a5da..0000000 Binary files a/examples/goldenboy/goldenboy_12.jpg and /dev/null differ diff --git a/examples/goldenboy/goldenboy_13.jpg b/examples/goldenboy/goldenboy_13.jpg deleted file mode 100644 index a212a77..0000000 Binary files a/examples/goldenboy/goldenboy_13.jpg and /dev/null differ diff --git a/examples/goldenboy/goldenboy_14.jpg b/examples/goldenboy/goldenboy_14.jpg deleted file mode 100644 index 1839320..0000000 Binary files a/examples/goldenboy/goldenboy_14.jpg and /dev/null differ diff --git a/examples/goldenboy/goldenboy_15.jpg b/examples/goldenboy/goldenboy_15.jpg deleted file mode 100644 index 92afab6..0000000 Binary files a/examples/goldenboy/goldenboy_15.jpg and /dev/null differ diff --git a/examples/goldenboy/goldenboy_16.jpg b/examples/goldenboy/goldenboy_16.jpg deleted file mode 100644 index b1dacc0..0000000 Binary files a/examples/goldenboy/goldenboy_16.jpg and /dev/null differ diff --git a/examples/goldenboy/goldenboy_17.jpg b/examples/goldenboy/goldenboy_17.jpg deleted file mode 100644 index 0318aaf..0000000 Binary files a/examples/goldenboy/goldenboy_17.jpg and /dev/null differ diff --git a/examples/goldenboy/goldenboy_18.jpg b/examples/goldenboy/goldenboy_18.jpg deleted file mode 100644 index 894c9fd..0000000 Binary files a/examples/goldenboy/goldenboy_18.jpg and /dev/null differ diff --git a/examples/goldenboy/goldenboy_19.jpg b/examples/goldenboy/goldenboy_19.jpg deleted file mode 100644 index 88b41f2..0000000 Binary files a/examples/goldenboy/goldenboy_19.jpg and /dev/null differ diff --git a/examples/goldenboy/goldenboy_20.jpg b/examples/goldenboy/goldenboy_20.jpg deleted file mode 100644 index 595a1e4..0000000 Binary files a/examples/goldenboy/goldenboy_20.jpg and /dev/null differ diff --git a/examples/goldenboy/goldenboy_21.jpg b/examples/goldenboy/goldenboy_21.jpg deleted file mode 100644 index 95582ec..0000000 Binary files a/examples/goldenboy/goldenboy_21.jpg and /dev/null differ diff --git a/examples/goldenboy/goldenboy_22.jpg b/examples/goldenboy/goldenboy_22.jpg deleted file mode 100644 index 6a2f777..0000000 Binary files a/examples/goldenboy/goldenboy_22.jpg and /dev/null differ diff --git a/examples/goldenboy/goldenboy_23.jpg b/examples/goldenboy/goldenboy_23.jpg deleted file mode 100644 index 69b458b..0000000 Binary files a/examples/goldenboy/goldenboy_23.jpg and /dev/null differ diff --git a/examples/goldenboy/goldenboy_24.jpg b/examples/goldenboy/goldenboy_24.jpg deleted file mode 100644 index a287f6e..0000000 Binary files a/examples/goldenboy/goldenboy_24.jpg and /dev/null differ diff --git a/examples/goldenboy/goldenboy_25.jpg b/examples/goldenboy/goldenboy_25.jpg deleted file mode 100644 index cb814d6..0000000 Binary files a/examples/goldenboy/goldenboy_25.jpg and /dev/null differ diff --git a/lib/.jshintrc b/lib/.jshintrc deleted file mode 100644 index e310d21..0000000 --- a/lib/.jshintrc +++ /dev/null @@ -1,21 +0,0 @@ -{ - "predef": [ - "jQuery", - "Handlebars", - "Pixastic" - ], - "forin": true, - "noarg": true, - "noempty": true, - "eqeqeq": true, - "bitwise": true, - "strict": true, - "undef": true, - "unused": true, - "curly": true, - "browser": true, - "maxerr": 50, - "quotmark": "single", - "trailing": false, - "indent": 2 -} diff --git a/lib/ComicBook.js b/lib/ComicBook.js deleted file mode 100755 index c9499d0..0000000 --- a/lib/ComicBook.js +++ /dev/null @@ -1,879 +0,0 @@ -/* exported ComicBook */ - -var ComicBook = (function ($) { - - 'use strict'; - - /** - * Merge two arrays. Any properties in b will replace the same properties in - * a. New properties from b will be added to a. - * - * @param a {Object} - * @param b {Object} - */ - function merge(a, b) { - - var prop; - - if (typeof b === 'undefined') { b = {}; } - - for (prop in a) { - if (a.hasOwnProperty(prop)) { - if (prop in b) { continue; } - b[prop] = a[prop]; - } - } - - return b; - } - - /** - * Exception class. Always throw an instance of this when throwing exceptions. - * - * @param {String} type - * @param {Object} object - * @returns {ComicBookException} - */ - var ComicBookException = { - INVALID_ACTION: 'invalid action', - INVALID_PAGE: 'invalid page', - INVALID_PAGE_TYPE: 'invalid page type', - UNDEFINED_CONTROL: 'undefined control', - INVALID_ZOOM_MODE: 'invalid zoom mode', - INVALID_NAVIGATION_EVENT: 'invalid navigation event' - }; - - function ComicBook(id, srcs, opts) { - - var self = this; - var canvas_id = id; // canvas element id - this.srcs = srcs; // array of image srcs for pages - - var defaults = { - displayMode: 'double', // single / double - zoomMode: 'fitWindow', // manual / fitWidth / fitWindow - manga: false, // true / false - enhance: {}, - keyboard: { - next: 78, - previous: 80, - toolbar: 84, - toggleLayout: 76 - }, - libPath: '/lib/', - forward_buffer: 3 - }; - - this.isMobile = false; - - // mobile enhancements - if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|Mobile/i.test(navigator.userAgent)) { - this.isMobile = true; - document.body.classList.add('mobile'); - defaults.displayMode = 'single'; - - window.addEventListener('load', function () { - setTimeout(function () { - window.scrollTo(0, 1); - }, 0); - }); - } - - var options = merge(defaults, opts); // options array for internal use - - var no_pages = srcs.length; - var pages = []; // array of preloaded Image objects - var canvas; // the HTML5 canvas object - var context; // the 2d drawing context - var loaded = []; // the images that have been loaded so far - var scale = 1; // page zoom scale, 1 = 100% - var is_double_page_spread = false; - var controlsRendered = false; // have the user controls been inserted into the dom yet? - var page_requested = false; // used to request non preloaded pages - var shiv = false; - - /** - * Gets the window.innerWidth - scrollbars - */ - function windowWidth() { - - var height = window.innerHeight + 1; - - if (shiv === false) { - shiv = $(document.createElement('div')) - .attr('id', 'cb-width-shiv') - .css({ - width: '100%', - position: 'absolute', - top: 0, - zIndex: '-1000' - }); - - $('body').append(shiv); - } - - shiv.height(height); - - return shiv.innerWidth(); - } - - /** - * enables the back button - */ - function checkHash() { - - var hash = getHash(); - - if (hash !== pointer && loaded.indexOf(hash) > -1) { - pointer = hash; - self.draw(); - } - } - - function getHash() { - var hash = parseInt(location.hash.substring(1),10) - 1 || 0; - if (hash < 0) { - setHash(0); - hash = 0; - } - return hash; - } - - function setHash(pageNo) { - location.hash = pageNo; - } - - // page hash on first load - var hash = getHash(); - - // the current page, can pass a default as a url hash - var pointer = (hash < srcs.length) ? hash : 0; - - /** - * Setup the canvas element for use throughout the class. - * - * @see #ComicBook.prototype.draw - * @see #ComicBook.prototype.enhance - */ - function init() { - - // setup canvas - canvas = document.getElementById(canvas_id); - context = canvas.getContext('2d'); - - // render user controls - if (controlsRendered === false) { - self.renderControls(); - controlsRendered = true; - } - - // add page controls - window.addEventListener('keydown', self.navigation, false); - window.addEventListener('hashchange', checkHash, false); - } - - window.addEventListener('touchstart', function (e) { - var $el = $(e.target); - if ($el.attr('id') === 'comic') { - self.toggleToolbar(); - } - if ($el.data('toggle') === 'dropdown' ) { - $el.siblings('.dropdown').toggle(); - } - }, false); - - /** - * Render Handlebars templates. Templates with data-trigger & data-action will - * have the specified events bound. - */ - ComicBook.prototype.renderControls = function () { - - var controls = {}, $toolbar; - - $.each(Handlebars.templates, function (name, template) { - - var $template = $(template().trim()); - controls[name] = $template; - - // add event listeners to controls that specify callbacks - $template.find('*').andSelf().filter('[data-action][data-trigger]').each(function () { - - var $this = $(this); - var trigger = $this.data('trigger'); - var action = $this.data('action'); - - // trigger a direct method if exists - if (typeof self[$this.data('action')] === 'function') { - $this.on(trigger, self[action]); - } - - // throw an event to be caught outside if the app code - $this.on(trigger, function (e) { - $(self).trigger(trigger, e); - }); - }); - - $(canvas).before($template); - }); - - this.controls = controls; - - $toolbar = this.getControl('toolbar'); - $toolbar - .find('.manga-' + options.manga).show().end() - .find('.manga-' + !options.manga).hide().end() - .find('.layout').hide().end().find('.layout-' + options.displayMode).show(); - - }; - - ComicBook.prototype.getControl = function (control) { - if (typeof this.controls[control] !== 'object') { - throw ComicBookException.UNDEFINED_CONTROL + ' ' + control; - } - return this.controls[control]; - }; - - ComicBook.prototype.showControl = function (control) { - this.getControl(control).show().addClass('open'); - }; - - ComicBook.prototype.hideControl = function (control) { - this.getControl(control).removeClass('open').hide(); - }; - - ComicBook.prototype.toggleControl = function (control) { - this.getControl(control).toggle().toggleClass('open'); - }; - - ComicBook.prototype.toggleLayout = function() { - - var $toolbar = self.getControl('toolbar'); - var displayMode = (options.displayMode === 'single') ? 'double' : 'single'; - - options.displayMode = displayMode; - - $toolbar.find('.layout').hide().end().find('.layout-' + options.displayMode).show(); - - self.drawPage(); - }; - - /** - * Get the image for a given page. - * - * @return Image - */ - ComicBook.prototype.getPage = function (i) { - - if (i < 0 || i > srcs.length-1) { - throw ComicBookException.INVALID_PAGE + ' ' + i; - } - - if (typeof pages[i] === 'object') { - return pages[i]; - } else { - page_requested = i; - this.showControl('loadingOverlay'); - } - }; - - /** - * @see #preload - */ - ComicBook.prototype.draw = function () { - - init(); - - // resize navigation controls - $('.navigate').outerHeight(window.innerHeight); - $('#cb-loading-overlay').outerWidth(windowWidth()).height(window.innerHeight); - - // preload images if needed - if (pages.length !== no_pages) { - this.preload(); - } else { - this.drawPage(); - } - }; - - /** - * Zoom the canvas - * - * @param new_scale {Number} Scale the canvas to this ratio - */ - ComicBook.prototype.zoom = function (new_scale) { - options.zoomMode = 'manual'; - scale = new_scale; - if (typeof this.getPage(pointer) === 'object') { this.drawPage(); } - }; - - ComicBook.prototype.zoomIn = function () { - self.zoom(scale + 0.1); - }; - - ComicBook.prototype.zoomOut = function () { - self.zoom(scale - 0.1); - }; - - ComicBook.prototype.fitWidth = function () { - options.zoomMode = 'fitWidth'; - self.drawPage(); - }; - - ComicBook.prototype.fitWindow = function () { - options.zoomMode = 'fitWindow'; - self.drawPage(); - }; - - /** - * Preload all images, draw the page only after a given number have been loaded. - * - * @see #drawPage - */ - ComicBook.prototype.preload = function () { - - var i = pointer; // the current page counter for this method - var rendered = false; - var queue = []; - - this.showControl('loadingOverlay'); - - function loadImage(i) { - - var page = new Image(); - page.src = srcs[i]; - - page.onload = function () { - - pages[i] = this; - loaded.push(i); - - $('#cb-progress-bar .progressbar-value').css('width', Math.floor((loaded.length / no_pages) * 100) + '%'); - - // double page mode needs an extra page added - var buffer = (options.displayMode === 'double' && pointer < srcs.length-1) ? 1 : 0; - - // start rendering the comic when the requested page is ready - if ((rendered === false && ($.inArray(pointer + buffer, loaded) !== -1) || - (typeof page_requested === 'number' && $.inArray(page_requested, loaded) !== -1)) - ) { - // if the user is waiting for a page to be loaded, render that one instead of the default pointer - if (typeof page_requested === 'number') { - pointer = page_requested-1; - page_requested = false; - } - - self.drawPage(); - self.hideControl('loadingOverlay'); - rendered = true; - } - - if (queue.length) { - loadImage(queue[0]); - queue.splice(0,1); - } else { - $('#cb-status').delay(500).fadeOut(); - } - }; - } - - // loads pages in both directions so you don't have to wait for all pages - // to be loaded before you can scroll backwards - function preload(start, stop) { - - var j = 0; - var count = 1; - var forward = start; - var backward = start-1; - - while (forward <= stop) { - - if (count > options.forward_buffer && backward > -1) { - queue.push(backward); - backward--; - count = 0; - } else { - queue.push(forward); - forward++; - } - count++; - } - - while (backward > -1) { - queue.push(backward); - backward--; - } - - loadImage(queue[j]); - } - - preload(i, srcs.length-1); - }; - - ComicBook.prototype.pageLoaded = function (page_no) { - return (typeof loaded[page_no-1] !== 'undefined'); - }; - - /** - * Draw the current page in the canvas - */ - ComicBook.prototype.drawPage = function(page_no, reset_scroll) { - - var scrollY; - - reset_scroll = (typeof reset_scroll !== 'undefined') ? reset_scroll : true; - scrollY = reset_scroll ? 0 : window.scrollY; - - // if a specific page is given try to render it, if not bail and wait for preload() to render it - if (typeof page_no === 'number' && page_no < srcs.length && page_no > 0) { - pointer = page_no-1; - if (!this.pageLoaded(page_no)) { - this.showControl('loadingOverlay'); - return; - } - } - - if (pointer < 0) { pointer = 0; } - - var zoom_scale; - var offsetW = 0, offsetH = 0; - - var page = self.getPage(pointer); - var page2 = false; - - if (options.displayMode === 'double' && pointer < srcs.length-1) { - page2 = self.getPage(pointer + 1); - } - - if (typeof page !== 'object') { - throw ComicBookException.INVALID_PAGE_TYPE + ' ' + typeof page; - } - - var width = page.width, height = page.height; - - // reset the canvas to stop duplicate pages showing - canvas.width = 0; - canvas.height = 0; - - // show double page spreads on a single page - is_double_page_spread = ( - typeof page2 === 'object' && - (page.width > page.height || page2.width > page2.height) && - options.displayMode === 'double' - ); - if (is_double_page_spread) { options.displayMode = 'single'; } - - if (options.displayMode === 'double') { - - // for double page spreads, factor in the width of both pages - if (typeof page2 === 'object') { width += page2.width; } - - // if this is the last page and there is no page2, still keep the canvas wide - else { width += width; } - } - - // update the page scale if a non manual mode has been chosen - switch (options.zoomMode) { - - case 'manual': - document.body.style.overflowX = 'auto'; - zoom_scale = (options.displayMode === 'double') ? scale * 2 : scale; - break; - - case 'fitWidth': - document.body.style.overflowX = 'hidden'; - - // scale up if the window is wider than the page, scale down if the window - // is narrower than the page - zoom_scale = (windowWidth() > width) ? ((windowWidth() - width) / windowWidth()) + 1 : windowWidth() / width; - - // update the interal scale var so switching zoomModes while zooming will be smooth - scale = zoom_scale; - break; - - case 'fitWindow': - document.body.style.overflowX = 'hidden'; - - var width_scale = (windowWidth() > width) ? - ((windowWidth() - width) / windowWidth()) + 1 // scale up if the window is wider than the page - : windowWidth() / width; // scale down if the window is narrower than the page - var windowHeight = window.innerHeight; - var height_scale = (windowHeight > height) ? - ((windowHeight - height) / windowHeight) + 1 // scale up if the window is wider than the page - : windowHeight / height; // scale down if the window is narrower than the page - - zoom_scale = (width_scale > height_scale) ? height_scale : width_scale; - scale = zoom_scale; - break; - - default: - throw ComicBookException.INVALID_ZOOM_MODE + ' ' + options.zoomMode; - } - - var canvas_width = page.width * zoom_scale; - var canvas_height = page.height * zoom_scale; - - var page_width = (options.zoomMode === 'manual') ? page.width * scale : canvas_width; - var page_height = (options.zoomMode === 'manual') ? page.height * scale : canvas_height; - - canvas_height = page_height; - - // make sure the canvas is always at least full screen, even if the page is more narrow than the screen - canvas.width = (canvas_width < windowWidth()) ? windowWidth() : canvas_width; - canvas.height = (canvas_height < window.innerHeight) ? window.innerHeight : canvas_height; - - // always keep pages centered - if (options.zoomMode === 'manual' || options.zoomMode === 'fitWindow') { - - // work out a horizontal position - if (canvas_width < windowWidth()) { - offsetW = (windowWidth() - page_width) / 2; - if (options.displayMode === 'double') { offsetW = offsetW - page_width / 2; } - } - - // work out a vertical position - if (canvas_height < window.innerHeight) { - offsetH = (window.innerHeight - page_height) / 2; - } - } - - // in manga double page mode reverse the page(s) - if (options.manga && options.displayMode === 'double' && typeof page2 === 'object') { - var tmpPage = page; - var tmpPage2 = page2; - page = tmpPage2; - page2 = tmpPage; - } - - // draw the page(s) - context.drawImage(page, offsetW, offsetH, page_width, page_height); - if (options.displayMode === 'double' && typeof page2 === 'object') { - context.drawImage(page2, page_width + offsetW, offsetH, page_width, page_height); - } - - this.pixastic = new Pixastic(context, options.libPath + 'pixastic/'); - - // apply any image enhancements previously defined - $.each(options.enhance, function(action, options) { - self.enhance[action](options); - }); - - var current_page = - (options.displayMode === 'double' && - pointer + 2 <= srcs.length) ? (pointer + 1) + '-' + (pointer + 2) : pointer + 1; - - this.getControl('toolbar') - .find('#current-page').text(current_page) - .end() - .find('#page-count').text(srcs.length); - - // revert page mode back to double if it was auto switched for a double page spread - if (is_double_page_spread) { options.displayMode = 'double'; } - - // disable the fit width button if needed - $('button.cb-fit-width').attr('disabled', (options.zoomMode === 'fitWidth')); - $('button.cb-fit-window').attr('disabled', (options.zoomMode === 'fitWindow')); - - // disable prev/next buttons if not needed - $('.navigate').show(); - if (pointer === 0) { - if (options.manga) { - $('.navigate-left').show(); - $('.navigate-right').hide(); - } else { - $('.navigate-left').hide(); - $('.navigate-right').show(); - } - } - - if (pointer === srcs.length-1 || (typeof page2 === 'object' && pointer === srcs.length-2)) { - if (options.manga) { - $('.navigate-left').hide(); - $('.navigate-right').show(); - } else { - $('.navigate-left').show(); - $('.navigate-right').hide(); - } - } - - if (pointer !== getHash()){ - $(this).trigger('navigate'); - } - - // update hash location - if (getHash() !== pointer) { - setHash(pointer + 1); - } - }; - - /** - * Increment the counter and draw the page in the canvas - * - * @see #drawPage - */ - ComicBook.prototype.drawNextPage = function () { - - var page; - - try { - page = self.getPage(pointer+1); - } catch (e) {} - - if (!page) { return false; } - - if (pointer + 1 < pages.length) { - pointer += (options.displayMode === 'single' || is_double_page_spread) ? 1 : 2; - try { - self.drawPage(); - } catch (e) {} - } - - // make sure the top of the page is in view - window.scroll(0, 0); - }; - - /** - * Decrement the counter and draw the page in the canvas - * - * @see #drawPage - */ - ComicBook.prototype.drawPrevPage = function () { - - var page; - - try { - page = self.getPage(pointer-1); - } catch (e) {} - - if (!page) { return false; } - - is_double_page_spread = (page.width > page.height); // need to run double page check again here as we are going backwards - - if (pointer > 0) { - pointer -= (options.displayMode === 'single' || is_double_page_spread) ? 1 : 2; - self.drawPage(); - } - - // make sure the top of the page is in view - window.scroll(0, 0); - }; - - ComicBook.prototype.brightness = function () { - self.enhance.brightness({ brightness: $(this).val() }); - }; - - ComicBook.prototype.contrast = function () { - self.enhance.brightness({ contrast: $(this).val() }); - }; - - ComicBook.prototype.sharpen = function () { - self.enhance.sharpen({ strength: $(this).val() }); - }; - - ComicBook.prototype.desaturate = function () { - if ($(this).is(':checked')) { - self.enhance.desaturate(); - } else { - self.enhance.resaturate(); - } - }; - - ComicBook.prototype.resetEnhancements = function () { - self.enhance.reset(); - }; - - /** - * Apply image enhancements to the canvas. - * - * Powered by the awesome Pixastic: http://www.pixastic.com/ - * - * TODO: reset & apply all image enhancements each time before applying new one - * TODO: abstract this into an 'Enhance' object, separate from ComicBook? - */ - ComicBook.prototype.enhance = { - - /** - * Reset enhancements. - * This can reset a specific enhancement if the method name is passed, or - * it will reset all. - * - * @param method {string} the specific enhancement to reset - */ - reset: function (method) { - if (!method) { - options.enhance = {}; - } else { - delete options.enhance[method]; - } - self.drawPage(null, false); - }, - - /** - * Pixastic progress callback - * @param {float} progress - */ - // progress: function (progress) { - progress: function () { - // console.info(Math.floor(progress * 100)); - }, - - /** - * Pixastic on complete callback - */ - done: function () { - - }, - - /** - * Adjust brightness / contrast - * - * params - * brightness (int) -150 to 150 - * contrast: (float) -1 to infinity - * - * @param {Object} params Brightness & contrast levels - * @param {Boolean} reset Reset before applying more enhancements? - */ - brightness: function (params, reset) { - - if (reset !== false) { this.reset('brightness'); } - - // merge user options with defaults - var opts = merge({ brightness: 0, contrast: 0 }, params); - - // remember options for later - options.enhance.brightness = opts; - - // run the enhancement - self.pixastic.brightness({ - brightness: opts.brightness, - contrast: opts.contrast - }).done(this.done, this.progress); - }, - - /** - * Force black and white - */ - desaturate: function () { - options.enhance.desaturate = {}; - self.pixastic.desaturate().done(this.done, this.progress); - }, - - /** - * Undo desaturate - */ - resaturate: function() { - delete options.enhance.desaturate; - self.drawPage(null, false); - }, - - /** - * Sharpen - * - * options: - * strength: number (-1 to infinity) - * - * @param {Object} options - */ - sharpen: function (params) { - - this.desharpen(); - - var opts = merge({ strength: 0 }, params); - - options.enhance.sharpen = opts; - - self.pixastic.sharpen3x3({ - strength: opts.strength - }).done(this.done, this.progress); - }, - - desharpen: function() { - delete options.enhance.sharpen; - self.drawPage(null, false); - } - }; - - ComicBook.prototype.navigation = function (e) { - - // disable navigation when the overlay is showing - if ($('#cb-loading-overlay').is(':visible')) { return false; } - - var side = false; - - switch (e.type) { - - case 'click': - side = e.currentTarget.getAttribute('data-navigate-side'); - break; - - case 'keydown': - - // navigation - if (e.keyCode === options.keyboard.previous) { side = 'left'; } - if (e.keyCode === options.keyboard.next) { side = 'right'; } - - // display controls - if (e.keyCode === options.keyboard.toolbar) { - self.toggleToolbar(); - } - if (e.keyCode === options.keyboard.toggleLayout) { - self.toggleLayout(); - } - break; - - default: - throw ComicBookException.INVALID_NAVIGATION_EVENT + ' ' + e.type; - } - - if (side) { - - e.stopPropagation(); - - // western style (left to right) - if (!options.manga) { - if (side === 'left') { self.drawPrevPage(); } - if (side === 'right') { self.drawNextPage(); } - } - // manga style (right to left) - else { - if (side === 'left') { self.drawNextPage(); } - if (side === 'right') { self.drawPrevPage(); } - } - - return false; - } - }; - - ComicBook.prototype.toggleReadingMode = function () { - options.manga = !options.manga; - self.getControl('toolbar') - .find('.manga-' + options.manga).show().end() - .find('.manga-' + !options.manga).hide(); - }; - - ComicBook.prototype.toggleToolbar = function () { - self.toggleControl('toolbar'); - }; - - ComicBook.prototype.destroy = function () { - - $.each(this.controls, function (name, $control) { - $control.remove(); - }); - - canvas.width = 0; - canvas.height = 0; - - window.removeEventListener('keydown', this.navigation, false); - window.removeEventListener('hashchange', checkHash, false); - - setHash(''); - - // $(this).trigger('destroy'); - }; - - } - - return ComicBook; - -})(jQuery); diff --git a/lib/tests/img/1.png b/lib/tests/img/1.png deleted file mode 100755 index a8d2c9a..0000000 Binary files a/lib/tests/img/1.png and /dev/null differ diff --git a/lib/tests/img/2.png b/lib/tests/img/2.png deleted file mode 100755 index c41255b..0000000 Binary files a/lib/tests/img/2.png and /dev/null differ diff --git a/lib/tests/img/3.png b/lib/tests/img/3.png deleted file mode 100755 index c8e349a..0000000 Binary files a/lib/tests/img/3.png and /dev/null differ diff --git a/lib/tests/img/4.png b/lib/tests/img/4.png deleted file mode 100755 index 0a334c2..0000000 Binary files a/lib/tests/img/4.png and /dev/null differ diff --git a/lib/tests/img/5.png b/lib/tests/img/5.png deleted file mode 100755 index fa53250..0000000 Binary files a/lib/tests/img/5.png and /dev/null differ diff --git a/lib/tests/img/6.png b/lib/tests/img/6.png deleted file mode 100755 index f18c20c..0000000 Binary files a/lib/tests/img/6.png and /dev/null differ diff --git a/lib/tests/index.html b/lib/tests/index.html deleted file mode 100644 index fc2ecef..0000000 --- a/lib/tests/index.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - Comic Book Reader Test Suite - - - - - - - - - - - - - - - - - -
-
-
-
- - diff --git a/lib/tests/phantom.js b/lib/tests/phantom.js deleted file mode 100644 index 3a5de31..0000000 --- a/lib/tests/phantom.js +++ /dev/null @@ -1,63 +0,0 @@ -// Simple phantom.js integration script -// Taken from Twitter Bootstrap - -function waitFor(testFx, onReady, timeOutMillis) { - var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 5001 //< Default Max Timout is 5s - , start = new Date().getTime() - , condition = false - , interval = setInterval(function () { - if ((new Date().getTime() - start < maxtimeOutMillis) && !condition) { - // If not time-out yet and condition not yet fulfilled - condition = (typeof(testFx) === "string" ? eval(testFx) : testFx()) //< defensive code - } else { - if (!condition) { - // If condition still not fulfilled (timeout but condition is 'false') - console.log("'waitFor()' timeout") - phantom.exit(1) - } else { - // Condition fulfilled (timeout and/or condition is 'true') - typeof(onReady) === "string" ? eval(onReady) : onReady() //< Do what it's supposed to do once the condition is fulfilled - clearInterval(interval) //< Stop this interval - } - } - }, 100) //< repeat check every 100ms -} - - -if (phantom.args.length === 0 || phantom.args.length > 2) { - console.log('Usage: phantom.js URL') - phantom.exit() -} - -var page = new WebPage() - -// Route "console.log()" calls from within the Page context to the main Phantom context (i.e. current "this") -page.onConsoleMessage = function(msg) { - console.log(msg) -}; - -page.open(phantom.args[0], function(status){ - if (status !== "success") { - console.log("Unable to access network") - phantom.exit() - } else { - waitFor(function(){ - return page.evaluate(function(){ - var el = document.getElementById('qunit-testresult') - if (el && el.innerText.match('completed')) { - return true - } - return false - }) - }, function(){ - var failedNum = page.evaluate(function(){ - var el = document.getElementById('qunit-testresult') - try { - return el.getElementsByClassName('failed')[0].innerHTML - } catch (e) { } - return 10000 - }); - phantom.exit((parseInt(failedNum, 10) > 0) ? 1 : 0) - }) - } -}) diff --git a/lib/tests/server.js b/lib/tests/server.js deleted file mode 100755 index 3964d86..0000000 --- a/lib/tests/server.js +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Simple connect server for phantom.js - * Adapted from Twitter Bootstrap - */ - -var connect = require('connect'), - http = require('http'), - fs = require('fs'), - app = connect(), - pid_path = __dirname + '/pid.txt'; - -// clean up after failed test runs -if (fs.existsSync(pid_path)) { - try { - var pid = fs.readFileSync(pid_path, { encoding: 'utf-8' }); - process.kill(pid, 'SIGHUP'); - } catch (e) {} - fs.unlinkSync(pid_path); -} - -app.use(connect.static(__dirname + '/../../')); - -http.createServer(app).listen(3000); - -fs.writeFileSync(pid_path, process.pid, 'utf-8'); diff --git a/lib/tests/unit/ComicBook.js b/lib/tests/unit/ComicBook.js deleted file mode 100644 index fc93ddd..0000000 --- a/lib/tests/unit/ComicBook.js +++ /dev/null @@ -1,62 +0,0 @@ -/* global $: false, module: false, test: false, equal: false, ComicBook: false console: false */ - -$(function () { - - 'use strict'; - - var $fixture; - var book; - - function initBook() { - - $fixture = $('#qunit-fixture'); - $fixture.append(''); - - book = new ComicBook( - 'comic', - ['img/1.png','img/2.png','img/3.png','img/4.png','img/5.png','img/6.png'], - { libPath: '../vendor/' } - ); - } - - module('not yet rendered comic', { - setup: initBook - }); - - test('controls shouldn\'t be renderd yet', function () { - equal($('.cb-control, .toolbar').length, 0, 'book not drawn yet, nothing should be rendered'); - }); - - module('rendered comic', { - - setup: function () { - initBook(); - book.draw(); - }, - - teardown: function () { - } - }); - - test('all controls should be rendered', function () { - equal($('.cb-control, .toolbar').length, 5, 'All toolbar elements should have rendered after book.draw'); - }); - - // navigate on keyboard - // don't navigate if nothing left - // show current page - // customise keyboard control - // dropdown menus - // apply effects - // maximise - // minimise - // fit width - // single page / double page - // single page should allow double page spreads - // preloading - // update hash - // resume based on hash - // load from middle of page - // emit custom events based on data-attributes - // destroy -}); diff --git a/lib/tests/unit/logger.js b/lib/tests/unit/logger.js deleted file mode 100644 index 96514d5..0000000 --- a/lib/tests/unit/logger.js +++ /dev/null @@ -1,24 +0,0 @@ -/* jshint strict: false */ -/* global console: false, QUnit: false */ - -// Logging setup for phantom integration -// Taken from Twitter Bootstrap - -QUnit.begin = function () { - console.log('Starting test suite'); - console.log('================================================\n'); -}; - -QUnit.moduleDone = function (opts) { - if (opts.failed === 0) { - console.log('\u2714 All tests passed in "' + opts.name + '" module'); - } else { - console.log('\u2716 ' + opts.failed + ' tests failed in "' + opts.name + '" module'); - } -}; - -QUnit.done = function (opts) { - console.log('\n================================================'); - console.log('Tests completed in ' + opts.runtime + ' milliseconds'); - console.log(opts.passed + ' tests of ' + opts.total + ' passed, ' + opts.failed + ' failed.'); -}; diff --git a/lib/tests/vendor/qunit-1.11.0.css b/lib/tests/vendor/qunit-1.11.0.css deleted file mode 100644 index d7fc0c8..0000000 --- a/lib/tests/vendor/qunit-1.11.0.css +++ /dev/null @@ -1,244 +0,0 @@ -/** - * QUnit v1.11.0 - A JavaScript Unit Testing Framework - * - * http://qunitjs.com - * - * Copyright 2012 jQuery Foundation and other contributors - * Released under the MIT license. - * http://jquery.org/license - */ - -/** Font Family and Sizes */ - -#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult { - font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif; -} - -#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } -#qunit-tests { font-size: smaller; } - - -/** Resets */ - -#qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter { - margin: 0; - padding: 0; -} - - -/** Header */ - -#qunit-header { - padding: 0.5em 0 0.5em 1em; - - color: #8699a4; - background-color: #0d3349; - - font-size: 1.5em; - line-height: 1em; - font-weight: normal; - - border-radius: 5px 5px 0 0; - -moz-border-radius: 5px 5px 0 0; - -webkit-border-top-right-radius: 5px; - -webkit-border-top-left-radius: 5px; -} - -#qunit-header a { - text-decoration: none; - color: #c2ccd1; -} - -#qunit-header a:hover, -#qunit-header a:focus { - color: #fff; -} - -#qunit-testrunner-toolbar label { - display: inline-block; - padding: 0 .5em 0 .1em; -} - -#qunit-banner { - height: 5px; -} - -#qunit-testrunner-toolbar { - padding: 0.5em 0 0.5em 2em; - color: #5E740B; - background-color: #eee; - overflow: hidden; -} - -#qunit-userAgent { - padding: 0.5em 0 0.5em 2.5em; - background-color: #2b81af; - color: #fff; - text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; -} - -#qunit-modulefilter-container { - float: right; -} - -/** Tests: Pass/Fail */ - -#qunit-tests { - list-style-position: inside; -} - -#qunit-tests li { - padding: 0.4em 0.5em 0.4em 2.5em; - border-bottom: 1px solid #fff; - list-style-position: inside; -} - -#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running { - display: none; -} - -#qunit-tests li strong { - cursor: pointer; -} - -#qunit-tests li a { - padding: 0.5em; - color: #c2ccd1; - text-decoration: none; -} -#qunit-tests li a:hover, -#qunit-tests li a:focus { - color: #000; -} - -#qunit-tests li .runtime { - float: right; - font-size: smaller; -} - -.qunit-assert-list { - margin-top: 0.5em; - padding: 0.5em; - - background-color: #fff; - - border-radius: 5px; - -moz-border-radius: 5px; - -webkit-border-radius: 5px; -} - -.qunit-collapsed { - display: none; -} - -#qunit-tests table { - border-collapse: collapse; - margin-top: .2em; -} - -#qunit-tests th { - text-align: right; - vertical-align: top; - padding: 0 .5em 0 0; -} - -#qunit-tests td { - vertical-align: top; -} - -#qunit-tests pre { - margin: 0; - white-space: pre-wrap; - word-wrap: break-word; -} - -#qunit-tests del { - background-color: #e0f2be; - color: #374e0c; - text-decoration: none; -} - -#qunit-tests ins { - background-color: #ffcaca; - color: #500; - text-decoration: none; -} - -/*** Test Counts */ - -#qunit-tests b.counts { color: black; } -#qunit-tests b.passed { color: #5E740B; } -#qunit-tests b.failed { color: #710909; } - -#qunit-tests li li { - padding: 5px; - background-color: #fff; - border-bottom: none; - list-style-position: inside; -} - -/*** Passing Styles */ - -#qunit-tests li li.pass { - color: #3c510c; - background-color: #fff; - border-left: 10px solid #C6E746; -} - -#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } -#qunit-tests .pass .test-name { color: #366097; } - -#qunit-tests .pass .test-actual, -#qunit-tests .pass .test-expected { color: #999999; } - -#qunit-banner.qunit-pass { background-color: #C6E746; } - -/*** Failing Styles */ - -#qunit-tests li li.fail { - color: #710909; - background-color: #fff; - border-left: 10px solid #EE5757; - white-space: pre; -} - -#qunit-tests > li:last-child { - border-radius: 0 0 5px 5px; - -moz-border-radius: 0 0 5px 5px; - -webkit-border-bottom-right-radius: 5px; - -webkit-border-bottom-left-radius: 5px; -} - -#qunit-tests .fail { color: #000000; background-color: #EE5757; } -#qunit-tests .fail .test-name, -#qunit-tests .fail .module-name { color: #000000; } - -#qunit-tests .fail .test-actual { color: #EE5757; } -#qunit-tests .fail .test-expected { color: green; } - -#qunit-banner.qunit-fail { background-color: #EE5757; } - - -/** Result */ - -#qunit-testresult { - padding: 0.5em 0.5em 0.5em 2.5em; - - color: #2b81af; - background-color: #D2E0E6; - - border-bottom: 1px solid white; -} -#qunit-testresult .module-name { - font-weight: bold; -} - -/** Fixture */ - -#qunit-fixture { - position: absolute; - top: -10000px; - left: -10000px; - width: 1000px; - height: 1000px; -} diff --git a/lib/tests/vendor/qunit-1.11.0.js b/lib/tests/vendor/qunit-1.11.0.js deleted file mode 100644 index 302545f..0000000 --- a/lib/tests/vendor/qunit-1.11.0.js +++ /dev/null @@ -1,2152 +0,0 @@ -/** - * QUnit v1.11.0 - A JavaScript Unit Testing Framework - * - * http://qunitjs.com - * - * Copyright 2012 jQuery Foundation and other contributors - * Released under the MIT license. - * http://jquery.org/license - */ - -(function( window ) { - -var QUnit, - assert, - config, - onErrorFnPrev, - testId = 0, - fileName = (sourceFromStacktrace( 0 ) || "" ).replace(/(:\d+)+\)?/, "").replace(/.+\//, ""), - toString = Object.prototype.toString, - hasOwn = Object.prototype.hasOwnProperty, - // Keep a local reference to Date (GH-283) - Date = window.Date, - defined = { - setTimeout: typeof window.setTimeout !== "undefined", - sessionStorage: (function() { - var x = "qunit-test-string"; - try { - sessionStorage.setItem( x, x ); - sessionStorage.removeItem( x ); - return true; - } catch( e ) { - return false; - } - }()) - }, - /** - * Provides a normalized error string, correcting an issue - * with IE 7 (and prior) where Error.prototype.toString is - * not properly implemented - * - * Based on http://es5.github.com/#x15.11.4.4 - * - * @param {String|Error} error - * @return {String} error message - */ - errorString = function( error ) { - var name, message, - errorString = error.toString(); - if ( errorString.substring( 0, 7 ) === "[object" ) { - name = error.name ? error.name.toString() : "Error"; - message = error.message ? error.message.toString() : ""; - if ( name && message ) { - return name + ": " + message; - } else if ( name ) { - return name; - } else if ( message ) { - return message; - } else { - return "Error"; - } - } else { - return errorString; - } - }, - /** - * Makes a clone of an object using only Array or Object as base, - * and copies over the own enumerable properties. - * - * @param {Object} obj - * @return {Object} New object with only the own properties (recursively). - */ - objectValues = function( obj ) { - // Grunt 0.3.x uses an older version of jshint that still has jshint/jshint#392. - /*jshint newcap: false */ - var key, val, - vals = QUnit.is( "array", obj ) ? [] : {}; - for ( key in obj ) { - if ( hasOwn.call( obj, key ) ) { - val = obj[key]; - vals[key] = val === Object(val) ? objectValues(val) : val; - } - } - return vals; - }; - -function Test( settings ) { - extend( this, settings ); - this.assertions = []; - this.testNumber = ++Test.count; -} - -Test.count = 0; - -Test.prototype = { - init: function() { - var a, b, li, - tests = id( "qunit-tests" ); - - if ( tests ) { - b = document.createElement( "strong" ); - b.innerHTML = this.nameHtml; - - // `a` initialized at top of scope - a = document.createElement( "a" ); - a.innerHTML = "Rerun"; - a.href = QUnit.url({ testNumber: this.testNumber }); - - li = document.createElement( "li" ); - li.appendChild( b ); - li.appendChild( a ); - li.className = "running"; - li.id = this.id = "qunit-test-output" + testId++; - - tests.appendChild( li ); - } - }, - setup: function() { - if ( this.module !== config.previousModule ) { - if ( config.previousModule ) { - runLoggingCallbacks( "moduleDone", QUnit, { - name: config.previousModule, - failed: config.moduleStats.bad, - passed: config.moduleStats.all - config.moduleStats.bad, - total: config.moduleStats.all - }); - } - config.previousModule = this.module; - config.moduleStats = { all: 0, bad: 0 }; - runLoggingCallbacks( "moduleStart", QUnit, { - name: this.module - }); - } else if ( config.autorun ) { - runLoggingCallbacks( "moduleStart", QUnit, { - name: this.module - }); - } - - config.current = this; - - this.testEnvironment = extend({ - setup: function() {}, - teardown: function() {} - }, this.moduleTestEnvironment ); - - this.started = +new Date(); - runLoggingCallbacks( "testStart", QUnit, { - name: this.testName, - module: this.module - }); - - // allow utility functions to access the current test environment - // TODO why?? - QUnit.current_testEnvironment = this.testEnvironment; - - if ( !config.pollution ) { - saveGlobal(); - } - if ( config.notrycatch ) { - this.testEnvironment.setup.call( this.testEnvironment ); - return; - } - try { - this.testEnvironment.setup.call( this.testEnvironment ); - } catch( e ) { - QUnit.pushFailure( "Setup failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) ); - } - }, - run: function() { - config.current = this; - - var running = id( "qunit-testresult" ); - - if ( running ) { - running.innerHTML = "Running:
" + this.nameHtml; - } - - if ( this.async ) { - QUnit.stop(); - } - - this.callbackStarted = +new Date(); - - if ( config.notrycatch ) { - this.callback.call( this.testEnvironment, QUnit.assert ); - this.callbackRuntime = +new Date() - this.callbackStarted; - return; - } - - try { - this.callback.call( this.testEnvironment, QUnit.assert ); - this.callbackRuntime = +new Date() - this.callbackStarted; - } catch( e ) { - this.callbackRuntime = +new Date() - this.callbackStarted; - - QUnit.pushFailure( "Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) ); - // else next test will carry the responsibility - saveGlobal(); - - // Restart the tests if they're blocking - if ( config.blocking ) { - QUnit.start(); - } - } - }, - teardown: function() { - config.current = this; - if ( config.notrycatch ) { - if ( typeof this.callbackRuntime === "undefined" ) { - this.callbackRuntime = +new Date() - this.callbackStarted; - } - this.testEnvironment.teardown.call( this.testEnvironment ); - return; - } else { - try { - this.testEnvironment.teardown.call( this.testEnvironment ); - } catch( e ) { - QUnit.pushFailure( "Teardown failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) ); - } - } - checkPollution(); - }, - finish: function() { - config.current = this; - if ( config.requireExpects && this.expected === null ) { - QUnit.pushFailure( "Expected number of assertions to be defined, but expect() was not called.", this.stack ); - } else if ( this.expected !== null && this.expected !== this.assertions.length ) { - QUnit.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack ); - } else if ( this.expected === null && !this.assertions.length ) { - QUnit.pushFailure( "Expected at least one assertion, but none were run - call expect(0) to accept zero assertions.", this.stack ); - } - - var i, assertion, a, b, time, li, ol, - test = this, - good = 0, - bad = 0, - tests = id( "qunit-tests" ); - - this.runtime = +new Date() - this.started; - config.stats.all += this.assertions.length; - config.moduleStats.all += this.assertions.length; - - if ( tests ) { - ol = document.createElement( "ol" ); - ol.className = "qunit-assert-list"; - - for ( i = 0; i < this.assertions.length; i++ ) { - assertion = this.assertions[i]; - - li = document.createElement( "li" ); - li.className = assertion.result ? "pass" : "fail"; - li.innerHTML = assertion.message || ( assertion.result ? "okay" : "failed" ); - ol.appendChild( li ); - - if ( assertion.result ) { - good++; - } else { - bad++; - config.stats.bad++; - config.moduleStats.bad++; - } - } - - // store result when possible - if ( QUnit.config.reorder && defined.sessionStorage ) { - if ( bad ) { - sessionStorage.setItem( "qunit-test-" + this.module + "-" + this.testName, bad ); - } else { - sessionStorage.removeItem( "qunit-test-" + this.module + "-" + this.testName ); - } - } - - if ( bad === 0 ) { - addClass( ol, "qunit-collapsed" ); - } - - // `b` initialized at top of scope - b = document.createElement( "strong" ); - b.innerHTML = this.nameHtml + " (" + bad + ", " + good + ", " + this.assertions.length + ")"; - - addEvent(b, "click", function() { - var next = b.parentNode.lastChild, - collapsed = hasClass( next, "qunit-collapsed" ); - ( collapsed ? removeClass : addClass )( next, "qunit-collapsed" ); - }); - - addEvent(b, "dblclick", function( e ) { - var target = e && e.target ? e.target : window.event.srcElement; - if ( target.nodeName.toLowerCase() === "span" || target.nodeName.toLowerCase() === "b" ) { - target = target.parentNode; - } - if ( window.location && target.nodeName.toLowerCase() === "strong" ) { - window.location = QUnit.url({ testNumber: test.testNumber }); - } - }); - - // `time` initialized at top of scope - time = document.createElement( "span" ); - time.className = "runtime"; - time.innerHTML = this.runtime + " ms"; - - // `li` initialized at top of scope - li = id( this.id ); - li.className = bad ? "fail" : "pass"; - li.removeChild( li.firstChild ); - a = li.firstChild; - li.appendChild( b ); - li.appendChild( a ); - li.appendChild( time ); - li.appendChild( ol ); - - } else { - for ( i = 0; i < this.assertions.length; i++ ) { - if ( !this.assertions[i].result ) { - bad++; - config.stats.bad++; - config.moduleStats.bad++; - } - } - } - - runLoggingCallbacks( "testDone", QUnit, { - name: this.testName, - module: this.module, - failed: bad, - passed: this.assertions.length - bad, - total: this.assertions.length, - duration: this.runtime - }); - - QUnit.reset(); - - config.current = undefined; - }, - - queue: function() { - var bad, - test = this; - - synchronize(function() { - test.init(); - }); - function run() { - // each of these can by async - synchronize(function() { - test.setup(); - }); - synchronize(function() { - test.run(); - }); - synchronize(function() { - test.teardown(); - }); - synchronize(function() { - test.finish(); - }); - } - - // `bad` initialized at top of scope - // defer when previous test run passed, if storage is available - bad = QUnit.config.reorder && defined.sessionStorage && - +sessionStorage.getItem( "qunit-test-" + this.module + "-" + this.testName ); - - if ( bad ) { - run(); - } else { - synchronize( run, true ); - } - } -}; - -// Root QUnit object. -// `QUnit` initialized at top of scope -QUnit = { - - // call on start of module test to prepend name to all tests - module: function( name, testEnvironment ) { - config.currentModule = name; - config.currentModuleTestEnvironment = testEnvironment; - config.modules[name] = true; - }, - - asyncTest: function( testName, expected, callback ) { - if ( arguments.length === 2 ) { - callback = expected; - expected = null; - } - - QUnit.test( testName, expected, callback, true ); - }, - - test: function( testName, expected, callback, async ) { - var test, - nameHtml = "" + escapeText( testName ) + ""; - - if ( arguments.length === 2 ) { - callback = expected; - expected = null; - } - - if ( config.currentModule ) { - nameHtml = "" + escapeText( config.currentModule ) + ": " + nameHtml; - } - - test = new Test({ - nameHtml: nameHtml, - testName: testName, - expected: expected, - async: async, - callback: callback, - module: config.currentModule, - moduleTestEnvironment: config.currentModuleTestEnvironment, - stack: sourceFromStacktrace( 2 ) - }); - - if ( !validTest( test ) ) { - return; - } - - test.queue(); - }, - - // Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through. - expect: function( asserts ) { - if (arguments.length === 1) { - config.current.expected = asserts; - } else { - return config.current.expected; - } - }, - - start: function( count ) { - // QUnit hasn't been initialized yet. - // Note: RequireJS (et al) may delay onLoad - if ( config.semaphore === undefined ) { - QUnit.begin(function() { - // This is triggered at the top of QUnit.load, push start() to the event loop, to allow QUnit.load to finish first - setTimeout(function() { - QUnit.start( count ); - }); - }); - return; - } - - config.semaphore -= count || 1; - // don't start until equal number of stop-calls - if ( config.semaphore > 0 ) { - return; - } - // ignore if start is called more often then stop - if ( config.semaphore < 0 ) { - config.semaphore = 0; - QUnit.pushFailure( "Called start() while already started (QUnit.config.semaphore was 0 already)", null, sourceFromStacktrace(2) ); - return; - } - // A slight delay, to avoid any current callbacks - if ( defined.setTimeout ) { - window.setTimeout(function() { - if ( config.semaphore > 0 ) { - return; - } - if ( config.timeout ) { - clearTimeout( config.timeout ); - } - - config.blocking = false; - process( true ); - }, 13); - } else { - config.blocking = false; - process( true ); - } - }, - - stop: function( count ) { - config.semaphore += count || 1; - config.blocking = true; - - if ( config.testTimeout && defined.setTimeout ) { - clearTimeout( config.timeout ); - config.timeout = window.setTimeout(function() { - QUnit.ok( false, "Test timed out" ); - config.semaphore = 1; - QUnit.start(); - }, config.testTimeout ); - } - } -}; - -// `assert` initialized at top of scope -// Asssert helpers -// All of these must either call QUnit.push() or manually do: -// - runLoggingCallbacks( "log", .. ); -// - config.current.assertions.push({ .. }); -// We attach it to the QUnit object *after* we expose the public API, -// otherwise `assert` will become a global variable in browsers (#341). -assert = { - /** - * Asserts rough true-ish result. - * @name ok - * @function - * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" ); - */ - ok: function( result, msg ) { - if ( !config.current ) { - throw new Error( "ok() assertion outside test context, was " + sourceFromStacktrace(2) ); - } - result = !!result; - - var source, - details = { - module: config.current.module, - name: config.current.testName, - result: result, - message: msg - }; - - msg = escapeText( msg || (result ? "okay" : "failed" ) ); - msg = "" + msg + ""; - - if ( !result ) { - source = sourceFromStacktrace( 2 ); - if ( source ) { - details.source = source; - msg += "
Source:
" + escapeText( source ) + "
"; - } - } - runLoggingCallbacks( "log", QUnit, details ); - config.current.assertions.push({ - result: result, - message: msg - }); - }, - - /** - * Assert that the first two arguments are equal, with an optional message. - * Prints out both actual and expected values. - * @name equal - * @function - * @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" ); - */ - equal: function( actual, expected, message ) { - /*jshint eqeqeq:false */ - QUnit.push( expected == actual, actual, expected, message ); - }, - - /** - * @name notEqual - * @function - */ - notEqual: function( actual, expected, message ) { - /*jshint eqeqeq:false */ - QUnit.push( expected != actual, actual, expected, message ); - }, - - /** - * @name propEqual - * @function - */ - propEqual: function( actual, expected, message ) { - actual = objectValues(actual); - expected = objectValues(expected); - QUnit.push( QUnit.equiv(actual, expected), actual, expected, message ); - }, - - /** - * @name notPropEqual - * @function - */ - notPropEqual: function( actual, expected, message ) { - actual = objectValues(actual); - expected = objectValues(expected); - QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message ); - }, - - /** - * @name deepEqual - * @function - */ - deepEqual: function( actual, expected, message ) { - QUnit.push( QUnit.equiv(actual, expected), actual, expected, message ); - }, - - /** - * @name notDeepEqual - * @function - */ - notDeepEqual: function( actual, expected, message ) { - QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message ); - }, - - /** - * @name strictEqual - * @function - */ - strictEqual: function( actual, expected, message ) { - QUnit.push( expected === actual, actual, expected, message ); - }, - - /** - * @name notStrictEqual - * @function - */ - notStrictEqual: function( actual, expected, message ) { - QUnit.push( expected !== actual, actual, expected, message ); - }, - - "throws": function( block, expected, message ) { - var actual, - expectedOutput = expected, - ok = false; - - // 'expected' is optional - if ( typeof expected === "string" ) { - message = expected; - expected = null; - } - - config.current.ignoreGlobalErrors = true; - try { - block.call( config.current.testEnvironment ); - } catch (e) { - actual = e; - } - config.current.ignoreGlobalErrors = false; - - if ( actual ) { - // we don't want to validate thrown error - if ( !expected ) { - ok = true; - expectedOutput = null; - // expected is a regexp - } else if ( QUnit.objectType( expected ) === "regexp" ) { - ok = expected.test( errorString( actual ) ); - // expected is a constructor - } else if ( actual instanceof expected ) { - ok = true; - // expected is a validation function which returns true is validation passed - } else if ( expected.call( {}, actual ) === true ) { - expectedOutput = null; - ok = true; - } - - QUnit.push( ok, actual, expectedOutput, message ); - } else { - QUnit.pushFailure( message, null, 'No exception was thrown.' ); - } - } -}; - -/** - * @deprecate since 1.8.0 - * Kept assertion helpers in root for backwards compatibility. - */ -extend( QUnit, assert ); - -/** - * @deprecated since 1.9.0 - * Kept root "raises()" for backwards compatibility. - * (Note that we don't introduce assert.raises). - */ -QUnit.raises = assert[ "throws" ]; - -/** - * @deprecated since 1.0.0, replaced with error pushes since 1.3.0 - * Kept to avoid TypeErrors for undefined methods. - */ -QUnit.equals = function() { - QUnit.push( false, false, false, "QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead" ); -}; -QUnit.same = function() { - QUnit.push( false, false, false, "QUnit.same has been deprecated since 2009 (e88049a0), use QUnit.deepEqual instead" ); -}; - -// We want access to the constructor's prototype -(function() { - function F() {} - F.prototype = QUnit; - QUnit = new F(); - // Make F QUnit's constructor so that we can add to the prototype later - QUnit.constructor = F; -}()); - -/** - * Config object: Maintain internal state - * Later exposed as QUnit.config - * `config` initialized at top of scope - */ -config = { - // The queue of tests to run - queue: [], - - // block until document ready - blocking: true, - - // when enabled, show only failing tests - // gets persisted through sessionStorage and can be changed in UI via checkbox - hidepassed: false, - - // by default, run previously failed tests first - // very useful in combination with "Hide passed tests" checked - reorder: true, - - // by default, modify document.title when suite is done - altertitle: true, - - // when enabled, all tests must call expect() - requireExpects: false, - - // add checkboxes that are persisted in the query-string - // when enabled, the id is set to `true` as a `QUnit.config` property - urlConfig: [ - { - id: "noglobals", - label: "Check for Globals", - tooltip: "Enabling this will test if any test introduces new properties on the `window` object. Stored as query-strings." - }, - { - id: "notrycatch", - label: "No try-catch", - tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging exceptions in IE reasonable. Stored as query-strings." - } - ], - - // Set of all modules. - modules: {}, - - // logging callback queues - begin: [], - done: [], - log: [], - testStart: [], - testDone: [], - moduleStart: [], - moduleDone: [] -}; - -// Export global variables, unless an 'exports' object exists, -// in that case we assume we're in CommonJS (dealt with on the bottom of the script) -if ( typeof exports === "undefined" ) { - extend( window, QUnit ); - - // Expose QUnit object - window.QUnit = QUnit; -} - -// Initialize more QUnit.config and QUnit.urlParams -(function() { - var i, - location = window.location || { search: "", protocol: "file:" }, - params = location.search.slice( 1 ).split( "&" ), - length = params.length, - urlParams = {}, - current; - - if ( params[ 0 ] ) { - for ( i = 0; i < length; i++ ) { - current = params[ i ].split( "=" ); - current[ 0 ] = decodeURIComponent( current[ 0 ] ); - // allow just a key to turn on a flag, e.g., test.html?noglobals - current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true; - urlParams[ current[ 0 ] ] = current[ 1 ]; - } - } - - QUnit.urlParams = urlParams; - - // String search anywhere in moduleName+testName - config.filter = urlParams.filter; - - // Exact match of the module name - config.module = urlParams.module; - - config.testNumber = parseInt( urlParams.testNumber, 10 ) || null; - - // Figure out if we're running the tests from a server or not - QUnit.isLocal = location.protocol === "file:"; -}()); - -// Extend QUnit object, -// these after set here because they should not be exposed as global functions -extend( QUnit, { - assert: assert, - - config: config, - - // Initialize the configuration options - init: function() { - extend( config, { - stats: { all: 0, bad: 0 }, - moduleStats: { all: 0, bad: 0 }, - started: +new Date(), - updateRate: 1000, - blocking: false, - autostart: true, - autorun: false, - filter: "", - queue: [], - semaphore: 1 - }); - - var tests, banner, result, - qunit = id( "qunit" ); - - if ( qunit ) { - qunit.innerHTML = - "

" + escapeText( document.title ) + "

" + - "

" + - "
" + - "

" + - "
    "; - } - - tests = id( "qunit-tests" ); - banner = id( "qunit-banner" ); - result = id( "qunit-testresult" ); - - if ( tests ) { - tests.innerHTML = ""; - } - - if ( banner ) { - banner.className = ""; - } - - if ( result ) { - result.parentNode.removeChild( result ); - } - - if ( tests ) { - result = document.createElement( "p" ); - result.id = "qunit-testresult"; - result.className = "result"; - tests.parentNode.insertBefore( result, tests ); - result.innerHTML = "Running...
     "; - } - }, - - // Resets the test setup. Useful for tests that modify the DOM. - reset: function() { - var fixture = id( "qunit-fixture" ); - if ( fixture ) { - fixture.innerHTML = config.fixture; - } - }, - - // Trigger an event on an element. - // @example triggerEvent( document.body, "click" ); - triggerEvent: function( elem, type, event ) { - if ( document.createEvent ) { - event = document.createEvent( "MouseEvents" ); - event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView, - 0, 0, 0, 0, 0, false, false, false, false, 0, null); - - elem.dispatchEvent( event ); - } else if ( elem.fireEvent ) { - elem.fireEvent( "on" + type ); - } - }, - - // Safe object type checking - is: function( type, obj ) { - return QUnit.objectType( obj ) === type; - }, - - objectType: function( obj ) { - if ( typeof obj === "undefined" ) { - return "undefined"; - // consider: typeof null === object - } - if ( obj === null ) { - return "null"; - } - - var match = toString.call( obj ).match(/^\[object\s(.*)\]$/), - type = match && match[1] || ""; - - switch ( type ) { - case "Number": - if ( isNaN(obj) ) { - return "nan"; - } - return "number"; - case "String": - case "Boolean": - case "Array": - case "Date": - case "RegExp": - case "Function": - return type.toLowerCase(); - } - if ( typeof obj === "object" ) { - return "object"; - } - return undefined; - }, - - push: function( result, actual, expected, message ) { - if ( !config.current ) { - throw new Error( "assertion outside test context, was " + sourceFromStacktrace() ); - } - - var output, source, - details = { - module: config.current.module, - name: config.current.testName, - result: result, - message: message, - actual: actual, - expected: expected - }; - - message = escapeText( message ) || ( result ? "okay" : "failed" ); - message = "" + message + ""; - output = message; - - if ( !result ) { - expected = escapeText( QUnit.jsDump.parse(expected) ); - actual = escapeText( QUnit.jsDump.parse(actual) ); - output += ""; - - if ( actual !== expected ) { - output += ""; - output += ""; - } - - source = sourceFromStacktrace(); - - if ( source ) { - details.source = source; - output += ""; - } - - output += "
    Expected:
    " + expected + "
    Result:
    " + actual + "
    Diff:
    " + QUnit.diff( expected, actual ) + "
    Source:
    " + escapeText( source ) + "
    "; - } - - runLoggingCallbacks( "log", QUnit, details ); - - config.current.assertions.push({ - result: !!result, - message: output - }); - }, - - pushFailure: function( message, source, actual ) { - if ( !config.current ) { - throw new Error( "pushFailure() assertion outside test context, was " + sourceFromStacktrace(2) ); - } - - var output, - details = { - module: config.current.module, - name: config.current.testName, - result: false, - message: message - }; - - message = escapeText( message ) || "error"; - message = "" + message + ""; - output = message; - - output += ""; - - if ( actual ) { - output += ""; - } - - if ( source ) { - details.source = source; - output += ""; - } - - output += "
    Result:
    " + escapeText( actual ) + "
    Source:
    " + escapeText( source ) + "
    "; - - runLoggingCallbacks( "log", QUnit, details ); - - config.current.assertions.push({ - result: false, - message: output - }); - }, - - url: function( params ) { - params = extend( extend( {}, QUnit.urlParams ), params ); - var key, - querystring = "?"; - - for ( key in params ) { - if ( !hasOwn.call( params, key ) ) { - continue; - } - querystring += encodeURIComponent( key ) + "=" + - encodeURIComponent( params[ key ] ) + "&"; - } - return window.location.protocol + "//" + window.location.host + - window.location.pathname + querystring.slice( 0, -1 ); - }, - - extend: extend, - id: id, - addEvent: addEvent - // load, equiv, jsDump, diff: Attached later -}); - -/** - * @deprecated: Created for backwards compatibility with test runner that set the hook function - * into QUnit.{hook}, instead of invoking it and passing the hook function. - * QUnit.constructor is set to the empty F() above so that we can add to it's prototype here. - * Doing this allows us to tell if the following methods have been overwritten on the actual - * QUnit object. - */ -extend( QUnit.constructor.prototype, { - - // Logging callbacks; all receive a single argument with the listed properties - // run test/logs.html for any related changes - begin: registerLoggingCallback( "begin" ), - - // done: { failed, passed, total, runtime } - done: registerLoggingCallback( "done" ), - - // log: { result, actual, expected, message } - log: registerLoggingCallback( "log" ), - - // testStart: { name } - testStart: registerLoggingCallback( "testStart" ), - - // testDone: { name, failed, passed, total, duration } - testDone: registerLoggingCallback( "testDone" ), - - // moduleStart: { name } - moduleStart: registerLoggingCallback( "moduleStart" ), - - // moduleDone: { name, failed, passed, total } - moduleDone: registerLoggingCallback( "moduleDone" ) -}); - -if ( typeof document === "undefined" || document.readyState === "complete" ) { - config.autorun = true; -} - -QUnit.load = function() { - runLoggingCallbacks( "begin", QUnit, {} ); - - // Initialize the config, saving the execution queue - var banner, filter, i, label, len, main, ol, toolbar, userAgent, val, - urlConfigCheckboxesContainer, urlConfigCheckboxes, moduleFilter, - numModules = 0, - moduleFilterHtml = "", - urlConfigHtml = "", - oldconfig = extend( {}, config ); - - QUnit.init(); - extend(config, oldconfig); - - config.blocking = false; - - len = config.urlConfig.length; - - for ( i = 0; i < len; i++ ) { - val = config.urlConfig[i]; - if ( typeof val === "string" ) { - val = { - id: val, - label: val, - tooltip: "[no tooltip available]" - }; - } - config[ val.id ] = QUnit.urlParams[ val.id ]; - urlConfigHtml += ""; - } - - moduleFilterHtml += ""; - - // `userAgent` initialized at top of scope - userAgent = id( "qunit-userAgent" ); - if ( userAgent ) { - userAgent.innerHTML = navigator.userAgent; - } - - // `banner` initialized at top of scope - banner = id( "qunit-header" ); - if ( banner ) { - banner.innerHTML = "" + banner.innerHTML + " "; - } - - // `toolbar` initialized at top of scope - toolbar = id( "qunit-testrunner-toolbar" ); - if ( toolbar ) { - // `filter` initialized at top of scope - filter = document.createElement( "input" ); - filter.type = "checkbox"; - filter.id = "qunit-filter-pass"; - - addEvent( filter, "click", function() { - var tmp, - ol = document.getElementById( "qunit-tests" ); - - if ( filter.checked ) { - ol.className = ol.className + " hidepass"; - } else { - tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " "; - ol.className = tmp.replace( / hidepass /, " " ); - } - if ( defined.sessionStorage ) { - if (filter.checked) { - sessionStorage.setItem( "qunit-filter-passed-tests", "true" ); - } else { - sessionStorage.removeItem( "qunit-filter-passed-tests" ); - } - } - }); - - if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem( "qunit-filter-passed-tests" ) ) { - filter.checked = true; - // `ol` initialized at top of scope - ol = document.getElementById( "qunit-tests" ); - ol.className = ol.className + " hidepass"; - } - toolbar.appendChild( filter ); - - // `label` initialized at top of scope - label = document.createElement( "label" ); - label.setAttribute( "for", "qunit-filter-pass" ); - label.setAttribute( "title", "Only show tests and assertons that fail. Stored in sessionStorage." ); - label.innerHTML = "Hide passed tests"; - toolbar.appendChild( label ); - - urlConfigCheckboxesContainer = document.createElement("span"); - urlConfigCheckboxesContainer.innerHTML = urlConfigHtml; - urlConfigCheckboxes = urlConfigCheckboxesContainer.getElementsByTagName("input"); - // For oldIE support: - // * Add handlers to the individual elements instead of the container - // * Use "click" instead of "change" - // * Fallback from event.target to event.srcElement - addEvents( urlConfigCheckboxes, "click", function( event ) { - var params = {}, - target = event.target || event.srcElement; - params[ target.name ] = target.checked ? true : undefined; - window.location = QUnit.url( params ); - }); - toolbar.appendChild( urlConfigCheckboxesContainer ); - - if (numModules > 1) { - moduleFilter = document.createElement( 'span' ); - moduleFilter.setAttribute( 'id', 'qunit-modulefilter-container' ); - moduleFilter.innerHTML = moduleFilterHtml; - addEvent( moduleFilter.lastChild, "change", function() { - var selectBox = moduleFilter.getElementsByTagName("select")[0], - selectedModule = decodeURIComponent(selectBox.options[selectBox.selectedIndex].value); - - window.location = QUnit.url( { module: ( selectedModule === "" ) ? undefined : selectedModule } ); - }); - toolbar.appendChild(moduleFilter); - } - } - - // `main` initialized at top of scope - main = id( "qunit-fixture" ); - if ( main ) { - config.fixture = main.innerHTML; - } - - if ( config.autostart ) { - QUnit.start(); - } -}; - -addEvent( window, "load", QUnit.load ); - -// `onErrorFnPrev` initialized at top of scope -// Preserve other handlers -onErrorFnPrev = window.onerror; - -// Cover uncaught exceptions -// Returning true will surpress the default browser handler, -// returning false will let it run. -window.onerror = function ( error, filePath, linerNr ) { - var ret = false; - if ( onErrorFnPrev ) { - ret = onErrorFnPrev( error, filePath, linerNr ); - } - - // Treat return value as window.onerror itself does, - // Only do our handling if not surpressed. - if ( ret !== true ) { - if ( QUnit.config.current ) { - if ( QUnit.config.current.ignoreGlobalErrors ) { - return true; - } - QUnit.pushFailure( error, filePath + ":" + linerNr ); - } else { - QUnit.test( "global failure", extend( function() { - QUnit.pushFailure( error, filePath + ":" + linerNr ); - }, { validTest: validTest } ) ); - } - return false; - } - - return ret; -}; - -function done() { - config.autorun = true; - - // Log the last module results - if ( config.currentModule ) { - runLoggingCallbacks( "moduleDone", QUnit, { - name: config.currentModule, - failed: config.moduleStats.bad, - passed: config.moduleStats.all - config.moduleStats.bad, - total: config.moduleStats.all - }); - } - - var i, key, - banner = id( "qunit-banner" ), - tests = id( "qunit-tests" ), - runtime = +new Date() - config.started, - passed = config.stats.all - config.stats.bad, - html = [ - "Tests completed in ", - runtime, - " milliseconds.
    ", - "", - passed, - " assertions of ", - config.stats.all, - " passed, ", - config.stats.bad, - " failed." - ].join( "" ); - - if ( banner ) { - banner.className = ( config.stats.bad ? "qunit-fail" : "qunit-pass" ); - } - - if ( tests ) { - id( "qunit-testresult" ).innerHTML = html; - } - - if ( config.altertitle && typeof document !== "undefined" && document.title ) { - // show ✖ for good, ✔ for bad suite result in title - // use escape sequences in case file gets loaded with non-utf-8-charset - document.title = [ - ( config.stats.bad ? "\u2716" : "\u2714" ), - document.title.replace( /^[\u2714\u2716] /i, "" ) - ].join( " " ); - } - - // clear own sessionStorage items if all tests passed - if ( config.reorder && defined.sessionStorage && config.stats.bad === 0 ) { - // `key` & `i` initialized at top of scope - for ( i = 0; i < sessionStorage.length; i++ ) { - key = sessionStorage.key( i++ ); - if ( key.indexOf( "qunit-test-" ) === 0 ) { - sessionStorage.removeItem( key ); - } - } - } - - // scroll back to top to show results - if ( window.scrollTo ) { - window.scrollTo(0, 0); - } - - runLoggingCallbacks( "done", QUnit, { - failed: config.stats.bad, - passed: passed, - total: config.stats.all, - runtime: runtime - }); -} - -/** @return Boolean: true if this test should be ran */ -function validTest( test ) { - var include, - filter = config.filter && config.filter.toLowerCase(), - module = config.module && config.module.toLowerCase(), - fullName = (test.module + ": " + test.testName).toLowerCase(); - - // Internally-generated tests are always valid - if ( test.callback && test.callback.validTest === validTest ) { - delete test.callback.validTest; - return true; - } - - if ( config.testNumber ) { - return test.testNumber === config.testNumber; - } - - if ( module && ( !test.module || test.module.toLowerCase() !== module ) ) { - return false; - } - - if ( !filter ) { - return true; - } - - include = filter.charAt( 0 ) !== "!"; - if ( !include ) { - filter = filter.slice( 1 ); - } - - // If the filter matches, we need to honour include - if ( fullName.indexOf( filter ) !== -1 ) { - return include; - } - - // Otherwise, do the opposite - return !include; -} - -// so far supports only Firefox, Chrome and Opera (buggy), Safari (for real exceptions) -// Later Safari and IE10 are supposed to support error.stack as well -// See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack -function extractStacktrace( e, offset ) { - offset = offset === undefined ? 3 : offset; - - var stack, include, i; - - if ( e.stacktrace ) { - // Opera - return e.stacktrace.split( "\n" )[ offset + 3 ]; - } else if ( e.stack ) { - // Firefox, Chrome - stack = e.stack.split( "\n" ); - if (/^error$/i.test( stack[0] ) ) { - stack.shift(); - } - if ( fileName ) { - include = []; - for ( i = offset; i < stack.length; i++ ) { - if ( stack[ i ].indexOf( fileName ) !== -1 ) { - break; - } - include.push( stack[ i ] ); - } - if ( include.length ) { - return include.join( "\n" ); - } - } - return stack[ offset ]; - } else if ( e.sourceURL ) { - // Safari, PhantomJS - // hopefully one day Safari provides actual stacktraces - // exclude useless self-reference for generated Error objects - if ( /qunit.js$/.test( e.sourceURL ) ) { - return; - } - // for actual exceptions, this is useful - return e.sourceURL + ":" + e.line; - } -} -function sourceFromStacktrace( offset ) { - try { - throw new Error(); - } catch ( e ) { - return extractStacktrace( e, offset ); - } -} - -/** - * Escape text for attribute or text content. - */ -function escapeText( s ) { - if ( !s ) { - return ""; - } - s = s + ""; - // Both single quotes and double quotes (for attributes) - return s.replace( /['"<>&]/g, function( s ) { - switch( s ) { - case '\'': - return '''; - case '"': - return '"'; - case '<': - return '<'; - case '>': - return '>'; - case '&': - return '&'; - } - }); -} - -function synchronize( callback, last ) { - config.queue.push( callback ); - - if ( config.autorun && !config.blocking ) { - process( last ); - } -} - -function process( last ) { - function next() { - process( last ); - } - var start = new Date().getTime(); - config.depth = config.depth ? config.depth + 1 : 1; - - while ( config.queue.length && !config.blocking ) { - if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) { - config.queue.shift()(); - } else { - window.setTimeout( next, 13 ); - break; - } - } - config.depth--; - if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) { - done(); - } -} - -function saveGlobal() { - config.pollution = []; - - if ( config.noglobals ) { - for ( var key in window ) { - // in Opera sometimes DOM element ids show up here, ignore them - if ( !hasOwn.call( window, key ) || /^qunit-test-output/.test( key ) ) { - continue; - } - config.pollution.push( key ); - } - } -} - -function checkPollution() { - var newGlobals, - deletedGlobals, - old = config.pollution; - - saveGlobal(); - - newGlobals = diff( config.pollution, old ); - if ( newGlobals.length > 0 ) { - QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join(", ") ); - } - - deletedGlobals = diff( old, config.pollution ); - if ( deletedGlobals.length > 0 ) { - QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join(", ") ); - } -} - -// returns a new Array with the elements that are in a but not in b -function diff( a, b ) { - var i, j, - result = a.slice(); - - for ( i = 0; i < result.length; i++ ) { - for ( j = 0; j < b.length; j++ ) { - if ( result[i] === b[j] ) { - result.splice( i, 1 ); - i--; - break; - } - } - } - return result; -} - -function extend( a, b ) { - for ( var prop in b ) { - if ( b[ prop ] === undefined ) { - delete a[ prop ]; - - // Avoid "Member not found" error in IE8 caused by setting window.constructor - } else if ( prop !== "constructor" || a !== window ) { - a[ prop ] = b[ prop ]; - } - } - - return a; -} - -/** - * @param {HTMLElement} elem - * @param {string} type - * @param {Function} fn - */ -function addEvent( elem, type, fn ) { - // Standards-based browsers - if ( elem.addEventListener ) { - elem.addEventListener( type, fn, false ); - // IE - } else { - elem.attachEvent( "on" + type, fn ); - } -} - -/** - * @param {Array|NodeList} elems - * @param {string} type - * @param {Function} fn - */ -function addEvents( elems, type, fn ) { - var i = elems.length; - while ( i-- ) { - addEvent( elems[i], type, fn ); - } -} - -function hasClass( elem, name ) { - return (" " + elem.className + " ").indexOf(" " + name + " ") > -1; -} - -function addClass( elem, name ) { - if ( !hasClass( elem, name ) ) { - elem.className += (elem.className ? " " : "") + name; - } -} - -function removeClass( elem, name ) { - var set = " " + elem.className + " "; - // Class name may appear multiple times - while ( set.indexOf(" " + name + " ") > -1 ) { - set = set.replace(" " + name + " " , " "); - } - // If possible, trim it for prettiness, but not neccecarily - elem.className = window.jQuery ? jQuery.trim( set ) : ( set.trim ? set.trim() : set ); -} - -function id( name ) { - return !!( typeof document !== "undefined" && document && document.getElementById ) && - document.getElementById( name ); -} - -function registerLoggingCallback( key ) { - return function( callback ) { - config[key].push( callback ); - }; -} - -// Supports deprecated method of completely overwriting logging callbacks -function runLoggingCallbacks( key, scope, args ) { - var i, callbacks; - if ( QUnit.hasOwnProperty( key ) ) { - QUnit[ key ].call(scope, args ); - } else { - callbacks = config[ key ]; - for ( i = 0; i < callbacks.length; i++ ) { - callbacks[ i ].call( scope, args ); - } - } -} - -// Test for equality any JavaScript type. -// Author: Philippe Rathé -QUnit.equiv = (function() { - - // Call the o related callback with the given arguments. - function bindCallbacks( o, callbacks, args ) { - var prop = QUnit.objectType( o ); - if ( prop ) { - if ( QUnit.objectType( callbacks[ prop ] ) === "function" ) { - return callbacks[ prop ].apply( callbacks, args ); - } else { - return callbacks[ prop ]; // or undefined - } - } - } - - // the real equiv function - var innerEquiv, - // stack to decide between skip/abort functions - callers = [], - // stack to avoiding loops from circular referencing - parents = [], - - getProto = Object.getPrototypeOf || function ( obj ) { - return obj.__proto__; - }, - callbacks = (function () { - - // for string, boolean, number and null - function useStrictEquality( b, a ) { - /*jshint eqeqeq:false */ - if ( b instanceof a.constructor || a instanceof b.constructor ) { - // to catch short annotaion VS 'new' annotation of a - // declaration - // e.g. var i = 1; - // var j = new Number(1); - return a == b; - } else { - return a === b; - } - } - - return { - "string": useStrictEquality, - "boolean": useStrictEquality, - "number": useStrictEquality, - "null": useStrictEquality, - "undefined": useStrictEquality, - - "nan": function( b ) { - return isNaN( b ); - }, - - "date": function( b, a ) { - return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf(); - }, - - "regexp": function( b, a ) { - return QUnit.objectType( b ) === "regexp" && - // the regex itself - a.source === b.source && - // and its modifers - a.global === b.global && - // (gmi) ... - a.ignoreCase === b.ignoreCase && - a.multiline === b.multiline && - a.sticky === b.sticky; - }, - - // - skip when the property is a method of an instance (OOP) - // - abort otherwise, - // initial === would have catch identical references anyway - "function": function() { - var caller = callers[callers.length - 1]; - return caller !== Object && typeof caller !== "undefined"; - }, - - "array": function( b, a ) { - var i, j, len, loop; - - // b could be an object literal here - if ( QUnit.objectType( b ) !== "array" ) { - return false; - } - - len = a.length; - if ( len !== b.length ) { - // safe and faster - return false; - } - - // track reference to avoid circular references - parents.push( a ); - for ( i = 0; i < len; i++ ) { - loop = false; - for ( j = 0; j < parents.length; j++ ) { - if ( parents[j] === a[i] ) { - loop = true;// dont rewalk array - } - } - if ( !loop && !innerEquiv(a[i], b[i]) ) { - parents.pop(); - return false; - } - } - parents.pop(); - return true; - }, - - "object": function( b, a ) { - var i, j, loop, - // Default to true - eq = true, - aProperties = [], - bProperties = []; - - // comparing constructors is more strict than using - // instanceof - if ( a.constructor !== b.constructor ) { - // Allow objects with no prototype to be equivalent to - // objects with Object as their constructor. - if ( !(( getProto(a) === null && getProto(b) === Object.prototype ) || - ( getProto(b) === null && getProto(a) === Object.prototype ) ) ) { - return false; - } - } - - // stack constructor before traversing properties - callers.push( a.constructor ); - // track reference to avoid circular references - parents.push( a ); - - for ( i in a ) { // be strict: don't ensures hasOwnProperty - // and go deep - loop = false; - for ( j = 0; j < parents.length; j++ ) { - if ( parents[j] === a[i] ) { - // don't go down the same path twice - loop = true; - } - } - aProperties.push(i); // collect a's properties - - if (!loop && !innerEquiv( a[i], b[i] ) ) { - eq = false; - break; - } - } - - callers.pop(); // unstack, we are done - parents.pop(); - - for ( i in b ) { - bProperties.push( i ); // collect b's properties - } - - // Ensures identical properties name - return eq && innerEquiv( aProperties.sort(), bProperties.sort() ); - } - }; - }()); - - innerEquiv = function() { // can take multiple arguments - var args = [].slice.apply( arguments ); - if ( args.length < 2 ) { - return true; // end transition - } - - return (function( a, b ) { - if ( a === b ) { - return true; // catch the most you can - } else if ( a === null || b === null || typeof a === "undefined" || - typeof b === "undefined" || - QUnit.objectType(a) !== QUnit.objectType(b) ) { - return false; // don't lose time with error prone cases - } else { - return bindCallbacks(a, callbacks, [ b, a ]); - } - - // apply transition with (1..n) arguments - }( args[0], args[1] ) && arguments.callee.apply( this, args.splice(1, args.length - 1 )) ); - }; - - return innerEquiv; -}()); - -/** - * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | - * http://flesler.blogspot.com Licensed under BSD - * (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008 - * - * @projectDescription Advanced and extensible data dumping for Javascript. - * @version 1.0.0 - * @author Ariel Flesler - * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html} - */ -QUnit.jsDump = (function() { - function quote( str ) { - return '"' + str.toString().replace( /"/g, '\\"' ) + '"'; - } - function literal( o ) { - return o + ""; - } - function join( pre, arr, post ) { - var s = jsDump.separator(), - base = jsDump.indent(), - inner = jsDump.indent(1); - if ( arr.join ) { - arr = arr.join( "," + s + inner ); - } - if ( !arr ) { - return pre + post; - } - return [ pre, inner + arr, base + post ].join(s); - } - function array( arr, stack ) { - var i = arr.length, ret = new Array(i); - this.up(); - while ( i-- ) { - ret[i] = this.parse( arr[i] , undefined , stack); - } - this.down(); - return join( "[", ret, "]" ); - } - - var reName = /^function (\w+)/, - jsDump = { - // type is used mostly internally, you can fix a (custom)type in advance - parse: function( obj, type, stack ) { - stack = stack || [ ]; - var inStack, res, - parser = this.parsers[ type || this.typeOf(obj) ]; - - type = typeof parser; - inStack = inArray( obj, stack ); - - if ( inStack !== -1 ) { - return "recursion(" + (inStack - stack.length) + ")"; - } - if ( type === "function" ) { - stack.push( obj ); - res = parser.call( this, obj, stack ); - stack.pop(); - return res; - } - return ( type === "string" ) ? parser : this.parsers.error; - }, - typeOf: function( obj ) { - var type; - if ( obj === null ) { - type = "null"; - } else if ( typeof obj === "undefined" ) { - type = "undefined"; - } else if ( QUnit.is( "regexp", obj) ) { - type = "regexp"; - } else if ( QUnit.is( "date", obj) ) { - type = "date"; - } else if ( QUnit.is( "function", obj) ) { - type = "function"; - } else if ( typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined" ) { - type = "window"; - } else if ( obj.nodeType === 9 ) { - type = "document"; - } else if ( obj.nodeType ) { - type = "node"; - } else if ( - // native arrays - toString.call( obj ) === "[object Array]" || - // NodeList objects - ( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) ) - ) { - type = "array"; - } else if ( obj.constructor === Error.prototype.constructor ) { - type = "error"; - } else { - type = typeof obj; - } - return type; - }, - separator: function() { - return this.multiline ? this.HTML ? "
    " : "\n" : this.HTML ? " " : " "; - }, - // extra can be a number, shortcut for increasing-calling-decreasing - indent: function( extra ) { - if ( !this.multiline ) { - return ""; - } - var chr = this.indentChar; - if ( this.HTML ) { - chr = chr.replace( /\t/g, " " ).replace( / /g, " " ); - } - return new Array( this._depth_ + (extra||0) ).join(chr); - }, - up: function( a ) { - this._depth_ += a || 1; - }, - down: function( a ) { - this._depth_ -= a || 1; - }, - setParser: function( name, parser ) { - this.parsers[name] = parser; - }, - // The next 3 are exposed so you can use them - quote: quote, - literal: literal, - join: join, - // - _depth_: 1, - // This is the list of parsers, to modify them, use jsDump.setParser - parsers: { - window: "[Window]", - document: "[Document]", - error: function(error) { - return "Error(\"" + error.message + "\")"; - }, - unknown: "[Unknown]", - "null": "null", - "undefined": "undefined", - "function": function( fn ) { - var ret = "function", - // functions never have name in IE - name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1]; - - if ( name ) { - ret += " " + name; - } - ret += "( "; - - ret = [ ret, QUnit.jsDump.parse( fn, "functionArgs" ), "){" ].join( "" ); - return join( ret, QUnit.jsDump.parse(fn,"functionCode" ), "}" ); - }, - array: array, - nodelist: array, - "arguments": array, - object: function( map, stack ) { - var ret = [ ], keys, key, val, i; - QUnit.jsDump.up(); - keys = []; - for ( key in map ) { - keys.push( key ); - } - keys.sort(); - for ( i = 0; i < keys.length; i++ ) { - key = keys[ i ]; - val = map[ key ]; - ret.push( QUnit.jsDump.parse( key, "key" ) + ": " + QUnit.jsDump.parse( val, undefined, stack ) ); - } - QUnit.jsDump.down(); - return join( "{", ret, "}" ); - }, - node: function( node ) { - var len, i, val, - open = QUnit.jsDump.HTML ? "<" : "<", - close = QUnit.jsDump.HTML ? ">" : ">", - tag = node.nodeName.toLowerCase(), - ret = open + tag, - attrs = node.attributes; - - if ( attrs ) { - for ( i = 0, len = attrs.length; i < len; i++ ) { - val = attrs[i].nodeValue; - // IE6 includes all attributes in .attributes, even ones not explicitly set. - // Those have values like undefined, null, 0, false, "" or "inherit". - if ( val && val !== "inherit" ) { - ret += " " + attrs[i].nodeName + "=" + QUnit.jsDump.parse( val, "attribute" ); - } - } - } - ret += close; - - // Show content of TextNode or CDATASection - if ( node.nodeType === 3 || node.nodeType === 4 ) { - ret += node.nodeValue; - } - - return ret + open + "/" + tag + close; - }, - // function calls it internally, it's the arguments part of the function - functionArgs: function( fn ) { - var args, - l = fn.length; - - if ( !l ) { - return ""; - } - - args = new Array(l); - while ( l-- ) { - // 97 is 'a' - args[l] = String.fromCharCode(97+l); - } - return " " + args.join( ", " ) + " "; - }, - // object calls it internally, the key part of an item in a map - key: quote, - // function calls it internally, it's the content of the function - functionCode: "[code]", - // node calls it internally, it's an html attribute value - attribute: quote, - string: quote, - date: quote, - regexp: literal, - number: literal, - "boolean": literal - }, - // if true, entities are escaped ( <, >, \t, space and \n ) - HTML: false, - // indentation unit - indentChar: " ", - // if true, items in a collection, are separated by a \n, else just a space. - multiline: true - }; - - return jsDump; -}()); - -// from jquery.js -function inArray( elem, array ) { - if ( array.indexOf ) { - return array.indexOf( elem ); - } - - for ( var i = 0, length = array.length; i < length; i++ ) { - if ( array[ i ] === elem ) { - return i; - } - } - - return -1; -} - -/* - * Javascript Diff Algorithm - * By John Resig (http://ejohn.org/) - * Modified by Chu Alan "sprite" - * - * Released under the MIT license. - * - * More Info: - * http://ejohn.org/projects/javascript-diff-algorithm/ - * - * Usage: QUnit.diff(expected, actual) - * - * QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) == "the quick brown fox jumped jumps over" - */ -QUnit.diff = (function() { - /*jshint eqeqeq:false, eqnull:true */ - function diff( o, n ) { - var i, - ns = {}, - os = {}; - - for ( i = 0; i < n.length; i++ ) { - if ( !hasOwn.call( ns, n[i] ) ) { - ns[ n[i] ] = { - rows: [], - o: null - }; - } - ns[ n[i] ].rows.push( i ); - } - - for ( i = 0; i < o.length; i++ ) { - if ( !hasOwn.call( os, o[i] ) ) { - os[ o[i] ] = { - rows: [], - n: null - }; - } - os[ o[i] ].rows.push( i ); - } - - for ( i in ns ) { - if ( !hasOwn.call( ns, i ) ) { - continue; - } - if ( ns[i].rows.length === 1 && hasOwn.call( os, i ) && os[i].rows.length === 1 ) { - n[ ns[i].rows[0] ] = { - text: n[ ns[i].rows[0] ], - row: os[i].rows[0] - }; - o[ os[i].rows[0] ] = { - text: o[ os[i].rows[0] ], - row: ns[i].rows[0] - }; - } - } - - for ( i = 0; i < n.length - 1; i++ ) { - if ( n[i].text != null && n[ i + 1 ].text == null && n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null && - n[ i + 1 ] == o[ n[i].row + 1 ] ) { - - n[ i + 1 ] = { - text: n[ i + 1 ], - row: n[i].row + 1 - }; - o[ n[i].row + 1 ] = { - text: o[ n[i].row + 1 ], - row: i + 1 - }; - } - } - - for ( i = n.length - 1; i > 0; i-- ) { - if ( n[i].text != null && n[ i - 1 ].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null && - n[ i - 1 ] == o[ n[i].row - 1 ]) { - - n[ i - 1 ] = { - text: n[ i - 1 ], - row: n[i].row - 1 - }; - o[ n[i].row - 1 ] = { - text: o[ n[i].row - 1 ], - row: i - 1 - }; - } - } - - return { - o: o, - n: n - }; - } - - return function( o, n ) { - o = o.replace( /\s+$/, "" ); - n = n.replace( /\s+$/, "" ); - - var i, pre, - str = "", - out = diff( o === "" ? [] : o.split(/\s+/), n === "" ? [] : n.split(/\s+/) ), - oSpace = o.match(/\s+/g), - nSpace = n.match(/\s+/g); - - if ( oSpace == null ) { - oSpace = [ " " ]; - } - else { - oSpace.push( " " ); - } - - if ( nSpace == null ) { - nSpace = [ " " ]; - } - else { - nSpace.push( " " ); - } - - if ( out.n.length === 0 ) { - for ( i = 0; i < out.o.length; i++ ) { - str += "" + out.o[i] + oSpace[i] + ""; - } - } - else { - if ( out.n[0].text == null ) { - for ( n = 0; n < out.o.length && out.o[n].text == null; n++ ) { - str += "" + out.o[n] + oSpace[n] + ""; - } - } - - for ( i = 0; i < out.n.length; i++ ) { - if (out.n[i].text == null) { - str += "" + out.n[i] + nSpace[i] + ""; - } - else { - // `pre` initialized at top of scope - pre = ""; - - for ( n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) { - pre += "" + out.o[n] + oSpace[n] + ""; - } - str += " " + out.n[i].text + nSpace[i] + pre; - } - } - } - - return str; - }; -}()); - -// for CommonJS enviroments, export everything -if ( typeof exports !== "undefined" ) { - extend( exports, QUnit ); -} - -// get at whatever the global object is, like window in browsers -}( (function() {return this;}.call()) )); diff --git a/lib/vendor/handlebars.runtime-1.0.rc.1.min.js b/lib/vendor/handlebars.runtime-1.0.rc.1.min.js deleted file mode 100755 index cd031ac..0000000 --- a/lib/vendor/handlebars.runtime-1.0.rc.1.min.js +++ /dev/null @@ -1,2 +0,0 @@ -// lib/handlebars/base.js -/*jshint eqnull:true*/this.Handlebars={},function(e){e.VERSION="1.0.rc.1",e.helpers={},e.partials={},e.registerHelper=function(e,t,n){n&&(t.not=n),this.helpers[e]=t},e.registerPartial=function(e,t){this.partials[e]=t},e.registerHelper("helperMissing",function(e){if(arguments.length===2)return undefined;throw new Error("Could not find property '"+e+"'")});var t=Object.prototype.toString,n="[object Function]";e.registerHelper("blockHelperMissing",function(r,i){var s=i.inverse||function(){},o=i.fn,u="",a=t.call(r);return a===n&&(r=r.call(this)),r===!0?o(this):r===!1||r==null?s(this):a==="[object Array]"?r.length>0?e.helpers.each(r,i):s(this):o(r)}),e.K=function(){},e.createFrame=Object.create||function(t){e.K.prototype=t;var n=new e.K;return e.K.prototype=null,n},e.registerHelper("each",function(t,n){var r=n.fn,i=n.inverse,s=0,o="",u;n.data&&(u=e.createFrame(n.data));if(t&&typeof t=="object")if(t instanceof Array)for(var a=t.length;s":">",'"':""","'":"'","`":"`"},t=/[&<>"'`]/g,n=/[&<>"'`]/,r=function(t){return e[t]||"&"};Handlebars.Utils={escapeExpression:function(e){return e instanceof Handlebars.SafeString?e.toString():e==null||e===!1?"":n.test(e)?e.replace(t,r):e},isEmpty:function(e){return typeof e=="undefined"?!0:e===null?!0:e===!1?!0:Object.prototype.toString.call(e)==="[object Array]"&&e.length===0?!0:!1}}}(),Handlebars.VM={template:function(e){var t={escapeExpression:Handlebars.Utils.escapeExpression,invokePartial:Handlebars.VM.invokePartial,programs:[],program:function(e,t,n){var r=this.programs[e];return n?Handlebars.VM.program(t,n):r?r:(r=this.programs[e]=Handlebars.VM.program(t),r)},programWithDepth:Handlebars.VM.programWithDepth,noop:Handlebars.VM.noop};return function(n,r){return r=r||{},e.call(t,Handlebars,n,r.helpers,r.partials,r.data)}},programWithDepth:function(e,t,n){var r=Array.prototype.slice.call(arguments,2);return function(n,i){return i=i||{},e.apply(this,[n,i.data||t].concat(r))}},program:function(e,t){return function(n,r){return r=r||{},e(n,r.data||t)}},noop:function(){return""},invokePartial:function(e,t,n,r,i,s){var o={helpers:r,partials:i,data:s};if(e===undefined)throw new Handlebars.Exception("The partial "+t+" could not be found");if(e instanceof Function)return e(n,o);if(!Handlebars.compile)throw new Handlebars.Exception("The partial "+t+" could not be compiled when running in runtime-only mode");return i[t]=Handlebars.compile(e,{data:s!==undefined}),i[t](n,o)}},Handlebars.template=Handlebars.VM.template; \ No newline at end of file diff --git a/lib/vendor/jquery-2.0.0.min.js b/lib/vendor/jquery-2.0.0.min.js deleted file mode 100755 index b18e05a..0000000 --- a/lib/vendor/jquery-2.0.0.min.js +++ /dev/null @@ -1,6 +0,0 @@ -/*! jQuery v2.0.0 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license -//@ sourceMappingURL=jquery.min.map -*/ -(function(e,undefined){var t,n,r=typeof undefined,i=e.location,o=e.document,s=o.documentElement,a=e.jQuery,u=e.$,l={},c=[],f="2.0.0",p=c.concat,h=c.push,d=c.slice,g=c.indexOf,m=l.toString,y=l.hasOwnProperty,v=f.trim,x=function(e,n){return new x.fn.init(e,n,t)},b=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,w=/\S+/g,T=/^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,k=/^-ms-/,N=/-([\da-z])/gi,E=function(e,t){return t.toUpperCase()},S=function(){o.removeEventListener("DOMContentLoaded",S,!1),e.removeEventListener("load",S,!1),x.ready()};x.fn=x.prototype={jquery:f,constructor:x,init:function(e,t,n){var r,i;if(!e)return this;if("string"==typeof e){if(r="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:T.exec(e),!r||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof x?t[0]:t,x.merge(this,x.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:o,!0)),C.test(r[1])&&x.isPlainObject(t))for(r in t)x.isFunction(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return i=o.getElementById(r[2]),i&&i.parentNode&&(this.length=1,this[0]=i),this.context=o,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):x.isFunction(e)?n.ready(e):(e.selector!==undefined&&(this.selector=e.selector,this.context=e.context),x.makeArray(e,this))},selector:"",length:0,toArray:function(){return d.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return x.each(this,e,t)},ready:function(e){return x.ready.promise().done(e),this},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(x.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:h,sort:[].sort,splice:[].splice},x.fn.init.prototype=x.fn,x.extend=x.fn.extend=function(){var e,t,n,r,i,o,s=arguments[0]||{},a=1,u=arguments.length,l=!1;for("boolean"==typeof s&&(l=s,s=arguments[1]||{},a=2),"object"==typeof s||x.isFunction(s)||(s={}),u===a&&(s=this,--a);u>a;a++)if(null!=(e=arguments[a]))for(t in e)n=s[t],r=e[t],s!==r&&(l&&r&&(x.isPlainObject(r)||(i=x.isArray(r)))?(i?(i=!1,o=n&&x.isArray(n)?n:[]):o=n&&x.isPlainObject(n)?n:{},s[t]=x.extend(l,o,r)):r!==undefined&&(s[t]=r));return s},x.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),noConflict:function(t){return e.$===x&&(e.$=u),t&&e.jQuery===x&&(e.jQuery=a),x},isReady:!1,readyWait:1,holdReady:function(e){e?x.readyWait++:x.ready(!0)},ready:function(e){(e===!0?--x.readyWait:x.isReady)||(x.isReady=!0,e!==!0&&--x.readyWait>0||(n.resolveWith(o,[x]),x.fn.trigger&&x(o).trigger("ready").off("ready")))},isFunction:function(e){return"function"===x.type(e)},isArray:Array.isArray,isWindow:function(e){return null!=e&&e===e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[m.call(e)]||"object":typeof e},isPlainObject:function(e){if("object"!==x.type(e)||e.nodeType||x.isWindow(e))return!1;try{if(e.constructor&&!y.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(t){return!1}return!0},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||o;var r=C.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=x.buildFragment([e],t,i),i&&x(i).remove(),x.merge([],r.childNodes))},parseJSON:JSON.parse,parseXML:function(e){var t,n;if(!e||"string"!=typeof e)return null;try{n=new DOMParser,t=n.parseFromString(e,"text/xml")}catch(r){t=undefined}return(!t||t.getElementsByTagName("parsererror").length)&&x.error("Invalid XML: "+e),t},noop:function(){},globalEval:function(e){var t,n=eval;e=x.trim(e),e&&(1===e.indexOf("use strict")?(t=o.createElement("script"),t.text=e,o.head.appendChild(t).parentNode.removeChild(t)):n(e))},camelCase:function(e){return e.replace(k,"ms-").replace(N,E)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,s=j(e);if(n){if(s){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(s){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:function(e){return null==e?"":v.call(e)},makeArray:function(e,t){var n=t||[];return null!=e&&(j(Object(e))?x.merge(n,"string"==typeof e?[e]:e):h.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:g.call(t,e,n)},merge:function(e,t){var n=t.length,r=e.length,i=0;if("number"==typeof n)for(;n>i;i++)e[r++]=t[i];else while(t[i]!==undefined)e[r++]=t[i++];return e.length=r,e},grep:function(e,t,n){var r,i=[],o=0,s=e.length;for(n=!!n;s>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,s=j(e),a=[];if(s)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(a[a.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(a[a.length]=r);return p.apply([],a)},guid:1,proxy:function(e,t){var n,r,i;return"string"==typeof t&&(n=e[t],t=e,e=n),x.isFunction(e)?(r=d.call(arguments,2),i=function(){return e.apply(t||this,r.concat(d.call(arguments)))},i.guid=e.guid=e.guid||x.guid++,i):undefined},access:function(e,t,n,r,i,o,s){var a=0,u=e.length,l=null==n;if("object"===x.type(n)){i=!0;for(a in n)x.access(e,t,a,n[a],!0,o,s)}else if(r!==undefined&&(i=!0,x.isFunction(r)||(s=!0),l&&(s?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(x(e),n)})),t))for(;u>a;a++)t(e[a],n,s?r:r.call(e[a],a,t(e[a],n)));return i?e:l?t.call(e):u?t(e[0],n):o},now:Date.now,swap:function(e,t,n,r){var i,o,s={};for(o in t)s[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=s[o];return i}}),x.ready.promise=function(t){return n||(n=x.Deferred(),"complete"===o.readyState?setTimeout(x.ready):(o.addEventListener("DOMContentLoaded",S,!1),e.addEventListener("load",S,!1))),n.promise(t)},x.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){l["[object "+t+"]"]=t.toLowerCase()});function j(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}t=x(o),function(e,undefined){var t,n,r,i,o,s,a,u,l,c,f,p,h,d,g,m,y="sizzle"+-new Date,v=e.document,b={},w=0,T=0,C=ot(),k=ot(),N=ot(),E=!1,S=function(){return 0},j=typeof undefined,D=1<<31,A=[],L=A.pop,q=A.push,H=A.push,O=A.slice,F=A.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},P="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",R="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",W=M.replace("w","w#"),$="\\["+R+"*("+M+")"+R+"*(?:([*^$|!~]?=)"+R+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+W+")|)|)"+R+"*\\]",B=":("+M+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+$.replace(3,8)+")*)|.*)\\)|)",I=RegExp("^"+R+"+|((?:^|[^\\\\])(?:\\\\.)*)"+R+"+$","g"),z=RegExp("^"+R+"*,"+R+"*"),_=RegExp("^"+R+"*([>+~]|"+R+")"+R+"*"),X=RegExp(R+"*[+~]"),U=RegExp("="+R+"*([^\\]'\"]*)"+R+"*\\]","g"),Y=RegExp(B),V=RegExp("^"+W+"$"),G={ID:RegExp("^#("+M+")"),CLASS:RegExp("^\\.("+M+")"),TAG:RegExp("^("+M.replace("w","w*")+")"),ATTR:RegExp("^"+$),PSEUDO:RegExp("^"+B),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+R+"*(even|odd|(([+-]|)(\\d*)n|)"+R+"*(?:([+-]|)"+R+"*(\\d+)|))"+R+"*\\)|)","i"),"boolean":RegExp("^(?:"+P+")$","i"),needsContext:RegExp("^"+R+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+R+"*((?:-\\d)?\\d*)"+R+"*\\)|)(?=[^-]|$)","i")},J=/^[^{]+\{\s*\[native \w/,Q=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,K=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,et=/'|\\/g,tt=/\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g,nt=function(e,t){var n="0x"+t-65536;return n!==n?t:0>n?String.fromCharCode(n+65536):String.fromCharCode(55296|n>>10,56320|1023&n)};try{H.apply(A=O.call(v.childNodes),v.childNodes),A[v.childNodes.length].nodeType}catch(rt){H={apply:A.length?function(e,t){q.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function it(e){return J.test(e+"")}function ot(){var e,t=[];return e=function(n,i){return t.push(n+=" ")>r.cacheLength&&delete e[t.shift()],e[n]=i}}function st(e){return e[y]=!0,e}function at(e){var t=c.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function ut(e,t,n,r){var i,o,s,a,u,f,d,g,x,w;if((t?t.ownerDocument||t:v)!==c&&l(t),t=t||c,n=n||[],!e||"string"!=typeof e)return n;if(1!==(a=t.nodeType)&&9!==a)return[];if(p&&!r){if(i=Q.exec(e))if(s=i[1]){if(9===a){if(o=t.getElementById(s),!o||!o.parentNode)return n;if(o.id===s)return n.push(o),n}else if(t.ownerDocument&&(o=t.ownerDocument.getElementById(s))&&m(t,o)&&o.id===s)return n.push(o),n}else{if(i[2])return H.apply(n,t.getElementsByTagName(e)),n;if((s=i[3])&&b.getElementsByClassName&&t.getElementsByClassName)return H.apply(n,t.getElementsByClassName(s)),n}if(b.qsa&&(!h||!h.test(e))){if(g=d=y,x=t,w=9===a&&e,1===a&&"object"!==t.nodeName.toLowerCase()){f=gt(e),(d=t.getAttribute("id"))?g=d.replace(et,"\\$&"):t.setAttribute("id",g),g="[id='"+g+"'] ",u=f.length;while(u--)f[u]=g+mt(f[u]);x=X.test(e)&&t.parentNode||t,w=f.join(",")}if(w)try{return H.apply(n,x.querySelectorAll(w)),n}catch(T){}finally{d||t.removeAttribute("id")}}}return kt(e.replace(I,"$1"),t,n,r)}o=ut.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},l=ut.setDocument=function(e){var t=e?e.ownerDocument||e:v;return t!==c&&9===t.nodeType&&t.documentElement?(c=t,f=t.documentElement,p=!o(t),b.getElementsByTagName=at(function(e){return e.appendChild(t.createComment("")),!e.getElementsByTagName("*").length}),b.attributes=at(function(e){return e.className="i",!e.getAttribute("className")}),b.getElementsByClassName=at(function(e){return e.innerHTML="
    ",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),b.sortDetached=at(function(e){return 1&e.compareDocumentPosition(c.createElement("div"))}),b.getById=at(function(e){return f.appendChild(e).id=y,!t.getElementsByName||!t.getElementsByName(y).length}),b.getById?(r.find.ID=function(e,t){if(typeof t.getElementById!==j&&p){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},r.filter.ID=function(e){var t=e.replace(tt,nt);return function(e){return e.getAttribute("id")===t}}):(r.find.ID=function(e,t){if(typeof t.getElementById!==j&&p){var n=t.getElementById(e);return n?n.id===e||typeof n.getAttributeNode!==j&&n.getAttributeNode("id").value===e?[n]:undefined:[]}},r.filter.ID=function(e){var t=e.replace(tt,nt);return function(e){var n=typeof e.getAttributeNode!==j&&e.getAttributeNode("id");return n&&n.value===t}}),r.find.TAG=b.getElementsByTagName?function(e,t){return typeof t.getElementsByTagName!==j?t.getElementsByTagName(e):undefined}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},r.find.CLASS=b.getElementsByClassName&&function(e,t){return typeof t.getElementsByClassName!==j&&p?t.getElementsByClassName(e):undefined},d=[],h=[],(b.qsa=it(t.querySelectorAll))&&(at(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||h.push("\\["+R+"*(?:value|"+P+")"),e.querySelectorAll(":checked").length||h.push(":checked")}),at(function(e){var t=c.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("t",""),e.querySelectorAll("[t^='']").length&&h.push("[*^$]="+R+"*(?:''|\"\")"),e.querySelectorAll(":enabled").length||h.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),h.push(",.*:")})),(b.matchesSelector=it(g=f.webkitMatchesSelector||f.mozMatchesSelector||f.oMatchesSelector||f.msMatchesSelector))&&at(function(e){b.disconnectedMatch=g.call(e,"div"),g.call(e,"[s!='']:x"),d.push("!=",B)}),h=h.length&&RegExp(h.join("|")),d=d.length&&RegExp(d.join("|")),m=it(f.contains)||f.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},S=f.compareDocumentPosition?function(e,n){if(e===n)return E=!0,0;var r=n.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(n);return r?1&r||!b.sortDetached&&n.compareDocumentPosition(e)===r?e===t||m(v,e)?-1:n===t||m(v,n)?1:u?F.call(u,e)-F.call(u,n):0:4&r?-1:1:e.compareDocumentPosition?-1:1}:function(e,n){var r,i=0,o=e.parentNode,s=n.parentNode,a=[e],l=[n];if(e===n)return E=!0,0;if(!o||!s)return e===t?-1:n===t?1:o?-1:s?1:u?F.call(u,e)-F.call(u,n):0;if(o===s)return lt(e,n);r=e;while(r=r.parentNode)a.unshift(r);r=n;while(r=r.parentNode)l.unshift(r);while(a[i]===l[i])i++;return i?lt(a[i],l[i]):a[i]===v?-1:l[i]===v?1:0},c):c},ut.matches=function(e,t){return ut(e,null,null,t)},ut.matchesSelector=function(e,t){if((e.ownerDocument||e)!==c&&l(e),t=t.replace(U,"='$1']"),!(!b.matchesSelector||!p||d&&d.test(t)||h&&h.test(t)))try{var n=g.call(e,t);if(n||b.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(r){}return ut(t,c,null,[e]).length>0},ut.contains=function(e,t){return(e.ownerDocument||e)!==c&&l(e),m(e,t)},ut.attr=function(e,t){(e.ownerDocument||e)!==c&&l(e);var n=r.attrHandle[t.toLowerCase()],i=n&&n(e,t,!p);return i===undefined?b.attributes||!p?e.getAttribute(t):(i=e.getAttributeNode(t))&&i.specified?i.value:null:i},ut.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},ut.uniqueSort=function(e){var t,n=[],r=0,i=0;if(E=!b.detectDuplicates,u=!b.sortStable&&e.slice(0),e.sort(S),E){while(t=e[i++])t===e[i]&&(r=n.push(i));while(r--)e.splice(n[r],1)}return e};function lt(e,t){var n=t&&e,r=n&&(~t.sourceIndex||D)-(~e.sourceIndex||D);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function ct(e,t,n){var r;return n?undefined:(r=e.getAttributeNode(t))&&r.specified?r.value:e[t]===!0?t.toLowerCase():null}function ft(e,t,n){var r;return n?undefined:r=e.getAttribute(t,"type"===t.toLowerCase()?1:2)}function pt(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function ht(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function dt(e){return st(function(t){return t=+t,st(function(n,r){var i,o=e([],n.length,t),s=o.length;while(s--)n[i=o[s]]&&(n[i]=!(r[i]=n[i]))})})}i=ut.getText=function(e){var t,n="",r=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=i(e)}else if(3===o||4===o)return e.nodeValue}else for(;t=e[r];r++)n+=i(t);return n},r=ut.selectors={cacheLength:50,createPseudo:st,match:G,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(tt,nt),e[3]=(e[4]||e[5]||"").replace(tt,nt),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||ut.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&ut.error(e[0]),e},PSEUDO:function(e){var t,n=!e[5]&&e[2];return G.CHILD.test(e[0])?null:(e[4]?e[2]=e[4]:n&&Y.test(n)&&(t=gt(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(tt,nt).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=C[e+" "];return t||(t=RegExp("(^|"+R+")"+e+"("+R+"|$)"))&&C(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==j&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=ut.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),s="last"!==e.slice(-4),a="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,h,d,g=o!==s?"nextSibling":"previousSibling",m=t.parentNode,v=a&&t.nodeName.toLowerCase(),x=!u&&!a;if(m){if(o){while(g){f=t;while(f=f[g])if(a?f.nodeName.toLowerCase()===v:1===f.nodeType)return!1;d=g="only"===e&&!d&&"nextSibling"}return!0}if(d=[s?m.firstChild:m.lastChild],s&&x){c=m[y]||(m[y]={}),l=c[e]||[],h=l[0]===w&&l[1],p=l[0]===w&&l[2],f=h&&m.childNodes[h];while(f=++h&&f&&f[g]||(p=h=0)||d.pop())if(1===f.nodeType&&++p&&f===t){c[e]=[w,h,p];break}}else if(x&&(l=(t[y]||(t[y]={}))[e])&&l[0]===w)p=l[1];else while(f=++h&&f&&f[g]||(p=h=0)||d.pop())if((a?f.nodeName.toLowerCase()===v:1===f.nodeType)&&++p&&(x&&((f[y]||(f[y]={}))[e]=[w,p]),f===t))break;return p-=i,p===r||0===p%r&&p/r>=0}}},PSEUDO:function(e,t){var n,i=r.pseudos[e]||r.setFilters[e.toLowerCase()]||ut.error("unsupported pseudo: "+e);return i[y]?i(t):i.length>1?(n=[e,e,"",t],r.setFilters.hasOwnProperty(e.toLowerCase())?st(function(e,n){var r,o=i(e,t),s=o.length;while(s--)r=F.call(e,o[s]),e[r]=!(n[r]=o[s])}):function(e){return i(e,0,n)}):i}},pseudos:{not:st(function(e){var t=[],n=[],r=s(e.replace(I,"$1"));return r[y]?st(function(e,t,n,i){var o,s=r(e,null,i,[]),a=e.length;while(a--)(o=s[a])&&(e[a]=!(t[a]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:st(function(e){return function(t){return ut(e,t).length>0}}),contains:st(function(e){return function(t){return(t.textContent||t.innerText||i(t)).indexOf(e)>-1}}),lang:st(function(e){return V.test(e||"")||ut.error("unsupported lang: "+e),e=e.replace(tt,nt).toLowerCase(),function(t){var n;do if(n=p?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===f},focus:function(e){return e===c.activeElement&&(!c.hasFocus||c.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!r.pseudos.empty(e)},header:function(e){return Z.test(e.nodeName)},input:function(e){return K.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:dt(function(){return[0]}),last:dt(function(e,t){return[t-1]}),eq:dt(function(e,t,n){return[0>n?n+t:n]}),even:dt(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:dt(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:dt(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:dt(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}};for(t in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})r.pseudos[t]=pt(t);for(t in{submit:!0,reset:!0})r.pseudos[t]=ht(t);function gt(e,t){var n,i,o,s,a,u,l,c=k[e+" "];if(c)return t?0:c.slice(0);a=e,u=[],l=r.preFilter;while(a){(!n||(i=z.exec(a)))&&(i&&(a=a.slice(i[0].length)||a),u.push(o=[])),n=!1,(i=_.exec(a))&&(n=i.shift(),o.push({value:n,type:i[0].replace(I," ")}),a=a.slice(n.length));for(s in r.filter)!(i=G[s].exec(a))||l[s]&&!(i=l[s](i))||(n=i.shift(),o.push({value:n,type:s,matches:i}),a=a.slice(n.length));if(!n)break}return t?a.length:a?ut.error(e):k(e,u).slice(0)}function mt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function yt(e,t,r){var i=t.dir,o=r&&"parentNode"===i,s=T++;return t.first?function(t,n,r){while(t=t[i])if(1===t.nodeType||o)return e(t,n,r)}:function(t,r,a){var u,l,c,f=w+" "+s;if(a){while(t=t[i])if((1===t.nodeType||o)&&e(t,r,a))return!0}else while(t=t[i])if(1===t.nodeType||o)if(c=t[y]||(t[y]={}),(l=c[i])&&l[0]===f){if((u=l[1])===!0||u===n)return u===!0}else if(l=c[i]=[f],l[1]=e(t,r,a)||n,l[1]===!0)return!0}}function vt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function xt(e,t,n,r,i){var o,s=[],a=0,u=e.length,l=null!=t;for(;u>a;a++)(o=e[a])&&(!n||n(o,r,i))&&(s.push(o),l&&t.push(a));return s}function bt(e,t,n,r,i,o){return r&&!r[y]&&(r=bt(r)),i&&!i[y]&&(i=bt(i,o)),st(function(o,s,a,u){var l,c,f,p=[],h=[],d=s.length,g=o||Ct(t||"*",a.nodeType?[a]:a,[]),m=!e||!o&&t?g:xt(g,p,e,a,u),y=n?i||(o?e:d||r)?[]:s:m;if(n&&n(m,y,a,u),r){l=xt(y,h),r(l,[],a,u),c=l.length;while(c--)(f=l[c])&&(y[h[c]]=!(m[h[c]]=f))}if(o){if(i||e){if(i){l=[],c=y.length;while(c--)(f=y[c])&&l.push(m[c]=f);i(null,y=[],l,u)}c=y.length;while(c--)(f=y[c])&&(l=i?F.call(o,f):p[c])>-1&&(o[l]=!(s[l]=f))}}else y=xt(y===s?y.splice(d,y.length):y),i?i(null,s,y,u):H.apply(s,y)})}function wt(e){var t,n,i,o=e.length,s=r.relative[e[0].type],u=s||r.relative[" "],l=s?1:0,c=yt(function(e){return e===t},u,!0),f=yt(function(e){return F.call(t,e)>-1},u,!0),p=[function(e,n,r){return!s&&(r||n!==a)||((t=n).nodeType?c(e,n,r):f(e,n,r))}];for(;o>l;l++)if(n=r.relative[e[l].type])p=[yt(vt(p),n)];else{if(n=r.filter[e[l].type].apply(null,e[l].matches),n[y]){for(i=++l;o>i;i++)if(r.relative[e[i].type])break;return bt(l>1&&vt(p),l>1&&mt(e.slice(0,l-1)).replace(I,"$1"),n,i>l&&wt(e.slice(l,i)),o>i&&wt(e=e.slice(i)),o>i&&mt(e))}p.push(n)}return vt(p)}function Tt(e,t){var i=0,o=t.length>0,s=e.length>0,u=function(u,l,f,p,h){var d,g,m,y=[],v=0,x="0",b=u&&[],T=null!=h,C=a,k=u||s&&r.find.TAG("*",h&&l.parentNode||l),N=w+=null==C?1:Math.random()||.1;for(T&&(a=l!==c&&l,n=i);null!=(d=k[x]);x++){if(s&&d){g=0;while(m=e[g++])if(m(d,l,f)){p.push(d);break}T&&(w=N,n=++i)}o&&((d=!m&&d)&&v--,u&&b.push(d))}if(v+=x,o&&x!==v){g=0;while(m=t[g++])m(b,y,l,f);if(u){if(v>0)while(x--)b[x]||y[x]||(y[x]=L.call(p));y=xt(y)}H.apply(p,y),T&&!u&&y.length>0&&v+t.length>1&&ut.uniqueSort(p)}return T&&(w=N,a=C),b};return o?st(u):u}s=ut.compile=function(e,t){var n,r=[],i=[],o=N[e+" "];if(!o){t||(t=gt(e)),n=t.length;while(n--)o=wt(t[n]),o[y]?r.push(o):i.push(o);o=N(e,Tt(i,r))}return o};function Ct(e,t,n){var r=0,i=t.length;for(;i>r;r++)ut(e,t[r],n);return n}function kt(e,t,n,i){var o,a,u,l,c,f=gt(e);if(!i&&1===f.length){if(a=f[0]=f[0].slice(0),a.length>2&&"ID"===(u=a[0]).type&&9===t.nodeType&&p&&r.relative[a[1].type]){if(t=(r.find.ID(u.matches[0].replace(tt,nt),t)||[])[0],!t)return n;e=e.slice(a.shift().value.length)}o=G.needsContext.test(e)?0:a.length;while(o--){if(u=a[o],r.relative[l=u.type])break;if((c=r.find[l])&&(i=c(u.matches[0].replace(tt,nt),X.test(a[0].type)&&t.parentNode||t))){if(a.splice(o,1),e=i.length&&mt(a),!e)return H.apply(n,i),n;break}}}return s(e,f)(i,t,!p,n,X.test(e)),n}r.pseudos.nth=r.pseudos.eq;function Nt(){}Nt.prototype=r.filters=r.pseudos,r.setFilters=new Nt,b.sortStable=y.split("").sort(S).join("")===y,l(),[0,0].sort(S),b.detectDuplicates=E,at(function(e){if(e.innerHTML="","#"!==e.firstChild.getAttribute("href")){var t="type|href|height|width".split("|"),n=t.length;while(n--)r.attrHandle[t[n]]=ft}}),at(function(e){if(null!=e.getAttribute("disabled")){var t=P.split("|"),n=t.length;while(n--)r.attrHandle[t[n]]=ct}}),x.find=ut,x.expr=ut.selectors,x.expr[":"]=x.expr.pseudos,x.unique=ut.uniqueSort,x.text=ut.getText,x.isXMLDoc=ut.isXML,x.contains=ut.contains}(e);var D={};function A(e){var t=D[e]={};return x.each(e.match(w)||[],function(e,n){t[n]=!0}),t}x.Callbacks=function(e){e="string"==typeof e?D[e]||A(e):x.extend({},e);var t,n,r,i,o,s,a=[],u=!e.once&&[],l=function(f){for(t=e.memory&&f,n=!0,s=i||0,i=0,o=a.length,r=!0;a&&o>s;s++)if(a[s].apply(f[0],f[1])===!1&&e.stopOnFalse){t=!1;break}r=!1,a&&(u?u.length&&l(u.shift()):t?a=[]:c.disable())},c={add:function(){if(a){var n=a.length;(function s(t){x.each(t,function(t,n){var r=x.type(n);"function"===r?e.unique&&c.has(n)||a.push(n):n&&n.length&&"string"!==r&&s(n)})})(arguments),r?o=a.length:t&&(i=n,l(t))}return this},remove:function(){return a&&x.each(arguments,function(e,t){var n;while((n=x.inArray(t,a,n))>-1)a.splice(n,1),r&&(o>=n&&o--,s>=n&&s--)}),this},has:function(e){return e?x.inArray(e,a)>-1:!(!a||!a.length)},empty:function(){return a=[],o=0,this},disable:function(){return a=u=t=undefined,this},disabled:function(){return!a},lock:function(){return u=undefined,t||c.disable(),this},locked:function(){return!u},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],!a||n&&!u||(r?u.push(t):l(t)),this},fire:function(){return c.fireWith(this,arguments),this},fired:function(){return!!n}};return c},x.extend({Deferred:function(e){var t=[["resolve","done",x.Callbacks("once memory"),"resolved"],["reject","fail",x.Callbacks("once memory"),"rejected"],["notify","progress",x.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return x.Deferred(function(n){x.each(t,function(t,o){var s=o[0],a=x.isFunction(e[t])&&e[t];i[o[1]](function(){var e=a&&a.apply(this,arguments);e&&x.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[s+"With"](this===r?n.promise():this,a?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?x.extend(e,r):r}},i={};return r.pipe=r.then,x.each(t,function(e,o){var s=o[2],a=o[3];r[o[1]]=s.add,a&&s.add(function(){n=a},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=s.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=d.call(arguments),r=n.length,i=1!==r||e&&x.isFunction(e.promise)?r:0,o=1===i?e:x.Deferred(),s=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?d.call(arguments):r,n===a?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},a,u,l;if(r>1)for(a=Array(r),u=Array(r),l=Array(r);r>t;t++)n[t]&&x.isFunction(n[t].promise)?n[t].promise().done(s(t,l,n)).fail(o.reject).progress(s(t,u,a)):--i;return i||o.resolveWith(l,n),o.promise()}}),x.support=function(t){var n=o.createElement("input"),r=o.createDocumentFragment(),i=o.createElement("div"),s=o.createElement("select"),a=s.appendChild(o.createElement("option"));return n.type?(n.type="checkbox",t.checkOn=""!==n.value,t.optSelected=a.selected,t.reliableMarginRight=!0,t.boxSizingReliable=!0,t.pixelPosition=!1,n.checked=!0,t.noCloneChecked=n.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!a.disabled,n=o.createElement("input"),n.value="t",n.type="radio",t.radioValue="t"===n.value,n.setAttribute("checked","t"),n.setAttribute("name","t"),r.appendChild(n),t.checkClone=r.cloneNode(!0).cloneNode(!0).lastChild.checked,t.focusinBubbles="onfocusin"in e,i.style.backgroundClip="content-box",i.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===i.style.backgroundClip,x(function(){var n,r,s="padding:0;margin:0;border:0;display:block;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box",a=o.getElementsByTagName("body")[0];a&&(n=o.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",a.appendChild(n).appendChild(i),i.innerHTML="",i.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%",x.swap(a,null!=a.style.zoom?{zoom:1}:{},function(){t.boxSizing=4===i.offsetWidth}),e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(i,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(i,null)||{width:"4px"}).width,r=i.appendChild(o.createElement("div")),r.style.cssText=i.style.cssText=s,r.style.marginRight=r.style.width="0",i.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),a.removeChild(n))}),t):t}({});var L,q,H=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,O=/([A-Z])/g;function F(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=x.expando+Math.random()}F.uid=1,F.accepts=function(e){return e.nodeType?1===e.nodeType||9===e.nodeType:!0},F.prototype={key:function(e){if(!F.accepts(e))return 0;var t={},n=e[this.expando];if(!n){n=F.uid++;try{t[this.expando]={value:n},Object.defineProperties(e,t)}catch(r){t[this.expando]=n,x.extend(e,t)}}return this.cache[n]||(this.cache[n]={}),n},set:function(e,t,n){var r,i=this.key(e),o=this.cache[i];if("string"==typeof t)o[t]=n;else if(x.isEmptyObject(o))this.cache[i]=t;else for(r in t)o[r]=t[r]},get:function(e,t){var n=this.cache[this.key(e)];return t===undefined?n:n[t]},access:function(e,t,n){return t===undefined||t&&"string"==typeof t&&n===undefined?this.get(e,t):(this.set(e,t,n),n!==undefined?n:t)},remove:function(e,t){var n,r,i=this.key(e),o=this.cache[i];if(t===undefined)this.cache[i]={};else{x.isArray(t)?r=t.concat(t.map(x.camelCase)):t in o?r=[t]:(r=x.camelCase(t),r=r in o?[r]:r.match(w)||[]),n=r.length;while(n--)delete o[r[n]]}},hasData:function(e){return!x.isEmptyObject(this.cache[e[this.expando]]||{})},discard:function(e){delete this.cache[this.key(e)]}},L=new F,q=new F,x.extend({acceptData:F.accepts,hasData:function(e){return L.hasData(e)||q.hasData(e)},data:function(e,t,n){return L.access(e,t,n)},removeData:function(e,t){L.remove(e,t)},_data:function(e,t,n){return q.access(e,t,n)},_removeData:function(e,t){q.remove(e,t)}}),x.fn.extend({data:function(e,t){var n,r,i=this[0],o=0,s=null;if(e===undefined){if(this.length&&(s=L.get(i),1===i.nodeType&&!q.get(i,"hasDataAttrs"))){for(n=i.attributes;n.length>o;o++)r=n[o].name,0===r.indexOf("data-")&&(r=x.camelCase(r.substring(5)),P(i,r,s[r]));q.set(i,"hasDataAttrs",!0)}return s}return"object"==typeof e?this.each(function(){L.set(this,e)}):x.access(this,function(t){var n,r=x.camelCase(e);if(i&&t===undefined){if(n=L.get(i,e),n!==undefined)return n;if(n=L.get(i,r),n!==undefined)return n;if(n=P(i,r,undefined),n!==undefined)return n}else this.each(function(){var n=L.get(this,r);L.set(this,r,t),-1!==e.indexOf("-")&&n!==undefined&&L.set(this,e,t)})},null,t,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){L.remove(this,e)})}});function P(e,t,n){var r;if(n===undefined&&1===e.nodeType)if(r="data-"+t.replace(O,"-$1").toLowerCase(),n=e.getAttribute(r),"string"==typeof n){try{n="true"===n?!0:"false"===n?!1:"null"===n?null:+n+""===n?+n:H.test(n)?JSON.parse(n):n}catch(i){}L.set(e,t,n)}else n=undefined;return n}x.extend({queue:function(e,t,n){var r;return e?(t=(t||"fx")+"queue",r=q.get(e,t),n&&(!r||x.isArray(n)?r=q.access(e,t,x.makeArray(n)):r.push(n)),r||[]):undefined},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),o=x._queueHooks(e,t),s=function(){x.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),o.cur=i,i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,s,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return q.get(e,n)||q.access(e,n,{empty:x.Callbacks("once memory").add(function(){q.remove(e,[t+"queue",n])})})}}),x.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),n>arguments.length?x.queue(this[0],e):t===undefined?this:this.each(function(){var n=x.queue(this,e,t); -x._queueHooks(this,e),"fx"===e&&"inprogress"!==n[0]&&x.dequeue(this,e)})},dequeue:function(e){return this.each(function(){x.dequeue(this,e)})},delay:function(e,t){return e=x.fx?x.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=x.Deferred(),o=this,s=this.length,a=function(){--r||i.resolveWith(o,[o])};"string"!=typeof e&&(t=e,e=undefined),e=e||"fx";while(s--)n=q.get(o[s],e+"queueHooks"),n&&n.empty&&(r++,n.empty.add(a));return a(),i.promise(t)}});var R,M,W=/[\t\r\n]/g,$=/\r/g,B=/^(?:input|select|textarea|button)$/i;x.fn.extend({attr:function(e,t){return x.access(this,x.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){x.removeAttr(this,e)})},prop:function(e,t){return x.access(this,x.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[x.propFix[e]||e]})},addClass:function(e){var t,n,r,i,o,s=0,a=this.length,u="string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).addClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];a>s;s++)if(n=this[s],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(W," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=x.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,s=0,a=this.length,u=0===arguments.length||"string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).removeClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];a>s;s++)if(n=this[s],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(W," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?x.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e,i="boolean"==typeof t;return x.isFunction(e)?this.each(function(n){x(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var o,s=0,a=x(this),u=t,l=e.match(w)||[];while(o=l[s++])u=i?u:!a.hasClass(o),a[u?"addClass":"removeClass"](o)}else(n===r||"boolean"===n)&&(this.className&&q.set(this,"__className__",this.className),this.className=this.className||e===!1?"":q.get(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(W," ").indexOf(t)>=0)return!0;return!1},val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=x.isFunction(e),this.each(function(n){var i,o=x(this);1===this.nodeType&&(i=r?e.call(this,n,o.val()):e,null==i?i="":"number"==typeof i?i+="":x.isArray(i)&&(i=x.map(i,function(e){return null==e?"":e+""})),t=x.valHooks[this.type]||x.valHooks[this.nodeName.toLowerCase()],t&&"set"in t&&t.set(this,i,"value")!==undefined||(this.value=i))});if(i)return t=x.valHooks[i.type]||x.valHooks[i.nodeName.toLowerCase()],t&&"get"in t&&(n=t.get(i,"value"))!==undefined?n:(n=i.value,"string"==typeof n?n.replace($,""):null==n?"":n)}}}),x.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,s=o?null:[],a=o?i+1:r.length,u=0>i?a:o?i:0;for(;a>u;u++)if(n=r[u],!(!n.selected&&u!==i||(x.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&x.nodeName(n.parentNode,"optgroup"))){if(t=x(n).val(),o)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=x.makeArray(t),s=i.length;while(s--)r=i[s],(r.selected=x.inArray(x(r).val(),o)>=0)&&(n=!0);return n||(e.selectedIndex=-1),o}}},attr:function(e,t,n){var i,o,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return typeof e.getAttribute===r?x.prop(e,t,n):(1===s&&x.isXMLDoc(e)||(t=t.toLowerCase(),i=x.attrHooks[t]||(x.expr.match.boolean.test(t)?M:R)),n===undefined?i&&"get"in i&&null!==(o=i.get(e,t))?o:(o=x.find.attr(e,t),null==o?undefined:o):null!==n?i&&"set"in i&&(o=i.set(e,n,t))!==undefined?o:(e.setAttribute(t,n+""),n):(x.removeAttr(e,t),undefined))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(w);if(o&&1===e.nodeType)while(n=o[i++])r=x.propFix[n]||n,x.expr.match.boolean.test(n)&&(e[r]=!1),e.removeAttribute(n)},attrHooks:{type:{set:function(e,t){if(!x.support.radioValue&&"radio"===t&&x.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{"for":"htmlFor","class":"className"},prop:function(e,t,n){var r,i,o,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return o=1!==s||!x.isXMLDoc(e),o&&(t=x.propFix[t]||t,i=x.propHooks[t]),n!==undefined?i&&"set"in i&&(r=i.set(e,n,t))!==undefined?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){return e.hasAttribute("tabindex")||B.test(e.nodeName)||e.href?e.tabIndex:-1}}}}),M={set:function(e,t,n){return t===!1?x.removeAttr(e,n):e.setAttribute(n,n),n}},x.each(x.expr.match.boolean.source.match(/\w+/g),function(e,t){var n=x.expr.attrHandle[t]||x.find.attr;x.expr.attrHandle[t]=function(e,t,r){var i=x.expr.attrHandle[t],o=r?undefined:(x.expr.attrHandle[t]=undefined)!=n(e,t,r)?t.toLowerCase():null;return x.expr.attrHandle[t]=i,o}}),x.support.optSelected||(x.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null}}),x.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){x.propFix[this.toLowerCase()]=this}),x.each(["radio","checkbox"],function(){x.valHooks[this]={set:function(e,t){return x.isArray(t)?e.checked=x.inArray(x(e).val(),t)>=0:undefined}},x.support.checkOn||(x.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var I=/^key/,z=/^(?:mouse|contextmenu)|click/,_=/^(?:focusinfocus|focusoutblur)$/,X=/^([^.]*)(?:\.(.+)|)$/;function U(){return!0}function Y(){return!1}function V(){try{return o.activeElement}catch(e){}}x.event={global:{},add:function(e,t,n,i,o){var s,a,u,l,c,f,p,h,d,g,m,y=q.get(e);if(y){n.handler&&(s=n,n=s.handler,o=s.selector),n.guid||(n.guid=x.guid++),(l=y.events)||(l=y.events={}),(a=y.handle)||(a=y.handle=function(e){return typeof x===r||e&&x.event.triggered===e.type?undefined:x.event.dispatch.apply(a.elem,arguments)},a.elem=e),t=(t||"").match(w)||[""],c=t.length;while(c--)u=X.exec(t[c])||[],d=m=u[1],g=(u[2]||"").split(".").sort(),d&&(p=x.event.special[d]||{},d=(o?p.delegateType:p.bindType)||d,p=x.event.special[d]||{},f=x.extend({type:d,origType:m,data:i,handler:n,guid:n.guid,selector:o,needsContext:o&&x.expr.match.needsContext.test(o),namespace:g.join(".")},s),(h=l[d])||(h=l[d]=[],h.delegateCount=0,p.setup&&p.setup.call(e,i,g,a)!==!1||e.addEventListener&&e.addEventListener(d,a,!1)),p.add&&(p.add.call(e,f),f.handler.guid||(f.handler.guid=n.guid)),o?h.splice(h.delegateCount++,0,f):h.push(f),x.event.global[d]=!0);e=null}},remove:function(e,t,n,r,i){var o,s,a,u,l,c,f,p,h,d,g,m=q.hasData(e)&&q.get(e);if(m&&(u=m.events)){t=(t||"").match(w)||[""],l=t.length;while(l--)if(a=X.exec(t[l])||[],h=g=a[1],d=(a[2]||"").split(".").sort(),h){f=x.event.special[h]||{},h=(r?f.delegateType:f.bindType)||h,p=u[h]||[],a=a[2]&&RegExp("(^|\\.)"+d.join("\\.(?:.*\\.|)")+"(\\.|$)"),s=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||a&&!a.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));s&&!p.length&&(f.teardown&&f.teardown.call(e,d,m.handle)!==!1||x.removeEvent(e,h,m.handle),delete u[h])}else for(h in u)x.event.remove(e,h+t[l],n,r,!0);x.isEmptyObject(u)&&(delete m.handle,q.remove(e,"events"))}},trigger:function(t,n,r,i){var s,a,u,l,c,f,p,h=[r||o],d=y.call(t,"type")?t.type:t,g=y.call(t,"namespace")?t.namespace.split("."):[];if(a=u=r=r||o,3!==r.nodeType&&8!==r.nodeType&&!_.test(d+x.event.triggered)&&(d.indexOf(".")>=0&&(g=d.split("."),d=g.shift(),g.sort()),c=0>d.indexOf(":")&&"on"+d,t=t[x.expando]?t:new x.Event(d,"object"==typeof t&&t),t.isTrigger=i?2:3,t.namespace=g.join("."),t.namespace_re=t.namespace?RegExp("(^|\\.)"+g.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=undefined,t.target||(t.target=r),n=null==n?[t]:x.makeArray(n,[t]),p=x.event.special[d]||{},i||!p.trigger||p.trigger.apply(r,n)!==!1)){if(!i&&!p.noBubble&&!x.isWindow(r)){for(l=p.delegateType||d,_.test(l+d)||(a=a.parentNode);a;a=a.parentNode)h.push(a),u=a;u===(r.ownerDocument||o)&&h.push(u.defaultView||u.parentWindow||e)}s=0;while((a=h[s++])&&!t.isPropagationStopped())t.type=s>1?l:p.bindType||d,f=(q.get(a,"events")||{})[t.type]&&q.get(a,"handle"),f&&f.apply(a,n),f=c&&a[c],f&&x.acceptData(a)&&f.apply&&f.apply(a,n)===!1&&t.preventDefault();return t.type=d,i||t.isDefaultPrevented()||p._default&&p._default.apply(h.pop(),n)!==!1||!x.acceptData(r)||c&&x.isFunction(r[d])&&!x.isWindow(r)&&(u=r[c],u&&(r[c]=null),x.event.triggered=d,r[d](),x.event.triggered=undefined,u&&(r[c]=u)),t.result}},dispatch:function(e){e=x.event.fix(e);var t,n,r,i,o,s=[],a=d.call(arguments),u=(q.get(this,"events")||{})[e.type]||[],l=x.event.special[e.type]||{};if(a[0]=e,e.delegateTarget=this,!l.preDispatch||l.preDispatch.call(this,e)!==!1){s=x.event.handlers.call(this,e,u),t=0;while((i=s[t++])&&!e.isPropagationStopped()){e.currentTarget=i.elem,n=0;while((o=i.handlers[n++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(o.namespace))&&(e.handleObj=o,e.data=o.data,r=((x.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,a),r!==undefined&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return l.postDispatch&&l.postDispatch.call(this,e),e.result}},handlers:function(e,t){var n,r,i,o,s=[],a=t.delegateCount,u=e.target;if(a&&u.nodeType&&(!e.button||"click"!==e.type))for(;u!==this;u=u.parentNode||this)if(u.disabled!==!0||"click"!==e.type){for(r=[],n=0;a>n;n++)o=t[n],i=o.selector+" ",r[i]===undefined&&(r[i]=o.needsContext?x(i,this).index(u)>=0:x.find(i,this,null,[u]).length),r[i]&&r.push(o);r.length&&s.push({elem:u,handlers:r})}return t.length>a&&s.push({elem:this,handlers:t.slice(a)}),s},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,t){var n,r,i,s=t.button;return null==e.pageX&&null!=t.clientX&&(n=e.target.ownerDocument||o,r=n.documentElement,i=n.body,e.pageX=t.clientX+(r&&r.scrollLeft||i&&i.scrollLeft||0)-(r&&r.clientLeft||i&&i.clientLeft||0),e.pageY=t.clientY+(r&&r.scrollTop||i&&i.scrollTop||0)-(r&&r.clientTop||i&&i.clientTop||0)),e.which||s===undefined||(e.which=1&s?1:2&s?3:4&s?2:0),e}},fix:function(e){if(e[x.expando])return e;var t,n,r,i=e.type,o=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=z.test(i)?this.mouseHooks:I.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new x.Event(o),t=r.length;while(t--)n=r[t],e[n]=o[n];return 3===e.target.nodeType&&(e.target=e.target.parentNode),s.filter?s.filter(e,o):e},special:{load:{noBubble:!0},focus:{trigger:function(){return this!==V()&&this.focus?(this.focus(),!1):undefined},delegateType:"focusin"},blur:{trigger:function(){return this===V()&&this.blur?(this.blur(),!1):undefined},delegateType:"focusout"},click:{trigger:function(){return"checkbox"===this.type&&this.click&&x.nodeName(this,"input")?(this.click(),!1):undefined},_default:function(e){return x.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){e.result!==undefined&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=x.extend(new x.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?x.event.trigger(i,null,t):x.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},x.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)},x.Event=function(e,t){return this instanceof x.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.getPreventDefault&&e.getPreventDefault()?U:Y):this.type=e,t&&x.extend(this,t),this.timeStamp=e&&e.timeStamp||x.now(),this[x.expando]=!0,undefined):new x.Event(e,t)},x.Event.prototype={isDefaultPrevented:Y,isPropagationStopped:Y,isImmediatePropagationStopped:Y,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=U,e&&e.preventDefault&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=U,e&&e.stopPropagation&&e.stopPropagation()},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=U,this.stopPropagation()}},x.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){x.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!x.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),x.support.focusinBubbles||x.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){x.event.simulate(t,e.target,x.event.fix(e),!0)};x.event.special[t]={setup:function(){0===n++&&o.addEventListener(e,r,!0)},teardown:function(){0===--n&&o.removeEventListener(e,r,!0)}}}),x.fn.extend({on:function(e,t,n,r,i){var o,s;if("object"==typeof e){"string"!=typeof t&&(n=n||t,t=undefined);for(s in e)this.on(s,t,n,e[s],i);return this}if(null==n&&null==r?(r=t,n=t=undefined):null==r&&("string"==typeof t?(r=n,n=undefined):(r=n,n=t,t=undefined)),r===!1)r=Y;else if(!r)return this;return 1===i&&(o=r,r=function(e){return x().off(e),o.apply(this,arguments)},r.guid=o.guid||(o.guid=x.guid++)),this.each(function(){x.event.add(this,e,r,n,t)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,x(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return(t===!1||"function"==typeof t)&&(n=t,t=undefined),n===!1&&(n=Y),this.each(function(){x.event.remove(this,e,n,t)})},trigger:function(e,t){return this.each(function(){x.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];return n?x.event.trigger(e,t,n,!0):undefined}});var G=/^.[^:#\[\.,]*$/,J=x.expr.match.needsContext,Q={children:!0,contents:!0,next:!0,prev:!0};x.fn.extend({find:function(e){var t,n,r,i=this.length;if("string"!=typeof e)return t=this,this.pushStack(x(e).filter(function(){for(r=0;i>r;r++)if(x.contains(t[r],this))return!0}));for(n=[],r=0;i>r;r++)x.find(e,this[r],n);return n=this.pushStack(i>1?x.unique(n):n),n.selector=(this.selector?this.selector+" ":"")+e,n},has:function(e){var t=x(e,this),n=t.length;return this.filter(function(){var e=0;for(;n>e;e++)if(x.contains(this,t[e]))return!0})},not:function(e){return this.pushStack(Z(this,e||[],!0))},filter:function(e){return this.pushStack(Z(this,e||[],!1))},is:function(e){return!!e&&("string"==typeof e?J.test(e)?x(e,this.context).index(this[0])>=0:x.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,o=[],s=J.test(e)||"string"!=typeof e?x(e,t||this.context):0;for(;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(11>n.nodeType&&(s?s.index(n)>-1:1===n.nodeType&&x.find.matchesSelector(n,e))){n=o.push(n);break}return this.pushStack(o.length>1?x.unique(o):o)},index:function(e){return e?"string"==typeof e?g.call(x(e),this[0]):g.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?x(e,t):x.makeArray(e&&e.nodeType?[e]:e),r=x.merge(this.get(),n);return this.pushStack(x.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function K(e,t){while((e=e[t])&&1!==e.nodeType);return e}x.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return x.dir(e,"parentNode")},parentsUntil:function(e,t,n){return x.dir(e,"parentNode",n)},next:function(e){return K(e,"nextSibling")},prev:function(e){return K(e,"previousSibling")},nextAll:function(e){return x.dir(e,"nextSibling")},prevAll:function(e){return x.dir(e,"previousSibling")},nextUntil:function(e,t,n){return x.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return x.dir(e,"previousSibling",n)},siblings:function(e){return x.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return x.sibling(e.firstChild)},contents:function(e){return x.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:x.merge([],e.childNodes)}},function(e,t){x.fn[e]=function(n,r){var i=x.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=x.filter(r,i)),this.length>1&&(Q[e]||x.unique(i),"p"===e[0]&&i.reverse()),this.pushStack(i)}}),x.extend({filter:function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?x.find.matchesSelector(r,e)?[r]:[]:x.find.matches(e,x.grep(t,function(e){return 1===e.nodeType}))},dir:function(e,t,n){var r=[],i=n!==undefined;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&x(e).is(n))break;r.push(e)}return r},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function Z(e,t,n){if(x.isFunction(t))return x.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return x.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(G.test(t))return x.filter(t,e,n);t=x.filter(t,e)}return x.grep(e,function(e){return g.call(t,e)>=0!==n})}var et=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,tt=/<([\w:]+)/,nt=/<|&#?\w+;/,rt=/<(?:script|style|link)/i,it=/^(?:checkbox|radio)$/i,ot=/checked\s*(?:[^=]|=\s*.checked.)/i,st=/^$|\/(?:java|ecma)script/i,at=/^true\/(.*)/,ut=/^\s*\s*$/g,lt={option:[1,""],thead:[1,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],_default:[0,"",""]};lt.optgroup=lt.option,lt.tbody=lt.tfoot=lt.colgroup=lt.caption=lt.col=lt.thead,lt.th=lt.td,x.fn.extend({text:function(e){return x.access(this,function(e){return e===undefined?x.text(this):this.empty().append((this[0]&&this[0].ownerDocument||o).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=ct(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=ct(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=e?x.filter(e,this):this,i=0;for(;null!=(n=r[i]);i++)t||1!==n.nodeType||x.cleanData(gt(n)),n.parentNode&&(t&&x.contains(n.ownerDocument,n)&&ht(gt(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++)1===e.nodeType&&(x.cleanData(gt(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return x.clone(this,e,t)})},html:function(e){return x.access(this,function(e){var t=this[0]||{},n=0,r=this.length;if(e===undefined&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!rt.test(e)&&!lt[(tt.exec(e)||["",""])[1].toLowerCase()]){e=e.replace(et,"<$1>");try{for(;r>n;n++)t=this[n]||{},1===t.nodeType&&(x.cleanData(gt(t,!1)),t.innerHTML=e);t=0}catch(i){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=x.map(this,function(e){return[e.nextSibling,e.parentNode]}),t=0;return this.domManip(arguments,function(n){var r=e[t++],i=e[t++];i&&(x(this).remove(),i.insertBefore(n,r))},!0),t?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t,n){e=p.apply([],e);var r,i,o,s,a,u,l=0,c=this.length,f=this,h=c-1,d=e[0],g=x.isFunction(d);if(g||!(1>=c||"string"!=typeof d||x.support.checkClone)&&ot.test(d))return this.each(function(r){var i=f.eq(r);g&&(e[0]=d.call(this,r,i.html())),i.domManip(e,t,n)});if(c&&(r=x.buildFragment(e,this[0].ownerDocument,!1,!n&&this),i=r.firstChild,1===r.childNodes.length&&(r=i),i)){for(o=x.map(gt(r,"script"),ft),s=o.length;c>l;l++)a=r,l!==h&&(a=x.clone(a,!0,!0),s&&x.merge(o,gt(a,"script"))),t.call(this[l],a,l);if(s)for(u=o[o.length-1].ownerDocument,x.map(o,pt),l=0;s>l;l++)a=o[l],st.test(a.type||"")&&!q.access(a,"globalEval")&&x.contains(u,a)&&(a.src?x._evalUrl(a.src):x.globalEval(a.textContent.replace(ut,"")))}return this}}),x.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){x.fn[e]=function(e){var n,r=[],i=x(e),o=i.length-1,s=0;for(;o>=s;s++)n=s===o?this:this.clone(!0),x(i[s])[t](n),h.apply(r,n.get());return this.pushStack(r)}}),x.extend({clone:function(e,t,n){var r,i,o,s,a=e.cloneNode(!0),u=x.contains(e.ownerDocument,e);if(!(x.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||x.isXMLDoc(e)))for(s=gt(a),o=gt(e),r=0,i=o.length;i>r;r++)mt(o[r],s[r]);if(t)if(n)for(o=o||gt(e),s=s||gt(a),r=0,i=o.length;i>r;r++)dt(o[r],s[r]);else dt(e,a);return s=gt(a,"script"),s.length>0&&ht(s,!u&>(e,"script")),a},buildFragment:function(e,t,n,r){var i,o,s,a,u,l,c=0,f=e.length,p=t.createDocumentFragment(),h=[];for(;f>c;c++)if(i=e[c],i||0===i)if("object"===x.type(i))x.merge(h,i.nodeType?[i]:i);else if(nt.test(i)){o=o||p.appendChild(t.createElement("div")),s=(tt.exec(i)||["",""])[1].toLowerCase(),a=lt[s]||lt._default,o.innerHTML=a[1]+i.replace(et,"<$1>")+a[2],l=a[0];while(l--)o=o.firstChild;x.merge(h,o.childNodes),o=p.firstChild,o.textContent=""}else h.push(t.createTextNode(i));p.textContent="",c=0;while(i=h[c++])if((!r||-1===x.inArray(i,r))&&(u=x.contains(i.ownerDocument,i),o=gt(p.appendChild(i),"script"),u&&ht(o),n)){l=0;while(i=o[l++])st.test(i.type||"")&&n.push(i)}return p},cleanData:function(e){var t,n,r,i=e.length,o=0,s=x.event.special;for(;i>o;o++){if(n=e[o],x.acceptData(n)&&(t=q.access(n)))for(r in t.events)s[r]?x.event.remove(n,r):x.removeEvent(n,r,t.handle);L.discard(n),q.discard(n)}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"text",async:!1,global:!1,success:x.globalEval})}});function ct(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function ft(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function pt(e){var t=at.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function ht(e,t){var n=e.length,r=0;for(;n>r;r++)q.set(e[r],"globalEval",!t||q.get(t[r],"globalEval"))}function dt(e,t){var n,r,i,o,s,a,u,l;if(1===t.nodeType){if(q.hasData(e)&&(o=q.access(e),s=x.extend({},o),l=o.events,q.set(t,s),l)){delete s.handle,s.events={};for(i in l)for(n=0,r=l[i].length;r>n;n++)x.event.add(t,i,l[i][n])}L.hasData(e)&&(a=L.access(e),u=x.extend({},a),L.set(t,u))}}function gt(e,t){var n=e.getElementsByTagName?e.getElementsByTagName(t||"*"):e.querySelectorAll?e.querySelectorAll(t||"*"):[];return t===undefined||t&&x.nodeName(e,t)?x.merge([e],n):n}function mt(e,t){var n=t.nodeName.toLowerCase();"input"===n&&it.test(e.type)?t.checked=e.checked:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}x.fn.extend({wrapAll:function(e){var t;return x.isFunction(e)?this.each(function(t){x(this).wrapAll(e.call(this,t))}):(this[0]&&(t=x(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this)},wrapInner:function(e){return x.isFunction(e)?this.each(function(t){x(this).wrapInner(e.call(this,t))}):this.each(function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=x.isFunction(e);return this.each(function(n){x(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){x.nodeName(this,"body")||x(this).replaceWith(this.childNodes)}).end()}});var yt,vt,xt=/^(none|table(?!-c[ea]).+)/,bt=/^margin/,wt=RegExp("^("+b+")(.*)$","i"),Tt=RegExp("^("+b+")(?!px)[a-z%]+$","i"),Ct=RegExp("^([+-])=("+b+")","i"),kt={BODY:"block"},Nt={position:"absolute",visibility:"hidden",display:"block"},Et={letterSpacing:0,fontWeight:400},St=["Top","Right","Bottom","Left"],jt=["Webkit","O","Moz","ms"];function Dt(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=jt.length;while(i--)if(t=jt[i]+n,t in e)return t;return r}function At(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(e.ownerDocument,e)}function Lt(t){return e.getComputedStyle(t,null)}function qt(e,t){var n,r,i,o=[],s=0,a=e.length;for(;a>s;s++)r=e[s],r.style&&(o[s]=q.get(r,"olddisplay"),n=r.style.display,t?(o[s]||"none"!==n||(r.style.display=""),""===r.style.display&&At(r)&&(o[s]=q.access(r,"olddisplay",Pt(r.nodeName)))):o[s]||(i=At(r),(n&&"none"!==n||!i)&&q.set(r,"olddisplay",i?n:x.css(r,"display"))));for(s=0;a>s;s++)r=e[s],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[s]||"":"none"));return e}x.fn.extend({css:function(e,t){return x.access(this,function(e,t,n){var r,i,o={},s=0;if(x.isArray(t)){for(r=Lt(e),i=t.length;i>s;s++)o[t[s]]=x.css(e,t[s],!1,r);return o}return n!==undefined?x.style(e,t,n):x.css(e,t)},e,t,arguments.length>1)},show:function(){return qt(this,!0)},hide:function(){return qt(this)},toggle:function(e){var t="boolean"==typeof e;return this.each(function(){(t?e:At(this))?x(this).show():x(this).hide()})}}),x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=yt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":"cssFloat"},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,s,a=x.camelCase(t),u=e.style;return t=x.cssProps[a]||(x.cssProps[a]=Dt(u,a)),s=x.cssHooks[t]||x.cssHooks[a],n===undefined?s&&"get"in s&&(i=s.get(e,!1,r))!==undefined?i:u[t]:(o=typeof n,"string"===o&&(i=Ct.exec(n))&&(n=(i[1]+1)*i[2]+parseFloat(x.css(e,t)),o="number"),null==n||"number"===o&&isNaN(n)||("number"!==o||x.cssNumber[a]||(n+="px"),x.support.clearCloneStyle||""!==n||0!==t.indexOf("background")||(u[t]="inherit"),s&&"set"in s&&(n=s.set(e,n,r))===undefined||(u[t]=n)),undefined)}},css:function(e,t,n,r){var i,o,s,a=x.camelCase(t);return t=x.cssProps[a]||(x.cssProps[a]=Dt(e.style,a)),s=x.cssHooks[t]||x.cssHooks[a],s&&"get"in s&&(i=s.get(e,!0,n)),i===undefined&&(i=yt(e,t,r)),"normal"===i&&t in Et&&(i=Et[t]),""===n||n?(o=parseFloat(i),n===!0||x.isNumeric(o)?o||0:i):i}}),yt=function(e,t,n){var r,i,o,s=n||Lt(e),a=s?s.getPropertyValue(t)||s[t]:undefined,u=e.style;return s&&(""!==a||x.contains(e.ownerDocument,e)||(a=x.style(e,t)),Tt.test(a)&&bt.test(t)&&(r=u.width,i=u.minWidth,o=u.maxWidth,u.minWidth=u.maxWidth=u.width=a,a=s.width,u.width=r,u.minWidth=i,u.maxWidth=o)),a};function Ht(e,t,n){var r=wt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function Ot(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,s=0;for(;4>o;o+=2)"margin"===n&&(s+=x.css(e,n+St[o],!0,i)),r?("content"===n&&(s-=x.css(e,"padding"+St[o],!0,i)),"margin"!==n&&(s-=x.css(e,"border"+St[o]+"Width",!0,i))):(s+=x.css(e,"padding"+St[o],!0,i),"padding"!==n&&(s+=x.css(e,"border"+St[o]+"Width",!0,i)));return s}function Ft(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Lt(e),s=x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=yt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Tt.test(i))return i;r=s&&(x.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+Ot(e,t,n||(s?"border":"content"),r,o)+"px"}function Pt(e){var t=o,n=kt[e];return n||(n=Rt(e,t),"none"!==n&&n||(vt=(vt||x("