Prototype of "localstorage" in RequireJS - javascript

I currently have added some methods to "localStorage".
/**
*
* MOVED TO: https://github.com/iFind/html5MultidimensionalStorage
*
* This methods extends the default HTML5 Storage object and add support
* to set and get multidimensional data
*
* #example Storage.setObj('users.albums.sexPistols',"blah");
* #example Storage.setObj('users.albums.sexPistols',{ sid : "My Way", nancy : "Bitch" });
* #example Storage.setObj('users.albums.sexPistols.sid',"Other songs");
*
* #example Storage.getObj('users');
* #example Storage.getObj('users.albums');
* #example Storage.getObj('users.albums.sexPistols');
* #example Storage.getObj('users.albums.sexPistols.sid');
* #example Storage.getObj('users.albums.sexPistols.nancy');
*
* This is just a prototype and is not recommended to use at production apps
* USE AT YOUR OWN RISK
*
* #author Klederson Bueno <klederson#klederson.com>
* #author Gabor Zsoter <helo#zsitro.com>
*/
//Add Storage support for objects
Storage.prototype.__walker = function(path,o) {
//Validate if path is an object otherwise returns false
if(typeof path !== "object")
return undefined;
if(path.length === 0){
return o;
}
for(var i in path){
var prop = path[i];
//Check if path step exists
if(o.hasOwnProperty(prop)){
var val = o[prop];
if(typeof val == 'object'){
path.splice(0,1);
return this.__walker(path,val);
} else {
return val;
}
}
}
};
Storage.prototype.setObj = function(key, value) {
var key = encodeURIComponent(key);
var path = key.split('.');
//First level is always the localStorage key pair item
var _key = path[0];
var os = this.getItem(_key) !== null ? JSON.parse(this.getItem(_key)) : null; //general storage key pair element
path.splice(0,1);
if(os === null) {
os = {};
this.setItem(_key,JSON.stringify(os));
}
var innerWalker = function(path,o) {
//Validate if path is an object otherwise returns false
if(typeof path !== "object")
return undefined;
if(path.length == 1) {
o[path[0]] = value;
return o;
} else if(path.length === 0) {
os = value;
return os;
}
var val = null;
for(var i in path){
var prop = path[i];
//Check if path step exists
if(o.hasOwnProperty(prop)) {
val = o[prop];
if(typeof val == 'object'){
path.splice(0,1);
return innerWalker(path,val);
}
} else {
//create depth
o[prop] = {};
val = o[prop];
path.splice(0,1);
return innerWalker(path,val);
}
}
};
innerWalker(path,os);
this.setItem(_key,JSON.stringify(os));
};
Storage.prototype.getObj = function(key) {
var key = encodeURIComponent(key);
key = key.split('.');
//First level is always the localStorage key pair item
var _key = key[0];
var o = this.getItem(_key) ? JSON.parse(this.getItem(_key)) : null;
if(o === null)
return undefined;
key.splice(0,1);
return this.__walker(key,o);
};
In another class, I do:
define(['jquery', '_Errors'], function($, Errors) {
[...]
localStorage.getObj('blabla');
[...]
});
Before I was migrating to RequireJS i simply had all my functions / prototypes in a file called functions.js - is this still possible? Or do I need to specify all the functions I am going to use in every file?

You have two general choices to load your functions.js file:
Load the file that modifies Storage outside of RequireJS. This means putting it in its own script element. I would load it before RequireJS loads so that everything loaded by RequireJS benefits from changes made by this file.
Have RequireJS load functions.js. You'd need a configuration like:
paths: {
functions: "path/to/functions.js"
},
shim: {
functions: {
// This should be something that only your file creates.
// In some circumstances it is used by RequireJS to check whether something
// has loaded.
exports: 'Storage.prototype.getObj'
}
}
But then every module you have that needs to use the functions added by functions need to list it among its dependencies:
define(['jquery', '_Errors', 'functions'], function ($, Errors) {
[...]
localStorage.getObj('blabla');
[...]
});
Whenever I load things that make modifications start modifying things like Element, Node, or Storage, I prefer to use the first option above. I want all of this to be loaded first, so that everything that comes after sees the same environment. So it is all loaded outside RequireJS. If you find that performance becomes an issue because you are loading a lot of these small files, you could always customize your optimization step to concatenate these files and add them at the start of the bundle produced by r.js.

Related

Bigcommerce Stencil: Javascript Error "Object doesn't support property or method 'assign'" in Windows Safari and IE

Not sure what's happening but this error is preventing the page to display.
The error message is pointing to my bundle.js file (stencil bundle with webpack, babel, etc) and specifically to the Object.assign() method in the stencil-utils package. It's line 27 of #bigcommerce/stencil-utils/src/lib/request.js
Here's the section of code producing the error.
const defaultOptions = {
method: 'GET',
remote: false,
requestOptions: {
formData: null,
params: {},
config: {},
template: [],
},
};
const options = Object.assign({}, defaultOptions, opts);
const data = options.requestOptions.formData ? options.requestOptions.formData : options.requestOptions.params;
const headers = {
'stencil-config': options.requestOptions.config ? JSON.stringify(options.requestOptions.config) : '{}',
'stencil-options': '{}',
};
Any ideas of what might be causing this?
It happens you're using the Object.assign method, which isn't supported by all browsers. Both Internet Explorer and Safari (for Windows) aren't anymore being officially updated.
Anyway, there's a polyfill of Object.assign in this page. You can apply it in the top of your code.
This is my own polyfill, which does optionally avoid creating objects/arrays references (except for objects using any additional interface like Image, etc...).
typeof Object.assign !== "function" &&
(function() {
/**
* Return main instance of value.
* #param {...} value
* #returns
*/
function getMainInstance(value) {
// get instance in this format: [object Instance]
var ic = Object.prototype.toString.call(value);
// returns string between '[object ' and ']'
return ic.substring(ic.indexOf(" ") + 1, ic.lastIndexOf("]")).toLowerCase();
}
Object.assign = function(target) {
/* check if target isn't a object */
if (typeof target !== "object") target = {};
/* last target path */
var lastPath = target;
/* list containing target paths */
var locations = [];
/* consume specific array/object */
function consume(source) {
/* iterate each property to copy */
for (var i in source) {
var instance = getMainInstance(source[i]);
if (instance === "object" || instance === "array") {
lastPath =
lastPath[i] =
locations[locations.length] = (instance === "array" ? [] : {})
consume(source[i]);
} else {
lastPath[i] = source[i];
}
}
var len = -- locations.length;
lastPath = locations[--len] || target;
}
for (var i = 1, source; source = arguments[i]; i++) {
if (typeof source === "object") consume(source);
}
return target;
};
})();

Custom script in Meteor

I just started playing with Meteor and i was testing few things. Based on the Meteor Guide i was testing below code to understand the best way to write packages but i never been successful. What is wrong with the following code and what is good way (if not all but atleast few good ways) to write the packages for Meteor app which can be placed in lib folder.
/lib/exports.js
if (org === void 0){
var org = {}
}
if(bjse === void 0){
var bjse = {};
if(typeof exports != "undefined"){
bjse = exports;
}
bjse.api = {};
}
/lib/file1.js
// mypackage.js
bjse.api.Whirlygig = function (name) {
var self = this;
self.name = name; // name of the remote weasel
self.values = {}; // remote key name -> 0-indexed value
};
_.extend(Whirlygig.prototype, {
// Take a key/value pair from the remote Weasel and save it locally.
addValue: function (x) {
// Weasels use 1-indexed arrays. Subtract 1 to convert to 0-indexed.
self.values[x.key] = x.value - 1;
},
// Return a list of stored values in a format suitable for sending to
// a Weasel.
serialize: function () {
return _.map(self.values, function (v, k) {
var newVal = mungeValue(v, false /* foldValue */);
// Weasels use 1-indexed arrays. Add 1 to convert back to 1-indexed.
newVal = newVal + 1;
return {key: k, value: newVal};
});
}
});
/server/methods.js
Meteor.methods({
createConnections: function(){
....
var serializeObj = bjse.api.Whirlygig.serialize(..);
But i get Whirlygig not defined.
Update
I want to use bjse as namespace but it always appears as not defined in other files.
In exports you want:
if (typeof(org) === 'undefined'){
var org = {}
}
if(typeof(bjse) === 'undefined'){
var bjse = {};
if(typeof exports != "undefined"){
bjse = exports;
}
bjse.api = {};
}

what is the purpose of the function annotation method in the Facebook JavaScript SDK?

I'm starting using facebook javscript SDK, and I found a interesting thing when I read the source code.
The code example is as belowed:
/**
* Annotates a function with a meta object
*/
function annotate(fn, meta) {
meta.superClass = fn.__superConstructor__;
fn.__TCmeta = meta;
return fn;
}
// export to global
__w = annotate;
/**
* when using the annotate function
*/
function sprintf(format) {
// function body
}
__w(sprintf, {"signature":"function(string)"}); // <-- what is the purpose of doing this?
So, my question is what is that used for? What is the benefit doing this?
FYI, the whole source code is here, where you can see lots of annotate() is being used
http://connect.facebook.net/en_US/all/debug.js
It appears to be a home-grown strong typing setup:
/**
* A recursive descent analyzer which takes a value and a typehint, validating
* whether or not the value matches the typehint.
* The function will call it self as long as both the value and the typehint
* yields a nested component. This means that we will never recurse deeper
* than needed, and also that we automatically get support for
* > equals([], 'array<string>') // true
* > equals(['string'], 'array') // true
*/
function equals(value, node) {
var type = typeof value;
var subType;
var nextNode;
var nextValue;
//: Nullable types are delimited with a leading ?
//: ?string, ?boolean, etc.
var nullable = /^\?/.test(node);
if (nullable) {
node = node.substring(1);
}
//: snip ...
switch (type) {
// start by testing the most common types
case 'boolean':
case 'number':
case 'string':
case 'undefined':
break;
default:
//: snip ...
// let functions with signatures also match 'function'
type = value.__TCmeta && node !== 'function'
? value.__TCmeta.signature
: 'function';
} else if (type === 'object' || type === 'function') {
// HTMLObjectElements has a typeof function in FF
var constructor = value.constructor;
if (constructor && constructor.__TCmeta) {
// The value is a custom type
//: snip ...
while (constructor && constructor.__TCmeta) {
if (constructor.__TCmeta.type == node) {
type = node;
break;
}
constructor = constructor.__TCmeta.superClass;
}
//: snip ...
}
}
}
}
if (nullable && /undefined|null/.test(type)) {
return true;
}
if (type in typeInterfaces) {
var interfaces = typeInterfaces[type], i = interfaces.length;
while (i--) {
if (interfaces[i] === node) {
type = node;
break;
}
}
}
currentType.push(type);
return nextValue && nextNode
? node === type && equals(nextValue, nextNode)
: subType && nextNode
? node === type && subType === nextNode
: node === type;
}
/**
* Given a value and a typehint (can be a union type), this will return
* whether or not the passed in value matches the typehint.
*/
function matches(value, node) {
var nodes = node.split('|'), i = nodes.length;
while (i--) {
currentType = [];
if (equals(value, nodes[i])) {
return true;
}
}
return false;
}
The reason they use the annotate function is to allow type hinting for custom types and function signatures. Without annotate you could only do matches(someVar, "function"). With annotate you can do matches(someVar, "function(string, ?int)|function(string)") and only accept functions that take a string and a nullable integer or functions that only accept a string.

how to define global namespace in javascript

is there a way to define global namespace, so that i can call function from this namespace from all my page?
e.g
// in one file i define below code
DefineNameSpace("my.namespace.api", {
addObject: function(obj) {
// store obj into indexDB
},
readAllObject: function() {
// return array of object from indexdb
}
})
// so that in another javascript file i can do
my.namespace.api.addObject({name: "foo", desc: "bar"});
is there a way to implement "DefineNameSpace" method?
Thanks
one way to do it, which is very simple, is this:
my = {
namespace: {
api : {}
}
}
my.namespace.api.addObject = function (obj) { }
you're actually creating objects but in this way it will function as a namespace just as well :)
hm it's not the method you're implementing. But building a namespace with a method would require the function to be called before the script files are loaded where the namespace is used like that, otherwise those lines of code are called before the DefineNamespace method is called and you will run into parts of namespaces that are undefined at that point. With above solution that won't be the case, although it is not dynamic unfortunately.
building a namespace dynamically can be done in the following way:
// the root of the namespace would still be handy to have declared here
var my = {};
function defineNamespace(namespaceStr) {
var namespaceSegments = namespaceStr.split(".");
var namespaceSoFar = null;
// iterate through namespace parts
for (var i = 0; i < namespaceSegments.length; i++) {
var segment = namespaceSegments[i];
if (i == 0) {
// if namespace starts with my, use that
if (segment == "my") {
// set pointer to my
namespaceSoFar = my;
}
else {
// create new root namespace (not tested this, but think this should work)
var otherNamespace = eval(segment);
if (typeof otherNamespace == "undefined") {
eval(segment + " = {};");
}
// set pointer to created root namespace
namespaceSoFar = eval(segment);
}
}
else {
// further build the namespace
if (typeof namespaceSoFar[segment] == "undefined") {
namespaceSoFar[segment] = {};
}
// update the pointer (my -> my.namespace) for use in the next iteration
namespaceSoFar = namespaceSoFar[segment];
}
}
}

Ember model to json

I am looking for an efficient way to translate my Ember object to a json string, to use it in a websocket message below
/*
* Model
*/
App.node = Ember.Object.extend({
name: 'theName',
type: 'theType',
value: 'theValue',
})
The websocket method:
App.io.emit('node', {node: hash});
hash should be the json representation of the node. {name: thename, type: theType, ..}
There must be a fast onliner to do this.. I dont want to do it manualy since i have many attributes and they are likely to change..
As stated you can take inspiration from the ember-runtime/lib/core.js#inspect function to get the keys of an object, see http://jsfiddle.net/pangratz666/UUusD/
App.Jsonable = Ember.Mixin.create({
getJson: function() {
var v, ret = [];
for (var key in this) {
if (this.hasOwnProperty(key)) {
v = this[key];
if (v === 'toString') {
continue;
} // ignore useless items
if (Ember.typeOf(v) === 'function') {
continue;
}
ret.push(key);
}
}
return this.getProperties.apply(this, ret);
}
});
Note, since commit 1124005 - which is available in ember-latest.js and in the next release - you can pass the ret array directly to getProperties, so the return statement of the getJson function looks like this:
return this.getProperties(ret);
You can get a plain JS object (or hash) from an Ember.Object instance by calling getProperties() with a list of keys.
If you want it as a string, you can use JSON.stringify().
For example:
var obj = Ember.Object.create({firstName: 'Erik', lastName: 'Bryn', login: 'ebryn'}),
hash = obj.getProperties('firstName', 'lastName'), // => {firstName: 'Erik', lastName: 'Bryn'}
stringHash = JSON.stringify(hash); // => '{"firstName": "Erik", "lastName": "Bryn"}'
I have also been struggling with this. As Mirko says, if you pass the ember object to JSON.stringify you will get circular reference error. However if you store the object inside one property and use stringify on that object, it works, even nested subproperties.
var node = Ember.Object.create({
data: {
name: 'theName',
type: 'theType',
value: 'theValue'
}
});
console.log(JSON.stringify(node.get('data')));
However, this only works in Chrome, Safari and Firefox. In IE8 I get a stack overflow so this isn't a viable solution.
I have resorted to creating JSON schemas over my object models and written a recursive function to iterate over the objects using the properties in the schemas and then construct pure Javascript objects which I can then stringify and send to my server. I also use the schemas for validation so this solution works pretty well for me but if you have very large and dynamic data models this isn't possible. I'm also interested in simpler ways to accomplish this.
I modifed #pangratz solution slightly to make it handle nested hierarchies of Jsonables:
App.Jsonable = Ember.Mixin.create({
getJson: function() {
var v, json = {};
for (var key in this) {
if (this.hasOwnProperty(key)) {
v = this[key];
if (v === 'toString') {
continue;
}
if (Ember.typeOf(v) === 'function') {
continue;
}
if (App.Jsonable.detect(v))
v = v.getJson();
json[key] = v;
}
}
return json;
}
});
App.io.emit('node', {node: node.toJSON()});
Or if you have an ID property and want to include it:
App.io.emit('node', {node: node.toJSON({includeId: true})});
Will this work for you?
var json = JSON.stringify( Ember.getMeta( App.node, 'values') );
The false is optional, but would be more performant if you do not intend to modify any of the properties, which is the case according to your question. This works for me, but I am wary that Ember.meta is a private method and may work differently or not even be available in future releases. (Although, it isn't immediately clear to me if Ember.getMeta() is private). You can view it in its latest source form here:
https://github.com/emberjs/ember.js/blob/master/packages/ember-metal/lib/utils.js
The values property contains only 'normal' properties. You can collect any cached, computed properties from Ember.meta( App.node, false ).cached. So, provided you use jQuery with your build, you can easily merge these two objects like so:
$.extend( {}, Ember.getMeta(App.node, 'values'), Ember.getMeta(App.node, 'cache') );
Sadly, I haven't found a way to get sub-structures like array properties in this manner.
I've written an extensive article on how you can convert ember models into native objects or JSON which may help you or others :)
http://pixelchild.com.au/post/44614363941/how-to-convert-ember-objects-to-json
http://byronsalau.com/blog/convert-ember-objects-to-json/
I modified #Kevin-pauli solution to make it works with arrays as well:
App.Jsonable = Ember.Mixin.create({
getJson: function() {
var v, json = {}, inspectArray = function (aSome) {
if (Ember.typeof(aSome) === 'array') {
return aSome.map(inspectArray);
}
if (Jsonable.detect(aSome)) {
return aSome.getJson();
}
return aSome;
};
for (var key in this) {
if (this.hasOwnProperty(key)) {
v = this[key];
if (v === 'toString') {
continue;
}
if (Ember.typeOf(v) === 'function') {
continue;
}
if (Ember.typeOf(v) === 'array') {
v = v.map(inspectArray);
}
if (App.Jsonable.detect(v))
v = v.getJson();
json[key] = v;
}
}
return json;
}
});
I also made some further modification to get the best of both worlds. With the following version I check if the Jsonable object has a specific property that informs me on which of its properties should be serialized:
App.Jsonable = Ember.Mixin.create({
getJson: function() {
var v, json = {}, base, inspectArray = function (aSome) {
if (Ember.typeof(aSome) === 'array') {
return aSome.map(inspectArray);
}
if (Jsonable.detect(aSome)) {
return aSome.getJson();
}
return aSome;
};
if (!Ember.isNone(this.get('jsonProperties'))) {
// the object has a selective list of properties to inspect
base = this.getProperties(this.get('jsonProperties'));
} else {
// no list given: let's use all the properties
base = this;
}
for (var key in base) {
if (base.hasOwnProperty(key)) {
v = base[key];
if (v === 'toString') {
continue;
}
if (Ember.typeOf(v) === 'function') {
continue;
}
if (Ember.typeOf(v) === 'array') {
v = v.map(inspectArray);
}
if (App.Jsonable.detect(v))
v = v.getJson();
json[key] = v;
}
}
return json;
}
});
I am using this little tweak and I am happy with it. I hope it'll help others as well!
Thanks to #pangratz and #Kevin-Pauli for their solution!
Here I take #leo, #pangratz and #kevin-pauli solution a little step further. Now it iterates not only with arrays but also through has many relationships, it doesn't check if a value has the type Array but it calls the isArray function defined in Ember's API.
Coffeescript
App.Jsonable = Em.Mixin.create
getJson: ->
jsonValue = (attr) ->
return attr.map(jsonValue) if Em.isArray(attr)
return attr.getJson() if App.Jsonable.detect(attr)
attr
base =
if Em.isNone(#get('jsonProperties'))
# no list given: let's use all the properties
this
else
# the object has a selective list of properties to inspect
#getProperties(#get('jsonProperties'))
hash = {}
for own key, value of base
continue if value is 'toString' or Em.typeOf(value) is 'function'
json[key] = jsonValue(value)
json
Javascript
var hasProp = {}.hasOwnProperty;
App.Jsonable = Em.Mixin.create({
getJson: function() {
var base, hash, hashValue, key, value;
jsonValue = function(attr) {
if (Em.isArray(attr)) {
return attr.map(jsonValue);
}
if (App.Jsonable.detect(attr)) {
return attr.getJson();
}
return attr;
};
base = Em.isNone(this.get('jsonProperties')) ? this : this.getProperties(this.get('jsonProperties'));
json = {};
for (key in base) {
if (!hasProp.call(base, key)) continue;
value = base[key];
if (value === 'toString' || Em.typeOf(value) === 'function') {
continue;
}
json[key] = jsonValue(value);
}
return json;
}
});
Ember Data Model's object counts with a toJSON method which optionally receives an plain object with includeId property used to convert an Ember Data Model into a JSON with the properties of the model.
https://api.emberjs.com/ember-data/2.10/classes/DS.Model/methods/toJSON?anchor=toJSON
You can use it as follows:
const objects = models.map((model) => model.toJSON({ includeId: true }));
Hope it helps. Enjoy!
I have:
fixed and simplified code
added circular reference prevention
added use of get of value
removed all of the default properties of an empty component
//Modified by Shimon Doodkin
//Based on answers of: #leo, #pangratz, #kevin-pauli, #Klaus
//http://stackoverflow.com/questions/8669340
App.Jsonable = Em.Mixin.create({
getJson : function (keysToSkip, visited) {
//getJson() called with no arguments,
// they are to pass on values during recursion.
if (!keysToSkip)
keysToSkip = Object.keys(Ember.Component.create());
if (!visited)
visited = [];
visited.push(this);
var getIsFunction;
var jsonValue = function (attr, key, obj) {
if (Em.isArray(attr))
return attr.map(jsonValue);
if (App.Jsonable.detect(attr))
return attr.getJson(keysToSkip, visited);
return getIsFunction?obj.get(key):attr;
};
var base;
if (!Em.isNone(this.get('jsonProperties')))
base = this.getProperties(this.get('jsonProperties'));
else
base = this;
getIsFunction=Em.typeOf(base.get) === 'function';
var json = {};
var hasProp = Object.prototype.hasOwnProperty;
for (var key in base) {
if (!hasProp.call(base, key) || keysToSkip.indexOf(key) != -1)
continue;
var value = base[key];
// there are usual circular references
// on keys: ownerView, controller, context === base
if ( value === base ||
value === 'toString' ||
Em.typeOf(value) === 'function')
continue;
// optional, works also without this,
// the rule above if value === base covers the usual case
if (visited.indexOf(value) != -1)
continue;
json[key] = jsonValue(value, key, base);
}
visited.pop();
return json;
}
});
/*
example:
DeliveryInfoInput = Ember.Object.extend(App.Jsonable,{
jsonProperties: ["title","value","name"], //Optionally specify properties for json
title:"",
value:"",
input:false,
textarea:false,
size:22,
rows:"",
name:"",
hint:""
})
*/
Ember.js appears to have a JSON library available. I hopped into a console (Firebug) on one the Todos example and the following worked for me:
hash = { test:4 }
JSON.stringify(hash)
So you should be able to just change your line to
App.io.emit('node', { node:JSON.stringify(hash) })

Categories