Skip to content

Instantly share code, notes, and snippets.

@ghinch
Created September 23, 2010 16:41
Show Gist options
  • Save ghinch/593942 to your computer and use it in GitHub Desktop.
Save ghinch/593942 to your computer and use it in GitHub Desktop.
var data = [
{author : {surname : 'Roberts', firstname : 'Gregory David'}, title : 'Shantaram', published : (new Date('09/25/2005'))},
{author : {surname : 'Gibson', firstname : 'William'}, title : 'Neuromancer', published : (new Date('07/01/1984'))},
{author : {surname : 'Rowling', firstname : 'J.K.'}, title : 'Harry Potter', published : (new Date('06/30/1997'))},
{author : {surname : 'Simon', firstname : 'Ted'}, title : 'Jupiter\'s Travels', published : (new Date('01/01/1979'))},
{author : {surname : 'Tolkien', firstname : 'J.R.R.'}, title : 'The Hobbit', published : (new Date('09/21/1937'))}
];
var ds = new Y.DataSource.Local({
source : data,
plugins : [
{fn : Y.Plugin.DataSourceArraySchema}
]
});
var Author = Y.Base.create('author', Y.Model, [], {
}, {
ATTRS : {
surname : {},
firstname : {}
}
});
var Book = Y.Base.create('book', Y.Model, [], {
}, {
ATTRS : {
author : {
setter : Y.Model.reference(Author)
},
title : {
setter : 'string'
},
published : {
setter : 'date'
}
},
RENDERERS : {
published : {fn : Y.Model.RENDER.DATE, cfg : {format : '%D'}},
author : {
fn : function (val, key) {
return val.render('firstname') + ' ' + val.render('surname');
}
}
}
});
function sortAuthor (a, b, desc) {
var aName = a.surname,
bName = b.surname;
return Y.Sort.compare(aName, bName, desc);
}
var qm = new Y.QueryManagerLocal({
sortedBy : 'author',
sortFunctions : {
author : sortAuthor
},
datasource : ds,
model : Book
});
var t = new Y.Table({
sortColumn : 'author',
columns : Book.members()
});
t.on('sortColumnChange', function (e) {
qm.set('sortedBy', e.newVal);
qm.fetch();
});
qm.after('results', function (e) {
t.set('data', e.results);
});
t.render('#content');
qm.fetch();
YUI.add('model', function (Y) {
Y.Model = Y.Base.create('model', Y.Base, [], {
// These methods allow setters to be strings for primitive types
'string' : function (val, key) {
return String(val);
},
'number' : function (val, key) {
return Number(val);
},
'date' : function (val, key) {
return new Date(val);
},
// @TODO : implement editors
edit : function (key) {
return this.constructor.EDITORS[key].call(this, this.get(key));
},
// Draws out the value for the given key using a supplied render function (or just returns the value)
render : function (key) {
var renderer = (this.constructor.RENDERERS ? this.constructor.RENDERERS[key] : null),
val = this.get(key);
if (renderer) {
if (Y.Lang.isFunction(renderer)) {
return renderer.call(this, val);
} else if (Y.Lang.isObject(renderer) && renderer.fn) {
return renderer.fn.call(this, val, renderer.cfg);
}
}
return val;
},
// Compare if two instances are the same by turning them into objects and comparing them as JSON.
// Would be nice if we had md5 here... this may have problems though, if Model references are included,
// may just need to do top level comparison
compare : function (other) {
if (!other instanceof this.constructor) {
return false;
}
var selfHash = Y.JSON.stringify(this.toObject()),
otherHash = Y.JSON.stringify(other.toObject());
return selfHash == otherHash;
},
// Turns this model instance into a Javascript object which can be serialized into another data format
toObject : function () {
var c = this.constructor,
members = c.members(),
obj = {};
Y.Array.each(members, function (member) {
obj[member] = this.get(member);
}, this);
return obj;
}
}, {
// @TODO : implement some default editors
EDIT : {
},
// @TODO : implement more default renderers
RENDER : {
DATE : function (val, key, cfg) {
return Y.DataType.Date.format(val, cfg);
}
},
// Class method for returning a list of all the member properties (ATTRS) of this constructor,
// up the inheritance chain
members : function () {
var c = this.prototype.constructor,
map = [];
while (c) {
try {
if (c.ATTRS) {
Y.Object.each(c.ATTRS, function (val, key) {
if (!val.readOnly) {
map.push(key);
}
});
}
} catch (err) {}
c = (c.superclass ? c.superclass.constructor : null);
}
return map;
},
// Utility method to create a reference to another Model constructor as the
// value to set for a property.
// @TODO : Look into making this lazy to a remote source
reference : function (model) {
if (model.prototype) {
function createModel (cfg, key, construct) {
try {
return new construct(cfg);
} catch (err) {}
}
return Y.rbind(createModel, this, model);
}
},
// Same as above but creates an array of Models rather than just one
referenceMany : function (model) {
if (model.prototype) {
function createModels (cfg, key, construct) {
var data = [];
try {
Y.Array.each(cfg, function (o) {
data.push(new construct(o));
});
} finally {
return data;
}
}
return Y.rbind(createModels, this, model);
}
}
});
// Allows the class method 'members' to be aggregated across all classes
// which inherit from Y.Model
Y.Base._buildCfg.custom.members = function (prop, r, s) {
var c = r.superclass.constructor;
while (c.superclass) {
if (c == Y.Model) {
r.members = Y.Model.members;
break;
}
c = c.superclass.constructor;
}
};
}, '', {requires : ['base-build', 'datatype-date', 'json-stringify']});
YUI.add('query-manager', function (Y) {
// Taken more or less verbatim from YUI 2
Y.namespace('Sort').compare = function (a, b, desc) {
if((a === null) || a === undefined) {
if((b === null) || b === undefined) {
return 0;
}
else {
return 1;
}
} else if((b === null) || b === undefined) {
return -1;
}
if(Y.Lang.isString(a)) {
a = a.toLowerCase();
}
if(Y.Lang.isString(b)) {
b = b.toLowerCase();
}
if(a < b) {
return (desc) ? 1 : -1;
}
else if (a > b) {
return (desc) ? -1 : 1;
}
else {
return 0;
}
};
Y.QueryManagerBase = Y.Base.create('query-manager', Y.Base, [], {
// Takes an array of results and turns them into an ArrayList
// of model instances
_processResults : function (results) {
var resultSet = [],
model = this.get('model');
Y.Array.each(results, function (result) {
var m = new model(result);
if (m) {
resultSet.push(m);
}
});
this.fire('results', {
results : (new Y.ArrayList(resultSet))
});
},
initializer : function () {
this.publish('results');
},
// Makes the request to the datasource
fetch : function (request) {
var ds = this.get('datasource');
request = request || '';
ds.sendRequest({
request : request,
callback : {
success : Y.bind(function (e) {
this._processResults(e.response.results);
}, this)
}
});
}
}, {
ATTRS : {
model : {},
datasource : {
setter : function (val, key) {
var model = this.get('model');
if(model && val.schema) {
var fields = model.members(),
currentSchema = val.schema.get('schema') || {};
newSchema = Y.merge(currentSchema, {
resultFields : fields
});
val.schema.set('schema', newSchema);
}
}
}
}
});
// For use in any case where all data is local (ie: no AJAX request required)
Y.QueryManagerLocal = Y.Base.create('query-manager-local', Y.QueryManagerBase, [], {
// Sorts the results as defined by the config before processing them
_processResults : function (results) {
var sortedBy = this.get('sortedBy'),
sortDesc = this.get('sortDesc'),
sortFunctions = this.get('sortFunctions'),
sortFn = sortFunctions[sortedBy] || Y.Sort.compare;
results.sort(Y.bind(function (a, b) {
return sortFn.call(this, a[sortedBy], b[sortedBy], sortDesc);
}, this));
Y.QueryManagerLocal.superclass._processResults.call(this, results);
},
initializer : function () {
// If we sort on the same col we already have selected, we just reverse the order
this.on('sortedByChange', function (e) {
var sortDesc = this.get('sortDesc');
this.set('sortDesc', (e.newVal == e.prevVal ? (!sortDesc) : this.get('defaultSortDesc')));
}, this);
}
}, {
ATTRS : {
defaultSortDesc : {
value : false
},
sortFunctions : {},
sortDesc : {
valueFn : function () {
return this.get('defaultSortDesc');
}
},
sortedBy : {
valueFn : function () {
var model = this.get('model');
if (model) {
return model.members()[0];
}
}
}
}
});
}, '', {requires : ['collection', 'base-build']});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment