/files/ixjs/1.0.6/ix.js

https://gitlab.com/Mirros/jsdelivr · JavaScript · 1248 lines · 869 code · 101 blank · 278 comment · 185 complexity · 88e6ab236b664904fa6c3a4ac16d5c33 MD5 · raw file

  1. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
  2. (function (root, factory) {
  3. var freeExports = typeof exports == 'object' && exports &&
  4. (typeof root == 'object' && root && root == root.global && (window = root), exports);
  5. // Because of build optimizers
  6. if (typeof define === 'function' && define.amd) {
  7. define(['Ix', 'exports'], function (Ix, exports) {
  8. root.Ix = factory(root, exports, Ix);
  9. return root.Ix;
  10. });
  11. } else if (typeof module == 'object' && module && module.exports == freeExports) {
  12. module.exports = factory(root, module.exports, require('./l2o'));
  13. } else {
  14. root.Ix = factory(root, {}, root.Ix);
  15. }
  16. }(this, function (global, exp, root, undefined) {
  17. var isEqual = root.Internals.isEqual;
  18. function noop () { }
  19. function identity (x) { return x; }
  20. function defaultComparer (x, y) { return x > y ? 1 : x < y ? -1 : 0; }
  21. function defaultEqualityComparer (x, y) { return isEqual(x, y); }
  22. function arrayIndexOf(key, comparer) {
  23. comparer || (comparer = defaultEqualityComparer);
  24. for (var i = 0, len = this.length; i < len; i++) {
  25. if (comparer(key, this[i])) {
  26. return i;
  27. }
  28. }
  29. return -1;
  30. }
  31. var seqNoElements = 'Sequence contains no elements.';
  32. var objectDisposed = 'Object disposed';
  33. var slice = Array.prototype.slice;
  34. var Enumerable = root.Enumerable,
  35. EnumerablePrototype = Enumerable.prototype,
  36. enumerableConcat = Enumerable.concat,
  37. enumerableEmpty = Enumerable.empty,
  38. enumerableFromArray = Enumerable.fromArray,
  39. enumerableRepeat = Enumerable.repeat,
  40. enumeratorCreate = root.Enumerator.create
  41. inherits = root.Internals.inherits;
  42. /**
  43. * Determines whether an enumerable sequence is empty.
  44. * @return {Boolean} true if the sequence is empty; false otherwise.
  45. */
  46. EnumerablePrototype.isEmpty = function () {
  47. return !this.any();
  48. };
  49. function extremaBy (source, keySelector, comparer) {
  50. var result = [], e = source.getEnumerator();
  51. try {
  52. if (!e.moveNext()) { throw new Error(seqNoElements); }
  53. var current = e.getCurrent(),
  54. resKey = keySelector(current);
  55. result.push(current);
  56. while (e.moveNext()) {
  57. var cur = e.getCurrent(),
  58. key = keySelector(cur),
  59. cmp = comparer(key, resKey);
  60. if (cmp === 0) {
  61. result.push(cur);
  62. } else if (cmp > 0) {
  63. result = [cur];
  64. resKey = key;
  65. }
  66. }
  67. } finally {
  68. e.dispose();
  69. }
  70. return enumerableFromArray(result);
  71. }
  72. /**
  73. * Returns the elements with the minimum key value by using the specified comparer to compare key values.
  74. * @param keySelector Key selector used to extract the key for each element in the sequence.
  75. * @param comparer Comparer used to determine the minimum key value.
  76. * @return List with the elements that share the same minimum key value.
  77. */
  78. EnumerablePrototype.minBy = function (keySelector, comparer) {
  79. comparer || (comparer = defaultComparer);
  80. return extremaBy(this, keySelector, function (key, minValue) {
  81. return -comparer(key, minValue);
  82. });
  83. };
  84. /**
  85. * Returns the elements with the minimum key value by using the specified comparer to compare key values.
  86. * @param keySelector Key selector used to extract the key for each element in the sequence.
  87. * @param comparer Comparer used to determine the maximum key value.
  88. * @return List with the elements that share the same maximum key value.
  89. */
  90. EnumerablePrototype.maxBy = function (keySelector, comparer) {
  91. comparer || (comparer = defaultComparer);
  92. return extremaBy(this, keySelector, comparer);
  93. };
  94. var SharedBuffer = (function () {
  95. inherits(SharedBuffer, Enumerable);
  96. function SharedBuffer (source) {
  97. this.disposed = false;
  98. this.source = source;
  99. }
  100. SharedBuffer.prototype.getEnumerator = function () {
  101. if (this.disposed) {
  102. throw new Error('Object disposed');
  103. }
  104. var current, self = this;
  105. return enumeratorCreate(
  106. function () {
  107. if (self.source.moveNext()) {
  108. current = self.source.getCurrent();
  109. return true;
  110. }
  111. return false;
  112. },
  113. function () { return current; });
  114. };
  115. SharedBuffer.prototype.dispose = function () {
  116. if (!this.disposed) {
  117. this.disposed = true;
  118. this.source.dispose();
  119. this.source = null;
  120. }
  121. };
  122. return SharedBuffer;
  123. }());
  124. /**
  125. * Shares the source sequence within a selector function where each enumerator can fetch the next element from the source sequence.
  126. *
  127. * var rng = Enumerable.range(0, 10).share();
  128. *
  129. * var e1 = rng.getEnumerator(); // Both e1 and e2 will consume elements from
  130. * var e2 = rng.getEnumerator(); // the source sequence.
  131. *
  132. * ok(e1.moveNext());
  133. * equal(0, e1.getCurrent());
  134. *
  135. * ok(e1.moveNext());
  136. * equal(1, e1.getCurrent());
  137. *
  138. * ok(e2.moveNext()); // e2 "steals" element 2
  139. * equal(2, e2.getCurrent());
  140. *
  141. * ok(e1.moveNext()); // e1 can't see element 2
  142. * equal(3, e1.getCurrent());
  143. *
  144. * @param {Function} [selector] Selector function with shared access to the source sequence for each enumerator.
  145. * @return Sequence resulting from applying the selector function to the shared view over the source sequence.
  146. */
  147. EnumerablePrototype.share = function (selector) {
  148. var source = this;
  149. return !selector ?
  150. new SharedBuffer(source.getEnumerator()) :
  151. new Enumerable(function () { return selector(source.share()).getEnumerator(); });
  152. };
  153. function RefCountList(readerCount) {
  154. this.readerCount = readerCount;
  155. this.list = {};
  156. this.length = 0;
  157. }
  158. var RefCountListPrototype = RefCountList.prototype;
  159. RefCountListPrototype.clear = function () {
  160. this.list = {};
  161. this.length = 0;
  162. };
  163. RefCountListPrototype.get = function (i) {
  164. if (!this.list[i]) {
  165. throw new Error('Element no longer available in the buffer.');
  166. }
  167. var res = this.list[i];
  168. if (--res.length === 0) { delete this.list[i]; }
  169. return res.value;
  170. };
  171. RefCountListPrototype.push = function (item) {
  172. this.list[this.length] = { value: item, length: this.readerCount };
  173. this.length++;
  174. };
  175. RefCountListPrototype.done = function (index) {
  176. for (var i = index; i < this.length; i++) {
  177. this.get(i);
  178. }
  179. this.readerCount--;
  180. };
  181. var PublishedBuffer = (function () {
  182. inherits(PublishedBuffer, Enumerable);
  183. function PublishedBuffer(source) {
  184. this.source = source;
  185. this.buffer = new RefCountList(0);
  186. this.disposed = false;
  187. this.stopped = false;
  188. this.error = null;
  189. }
  190. function getEnumerator(i) {
  191. var currentValue, self = this, isDisposed = false, isFirst = true, isDone = false;
  192. return enumeratorCreate(
  193. function () {
  194. if (self.disposed) { throw new Error('Object disposed'); }
  195. if (!isFirst) { i++; }
  196. var hasValue = false, current;
  197. if (i >= self.buffer.length) {
  198. if (!self.stopped) {
  199. try {
  200. hasValue = self.source.moveNext();
  201. if (hasValue) { current = self.source.getCurrent(); }
  202. } catch (e) {
  203. self.stopped = true;
  204. self.error = e;
  205. self.source.dispose();
  206. isDisposed = true;
  207. }
  208. }
  209. if (self.stopped) {
  210. if (self.error) {
  211. self.buffer && self.buffer.done(i + 1);
  212. isDone = true;
  213. throw self.error;
  214. } else {
  215. self.buffer && self.buffer.done(i + 1);
  216. isDone = true;
  217. return false;
  218. }
  219. }
  220. if (hasValue) {
  221. self.buffer.push(current);
  222. }
  223. } else {
  224. hasValue = true;
  225. }
  226. if (hasValue) {
  227. currentValue = self.buffer.get(i);
  228. isFirst = false;
  229. return true;
  230. } else {
  231. self.buffer && self.buffer.done(i + 1);
  232. isDone = true;
  233. return false;
  234. }
  235. },
  236. function () { return currentValue; },
  237. function () { isDone && self.buffer && self.buffer.done(i); }
  238. );
  239. }
  240. PublishedBuffer.prototype.getEnumerator = function () {
  241. if (this.disposed) {
  242. throw new Error('Object disposed');
  243. }
  244. var i = this.buffer.length;
  245. this.buffer.readerCount++;
  246. return getEnumerator.call(this, i);
  247. };
  248. PublishedBuffer.prototype.dispose = function () {
  249. if (!this.disposed) {
  250. this.source.dispose();
  251. this.source = null;
  252. this.buffer.clear();
  253. this.buffer = null;
  254. this.disposed = true;
  255. }
  256. };
  257. return PublishedBuffer;
  258. }());
  259. /**
  260. * Publishes the source sequence within a selector function where each enumerator can obtain a view over a tail of the source sequence.
  261. *
  262. * var rng = Enumerable.Range(0, 10).Publish();
  263. *
  264. * var e1 = rng.getEnumerator(); // e1 has a view on the source starting from element 0
  265. *
  266. * ok(e1.moveNext());
  267. * equal(0, e1.getCurrent());
  268. *
  269. * ok(e1.moveNext());
  270. * equal(1, e1.getCurrent());
  271. *
  272. * var e2 = rng.getEnumerator();
  273. *
  274. * ok(e2.moveNext()); // e2 has a view on the source starting from element 2
  275. * equal(2, e2.getCurrent());
  276. *
  277. * ok(e1.moveNext()); // e1 continues to enumerate over its view
  278. * equal(2, e1.getCurrent());
  279. *
  280. * @param selector Selector function with published access to the source sequence for each enumerator.
  281. * @return Sequence resulting from applying the selector function to the published view over the source sequence.
  282. */
  283. EnumerablePrototype.publish = function (selector) {
  284. var source = this;
  285. return !selector ?
  286. new PublishedBuffer(source.getEnumerator()) :
  287. new Enumerable(function () { return selector(source.publish()).getEnumerator(); });
  288. };
  289. function MaxRefCountList() {
  290. this.list = [];
  291. this.length = 0;
  292. }
  293. var MaxRefCountListPrototype = MaxRefCountList.prototype;
  294. MaxRefCountListPrototype.done = noop;
  295. MaxRefCountListPrototype.push = function (item) {
  296. this.list[this.length++] = item;
  297. };
  298. MaxRefCountListPrototype.clear = function () {
  299. this.list = [];
  300. this.length = 0;
  301. };
  302. MaxRefCountListPrototype.get = function (i) {
  303. return this.list[i];
  304. };
  305. var MemoizedBuffer = (function () {
  306. inherits(MemoizedBuffer, Enumerable);
  307. function MemoizedBuffer(source, buffer) {
  308. this.source = source;
  309. this.buffer = buffer
  310. this.stopped = false;
  311. this.error = null;
  312. this.disposed = false;
  313. }
  314. MemoizedBuffer.prototype.getEnumerator = function () {
  315. if (this.disposed) {
  316. throw new Error('Object disposed');
  317. }
  318. var i = 0, currentValue, self = this, isDisposed = false, isFirst = true, isDone = false;
  319. return enumeratorCreate(
  320. function () {
  321. if (self.disposed) { throw new Error('Object disposed'); }
  322. if (!isFirst) { i++; }
  323. var hasValue = false, current;
  324. if (i >= self.buffer.length) {
  325. if (!self.stopped) {
  326. try {
  327. hasValue = self.source.moveNext();
  328. if (hasValue) { current = self.source.getCurrent(); }
  329. } catch (e) {
  330. self.stopped = true;
  331. self.error = e;
  332. self.source.dispose();
  333. isDisposed = true;
  334. }
  335. }
  336. if (self.stopped) {
  337. if (self.error) {
  338. self.buffer && self.buffer.done(i + 1);
  339. isDone = true;
  340. throw self.error;
  341. } else {
  342. self.buffer && self.buffer.done(i + 1);
  343. isDone = true;
  344. return false;
  345. }
  346. }
  347. if (hasValue) {
  348. self.buffer.push(current);
  349. }
  350. } else {
  351. hasValue = true;
  352. }
  353. if (hasValue) {
  354. currentValue = self.buffer.get(i);
  355. isFirst = false;
  356. return true;
  357. } else {
  358. self.buffer && self.buffer.done(i + 1);
  359. isDone = true;
  360. return false;
  361. }
  362. },
  363. function () { return currentValue; },
  364. function () { isDone && self.buffer && self.buffer.done(i); }
  365. );
  366. };
  367. MemoizedBuffer.prototype.dispose = function () {
  368. if (!this.disposed) {
  369. this.source.dispose();
  370. this.source = null;
  371. this.buffer.clear();
  372. this.buffer = null;
  373. this.disposed = true;
  374. }
  375. };
  376. return MemoizedBuffer;
  377. }());
  378. /**
  379. * Memoizes the source sequence within a selector function where a specified number of enumerators can get access to all of the sequence's elements without causing multiple enumerations over the source.
  380. *
  381. * var rng = Enumerable.range(0, 10).doAction(function (x) { console.log(x); }).memoize();
  382. *
  383. * var e1 = rng.getEnumerator();
  384. *
  385. * ok(e1.moveNext()); // Prints 0
  386. * equal(0, e1.getCurrent());
  387. *
  388. * ok(e1.moveNext()); // Prints 1
  389. * equal(1, e1.getCurrent());
  390. *
  391. * var e2 = rng.getEnumerator();
  392. *
  393. * ok(e2.moveNext()); // Doesn't print anything; the side-effect of Do
  394. * equal(0, e2.getCurrent()); // has already taken place during e1's iteration.
  395. *
  396. * ok(e1.moveNext()); // Prints 2
  397. * equal(2, e1.getCurrent());
  398. *
  399. * @param readerCount Number of enumerators that can access the underlying buffer. Once every enumerator has obtained an element from the buffer, the element is removed from the buffer.
  400. * @param selector Selector function with memoized access to the source sequence for a specified number of enumerators.
  401. * @return Sequence resulting from applying the selector function to the memoized view over the source sequence.
  402. */
  403. EnumerablePrototype.memoize = function () {
  404. var source = this;
  405. if (arguments.length === 0) {
  406. return new MemoizedBuffer(source.getEnumerator(), new MaxRefCountList());
  407. } else if (arguments.length === 1 && typeof arguments[0] === 'function') {
  408. return new Enumerable(function () { return arguments[1](source.memoize()).getEnumerator(); });
  409. } else if (arguments.length === 1 && typeof arguments[0] === 'number') {
  410. return new MemoizedBuffer(source.getEnumerator(), new RefCountList(arguments[0]));
  411. } else {
  412. return new Enumerable(function () { return arguments[1](source.memoize(arguments[0])).getEnumerator(); });
  413. }
  414. };
  415. /**
  416. * Returns a sequence that throws an exception upon enumeration. An alias for this method is throwException for <IE9.
  417. * @example
  418. * var result = Enumerable.throw(new Error('error'));
  419. * @param {Object} exception Exception to throw upon enumerating the resulting sequence.
  420. * @returns {Enumerable} Sequence that throws the specified exception upon enumeration.
  421. */
  422. Enumerable['throw'] = Enumerable.throwException = function (value) {
  423. return new Enumerable(function () {
  424. return enumeratorCreate(
  425. function () { throw value; },
  426. noop);
  427. });
  428. };
  429. /**
  430. * Creates an enumerable sequence based on an enumerable factory function.
  431. * @example
  432. * var result = Enumerable.defer(function () { return Enumerable.range(0, 10); });
  433. * @param {Function} enumerableFactory Enumerable factory function.
  434. * @returns {Enumerable} Sequence that will invoke the enumerable factory upon a call to GetEnumerator.
  435. */
  436. var enumerableDefer = Enumerable.defer = function (enumerableFactory) {
  437. return new Enumerable(function () {
  438. var enumerator;
  439. return enumeratorCreate(function () {
  440. enumerator || (enumerator = enumerableFactory().getEnumerator());
  441. return enumerator.moveNext();
  442. }, function () {
  443. return enumerator.getCurrent();
  444. }, function () {
  445. enumerator.dispose();
  446. });
  447. });
  448. };
  449. /**
  450. * Generates a sequence by mimicking a for loop.
  451. * @example
  452. * var result = Enumerable.generate(
  453. * 0,
  454. * function (x) { return x < 10; },
  455. * function (x) { return x + 1; },
  456. * function (x) { return x * x });
  457. * @param {Any} initialState Initial state of the generator loop.
  458. * @param {Function} condition Loop condition.
  459. * @param {Function} iterate State update function to run after every iteration of the generator loop.
  460. * @param {Function} resultSelector Result selector to compute resulting sequence elements.
  461. * @returns {Enumerable} Sequence obtained by running the generator loop, yielding computed elements.
  462. */
  463. Enumerable.generate = function (initialState, condition, iterate, resultSelector) {
  464. return new Enumerable(function () {
  465. var state, current, initialized = false;
  466. return enumeratorCreate(function () {
  467. if (!initialized) {
  468. state = initialState;
  469. initialized = true;
  470. } else {
  471. state = iterate(state);
  472. if (!condition(state)) {
  473. return false;
  474. }
  475. }
  476. current = resultSelector(state);
  477. return true;
  478. }, function () { return current; });
  479. });
  480. };
  481. /**
  482. * Generates a sequence that's dependent on a resource object whose lifetime is determined by the sequence usage duration.
  483. * @example
  484. * var result = Enumerable.using(function () { return new QuerySource(); }, function (x) { return x.get(42); });
  485. * @param {Function} resourceFactory Resource factory function.
  486. * @param {Function} enumerableFactory Enumerable factory function, having access to the obtained resource.
  487. * @returns {Enumerable} Sequence whose use controls the lifetime of the associated obtained resource.
  488. */
  489. Enumerable.using = function (resourceFactory, enumerableFactory) {
  490. return new Enumerable(function () {
  491. var current, first = true, e, res;
  492. return enumeratorCreate(function () {
  493. if (first) {
  494. res = resourceFactory();
  495. e = enumerableFactory(res).getEnumerator();
  496. first = false;
  497. }
  498. if (!e.moveNext()) {
  499. return false;
  500. }
  501. current = e.getCurrent();
  502. return true;
  503. }, function () {
  504. return current;
  505. }, function () {
  506. e && e.dispose();
  507. res && res.dispose();
  508. });
  509. });
  510. };
  511. function functionBind(f, context) {
  512. return function () {
  513. f.apply(context, arguments);
  514. };
  515. }
  516. /**
  517. * Lazily invokes an action for each value in the sequence, and executes an action upon successful or exceptional termination.
  518. * There is an alias for this method doAction for browsers <IE9.
  519. * @example
  520. * e.do(onNext);
  521. * e.do(onNext, onError);
  522. * e.do(onNExt, onError, onCompleted);
  523. * e.do(observer);
  524. * @param onNext Action to invoke for each element or Observer.
  525. * @param onError Action to invoke on exceptional termination of the sequence.
  526. * @param onCompleted Action to invoke on successful termination of the sequence.
  527. * @returns {Enumerable} Sequence exhibiting the specified side-effects upon enumeration.
  528. */
  529. EnumerablePrototype['do'] = EnumerablePrototype.doAction = function (onNext, onError, onCompleted) {
  530. var oN, oE, oC, self = this;
  531. if (typeof onNext === 'object') {
  532. oN = functionBind(onNext.onNext, onNext);
  533. oE = functionBind(onNext.onError, onNext);
  534. oC = functionBind(onNext.onCompleted, onNext);
  535. } else {
  536. oN = onNext;
  537. oE = onError || noop;
  538. oC = onCompleted || noop;
  539. }
  540. return new Enumerable(function () {
  541. var e, done, current;
  542. return enumeratorCreate(
  543. function () {
  544. e || (e = self.getEnumerator());
  545. try {
  546. if (!e.moveNext()) {
  547. oC();
  548. return false;
  549. }
  550. current = e.getCurrent();
  551. } catch (e) {
  552. oE(e);
  553. throw e;
  554. }
  555. oN(current);
  556. return true;
  557. },
  558. function () { return current; },
  559. function () { e && e.dispose(); }
  560. );
  561. });
  562. };
  563. /**
  564. * Generates a sequence of buffers over the source sequence, with specified length and possible overlap.
  565. * @example
  566. * var result = Enumerable.range(0, 10).bufferWithCount(2);
  567. * var result = Enumerable.range(0, 10).bufferWithCount(5, 1);
  568. * @param {Number} count Number of elements for allocated buffers.
  569. * @param {Number} [skip] Number of elements to skip between the start of consecutive buffers.
  570. * @returns {Enumerable} Sequence of buffers containing source sequence elements.
  571. */
  572. EnumerablePrototype.bufferWithCount = function (count, skip) {
  573. var parent = this;
  574. if (skip == null) { skip = count; }
  575. return new Enumerable(function () {
  576. var buffers = [], i = 0, e, current;
  577. return enumeratorCreate(
  578. function () {
  579. e || (e = parent.getEnumerator());
  580. while (true) {
  581. if (e.moveNext()) {
  582. if (i % skip === 0) {
  583. buffers.push([]);
  584. }
  585. for (var idx = 0, len = buffers.length; idx < len; idx++) {
  586. buffers[idx].push(e.getCurrent());
  587. }
  588. if (buffers.length > 0 && buffers[0].length === count) {
  589. current = Enumerable.fromArray(buffers.shift());
  590. ++i;
  591. return true;
  592. }
  593. ++i;
  594. } else {
  595. if (buffers.length > 0) {
  596. current = Enumerable.fromArray(buffers.shift());
  597. return true;
  598. }
  599. return false;
  600. }
  601. }
  602. },
  603. function () { return current; },
  604. function () { e.dispose(); });
  605. });
  606. };
  607. /**
  608. * Ignores all elements in the source sequence.
  609. * @returns {Enumerable} Source sequence without its elements.
  610. */
  611. EnumerablePrototype.ignoreElements = function() {
  612. var parent = this;
  613. return new Enumerable(function () {
  614. var e;
  615. return enumeratorCreate(
  616. function () {
  617. e = parent.getEnumerator();
  618. while (e.moveNext()) { }
  619. return false;
  620. },
  621. function () {
  622. throw new Error('Operation is not valid due to the current state of the object.');
  623. },
  624. function () { e.dispose(); }
  625. );
  626. });
  627. };
  628. /**
  629. * Returns elements with a distinct key value by using the specified equality comparer to compare key values.
  630. * @param keySelector Key selector.
  631. * @param comparer Comparer used to compare key values.
  632. * @returns {Enumerable} Sequence that contains the elements from the source sequence with distinct key values.
  633. */
  634. EnumerablePrototype.distinctBy = function(keySelector, comparer) {
  635. comparer || (comparer = defaultEqualityComparer);
  636. var parent = this;
  637. return new Enumerable(function () {
  638. var current, map = [], e;
  639. return enumeratorCreate(
  640. function () {
  641. e || (e = parent.getEnumerator());
  642. while (true) {
  643. if (!e.moveNext()) { return false; }
  644. var item = e.getCurrent(), key = keySelector(item);
  645. if (arrayIndexOf.call(map, key, comparer) === -1) {
  646. map.push(item);
  647. current = item;
  648. return true;
  649. }
  650. }
  651. },
  652. function () { return current; },
  653. function () { e && e.dispose(); }
  654. );
  655. });
  656. };
  657. /**
  658. * Returns consecutive distinct elements based on a key value by using the specified equality comparer to compare key values.
  659. * @param keySelector Key selector.
  660. * @param comparer Comparer used to compare key values.
  661. * @returns {Enumerable} Sequence without adjacent non-distinct elements.
  662. */
  663. EnumerablePrototype.distinctUntilChanged = function (keySelector, comparer) {
  664. keySelector || (keySelector = identity);
  665. comparer || (comparer = defaultEqualityComparer);
  666. var parent = this;
  667. return new Enumerable(function () {
  668. var current, e, currentKey, hasCurrentKey;
  669. return enumeratorCreate(
  670. function () {
  671. e || (e = parent.getEnumerator());
  672. while (true) {
  673. if (!e.moveNext()) {
  674. return false;
  675. }
  676. var item = e.getCurrent(),
  677. key = keySelector(item),
  678. comparerEquals = false;
  679. if (hasCurrentKey) {
  680. comparerEquals = comparer(currentKey, key);
  681. }
  682. if (!hasCurrentKey || !comparerEquals) {
  683. current = item;
  684. currentKey = key;
  685. hasCurrentKey = true;
  686. return true;
  687. }
  688. }
  689. },
  690. function () { return current; },
  691. function () { e && e.dispose(); });
  692. });
  693. };
  694. /**
  695. * Expands the sequence by recursively applying a selector function.
  696. * @param selector Selector function to retrieve the next sequence to expand.
  697. * @returns {Enumerable} Sequence with results from the recursive expansion of the source sequence.
  698. */
  699. EnumerablePrototype.expand = function(selector) {
  700. var parent = this;
  701. return new Enumerable(function () {
  702. var current, q = [parent], inner;
  703. return enumeratorCreate(
  704. function () {
  705. while (true) {
  706. if (!inner) {
  707. if (q.length === 0) { return false; }
  708. inner = q.shift().getEnumerator();
  709. }
  710. if (inner.moveNext()) {
  711. current = inner.getCurrent();
  712. q.push(selector(current));
  713. return true;
  714. } else {
  715. inner.dispose();
  716. inner = null;
  717. }
  718. }
  719. },
  720. function () { return current; },
  721. function () { inner && inner.dispose(); }
  722. );
  723. });
  724. };
  725. /**
  726. * Returns the source sequence prefixed with the specified value.
  727. * @param values Values to prefix the sequence with.
  728. * @returns {Enumerable} Sequence starting with the specified prefix value, followed by the source sequence.
  729. */
  730. EnumerablePrototype.startWith = function () {
  731. return enumerableConcat(enumerableFromArray(slice.call(arguments)), this);
  732. };
  733. function scan (seed, accumulator) {
  734. var source = this;
  735. return new Enumerable(function () {
  736. var current, e, acc = seed;
  737. return enumeratorCreate(
  738. function () {
  739. e || (e = source.getEnumerator());
  740. if (!e.moveNext()) { return false; }
  741. var item = e.getCurrent();
  742. acc = accumulator(acc, item);
  743. current = acc;
  744. return true;
  745. },
  746. function () { return current; },
  747. function () { e && e.dispose(); }
  748. );
  749. });
  750. }
  751. function scan1 (accumulator) {
  752. var source = this;
  753. return new Enumerable(function () {
  754. var current, e, acc, hasSeed = false;
  755. return enumeratorCreate(
  756. function () {
  757. e || (e = source.getEnumerator());
  758. while(true) {
  759. if (!e.moveNext()) { return false; }
  760. var item = e.getCurrent();
  761. if (!hasSeed) {
  762. hasSeed = true;
  763. acc = item;
  764. continue;
  765. }
  766. acc = accumulator(acc, item);
  767. current = acc;
  768. return true;
  769. }
  770. },
  771. function () { return current; },
  772. function () { e && e.dispose(); }
  773. );
  774. });
  775. }
  776. /**
  777. * Generates a sequence of accumulated values by scanning the source sequence and applying an accumulator function.
  778. * @param seed Accumulator seed value.
  779. * @param accumulator Accumulation function to apply to the current accumulation value and each element of the sequence.
  780. * @returns {Enumerable} Sequence with all intermediate accumulation values resulting from scanning the sequence.
  781. */
  782. EnumerablePrototype.scan = function (/* seed, accumulator */) {
  783. var f = arguments.length === 1 ? scan1 : scan;
  784. return f.apply(this, arguments);
  785. };
  786. /**
  787. * Returns a specified number of contiguous elements from the end of the sequence.
  788. * @param count The number of elements to take from the end of the sequence.
  789. * @returns {Enumerable} Sequence with the specified number of elements counting from the end of the source sequence.
  790. */
  791. EnumerablePrototype.takeLast = function (count) {
  792. var parent = this;
  793. return new Enumerable(function () {
  794. var current, e, q;
  795. return enumeratorCreate(
  796. function () {
  797. e || (e = parent.getEnumerator());
  798. if (!q) {
  799. q = [];
  800. while (e.moveNext()) {
  801. q.push(e.getCurrent());
  802. if (q.length > count) {
  803. q.shift();
  804. }
  805. }
  806. }
  807. if (q.length === 0) {
  808. return false;
  809. }
  810. current = q.shift();
  811. return true;
  812. },
  813. function () { return current; },
  814. function () { e && e.dispose(); }
  815. );
  816. });
  817. };
  818. /**
  819. * Bypasses a specified number of contiguous elements from the end of the sequence and returns the remaining elements.
  820. * @param count The number of elements to skip from the end of the sequence before returning the remaining elements.
  821. * @returns {Enumerable} Sequence bypassing the specified number of elements counting from the end of the source sequence.
  822. */
  823. EnumerablePrototype.skipLast = function (count) {
  824. var parent = this;
  825. return new Enumerable(function () {
  826. var current, e, q = [];
  827. return enumeratorCreate(
  828. function () {
  829. e || (e = parent.getEnumerator());
  830. while (true) {
  831. if (!e.moveNext()) {
  832. return false;
  833. }
  834. q.push(e.getCurrent());
  835. if (q.length > count) {
  836. current = q.shift();
  837. return true;
  838. }
  839. }
  840. },
  841. function () { return current; },
  842. function () { e && e.dispose(); }
  843. );
  844. });
  845. };
  846. /**
  847. * Repeats and concatenates the source sequence the given number of times.
  848. * @param count Number of times to repeat the source sequence.
  849. * @returns {Enumerable} Sequence obtained by concatenating the source sequence to itself the specified number of times.
  850. */
  851. EnumerablePrototype.repeat = function (count) {
  852. var parent = this;
  853. return enumerableRepeat(0, count).selectMany(function () { return parent; });
  854. };
  855. function catchExceptionHandler (source, handler) {
  856. return new Enumerable(function () {
  857. var current, e, errE;
  858. return enumeratorCreate(
  859. function () {
  860. e || (e = source.getEnumerator());
  861. while (true) {
  862. var b, c;
  863. try {
  864. b = e.moveNext();
  865. c = e.getCurrent();
  866. } catch (e) {
  867. errE = handler(e);
  868. break;
  869. }
  870. if (!b) {
  871. return false;
  872. }
  873. current = c;
  874. return true;
  875. }
  876. if (errE) {
  877. e.dispose();
  878. e = errE.getEnumerator();
  879. if (!e.moveNext()) { return false; }
  880. current = e.getCurrent();
  881. return true;
  882. }
  883. },
  884. function () { return current; },
  885. function () {
  886. e && e.dispose();
  887. });
  888. });
  889. }
  890. /**
  891. * Creates a sequence that returns the elements of the first sequence, switching to the second in case of an error.
  892. * An alias for this method is catchException for browsers <IE9.
  893. * @param second Second sequence, concatenated to the result in case the first sequence completes exceptionally or handler to invoke when an exception of the specified type occurs.
  894. * @returns {Enumerable} The first sequence, followed by the second sequence in case an error is produced.
  895. */
  896. EnumerablePrototype['catch'] = EnumerablePrototype.catchException = function (secondOrHandler) {
  897. if (arguments.length === 0) {
  898. return enumerableCatch(this); // Already IE<IE<T>>
  899. } else if (typeof secondOrHandler === 'function') {
  900. return catchExceptionHandler(this, secondOrHandler); // use handler
  901. } else {
  902. var args = slice.call(arguments);
  903. args.unshift(this);
  904. return enumerableCatch.apply(null, args); // create IE<IE<T>>
  905. }
  906. };
  907. /**
  908. * Creates a sequence by concatenating source sequences until a source sequence completes successfully.
  909. * An alias for this method is catchException for browsers <IE9.
  910. * @returns {Enumerable} Sequence that continues to concatenate source sequences while errors occur.
  911. */
  912. var enumerableCatch = Enumerable['catch'] = Enumerable.catchException = function () {
  913. // Check arguments
  914. var sources = Enumerable.fromArray(arguments);
  915. return new Enumerable(function () {
  916. var outerE, hasOuter, innerE, current, error;
  917. return enumeratorCreate(
  918. function () {
  919. outerE || (outerE = sources.getEnumerator());
  920. while (true) {
  921. while (true) {
  922. if (!innerE) {
  923. if (!outerE.moveNext()) {
  924. if (error) { throw error; }
  925. return false;
  926. } else {
  927. error = null;
  928. }
  929. innerE = outerE.getCurrent().getEnumerator();
  930. }
  931. var b, c;
  932. try {
  933. b = innerE.moveNext();
  934. c = innerE.getCurrent();
  935. } catch (e) {
  936. error = e;
  937. innerE.dispose();
  938. innerE = null;
  939. break;
  940. }
  941. if (!b) {
  942. innerE.dispose();
  943. innerE = null;
  944. break;
  945. }
  946. current = c;
  947. return true;
  948. }
  949. if (error == null) {
  950. break;
  951. }
  952. }
  953. },
  954. function () {
  955. return current;
  956. },
  957. function () {
  958. innerE && innerE.dispose();
  959. outerE && outerE.dispose();
  960. }
  961. );
  962. });
  963. };
  964. /**
  965. * Creates a sequence whose termination or disposal of an enumerator causes a finally action to be executed.
  966. * An alias for this method is finallyDo for browsers <IE9.
  967. * @example
  968. * var result = Enumerable.range(1, 10).finally(function () { console.log('done!'); });
  969. * @param {Function} finallyAction Action to run upon termination of the sequence, or when an enumerator is disposed.
  970. * @returns {Enumerable} Source sequence with guarantees on the invocation of the finally action.
  971. */
  972. EnumerablePrototype['finally'] = EnumerablePrototype.finallyDo = function (finallyAction) {
  973. var parent = this;
  974. return new Enumerable(function () {
  975. var e, finallyCalled = false;
  976. return enumeratorCreate(
  977. function () {
  978. e || (e = parent.getEnumerator());
  979. var next;
  980. try {
  981. next = e.moveNext();
  982. if (!next) {
  983. finallyAction();
  984. finallyCalled = true;
  985. return false;
  986. }
  987. return next;
  988. } catch (e) {
  989. finallyAction();
  990. finallyCalled = true;
  991. throw e;
  992. }
  993. },
  994. function () { return e.getCurrent(); },
  995. function () {
  996. !finallyCalled && finallyAction();
  997. e && e.dispose();
  998. }
  999. );
  1000. });
  1001. };
  1002. /**
  1003. * Creates a sequence that concatenates both given sequences, regardless of whether an error occurs.
  1004. * @param {Enumerable} second Second sequence.
  1005. * @returns {Enumerable} Sequence concatenating the elements of both sequences, ignoring errors.
  1006. */
  1007. EnumerablePrototype.onErrorResumeNext = function (second) {
  1008. return onErrorResumeNext.apply(null, [this, second]);
  1009. };
  1010. /**
  1011. * Creates a sequence that concatenates the given sequences, regardless of whether an error occurs in any of the sequences.
  1012. * @returns {Enumerable} Sequence concatenating the elements of the given sequences, ignoring errors.
  1013. */
  1014. var onErrorResumeNext = Enumerable.onErrorResumeNext = function () {
  1015. var sources = arguments;
  1016. return new Enumerable(function () {
  1017. var current, index = 0, inner;
  1018. return enumeratorCreate(function () {
  1019. while (index < sources.length) {
  1020. inner || (inner = sources[index].getEnumerator());
  1021. try {
  1022. var result = inner.moveNext();
  1023. if (result) {
  1024. current = inner.getCurrent();
  1025. return true;
  1026. }
  1027. }
  1028. catch (e) { }
  1029. inner.dispose();
  1030. inner = null;
  1031. index++;
  1032. }
  1033. return false;
  1034. },
  1035. function () { return current; },
  1036. function () { inner && inner.dispose(); });
  1037. });
  1038. };
  1039. /**
  1040. * Creates a sequence that retries enumerating the source sequence as long as an error occurs, with the specified maximum number of retries.
  1041. * @param {Number} retryCount Maximum number of retries.
  1042. * @returns {Enumerable} Sequence concatenating the results of the source sequence as long as an error occurs.
  1043. */
  1044. EnumerablePrototype.retry = function (retryCount) {
  1045. var parent = this;
  1046. return new Enumerable(function () {
  1047. var current, e, count = retryCount, hasCount = retryCount != null;
  1048. return enumeratorCreate(
  1049. function () {
  1050. e || (e = parent.getEnumerator());
  1051. while (true) {
  1052. try {
  1053. if (e.moveNext()) {
  1054. current = e.getCurrent();
  1055. return true;
  1056. } else {
  1057. return false;
  1058. }
  1059. }
  1060. catch (err) {
  1061. if (hasCount && --count === 0) {
  1062. throw err;
  1063. } else {
  1064. e = parent.getEnumerator(); // retry again
  1065. error = null;
  1066. }
  1067. }
  1068. }
  1069. },
  1070. function () { return current; },
  1071. function () { e.dispose(); }
  1072. );
  1073. });
  1074. };
  1075. /**
  1076. * Generates an enumerable sequence by repeating a source sequence as long as the given loop condition holds.
  1077. * An alias for this method is whileDo for browsers <IE9.
  1078. * @example
  1079. * var result = Enumerable.while(function () { return true; }, Enumerable.range(1, 10));
  1080. * @param {Function} condition Loop condition.
  1081. * @param {Enumerable} source Sequence to repeat while the condition evaluates true.
  1082. * @returns {Enumerable} Sequence generated by repeating the given sequence while the condition evaluates to true.
  1083. */
  1084. var enumerableWhileDo = Enumerable['while'] = Enumerable.whileDo = function (condition, source) {
  1085. return enumerableRepeat(source).takeWhile(condition).selectMany(identity);
  1086. };
  1087. /**
  1088. * Returns an enumerable sequence based on the evaluation result of the given condition.
  1089. * An alias for this method is ifThen for browsers <IE9
  1090. * @example
  1091. * var result = Enumerable.if(function () { return true; }, Enumerable.range(0, 10));
  1092. * var result = Enumerable.if(function () { return false; }, Enumerable.range(0, 10), Enumerable.return(42));
  1093. * @param {Function} condition Condition to evaluate.
  1094. * @param {Enumerable} thenSource Sequence to return in case the condition evaluates true.
  1095. * @param {Enumerable} [elseSource] Optional sequence to return in case the condition evaluates false; else an empty sequence.
  1096. * @return Either of the two input sequences based on the result of evaluating the condition.
  1097. */
  1098. Enumerable['if'] = Enumerable.ifThen = function (condition, thenSource, elseSource) {
  1099. elseSource || (elseSource = enumerableEmpty());
  1100. return enumerableDefer(function () { return condition() ? thenSource : elseSource; });
  1101. };
  1102. /**
  1103. * Generates an enumerable sequence by repeating a source sequence as long as the given loop postcondition holds.
  1104. * @example
  1105. * var result = Enumerable.doWhile(Enumerable.range(0, 10), function () { return true; });
  1106. * @param {Enumerable} source Source sequence to repeat while the condition evaluates true.
  1107. * @param {Function} condition Loop condition.
  1108. * @returns {Enumerable} Sequence generated by repeating the given sequence until the condition evaluates to false.
  1109. */
  1110. Enumerable.doWhile = function (source, condition) {
  1111. return source.concat(enumerableWhileDo(condition, source));
  1112. };
  1113. /**
  1114. * Returns a sequence from a dictionary based on the result of evaluating a selector function, also specifying a default sequence.
  1115. * An alias for this method is switchCase for browsers <IE9.
  1116. * @example
  1117. * var result = Enumerable.case(function (x) { return x; }, {1: 42, 2: 25});
  1118. * var result = Enumerable.case(function (x) { return x; }, {1: 42, 2: 25}, Enumerable.return(56));
  1119. * @param {Function} selector Selector function used to pick a sequence from the given sources.
  1120. * @param {Object} sources Dictionary mapping selector values onto resulting sequences.
  1121. * @param {Enumerable} [defaultSource] Default sequence to return in case there's no corresponding source for the computed selector value; if not provided defaults to empty Enumerable.
  1122. * @returns {Enumerable} The source sequence corresponding with the evaluated selector value; otherwise, the default source.
  1123. */
  1124. Enumerable['case'] = Enumerable.switchCase = function (selector, sources, defaultSource) {
  1125. defaultSource || (defaultSource = enumerableEmpty());
  1126. return enumerableDefer(function () {
  1127. var result = sources[selector()]
  1128. if (!result) {
  1129. result = defaultSource;
  1130. }
  1131. return result;
  1132. });
  1133. };
  1134. /**
  1135. * Generates a sequence by enumerating a source sequence, mapping its elements on result sequences, and concatenating those sequences.
  1136. * An alias for this method is forIn for browsers <IE9.
  1137. * @example
  1138. * var result = Enumerable.for(Enumerable.range(0, 10), function (x) { return Enumerable.return(x); });
  1139. * @param {Enumerable} source Source sequence.
  1140. * @param {Function} resultSelector Result selector to evaluate for each iteration over the source.
  1141. * @return {Enumerable} Sequence concatenating the inner sequences that result from evaluating the result selector on elements from the source.
  1142. */
  1143. Enumerable['for'] = Enumerable.forIn = function (source, resultSelector) {
  1144. return source.select(resultSelector);
  1145. };
  1146. retur