mirror of
				https://github.com/janeczku/calibre-web
				synced 2025-10-31 15:23:02 +00:00 
			
		
		
		
	This commit is contained in:
		
							
								
								
									
										105
									
								
								cps/static/css/kthoom.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								cps/static/css/kthoom.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,105 @@ | |||||||
|  | body { | ||||||
|  | 	background: #444; | ||||||
|  |     overflow: hidden; | ||||||
|  | 	color: white; | ||||||
|  | 	font-family: sans-serif; | ||||||
|  | 	margin: 0px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .main { | ||||||
|  | 	position: re; | ||||||
|  | 	left: 5px; | ||||||
|  |     overflow: hidden; | ||||||
|  | 	right: 5px; | ||||||
|  | 	text-align: center; | ||||||
|  |     top: 5px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #progress { | ||||||
|  | 	position: absolute; | ||||||
|  | 	display: inline; | ||||||
|  | 	left: 90px; | ||||||
|  | 	right: 160px; | ||||||
|  | 	height: 20px; | ||||||
|  | 	margin-top: 1px; | ||||||
|  | 	text-align: right; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .hide { | ||||||
|  | 	display: none !important; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #mainText { | ||||||
|  |   text-align: left; | ||||||
|  |   width: 90%; | ||||||
|  |   position: relative; | ||||||
|  |   top: 10px; | ||||||
|  |   background: #ccc; | ||||||
|  |   color: black; | ||||||
|  |   margin-right: auto; | ||||||
|  |   margin-left: auto; | ||||||
|  |   padding: 10px; | ||||||
|  |   word-wrap: break-word; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #mainImage{ | ||||||
|  |    margin-top: 32px;  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #titlebar.main { | ||||||
|  |   opacity: 0; | ||||||
|  |   position: absolute; | ||||||
|  |   top: 0; | ||||||
|  |   height: 30px; | ||||||
|  |   left: 0; | ||||||
|  |   right: 0; | ||||||
|  |   background-color: black; | ||||||
|  |   padding-bottom: 70px; | ||||||
|  |   -webkit-transition: opacity 0.2s ease; | ||||||
|  |   -moz-transition: opacity 0.2s ease; | ||||||
|  |   transition: opacity 0.2s ease; | ||||||
|  |   background: -moz-linear-gradient(top, rgba(0,2,34,1) 0%, rgba(0,1,24,1) 30%, rgba(0,0,0,0) 100%); /* FF3.6+ */ | ||||||
|  |   background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(0,2,34,1)), color-stop(30%,rgba(0,1,24,1)), color-stop(100%,rgba(0,0,0,0))); /* Chrome,Safari4+ */ | ||||||
|  |   background: -webkit-linear-gradient(top, rgba(0,2,34,1) 0%,rgba(0,1,24,1) 30%,rgba(0,0,0,0) 100%); /* Chrome10+,Safari5.1+ */ | ||||||
|  |   background: -o-linear-gradient(top, rgba(0,2,34,1) 0%,rgba(0,1,24,1) 30%,rgba(0,0,0,0) 100%); /* Opera11.10+ */ | ||||||
|  |   background: -ms-linear-gradient(top, rgba(0,2,34,1) 0%,rgba(0,1,24,1) 30%,rgba(0,0,0,0) 100%); /* IE10+ */ | ||||||
|  |   filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#000222', endColorstr='#00000000',GradientType=0 ); /* IE6-9 */ | ||||||
|  |   background: linear-gradient(top, rgba(0,2,34,1) 0%,rgba(0,1,24,1) 30%,rgba(0,0,0,0) 100%); /* W3C */ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #prev { | ||||||
|  |   left: 40px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #next { | ||||||
|  |   right: 40px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .arrow { | ||||||
|  |   position: absolute; | ||||||
|  |   top: 50%; | ||||||
|  |   margin-top: -32px; | ||||||
|  |   font-size: 64px; | ||||||
|  |   color: #E2E2E2; | ||||||
|  |   font-family: arial, sans-serif; | ||||||
|  |   font-weight: bold; | ||||||
|  |   cursor: pointer; | ||||||
|  |   -webkit-user-select: none; | ||||||
|  |   -khtml-user-select: none; | ||||||
|  |   -moz-user-select: none; | ||||||
|  |   -ms-user-select: none; | ||||||
|  |   user-select: none; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .arrow:hover { | ||||||
|  |   color: #777; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .arrow:active, | ||||||
|  | .arrow.active { | ||||||
|  |   color: #000; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
							
								
								
									
										353
									
								
								cps/static/js/archive.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										353
									
								
								cps/static/js/archive.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,353 @@ | |||||||
|  | /** | ||||||
|  |  * 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.<string, Array>} | ||||||
|  |    */ | ||||||
|  |   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) }); | ||||||
|  |     if (e.type == bitjs.archive.UnarchiveEvent.Type.FINISH) { | ||||||
|  |         this.worker_.terminate(); | ||||||
|  |     } | ||||||
|  |   } 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}); | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Terminates the Web Worker for this Unarchiver and returns immediately. | ||||||
|  |  */ | ||||||
|  | bitjs.archive.Unarchiver.prototype.stop = function() { | ||||||
|  |   if (this.worker_) { | ||||||
|  |     this.worker_.terminate(); | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 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' }; | ||||||
|  |  | ||||||
|  | })(); | ||||||
							
								
								
									
										483
									
								
								cps/static/js/io.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										483
									
								
								cps/static/js/io.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,483 @@ | |||||||
|  | /* | ||||||
|  |  * 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. | ||||||
|  |  * | ||||||
|  |  * @param {ArrayBuffer} ab An ArrayBuffer object or a Uint8Array. | ||||||
|  |  * @param {boolean} rtl Whether the stream reads bits from the byte starting | ||||||
|  |  *     from bit 7 to 0 (true) or bit 0 to 7 (false). | ||||||
|  |  * @param {Number} opt_offset The offset into the ArrayBuffer | ||||||
|  |  * @param {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. | ||||||
|  |  * @param {number} n The number of bits to peek. | ||||||
|  |  * @param {boolean=} movePointers Whether to move the pointer, defaults false. | ||||||
|  |  * @return {number} The peeked bits, as an unsigned number. | ||||||
|  |  */ | ||||||
|  | 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. | ||||||
|  |  * @param {number} n The number of bits to peek. | ||||||
|  |  * @param {boolean=} movePointers Whether to move the pointer, defaults false. | ||||||
|  |  * @return {number} The peeked bits, as an unsigned number. | ||||||
|  |  */ | ||||||
|  | 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); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Reads n bits out of the stream, consuming them (moving the bit pointer). | ||||||
|  |  * @param {number} n The number of bits to read. | ||||||
|  |  * @return {number} The read bits, as an unsigned number. | ||||||
|  |  */ | ||||||
|  | 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. | ||||||
|  |  * @param {number} n The number of bytes to peek. | ||||||
|  |  * @param {boolean=} movePointers Whether to move the pointer, defaults false. | ||||||
|  |  * @return {Uint8Array} The subarray. | ||||||
|  |  */ | ||||||
|  | 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; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @param {number} n The number of bytes to read. | ||||||
|  |  * @return {Uint8Array} The subarray. | ||||||
|  |  */ | ||||||
|  | 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.  In this buffer, everything must be byte-aligned. | ||||||
|  |  * | ||||||
|  |  * @param {ArrayBuffer} ab The ArrayBuffer object. | ||||||
|  |  * @param {number=} opt_offset The offset into the ArrayBuffer | ||||||
|  |  * @param {number=} opt_length The length of this BitStream | ||||||
|  |  * @constructor | ||||||
|  |  */ | ||||||
|  | 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? | ||||||
|  |  * @param {number} n The number of bytes to peek at. | ||||||
|  |  * @return {number} The n bytes interpreted as an unsigned 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. | ||||||
|  |  * @param {number} n The number of bytes to read. | ||||||
|  |  * @return {number} The n bytes interpreted as an unsigned number. | ||||||
|  |  */ | ||||||
|  | bitjs.io.ByteStream.prototype.readNumber = function(n) { | ||||||
|  |   var num = this.peekNumber( n ); | ||||||
|  |   this.ptr += n; | ||||||
|  |   return num; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Returns the next n bytes as a signed number but does not advance the | ||||||
|  |  * pointer. | ||||||
|  |  * @param {number} n The number of bytes to read. | ||||||
|  |  * @return {number} The bytes interpreted as a signed number. | ||||||
|  |  */ | ||||||
|  | bitjs.io.ByteStream.prototype.peekSignedNumber = function(n) { | ||||||
|  |   var num = this.peekNumber(n); | ||||||
|  |   var HALF = Math.pow(2, (n * 8) - 1); | ||||||
|  |   var FULL = HALF * 2; | ||||||
|  |  | ||||||
|  |   if (num >= HALF) num -= FULL; | ||||||
|  |  | ||||||
|  |   return num; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Returns the next n bytes as a signed number and advances the stream pointer. | ||||||
|  |  * @param {number} n The number of bytes to read. | ||||||
|  |  * @return {number} The bytes interpreted as a signed number. | ||||||
|  |  */ | ||||||
|  | bitjs.io.ByteStream.prototype.readSignedNumber = function(n) { | ||||||
|  |   var num = this.peekSignedNumber(n); | ||||||
|  |   this.ptr += n; | ||||||
|  |   return num; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * This returns n bytes as a sub-array, advancing the pointer if movePointers | ||||||
|  |  * is true. | ||||||
|  |  * @param {number} n The number of bytes to read. | ||||||
|  |  * @param {boolean} movePointers Whether to move the pointers. | ||||||
|  |  * @return {Uint8Array} The subarray. | ||||||
|  |  */ | ||||||
|  | bitjs.io.ByteStream.prototype.peekBytes = function(n, movePointers) { | ||||||
|  |   if (n <= 0 || typeof n != typeof 1) { | ||||||
|  |     return null; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   var result = this.bytes.subarray(this.ptr, this.ptr + n); | ||||||
|  |  | ||||||
|  |   if (movePointers) { | ||||||
|  |     this.ptr += n; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return result; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Reads the next n bytes as a sub-array. | ||||||
|  |  * @param {number} n The number of bytes to read. | ||||||
|  |  * @return {Uint8Array} The subarray. | ||||||
|  |  */ | ||||||
|  | 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. | ||||||
|  |  * @param {number} n The number of bytes to peek at. | ||||||
|  |  * @return {string} The next n bytes as a string. | ||||||
|  |  */ | ||||||
|  | bitjs.io.ByteStream.prototype.peekString = function(n) { | ||||||
|  |   if (n <= 0 || typeof n != typeof 1) { | ||||||
|  |     return ""; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   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 an ASCII string and advances the stream pointer | ||||||
|  |  * n bytes. | ||||||
|  |  * @param {number} n The number of bytes to read. | ||||||
|  |  * @return {string} The next n bytes as a string. | ||||||
|  |  */ | ||||||
|  | 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. | ||||||
|  |  * @param {number} numBytes The number of bytes to allocate. | ||||||
|  |  * @constructor | ||||||
|  |  */ | ||||||
|  | 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; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @param {number} b The byte to insert. | ||||||
|  |  */ | ||||||
|  | bitjs.io.ByteBuffer.prototype.insertByte = function(b) { | ||||||
|  |   // TODO: throw if byte is invalid? | ||||||
|  |   this.data[this.ptr++] = b; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @param {Array.<number>|Uint8Array|Int8Array} bytes The bytes to insert. | ||||||
|  |  */ | ||||||
|  | bitjs.io.ByteBuffer.prototype.insertBytes = function(bytes) { | ||||||
|  |   // TODO: throw if bytes is invalid? | ||||||
|  |   this.data.set(bytes, this.ptr); | ||||||
|  |   this.ptr += bytes.length; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Writes an unsigned number into the next n bytes.  If the number is too large | ||||||
|  |  * to fit into n bytes or is negative, an error is thrown. | ||||||
|  |  * @param {number} num The unsigned number to write. | ||||||
|  |  * @param {number} numBytes The number of bytes to write the number into. | ||||||
|  |  */ | ||||||
|  | bitjs.io.ByteBuffer.prototype.writeNumber = function(num, numBytes) { | ||||||
|  |   if (numBytes < 1) { | ||||||
|  |     throw 'Trying to write into too few bytes: ' + numBytes; | ||||||
|  |   } | ||||||
|  |   if (num < 0) { | ||||||
|  |     throw 'Trying to write a negative number (' + num + | ||||||
|  |         ') as an unsigned number to an ArrayBuffer'; | ||||||
|  |   } | ||||||
|  |   if (num > (Math.pow(2, numBytes * 8) - 1)) { | ||||||
|  |     throw 'Trying to write ' + num + ' into only ' + numBytes + ' bytes'; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Roll 8-bits at a time into an array of bytes. | ||||||
|  |   var bytes = []; | ||||||
|  |   while (numBytes-- > 0) { | ||||||
|  |     var eightBits = num & 255; | ||||||
|  |     bytes.push(eightBits); | ||||||
|  |     num >>= 8; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   this.insertBytes(bytes); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Writes a signed number into the next n bytes.  If the number is too large | ||||||
|  |  * to fit into n bytes, an error is thrown. | ||||||
|  |  * @param {number} num The signed number to write. | ||||||
|  |  * @param {number} numBytes The number of bytes to write the number into. | ||||||
|  |  */ | ||||||
|  | bitjs.io.ByteBuffer.prototype.writeSignedNumber = function(num, numBytes) { | ||||||
|  |   if (numBytes < 1) { | ||||||
|  |     throw 'Trying to write into too few bytes: ' + numBytes; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   var HALF = Math.pow(2, (numBytes * 8) - 1); | ||||||
|  |   if (num >= HALF || num < -HALF) { | ||||||
|  |     throw 'Trying to write ' + num + ' into only ' + numBytes + ' bytes'; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Roll 8-bits at a time into an array of bytes. | ||||||
|  |   var bytes = []; | ||||||
|  |   while (numBytes-- > 0) { | ||||||
|  |     var eightBits = num & 255; | ||||||
|  |     bytes.push(eightBits); | ||||||
|  |     num >>= 8; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   this.insertBytes(bytes); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @param {string} str The ASCII string to write. | ||||||
|  |  */ | ||||||
|  | bitjs.io.ByteBuffer.prototype.writeASCIIString = function(str) { | ||||||
|  |   for (var i = 0; i < str.length; ++i) { | ||||||
|  |     var curByte = str.charCodeAt(i); | ||||||
|  |     if (curByte < 0 || curByte > 255) { | ||||||
|  |       throw 'Trying to write a non-ASCII string!'; | ||||||
|  |     } | ||||||
|  |     this.insertByte(curByte); | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | })(); | ||||||
							
								
								
									
										630
									
								
								cps/static/js/kthoom.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										630
									
								
								cps/static/js/kthoom.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,630 @@ | |||||||
|  | /* | ||||||
|  |  * kthoom.js | ||||||
|  |  * | ||||||
|  |  * Licensed under the MIT License | ||||||
|  |  * | ||||||
|  |  * Copyright(c) 2011 Google Inc. | ||||||
|  |  * Copyright(c) 2011 antimatter15 | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | /* Reference Documentation: | ||||||
|  |  | ||||||
|  |   * Web Workers: http://www.whatwg.org/specs/web-workers/current-work/ | ||||||
|  |   * Web Workers in Mozilla: https://developer.mozilla.org/En/Using_web_workers | ||||||
|  |   * File API (FileReader): http://www.w3.org/TR/FileAPI/ | ||||||
|  |   * Typed Arrays: http://www.khronos.org/registry/typedarray/specs/latest/#6 | ||||||
|  |  | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | if (!window.console) { | ||||||
|  |   window.console = {}; | ||||||
|  |   window.console.log = function(str) {}; | ||||||
|  |   window.console.dir = function(str) {}; | ||||||
|  | } | ||||||
|  | if (window.opera) { | ||||||
|  |   window.console.log = function(str) {opera.postError(str);}; | ||||||
|  |   window.console.dir = function(str) {}; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // gets the element with the given id | ||||||
|  | function getElem(id) { | ||||||
|  |   if (document.documentElement.querySelector) { | ||||||
|  |     // querySelector lookup | ||||||
|  |     return document.body.querySelector('#' + id); | ||||||
|  |   }   | ||||||
|  |   // getElementById lookup | ||||||
|  |   return document.getElementById(id); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | if (window.kthoom == undefined) { | ||||||
|  |   window.kthoom = {}; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // key codes | ||||||
|  | kthoom.Key = { | ||||||
|  |     ESCAPE: 27, | ||||||
|  |     LEFT: 37, | ||||||
|  |     UP: 38, | ||||||
|  |     RIGHT: 39, | ||||||
|  |     DOWN: 40,  | ||||||
|  |     A: 65, B: 66, C: 67, D: 68, E: 69, F: 70, G: 71, H: 72, I: 73, J: 74, K: 75, L: 76, M: 77,  | ||||||
|  |     N: 78, O: 79, P: 80, Q: 81, R: 82, S: 83, T: 84, U: 85, V: 86, W: 87, X: 88, Y: 89, Z: 90, | ||||||
|  |     QUESTION_MARK: 191, | ||||||
|  |     LEFT_SQUARE_BRACKET: 219, | ||||||
|  |     RIGHT_SQUARE_BRACKET: 221 | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // The rotation orientation of the comic. | ||||||
|  | kthoom.rotateTimes = 0; | ||||||
|  |  | ||||||
|  | // global variables | ||||||
|  | var unarchiver = null; | ||||||
|  | var currentImage = 0; | ||||||
|  | var imageFiles = []; | ||||||
|  | var imageFilenames = []; | ||||||
|  | var totalImages = 0; | ||||||
|  | var lastCompletion = 0; | ||||||
|  | var library = { | ||||||
|  |   allBooks: [], | ||||||
|  |   currentBookNum: 0, | ||||||
|  | }; | ||||||
|  |    | ||||||
|  | var hflip = false, vflip = false, fitMode = kthoom.Key.B; | ||||||
|  | var canKeyNext = true, canKeyPrev = true; | ||||||
|  |  | ||||||
|  | kthoom.saveSettings = function() { | ||||||
|  |   localStorage.kthoom_settings = JSON.stringify({ | ||||||
|  |     rotateTimes: kthoom.rotateTimes, | ||||||
|  |     hflip: hflip, | ||||||
|  |     vflip: vflip, | ||||||
|  |     fitMode: fitMode | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | kthoom.loadSettings = function() { | ||||||
|  |   try { | ||||||
|  |     if (localStorage.kthoom_settings.length < 10) return; | ||||||
|  |     var s = JSON.parse(localStorage.kthoom_settings); | ||||||
|  |     kthoom.rotateTimes = s.rotateTimes; | ||||||
|  |     hflip = s.hflip; | ||||||
|  |     vflip = s.vflip; | ||||||
|  |     fitMode = s.fitMode; | ||||||
|  |   } catch(err) { | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Stores an image filename and its data: URI. | ||||||
|  | // TODO: investigate if we really need to store as base64 (leave off ;base64 and just | ||||||
|  | //       non-safe URL characters are encoded as %xx ?) | ||||||
|  | //       This would save 25% on memory since base64-encoded strings are 4/3 the size of the binary | ||||||
|  | kthoom.ImageFile = function(file) { | ||||||
|  |   this.filename = file.filename; | ||||||
|  |   var fileExtension = file.filename.split('.').pop().toLowerCase(); | ||||||
|  |   var mimeType = fileExtension == 'png' ? 'image/png' : | ||||||
|  |       (fileExtension == 'jpg' || fileExtension == 'jpeg') ? 'image/jpeg' : | ||||||
|  |       fileExtension == 'gif' ? 'image/gif' : undefined; | ||||||
|  |   this.dataURI = createURLFromArray(file.fileData, mimeType); | ||||||
|  |   this.data = file; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | kthoom.initProgressMeter = function() { | ||||||
|  |   var svgns = 'http://www.w3.org/2000/svg'; | ||||||
|  |   var pdiv = document.getElementById('progress'); | ||||||
|  |   var svg = document.createElementNS(svgns, 'svg'); | ||||||
|  |   svg.style.width = '100%'; | ||||||
|  |   svg.style.height = '100%'; | ||||||
|  |    | ||||||
|  |   var defs = document.createElementNS(svgns, 'defs'); | ||||||
|  |  | ||||||
|  |   var patt = document.createElementNS(svgns, 'pattern'); | ||||||
|  |   patt.id = 'progress_pattern'; | ||||||
|  |   patt.setAttribute('width', '30'); | ||||||
|  |   patt.setAttribute('height', '20'); | ||||||
|  |   patt.setAttribute('patternUnits', 'userSpaceOnUse'); | ||||||
|  |  | ||||||
|  |   var rect = document.createElementNS(svgns, 'rect'); | ||||||
|  |   rect.setAttribute('width', '100%'); | ||||||
|  |   rect.setAttribute('height', '100%'); | ||||||
|  |   rect.setAttribute('fill', '#cc2929'); | ||||||
|  |    | ||||||
|  |   var poly = document.createElementNS(svgns, 'polygon'); | ||||||
|  |   poly.setAttribute('fill', 'yellow'); | ||||||
|  |   poly.setAttribute('points', '15,0 30,0 15,20 0,20'); | ||||||
|  |  | ||||||
|  |   patt.appendChild(rect); | ||||||
|  |   patt.appendChild(poly); | ||||||
|  |   defs.appendChild(patt); | ||||||
|  |    | ||||||
|  |   svg.appendChild(defs); | ||||||
|  |    | ||||||
|  |   var g = document.createElementNS(svgns, 'g'); | ||||||
|  |    | ||||||
|  |   var outline = document.createElementNS(svgns, 'rect'); | ||||||
|  |   outline.setAttribute('y', '1'); | ||||||
|  |   outline.setAttribute('width', '100%'); | ||||||
|  |   outline.setAttribute('height', '15'); | ||||||
|  |   outline.setAttribute('fill', '#777'); | ||||||
|  |   outline.setAttribute('stroke', 'white'); | ||||||
|  |   outline.setAttribute('rx', '5'); | ||||||
|  |   outline.setAttribute('ry', '5'); | ||||||
|  |   g.appendChild(outline); | ||||||
|  |  | ||||||
|  |   var title = document.createElementNS(svgns, 'text'); | ||||||
|  |   title.id = 'progress_title'; | ||||||
|  |   title.appendChild(document.createTextNode('0%')); | ||||||
|  |   title.setAttribute('y', '13'); | ||||||
|  |   title.setAttribute('x', '99.5%'); | ||||||
|  |   title.setAttribute('fill', 'white'); | ||||||
|  |   title.setAttribute('font-size', '12px'); | ||||||
|  |   title.setAttribute('text-anchor', 'end'); | ||||||
|  |   g.appendChild(title); | ||||||
|  |  | ||||||
|  |   var meter = document.createElementNS(svgns, 'rect'); | ||||||
|  |   meter.id = 'meter'; | ||||||
|  |   meter.setAttribute('width', '0%'); | ||||||
|  |   meter.setAttribute('height', '17'); | ||||||
|  |   meter.setAttribute('fill', 'url(#progress_pattern)'); | ||||||
|  |   meter.setAttribute('rx', '5'); | ||||||
|  |   meter.setAttribute('ry', '5'); | ||||||
|  |    | ||||||
|  |   var meter2 = document.createElementNS(svgns, 'rect'); | ||||||
|  |   meter2.id = 'meter2'; | ||||||
|  |   meter2.setAttribute('width', '0%'); | ||||||
|  |   meter2.setAttribute('height', '17'); | ||||||
|  |   meter2.setAttribute('opacity', '0.8'); | ||||||
|  |   meter2.setAttribute('fill', '#007fff'); | ||||||
|  |   meter2.setAttribute('rx', '5'); | ||||||
|  |   meter2.setAttribute('ry', '5'); | ||||||
|  |    | ||||||
|  |   g.appendChild(meter); | ||||||
|  |   g.appendChild(meter2); | ||||||
|  |    | ||||||
|  |   var page = document.createElementNS(svgns, 'text'); | ||||||
|  |   page.id = 'page'; | ||||||
|  |   page.appendChild(document.createTextNode('0/0')); | ||||||
|  |   page.setAttribute('y', '13'); | ||||||
|  |   page.setAttribute('x', '0.5%'); | ||||||
|  |   page.setAttribute('fill', 'white'); | ||||||
|  |   page.setAttribute('font-size', '12px'); | ||||||
|  |   g.appendChild(page); | ||||||
|  |    | ||||||
|  |    | ||||||
|  |   svg.appendChild(g); | ||||||
|  |   pdiv.appendChild(svg); | ||||||
|  |    | ||||||
|  |   svg.onclick = function(e) { | ||||||
|  |     for (var x = pdiv, l = 0; x != document.documentElement; x = x.parentNode) l += x.offsetLeft; | ||||||
|  |     var page = Math.max(1, Math.ceil(((e.clientX - l)/pdiv.offsetWidth) * totalImages)) - 1; | ||||||
|  |     currentImage = page; | ||||||
|  |     updatePage(); | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | kthoom.setProgressMeter = function(pct, opt_label) { | ||||||
|  |   var pct = (pct*100); | ||||||
|  |   var part = 1/totalImages; | ||||||
|  |   var remain = ((pct - lastCompletion)/100)/part; | ||||||
|  |   var fract = Math.min(1, remain); | ||||||
|  |   var smartpct = ((imageFiles.length/totalImages) + fract * part )* 100; | ||||||
|  |   if (totalImages == 0) smartpct = pct; | ||||||
|  |    | ||||||
|  |    // + Math.min((pct - lastCompletion), 100/totalImages * 0.9 + (pct - lastCompletion - 100/totalImages)/2, 100/totalImages); | ||||||
|  |   var oldval = parseFloat(getElem('meter').getAttribute('width')); | ||||||
|  |   if (isNaN(oldval)) oldval = 0; | ||||||
|  |   var weight = 0.5; | ||||||
|  |   smartpct = (weight * smartpct + (1-weight) * oldval); | ||||||
|  |   if (pct == 100) smartpct = 100; | ||||||
|  |      | ||||||
|  |   if (!isNaN(smartpct)) { | ||||||
|  |     getElem('meter').setAttribute('width', smartpct + '%'); | ||||||
|  |   } | ||||||
|  |   var title = getElem('progress_title'); | ||||||
|  |   while (title.firstChild) title.removeChild(title.firstChild); | ||||||
|  |  | ||||||
|  |   var labelText = pct.toFixed(2) + '% ' + imageFiles.length + '/' + totalImages + ''; | ||||||
|  |   if (opt_label) { | ||||||
|  |     labelText = opt_label + ' ' + labelText; | ||||||
|  |   } | ||||||
|  |   title.appendChild(document.createTextNode(labelText)); | ||||||
|  |   // fade it out as it approaches finish | ||||||
|  |   //title.setAttribute('fill-opacity', (pct > 90) ? ((100-pct)*5)/100 : 1); | ||||||
|  |  | ||||||
|  |   getElem('meter2').setAttribute('width', | ||||||
|  |       100 * (totalImages == 0 ? 0 : ((currentImage+1)/totalImages)) + '%'); | ||||||
|  |    | ||||||
|  |   var title = getElem('page'); | ||||||
|  |   while (title.firstChild) title.removeChild(title.firstChild); | ||||||
|  |   title.appendChild(document.createTextNode( (currentImage+1) + '/' + totalImages )); | ||||||
|  |    | ||||||
|  |    | ||||||
|  |   if (pct > 0) { | ||||||
|  |     //getElem('nav').className = ''; | ||||||
|  |     getElem('progress').className = ''; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function loadFromArrayBuffer(ab) { | ||||||
|  |   var start = (new Date).getTime(); | ||||||
|  |   var h = new Uint8Array(ab, 0, 10); | ||||||
|  |   var pathToBitJS = '../../static/js/'; | ||||||
|  |   if (h[0] == 0x52 && h[1] == 0x61 && h[2] == 0x72 && h[3] == 0x21) { //Rar! | ||||||
|  |     unarchiver = new bitjs.archive.Unrarrer(ab, pathToBitJS); | ||||||
|  |   } else if (h[0] == 80 && h[1] == 75) { //PK (Zip) | ||||||
|  |     unarchiver = new bitjs.archive.Unzipper(ab, pathToBitJS); | ||||||
|  |   } else { // Try with tar | ||||||
|  |     unarchiver = new bitjs.archive.Untarrer(ab, pathToBitJS); | ||||||
|  |   } | ||||||
|  |   // Listen for UnarchiveEvents. | ||||||
|  |   if (unarchiver) { | ||||||
|  |     unarchiver.addEventListener(bitjs.archive.UnarchiveEvent.Type.PROGRESS, | ||||||
|  |       function(e) { | ||||||
|  |         var percentage = e.currentBytesUnarchived / e.totalUncompressedBytesInArchive; | ||||||
|  |         totalImages = e.totalFilesInArchive; | ||||||
|  |         kthoom.setProgressMeter(percentage, 'Unzipping'); | ||||||
|  |         // display nav | ||||||
|  |         lastCompletion = percentage * 100;          | ||||||
|  |       }); | ||||||
|  |     unarchiver.addEventListener(bitjs.archive.UnarchiveEvent.Type.EXTRACT, | ||||||
|  |       function(e) { | ||||||
|  |         // convert DecompressedFile into a bunch of ImageFiles | ||||||
|  |         if (e.unarchivedFile) { | ||||||
|  |           var f = e.unarchivedFile; | ||||||
|  |           // add any new pages based on the filename | ||||||
|  |           if (imageFilenames.indexOf(f.filename) == -1) { | ||||||
|  |             imageFilenames.push(f.filename); | ||||||
|  |             imageFiles.push(new kthoom.ImageFile(f)); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         // display first page if we haven't yet | ||||||
|  |         if (imageFiles.length == currentImage + 1) { | ||||||
|  |           updatePage(); | ||||||
|  |         }             | ||||||
|  |       }); | ||||||
|  |     unarchiver.addEventListener(bitjs.archive.UnarchiveEvent.Type.FINISH, | ||||||
|  |       function(e) { | ||||||
|  |         var diff = ((new Date).getTime() - start)/1000; | ||||||
|  |         console.log('Unarchiving done in ' + diff + 's'); | ||||||
|  |       }) | ||||||
|  |     unarchiver.start(); | ||||||
|  |   } else { | ||||||
|  |     alert('Some error'); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | var createURLFromArray = function(array, mimeType) { | ||||||
|  |   var offset = array.byteOffset, len = array.byteLength; | ||||||
|  |   var bb, url; | ||||||
|  |   var blob; | ||||||
|  |  | ||||||
|  |   // TODO: Move all this browser support testing to a common place | ||||||
|  |   //     and do it just once. | ||||||
|  |  | ||||||
|  |   // Blob constructor, see http://dev.w3.org/2006/webapi/FileAPI/#dfn-Blob. | ||||||
|  |   if (typeof Blob == 'function') { | ||||||
|  |     blob = new Blob([array], {type: mimeType}); | ||||||
|  |   } else { | ||||||
|  |     throw 'Browser support for Blobs is missing.' | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (blob.slice) { | ||||||
|  |     blob = blob.slice(offset, offset + len, mimeType); | ||||||
|  |   } else { | ||||||
|  |     throw 'Browser support for Blobs is missing.' | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if ((typeof URL != 'function' && typeof URL != 'object') || | ||||||
|  |       typeof URL.createObjectURL != 'function') { | ||||||
|  |     throw 'Browser support for Object URLs is missing'; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return URL.createObjectURL(blob); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | function updatePage() { | ||||||
|  |   var title = getElem('page'); | ||||||
|  |   while (title.firstChild) title.removeChild(title.firstChild); | ||||||
|  |   title.appendChild(document.createTextNode( (currentImage+1) + '/' + totalImages )); | ||||||
|  |    | ||||||
|  |   getElem('meter2').setAttribute('width', | ||||||
|  |       100 * (totalImages == 0 ? 0 : ((currentImage+1)/totalImages)) + '%'); | ||||||
|  |   if (imageFiles[currentImage]) { | ||||||
|  |     setImage(imageFiles[currentImage].dataURI); | ||||||
|  |   } else { | ||||||
|  |     setImage('loading'); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function setImage(url) { | ||||||
|  |   var canvas = getElem('mainImage'); | ||||||
|  |   var x = canvas.getContext('2d'); | ||||||
|  |   document.getElementById('mainText').style.display = 'none'; | ||||||
|  |   if (url == 'loading') { | ||||||
|  |     updateScale(true); | ||||||
|  |     canvas.width = innerWidth - 100; | ||||||
|  |     canvas.height = 200; | ||||||
|  |     x.fillStyle = 'red'; | ||||||
|  |     x.font = '50px sans-serif'; | ||||||
|  |     x.strokeStyle = 'black'; | ||||||
|  |     x.fillText('Loading Page #' + (currentImage + 1), 100, 100) | ||||||
|  |   } else { | ||||||
|  |     if (document.body.scrollHeight/innerHeight > 1) { | ||||||
|  |       document.body.style.overflowY = 'scroll'; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     var img = new Image(); | ||||||
|  |     img.onerror = function(e) { | ||||||
|  |       canvas.width = innerWidth - 100; | ||||||
|  |       canvas.height = 300; | ||||||
|  |       updateScale(true); | ||||||
|  |       x.fillStyle = 'orange'; | ||||||
|  |       x.font = '50px sans-serif'; | ||||||
|  |       x.strokeStyle = 'black'; | ||||||
|  |       x.fillText('Page #' + (currentImage+1) + ' (' + | ||||||
|  |           imageFiles[currentImage].filename + ')', 100, 100) | ||||||
|  |       x.fillStyle = 'red'; | ||||||
|  |       x.fillText('Is corrupt or not an image', 100, 200); | ||||||
|  |        | ||||||
|  |       if (/(html|htm)$/.test(imageFiles[currentImage].filename)) { | ||||||
|  |         var xhr = new XMLHttpRequest(); | ||||||
|  |         xhr.open('GET', url, true); | ||||||
|  |         xhr.onload = function() { | ||||||
|  |           document.getElementById('mainText').style.display = ''; | ||||||
|  |           document.getElementById('mainText').innerHTML = '<iframe style="width:100%;height:700px;border:0" src="data:text/html,'+escape(xhr.responseText)+'"></iframe>'; | ||||||
|  |         } | ||||||
|  |         xhr.send(null); | ||||||
|  |       } else if (!/(jpg|jpeg|png|gif)$/.test(imageFiles[currentImage].filename) && imageFiles[currentImage].data.uncompressedSize < 10*1024) { | ||||||
|  |         var xhr = new XMLHttpRequest(); | ||||||
|  |         xhr.open('GET', url, true); | ||||||
|  |         xhr.onload = function() { | ||||||
|  |           document.getElementById('mainText').style.display = ''; | ||||||
|  |           document.getElementById('mainText').innerText = xhr.responseText; | ||||||
|  |         }; | ||||||
|  |         xhr.send(null); | ||||||
|  |       } | ||||||
|  |     }; | ||||||
|  |     img.onload = function() { | ||||||
|  |       var h = img.height,  | ||||||
|  |           w = img.width,  | ||||||
|  |           sw = w,  | ||||||
|  |           sh = h; | ||||||
|  |       kthoom.rotateTimes =  (4 + kthoom.rotateTimes) % 4; | ||||||
|  |       x.save(); | ||||||
|  |       if (kthoom.rotateTimes % 2 == 1) { sh = w; sw = h;} | ||||||
|  |       canvas.height = sh; | ||||||
|  |       canvas.width = sw; | ||||||
|  |       x.translate(sw/2, sh/2); | ||||||
|  |       x.rotate(Math.PI/2 * kthoom.rotateTimes); | ||||||
|  |       x.translate(-w/2, -h/2); | ||||||
|  |       if (vflip) { | ||||||
|  |         x.scale(1, -1) | ||||||
|  |         x.translate(0, -h); | ||||||
|  |       } | ||||||
|  |       if (hflip) { | ||||||
|  |         x.scale(-1, 1) | ||||||
|  |         x.translate(-w, 0); | ||||||
|  |       } | ||||||
|  |       canvas.style.display = 'none'; | ||||||
|  |       scrollTo(0,0); | ||||||
|  |       x.drawImage(img, 0, 0); | ||||||
|  |        | ||||||
|  |       updateScale(); | ||||||
|  |          | ||||||
|  |       canvas.style.display = ''; | ||||||
|  |       document.body.style.overflowY = ''; | ||||||
|  |       x.restore(); | ||||||
|  |     }; | ||||||
|  |     img.src = url; | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function showPrevPage() { | ||||||
|  |   currentImage--; | ||||||
|  |  | ||||||
|  |   if (currentImage < 0) { | ||||||
|  |     if (library.allBooks.length == 1) { | ||||||
|  |       currentImage = imageFiles.length - 1; | ||||||
|  |     } else if (library.currentBookNum > 0) { | ||||||
|  |       loadPrevBook(); | ||||||
|  |     } else { | ||||||
|  |       // Freeze on the current page. | ||||||
|  |       currentImage++; | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   updatePage(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function showNextPage() { | ||||||
|  |   currentImage++; | ||||||
|  |    | ||||||
|  |   if (currentImage >= Math.max(totalImages, imageFiles.length)) { | ||||||
|  |     if (library.allBooks.length == 1) { | ||||||
|  |       currentImage = 0; | ||||||
|  |     } else if (library.currentBookNum < library.allBooks.length - 1) { | ||||||
|  |       loadNextBook(); | ||||||
|  |     } else { | ||||||
|  |       // Freeze on the current page. | ||||||
|  |       currentImage--; | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   updatePage(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function updateScale(clear) { | ||||||
|  |   var mainImageStyle = getElem('mainImage').style; | ||||||
|  |   mainImageStyle.width = ''; | ||||||
|  |   mainImageStyle.height = ''; | ||||||
|  |   mainImageStyle.maxWidth = ''; | ||||||
|  |   mainImageStyle.maxHeight = ''; | ||||||
|  |   var maxheight = innerHeight - 15; | ||||||
|  |   if (!/main/.test(getElem('titlebar').className)) { | ||||||
|  |     maxheight -= 25; | ||||||
|  |   } | ||||||
|  |   if (clear || fitMode == kthoom.Key.N) { | ||||||
|  |   } else if (fitMode == kthoom.Key.B) { | ||||||
|  |     mainImageStyle.maxWidth = '100%'; | ||||||
|  |     mainImageStyle.maxHeight = maxheight + 'px'; | ||||||
|  |   } else if (fitMode == kthoom.Key.H) { | ||||||
|  |     mainImageStyle.height = maxheight + 'px'; | ||||||
|  |   } else if (fitMode == kthoom.Key.W) { | ||||||
|  |     mainImageStyle.width = '100%'; | ||||||
|  |   } | ||||||
|  |   kthoom.saveSettings(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function keyHandler(evt) { | ||||||
|  |   var code = evt.keyCode; | ||||||
|  |  | ||||||
|  |   // If the overlay is shown, the only keystroke we handle is closing it. | ||||||
|  |   var overlayStyle = getElem('overlay').style; | ||||||
|  |   var overlayShown = (overlayStyle.display != 'none'); | ||||||
|  |   if (overlayShown) { | ||||||
|  |     if (code == kthoom.Key.QUESTION_MARK || code == kthoom.Key.ESCAPE) { | ||||||
|  |       getElem('menu').classList.remove('opened'); | ||||||
|  |       overlayStyle.display = 'none'; | ||||||
|  |     } | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |   if (getComputedStyle(getElem('progress')).display == 'none') return; | ||||||
|  |   canKeyNext = ((document.body.offsetWidth+document.body.scrollLeft)/ document.body.scrollWidth) >= 1; | ||||||
|  |   canKeyPrev = (scrollX <= 0); | ||||||
|  |  | ||||||
|  |   if (evt.ctrlKey || evt.shiftKey || evt.metaKey) return; | ||||||
|  |   switch(code) { | ||||||
|  |     case kthoom.Key.X: | ||||||
|  |       toggleToolbar(); | ||||||
|  |       break; | ||||||
|  |     case kthoom.Key.LEFT: | ||||||
|  |       if (canKeyPrev) showPrevPage(); | ||||||
|  |       break; | ||||||
|  |     case kthoom.Key.RIGHT: | ||||||
|  |       if (canKeyNext) showNextPage(); | ||||||
|  |       break; | ||||||
|  |     case kthoom.Key.LEFT_SQUARE_BRACKET: | ||||||
|  |       if (library.currentBookNum > 0) { | ||||||
|  |         loadPrevBook(); | ||||||
|  |       } | ||||||
|  |       break; | ||||||
|  |     case kthoom.Key.RIGHT_SQUARE_BRACKET: | ||||||
|  |       if (library.currentBookNum < library.allBooks.length - 1) { | ||||||
|  |         loadNextBook(); | ||||||
|  |       } | ||||||
|  |       break; | ||||||
|  |     case kthoom.Key.L: | ||||||
|  |       kthoom.rotateTimes--; | ||||||
|  |       if (kthoom.rotateTimes < 0) { | ||||||
|  |         kthoom.rotateTimes = 3; | ||||||
|  |       } | ||||||
|  |       updatePage(); | ||||||
|  |       break; | ||||||
|  |     case kthoom.Key.R: | ||||||
|  |       kthoom.rotateTimes++; | ||||||
|  |       if (kthoom.rotateTimes > 3) { | ||||||
|  |         kthoom.rotateTimes = 0; | ||||||
|  |       } | ||||||
|  |       updatePage(); | ||||||
|  |       break; | ||||||
|  |     case kthoom.Key.F: | ||||||
|  |       if (!hflip && !vflip) { | ||||||
|  |         hflip = true; | ||||||
|  |       } else if(hflip == true) { | ||||||
|  |         vflip = true; | ||||||
|  |         hflip = false; | ||||||
|  |       } else if(vflip == true) { | ||||||
|  |         vflip = false; | ||||||
|  |       } | ||||||
|  |       updatePage(); | ||||||
|  |       break; | ||||||
|  |     case kthoom.Key.W: | ||||||
|  |       fitMode = kthoom.Key.W; | ||||||
|  |       updateScale(); | ||||||
|  |       break; | ||||||
|  |     case kthoom.Key.H: | ||||||
|  |       fitMode = kthoom.Key.H; | ||||||
|  |       updateScale(); | ||||||
|  |       break; | ||||||
|  |     case kthoom.Key.B: | ||||||
|  |       fitMode = kthoom.Key.B; | ||||||
|  |       updateScale(); | ||||||
|  |       break; | ||||||
|  |     case kthoom.Key.N: | ||||||
|  |       fitMode = kthoom.Key.N; | ||||||
|  |       updateScale(); | ||||||
|  |       break; | ||||||
|  |     default: | ||||||
|  |       //console.log('KeyCode = ' + code); | ||||||
|  |       break; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function init(filename) { | ||||||
|  |   if (!window.FileReader) { | ||||||
|  |     alert('Sorry, kthoom will not work with your browser because it does not support the File API.  Please try kthoom with Chrome 12+ or Firefox 7+'); | ||||||
|  |   } else { | ||||||
|  |       var request = new XMLHttpRequest(); | ||||||
|  |       request.open("GET",filename); | ||||||
|  |       request.responseType="arraybuffer"; | ||||||
|  |       request.setRequestHeader("X-Test","test1"); | ||||||
|  |       request.setRequestHeader("X-Test","test2"); | ||||||
|  |       request.addEventListener('load', function(event) { | ||||||
|  |       if (request.status >= 200 && request.status < 300) { | ||||||
|  |             loadFromArrayBuffer(request.response); | ||||||
|  |       } else { | ||||||
|  |           console.warn(request.statusText, request.responseText); | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |     request.send(); | ||||||
|  |     kthoom.initProgressMeter(); | ||||||
|  |     document.body.className += /AppleWebKit/.test(navigator.userAgent) ? ' webkit' : ''; | ||||||
|  |     //kthoom.resetFileUploader(); | ||||||
|  |     kthoom.loadSettings(); | ||||||
|  |     document.addEventListener('keydown', keyHandler, false); | ||||||
|  |     window.addEventListener('resize', function() { | ||||||
|  |       var f = (screen.width - innerWidth < 4 && screen.height - innerHeight < 4); | ||||||
|  |       getElem('titlebar').className = f ? 'main' : ''; | ||||||
|  |       updateScale(); | ||||||
|  |     }, false); | ||||||
|  |     getElem('mainImage').addEventListener('click', function(evt) { | ||||||
|  |       // Firefox does not support offsetX/Y so we have to manually calculate | ||||||
|  |       // where the user clicked in the image. | ||||||
|  |       var mainContentWidth = getElem('mainContent').clientWidth; | ||||||
|  |       var mainContentHeight = getElem('mainContent').clientHeight; | ||||||
|  |       var comicWidth = evt.target.clientWidth; | ||||||
|  |       var comicHeight = evt.target.clientHeight; | ||||||
|  |       var offsetX = (mainContentWidth - comicWidth) / 2; | ||||||
|  |       var offsetY = (mainContentHeight - comicHeight) / 2; | ||||||
|  |       var clickX = !!evt.offsetX ? evt.offsetX : (evt.clientX - offsetX); | ||||||
|  |       var clickY = !!evt.offsetY ? evt.offsetY : (evt.clientY - offsetY); | ||||||
|  |  | ||||||
|  |       // Determine if the user clicked/tapped the left side or the | ||||||
|  |       // right side of the page. | ||||||
|  |       var clickedPrev = false; | ||||||
|  |       switch (kthoom.rotateTimes) { | ||||||
|  |         case 0: | ||||||
|  |           clickedPrev = clickX < (comicWidth / 2); | ||||||
|  |           break; | ||||||
|  |         case 1: | ||||||
|  |           clickedPrev = clickY < (comicHeight / 2); | ||||||
|  |           break; | ||||||
|  |         case 2: | ||||||
|  |           clickedPrev = clickX > (comicWidth / 2); | ||||||
|  |           break; | ||||||
|  |         case 3: | ||||||
|  |           clickedPrev = clickY > (comicHeight / 2); | ||||||
|  |           break; | ||||||
|  |       } | ||||||
|  |       if (clickedPrev) { | ||||||
|  |         showPrevPage(); | ||||||
|  |       } else { | ||||||
|  |         showNextPage(); | ||||||
|  |       } | ||||||
|  |     }, false); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										896
									
								
								cps/static/js/unrar.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										896
									
								
								cps/static/js/unrar.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,896 @@ | |||||||
|  | /** | ||||||
|  |  * 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<<BitLength)) { | ||||||
|  |       DDecode[Slot]=Dist; | ||||||
|  |       DBits[Slot]=BitLength; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   var Bits; | ||||||
|  |   //tablesRead = false; | ||||||
|  |  | ||||||
|  |   rOldDist = [0,0,0,0] | ||||||
|  |    | ||||||
|  |   lastDist = 0; | ||||||
|  |   lastLength = 0; | ||||||
|  |  | ||||||
|  |   for (var i = UnpOldTable.length; i--;) UnpOldTable[i] = 0; | ||||||
|  |      | ||||||
|  |   // read in Huffman tables | ||||||
|  |   RarReadTables(bstream); | ||||||
|  |   | ||||||
|  |   while (true) { | ||||||
|  |     var num = RarDecodeNumber(bstream, LD); | ||||||
|  |      | ||||||
|  |     if (num < 256) { | ||||||
|  |       rBuffer.insertByte(num); | ||||||
|  |       continue; | ||||||
|  |     } | ||||||
|  |     if (num >= 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; | ||||||
|  |  | ||||||
|  |       // Create a new buffer and copy it over. | ||||||
|  |       var len = this.header.packSize; | ||||||
|  |       var newBuffer = new bitjs.io.ByteBuffer(len); | ||||||
|  |       newBuffer.insertBytes(this.fileData); | ||||||
|  |       this.fileData = newBuffer.data; | ||||||
|  |     } 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) { | ||||||
|  |         var aname = a.filename.toLowerCase(); | ||||||
|  |         var bname = b.filename.toLowerCase(); | ||||||
|  |         return aname > bname ? 1 : -1; | ||||||
|  | 		  }); | ||||||
|  |  | ||||||
|  |       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); | ||||||
|  | }; | ||||||
							
								
								
									
										168
									
								
								cps/static/js/untar.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								cps/static/js/untar.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,168 @@ | |||||||
|  | /** | ||||||
|  |  * 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) { | ||||||
|  |       var aname = a.filename.toLowerCase(); | ||||||
|  |       var bname = b.filename.toLowerCase(); | ||||||
|  |       return aname > bname ? 1 : -1; | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   // 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); | ||||||
|  | }; | ||||||
							
								
								
									
										617
									
								
								cps/static/js/unzip.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										617
									
								
								cps/static/js/unzip.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,617 @@ | |||||||
|  | /** | ||||||
|  |  * 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) { | ||||||
|  | 			var aname = a.filename.toLowerCase(); | ||||||
|  | 			var bname = b.filename.toLowerCase(); | ||||||
|  | 			return aname > bname ? 1 : -1; | ||||||
|  | 		}); | ||||||
|  |  | ||||||
|  | 		// 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 <length = 5, distance = 2> | ||||||
|  | 				//  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); | ||||||
|  | }; | ||||||
							
								
								
									
										35
									
								
								cps/templates/readcbr.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								cps/templates/readcbr.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | |||||||
|  | <!DOCTYPE html> | ||||||
|  | <html> | ||||||
|  |   <head> | ||||||
|  |   	<meta charset="utf-8"> | ||||||
|  |   	<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> | ||||||
|  |     <title>Comic Reader</title> | ||||||
|  |     <meta name="description" content=""> | ||||||
|  | 	<meta name="viewport" content="width=device-width, user-scalable=no"> | ||||||
|  |     <meta name="apple-mobile-web-app-capable" content="yes"> | ||||||
|  |     <link rel="stylesheet" href="{{ url_for('static', filename='css/kthoom.css') }}" type="text/css"/> | ||||||
|  |     <script src="{{ url_for('static', filename='js/libs/jquery.min.js') }}"></script> | ||||||
|  | 	<script src="{{ url_for('static', filename='js/kthoom.js') }}"></script> | ||||||
|  | 	<script src="{{ url_for('static', filename='js/archive.js') }}"></script> | ||||||
|  |     <script> | ||||||
|  |             document.onreadystatechange = function () { | ||||||
|  |               if (document.readyState == "complete") { | ||||||
|  |                 init("{{ url_for('static', filename=comicfile) }}"); | ||||||
|  |                 }                 | ||||||
|  |             }; | ||||||
|  |     </script>     | ||||||
|  |   </head> | ||||||
|  |   <body> | ||||||
|  |     <div class="main" id="main"> | ||||||
|  | 	    <div id="titlebar"> | ||||||
|  | 	      <div id="progress" class="hide"></div> | ||||||
|  | 	    </div> | ||||||
|  | 	    <div id="mainContent"> | ||||||
|  | 	      <div id="mainText" style="display:none"></div> | ||||||
|  |       	<canvas id="mainImage"></canvas> | ||||||
|  | 	    </div> | ||||||
|  |           <div id="prev" class="arrow" onclick="showPrevPage()">‹</div> | ||||||
|  |           <div id="next" class="arrow" onclick="showNextPage()">›</div> | ||||||
|  |     </div> | ||||||
|  |   </body> | ||||||
|  | </html> | ||||||
							
								
								
									
										10
									
								
								cps/web.py
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								cps/web.py
									
									
									
									
									
								
							| @@ -1807,11 +1807,13 @@ def read_book(book_id, book_format): | |||||||
|         return render_title_template('readpdf.html', pdffile=book_id, title=_(u"Read a Book")) |         return render_title_template('readpdf.html', pdffile=book_id, title=_(u"Read a Book")) | ||||||
|     elif book_format.lower() == "txt": |     elif book_format.lower() == "txt": | ||||||
|         return render_title_template('readtxt.html', txtfile=book_id, title=_(u"Read a Book")) |         return render_title_template('readtxt.html', txtfile=book_id, title=_(u"Read a Book")) | ||||||
|     elif book_format.lower() == "cbr": |     else: | ||||||
|         all_name = str(book_id) + "/" + book.data[0].name + ".cbr" |         for format in ["cbr","cbt","cbz"]: | ||||||
|         tmp_file = os.path.join(book_dir, book.data[0].name) + ".cbr" |             if book_format.lower() == format: | ||||||
|  |                 all_name = str(book_id) + "/" + book.data[0].name + "." + format | ||||||
|  |                 tmp_file = os.path.join(book_dir, book.data[0].name) + "." + format | ||||||
|                 if not os.path.exists(all_name): |                 if not os.path.exists(all_name): | ||||||
|             cbr_file = os.path.join(config.config_calibre_dir, book.path, book.data[0].name) + ".cbr" |                     cbr_file = os.path.join(config.config_calibre_dir, book.path, book.data[0].name) + "." + format | ||||||
|                     copyfile(cbr_file, tmp_file) |                     copyfile(cbr_file, tmp_file) | ||||||
|                 return render_title_template('readcbr.html', comicfile=all_name, title=_(u"Read a Book")) |                 return render_title_template('readcbr.html', comicfile=all_name, title=_(u"Read a Book")) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ Calibre Web is a web app providing a clean interface for browsing, reading and d | |||||||
|  |  | ||||||
| - Bootstrap 3 HTML5 interface | - Bootstrap 3 HTML5 interface | ||||||
| - full graphical setup | - full graphical setup | ||||||
| - User management | - User management with fine grained per-user permissions | ||||||
| - Admin interface | - Admin interface | ||||||
| - User Interface in dutch, english, french, german, italian, polish, russian, simplified chinese, spanish | - User Interface in dutch, english, french, german, italian, polish, russian, simplified chinese, spanish | ||||||
| - OPDS feed for eBook reader apps  | - OPDS feed for eBook reader apps  | ||||||
| @@ -21,10 +21,10 @@ Calibre Web is a web app providing a clean interface for browsing, reading and d | |||||||
| - Restrict eBook download to logged-in users | - Restrict eBook download to logged-in users | ||||||
| - Support for public user registration | - Support for public user registration | ||||||
| - Send eBooks to Kindle devices with the click of a button | - Send eBooks to Kindle devices with the click of a button | ||||||
| - Support for reading eBooks directly in the browser (.txt, .epub, .pdf) | - Support for reading eBooks directly in the browser (.txt, .epub, .pdf, .cbr, .cbt, .cbz) | ||||||
| - Upload new books in PDF, epub, fb2 format | - Upload new books in PDF, epub, fb2 format | ||||||
| - Support for Calibre custom columns | - Support for Calibre custom columns | ||||||
| - Fine grained per-user permissions | - Ability to hide content based on categories for certain users | ||||||
| - Self update capability | - Self update capability | ||||||
| - "Magic Link" login to make it easy to log on eReaders | - "Magic Link" login to make it easy to log on eReaders | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 OzzieIsaacs
					OzzieIsaacs