Icontem

File: jquery.handleStorage.js

Recommend this page to a friend!
  Classes of Jason Gerfen  >  jQuery.handleStorage  >  jquery.handleStorage.js  >  Download  
File: jquery.handleStorage.js
Role: Class source
Content type: text/plain
Description: Class source
Class: jQuery.handleStorage
Save forms in cookies, local or session storage
Author: By
Last change: Tue Jan 29 10:51:36 MST 2013 - Improved key creation & strength for transparently encrypting form data
Mon Sep 3 19:33:37 MDT 2012 - Problem with key on encrypting form elements and array of radio or checkbox elements
Mon Sep 3 18:38:55 MDT 2012 - Working on resolving a bug within the form selector when multiple forms using the same storage key and adding support for radio and checkboxes
Date: 5 years ago
Size: 13,271 bytes
 

Contents

Class file image Download
/**
 *
 * jQuery plugin to impliment local storage with optional AES
 * encryption support via the Gibberish-AES libraries
 *
 * Fork me @ https://www.github.com/jas-/jQuery.handleStorage
 *
 * FEATURES:
 * - HTML5 localStorage support
 * - HTML5 sessionStorage support
 * - Cookie support
 * - AES encryption support
 *
 * REQUIREMENTS:
 * - jQuery libraries (required - http://www.jquery.com)
 * - jQuery cookie plugin (optional - http://plugins.jquery.com/files/jquery.cookie.js.txt)
 * - Gibberish-AES libraries (optional - https://github.com/mdp/gibberish-aes)
 *
 * OPTIONS:
 * - appID:    Unique identifier used as storage object key
 * - interval: Amount of time betwen auto-saves (default 5sec)
 * - storage:  HTML5 localStorage, sessionStorage and cookies supported
 * - aes:      Use AES encryption for local storage
 * - uuid:     Random RFC-4122 string used as AES password
 *
 * Author: Jason Gerfen
 * Email: jason.gerfen@gmail.com
 * Copyright: Jason Gerfen
 *
 * License: GPL
 *
 */

(function($){

 /**
  * @function jQuery.handleStorage
  * @abstract Plug-in used to assist in client storage items
  * @param method string Method to employ for form ID DOM object
  *                      init (transparent getting and setting of
  *                      form elements)
  * @param options object options object for specific operations
  *                       appID, storage, aes
  */

 $.fn.handleStorage = function(method){

  /**
   * @object defaults
   * @abstract Default set of options for plug-in
   */
  var defaults = {
   appID:       'jQuery.handleStorage',  // Application ID, used as index
   storage:     'localStorage',          // Storage type localStorage || sessionStorage || cookie (cookie storage requires jQuery cookie plugin)
   interval:    5000,                    // Amount of time between auto-saves (default is 5sec)
   aes:         false,                   // Use AES encryption? (true or false)
   uuid:        '',                      // Random RFC-4122 string
   form:        '',                      // Place holder for form ID
   data:        {},                      // Place holder for storage objects
   callback:    function(){},            // An on save callback
   preCallback: function(){},            // Process callback prior to save
   errCallback: function(){}             // Callback to execute on error
  };

  /**
   * @object methods
   * @abstract Plug-in methods
   */
  var methods = {

   /**
    * @method init
    * @abstract Default plug-in method. Provides transparent client storage
    *           of form data using HTML5 client storage or cookies with
    *           optional AES encryption support
    */
   init: function(o){
    var opts = $.extend({}, defaults, o);
    opts.form = $(this).attr('id');
    if (vO(opts)){
     opts.data[opts.appID] = (_e(opts)) ? _e(opts) : {};
     var orig = gStore(opts);
     if ((typeof orig==='object')&&(sChk(orig)>0)){
      sF(opts, orig);
     }
     ((opts.preCallback)&&($.isFunction(opts.preCallback))) ? opts.preCallback($(this)) : false;
     $('#'+opts.form).delegate('input, input:radio:selected, input:checkbox:checked, select, textarea', 'change keyup blur submit', function(){
      svF(opts);
     });
     setInterval(function(){svF(opts);}, opts.interval);
     return true;
    } else {
     return false;
    }
   }

  };

  /**
   * @function sChk
   * @abstract Performs a check on object sizes
   */
  var sChk = function(obj) {
   var n = 0;
   $.each(obj, function(k, v){
    if (obj.hasOwnProperty(k)) n++;
   });
   return n;
  }

  /**
   * @function _l
   * @abstract Performs check on current value of local storage objects
   *           Uses 5mb for HTML5 local/session storage & 4k for cookies
   */
  var _l = function(type) {
   var lim = /local|session/.test(type) ? 1024 * 1025 * 5 : 1024 * 4;
   if (lim - unescape(encodeURIComponent(JSON.stringify(type))).length <= 0) {
    console.log('Maximum quota has been met using '+type);
    return false;
   }
   return true;
  }

  /**
   * @function sI
   * @abstract Proxy function for setting data with specified client storage
   *           option
   */
  var sI = function(type, k, v) {
   var x = false;
   type = (vStore(type)) ? type : 'cookie';
   if (_l(type)) {
    switch(type) {
     case 'localStorage':
      x = sL(k, v);
      break;
     case 'sessionStorage':
      x = sS(k, v);
      break;
     case 'cookie':
      x = sC(k, v);
      break;
     default:
      x = sL(k, v);
      break;
    }
   }
   return x;
  }

  /**
   * @function gI
   * @abstract Proxy function for getting data with specified client storage
   *           option
   */
  var gI = function(type, k) {
   var x = false;
   type = (vStore(type)) ? type : 'cookie';
   switch(type) {
    case 'localStorage':
     x = gL(k);
     break;
    case 'sessionStorage':
     x = gS(k);
     break;
    case 'cookie':
     x = gC(k);
     break;
    default:
     x = gL(k);
     break;
   }
   return x;
  }

  /**
   * @function sL
   * @abstract Function used to set localStorage items
   */
  var sL = function(k, v) {
   return (localStorage.setItem(k, v)) ? false : true;
  }

  /**
   * @function sS
   * @abstract Function used to set sessionStorage items
   */
  var sS = function(k, v) {
   return (sessionStorage.setItem(k, v)) ? false : true;
  }

  /**
   * @function sC
   * @abstract Function used to set cookie items
   */
  var sC = function(k, v) {
   if (typeof $.cookie === 'function') {
    return ($.cookie(k, v, {expires: 7})) ? true : false;
   } else {
    return false;
   }
  }

  /**
   * @function gL
   * @abstract Function used to get localStorage items
   */
  var gL = function(k) {
   return (localStorage.getItem(k)) ? localStorage.getItem(k) : false;
  }

  /**
   * @function gS
   * @abstract Function used to get sessionStorage items
   */
  var gS = function(k) {
   return (sessionStorage.getItem(k)) ? sessionStorage.getItem(k) : false;
  }

  /**
   * @function sC
   * @abstract Function used to get cookie items
   */
  var gC = function(name) {
   if (typeof $.cookie === 'function') {
    return ($.cookie(name)) ? $.cookie(name) : false;
   } else {
    return false;
   }
  }

  /**
   * @function _e
   * @abstract Function used to return configured storage items
   *           as JSON object
   */
  var _e = function(o) {
   return (gI(o.storage, o.appID)) ? JSON.parse(gI(o.storage, o.appID)) : false;
  }

  /**
   * @function gStore
   * @abstract Function to compare and decrypt if necessary, existing
   *           storage items to configured form input elements
   */
  var gStore = function(o) {
   var ret={}, x;
   if (typeof o.data[o.appID][o.form]==='object'){
    $.each($('#'+o.form+' > :input'), function(k, v){
     if ((vStr(v.name)!==false)&&(vStr(o.data[o.appID][o.form][v.name])!==false)){
      if (typeof(o.data[o.appID][o.form][v.name])=='object') {
       var _x = []; var _i = 0;
       $.each(o.data[o.appID][o.form][v.name], function(a, b){
        _x[_i] = ((o.aes)&&(o.data[o.appID][o.form]['uuid'])&&(x!==false)) ? GibberishAES.dec(b, _s(uid(), __strIV(o.data[o.appID][o.form]['uuid']))) : b;
       });
       ret[v.name] = _x;
       __r(ret[v.name]);
      } else {
       ret[v.name] = ((o.aes)&&(o.data[o.appID][o.form]['uuid'])&&(x!==false)) ? GibberishAES.dec(o.data[o.appID][o.form][v.name], _s(uid(), __strIV(o.data[o.appID][o.form]['uuid']))) : o.data[o.appID][o.form][v.name];
      }
     }
    });
   }
   return ret;
  }

  /**
   * @function sF
   * @abstract Sets input values within configured form
   */
  var sF = function(o, arg){
   if (sChk(arg)>0){
    $.each(arg, function(a, b){
     if (($('#'+o.form+' > input[name='+a+']').attr('name')===a)||($('#'+o.form+' > select[name='+a+']').attr('name')===a)||($('#'+o.form+' > textarea[name='+a+']').attr('name')===a)&&(vStr(b)!==false)){
      if ((/checkbox|radio/.test($('#'+o.form+' > input[name='+a+']').attr('type')))&&($('#'+o.form+' > input[name='+a+']').attr('value')==b)) {
       $('#'+o.form+' > input[name='+a+']').attr('checked', true);
      } else {
       $('#'+o.form+' > input[name='+a+'], #'+o.form+' > select[name='+a+'], #'+o.form+' > textarea[name='+a+']').val(b);
      }
     }
    });
   }
  }

  /**
   * @function svF
   * @abstract Saves non-null form elements to configured client storage
   *           mechanism, encrypting if configured as a nested JSON object
   */
  var svF = function(o){
   var x={}; x[o.form]={};
   x[o.form]['uuid'] = ((o.aes)&&(!o.uuid)) ? hK(o) : o.uuid;
   $.each($('#'+o.form+' > :input'), function(k, v){
    if ((vStr(v.value)!==false)&&(vStr(v.name)!==false)){
     if (/checkbox|radio/.test(v.type)){
      x[o.form][v.name]=gG(o,v,v.type,x[o.form]['uuid']);
     } else {
      x[o.form][v.name] = ((o.aes)&&(x[o.form]['uuid'])) ? GibberishAES.enc(v.value, _s(uid(), __strIV(x[o.form]['uuid']))) : v.value;
     }
    }
   });
   o.data[o.appID] = (sChk(o.data[o.appID])>0) ?
    $.extend({}, o.data[o.appID], x) : x;
   if (sI(o.storage, o.appID, JSON.stringify(o.data[o.appID]))){
    ((o.callback)&&($.isFunction(o.callback))) ? o.callback.call($(this)) : false;
   } else {
    ((o.errCallback)&&($.isFunction(o.errCallback))) ? o.errCallback.call($(this)) : false;
   }
  }

  /**
   * @function gG
   * @abstract Return array of checked checkboxes or selected radio elements
   */
  var gG = function(o, obj, t, key){
   return $('#'+o.form+' > input:'+t+':checked').map(function(){
    return ((o.aes)&&(key)) ? GibberishAES.enc(this.value, _s(uid(), __strIV(key))) : this.value;
   }).get();
  }
  
  /**
   * @function vStr
   * @abstract Verifies string integrity
   */
  var vStr = function(string){
   if (string){
    return ((string===false)||(string.length===0)||(!string)||(string===null)||(string==='')||(typeof string==='undefined')) ? false : true;
   } else {
    return false;
   }
  }

  /**
   * @function vStore
   * @abstract Ensures configured storage mechanism is available for the
   *           browser engine
   */
  var vStore = function(type){
   try {
    return ((type in window)&&(window[type])) ? true : false;
   } catch (e) {
    console.log('The '+type+' storage mechanism does not exist, please upgrade your browser');
    return false;
   }
  }

  /**
   * @function vO
   * @abstract Tests configured o vs. available functionality
   */
  var vO = function(opts){
   var ret = true;
   if (opts.aes){
    if (!$.isFunction(GibberishAES.enc)){
     console.log('AES use specified but required libraries not available. Please include the Gibberish-AES libs...');
     ret = false;
    }
   }
   if (opts.storage==='cookie'){
    if (!$.isFunction($.cookie)){
     console.log('Cookie use specified but required libraries not available. Please include the jQuery cookie plugin...');
     ret = false;
    }
   }
   return ret;
  }

  /**
   * @function gUUID
   * @abstract Generates a valid RFC-4122 UUID
   */
  var gUUID = function(len){
   var chars = '0123456789abcdef'.split('');
   var uuid = [], rnd = Math.random, r;
   uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
   uuid[14] = '4';
   for (var i = 0; i < 36; i++){
    if (!uuid[i]){
     r = 0 | rnd()*16;
     uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r & 0xf];
    }
   }
   return (len!==null) ?
    uuid.join('').replace(/-/g, '').split('',len).join('') : uuid.join('');
  }

  /**
   * @function uid
   * @abstract Generate uid
   */
   var uid = function(){
		return window.navigator.appName+
				window.navigator.appCodeName+
				window.navigator.product+
				window.navigator.productSub+
				window.navigator.appVersion+
				window.navigator.buildID+
				window.navigator.userAgent+
				window.navigator.language+
				window.navigator.platform+
				window.navigator.oscpu;
	}

  /**
   * @function _s
   * @abstract Strengthen key
   */
  var _s = function(str, slt){
    var _h = []; _h[0] = GibberishAES.Hash.MD5(str);
    var _r = []; _r = _h[0]; var _d;
    for (i = 1; i < 3; i++){
        _h[i] = GibberishAES.Hash.MD5(_h[i - 1].concat(slt));
        _d = _r.concat(_h[i]);
    }
    return JSON.stringify(_d);
  }
  
  /**
   * @function hK
   * @abstract Performs key generation or retrieval
   */
  var hK = function(o) {
   if (o.aes){
    var x = (_e(o)) ? _e(o) : {};
    x[o.form] = (x[o.form]) ? x[o.form] : {};
    var y = (vStr(x[o.form]['uuid'])) ?
     x[o.form]['uuid'] : gUUID(null);
    return y;
   }
   return false;
  }

  /**
   * @function strIV
   * @abstract Generate IV from string
   */
  var __strIV = function(s){
   return (s) ?
    encodeURI(s.replace(/-/gi, '').substring(16,Math.ceil(16*s.length)%s.length)) :
    false;
  }

  /**
   * @function __r
   * @abstract Function used help debug objects recursively
   */
  var __r = function(obj){
   $.each(obj, function(x,y){
    if (typeof y==='object'){
     __r(y);
    } else {
     console.log(x+' => '+y);
    }
   });
  }

  /* robot, do something */
  if (methods[method]){
   return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
  } else if ((typeof method==='object')||(!method)){
   return methods.init.apply(this, arguments);
  } else {
   $.error('Method '+method+' does not exist on '+opts.name);
  }
 };
})(jQuery);