three-codeeditor
Version:
codeeditor for three.js
1,449 lines (1,263 loc) • 244 kB
JavaScript
/*global window, process, global*/
;(function(Global) {
var globalInterfaceSpec = [
{action: "installMethods", target: "Array", sources: ["arr"], methods: ["from","genN","range","withN"]},
{action: "installMethods", target: "Array.prototype", sources: ["arr"], methods: ["all","any","batchify","clear","clone","collect","compact","delimWith","detect","doAndContinue","each","equals","filterByKey","findAll","first","flatten","forEachShowingProgress","grep","groupBy","groupByKey","histogram","include","inject","intersect","invoke","last","mapAsync", "mapAsyncSeries", "mask","max","min","mutableCompact","nestedDelay","partition","pluck","pushAll","pushAllAt","pushAt","pushIfNotIncluded","reMatches","reject","rejectByKey","remove","removeAt","replaceAt","rotate","shuffle","size","sortBy","sortByKey","sum","swap","toArray","toTuples","union","uniq","uniqBy","without","withoutAll","zip"], alias: [["select", "filter"],["find","detect"]]},
{action: "installMethods", target: "Date", sources: ["date"], methods: [/*"parse"*/]},
{action: "installMethods", target: "Date.prototype", sources: ["date"], methods: ["equals","format","relativeTo"]},
{action: "installMethods", target: "Function", sources: ["fun"], methods: ["fromString"]},
{action: "installMethods", target: "Function.prototype", sources: ["fun"], methods: [/*"addProperties",*/"addToObject","argumentNames","asScript","asScriptOf","binds","curry","delay","functionNames","localFunctionNames","getOriginal","getVarMapping","logCalls","logCompletion","logErrors","qualifiedMethodName","setProperty","traceCalls","wrap"]},
{action: "installMethods", target: "Number", sources: ["num"], methods: []},
{action: "installMethods", target: "Number.prototype", sources: ["num"], methods: ["detent","randomSmallerInteger","roundTo","toDegrees","toRadians"]},
{action: "installMethods", target: "Object", sources: ["obj"], methods: ["addScript","clone","deepCopy","extend","inherit","isArray","isBoolean","isElement","isEmpty","isFunction","isNumber","isObject","isRegExp","isString","isUndefined","merge","mergePropertyInHierarchy","values","valuesInPropertyHierarchy"]},
{action: "installMethods", target: "Object.prototype", sources: ["obj"], methods: []},
{action: "installMethods", target: "String.prototype", sources: ["string"], methods: ["camelize","capitalize","digitValue","empty","endsWith","hashCode","include","pad","regExpEscape","startsWith","startsWithVowel","succ","times","toArray","toQueryParams","truncate"]},
{action: "installMethods", target: "Function.prototype", sources: ["class"], methods: ["create","addMethods","isSubclassOf","superclasses","categoryNameFor","remove"], alias: [["subclass", "create"]]},
{action: "installObject", target: "Numbers", source: "num", methods: ["average","between","convertLength","humanReadableByteSize","median","normalRandom","parseLength","random","sort"]},
{action: "installObject", target: "Properties", source: "properties", methods: ["all","allOwnPropertiesOrFunctions","allProperties","any","forEachOwn","hash","nameFor","own","ownValues","values"]},
{action: "installObject", target: "Strings", source: "string", methods: ["camelCaseString","createDataURI","diff","format","formatFromArray","indent","lineIndexComputer","lines","md5","newUUID","nonEmptyLines","pad","paragraphs","peekLeft","peekRight","print","printNested","printTable","printTree","quote","reMatches","removeSurroundingWhitespaces","stringMatch","tableize","tokens","unescapeCharacterEntities","withDecimalPrecision"]},
{action: "installObject", target: "Objects", source: "obj", methods: ["asObject", "equals","inspect","isMutableType","safeToString","shortPrintStringOf","typeStringOf"]},
{action: "installObject", target: "Functions", source: "fun", methods: ["all","compose","composeAsync","createQueue","debounce","debounceNamed","either","extractBody","flip","notYetImplemented","once","own","throttle","throttleNamed","timeToRun","timeToRunN","waitFor","workerWithCallbackQueue","wrapperChain"]},
{action: "installObject", target: "Grid", source: "grid"},
{action: "installObject", target: "Interval", source: "interval"},
{action: "installObject", target: "lively.ArrayProjection", source: "arrayProjection"},
{action: "installObject", target: "lively.Closure", source: "Closure"},
{action: "installObject", target: "lively.Grouping", source: "Group"},
{action: "installObject", target: "lively.PropertyPath", source: "Path"},
{action: "installObject", target: "lively.Worker", source: "worker"},
{action: "installObject", target: "lively.Class", source: "classHelper"}
];
var isNode = typeof process !== 'undefined'
&& process.versions && process.versions.node;
var livelyLang = createLivelyLangObject();
if (isNode) module.exports = livelyLang;
else {
livelyLang._prevLivelyGlobal = Global.lively;
if (!Global.lively) Global.lively = {};
if (!Global.lively.lang) Global.lively.lang = livelyLang;
else {
for (var name in livelyLang)
Global.lively.lang[name] = livelyLang[name];
}
}
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
function createLivelyLangObject() {
return {
chain: chain,
noConflict: noConflict,
installGlobals: installGlobals,
uninstallGlobals: uninstallGlobals,
globalInterfaceSpec: globalInterfaceSpec,
deprecatedLivelyPatches: deprecatedLivelyPatches
};
}
function chain(object) {
if (!object) return object;
var chained;
if (Array.isArray(object)) return createChain(livelyLang.arr, object);
if (object.constructor.name === "Date") return createChain(livelyLang.date, object);
switch (typeof object) {
case 'string': return createChain(livelyLang.string, object);
case 'object': return createChain(livelyLang.obj, object);
case 'function': return createChain(livelyLang.fun, object);
case 'number': return createChain(livelyLang.num, object);
}
throw new Error("Chain for object " + object + " (" + object.constructor.name + ") no supported");
}
function createChain(interfaceObj, obj) {
return Object.keys(interfaceObj).reduce(function(chained, methodName) {
chained[methodName] = function(/*args*/) {
var args = Array.prototype.slice.call(arguments),
result = interfaceObj[methodName].apply(null, [obj].concat(args));
return chain(result);
}
return chained;
}, {value: function() { return obj; }});
}
function noConflict() {
if (!isNode) {
var keepLivelyNS = livelyLang._prevLivelyGlobal;
if (!keepLivelyNS) delete Global.lively
else delete Global.lively.lang
}
return livelyLang;
}
function installGlobals() {
globalInterfaceSpec.forEach(function(ea) {
if (ea.action === "installMethods") {
var targetPath = livelyLang.Path(ea.target);
if (!targetPath.isIn(Global)) targetPath.set(Global, {}, true);
var sourcePath = livelyLang.Path(ea.sources[0]);
ea.methods.forEach(function(name) {
installProperty(
sourcePath.concat([name]),
targetPath.concat([name]));
});
if (ea.alias)
ea.alias.forEach(function(mapping) {
installProperty(
sourcePath.concat([mapping[1]]),
targetPath.concat([mapping[0]]));
});
} else if (ea.action === "installObject") {
var targetPath = livelyLang.Path(ea.target);
var source = livelyLang.Path(ea.source).get(livelyLang);
targetPath.set(Global, source, true);
} else throw new Error("Cannot deal with global setup action: " + ea.action);
});
}
function installProperty(sourcePath, targetPath) {
if (!sourcePath.isIn(livelyLang)) {
var err = new Error("property not provided by lively.lang: " + sourcePath);
console.error(err.stack || err);
throw err;
}
var prop = sourcePath.get(livelyLang);
if (typeof prop === "function" && targetPath.slice(-2, -1).toString() === "prototype") {
var origFunc = prop;
prop = function(/*this and args*/) {
var args = Array.prototype.slice.call(arguments);
args.unshift(this);
return origFunc.apply(null, args);
};
prop.toString = function() { return origFunc.toString(); };
}
targetPath.set(Global, prop, true);
}
function uninstallGlobals() {
globalInterfaceSpec.forEach(function(ea) {
if (ea.action === "installMethods") {
var p = livelyLang.Path(ea.target)
var target = p.get(Global);
if (!target) return;
ea.methods.forEach(function(name) { delete target[name]; });
if (ea.alias)
ea.alias.forEach(function(mapping) { delete target[mapping[0]]; });
} else if (ea.action === "installObject") {
var p = livelyLang.Path(ea.target);
p.del(Global);
} else throw new Error("Cannot deal with global setup action: " + ea.action);
})
}
function deprecatedLivelyPatches() {
livelyLang.installGlobals();
Global.$A = Array.from;
// We need to redefine Function.evalJS here b/c the original definition is
// in a JS 'use strict' block. However, not all function sources we pass in
// #evalJS from Lively adhere to the strictness rules. To allow those
// functions for now we define the creator again outside of a strictness block.
Function.evalJS = livelyLang.fun.evalJS = function(src) { return eval(src); }
livelyLang.Path.type = livelyLang.PropertyPath;
livelyLang.Path.prototype.serializeExpr = function () {
// ignore-in-doc
return 'lively.PropertyPath(' + livelyLang.obj.inspect(this.parts()) + ')';
}
livelyLang.Closure.type = "lively.Closure";
livelyLang.fun.methodChain = livelyLang.fun.wrapperChain;
if (typeof JSON !== "undefined") JSON.prettyPrint = function(jso) { return JSON.stringify(jso, null, 2); };
Global.NativeArrayFunctions = livelyLang.arrNative;
}
})(typeof window !== "undefined" ? window : global);
;/*global process, require*/
/*
* A simple node.js-like cross-platform event emitter implementation.
*/
;(function(exports) {
"use strict";
var isNode = typeof process !== 'undefined' && process.versions && process.versions.node;
// A simple node.js-like cross-platform event emitter implementation that can
// be used as a mixin. Emitters support the methods: `on(eventName, handlerFunc)`,
// `once(eventName, handlerFunc)`, `emit(eventName, eventData)`,
// `removeListener(eventName, handlerFunc)`, `removeAllListeners(eventName)`
// Example:
// var emitter = events.makeEmitter({});
// var log = [];
// emitter.on("test", function() { log.push("listener1"); });
// emitter.once("test", function() { log.push("listener2"); });
// emitter.emit("test");
// emitter.emit("test");
// log // => ["listener1","listener2","listener1"]
// emitter.removeAllListeners("test");
// emitter.emit("test");
// log // => is still ["listener1","listener2","listener1"]
var events = exports.events = {
makeEmitter: isNode ? function(obj) {
if (obj.on && obj.removeListener) return obj;
var events = require("events");
require("util")._extend(obj, events.EventEmitter.prototype);
events.EventEmitter.call(obj);
return obj;
} : function(obj) {
if (obj.on && obj.removeListener) return obj;
obj.listeners = {};
obj.on = function(type, handler) {
if (!handler) return;
if (!obj.listeners[type]) obj.listeners[type] = [];
obj.listeners[type].push(handler);
}
obj.once = function(type, handler) {
if (!handler) return;
function onceHandler(/*ignore-in-docs args*/) {
obj.removeListener(type, onceHandler);
handler.apply(this, arguments);
}
obj.on(type, onceHandler);
}
obj.removeListener = function(type, handler) {
if (!obj.listeners[type]) return;
obj.listeners[type] = obj.listeners[type].filter(function(h) {
return h !== handler; });
}
obj.removeAllListeners = function(type) {
if (!obj.listeners[type]) return;
obj.listeners[type] = [];
}
obj.emit = function(/*type and args*/) {
var args = Array.prototype.slice.call(arguments);
var type = args.shift();
var handlers = obj.listeners[type];
if (!handlers || !handlers.length) return;
handlers.forEach(function(handler) {
try { handler.apply(null, args) } catch (e) {
console.error("Error in event handler: %s", e.stack || String(e));
}
});
}
return obj;
}
};
})(typeof lively !== 'undefined' && lively.lang ? lively.lang : require('./base'));
;/*global*/
/*
* Utility functions that help to inspect, enumerate, and create JS objects
*/
;(function(exports) {
"use strict";
// -=-=-=-=-=-=-=-=-
// internal helper
// -=-=-=-=-=-=-=-=-
// serveral methods in lib/object.js are inspired or derived from
// Prototype JavaScript framework, version 1.6.0_rc1
// (c) 2005-2007 Sam Stephenson
// Prototype is freely distributable under the terms of an MIT-style license.
// For details, see the Prototype web site: http://www.prototypejs.org/
function print(object) {
if (object && obj.isArray(object)) { return '[' + object.map(print) + ']'; }
if (typeof object !== "string") { return String(object); }
var result = String(object);
result = result.replace(/\n/g, '\\n\\\n');
result = result.replace(/(")/g, '\\$1');
result = '\"' + result + '\"';
return result;
}
function argumentNames(func) {
if (func.superclass) return [];
var names = func.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1].
replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '').
replace(/\s+/g, '').split(',');
return names.length == 1 && !names[0] ? [] : names;
}
function indent(str, indentString, depth) {
if (!depth || depth <= 0) return str;
while (depth > 0) { depth--; str = indentString + str; }
return str;
}
// show-in-doc
var obj = exports.obj = {
// -=-=-=-=-
// testing
// -=-=-=-=-
isArray: function(obj) { /*show-in-doc*/ return obj && Array.isArray(obj); },
isElement: function(object) { /*show-in-doc*/ return object && object.nodeType == 1; },
isFunction: function(object) { /*show-in-doc*/ return object instanceof Function; },
isBoolean: function(object) { /*show-in-doc*/ return typeof object == "boolean"; },
isString: function(object) { /*show-in-doc*/ return typeof object == "string"; },
isNumber: function(object) { /*show-in-doc*/ return typeof object == "number"; },
isUndefined: function(object) { /*show-in-doc*/ return typeof object == "undefined"; },
isRegExp: function(object) { /*show-in-doc*/ return object instanceof RegExp; },
isObject: function(object) { /*show-in-doc*/ return typeof object == "object"; },
isEmpty: function(object) {
/*show-in-doc*/
for (var key in object)
if (object.hasOwnProperty(key)) return false;
return true;
},
equals: function(a, b) {
// Is object `a` structurally equivalent to object `b`? Deep comparison.
if (!a && !b) return true;
if (!a || !b) return false;
switch (a.constructor) {
case String:
case Date:
case Boolean:
case Number: return a == b;
};
if (typeof a.isEqualNode === "function") return a.isEqualNode(b);
if (typeof a.equals === "function") return a.equals(b);
return cmp(a, b) && cmp(b, a);
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
function cmp(left, right) {
for (var name in left) {
if (typeof left[name] === "function") continue;
if (!obj.equals(left[name], right[name])) return false;
}
return true;
}
},
// -=-=-=-=-=-
// accessing
// -=-=-=-=-=-
keys: Object.keys || function(object) {
// like Object.keys
var keys = [];
for (var property in object) keys.push(property);
return keys;
},
values: function(object) {
// Example:
// var obj1 = {x: 22}, obj2 = {x: 23, y: {z: 3}};
// obj2.__proto__ = obj1;
// obj.values(obj1) // => [22]
// obj.values(obj2) // => [23,{z: 3}]
return object ? Object.keys(object).map(function(k) { return object[k]; }) : [];
},
addScript: function (object, funcOrString, optName, optMapping) {
var func = exports.fun.fromString(funcOrString);
return exports.fun.asScriptOf(func, object, optName, optMapping);
},
// -=-=-=-=-
// mutation
// -=-=-=-=-
extend: function(destination, source) {
// Add all properties of `source` to `destination`.
// Example:
// var dest = {x: 22}, src = {x: 23, y: 24}
// obj.extend(dest, src);
// dest // => {x: 23,y: 24}
var currentCategoryNames = null;
for (var i = 1; i < arguments.length; i++) {
if (typeof arguments[i] == "string") {
var catName = arguments[i];
if (!destination.categories) destination.categories = {};
if (!destination.categories[catName]) destination.categories[catName] = [];
currentCategoryNames = destination.categories[catName];
continue;
}
var source = arguments[i];
for (var property in source) {
var getter = source.__lookupGetter__(property),
setter = source.__lookupSetter__(property);
if (getter) destination.__defineGetter__(property, getter);
if (setter) destination.__defineSetter__(property, setter);
if (getter || setter) continue;
var sourceObj = source[property];
destination[property] = sourceObj;
if (currentCategoryNames) currentCategoryNames.push(property);
if (typeof sourceObj === "function") {
if ((!sourceObj.name || (sourceObj.name.length == 0)) && !sourceObj.displayName) sourceObj.displayName = property;
// remember the module that contains the definition
if (typeof lively !== 'undefined' && lively.Module && lively.Module.current)
sourceObj.sourceModule = lively.Module.current();
}
}
}
return destination;
},
// -=-=-=-=-
// clone
// -=-=-=-=-
clone: function(object) {
// Shallow copy
return Array.isArray(object) ?
Array.prototype.slice.call(object) : exports.obj.extend({}, object);
},
extract: function(properties, object, mapFunc) {
return properties.reduce(function(extracted, name) {
if (object.hasOwnProperty(name)) {
var val = mapFunc ? mapFunc(name, object[name]) : object[name];
extracted[name] = val;
}
return extracted;
}, {});
},
// -=-=-=-=-=-
// inspection
// -=-=-=-=-=-
inspect: function inspect(object, options, depth) {
// Prints a human-readable representation of `obj`. The printed
// representation will be syntactically correct JavaScript but will not
// necessarily evaluate to a structurally identical object. `inspect` is
// meant to be used while interactivively exploring JavaScript programs and
// state.
//
// `options` can be {printFunctionSource: BOOLEAN, escapeKeys: BOOLEAN, maxDepth: NUMBER}
options = options || {};
depth = depth || 0;
if (!object) return print(object);
// print function
if (typeof object === 'function') {
return options.printFunctionSource ? String(object) :
'function' + (object.name ? ' ' + object.name : '')
+ '(' + argumentNames(object).join(',') + ') {/*...*/}';
}
// print "primitive"
switch (object.constructor) {
case String:
case Boolean:
case RegExp:
case Number: return print(object);
};
if (typeof object.serializeExpr === 'function')
return object.serializeExpr();
var isArray = object && Array.isArray(object),
openBr = isArray ? '[' : '{', closeBr = isArray ? ']' : '}';
if (options.maxDepth && depth >= options.maxDepth)
return openBr + '/*...*/' + closeBr;
var printedProps = [];
if (isArray) {
printedProps = object.map(function(ea) { return inspect(ea, options, depth); });
} else {
printedProps = Object.keys(object)
.sort(function(a, b) {
var aIsFunc = typeof object[a] === 'function',
bIsFunc = typeof object[b] === 'function';
if (aIsFunc === bIsFunc) {
if (a < b) return -1;
if (a > b) return 1;
return 0;
}
return aIsFunc ? 1 : -1;
})
.map(function(key, i) {
if (isArray) inspect(object[key], options, depth + 1);
var printedVal = inspect(object[key], options, depth + 1);
return options.escapeKeys ?
Strings.print(key) : key + ": " + printedVal;
});
}
if (printedProps.length === 0) { return openBr + closeBr; }
var printedPropsJoined = printedProps.join(','),
useNewLines = !isArray
&& (!options.minLengthForNewLine
|| printedPropsJoined.length >= options.minLengthForNewLine),
ind = indent('', options.indent || ' ', depth),
propIndent = indent('', options.indent || ' ', depth + 1),
startBreak = useNewLines ? '\n' + propIndent: '',
endBreak = useNewLines ? '\n' + ind : '';
if (useNewLines) printedPropsJoined = printedProps.join(',' + startBreak);
return openBr + startBreak + printedPropsJoined + endBreak + closeBr;
},
// -=-=-=-=-
// merging
// -=-=-=-=-
merge: function(objs) {
// `objs` can be a list of objects. The return value will be a new object,
// containing all properties of all objects. If the same property exist in
// multiple objects, the right-most property takes precedence.
//
// Like `extend` but will not mutate objects in `objs`.
// if objs are arrays just concat them
// if objs are real objs then merge propertdies
if (arguments.length > 1) {
return obj.merge(Array.prototype.slice.call(arguments));
}
if (Array.isArray(objs[0])) { // test for all?
return Array.prototype.concat.apply([], objs);
}
return objs.reduce(function(merged, ea) {
for (var name in ea)
if (ea.hasOwnProperty(name))
merged[name] = ea[name];
return merged;
}, {});
},
// -=-=-=-=-=-=-
// inheritance
// -=-=-=-=-=-=-
inherit: function(obj) { return Object.create(obj); },
valuesInPropertyHierarchy: function(obj, name) {
// Lookup all properties named name in the proto hierarchy of obj.
// Example:
// var a = {foo: 3}, b = Object.create(a), c = Object.create(b);
// c.foo = 4;
// obj.valuesInPropertyHierarchy(c, "foo") // => [3,4]
var result = [], lookupObj = obj;
while (lookupObj) {
if (lookupObj.hasOwnProperty(name)) result.unshift(lookupObj[name])
lookupObj = Object.getPrototypeOf(lookupObj);
}
return result;
},
mergePropertyInHierarchy: function(obj, propName) {
// like `merge` but automatically gets all definitions of the value in the
// prototype chain and merges those.
// Example:
// var o1 = {x: {foo: 23}}, o2 = {x: {foo: 24, bar: 15}}, o3 = {x: {baz: "zork"}};
// o2.__proto__ = o1; o3.__proto__ = o2;
// obj.mergePropertyInHierarchy(o3, "x");
// // => {bar: 15, baz: "zork",foo: 24}
return this.merge(this.valuesInPropertyHierarchy(obj, propName));
},
deepCopy: function (object) {
// Recursively traverses `object` and its properties to create a copy.
if (!object || typeof object !== "object") return object;
var result = Array.isArray(object) ? Array(object.length) : {};
for (var key in object) {
if (object.hasOwnProperty(key))
result[key] = obj.deepCopy(object[key]);
}
return result;
},
// -=-=-=-=-=-=-=-=-
// stringification
// -=-=-=-=-=-=-=-=-
typeStringOf: function(obj) {
// ignore-in-doc
if (obj === null) return "null";
if (typeof obj === "undefined") return "undefined";
return obj.constructor.name;
},
shortPrintStringOf: function(obj) {
// ignore-in-doc
// primitive values
if (!this.isMutableType(obj)) return this.safeToString(obj);
// constructed objects
if (obj.constructor.name !== 'Object' && !Array.isArray(obj)) {
if(obj.constructor.name)
return obj.constructor.name ?
obj.constructor.name :
Object.prototype.toString.call(obj).split(" ")[1].split("]")[0];
}
// arrays or plain objects
var typeString = "";
function displayTypeAndLength(obj, collectionType, firstBracket, secondBracket) {
if (obj.constructor.name === collectionType) {
typeString += firstBracket;
if (obj.length || Object.keys(obj).length) typeString += "...";
typeString += secondBracket;
}
}
displayTypeAndLength(obj, "Object", "{", "}");
displayTypeAndLength(obj, "Array", "[", "]");
return typeString;
},
isMutableType: function(obj) {
// Is `obj` a value or mutable type?
var immutableTypes = ["null", "undefined", "Boolean", "Number", "String"];
return immutableTypes.indexOf(this.typeStringOf(obj)) === -1;
},
safeToString: function(obj) {
// Like `toString` but catches errors.
try {
return (obj ? obj.toString() : String(obj)).replace('\n','');
} catch (e) { return '<error printing object>'; }
},
asObject: function(obj) {
switch (typeof obj) {
case 'string':
return new String(obj);
case 'boolean':
return new Boolean(obj);
case 'number':
return new Number(obj);
default:
return obj;
}
}
};
// ignore-in-doc
// -=-=-=-=-=-
// properties
// -=-=-=-=-=-
var properties = exports.properties = {
all: function(object, predicate) {
// ignore-in-doc
var a = [];
for (var name in object) {
if ((object.__lookupGetter__(name) || typeof object[name] !== 'function')
&& (predicate ? predicate(name, object) : true))
a.push(name);
}
return a;
},
allOwnPropertiesOrFunctions: function(obj, predicate) {
// ignore-in-doc
return Object.getOwnPropertyNames(obj).reduce(function(result, name) {
if (predicate ? predicate(obj, name) : true) result.push(name);
return result;
}, []);
},
own: function(object) {
// ignore-in-doc
var a = [];
for (var name in object) {
if (object.hasOwnProperty(name) && (object.__lookupGetter__(name)
|| object[name] !== 'function'))
a.push(name);
}
return a;
},
forEachOwn: function(object, func, context) {
// ignore-in-doc
var result = [];
for (var name in object) {
if (!object.hasOwnProperty(name)) continue;
var value = object[name];
if (value !== 'function') {
result.push(func.call(context || this, name, value));
}
}
return result;
},
nameFor: function(object, value) {
// ignore-in-doc
for (var name in object) {
if (object[name] === value) return name;
}
return undefined;
},
values: function(obj) {
// ignore-in-doc
var values = [];
for (var name in obj) values.push(obj[name]);
return values;
},
ownValues: function(obj) {
// ignore-in-doc
var values = [];
for (var name in obj) {
if (obj.hasOwnProperty(name)) values.push(obj[name]);
}
return values;
},
any: function(obj, predicate) {
// ignore-in-doc
for (var name in obj) {
if (predicate(obj, name)) return true;
}
return false;
},
allProperties: function(obj, predicate) {
// ignore-in-doc
var result = [];
for (var name in obj) {
if (predicate ? predicate(obj, name) : true)
result.push(name);
}
return result;
},
hash: function(obj) {
// ignore-in-doc
// Using the property names of `obj` to generate a hash value.
return Object.keys(obj).sort().join('').hashCode();
}
};
// -=-=-=-=-=-=-=-=-=-=-=-=-=-
// js object path accessor
// -=-=-=-=-=-=-=-=-=-=-=-=-=-
// A `Path` is an objectified chain of property names (kind of a "complex"
// getter and setter). Path objects can make access and writes into deeply nested
// structures more convenient. `Path` provide "safe" get and set operations and
// can be used for debugging by providing a hook that allows users to find out
// when get/set operations happen.
var Path = exports.Path = function Path(p, splitter) {
if (p instanceof Path) return p;
if (!(this instanceof Path)) return new Path(p, splitter);
if (splitter) this.setSplitter(splitter);
return this.fromPath(p);
}
obj.extend(Path, {
superclass: Object,
type: 'Path',
categories: {}
});
obj.extend(Path.prototype, {
isPathAccessor: true,
splitter: '.',
fromPath: function(path) {
// ignore-in-doc
if (obj.isString(path) && path !== '' && path !== this.splitter) {
this._parts = path.split(this.splitter);
this._path = path;
} else if (obj.isArray(path)) {
this._parts = [].concat(path);
this._path = path.join(this.splitter);
} else {
this._parts = [];
this._path = '';
}
return this;
},
setSplitter: function(splitter) {
// ignore-in-doc
if (splitter) this.splitter = splitter;
return this;
},
parts: function() { /*key names as array*/ return this._parts; },
size: function() { /*show-in-doc*/ return this._parts.length; },
slice: function(n, m) { /*show-in-doc*/ return Path(this.parts().slice(n, m)); },
normalizePath: function() {
// ignore-in-doc
// FIXME: define normalization
return this._path;
},
isRoot: function(obj) { return this._parts.length === 0; },
isIn: function(obj) {
// Does the Path resolve to a value when applied to `obj`?
if (this.isRoot()) return true;
var parent = this.get(obj, -1);
return parent && parent.hasOwnProperty(this._parts[this._parts.length-1]);
},
equals: function(obj) {
// Example:
// var p1 = Path("foo.1.bar.baz"), p2 = Path(["foo", 1, "bar", "baz"]);
// // Path's can be both created via strings or pre-parsed with keys in a list.
// p1.equals(p2) // => true
return obj && obj.isPathAccessor && this.parts().equals(obj.parts());
},
isParentPathOf: function(otherPath) {
// Example:
// var p1 = Path("foo.1.bar.baz"), p2 = Path("foo.1.bar");
// p2.isParentPathOf(p1) // => true
// p1.isParentPathOf(p2) // => false
otherPath = otherPath && otherPath.isPathAccessor ?
otherPath : Path(otherPath);
var parts = this.parts(),
otherParts = otherPath.parts();
for(var i = 0; i < parts.length; i ++) {
if (parts[i] != otherParts[i]) return false
}
return true
},
relativePathTo: function(otherPath) {
// Example:
// var p1 = Path("foo.1.bar.baz"), p2 = Path("foo.1");
// p2.relativePathTo(p1) // => Path(["bar","baz"])
// p1.relativePathTo(p2) // => undefined
otherPath = Path(otherPath);
return this.isParentPathOf(otherPath) ?
otherPath.slice(this.size(), otherPath.size()) : undefined;
},
del: function(obj) {
if (this.isRoot()) return false;
var parent = obj
for (var i = 0; i < this._parts.length-1; i++) {
var part = this._parts[i];
if (parent.hasOwnProperty(part)) {
parent = parent[part];
} else return false;
}
return delete parent[this._parts[this._parts.length-1]];
},
set: function(obj, val, ensure) {
// Deeply resolve path in `obj` and set the resulting property to `val`. If
// `ensure` is true, create nested structure in between as necessary.
// Example:
// var o1 = {foo: {bar: {baz: 42}}};
// var path = Path("foo.bar.baz");
// path.set(o1, 43)
// o1 // => {foo: {bar: {baz: 43}}}
// var o2 = {foo: {}};
// path.set(o2, 43, true)
// o2 // => {foo: {bar: {baz: 43}}}
if (this.isRoot()) return undefined;
var parent = obj
for (var i = 0; i < this._parts.length-1; i++) {
var part = this._parts[i];
if (parent.hasOwnProperty(part) && (typeof parent[part] === "object" || typeof parent[part] === "function")) {
parent = parent[part];
} else if (ensure) {
parent = parent[part] = {};
} else {
return undefined;
}
}
return parent[this._parts[this._parts.length-1]] = val;
},
get: function(obj, n) {
// show-in-doc
var parts = n ? this._parts.slice(0, n) : this._parts;
return parts.reduce(function(current, pathPart) {
return current ? current[pathPart] : current; }, obj);
},
concat: function(p, splitter) {
// show-in-doc
return Path(this.parts().concat(Path(p, splitter).parts()));
},
toString: function() { return this.normalizePath(); },
serializeExpr: function() {
// ignore-in-doc
return 'Path(' + Objects.inspect(this.parts()) + ')';
},
watch: function(options) {
// React or be notified on reads or writes to a path in a `target`. Options:
// ```js
// {
// target: OBJECT,
// uninstall: BOOLEAN,
// onGet: FUNCTION,
// onSet: FUNCTION,
// haltWhenChanged: BOOLEAN,
// verbose: BOOLEAN
// }
// ```
// Example:
// // Quite useful for debugging to find out what call-sites change an object.
// var o = {foo: {bar: 23}};
// Path("foo.bar").watch({target: o, verbose: true});
// o.foo.bar = 24; // => You should see: "[object Object].bar changed: 23 -> 24"
if (!options || this.isRoot()) return;
var target = options.target,
parent = this.get(target, -1),
propName = exports.arr.last(this.parts()),
newPropName = 'propertyWatcher$' + propName,
watcherIsInstalled = parent && parent.hasOwnProperty(newPropName),
uninstall = options.uninstall,
haltWhenChanged = options.haltWhenChanged,
showStack = options.showStack,
getter = parent.__lookupGetter__(propName),
setter = parent.__lookupSetter__(propName);
if (!target || !propName || !parent) return;
if (uninstall) {
if (!watcherIsInstalled) return;
delete parent[propName];
parent[propName] = parent[newPropName];
delete parent[newPropName];
var msg = 'Watcher for ' + parent + '.' + propName + ' uninstalled';
show(msg);
return;
}
if (watcherIsInstalled) {
var msg = 'Watcher for ' + parent + '.' + propName + ' already installed';
show(msg);
return;
}
if (getter || setter) {
var msg = parent + '["' + propName + '"] is a getter/setter, watching not support';
console.log(msg);
if (typeof show === "undefined") show(msg);
return;
}
// observe slots, for debugging
parent[newPropName] = parent[propName];
parent.__defineSetter__(propName, function(v) {
var oldValue = parent[newPropName];
if (options.onSet) options.onSet(v, oldValue);
var msg = parent + "." + propName + " changed: " + oldValue + " -> " + v;
if (showStack) msg += '\n' + (typeof lively !== "undefined" ?
lively.printStack() : console.trace());
if (options.verbose) {
console.log(msg);
if (typeof show !== 'undefined') show(msg);
}
if (haltWhenChanged) debugger;
return parent[newPropName] = v;
});
parent.__defineGetter__(propName, function() {
if (options.onGet) options.onGet(parent[newPropName]);
return parent[newPropName];
});
var msg = 'Watcher for ' + parent + '.' + propName + ' installed';
console.log(msg);
if (typeof show !== 'undefined') show(msg);
},
debugFunctionWrapper: function(options) {
// ignore-in-doc
// options = {target, [haltWhenChanged, showStack, verbose, uninstall]}
var target = options.target,
parent = this.get(target, -1),
funcName = this.parts().last(),
uninstall = options.uninstall,
haltWhenChanged = options.haltWhenChanged === undefined ? true : options.haltWhenChanged,
showStack = options.showStack,
func = parent && funcName && parent[funcName],
debuggerInstalled = func && func.isDebugFunctionWrapper;
if (!target || !funcName || !func || !parent) return;
if (uninstall) {
if (!debuggerInstalled) return;
parent[funcName] = parent[funcName].debugTargetFunction;
var msg = 'Uninstalled debugFunctionWrapper for ' + parent + '.' + funcName;
console.log(msg);
if (typeof show !== 'undefined') show(msg);
show(msg);
return;
}
if (debuggerInstalled) {
var msg = 'debugFunctionWrapper for ' + parent + '.' + funcName + ' already installed';
console.log(msg);
if (typeof show !== 'undefined') show(msg);
return;
}
var debugFunc = parent[funcName] = func.wrap(function(proceed) {
var args = Array.from(arguments);
if (haltWhenChanged) debugger;
if (showStack) show(lively.printStack());
if (options.verbose) show(funcName + ' called');
return args.shift().apply(parent, args);
});
debugFunc.isDebugFunctionWrapper = true;
debugFunc.debugTargetFunction = func;
var msg = 'debugFunctionWrapper for ' + parent + '.' + funcName + ' installed';
console.log(msg);
if (typeof show !== 'undefined') show(msg);
}
});
})(typeof lively !== 'undefined' && lively.lang ? lively.lang : require('./base'));
;
/*
* Methods to make working with arrays more convenient and collection-like
* abstractions for groups, intervals, grids.
*/
;(function(exports) {
"use strict";
// Pure JS implementations of native Array methods.
var arrNative = exports.arrNative = {
sort: function(sortFunc) {
// show-in-doc
if (!sortFunc) {
sortFunc = function(x,y) {
if (x < y) return -1;
if (x > y) return 1;
return 0;
};
}
var len = this.length, sorted = [];
for (var i = 0; i < this.length; i++) {
var inserted = false;
for (var j = 0; j < sorted.length; j++) {
if (1 === sortFunc(sorted[j], this[i])) {
inserted = true;
sorted[j+1] = sorted[j];
sorted[j] = this[i];
break;
}
}
if (!inserted) sorted.push(this[i]);
}
return sorted;
},
filter: function(iterator, context) {
// show-in-doc
var results = [];
for (var i = 0; i < this.length; i++) {
if (!this.hasOwnProperty(i)) continue;
var value = this[i];
if (iterator.call(context, value, i)) results.push(value);
}
return results;
},
forEach: function(iterator, context) {
// show-in-doc
for (var i = 0, len = this.length; i < len; i++) {
iterator.call(context, this[i], i, this); }
},
some: function(iterator, context) {
// show-in-doc
return this.detect(iterator, context) !== undefined;
},
every: function(iterator, context) {
// show-in-doc
var result = true;
for (var i = 0, len = this.length; i < len; i++) {
result = result && !! iterator.call(context, this[i], i);
if (!result) break;
}
return result;
},
map: function(iterator, context) {
// show-in-doc
var results = [];
this.forEach(function(value, index) {
results.push(iterator.call(context, value, index));
});
return results;
},
reduce: function(iterator, memo, context) {
// show-in-doc
var start = 0;
if (!arguments.hasOwnProperty(1)) { start = 1; memo = this[0]; }
for (var i = start; i < this.length; i++)
memo = iterator.call(context, memo, this[i], i, this);
return memo;
},
reduceRight: function(iterator, memo, context) {
// show-in-doc
var start = this.length-1;
if (!arguments.hasOwnProperty(1)) { start--; memo = this[this.length-1]; }
for (var i = start; i >= 0; i--)
memo = iterator.call(context, memo, this[i], i, this);
return memo;
}
};
// variety of functions for Arrays
var arr = exports.arr = {
// -=-=-=-=-=-=-=-
// array creations
// -=-=-=-=-=-=-=-
range: function(begin, end, step) {
// Examples:
// arr.range(0,5) // => [0,1,2,3,4,5]
// arr.range(0,10,2) // => [0,2,4,6,8,10]
step = step || 1
var result = [];
for (var i = begin; i <= end; i += step)
result.push(i);
return result;
},
from: function(iterable) {
// Makes JS arrays out of array like objects like `arguments` or DOM `childNodes`
if (!iterable) return [];
if (Array.isArray(iterable)) return iterable;
if (iterable.toArray) return iterable.toArray();
var length = iterable.length,
results = new Array(length);
while (length--) results[length] = iterable[length];
return results;
},
withN: function(n, obj) {
// Example:
// arr.withN(3, "Hello") // => ["Hello","Hello","Hello"]
var result = new Array(n);
while (n > 0) result[--n] = obj;
return result;
},
genN: function(n, generator) {
// Number -> Function -> Array
// Takes a generator function that is called for each `n`.
// Example:
// arr.genN(3, num.random) // => [46,77,95]
var result = new Array(n);
while (n > 0) result[--n] = generator(n);
return result;
},
// -=-=-=-=-
// filtering
// -=-=-=-=-
filter: function(array, iterator, context) {
// [a] -> (a -> Boolean) -> c? -> [a]
// Calls `iterator` for each element in `array` and returns a subset of it
// including the elements for which `iterator` returned a truthy value.
// Like `Array.prototype.filter`.
return array.filter(iterator, context);
},
detect: function(arr, iterator, context) {
// [a] -> (a -> Boolean) -> c? -> a
// returns the first occurrence of an element in `arr` for which iterator
// returns a truthy value
for (var value, i = 0, len = arr.length; i < len; i++) {
value = arr[i];
if (iterator.call(context, value, i)) return value;
}
return undefined;
},
filterByKey: function(arr, key) {
// [a] -> String -> [a]
// Example:
// var objects = [{x: 3}, {y: 4}, {x:5}]
// arr.filterByKey(objects, "x") // => [{x: 3},{x: 5}]
return arr.filter(function(ea) { return !!ea[key]; });
},
grep: function(arr, filter, context) {
// [a] -> String|RegExp -> [a]
// `filter` can be a String or RegExp. Will stringify each element in
// Example:
// ["Hello", "World", "Lively", "User"].grep("l") // => ["Hello","World","Lively"]
if (typeof filter === 'string') filter = new RegExp(filter, 'i');
return arr.filter(filter.test.bind(filter))
},
mask: function(array, mask) {
// select every element in array for which array's element is truthy
// Example: [1,2,3].mask([false, true, false]) => [2]
return array.filter(function(_, i) { return !!mask[i]; });
},
reject: function(array, func, context) {
// show-in-doc
function iterator(val, i) { return !func.call(context, val, i); }
return array.filter(iterator);
},
rejectByKey: function(array, key) {
// show-in-doc
return array.filter(function(ea) { return !ea[key]; });
},
without: function(array, elem) {
// non-mutating
// Example:
// arr.without([1,2,3,4,5,6], 3) // => [1,2,4,5,6]
return array.filter(function(value) { return value !== elem; });
},
withoutAll: function(array, otherArr) {
// non-mutating
// Example:
// arr.withoutAll([1,2,3,4,5,6], [3,4]) // => [1,2,5,6]
return array.filter(function(value) {
return otherArr.indexOf(value) === -1;
});
},
uniq: function(array, sorted) {
// non-mutating
// Removes duplicates from array.
return array.inject([], function(a, value, index) {
if (0 === index || (sorted ? a.last() != value : !a.include(value)))
a.push(value);
return a;
});
},
uniqBy: function(array, comparator, context) {
// like `arr.uniq` but with custom equality: `comparator(a,b)` returns
// BOOL. True if a and be should be regarded equal, false otherwise.
var result = arr.clone(array);
for (var i = 0; i < result.length; i++) {
var item = array[i];
for (var j = i+1; j < result.length; j++) {
if (comparator.call(context, item, result[j])) {
arr.removeAt(result, j); j--;
}
}
}
return result;
},
compact: function(array) {
// removes falsy values
// Example:
// arr.compact([1,2,undefined,4,0]) // => [1,2,4]
return array.filter(function(ea) { return !!ea; });
},
mutableCompact: function(array) {
// fix gaps that were created with 'delete'
var i = 0, j = 0, len = array.length;
while (i < len) {
if (array.hasOwnProperty(i)) array[j++] = array[i];
i++;
}
while (j++ < len) array.pop();
return array;
},
// -=-=-=-=-
// iteration
// -=-=-=-=-
forEach: function(array, iterator, context) {
// [a] -> (a -> Undefined) -> c? -> Undefined
// `iterator` is called on each element in `array` for side effects. Like
// `Array.prototype.forEach`.
return array.forEach(iterator, context);
},
zip: function(/*arr, arr2, arr3*/) {
// Takes any number of lists as arguments. Combines them elment-wise.
// Example:
// arr.zip([1,2,3], ["a", "b", "c"], ["A", "B"])
// // => [[1,"a","A"],[2,"b","B"],[3,"c",undefined]]
var args = arr.from(arguments),
array = args.shift(),
iterator = typeof arr.last(args) === 'function' ?
args.pop() : function(x) { return x; },
collections = [array].concat(args).map(arr.from);
return array.map(function(value, index) {
return iterator(arr.pluck(collections, index), index); });
},
flatten: function flatten(array) {
// Turns a nested collection into a flat one.
// Example:
// arr.flatten([1, [2, [3,4,5], [6]], 7,8])
// // => [1,2,3,4,5,6,7,8]
return array.reduce(function(flattened, value) {
return flattened.concat(Array.isArray(value) ?
flatten(value) : [value]);
}, []);
},
delimWith: function(array, delim) {
return array.reduce(function(xs, x) {
if (xs.length > 0) xs.push(delim)
xs.push(x); return xs;
}, []);
},
// -=-=-=-=-
// mapping
// -=-=-=-=-
map: function(array, iterator, context) {
// [a] -> (a -> b) -> c? -> [b]
// Applies `iterator` to each element of `array` and returns a new Array
// with the results of those calls. Like `Array.prototype.some`.
return array.map(iterator, context);
},
invoke: function(array, method, arg1, arg2, arg3, arg4, arg5, arg6) {
// Calls `method` on each element in `array`, passing all arguments. Often
// a handy way to avoid verbose `map` calls.
// Example: arr.invoke(["hello", "world"], "toUpperCase") // => ["HELLO","WORLD"]
return array.map(function(ea) {
return ea[method](arg1, arg2, arg3, arg4, arg5, arg6);
});
},
pluck: function(array, property) {
// Returns `property` or undefined from each element of array. For quick
// `map`s and similar to `invoke`.
// Example: arr.pluck(["hello", "world"], 0) // => ["h","w"]
return array.map(function(ea) { return ea[property]; });
},
// -=-=-=-=-
// folding
// -=-=-=-=-
reduce: function(array, iterator, memo, context) {
// Array -> Function -> Object? -> Object? -> Object?
// Applies `iterator` to each element of `array` and returns a new Array
// with the results of those calls. Like `Array.prototype.some`.
return array.reduce(iterator, memo, context);
},
reduceRight: function(array, iterator, memo, context) {
// show-in-doc
return array.reduceRight(iterator, memo, context);
},
// -=-=-=-=-
// testing
// -=-=-=-=-
isArray: Array.isArray,
include: function(array, object) {
// Example: arr.include([1,2,3], 2) // => true
return array.indexOf(object) !== -1;
},
some: function(array, iterator, context) {
// [a] -> (a -> Boolean) -> c? -> Boolean
// Returns true if there is at least one abject in `array` for which
// `iterator` returns a truthy result. Like `Array.prototype.some`.
return array.some(iterator, context);
},
every: function(array, iterator, context) {
// [a] -> (a -> Boolean) -> c? -> Boolean
// Returns true if for all abjects in `array` `iterator` returns a truthy
// result. Like `Array.prototype.every`.
return array.every(iterator, context);
},
equals: function(array, otherArray) {
// Returns true iff each element in `array` is equal (`==`) to its
// corresponding element in `otherArray`
var len = array.length;
if (!otherArray || len !== otherArray.length) return false;
for (var i = 0; i < len; i++) {
if (array[i] && otherArray[i] && array[i].equals && otherArray[i].equals) {
if (!array[i].equals(otherArray[i])) {
return false;
} else {
continue;
}
}
if (array[i] != otherArray[i]) return false;
}
return true;
},
// -=-=-=-=-
// sorting
// -=-=-=-=-
sort: function(array, sortFunc) {
// [a] -> (a -> Number)? -> [a]
// Just `Array.prototype.sort`
return array.sort(sortFunc);
},
sortBy: function(array, iterator, context) {
// Example:
// arr.sortBy(["Hello", "Lively", "User"], function(ea) {
// return ea.charCodeAt(ea.length-1); }) // => ["Hello","User","Lively"]
return arr.pluck(
array.map(function(value, index) {
return {value: value,criteria: iterator.call(context, value, index)};
}).sort(function(left, right) {
var a = left.criteria, b = right.criteria;
return a