Created
May 22, 2010 19:28
-
-
Save kriszyp/410295 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require.define({ | |
"promise":function(require, exports, module) { | |
// Kris Zyp | |
// this is based on the CommonJS spec for promises: | |
// http://wiki.commonjs.org/wiki/Promises | |
// Includes convenience functions for promises, much of this is taken from Tyler Close's ref_send | |
// and Kris Kowal's work on promises. | |
// // MIT License | |
// A typical usage: | |
// A default Promise constructor can be used to create a self-resolving deferred/promise: | |
// var Promise = require("promise").Promise; | |
// var promise = new Promise(); | |
// asyncOperation(function(){ | |
// Promise.resolve("succesful result"); | |
// }); | |
// promise -> given to the consumer | |
// | |
// A consumer can use the promise | |
// promise.then(function(result){ | |
// ... when the action is complete this is executed ... | |
// }, | |
// function(error){ | |
// ... executed when the promise fails | |
// }); | |
// | |
// Alternately, a provider can create a deferred and resolve it when it completes an action. | |
// The deferred object a promise object that provides a separation of consumer and producer to protect | |
// promises from being fulfilled by untrusted code. | |
// var defer = require("promise").defer; | |
// var deferred = defer(); | |
// asyncOperation(function(){ | |
// deferred.resolve("succesful result"); | |
// }); | |
// deferred.promise -> given to the consumer | |
// | |
// Another way that a consumer can use the promise (using promise.then is also allowed) | |
// var when = require("promise").when; | |
// when(promise,function(result){ | |
// ... when the action is complete this is executed ... | |
// }, | |
// function(error){ | |
// ... executed when the promise fails | |
// }); | |
exports.errorTimeout = 100; | |
var freeze = Object.freeze || function(){}; | |
/** | |
* Default constructor that creates a self-resolving Promise. Not all promise implementations | |
* need to use this constructor. | |
*/ | |
var Promise = function(canceller){ | |
}; | |
/** | |
* Promise implementations must provide a "then" function. | |
*/ | |
Promise.prototype.then = function(resolvedCallback, errorCallback, progressCallback){ | |
throw new TypeError("The Promise base class is abstract, this function must be implemented by the Promise implementation"); | |
}; | |
/** | |
* If an implementation of a promise supports a concurrency model that allows | |
* execution to block until the promise is resolved, the wait function may be | |
* added. | |
*/ | |
/** | |
* If an implementation of a promise can be cancelled, it may add this function | |
*/ | |
// Promise.prototype.cancel = function(){ | |
// }; | |
Promise.prototype.get = function(propertyName){ | |
return this.then(function(value){ | |
return value[propertyName]; | |
}); | |
}; | |
Promise.prototype.put = function(propertyName, value){ | |
return this.then(function(object){ | |
return object[propertyName] = value; | |
}); | |
}; | |
Promise.prototype.call = function(functionName /*, args */){ | |
return this.then(function(value){ | |
return value[propertyName].apply(value, Array.prototype.slice.call(arguments, 1)); | |
}); | |
}; | |
/** Dojo/NodeJS methods*/ | |
Promise.prototype.addCallback = function(callback){ | |
return this.then(callback); | |
}; | |
Promise.prototype.addErrback = function(errback){ | |
return this.then(function(){}, errback); | |
}; | |
/*Dojo methods*/ | |
Promise.prototype.addBoth = function(callback){ | |
return this.then(callback, callback); | |
}; | |
Promise.prototype.addCallbacks = function(callback, errback){ | |
return this.then(callback, errback); | |
}; | |
/*NodeJS method*/ | |
Promise.prototype.wait = function(){ | |
return exports.wait(this); | |
}; | |
Deferred.prototype = Promise.prototype; | |
// A deferred provides an API for creating and resolving a promise. | |
exports.Promise = exports.Deferred = exports.defer = defer; | |
function defer(){ | |
return new Deferred(); | |
} | |
var contextHandler = exports.contextHandler = {}; | |
function Deferred(canceller){ | |
var result, finished, isError, waiting = [], handled; | |
var promise = this.promise = new Promise(); | |
var currentContextHandler = contextHandler.getHandler && contextHandler.getHandler(); | |
function notifyAll(value){ | |
if(finished){ | |
throw new Error("This deferred has already been resolved"); | |
} | |
result = value; | |
finished = true; | |
for(var i = 0; i < waiting.length; i++){ | |
notify(waiting[i]); | |
} | |
} | |
function notify(listener){ | |
var func = (isError ? listener.error : listener.resolved); | |
if(func){ | |
handled = true; | |
if(currentContextHandler){ | |
currentContextHandler.resume(); | |
} | |
try{ | |
var newResult = func(result); | |
if(newResult && typeof newResult.then === "function"){ | |
newResult.then(listener.deferred.resolve, listener.deferred.reject); | |
return; | |
} | |
listener.deferred.resolve(newResult); | |
} | |
catch(e){ | |
listener.deferred.reject(e); | |
} | |
finally{ | |
if(currentContextHandler){ | |
currentContextHandler.suspend(); | |
} | |
} | |
} | |
else{ | |
if(isError){ | |
if (listener.deferred.reject(result, true)) { | |
handled = true; | |
} | |
} | |
else{ | |
listener.deferred.resolve.apply(listener.deferred, result); | |
} | |
} | |
} | |
// calling resolve will resolve the promise | |
this.resolve = this.callback = this.emitSuccess = function(value){ | |
notifyAll(value); | |
}; | |
// calling error will indicate that the promise failed | |
var reject = this.reject = this.errback = this.emitError = function(error, dontThrow){ | |
isError = true; | |
notifyAll(error); | |
if (!dontThrow && typeof setTimeout !== "undefined") { | |
setTimeout(function () { | |
if (!handled) { | |
throw error; | |
} | |
}, exports.errorTimeout); | |
} | |
return handled; | |
}; | |
// call progress to provide updates on the progress on the completion of the promise | |
this.progress = function(update){ | |
for(var i = 0; i < waiting.length; i++){ | |
var progress = waiting[i].progress; | |
progress && progress(update); | |
} | |
} | |
// provide the implementation of the promise | |
this.then = promise.then = function(resolvedCallback, errorCallback, progressCallback){ | |
var returnDeferred = new Deferred(promise.cancel); | |
var listener = {resolved: resolvedCallback, error: errorCallback, progress: progressCallback, deferred: returnDeferred}; | |
if(finished){ | |
notify(listener); | |
} | |
else{ | |
waiting.push(listener); | |
} | |
return returnDeferred.promise; | |
}; | |
var timeout; | |
if(typeof setTimeout !== "undefined") { | |
this.timeout = function (ms) { | |
if (ms === undefined) { | |
return timeout; | |
} | |
timeout = ms; | |
setTimeout(function () { | |
if (!finished) { | |
if (promise.cancel) { | |
promise.cancel(new Error("timeout")); | |
} | |
else { | |
reject(new Error("timeout")); | |
} | |
} | |
}, ms); | |
return promise; | |
}; | |
} | |
if(canceller){ | |
this.cancel = promise.cancel = function(){ | |
var error = canceller(); | |
if(!(error instanceof Error)){ | |
error = new Error(error); | |
} | |
reject(error); | |
} | |
} | |
freeze(promise); | |
}; | |
function perform(value, async, sync){ | |
try{ | |
if(value && typeof value.then === "function"){ | |
value = async(value); | |
} | |
else{ | |
value = sync(value); | |
} | |
if(value && typeof value.then === "function"){ | |
return value; | |
} | |
var deferred = new Deferred(); | |
deferred.resolve(value); | |
return deferred.promise; | |
}catch(e){ | |
var deferred = new Deferred(); | |
deferred.reject(e); | |
return deferred.promise; | |
} | |
} | |
/** | |
* Promise manager to make it easier to consume promises | |
*/ | |
/** | |
* Registers an observer on a promise. | |
* @param value promise or value to observe | |
* @param resolvedCallback function to be called with the resolved value | |
* @param rejectCallback function to be called with the rejection reason | |
* @param progressCallback function to be called when progress is made | |
* @return promise for the return value from the invoked callback | |
*/ | |
exports.whenPromise = function(value, resolvedCallback, rejectCallback, progressCallback){ | |
return perform(value, function(value){ | |
return value.then(resolvedCallback, rejectCallback, progressCallback); | |
}, | |
function(value){ | |
return resolvedCallback(value); | |
}); | |
}; | |
/** | |
* Registers an observer on a promise. | |
* @param value promise or value to observe | |
* @param resolvedCallback function to be called with the resolved value | |
* @param rejectCallback function to be called with the rejection reason | |
* @param progressCallback function to be called when progress is made | |
* @return promise for the return value from the invoked callback or the value if it | |
* is a non-promise value | |
*/ | |
exports.when = function(value, resolvedCallback, rejectCallback, progressCallback){ | |
if(value && typeof value.then === "function"){ | |
return exports.whenPromise(value, resolvedCallback, rejectCallback, progressCallback); | |
} | |
return resolvedCallback(value); | |
}; | |
/** | |
* Gets the value of a property in a future turn. | |
* @param target promise or value for target object | |
* @param property name of property to get | |
* @return promise for the property value | |
*/ | |
exports.get = function(target, property){ | |
return perform(target, function(target){ | |
return target.get(property); | |
}, | |
function(target){ | |
return target[property] | |
}); | |
}; | |
/** | |
* Invokes a method in a future turn. | |
* @param target promise or value for target object | |
* @param methodName name of method to invoke | |
* @param args array of invocation arguments | |
* @return promise for the return value | |
*/ | |
exports.post = function(target, methodName, args){ | |
return perform(target, function(target){ | |
return target.call(property, args); | |
}, | |
function(target){ | |
return target[methodName].apply(target, args); | |
}); | |
}; | |
/** | |
* Sets the value of a property in a future turn. | |
* @param target promise or value for target object | |
* @param property name of property to set | |
* @param value new value of property | |
* @return promise for the return value | |
*/ | |
exports.put = function(target, property, value){ | |
return perform(target, function(target){ | |
return target.put(property, value); | |
}, | |
function(target){ | |
return target[property] = value; | |
}); | |
}; | |
/** | |
* Waits for the given promise to finish, blocking (and executing other events) | |
* if necessary to wait for the promise to finish. If target is not a promise | |
* it will return the target immediately. If the promise results in an reject, | |
* that reject will be thrown. | |
* @param target promise or value to wait for. | |
* @return the value of the promise; | |
*/ | |
exports.wait = function(target){ | |
if(!queue){ | |
throw new Error("Can not wait, the event-queue module is not available"); | |
} | |
if(target && typeof target.then === "function"){ | |
var isFinished, isError, result; | |
target.then(function(value){ | |
isFinished = true; | |
result = value; | |
}, | |
function(error){ | |
isFinished = true; | |
isError = true; | |
result = error; | |
}); | |
while(!isFinished){ | |
queue.processNextEvent(true); | |
} | |
if(isError){ | |
throw result; | |
} | |
return result; | |
} | |
else{ | |
return target; | |
} | |
}; | |
/** | |
* Takes an array of promises and returns a promise that is fulfilled once all | |
* the promises in the array are fulfilled | |
* @param array The array of promises | |
* @return the promise that is fulfilled when all the array is fulfilled, resolved to the array of results | |
*/ | |
exports.all = function(array){ | |
var deferred = new Deferred(); | |
if(!(array instanceof Array)){ | |
array = Array.prototype.slice.call(arguments); | |
} | |
var fulfilled = 0, length = array.length; | |
var results = []; | |
array.forEach(function(promise, index){ | |
exports.when(promise, each, each); | |
function each(value){ | |
results[index] = value; | |
fulfilled++; | |
if(fulfilled === length){ | |
deferred.resolve(results); | |
} | |
} | |
}); | |
return deferred.promise; | |
}; | |
/** | |
* Takes an array of promises and returns a promise that is fulfilled when the first | |
* promise in the array of promises is fulfilled | |
* @param array The array of promises | |
* @return a promise that is fulfilled with the value of the value of first promise to be fulfilled | |
*/ | |
exports.first = function(array){ | |
var deferred = new Deferred(); | |
if(!(array instanceof Array)){ | |
array = Array.prototype.slice.call(arguments); | |
} | |
var fulfilled; | |
array.forEach(function(promise, index){ | |
exports.when(promise, function(value){ | |
if (!fulfilled) { | |
fulfilled = true; | |
deferred.resolve(value); | |
} | |
}, | |
function(error){ | |
if (!fulfilled) { | |
fulfilled = true; | |
deferred.resolve(error); | |
} | |
}); | |
}); | |
return deferred.promise; | |
}; | |
/** | |
* Takes an array of asynchronous functions (that return promises) and | |
* executes them sequentially. Each funtion is called with the return value of the last function | |
* @param array The array of function | |
* @param startingValue The value to pass to the first function | |
* @return the value returned from the last function | |
*/ | |
exports.seq = function(array, startingValue){ | |
array = array.concat(); // make a copy | |
var deferred = new Deferred(); | |
function next(value){ | |
var nextAction = array.shift(); | |
if(nextAction){ | |
exports.when(nextAction(value), next, deferred.reject); | |
} | |
else { | |
deferred.resolve(value); | |
} | |
} | |
next(startingValue); | |
return deferred.promise; | |
}; | |
/** | |
* Delays for a given amount of time and then fulfills the returned promise. | |
* @param milliseconds The number of milliseconds to delay | |
* @return A promise that will be fulfilled after the delay | |
*/ | |
if(typeof setTimeout !== "undefined") { | |
exports.delay = function(milliseconds) { | |
var deferred = new Deferred(); | |
setTimeout(function(){ | |
deferred.resolve(); | |
}, milliseconds); | |
return deferred.promise; | |
}; | |
} | |
/** | |
* Runs a function that takes a callback, but returns a Promise instead. | |
* @param func node compatible async function which takes a callback as its last argument | |
* @return promise for the return value from the callback from the function | |
*/ | |
exports.execute = function(asyncFunction){ | |
var args = Array.prototype.slice.call(arguments, 1); | |
var deferred = new Deferred(); | |
args.push(function(error, result){ | |
if(error) { | |
deferred.emitError(error); | |
} | |
else { | |
if(arguments.length > 2){ | |
// if there are multiple success values, we return an array | |
Array.prototype.shift.call(arguments, 1); | |
deferred.emitSuccess(arguments); | |
} | |
else{ | |
deferred.emitSuccess(result); | |
} | |
} | |
}); | |
asyncFunction.apply(this, args); | |
return deferred.promise; | |
}; | |
/** | |
* Converts a Node async function to a promise returning function | |
* @param func node compatible async function which takes a callback as its last argument | |
* @return A function that returns a promise | |
*/ | |
exports.convertNodeAsyncFunction = function(asyncFunction, callbackNotDeclared){ | |
var arity = asyncFunction.length; | |
return function(){ | |
var deferred = new Deferred(); | |
if(callbackNotDeclared){ | |
arity = arguments.length + 1; | |
} | |
arguments.length = arity; | |
arguments[arity - 1] = function(error, result){ | |
if(error) { | |
deferred.emitError(error); | |
} | |
else { | |
if(arguments.length > 2){ | |
// if there are multiple success values, we return an array | |
Array.prototype.shift.call(arguments, 1); | |
deferred.emitSuccess(arguments); | |
} | |
else{ | |
deferred.emitSuccess(result); | |
} | |
} | |
}; | |
asyncFunction.apply(this, arguments); | |
return deferred.promise; | |
}; | |
}; | |
}, | |
"perstore/resource-query":function(require, exports, module) {/** | |
* This module provides querying functionality | |
*/ | |
exports.jsonQueryCompatible = true; | |
var operatorMap = { | |
"=": "eq", | |
"==": "eq", | |
">": "gt", | |
">=": "ge", | |
"<": "lt", | |
"<=": "le", | |
"!=": "ne" | |
}; | |
var parseQuery = exports.parseQuery = function(/*String|Object*/query, parameters){ | |
var term = new Query(); | |
var topTerm = term; | |
if(typeof query === "object"){ | |
if(query instanceof Array){ | |
return query; | |
} | |
for(var i in query){ | |
var term = new Query(); | |
topTerm.args.push(term); | |
term.name = "eq"; | |
term.args = [i, query[i]]; | |
} | |
return topTerm; | |
} | |
if(query.charAt(0) == "?"){ | |
throw new Error("Query must not start with ?"); | |
} | |
if(exports.jsonQueryCompatible){ | |
query = query.replace(/%3C=/g,"=le=").replace(/%3E=/g,"=ge=").replace(/%3C/g,"=lt=").replace(/%3E/g,"=gt="); | |
} | |
// convert FIQL to normalized call syntax form | |
query = query.replace(/([\+\*\-:\w%\._]+)(>|<|[<>!]?=([\w]*=)?)([\+\*\$\-:\w%\._]+|\([\+\*\$\-:\w%\._,]+\))/g, function(t, property, operator, operatorSuffix, value){ | |
if(operator.length < 3){ | |
if(!operatorMap[operator]){ | |
throw new Error("Illegal operator " + operator); | |
} | |
operator = operatorMap[operator]; | |
} | |
else{ | |
operator = operator.substring(1, operator.length - 1); | |
} | |
return operator + '(' + property + "," + value + ")"; | |
}); | |
if(query.charAt(0)=="?"){ | |
query = query.substring(1); | |
} | |
var leftoverCharacters = query.replace(/(\))|([&\|,])?([\+\*\$\-:\w%\._]*)(\(?)/g, | |
// <-closedParan->|<-delim-- propertyOrValue -----(> | | |
function(t, closedParan, delim, propertyOrValue, openParan){ | |
if(delim){ | |
if(delim === "&"){ | |
forceOperator("and"); | |
} | |
if(delim === "|"){ | |
forceOperator("or"); | |
} | |
} | |
if(openParan){ | |
var newTerm = new Query(); | |
newTerm.name = propertyOrValue, | |
newTerm.parent = term; | |
call(newTerm); | |
} | |
else if(closedParan){ | |
var isArray = !term.name; | |
term = term.parent; | |
if(!term){ | |
throw new URIError("Closing paranthesis without an opening paranthesis"); | |
} | |
if(isArray){ | |
term.args.push(term.args.pop().args); | |
} | |
} | |
else if(propertyOrValue){ | |
term.args.push(stringToValue(propertyOrValue, parameters)); | |
} | |
return ""; | |
}); | |
if(term.parent){ | |
throw new URIError("Opening paranthesis without a closing paranthesis"); | |
} | |
if(leftoverCharacters){ | |
// any extra characters left over from the replace indicates invalid syntax | |
throw new URIError("Illegal character in query string encountered " + leftoverCharacters); | |
} | |
function call(newTerm){ | |
term.args.push(newTerm); | |
term = newTerm; | |
} | |
function forceOperator(operator){ | |
if(!term.name){ | |
term.name = operator; | |
} | |
else if(term.name !== operator){ | |
var last = term.args.pop(); | |
var newSet = new Query(operator); | |
newSet.parent = term.parent; | |
call(newSet); | |
term.args.push(last); | |
} | |
} | |
function removeParentProperty(obj) { | |
if(obj && obj.args){ | |
delete obj.parent; | |
obj.args.forEach(removeParentProperty); | |
} | |
return obj; | |
}; | |
removeParentProperty(topTerm); | |
return topTerm; | |
}; | |
function encodeString(s) { | |
if (typeof s === "string") { | |
s = encodeURIComponent(s); | |
if (s.match(/[\(\)]/)) { | |
s = s.replace("(","%28").replace(")","%29"); | |
}; | |
} | |
return s; | |
} | |
exports.encodeValue = function(val) { | |
var encoded; | |
if (val !== exports.converters["default"]('' + ( | |
val.toISOString && val.toISOString() || val.toString() | |
))) { | |
var type = typeof val; | |
if(type === "object"){ | |
type = "epoch"; | |
val = val.getTime(); | |
} | |
if(type === "string") { | |
val = encodeString(val); | |
encoded = true; | |
} | |
val = [type, val].join(":"); | |
} | |
if (!encoded && typeof val === "string") val = encodeString(val); | |
return val; | |
}; | |
function queryToString(part) { | |
if (Array.isArray(part)) { | |
return part.map(function(arg) { | |
return queryToString(arg); | |
}).join(","); | |
} | |
if (part && part.name && part.args) { | |
return [ | |
part.name, | |
"(", | |
part.args.map(function(arg) { | |
return queryToString(arg); | |
}).join(","), | |
")" | |
].join(""); | |
} | |
return exports.encodeValue(part); | |
}; | |
exports.jsOperatorMap = { | |
"eq" : "===", | |
"ne" : "!==", | |
"le" : "<=", | |
"ge" : ">=", | |
"lt" : "<", | |
"gt" : ">" | |
} | |
exports.commonOperatorMap = { | |
"and" : "&", | |
"or" : "|", | |
"eq" : "=", | |
"ne" : "!=", | |
"le" : "<=", | |
"ge" : ">=", | |
"lt" : "<", | |
"gt" : ">" | |
} | |
exports.operators = { | |
sort: function(){ | |
var terms = []; | |
for(var i = 0; i < arguments.length; i++){ | |
var sortAttribute = arguments[i]; | |
var firstChar = sortAttribute.charAt(0); | |
var term = {attribute: sortAttribute, ascending: true}; | |
if (firstChar == "-" || firstChar == "+") { | |
if(firstChar == "-"){ | |
term.ascending = false; | |
} | |
term.attribute = term.attribute.substring(1); | |
} | |
terms.push(term); | |
} | |
this.sort(function(a, b){ | |
for (var i = 0; i < terms.length; i++) { | |
var term = terms[i]; | |
if (a[term.attribute] != b[term.attribute]) { | |
return term.ascending == a[term.attribute] > b[term.attribute] ? 1 : -1; | |
} | |
} | |
return true; //undefined? | |
}); | |
return this; | |
}, | |
"in": filter(function(value, values){ | |
return values.indexOf(value) > -1; | |
}), | |
contains: filter(function(array, value){ | |
if(value instanceof Array){ | |
return value.some.call(arguments, function(value){ | |
return array.indexOf(value) > -1; | |
}); | |
} | |
else{ | |
return array.indexOf(value) > -1; | |
} | |
}), | |
or: function(){ | |
var items = []; | |
//TODO: remove duplicates and use condition property | |
for(var i = 0; i < arguments.length; i++){ | |
items = items.concat(arguments[i].call(this)); | |
} | |
return items; | |
}, | |
and: function(){ | |
var items = this; | |
// TODO: use condition property | |
for(var i = 0; i < arguments.length; i++){ | |
items = arguments[i].call(items); | |
} | |
return items; | |
}, | |
select: function(first){ | |
if(arguments.length == 1){ | |
return this.map(function(object){ | |
return object[first]; | |
}); | |
} | |
var args = arguments; | |
return this.map(function(object){ | |
var selected = {}; | |
for(var i = 0; i < args.length; i++){ | |
var propertyName= args[i]; | |
if(object.hasOwnProperty(propertyName)){ | |
selected[propertyName] = object[propertyName]; | |
} | |
} | |
return selected; | |
}); | |
}, | |
slice: function(){ | |
return this.slice.apply(this, arguments); | |
}, | |
distinct: function(){ | |
var primitives = {}; | |
var needCleaning = []; | |
var newResults = this.filter(function(value){ | |
if(value && typeof value == "object"){ | |
if(!value.__found__){ | |
value.__found__ = function(){};// get ignored by JSON serialization | |
needCleaning.push(value); | |
return true; | |
} | |
}else{ | |
if(!primitives[value]){ | |
primitives[value] = true; | |
return true; | |
} | |
} | |
}); | |
needCleaning.forEach(function(object){ | |
delete object.__found__; | |
}); | |
return newResults; | |
}, | |
recurse: function(property){ | |
// TODO: this needs to use lazy-array | |
var newResults = []; | |
function recurse(value){ | |
if(value instanceof Array){ | |
value.forEach(recurse); | |
}else{ | |
newResults.push(value); | |
if(property){ | |
value = value[property]; | |
if(value && typeof value == "object"){ | |
recurse(value); | |
} | |
}else{ | |
for(var i in value){ | |
if(value[i] && typeof value[i] == "object"){ | |
recurse(value[i]); | |
} | |
} | |
} | |
} | |
} | |
recurse(this); | |
return newResults; | |
}, | |
aggregate: function(){ | |
var distinctives = []; | |
var aggregates = []; | |
for(var i = 0; i < arguments.length; i++){ | |
var arg = arguments[i]; | |
if(typeof arg === "function"){ | |
aggregates.push(arg); | |
}else{ | |
distinctives.push(arg); | |
} | |
} | |
var distinctObjects = {}; | |
var dl = distinctives.length; | |
this.forEach(function(object){ | |
var key = ""; | |
for(var i = 0; i < dl;i++){ | |
key += '/' + object[distinctives[i]]; | |
} | |
var arrayForKey = distinctObjects[key]; | |
if(!arrayForKey){ | |
arrayForKey = distinctObjects[key] = []; | |
} | |
arrayForKey.push(object); | |
}); | |
var al = aggregates.length; | |
var newResults = []; | |
for(var key in distinctObjects){ | |
var arrayForKey = distinctObjects[key]; | |
var newObject = {}; | |
for(var i = 0; i < dl;i++){ | |
var property = distinctives[i]; | |
newObject[property] = arrayForKey[0][property]; | |
} | |
for(var i = 0; i < al;i++){ | |
var aggregate = aggregates[i]; | |
newObject[i] = aggregate.call(arrayForKey); | |
} | |
newResults.push(newObject); | |
} | |
return newResults; | |
}, | |
sum: reducer(function(a, b){ | |
return a + b; | |
}), | |
mean: function(property){ | |
return exports.operators.sum.call(this, property)/this.length; | |
}, | |
max: reducer(function(a, b){ | |
return Math.max(a, b); | |
}), | |
min: reducer(function(a, b){ | |
return Math.min(a, b); | |
}) | |
}; | |
exports.filter = filter; | |
function filter(condition){ | |
var filter = function(property){ | |
var args = arguments; | |
var filtered = []; | |
for(var i = 0, length = this.length; i < length; i++){ | |
var item = this[i]; | |
arguments[0] = evaluateProperty(item, property); | |
if(condition.apply(this, arguments)){ | |
filtered.push(item); | |
} | |
} | |
return filtered; | |
}; | |
filter.condition = condition; | |
return filter; | |
}; | |
function reducer(func){ | |
return function(property){ | |
if(property){ | |
return this.map(function(object){ | |
return object[property]; | |
}).reduce(func); | |
}else{ | |
return this.reduce(func); | |
} | |
} | |
} | |
exports.evaluateProperty = evaluateProperty; | |
function evaluateProperty(object, property){ | |
if(property.indexOf(".") === -1){ | |
return object[decodeURIComponent(property)]; | |
} | |
else{ | |
property.split(".").forEach(function(part){ | |
object = object[decodeURIComponent(part)]; | |
}); | |
return object; | |
} | |
}; | |
var conditionEvaluator = exports.conditionEvaluator = function(condition){ | |
var jsOperator = exports.jsOperatorMap[term.name]; | |
if(jsOperator){ | |
js += "(function(item){return item." + term[0] + jsOperator + "parameters[" + (index -1) + "][1];});"; | |
} | |
else{ | |
js += "operators['" + term.name + "']"; | |
} | |
return eval(js); | |
}; | |
exports.executeQuery = function(query, options, target){ | |
return exports.query(query, options, target); | |
} | |
exports.query = function(query, options, target){ | |
query = parseQuery(query, options && options.parameters); | |
function t(){} | |
t.prototype = exports.operators; | |
var operators = new t; | |
// inherit from exports.operators | |
for(var i in options.operators){ | |
operators[i] = options.operators[i]; | |
} | |
var parameters = options.parameters || []; | |
var js = ""; | |
function queryToJS(value){ | |
if(value && typeof value === "object" && !(value instanceof Array)){ | |
var jsOperator = exports.jsOperatorMap[value.name]; | |
if(jsOperator){ | |
return "(function(){var filtered = []; for(var i = 0, length = this.length; i < length; i++){var item = this[i];if(item." + value.args[0] + jsOperator + queryToJS(value.args[1]) + "){filtered.push(item);}} return filtered;})"; | |
}else{ | |
return "(function(){return operators['" + value.name + "'].call(this" + | |
(value && value.args && value.args.length > 0 ? (", " + value.args.map(queryToJS).join(",")) : "") + | |
")})"; | |
} | |
}else{ | |
return JSON.stringify(value); | |
} | |
} | |
var evaluator = eval("(function(target){return " + queryToJS(query) + ".call(target);})"); | |
if(options.start || options.end){ | |
var totalCount = results.length; | |
results = results.slice(options.start || 0, (options.end || Infinity) + 1); | |
results.totalCount = totalCount; | |
} | |
return target ? evaluator(target) : evaluator; | |
} | |
function throwMaxIterations(){ | |
throw new Error("Query has taken too much computation, and the user is not allowed to execute resource-intense queries. Increase maxIterations in your config file to allow longer running non-indexed queries to be processed."); | |
} | |
exports.maxIterations = 10000; | |
function stringToValue(string, parameters){ | |
var converter = exports.converters['default']; | |
if(string.charAt(0) === "$"){ | |
var param_index = parseInt(string.substring(1)) - 1; | |
return param_index >= 0 && parameters ? parameters[param_index] : undefined; | |
} | |
if(string.indexOf(":") > -1){ | |
var parts = string.split(":",2); | |
converter = exports.converters[parts[0]]; | |
if(!converter){ | |
throw new URIError("Unknown converter " + parts[0]); | |
} | |
string = parts[1]; | |
} | |
return converter(string); | |
}; | |
var autoConverted = exports.autoConverted = { | |
"true": true, | |
"false": false, | |
"null": null, | |
"undefined": undefined, | |
"Infinity": Infinity, | |
"-Infinity": -Infinity | |
}; | |
exports.converters = { | |
auto: function(string){ | |
if(autoConverted.hasOwnProperty(string)){ | |
return autoConverted[string]; | |
} | |
var number = +string; | |
if(isNaN(number) || number.toString() !== string){ | |
/* var isoDate = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(date); | |
if (isoDate) { | |
return new Date(Date.UTC(+isoDate[1], +isoDate[2] - 1, +isoDate[3], +isoDate[4], +isoDate[5], +isoDate[6])); | |
}*/ | |
string = decodeURIComponent(string); | |
if(exports.jsonQueryCompatible){ | |
if(string.charAt(0) == "'" && string.charAt(string.length-1) == "'"){ | |
return JSON.parse('"' + string.substring(1,string.length-1) + '"'); | |
} | |
} | |
return string; | |
} | |
return number; | |
}, | |
number: function(x){ | |
var number = +x; | |
if(isNaN(number)){ | |
throw new URIError("Invalid number " + number); | |
} | |
return number; | |
}, | |
epoch: function(x){ | |
var date = new Date(+x); | |
if (isNaN(date.getTime())) { | |
throw new URIError("Invalid date " + x); | |
} | |
return date; | |
}, | |
isodate: function(x){ | |
// four-digit year | |
var date = '0000'.substr(0,4-x.length)+x; | |
// pattern for partial dates | |
date += '0000-01-01T00:00:00Z'.substring(date.length); | |
return exports.converters.date(date); | |
}, | |
date: function(x){ | |
var isoDate = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(x); | |
if (isoDate) { | |
date = new Date(Date.UTC(+isoDate[1], +isoDate[2] - 1, +isoDate[3], +isoDate[4], +isoDate[5], +isoDate[6])); | |
}else{ | |
date = new Date(x); | |
} | |
if (isNaN(date.getTime())){ | |
throw new URIError("Invalid date " + x); | |
} | |
return date; | |
}, | |
"boolean": function(x){ | |
return x === "true"; | |
}, | |
string: function(string){ | |
return decodeURIComponent(string); | |
} | |
}; | |
// exports.converters["default"] can be changed to a different converter if you want | |
// a different default converter, for example: | |
// RQ = require("perstore/resource-query"); | |
// RQ.converters["default"] = RQ.converter.string; | |
exports.converters["default"] = exports.converters.auto; | |
try{ | |
var when = require("promise").when; | |
}catch(e){ | |
when = function(value, callback){callback(value)}; | |
} | |
exports.knownOperators = ["and", "or", "eq", "ne", "le", "lt", "gt", "ge", "sort", "in", "select", "contains"]; | |
exports.arrayMethods = ["forEach", "reduce", "map", "filter", "indexOf", "some", "every"]; | |
exports.Query = function(seed, params){ | |
if (seed) return exports.parseQuery(seed, params); | |
return new Query(); | |
}; | |
function Query(name){ | |
this.name = name || "and"; | |
this.args = []; | |
} | |
exports.Query.prototype = Query.prototype; | |
Query.prototype.toString = function(){ | |
return this.name === "and" ? | |
this.args.map(queryToString).join("&") : | |
queryToString(this); | |
}; | |
exports.updateQueryMethods = function(){ | |
exports.knownOperators.forEach(function(name){ | |
Query.prototype[name] = function(){ | |
var newQuery = new Query(); | |
newQuery.executor = this.executor; | |
var newTerm = new Query(name); | |
newTerm.args = Array.prototype.slice.call(arguments); | |
newQuery.args = this.args.concat([newTerm]); | |
return newQuery; | |
}; | |
}); | |
exports.arrayMethods.forEach(function(name){ | |
Query.prototype[name] = function(){ | |
var args = arguments; | |
return when(this.executor(this), function(results){ | |
return results[name].apply(results, args); | |
}); | |
}; | |
}); | |
}; | |
exports.updateQueryMethods(); | |
}, | |
"browser":function(require, exports, module) {console.log(require("perstore/resource-query")); | |
} | |
},[]);require.ensure&&require.ensure(["browser"], function(require){require("browser");}); |
dvv
commented
May 22, 2010
<script src="js/yabble.js"></script>
<script>
// Configure Yabble
require.setModuleRoot("lib/");
require.useScriptTags();
</script>
<script src="lib/browser.js"></script>
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment