Comparing version 0.4.0 to 0.4.1
56
data.js
@@ -23,3 +23,3 @@ // (c) 2011 Michael Aufreiter | ||
// Current version of the library. Keep in sync with `package.json`. | ||
Data.VERSION = '0.4.0'; | ||
Data.VERSION = '0.4.1'; | ||
@@ -1029,3 +1029,3 @@ // Require Underscore, if we're on the server, and it's not already present. | ||
}); | ||
if (this._dirty) this.g.trigger('dirty'); | ||
if (this._dirty) this.g.trigger('dirty', this); | ||
}, | ||
@@ -1127,3 +1127,4 @@ | ||
that._dirty = true; | ||
that.g.trigger('dirty'); | ||
that.g.trigger('dirty', that); | ||
that.g.snapshot(); | ||
}); | ||
@@ -1169,3 +1170,3 @@ } else { | ||
Data.Graph = _.inherits(Data.Node, { | ||
constructor: function(g, dirty) { | ||
constructor: function(g, options) { | ||
var that = this; | ||
@@ -1177,3 +1178,8 @@ Data.Node.call(this); | ||
if (!g) return; | ||
this.merge(g, dirty); | ||
this.merge(g, options.dirty); | ||
if (options.persistent) { | ||
this.persistent = options.persistent; | ||
this.restore(); // Restore data | ||
} | ||
}, | ||
@@ -1236,3 +1242,3 @@ | ||
that.set('nodes', key, new Data.Type(that, key, node)); | ||
that.get(key)._dirty = dirty; | ||
that.get(key)._dirty = node._dirty ? node._dirty : dirty; | ||
} | ||
@@ -1263,3 +1269,4 @@ return true; | ||
}); | ||
that.get(key)._dirty = dirty; | ||
that.get(key)._dirty = node._dirty ? node._dirty : dirty; | ||
if (!node._id) node._id = key; | ||
@@ -1276,2 +1283,5 @@ return true; | ||
}); | ||
// Create a new snapshot | ||
this.snapshot(); | ||
return this; | ||
@@ -1295,2 +1305,3 @@ }, | ||
this.set('nodes', node._id, res); | ||
this.snapshot(); | ||
return res; | ||
@@ -1322,3 +1333,4 @@ } else { // Delegate to Data.Node#set | ||
}); | ||
this.trigger('dirty'); | ||
this.trigger('dirty', node); | ||
this.snapshot(); | ||
}, | ||
@@ -1333,2 +1345,14 @@ | ||
// Memoize a snapshot of the current graph | ||
snapshot: function() { | ||
if (!this.persistent) return; | ||
localStorage.setItem("graph", JSON.stringify(this.toJSON(true))); | ||
}, | ||
// Restore latest snapshot from localStorage | ||
restore: function() { | ||
var snapshot = JSON.parse(localStorage.getItem("graph")); | ||
if (snapshot) this.merge(snapshot); | ||
}, | ||
// Fetches a new subgraph from the adapter and either merges the new nodes | ||
@@ -1372,5 +1396,4 @@ // into the current set of nodes | ||
if (err) return callback(err); | ||
that.merge(g, false); | ||
that.merge(g, false); | ||
// Check for rejectedNodes / conflictedNodes | ||
@@ -1386,2 +1409,5 @@ validNodes.each(function(n, key) { | ||
// Update localStorage | ||
if (this.persistent) that.snapshot(); | ||
if (that.invalidNodes().length > 0) that.trigger('invalid'); | ||
@@ -1449,3 +1475,3 @@ if (that.conflictedNodes().length > 0) that.trigger('conflicted'); | ||
// Serializes the graph to the JSON-based exchange format | ||
toJSON: function() { | ||
toJSON: function(extended) { | ||
var result = {}; | ||
@@ -1458,2 +1484,8 @@ | ||
result[key] = obj.toJSON(); | ||
if (extended) { | ||
// include special properties | ||
if (obj._dirty) result[key]._dirty = true; | ||
if (obj._conflicted) result[key]._conclicted = true; | ||
if (obj._rejected) result[key].rejected = true; | ||
} | ||
} | ||
@@ -1468,3 +1500,3 @@ }); | ||
// Data.Collection | ||
@@ -1471,0 +1503,0 @@ // -------------- |
@@ -1,2 +0,2 @@ | ||
(function(){var e;e=typeof exports!=="undefined"?exports:this.Data={};e.VERSION="0.4.0";var g=this._;if(!g&&typeof require!=="undefined")g=require("underscore");e.VALUE_TYPES=["string","object","number","boolean","date"];e.isValueType=function(a){return g.include(e.VALUE_TYPES,a)};e.matches=function(a,b){b=g.isArray(b)?b:[b];var c=false;g.each(b,function(d){if(!c){var f=false;g.each(d,function(h,i){if(!f){var k,l=i.match(/^([a-z_]{1,30})(=|==|!=|>|>=|<|<=|\|=|&=)?$/),j=l[1];l=l[2]||(j=="type"||g.isArray(h)? | ||
(function(){var e;e=typeof exports!=="undefined"?exports:this.Data={};e.VERSION="0.4.1";var g=this._;if(!g&&typeof require!=="undefined")g=require("underscore");e.VALUE_TYPES=["string","object","number","boolean","date"];e.isValueType=function(a){return g.include(e.VALUE_TYPES,a)};e.matches=function(a,b){b=g.isArray(b)?b:[b];var c=false;g.each(b,function(d){if(!c){var f=false;g.each(d,function(h,i){if(!f){var k,l=i.match(/^([a-z_]{1,30})(=|==|!=|>|>=|<|<=|\|=|&=)?$/),j=l[1];l=l[2]||(j=="type"||g.isArray(h)? | ||
"|=":"=");if(l==="|="){l=g.isArray(h)?h:[h];var m=g.isArray(a[j])?a[j]:[a[j]];k=false;g.each(l,function(o){if(g.include(m,o))k=true})}else if(l==="&="){l=g.isArray(h)?h:[h];m=g.isArray(a[j])?a[j]:[a[j]];k=g.intersect(m,l).length===l.length}else switch(l){case "!=":k=!g.isEqual(a[j],h);break;case ">":k=a[j]>h;break;case ">=":k=a[j]>=h;break;case "<":k=a[j]<h;break;case "<=":k=a[j]<=h;break;default:k=g.isEqual(a[j],h);break}if(!k)return f=true}});if(!f)return c=true}});return c};e.uuid=function(a){for(var b= | ||
@@ -22,15 +22,16 @@ "0123456789abcdefghijklmnopqrstuvwxyz".split(""),c=[],d=0;d<32;d++)c[d]=b[0|Math.random()*16];return(a?a:"")+c.join("")};g.Events={bind:function(a,b){this._callbacks||(this._callbacks={});(this._callbacks[a]||(this._callbacks[a]=[])).push(b);return this},unbind:function(a,b){var c;if(a){if(c=this._callbacks)if(b){c=c[a];if(!c)return this;for(var d=0,f=c.length;d<f;d++)if(b===c[d]){c.splice(d,1);break}}else c[a]=[]}else this._callbacks={};return this},trigger:function(a){var b,c,d,f;if(!(c=this._callbacks))return this; | ||
properties:function(){var a=new e.Hash;this._types.each(function(b){b.all("properties").each(function(c){a.set(c.key,c)})});return a},build:function(){var a=this,b=g.isArray(this.data.type)?this.data.type:[this.data.type];if(!this.data)throw Error("Object has no data, and cannot be built");this._rev=this.data._rev;this._conflicted=this.data._conflicted;this._deleted=this.data._deleted;this.type=this.g.get("nodes",g.last(b));g.each(b,function(c){a._types.set(c,a.g.get("nodes",c));a._types.get(c).all("properties").each(function(d, | ||
f){function h(i){i=g.isArray(i)?i:[i];a.replace(d.key,d.registerValues(i,a))}if(a.data[f]!==undefined)h(a.data[f]);else d["default"]&&h(d["default"])})});this._dirty&&this.g.trigger("dirty")},validate:function(){if(this.type.key==="/type/type")return true;var a=this;this.errors=[];this.properties().each(function(b,c){if(a.get(c)===undefined||a.get(c)===null||a.get(c)==="")b.required&&a.errors.push({property:c,message:'Property "'+b.name+'" is required'});else{var d=b.expectedTypes,f=function(h,i){if(g.include(i, | ||
typeof h))return true;if(!h.data)return true;if(h instanceof e.Object&&g.intersect(i,h.types().keys()).length>0)return true;if(typeof h==="object"&&g.include(i,h.constructor.name.toLowerCase()))return true;return false};b.unique&&!f(a.get(c),d)&&a.errors.push({property:c,message:'Invalid type for property "'+b.name+'"'});!b.unique&&!g.all(a.get(c).values(),function(h){return f(h,d)})&&a.errors.push({property:c,message:'Invalid value type for property "'+b.name+'"'})}if(b.validator)RegExp(b.validator).test(a.get(c))|| | ||
f){function h(i){i=g.isArray(i)?i:[i];a.replace(d.key,d.registerValues(i,a))}if(a.data[f]!==undefined)h(a.data[f]);else d["default"]&&h(d["default"])})});this._dirty&&this.g.trigger("dirty",this)},validate:function(){if(this.type.key==="/type/type")return true;var a=this;this.errors=[];this.properties().each(function(b,c){if(a.get(c)===undefined||a.get(c)===null||a.get(c)==="")b.required&&a.errors.push({property:c,message:'Property "'+b.name+'" is required'});else{var d=b.expectedTypes,f=function(h, | ||
i){if(g.include(i,typeof h))return true;if(!h.data)return true;if(h instanceof e.Object&&g.intersect(i,h.types().keys()).length>0)return true;if(typeof h==="object"&&g.include(i,h.constructor.name.toLowerCase()))return true;return false};b.unique&&!f(a.get(c),d)&&a.errors.push({property:c,message:'Invalid type for property "'+b.name+'"'});!b.unique&&!g.all(a.get(c).values(),function(h){return f(h,d)})&&a.errors.push({property:c,message:'Invalid value type for property "'+b.name+'"'})}if(b.validator)RegExp(b.validator).test(a.get(c))|| | ||
a.errors.push({property:c,message:'Invalid value for property "'+b.name+'"'})});return this.errors.length===0},get:function(a,b){if(!this.data)return null;var c=this.properties().get(a);if(!c)return null;return arguments.length===1?c.isObjectType()?c.unique?this.first(a):this.all(a):c.unique?this.value(a):this.values(a):e.Node.prototype.get.call(this,a,b)},set:function(a){var b=this;if(arguments.length===1)g.each(a,function(c,d){var f=b.properties().get(d);if(f){b.replace(f.key,f.registerValues(g.isArray(c)? | ||
c:[c],b));b._dirty=true;b.g.trigger("dirty")}});else return e.Node.prototype.set.call(this,arguments[0],arguments[1],arguments[2])},toJSON:function(){var a=this;result={};g.each(this._properties,function(b,c){var d=a.properties().get(c);result[c]=d.isObjectType()?d.unique?a.all(c).keys()[0]:a.all(c).keys():d.unique?a.value(c):a.values(c).values()});result.type=this.types().keys();result._id=this._id;if(this._rev!==undefined)result._rev=this._rev;if(this._deleted)result._deleted=this._deleted;return result}}); | ||
g.extend(e.Object.prototype,g.Events);e.Graph=g.inherits(e.Node,{constructor:function(a,b){e.Node.call(this);this.watchers={};this.replace("nodes",new e.Hash);a&&this.merge(a,b)},connect:function(a,b){if(typeof exports!=="undefined")this.adapter=new (require(__dirname+"/adapters/"+a+"_adapter"))(this,b);else{if(!e.Adapters[a])throw Error('Adapter "'+a+'" not found');this.adapter=new e.Adapters[a](this,b)}return this},connected:function(a){if(this.adapter.realtime)this.connectedCallback=a;else a()}, | ||
serve:function(a){require(__dirname+"/server").initialize(a,this)},watch:function(a,b,c){this.watchers[a]=c;this.adapter.watch(a,b,function(){})},unwatch:function(a){delete this.watchers[a];this.adapter.unwatch(a,function(){})},empty:function(){var a=this;g.each(this.objects().keys(),function(b){a.del(b);a.all("nodes").del(b)})},merge:function(a,b){var c=this;g.select(a,function(f,h){if(f.type==="/type/type"||f.type==="type"){if(!c.get("nodes",h)){c.set("nodes",h,new e.Type(c,h,f));c.get(h)._dirty= | ||
b}return true}return false});var d=g.select(a,function(f,h){if(f.type!=="/type/type"&&f.type!=="type"){var i=c.get("nodes",h),k=g.isArray(f.type)?f.type:[f.type];if(i)i.data=f;else{i=new e.Object(c,h,f);c.set("nodes",h,i)}g.each(k,function(l){if(!c.get("nodes",l))throw Error("Type '"+l+"' not found for "+h+"...");c.get("nodes",l).set("nodes",h,i)});c.get(h)._dirty=b;if(!f._id)f._id=h;return true}return false});g.each(d,function(f){f=c.get(f._id);f.data&&f.build()});return this},set:function(a){if(arguments.length=== | ||
2)a=g.extend(arguments[1],{_id:arguments[0]});var b=g.isArray(a.type)?a.type:[a.type];if(arguments.length<=2){a._id=a._id?a._id:e.uuid("/"+g.last(g.last(b).split("/"))+"/");b=this.get(a._id)?this.get(a._id):new e.Object(this,a._id,g.clone(a),true);b.data=a;b._dirty=true;b.build();this.set("nodes",a._id,b);return b}else return e.Node.prototype.set.call(this,arguments[0],arguments[1],arguments[2])},get:function(a){return arguments.length===1?this.get("nodes",a):e.Node.prototype.get.call(this,arguments[0], | ||
arguments[1])},del:function(a){var b=this.get(a);if(b){b._deleted=true;b._dirty=true;b.properties().each(function(c,d){var f=b.all(d);f&&c.unregisterValues(f,b)});this.trigger("dirty")}},find:function(a){return this.objects().select(function(b){return e.matches(b.toJSON(),a)})},fetch:function(a,b,c){var d=this,f=new e.Hash;if(typeof b==="function"&&typeof c==="undefined"){c=b;b={}}this.adapter.read(a,b,function(h,i){if(i){d.merge(i,false);g.each(i,function(k,l){f.set(l,d.get(l))})}h?c(h):c(null,f)})}, | ||
sync:function(a){a=a||function(){};var b=this,c=b.dirtyNodes(),d=new e.Hash;c.select(function(f,h){if(!f.validate||f.validate&&f.validate())d.set(h,f)});this.adapter.write(d.toJSON(),function(f,h){if(f)return a(f);b.merge(h,false);d.each(function(k,l){if(h[l]){k._dirty=false;k._rejected=false}else k._rejected=true});b.invalidNodes().length>0&&b.trigger("invalid");b.conflictedNodes().length>0&&b.trigger("conflicted");b.rejectedNodes().length>0&&b.trigger("rejected");var i=b.invalidNodes().union(b.conflictedNodes()).union(b.rejectedNodes()).length; | ||
a(i>0?i+" unsaved nodes":null)})},group:function(a,b,c){var d=new e.Collection;d.g=e.Transformers.group(this,a,b,c);return d},types:function(){return this.all("nodes").select(function(a){return a.type==="/type/type"||a.type==="type"})},objects:function(){return this.all("nodes").select(function(a){return a.type!=="/type/type"&&a.type!=="type"&&a.data&&!a._deleted})},dirtyNodes:function(){return this.all("nodes").select(function(a){return a._dirty&&(a.data||a instanceof e.Type)})},invalidNodes:function(){return this.all("nodes").select(function(a){return a.errors&& | ||
a.errors.length>0})},conflictedNodes:function(){return this.all("nodes").select(function(a){return a._conflicted})},rejectedNodes:function(){return this.all("nodes").select(function(a){return a._rejected})},toJSON:function(){var a={};this.all("nodes").each(function(b,c){if(b.data||b instanceof e.Type)a[c]=b.toJSON()});return a}});g.extend(e.Graph.prototype,g.Events);e.Collection=function(a){var b=this,c={"/type/item":{type:"/type/type",properties:{}}};if(a)c["/type/item"].indexes=a.indexes||{};if(a){g.each(a.properties, | ||
function(d,f){c["/type/item"].properties[f]=d});this.g=new e.Graph(c);g.each(a.items,function(d,f){b.set(f,d)})}else this.g=new e.Graph};g.extend(e.Collection.prototype,{get:function(){return this.g.get.apply(this.g,arguments)},set:function(a,b){this.g.set(a,g.extend(b,{type:"/type/item"}))},find:function(a){a["type|="]="/type/item";return this.g.find(a)},filter:function(a){return new e.Collection({properties:this.properties().toJSON(),items:this.find(a).toJSON()})},group:function(a,b){var c=new e.Collection; | ||
c.g=e.Transformers.group(this.g,"/type/item",a,b);return c},properties:function(){return this.g.get("nodes","/type/item").all("properties")},items:function(){return this.g.objects()},indexes:function(){return this.g.get("/type/item").indexes},toJSON:function(){return{properties:this.g.toJSON()["/type/item"].properties,items:this.g.objects().toJSON()}}})})(); | ||
c:[c],b));b._dirty=true;b.g.trigger("dirty",b);b.g.snapshot()}});else return e.Node.prototype.set.call(this,arguments[0],arguments[1],arguments[2])},toJSON:function(){var a=this;result={};g.each(this._properties,function(b,c){var d=a.properties().get(c);result[c]=d.isObjectType()?d.unique?a.all(c).keys()[0]:a.all(c).keys():d.unique?a.value(c):a.values(c).values()});result.type=this.types().keys();result._id=this._id;if(this._rev!==undefined)result._rev=this._rev;if(this._deleted)result._deleted=this._deleted; | ||
return result}});g.extend(e.Object.prototype,g.Events);e.Graph=g.inherits(e.Node,{constructor:function(a,b){e.Node.call(this);this.watchers={};this.replace("nodes",new e.Hash);if(a){this.merge(a,b.dirty);if(b.persistent){this.persistent=b.persistent;this.restore()}}},connect:function(a,b){if(typeof exports!=="undefined")this.adapter=new (require(__dirname+"/adapters/"+a+"_adapter"))(this,b);else{if(!e.Adapters[a])throw Error('Adapter "'+a+'" not found');this.adapter=new e.Adapters[a](this,b)}return this}, | ||
connected:function(a){if(this.adapter.realtime)this.connectedCallback=a;else a()},serve:function(a){require(__dirname+"/server").initialize(a,this)},watch:function(a,b,c){this.watchers[a]=c;this.adapter.watch(a,b,function(){})},unwatch:function(a){delete this.watchers[a];this.adapter.unwatch(a,function(){})},empty:function(){var a=this;g.each(this.objects().keys(),function(b){a.del(b);a.all("nodes").del(b)})},merge:function(a,b){var c=this;g.select(a,function(f,h){if(f.type==="/type/type"||f.type=== | ||
"type"){if(!c.get("nodes",h)){c.set("nodes",h,new e.Type(c,h,f));c.get(h)._dirty=f._dirty?f._dirty:b}return true}return false});var d=g.select(a,function(f,h){if(f.type!=="/type/type"&&f.type!=="type"){var i=c.get("nodes",h),k=g.isArray(f.type)?f.type:[f.type];if(i)i.data=f;else{i=new e.Object(c,h,f);c.set("nodes",h,i)}g.each(k,function(l){if(!c.get("nodes",l))throw Error("Type '"+l+"' not found for "+h+"...");c.get("nodes",l).set("nodes",h,i)});c.get(h)._dirty=f._dirty?f._dirty:b;if(!f._id)f._id= | ||
h;return true}return false});g.each(d,function(f){f=c.get(f._id);f.data&&f.build()});this.snapshot();return this},set:function(a){if(arguments.length===2)a=g.extend(arguments[1],{_id:arguments[0]});var b=g.isArray(a.type)?a.type:[a.type];if(arguments.length<=2){a._id=a._id?a._id:e.uuid("/"+g.last(g.last(b).split("/"))+"/");b=this.get(a._id)?this.get(a._id):new e.Object(this,a._id,g.clone(a),true);b.data=a;b._dirty=true;b.build();this.set("nodes",a._id,b);this.snapshot();return b}else return e.Node.prototype.set.call(this, | ||
arguments[0],arguments[1],arguments[2])},get:function(a){return arguments.length===1?this.get("nodes",a):e.Node.prototype.get.call(this,arguments[0],arguments[1])},del:function(a){var b=this.get(a);if(b){b._deleted=true;b._dirty=true;b.properties().each(function(c,d){var f=b.all(d);f&&c.unregisterValues(f,b)});this.trigger("dirty",b);this.snapshot()}},find:function(a){return this.objects().select(function(b){return e.matches(b.toJSON(),a)})},snapshot:function(){this.persistent&&localStorage.setItem("graph", | ||
JSON.stringify(this.toJSON(true)))},restore:function(){var a=JSON.parse(localStorage.getItem("graph"));a&&this.merge(a)},fetch:function(a,b,c){var d=this,f=new e.Hash;if(typeof b==="function"&&typeof c==="undefined"){c=b;b={}}this.adapter.read(a,b,function(h,i){if(i){d.merge(i,false);g.each(i,function(k,l){f.set(l,d.get(l))})}h?c(h):c(null,f)})},sync:function(a){a=a||function(){};var b=this,c=b.dirtyNodes(),d=new e.Hash;c.select(function(f,h){if(!f.validate||f.validate&&f.validate())d.set(h,f)}); | ||
this.adapter.write(d.toJSON(),function(f,h){if(f)return a(f);b.merge(h,false);d.each(function(k,l){if(h[l]){k._dirty=false;k._rejected=false}else k._rejected=true});this.persistent&&b.snapshot();b.invalidNodes().length>0&&b.trigger("invalid");b.conflictedNodes().length>0&&b.trigger("conflicted");b.rejectedNodes().length>0&&b.trigger("rejected");var i=b.invalidNodes().union(b.conflictedNodes()).union(b.rejectedNodes()).length;a(i>0?i+" unsaved nodes":null)})},group:function(a,b,c){var d=new e.Collection; | ||
d.g=e.Transformers.group(this,a,b,c);return d},types:function(){return this.all("nodes").select(function(a){return a.type==="/type/type"||a.type==="type"})},objects:function(){return this.all("nodes").select(function(a){return a.type!=="/type/type"&&a.type!=="type"&&a.data&&!a._deleted})},dirtyNodes:function(){return this.all("nodes").select(function(a){return a._dirty&&(a.data||a instanceof e.Type)})},invalidNodes:function(){return this.all("nodes").select(function(a){return a.errors&&a.errors.length> | ||
0})},conflictedNodes:function(){return this.all("nodes").select(function(a){return a._conflicted})},rejectedNodes:function(){return this.all("nodes").select(function(a){return a._rejected})},toJSON:function(a){var b={};this.all("nodes").each(function(c,d){if(c.data||c instanceof e.Type){b[d]=c.toJSON();if(a){if(c._dirty)b[d]._dirty=true;if(c._conflicted)b[d]._conclicted=true;if(c._rejected)b[d].rejected=true}}});return b}});g.extend(e.Graph.prototype,g.Events);e.Collection=function(a){var b=this, | ||
c={"/type/item":{type:"/type/type",properties:{}}};if(a)c["/type/item"].indexes=a.indexes||{};if(a){g.each(a.properties,function(d,f){c["/type/item"].properties[f]=d});this.g=new e.Graph(c);g.each(a.items,function(d,f){b.set(f,d)})}else this.g=new e.Graph};g.extend(e.Collection.prototype,{get:function(){return this.g.get.apply(this.g,arguments)},set:function(a,b){this.g.set(a,g.extend(b,{type:"/type/item"}))},find:function(a){a["type|="]="/type/item";return this.g.find(a)},filter:function(a){return new e.Collection({properties:this.properties().toJSON(), | ||
items:this.find(a).toJSON()})},group:function(a,b){var c=new e.Collection;c.g=e.Transformers.group(this.g,"/type/item",a,b);return c},properties:function(){return this.g.get("nodes","/type/item").all("properties")},items:function(){return this.g.objects()},indexes:function(){return this.g.get("/type/item").indexes},toJSON:function(){return{properties:this.g.toJSON()["/type/item"].properties,items:this.g.objects().toJSON()}}})})(); |
@@ -1,21 +0,22 @@ | ||
{ | ||
"/type/user": { | ||
"_id": "/type/user", | ||
{ | ||
"/type/project": { | ||
"_id": "/type/project", | ||
"type": "/type/type", | ||
"name": "User", | ||
"name": "Project", | ||
"properties": { | ||
"username": { | ||
"name": "Username", | ||
"name": { | ||
"name": "Name", | ||
"unique": true, | ||
"type": "string", | ||
"required": true, | ||
"validator": "^[a-zA-Z_]{1}[a-zA-Z_0-9-]{2,20}$" | ||
"required": true | ||
}, | ||
"email": { | ||
"name": "Email", | ||
"unique": true, | ||
"type": "string", | ||
"required": true, | ||
"validator": "^(([^<>()[\\]\\\\.,;:\\s@\\\"]+(\\.[^<>()[\\]\\\\.,;:\\s@\\\"]+)*)|(\\\".+\\\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$" | ||
"tasks": { | ||
"name": "Tasks", | ||
"unique": false, | ||
"type": ["/type/task"] | ||
} | ||
}, | ||
"indexes": { | ||
"key": ["creator", "name"], | ||
"creator": ["creator"] | ||
} | ||
@@ -34,2 +35,7 @@ }, | ||
}, | ||
"complete": { | ||
"name": "Complete", | ||
"unique": true, | ||
"type": "boolean" | ||
}, | ||
"project": { | ||
@@ -44,61 +50,3 @@ "name": "Project Membership", | ||
} | ||
}, | ||
"/type/project": { | ||
"_id": "/type/project", | ||
"type": "/type/type", | ||
"name": "Project", | ||
"properties": { | ||
"creator": { | ||
"name": "Creator", | ||
"unique": true, | ||
"required": true, | ||
"type": "/type/user" | ||
}, | ||
"name": { | ||
"name": "Name", | ||
"unique": true, | ||
"type": "string", | ||
"required": true, | ||
"validator": "^[a-zA-Z_0-9]{1}[a-zA-Z_0-9-]{2,40}$" | ||
}, | ||
"descr": { | ||
"name": "Description", | ||
"unique": true, | ||
"type": "string", | ||
"default": "" | ||
}, | ||
"tasks": { | ||
"name": "Tasks", | ||
"unique": false, | ||
"type": ["/type/task"] | ||
} | ||
}, | ||
"indexes": { | ||
"key": ["creator", "name"], | ||
"creator": ["creator"] | ||
} | ||
}, | ||
"/user/demo": { | ||
"type": ["/type/user"], | ||
"username": "demo", | ||
"email": "demo@demo.com" | ||
}, | ||
"/project/test": { | ||
"type": ["/type/project"], | ||
"creator": "/user/demo", | ||
"name": "realtime_tasks_1", | ||
"descr": "A project description", | ||
"tasks": [] | ||
}, | ||
"/project/test2": { | ||
"type": ["/type/project"], | ||
"creator": "/user/demo", | ||
"name": "realtime_tasks_2", | ||
"descr": "A project description", | ||
"tasks": [] | ||
} | ||
} |
@@ -10,3 +10,3 @@ var fs = require('fs'); | ||
var graph = new Data.Graph(seed, true).connect('couch', { url: config.couchdb_url }); | ||
var graph = new Data.Graph(seed, {dirty: true}).connect('couch', { url: config.couchdb_url }); | ||
@@ -13,0 +13,0 @@ if (process.argv[2] == "--flush") { |
@@ -0,1 +1,4 @@ | ||
// Helpers | ||
// -------------- | ||
_.tpl = function(tpl, ctx) { | ||
@@ -6,9 +9,145 @@ source = $("script[name="+tpl+"]").html(); | ||
var Application = Backbone.View.extend({ | ||
// Project | ||
// -------------- | ||
var Project = Backbone.View.extend({ | ||
events: { | ||
'submit #new_task_form': 'createTask', | ||
'click .load-project': 'loadProject', | ||
'click .task .checkbox': 'toggleComplete', | ||
'change .task input': 'updateTask', | ||
'click .task .remove': 'removeTask' | ||
}, | ||
el: '#project', | ||
initialize: function() { | ||
var lru = localStorage.getItem('project'); | ||
// Use LRU project or create a new one | ||
if (lru) { | ||
this.model = graph.get(lru); | ||
} else { | ||
this.createProject(); | ||
} | ||
this.render(); | ||
}, | ||
removeTask: function(e) { | ||
var taskId = $(e.currentTarget).parent().attr('task'); | ||
graph.get(taskId).set({ | ||
"tasks": this.model.get('tasks').del(taskId).keys() | ||
}); | ||
this.render(); | ||
return false; | ||
}, | ||
toggleComplete: function(e) { | ||
var taskId = $(e.currentTarget).parent().attr('task'); | ||
graph.get(taskId).set({ | ||
"complete": !graph.get(taskId).get('complete') | ||
}); | ||
this.render(); | ||
return false; | ||
}, | ||
updateTask: function(e) { | ||
var taskId = $(e.currentTarget).parent().parent().attr('task'); | ||
$(e.currentTarget) | ||
graph.get(taskId).set({ | ||
"name": $(e.currentTarget).val() | ||
}); | ||
this.render(); | ||
return false; | ||
}, | ||
// Create a new project | ||
createProject: function() { | ||
this.model = graph.set({ | ||
"type": ["/type/project"], | ||
"name": "Project "+(graph.find({"type": "/type/project"}).length+1), | ||
"tasks": [] | ||
}); | ||
localStorage.setItem('project', this.model._id); | ||
}, | ||
loadProject: function(e) { | ||
this.model = graph.get($(e.currentTarget).attr('project')); | ||
localStorage.setItem('project', this.model._id); | ||
this.render(); | ||
return false; | ||
}, | ||
createTask: function(e) { | ||
var task = graph.set(null, { | ||
type: "/type/task", | ||
name: $('#task_name').val(), | ||
project: this.model._id | ||
}); | ||
// Append a new task to the project | ||
this.model.set({ | ||
tasks: this.model.get('tasks').keys().concat([task._id]) | ||
}); | ||
// Re-render with new item | ||
this.render(); | ||
return false; | ||
}, | ||
render: function() { | ||
if (this.model) { | ||
$(this.el).html(_.tpl('project', { | ||
project: this.model | ||
})); | ||
} else { | ||
$(this.el).html('Loading...'); | ||
} | ||
} | ||
}); | ||
// Application | ||
// -------------- | ||
var Application = Backbone.View.extend({ | ||
events: { | ||
'click a.start-sync': 'sync', | ||
'click a.reset': 'reset', | ||
'click a.create-project': 'createProject', | ||
}, | ||
createProject: function(e) { | ||
this.project.createProject(); | ||
this.render(); | ||
return false; | ||
}, | ||
reset: function() { | ||
localStorage.removeItem('graph'); | ||
localStorage.removeItem('project'); | ||
window.location.reload(true); | ||
return false; | ||
}, | ||
sync: function() { | ||
var that = this; | ||
// Sync with server | ||
$('#sync_state').html('Synchronizing...'); | ||
graph.sync(function(err) { | ||
if (!err) { | ||
$('#sync_state').html('Successfully synced.'); | ||
setTimeout(function() { | ||
$('#sync_state').html(''); | ||
}, 3000); | ||
that.project.render(); | ||
} else { | ||
console.log(err); | ||
confirm('There was an error during synchronization. The workspace will be reset for your own safety'); | ||
window.location.reload(true); | ||
} | ||
}); | ||
return false; | ||
}, | ||
initialize: function() { | ||
var that = this; | ||
// Load recently used project or create a new one | ||
this.project = new Project(); | ||
@@ -24,3 +163,3 @@ }, | ||
var app; | ||
var graph = new Data.Graph(seed, false).connect('nowjs'); | ||
var graph = new Data.Graph(seed, {dirty: false, persistent: true}).connect('ajax'); | ||
@@ -32,55 +171,9 @@ (function() { | ||
// -------------- | ||
// Once the graph is ready | ||
graph.connected(function() { | ||
app = new Application({el: '#container', session: session}); | ||
app.render(); | ||
window.sync = function(callback) { | ||
$('#sync_state').html('Synchronizing...'); | ||
graph.sync(function(err, invalidNodes) { | ||
window.pendingSync = false; | ||
if (!err && invalidNodes.length === 0) { | ||
$('#sync_state').html('Successfully synced.'); | ||
setTimeout(function() { | ||
$('#sync_state').html(''); | ||
}, 3000); | ||
if (callback) callback(); | ||
} else { | ||
console.log(err); | ||
console.log(invalidNodes.toJSON()); | ||
confirm('There was an error during synchronization. The workspace will be reset for your own safety'); | ||
window.location.reload(true); | ||
} | ||
}); | ||
}; | ||
window.pendingSync = false; | ||
graph.bind('dirty', function() { | ||
if (!window.pendingSync) { | ||
window.pendingSync = true; | ||
setTimeout(function() { | ||
window.sync(); | ||
}, 100); | ||
} | ||
}); | ||
// Rather handle this within sync? sync should probably return conflicting nodes as well | ||
graph.bind('conflicted', function() { | ||
if (!app.document.model) return; | ||
graph.fetch({ | ||
creator: app.document.model.get('creator')._id, | ||
name: app.document.model.get('name') | ||
}, {expand: true}, function(err) { | ||
app.document.render(); | ||
app.scrollTo('#document_wrapper'); | ||
}); | ||
notifier.notify({ | ||
message: 'There are conflicting nodes. The Document will be reset for your own safety.', | ||
type: 'error' | ||
}); | ||
}); | ||
}); | ||
}); | ||
})(); |
@@ -11,3 +11,3 @@ { | ||
"main" : "index", | ||
"version" : "0.4.0" | ||
"version" : "0.4.1" | ||
} |
@@ -100,8 +100,6 @@ var _ = require('underscore'); | ||
callback(err, g); | ||
// Notify channel users | ||
dispatchUpdates(g); | ||
}, this.user); | ||
callback(); | ||
}; | ||
}; |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
4896852
38
7431