From: dsc Date: Wed, 9 Feb 2011 23:32:49 +0000 (-0800) Subject: Checkpoint on unnecessary Inventory complexity. X-Git-Url: http://git.less.ly:3516/?a=commitdiff_plain;h=d85f01890440f5fb29ce6434221bf97711abadfe;p=tanks.git Checkpoint on unnecessary Inventory complexity. --- diff --git a/lib/jsparse.js b/lib/jsparse.js new file mode 100755 index 0000000..260c3c1 --- /dev/null +++ b/lib/jsparse.js @@ -0,0 +1,653 @@ +// Copyright (C) 2007 Chris Double. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +function identity(x) { + return x; +} + +function foldl(f, initial, seq) { + for(var i=0; i< seq.length; ++i) + initial = f(initial, seq[i]); + return initial; +} + +var memoize = true; + +function ParseState(input, index) { + this.input = input; + this.index = index || 0; + this.length = input.length - this.index; + this.cache = { }; + return this; +} + +ParseState.prototype.from = function(index) { + var r = new ParseState(this.input, this.index + index); + r.cache = this.cache; + r.length = this.length - index; + return r; +} + +ParseState.prototype.substring = function(start, end) { + return this.input.substring(start + this.index, (end || this.length) + this.index); +} + +ParseState.prototype.trimLeft = function() { + var s = this.substring(0); + var m = s.match(/^\s+/); + return m ? this.from(m[0].length) : this; +} + +ParseState.prototype.at = function(index) { + return this.input.charAt(this.index + index); +} + +ParseState.prototype.toString = function() { + return 'PS"' + this.substring(0) + '"'; +} + +ParseState.prototype.getCached = function(pid) { + if(!memoize) + return false; + + var p = this.cache[pid]; + if(p) + return p[this.index]; + else + return false; +} + +ParseState.prototype.putCached = function(pid, cached) { + if(!memoize) + return false; + + var p = this.cache[pid]; + if(p) + p[this.index] = cached; + else { + p = this.cache[pid] = { }; + p[this.index] = cached; + } +} + +function ps(str) { + return new ParseState(str); +} + +// 'r' is the remaining string to be parsed. +// 'matched' is the portion of the string that +// was successfully matched by the parser. +// 'ast' is the AST returned by the successfull parse. +function make_result(r, matched, ast) { + return { remaining: r, matched: matched, ast: ast }; +} + +var parser_id = 0; + +// 'token' is a parser combinator that given a string, returns a parser +// that parses that string value. The AST contains the string that was parsed. +function token(s) { + var pid = parser_id++; + return function(state) { + var savedState = state; + var cached = savedState.getCached(pid); + if(cached) + return cached; + + var r = state.length >= s.length && state.substring(0,s.length) == s; + if(r) + cached = { remaining: state.from(s.length), matched: s, ast: s }; + else + cached = false; + savedState.putCached(pid, cached); + return cached; + }; +} + +// Like 'token' but for a single character. Returns a parser that given a string +// containing a single character, parses that character value. +function ch(c) { + var pid = parser_id++; + return function(state) { + var savedState = state; + var cached = savedState.getCached(pid); + if(cached) + return cached; + var r = state.length >= 1 && state.at(0) == c; + if(r) + cached = { remaining: state.from(1), matched: c, ast: c }; + else + cached = false; + savedState.putCached(pid, cached); + return cached; + }; +} + +// 'range' is a parser combinator that returns a single character parser +// (similar to 'ch'). It parses single characters that are in the inclusive +// range of the 'lower' and 'upper' bounds ("a" to "z" for example). +function range(lower, upper) { + var pid = parser_id++; + return function(state) { + var savedState = state; + var cached = savedState.getCached(pid); + if(cached) + return cached; + + if(state.length < 1) + cached = false; + else { + var ch = state.at(0); + if(ch >= lower && ch <= upper) + cached = { remaining: state.from(1), matched: ch, ast: ch }; + else + cached = false; + } + savedState.putCached(pid, cached); + return cached; + }; +} + +// Helper function to convert string literals to token parsers +// and perform other implicit parser conversions. +function toParser(p) { + return (typeof(p) == "string") ? token(p) : p; +} + +// Parser combinator that returns a parser that +// skips whitespace before applying parser. +function whitespace(p) { + var p = toParser(p); + var pid = parser_id++; + return function(state) { + var savedState = state; + var cached = savedState.getCached(pid); + if(cached) + return cached; + + cached = p(state.trimLeft()); + savedState.putCached(pid, cached); + return cached; + }; +} + +// Parser combinator that passes the AST generated from the parser 'p' +// to the function 'f'. The result of 'f' is used as the AST in the result. +function action(p, f) { + var p = toParser(p); + var pid = parser_id++; + return function(state) { + var savedState = state; + var cached = savedState.getCached(pid); + if(cached) + return cached; + + var x = p(state); + if(x) { + x.ast = f(x.ast); + cached = x; + } + else { + cached = false; + } + savedState.putCached(pid, cached); + return cached; + }; +} + +// Given a parser that produces an array as an ast, returns a +// parser that produces an ast with the array joined by a separator. +function join_action(p, sep) { + return action(p, function(ast) { return ast.join(sep); }); +} + +// Given an ast of the form [ Expression, [ a, b, ...] ], convert to +// [ [ [ Expression [ a ] ] b ] ... ] +// This is used for handling left recursive entries in the grammar. e.g. +// MemberExpression: +// PrimaryExpression +// FunctionExpression +// MemberExpression [ Expression ] +// MemberExpression . Identifier +// new MemberExpression Arguments +function left_factor(ast) { + return foldl(function(v, action) { + return [ v, action ]; + }, + ast[0], + ast[1]); +} + +// Return a parser that left factors the ast result of the original +// parser. +function left_factor_action(p) { + return action(p, left_factor); +} + +// 'negate' will negate a single character parser. So given 'ch("a")' it will successfully +// parse any character except for 'a'. Or 'negate(range("a", "z"))' will successfully parse +// anything except the lowercase characters a-z. +function negate(p) { + var p = toParser(p); + var pid = parser_id++; + return function(state) { + var savedState = state; + var cached = savedState.getCached(pid); + if(cached) + return cached; + + if(state.length >= 1) { + var r = p(state); + if(!r) + cached = make_result(state.from(1), state.at(0), state.at(0)); + else + cached = false; + } + else { + cached = false; + } + savedState.putCached(pid, cached); + return cached; + }; +} + +// 'end_p' is a parser that is successful if the input string is empty (ie. end of parse). +function end_p(state) { + if(state.length == 0) + return make_result(state, undefined, undefined); + else + return false; +} + +// 'nothing_p' is a parser that always fails. +function nothing_p(state) { + return false; +} + +// 'sequence' is a parser combinator that processes a number of parsers in sequence. +// It can take any number of arguments, each one being a parser. The parser that 'sequence' +// returns succeeds if all the parsers in the sequence succeeds. It fails if any of them fail. +function sequence() { + var parsers = []; + for(var i = 0; i < arguments.length; ++i) + parsers.push(toParser(arguments[i])); + var pid = parser_id++; + return function(state) { + var savedState = state; + var cached = savedState.getCached(pid); + if(cached) { + return cached; + } + + var ast = []; + var matched = ""; + var i; + for(i=0; i< parsers.length; ++i) { + var parser = parsers[i]; + var result = parser(state); + if(result) { + state = result.remaining; + if(result.ast != undefined) { + ast.push(result.ast); + matched = matched + result.matched; + } + } + else { + break; + } + } + if(i == parsers.length) { + cached = make_result(state, matched, ast); + } + else + cached = false; + savedState.putCached(pid, cached); + return cached; + }; +} + +// Like sequence, but ignores whitespace between individual parsers. +function wsequence() { + var parsers = []; + for(var i=0; i < arguments.length; ++i) { + parsers.push(whitespace(toParser(arguments[i]))); + } + return sequence.apply(null, parsers); +} + +// 'choice' is a parser combinator that provides a choice between other parsers. +// It takes any number of parsers as arguments and returns a parser that will try +// each of the given parsers in order. The first one that succeeds results in a +// successfull parse. It fails if all parsers fail. +function choice() { + var parsers = []; + for(var i = 0; i < arguments.length; ++i) + parsers.push(toParser(arguments[i])); + var pid = parser_id++; + return function(state) { + var savedState = state; + var cached = savedState.getCached(pid); + if(cached) { + return cached; + } + var i; + for(i=0; i< parsers.length; ++i) { + var parser=parsers[i]; + var result = parser(state); + if(result) { + break; + } + } + if(i == parsers.length) + cached = false; + else + cached = result; + savedState.putCached(pid, cached); + return cached; + } +} + +// 'butnot' is a parser combinator that takes two parsers, 'p1' and 'p2'. +// It returns a parser that succeeds if 'p1' matches and 'p2' does not, or +// 'p1' matches and the matched text is longer that p2's. +// Useful for things like: butnot(IdentifierName, ReservedWord) +function butnot(p1,p2) { + var p1 = toParser(p1); + var p2 = toParser(p2); + var pid = parser_id++; + + // match a but not b. if both match and b's matched text is shorter + // than a's, a failed match is made + return function(state) { + var savedState = state; + var cached = savedState.getCached(pid); + if(cached) + return cached; + + var br = p2(state); + if(!br) { + cached = p1(state); + } else { + var ar = p1(state); + if(ar.matched.length > br.matched.length) + cached = ar; + else + cached = false; + } + savedState.putCached(pid, cached); + return cached; + } +} + +// 'difference' is a parser combinator that takes two parsers, 'p1' and 'p2'. +// It returns a parser that succeeds if 'p1' matches and 'p2' does not. If +// both match then if p2's matched text is shorter than p1's it is successfull. +function difference(p1,p2) { + var p1 = toParser(p1); + var p2 = toParser(p2); + var pid = parser_id++; + + // match a but not b. if both match and b's matched text is shorter + // than a's, a successfull match is made + return function(state) { + var savedState = sate; + var cached = savedState.getCached(pid); + if(cached) + return cached; + + var br = p2(state); + if(!br) { + cached = p1(state); + } else { + var ar = p1(state); + if(ar.matched.length >= br.matched.length) + cached = br; + else + cached = ar; + } + savedState.putCached(pid, cached); + return cached; + } +} + + +// 'xor' is a parser combinator that takes two parsers, 'p1' and 'p2'. +// It returns a parser that succeeds if 'p1' or 'p2' match but fails if +// they both match. +function xor(p1, p2) { + var p1 = toParser(p1); + var p2 = toParser(p2); + var pid = parser_id++; + + // match a or b but not both + return function(state) { + var savedState = state; + var cached = savedState.getCached(pid); + if(cached) + return cached; + + var ar = p1(state); + var br = p2(state); + if(ar && br) + cached = false; + else + cached = ar || br; + savedState.putCached(pid, cached); + return cached; + } +} + +// A parser combinator that takes one parser. It returns a parser that +// looks for zero or more matches of the original parser. +function repeat0(p) { + var p = toParser(p); + var pid = parser_id++; + + return function(state) { + var savedState = state; + var cached = savedState.getCached(pid); + if(cached) { + return cached; + } + + var ast = []; + var matched = ""; + var result; + while(result = p(state)) { + ast.push(result.ast); + matched = matched + result.matched; + if(result.remaining.index == state.index) + break; + state = result.remaining; + } + cached = make_result(state, matched, ast); + savedState.putCached(pid, cached); + return cached; + } +} + +// A parser combinator that takes one parser. It returns a parser that +// looks for one or more matches of the original parser. +function repeat1(p) { + var p = toParser(p); + var pid = parser_id++; + + return function(state) { + var savedState = state; + var cached = savedState.getCached(pid); + if(cached) + return cached; + + var ast = []; + var matched = ""; + var result= p(state); + if(!result) + cached = false; + else { + while(result) { + ast.push(result.ast); + matched = matched + result.matched; + if(result.remaining.index == state.index) + break; + state = result.remaining; + result = p(state); + } + cached = make_result(state, matched, ast); + } + savedState.putCached(pid, cached); + return cached; + } +} + +// A parser combinator that takes one parser. It returns a parser that +// matches zero or one matches of the original parser. +function optional(p) { + var p = toParser(p); + var pid = parser_id++; + return function(state) { + var savedState = state; + var cached = savedState.getCached(pid); + if(cached) + return cached; + var r = p(state); + cached = r || make_result(state, "", false); + savedState.putCached(pid, cached); + return cached; + } +} + +// A parser combinator that ensures that the given parser succeeds but +// ignores its result. This can be useful for parsing literals that you +// don't want to appear in the ast. eg: +// sequence(expect("("), Number, expect(")")) => ast: Number +function expect(p) { + return action(p, function(ast) { return undefined; }); +} + +function chain(p, s, f) { + var p = toParser(p); + + return action(sequence(p, repeat0(action(sequence(s, p), f))), + function(ast) { return [ast[0]].concat(ast[1]); }); +} + +// A parser combinator to do left chaining and evaluation. Like 'chain', it expects a parser +// for an item and for a seperator. The seperator parser's AST result should be a function +// of the form: function(lhs,rhs) { return x; } +// Where 'x' is the result of applying some operation to the lhs and rhs AST's from the item +// parser. +function chainl(p, s) { + var p = toParser(p); + return action(sequence(p, repeat0(sequence(s, p))), + function(ast) { + return foldl(function(v, action) { return action[0](v, action[1]); }, ast[0], ast[1]); + }); +} + +// A parser combinator that returns a parser that matches lists of things. The parser to +// match the list item and the parser to match the seperator need to +// be provided. The AST is the array of matched items. +function list(p, s) { + return chain(p, s, function(ast) { return ast[1]; }); +} + +// Like list, but ignores whitespace between individual parsers. +function wlist() { + var parsers = []; + for(var i=0; i < arguments.length; ++i) { + parsers.push(whitespace(arguments[i])); + } + return list.apply(null, parsers); +} + +// A parser that always returns a zero length match +function epsilon_p(state) { + return make_result(state, "", undefined); +} + +// Allows attaching of a function anywhere in the grammer. If the function returns +// true then parse succeeds otherwise it fails. Can be used for testing if a symbol +// is in the symbol table, etc. +function semantic(f) { + var pid = parser_id++; + return function(state) { + var savedState = state; + var cached = savedState.getCached(pid); + if(cached) + return cached; + cached = f() ? make_result(state, "", undefined) : false; + savedState.putCached(pid, cached); + return cached; + } +} + +// The `and` predicate asserts that a certain conditional +// syntax is satisfied before evaluating another production. Eg: +// sequence(and("0"), oct_p) +// (if a leading zero, then parse octal) +// It succeeds if 'p' succeeds and fails if 'p' fails. It never +// consume any input however, and doesn't put anything in the resulting +// AST. +function and(p) { + var p = toParser(p); + var pid = parser_id++; + return function(state) { + var savedState = state; + var cached = savedState.getCached(pid); + if(cached) + return cached; + var r = p(state); + cached = r ? make_result(state, "", undefined) : false; + savedState.putCached(pid, cached); + return cached; + } +} + +// The opposite of 'and'. It fails if 'p' succeeds and succeeds if +// 'p' fails. It never consumes any input. This combined with 'and' can +// be used for 'lookahead' and disambiguation of cases. +// +// Compare: +// sequence("a",choice("+","++"),"b") +// parses a+b +// but not a++b because the + matches the first part and peg's don't +// backtrack to other choice options if they succeed but later things fail. +// +// sequence("a",choice(sequence("+", not("+")),"++"),"b") +// parses a+b +// parses a++b +// +function not(p) { + var p = toParser(p); + var pid = parser_id++; + return function(state) { + var savedState = state; + var cached = savedState.getCached(pid); + if(cached) + return cached; + cached = p(state) ? false : make_result(state, "", undefined); + savedState.putCached(pid, cached); + return cached; + } +} + diff --git a/src/Y/class.cjs b/src/Y/class.cjs index 65b7d9d..c70ec20 100644 --- a/src/Y/class.cjs +++ b/src/Y/class.cjs @@ -109,6 +109,7 @@ function Class(className, Parent, members) { var k = classStatics[i]; NewClass[k] = ClassFactory[k]; } + NewClass.instantiate = instantiate.partial(NewClass); // Copy parent methods, then add new instance methods for (var k in parentMembers) diff --git a/src/Y/modules/y.event.cjs b/src/Y/modules/y.event.cjs index 0135273..3cb8030 100644 --- a/src/Y/modules/y.event.cjs +++ b/src/Y/modules/y.event.cjs @@ -113,23 +113,22 @@ Y.YObject.subclass('Event', { }, emit : function emit(evtname, trigger, data){ - var q = this.queues[evtname] + var evt, A = arguments + , q = this.queues[evtname] , L = q ? q.length : 0 ; - if ( !L ) - return this; - - q = q._o.slice(0); - var A = arguments - , target = this.target - , evt = new Event(evtname, target, trigger, data) - , method = (A.length > 3 ? 'apply' : 'call') - , args = (A.length > 3 ? [evt].concat(Y(A,3)) : evt) - ; - for (var i=0, fn=q[i]; i 3 ? 'apply' : 'call') + , args = (A.length > 3 ? [evt].concat(Y(A,3)) : evt) + ; + for (var i=0, fn=q[i]; i 3 ? 'apply' : 'call') + , args = (A.length > 3 ? [evt].concat(Y(A,3)) : evt) + ; + for (var i=0, fn=q[i]; i [ "+_match_cache[event].join(",\n ")+" ]"); + return _match_cache[event]; +} + +/** + * Calculates the set of all unique subsets of S (without regard for order) of size k (ie, combination (S k). + * @param {Array} S A collection. + * @param {Number} k Length of combinations to calculate. + * @return {Array} Array of sub-Arrays of S. + */ +function comb(S, k){ + if ( !S || k <= 0 ) + return new Y.YArray(); + + if (k === 1) + return Y(S).map(mkArray); + + return S.reduce(function nComb(acc, v, i){ + var subsets = comb(S.slice(i+1), k-1); + return acc.concat( subsets.invoke('concat', [v]) ); + }, new Y.YArray()); +} +function mkArray(v){ return new Y.YArray([v]); } + +/** + * Calculates the powerset of S (set of all subsets, P(S)). + * @param {Array} S A collection. + * @param {Number} [n] Optional upper-bound on subset size. Defaults to the length of S. + * @return {Array} Array of sub-Arrays of S. + */ +function powerset(S, n){ + n = (n < 0 || n === undefined) ? S.length : (!S ? 0 : n); + for (var P = new Y.YArray(), i=1; i <= n; ++i) + P.extend( comb(S,i) ); + return P; +} + +/** + * @param {Array} T Array of Event-part Arrays. + * @return {Array} + */ +function propertyset(T){ + var ps = new Y.YArray(); + if ( !T || !T.length ) + return ps; + + return T.reduce(function(acc, t){ + return acc.extend(t.split('-'), t); + }, ps) + .concat(T, powerset(T).map(joiner)) + .unique(); +} + +var _prefixes = [ 'state', 'core', 'chrome', 'video', 'share' ] +, _suffixes = [ 'user', 'auto' ] +; +function cmp( a, b ) { + var a_suffix = _suffixes.indexOf(a) !== -1 + , b_suffix = _suffixes.indexOf(b) !== -1 + , a_prefix = _prefixes.indexOf(a) !== -1 + , b_prefix = _prefixes.indexOf(b) !== -1 + ; + if (a_prefix === b_prefix && a_suffix === b_suffix) + return 0; + else if (a_prefix || b_suffix) + return -1; + else + return 1; +} + +function shove(A, v){ A.push(v); return A; } +function sorter(A){ A.sort(cmp); return A; } +function joiner(A){ return A.join('.'); } + +function unique(A){ + return A.reduce(function(acc, v) { + return (acc.indexOf(v) === -1) ? shove(acc, v) : acc; + }, Y([])); +} + + +Y.extend( exports, { comb:comb, powerset:powerset, propertyset:propertyset, unique:unique, getMatchingEvents:getMatchingEvents }); +Y.extend( Y['event'], exports ); diff --git a/src/Y/types/array.cjs b/src/Y/types/array.cjs index 6b3a9eb..011cb36 100644 --- a/src/Y/types/array.cjs +++ b/src/Y/types/array.cjs @@ -17,7 +17,8 @@ YCollection.subclass('YArray', function(YArray){ // Add YArray to the things that count as arrays type.isArray.types.push(YArray); - var newYArray = YArray.instantiate.bind(YArray); + var newYArray = YArray.instantiate; + // var newYArray = YArray.instantiate.bind(YArray); proxy({ target:YArray, donor:_Array, context:'_o', names:'indexOf lastIndexOf shift join'.split(' ') }); @@ -30,14 +31,19 @@ YCollection.subclass('YArray', function(YArray){ proxy({ target:YArray, donor:_Array, context:'_o', wrap:newYArray, names:['slice'] }); + function unwrapY(o){ + return (o instanceof YArray ? o.end() : o); + } + this['init'] = function initYArray(o){ - if (o instanceof YArray) - this._o = o._o; - else - this._o = o || []; + this._o = ( + ( o === undefined ? [] : + ( o instanceof Array ? o : + ( o instanceof YArray ? o._o : + [o] )))); // lisp strikes back! }; this['get'] = @@ -105,27 +111,6 @@ YCollection.subclass('YArray', function(YArray){ return v; }; - /** - * Intersects this YArray with another collection, returning a new YArray. - * The membership test uses Y(a).has(), so it is possible to intersect collections of different types. - * For YArray and YObject, .has() uses strict equality (===) via .indexOf(). - * - * @param {Array|Object|YCollection} a Comparison collection. - * @return {YArray} A new YArray of all elements in {this} found in the supplied collection. - * - * var foo = /foo/; - * var A = [foo, 'A', 1, 2, 3, 'C', /foo/]; - * var B = [foo, 'B', 3, 'A', 1, /foo/]; - * var I = Y(A).intersect(B); - * I.toString() === "YArray([/foo/,A,1,3])"; // true - * I.get(0) === foo; // true - */ - this['intersect'] = - function intersect(a){ - var A = Y(a); - return this.filter(A.has, A); - }; - this['clear'] = function clear(){ var A = this._o; @@ -134,10 +119,6 @@ YCollection.subclass('YArray', function(YArray){ return this; }; - function unwrapY(o){ - return (o instanceof YArray ? o.end() : o); - } - this['concat'] = function concat( donor ){ var A = this._o; @@ -234,5 +215,55 @@ YCollection.subclass('YArray', function(YArray){ }, Y({}), this ); }; + /** + * Set Intersection (A ^ B) + * Intersects this YArray with another collection, returning a new YArray. + * The membership test uses Y(a).has(), so it is possible to intersect collections of different types. + * For YArray and YObject, .has() uses strict equality (===) via .indexOf(). + * + * @param {Array|Object|YCollection} a Comparison collection. + * @return {YArray} A new YArray of all elements in {this} found in the supplied collection. + * + * var foo = /foo/; + * var A = [foo, 'A', 1, 2, 3, 'C', /foo/]; + * var B = [foo, 'B', 3, 'A', 1, /foo/]; + * var I = Y(A).intersect(B); + * I.toString() === "YArray([/foo/,A,1,3])"; // true + * I.get(0) === foo; // true + */ + this['intersect'] = + function intersect(a){ + var A = Y(a); + return this.filter(A.has, A); + }; + + /** + * Set Union (A v B) + * @param {Array|Object|YCollection} a Comparison collection. + * @return {YArray} A new YArray of all elements in both collections, but without duplicates. + */ + this['union'] = + function union(a){ + return this.concat(a).unique(); + }; + + /** + * Set Difference (A - B) + * @param {Array|Object|YCollection} a Comparison collection. + * @return {YArray} A new YArray of only elements in this not in supplied collection. + */ + this['difference'] = + function difference(a){ + var A = Y(a); + return this.filter(Y(A.has).compose(op.not), A); + }; + + // Symmetric Difference + this['xor'] = + function xor(a){ + return this.difference(a).concat( Y(a).difference(this) ); + }; + + return this; }); diff --git a/src/Y/types/function.cjs b/src/Y/types/function.cjs index 22143a7..28e4e93 100644 --- a/src/Y/types/function.cjs +++ b/src/Y/types/function.cjs @@ -123,11 +123,14 @@ function genericize(fn) { -function _composer(x,fn){ return fn.call(this, x); } + function compose(f,g){ var fns = slice.call(arguments).map(_Function.toFunction); - return function(){ - return fns.reduce(_composer, slice.call(arguments), this); + return function(x){ + var self = this; + return fns.reduce(function (x, fn){ + return fn.call(self, x); + }, x); }; } diff --git a/src/Y/types/string.cjs b/src/Y/types/string.cjs index 676a1a9..5f8b808 100644 --- a/src/Y/types/string.cjs +++ b/src/Y/types/string.cjs @@ -12,7 +12,8 @@ var YCollection = require('Y/types/collection').YCollection exports['YString'] = YCollection.subclass('YString', function(YString){ - var newYString = YString.instantiate.bind(YString); + // var newYString = YString.instantiate.bind(YString); + var newYString = YString.instantiate; proxy({ target:YString, donor:String, context:'_o', wrap:newYString, names:'slice substr substring concat replace toLowerCase toUpperCase'.split(' ') }); proxy({ target:YString, donor:String, context:'_o', diff --git a/src/evt.cjs b/src/evt.cjs index 83a0242..d8c1880 100644 --- a/src/evt.cjs +++ b/src/evt.cjs @@ -28,7 +28,7 @@ var Y = require('Y').Y , Emitter = require('Y/modules/y.event').Emitter , unwrap = require('Y/types/function').unwrap -, isFunction = Y.isFunction +// , isFunction = Y.isFunction , KNOWN_CLASSES = Class.KNOWN_CLASSES = exports['KNOWN_CLASSES'] = {} , P = 'prototype' @@ -89,7 +89,7 @@ function ConstructorTemplate() { }, instance); var initialise = instance.__initialise__; - if ( isFunction(initialise) ) + if ( typeof initialise == 'function' ) initialise.apply(instance, args); cls.emit('created', instance, { @@ -120,7 +120,7 @@ function createInitialise(cls){ Y.bindAll(instance, binds); var init = cls.fn.init; - if ( isFunction(init) ) { + if ( typeof init == 'function' ) { var result = init.apply(instance, arguments); if (result) instance = result; // XXX: I think this needs to go away } @@ -165,7 +165,7 @@ function Class(className, Parent, members){ , parentMembers = {} ; - if ( !members && !isFunction(Parent) ) { + if ( !members && !(typeof Parent == 'function') ) { members = Parent; Parent = null; } @@ -176,7 +176,7 @@ function Class(className, Parent, members){ Parent = Object; // Parent is the prototype - if ( !isFunction(Parent) ) { + if ( !(typeof Parent == 'function') ) { SuperClass = getProto(Parent).constructor || Object; prototype = Parent; @@ -258,7 +258,7 @@ function Class(className, Parent, members){ } // Either invoke body constructor... - if ( isFunction(members) ) { + if ( typeof members == 'function' ) { // body fn is responsible for calling mixin, attaching statics, etc members.call(prototype, NewClass); @@ -275,7 +275,7 @@ function Class(className, Parent, members){ // Notify mixins to let them finish up any customization if (mixins.length) mixins.forEach(function(mxn){ - if ( mxn && isFunction(mxn.emit) ) + if ( mxn && (typeof mxn.emit == 'function') ) mxn.emit('mixin', NewClass, { 'mixin':mxn, 'cls':NewClass }); }); @@ -355,18 +355,10 @@ new Class('Mixin', Class, { }, /** + * @param {String} key * @return The merged pairs at key taken from all bases. */ - aggregate : function aggregate(key){ - return this.__bases__ - .clone() - .unshift(this) - .reverse() - .reduce(function(acc, base){ - var proto = (base instanceof Function ? base.prototype : base); - return Y.extend(acc, proto[key]); - }, {}); - } + aggregate : Y(aggregate).methodize() } @@ -424,17 +416,31 @@ function mixin(cls, _mxn){ return; // Register onCreate to fire whenever a new instance is initialized - if ( isFunction(cls.on) && hasOwn.call(mproto,'onCreate') && isFunction(onCreate) ) + if ( (typeof cls.on == 'function') && hasOwn.call(mproto,'onCreate') && (typeof onCreate == 'function') ) cls.on('create', onCreate); // Fire the mixin event on this Mixin only if we're done constructing the class - if ( isFunction(mxn.emit) && mixin.caller !== Class ) + if ( (typeof mxn.emit == 'function') && mixin.caller !== Class ) mxn.emit('mixin', cls, { 'mixin':mxn, 'cls':cls }); }); return cls; } +function aggregate(cls, key){ + if ( !(cls instanceof Class) && cls.__class__ ) + cls = cls.__class__; + return cls.__bases__ + .clone() + .unshift(cls) + .reverse() + .reduce(function(acc, base){ + var proto = (base instanceof Function ? base.prototype : base); + return Y.extend(acc, proto[key]); + }, {}); +} + + Mixin.on('subclass', function onMixinSubclass(evt){ var d = evt.data @@ -443,13 +449,14 @@ Mixin.on('subclass', , onMixin = members.onMixin ; - if ( isFunction(onMixin) ) + if ( typeof onMixin == 'function' ) mxn.on('mixin', onMixin); // console.log('Mixin.subclass()', mxn, '<', d.parent, 'onMixin:', onMixin); }); + // Expose exports['Class'] = exports['subclass'] = Class; @@ -457,3 +464,4 @@ exports['instantiate'] = instantiate; exports['fabricate'] = Y.fabricate; exports['lookupClass'] = lookupClass; exports['mixin'] = mixin; +exports['aggregate'] = aggregate; diff --git a/src/ezl/layer/index.cjs b/src/ezl/layer/index.cjs index 55b4392..cfae21f 100644 --- a/src/ezl/layer/index.cjs +++ b/src/ezl/layer/index.cjs @@ -1,9 +1,13 @@ var Y = require('Y').Y +, layerable = require('ezl/layer/layerable') , layer = require('ezl/layer/layer') , html = require('ezl/layer/html') ; Y.core.extend(exports, { + 'layerable' : layerable, + 'Layerable' : layerable.Layerable, + 'layer' : layer, 'Layer' : layer.Layer, diff --git a/src/ezl/layer/layer.cjs b/src/ezl/layer/layer.cjs index bbfc418..7db1b84 100644 --- a/src/ezl/layer/layer.cjs +++ b/src/ezl/layer/layer.cjs @@ -1,764 +1,19 @@ -//#ensure "jquery" -var Y = require('Y').Y -, op = require('Y/op') - -, evt = require('evt') - -, Vec = require('ezl/math/vec').Vec -, Loc = require('ezl/loc/loc').Loc -, BoundingBox = require('ezl/loc/boundingbox').BoundingBox -, Animation = require('ezl/loop/fx').Animation -, - -CONTEXT_ATTRS = Y('globalAlpha globalCompositeOperation strokeStyle fillStyle lineWidth lineCap lineJoin miterLimit shadowOffsetX shadowOffsetY shadowBlur shadowColor'.split(' ')), -FAUX_ACCESSORS = Y('width height position stroke fill origin rotate scale translate title'.split(' ')) -, _X = 0, _Y = 1 +var Y = require('Y') +, evt = require('evt') +, Layerable = require('ezl/layer/layerable').Layerable , - - Layer = exports['Layer'] = -new evt.Class('Layer', { - __bind__ : [ 'tick' ], - - /// Class Defaults /// - - // _layer* is applied to this.layer (the DOM Element) - _layerHtml : '
', // HTML that will become the layer - _layerId : null, // HTML id attribute - _layerAttrs : null, // HTML attributes - _layerClasses : 'ezl layer',// CSS classes - - originX : 0, - originY : 0, - - hasCanvas : true, // Whether to create a canvas - useCanvasScaling : false, // Default to CSS3 scaling - alwaysClearDrawing : true, // Whether to clear the canvas content before redraw - alwaysClearAttrs : false, // Whether to remove all canvas attributes (CONTEXT_ATTRS) - // and transforms (scale, rotate, translate) and reset defaults before redraw - - - - /// State /// - - canvas : null, - parent : null, - children : null, - - dirty : true, - ctx : null, - animActive : null, - animQueue : null, - _erased : null, - - layerWidth : 0, canvasWidth : 0, realWidth : 0, - layerHeight : 0, canvasHeight : 0, realHeight : 0, - - x: 0, y: 0, loc : null, // Position relative to parent - - // Bleeds are marks outside the declared layer-size - negBleed : null, // Loc - posBleed : null, // Loc - - // Transforms - _origin : null, // rotational origin - transform : null, // Object - - - /// Setup /// +evt.subclass('Layer', { + __mixins__ : [ Layerable ], + __bind__ : [], - init : function init(props, attrs, html){ - if (props !== undefined && props !== null) { - switch (typeof props) { - case 'boolean' : this.hasCanvas = props; break; - case 'string' : this._layerHtml = props; break; - case 'object' : Y.core.extend(this, props); break; - } - } - - this.children = new Y.YArray(); - this.animActive = new Y.YArray(); - this.animQueue = new Y.YArray(); - this._erased = new Y.YArray(); - - this.loc = new Loc(0,0); - this.negBleed = new Loc(0,0); - this.posBleed = new Loc(0,0); - - this.bbox = new BoundingBox(0,0, 0,0, this.originX,this.originY); - this._origin = this.bbox.origin; - - this.transform = { - rotate : 0, - scale : new Loc(1.0,1.0), - translate : new Loc(0,0) // translates canvas - }; - - this.layer = jQuery(html || this._layerHtml) - .addClass(this._layerClasses) - .data('layer', this); - this.layer[0].layer = this; - - if (this._layerAttrs) this.layer.attr(this._layerAttrs); - if (this._layerId) this.layer.attr('id', this._layerId); - if (attrs) this.layer.attr(attrs); - - if (this.hasCanvas) { - this.canvas = jQuery('') - .appendTo(this.layer); - this.ctx = this.canvas[0].getContext('2d'); - this.canvas[0].layer = this; - } else { - this.canvas = jQuery(); - } - - // this.tick = this.tick.bind(this); - }, - - /// Scene Graph Heirarchy /// - - /** - * @param {Layer} child - * @return {this} - */ - append : function append(child){ - new Y(arguments).invoke('appendTo', this); - return this; - }, - - /** - * @param {Layer} parent - * @return {this} - */ - appendTo : function appendTo(parent){ - if (!parent) return this; - - // Always ensure we detach from the DOM and redraw this node - this.remove(); - this.dirty = true; - - if ( !(parent instanceof Layer) && (parent.shape instanceof Layer) ) - parent = parent.shape; - - // Layer? Add self as new child, fix DOM - if ( parent instanceof Layer ) { - this.parent = parent; - parent.children.push(this); - parent.layer.append(this.layer); - - // Otherwise: Attach to a DOM node as a new root layer node, leave parent null - } else - $(parent).append(this.layer); - - return this; - }, - - /** - * @param {Layer} child - * @return {this} - */ - prepend : function prepend(child){ - new Y(arguments).invoke('prependTo', this); - return this; - }, - - /** - * @param {Layer} parent - * @return {this} - */ - prependTo : function prependTo(parent){ - if (!parent) return this; - - // Always ensure we detach from the DOM and redraw this node - this.remove(); - this.dirty = true; - - if ( !(parent instanceof Layer) && (parent.shape instanceof Layer) ) - parent = parent.shape; - - // Layer? Add self as new child, fix DOM - if ( parent instanceof Layer ) { - this.parent = parent; - parent.children.unshift(this); - parent.layer.prepend(this.layer); - - // Otherwise: Attach to a DOM node as a new root layer node, leave parent null - } else - $(parent).prepend(this.layer); - - return this; - }, - - /** - * Removes this layer from its parent and the DOM. - */ - remove : function remove(){ - if (this.parent) - this.parent.children.remove(this); - this.parent = null; - this.layer.remove(); - return this; - }, - - /** - * Clears this layer and destroys all children. - */ - empty : function empty(ctx){ - this.children.invoke('remove'); - this.clear(false, ctx); - return this; - }, - - /** - * @return The root of the scene graph. - */ - root : function root(){ - if (this.parent) - return this.parent.root(); - else - return this; - }, - - - - - - /// Attributes /// - - // set : function set(key, value, def){ - // if ( key === undefined || (value === undefined && def === undefined) ) - // return this; - // - // value = (value !== undefined ? value : def); - // if ( FAUX_ACCESSORS.has(key) ) - // this[key](value); - // else - // this[key] = value; - // - // return this; - // }, - - attr : Y.attr.methodize(), - - size : function size(w,h){ - if (w === undefined && h === undefined) - return new Vec(this.layerWidth,this.layerHeight); - - if (w === null) w = this.realWidth; - if (h === null) h = this.realHeight; - - this.realWidth = w; - this.realHeight = h; - - // HTMLElement.{width,height} is a long - this.layerWidth = Math.round(w); - this.layerHeight = Math.round(h); - - var bb = this.bbox.resize(w,h) - , nb = this.negBleed, pb = this.posBleed - - , cw = this.canvasWidth = Math.ceil(w + nb.x + pb.x) - , ch = this.canvasHeight = Math.ceil(h + nb.y + pb.y) - ; - - this.layer.css({ - 'width' : w, 'height' : h, - 'left' : bb.x1, 'top' : bb.y1 - }); - this.canvas.css({ - 'width' : cw, 'height' : ch, - 'margin-left' : -nb.x, 'margin-top' : -nb.y - }); - var el = this.canvas[0]; - if (el) { - el.width = cw; - el.height = ch; - } - - return this; - }, - - /** - * Changes the layer's width and then updates the canvas. - */ - width : function width(w){ - if (w === undefined) - return this.layerWidth; - - this.layerWidth = w; - - var bb = this.bbox.resize(w, this.layerHeight) - // , ro = bb.relOrigin - , nb = this.negBleed - , cw = this.canvasWidth = Math.ceil(w + nb.x + this.posBleed.x); // HTMLCanvas.width is a long - - this.layer.css({ - 'width' : w, - 'left' : bb.x1, - // 'margin-left' : -ro.x - }); - - this.canvas.css({ - 'width' : cw, - 'margin-left' : -nb.x - }); - if (this.canvas.length) this.canvas[0].width = cw; - - return this; - }, - - height : function height(h){ - if (h === undefined) - return this.layerHeight; - - this.layerHeight = h; - - var bb = this.bbox.resize(this.layerWidth, h) - // , ro = bb.relOrigin - , nb = this.negBleed - , ch = this.canvasHeight = Math.ceil(h + nb.y + this.posBleed.y); // HTMLCanvas.height is a long - - this.layer.css({ - 'height' : h, - 'top' : bb.y1, - // 'margin-top' : -ro.y - }); - - this.canvas.css({ - 'height' : ch, - 'margin-top' : -nb.y - }); - if (this.canvas.length) this.canvas[0].height = ch; - - return this; - }, - - /** - * position() -> {Loc} - * Gets position of the layer relative to the parent. - * @return {Loc} - */ - /** - * position(x,y) -> {this} - * Sets the position of this node, and then returns it. - * @param {Number|String|undefined} x - * @param {Number|String|undefined} y If omitted, this method must be invoked with `undefined` as the first argument. - * @return {this} - */ - /** - * position(pos) -> {this} - * Sets the position of this node, and then returns it. - * @param {Object} pos An object with "x" and/or "y" properties as in `position(x,y)`. - * @return {this} - */ - position : function position(x,y){ - if (x === undefined && y === undefined) - return this.layer.position(); - - if ( x instanceof Array ) { - y = x[_Y]; x = x[_X]; - } else if ( Y.isPlainObject(x) ){ - y = ('top' in x ? x.top : x.y); - x = ('left' in x ? x.left : x.x); - } - - var bbox = this.bbox.relocate(x,y); - this.css({ - 'left' : bbox.x1, - 'top' : bbox.y1 - }); - return this; - }, - - stroke : function stroke(style, width){ - if (style === undefined && width === undefined) - return this.strokeStyle; - - if (width !== undefined) - this.lineWidth = width; - - this.dirty = true; - this.strokeStyle = style; - return this; - }, - fill : function fill(style){ - if (style === undefined) - return this.fillStyle; + init : function initLayer(props, attrs, html){ + this.setup(props, attrs, html); - this.dirty = true; - this.fillStyle = style; - return this; - }, - - hide : makeDelegate('hide'), - show : makeDelegate('show'), - - /** CSS properties */ - css : makeDelegate('css'), - - /** Position relative to document. */ - offset : makeDelegate('offset'), - - - - /// Transformations /// - - /** - * Gets and sets the origin-location for this shape, used for - * position and rotation. Defaults to the midpoint. - */ - origin : function origin(x,y){ - var o = this._origin; - if (arguments.length === 0) - return o.absolute(this.layerWidth, this.layerHeight); - - if ( x instanceof Array ) { - y = x[_Y]; x = x[_X]; - } - o.x = x; - o.y = y; - return this._applyTransforms(); - }, - - /** - * Rotates this layer by r radians. - */ - rotate : function rotate(r){ - var t = this.transform; - if (r === undefined) - return t.rotate; - - t.rotate = r; - return this._applyTransforms(); - }, - - /** - * Scales this layer by (sx,sy), and then applies scaling relatively - * to all sublayers (preserving knowledge of their individual scaling). - */ - scale : function scale(sx,sy){ - var ts = this.transform.scale; - if (arguments.length === 0) - return ts; - - if ( sx instanceof Array ) { - sy = sx[_Y]; sx = sx[_X]; - } - ts.x = sx; - ts.y = sy; - this.dirty = true; - return this._applyTransforms(); - }, - - /** - * Translates draw calls by (x,y) within this layer only. This allows you to - * functionally move the origin of the coordinate system for this layer. - */ - translate : function translate(tx,ty){ - var tt = this.transform.translate; - if (arguments.length === 0) - return tt; - - if ( tx instanceof Array ) { - ty = tx[_Y]; tx = tx[_X]; - } - tt.x = tx; - tt.y = ty; - this.dirty = true; - return this; - }, - - /** - * @private - */ - _applyTransforms : function _applyTransforms(){ - var t = this.transform, tfns = []; - - if (t.rotate !== 0) - tfns.push('rotate('+t.rotate+'rad)'); - - if (!this.useCanvasScaling && (t.scale.x !== 1 || t.scale.y !== 1)) - tfns.push('scale('+t.scale.x+','+t.scale.y+')'); - - var trans = (tfns.length ? tfns.join(' ') : 'none') - , o = this._origin.toUnits('px') - , origin = o.x+' '+o.y ; - this.layer.css(['', '-moz-', '-webkit-'] - .reduce( - function(values, prefix){ - values[prefix+'transform'] = trans; - values[prefix+'transform-origin'] = origin; - return values; - }, - {}) ); - return this; - }, - - - - - - - - /// Iterators /// - - invoke : function invoke(name){ - var args = Y(arguments,1); - // this[name].apply(this, args); - // this.children.invoke.apply(this.children, ['invoke', name].concat(args)); - return this._invoke(name, args); - }, - _invoke : function _invoke(name, args){ - this[name].apply(this, args); - this.children.invoke('_invoke', name, args); - return this; - }, - - setAll : function setAll(k,v){ - this[k] = v; - this.children.invoke('setAll', k,v); - return this; - }, - - /** - * Reduce "up", across this and parents, inner to outer: - * acc = fn.call(context || node, acc, node) - */ - reduceup : function reduceup(fn, acc, context){ - acc = fn.call(context || this, acc, this); - return ( this.parent ? this.parent.reduceup(fn, acc, context) : acc ); - }, - - /** - * Reduce "down", across this and children, depth-first: - * acc = fn.call(context || node, acc, node) - */ - reduce : function reduce(fn, acc, context){ - acc = fn.call(context || this, acc, this); - return this.children.reduce(function(acc, child){ - return child.reduce(fn, acc, context); - }, acc); - }, - - - - - - - /// Drawing Functions /// - - /** - * @param {CanvasDrawingContext2D} [ctx=this.ctx] Forces context to use rather than the layer's own. - * @param {Boolean} [force=false] Forces redraw. - */ - draw : function draw(ctx, force){ - var _ctx = ctx || this.ctx; - - if ( this.dirty || force ){ - this.dirty = false; - this._openPath(_ctx); - this.render(_ctx); - this._closePath(_ctx); - if (_ctx) this._erased.forEach(this._erase, this); - } - - this.children.invoke('draw', ctx, force); - return this; - }, - - _openPath : function _openPath(ctx){ - if (!ctx) return this; - - var self = this - , neg = this.negBleed - , t = this.transform - , w = this.canvasWidth, h = this.canvasHeight ; - - ctx.beginPath(); - - // TODO: only alwaysClearAttrs should reset transforms? - // if (this.alwaysClearAttrs) - ctx.setTransform(1,0,0,1,0,0); - - if (this.alwaysClearDrawing) - ctx.clearRect(-w,-h, 2*w,2*h); - - // if (this.alwaysClearAttrs) { - if (this.useCanvasScaling && (t.scale.x !== 1 || t.scale.y !== 1)) - ctx.scale(t.scale.x,t.scale.y); - - ctx.translate(neg.x, neg.y); - ctx.translate(t.translate.x, t.translate.y); - // } - - // Set context attributes - CONTEXT_ATTRS.forEach(function(name){ - if (this[name] !== undefined) - ctx[name] = this[name]; - else if (this.alwaysClearAttrs) - delete ctx[name]; - }, this); - - return this; - }, - - /** To be implemented by subclasses. */ - render : function render(ctx){ return this; }, - - _closePath : function _closePath(ctx){ - if (ctx) ctx.closePath(); - return this; - }, - - erase : function erase(x,y, w,h, alsoChildren){ - this._erased.push({ 'x':x,'y':y, 'w':w,'h':h, 'alsoChildren':alsoChildren }); - this.dirty = true; - if (alsoChildren) - this.children.invoke('erase', x,y, w,h, alsoChildren); - return this; - }, - - _erase : function _erase(args){ - var x = args.x, y = args.y - , w = args.w, h = args.h; - - if (w < 0) w = this.canvas.width() + w; - if (h < 0) h = this.canvas.height() + h; - - this.ctx.beginPath(); - this.ctx.clearRect(x,y, w,h); - this.ctx.closePath(); - }, - - /** - * Clears this layer and optionally all children. - */ - clear : function clear(alsoChildren, ctx){ - ctx = ctx || this.ctx; - - if (ctx) { - var w = this.canvas.width() - , h = this.canvas.height(); - ctx.beginPath(); - ctx.setTransform(1,0,0,1,0,0); - ctx.clearRect(-w,-h, 2*w,2*h); - ctx.closePath(); - } - - if (alsoChildren) - this.children.invoke('clear'); - - return this; - }, - - /** - * @param {Number} duration Duration (ms) of animation. - * @param {Object} props Object whose keys are the property-names to animate - * paired with the target value. Animated properties can also be relative. - * If a value is supplied with a leading += or -= sequence of characters, - * then the target value is computed by adding or subtracting the given - * number from the current value of the property. - * @param {Object} [options] Animation options: - * * {Number} [delay=0] Milliseconds to wait before beginning animation. - * * {Boolean} [queue=true] Indicates whether to place the animation - * in the effects queue to run when other animations have finished. - * If false, the animation will begin after `delay` milliseconds; - * likewise, if `delay` is non-zero `queue` defaults to `false` - * instead. - * * {String|Function|Object} [easing='linear'] Name of easing function - * used to calculate each step value, or a function, or a map of - * properties to either of the above. - * * {Function} [complete] Function called on animation completion. - * * {Function} [step] Function called after each step of the animation. - * @param {Float} [now] Time (ms) to use as the start-time. If omitted, the - * current time is used. - */ - animate : function animate(duration, props, options, now) { - var A = new Animation(this, duration, props, options); - if ( A.options.queue && this.animActive.length ) - this.animQueue.push(A); - else - this.animActive.push(A.start(now || new Date().getTime())); - return this; - }, - - tick : function tick(elapsed, now){ - if (elapsed instanceof Event) { - var d = evt.data; - now = d.now; - elapsed = d.elapsed; - } - - this.animActive = this.animActive.filter(function(anim){ - return anim.tick(elapsed, now); - }); - - var childrenRunning = this.children.invoke('tick', elapsed, now).some(op.I) - , running = !!this.animActive.length; - if ( !running && this.animQueue.length ) - this.animActive.push( this.animQueue.shift().start(now) ); - - return childrenRunning || running; - }, - - /// Debuggging /// - - // for debugging - point : function point(x,y, color){ - var ctx = this.ctx; - // this._openPath(ctx); - - var r = 2; - ctx.beginPath(); - ctx.arc(x,y, r, 0, Math.PI*2, false); - ctx.fillStyle = color || '#FFFFFF'; - ctx.fill(); - ctx.closePath(); - - // this._closePath(ctx); - return this; - }, - - title : function title(val){ - if (val === undefined) - return this.layer.attr('title'); - - this.layer.attr('title', val); - return this; - }, - - - - /// Misc /// - toString : function(){ - var pos = ((this.layer.length && this.layer.parent()[0]) ? this.position() : {top:NaN, left:NaN}); - return this.className+'['+pos.left+','+pos.top+']( children='+this.children.size()+' )'; } -}); - -function makeDelegate(name, dirties, prop){ - prop = prop || 'layer'; - return function delegate(){ - if (dirties && arguments.length) - this.dirty = true; - - var target = this[prop] - , result = target[name].apply(target, arguments); - - return (result !== target ? result : this); - }; -} - -// Install CSS styles -$(function(){ - $('