Rxjs observing object updates and changes - javascript

I am currently trying to observe any changes to a given object including all of it's elements.
The following code only fires when an object[x] is updates, but not if individually updating object[x]'s elements such as object[x][y]
<script>
var elem = document.getElementById("test1");
var log = function(x) {
elem.innerHTML += x + "<br/><br/><br/>";
};
var a = [{a:1,b:2},
{a:2,b:5}
];
var source = Rx.Observable
.ofObjectChanges(a)
.map(function(x) {
return JSON.stringify(x);
});
var subscription = source.subscribe(
function (x) {log(x);},
function (err) {log(err);},
function () {log('Completed');}
);
a[0] = a[1];
</script>
This code runs and fires correctly.
however. if I instead to this
a[0]['a'] = 3;
Then nothing happens.
EDIT
A better way to phrase this, how can I observe changes from an array of objects?

If you want only the nested object changes:
var source = rx.Observable.from(a).flatMap(function(item) {
return rx.Observable.ofObjectChanges(item);
});
If you also want changes like a[0] = a[1]:
var source = rx.Observable.merge(
rx.Observable.ofArrayChanges(a),
rx.Observable.from(a).flatMap(function(item) {
return rx.Observable.ofObjectChanges(item);
})
);
The flatMap or selectMany (they are the same function) will allow you to iterate over a value and execute a function that returns an Observable. The values from all these Observables are "flattened" onto a new stream that is returned.
http://reactivex.io/documentation/operators/flatmap.html

Perhaps something like this by merging two Observables (one for the array and the other observing the elements of the array):
var a = [
{a:1,b:2},
{a:2,b:5}
];
var source1 = Rx.Observable.ofArrayChanges(a).map(function(x) {
return JSON.stringify(x);
});
var source2 = Rx.Observable
.fromArray(a.map(function(o, i) { return [o, i]; }))
.flatMap(function(oi) {
return Rx.Observable.ofObjectChanges(oi[0])
.map(function(x) {
var y = {
type: x.type,
object: x.object,
name: x.name,
oldValue: x.oldValue,
arrayIndex: oi[1] // pass the index of the member that changed
};
return JSON.stringify(y);
});
})
source = source1.merge(source2)
var subscription = source.subscribe(
function (x) {log(x);},
function (err) {log(err);},
function () {log('Completed');}
);
a[0] = a[1]
a[1]['b'] = 7
Thanks to #electrichead here we're not using concatMap because the sources that we made by ofObjectChanges and ofArrayChanges never complete.

Here's a working example of Rx.Observable.ofNestedObjectChanges simple implementation, you can get the gist of it and implement you own.
http://jsbin.com/wekote/edit?js,console
Rx.Observable.ofNestedObjectChanges = function(obj) {
if (obj == null) { throw new TypeError('object must not be null or undefined.'); }
if (typeof Object.observe !== 'function' && typeof Object.unobserve !== 'function') { throw new TypeError('Object.observe is not supported on your platform') }
return new Rx.AnonymousObservable(function(observer) {
function observerFn(changes) {
for(var i = 0, len = changes.length; i < len; i++) {
observer.onNext(changes[i]);
}
}
Object.observe(obj, observerFn);
//Recursive observers hooks - same observerFn
traverseObjectTree(obj, observerFn);
function traverseObjectTree(element, observerFn){
for(var i=0;i<Object.keys(element).length;i++){
var myObj = element[Object.keys(element)[i]];
if(typeof myObj === "object"){
Object.observe(myObj, observerFn);
traverseObjectTree(myObj,observerFn);
}
}
}
return function () {
Object.unobserve(obj, observerFn);
};
});
};
//Test
var json = {
element : {
name : "Yocto",
job : {
title: "Designer"
}
},
element1: {
name : "Mokto"
}
};
setTimeout(function(){
json.element.job.title = "A Great Designer";
},3000);
var source = Rx.Observable.ofNestedObjectChanges(json);
var subscription = source.subscribe(
function (x) {
console.log(x);
},
function (err) {
console.log('Error: %s', err);
},
function () {
console.log('Completed');
});
json.element.name = "Candy Joe";

Related

arguments.length run the function if 3 arguments are passed, otherwise throw an error object

TasksI need to modify the displaySortedTaskList function so that it runs if there are 3 arguments passed, and throws an error object with a message if there aren't 3 arguments passed. My attempt:
"use strict";
var sortTaskList = function(tasks) {
var isArray = Array.isArray(tasks);
if (isArray) {
tasks.sort();
}
return isArray;
};
var displaySortedTaskList = function(tasks, div, handler) {
if(arguments.length = Function.length){
var html = "";
var isArray = sortTaskList(tasks);
if (isArray) {
//create and load html string from sorted array
for (var i in tasks) {
html = html.concat("<p>");
html = html.concat("<a href='#' id='", i, "'>Delete</a>");
html = html.concat(tasks[i]);
html = html.concat("</p>");
}
div.innerHTML = html;
// get links, loop and add onclick event handler
var links = div.getElementsByTagName("a");
for (var i = 0; i < links.length; i++) {
links[i].onclick = handler;
}
}
} else {document.getElementById("message").innerHTML = "The displaySortedTaskList function of the tasklist library requires three arguments"}
};
var deleteTask = function(tasks, i) {
var isArray = sortTaskList(tasks);
if (isArray) { tasks.splice(i, 1); }
};
var capitalizeTask = function(task) {
var first = task.substring(0,1);
return first.toUpperCase() + task.substring(1);
};
You might use rest parameters and check whether the length of the array is 3:
var displaySortedTaskList = function(...args) {
if (args.length !== 3) {
document.getElementById("message").textContent = "The displaySortedTaskList function of the tasklist library requires three arguments";
return;
// or `throw new Error('not enough args')` ?
}
const [tasks, div, handler] = args;
// rest of your code
(note that you should assign to .textContent when inserting text - .innerHTML is appropriate when inserting HTML markup, which is not the case here)
Live snippet:
var displaySortedTaskList = function(...args) {
if (args.length !== 3) {
return console.log('error');
}
console.log('rest of the code');
}
displaySortedTaskList('foo', 'bar');
displaySortedTaskList('foo', 'bar', 'baz');
displaySortedTaskList('foo', 'bar', 'baz', 'buzz');

Writing a function to set some but not necessarily all parameters in another function

I had a coding interview test that asked the following question which I was not able to fully solve. I'm wondering the best way to do this following my approach -- also sorry this is long.
You are given a function to read in like this (not necessarily 2 parameters):
function add(a, b) {
return a + b;
}
The objective is to create a function to initialize some of those variables and again call the function to perform the calculation like, function setParam(func, params). To use this you would do the following:
_add = setParam(add, {b:9})
_add(10) // should return 19
My solution was to parse the function to see how many parameters there are, then set them using the given parameters but since I barely know javascript I was never able to actually return a function with only some variables set and others still undefined.
(attempt at solution)
function setParam(func, params) {
// varray is an array of the the varriables from the function, func
// ie varray = [a,b] in this test
var varray = /function[^\(]*\(([^\)]*)\)/.exec(func.toString())[1].split(',');
//creates an array, paramset, that has the variables in func defined
//where possible
// ex paramset = [a,9] if only b was set
var paramsset = []
for (i = 0; i < varray.length; i++) {
if (typeof(params[varray[i]]) == "undefined"){
paramsset[i] = varray[i];
} else {
paramsset[i] = params[varray[i]];
}
}
//////
// need to modify existing function and return with added parameters
// where I'm stuck as this doesn't work.
newfunc = (function(){
var _func = func;
return function() {
return _func.apply(this, paramsset);
}
})();
newfunc()
}
I'm sure I'm not doing this the correct way, but any help would be appreciated.
I'm certainly not advocating to go towards that solution, but I still implemented something to follow your initial's API design for fun. The signatures weak map is necessary in order to preserve the initial function's signature so that we can call setParams again on partially applied functions.
var setParams = (function () {
var signatures = new WeakMap();
return function (fn, paramsToApply) {
var signature = signatureOf(fn), newFn;
validateParams(paramsToApply, signature.params);
newFn = function () {
var params = appliedParamsFrom(arguments, paramsToApply, signature.indexes);
return fn.apply(this, params);
};
signatures.set(newFn, signature);
return newFn;
};
function signatureOf(fn) {
return signatures.has(fn)?
signatures.get(fn) :
parseSignatureOf(fn);
}
function parseSignatureOf(fn) {
return String(fn)
.match(/function.*?\((.*?)\)/)[1]
.replace(/\s+/g, '')
.split(',')
.reduce(function (r, param, index) {
r.indexes[param] = index;
r.params.push(param);
return r;
}, { indexes: {}, params: [] });
}
function validateParams(paramsToApply, actualParams) {
Object.keys(paramsToApply).forEach(function (param) {
if (actualParams.indexOf(param) == -1) throw new Error("parameter '" + param + "' could not be found in the function's signature which is: 'function (" + actualParams + ")'");
});
}
function appliedParamsFrom(args, paramsToApply, paramsIndex) {
var appliedParams = [],
usedIndexes = [],
argsIndex = 0,
argsLen = args.length,
argSpotIndex = 0;
Object.keys(paramsToApply).forEach(function (param) {
var index = paramsIndex[param];
appliedParams[index] = paramsToApply[param];
usedIndexes.push(index);
});
while (argsIndex < argsLen) {
if (usedIndexes.indexOf(argSpotIndex) == -1) {
appliedParams[argSpotIndex] = args[argsIndex++];
}
++argSpotIndex;
}
return appliedParams;
}
})();
function add(a, b) { return a + b; }
var addTo9 = setParams(add, { b: 9 });
var add10To9 = setParams(addTo9, { a: 10 });
document.write(addTo9(10) + ', ' + add10To9());
Now, note that JavaScript comes with the Function.prototype.bind function which allows to perform in-order partial function application. The first parameter to bind has nothing to do with arguments, it's to bind the this value.
function add(a, b) { return a + b; }
var addTo9 = add.bind(null, 9);
document.write(addTo9(10));
And finally, an implementation with a placholder if you need one:
var partial = (function (undefined) {
var PLACEHOLDER = {};
function partial(fn, partialArgs) {
return function () {
return fn.apply(this, applyPartialArgs(arguments, partialArgs));
};
}
Object.defineProperty(partial, 'PLACEHOLDER', {
get: function () { return PLACEHOLDER; }
});
return partial;
function applyPartialArgs(args, partialArgs) {
var appliedArgs = partialArgs.map(function (arg) {
return arg === PLACEHOLDER? undefined : arg;
}),
partialArgsLen = partialArgs.length,
argsLen = args.length,
argsIndex = 0,
argSpotIndex = 0;
while (argsIndex < argsLen) {
if (
partialArgs[argSpotIndex] === PLACEHOLDER ||
argSpotIndex >= partialArgsLen
) {
appliedArgs[argSpotIndex] = args[argsIndex++];
}
++argSpotIndex;
}
return appliedArgs;
}
})();
function add(a, b, c, d) {
return a + b + c + d;
}
var _ = partial.PLACEHOLDER;
var addTo9 = partial(add, [_, 5, _, 4]);
document.write(addTo9(5, 5));
I'm guessing that they might have been testing for knowledge of partial application. (not currying)
Edit: Edited based upon your comments. This is Crockford's curry function straight from his book.
function add(a, b) {
return a + b;
}
if (!Function.prototype.partial) {
Function.prototype.partial = function() {
var slice = Array.prototype.slice,
args = new Array(arguments.length),
that = this;
for (var i = 0; i < args.length; i++) {
args[i] = arguments[i];
}
return function() {
return that.apply(null, args.concat(slice.apply(arguments)));
}
};
}
var example = add.partial(4);
console.log(example(10)); // output 14
console.log(example(20)); // output 24
var example = adder(4) assigns example to be function with a closure with a (in this case 4). When example is called like in the console.log, it will in effect be returning "the value of a when example was assigned, plus this new number."
Walkthrough of the partial() function:
Converts arguments to an array
returns a function gets passed the arguments given, which can be called later. It has a closure with the previously assigned arguments.

What is wrong with my observable pattern?

I'm testing the observable pattern in javascript. My callbacks in the array never seem to execute. What is wrong with my syntax?
<script type="text/javascript">
var Book = function (value) {
var onChanging = [];
this.name = function () {
for (var i = 0; i < onChanging.length; i++) {
onChanging[i]();
}
return value;
}
this.addTest = function (fn) {
onChanging.push(fn);
}
}
var b = new Book(13);
b.addTest(function () { console.log("executing"); return true; });
b.name = 15;
</script>
From your code above it looks like you need to call your function name instead of assigning a value something like:
var b = new Book(13);
b.addTest(function () { console.log("executing"); return true; });
b.name(); //<-- Before b.name = 15
Setting b.name = 15 doesn't execute the function, it just overwrites the value of b.name.
You could use getters and setters to react to a changing value. See John Resig's blog post or the MDN reference
I edited your code to use them:
var Book = function (value) {
this.onChanging = [];
this._name = "";
}
Book.prototype = {
addTest: function (fn) {
this.onChanging.push(fn);
},
get name() {
return this._name;
},
set name(val) {
for (var i = 0; i < this.onChanging.length; i++) {
this.onChanging[i](val);
}
this._name = val;
}
};
var b = new Book(13);
b.addTest(function (val) {
console.log("executing", val);
return true;
});
b.name = 15;
b.name = 17;
working demo.
You can also make a more generic solution that can work for all your properties without having to define the getters and setters, a lot of frameworks use this approach.
Book = function () {
this._events = [];
this._rawdata = {};
}
Book.prototype = {
bind: function (fn) {
this._events.push(fn);
},
// pass the property, and it returns its value, pass the value and it sets it!
attr: function (property, val) {
if (typeof val === "undefined") return this._rawdata[property];
this._rawdata[property] = val;
for (var i = 0; i < this._events.length; i++)
// we pass out the val and the property
this._events[i](val, property);
}
};
b = new Book();
b.bind(function (val) {
console.log("executing", val);
return true;
});
b.attr("name","The Hobbit");
b.attr("SKU" ,1700109393901);
console.log(b.attr("name")); // --> The Hobbit
http://jsfiddle.net/wv4ch6as/
Of course you would want to change the binder so that you can bind onto properties not one bind for all properties, but I think this gets the idea.

Create an object with modified versions of all methods in a source object [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 8 years ago.
I want to create an object that has modified versions of all of the methods in a source object, but I'm having trouble using for...in.
If this is my source object:
var raw = {};
raw.add = function(a,b){return a + b;}
raw.sub = function(a,b){return a - b;}
raw.neg = function(a){return -a;}
raw.sqrt = function(a){return Math.sqrt(a);}
It works if I recreate the list of properties in an array of strings:
var mod2 = Object.create(raw);
var proplist = ["add", "sub", "neg", "sqrt"];
proplist.forEach(function(prop){
mod2[prop] = function(){
var arglist = [].slice.apply(arguments);
var out = [];
if(arglist.length == 1){
[].concat(arglist[0]).forEach(function(d){ out.push(raw[prop](d)); });
}
else if(arglist.length == 2){
[].concat(arglist[0]).forEach(function(d1){
[].concat(arglist[1]).forEach(function(d2){
out.push(raw[prop](d1,d2));
})
});
}
return out;
}
});
But my attempt to use for..in doesn't work, all of the methods in the new object will do "sqrt":
var modified = Object.create(raw);
for(prop in raw){
modified[prop] = function(){
var arglist = [].slice.apply(arguments);
var out = [];
if(arglist.length == 1){
[].concat(arglist[0]).forEach(function(d){ out.push(raw[prop](d)); });
}
else if(arglist.length == 2){
[].concat(arglist[0]).forEach(function(d1){
[].concat(arglist[1]).forEach(function(d2){
out.push(raw[prop](d1,d2));
})
});
}
return out;
}
}
What is the best way to iterate through the methods automatically?
The issue with your second implementation is that you are using prop in your new method (which will be called sometime later), but the for loop that creates prop has already run to completion by the time that method is called sometime later so prop is not the right value any more (it will always be the last property). I fixed that in my implementation by capturing prop in an IIFE (immediately invoked function expression) so it would be frozen separately for each pass through the for loop. Your first implementation doesn't have that problem because you're using .forEach() on the array of properties which uses a callback function which captures the value of prop for you automatically into a closure.
So here's the result with these changes to your implementation:
Add an IIFE to freeze the value of prop for use in the new methods.
Add an extra check to make sure the methods we're copying are not inherited and are functions.
Initialized raw to a plain object as I don't see any reason to use Object.create() here.
The code:
var raw = {};
raw.add = function(a,b){return a + b;}
raw.sub = function(a,b){return a - b;}
raw.neg = function(a){return -a;}
raw.sqrt = function(a){return Math.sqrt(a);}
var modified = {};
for (prop in raw) {
if (raw.hasOwnProperty(prop) && typeof raw[prop] === "function") {
(function (prop) {
modified[prop] = function () {
var arglist = [].slice.apply(arguments);
var out = [];
if (arglist.length == 1) {
[].concat(arglist[0]).forEach(function (d) {
out.push(raw[prop](d));
});
} else if (arglist.length == 2) {
[].concat(arglist[0]).forEach(function (d1) {
[].concat(arglist[1]).forEach(function (d2) {
out.push(raw[prop](d1, d2));
})
});
}
return out;
}
})(prop);
}
}
Working demo: http://jsfiddle.net/jfriend00/5LcLh/
<script>
var raw = {};
raw.add = function () { console.log('add default method'); }
raw.sub = function () { console.log('sub default method'); }
raw.neg = function () { console.log('neg default method'); }
raw.sqrt = function () { console.log('sqrt default method'); }
console.log('*****************');
console.log('before modifying');
console.log('*****************');
raw.add();
raw.sub();
raw.neg();
raw.sqrt();
var proplist = ["add", "sub", "neg", "sqrt"];
console.log('*****************');
console.log('after modifying');
console.log('*****************');
console.log('');
var modified = Object.create(raw);
for (prop in proplist) {
if (prop == 0)
console.log('rewriting methods and calling methods inside loop................');
modified[proplist[prop]] = function () { console.log(proplist[prop] + ' method modified, ' + proplist.length + ' argument passed') }
modified[proplist[prop]]();
}
console.log('');
console.log('trying call methods after loop is done................');
modified.add();
modified.sub();
modified.neg();
modified.sqrt();
console.log('...it is becaouse "prop" variable in loop holding last count number ' + prop);
</script>
thanks to arnold.NET.JS's response clarifying the problem, I see that closure is one way to do it:
var raw = {};
raw.add = function(a,b){return a + b;}
raw.sub = function(a,b){return a - b;}
raw.neg = function(a){return -a;}
raw.sqrt = function(a){return Math.sqrt(a);}
var mod = Object.create(raw);
for(prop in raw){
mod[prop] = (function(){
var propname = prop;
function f(){
var arglist = [].slice.apply(arguments);
var out = [];
if(arglist.length == 1){
[].concat(arglist[0]).forEach(function(d){ out.push(raw[propname](d)); });
}
else if(arglist.length == 2){
[].concat(arglist[0]).forEach(function(d1){
[].concat(arglist[1]).forEach(function(d2){
out.push(raw[propname](d1,d2));
})
});
}
return out;
}
return f;
})();
}

LocalStorage and JSON.stringify JSON.parse

I have been working on a project that allows the user to submit memories about a place they have visited and tracks the location of when the memory was submitted. My only problem is trying to use localStorage with the app, I read about the JSON.stringify and JSON.parse, and don't understand how to use them in my code yet.
This is my form.js
It processes the form and grabs the text fields. It clears the form when the add button(on the display details page) or the enter details button is clicked. Finally it receives the information and sends out the message back to the window.
function processForm(){
var locate = document.myform.locate.value;
var details = document.myform.details.value;
var storeData = [];
localStorage.setItem("locate", JSON.stringify(locate));
localStorage.setItem("details", JSON.stringify(details));
alert("Saved: " + localStorage.getItem("locate") + ", and " + localStorage.getItem("details"));
var date = new Date,
day = date.getDate(),
month = date.getMonth() + 1,
year = date.getFullYear(),
hour = date.getHours(),
minute = date.getMinutes(),
ampm = hour > 12 ? "PM" : "AM";
hour = hour % 12;
hour = hour ? hour : 12; // zero = 12
minute = minute > 9 ? minute : "0" + minute;
hour = hour > 9 ? hour : "0" + hour;
date = month + "/" + day + "/" + year + " " + hour + ":" + minute + " " + ampm;
localStorage.setItem("date", JSON.stringify(date));
storeData.push(locate, details, date);
localStorage.setItem("storeData", JSON.stringify(storeData));
}
function clearForm(){
$('#myform').get(0).reset();
}
function retrieveFormInfo(){
var data = JSON.parse(localStorage.getItem("storeData"));
var locate = JSON.parse(localStorage.getItem("locate"));
$("#locate2").html("Place: " + locate);
var details = JSON.parse(localStorage.getItem("details"));
$("#details2").html("Description: " + details);
var date = JSON.parse(localStorage.getItem("date"));
$("#date").html(date);
}
But the major problem I am running into is I do know how to take that information in correctly using the JSON.stringify and JSON.parse and appending it to the window with html elements dynamically, Mainly like a list of memories.
Any help is appreciated!
localStorage stores key value pairs as strings only (you can use integer for keys but they get converted to string automatically).
Storage objects are simple key-value stores, similar to objects, but they stay intact through page loads. The keys and the values are always strings (note that, as with objects, integer keys will be automatically converted to strings) reference
let's say you have an array to be stored with each item being a json object.
You got 2 options:
Option 1:
stringify every item and store in locaStorage
var item = {input1: 'input1value', input2: 'input2value' };
localStorage.setItem( itemIndex, JSON.stringify(item) );
to retrive the items iterate over localStorage items and then convert the item to JSON object:
for(var i=0;i<localStorage.length; i++) {
var key = localStorage.key( i );
var item = JSON.parse( localStorage.getItem( key ) );
}
Option 2:
stringify the entire array and store in localStorage
localStorage.setItem( 'memoriesdata', JSON.stringify( arr ) );
to read the data read the item as string then convert to JSON object
var arr = JSON.parse( localStorage.getItem('memoriesdata') );
First get values of your input fields into a javascript object.
var myMemory = {};
myMemory.location = document.getElementById('location').value;
myMemory.description = document.getElementById('description').value;
Now save myMemory to localStorage,this can be done on a form submission or a button press. We can store as an array of memories and add item to it every time.
//if user already has memories in local, get that array and push into it.
//else create a blank array and add the memory.
memories = localStorage.getItem('memories') ?
JSON.parse(localStorage.getItem('memories')) :
[];
memories.push(myMemory);
localStorage.setItem('memories', JSON.stringify(memories));
I use this Storage implementation. It's inspired by many storage plugins out there... It handles any value serilizable by JSON.stringify function, and should work xbrowser (and in 'cookie-disabled' firefox):
//
// api:
//
// .clear() empties storage
// .each() loops storage (key, value) pairs
// .fetch() get a value by key
// .has() checks if there is a key set
// .ls() lists all keys
// .raw() string value actually stored
// .reload() reads in serialized data
// .rm() removes key(s)
// .set() setup value(s)
// .type() storage type used 'localStorage/globalStorage/userData'
// .valid() is storage engine setup correctly
//
;
((function(name, def, glob, doc) {
// add 'store' id to globals
this[name] = def(glob, doc);
}).call(
this, "store", function(glob, doc) {
// private (function) store version
var stclient;
var driver = {
// obj : storage_native{},
// type : storage_type
};
var engine = {
// read : (func),
// write : (func)
};
var _ = {
a: Array.prototype,
del: function(node) { // , ...fields
_.slc(arguments, 1).
forEach(function(field) {
delete this[field];
}, node);
return node;
},
each: function(array, callback, context) {
context ||
(context = array);
array.
some(function() {
return false === callback.apply(context, arguments);
});
return array;
},
hasown: Function.prototype.call.bind(Object.prototype.hasOwnProperty),
jsdc: JSON.parse, // decode
jsec: JSON.stringify, // encode
keys: Object.keys, // shimed .keys
ns: "storage5", // single property name to keep serialized storage data under
object: null, // parsed storage data
slc: Function.prototype.call.bind(Array.prototype.slice),
test: {
isemptyobj: function(node) {
for (var x in node)
return false;
return true;
},
isplainobj: function(node) {
return '[object Object]' == Object.prototype.toString.call(node);
},
},
testval: 'storage' + Math.random(), // test value for implementation check
rig: function(target, items) {
for (var field in items)
if (items.hasOwnProperty(field))
target[field] = items[field];
return target;
},
clone: function(node) {
return _.jsdc(_.jsec(node));
},
puts: function() {
engine.write(_.jsec(_.object));
},
};
stclient = function storage5() {
return arguments.length ?
storage5.set.apply(storage5, arguments) :
storage5.fetch();
};
// _init on load|ready
window.addEventListener('load', _init, false);
return _.rig(stclient, {
clear: function() {
return _.object = {}, _.puts(), this;
},
each: function(callback, context) {
context ||
(context = this.fetch());
_.each(this.ls(), function(field) {
return callback.call(context, field, this.fetch(field));
}, this);
return this;
},
fetch: function(key) {
return (arguments.length) ?
_.object[key] : _.clone(_.object);
},
has: function(name) {
return _.hasown(_.object, name);
},
ls: function() {
return _.keys(_.object);
},
raw: function() {
return engine.read();
},
reload: _load,
rm: function() {
_.del.apply(null, _.a.concat.apply([_.object], arguments));
return _.puts(), this;
},
set: function(input, value) {
var len = arguments.length;
var flag = 1;
if (len) {
if (_.test.isplainobj(input)) {
_.keys(input).
forEach(function(field) {
_.object[field] = input[field];
});
} else {
if (1 < len)
_.object[input] = value;
else
flag = 0;
}
flag && _.puts();
}
return this;
},
type: function() {
return driver.type || null;
},
valid: function() {
return !_.test.isemptyobj(driver);
},
});
function _init() {
var flag = 0;
var stnative;
if ("localStorage" in glob) {
try {
if ((stnative = glob["localStorage"])) {
// inits localStorage
_initlocst(stnative, driver, engine);
flag = 1;
}
} catch (e) {}
}
if (!flag) {
if ("globalStorage" in glob) {
try {
if ((stnative = glob["globalStorage"])) {
// inits globalStorage
_initglobst(stnative, driver, engine);
flag = 1;
}
} catch (e) {}
}
if (!flag) {
// inits userDataStorage
_initusrdatast(doc.createElement(_.ns), driver, engine);
}
}
// parse serialized storage data
_load();
}
function _initlocst(stnative, driver, engine) {
stnative[_.testval] = _.testval;
if (_.testval === stnative[_.testval]) {
try {
stnative.removeItem(_.testval);
} catch (e) {
try {
delete stnative[_.testval];
} catch (e) {}
}
driver.obj = stnative;
driver.type = "localStorage";
engine.read = function() {
return driver.obj[_.ns];
};
engine.write = function(stringvalue) {
driver.obj[_.ns] = stringvalue;
return stringvalue;
};
}
}
function _initglobst(stnative, driver, engine) {
var host = glob.location.hostname;
driver.obj = (/localhost/i).test(host) ?
stnative["localhost.localdomain"] : stnative[host];
driver.type = "globalStorage";
engine.read = function() {
return driver.obj[_.ns];
};
engine.write = function(stringvalue) {
driver.obj[_.ns] = stringvalue;
return stringvalue;
};
}
function _initusrdatast(node, driver, engine) {
try {
node.id = _.ns;
node.style.display = "none";
node.style.behavior = "url('#default#userData')";
doc.
getElementsByTagName("head")[0].
appendChild(node);
node.load(_.ns);
node.setAttribute(_.testval, _.testval);
node.save(_.ns);
if (_.testval === node.getAttribute(_.testval)) {
try {
node.removeAttribute(_.testval);
node.save(_.ns);
} catch (e) {}
driver.obj = node;
driver.type = "userData";
engine.read = function() {
return driver.obj.getAttribute(_.ns);
};
engine.write = function(stringvalue) {
driver.obj.setAttribute(_.ns, stringvalue);
driver.obj.save(_.ns);
return stringvalue;
};
}
} catch (e) {
doc.
getElementsByTagName("head")[0].
removeChild(node);
}
node = null;
}
function _load() {
try {
_.object = _.jsdc((engine.read() || engine.write("{}")));
} catch (e) {
_.object = {};
}
}
}, window, document));
//eof
Vanilla JS:
var printStorageBody = function () {
var body = document.querySelector("body");
var pre = document.createElement("pre");
body.innerHTML = "";
pre.innerText = JSON.stringify(localStorage, null, '\t');
body.appendChild(pre);
}
jQuery:
var printStorageBody = function () {
$("body").html("");
$("<pre>")
.text(JSON.stringify(localStorage, null, '\t'))
.appendTo("body");
}

Categories

Resources