PageSourceSearch

http://mahabadcity.ir/JavaScripts/Modules/TopNews/jsinq-enumerable.js

js mahabadcity.ir collected 2026-10-02 23:45:48 UTC 45,525 bytes, 1,623 lines download raw bytes

1/* 
2 * JSINQ, JavaScript integrated query
3 * Copyright (c) 2009 Kai Jäger. Some rights reserved.
4 *
5 * Use of this source code is governed by an MIT-style license that can be
6 * found in the license.txt file. 
7 */
8 
9 if (typeof jsinq == 'undefined') {
10	jsinq = {};
11 }
12 
13(function() {
14	/**
15	 * An exception that is thrown when an operation performed on an object was
16	 * unsuccessful due to the state that the object was in.
17	 * @constructor
18	 */
19	function InvalidOperationException() { }		
20	InvalidOperationException.prototype = new Error();
21	InvalidOperationException.prototype.name = 'InvalidOperationException';
22	InvalidOperationException.prototype.message = 
23		'Operation is not valid due to the current state of the object.';
24
25	/**
26	 * An exception that is thrown when the value of a parameter is outside the
27	 * allowable range.
28	 * @constructor
29	 * @param parameter Name of the parameter that was out of range
30	 */
31	function ArgumentOutOfRangeException(parameter) { 
32		this.message += parameter;
33	}
34	ArgumentOutOfRangeException.prototype = new Error();
35	ArgumentOutOfRangeException.prototype.name = 'ArgumentOutOfRangeException';
36	ArgumentOutOfRangeException.prototype.message = 
37		'Specified argument was out of the range of valid values.\r\n' +
38		'Parameter name: ';
39
40	/**
41	 * Provides a method to support the comparison of two objects for equality.
42	 * @constructor
43	 */
44	function EqualityComparer() { }
45	EqualityComparer.prototype = {
46		/**
47		 * Returns true if a and b are equal.
48		 * @param a The first object
49		 * @param b The second object
50		 * @return True if a and b are equal
51		 */
52		equals: function(a, b) {
53			return a == b;
54		}	
55	};
56
57	(function() {
58		var defaultComparer = new EqualityComparer();
59		/**
60		 * Returns the default comparer which uses JavaScript's built-in non-
61		 * strict equals-operator to compare two values for equality.
62		 * @return The default comparer
63		 */
64		EqualityComparer.getDefault = function() {
65			return defaultComparer;
66		};
67	})();		
68
69	/**
70	 * Provides a method to support the comparison of two objects.
71	 * @constructor
72	 */
73	function Comparer() { }
74	Comparer.prototype = {
75		/**
76		 * Returns a value less than zero if a is less than b, a value greater 
77		 * zero if a is greater than b and zero if a equals b.
78		 * @param a The first object
79		 * @param b The second object
80		 * @return A value indicating whether a is equal to, less than or
81		 * 	greater than b
82		 */
83		compare: function(a, b) {
84			return a < b ? -1 : (a > b ? 1 : 0);
85		}
86	};
87
88	(function() {
89		var defaultComparer = new Comparer();
90		/**
91		 * Returns the default comparer which uses JavaScript's built-in less-
92		 * than and greater-than operators to compare two values.
93		 * @return The default comparer
94		 */
95		Comparer.getDefault = function() {
96			return defaultComparer;
97		};
98	})();	
99
100	/**
101	 * Gives access to an Enumerator that can be used to enumerate the object.
102	 * Invoke this constructor with either a scalar value to create a singleton
103	 * list or with an array or a NodeList to make it enumerable.
104	 * @constructor
105	 * @param value The object to enumerate
106	 */
107	function Enumerable(value) {	
108		if (arguments.length == 0) {
109			value = [];
110		} else if (typeof value.length == 'undefined' || (value.length > 0 && 
111			typeof value[0] == 'undefined')) {
112			value = [value];
113		}
114		
115		/**
116		 * Returns a new Enumerator
117		 * @return A new enumerator
118		 */
119		this.getEnumerator = function() {
120			return new function() {
121				var index = -1;
122						
123				/**
124				 * Moves the internal cursor to the next value. Returns false 
125				 * if the cursor has been moved past the end of the collection.
126				 * @return False if the cursor has been moved past the end of 
127				 * 	the collection
128				 */
129				this.moveNext = function() {
130					++index;
131					return index < value.length;
132				};					
133							
134				/**
135				 * Returns the element in the collection that the internal  
136				 * cursor currently points to. Make sure to call moveNext 
137				 * before calling current. When calling current after an 
138				 * unsuccessful call to moveNext, the method will throw an 
139				 * exception.
140				 * @return The current element
141				 */
142				this.current = function() {
143					if (index < 0 || index >= value.length) {
144						throw new InvalidOperationException();
145					}
146					return value[index];
147				};	
148						
149				/**
150				 * Places the internal cursor before the first element in the 
151				 * collection.
152				 */
153				this.reset = function() {
154					index = -1;
155				};									
156			};
157		};
158	}
159
160	/**
161	 * Returns an empty Enumerable
162	 * @return An empty Enumerable
163	 */
164	Enumerable.empty = function() {	
165		return new Enumerable();
166	};
167
168	/**
169	 * Returns an Enumerable for the specified range of numbers
170	 */
171	Enumerable.range = function(start, count) {
172		if (count < 0) {
173			throw new ArgumentOutOfRangeException();
174		}
175		var func = function() {
176			this.getEnumerator = function() {
177				return new function() {
178					var index = -1;
179					var hasNext = false;
180					this.moveNext = function() {
181						hasNext = false;
182						if (index < count - 1) {
183							hasNext = true;
184							++index;
185							return true;
186						}
187						return false;
188					};
189					
190					this.current = function() {
191						if (hasNext) {
192							return start + index;
193						} else {
194							throw new InvalidOperationException();	
195						}
196					};	
197					
198					this.reset = function() { 
199						index = -1;
200					};
201				};
202			};
203		};
204		func.prototype = Enumerable.prototype;
205		return new func();
206	};
207
208	/**
209	 * Returns an Enumerable that contains the specified element "count" times
210	 */
211	Enumerable.repeat = function(element, count) {
212		if (count < 0) {
213			throw new ArgumentOutOfRangeException();	
214		}
215		var func = function() {
216			this.getEnumerator = function() {
217				return new function() {
218					var index = -1;
219					var hasNext = false;
220					this.moveNext = function() {
221						hasNext = false;
222						if (index < count - 1) {
223							hasNext = true;
224							++index;
225							return true;
226						}
227						return false;
228					};
229					
230					this.current = function() {
231						if (hasNext) {
232							return element;
233						} else {
234							throw new InvalidOperationException();	
235						}
236					};	
237					
238					this.reset = function() { 
239						index = -1;
240						hasNext = false;
241					};
242				};
243			};
244		};
245		func.prototype = Enumerable.prototype;
246		return new func();
247	};
248
249	Enumerable.prototype = (function() {
250		// Identity function used as a default value for certain method 
251		// overloads. See below.
252		function identity(value) {
253			return value;
254		}
255					
256		// An associative container that maintains a list of key-value pairs.
257		// Since JavaScript's built-in associative arrays do not properly 
258		// support complex types as keys and also lack support for custom 
259		// comparators, this container is used instead.
260		// Todo: This is really slow when using complex types as keys.
261		function Hash(comparer) {
262			if (arguments.length == 0 || 
263				arguments[0] == EqualityComparer.getDefault()) {
264				this.comparer = null;
265			}
266			this.comparer = arguments[0];
267			this.primitiveItems = {};
268			this.complexItems = [];
269		}
270		Hash.prototype = {
271			lookUp: function(key, func) {
272				var funcResult;
273				
274				// Primitive keys are kept in an associative array, complex 
275				// keys in a linear array
276				if (this.comparer == null && (typeof key == 'string' || 
277					typeof key == 'number' || typeof key == 'boolean' || 
278					typeof key == 'null')) {
279					var value = this.primitiveItems[key];
280					if (typeof value != 'undefined') {
281						funcResult = func(value);							
282					} else {
283						funcResult = func();
284					}						
285					if (funcResult.length > 1) {
286						this.primitiveItems[key] = funcResult[1];
287					}						
288					return funcResult[0];
289				} else {
290					var length = this.complexItems.length;
291					var item;
292					for (var i = 0; i < length; i++) {
293						item = this.complexItems[i];
294						if ((this.comparer != null && 
295							this.comparer.equals(item.key, key)) ||
296							(this.comparer == null && item.key == key)) {
297							funcResult = func(item.element);
298							if (funcResult.length > 1) {
299								item.element = funcResult[1];
300							}
301							return funcResult[0];
302						}
303					}
304					funcResult = func();
305					if (funcResult.length > 1) {
306						this.complexItems.push({key: key, 
307							element: funcResult[1]});
308					}
309					return funcResult[0];
310				}					
311			},
312			keyExists: function(key) {
313				return this.lookUp(key, function(value) {
314					return [arguments.length == 1];
315				});	
316			},
317			get: function(key) {
318				return this.lookUp(key, function(value) {
319					if (arguments.length == 0) {
320						throw false;	
321					}
322					return [value];
323				});											
324			},
325			put: function(key, newValue, overwrite) {
326				if (arguments.length < 3) {
327					overwrite = false;
328				}
329				return this.lookUp(key, function(value) {
330					if (arguments.length == 0 || overwrite) {
331						return [true, newValue];
332					} else {
333						return [false];
334					}
335				});							
336			},
337			toArray: function() {
338				var result = [];
339				for (var key in this.primitiveItems) {
340					result.push({key: key, element: this.primitiveItems[key]});
341				}
342				return result.concat(this.complexItems);
343			},
344			empty: function() {
345				this.primitiveItems = {};
346				this.complexItems = [];
347			}
348		};
349		
350		// The two possible parameters for the orderBy function (see below)
351		var ASCENDING = 1;
352		var DESCENDING = -1;
353		
354		// Implementation of Enumerable.orderBy, Enumerable.orderByDescending 
355		// as well as OrderedEnumerable.thenBy and 
356		// OrderedEnumerable.thenByDescending. This is implemented here, to 
357		// avoid code duplication. The function returns a different 
358		// implementation of the orderBy function based on the direction 
359		// parameter.
360		function orderBy(direction) {
361			return function(keySelector) {
362				var _this = this;
363				var parentSelectors = [];
364				var comparer = Comparer.getDefault();
365				if (arguments.length >= 3) {
366					comparer = arguments[1];
367					parentSelectors = arguments[2];
368				}	else if (arguments.length >= 2) {
369					if (arguments[1] instanceof Array) {
370						parentSelectors = arguments[1];
371					} else {
372						comparer = arguments[1];
373					}
374				}			
375				var selectors = [[keySelector, direction, comparer]];
376				selectors = selectors.concat(parentSelectors);
377				
378				var func = function() {
379					this.getEnumerator = function() {						
380						return new function() {
381							var itemEnumerator = null;
382							
383							function lazyInitialize() {
384								var array = _this.toArray();
385								array.sort(function(a, b) {
386									var result = 0;
387									var selector;
388									var direction;
389									var comparer;
390									// Go through all selectors
391									for (var i = selectors.length - 1; 
392										i >= 0; i--) {
393										result = 0;
394										selector = selectors[i][0];
395										direction = selectors[i][1];
396										comparer = selectors[i][2];
397										var keyA = selector(a);
398										var keyB = selector(b);
399										var compareResult = comparer.compare(
400											keyA, keyB);
401										if (compareResult != 0) {										
402											return compareResult * direction;
403										}																	
404									}
405									return 0;
406								});
407								itemEnumerator = (new Enumerable(array)).
408									getEnumerator();
409							}
410							
411							this.moveNext = function() {
412								if (itemEnumerator == null) {
413									lazyInitialize();
414								}
415								return itemEnumerator.moveNext();
416							};
417							
418							this.current = function() {
419								if (itemEnumerator == null) {
420									throw new InvalidOperationException();
421								}	
422								return itemEnumerator.current();
423							};
424							
425							this.reset = function() {
426								if (itemEnumerator != null) {
427									itemEnumerator.reset();
428								}
429							};
430						};
431					};
432					
433					// To avoid having to sort the Enumerable multiple times, 
434					// thenBy does not actually perform any sorting at all. 
435					// Instead, a chain of orderBy and thenBys essentially 
436					// becomes an array of selectors. ThenBy then bypasses the 
437					// previous orderBy and thenBys and returns a new 
438					// Enumerator that sorts the original (unsorted) Enumerable
439					// directly using the array of selectors (See above).
440					this.thenBy = function(keySelector, comparer) {	
441						var orderByFunc = orderBy(ASCENDING);
442						if (arguments.length < 2) {
443							comparer = Comparer.getDefault();
444						}
445						return orderByFunc.call(_this, keySelector, comparer, 
446							selectors);
447					};
448					
449					this.thenByDescending = function(keySelector, comparer) {
450						var orderByFunc = orderBy(DESCENDING);
451						if (arguments.length < 2) {
452							comparer = Comparer.getDefault();
453						}
454						return orderByFunc.call(_this, keySelector, comparer, 
455							selectors);		
456					};
457				};	
458				func.prototype = Enumerable.prototype;
459				return new func();				
460			};
461		}
462		
463		// Implementation of Enumerable.join and Enumerable.groupJoin. Again, 
464		// this helps in avoiding code-duplication.
465		function join(group) {
466			return function (second, outerKeySelector, innerKeySelector, 
467				resultSelector, comparer) {
468				var _this = this;
469				if (arguments.length < 5) {
470					comparer = EqualityComparer.getDefault();
471				}
472				
473				var func = function() {
474					this.getEnumerator = function() {
475						return new function() {
476							var hash = null;
477							var enumerator = _this.getEnumerator();
478							var hasNext = false;
479							var firstElement;
480							var secondElement;
481							var secondList = null;
482							var index = -1;
483							
484							function lazyInitialize() {
485								hash = new Hash(comparer);
486								var secondEnumerator = second.getEnumerator();
487								while (secondEnumerator.moveNext()) {
488									var current = secondEnumerator.current();
489									var key = innerKeySelector(current);
490									if (!hash.put(key, [current])) {
491										var array = hash.get(key);
492										array.push(current);
493									}
494								}
495							}
496							
497							this.moveNext = function() {
498								if (hash == null) {
499									lazyInitialize();	
500								}		
501								var current;
502								var key;
503								if (group) {
504									hasNext = enumerator.moveNext();
505									if (hasNext) {										
506										current = enumerator.current();
507										key = outerKeySelector(current);
508										firstElement = current;
509										if (hash.keyExists(key)) {
510											secondElement = new Enumerable(
511												hash.get(key));
512										} else {
513											secondElement = Enumerable.empty();
514										}
515									}
516								} else {
517									if (secondList != null && ++index < 
518										secondList.length) {
519										secondElement = secondList[index];
520										hasNext = true;
521									} else {
522										hasNext = false;
523										while (enumerator.moveNext()) {
524											current = enumerator.current();
525											key = outerKeySelector(current);
526											if (hash.keyExists(key)) {
527												secondList = hash.get(key);
528												secondElement = secondList[0];
529												index = 0;
530												hasNext = true;
531												firstElement = current;
532												break;
533											} else {
534												continue;
535											}														
536										}
537									}
538								}
539								return hasNext;
540							};
541							
542							this.current = function() {
543								if (!hasNext) {
544									throw new InvalidOperationException();
545								}
546								return resultSelector(firstElement, 
547									secondElement);
548							};
549							
550							this.reset = function() {
551								enumerator.reset();
552							};
553						};	
554					};	
555				};
556				func.prototype = Enumerable.prototype;
557				return new func();
558			};
559		}
560		
561		return {
562			/**
563			 * Accumulates the elements of an Enumerable using an accumulator 
564			 * function. 
565			 *
566			 * Overloads:
567			 *   aggregate(func)
568			 *   aggregate(seed, func)
569			 *   aggregate(seed, func, resultSelector)
570			 */
571			aggregate: function() {
572				var enumerator = this.getEnumerator();
573				
574				if (!enumerator.moveNext()) {
575					throw new InvalidOperationException();
576				}						
577				
578				var running;
579				var func;
580				var resultSelector = identity;
581				if (arguments.length >= 2) {
582					running = arguments[0];
583					func = arguments[1];
584					if (arguments.length >= 3) {
585						resultSelector = arguments[2];
586					}
587					enumerator.reset();
588				} else {
589					func = arguments[0];		
590					running = enumerator.current();
591				}
592				while (enumerator.moveNext()) {
593					running = func(running, enumerator.current());
594				} 
595				return resultSelector(running);
596			},
597			
598			/**
599			 * Returns true if all elements in the Enumerable satisfy the 
600			 * specified condition.
601			 */
602			all: function(predicate) {		
603				var enumerator = this.getEnumerator();
604				while (enumerator.moveNext()) {
605					if (!predicate(enumerator.current())) {
606						return false;
607					}
608				}				
609				return true;
610			},
611			
612			/**
613			 * Returns true if the Enumerable contains any elements or if at  
614			 * least one of the elements in the Enumerable satisfy a specified 
615			 * condition.
616			 *
617			 * Overloads:
618			 *   any()
619			 *   any(predicate)
620			 */		
621			any: function(predicate) {
622				var enumerator = this.getEnumerator();
623				if (arguments.length == 0) {
624					return enumerator.moveNext();
625				}
626				var array = [];
627				while (enumerator.moveNext()) {
628					if (predicate(enumerator.current())) {
629						return true;
630					}
631				}				
632				return false;					
633			},
634			
635			/**
636			 * Returns the average of the elements in the Enumerable, 
637			 * optionally using the specified selector function.
638			 *
639			 * Overloads:
640			 *   average()
641			 *   average(selector)
642			 */		
643			average: function(selector) {
644				if (arguments.length == 0) {
645					selector = identity;
646				}
647				var count = 0;
648				var sum = this.aggregate(0, function(running, current) {
649					++count;	
650					return running + selector(current);
651				});
652				return sum / count;
653			},
654			
655			/**
656			 * Creates a new Enumerable that is the result of the concatenation
657			 * of two Enumerables.
658			 */		
659			concat: function(second) {
660				var _this = this;
661				var func = function() {
662					this.getEnumerator = function() {
663						return new function() {
664							var firstEnumerator = _this.getEnumerator();
665							var secondEnumerator = second.getEnumerator();
666							var enumerator = firstEnumerator;
667							var canSwap = true;
668							this.moveNext = function() {
669								if (!enumerator.moveNext()) {
670									if (canSwap) {
671										enumerator = secondEnumerator;
672										return enumerator.moveNext();
673									}
674									return false;
675								}
676								return true;
677							};
678							
679							this.current = function() {
680								return enumerator.current();
681							};			
682							
683							this.reset = function() {
684								canSwap = true;
685								firstEnumerator.reset();
686								secondEnumerator.reset();
687								enumerator = firstEnumerator;
688							};		
689						};
690					};
691				};
692				func.prototype = Enumerable.prototype;
693				return new func();			
694			},
695			
696			/**
697			 * Returns true if the Enumerable contains the specified element.
698			 * Optionally uses the specified comparer.
699			 * 
700			 * Overloads:
701			 *   contains(value)
702			 *   contains(value, comparer)
703			 */		
704			contains: function(value, comparer) {
705				if (arguments.length == 1) {
706					comparer = EqualityComparer.getDefault();
707				}
708				return this.any(function(item) {
709					return comparer.equals(item, value);
710				});		
711			},
712			
713			/**
714			 * Returns the number of elements in the Enumerable. If a predicate
715			 * function is specified, only those elements will be counted that
716			 * satisfy the given condition.
717			 */		
718			count: function(predicate) {
719				var count = 0;
720				var enumerator = this.getEnumerator();
721				var hasPredicate = typeof predicate == 'function';
722				while (enumerator.moveNext()) {
723					if ((hasPredicate && predicate(enumerator.current())) || 
724						!hasPredicate) {
725						++count;
726					}						
727				}				
728				return count;
729			},				
730			
731			/**
732			 * Returns the Enumerable or a new Enumerable containing only the 
733			 * specified default value, should the Enumerable be empty.
734			 */		
735			defaultIfEmpty: function(defaultValue) {
736				var isEmpty = !this.any(function(item) {
737					return true;
738				});						
739				if (isEmpty) {
740					return new Enumerable(defaultValue);
741				}
742				return this;
743			},
744			
745			/**
746			 * Returns a new Enumerable that contains only distinct elements.
747			 * Optionally uses a specified comparer.
748			 *
749			 * Overloads:
750			 *   distinct()
751			 *   distinct(comparer)
752			 */		
753			distinct: function(comparer) {
754				var _this = this;					
755				var hasComparer = arguments.length > 0;
756				
757				var func = function() {
758					this.getEnumerator = function() {
759						return new function() {
760							var enumerator = _this.getEnumerator();
761							var hash;
762							if (hasComparer) {	
763								hash = new Hash(comparer);
764							} else {
765								hash = new Hash();
766							}			
767								
768							this.moveNext = function() {
769								while (enumerator.moveNext()) {
770									if (hash.put(enumerator.current(), true)) {
771										return true;											
772									}
773								}
774								return false;
775							};
776							
777							this.current = function() {
778								return enumerator.current();
779							};			
780							
781							this.reset = function() {
782								enumerator.reset();
783								hash.empty();
784							};		
785						};
786					};
787				};
788				func.prototype = Enumerable.prototype;
789				return new func();	
790			},
791			
792			/**
793			 * Returns the element at the specified offset or throws an 
794			 * exception if the index is out of bounds.
795			 */		
796			elementAt: function(index) {
797				var element;
798				var found = this.any(function(item) {
799					element = item;
800					return index-- == 0;
801				});	
802				if (!found) {
803					throw new ArgumentOutOfRangeException('index');
804				}
805				return element;
806			},
807			
808			/**
809			 * Same as elementAt, except it returns the specified defaultValue 
810			 * if the given index is out of bounds. This differs from the LINQ 
811			 * implementation in that the default value needs to be specified 
812			 * explicitly.
813			 */		
814			elementAtOrDefault: function(index, defaultValue) {
815				try {
816					return this.elementAt(index);
817				} catch (e) {
818					return defaultValue;
819				}	
820			},
821			
822			/**
823			 * Returns a new Enumerable containing all elements from this 
824			 * Enumerable except those contained in a second Enumerable (i.e. 
825			 * the set difference between two enumerables). Optionally uses the 
826			 * specified comparer.
827			 *
828			 * Overloads:
829			 *   except(second)
830			 *   except(second, comparer)
831			 */		
832			except: function(second, comparer) {
833				if (arguments.length < 2) {
834					comparer = EqualityComparer.getDefault();
835				}
836				return this.where(function(item) {
837					return !second.any(function(compare) {
838						return comparer.equals(item, compare);
839					});
840				});	
841			},
842			
843			/**
844			 * Returns the first element in the Enumerable or throws an 
845			 * exception if the Enumerable is empty. If a predicate function 
846			 * is specified, the method returns the first element that 
847			 * satisfies the given condition. If the Enumerable is empty or no 
848			 * element satisfies the specified condition, an exception is 
849			 * thrown.
850			 *
851			 * Overloads:
852			 *   first()
853			 *   first(predicate)
854			 */		
855			first: function(predicate) {
856				if (arguments.length == 0) {
857					try {
858						return this.elementAt(0);
859					} catch (e) {
860						throw new InvalidOperationException();	
861					}
862				} else {
863					var element;
864					var found = this.any(function(item) {
865						if (predicate(item)) {
866							element = item;
867							return true;
868						}
869						return false;
870					});
871					if (!found) {
872						throw new InvalidOperationException();
873					}
874					return element;
875				}
876			},
877			
878			/**
879			 * Same as first, except it returns a specified defaultValue if 
880			 * the Enumerable is empty or no element satisfies the specified 
881			 * condition. This method is different from its LINQ counterpart 
882			 * in that it requires the default value to be specified 
883			 * explicitly.
884			 *
885			 * Overloads:
886			 *   firstOrDefault(defaultValue)
887			 *   firstOrDefault(predicate, defaultValue)
888			 */		
889			firstOrDefault: function() {
890				if (arguments.length == 1) {
891					var defaultValue = arguments[0];	
892				} else if (arguments.length > 1) {
893					var predicate = arguments[0];
894					var defaultValue = arguments[1];
895				}
896				try {
897					if (arguments.length > 1) {
898						return this.first(predicate);
899					} else {
900						return this.first();
901					}
902				} catch (e) {
903					return defaultValue;
904				}						
905			},
906			
907			/**
908			 * Groups the elements in the Enumerable. Note that this method  
909			 * resolves overloads based on function arity. Result selectors 
910			 * are expected to have two or more formal parameters while 
911			 * element- and key-selectors are expected to have just one.
912			 *
913			 * Overloads:
914			 *   groupBy(keySelector)
915			 *   groupBy(keySelector, comparer)
916	 		 *   groupBy(keySelector, elementSelector)
917			 *   groupBy(keySelector, resultSelector)
918	 		 *   groupBy(keySelector, resultSelector, comparer)
919			 *   groupBy(keySelector, elementSelector, comparer) 		 
920			 *   groupBy(keySelector, elementSelector, resultSelector) 	
921			 *   groupBy(keySelector, elementSelector, resultSelector, 
922			 *           comparer) 	
923			 */		
924			groupBy: function() {
925				var keySelector = arguments[0];
926				var elementSelector = identity;
927				var resultSelector = identity;
928				var comparer = EqualityComparer.getDefault();
929				
930				// Resolve overloads
931				// Todo: Dealing with overloads based on function arity is  
932				// probably a bad idea. Maybe there is a better way?
933				if (arguments.length == 2) {
934					if (typeof arguments[1].equals == 'function') {
935						comparer = arguments[1];
936					} else if (arguments[1].arity >= 2) {
937						resultSelector = arguments[1];
938					} else {
939						elementSelector = arguments[1];
940					}
941				} else if (arguments.length == 3) {
942					if (arguments[1].arity >= 2) {
943						resultSelector = arguments[1];
944						comparer = arguments[2];
945					} else {
946						elementSelector = arguments[1];
947						if (typeof arguments[2].equals == 'function') {
948							comparer = arguments[2];
949						} else {
950							resultSelector = arguments[2];
951						}
952					}
953				} else if (arguments.length > 3) {
954					elementSelector = arguments[1];
955					resultSelector = arguments[2];
956					comparer = arguments[3];
957				}
958				
959				var _this = this;
960				
961				var func = function() {
962					this.getEnumerator = function() {
963						return new function() {
964							var resultSet = null;
965							var index = -1;
966							
967							function lazyInitialize() {
968								var itemEnumerator = _this.getEnumerator();
969								var hash = new Hash(comparer);
970								
971								while (itemEnumerator.moveNext()) {
972									var current = itemEnumerator.current();
973									var key = keySelector(current);	
974									if (hash.keyExists(key)) {
975										var array = hash.get(key);
976										array.push(elementSelector(current));
977									} else {
978										hash.put(key, 
979											[elementSelector(current)]);
980									}
981								}									
982								resultSet = hash.toArray();
983							}
984							
985							this.moveNext = function() {
986								if (resultSet == null) {
987									lazyInitialize();
988								}
989								++index;
990								return index < resultSet.length;
991							};
992							
993							this.current = function() {
994								if (index < 0 || index >= resultSet.length) {
995									throw new InvalidOperationException();
996								}
997								var current = resultSet[index];
998								var elements = new Enumerable(
999									current.element);
1000								if (resultSelector != identity) {		
1001									return resultSelector(current.key, 
1002										elements);
1003								} else {			
1004									var Grouping = function(key, elements) {
1005										this.getKey = function() {
1006											return key;
1007										};
1008										
1009										this.getEnumerator = function() {
1010											return elements.getEnumerator();	
1011										};
1012									};
1013									Grouping.prototype = Enumerable.prototype;
1014									return new Grouping(current.key, 
1015										elements);
1016								}
1017							};
1018							
1019							this.reset = function() {
1020								index = -1;
1021							};
1022						};
1023					};
1024				};
1025				func.prototype = Enumerable.prototype;
1026				return new func();
1027			},
1028			
1029			/**
1030			 * Correlates the elements in two Enumerables based on their keys 
1031			 * and groups the results. 
1032			 *
1033			 * Overloads:
1034			 *   groupJoin(inner, outerKeySelector, innerKeySelector, 
1035			 *             resultSelector)
1036			 *   groupJoin(inner, outerKeySelector, innerKeySelector, 
1037			 *             resultSelector, comparer)
1038			 */		
1039			groupJoin: join(true),
1040			
1041			/**
1042			 * Returns a new Enumerable that is the result of the intersection
1043			 * of two Enumerables. Optionally uses the specified comparer.
1044			 * 
1045			 * Overloads:
1046			 *   intersect(second)
1047			 *   intersect(second, comparer)
1048			 */		
1049			intersect: function(second, comparer) {
1050				if (arguments.length < 2) {
1051					comparer = EqualityComparer.getDefault();
1052				}
1053				return this.distinct(comparer).where(function(item) {
1054					return second.contains(item, comparer);
1055				});
1056			},
1057			
1058			/**
1059			 * Correlates the elements in two Enumerables based on their 
1060			 * keys. 
1061			 *
1062			 * Overloads:
1063			 *   join(inner, outerKeySelector, innerKeySelector, 
1064			 *        resultSelector)
1065			 *   join(inner, outerKeySelector, innerKeySelector, 
1066			 *        resultSelector, comparer)
1067			 */		
1068			join: join(false),
1069			
1070			/**
1071			 * Returns the last element in the Enumerable or throws an 
1072			 * exception, if the Enumerable is empty. If a predicate function 
1073			 * is specified, the method returns the last element that 
1074			 * satisfies the given condition. If no element satisfies the 
1075			 * specified condition, an exception is thrown.
1076			 *
1077			 * Overloads:
1078			 *   last()
1079			 *   last(predicate)
1080			 */		
1081			last: function(predicate) {
1082				var hasPredicate = typeof predicate == 'function';
1083				
1084				var last;
1085				var isEmpty = true;
1086				this.any(function(item) {
1087					if (!hasPredicate || predicate(item)) {
1088						last = item;
1089						isEmpty = false;
1090					}
1091					return false;	
1092				});					
1093				
1094				if (isEmpty) {
1095					throw new InvalidOperationException();	
1096				}				
1097				return last;
1098			},
1099			
1100			/**
1101			 * Same as last, except it returns a specified defaultValue if the
1102			 * Enumerable is empty or not element satisfies the specified 
1103			 * condition. This method is different from its LINQ counterpart 
1104			 * in that it requires the default value to be specified 
1105			 * explicitly.
1106			 *
1107			 * Overloads:
1108			 *   lastOrDefault(defaultValue)
1109			 *   lastOrDefault(predicate, defaultValue)
1110			 */		
1111			lastOrDefault: function() {
1112				if (arguments.length == 1) {
1113					var defaultValue = arguments[0];	
1114				} else if (arguments.length > 1) {
1115					var predicate = arguments[0];
1116					var defaultValue = arguments[1];
1117				}
1118				try {
1119					if (arguments.length > 1) {
1120						return this.last(predicate);
1121					} else {
1122						return this.last();
1123					}
1124				} catch (e) {
1125					return defaultValue;
1126				}						
1127			},
1128			
1129			/**
1130			 * Returns the maximum value in the Enumerable.
1131			 * 
1132			 * Overloads:
1133			 *   max()
1134			 *   max(selector)
1135			 */		
1136			max: function(selector) {
1137				if (arguments.length == 0) {
1138					selector = identity;
1139				}
1140				var isFirst = true;
1141				return this.aggregate(function(running, current) {
1142					if (isFirst) {
1143						running = selector(running);
1144						isFirst = false;
1145					}
1146					return Math.max(running, selector(current));
1147				});
1148			},
1149			
1150			/**
1151			 * Returns the minimum value in the Enumerable.
1152			 * 
1153			 * Overloads:
1154			 *   min()
1155			 *   min(selector)
1156			 */		
1157			min: function(selector) {
1158				if (arguments.length == 0) {
1159					selector = identity;
1160				}
1161				var isFirst = true;
1162				return this.aggregate(function(running, current) {
1163					if (isFirst) {
1164						running = selector(running);
1165						isFirst = false;
1166					}
1167					return Math.min(running, selector(current));
1168				});
1169			},	
1170			
1171			/**
1172			 * Sorts the elements in the Enumerable in ascending order.
1173			 *
1174			 * Overloads:
1175			 *   orderBy(keySelector)
1176			 *   orderBy(keySelector, comparer)
1177			 */		
1178			orderBy: orderBy(ASCENDING),
1179			
1180			/**
1181			 * Sorts the elements in the Enumerable in descending order.
1182			 *
1183			 * Overloads:
1184			 *   orderByDescending(keySelector)
1185			 *   orderByDescending(keySelector, comparer)
1186			 */	
1187			orderByDescending: orderBy(DESCENDING),							
1188			
1189			/**
1190			 * Reverses the order of the elements in the Enumerable.
1191			 */		
1192			reverse: function() {
1193				var _this = this;
1194				var func = function() {
1195					this.getEnumerator = function() {
1196						return new function() {
1197							var enumerator = null;
1198							
1199							this.moveNext = function() {
1200								if (enumerator == null) {
1201									enumerator = new Enumerable(
1202										_this.toArray().reverse()).
1203										getEnumerator();
1204								}
1205								return enumerator.moveNext();
1206							};
1207							
1208							this.current = function() {
1209								if (enumerator == null) {
1210									throw new InvalidOperationException();
1211								}
1212								return enumerator.current();
1213							};
1214							
1215							this.reset = function() {
1216								if (enumerator != null) {
1217									enumerator.reset();
1218								}							
1219							};
1220						};
1221					}
1222				};
1223				func.prototype = Enumerable.prototype;
1224				return new func();			
1225			},
1226			
1227			/**
1228			 * Projects each of the elements in the Enumerable into a new 
1229			 * form. This differes from its LINQ counterpart in that the 
1230			 * selector function always receives the index of the current  
1231			 * element as its second parameter.
1232			 */		
1233			select: function(selector) {
1234				var _this = this;
1235				var func = function() {
1236					this.getEnumerator = function() {
1237						return new function() {
1238							var itemEnumerator = _this.getEnumerator();
1239							var index = -1;
1240							
1241							this.moveNext = function() {
1242								++index;
1243								return itemEnumerator.moveNext();							
1244							};
1245							
1246							this.current = function() {
1247								return selector(itemEnumerator.current(), 
1248									index);
1249							};			
1250							
1251							this.reset = function() {
1252								itemEnumerator.reset();
1253								index = -1;
1254							};		
1255						};
1256					};
1257				};
1258				func.prototype = Enumerable.prototype;
1259				return new func();
1260			},
1261			
1262			/**
1263			 * Projects each of the elements in the Enumerable into a new 
1264			 * Enumerable and then flattens the result into a single  
1265			 * Enumerable again. This differes from its LINQ counterpart in  
1266			 * that the collection selector function always receives the 
1267			 * index of the current element as its second parameter.
1268			 *
1269			 * Overloads:
1270			 *   selectMany(selector)
1271			 *   selectMany(collectionSelector, resultSelector)
1272			 */		
1273			selectMany: function(collectionSelector, resultSelector) {
1274				var hasResultSelector = arguments.length > 1;
1275				var _this = this;
1276				var func = function() {	
1277					this.getEnumerator = function() {
1278						return new function() {
1279							var itemEnumerator = _this.getEnumerator();
1280							var item = null;
1281							var subItemEnumerator = null;
1282							var hasCurrent = false;
1283							var index = 0;
1284				
1285							this.moveNext = function() {
1286								hasCurrent = false;
1287								var noMoveNext = true;
1288								while (true) {
1289									if (subItemEnumerator == null || 
1290										!subItemEnumerator.moveNext()) {
1291										if (!itemEnumerator.moveNext()) {										
1292											break;
1293										}
1294										item = itemEnumerator.current();
1295										subItemEnumerator = 
1296											collectionSelector(item, index++).
1297											getEnumerator();
1298										noMoveNext = false;
1299									}		
1300									if (noMoveNext || 
1301										subItemEnumerator.moveNext()) {
1302										hasCurrent = true;
1303										break;
1304									}
1305								}	
1306								return hasCurrent;
1307							};
1308								
1309							this.current = function() {
1310								if (!hasCurrent) {
1311									throw new InvalidOperationException();
1312								}
1313								if (hasResultSelector) {
1314									return resultSelector(item, 
1315										subItemEnumerator.current());
1316								} else {
1317									return subItemEnumerator.current();
1318								}
1319							};
1320							
1321							this.reset = function() {
1322								hasCurrent = false;
1323								subItemEnumerator = null;
1324								item = null;
1325								itemEnumerator.reset();
1326								index = 0;
1327							};
1328						};
1329					};
1330				};	
1331				func.prototype = Enumerable.prototype;
1332				return new func();
1333			},		
1334			
1335			/**
1336			 * Returns true if the Enumerable is identical to another 
1337			 * Enumerable by comparing the elements using an optional 
1338			 * comparer.
1339			 *
1340			 * Overloads:
1341			 *   sequenceEqual(second)
1342			 *   sequenceEqual(second, comparer)
1343			 */		
1344			sequenceEqual: function(second, comparer) {
1345				if (this == second) {
1346					return true;
1347				}
1348				if (arguments.length < 2) {
1349					comparer = EqualityComparer.getDefault();
1350				}					
1351				var firstEnumerator = this.getEnumerator();
1352				var secondEnumerator = second.getEnumerator();
1353				
1354				while (firstEnumerator.moveNext() && 
1355					secondEnumerator.moveNext()) {
1356					if (!comparer.equals(firstEnumerator.current(), 
1357						secondEnumerator.current())) {
1358						return false;
1359					}
1360				}					
1361				return true;
1362			},
1363			
1364			/**
1365			 * Returns the only element in the Enumerable or throws an  
1366			 * exception either if the Enumerable is empty or if there is more 
1367			 * than one element in the Enumerable. Optionally tests the 
1368			 * returned element against a predicate function and throws an 
1369			 * exception if the element does not satisfy its condition.
1370			 *
1371			 * Overloads:
1372			 *   single()
1373			 *   single(predicate)
1374			 */		
1375			single: function(predicate) {
1376				var hasPredicate = arguments.length > 0;
1377				var enumerator = this.getEnumerator();
1378				if (!enumerator.moveNext()) {
1379					throw new InvalidOperationException();	
1380				} 
1381				var current = enumerator.current();
1382				if (enumerator.moveNext() || (hasPredicate && 
1383					!predicate(current))) {
1384					throw new InvalidOperationException();	
1385				}
1386				return current;
1387			},
1388			
1389			/**
1390			 * Same as single, except that a specified default value is 
1391			 * returned if the Enumerable is empty, contains more than one 
1392			 * element or if the element does not satisfy the specified 
1393			 * condition. This method differs from its LINQ counterpart in 
1394			 * that it requires the default value to be specified explicitly.
1395			 *
1396			 * Overloads:
1397			 *   singleOrDefault(defaultValue)
1398			 *   singleOrDefault(predicate, defaultValue)
1399			 */		
1400			singleOrDefault: function() {
1401				if (arguments.length == 1) {
1402					var defaultValue = arguments[0];	
1403				} else if (arguments.length > 1) {
1404					var predicate = arguments[0];
1405					var defaultValue = arguments[1];
1406				}
1407				try {
1408					if (arguments.length > 1) {
1409						return this.single(predicate);
1410					} else {
1411						return this.single();
1412					}
1413				} catch (e) {
1414					return defaultValue;
1415				}									
1416			},
1417			
1418			/**
1419			 * Returns a new Enumerable that is the result of skipping the 
1420			 * first "count" elements of the Enumerable.     
1421			 */		
1422			skip: function(count) {
1423				if (count == 0) {
1424					return this;
1425				}
1426				return this.skipWhile(function(item, index) {
1427					return index < count;
1428				});
1429			},
1430			
1431			/**
1432			 * Returns a new Enumerable that is the result of skipping all 
1433			 * elements from the beginning of the Enumerable that satisfy a  
1434			 * specified condition.
1435			 */		
1436			skipWhile: function(predicate) {
1437				var _this = this;
1438				var func = function() {
1439					this.getEnumerator = function() {
1440						return new function() {
1441							var enumerator = _this.getEnumerator();
1442							var isInitialized = false;
1443							var index = 0;
1444														
1445							this.moveNext = function() {
1446								if (!isInitialized) {
1447									var canMoveNext = false;
1448									while ((canMoveNext = 
1449										enumerator.moveNext()) &&
1450										predicate(enumerator.current(), 
1451										index++)) { }
1452									isInitialized = true;
1453									return canMoveNext;
1454								} else {
1455									return enumerator.moveNext();
1456								}
1457							};
1458							
1459							this.current = function() {
1460								return enumerator.current();
1461							};
1462							
1463							this.reset = function() {
1464								enumerator.reset();
1465								isInitialized = false;
1466								index = 0;
1467							};
1468						};
1469					};
1470				};	
1471				func.prototype = Enumerable.prototype;
1472				return new func();					
1473			},
1474			
1475			/**
1476			 * Returns the sum of the elements in the Enumerable, optionally 
1477			 * using a specified selector.
1478			 *
1479			 * Overloads:
1480			 *   sum()
1481			 *   sum(selector)
1482			 */		
1483			sum: function(selector) {
1484				if (arguments.length == 0) {
1485					selector = identity;
1486				}
1487				return this.aggregate(0, function(running, current) {
1488					return running + selector(current);
1489				});
1490			},		
1491			
1492			/**
1493			 * Returns a new Enumerable that is the result of extracting the 
1494			 * first "count" elements from the Enumerable.
1495			 */		
1496			take: function(count) {
1497				if (count == 0) {
1498					return Enumerable.empty();
1499				}
1500				return this.takeWhile(function(item, index) {
1501					return index < count;
1502				});					
1503			},		
1504			
1505			/**
1506			 * Returns a new Enumerable that is the result of extracting all 
1507			 * elements from the beginning of the Enumerable that satisfy the  
1508			 * specified condition.
1509			 */		
1510			takeWhile: function(predicate) {
1511				var _this = this;
1512				var func = function() {
1513					this.getEnumerator = function() {
1514						return new function() {
1515							var enumerator = _this.getEnumerator();
1516							var isOperational = true;
1517							var index = 0;
1518														
1519							this.moveNext = function() {
1520								if (isOperational) {
1521									isOperational = enumerator.moveNext() && 
1522										predicate(enumerator.current(), 
1523										index++);
1524								}
1525								return isOperational;
1526							};
1527							
1528							this.current = function() {
1529								if (!isOperational) {
1530									throw new InvalidOperationException();
1531								}
1532								return enumerator.current();
1533							};
1534							
1535							this.reset = function() {
1536								enumerator.reset();
1537								isOperational = true;
1538								index = 0;
1539							};
1540						};
1541					};
1542				};	
1543				func.prototype = Enumerable.prototype;
1544				return new func();						
1545			},
1546			
1547			/**
1548			 * Returns an array containg all the elements in Enumerable.
1549			 */		
1550			toArray: function() {
1551				var enumerator = this.getEnumerator();
1552				var array = [];
1553				while (enumerator.moveNext()) {
1554					array.push(enumerator.current());
1555				}				
1556				return array;
1557			},
1558			
1559			/**
1560			 * Returns a new Enumerable that is the union of two Enumerables. 
1561			 * Optionally uses the specified comparer.
1562			 *
1563			 * Overloads:
1564			 *   union(second)
1565			 *   union(second, comparer)
1566			 */		
1567			union: function(second, comparer) {
1568				if (arguments.length < 2) {
1569					comparer = EqualityComparer.getDefault();
1570				}
1571				return this.concat(second).distinct(comparer);
1572			},
1573			
1574			/**
1575			 * Returns a new Enumerable that contains only the elements of the
1576			 * Enumerable that satisfy the specified condition.
1577			 */		
1578			where: function(predicate) {
1579				var _this = this;
1580				var func = function() {
1581					this.getEnumerator = function() {
1582						return new function() {
1583							var itemEnumerator = _this.getEnumerator();
1584							var current;
1585							var hasCurrent = false;
1586							this.moveNext = function() {
1587								hasCurrent = false;
1588								while (itemEnumerator.moveNext()) {
1589									current = itemEnumerator.current();
1590									if (predicate(current)) {
1591										hasCurrent = true;
1592										break;
1593									}
1594								}
1595								return hasCurrent;
1596							};
1597							
1598							this.current = function() {
1599								if (!hasCurrent) {
1600									throw new InvalidOperationException();
1601								}
1602								return current;
1603							};			
1604							
1605							this.reset = function() {
1606								itemEnumerator.reset();
1607								hasCurrent = false;
1608							};		
1609						};
1610					};
1611				};
1612				func.prototype = Enumerable.prototype;
1613				return new func();
1614			}				
1615		};
1616	})();
1617	
1618	this.InvalidOperationException = InvalidOperationException;
1619	this.ArgumentOutOfRangeException = ArgumentOutOfRangeException;
1620	this.EqualityComparer = EqualityComparer;
1621	this.Comparer = Comparer;
1622	this.Enumerable = Enumerable;
1623}).call(jsinq);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.