From 21de93375f23daede8e4e9ac9d8bd1984fdce7d0 Mon Sep 17 00:00:00 2001 From: Hannes Mannerheim Date: Mon, 21 Sep 2015 18:30:37 +0200 Subject: [PATCH] compress data in localStorage with lz-string --- actions/qvitter.php | 1 + js/lib/lz-string.js | 501 +++++++++++++++++++++++++++++++++++++++++++ js/misc-functions.js | 41 +++- 3 files changed, 537 insertions(+), 6 deletions(-) create mode 100644 js/lib/lz-string.js diff --git a/actions/qvitter.php b/actions/qvitter.php index 50386db..f732ec8 100644 --- a/actions/qvitter.php +++ b/actions/qvitter.php @@ -515,6 +515,7 @@ class QvitterAction extends ApiAction + diff --git a/js/lib/lz-string.js b/js/lib/lz-string.js new file mode 100644 index 0000000..2c4a11a --- /dev/null +++ b/js/lib/lz-string.js @@ -0,0 +1,501 @@ +// Copyright (c) 2013 Pieroxy +// This work is free. You can redistribute it and/or modify it +// under the terms of the WTFPL, Version 2 +// For more information see LICENSE.txt or http://www.wtfpl.net/ +// +// For more information, the home page: +// http://pieroxy.net/blog/pages/lz-string/testing.html +// +// LZ-based compression algorithm, version 1.4.4 +var LZString = (function() { + +// private property +var f = String.fromCharCode; +var keyStrBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; +var keyStrUriSafe = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$"; +var baseReverseDic = {}; + +function getBaseValue(alphabet, character) { + if (!baseReverseDic[alphabet]) { + baseReverseDic[alphabet] = {}; + for (var i=0 ; i>> 8; + buf[i*2+1] = current_value % 256; + } + return buf; + }, + + //decompress from uint8array (UCS-2 big endian format) + decompressFromUint8Array:function (compressed) { + if (compressed===null || compressed===undefined){ + return LZString.decompress(compressed); + } else { + var buf=new Array(compressed.length/2); // 2 bytes per character + for (var i=0, TotalLen=buf.length; i> 1; + } + } else { + value = 1; + for (i=0 ; i> 1; + } + } + context_enlargeIn--; + if (context_enlargeIn == 0) { + context_enlargeIn = Math.pow(2, context_numBits); + context_numBits++; + } + delete context_dictionaryToCreate[context_w]; + } else { + value = context_dictionary[context_w]; + for (i=0 ; i> 1; + } + + + } + context_enlargeIn--; + if (context_enlargeIn == 0) { + context_enlargeIn = Math.pow(2, context_numBits); + context_numBits++; + } + // Add wc to the dictionary. + context_dictionary[context_wc] = context_dictSize++; + context_w = String(context_c); + } + } + + // Output the code for w. + if (context_w !== "") { + if (Object.prototype.hasOwnProperty.call(context_dictionaryToCreate,context_w)) { + if (context_w.charCodeAt(0)<256) { + for (i=0 ; i> 1; + } + } else { + value = 1; + for (i=0 ; i> 1; + } + } + context_enlargeIn--; + if (context_enlargeIn == 0) { + context_enlargeIn = Math.pow(2, context_numBits); + context_numBits++; + } + delete context_dictionaryToCreate[context_w]; + } else { + value = context_dictionary[context_w]; + for (i=0 ; i> 1; + } + + + } + context_enlargeIn--; + if (context_enlargeIn == 0) { + context_enlargeIn = Math.pow(2, context_numBits); + context_numBits++; + } + } + + // Mark the end of the stream + value = 2; + for (i=0 ; i> 1; + } + + // Flush the last char + while (true) { + context_data_val = (context_data_val << 1); + if (context_data_position == bitsPerChar-1) { + context_data.push(getCharFromInt(context_data_val)); + break; + } + else context_data_position++; + } + return context_data.join(''); + }, + + decompress: function (compressed) { + if (compressed == null) return ""; + if (compressed == "") return null; + return LZString._decompress(compressed.length, 32768, function(index) { return compressed.charCodeAt(index); }); + }, + + _decompress: function (length, resetValue, getNextValue) { + var dictionary = [], + next, + enlargeIn = 4, + dictSize = 4, + numBits = 3, + entry = "", + result = [], + i, + w, + bits, resb, maxpower, power, + c, + data = {val:getNextValue(0), position:resetValue, index:1}; + + for (i = 0; i < 3; i += 1) { + dictionary[i] = i; + } + + bits = 0; + maxpower = Math.pow(2,2); + power=1; + while (power!=maxpower) { + resb = data.val & data.position; + data.position >>= 1; + if (data.position == 0) { + data.position = resetValue; + data.val = getNextValue(data.index++); + } + bits |= (resb>0 ? 1 : 0) * power; + power <<= 1; + } + + switch (next = bits) { + case 0: + bits = 0; + maxpower = Math.pow(2,8); + power=1; + while (power!=maxpower) { + resb = data.val & data.position; + data.position >>= 1; + if (data.position == 0) { + data.position = resetValue; + data.val = getNextValue(data.index++); + } + bits |= (resb>0 ? 1 : 0) * power; + power <<= 1; + } + c = f(bits); + break; + case 1: + bits = 0; + maxpower = Math.pow(2,16); + power=1; + while (power!=maxpower) { + resb = data.val & data.position; + data.position >>= 1; + if (data.position == 0) { + data.position = resetValue; + data.val = getNextValue(data.index++); + } + bits |= (resb>0 ? 1 : 0) * power; + power <<= 1; + } + c = f(bits); + break; + case 2: + return ""; + } + dictionary[3] = c; + w = c; + result.push(c); + while (true) { + if (data.index > length) { + return ""; + } + + bits = 0; + maxpower = Math.pow(2,numBits); + power=1; + while (power!=maxpower) { + resb = data.val & data.position; + data.position >>= 1; + if (data.position == 0) { + data.position = resetValue; + data.val = getNextValue(data.index++); + } + bits |= (resb>0 ? 1 : 0) * power; + power <<= 1; + } + + switch (c = bits) { + case 0: + bits = 0; + maxpower = Math.pow(2,8); + power=1; + while (power!=maxpower) { + resb = data.val & data.position; + data.position >>= 1; + if (data.position == 0) { + data.position = resetValue; + data.val = getNextValue(data.index++); + } + bits |= (resb>0 ? 1 : 0) * power; + power <<= 1; + } + + dictionary[dictSize++] = f(bits); + c = dictSize-1; + enlargeIn--; + break; + case 1: + bits = 0; + maxpower = Math.pow(2,16); + power=1; + while (power!=maxpower) { + resb = data.val & data.position; + data.position >>= 1; + if (data.position == 0) { + data.position = resetValue; + data.val = getNextValue(data.index++); + } + bits |= (resb>0 ? 1 : 0) * power; + power <<= 1; + } + dictionary[dictSize++] = f(bits); + c = dictSize-1; + enlargeIn--; + break; + case 2: + return result.join(''); + } + + if (enlargeIn == 0) { + enlargeIn = Math.pow(2, numBits); + numBits++; + } + + if (dictionary[c]) { + entry = dictionary[c]; + } else { + if (c === dictSize) { + entry = w + w.charAt(0); + } else { + return null; + } + } + result.push(entry); + + // Add w+entry[0] to the dictionary. + dictionary[dictSize++] = w + entry.charAt(0); + enlargeIn--; + + w = entry; + + if (enlargeIn == 0) { + enlargeIn = Math.pow(2, numBits); + numBits++; + } + + } + } +}; + return LZString; +})(); + +if (typeof define === 'function' && define.amd) { + define(function () { return LZString; }); +} else if( typeof module !== 'undefined' && module != null ) { + module.exports = LZString +} diff --git a/js/misc-functions.js b/js/misc-functions.js index 77cddbb..64695f7 100644 --- a/js/misc-functions.js +++ b/js/misc-functions.js @@ -53,7 +53,7 @@ function localStorageObjectCache_STORE(name, unique_id, object) { if(localStorageIsEnabled()) { - if(object === false || object.length < 1) { + if(object === false || object === null || object.length < 1) { // false or an empty object means we remove this entry if(typeof localStorage[name + '-' + unique_id] != 'undefined' && localStorage[name + '-' + unique_id] !== null) { delete localStorage[name + '-' + unique_id]; @@ -63,7 +63,7 @@ function localStorageObjectCache_STORE(name, unique_id, object) { var dataToSave = {}; dataToSave.modified = Date.now(); - dataToSave.data = object; + dataToSave.cdata = LZString.compress(JSON.stringify(object)); try { localStorage.setItem(name + '-' + unique_id, JSON.stringify(dataToSave)); @@ -134,14 +134,25 @@ function localStorageObjectCache_GET(name, unique_id) { if(localStorageIsEnabled()) { if(typeof localStorage[name + '-' + unique_id] != 'undefined' && localStorage[name + '-' + unique_id] !== null) { - var parsedObject = JSON.parse(localStorage[name + '-' + unique_id]); + try { + var parsedObject = JSON.parse(localStorage[name + '-' + unique_id]); + } + catch(e) { + return false; + } if(typeof parsedObject.modified == 'undefined' || parsedObject.modified === null) { // invalid or old localstorage object found, check the whole localstorage! checkLocalStorage(); return false; } else { - return parsedObject.data; + try { + var decompressedAndParsed = JSON.parse(LZString.decompress(parsedObject.cdata)); + return decompressedAndParsed; + } + catch(e) { + return false; + } } } else { @@ -164,6 +175,7 @@ function checkLocalStorage() { var dateNow = Date.now() var corrected = 0; var deleted = 0; + var compressed = 0; $.each(localStorage, function(k,entry){ if(typeof entry == 'string') { @@ -198,7 +210,7 @@ function checkLocalStorage() { if(typeof entryParsed.modified == 'undefined' || entryParsed.modified === null) { var newEntry = {}; newEntry.modified = dateNow - corrected; // we want as unique dates as possible - newEntry.data = entryParsed; + newEntry.cdata = entryParsed; try { localStorage.setItem(k, JSON.stringify(newEntry)); } @@ -209,11 +221,28 @@ function checkLocalStorage() { }); } } + entryParsed = newEntry; corrected++; } + + // compress uncompressed data + if(typeof entryParsed.data != 'undefined') { + // but not if it's not containing any data (some bug may have saved an empty, false or null value) + if(entryParsed.data === false || entryParsed.data === null || entryParsed.data.length == 0) { + delete localStorage[k]; + deleted++; + return true; + } + var dataCompressed = LZString.compress(JSON.stringify(entryParsed.data)); + var newCompressedEntry = {}; + newCompressedEntry.modified = entryParsed.modified; + newCompressedEntry.cdata = dataCompressed; + localStorage.setItem(k, JSON.stringify(newCompressedEntry)); + compressed++; + } } }); - console.log(corrected + ' entries corrected, ' + deleted + ' entries deleted'); + console.log(corrected + ' entries corrected, ' + deleted + ' entries deleted, ' + compressed + ' entries compressed'); }