Skip to content

Instantly share code, notes, and snippets.

@kalley
Last active December 18, 2015 11:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kalley/5776979 to your computer and use it in GitHub Desktop.
Save kalley/5776979 to your computer and use it in GitHub Desktop.
plugin to facilitate using lz-string (http://pieroxy.net/blog/pages/lz-string/index.html) to compress values before adding them to localStorage using a webworker (technically, you could use anything you want, since the url for the worker gets passed in per instance). Also added "exists" method to check if a key exists in localStorage already. Ad…
// jquery.localCompress
// get, set return promises
(function($, localStorage) {
$.localCompress = function(workerURL) {
var initWorker = function(onmessage, type, value) {
var worker = new Worker(workerURL);
worker.onmessage = onmessage;
worker.postMessage({
type: type,
value: value
});
};
return {
get: function(key) {
var storageDfd = $.Deferred();
var item = localStorage.getItem(key);
if ( item ) {
initWorker(function(ev) {
storageDfd.resolve(JSON.parse(ev.data));
}, 'decompress', item);
} else {
storageDfd.reject(null);
}
return storageDfd;
},
set: function(key, val) {
var storageDfd = $.Deferred();
var self = this;
storageDfd.then(function(compressed) {
try {
localStorage.setItem(key, compressed);
} catch(err) {
$(window).trigger('storageError', [err]);
}
self.length = localStorage.length;
});
initWorker(function(ev) {
storageDfd.resolve(ev.data);
}, 'compress', JSON.stringify(val));
return storageDfd;
},
remove: function(key) {
var returnVal = localStorage.removeItem(key);
this.length = localStorage.length;
return returnVal;
},
clear: function() {
var returnVal = localStorage.clear();
this.length = localStorage.length;
return returnVal;
},
key: function(index) {
return localStorage.key(index);
},
exists: function(key) {
var exists = false;
for ( var i = 0; i < this.length; ++i ) {
if ( this.key(i) === key ) {
exists = true;
}
}
return exists;
},
length: localStorage.length
};
};
})(jQuery, localStorage);
// Copyright (c) 2013 Pieroxy <pieroxy@pieroxy.net>
// 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/
//
// See the rationale behind the optimizations here:
// http://pieroxy.net/blog/pages/lz-string/testing.html
//
// LZ-based compression algorithm, version 1.1.0
var LZString = {
compress: function (uncompressed) {
var i, value,
context_dictionary= {},
context_dictionaryToCreate= {},
context_c="",
context_wc="",
context_w="",
context_enlargeIn= 2, // Compensate for the first entry which should not count
context_dictSize= 3,
context_numBits= 2,
context_result= "",
context_data_string="",
context_data_val=0,
context_data_position=0,
ii;
for (ii = 0; ii < uncompressed.length; ii += 1) {
context_c = uncompressed.charAt(ii);
if (!context_dictionary.hasOwnProperty(context_c)) {
context_dictionary[context_c] = context_dictSize++;
context_dictionaryToCreate[context_c] = true;
}
context_wc = context_w + context_c;
if (context_dictionary.hasOwnProperty(context_wc)) {
context_w = context_wc;
} else {
if (context_dictionaryToCreate.hasOwnProperty(context_w)) {
if (context_w.charCodeAt(0)<256) {
for (i=0 ; i<context_numBits ; i++) {
context_data_val = (context_data_val << 1);
if (context_data_position == 15) {
context_data_position = 0;
context_data_string += String.fromCharCode(context_data_val);
context_data_val = 0;
} else {
context_data_position++;
}
}
value = context_w.charCodeAt(0);
for (i=0 ; i<8 ; i++) {
context_data_val = (context_data_val << 1) | (value&1);
if (context_data_position == 15) {
context_data_position = 0;
context_data_string += String.fromCharCode(context_data_val);
context_data_val = 0;
} else {
context_data_position++;
}
value = value >> 1;
}
} else {
value = 1;
for (i=0 ; i<context_numBits ; i++) {
context_data_val = (context_data_val << 1) | value;
if (context_data_position == 15) {
context_data_position = 0;
context_data_string += String.fromCharCode(context_data_val);
context_data_val = 0;
} else {
context_data_position++;
}
value = 0;
}
value = context_w.charCodeAt(0);
for (i=0 ; i<16 ; i++) {
context_data_val = (context_data_val << 1) | (value&1);
if (context_data_position == 15) {
context_data_position = 0;
context_data_string += String.fromCharCode(context_data_val);
context_data_val = 0;
} else {
context_data_position++;
}
value = value >> 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<context_numBits ; i++) {
context_data_val = (context_data_val << 1) | (value&1);
if (context_data_position == 15) {
context_data_position = 0;
context_data_string += String.fromCharCode(context_data_val);
context_data_val = 0;
} else {
context_data_position++;
}
value = value >> 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 (context_dictionaryToCreate.hasOwnProperty(context_w)) {
if (context_w.charCodeAt(0)<256) {
for (i=0 ; i<context_numBits ; i++) {
context_data_val = (context_data_val << 1);
if (context_data_position == 15) {
context_data_position = 0;
context_data_string += String.fromCharCode(context_data_val);
context_data_val = 0;
} else {
context_data_position++;
}
}
value = context_w.charCodeAt(0);
for (i=0 ; i<8 ; i++) {
context_data_val = (context_data_val << 1) | (value&1);
if (context_data_position == 15) {
context_data_position = 0;
context_data_string += String.fromCharCode(context_data_val);
context_data_val = 0;
} else {
context_data_position++;
}
value = value >> 1;
}
} else {
value = 1;
for (i=0 ; i<context_numBits ; i++) {
context_data_val = (context_data_val << 1) | value;
if (context_data_position == 15) {
context_data_position = 0;
context_data_string += String.fromCharCode(context_data_val);
context_data_val = 0;
} else {
context_data_position++;
}
value = 0;
}
value = context_w.charCodeAt(0);
for (i=0 ; i<16 ; i++) {
context_data_val = (context_data_val << 1) | (value&1);
if (context_data_position == 15) {
context_data_position = 0;
context_data_string += String.fromCharCode(context_data_val);
context_data_val = 0;
} else {
context_data_position++;
}
value = value >> 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<context_numBits ; i++) {
context_data_val = (context_data_val << 1) | (value&1);
if (context_data_position == 15) {
context_data_position = 0;
context_data_string += String.fromCharCode(context_data_val);
context_data_val = 0;
} else {
context_data_position++;
}
value = value >> 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<context_numBits ; i++) {
context_data_val = (context_data_val << 1) | (value&1);
if (context_data_position == 15) {
context_data_position = 0;
context_data_string += String.fromCharCode(context_data_val);
context_data_val = 0;
} else {
context_data_position++;
}
value = value >> 1;
}
// Flush the last char
while (true) {
context_data_val = (context_data_val << 1);
if (context_data_position == 15) {
context_data_string += String.fromCharCode(context_data_val);
break;
}
else context_data_position++;
}
return context_data_string;
},
readBits : function(numBits, data) {
var bits = 0, resb;
var maxpower = Math.pow(2,numBits);
var power=1;
while (power!=maxpower) {
resb = data.val & data.position;
data.position >>= 1;
if (data.position == 0) {
data.position = 32768;
data.val = data.string.charCodeAt(data.index++);
}
bits |= (resb>0 ? 1 : 0) * power;
power <<= 1;
}
return bits;
},
decompress: function (compressed) {
var dictionary = [],
next,
enlargeIn = 4,
dictSize = 4,
numBits = 3,
entry = "",
result = "",
i,
w,
bits, resb, maxpower, power,
c,
errorCount=0,
literal,
data = {string:compressed, val:compressed.charCodeAt(0), position:32768, 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 = 32768;
data.val = data.string.charCodeAt(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 = 32768;
data.val = data.string.charCodeAt(data.index++);
}
bits |= (resb>0 ? 1 : 0) * power;
power <<= 1;
}
c = String.fromCharCode(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 = 32768;
data.val = data.string.charCodeAt(data.index++);
}
bits |= (resb>0 ? 1 : 0) * power;
power <<= 1;
}
c = String.fromCharCode(bits);
break;
case 2:
return "";
}
dictionary[3] = c;
w = result = c;
while (true) {
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 = 32768;
data.val = data.string.charCodeAt(data.index++);
}
bits |= (resb>0 ? 1 : 0) * power;
power <<= 1;
}
switch (c = bits) {
case 0:
if (errorCount++ > 10000) return "Error";
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 = 32768;
data.val = data.string.charCodeAt(data.index++);
}
bits |= (resb>0 ? 1 : 0) * power;
power <<= 1;
}
dictionary[dictSize++] = String.fromCharCode(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 = 32768;
data.val = data.string.charCodeAt(data.index++);
}
bits |= (resb>0 ? 1 : 0) * power;
power <<= 1;
}
dictionary[dictSize++] = String.fromCharCode(bits);
c = dictSize-1;
enlargeIn--;
break;
case 2:
return result;
}
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 += 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 result;
}
};
// This is the worker. Above is lz-string.js
self.addEventListener('message', function(ev) {
var value = ev.data.value;
var type = ev.data.type;
value = LZString[type](value);
self.postMessage(value);
self.close();
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment