diff --git a/README.md b/README.md index ef5f370..8a53059 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,8 @@ Dual license notice: The default license for this project is GPL 3.0. However, i - [jQuery UI](https://jqueryui.com/) - [github.com/kamranahmedse/jquery-toast-plugin](https://github.com/kamranahmedse/jquery-toast-plugin) - [github.com/hiddentao/squel](https://github.com/hiddentao/squel) - - [github.com/BorisMoore/jsrender](https://github.com/BorisMoore/jsrender) + - [github.com/BorisMoore/jsrender](https://github.com/BorisMoore/jsrender) - Templating engine + - [github.com/mihaifm/linq](https://github.com/mihaifm/linq) - LINQ for JavaScript - [Includes binaries](https://github.com/gnh1201/welsonjs/blob/master/bin/README.MD) - [module.exports](https://nodejs.org/en/knowledge/getting-started/what-is-require/), CommonJS, UMD compatibility - [NPM](https://www.npmjs.com/) compatibility diff --git a/app.js b/app.js index a33aed6..7be0f5e 100644 --- a/app.js +++ b/app.js @@ -560,6 +560,9 @@ var is = require("app/assets/js/is-0.9.0.min"); //var Intl = require("app/assets/js/Intl-1.2.5-e93b114.min"); //console.log(new Intl.NumberFormat().format(1234567890.123456)); +// linq.js - LINQ for JavaScript +var Enumerable = require("app/assets/js/linq-4.0.2.wsh.js")._default; + // Dive into entrypoint function __main__() { console.log(""); diff --git a/app/assets/js/linq-4.0.2.wsh.js b/app/assets/js/linq-4.0.2.wsh.js new file mode 100644 index 0000000..92855cf --- /dev/null +++ b/app/assets/js/linq-4.0.2.wsh.js @@ -0,0 +1,3070 @@ +/*-------------------------------------------------------------------------- + * linq.js - LINQ for JavaScript + * licensed under MIT License + *------------------------------------------------------------------------*/ + +var Functions = { + Identity: function (x) { return x; }, + True: function () { return true; }, + Blank: function () { } +}; + +var Types = { + Boolean: typeof true, + Number: typeof 0, + String: typeof "", + Object: typeof {}, + Undefined: typeof undefined, + Function: typeof function () { } +}; + +var funcCache = { "": Functions.Identity }; + +var Utils = { + createLambda: function (expression) { + if (expression == null) return Functions.Identity; + if (typeof expression === Types.String) { + // get from cache + var f = funcCache[expression]; + if (f != null) { + return f; + } + + if (expression.indexOf("=>") === -1) { + var regexp = new RegExp("[$]+", "g"); + + var maxLength = 0; + var match; + while ((match = regexp.exec(expression)) != null) { + if (match[0].length > maxLength) { + maxLength = match[0].length; + } + } + + var argArray = []; + for (var i = 1; i <= maxLength; i++) { + var dollar = ""; + for (var j = 0; j < i; j++) { + dollar += "$"; + } + argArray.push(dollar); + } + + var args = argArray.join(","); + + f = new Function(args, "return " + expression); + funcCache[expression] = f; + return f; + } + else { + var expr = expression.match(/^[(\s]*([^()]*?)[)\s]*=>(.*)/); + f = new Function(expr[1], (expr[2].match(/\breturn\b/) ? expr[2] : "return " + expr[2])); + funcCache[expression] = f; + return f; + } + } + return expression; + }, + + defineProperty: function (target, methodName, value) { + Object.defineProperty(target, methodName, { + enumerable: false, + configurable: true, + writable: true, + value: value + }) + }, + + compare: function (a, b) { + return (a === b) ? 0 : (a > b) ? 1 : -1; + }, + + dispose: function (obj) { + if (obj != null) obj.dispose(); + }, + + hasNativeIteratorSupport: function () { + return typeof Symbol !== 'undefined' && typeof Symbol.iterator !== 'undefined'; + } +}; + +var State = { Before: 0, Running: 1, After: 2 }; + +var IEnumerator = function (initialize, tryGetNext, dispose) { + var yielder = new Yielder(); + var state = State.Before; + + this.current = yielder.current; + + this.moveNext = function () { + try { + switch (state) { + case State.Before: + state = State.Running; + initialize(); + // fall through + + case State.Running: + if (tryGetNext.apply(yielder)) { + return true; + } + else { + this.dispose(); + return false; + } + // fall through + + case State.After: + return false; + } + } + catch (e) { + this.dispose(); + throw e; + } + }; + + this.dispose = function () { + if (state != State.Running) return; + + try { + dispose(); + } + finally { + state = State.After; + } + }; +}; + +// tryGetNext yielder +var Yielder = function () { + var current = null; + this.current = function () { return current; }; + this.yieldReturn = function (value) { + current = value; + return true; + }; + this.yieldBreak = function () { + return false; + }; +}; + +// Enumerable constuctor +var Enumerable = function (getEnumerator) { + this.getEnumerator = getEnumerator; +}; + +/////////////////// +// Utility Methods + +Enumerable.Utils = {}; + +Enumerable.Utils.createLambda = function (expression) { + return Utils.createLambda(expression); +}; + +Enumerable.Utils.createEnumerable = function (getEnumerator) { + return new Enumerable(getEnumerator); +}; + +Enumerable.Utils.createEnumerator = function (initialize, tryGetNext, dispose) { + return new IEnumerator(initialize, tryGetNext, dispose); +}; + +Enumerable.Utils.extendTo = function (type) { + var typeProto = type.prototype; + var enumerableProto; + + if (type === Array) { + enumerableProto = ArrayEnumerable.prototype; + Utils.defineProperty(typeProto, "getSource", function () { + return this; + }); + } + else { + enumerableProto = Enumerable.prototype; + Utils.defineProperty(typeProto, "getEnumerator", function () { + return Enumerable.from(this).getEnumerator(); + }); + } + + for (var methodName in enumerableProto) { + var func = enumerableProto[methodName]; + + // already extended + if (typeProto[methodName] == func) continue; + + // already defined(example Array#reverse/join/forEach...) + if (typeProto[methodName] != null) { + methodName = methodName + "ByLinq"; + if (typeProto[methodName] == func) continue; // recheck + } + + if (func instanceof Function) { + Utils.defineProperty(typeProto, methodName, func); + } + } +}; + +Enumerable.Utils.recallFrom = function (type) { + var typeProto = type.prototype; + var enumerableProto; + + if (type === Array) { + enumerableProto = ArrayEnumerable.prototype; + delete typeProto.getSource; + } + else { + enumerableProto = Enumerable.prototype; + delete typeProto.getEnumerator; + } + + for (var methodName in enumerableProto) { + var func = enumerableProto[methodName]; + + if (typeProto[methodName + 'ByLinq']) { + delete typeProto[methodName + 'ByLinq']; + } + else if (typeProto[methodName] == func && func instanceof Function) { + delete typeProto[methodName]; + } + } +}; + +////////////// +// Generators + +Enumerable.choice = function () { + var args = arguments; + + return new Enumerable(function () { + return new IEnumerator( + function () { + args = (args[0] instanceof Array) ? args[0] + : (args[0].getEnumerator != null) ? args[0].toArray() + : args; + }, + function () { + return this.yieldReturn(args[Math.floor(Math.random() * args.length)]); + }, + Functions.Blank); + }); +}; + +Enumerable.cycle = function () { + var args = arguments; + + return new Enumerable(function () { + var index = 0; + return new IEnumerator( + function () { + args = (args[0] instanceof Array) ? args[0] + : (args[0].getEnumerator != null) ? args[0].toArray() + : args; + }, + function () { + if (index >= args.length) index = 0; + return this.yieldReturn(args[index++]); + }, + Functions.Blank); + }); +}; + +Enumerable.empty = function () { + return new Enumerable(function () { + return new IEnumerator( + Functions.Blank, + function () { return false; }, + Functions.Blank); + }); +}; + +Enumerable.from = function (obj) { + if (obj == null) { + return Enumerable.empty(); + } + if (obj instanceof Enumerable) { + return obj; + } + if (typeof obj == Types.Number || typeof obj == Types.Boolean) { + return Enumerable.repeat(obj, 1); + } + if (typeof obj == Types.String) { + return new Enumerable(function () { + var index = 0; + return new IEnumerator( + Functions.Blank, + function () { + return (index < obj.length) ? this.yieldReturn(obj.charAt(index++)) : false; + }, + Functions.Blank); + }); + } + if (typeof obj == Types.Function && Object.keys(obj).length == 0) { + return new Enumerable(function () { + var orig; + + return new IEnumerator( + function () { + orig = obj()[Symbol.iterator](); + }, + function () { + var next = orig.next(); + return (next.done ? false : (this.yieldReturn(next.value))); + }, + Functions.Blank); + }); + } + + if (typeof obj != Types.Function) { + // array or array-like object + if (typeof obj.length == Types.Number) { + return new ArrayEnumerable(obj); + } + + // iterable object + if (typeof Symbol !== 'undefined' && typeof obj[Symbol.iterator] !== 'undefined') { + return new Enumerable(function () { + return new IEnumerator( + Functions.Blank, + function () { + var next = obj.next(); + return (next.done ? false : (this.yieldReturn(next.value))); + }, + Functions.Blank); + }); + } + } + + // case function/object: create keyValuePair[] + return new Enumerable(function () { + var array = []; + var index = 0; + + return new IEnumerator( + function () { + for (var key in obj) { + var value = obj[key]; + if (!(value instanceof Function) && Object.prototype.hasOwnProperty.call(obj, key)) { + array.push({ key: key, value: value }); + } + } + }, + function () { + return (index < array.length) + ? this.yieldReturn(array[index++]) + : false; + }, + Functions.Blank); + }); +}, + + Enumerable.make = function (element) { + return Enumerable.repeat(element, 1); + }; + +// Overload:function(input, pattern) +// Overload:function(input, pattern, flags) +Enumerable.matches = function (input, pattern, flags) { + if (flags == null) flags = ""; + + if (pattern instanceof RegExp) { + flags += (pattern.ignoreCase) ? "i" : ""; + flags += (pattern.multiline) ? "m" : ""; + pattern = pattern.source; + } + if (flags.indexOf("g") === -1) flags += "g"; + + return new Enumerable(function () { + var regex; + return new IEnumerator( + function () { regex = new RegExp(pattern, flags); }, + function () { + var match = regex.exec(input); + return (match) ? this.yieldReturn(match) : false; + }, + Functions.Blank); + }); +}; + +// Overload:function(start, count) +// Overload:function(start, count, step) +Enumerable.range = function (start, count, step) { + if (step == null) step = 1; + + return new Enumerable(function () { + var value; + var index = 0; + + return new IEnumerator( + function () { value = start - step; }, + function () { + return (index++ < count) + ? this.yieldReturn(value += step) + : this.yieldBreak(); + }, + Functions.Blank); + }); +}; + +// Overload:function(start, count) +// Overload:function(start, count, step) +Enumerable.rangeDown = function (start, count, step) { + if (step == null) step = 1; + + return new Enumerable(function () { + var value; + var index = 0; + + return new IEnumerator( + function () { value = start + step; }, + function () { + return (index++ < count) + ? this.yieldReturn(value -= step) + : this.yieldBreak(); + }, + Functions.Blank); + }); +}; + +// Overload:function(start, to) +// Overload:function(start, to, step) +Enumerable.rangeTo = function (start, to, step) { + if (step == null) step = 1; + + if (start < to) { + return new Enumerable(function () { + var value; + + return new IEnumerator( + function () { value = start - step; }, + function () { + var next = value += step; + return (next <= to) + ? this.yieldReturn(next) + : this.yieldBreak(); + }, + Functions.Blank); + }); + } + else { + return new Enumerable(function () { + var value; + + return new IEnumerator( + function () { value = start + step; }, + function () { + var next = value -= step; + return (next >= to) + ? this.yieldReturn(next) + : this.yieldBreak(); + }, + Functions.Blank); + }); + } +}; + +// Overload:function(element) +// Overload:function(element, count) +Enumerable.repeat = function (element, count) { + if (count != null) + return Enumerable.repeat(element).take(count); + + return new Enumerable(function () { + return new IEnumerator( + Functions.Blank, + function () { return this.yieldReturn(element); }, + Functions.Blank); + }); +}; + +Enumerable.repeatWithFinalize = function (initializer, finalizer) { + initializer = Utils.createLambda(initializer); + finalizer = Utils.createLambda(finalizer); + + return new Enumerable(function () { + var element; + return new IEnumerator( + function () { element = initializer(); }, + function () { return this.yieldReturn(element); }, + function () { + if (element != null) { + finalizer(element); + element = null; + } + }); + }); +}; + +// Overload:function(func) +// Overload:function(func, count) +Enumerable.generate = function (func, count) { + if (count != null) + return Enumerable.generate(func).take(count); + + func = Utils.createLambda(func); + + return new Enumerable(function () { + return new IEnumerator( + Functions.Blank, + function () { return this.yieldReturn(func()); }, + Functions.Blank); + }); +}; + +// Overload:function() +// Overload:function(start) +// Overload:function(start, step) +Enumerable.toInfinity = function (start, step) { + if (start == null) start = 0; + if (step == null) step = 1; + + return new Enumerable(function () { + var value; + return new IEnumerator( + function () { value = start - step; }, + function () { return this.yieldReturn(value += step); }, + Functions.Blank); + }); +}; + +// Overload:function() +// Overload:function(start) +// Overload:function(start, step) +Enumerable.toNegativeInfinity = function (start, step) { + if (start == null) start = 0; + if (step == null) step = 1; + + return new Enumerable(function () { + var value; + return new IEnumerator( + function () { value = start + step; }, + function () { return this.yieldReturn(value -= step); }, + Functions.Blank); + }); +}; + +Enumerable.unfold = function (seed, func) { + func = Utils.createLambda(func); + + return new Enumerable(function () { + var isFirst = true; + var value; + return new IEnumerator( + Functions.Blank, + function () { + if (isFirst) { + isFirst = false; + value = seed; + return this.yieldReturn(value); + } + value = func(value); + return this.yieldReturn(value); + }, + Functions.Blank); + }); +}; + +Enumerable.defer = function (enumerableFactory) { + return new Enumerable(function () { + var enumerator; + + return new IEnumerator( + function () { enumerator = Enumerable.from(enumerableFactory()).getEnumerator(); }, + function () { + return (enumerator.moveNext()) + ? this.yieldReturn(enumerator.current()) + : this.yieldBreak(); + }, + function () { + Utils.dispose(enumerator); + }); + }); +}; + +///////////////////// +// Extension Methods + +//////////////////////////////////// +// Projection and Filtering Methods + +// Overload:function(func) +// Overload:function(func, resultSelector) +// Overload:function(func, resultSelector) +Enumerable.prototype.traverseBreadthFirst = function (func, resultSelector) { + var source = this; + func = Utils.createLambda(func); + resultSelector = Utils.createLambda(resultSelector); + + return new Enumerable(function () { + var enumerator; + var nestLevel = 0; + var buffer = []; + + return new IEnumerator( + function () { enumerator = source.getEnumerator(); }, + function () { + while (true) { + if (enumerator.moveNext()) { + buffer.push(enumerator.current()); + return this.yieldReturn(resultSelector(enumerator.current(), nestLevel)); + } + + var next = Enumerable.from(buffer).selectMany(function (x) { return func(x); }); + if (!next.any()) { + return false; + } + else { + nestLevel++; + buffer = []; + Utils.dispose(enumerator); + enumerator = next.getEnumerator(); + } + } + }, + function () { Utils.dispose(enumerator); }); + }); +}; + +// Overload:function(func) +// Overload:function(func, resultSelector) +// Overload:function(func, resultSelector) +Enumerable.prototype.traverseDepthFirst = function (func, resultSelector) { + var source = this; + func = Utils.createLambda(func); + resultSelector = Utils.createLambda(resultSelector); + + return new Enumerable(function () { + var enumeratorStack = []; + var enumerator; + + return new IEnumerator( + function () { enumerator = source.getEnumerator(); }, + function () { + while (true) { + if (enumerator.moveNext()) { + var value = resultSelector(enumerator.current(), enumeratorStack.length); + enumeratorStack.push(enumerator); + enumerator = Enumerable.from(func(enumerator.current())).getEnumerator(); + return this.yieldReturn(value); + } + + if (enumeratorStack.length <= 0) return false; + Utils.dispose(enumerator); + enumerator = enumeratorStack.pop(); + } + }, + function () { + try { + Utils.dispose(enumerator); + } + finally { + Enumerable.from(enumeratorStack).forEach(function (s) { s.dispose(); }); + } + }); + }); +}; + +Enumerable.prototype.flatten = function () { + var source = this; + + return new Enumerable(function () { + var enumerator; + var middleEnumerator = null; + + return new IEnumerator( + function () { enumerator = source.getEnumerator(); }, + function () { + while (true) { + if (middleEnumerator != null) { + if (middleEnumerator.moveNext()) { + return this.yieldReturn(middleEnumerator.current()); + } + else { + middleEnumerator = null; + } + } + + if (enumerator.moveNext()) { + if (enumerator.current() instanceof Array) { + Utils.dispose(middleEnumerator); + middleEnumerator = Enumerable.from(enumerator.current()) + .selectMany(Functions.Identity) + .flatten() + .getEnumerator(); + continue; + } + else { + return this.yieldReturn(enumerator.current()); + } + } + + return false; + } + }, + function () { + try { + Utils.dispose(enumerator); + } + finally { + Utils.dispose(middleEnumerator); + } + }); + }); +}; + +Enumerable.prototype.pairwise = function (selector) { + var source = this; + selector = Utils.createLambda(selector); + + return new Enumerable(function () { + var enumerator; + + return new IEnumerator( + function () { + enumerator = source.getEnumerator(); + enumerator.moveNext(); + }, + function () { + var prev = enumerator.current(); + return (enumerator.moveNext()) + ? this.yieldReturn(selector(prev, enumerator.current())) + : false; + }, + function () { Utils.dispose(enumerator); }); + }); +}; + +// Overload:function(func) +// Overload:function(seed,func) +Enumerable.prototype.scan = function (seed, func) { + var isUseSeed; + if (func == null) { + func = Utils.createLambda(seed); + isUseSeed = false; + } else { + func = Utils.createLambda(func); + isUseSeed = true; + } + var source = this; + + return new Enumerable(function () { + var enumerator; + var value; + var isFirst = true; + + return new IEnumerator( + function () { enumerator = source.getEnumerator(); }, + function () { + if (isFirst) { + isFirst = false; + if (!isUseSeed) { + if (enumerator.moveNext()) { + return this.yieldReturn(value = enumerator.current()); + } + } + else { + return this.yieldReturn(value = seed); + } + } + + return (enumerator.moveNext()) + ? this.yieldReturn(value = func(value, enumerator.current())) + : false; + }, + function () { Utils.dispose(enumerator); }); + }); +}; + +// Overload:function(selector) +// Overload:function(selector) +Enumerable.prototype.select = function (selector) { + selector = Utils.createLambda(selector); + + if (selector.length <= 1) { + return new WhereSelectEnumerable(this, null, selector); + } + else { + var source = this; + + return new Enumerable(function () { + var enumerator; + var index = 0; + + return new IEnumerator( + function () { enumerator = source.getEnumerator(); }, + function () { + return (enumerator.moveNext()) + ? this.yieldReturn(selector(enumerator.current(), index++)) + : false; + }, + function () { Utils.dispose(enumerator); }); + }); + } +}; + +// Overload:function(collectionSelector) +// Overload:function(collectionSelector) +// Overload:function(collectionSelector,resultSelector) +// Overload:function(collectionSelector,resultSelector) +Enumerable.prototype.selectMany = function (collectionSelector, resultSelector) { + var source = this; + collectionSelector = Utils.createLambda(collectionSelector); + if (resultSelector == null) resultSelector = function (a, b) { return b; }; + resultSelector = Utils.createLambda(resultSelector); + + return new Enumerable(function () { + var enumerator; + var middleEnumerator = undefined; + var index = 0; + + return new IEnumerator( + function () { enumerator = source.getEnumerator(); }, + function () { + if (middleEnumerator === undefined) { + if (!enumerator.moveNext()) return false; + } + do { + if (middleEnumerator == null) { + var middleSeq = collectionSelector(enumerator.current(), index++); + middleEnumerator = Enumerable.from(middleSeq).getEnumerator(); + } + if (middleEnumerator.moveNext()) { + return this.yieldReturn(resultSelector(enumerator.current(), middleEnumerator.current())); + } + Utils.dispose(middleEnumerator); + middleEnumerator = null; + } while (enumerator.moveNext()); + return false; + }, + function () { + try { + Utils.dispose(enumerator); + } + finally { + Utils.dispose(middleEnumerator); + } + }); + }); +}; + +// Overload:function(predicate) +// Overload:function(predicate) +Enumerable.prototype.where = function (predicate) { + predicate = Utils.createLambda(predicate); + + if (predicate.length <= 1) { + return new WhereEnumerable(this, predicate); + } + else { + var source = this; + + return new Enumerable(function () { + var enumerator; + var index = 0; + + return new IEnumerator( + function () { enumerator = source.getEnumerator(); }, + function () { + while (enumerator.moveNext()) { + if (predicate(enumerator.current(), index++)) { + return this.yieldReturn(enumerator.current()); + } + } + return false; + }, + function () { Utils.dispose(enumerator); }); + }); + } +}; + + +// Overload:function(selector) +// Overload:function(selector) +Enumerable.prototype.choose = function (selector) { + selector = Utils.createLambda(selector); + var source = this; + + return new Enumerable(function () { + var enumerator; + var index = 0; + + return new IEnumerator( + function () { enumerator = source.getEnumerator(); }, + function () { + while (enumerator.moveNext()) { + var result = selector(enumerator.current(), index++); + if (result != null) { + return this.yieldReturn(result); + } + } + return this.yieldBreak(); + }, + function () { Utils.dispose(enumerator); }); + }); +}; + +Enumerable.prototype.ofType = function (type) { + var typeName; + switch (type) { + case Number: + typeName = Types.Number; + break; + case String: + typeName = Types.String; + break; + case Boolean: + typeName = Types.Boolean; + break; + case Function: + typeName = Types.Function; + break; + default: + typeName = null; + break; + } + return (typeName === null) + ? this.where(function (x) { return x instanceof type; }) + : this.where(function (x) { return typeof x === typeName; }); +}; + +// mutiple arguments, last one is selector, others are enumerable +Enumerable.prototype.zip = function () { + var args = arguments; + var selector = Utils.createLambda(arguments[arguments.length - 1]); + + var source = this; + // optimized case:argument is 2 + if (arguments.length == 2) { + var second = arguments[0]; + + return new Enumerable(function () { + var firstEnumerator; + var secondEnumerator; + var index = 0; + + return new IEnumerator( + function () { + firstEnumerator = source.getEnumerator(); + secondEnumerator = Enumerable.from(second).getEnumerator(); + }, + function () { + if (firstEnumerator.moveNext() && secondEnumerator.moveNext()) { + return this.yieldReturn(selector(firstEnumerator.current(), secondEnumerator.current(), index++)); + } + return false; + }, + function () { + try { + Utils.dispose(firstEnumerator); + } finally { + Utils.dispose(secondEnumerator); + } + }); + }); + } + else { + return new Enumerable(function () { + var enumerators; + var index = 0; + + return new IEnumerator( + function () { + var array = Enumerable.make(source) + .concat(Enumerable.from(args).takeExceptLast().select(Enumerable.from)) + .select(function (x) { return x.getEnumerator() }) + .toArray(); + enumerators = Enumerable.from(array); + }, + function () { + if (enumerators.all(function (x) { return x.moveNext() })) { + var array = enumerators + .select(function (x) { return x.current() }) + .toArray(); + array.push(index++); + return this.yieldReturn(selector.apply(null, array)); + } + else { + return this.yieldBreak(); + } + }, + function () { + Enumerable.from(enumerators).forEach(Utils.dispose); + }); + }); + } +}; + +// mutiple arguments +Enumerable.prototype.merge = function () { + var args = arguments; + var source = this; + + return new Enumerable(function () { + var enumerators; + var index = -1; + + return new IEnumerator( + function () { + enumerators = Enumerable.make(source) + .concat(Enumerable.from(args).select(Enumerable.from)) + .select(function (x) { return x.getEnumerator() }) + .toArray(); + }, + function () { + while (enumerators.length > 0) { + index = (index >= enumerators.length - 1) ? 0 : index + 1; + var enumerator = enumerators[index]; + + if (enumerator.moveNext()) { + return this.yieldReturn(enumerator.current()); + } + else { + enumerator.dispose(); + enumerators.splice(index--, 1); + } + } + return this.yieldBreak(); + }, + function () { + Enumerable.from(enumerators).forEach(Utils.dispose); + }); + }); +}; + +//////////////// +// Join Methods + +// Overload:function (inner, outerKeySelector, innerKeySelector, resultSelector) +// Overload:function (inner, outerKeySelector, innerKeySelector, resultSelector, compareSelector) +Enumerable.prototype.join = function (inner, outerKeySelector, innerKeySelector, resultSelector, compareSelector) { + outerKeySelector = Utils.createLambda(outerKeySelector); + innerKeySelector = Utils.createLambda(innerKeySelector); + resultSelector = Utils.createLambda(resultSelector); + compareSelector = Utils.createLambda(compareSelector); + var source = this; + + return new Enumerable(function () { + var outerEnumerator; + var lookup; + var innerElements = null; + var innerCount = 0; + + return new IEnumerator( + function () { + outerEnumerator = source.getEnumerator(); + lookup = Enumerable.from(inner).toLookup(innerKeySelector, Functions.Identity, compareSelector); + }, + function () { + while (true) { + if (innerElements != null) { + var innerElement = innerElements[innerCount++]; + if (innerElement !== undefined) { + return this.yieldReturn(resultSelector(outerEnumerator.current(), innerElement)); + } + + innerElement = null; + innerCount = 0; + } + + if (outerEnumerator.moveNext()) { + var key = outerKeySelector(outerEnumerator.current()); + innerElements = lookup.get(key).toArray(); + } else { + return false; + } + } + }, + function () { Utils.dispose(outerEnumerator); }); + }); +}; + +// Overload:function (inner, outerKeySelector, innerKeySelector, resultSelector) +// Overload:function (inner, outerKeySelector, innerKeySelector, resultSelector, compareSelector) +Enumerable.prototype.leftJoin = function (inner, outerKeySelector, innerKeySelector, resultSelector, compareSelector) { + outerKeySelector = Utils.createLambda(outerKeySelector); + innerKeySelector = Utils.createLambda(innerKeySelector); + resultSelector = Utils.createLambda(resultSelector); + compareSelector = Utils.createLambda(compareSelector); + var source = this; + + return new Enumerable(function () { + var outerEnumerator; + var lookup; + var innerElements = null; + var innerCount = 0; + + return new IEnumerator( + function () { + outerEnumerator = source.getEnumerator(); + lookup = Enumerable.from(inner).toLookup(innerKeySelector, Functions.Identity, compareSelector); + }, + function () { + while (true) { + if (innerElements != null) { + var innerElement = innerElements[innerCount++]; + if (innerElement !== undefined) { + return this.yieldReturn(resultSelector(outerEnumerator.current(), innerElement)); + } + + innerElement = null; + innerCount = 0; + } + + if (outerEnumerator.moveNext()) { + var key = outerKeySelector(outerEnumerator.current()); + innerElements = lookup.get(key).toArray(); + // execute once if innerElements is NULL + if (innerElements == null || innerElements.length == 0) { + return this.yieldReturn(resultSelector(outerEnumerator.current(), null)); + } + } else { + return false; + } + } + }, + function () { Utils.dispose(outerEnumerator); }); + }); +}; + +// Overload:function (inner, outerKeySelector, innerKeySelector, resultSelector) +// Overload:function (inner, outerKeySelector, innerKeySelector, resultSelector, compareSelector) +Enumerable.prototype.groupJoin = function (inner, outerKeySelector, innerKeySelector, resultSelector, compareSelector) { + outerKeySelector = Utils.createLambda(outerKeySelector); + innerKeySelector = Utils.createLambda(innerKeySelector); + resultSelector = Utils.createLambda(resultSelector); + compareSelector = Utils.createLambda(compareSelector); + var source = this; + + return new Enumerable(function () { + var enumerator = source.getEnumerator(); + var lookup = null; + + return new IEnumerator( + function () { + enumerator = source.getEnumerator(); + lookup = Enumerable.from(inner).toLookup(innerKeySelector, Functions.Identity, compareSelector); + }, + function () { + if (enumerator.moveNext()) { + var innerElement = lookup.get(outerKeySelector(enumerator.current())); + return this.yieldReturn(resultSelector(enumerator.current(), innerElement)); + } + return false; + }, + function () { Utils.dispose(enumerator); }); + }); +}; + +/////////////// +// Set Methods + +Enumerable.prototype.all = function (predicate) { + predicate = Utils.createLambda(predicate); + + var result = true; + this.forEach(function (x) { + if (!predicate(x)) { + result = false; + return false; // break + } + }); + return result; +}; + +// Overload:function() +// Overload:function(predicate) +Enumerable.prototype.any = function (predicate) { + predicate = Utils.createLambda(predicate); + + var enumerator = this.getEnumerator(); + try { + if (arguments.length == 0) return enumerator.moveNext(); // case:function() + + while (enumerator.moveNext()) // case:function(predicate) + { + if (predicate(enumerator.current())) return true; + } + return false; + } + finally { + Utils.dispose(enumerator); + } +}; + +Enumerable.prototype.isEmpty = function () { + return !this.any(); +}; + +// multiple arguments +Enumerable.prototype.concat = function () { + var source = this; + + if (arguments.length == 1) { + var second = arguments[0]; + + return new Enumerable(function () { + var firstEnumerator; + var secondEnumerator; + + return new IEnumerator( + function () { firstEnumerator = source.getEnumerator(); }, + function () { + if (secondEnumerator == null) { + if (firstEnumerator.moveNext()) return this.yieldReturn(firstEnumerator.current()); + secondEnumerator = Enumerable.from(second).getEnumerator(); + } + if (secondEnumerator.moveNext()) return this.yieldReturn(secondEnumerator.current()); + return false; + }, + function () { + try { + Utils.dispose(firstEnumerator); + } + finally { + Utils.dispose(secondEnumerator); + } + }); + }); + } + else { + var args = arguments; + + return new Enumerable(function () { + var enumerators; + + return new IEnumerator( + function () { + enumerators = Enumerable.make(source) + .concat(Enumerable.from(args).select(Enumerable.from)) + .select(function (x) { return x.getEnumerator() }) + .toArray(); + }, + function () { + while (enumerators.length > 0) { + var enumerator = enumerators[0]; + + if (enumerator.moveNext()) { + return this.yieldReturn(enumerator.current()); + } + else { + enumerator.dispose(); + enumerators.splice(0, 1); + } + } + return this.yieldBreak(); + }, + function () { + Enumerable.from(enumerators).forEach(Utils.dispose); + }); + }); + } +}; + +Enumerable.prototype.insert = function (index, second) { + var source = this; + + return new Enumerable(function () { + var firstEnumerator; + var secondEnumerator; + var count = 0; + var isEnumerated = false; + + return new IEnumerator( + function () { + firstEnumerator = source.getEnumerator(); + secondEnumerator = Enumerable.from(second).getEnumerator(); + }, + function () { + if (count == index && secondEnumerator.moveNext()) { + isEnumerated = true; + return this.yieldReturn(secondEnumerator.current()); + } + if (firstEnumerator.moveNext()) { + count++; + return this.yieldReturn(firstEnumerator.current()); + } + if (!isEnumerated && secondEnumerator.moveNext()) { + return this.yieldReturn(secondEnumerator.current()); + } + return false; + }, + function () { + try { + Utils.dispose(firstEnumerator); + } + finally { + Utils.dispose(secondEnumerator); + } + }); + }); +}; + +Enumerable.prototype.alternate = function (alternateValueOrSequence) { + var source = this; + + return new Enumerable(function () { + var buffer; + var enumerator; + var alternateSequence; + var alternateEnumerator; + + return new IEnumerator( + function () { + if (alternateValueOrSequence instanceof Array || alternateValueOrSequence.getEnumerator != null) { + alternateSequence = Enumerable.from(Enumerable.from(alternateValueOrSequence).toArray()); // freeze + } + else { + alternateSequence = Enumerable.make(alternateValueOrSequence); + } + enumerator = source.getEnumerator(); + if (enumerator.moveNext()) buffer = enumerator.current(); + }, + function () { + while (true) { + if (alternateEnumerator != null) { + if (alternateEnumerator.moveNext()) { + return this.yieldReturn(alternateEnumerator.current()); + } + else { + alternateEnumerator = null; + } + } + + if (buffer == null && enumerator.moveNext()) { + buffer = enumerator.current(); // hasNext + alternateEnumerator = alternateSequence.getEnumerator(); + continue; // GOTO + } + else if (buffer != null) { + var retVal = buffer; + buffer = null; + return this.yieldReturn(retVal); + } + + return this.yieldBreak(); + } + }, + function () { + try { + Utils.dispose(enumerator); + } + finally { + Utils.dispose(alternateEnumerator); + } + }); + }); +}; + +// Overload:function(value) +// Overload:function(value, compareSelector) +Enumerable.prototype.contains = function (value, compareSelector) { + compareSelector = Utils.createLambda(compareSelector); + var enumerator = this.getEnumerator(); + try { + while (enumerator.moveNext()) { + if (compareSelector(enumerator.current()) === value) return true; + } + return false; + } + finally { + Utils.dispose(enumerator); + } +}; + +Enumerable.prototype.defaultIfEmpty = function (defaultValue) { + var source = this; + if (defaultValue === undefined) defaultValue = null; + + return new Enumerable(function () { + var enumerator; + var isFirst = true; + + return new IEnumerator( + function () { enumerator = source.getEnumerator(); }, + function () { + if (enumerator.moveNext()) { + isFirst = false; + return this.yieldReturn(enumerator.current()); + } + else if (isFirst) { + isFirst = false; + return this.yieldReturn(defaultValue); + } + return false; + }, + function () { Utils.dispose(enumerator); }); + }); +}; + +// Overload:function() +// Overload:function(compareSelector) +Enumerable.prototype.distinct = function (compareSelector) { + return this.except(Enumerable.empty(), compareSelector); +}; + +Enumerable.prototype.distinctUntilChanged = function (compareSelector) { + compareSelector = Utils.createLambda(compareSelector); + var source = this; + + return new Enumerable(function () { + var enumerator; + var compareKey; + var initial; + + return new IEnumerator( + function () { + enumerator = source.getEnumerator(); + }, + function () { + while (enumerator.moveNext()) { + var key = compareSelector(enumerator.current()); + + if (initial) { + initial = false; + compareKey = key; + return this.yieldReturn(enumerator.current()); + } + + if (compareKey === key) { + continue; + } + + compareKey = key; + return this.yieldReturn(enumerator.current()); + } + return this.yieldBreak(); + }, + function () { Utils.dispose(enumerator); }); + }); +}; + +// Overload:function(second) +// Overload:function(second, compareSelector) +Enumerable.prototype.except = function (second, compareSelector) { + compareSelector = Utils.createLambda(compareSelector); + var source = this; + + return new Enumerable(function () { + var enumerator; + var keys; + + return new IEnumerator( + function () { + enumerator = source.getEnumerator(); + keys = new Dictionary(compareSelector); + Enumerable.from(second).forEach(function (key) { keys.add(key); }); + }, + function () { + while (enumerator.moveNext()) { + var current = enumerator.current(); + if (!keys.contains(current)) { + keys.add(current); + return this.yieldReturn(current); + } + } + return false; + }, + function () { Utils.dispose(enumerator); }); + }); +}; + +// Overload:function(second) +// Overload:function(second, compareSelector) +Enumerable.prototype.intersect = function (second, compareSelector) { + compareSelector = Utils.createLambda(compareSelector); + var source = this; + + return new Enumerable(function () { + var enumerator; + var keys; + var outs; + + return new IEnumerator( + function () { + enumerator = source.getEnumerator(); + + keys = new Dictionary(compareSelector); + Enumerable.from(second).forEach(function (key) { keys.add(key); }); + outs = new Dictionary(compareSelector); + }, + function () { + while (enumerator.moveNext()) { + var current = enumerator.current(); + if (!outs.contains(current) && keys.contains(current)) { + outs.add(current); + return this.yieldReturn(current); + } + } + return false; + }, + function () { Utils.dispose(enumerator); }); + }); +}; + +// Overload:function(second) +// Overload:function(second, compareSelector) +Enumerable.prototype.sequenceEqual = function (second, compareSelector) { + compareSelector = Utils.createLambda(compareSelector); + + var firstEnumerator = this.getEnumerator(); + try { + var secondEnumerator = Enumerable.from(second).getEnumerator(); + try { + while (firstEnumerator.moveNext()) { + if (!secondEnumerator.moveNext() + || compareSelector(firstEnumerator.current()) !== compareSelector(secondEnumerator.current())) { + return false; + } + } + + if (secondEnumerator.moveNext()) return false; + return true; + } + finally { + Utils.dispose(secondEnumerator); + } + } + finally { + Utils.dispose(firstEnumerator); + } +}; + +Enumerable.prototype.union = function (second, compareSelector) { + compareSelector = Utils.createLambda(compareSelector); + var source = this; + + return new Enumerable(function () { + var firstEnumerator; + var secondEnumerator; + var keys; + + return new IEnumerator( + function () { + firstEnumerator = source.getEnumerator(); + keys = new Dictionary(compareSelector); + }, + function () { + var current; + if (secondEnumerator === undefined) { + while (firstEnumerator.moveNext()) { + current = firstEnumerator.current(); + if (!keys.contains(current)) { + keys.add(current); + return this.yieldReturn(current); + } + } + secondEnumerator = Enumerable.from(second).getEnumerator(); + } + while (secondEnumerator.moveNext()) { + current = secondEnumerator.current(); + if (!keys.contains(current)) { + keys.add(current); + return this.yieldReturn(current); + } + } + return false; + }, + function () { + try { + Utils.dispose(firstEnumerator); + } + finally { + Utils.dispose(secondEnumerator); + } + }); + }); +}; + +//////////////////// +// Ordering Methods + +Enumerable.prototype.orderBy = function (keySelector, comparer) { + return new OrderedEnumerable(this, keySelector, comparer, false); +}; + +Enumerable.prototype.orderByDescending = function (keySelector, comparer) { + return new OrderedEnumerable(this, keySelector, comparer, true); +}; + +Enumerable.prototype.reverse = function () { + var source = this; + + return new Enumerable(function () { + var buffer; + var index; + + return new IEnumerator( + function () { + buffer = source.toArray(); + index = buffer.length; + }, + function () { + return (index > 0) + ? this.yieldReturn(buffer[--index]) + : false; + }, + Functions.Blank); + }); +}; + +Enumerable.prototype.shuffle = function () { + var source = this; + + return new Enumerable(function () { + var buffer; + + return new IEnumerator( + function () { buffer = source.toArray(); }, + function () { + if (buffer.length > 0) { + var i = Math.floor(Math.random() * buffer.length); + return this.yieldReturn(buffer.splice(i, 1)[0]); + } + return false; + }, + Functions.Blank); + }); +}; + +Enumerable.prototype.weightedSample = function (weightSelector) { + weightSelector = Utils.createLambda(weightSelector); + var source = this; + + return new Enumerable(function () { + var sortedByBound; + var totalWeight = 0; + + return new IEnumerator( + function () { + sortedByBound = source + .choose(function (x) { + var weight = weightSelector(x); + if (weight <= 0) return null; // ignore 0 + + totalWeight += weight; + return { value: x, bound: totalWeight }; + }) + .toArray(); + }, + function () { + if (sortedByBound.length > 0) { + var draw = Math.floor(Math.random() * totalWeight) + 1; + + var lower = -1; + var upper = sortedByBound.length; + while (upper - lower > 1) { + var index = Math.floor((lower + upper) / 2); + if (sortedByBound[index].bound >= draw) { + upper = index; + } + else { + lower = index; + } + } + + return this.yieldReturn(sortedByBound[upper].value); + } + + return this.yieldBreak(); + }, + Functions.Blank); + }); +}; + +//////////////////// +// Grouping Methods + +// Overload:function(keySelector) +// Overload:function(keySelector,elementSelector) +// Overload:function(keySelector,elementSelector,resultSelector) +// Overload:function(keySelector,elementSelector,resultSelector,compareSelector) +Enumerable.prototype.groupBy = function (keySelector, elementSelector, resultSelector, compareSelector) { + var source = this; + keySelector = Utils.createLambda(keySelector); + elementSelector = Utils.createLambda(elementSelector); + if (resultSelector != null) resultSelector = Utils.createLambda(resultSelector); + compareSelector = Utils.createLambda(compareSelector); + + return new Enumerable(function () { + var enumerator; + + return new IEnumerator( + function () { + enumerator = source.toLookup(keySelector, elementSelector, compareSelector) + .toEnumerable() + .getEnumerator(); + }, + function () { + while (enumerator.moveNext()) { + return (resultSelector == null) + ? this.yieldReturn(enumerator.current()) + : this.yieldReturn(resultSelector(enumerator.current().key(), enumerator.current())); + } + return false; + }, + function () { Utils.dispose(enumerator); }); + }); +}; + +// Overload:function(keySelector) +// Overload:function(keySelector,elementSelector) +// Overload:function(keySelector,elementSelector,resultSelector) +// Overload:function(keySelector,elementSelector,resultSelector,compareSelector) +Enumerable.prototype.partitionBy = function (keySelector, elementSelector, resultSelector, compareSelector) { + var source = this; + keySelector = Utils.createLambda(keySelector); + elementSelector = Utils.createLambda(elementSelector); + compareSelector = Utils.createLambda(compareSelector); + var hasResultSelector; + if (resultSelector == null) { + hasResultSelector = false; + resultSelector = function (key, group) { return new Grouping(key, group); }; + } + else { + hasResultSelector = true; + resultSelector = Utils.createLambda(resultSelector); + } + + return new Enumerable(function () { + var enumerator; + var key; + var compareKey; + var group = []; + + return new IEnumerator( + function () { + enumerator = source.getEnumerator(); + if (enumerator.moveNext()) { + key = keySelector(enumerator.current()); + compareKey = compareSelector(key); + group.push(elementSelector(enumerator.current())); + } + }, + function () { + var hasNext; + while ((hasNext = enumerator.moveNext()) == true) { + if (compareKey === compareSelector(keySelector(enumerator.current()))) { + group.push(elementSelector(enumerator.current())); + } + else break; + } + + if (group.length > 0) { + var result = (hasResultSelector) + ? resultSelector(key, Enumerable.from(group)) + : resultSelector(key, group); + if (hasNext) { + key = keySelector(enumerator.current()); + compareKey = compareSelector(key); + group = [elementSelector(enumerator.current())]; + } + else group = []; + + return this.yieldReturn(result); + } + + return false; + }, + function () { Utils.dispose(enumerator); }); + }); +}; + +Enumerable.prototype.buffer = function (count) { + var source = this; + + return new Enumerable(function () { + var enumerator; + + return new IEnumerator( + function () { enumerator = source.getEnumerator(); }, + function () { + var array = []; + var index = 0; + while (enumerator.moveNext()) { + array.push(enumerator.current()); + if (++index >= count) return this.yieldReturn(array); + } + if (array.length > 0) return this.yieldReturn(array); + return false; + }, + function () { Utils.dispose(enumerator); }); + }); +}; + +///////////////////// +// Aggregate Methods + +// Overload:function(func) +// Overload:function(seed,func) +// Overload:function(seed,func,resultSelector) +Enumerable.prototype.aggregate = function (seed, func, resultSelector) { + resultSelector = Utils.createLambda(resultSelector); + return resultSelector(this.scan(seed, func, resultSelector).last()); +}; + +// Overload:function() +// Overload:function(selector) +Enumerable.prototype.average = function (selector) { + selector = Utils.createLambda(selector); + + var sum = 0; + var count = 0; + this.forEach(function (x) { + sum += selector(x); + ++count; + }); + + return sum / count; +}; + +// Overload:function() +// Overload:function(predicate) +Enumerable.prototype.count = function (predicate) { + predicate = (predicate == null) ? Functions.True : Utils.createLambda(predicate); + + var count = 0; + this.forEach(function (x, i) { + if (predicate(x, i)) ++count; + }); + return count; +}; + +// Overload:function() +// Overload:function(selector) +Enumerable.prototype.max = function (selector) { + if (selector == null) selector = Functions.Identity; + return this.select(selector).aggregate(function (a, b) { return (a > b) ? a : b; }); +}; + +// Overload:function() +// Overload:function(selector) +Enumerable.prototype.min = function (selector) { + if (selector == null) selector = Functions.Identity; + return this.select(selector).aggregate(function (a, b) { return (a < b) ? a : b; }); +}; + +Enumerable.prototype.maxBy = function (keySelector) { + keySelector = Utils.createLambda(keySelector); + return this.aggregate(function (a, b) { return (keySelector(a) > keySelector(b)) ? a : b; }); +}; + +Enumerable.prototype.minBy = function (keySelector) { + keySelector = Utils.createLambda(keySelector); + return this.aggregate(function (a, b) { return (keySelector(a) < keySelector(b)) ? a : b; }); +}; + +// Overload:function() +// Overload:function(selector) +Enumerable.prototype.sum = function (selector) { + if (selector == null) selector = Functions.Identity; + return this.select(selector).aggregate(0, function (a, b) { return a + b; }); +}; + +////////////////// +// Paging Methods + +Enumerable.prototype.elementAt = function (index) { + var value; + var found = false; + this.forEach(function (x, i) { + if (i == index) { + value = x; + found = true; + return false; + } + }); + + if (!found) throw new Error("index is less than 0 or greater than or equal to the number of elements in source."); + return value; +}; + +Enumerable.prototype.elementAtOrDefault = function (index, defaultValue) { + if (defaultValue === undefined) defaultValue = null; + var value; + var found = false; + this.forEach(function (x, i) { + if (i == index) { + value = x; + found = true; + return false; + } + }); + + return (!found) ? defaultValue : value; +}; + +// Overload:function() +// Overload:function(predicate) +Enumerable.prototype.first = function (predicate) { + if (predicate != null) return this.where(predicate).first(); + + var value; + var found = false; + this.forEach(function (x) { + value = x; + found = true; + return false; + }); + + if (!found) throw new Error("first:No element satisfies the condition."); + return value; +}; + +Enumerable.prototype.firstOrDefault = function (predicate, defaultValue) { + if (predicate !== undefined) { + if (typeof predicate === Types.Function || typeof Utils.createLambda(predicate) === Types.Function) { + return this.where(predicate).firstOrDefault(undefined, defaultValue); + } + defaultValue = predicate; + } + + var value; + var found = false; + this.forEach(function (x) { + value = x; + found = true; + return false; + }); + return (!found) ? defaultValue : value; +}; + +// Overload:function() +// Overload:function(predicate) +Enumerable.prototype.last = function (predicate) { + if (predicate != null) return this.where(predicate).last(); + + var value; + var found = false; + this.forEach(function (x) { + found = true; + value = x; + }); + + if (!found) throw new Error("last:No element satisfies the condition."); + return value; +}; + +Enumerable.prototype.lastOrDefault = function (predicate, defaultValue) { + if (predicate !== undefined) { + if (typeof predicate === Types.Function || typeof Utils.createLambda(predicate) === Types.Function) { + return this.where(predicate).lastOrDefault(undefined, defaultValue); + } + defaultValue = predicate; + } + + var value; + var found = false; + this.forEach(function (x) { + found = true; + value = x; + }); + return (!found) ? defaultValue : value; +}; + +// Overload:function() +// Overload:function(predicate) +Enumerable.prototype.single = function (predicate) { + if (predicate != null) return this.where(predicate).single(); + + var value; + var found = false; + this.forEach(function (x) { + if (!found) { + found = true; + value = x; + } else throw new Error("single:sequence contains more than one element."); + }); + + if (!found) throw new Error("single:No element satisfies the condition."); + return value; +}; + +// Overload:function(defaultValue) +// Overload:function(defaultValue,predicate) +Enumerable.prototype.singleOrDefault = function (predicate, defaultValue) { + if (defaultValue === undefined) defaultValue = null; + if (predicate != null) return this.where(predicate).singleOrDefault(null, defaultValue); + + var value; + var found = false; + this.forEach(function (x) { + if (!found) { + found = true; + value = x; + } else throw new Error("single:sequence contains more than one element."); + }); + + return (!found) ? defaultValue : value; +}; + +Enumerable.prototype.skip = function (count) { + var source = this; + + return new Enumerable(function () { + var enumerator; + var index = 0; + + return new IEnumerator( + function () { + enumerator = source.getEnumerator(); + while (index++ < count && enumerator.moveNext()) { } + }, + function () { + return (enumerator.moveNext()) + ? this.yieldReturn(enumerator.current()) + : false; + }, + function () { Utils.dispose(enumerator); }); + }); +}; + +// Overload:function(predicate) +// Overload:function(predicate) +Enumerable.prototype.skipWhile = function (predicate) { + predicate = Utils.createLambda(predicate); + var source = this; + + return new Enumerable(function () { + var enumerator; + var index = 0; + var isSkipEnd = false; + + return new IEnumerator( + function () { enumerator = source.getEnumerator(); }, + function () { + while (!isSkipEnd) { + if (enumerator.moveNext()) { + if (!predicate(enumerator.current(), index++)) { + isSkipEnd = true; + return this.yieldReturn(enumerator.current()); + } + continue; + } else return false; + } + + return (enumerator.moveNext()) + ? this.yieldReturn(enumerator.current()) + : false; + + }, + function () { Utils.dispose(enumerator); }); + }); +}; + +Enumerable.prototype.take = function (count) { + var source = this; + + return new Enumerable(function () { + var enumerator; + var index = 0; + + return new IEnumerator( + function () { enumerator = source.getEnumerator(); }, + function () { + return (index++ < count && enumerator.moveNext()) + ? this.yieldReturn(enumerator.current()) + : false; + }, + function () { Utils.dispose(enumerator); } + ); + }); +}; + +// Overload:function(predicate) +// Overload:function(predicate) +Enumerable.prototype.takeWhile = function (predicate) { + predicate = Utils.createLambda(predicate); + var source = this; + + return new Enumerable(function () { + var enumerator; + var index = 0; + + return new IEnumerator( + function () { enumerator = source.getEnumerator(); }, + function () { + return (enumerator.moveNext() && predicate(enumerator.current(), index++)) + ? this.yieldReturn(enumerator.current()) + : false; + }, + function () { Utils.dispose(enumerator); }); + }); +}; + +// Overload:function() +// Overload:function(count) +Enumerable.prototype.takeExceptLast = function (count) { + if (count == null) count = 1; + var source = this; + + return new Enumerable(function () { + if (count <= 0) return source.getEnumerator(); // do nothing + + var enumerator; + var q = []; + + return new IEnumerator( + function () { enumerator = source.getEnumerator(); }, + function () { + while (enumerator.moveNext()) { + if (q.length == count) { + q.push(enumerator.current()); + return this.yieldReturn(q.shift()); + } + q.push(enumerator.current()); + } + return false; + }, + function () { Utils.dispose(enumerator); }); + }); +}; + +Enumerable.prototype.takeFromLast = function (count) { + if (count <= 0 || count == null) return Enumerable.empty(); + var source = this; + + return new Enumerable(function () { + var sourceEnumerator; + var enumerator; + var q = []; + + return new IEnumerator( + function () { sourceEnumerator = source.getEnumerator(); }, + function () { + while (sourceEnumerator.moveNext()) { + if (q.length == count) q.shift(); + q.push(sourceEnumerator.current()); + } + if (enumerator == null) { + enumerator = Enumerable.from(q).getEnumerator(); + } + return (enumerator.moveNext()) + ? this.yieldReturn(enumerator.current()) + : false; + }, + function () { Utils.dispose(enumerator); }); + }); +}; + +// Overload:function(item) +// Overload:function(predicate) +Enumerable.prototype.indexOf = function (item) { + var found = null; + + // item as predicate + if (typeof (item) === Types.Function) { + this.forEach(function (x, i) { + if (item(x, i)) { + found = i; + return false; + } + }); + } + else { + this.forEach(function (x, i) { + if (x === item) { + found = i; + return false; + } + }); + } + + return (found !== null) ? found : -1; +}; + +// Overload:function(item) +// Overload:function(predicate) +Enumerable.prototype.lastIndexOf = function (item) { + var result = -1; + + // item as predicate + if (typeof (item) === Types.Function) { + this.forEach(function (x, i) { + if (item(x, i)) result = i; + }); + } + else { + this.forEach(function (x, i) { + if (x === item) result = i; + }); + } + + return result; +}; + +/////////////////// +// Convert Methods + +Enumerable.prototype.cast = function () { + return this; +}; + +Enumerable.prototype.asEnumerable = function () { + return Enumerable.from(this); +}; + +Enumerable.prototype.toArray = function () { + var array = []; + this.forEach(function (x) { array.push(x); }); + return array; +}; + +// Overload:function(keySelector) +// Overload:function(keySelector, elementSelector) +// Overload:function(keySelector, elementSelector, compareSelector) +Enumerable.prototype.toLookup = function (keySelector, elementSelector, compareSelector) { + keySelector = Utils.createLambda(keySelector); + elementSelector = Utils.createLambda(elementSelector); + compareSelector = Utils.createLambda(compareSelector); + + var dict = new Dictionary(compareSelector); + this.forEach(function (x) { + var key = keySelector(x); + var element = elementSelector(x); + + var array = dict.get(key); + if (array !== undefined) array.push(element); + else dict.add(key, [element]); + }); + return new Lookup(dict); +}; + +Enumerable.prototype.toObject = function (keySelector, elementSelector) { + keySelector = Utils.createLambda(keySelector); + elementSelector = Utils.createLambda(elementSelector); + + var obj = {}; + this.forEach(function (x) { + obj[keySelector(x)] = elementSelector(x); + }); + return obj; +}; + +// Overload:function(keySelector, elementSelector) +// Overload:function(keySelector, elementSelector, compareSelector) +Enumerable.prototype.toDictionary = function (keySelector, elementSelector, compareSelector) { + keySelector = Utils.createLambda(keySelector); + elementSelector = Utils.createLambda(elementSelector); + compareSelector = Utils.createLambda(compareSelector); + + var dict = new Dictionary(compareSelector); + this.forEach(function (x) { + dict.add(keySelector(x), elementSelector(x)); + }); + return dict; +}; + +// Overload:function() +// Overload:function(replacer) +// Overload:function(replacer, space) +Enumerable.prototype.toJSONString = function (replacer, space) { + if (typeof JSON === Types.Undefined || JSON.stringify == null) { + throw new Error("toJSONString can't find JSON.stringify. This works native JSON support Browser or include json2.js"); + } + return JSON.stringify(this.toArray(), replacer, space); +}; + +// Overload:function() +// Overload:function(separator) +// Overload:function(separator,selector) +Enumerable.prototype.toJoinedString = function (separator, selector) { + if (separator == null) separator = ""; + if (selector == null) selector = Functions.Identity; + + return this.select(selector).toArray().join(separator); +}; + +////////////////// +// Action Methods + +// Overload:function(action) +// Overload:function(action) +Enumerable.prototype.doAction = function (action) { + var source = this; + action = Utils.createLambda(action); + + return new Enumerable(function () { + var enumerator; + var index = 0; + + return new IEnumerator( + function () { enumerator = source.getEnumerator(); }, + function () { + if (enumerator.moveNext()) { + action(enumerator.current(), index++); + return this.yieldReturn(enumerator.current()); + } + return false; + }, + function () { Utils.dispose(enumerator); }); + }); +}; + +// Overload:function(action) +// Overload:function(action) +// Overload:function(func) +// Overload:function(func) +Enumerable.prototype.forEach = function (action) { + action = Utils.createLambda(action); + + var index = 0; + var enumerator = this.getEnumerator(); + try { + while (enumerator.moveNext()) { + if (action(enumerator.current(), index++) === false) break; + } + } finally { + Utils.dispose(enumerator); + } +}; + +Enumerable.prototype.force = function () { + var enumerator = this.getEnumerator(); + + try { + while (enumerator.moveNext()) { } + } + finally { + Utils.dispose(enumerator); + } +}; + +////////////////////// +// Functional Methods + +Enumerable.prototype.letBind = function (func) { + func = Utils.createLambda(func); + var source = this; + + return new Enumerable(function () { + var enumerator; + + return new IEnumerator( + function () { + enumerator = Enumerable.from(func(source)).getEnumerator(); + }, + function () { + return (enumerator.moveNext()) + ? this.yieldReturn(enumerator.current()) + : false; + }, + function () { Utils.dispose(enumerator); }); + }); +}; + +Enumerable.prototype.share = function () { + var source = this; + var sharedEnumerator; + var disposed = false; + + return new DisposableEnumerable(function () { + return new IEnumerator( + function () { + if (sharedEnumerator == null) { + sharedEnumerator = source.getEnumerator(); + } + }, + function () { + if (disposed) throw new Error("enumerator is disposed"); + + return (sharedEnumerator.moveNext()) + ? this.yieldReturn(sharedEnumerator.current()) + : false; + }, + Functions.Blank + ); + }, function () { + disposed = true; + Utils.dispose(sharedEnumerator); + }); +}; + +Enumerable.prototype.memoize = function () { + var source = this; + var cache; + var enumerator; + var disposed = false; + + return new DisposableEnumerable(function () { + var index = -1; + + return new IEnumerator( + function () { + if (enumerator == null) { + enumerator = source.getEnumerator(); + cache = []; + } + }, + function () { + if (disposed) throw new Error("enumerator is disposed"); + + index++; + if (cache.length <= index) { + return (enumerator.moveNext()) + ? this.yieldReturn(cache[index] = enumerator.current()) + : false; + } + + return this.yieldReturn(cache[index]); + }, + Functions.Blank + ); + }, function () { + disposed = true; + Utils.dispose(enumerator); + cache = null; + }); +}; + +// Iterator support (ES6 for..of) +if (Utils.hasNativeIteratorSupport()) { + Enumerable.prototype[Symbol.iterator] = function () { + return { + enumerator: this.getEnumerator(), + next: function () { + if (this.enumerator.moveNext()) { + return { + done: false, + value: this.enumerator.current() + }; + } else { + return { done: true }; + } + } + }; + }; +} + +////////////////////////// +// Error Handling Methods + +Enumerable.prototype.catchError = function (handler) { + handler = Utils.createLambda(handler); + var source = this; + + return new Enumerable(function () { + var enumerator; + + return new IEnumerator( + function () { enumerator = source.getEnumerator(); }, + function () { + try { + return (enumerator.moveNext()) + ? this.yieldReturn(enumerator.current()) + : false; + } catch (e) { + handler(e); + return false; + } + }, + function () { Utils.dispose(enumerator); }); + }); +}; + +Enumerable.prototype.finallyAction = function (finallyAction) { + finallyAction = Utils.createLambda(finallyAction); + var source = this; + + return new Enumerable(function () { + var enumerator; + + return new IEnumerator( + function () { enumerator = source.getEnumerator(); }, + function () { + return (enumerator.moveNext()) + ? this.yieldReturn(enumerator.current()) + : false; + }, + function () { + try { + Utils.dispose(enumerator); + } finally { + finallyAction(); + } + }); + }); +}; + +///////////////// +// Debug Methods + +// Overload:function() +// Overload:function(selector) +Enumerable.prototype.log = function (selector) { + selector = Utils.createLambda(selector); + + return this.doAction(function (item) { + if (typeof console !== Types.Undefined) { + console.log(selector(item)); + } + }); +}; + +// Overload:function() +// Overload:function(message) +// Overload:function(message,selector) +Enumerable.prototype.trace = function (message, selector) { + if (message == null) message = "Trace"; + selector = Utils.createLambda(selector); + + return this.doAction(function (item) { + if (typeof console !== Types.Undefined) { + console.log(message, selector(item)); + } + }); +}; + +/////////// +// Private + +var OrderedEnumerable = function (source, keySelector, comparer, descending, parent) { + this.source = source; + this.keySelector = Utils.createLambda(keySelector); + this.descending = descending; + this.parent = parent; + + if (comparer) + this.comparer = Utils.createLambda(comparer); +}; +OrderedEnumerable.prototype = new Enumerable(); + +OrderedEnumerable.prototype.createOrderedEnumerable = function (keySelector, comparer, descending) { + return new OrderedEnumerable(this.source, keySelector, comparer, descending, this); +}; + +OrderedEnumerable.prototype.thenBy = function (keySelector, comparer) { + return this.createOrderedEnumerable(keySelector, comparer, false); +}; + +OrderedEnumerable.prototype.thenByDescending = function (keySelector, comparer) { + return this.createOrderedEnumerable(keySelector, comparer, true); +}; + +OrderedEnumerable.prototype.getEnumerator = function () { + var self = this; + var buffer; + var indexes; + var index = 0; + + return new IEnumerator( + function () { + buffer = []; + indexes = []; + self.source.forEach(function (item, index) { + buffer.push(item); + indexes.push(index); + }); + var sortContext = SortContext.create(self, null); + sortContext.GenerateKeys(buffer); + + indexes.sort(function (a, b) { return sortContext.compare(a, b); }); + }, + function () { + return (index < indexes.length) + ? this.yieldReturn(buffer[indexes[index++]]) + : false; + }, + Functions.Blank + ); +}; + +var SortContext = function (keySelector, comparer, descending, child) { + this.keySelector = keySelector; + this.descending = descending; + this.child = child; + this.comparer = comparer; + this.keys = null; +}; + +SortContext.create = function (orderedEnumerable, currentContext) { + var context = new SortContext( + orderedEnumerable.keySelector, orderedEnumerable.comparer, orderedEnumerable.descending, currentContext + ); + + if (orderedEnumerable.parent != null) return SortContext.create(orderedEnumerable.parent, context); + return context; +}; + +SortContext.prototype.GenerateKeys = function (source) { + var len = source.length; + var keySelector = this.keySelector; + var keys = new Array(len); + for (var i = 0; i < len; i++) keys[i] = keySelector(source[i]); + this.keys = keys; + + if (this.child != null) this.child.GenerateKeys(source); +}; + +SortContext.prototype.compare = function (index1, index2) { + var comparison = this.comparer ? + this.comparer(this.keys[index1], this.keys[index2]) : + Utils.compare(this.keys[index1], this.keys[index2]); + + if (comparison == 0) { + if (this.child != null) return this.child.compare(index1, index2); + return Utils.compare(index1, index2); + } + + return (this.descending) ? -comparison : comparison; +}; + +var DisposableEnumerable = function (getEnumerator, dispose) { + this.dispose = dispose; + Enumerable.call(this, getEnumerator); +}; +DisposableEnumerable.prototype = new Enumerable(); + +var ArrayEnumerable = function (source) { + this.getSource = function () { return source; }; +}; +ArrayEnumerable.prototype = new Enumerable(); + +ArrayEnumerable.prototype.any = function (predicate) { + return (predicate == null) + ? (this.getSource().length > 0) + : Enumerable.prototype.any.apply(this, arguments); +}; + +ArrayEnumerable.prototype.count = function (predicate) { + return (predicate == null) + ? this.getSource().length + : Enumerable.prototype.count.apply(this, arguments); +}; + +ArrayEnumerable.prototype.elementAt = function (index) { + var source = this.getSource(); + return (0 <= index && index < source.length) + ? source[index] + : Enumerable.prototype.elementAt.apply(this, arguments); +}; + +ArrayEnumerable.prototype.elementAtOrDefault = function (index, defaultValue) { + if (defaultValue === undefined) defaultValue = null; + var source = this.getSource(); + return (0 <= index && index < source.length) + ? source[index] + : defaultValue; +}; + +ArrayEnumerable.prototype.first = function (predicate) { + var source = this.getSource(); + return (predicate == null && source.length > 0) + ? source[0] + : Enumerable.prototype.first.apply(this, arguments); +}; + +ArrayEnumerable.prototype.firstOrDefault = function (predicate, defaultValue) { + if (predicate !== undefined) { + return Enumerable.prototype.firstOrDefault.apply(this, arguments); + } + defaultValue = predicate; + + var source = this.getSource(); + return source.length > 0 ? source[0] : defaultValue; +}; + +ArrayEnumerable.prototype.last = function (predicate) { + var source = this.getSource(); + return (predicate == null && source.length > 0) + ? source[source.length - 1] + : Enumerable.prototype.last.apply(this, arguments); +}; + +ArrayEnumerable.prototype.lastOrDefault = function (predicate, defaultValue) { + if (predicate !== undefined) { + return Enumerable.prototype.lastOrDefault.apply(this, arguments); + } + defaultValue = predicate; + + var source = this.getSource(); + return source.length > 0 ? source[source.length - 1] : defaultValue; +}; + +ArrayEnumerable.prototype.skip = function (count) { + var source = this.getSource(); + + return new Enumerable(function () { + var index; + + return new IEnumerator( + function () { index = (count < 0) ? 0 : count; }, + function () { + return (index < source.length) + ? this.yieldReturn(source[index++]) + : false; + }, + Functions.Blank); + }); +}; + +ArrayEnumerable.prototype.takeExceptLast = function (count) { + if (count == null) count = 1; + return this.take(this.getSource().length - count); +}; + +ArrayEnumerable.prototype.takeFromLast = function (count) { + return this.skip(this.getSource().length - count); +}; + +ArrayEnumerable.prototype.reverse = function () { + var source = this.getSource(); + + return new Enumerable(function () { + var index; + + return new IEnumerator( + function () { + index = source.length; + }, + function () { + return (index > 0) + ? this.yieldReturn(source[--index]) + : false; + }, + Functions.Blank); + }); +}; + +ArrayEnumerable.prototype.sequenceEqual = function (second, compareSelector) { + if ((second instanceof ArrayEnumerable || second instanceof Array) + && compareSelector == null + && Enumerable.from(second).count() != this.count()) { + return false; + } + + return Enumerable.prototype.sequenceEqual.apply(this, arguments); +}; + +ArrayEnumerable.prototype.toJoinedString = function (separator, selector) { + var source = this.getSource(); + if (selector != null || !(source instanceof Array)) { + return Enumerable.prototype.toJoinedString.apply(this, arguments); + } + + if (separator == null) separator = ""; + return source.join(separator); +}; + +ArrayEnumerable.prototype.getEnumerator = function () { + var source = this.getSource(); + var index = -1; + + // fast and simple enumerator + return { + current: function () { return source[index]; }, + moveNext: function () { + return ++index < source.length; + }, + dispose: Functions.Blank + }; +}; + +// optimization for multiple where and multiple select and whereselect + +var WhereEnumerable = function (source, predicate) { + this.prevSource = source; + this.prevPredicate = predicate; // predicate.length always <= 1 +}; +WhereEnumerable.prototype = new Enumerable(); + +WhereEnumerable.prototype.where = function (predicate) { + predicate = Utils.createLambda(predicate); + + if (predicate.length <= 1) { + var prevPredicate = this.prevPredicate; + var composedPredicate = function (x) { return prevPredicate(x) && predicate(x); }; + return new WhereEnumerable(this.prevSource, composedPredicate); + } + else { + // if predicate use index, can't compose + return Enumerable.prototype.where.call(this, predicate); + } +}; + +WhereEnumerable.prototype.select = function (selector) { + selector = Utils.createLambda(selector); + + return (selector.length <= 1) + ? new WhereSelectEnumerable(this.prevSource, this.prevPredicate, selector) + : Enumerable.prototype.select.call(this, selector); +}; + +WhereEnumerable.prototype.getEnumerator = function () { + var predicate = this.prevPredicate; + var source = this.prevSource; + var enumerator; + + return new IEnumerator( + function () { enumerator = source.getEnumerator(); }, + function () { + while (enumerator.moveNext()) { + if (predicate(enumerator.current())) { + return this.yieldReturn(enumerator.current()); + } + } + return false; + }, + function () { Utils.dispose(enumerator); }); +}; + +var WhereSelectEnumerable = function (source, predicate, selector) { + this.prevSource = source; + this.prevPredicate = predicate; // predicate.length always <= 1 or null + this.prevSelector = selector; // selector.length always <= 1 +}; +WhereSelectEnumerable.prototype = new Enumerable(); + +WhereSelectEnumerable.prototype.where = function (predicate) { + predicate = Utils.createLambda(predicate); + + return (predicate.length <= 1) + ? new WhereEnumerable(this, predicate) + : Enumerable.prototype.where.call(this, predicate); +}; + +WhereSelectEnumerable.prototype.select = function (selector) { + selector = Utils.createLambda(selector); + + if (selector.length <= 1) { + var prevSelector = this.prevSelector; + var composedSelector = function (x) { return selector(prevSelector(x)); }; + return new WhereSelectEnumerable(this.prevSource, this.prevPredicate, composedSelector); + } + else { + // if selector uses index, can't compose + return Enumerable.prototype.select.call(this, selector); + } +}; + +WhereSelectEnumerable.prototype.getEnumerator = function () { + var predicate = this.prevPredicate; + var selector = this.prevSelector; + var source = this.prevSource; + var enumerator; + + return new IEnumerator( + function () { enumerator = source.getEnumerator(); }, + function () { + while (enumerator.moveNext()) { + if (predicate == null || predicate(enumerator.current())) { + return this.yieldReturn(selector(enumerator.current())); + } + } + return false; + }, + function () { Utils.dispose(enumerator); }); +}; + +/////////////// +// Collections + +var Dictionary = (function () { + // static utility methods + var callHasOwnProperty = function (target, key) { + return Object.prototype.hasOwnProperty.call(target, key); + }; + + var computeHashCode = function (obj) { + if (obj === null) return "null"; + if (obj === undefined) return "undefined"; + + return (typeof obj.toString === Types.Function) + ? obj.toString() + : Object.prototype.toString.call(obj); + }; + + // LinkedList for Dictionary + var HashEntry = function (key, value) { + this.key = key; + this.value = value; + this.prev = null; + this.next = null; + }; + + var EntryList = function () { + this.first = null; + this.last = null; + }; + EntryList.prototype = + { + addLast: function (entry) { + if (this.last != null) { + this.last.next = entry; + entry.prev = this.last; + this.last = entry; + } else this.first = this.last = entry; + }, + + replace: function (entry, newEntry) { + if (entry.prev != null) { + entry.prev.next = newEntry; + newEntry.prev = entry.prev; + } else this.first = newEntry; + + if (entry.next != null) { + entry.next.prev = newEntry; + newEntry.next = entry.next; + } else this.last = newEntry; + + }, + + remove: function (entry) { + if (entry.prev != null) entry.prev.next = entry.next; + else this.first = entry.next; + + if (entry.next != null) entry.next.prev = entry.prev; + else this.last = entry.prev; + } + }; + + // Overload:function() + // Overload:function(compareSelector) + var Dictionary = function (compareSelector) { + this.countField = 0; + this.entryList = new EntryList(); + this.buckets = {}; // as Dictionary> + this.compareSelector = (compareSelector == null) ? Functions.Identity : compareSelector; + }; + Dictionary.prototype = + { + add: function (key, value) { + var compareKey = this.compareSelector(key); + var hash = computeHashCode(compareKey); + var entry = new HashEntry(key, value); + if (callHasOwnProperty(this.buckets, hash)) { + var array = this.buckets[hash]; + for (var i = 0; i < array.length; i++) { + if (this.compareSelector(array[i].key) === compareKey) { + this.entryList.replace(array[i], entry); + array[i] = entry; + return; + } + } + array.push(entry); + } else { + this.buckets[hash] = [entry]; + } + this.countField++; + this.entryList.addLast(entry); + }, + + get: function (key) { + var compareKey = this.compareSelector(key); + var hash = computeHashCode(compareKey); + if (!callHasOwnProperty(this.buckets, hash)) return undefined; + + var array = this.buckets[hash]; + for (var i = 0; i < array.length; i++) { + var entry = array[i]; + if (this.compareSelector(entry.key) === compareKey) return entry.value; + } + return undefined; + }, + + set: function (key, value) { + var compareKey = this.compareSelector(key); + var hash = computeHashCode(compareKey); + if (callHasOwnProperty(this.buckets, hash)) { + var array = this.buckets[hash]; + for (var i = 0; i < array.length; i++) { + if (this.compareSelector(array[i].key) === compareKey) { + var newEntry = new HashEntry(key, value); + this.entryList.replace(array[i], newEntry); + array[i] = newEntry; + return true; + } + } + } + return false; + }, + + contains: function (key) { + var compareKey = this.compareSelector(key); + var hash = computeHashCode(compareKey); + if (!callHasOwnProperty(this.buckets, hash)) return false; + + var array = this.buckets[hash]; + for (var i = 0; i < array.length; i++) { + if (this.compareSelector(array[i].key) === compareKey) return true; + } + return false; + }, + + clear: function () { + this.countField = 0; + this.buckets = {}; + this.entryList = new EntryList(); + }, + + remove: function (key) { + var compareKey = this.compareSelector(key); + var hash = computeHashCode(compareKey); + if (!callHasOwnProperty(this.buckets, hash)) return; + + var array = this.buckets[hash]; + for (var i = 0; i < array.length; i++) { + if (this.compareSelector(array[i].key) === compareKey) { + this.entryList.remove(array[i]); + array.splice(i, 1); + if (array.length == 0) delete this.buckets[hash]; + this.countField--; + return; + } + } + }, + + count: function () { + return this.countField; + }, + + toEnumerable: function () { + var self = this; + return new Enumerable(function () { + var currentEntry; + + return new IEnumerator( + function () { currentEntry = self.entryList.first; }, + function () { + if (currentEntry != null) { + var result = { key: currentEntry.key, value: currentEntry.value }; + currentEntry = currentEntry.next; + return this.yieldReturn(result); + } + return false; + }, + Functions.Blank); + }); + } + }; + + return Dictionary; +})(); + +// dictionary = Dictionary +var Lookup = function (dictionary) { + this.count = function () { + return dictionary.count(); + }; + this.get = function (key) { + return Enumerable.from(dictionary.get(key)); + }; + this.contains = function (key) { + return dictionary.contains(key); + }; + this.toEnumerable = function () { + return dictionary.toEnumerable().select(function (kvp) { + return new Grouping(kvp.key, kvp.value); + }); + }; +}; + +var Grouping = function (groupKey, elements) { + this.key = function () { + return groupKey; + }; + ArrayEnumerable.call(this, elements); +}; +Grouping.prototype = new ArrayEnumerable(); + +exports._default = Enumerable; diff --git a/data/test-misc.json b/data/test-misc.json index 9f400ec..a018f23 100644 --- a/data/test-misc.json +++ b/data/test-misc.json @@ -36,7 +36,7 @@ "tests": [ { "id": "gtkserver_test", - "description": "GTK Server를 이용한 GUI(그래픽 사용자 인터페이스) 구현 가능 여부 확인", + "description": "Graphical User Interface (GUI) test with GTK server", "tags": ["GTK", "GTK"] }, { @@ -71,8 +71,13 @@ }, { "id": "string_split", - "description": "String.split test", + "description": "String.split() test", "tags": ["Assertion"] + }, + { + "id": "linqjs", + "description": "linq.js (LINQ for JavaScript) test", + "tags": ["Library"] } ] } diff --git a/testloader.js b/testloader.js index 7e29a58..6a99367 100644 --- a/testloader.js +++ b/testloader.js @@ -934,6 +934,23 @@ var test_implements = { console.log(c); console.log(d); } + }, + + "linqjs": function() { + var a = Enumerable.range(1, 10) + .where(function(i) { return i % 3 == 0; }) + .select(function(i) { return i * 10; }) + ; + console.log(a.toArray().join(',')); + + var array = "monkey:red:apple:delicious:banana:long:train:fast:airplane:high:everest:sharp:seringue:painful".split(':'); + var b = Enumerable.from(array).select(function(val, i) { + return { + value: val, + index: i + } + }); + console.log(JSON.stringify(b.toArray())); } };