PageRenderTime 61ms CodeModel.GetById 25ms RepoModel.GetById 1ms 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

Large files files are truncated, but you can click here to view the full 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(fu

Large files files are truncated, but you can click here to view the full file