Skip to content

Instantly share code, notes, and snippets.

@labiak
Forked from kissarat/RedBone.js
Created October 18, 2018 07:20
Show Gist options
  • Save labiak/a96b2ebfa91a4ba405c6533995621c61 to your computer and use it in GitHub Desktop.
Save labiak/a96b2ebfa91a4ba405c6533995621c61 to your computer and use it in GitHub Desktop.
"use strict";
const DEBUG = !!+localStorage.get('DEBUG');
function base(self, parent) {
self.__proto__.__proto__ = parent;
}
function copy(a, b) {
if (b)
b = {};
for(var name in a)
b[name] = a[name];
return b;
}
function empty(object) {
for(var key in object)
return false;
return true;
}
var Events = {
on: function(name, call, ctx) {
if (!this._events)
this._events = {};
if (ctx)
call = call.bind(ctx);
if (!name)
name = '.';
(this._events || (this._events[name] = [])).push(call);
return this;
},
once: function(name, call, ctx) {
var self = this;
if (ctx)
call = call.bind(ctx);
var _once = (function (call) {
call();
self.off(_once);
}).bind(this, call);
this.on(name, _once);
return this;
},
off: function(name, call) {
if (!this._events)
return this;
if (!name)
this._events = {};
if (!this._events[name])
return this;
if (!call)
this._events[name] = [];
delete this._events[name][call];
return this;
},
trigger: function(name, args) {
var self = this;
if (this._events) {
if (this._events[name])
this._events[name].forEach(function (call) {
call(args);
});
if (this._events['.'])
this._events['.'].forEach(function (call) {
call(name, self, args);
});
}
return this;
}
};
function Model(attributes) {
base(this, Events);
this.attributes = attributes || {};
this.changed = {};
}
Model.prototype = {
initialize: Function(),
had: function(name) {
return undefined !== this.attributes[name];
},
has: function(name) {
return this.had(name) || undefined !== this.attributes[name];
},
get: function(name) {
if (this.changed[name])
return this.changed[name];
else
return this.attributes[name];
},
set: function(name, value) {
var changed = Object.create(null);
changed[name] = value;
this.change(changed);
return this;
},
change: function(changed) {
copy(changed, this.changed);
this.trigger('change', changed);
return this;
},
unset: function(name) {
delete this.changed[name];
delete this.attributes[name];
return this;
},
clear: function() {
for(var name in this.attributes)
this.unset(name);
return this;
},
hasChanged: function() {
return this.changed.length > 0;
},
changedAttributes: function() {
return copy(this.changed);
},
previous: function(name) {
return copy(this.attributes[name]);
},
previousAttributes: function() {
return copy(this.attributes);
},
isNew: function() {
return !!this.id;
},
dump: function() {
var dump = copy(this.attributes);
return copy(dump, this.changed);
},
clone: function(omits) {
var attributes;
if (omits) {
attributes = this.previousAttributes();
for(var i in omits)
delete attributes[omits[i]];
}
else
attributes = this.attributes;
return new this.constructor(attributes);
}
};
function List(list) {
base(this, Events);
this.list = list || [];
this.model = Model;
}
List.prototype = {
add: function(models) {
if (!(models instanceof Array))
models = [models];
for(var i in models) {
var m = models[i];
if (!(m instanceof Model)) {
m = new this.model(m);
models[i] = m;
}
this.list.push(m);
}
if (models.length > 0)
this.trigger('add', models);
return this;
},
update: function(models) {
if (!(models instanceof Array))
models = [models];
for(var i in models) {
var m = models[i];
if (!m.id)
throw new Error('Model id is not defined');
var existing = this.where({id: m.id}, true)();
if (!existing)
throw new Error('Model not found');
existing.update(models);
}
if (models.length > 0)
this.trigger('update', models);
return this;
},
change: function(models) {
var inserts = [];
for(var i in models) {
var m = models[i];
if (m instanceof Model && m.isNew())
inserts.push(m);
if (m.id) {
var existing = this.where({id: m.id}, true)();
if (existing)
existing.update(m);
else
inserts.push(m);
}
else throw new Error('Behaviour is undefined');
}
this.add(inserts);
},
where: function(attributes, lazy) {
var list = this.list;
var i = 0;
function iterate() {
var m = list[i];
if (!m)
return null;
var match = true;
for (var key in attributes)
if (m.get(key) != attributes[key]) {
match = false;
break;
}
i++;
if (match)
return m;
else
return iterate();
}
if (lazy)
return iterate();
else {
var matches = [];
var m;
while(m = iterate())
matches.push(m);
return matches;
}
}
};
function validate(changed, validates) {
var errors = {};
for(var name in changed) {
var validate = validates[name];
if (validate)
validate(name, changed[name], errors);
}
return errors;
}
function valid(model, validates) {
model.errors = {};
model.validates = validates || {};
model.on('change', function() {
model.errors = RedBone.validate(model.changed, model.validates);
});
model.isValid = function() {
return empty(this.errors);
}
}
function prettify(object, apply = DEBUG) {
return apply ? JSON.stringify(object, null, '\t') : JSON.stringify(object);
}
function permanent(id, object) {
return localStorage.setItem(id, prettify(object));
}
function Storage() {
}
Storage.prototype = {
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment