├── .gitignore ├── lzip128.png ├── lzip16.png ├── lzip48.png ├── devtools.html ├── devtools.js ├── README.md ├── manifest.json ├── LICENSE.md ├── background.js ├── getstorage.js ├── panel.js ├── panel.html └── lz-string.js /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /lzip128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriserickson/lz-localstorage-chrome-extension/HEAD/lzip128.png -------------------------------------------------------------------------------- /lzip16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriserickson/lz-localstorage-chrome-extension/HEAD/lzip16.png -------------------------------------------------------------------------------- /lzip48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriserickson/lz-localstorage-chrome-extension/HEAD/lzip48.png -------------------------------------------------------------------------------- /devtools.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /devtools.js: -------------------------------------------------------------------------------- 1 | chrome.devtools.panels.create("LZ Storage", "lzip128.png", "panel.html", function(panel) { 2 | 3 | }); 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## About 2 | 3 | This is a way to look at Local Storage when the storage is being encrypted with [LZString.js](https://github.com/pieroxy/lz-string). Also 4 | might be useful since it pretty prints JSON (double click on any row to get the Pretty Printed value). Very feature bare, banged 5 | out in a couple of hours as a tool I needed. 6 | 7 | ## Download 8 | 9 | Download from the [Chrome Web Store](https://chrome.google.com/webstore/detail/lzipped-local-storage/beicplgjaeliclenmidelkloajghllll). 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "LZipped Local Storage", 4 | "version": "0.2.2", 5 | "description": "LZipped Local Storage", 6 | "icons": { 7 | "16": "lzip16.png", 8 | "48": "lzip48.png", 9 | "128": "lzip128.png" 10 | }, 11 | "devtools_page": "devtools.html", 12 | "background": { 13 | "persistent": false, 14 | "scripts": [ 15 | "background.js" 16 | ] 17 | }, 18 | "permissions": [ 19 | "background", 20 | "http://*/*", 21 | "https://*/*" 22 | ], 23 | "web_accessible_resources": [ 24 | "getstorage.js", 25 | "background.js" 26 | ] 27 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Kris Erickson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /background.js: -------------------------------------------------------------------------------- 1 | // Chrome automatically creates a background.html page for this to execute. 2 | // This can access the inspected page via executeScript 3 | // 4 | // Can use: 5 | // chrome.tabs.* 6 | // chrome.extension.* 7 | 8 | 9 | chrome.extension.onConnect.addListener(function (port) { 10 | 11 | var _loadedTabId; 12 | 13 | var extensionListener = function (message, sender, sendResponse) { 14 | 15 | function getStorage(tabId, type) { 16 | chrome.tabs.executeScript(tabId, {code: 'lzLocalStorageGetLocalStorage.getStorage("' + type + '");'}) 17 | } 18 | 19 | //port.postMessage('test'); 20 | if (message.action) { 21 | var tabId = message.tabId; 22 | if (message.action == 'load' || (message.action == 'changeStorage' && !_loadedTabId)) { 23 | chrome.tabs.executeScript(tabId, {code: 'window.lzLocalStorageGetLocalStorage'}, function (res) { 24 | if (!res || !res[0]) { 25 | chrome.tabs.executeScript(tabId, {file: 'getstorage.js'}, function () { 26 | _loadedTabId = tabId; 27 | getStorage(tabId, message.storageType); 28 | }); 29 | } else { 30 | getStorage(tabId, message.storageType); 31 | } 32 | }); 33 | } else if (message.action == 'changeStorage' && _loadedTabId) { 34 | getStorage(tabId, message.storageType); 35 | } else if (message.action == 'storage') { 36 | port.postMessage(message); 37 | } 38 | } 39 | }; 40 | 41 | // Listens to messages sent from the panel 42 | chrome.extension.onMessage.addListener(extensionListener); 43 | 44 | port.onDisconnect.addListener(function(port) { 45 | chrome.extension.onMessage.removeListener(extensionListener); 46 | if (_loadedTabId) { 47 | chrome.tabs.executeScript(_loadedTabId, {code: 'lzLocalStorageGetLocalStorage.stopStorage();'}); 48 | } 49 | }); 50 | 51 | 52 | 53 | }); 54 | 55 | -------------------------------------------------------------------------------- /getstorage.js: -------------------------------------------------------------------------------- 1 | if (!window.lzLocalStorageGetLocalStorage) { 2 | 3 | (function () { 4 | 5 | function stringHash(str) { 6 | var hash = 5381; 7 | var length = str.length; 8 | 9 | while (length) { 10 | hash = (hash * 33) ^ str.charCodeAt(length); 11 | length -= 1; 12 | } 13 | 14 | /* JavaScript does bitwise operations (like XOR, above) on 32-bit signed 15 | * integers. Since we want the results to be always positive, convert the 16 | * signed int to an unsigned by doing an unsigned bitshift. */ 17 | return hash >>> 0; 18 | } 19 | 20 | 21 | function LZStorageGetLocalStorage() { 22 | 23 | var _hashResult = ''; 24 | var _timeout = 0; 25 | var _type; 26 | var self = this; 27 | 28 | function sendObjectToDevTools(message) { 29 | // The callback here can be used to execute something on receipt 30 | chrome.extension.sendMessage(message, function (message) { 31 | }); 32 | } 33 | 34 | function checkStorage() { 35 | var storageEngine = _type == 'session' ? window.sessionStorage : window.localStorage; 36 | var hash = ''; 37 | for (var i in storageEngine) { 38 | if (storageEngine.hasOwnProperty(i)) { 39 | hash += stringHash(storageEngine[i]).toString(); 40 | } 41 | } 42 | var hashResult = stringHash(hash); 43 | if (_hashResult != hashResult) { 44 | self.getStorage(); 45 | } 46 | _timeout = setTimeout(checkStorage, 5000); 47 | } 48 | 49 | this.getStorage = function (type) { 50 | if (_timeout) { 51 | clearTimeout(_timeout); 52 | _timeout = 0; 53 | 54 | } 55 | if (type) { 56 | _type = type; 57 | } 58 | var storageEngine = _type == 'session' ? window.sessionStorage : window.localStorage; 59 | var storageInfo = {}; 60 | var hash = ''; 61 | for (var i in storageEngine) { 62 | if (storageEngine.hasOwnProperty(i)) { 63 | storageInfo[i] = storageEngine[i]; 64 | hash += stringHash(storageInfo[i]).toString(); 65 | } 66 | } 67 | _hashResult = stringHash(hash); 68 | sendObjectToDevTools({action: 'storage', data: storageInfo, engine: _type}); 69 | _timeout = setTimeout(checkStorage, 5000); 70 | }; 71 | 72 | this.stopStorage = function () { 73 | if (_timeout) { 74 | clearTimeout(_timeout); 75 | _timeout = 0; 76 | } 77 | }; 78 | 79 | } 80 | 81 | window.lzLocalStorageGetLocalStorage = new LZStorageGetLocalStorage(); 82 | 83 | 84 | })(); 85 | } -------------------------------------------------------------------------------- /panel.js: -------------------------------------------------------------------------------- 1 | // dirty globals here :( 2 | var compressionType = 'utf16'; 3 | var lastData = {}; 4 | 5 | /** 6 | * Convert a json string into 7 | * @param jsonStr 8 | * @returns {string|XML} 9 | */ 10 | function syntaxHighlightJson(jsonStr) { 11 | try { 12 | var jsonObj = JSON.parse(jsonStr); 13 | var json = JSON.stringify(jsonObj, null, 4); 14 | } catch (e) { 15 | // If the json parsing fails... 16 | json = jsonStr; 17 | } 18 | json = json.replace(/&/g, '&').replace(//g, '>'); 19 | return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) { 20 | var cls = 'number'; 21 | if (/^"/.test(match)) { 22 | if (/:$/.test(match)) { 23 | cls = 'key'; 24 | } else { 25 | cls = 'string'; 26 | } 27 | } else if (/true|false/.test(match)) { 28 | cls = 'boolean'; 29 | } else if (/null/.test(match)) { 30 | cls = 'null'; 31 | } 32 | return '' + match + ''; 33 | }); 34 | } 35 | 36 | function humanFileSize(size) { 37 | if (size > 0) { 38 | var i = Math.floor(Math.log(size) / Math.log(1024)); 39 | return ( size / Math.pow(1024, i) ).toFixed(2) * 1 + ['B', 'KB', 'MB', 'GB', 'TB'][i]; 40 | } else { 41 | return '0 Bytes'; 42 | } 43 | } 44 | 45 | /** 46 | * On - Do event delegation for dynamic elements. 47 | * @param elSelector 48 | * @param eventName 49 | * @param selector 50 | * @param fn 51 | */ 52 | function on(elSelector, eventName, selector, fn) { 53 | var element = document.querySelector(elSelector); 54 | 55 | element.addEventListener(eventName, function(event) { 56 | var possibleTargets = element.querySelectorAll(selector); 57 | var target = event.target; 58 | 59 | for (var i = 0, l = possibleTargets.length; i < l; i++) { 60 | var el = target; 61 | var p = possibleTargets[i]; 62 | 63 | while(el && el !== element) { 64 | if (el === p) { 65 | return fn.call(p, event); 66 | } 67 | 68 | el = el.parentNode; 69 | } 70 | } 71 | }); 72 | } 73 | 74 | function decodeString(string) { 75 | var types = { 76 | utf16: 'decompressFromUTF16', 77 | utf16_unsafe: 'decompress', 78 | base64: 'decompressFromBase64', 79 | uriSafe: 'decompressFromEncodedURIComponent', 80 | uint8: 'decompressFromUint8Array' 81 | }; 82 | 83 | if(types[compressionType]) { 84 | try { 85 | var decoded = LZString[types[compressionType]](string); 86 | 87 | if(decoded === null) { 88 | // decoding failed 89 | return string; 90 | } 91 | 92 | return decoded; 93 | 94 | } catch(e) { 95 | return string 96 | } 97 | } 98 | 99 | return string; 100 | } 101 | 102 | function sanitize(str) { 103 | return str.replace(//g, '>'); 105 | } 106 | function updateUI(data) { 107 | var html = ''; 108 | var uncompressedSize = 0; 109 | var compressedSize = 0; 110 | 111 | for (var key in data) { 112 | if (data.hasOwnProperty(key)) { 113 | var value = data[key]; 114 | var compressed = false; 115 | if (value.charCodeAt(0) > 255 || /base64|uri/.test(compressionType)) { 116 | value = decodeString(data[key]); 117 | compressed = value !== data[key]; 118 | } 119 | 120 | compressedSize += data[key].length; 121 | uncompressedSize += value.length; 122 | var size = compressed ? '' + humanFileSize(data[key].length * 2) + ' (' + humanFileSize(value.length * 2) + ')' : 123 | '' + humanFileSize(value.length * 2) + ''; 124 | html += '' + 125 | '' + key + '' + 126 | '' + size + '' + 127 | '' + 128 | sanitize(value) + 129 | '' + 130 | '' + 131 | ''; 132 | } 133 | } 134 | 135 | document.querySelector('#tableBody').innerHTML = html; 136 | document.querySelector('#sizeInfo').style.display = 'inline-block'; 137 | document.querySelector('#compressedSize').innerText = humanFileSize(compressedSize * 2); // Times 2 since we are using UFT-16 to store in localStorage 138 | document.querySelector('#uncompressedSize').innerText = humanFileSize(uncompressedSize * 2); 139 | } 140 | 141 | 142 | (function createChannel() { 143 | //Create a port with background page for continous message communication 144 | var port = chrome.extension.connect({ 145 | name: "LZ Storage Extenstion" //Given a Name 146 | }); 147 | 148 | if (chrome.devtools.panels.themeName === 'dark') { 149 | document.querySelector('body').className += ' darkMode' 150 | } 151 | 152 | chrome.extension.sendMessage({action: "load", tabId: chrome.devtools.inspectedWindow.tabId, storageType: 'local'}); 153 | 154 | // Listen to messages from the background page 155 | port.onMessage.addListener(function (message) { 156 | 157 | if (message.action == 'storage') { 158 | lastData = message.data; 159 | updateUI(message.data); 160 | } 161 | 162 | }); 163 | 164 | 165 | }()); 166 | 167 | 168 | 169 | on('#tableBody', 'click', 'tr', function(event) { 170 | var nodeList = document.querySelectorAll('tr.selected'); 171 | for (var i = 0; i < nodeList.length; i++) { 172 | nodeList[i].classList.remove('selected'); 173 | } 174 | var node = event.target; 175 | while (node.nodeName != 'TR' && node.parentNode) { 176 | node = node.parentNode; 177 | } 178 | node.classList.add('selected'); 179 | }); 180 | 181 | on('#tableBody', 'dblclick', 'tr', function(event) { 182 | 183 | var node = event.target.classList.contains('value-column') ? event.target : event.target.querySelector('.value-column'); 184 | var jsonStr = node.innerText.trim(); 185 | var keyValue = node.attributes['data-key'].value; 186 | document.querySelector('#modalKeyValue').innerText = 'Value for "' + keyValue + '"'; 187 | // If it looks like JSON, then... 188 | if (jsonStr[0] == '{' || jsonStr[0] == '[') { 189 | document.querySelector('#jsonDetails').innerHTML = syntaxHighlightJson(jsonStr); 190 | } 191 | 192 | var style = document.querySelector('.modalDialog').style; 193 | style.opacity = 1; 194 | style.pointerEvents = 'auto'; 195 | }); 196 | 197 | document.getElementById('storageType').addEventListener('change', function(event) { 198 | chrome.extension.sendMessage({action: "load", tabId: chrome.devtools.inspectedWindow.tabId, storageType: event.target.value}); 199 | }); 200 | 201 | document.getElementById('compressionType').addEventListener('change', function(event) { 202 | compressionType = event.target.value; 203 | updateUI(lastData); 204 | }); 205 | 206 | document.querySelector('#close').addEventListener('click', function() { 207 | var style = document.querySelector('.modalDialog').style; 208 | style.opacity = 0; 209 | style.pointerEvents = 'none'; 210 | }); 211 | -------------------------------------------------------------------------------- /panel.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | LZipped Storage Viewer 4 | 5 | 235 | 236 | 237 |
238 |

Local Storage

239 | 243 | 250 | Compressed files shown in red. 251 | 252 | 253 |
254 | Compressed Size: 255 | Uncompressed Size: 256 |
257 |
258 |
259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 |
Key
Size
Value
272 |
273 | 274 |
275 |
X 276 | 277 |

278 | 279 |

280 |     
281 |
282 | 283 | 284 | 285 | 286 | 287 | -------------------------------------------------------------------------------- /lz-string.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Pieroxy 2 | // This work is free. You can redistribute it and/or modify it 3 | // under the terms of the WTFPL, Version 2 4 | // For more information see LICENSE.txt or http://www.wtfpl.net/ 5 | // 6 | // For more information, the home page: 7 | // http://pieroxy.net/blog/pages/lz-string/testing.html 8 | // 9 | // LZ-based compression algorithm, version 1.4.4 10 | var LZString = (function() { 11 | 12 | // private property 13 | var f = String.fromCharCode; 14 | var keyStrBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; 15 | var keyStrUriSafe = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$"; 16 | var baseReverseDic = {}; 17 | 18 | function getBaseValue(alphabet, character) { 19 | if (!baseReverseDic[alphabet]) { 20 | baseReverseDic[alphabet] = {}; 21 | for (var i=0 ; i>> 8; 66 | buf[i*2+1] = current_value % 256; 67 | } 68 | return buf; 69 | }, 70 | 71 | //decompress from uint8array (UCS-2 big endian format) 72 | decompressFromUint8Array:function (compressed) { 73 | if (compressed===null || compressed===undefined){ 74 | return LZString.decompress(compressed); 75 | } else { 76 | var buf=new Array(compressed.length/2); // 2 bytes per character 77 | for (var i=0, TotalLen=buf.length; i> 1; 159 | } 160 | } else { 161 | value = 1; 162 | for (i=0 ; i> 1; 184 | } 185 | } 186 | context_enlargeIn--; 187 | if (context_enlargeIn == 0) { 188 | context_enlargeIn = Math.pow(2, context_numBits); 189 | context_numBits++; 190 | } 191 | delete context_dictionaryToCreate[context_w]; 192 | } else { 193 | value = context_dictionary[context_w]; 194 | for (i=0 ; i> 1; 204 | } 205 | 206 | 207 | } 208 | context_enlargeIn--; 209 | if (context_enlargeIn == 0) { 210 | context_enlargeIn = Math.pow(2, context_numBits); 211 | context_numBits++; 212 | } 213 | // Add wc to the dictionary. 214 | context_dictionary[context_wc] = context_dictSize++; 215 | context_w = String(context_c); 216 | } 217 | } 218 | 219 | // Output the code for w. 220 | if (context_w !== "") { 221 | if (Object.prototype.hasOwnProperty.call(context_dictionaryToCreate,context_w)) { 222 | if (context_w.charCodeAt(0)<256) { 223 | for (i=0 ; i> 1; 244 | } 245 | } else { 246 | value = 1; 247 | for (i=0 ; i> 1; 269 | } 270 | } 271 | context_enlargeIn--; 272 | if (context_enlargeIn == 0) { 273 | context_enlargeIn = Math.pow(2, context_numBits); 274 | context_numBits++; 275 | } 276 | delete context_dictionaryToCreate[context_w]; 277 | } else { 278 | value = context_dictionary[context_w]; 279 | for (i=0 ; i> 1; 289 | } 290 | 291 | 292 | } 293 | context_enlargeIn--; 294 | if (context_enlargeIn == 0) { 295 | context_enlargeIn = Math.pow(2, context_numBits); 296 | context_numBits++; 297 | } 298 | } 299 | 300 | // Mark the end of the stream 301 | value = 2; 302 | for (i=0 ; i> 1; 312 | } 313 | 314 | // Flush the last char 315 | while (true) { 316 | context_data_val = (context_data_val << 1); 317 | if (context_data_position == bitsPerChar-1) { 318 | context_data.push(getCharFromInt(context_data_val)); 319 | break; 320 | } 321 | else context_data_position++; 322 | } 323 | return context_data.join(''); 324 | }, 325 | 326 | decompress: function (compressed) { 327 | if (compressed == null) return ""; 328 | if (compressed == "") return null; 329 | return LZString._decompress(compressed.length, 32768, function(index) { return compressed.charCodeAt(index); }); 330 | }, 331 | 332 | _decompress: function (length, resetValue, getNextValue) { 333 | var dictionary = [], 334 | next, 335 | enlargeIn = 4, 336 | dictSize = 4, 337 | numBits = 3, 338 | entry = "", 339 | result = [], 340 | i, 341 | w, 342 | bits, resb, maxpower, power, 343 | c, 344 | data = {val:getNextValue(0), position:resetValue, index:1}; 345 | 346 | for (i = 0; i < 3; i += 1) { 347 | dictionary[i] = i; 348 | } 349 | 350 | bits = 0; 351 | maxpower = Math.pow(2,2); 352 | power=1; 353 | while (power!=maxpower) { 354 | resb = data.val & data.position; 355 | data.position >>= 1; 356 | if (data.position == 0) { 357 | data.position = resetValue; 358 | data.val = getNextValue(data.index++); 359 | } 360 | bits |= (resb>0 ? 1 : 0) * power; 361 | power <<= 1; 362 | } 363 | 364 | switch (next = bits) { 365 | case 0: 366 | bits = 0; 367 | maxpower = Math.pow(2,8); 368 | power=1; 369 | while (power!=maxpower) { 370 | resb = data.val & data.position; 371 | data.position >>= 1; 372 | if (data.position == 0) { 373 | data.position = resetValue; 374 | data.val = getNextValue(data.index++); 375 | } 376 | bits |= (resb>0 ? 1 : 0) * power; 377 | power <<= 1; 378 | } 379 | c = f(bits); 380 | break; 381 | case 1: 382 | bits = 0; 383 | maxpower = Math.pow(2,16); 384 | power=1; 385 | while (power!=maxpower) { 386 | resb = data.val & data.position; 387 | data.position >>= 1; 388 | if (data.position == 0) { 389 | data.position = resetValue; 390 | data.val = getNextValue(data.index++); 391 | } 392 | bits |= (resb>0 ? 1 : 0) * power; 393 | power <<= 1; 394 | } 395 | c = f(bits); 396 | break; 397 | case 2: 398 | return ""; 399 | } 400 | dictionary[3] = c; 401 | w = c; 402 | result.push(c); 403 | while (true) { 404 | if (data.index > length) { 405 | return ""; 406 | } 407 | 408 | bits = 0; 409 | maxpower = Math.pow(2,numBits); 410 | power=1; 411 | while (power!=maxpower) { 412 | resb = data.val & data.position; 413 | data.position >>= 1; 414 | if (data.position == 0) { 415 | data.position = resetValue; 416 | data.val = getNextValue(data.index++); 417 | } 418 | bits |= (resb>0 ? 1 : 0) * power; 419 | power <<= 1; 420 | } 421 | 422 | switch (c = bits) { 423 | case 0: 424 | bits = 0; 425 | maxpower = Math.pow(2,8); 426 | power=1; 427 | while (power!=maxpower) { 428 | resb = data.val & data.position; 429 | data.position >>= 1; 430 | if (data.position == 0) { 431 | data.position = resetValue; 432 | data.val = getNextValue(data.index++); 433 | } 434 | bits |= (resb>0 ? 1 : 0) * power; 435 | power <<= 1; 436 | } 437 | 438 | dictionary[dictSize++] = f(bits); 439 | c = dictSize-1; 440 | enlargeIn--; 441 | break; 442 | case 1: 443 | bits = 0; 444 | maxpower = Math.pow(2,16); 445 | power=1; 446 | while (power!=maxpower) { 447 | resb = data.val & data.position; 448 | data.position >>= 1; 449 | if (data.position == 0) { 450 | data.position = resetValue; 451 | data.val = getNextValue(data.index++); 452 | } 453 | bits |= (resb>0 ? 1 : 0) * power; 454 | power <<= 1; 455 | } 456 | dictionary[dictSize++] = f(bits); 457 | c = dictSize-1; 458 | enlargeIn--; 459 | break; 460 | case 2: 461 | return result.join(''); 462 | } 463 | 464 | if (enlargeIn == 0) { 465 | enlargeIn = Math.pow(2, numBits); 466 | numBits++; 467 | } 468 | 469 | if (dictionary[c]) { 470 | entry = dictionary[c]; 471 | } else { 472 | if (c === dictSize) { 473 | entry = w + w.charAt(0); 474 | } else { 475 | return null; 476 | } 477 | } 478 | result.push(entry); 479 | 480 | // Add w+entry[0] to the dictionary. 481 | dictionary[dictSize++] = w + entry.charAt(0); 482 | enlargeIn--; 483 | 484 | w = entry; 485 | 486 | if (enlargeIn == 0) { 487 | enlargeIn = Math.pow(2, numBits); 488 | numBits++; 489 | } 490 | 491 | } 492 | } 493 | }; 494 | return LZString; 495 | })(); 496 | 497 | if (typeof define === 'function' && define.amd) { 498 | define(function () { return LZString; }); 499 | } else if( typeof module !== 'undefined' && module != null ) { 500 | module.exports = LZString 501 | } 502 | --------------------------------------------------------------------------------