DOM Caching Revisited

I recently found www.domcached.com. It was not exactly what I’ve looked for, so I changed some bits here, some bits there. This is the current version I’m currently working with. I consider adding real HTML5 database support. So what it’s for? DomCache is a trivial wrapper for the use of DOM Storage provided by modern browsers. It’s modelled after “memcached” and uses Prototype for JSON handling instead of working with Strings only.

DomCache = ( function() {
    var storage  = {};
    var backend  = false;
    var incrdecr = function( key, delta, namespace, initial, decr ) {
        var namespace = namespace || 'default';
        var delta     = delta     || 1;
        var initial   = initial   || 0;
        var time      = time      || false;

        if ( !key || ( ( typeof key != 'string' ) && ( typeof key != 'number' ) ) ) {
            throw new TypeError();
        }

        if ( ( typeof namespace != 'string' ) && ( typeof namespace != 'number' ) ) {
            throw new TypeError();
        }

        if ( typeof delta != 'number' ) {
            throw new TypeError();
        }

        if ( time && ( time < ( new Date() ).getTime() ) ) {
            time = ( new Date() ).getTime() + time;
        }

        if ( !storage[namespace] ) {
            storage[namespace] = {};
        }

        if ( !storage[namespace].hasOwnProperty( key ) || ( typeof storage[namespace][key] != 'number' ) ) {
            storage[namespace][key].value = initial;
            save();

            return initial;
        }

        if ( decr ) {
            storage[namespace][key].value -= delta;
        } else {
            storage[namespace][key].value += delta;
        }

        save();
        return value || true;
    };

    var save = function() {
        if ( backend ) {
            try {
                backend.dom_storage = Object.toJSON( storage );
                return true;
            } catch ( ex ) {
                if ( $( 'elm_domcached' ) ) {
                    try {
                        $( 'elm_domcached' ).setAttribute( 'domcached', backend.dom_storage );
                        $( 'elm_domcached' ).save( 'domcached' );

                        return true;
                    } catch (ex ) {
                    }
                }
            }
        }

        return false;
    };

    // initialization

    ( function() {
        if ( 'localStorage' in window ) {
            backend = window.localStorage;
        } else if ( 'globalStorage' in window ) {
            backend = window.globalStorage[document.domain];
        } else if ( 'addBehavior' in ( new Element( 'div' ) ) ) {
            $( document.getElementsByTagName( 'body' )[0] ).insert( '
' );
            $( 'elm_domcached' ).load( 'domcached' );

            try {
                var data = $( 'elm_domcached' ).getAttribute( 'domcached' );
            } catch ( ex ) {
                var data = "{}";
            }

            backend = {
                dom_storage: {}
            };

            if ( data && data.length ) {
                backend.dom_storage = data;
            }
        } else {
            return;
        }

        if ( ( 'dom_storage' in backend ) && backend.dom_storage ) {
            try {
                storage = String( backend.dom_storage ).evalJSON();
            } catch ( ex ) {
                backend.dom_storage = "{}";
            }
        } else {
            backend.dom_storage = "{}";
        }
    } )();

    // the public interface

    return {
        set: function( key, value, time, namespace ) {
            var namespace = namespace || 'default';
            var time      = time      || false;

            if ( !key || ( ( typeof key != 'string' ) && ( typeof key != 'number' ) ) ) {
                throw new TypeError();
            }

            if ( ( typeof namespace != 'string' ) && ( typeof namespace != 'number' ) ) {
                throw new TypeError();
            }

            if ( time && ( time < ( new Date() ).getTime() ) ) {
                time = ( new Date() ).getTime() + Math.ceil( time );
            }

            if ( !storage[namespace] ) {
                storage[namespace] = {};
            }

            storage[namespace][key] = {
                value: value,
                time:  time
            };

            save();
            return value || true;
        },

        get: function( key, namespace ) {
            var namespace = namespace || 'default';

            if ( ( typeof namespace != 'string' ) && ( typeof namespace != 'number' ) ) {
                throw new TypeError();
            }

            if ( storage[namespace] && storage[namespace][key] && ( !storage[namespace][key].time || ( storage[namespace][key].time < ( new Date() ).getTime() ) ) ) {
                return storage[namespace][key].value;
            }

            return null;
        },

        add: function( key, value, time, namespace ) {
            var namespace = namespace || 'default';
            var time      = time      || false;

            if ( !key || ( ( typeof key != 'string' ) && ( typeof key != 'number' ) ) ) {
                throw new TypeError();
            }

            if ( ( typeof namespace != 'string' ) && ( typeof namespace != 'number' ) ) {
                throw new TypeError();
            }

            if ( time && ( time < ( new Date() ).getTime() ) ) {
                time = ( new Date() ).getTime() + Math.ceil( time );
            }

            if ( !storage[namespace] ) {
                storage[namespace] = {};
            }

            storage[namespace][key] = {
                value: value,
                time:  time
            };

            save();
            return value || true;
        },

        replace: function( key, value, time, namespace ) {
            var namespace = namespace || 'default';
            var time      = time      || false;

            if ( !key || ( ( typeof key != 'string' ) && ( typeof key != 'number' ) ) ) {
                throw new TypeError();
            }

            if ( ( typeof namespace != 'string' ) && ( typeof namespace != 'number' ) ) {
                throw new TypeError();
            }

            if ( time && ( time < ( new Date() ).getTime() ) ) {
                time = ( new Date() ).getTime() + time;
            }

            if ( !storage[namespace] ) {
                storage[namespace] = {};
            }

            storage[namespace][key] = {
                value: value,
                time:  time
            };

            save();
            return value || true;
        },

        incr: function( key, delta, namespace, initial ) {
            return incrdecr( key, delta, namespace, initial );
        },

        decr: function( key, delta, namespace, initial ) {
            return incrdecr( key, delta, namespace, initial, true );
        },

        remove: function( key, namespace ) {
            var namespace = namespace || 'default';

            if ( ( typeof namespace != 'string' ) && ( typeof namespace != 'number' ) ) {
                throw new TypeError();
            }

            if ( storage[namespace] && storage[namespace][key] ) {
                delete storage[namespace][key];

                for ( var i in delete storage[namespace] ) {
                    if ( storage[namespace].hasOwnProperty( i ) ) {
                        return save();
                    }
                }

                delete storage[namespace];
                return save();
            }
        },

        removeAll: function() {
            storage = {};
            return save();
        }
    }
} )();

Here's a little example:


    

    

    
        
        

        
    


About this entry