PageRenderTime 59ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/js/stdlib.js

https://bitbucket.org/pthrasher/nocschedule
JavaScript | 2443 lines | 2014 code | 425 blank | 4 comment | 411 complexity | f79784df606caed8dff9ab1b700c7115 MD5 | raw file
  1. JS.MethodChain = function(base) {
  2. var queue = [],
  3. baseObject = base || {};
  4. this.____ = function(method, args) {
  5. queue.push({func: method, args: args});
  6. };
  7. this.fire = function(base) {
  8. return JS.MethodChain.fire(queue, base || baseObject);
  9. };
  10. };
  11. JS.MethodChain.fire = function(queue, object) {
  12. var method, property, i, n;
  13. loop: for (i = 0, n = queue.length; i < n; i++) {
  14. method = queue[i];
  15. if (object instanceof JS.MethodChain) {
  16. object.____(method.func, method.args);
  17. continue;
  18. }
  19. switch (typeof method.func) {
  20. case 'string': property = object[method.func]; break;
  21. case 'function': property = method.func; break;
  22. case 'object': object = method.func; continue loop; break;
  23. }
  24. object = (typeof property === 'function')
  25. ? property.apply(object, method.args)
  26. : property;
  27. }
  28. return object;
  29. };
  30. JS.MethodChain.prototype = {
  31. _: function() {
  32. var base = arguments[0],
  33. args, i, n;
  34. switch (typeof base) {
  35. case 'object': case 'function':
  36. args = [];
  37. for (i = 1, n = arguments.length; i < n; i++) args.push(arguments[i]);
  38. this.____(base, args);
  39. }
  40. return this;
  41. },
  42. toFunction: function() {
  43. var chain = this;
  44. return function(object) { return chain.fire(object); };
  45. }
  46. };
  47. JS.MethodChain.reserved = (function() {
  48. var names = [], key;
  49. for (key in new JS.MethodChain) names.push(key);
  50. return new RegExp('^(?:' + names.join('|') + ')$');
  51. })();
  52. JS.MethodChain.addMethod = function(name) {
  53. if (this.reserved.test(name)) return;
  54. var func = this.prototype[name] = function() {
  55. this.____(name, arguments);
  56. return this;
  57. };
  58. func.displayName = 'MethodChain#' + name;
  59. };
  60. JS.MethodChain.displayName = 'MethodChain';
  61. JS.MethodChain.addMethods = function(object) {
  62. var methods = [], property, i;
  63. for (property in object)
  64. Number(property) !== property && methods.push(property);
  65. if (object instanceof Array) {
  66. i = object.length;
  67. while (i--)
  68. typeof object[i] === 'string' && methods.push(object[i]);
  69. }
  70. i = methods.length;
  71. while (i--) this.addMethod(methods[i]);
  72. object.prototype &&
  73. this.addMethods(object.prototype);
  74. };
  75. it = its = function() { return new JS.MethodChain; };
  76. JS.Module.methodAdded(function(name) {
  77. JS.MethodChain.addMethod(name);
  78. });
  79. JS.Kernel.include({
  80. wait: function(time) {
  81. var chain = new JS.MethodChain;
  82. typeof time === 'number' &&
  83. setTimeout(chain.fire.bind(chain, this), time * 1000);
  84. this.forEach && typeof time === 'function' &&
  85. this.forEach(function() {
  86. setTimeout(chain.fire.bind(chain, arguments[0]), time.apply(this, arguments) * 1000);
  87. });
  88. return chain;
  89. },
  90. _: function() {
  91. var base = arguments[0],
  92. args = [],
  93. i, n;
  94. for (i = 1, n = arguments.length; i < n; i++) args.push(arguments[i]);
  95. return (typeof base === 'object' && base) ||
  96. (typeof base === 'function' && base.apply(this, args)) ||
  97. this;
  98. }
  99. }, true);
  100. JS.MethodChain.addMethods([
  101. "abbr", "abs", "accept", "acceptCharset", "accesskey", "acos", "action", "addEventListener",
  102. "adjacentNode", "align", "alignWithTop", "alink", "alt", "anchor", "appendChild", "appendedNode",
  103. "apply", "archive", "arguments", "arity", "asin", "atan", "atan2", "attrNode", "attributes",
  104. "axis", "background", "bgcolor", "big", "blink", "blur", "bold", "border", "call", "caller",
  105. "ceil", "cellpadding", "cellspacing", "char", "charAt", "charCodeAt", "charoff", "charset",
  106. "checked", "childNodes", "cite", "className", "classid", "clear", "click", "clientHeight",
  107. "clientLeft", "clientTop", "clientWidth", "cloneNode", "code", "codebase", "codetype", "color",
  108. "cols", "colspan", "compact", "concat", "content", "coords", "cos", "data", "datetime", "declare",
  109. "deep", "defer", "dir", "disabled", "dispatchEvent", "enctype", "event", "every", "exec", "exp",
  110. "face", "filter", "firstChild", "fixed", "floor", "focus", "fontcolor", "fontsize", "forEach",
  111. "frame", "frameborder", "fromCharCode", "getAttribute", "getAttributeNS", "getAttributeNode",
  112. "getAttributeNodeNS", "getDate", "getDay", "getElementsByTagName", "getElementsByTagNameNS",
  113. "getFullYear", "getHours", "getMilliseconds", "getMinutes", "getMonth", "getSeconds", "getTime",
  114. "getTimezoneOffset", "getUTCDate", "getUTCDay", "getUTCFullYear", "getUTCHours",
  115. "getUTCMilliseconds", "getUTCMinutes", "getUTCMonth", "getUTCSeconds", "getYear", "global",
  116. "handler", "hasAttribute", "hasAttributeNS", "hasAttributes", "hasChildNodes", "hasOwnProperty",
  117. "headers", "height", "href", "hreflang", "hspace", "htmlFor", "httpEquiv", "id", "ignoreCase",
  118. "index", "indexOf", "innerHTML", "input", "insertBefore", "insertedNode", "isPrototypeOf", "ismap",
  119. "italics", "join", "label", "lang", "language", "lastChild", "lastIndex", "lastIndexOf", "length",
  120. "link", "listener", "localName", "log", "longdesc", "map", "marginheight", "marginwidth", "match",
  121. "max", "maxlength", "media", "method", "min", "multiline", "multiple", "name", "namespace",
  122. "namespaceURI", "nextSibling", "node", "nodeName", "nodeType", "nodeValue", "nohref", "noresize",
  123. "normalize", "noshade", "now", "nowrap", "object", "offsetHeight", "offsetLeft", "offsetParent",
  124. "offsetTop", "offsetWidth", "onblur", "onchange", "onclick", "ondblclick", "onfocus", "onkeydown",
  125. "onkeypress", "onkeyup", "onload", "onmousedown", "onmousemove", "onmouseout", "onmouseover",
  126. "onmouseup", "onreset", "onselect", "onsubmit", "onunload", "ownerDocument", "parentNode", "parse",
  127. "pop", "pow", "prefix", "previousSibling", "profile", "prompt", "propertyIsEnumerable", "push",
  128. "random", "readonly", "reduce", "reduceRight", "rel", "removeAttribute", "removeAttributeNS",
  129. "removeAttributeNode", "removeChild", "removeEventListener", "removedNode", "replace",
  130. "replaceChild", "replacedNode", "rev", "reverse", "round", "rows", "rowspan", "rules", "scheme",
  131. "scope", "scrollHeight", "scrollIntoView", "scrollLeft", "scrollTop", "scrollWidth", "scrolling",
  132. "search", "selected", "setAttribute", "setAttributeNS", "setAttributeNode", "setAttributeNodeNS",
  133. "setDate", "setFullYear", "setHours", "setMilliseconds", "setMinutes", "setMonth", "setSeconds",
  134. "setTime", "setUTCDate", "setUTCFullYear", "setUTCHours", "setUTCMilliseconds", "setUTCMinutes",
  135. "setUTCMonth", "setUTCSeconds", "setYear", "shape", "shift", "sin", "size", "slice", "small",
  136. "some", "sort", "source", "span", "splice", "split", "sqrt", "src", "standby", "start", "strike",
  137. "style", "sub", "substr", "substring", "summary", "sup", "tabIndex", "tabindex", "tagName", "tan",
  138. "target", "test", "text", "textContent", "title", "toArray", "toFunction", "toGMTString",
  139. "toLocaleDateString", "toLocaleFormat", "toLocaleString", "toLocaleTimeString", "toLowerCase",
  140. "toSource", "toString", "toUTCString", "toUpperCase", "type", "unshift", "unwatch", "useCapture",
  141. "usemap", "valign", "value", "valueOf", "valuetype", "version", "vlink", "vspace", "watch", "width"
  142. ]);
  143. JS.Package = new JS.Class('Package', {
  144. initialize: function(loader) {
  145. this._loader = loader;
  146. this._deps = [];
  147. this._uses = [];
  148. this._names = [];
  149. this._waiters = [];
  150. this._loading = false;
  151. },
  152. addDependency: function(pkg) {
  153. if (JS.indexOf(this._deps, pkg) === -1) this._deps.push(pkg);
  154. },
  155. addSoftDependency: function(pkg) {
  156. if (JS.indexOf(this._uses, pkg) === -1) this._uses.push(pkg);
  157. },
  158. _getPackage: function(list, index) {
  159. var pkg = list[index];
  160. if (typeof pkg === 'string') pkg = list[index] = this.klass.getByName(pkg);
  161. return pkg;
  162. },
  163. addName: function(name) {
  164. if (!this.contains(name)) this._names.push(name);
  165. },
  166. contains: function(name) {
  167. return JS.indexOf(this._names, name) !== -1;
  168. },
  169. depsComplete: function(deps) {
  170. deps = deps || this._deps;
  171. var n = deps.length, dep;
  172. while (n--) {
  173. if (!this._getPackage(deps, n).isComplete()) return false;
  174. }
  175. return true;
  176. },
  177. isComplete: function() {
  178. return this.isLoaded() &&
  179. this.depsComplete(this._deps) &&
  180. this.depsComplete(this._uses);
  181. },
  182. isLoaded: function(withExceptions) {
  183. if (this._isLoaded) return true;
  184. var names = this._names,
  185. n = names.length,
  186. object;
  187. while (n--) {
  188. object = this.klass.getObject(names[n]);
  189. if (object !== undefined) continue;
  190. if (withExceptions)
  191. throw new Error('Expected package at ' + this._loader + ' to define ' + names[n]);
  192. else
  193. return false;
  194. }
  195. return this._isLoaded = true;
  196. },
  197. readyToLoad: function() {
  198. return !this.isLoaded() && this.depsComplete();
  199. },
  200. expand: function(list) {
  201. var deps = list || [], dep, n;
  202. n = this._deps.length;
  203. while (n--) this._getPackage(this._deps, n).expand(deps);
  204. if (JS.indexOf(deps, this) === -1) deps.push(this);
  205. n = this._uses.length;
  206. while (n--) this._getPackage(this._uses, n).expand(deps);
  207. return deps;
  208. },
  209. onload: function(block) {
  210. this._onload = block;
  211. },
  212. load: function(callback, context) {
  213. var self = this, handler, fireCallbacks;
  214. handler = function() {
  215. self._loading = false;
  216. callback.call(context || null);
  217. };
  218. if (this.isLoaded()) return setTimeout(handler, 1);
  219. this._waiters.push(handler);
  220. if (this._loading) return;
  221. fireCallbacks = function() {
  222. if (JS.isFn(self._onload)) self._onload();
  223. self.isLoaded(true);
  224. for (var i = 0, n = self._waiters.length; i < n; i++) self._waiters[i]();
  225. self._waiters = [];
  226. };
  227. this._loading = true;
  228. JS.isFn(this._loader)
  229. ? this._loader(fireCallbacks)
  230. : this.klass.Loader.loadFile(this._loader, fireCallbacks);
  231. },
  232. toString: function() {
  233. return 'Package:' + this._names[0];
  234. },
  235. extend: {
  236. _store: {},
  237. _env: this,
  238. getByPath: function(loader) {
  239. var path = loader.toString();
  240. return this._store[path] || (this._store[path] = new this(loader));
  241. },
  242. getByName: function(name) {
  243. for (var path in this._store) {
  244. if (this._store[path].contains(name))
  245. return this._store[path];
  246. }
  247. throw new Error('Could not find package containing ' + name);
  248. },
  249. getObject: function(name) {
  250. var object = this._env,
  251. parts = name.split('.'), part;
  252. while (part = parts.shift()) object = (object||{})[part];
  253. return object;
  254. },
  255. expand: function(list) {
  256. var packages = [], i, n;
  257. for (i = 0, n = list.length; i < n; i++)
  258. list[i].expand(packages);
  259. return packages;
  260. },
  261. load: function(list, counter, callback) {
  262. var ready = [],
  263. deferred = [],
  264. n = list.length,
  265. pkg;
  266. while (n--) {
  267. pkg = list[n];
  268. if (pkg.isComplete())
  269. counter -= 1;
  270. else
  271. (pkg.readyToLoad() ? ready : deferred).push(pkg);
  272. }
  273. if (counter === 0) return callback();
  274. n = ready.length;
  275. while (n--) ready[n].load(function() {
  276. this.load(deferred, --counter, callback);
  277. }, this);
  278. }
  279. }
  280. });
  281. JS.Package.extend({
  282. DomLoader: {
  283. usable: function() {
  284. return !!JS.Package.getObject('window.document.getElementsByTagName');
  285. },
  286. __FILE__: function() {
  287. var scripts = document.getElementsByTagName('script');
  288. return scripts[scripts.length - 1].src;
  289. },
  290. loadFile: function(path, fireCallbacks) {
  291. var self = this,
  292. tag = document.createElement('script');
  293. tag.type = 'text/javascript';
  294. tag.src = path;
  295. tag.onload = tag.onreadystatechange = function() {
  296. var state = tag.readyState, status = tag.status;
  297. if ( !state || state === 'loaded' || state === 'complete' || (state === 4 && status === 200) ) {
  298. fireCallbacks();
  299. tag.onload = tag.onreadystatechange = self._K;
  300. tag = null;
  301. }
  302. };
  303. ;;; window.console && console.info('Loading ' + path);
  304. document.getElementsByTagName('head')[0].appendChild(tag);
  305. },
  306. _K: function() {}
  307. },
  308. ServerLoader: {
  309. usable: function() {
  310. return JS.isFn(JS.Package.getObject('load')) &&
  311. JS.isFn(JS.Package.getObject('version'));
  312. },
  313. setup: function() {
  314. var self = this;
  315. load = (function(origLoad) {
  316. return function() {
  317. self._currentPath = arguments[0];
  318. return origLoad.apply(JS.Package._env, arguments);
  319. };
  320. })(load);
  321. },
  322. __FILE__: function() {
  323. return this._currentPath;
  324. },
  325. loadFile: function(path, fireCallbacks) {
  326. load(path);
  327. fireCallbacks();
  328. }
  329. }
  330. });
  331. (function() {
  332. var candidates = [ JS.Package.DomLoader,
  333. JS.Package.ServerLoader ],
  334. n = candidates.length,
  335. i, candidate;
  336. for (i = 0; i < n; i++) {
  337. candidate = candidates[i];
  338. if (candidate.usable()) {
  339. JS.Package.Loader = candidate;
  340. if (candidate.setup) candidate.setup();
  341. break;
  342. }
  343. }
  344. })();
  345. JS.Package.extend({
  346. DSL: {
  347. __FILE__: function() {
  348. return JS.Package.Loader.__FILE__();
  349. },
  350. pkg: function(name, path) {
  351. var pkg = path
  352. ? JS.Package.getByPath(path)
  353. : JS.Package.getByName(name);
  354. pkg.addName(name);
  355. return new JS.Package.Description(pkg);
  356. },
  357. file: function(path) {
  358. var pkg = JS.Package.getByPath(path);
  359. return new JS.Package.Description(pkg);
  360. },
  361. load: function(path, fireCallbacks) {
  362. JS.Package.loadFile(path, fireCallbacks);
  363. }
  364. },
  365. Description: new JS.Class({
  366. initialize: function(pkg) {
  367. this._pkg = pkg;
  368. },
  369. _batch: function(method, args) {
  370. var n = args.length, method = this._pkg[method], i;
  371. for (i = 0; i < n; i++) method.call(this._pkg, args[i]);
  372. return this;
  373. },
  374. provides: function() {
  375. return this._batch('addName', arguments);
  376. },
  377. requires: function() {
  378. return this._batch('addDependency', arguments);
  379. },
  380. uses: function() {
  381. return this._batch('addSoftDependency', arguments);
  382. },
  383. setup: function(block) {
  384. this._pkg.onload(block);
  385. return this;
  386. }
  387. })
  388. });
  389. JS.Package.DSL.loader = JS.Package.DSL.file;
  390. JS.Packages = function(declaration) {
  391. declaration.call(JS.Package.DSL);
  392. };
  393. JS.require = function() {
  394. var args = JS.array(arguments),
  395. requirements = [];
  396. while (typeof args[0] === 'string') requirements.push(JS.Package.getByName(args.shift()));
  397. requirements = JS.Package.expand(requirements);
  398. var fired = false, handler = function() {
  399. if (fired) return;
  400. fired = true;
  401. args[0] && args[0].call(args[1] || null);
  402. };
  403. JS.Package.load(requirements, requirements.length, handler);
  404. };
  405. require = JS.require;
  406. JS.Comparable = new JS.Module('Comparable', {
  407. extend: {
  408. ClassMethods: new JS.Module({
  409. compare: function(one, another) {
  410. return one.compareTo(another);
  411. }
  412. }),
  413. included: function(base) {
  414. base.extend(this.ClassMethods);
  415. }
  416. },
  417. lt: function(other) {
  418. return this.compareTo(other) < 0;
  419. },
  420. lte: function(other) {
  421. return this.compareTo(other) < 1;
  422. },
  423. gt: function(other) {
  424. return this.compareTo(other) > 0;
  425. },
  426. gte: function(other) {
  427. return this.compareTo(other) > -1;
  428. },
  429. eq: function(other) {
  430. return this.compareTo(other) === 0;
  431. },
  432. between: function(a, b) {
  433. return this.gte(a) && this.lte(b);
  434. }
  435. });
  436. JS.Enumerable = new JS.Module('Enumerable', {
  437. extend: {
  438. forEach: function(block, context) {
  439. if (!block) return new JS.Enumerator(this, 'forEach');
  440. for (var i = 0, n = this.length; i < n; i++) {
  441. if (this[i] !== undefined)
  442. block.call(context || null, this[i]);
  443. }
  444. return this;
  445. },
  446. isComparable: function(list) {
  447. return list.all(function(item) { return JS.isFn(item.compareTo) });
  448. },
  449. areEqual: function(one, another) {
  450. return one.equals
  451. ? one.equals(another)
  452. : (one === another);
  453. },
  454. Collection: new JS.Class({
  455. initialize: function(array) {
  456. this.length = 0;
  457. var push = Array.prototype.push;
  458. JS.Enumerable.forEach.call(array, function(item) {
  459. push.call(this, item);
  460. }, this);
  461. }
  462. })
  463. },
  464. all: function(block, context) {
  465. block = JS.Enumerable.toFn(block);
  466. var truth = true;
  467. this.forEach(function(item) {
  468. truth = truth && (block ? block.apply(context || null, arguments) : item);
  469. });
  470. return !!truth;
  471. },
  472. any: function(block, context) {
  473. block = JS.Enumerable.toFn(block);
  474. var truth = false;
  475. this.forEach(function(item) {
  476. truth = truth || (block ? block.apply(context || null, arguments) : item);
  477. });
  478. return !!truth;
  479. },
  480. count: function(block, context) {
  481. if (JS.isFn(this.size)) return this.size();
  482. var count = 0, object = block;
  483. if (block && !JS.isFn(block))
  484. block = function(x) { return JS.Enumerable.areEqual(x, object) };
  485. this.forEach(function() {
  486. if (!block || block.apply(context || null, arguments))
  487. count += 1;
  488. });
  489. return count;
  490. },
  491. cycle: function(n, block, context) {
  492. if (!block) return this.enumFor('cycle', n);
  493. block = JS.Enumerable.toFn(block);
  494. while (n--) this.forEach(block, context);
  495. },
  496. drop: function(n) {
  497. var entries = [];
  498. this.forEachWithIndex(function(item, i) {
  499. if (i >= n) entries.push(item);
  500. });
  501. return entries;
  502. },
  503. dropWhile: function(block, context) {
  504. if (!block) return this.enumFor('dropWhile');
  505. block = JS.Enumerable.toFn(block);
  506. var entries = [],
  507. drop = true;
  508. this.forEach(function(item) {
  509. if (drop) drop = drop && block.apply(context || null, arguments);
  510. if (!drop) entries.push(item);
  511. });
  512. return entries;
  513. },
  514. forEachCons: function(n, block, context) {
  515. if (!block) return this.enumFor('forEachCons', n);
  516. block = JS.Enumerable.toFn(block);
  517. var entries = this.toArray(),
  518. size = entries.length,
  519. limit = size - n,
  520. i;
  521. for (i = 0; i <= limit; i++)
  522. block.call(context || null, entries.slice(i, i+n));
  523. return this;
  524. },
  525. forEachSlice: function(n, block, context) {
  526. if (!block) return this.enumFor('forEachSlice', n);
  527. block = JS.Enumerable.toFn(block);
  528. var entries = this.toArray(),
  529. size = entries.length,
  530. m = Math.ceil(size/n),
  531. i;
  532. for (i = 0; i < m; i++)
  533. block.call(context || null, entries.slice(i*n, (i+1)*n));
  534. return this;
  535. },
  536. forEachWithIndex: function(offset, block, context) {
  537. if (JS.isFn(offset)) {
  538. context = block;
  539. block = offset;
  540. offset = 0;
  541. }
  542. offset = offset || 0;
  543. if (!block) return this.enumFor('forEachWithIndex', offset);
  544. block = JS.Enumerable.toFn(block);
  545. return this.forEach(function(item) {
  546. var result = block.call(context || null, item, offset);
  547. offset += 1;
  548. return result;
  549. });
  550. },
  551. forEachWithObject: function(object, block, context) {
  552. if (!block) return this.enumFor('forEachWithObject', object);
  553. block = JS.Enumerable.toFn(block);
  554. this.forEach(function() {
  555. var args = [object].concat(JS.array(arguments));
  556. block.apply(context || null, args);
  557. });
  558. return object;
  559. },
  560. find: function(block, context) {
  561. if (!block) return this.enumFor('find');
  562. block = JS.Enumerable.toFn(block);
  563. var needle = {}, K = needle;
  564. this.forEach(function(item) {
  565. if (needle !== K) return;
  566. needle = block.apply(context || null, arguments) ? item : needle;
  567. });
  568. return needle === K ? null : needle;
  569. },
  570. findIndex: function(needle, context) {
  571. if (needle === undefined) return this.enumFor('findIndex');
  572. var index = null,
  573. block = JS.isFn(needle);
  574. this.forEachWithIndex(function(item, i) {
  575. if (index !== null) return;
  576. if (JS.Enumerable.areEqual(needle, item) || (block && needle.apply(context || null, arguments)))
  577. index = i;
  578. });
  579. return index;
  580. },
  581. first: function(n) {
  582. var entries = this.toArray();
  583. return (n === undefined) ? entries[0] : entries.slice(0,n);
  584. },
  585. grep: function(pattern, block, context) {
  586. block = JS.Enumerable.toFn(block);
  587. var results = [];
  588. this.forEach(function(item) {
  589. var match = JS.isFn(pattern.match) ? pattern.match(item) : pattern(item);
  590. if (!match) return;
  591. if (block) item = block.apply(context || null, arguments);
  592. results.push(item);
  593. });
  594. return results;
  595. },
  596. groupBy: function(block, context) {
  597. if (!block) return this.enumFor('groupBy');
  598. block = JS.Enumerable.toFn(block);
  599. var hash = new JS.Hash();
  600. this.forEach(function(item) {
  601. var value = block.apply(context || null, arguments);
  602. if (!hash.hasKey(value)) hash.store(value, []);
  603. hash.get(value).push(item);
  604. });
  605. return hash;
  606. },
  607. inject: function(memo, block, context) {
  608. var args = JS.array(arguments),
  609. counter = 0,
  610. K = {};
  611. switch (args.length) {
  612. case 1: memo = K;
  613. block = args[0];
  614. break;
  615. case 2: if (JS.isFn(memo)) {
  616. memo = K;
  617. block = args[0];
  618. context = args[1];
  619. }
  620. }
  621. block = JS.Enumerable.toFn(block);
  622. this.forEach(function(item) {
  623. if (!counter++ && memo === K) return memo = item;
  624. var args = [memo].concat(JS.array(arguments));
  625. memo = block.apply(context || null, args);
  626. });
  627. return memo;
  628. },
  629. map: function(block, context) {
  630. if (!block) return this.enumFor('map');
  631. block = JS.Enumerable.toFn(block);
  632. var map = [];
  633. this.forEach(function() {
  634. map.push(block.apply(context || null, arguments));
  635. });
  636. return map;
  637. },
  638. max: function(block, context) {
  639. return this.minmax(block, context)[1];
  640. },
  641. maxBy: function(block, context) {
  642. if (!block) return this.enumFor('maxBy');
  643. return this.minmaxBy(block, context)[1];
  644. },
  645. member: function(needle) {
  646. return this.any(function(item) { return JS.Enumerable.areEqual(item, needle) });
  647. },
  648. min: function(block, context) {
  649. return this.minmax(block, context)[0];
  650. },
  651. minBy: function(block, context) {
  652. if (!block) return this.enumFor('minBy');
  653. return this.minmaxBy(block, context)[0];
  654. },
  655. minmax: function(block, context) {
  656. var list = this.sort(block, context);
  657. return [list[0], list[list.length - 1]];
  658. },
  659. minmaxBy: function(block, context) {
  660. if (!block) return this.enumFor('minmaxBy');
  661. var list = this.sortBy(block, context);
  662. return [list[0], list[list.length - 1]];
  663. },
  664. none: function(block, context) {
  665. return !this.any(block, context);
  666. },
  667. one: function(block, context) {
  668. block = JS.Enumerable.toFn(block);
  669. var count = 0;
  670. this.forEach(function(item) {
  671. if (block ? block.apply(context || null, arguments) : item) count += 1;
  672. });
  673. return count === 1;
  674. },
  675. partition: function(block, context) {
  676. if (!block) return this.enumFor('partition');
  677. block = JS.Enumerable.toFn(block);
  678. var ayes = [], noes = [];
  679. this.forEach(function(item) {
  680. (block.apply(context || null, arguments) ? ayes : noes).push(item);
  681. });
  682. return [ayes, noes];
  683. },
  684. reject: function(block, context) {
  685. if (!block) return this.enumFor('reject');
  686. block = JS.Enumerable.toFn(block);
  687. var map = [];
  688. this.forEach(function(item) {
  689. if (!block.apply(context || null, arguments)) map.push(item);
  690. });
  691. return map;
  692. },
  693. reverseForEach: function(block, context) {
  694. if (!block) return this.enumFor('reverseForEach');
  695. block = JS.Enumerable.toFn(block);
  696. var entries = this.toArray(),
  697. n = entries.length;
  698. while (n--) block.call(context || null, entries[n]);
  699. return this;
  700. },
  701. select: function(block, context) {
  702. if (!block) return this.enumFor('select');
  703. block = JS.Enumerable.toFn(block);
  704. var map = [];
  705. this.forEach(function(item) {
  706. if (block.apply(context || null, arguments)) map.push(item);
  707. });
  708. return map;
  709. },
  710. sort: function(block, context) {
  711. var comparable = JS.Enumerable.isComparable(this),
  712. entries = this.toArray();
  713. block = block || (comparable
  714. ? function(a,b) { return a.compareTo(b); }
  715. : null);
  716. return block
  717. ? entries.sort(function(a,b) { return block.call(context || null, a, b); })
  718. : entries.sort();
  719. },
  720. sortBy: function(block, context) {
  721. if (!block) return this.enumFor('sortBy');
  722. block = JS.Enumerable.toFn(block);
  723. var util = JS.Enumerable,
  724. map = new util.Collection(this.map(block, context)),
  725. comparable = util.isComparable(map);
  726. return new util.Collection(map.zip(this).sort(function(a, b) {
  727. a = a[0]; b = b[0];
  728. return comparable ? a.compareTo(b) : (a < b ? -1 : (a > b ? 1 : 0));
  729. })).map(function(item) { return item[1]; });
  730. },
  731. take: function(n) {
  732. var entries = [];
  733. this.forEachWithIndex(function(item, i) {
  734. if (i < n) entries.push(item);
  735. });
  736. return entries;
  737. },
  738. takeWhile: function(block, context) {
  739. if (!block) return this.enumFor('takeWhile');
  740. block = JS.Enumerable.toFn(block);
  741. var entries = [],
  742. take = true;
  743. this.forEach(function(item) {
  744. if (take) take = take && block.apply(context || null, arguments);
  745. if (take) entries.push(item);
  746. });
  747. return entries;
  748. },
  749. toArray: function() {
  750. return this.drop(0);
  751. },
  752. zip: function() {
  753. var util = JS.Enumerable,
  754. args = [],
  755. counter = 0,
  756. n = arguments.length,
  757. block, context;
  758. if (JS.isFn(arguments[n-1])) {
  759. block = arguments[n-1]; context = {};
  760. }
  761. if (JS.isFn(arguments[n-2])) {
  762. block = arguments[n-2]; context = arguments[n-1];
  763. }
  764. util.forEach.call(arguments, function(arg) {
  765. if (arg === block || arg === context) return;
  766. if (arg.toArray) arg = arg.toArray();
  767. if (JS.isType(arg, Array)) args.push(arg);
  768. });
  769. var results = this.map(function(item) {
  770. var zip = [item];
  771. util.forEach.call(args, function(arg) {
  772. zip.push(arg[counter] === undefined ? null : arg[counter]);
  773. });
  774. return ++counter && zip;
  775. });
  776. if (!block) return results;
  777. util.forEach.call(results, block, context);
  778. }
  779. });
  780. // http://developer.mozilla.org/en/docs/index.php?title=Core_JavaScript_1.5_Reference:Global_Objects:Array&oldid=58326
  781. JS.Enumerable.include({
  782. forEach: JS.Enumerable.forEach,
  783. collect: JS.Enumerable.instanceMethod('map'),
  784. detect: JS.Enumerable.instanceMethod('find'),
  785. entries: JS.Enumerable.instanceMethod('toArray'),
  786. every: JS.Enumerable.instanceMethod('all'),
  787. findAll: JS.Enumerable.instanceMethod('select'),
  788. filter: JS.Enumerable.instanceMethod('select'),
  789. some: JS.Enumerable.instanceMethod('any'),
  790. extend: {
  791. toFn: function(object) {
  792. if (!object) return object;
  793. if (object.toFunction) return object.toFunction();
  794. if (this.OPS[object]) return this.OPS[object];
  795. if (JS.isType(object, 'string') || JS.isType(object, String))
  796. return function() {
  797. var args = JS.array(arguments),
  798. target = args.shift(),
  799. method = target[object];
  800. return JS.isFn(method) ? method.apply(target, args) : method;
  801. };
  802. return object;
  803. },
  804. OPS: {
  805. '+': function(a,b) { return a + b },
  806. '-': function(a,b) { return a - b },
  807. '*': function(a,b) { return a * b },
  808. '/': function(a,b) { return a / b },
  809. '%': function(a,b) { return a % b },
  810. '^': function(a,b) { return a ^ b },
  811. '&': function(a,b) { return a & b },
  812. '&&': function(a,b) { return a && b },
  813. '|': function(a,b) { return a | b },
  814. '||': function(a,b) { return a || b },
  815. '==': function(a,b) { return a == b },
  816. '!=': function(a,b) { return a != b },
  817. '>': function(a,b) { return a > b },
  818. '>=': function(a,b) { return a >= b },
  819. '<': function(a,b) { return a < b },
  820. '<=': function(a,b) { return a <= b },
  821. '===': function(a,b) { return a === b },
  822. '!==': function(a,b) { return a !== b },
  823. '[]': function(a,b) { return a[b] },
  824. '()': function(a,b) { return a(b) }
  825. },
  826. Enumerator: new JS.Class({
  827. include: JS.Enumerable,
  828. extend: {
  829. DEFAULT_METHOD: 'forEach'
  830. },
  831. initialize: function(object, method, args) {
  832. this._object = object;
  833. this._method = method || this.klass.DEFAULT_METHOD;
  834. this._args = (args || []).slice();
  835. },
  836. forEach: function(block, context) {
  837. if (!block) return this;
  838. var args = this._args.slice();
  839. args.push(block);
  840. if (context) args.push(context);
  841. return this._object[this._method].apply(this._object, args);
  842. },
  843. cons: JS.Enumerable.instanceMethod('forEachCons'),
  844. reverse: JS.Enumerable.instanceMethod('reverseForEach'),
  845. slice: JS.Enumerable.instanceMethod('forEachSlice'),
  846. withIndex: JS.Enumerable.instanceMethod('forEachWithIndex'),
  847. withObject: JS.Enumerable.instanceMethod('forEachWithObject')
  848. })
  849. }
  850. }, false);
  851. JS.Enumerable.Collection.include(JS.Enumerable, true);
  852. JS.Kernel.include({
  853. enumFor: function(method) {
  854. var args = JS.array(arguments),
  855. method = args.shift();
  856. return new JS.Enumerable.Enumerator(this, method, args);
  857. }
  858. }, false);
  859. JS.Kernel.define('toEnum', JS.Kernel.instanceMethod('enumFor'), true);
  860. JS.LinkedList = new JS.Class('LinkedList', {
  861. include: JS.Enumerable || {},
  862. initialize: function(array, useNodes) {
  863. this.length = 0;
  864. this.first = this.last = null;
  865. if (!array) return;
  866. for (var i = 0, n = array.length; i < n; i++)
  867. this.push( useNodes ? new this.klass.Node(array[i]) : array[i] );
  868. },
  869. forEach: function(block, context) {
  870. if (!block) return this.enumFor('forEach');
  871. block = JS.Enumerable.toFn(block);
  872. var node = this.first,
  873. next, i, n;
  874. for (i = 0, n = this.length; i < n; i++) {
  875. next = node.next;
  876. block.call(context || null, node, i);
  877. if (node === this.last) break;
  878. node = next;
  879. }
  880. return this;
  881. },
  882. at: function(n) {
  883. if (n < 0 || n >= this.length) return undefined;
  884. var node = this.first;
  885. while (n--) node = node.next;
  886. return node;
  887. },
  888. pop: function() {
  889. return this.length ? this.remove(this.last) : undefined;
  890. },
  891. shift: function() {
  892. return this.length ? this.remove(this.first) : undefined;
  893. },
  894. // stubs - should be implemented by concrete list types
  895. insertAfter: function() {},
  896. push: function() {},
  897. remove: function() {},
  898. extend: {
  899. Node: new JS.Class({
  900. initialize: function(data) {
  901. this.data = data;
  902. this.prev = this.next = this.list = null;
  903. }
  904. })
  905. }
  906. });
  907. JS.LinkedList.Doubly = new JS.Class('LinkedList.Doubly', JS.LinkedList, {
  908. insertAt: function(n, newNode) {
  909. if (n < 0 || n >= this.length) return;
  910. this.insertBefore(this.at(n), newNode);
  911. },
  912. unshift: function(newNode) {
  913. this.length > 0
  914. ? this.insertBefore(this.first, newNode)
  915. : this.push(newNode);
  916. },
  917. insertBefore: function() {}
  918. });
  919. JS.LinkedList.insertTemplate = function(prev, next, pos) {
  920. return function(node, newNode) {
  921. if (node.list !== this) return;
  922. newNode[prev] = node;
  923. newNode[next] = node[next];
  924. node[next] = (node[next][prev] = newNode);
  925. if (newNode[prev] === this[pos]) this[pos] = newNode;
  926. newNode.list = this;
  927. this.length++;
  928. };
  929. };
  930. JS.LinkedList.Doubly.Circular = new JS.Class('LinkedList.Doubly.Circular', JS.LinkedList.Doubly, {
  931. insertAfter: JS.LinkedList.insertTemplate('prev', 'next', 'last'),
  932. insertBefore: JS.LinkedList.insertTemplate('next', 'prev', 'first'),
  933. push: function(newNode) {
  934. if (this.length)
  935. return this.insertAfter(this.last, newNode);
  936. this.first = this.last =
  937. newNode.prev = newNode.next = newNode;
  938. newNode.list = this;
  939. this.length = 1;
  940. },
  941. remove: function(removed) {
  942. if (removed.list !== this || this.length === 0) return null;
  943. if (this.length > 1) {
  944. removed.prev.next = removed.next;
  945. removed.next.prev = removed.prev;
  946. if (removed === this.first) this.first = removed.next;
  947. if (removed === this.last) this.last = removed.prev;
  948. } else {
  949. this.first = this.last = null;
  950. }
  951. removed.prev = removed.next = removed.list = null;
  952. this.length--;
  953. return removed;
  954. }
  955. });
  956. JS.Hash = new JS.Class('Hash', {
  957. include: JS.Enumerable || {},
  958. extend: {
  959. Pair: new JS.Class({
  960. include: JS.Comparable || {},
  961. setKey: function(key) {
  962. this[0] = this.key = key;
  963. },
  964. hasKey: function(key) {
  965. var my = this.key;
  966. return my.equals ? my.equals(key) : my === key;
  967. },
  968. setValue: function(value) {
  969. this[1] = this.value = value;
  970. },
  971. hasValue: function(value) {
  972. var my = this.value;
  973. return my.equals ? my.equals(value) : my === value;
  974. },
  975. compareTo: function(other) {
  976. return this.key.compareTo
  977. ? this.key.compareTo(other.key)
  978. : (this.key < other.key ? -1 : (this.key > other.key ? 1 : 0));
  979. },
  980. hash: function() {
  981. var key = JS.Hash.codeFor(this.key),
  982. value = JS.Hash.codeFor(this.value);
  983. return [key, value].sort().join('');
  984. }
  985. }),
  986. codeFor: function(object) {
  987. if (typeof object !== 'object') return String(object);
  988. return JS.isFn(object.hash)
  989. ? object.hash()
  990. : object.toString();
  991. }
  992. },
  993. initialize: function(object) {
  994. this.clear();
  995. if (!JS.isType(object, Array)) return this.setDefault(object);
  996. for (var i = 0, n = object.length; i < n; i += 2)
  997. this.store(object[i], object[i+1]);
  998. },
  999. forEach: function(block, context) {
  1000. if (!block) return this.enumFor('forEach');
  1001. block = JS.Enumerable.toFn(block);
  1002. var hash, bucket, i;
  1003. for (hash in this._buckets) {
  1004. if (!this._buckets.hasOwnProperty(hash)) continue;
  1005. bucket = this._buckets[hash];
  1006. i = bucket.length;
  1007. while (i--) block.call(context || null, bucket[i]);
  1008. }
  1009. return this;
  1010. },
  1011. _bucketForKey: function(key, createIfAbsent) {
  1012. var hash = this.klass.codeFor(key),
  1013. bucket = this._buckets[hash];
  1014. if (!bucket && createIfAbsent)
  1015. bucket = this._buckets[hash] = [];
  1016. return bucket;
  1017. },
  1018. _indexInBucket: function(bucket, key) {
  1019. var i = bucket.length,
  1020. ident = !!this._compareByIdentity;
  1021. while (i--) {
  1022. if (ident ? (bucket[i].key === key) : bucket[i].hasKey(key))
  1023. return i;
  1024. }
  1025. return -1;
  1026. },
  1027. assoc: function(key, createIfAbsent) {
  1028. var bucket, index, pair;
  1029. bucket = this._bucketForKey(key, createIfAbsent);
  1030. if (!bucket) return null;
  1031. index = this._indexInBucket(bucket, key);
  1032. if (index > -1) return bucket[index];
  1033. if (!createIfAbsent) return null;
  1034. this.size += 1; this.length += 1;
  1035. pair = new this.klass.Pair;
  1036. pair.setKey(key);
  1037. bucket.push(pair);
  1038. return pair;
  1039. },
  1040. rassoc: function(value) {
  1041. var key = this.key(value);
  1042. return key ? this.assoc(key) : null;
  1043. },
  1044. clear: function() {
  1045. this._buckets = {};
  1046. this.length = this.size = 0;
  1047. },
  1048. compareByIdentity: function() {
  1049. this._compareByIdentity = true;
  1050. },
  1051. comparesByIdentity: function() {
  1052. return !!this._compareByIdentity;
  1053. },
  1054. setDefault: function(value) {
  1055. this._default = value;
  1056. return this;
  1057. },
  1058. getDefault: function(key) {
  1059. return JS.isFn(this._default)
  1060. ? this._default(this, key)
  1061. : (this._default || null);
  1062. },
  1063. equals: function(other) {
  1064. if (!JS.isType(other, JS.Hash) || this.length !== other.length)
  1065. return false;
  1066. var result = true;
  1067. this.forEach(function(pair) {
  1068. if (!result) return;
  1069. var otherPair = other.assoc(pair.key);
  1070. if (otherPair === null || !otherPair.hasValue(pair.value)) result = false;
  1071. });
  1072. return result;
  1073. },
  1074. hash: function() {
  1075. var hashes = [];
  1076. this.forEach(function(pair) { hashes.push(pair.hash()) });
  1077. return hashes.sort().join('');
  1078. },
  1079. fetch: function(key, defaultValue) {
  1080. var pair = this.assoc(key);
  1081. if (pair) return pair.value;
  1082. if (defaultValue === undefined) throw new Error('key not found');
  1083. if (JS.isFn(defaultValue)) return defaultValue(key);
  1084. return defaultValue;
  1085. },
  1086. forEachKey: function(block, context) {
  1087. if (!block) return this.enumFor('forEachKey');
  1088. block = JS.Enumerable.toFn(block);
  1089. this.forEach(function(pair) {
  1090. block.call(context || null, pair.key);
  1091. });
  1092. return this;
  1093. },
  1094. forEachPair: function(block, context) {
  1095. if (!block) return this.enumFor('forEachPair');
  1096. block = JS.Enumerable.toFn(block);
  1097. this.forEach(function(pair) {
  1098. block.call(context || null, pair.key, pair.value);
  1099. });
  1100. return this;
  1101. },
  1102. forEachValue: function(block, context) {
  1103. if (!block) return this.enumFor('forEachValue');
  1104. block = JS.Enumerable.toFn(block);
  1105. this.forEach(function(pair) {
  1106. block.call(context || null, pair.value);
  1107. });
  1108. return this;
  1109. },
  1110. get: function(key) {
  1111. var pair = this.assoc(key);
  1112. return pair ? pair.value : this.getDefault(key);
  1113. },
  1114. hasKey: function(key) {
  1115. return !!this.assoc(key);
  1116. },
  1117. hasValue: function(value) {
  1118. var has = false, ident = !!this._compareByIdentity;
  1119. this.forEach(function(pair) {
  1120. if ((value.equals && !ident) ? value.equals(pair.value) : value === pair.value)
  1121. has = true;
  1122. });
  1123. return has;
  1124. },
  1125. invert: function() {
  1126. var hash = new this.klass;
  1127. this.forEach(function(pair) {
  1128. hash.store(pair.value, pair.key);
  1129. });
  1130. return hash;
  1131. },
  1132. isEmpty: function() {
  1133. for (var hash in this._buckets) {
  1134. if (this._buckets.hasOwnProperty(hash) && this._buckets[hash].length > 0)
  1135. return false;
  1136. }
  1137. return true;
  1138. },
  1139. key: function(value) {
  1140. var result = null;
  1141. this.forEach(function(pair) {
  1142. if (value.equals ? value.equals(pair.value) : (value === pair.value))
  1143. result = pair.key;
  1144. });
  1145. return result;
  1146. },
  1147. keys: function() {
  1148. var keys = [];
  1149. this.forEach(function(pair) { keys.push(pair.key) });
  1150. return keys;
  1151. },
  1152. merge: function(hash, block, context) {
  1153. var newHash = new this.klass;
  1154. newHash.update(this);
  1155. newHash.update(hash, block, context);
  1156. return newHash;
  1157. },
  1158. rehash: function() {
  1159. var temp = new this.klass;
  1160. temp._buckets = this._buckets;
  1161. this.clear();
  1162. this.update(temp);
  1163. },
  1164. remove: function(key, block) {
  1165. if (block === undefined) block = null;
  1166. var bucket, index, result;
  1167. bucket = this._bucketForKey(key);
  1168. if (!bucket) return JS.isFn(block) ? this.fetch(key, block) : this.getDefault(key);
  1169. index = this._indexInBucket(bucket, key);
  1170. if (index < 0) return JS.isFn(block) ? this.fetch(key, block) : this.getDefault(key);
  1171. result = bucket[index].value;
  1172. bucket.splice(index, 1);
  1173. this.size -= 1;
  1174. this.length -= 1;
  1175. if (bucket.length === 0)
  1176. delete this._buckets[this.klass.codeFor(key)];
  1177. return result;
  1178. },
  1179. removeIf: function(block, context) {
  1180. if (!block) return this.enumFor('removeIf');
  1181. block = JS.Enumerable.toFn(block);
  1182. this.forEach(function(pair) {
  1183. if (block.call(context || null, pair))
  1184. this.remove(pair.key);
  1185. }, this);
  1186. return this;
  1187. },
  1188. replace: function(hash) {
  1189. this.clear();
  1190. this.update(hash);
  1191. },
  1192. shift: function() {
  1193. var keys = this.keys();
  1194. if (keys.length === 0) return this.getDefault();
  1195. var pair = this.assoc(keys[0]);
  1196. this.remove(pair.key);
  1197. return pair;
  1198. },
  1199. store: function(key, value) {
  1200. this.assoc(key, true).setValue(value);
  1201. return value;
  1202. },
  1203. update: function(hash, block, context) {
  1204. var blockGiven = JS.isFn(block);
  1205. hash.forEach(function(pair) {
  1206. var key = pair.key, value = pair.value;
  1207. if (blockGiven && this.hasKey(key))
  1208. value = block.call(context || null, key, this.get(key), value);
  1209. this.store(key, value);
  1210. }, this);
  1211. },
  1212. values: function() {
  1213. var values = [];
  1214. this.forEach(function(pair) { values.push(pair.value) });
  1215. return values;
  1216. },
  1217. valuesAt: function() {
  1218. var i = arguments.length, results = [];
  1219. while (i--) results.push(this.get(arguments[i]));
  1220. return results;
  1221. }
  1222. });
  1223. JS.Hash.include({
  1224. includes: JS.Hash.instanceMethod('hasKey'),
  1225. index: JS.Hash.instanceMethod('key'),
  1226. put: JS.Hash.instanceMethod('store')
  1227. }, true);
  1228. JS.Set = new JS.Class('Set', {
  1229. extend: {
  1230. forEach: function(list, block, context) {
  1231. if (!list || !block) return;
  1232. if (list.forEach) return list.forEach(block, context);
  1233. for (var i = 0, n = list.length; i < n; i++) {
  1234. if (list[i] !== undefined)
  1235. block.call(context || null, list[i], i);
  1236. }
  1237. }
  1238. },
  1239. include: JS.Enumerable || {},
  1240. initialize: function(list, block, context) {
  1241. this.clear();
  1242. if (block) this.klass.forEach(list, function(item) {
  1243. this.add(block.call(context || null, item));
  1244. }, this);
  1245. else this.merge(list);
  1246. },
  1247. forEach: function(block, context) {
  1248. if (!block) return this.enumFor('forEach');
  1249. block = JS.Enumerable.toFn(block);
  1250. this.klass.forEach(this._members, block, context);
  1251. return this;
  1252. },
  1253. add: function(item) {
  1254. if (this.contains(item)) return false;
  1255. this._members.push(item);
  1256. this.length = this.size = this._members.length;
  1257. return true;
  1258. },
  1259. classify: function(block, context) {
  1260. if (!block) return this.enumFor('classify');
  1261. block = JS.Enumerable.toFn(block);
  1262. var classes = new JS.Hash();
  1263. this.forEach(function(item) {
  1264. var value = block.call(context || null, item);
  1265. if (!classes.hasKey(value)) classes.store(value, new this.klass);
  1266. classes.get(value).add(item);
  1267. }, this);
  1268. return classes;
  1269. },
  1270. clear: function() {
  1271. this._members = [];
  1272. this.length = this.size = 0;
  1273. },
  1274. complement: function(other) {
  1275. var set = new this.klass;
  1276. this.klass.forEach(other, function(item) {
  1277. if (!this.contains(item)) set.add(item);
  1278. }, this);
  1279. return set;
  1280. },
  1281. contains: function(item) {
  1282. return this._indexOf(item) !== -1;
  1283. },
  1284. difference: function(other) {
  1285. other = JS.isType(other, JS.Set) ? other : new JS.Set(other);
  1286. var set = new this.klass;
  1287. this.forEach(function(item) {
  1288. if (!other.contains(item)) set.add(item);
  1289. });
  1290. return set;
  1291. },
  1292. divide: function(block, context) {
  1293. if (!block) return this.enumFor('divide');
  1294. block = JS.Enumerable.toFn(block);
  1295. var classes = this.classify(block, context),
  1296. sets = new this.klass;
  1297. classes.forEachValue(sets.method('add'));
  1298. return sets;
  1299. },
  1300. equals: function(other) {
  1301. if (this.length !== other.length || !JS.isType(other, JS.Set)) return false;
  1302. var result = true;
  1303. this.forEach(function(item) {
  1304. if (!result) return;
  1305. if (!other.contains(item)) result = false;
  1306. });
  1307. return result;
  1308. },
  1309. hash: function() {
  1310. var hashes = [];
  1311. this.forEach(function(object) { hashes.push(JS.Hash.codeFor(object)) });
  1312. return hashes.sort().join('');
  1313. },
  1314. flatten: function(set) {
  1315. var copy = new this.klass;
  1316. copy._members = this._members;
  1317. if (!set) { set = this; set.clear(); }
  1318. copy.forEach(function(item) {
  1319. if (JS.isType(item, JS.Set)) item.flatten(set);
  1320. else set.add(item);
  1321. });
  1322. return set;
  1323. },
  1324. intersection: function(other) {
  1325. var set = new this.klass;
  1326. this.klass.forEach(other, function(item) {
  1327. if (this.contains(item)) set.add(item);
  1328. }, this);
  1329. return set;
  1330. },
  1331. isEmpty: function() {
  1332. return this._members.length === 0;
  1333. },
  1334. isProperSubset: function(other) {
  1335. return this._members.length < other._members.length && this.isSubset(other);
  1336. },
  1337. isProperSuperset: function(other) {
  1338. return this._members.length > other._members.length && this.isSuperset(other);
  1339. },
  1340. isSubset: function(other) {
  1341. var result = true;
  1342. this.forEach(function(item) {
  1343. if (!result) return;
  1344. if (!other.contains(item)) result = false;
  1345. });
  1346. return result;
  1347. },
  1348. isSuperset: function(other) {
  1349. return other.isSubset(this);
  1350. },
  1351. merge: function(list) {
  1352. this.klass.forEach(list, function(item) { this.add(item) }, this);
  1353. },
  1354. product: function(other) {
  1355. var pairs = new JS.Set;
  1356. this.forEach(function(item) {
  1357. this.klass.forEach(other, function(partner) {
  1358. pairs.add([item, partner]);
  1359. });
  1360. }, this);
  1361. return pairs;
  1362. },
  1363. rebuild: function() {
  1364. var members = this._members;
  1365. this.clear();
  1366. this.merge(members);
  1367. },
  1368. remove: function(item) {
  1369. var index = this._indexOf(item);
  1370. if (index === -1) return;
  1371. this._members.splice(index, 1);
  1372. this.length = this.size = this._members.length;
  1373. },
  1374. removeIf: function(block, context) {
  1375. if (!block) return this.enumFor('removeIf');
  1376. block = JS.Enumerable.toFn(block);
  1377. var members = this._members,
  1378. i = members.length;
  1379. while (i--) {
  1380. if (block.call(context || null, members[i]))
  1381. this.remove(members[i]);
  1382. }
  1383. return this;
  1384. },
  1385. replace: function(other) {
  1386. this.clear();
  1387. this.merge(other);
  1388. },
  1389. subtract: function(list) {
  1390. this.klass.forEach(list, function(item) {
  1391. this.remove(item);
  1392. }, this);
  1393. },
  1394. union: function(other) {
  1395. var set = new this.klass;
  1396. set.merge(this);
  1397. set.merge(other);
  1398. return set;
  1399. },
  1400. xor: function(other) {
  1401. var set = new JS.Set(other);
  1402. this.forEach(function(item) {
  1403. set[set.contains(item) ? 'remove' : 'add'](item);
  1404. });
  1405. return set;
  1406. },
  1407. _indexOf: function(item) {
  1408. var i = this._members.length,
  1409. equal = JS.Enumerable.areEqual;
  1410. while (i--) {
  1411. if (equal(item, this._members[i])) return i;
  1412. }
  1413. return -1;
  1414. }
  1415. });
  1416. JS.Set.include({
  1417. n: JS.Set.instanceMethod('intersection'),
  1418. u: JS.Set.instanceMethod('union'),
  1419. x: JS.Set.instanceMethod('product')
  1420. }, false);
  1421. JS.SortedSet = new JS.Class('SortedSet', JS.Set, {
  1422. extend: {
  1423. compare: function(one, another) {
  1424. return JS.isType(one, Object)
  1425. ? one.compareTo(another)
  1426. : (one < another ? -1 : (one > another ? 1 : 0));
  1427. }
  1428. },
  1429. add: function(item) {
  1430. var point = this._indexOf(item, true);
  1431. if (point === null) return;
  1432. this._members.splice(point, 0, item);
  1433. this.length = this.size = this._members.length;
  1434. },
  1435. _indexOf: function(item, insertionPoint) {
  1436. var items = this._members,
  1437. n = items.length,
  1438. i = 0,
  1439. d = n,
  1440. compare = this.klass.compare,
  1441. equal = JS.Enumerable.areEqual,
  1442. found;
  1443. if (n === 0) return insertionPoint ? 0 : -1;
  1444. if (compare(item, items[0]) < 1) { d = 0; i = 0; }
  1445. if (compare(item, items[n-1]) > 0) { d = 0; i = n; }
  1446. while (!equal(item, items[i]) && d > 0.5) {
  1447. d = d / 2;
  1448. i += (compare(item, items[i]) > 0 ? 1 : -1) * Math.round(d);
  1449. if (i > 0 && compare(item, items[i-1]) > 0 && compare(item, items[i]) < 1) d = 0;
  1450. }
  1451. // The pointer will end up at the start of any homogenous section. Step
  1452. // through the section until we find the needle or until the section ends.
  1453. while (items[i] && !equal(item, items[i]) &&
  1454. compare(item, items[i]) === 0) i += 1;
  1455. found = equal(item, items[i]);
  1456. return insertionPoint
  1457. ? (found ? null : i)
  1458. : (found ? i : -1);
  1459. }
  1460. });
  1461. JS.HashSet = new JS.Class('HashSet', JS.Set, {
  1462. forEach: function(block, context) {
  1463. if (!block) return this.enumFor('forEach');
  1464. block = JS.Enumerable.toFn(block);
  1465. this._members.forEachKey(block, context);
  1466. return this;
  1467. },
  1468. add: function(item) {
  1469. if (this.contains(item)) return false;
  1470. this._members.store(item, true);
  1471. this.length = this.size = this._members.length;
  1472. return true;
  1473. },
  1474. clear: function() {
  1475. this._members = new JS.Hash();
  1476. this.size = this.length = 0;
  1477. },
  1478. contains: function(item) {
  1479. return this._members.hasKey(item);
  1480. },
  1481. rebuild: function() {
  1482. this._members.rehash();
  1483. this.length = this.size = this._members.length;
  1484. },
  1485. remove: function(item) {
  1486. this._members.remove(item);
  1487. this.length = this.size = this._members.length;
  1488. },
  1489. removeIf: function(block, context) {
  1490. if (!block) return this.enumFor('removeIf');
  1491. block = JS.Enumerable.toFn(block);
  1492. this._members.removeIf(function(pair) {
  1493. return block.call(context || null, pair.key);
  1494. });
  1495. this.length = this.size = this._members.length;
  1496. return this;
  1497. }
  1498. });
  1499. JS.Enumerable.include({
  1500. toSet: function(klass, block, context) {
  1501. klass = klass || JS.Set;
  1502. return new klass(this, block, context);
  1503. }
  1504. }, true);
  1505. JS.Observable = new JS.Module('Observable', {
  1506. extend: {
  1507. DEFAULT_METHOD: 'update'
  1508. },
  1509. addObserver: function(observer, context) {
  1510. (this.__observers__ = this.__observers__ || []).push({_block: observer, _context: context || null});
  1511. },
  1512. removeObserver: function(observer, context) {
  1513. this.__observers__ = this.__observers__ || [];
  1514. context = context || null;
  1515. var i = this.countObservers();
  1516. while (i--) {
  1517. if (this.__observers__[i]._block === observer && this.__observers__[i]._context === context) {
  1518. this.__observers__.splice(i,1);
  1519. return;
  1520. }
  1521. }
  1522. },
  1523. removeObservers: function() {
  1524. this.__observers__ = [];
  1525. },
  1526. countObservers: function() {
  1527. return (this.__observers__ = this.__observers__ || []).length;
  1528. },
  1529. notifyObservers: function() {
  1530. if (!this.isChanged()) return;
  1531. var i = this.countObservers(), observer, block, context;
  1532. while (i--) {
  1533. observer = this.__observers__[i];
  1534. block = observer._block;
  1535. context = observer._context;
  1536. if (JS.isFn(block)) block.apply(context || null, arguments);
  1537. else block[context || JS.Observable.DEFAULT_METHOD].apply(block, arguments);
  1538. }
  1539. },
  1540. setChanged: function(state) {
  1541. this.__changed__ = !(state === false);
  1542. },
  1543. isChanged: function() {
  1544. if (this.__changed__ === undefined) this.__changed__ = true;
  1545. return !!this.__changed__;
  1546. }
  1547. });
  1548. JS.Observable.include({
  1549. subscribe: JS.Observable.instanceMethod('addObserver'),
  1550. unsubscribe: JS.Observable.instanceMethod('removeObserver')
  1551. }, true);
  1552. JS.Forwardable = new JS.Module('Forwardable', {
  1553. defineDelegator: function(subject, method, alias, resolve) {
  1554. alias = alias || method;
  1555. this.define(alias, function() {
  1556. var object = this[subject],
  1557. property = object[method];
  1558. return JS.isFn(property)
  1559. ? property.apply(object, arguments)
  1560. : property;
  1561. }, resolve !== false);
  1562. },
  1563. defineDelegators: function() {
  1564. var methods = JS.array(arguments),
  1565. subject = methods.shift(),
  1566. i = methods.length;
  1567. while (i--) this.defineDelegator(subject, methods[i], methods[i], false);
  1568. this.resolve();
  1569. }
  1570. });
  1571. JS.ConstantScope = new JS.Module('ConstantScope', {
  1572. extend: {
  1573. included: function(base) {
  1574. base.__consts__ = new JS.Module();
  1575. base.extend(this.ClassMethods);
  1576. base.include(base.__consts__);
  1577. base.extend(base.__consts__);
  1578. base.include(base.__mod__.__fns__);
  1579. base.extend(base.__eigen__().__fns__);
  1580. },
  1581. ClassMethods: new JS.Module({
  1582. extend: function() {
  1583. var constants = JS.ConstantScope.extract(arguments[0], this);
  1584. this.__consts__.include(constants);
  1585. this.callSuper();
  1586. },
  1587. include: function() {
  1588. var constants = JS.ConstantScope.extract(arguments[0], this);
  1589. this.__consts__.include(constants);
  1590. this.callSuper();
  1591. }
  1592. }),
  1593. extract: function(inclusions, base) {
  1594. if (!inclusions) return null;
  1595. if (JS.isType(inclusions, JS.Module)) return null;
  1596. var constants = {}, key, object;
  1597. for (key in inclusions) {
  1598. if (!/^[A-Z]/.test(key)) continue;
  1599. object = inclusions[key];
  1600. constants[key] = object;
  1601. delete inclusions[key];
  1602. if (JS.isType(object, JS.Module)) {
  1603. object.include(this);
  1604. object.__consts__.include(base.__consts__);
  1605. }
  1606. }
  1607. return constants;
  1608. }
  1609. }
  1610. });
  1611. JS.Decorator = new JS.Class('Decorator', {
  1612. initialize: function(decoree, methods) {
  1613. var decorator = new JS.Class(),
  1614. delegators = {},
  1615. method, func;
  1616. for (method in decoree.prototype) {
  1617. func = decoree.prototype[method];
  1618. if (JS.isFn(func) && func !== decoree) func = this.klass.delegate(method);
  1619. delegators[method] = func;
  1620. }
  1621. decorator.include(new JS.Module(delegators), false);
  1622. decorator.include(this.klass.InstanceMethods, false);
  1623. decorator.include(methods, true);
  1624. return decorator;
  1625. },
  1626. extend: {
  1627. delegate: function(name) {
  1628. return function() {
  1629. return this.component[name].apply(this.component, arguments);
  1630. };
  1631. },
  1632. InstanceMethods: new JS.Module({
  1633. initialize: function(component) {
  1634. this.component = component;
  1635. this.klass = this.constructor = component.klass;
  1636. var method, func;
  1637. for (method in component) {
  1638. if (this[method]) continue;
  1639. func = component[method];
  1640. if (JS.isFn(func)) func = JS.Decorator.delegate(method);
  1641. this[method] = func;
  1642. }
  1643. },
  1644. extend: function(source) {
  1645. this.component.extend(source);
  1646. var method, func;
  1647. for (method in source) {
  1648. func = source[method];
  1649. if (JS.isFn(func)) func = JS.Decorator.delegate(method);
  1650. this[method] = func;
  1651. }
  1652. }
  1653. })
  1654. }
  1655. });
  1656. if (JS.Proxy === undefined) JS.Proxy = {};
  1657. JS.Proxy.Virtual = new JS.Class('Proxy.Virtual', {
  1658. initialize: function(klass) {
  1659. var bridge = function() {},
  1660. proxy = new JS.Class(),
  1661. delegators = {},
  1662. method, func;
  1663. bridge.prototype = klass.prototype;
  1664. for (method in klass.prototype) {
  1665. func = klass.prototype[method];
  1666. if (JS.isFn(func) && func !== klass) func = this.klass.forward(method);
  1667. delegators[method] = func;
  1668. }
  1669. proxy.include({
  1670. initialize: function() {
  1671. var args = arguments,
  1672. subject = null;
  1673. this.__getSubject__ = function() {
  1674. subject = new bridge;
  1675. klass.apply(subject, args);
  1676. return (this.__getSubject__ = function() { return subject; })();
  1677. };
  1678. },
  1679. klass: klass,
  1680. constructor: klass
  1681. }, false);
  1682. proxy.include(new JS.Module(delegators), false);
  1683. proxy.include(this.klass.InstanceMethods, true);
  1684. return proxy;
  1685. },
  1686. extend: {
  1687. forward: function(name) {
  1688. return function() {
  1689. var subject = this.__getSubject__();
  1690. return subject[name].apply(subject, arguments);
  1691. };
  1692. },
  1693. InstanceMethods: new JS.Module({
  1694. extend: function(source) {
  1695. this.__getSubject__().extend(source);
  1696. var method, func;
  1697. for (method in source) {
  1698. func = source[method];
  1699. if (JS.isFn(func)) func = JS.Proxy.Virtual.forward(method);
  1700. this[method] = func;
  1701. }
  1702. }
  1703. })
  1704. }
  1705. });
  1706. JS.Command = new JS.Class('Command', {
  1707. initialize: function(functions) {
  1708. if (JS.isFn(functions))
  1709. functions = {execute: functions};
  1710. this._functions = functions;
  1711. this._stack = this._functions.stack || null;
  1712. },
  1713. execute: function(push) {
  1714. if (this._stack) this._stack._restart();
  1715. var exec = this._functions.execute;
  1716. if (exec) exec.apply(this);
  1717. if (this._stack && push !== false) this._stack.push(this);
  1718. },
  1719. undo: function() {
  1720. var exec = this._functions.undo;
  1721. if (exec) exec.apply(this);
  1722. },
  1723. extend: {
  1724. Stack: new JS.Class({
  1725. include: [JS.Observable || {}, JS.Enumerable || {}],
  1726. initialize: function(options) {
  1727. options = options || {};
  1728. this._redo = options.redo || null;
  1729. this.clear();
  1730. },
  1731. forEach: function(block, context) {
  1732. if (!block) return this.enumFor('forEach');
  1733. block = JS.Enumerable.toFn(block);
  1734. for (var i = 0, n = this._stack.length; i < n; i++) {
  1735. if (this._stack[i] !== undefined)
  1736. block.call(context || null, this._stack[i], i);
  1737. }
  1738. return this;
  1739. },
  1740. clear: function() {
  1741. this._stack = [];
  1742. this.length = this.pointer = 0;
  1743. },
  1744. _restart: function() {
  1745. if (this.pointer === 0 && this._redo && this._redo.execute)
  1746. this._redo.execute();
  1747. },
  1748. push: function(command) {
  1749. this._stack.splice(this.pointer, this.length);
  1750. this._stack.push(command);
  1751. this.length = this.pointer = this._stack.length;
  1752. if (this.notifyObservers) this.notifyObservers(this);
  1753. },
  1754. stepTo: function(position) {
  1755. if (position < 0 || position > this.length) return;
  1756. var i, n;
  1757. switch (true) {
  1758. case position > this.pointer :
  1759. for (i = this.pointer, n = position; i < n; i++)
  1760. this._stack[i].execute(false);
  1761. break;
  1762. case position < this.pointer :
  1763. if (this._redo && this._redo.execute) {
  1764. this._redo.execute();
  1765. for (i = 0, n = position; i < n; i++)
  1766. this._stack[i].execute(false);
  1767. } else {
  1768. for (i = 0, n = this.pointer - position; i < n; i++)
  1769. this._stack[this.pointer - i - 1].undo();
  1770. }
  1771. break;
  1772. }
  1773. this.pointer = position;
  1774. if (this.notifyObservers) this.notifyObservers(this);
  1775. },
  1776. undo: function() {
  1777. this.stepTo(this.pointer - 1);
  1778. },
  1779. redo: function() {
  1780. this.stepTo(this.pointer + 1);
  1781. }
  1782. })
  1783. }
  1784. });
  1785. JS.State = new JS.Module('State', {
  1786. __getState__: function(state) {
  1787. return (typeof state === 'object' && state) ||
  1788. (typeof state === 'string' && ((this.states || {})[state] || {})) ||
  1789. {};
  1790. },
  1791. setState: function(state) {
  1792. this.__state__ = this.__getState__(state);
  1793. JS.State.addMethods(this.__state__, this.klass);
  1794. },
  1795. inState: function() {
  1796. var i = arguments.length;
  1797. while (i--) {
  1798. if (this.__state__ === this.__getState__(arguments[i])) return true;
  1799. }
  1800. return false;
  1801. },
  1802. extend: {
  1803. stub: function() { return this; },
  1804. buildStubs: function(stubs, collection, states) {
  1805. var state, method;
  1806. for (state in states) {
  1807. collection[state] = {};
  1808. for (method in states[state]) stubs[method] = this.stub;
  1809. } },
  1810. buildCollection: function(module, states) {
  1811. var stubs = {},
  1812. collection = {},
  1813. superstates = module.lookup('states').pop() || {},
  1814. state, klass, methods, name;
  1815. this.buildStubs(stubs, collection, states);
  1816. this.buildStubs(stubs, collection, superstates);
  1817. for (state in collection) {
  1818. klass = (superstates[state]||{}).klass;
  1819. klass = klass ? new JS.Class(klass, states[state]) : new JS.Class(states[state]);
  1820. methods = {};
  1821. for (name in stubs) { if (!klass.prototype[name]) methods[name] = stubs[name]; }
  1822. klass.include(methods, false);
  1823. collection[state] = new klass;
  1824. }
  1825. if (module.__res__) this.addMethods(stubs, module.__res__.klass);
  1826. return collection;
  1827. },
  1828. addMethods: function(state, klass) {
  1829. if (!klass) return;
  1830. var methods = {},
  1831. p = klass.prototype,
  1832. method;
  1833. for (method in state) {
  1834. if (p[method]) continue;
  1835. p[method] = klass.__mod__.__fns__[method] = this.wrapped(method);
  1836. }
  1837. },
  1838. wrapped: function(method) {
  1839. return function() {
  1840. var func = (this.__state__ || {})[method];
  1841. return func ? func.apply(this, arguments): this;
  1842. };
  1843. }
  1844. }
  1845. });
  1846. JS.Module.include({define: (function(wrapped) {
  1847. return function(name, block) {
  1848. if (name === 'states' && typeof block === 'object')
  1849. arguments[1] = JS.State.buildCollection(this, block);
  1850. return wrapped.apply(this, arguments);
  1851. };
  1852. })(JS.Module.prototype.define)}, true);
  1853. JS.StackTrace = new JS.Module('StackTrace', {
  1854. extend: {
  1855. included: function(base) {
  1856. var module = base.__mod__ || base,
  1857. self = this,
  1858. method;
  1859. module.extend({define: function(name, func) {
  1860. if (!JS.isFn(func)) return this.callSuper();
  1861. var wrapper = self.wrap(func, module, name);
  1862. return this.callSuper(name, wrapper);
  1863. } });
  1864. for (method in module.__fns__)
  1865. module.define(method, module.__fns__[method], false);
  1866. module.resolve();
  1867. if (!module.__nom__) setTimeout(function() {
  1868. module.__nom__ = self.nameOf(base);
  1869. }, 1);
  1870. },
  1871. nameOf: function(object, root) {
  1872. var results = [], i, n, field, l;
  1873. if (JS.isType(object, Array)) {
  1874. for (i = 0, n = object.length; i < n; i++)
  1875. results.push(this.nameOf(object[i]));
  1876. return results;
  1877. }
  1878. if (object.__nom__) return object.__nom__;
  1879. field = [{name: null, o: root || this.root}];
  1880. l = 0;
  1881. while (typeof field === 'object' && l < this.maxDepth) {
  1882. l += 1;
  1883. field = this.descend(field, object);
  1884. }
  1885. if (typeof field == 'string') {
  1886. field = field.replace(/\.prototype\./g, '#');
  1887. object.__nom__ = field;
  1888. if (object.__meta__) object.__meta__.__nom__ = field + '.__meta__';
  1889. }
  1890. return object.__nom__;
  1891. },
  1892. descend: function(list, needle) {
  1893. var results = [],
  1894. n = list.length,
  1895. i = n,
  1896. key, item, name;
  1897. while (i--) {
  1898. item = list[i];
  1899. if (n > 1 && JS.indexOf(this.excluded, item.o) !== -1) continue;
  1900. if (JS.isType(item.o, Array)) continue;
  1901. name = item.name ? item.name + '.' : '';
  1902. for (key in item.o) {
  1903. if (needle && item.o[key] === needle) return name + key;
  1904. results.push({name: name + key, o: item.o[key]});
  1905. }
  1906. }
  1907. return results;
  1908. },
  1909. root: this,
  1910. excluded: [],
  1911. maxDepth: 8,
  1912. logLevel: 'full',
  1913. stack: new JS.Singleton({
  1914. _list: [],
  1915. indent: function() {
  1916. var indent = '',
  1917. n = this._list.length;
  1918. while (n--) indent += '| ';
  1919. return indent;
  1920. },
  1921. push: function(name, object, args) {
  1922. if (JS.StackTrace.logLevel === 'full') window.console &&
  1923. console.log(this.indent() + name + '(', args, ')');
  1924. this._list.push({name: name, object: object, args: args});
  1925. },
  1926. pop: function(result) {
  1927. var name = this._list.pop().name;
  1928. if (JS.StackTrace.logLevel === 'full') window.console &&
  1929. console.log(this.indent() + name + '() --> ', result);
  1930. return name;
  1931. },
  1932. top: function() {
  1933. return this._list[this._list.length - 1] || {};
  1934. },
  1935. backtrace: function() {
  1936. var i = this._list.length, item;
  1937. while (i--) {
  1938. item = this._list[i];
  1939. window.console && console.log(item.name, 'in', item.object, '(', item.args, ')');
  1940. }
  1941. }
  1942. }),
  1943. flush: function() {
  1944. this.stack._list = [];
  1945. },
  1946. print: function() {
  1947. this.stack.backtrace();
  1948. },
  1949. wrap: function(func, module, name) {
  1950. var self = JS.StackTrace;
  1951. var wrapper = function() {
  1952. var result, fullName = self.nameOf(module) + '#' + name;
  1953. self.stack.push(fullName, this, arguments);
  1954. if (self.logLevel === 'errors') {
  1955. try { result = func.apply(this, arguments); }
  1956. catch (e) {
  1957. if (e.logged) throw e;
  1958. e.logged = true;
  1959. window.console && console.error(e, 'thrown by', self.stack.top().name + '. Backtrace:');
  1960. self.print();
  1961. self.flush();
  1962. throw e;
  1963. }
  1964. } else {
  1965. result = func.apply(this, arguments);
  1966. }
  1967. self.stack.pop(result);
  1968. return result;
  1969. };
  1970. wrapper.toString = function() { return func.toString() };
  1971. return wrapper;
  1972. }
  1973. }
  1974. });
  1975. (function() {
  1976. var module = JS.StackTrace, key;
  1977. for (key in module.root) {
  1978. if (key !== 'JS') module.excluded.push(module.root[key]);
  1979. }
  1980. })();
  1981. JS.Ruby = function(klass, define) {
  1982. define.call(new JS.Ruby.ClassBuilder(klass));
  1983. };
  1984. JS.extend(JS.Ruby, {
  1985. extendDSL: function(builder, source) {
  1986. for (var method in source) {
  1987. if (builder[method] || !JS.isFn(source[method])) continue;
  1988. this.addMethod(builder, source, method);
  1989. }
  1990. },
  1991. addMethod: function(builder, source, method) {
  1992. builder[method] = function() {
  1993. var result = source[method].apply(source, arguments);
  1994. JS.Ruby.extendDSL(builder, source);
  1995. return result;
  1996. };
  1997. },
  1998. alias: function(object, builder) {
  1999. return function(newName, oldName) {
  2000. var old = object[oldName];
  2001. if (old !== undefined) this.def(newName, old);
  2002. if (builder) JS.Ruby.extendDSL(builder, object);
  2003. };
  2004. },
  2005. ClassBuilder: function(klass) {
  2006. this.def = klass.method('define');
  2007. this.alias = JS.Ruby.alias(klass.prototype);
  2008. this.self = {
  2009. def: JS.bind(function(name, method) {
  2010. var def = {}; def[name] = method;
  2011. klass.extend(def);
  2012. JS.Ruby.extendDSL(this, klass);
  2013. }, this),
  2014. alias: JS.Ruby.alias(klass, this)
  2015. };
  2016. JS.Ruby.extendDSL(this, klass);
  2017. }
  2018. });