PageSourceSearch

https://mohawkatlas.org/nunaliit2/n2.utils.js

js mohawkatlas.org collected 2026-10-08 03:45:16 UTC 34,980 bytes, 1,316 lines download raw bytes

1/*
2Copyright (c) 2010, Geomatics and Cartographic Research Centre, Carleton 
3University
4All rights reserved.
5
6Redistribution and use in source and binary forms, with or without 
7modification, are permitted provided that the following conditions are met:
8
9 - Redistributions of source code must retain the above copyright notice, 
10   this list of conditions and the following disclaimer.
11 - Redistributions in binary form must reproduce the above copyright notice,
12   this list of conditions and the following disclaimer in the documentation
13   and/or other materials provided with the distribution.
14 - Neither the name of the Geomatics and Cartographic Research Centre, 
15   Carleton University nor the names of its contributors may be used to 
16   endorse or promote products derived from this software without specific 
17   prior written permission.
18
19THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
21IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
22ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
23LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
24CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
25SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
26INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
27CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
28ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
29POSSIBILITY OF SUCH DAMAGE.
30
31$Id: n2.utils.js 8415 2012-08-07 15:39:35Z jpfiset $
32*/
33
34// @requires n2.core.js
35// @namespace nunaliit2
36;(function($n2){
37"use strict";
38
39/**
40 	Logs to the console a string and a set of arguments. This works best with Firebug installed.
41 	@name log
42 	@function
43 	@memberOf nunaliit2
44 	@param {string} msg Log message
45 	@param {Object} o1  Object to be inspected in log
46 */
47$n2.log = function(msg,o1) {
48	if( typeof window === 'object'
49	 && window.console 
50	 && typeof window.console.log === 'function' ) {
51		try { window.console.log.apply(window.console,arguments); }
52			catch(e) {};
53	};
54};
55
56/**
57	Logs an error to the console
58	@name logError
59	@function
60	@memberOf nunaliit2
61	@param {String} msg Log message
62	@param {Object} o1  Object to be inspected in log
63*/
64$n2.logError = function() {
65	if( typeof window === 'object'
66	 && window.console 
67	 && typeof window.console.error === 'function' ) {
68		try { window.console.error.apply(window.console,arguments); }
69			catch(e) {};
70	} else {
71		$n2.log.apply(this,arguments);
72	};
73};
74
75// Install logger on window
76if( typeof window !== 'undefined' ) {
77	if( typeof(window.log) === 'undefined' ) {
78		window.log = $n2.log;
79	};
80};
81
82var nativeArrayIndexOf = null;
83if( typeof Array !== 'undefined' && Array.prototype ){
84	nativeArrayIndexOf = Array.prototype.indexOf;
85};
86
87var nativeStringTrim = null;
88if( typeof String !== 'undefined' && String.prototype ){
89	nativeStringTrim = String.prototype.trim;
90};
91
92
93var cachedBrowserInfo = null;
94
95//*********************************************
96// reportError - This function reports an error
97// only once, supressing it after the first report.
98// The id is used to identify the error.
99
100$n2.ERROR_NO_SUPRESS = {};
101
102var reportedErrors = {};
103
104/**
105 	Alerts the user of an error. An error is presented to the user
106 	only the first time it is encountered. Subsequent similar errors
107 	are suppressed.
108 	@name reportError
109 	@function
110 	@memberOf nunaliit2
111 	@param {String} id A unique identifier for the error reported.
112 	@param {String} msg Error message presented to the user. If omitted,
113 	                    the id is presented to the user.
114 */
115$n2.reportError = function(id,str) {
116	if( arguments.length < 1 ) return;
117	
118	if( arguments.length < 2 ) str = id; 
119
120	$n2.logError.apply(null,arguments);	
121	if( id === $n2.ERROR_NO_SUPRESS ) {
122		// Report, always
123		alert(str);
124	} else if( reportedErrors[id] ) {
125		// do not report again
126	} else {
127		reportedErrors[id] = 1;
128		alert(str);
129	};	
130};
131
132$n2.reportErrorForced = function(str) {
133	$n2.reportError($n2.ERROR_NO_SUPRESS,str);	
134};
135
136//*********************************************
137// isDefined
138
139/**
140 	Returns true if a the given argument is defined.
141 	@name isDefined
142 	@function
143 	@memberOf nunaliit2
144 	@param {String} _v Argument to be tested.
145 */
146$n2.isDefined = function(_v) {
147	return('undefined' != typeof(_v) && null != _v);
148};
149
150if( typeof window !== 'undefined' ) {
151	if( typeof(window.isDefined) === 'undefined' ) {
152		window.isDefined = $n2.isDefined;
153	};
154};
155
156//*********************************************
157// isArray
158
159/**
160 	Returns true if a the given argument is an array.
161 	@name isArray
162 	@function
163 	@memberOf nunaliit2
164 	@param {Object} o Argument to be tested.
165 */
166$n2.isArray = function(o) {
167	if( o === null ) return false;
168	if( o === undefined ) return false;
169	if( typeof(o) !== 'object' ) return false;
170	if( typeof(o.length) !== 'number' ) return false;
171	if( typeof(o.push) !== 'function' ) return false;
172	if( typeof(o.pop) !== 'function' ) return false;
173	if( typeof(o.concat) !== 'function' ) return false;
174	if( typeof(o.join) !== 'function' ) return false;
175	if( typeof(o.slice) !== 'function' ) return false;
176	if( typeof(o.reverse) !== 'function' ) return false;
177	if( typeof(o.splice) !== 'function' ) return false;
178	if( typeof(o.sort) !== 'function' ) return false;
179	
180	return true;
181};
182
183//*********************************************
184// inArray
185
186/**
187	Returns the index of the element if found in
188	the array. Returns -1 if not found.
189	@name inArray
190	@function
191	@memberOf nunaliit2
192	@param {Object} elem Element searched for in array
193	@param {Array} arr Element searched for in array
194	@param {Number} index Index where the search is started
195	@return {Number} Index of found element. Returns -1, if not found.
196*/
197$n2.inArray = function( elem, arr, index ) {
198	var len;
199
200	if ( arr ) {
201		if ( nativeArrayIndexOf ) {
202			return nativeArrayIndexOf.call( arr, elem, index );
203		};
204
205		len = arr.length;
206		index = index ? index : 0;
207		index = index < 0 ? Math.max( 0, len + index ) : index;
208
209		for( ; index < len; ++index) {
210			if( index in arr 
211			 && arr[index] === elem ) {
212				return index;
213			};
214		};
215	};
216
217	return -1;
218};
219
220
221//*********************************************
222// getUniqueId
223
224var n2UniqueId = 0;
225/**
226	Returns an identifier unique to this instance of
227	nunaliit2.
228	@name getUniqueId
229	@function
230	@memberOf nunaliit2
231	@return {String} Unique string that can be used as an identifier
232*/
233$n2.getUniqueId = function(prefix) {
234	prefix = prefix ? prefix : 'nunaliit2_uniqueId_';
235	var id = prefix + n2UniqueId;
236	++n2UniqueId;
237	return id;
238};
239
240/**
241 	Returns the media type (image, video or audio)
242 	associated with the given file.
243 	@name getMediaType
244 	@function
245 	@memberOf nunaliit2
246 	@param {String} filename Name of file of given media file.
247 	@param {String} mimetype MIME Type associated with file.
248 	@returns {String} Type of media: 'audio', 'image' or 'video'
249 */
250$n2.getMediaType = function(filename, mimetype) {
251	var mediaType = null;
252
253	// Start with MIME type, if provided
254	if(typeof(mimetype) != 'undefined'
255	 && null != mimetype 
256	 && '' != mimetype
257	 ) {
258		var mimeClass = mimetype.split('/');
259		if ('image' === mimeClass[0] ||
260			'video' === mimeClass[0] ||
261			'audio' === mimeClass[0]) {
262			mediaType = mimeClass[0];
263		};
264	
265		// Known mime types	
266		if( null === mediaType
267		 && 'application/ogg' === mimetype ) {
268			mediaType = 'audio';
269		};
270	};
271	
272	if( null === mediaType 
273	 && typeof(filename) != 'undefined' 
274	 && null != filename 
275	 && '' != filename
276	 ) {
277		var fileFrags = filename.split('.');
278		var ext = fileFrags[fileFrags.length - 1];
279		
280		// known extensions
281		if( 'mp3' === ext.toLowerCase() ) {
282			mediaType = 'audio';
283		} else if( 'ogg' === ext.toLowerCase() ) {
284			mediaType = 'audio';
285		} else if( 'jpg' === ext.toLowerCase() ) {
286			mediaType = 'image';
287		} else if( 'png' === ext.toLowerCase() ) {
288			mediaType = 'image';
289		} else if( 'jpeg' === ext.toLowerCase() ) {
290			mediaType = 'image';
291		} else if( 'bmp' === ext.toLowerCase() ) {
292			mediaType = 'image';
293		} else if( 'mov' === ext.toLowerCase() ) {
294			mediaType = 'video';
295		};
296	};
297	return(mediaType);
298};
299
300/**
301 	Converts a textual representation of LongLat into an object
302 	with values for longitude and latitude. Return null if the
303 	textual representation can not be recognized.
304 	@name parseLongLatText
305 	@function
306 	@memberOf nunaliit2
307 	@param {String} text A textual representation of LongLat (45°32'48.94"N, 73°33'19.93"W)
308 	@returns {Object} Hash containing two properties: lat and long
309 */
310var longLatRe = /^\s*([0-9]{1,2})(°|d|\u00B0)\s*([0-9]{1,2})['m]\s*([0-9]{1,2}(\.[0-9]+)?)["s]\s*([NS])\s*,?\s*([0-9]{1,3})(°|d|\u00B0)\s*([0-9]{1,2})['m]\s*([0-9]{1,2}(\.[0-9]+)?)["s]\s*([WE])\s*$/;
311$n2.parseLongLatText = function(text) {
312	var result = {};
313	
314	//$n2.log('longLat text',text,longLatRe);
315
316	var matchObj = text.match(longLatRe);
317	if( null == matchObj ) {
318		return null;
319	};
320
321	//$n2.log('longLat',matchObj);
322
323	var longMult = 1;
324	if( 'S' === matchObj[6] ) {
325		longMult = -1;
326	};
327	result.lng = longMult * (
328		1 * matchObj[1] 
329		+ (1 * matchObj[3] / 60)
330		+ (1 * matchObj[4] / 3600)
331	);
332		
333	var latMult = 1;
334	if( 'W' === matchObj[12] ) {
335		latMult = -1;
336	};
337	result.lat = latMult * (
338		1 * matchObj[7] 
339		+ (1 * matchObj[9] / 60)
340		+ (1 * matchObj[10] / 3600)
341	);
342		
343	//$n2.log('parseLongLatText',text,result);
344
345	return result;
346};
347
348/**
349 * Generate the HTML representation of a hyperlink, given a text label,
350 * url, and optional css classes (single string or array of strings).
351 * If no url is included or it is an empty string, then a string is returned
352 * within a classed div.
353 * @param text label for hyperlink
354 * @param url text for url
355 * @param cls class string or array of class strings
356 */
357$n2.generateHyperlinkHTML = function(text, url, cls) {
358	function catStrings(ar) {
359		var out = '';
360		var spc = '';
361		for (var i=0; i < ar.length; i++) {
362			out.concat(spc, ar[i]);
363			spc = ' ';
364		};
365		return out;
366	};
367	
368	var clsString = '';
369	if ($n2.isArray(cls)) {
370		clsString = catStrings(cls);
371	} else {
372		clsString = cls;
373	};
374	
375	var classSpec = '';
376	if ('' !== clsString) {
377		classSpec = ' class="' + clsString + '"';
378	};
379	
380	var uTxt = url;
381	if (! $n2.isDefined(uTxt) || uTxt === '') {
382		uTxt = '';
383	};
384	
385	var label = text;
386	if (! $n2.isDefined(label) || label === '') {
387		label = uTxt; // replicate url as text if no label
388	};
389	
390	var out = '';
391	if (uTxt !== '') { // generate <a>
392		out = '<a' + classSpec + 
393			' href="' + uTxt +
394			'" target="_blank">' + label + '</a>';
395	} else { // return label string as classed div
396		out = '<div' + classSpec + '>' + label + '</div>'; 
397	};
398	
399	return out;
400};
401
402/**
403	Extends a javascript object using refinements.
404	@name extend
405	@function
406	@memberOf nunaliit2
407	@param {Object} target Target object to receive the extensions
408	@returns {Object} The target object that was extended
409*/
410$n2.extend = function() {
411	// copy reference to target object
412	var target = arguments[0] || {}
413		,deep = false;
414
415	// Handle a deep copy situation
416	var i = 1;
417	if ( typeof target === 'boolean' ) {
418		deep = target;
419		target = arguments[1] || {};
420		// skip the boolean and the target
421		i = 2;
422	}
423
424	for(var e=arguments.length; i<e; ++i) {
425		var options = arguments[i];
426
427		// Only deal with non-null/undefined values
428		if( null != options ) {
429			// Extend the base object
430			for(var name in options) {
431				var src = target[ name ];
432				var copy = options[ name ];
433
434				// Prevent never-ending loop
435				if ( target === copy ) {
436					continue;
437				}
438
439				// Recurse if we're merging arrays
440				if ( deep && copy && $n2.isArray(copy) ) {
441					var clone = (src && $n2.isArray(src)) ? src : [];
442
443					target[ name ] = $n2.extend( deep, clone, copy );
444					
445				} else if( deep && copy && typeof(copy) === 'object' ) {
446					// Recurse if we're merging objects
447					var clone = ('object' == typeof(src)) ? src : {};
448
449					target[ name ] = $n2.extend( deep, clone, copy );
450
451				} else if( copy !== undefined ) {
452					// Don't bring in undefined values
453					target[ name ] = copy;
454				}
455			}
456		}
457	}
458
459	// Return the modified object
460	return target;
461};
462
463/**
464	Makes a deep copy of the given object.
465	@name deepCopy
466	@function
467	@memberOf nunaliit2
468	@param {Object} o Object to be copied
469	@returns {Object} The copy of the object given in argument
470*/
471$n2.deepCopy = function(o) {
472	if( null === o ){
473		return o;
474		
475	} else if( typeof o === 'undefined' ) {
476		return o;
477		
478	} else if( $n2.isArray(o) ) {
479		var c = [];
480		for(var i=0,e=o.length; i<e; ++i){
481			c[i] = $n2.deepCopy(o[i]);
482		};
483		return c;
484		
485	} else if( typeof(o) === 'object' ) {
486		var c = {};
487		for(var i in o){
488			c[i] = $n2.deepCopy(o[i]);
489		};
490		return c;
491
492	} else {
493		return o;
494	};
495};
496
497/**
498	Helper function for localization. It redirects to l10n
499	package if it is loaded. This function request a string
500	localized for the current locale given a string in the
501	default language (generally english). If the l10n package
502	is not loaded, it returns the requested string.
503	@name loc
504	@function
505	@memberOf nunaliit2
506	@param {String} str String to be translated
507	@param {String} packageName Name of package making the
508	                            translation request
509	@returns {String} The string localized for the current locale.
510*/
511$n2.loc = function(str, packageName, args) {
512	if( $n2.l10n && $n2.l10n.getLocalizedString ) {
513		return $n2.l10n.getLocalizedString(str, packageName, args);
514	};
515	return str;
516};
517
518/**
519 * Trim function for strings. If available, use the one already provided.
520 * If not, use home made one.
521 * @name trim
522 * @function
523 * @memberOf nunaliit2
524 * @param {String} text String to be trimmed of leading and trailing white
525 *                      spaces.
526 * @returns {String} String which is a trimmed version of the input argument.
527 */
528var reLeftWhite = /^[\s\t\x0d\x0a]+/;
529var reRightWhite = /[\s\t\x0d\x0a]+$/;
530$n2.trim = function(text){
531	if( null === text ) {
532		return '';
533	};
534	
535	if( nativeStringTrim ) {
536		return nativeStringTrim.call(text);
537	};
538
539	// Use own version
540	return text.toString()
541		.replace(reLeftWhite, '')
542		.replace(reRightWhite, '')
543		;
544};
545
546/** @namespace nunaliit2.utils */
547$n2.utils = {
548	
549	_callbacks: {},
550		
551	/**
552	 * Converts a string to a string that can be used with a HTML id
553	 * or class name. Not all characters can be used in HTML identifiers
554	 * and class names and this routine escape the unwanted characters from
555	 * the given string. 
556	 * @name stringToHtmlId
557	 * @function
558	 * @memberOf nunaliit2.utils
559	 * @param {String} s String to be converted in a form where it can be used
560	 *                   as an HTML id or HTML class name.
561	 * @returns {String} String safe for HTML id or class name.
562	 */
563	stringToHtmlId: function(s){
564		var res = [];
565		for(var i=0,e=s.length; i<e; ++i) {
566			var c = s[i];
567			if( c >= 'a' && c <= 'z' ) { res.push(c); }
568			else if( c >= 'A' && c <= 'Z' ) { res.push(c); }
569			else if( c >= '0' && c <= '9' ) { res.push(c); }
570			else {
571				var code = c.charCodeAt(0);
572				var o0 = (code & 0x07) + 0x30;
573				var o1 = ((code >> 3) & 0x07) + 0x30;
574				var o2 = ((code >> 6) & 0x07) + 0x30;
575				res.push('_');
576				res.push( String.fromCharCode(o2) );
577				res.push( String.fromCharCode(o1) );
578				res.push( String.fromCharCode(o0) );
579			};
580		};
581		return res.join('');
582	},
583	
584	/**
585	 * Unescapes a string previously converted using stringToHtmlId(). 
586	 * @name unescapeHtmlId
587	 * @function
588	 * @memberOf nunaliit2.utils
589	 * @param {String} s String to be unescaped.
590	 * @returns {String} String initially passed to the stringToHtmlId() function.
591	 */
592	unescapeHtmlId: function(s){
593		var res = [];
594		for(var i=0,e=s.length; i<e; ++i) {
595			var c = s[i];
596			if( c === '_' ) { 
597				++i;
598				var o2 = s.charCodeAt(i);
599				++i;
600				var o1 = s.charCodeAt(i);
601				++i;
602				var o0 = s.charCodeAt(i);
603				
604				var b = ((o2-0x30)<<6)+((o1-0x30)<<3)+(o0-0x30);
605				res.push(String.fromCharCode(b));
606				
607			} else {
608				res.push(c);
609			};
610		};
611		return res.join('');
612	},
613
614	/**
615	 * Returns information about the browser. This is based on code found
616	 * here: http://www.quirksmode.org/js/detect.html 
617	 * @name getBrowserInfo
618	 * @function
619	 * @memberOf nunaliit2.utils
620	 * @returns {Object} Object containing information about the browser
621	 * where the application runs
622	 */
623	getBrowserInfo: function(){
624		
625		if( cachedBrowserInfo ){
626			return cachedBrowserInfo;
627		};
628
629		var dataBrowser = [
630   			{
631   				string: navigator.userAgent,
632   				subString: "Chrome",
633   				identity: "Chrome"
634   			},
635   			{ 	
636   				string: navigator.userAgent,
637   				subString: "OmniWeb",
638   				versionSearch: "OmniWeb/",
639   				identity: "OmniWeb"
640   			},
641   			{
642   				string: navigator.vendor,
643   				subString: "Apple",
644   				identity: "Safari",
645   				versionSearch: "Version"
646   			},
647   			{
648   				prop: window.opera,
649   				identity: "Opera",
650   				versionSearch: "Version"
651   			},
652   			{
653   				string: navigator.vendor,
654   				subString: "iCab",
655   				identity: "iCab"
656   			},
657   			{
658   				string: navigator.vendor,
659   				subString: "KDE",
660   				identity: "Konqueror"
661   			},
662   			{
663   				string: navigator.userAgent,
664   				subString: "Firefox",
665   				identity: "Firefox"
666   			},
667   			{
668   				string: navigator.vendor,
669   				subString: "Camino",
670   				identity: "Camino"
671   			},
672   			{		// for newer Netscapes (6+)
673   				string: navigator.userAgent,
674   				subString: "Netscape",
675   				identity: "Netscape"
676   			},
677   			{
678   				string: navigator.userAgent,
679   				subString: "MSIE",
680   				identity: "Explorer",
681   				versionSearch: "MSIE"
682   			},
683   			{
684   				string: navigator.userAgent,
685   				subString: "Gecko",
686   				identity: "Mozilla",
687   				versionSearch: "rv"
688   			},
689   			{ 		// for older Netscapes (4-)
690   				string: navigator.userAgent,
691   				subString: "Mozilla",
692   				identity: "Netscape",
693   				versionSearch: "Mozilla"
694   			}
695   		];
696		var dataOS = [
697  			{
698  				string: navigator.platform,
699  				subString: "Win",
700  				identity: "Windows"
701  			},
702  			{
703  				string: navigator.platform,
704  				subString: "Mac",
705  				identity: "Mac"
706  			},
707  			{
708  				string: navigator.userAgent,
709  				subString: "iPhone",
710  				identity: "iPhone/iPod"
711  			},
712  			{
713  				string: navigator.platform,
714  				subString: "Linux",
715  				identity: "Linux"
716  			}
717  		];
718		
719		var browserInfo = {};
720		var versionSearchString;
721		init();
722		cachedBrowserInfo = browserInfo;
723		return browserInfo;
724
725		function init() {
726			browserInfo.browser = searchString(dataBrowser) || "An unknown browser";
727			browserInfo.version = searchVersion(navigator.userAgent)
728				|| searchVersion(navigator.appVersion)
729				|| "an unknown version";
730			browserInfo.OS = searchString(dataOS) || "an unknown OS";
731		};
732		
733		function searchString(data) {
734			for (var i=0;i<data.length;i++)	{
735				var dataString = data[i].string;
736				var dataProp = data[i].prop;
737				versionSearchString = data[i].versionSearch || data[i].identity;
738				if (dataString) {
739					if (dataString.indexOf(data[i].subString) != -1)
740						return data[i].identity;
741				}
742				else if (dataProp)
743					return data[i].identity;
744			}
745		};
746
747		function searchVersion(dataString) {
748			var index = dataString.indexOf(versionSearchString);
749			if (index == -1) return;
750			return parseFloat(dataString.substring(index+versionSearchString.length+1));
751		};
752		
753	},
754
755	/**
756	 * Returns true if the input is a number, regardless if it is a number
757	 * in a string, or a direct number.
758	 * See http://stackoverflow.com/questions/18082/validate-numbers-in-javascript-isnumeric
759	 * @name isNumber
760	 * @function
761	 * @memberOf nunaliit2.utils
762	 * @param {String} String or number to be tested
763	 * @returns {Boolean} True if given parameter represents a number. False, otherwise.
764	 */
765	isNumber: function(n){
766		return !isNaN(parseFloat(n)) && isFinite(n);
767	},
768	
769	/**
770	 * Returns a object that describes the javascript declaration included
771	 * in the host page. The HTML of the host page is traversed to find all
772	 * javascript declarations and if one matches the name given in argument,
773	 * an object is returned to describe the declaration. If a matching
774	 * declaration is not found, null is returned.
775	 * @name findJavascriptDeclaration
776	 * @function
777	 * @memberOf nunaliit2.utils
778	 * @param javascriptFileName {String} name of the file for the seeked javascript
779	 * declaration. This should be the name and extension, without any path fragment.
780	 * @returns {Object} Object that describes the jaavascript declaration. Null if
781	 * the seeked declaration is not found.
782	 */
783	findJavascriptDeclaration: function(javascriptFileName){
784		var scriptLocation = null;
785		var scriptElem = null;
786		var pattern = new RegExp('(^|(.*?\\/))'+javascriptFileName+'$');
787	 
788		var scripts = document.getElementsByTagName('script');
789		for( var loop=0; loop<scripts.length; ++loop ) {
790			var src = scripts[loop].getAttribute('src');
791			if (src) {
792				var match = src.match(pattern);
793				if( match ) {
794					scriptLocation = match[1];
795					scriptElem = scripts[loop];
796					break;
797				}
798			}
799		};
800		
801		var result = null;
802		if( null != scriptElem ){
803			result = {
804				element: scriptElem
805				,location: scriptLocation
806				,name: javascriptFileName
807			};
808		};
809		
810		return result;
811	},
812	
813	/**
814	 * Inserts new javascript declarations at the end of the host document. Multiple 
815	 * declarations are added this way, each one based
816	 * on a name found in the given array of strings. Finally, a callback is given
817	 * which is called when the new declarations have been added and parsed.
818	 * @name insertJavascriptDeclarations
819	 * @function
820	 * @memberOf nunaliit2.utils
821	 * @param declarationDescription {Object} Declaration description previously
822	 * obtained using nunaliit2.utils.findJavascriptDeclaration(). All new declarations
823	 * are inserted previous to this one. Also, new declarations are made relative to
824	 * the path of this declaration.
825	 * @param names string[]} Names of the javascript files that should be inserted
826	 * as new declarations. These names may contain path fragments, relative to the
827	 * declaration description given in argument. 
828	 * @param callback {Function} Function called after all declarations have been inserted.
829	 * @returns {void}
830	 */
831	insertJavascriptDeclarations: function(declarationDescription, names, callback){
832		
833		if( typeof names === 'string' ){
834			names = [ names ];
835		};
836
837		var scriptLocation = declarationDescription.location;
838		var insertBeforeElem = declarationDescription.element;
839
840		// Keep track of callback
841		var loaded = false;
842
843		for( var i=0; i<names.length; ++i ) {
844			var script = document.createElement('script');
845			script.setAttribute('type', 'text/javascript');
846			script.setAttribute('src', scriptLocation + names[i]);
847			script.onreadystatechange = script.onload = function() {
848				if( !loaded ){
849					loaded = true;
850					if( typeof callback === 'function' ){
851						callback();
852					};
853				};
854			};
855
856			insertBeforeElem.parentNode.insertBefore(script,insertBeforeElem);
857		};
858	},
859	
860	/**
861	 * Accepts a formatting string and arguments. Returns the formatted
862	 * string. For example, $n2.formatString('{a} and {b}',{a:1,b:2}) returns
863	 * '1 and 2'
864	 * @name formatString
865	 * @function
866	 * @memberOf nunaliit2.utils
867	 * @param format {String} Formatting string that contains sequences to be
868	 * replaced by arguments. The sequences are names included within curly
869	 * braces, for example {a}
870	 * @param args {Object} Dictionary of arguments
871	 * @returns {String}
872	 */
873	formatString: function(format, args){
874		return format.replace(/{([^}]+)}/g, function(match, name) {
875			name = $n2.trim(name);
876			if( '' === name ) return match;
877			
878			return typeof args[name] !== 'undefined'
879				? args[name]
880				: match
881			;
882		});
883	},
884	
885	/**
886	 * Returns the number of milliseconds elapsed since 1 January 1970 00:00:00 UTC. This
887	 * is equivalent to Date.now(). However, Date.now() is not supported in IE8.
888	 * @name getCurrentTime
889	 * @function
890	 * @memberOf nunaliit2.utils
891	 * @returns {Number}
892	 */
893	getCurrentTime: Date.now || function () {
894		return +new Date();
895	},
896	
897	/**
898	 * Returns a string which represents the date formatted as specified.
899	 * @name formatDate
900	 * @function
901	 * @memberOf nunaliit2.utils
902	 * @returns {String}
903	 */
904	formatDate: function(date, format){
905		var str = format.replace('%Y',''+date.getFullYear());
906		
907		var monthNumStr = ''+(date.getMonth()+1);
908		if( monthNumStr.length < 2 ) monthNumStr = '0'+monthNumStr;
909		str = str.replace('%m',monthNumStr);
910		
911		var dateNumStr = ''+date.getDate();
912		if( dateNumStr.length < 2 ) dateNumStr = '0'+dateNumStr;
913		str = str.replace('%d',dateNumStr);
914		
915		var hoursNumStr = ''+date.getHours();
916		if( hoursNumStr.length < 2 ) hoursNumStr = '0'+hoursNumStr;
917		str = str.replace('%H',hoursNumStr);
918		
919		var minutesNumStr = ''+date.getMinutes();
920		if( minutesNumStr.length < 2 ) minutesNumStr = '0'+minutesNumStr;
921		str = str.replace('%M',minutesNumStr);
922		
923		var secondsNumStr = ''+date.getSeconds();
924		if( secondsNumStr.length < 2 ) secondsNumStr = '0'+secondsNumStr;
925		str = str.replace('%S',secondsNumStr);
926		
927		return str;
928	},
929	
930	/**
931	 * Returns a Javascript object returned in an XMLHttpRequest
932	 * with an error. 
933	 * @name parseHttpJsonError
934	 * @function
935	 * @memberOf nunaliit2.utils
936	 * @param {Object} Instance of XMLHttpRequest used to retrieve information
937	 * @param {String} Error string that should be returned if an object can not
938	 *                 be parsed.
939	 * @returns {Object} JSON object returned by the instance of XMLHttpRequest
940	 */
941	parseHttpJsonError: function(xmlHttpRequest,defaultText){
942		// Need JSON
943		if( !JSON || typeof(JSON.parse) !== 'function' ) {
944			return createDefault();
945		};
946		
947		// Need a response text
948		var text = xmlHttpRequest.responseText;
949		if( !text ) return createDefault();
950		
951		// Parse
952		var error = JSON.parse(text);
953		if( !error ) return createDefault();
954		
955		return error;
956		
957		function createDefault(){
958			return {
959				error: defaultText
960				,reason: defaultText
961			};
962		};
963	},
964
965	/**
966	 * Creates a version of the function where the rate of calls is limited to
967	 * one call per defined time out.
968	 * @name debounce
969	 * @function
970	 * @memberOf nunaliit2.utils
971	 * @param {Function} func The function that should be limited in the rate at which
972	 *                   it is fired.
973	 * @param {Number} wait Number of milliseconds between the calls to the function
974	 * @param {boolean} immediate If set, executes the function in parameters at the start. 
975	 *                            Otherwise, first execution is after wait time.
976	 * @returns {boolean} If true, the function is called immediately, and again in the
977	 *                    future after a period defined by the timeout.
978	 */
979	debounce: function(func, wait, immediate) {
980		var timeout = null;
981		return function() {
982			var context = this, args = arguments;
983			var later = function() {
984				timeout = null;
985				if (!immediate) func.apply(context, args);
986			};
987			if (immediate && !timeout) func.apply(context, args);
988			clearTimeout(timeout);
989			timeout = setTimeout(later, wait);
990		};
991	},
992
993	/**
994	 * Creates a version of the function where the rate of calls is throttled to no
995	 * more than a set rate. If, during the delay, the function is called again, then
996	 * it will result in a last call at the end of the delay.
997	 * @name throttle
998	 * @function
999	 * @memberOf nunaliit2.utils
1000	 * @param {Function} func The function that should be limited in the rate at which
1001	 *                   it is fired.
1002	 * @param {Number} wait Number of milliseconds between the calls to the function
1003	 * @returns {boolean} If true, the function is called immediately, and again in the
1004	 *                    future after a period defined by the timeout.
1005	 */
1006	throttle: function(func, wait) {
1007		var timeoutId = undefined;
1008		var lastRan = undefined;
1009		var throttledFn = function() {
1010			var context = this, args = arguments;
1011			var immediate = false;
1012			if( !lastRan ){
1013				immediate = true;
1014			} else {
1015				var timeSinceLastCall = Date.now() - lastRan;
1016				if( timeSinceLastCall >= wait ){
1017					immediate = true;
1018				};
1019			};
1020			if( immediate ){
1021				// Run it now.
1022				func.apply(context, args);
1023				lastRan = Date.now();
1024			} else if( timeoutId ){
1025				// Already waiting for a timeout. No
1026				// need to schedule again
1027			} else {
1028				// Schedule to be called again in the future
1029				var delay = wait - (Date.now() - lastRan);
1030				timeoutId = setTimeout(
1031					function(){
1032						func.apply(context, args);
1033						lastRan = Date.now();
1034						timeoutId = undefined;
1035					}
1036					,delay
1037				);
1038			};
1039		};
1040
1041		// Call this function for the next invocation to be immediate
1042		throttledFn.setImmediate = function(){
1043			lastRan = undefined;
1044		};
1045
1046		return throttledFn;
1047	},
1048
1049	/**
1050	 * Returns the identifier associated with the given element. If no identifier
1051	 * is currently assigned to the element, assign a unique one and then return it.
1052	 * @name getElementIdentifier
1053	 * @function
1054	 * @memberOf nunaliit2.utils
1055	 * @param {Object} elem DOM element where the identifier is desired.
1056	 * @returns {String} Element identifier
1057	 */
1058	getElementIdentifier: function(elem){
1059		var $elem = $(elem);
1060		var id = $elem.attr('id');
1061		if( !id ){
1062			id = $n2.getUniqueId();
1063			$elem.attr('id',id);
1064		};
1065		return id;
1066	},
1067	
1068	/**
1069	 * Returns an array of all values found in a map or array.
1070	 * @name values
1071	 * @function
1072	 * @memberOf nunaliit2.utils
1073	 * @param {Object} map Javascript object or array
1074	 * @returns {Object[]} Values found in the object or array
1075	 */
1076	values: function(map){
1077		var v = [];
1078		for(var i in map){
1079			v[v.length] = map[i];
1080		};
1081		return v;
1082	},
1083	
1084	/**
1085	 * Returns an array of all keys found in a map or array.
1086	 * @name keys
1087	 * @function
1088	 * @memberOf nunaliit2.utils
1089	 * @param {Object} map Javascript object or array
1090	 * @returns {Object[]} Keys found in the object or array
1091	 */
1092	keys: function(map){
1093		var v = [];
1094		for(var i in map){
1095			v[v.length] = i;
1096		};
1097		return v;
1098	}
1099};
1100
1101// HTML Escape
1102var htmlEscapeCharMap = {
1103	'&': '&amp;'
1104	,'<': '&lt;'
1105	,'>': '&gt;'
1106	,'"': '&quot;'
1107	,"'": '&#x27;'
1108	,'`': '&#x60;'
1109};
1110
1111var reHtmlCharSelect = /[&<>"'`]/g;
1112var reHtmlCharTest = /[&<>"'`]/;
1113
1114var htmlEscapeCharFn = function(match) {
1115 	return htmlEscapeCharMap[match] || "";
1116};
1117
1118/**
1119 * Accepts a string and returns an escaped version of the string, which
1120 * can be used in HTML. This function converts all the characters that are
1121 * reserved in HTML for the escaped version of the same character.
1122 * @name escapeHtml
1123 * @function
1124 * @memberOf nunaliit2.utils
1125 * @param str {String} String to be escaped for HTML
1126 * @returns {String} String escaped for use in HTML
1127 */
1128$n2.utils.escapeHtml = function(str) {
1129	if( typeof(str) !== 'string' ) {
1130		return "";
1131	};
1132	
1133	// Need to escape?
1134	if( !reHtmlCharTest.test(str) ) {
1135		return str;
1136	};
1137	
1138	// Perform escape
1139	return str.replace(reHtmlCharSelect, htmlEscapeCharFn);
1140};
1141
1142/**
1143 * Accepts a string and returns an instance of the named class.
1144 * Example: var canvasMap1 = $n2.utils.getInstance('$n2.canvasMap', opts)
1145 * @name getInstance
1146 * @function
1147 * @memberOf nunaliit2.utils
1148 * @param str {String} String of class name 
1149 * @returns Instance object
1150 */
1151$n2.utils.getInstance = function(str, var_args ) {
1152	if ( typeof(str) !== 'string'){
1153		return null;
1154	};
1155	var arr = str.split(".");
1156
1157	var fn = (window || this);
1158	for (var i = 0, len = arr.length; i < len; i++) {
1159		if (typeof fn === 'undefined'){
1160			return null;
1161		}
1162	    fn = fn[arr[i]];
1163	}
1164
1165	if (typeof fn !== "function") {
1166	  $n2.log("Constructor Function not found");
1167	  return null;
1168	}
1169	
1170
1171	var args = new Array(arguments.length -1);
1172	for (var i=1; i< arguments.length; i++) {
1173		args[i-1] = arguments[i];
1174	}
1175	
1176	var instance = new fn(var_args);
1177	
1178	if (typeof instance !== "object") {
1179		$n2.log("Target instance object creation error");
1180		return null;
1181	}
1182	return instance;
1183};
1184
1185/**
1186 * Accepts a string and returns a method of the named method.
1187 * Example: var canvasMethod = $n2.utils.getMethod('$n2.canvasMap.handleCanvasDisplay')
1188 * @name getMethod
1189 * @function
1190 * @memberOf nunaliit2.utils
1191 * @param str {String}
1191 String of method name 
1192 * @returns function 
1193 */
1194$n2.utils.getMethod = function(str, var_args ) {
1195	if ( typeof(str) !== 'string'){
1196		return null;
1197	};
1198	var arr = str.split(".");
1199
1200	var fn = (window || this);
1201	for (var i = 0, len = arr.length; i < len; i++) {
1202		if (typeof fn === 'undefined'){
1203			return null;
1204		}
1205	    fn = fn[arr[i]];
1206	}
1207	if (typeof fn !== "function") {
1208	  $n2.log("Target method is not a Function.");
1209	  return null;
1210	}
1211	
1212
1213	return fn;
1214};
1215/**
1216 * DFS
1217 * @name DFS
1218 * @function
1219 * @memberOf nunaliit2.utils
1220 * @param item {String} 
1221 * @param predicate1 function return true if item is legal, false otherwise
1222 * @param predicate2 function return true if item has embeded item inside itself
1223 * @param callback function to call on each item
1224 * @returns {void};
1225 */
1226$n2.utils.DFS = function(item, predicate1, predicate2, callback){
1227	if(!item || item === "" || Array.isArray(item) && item.length === 0  ) return;
1228	if ( typeof predicate1 === 'function'
1229		&& predicate1(item)){
1230		callback (item);
1231		return;
1232	}
1233	if( typeof predicate2 === 'function'
1234		&& predicate2(item)){
1235		if (Array.isArray(item)){
1236			for( var i = 0,e=item.length; i< e; i++){
1237				var innerfst = item[i];
1238				DFS(innerfst, predicate1, predicate2, callback);
1239			};
1240		} else if (typeof item  ===  'object'){
1241			for( var innerfst in item){
1242				DFS(item[innerfst], predicate1, predicate2, callback);
1243			};
1244		}
1245
1246	}
1247};
1248
1249$n2.keys = function (obj) {
1250	if (!Object.keys){
1251		var arr = [];
1252		for (var key in obj) {
1253			if (obj.hasOwnProperty(key)) {
1254				arr.push(key);
1255			}
1256		}
1257	} else {
1258		arr = Object.keys(obj);
1259	}
1260	return arr;
1261}
1262
1263$n2.utils.convertSMPTEtoSeconds = function (SMPTE) {
1264
1265	if (typeof SMPTE !== 'string') {
1266		throw new TypeError('Argument must be a string value');
1267	}
1268
1269	SMPTE = SMPTE.replace(',', '.');
1270
1271	var decimalLen = ~SMPTE.indexOf('.') ? SMPTE.split('.')[1].length : 0;
1272
1273	var secs = 0,
1274	    multiplier = 1;
1275
1276	SMPTE = SMPTE.split(':').reverse();
1277
1278	for (var i = 0, total = SMPTE.length; i < total; i++) {
1279		multiplier = 1;
1280		if (i > 0) {
1281			multiplier = Math.pow(60, i);
1282		}
1283		secs += Number(SMPTE[i]) * multiplier;
1284	}
1285	return Number(secs.toFixed(decimalLen));
1286};
1287
1288/**
1289 * Usage: processLargeArrayAsync(veryLargeArray, myCallback);
1290 * myCallback defined with args (value, index, array)
1291 */
1292$n2.utils.processLargeArrayAsync = function(array, fn, maxTimePerChunk, context) {
1293	context = context || window;
1294	maxTimePerChunk = maxTimePerChunk || 200;
1295	var index = 0;
1296
1297	function now() {
1298		return new Date().getTime();
1299	}
1300
1301	function doChunk() {
1302		var startTime = now();
1303		while (index < array.length && (now() - startTime) <= maxTimePerChunk) {
1304			// callback called with args (value, index, array)
1305			fn.call(context, array[index], index, array);
1306			++index;
1307		}
1308		if (index < array.length) {
1309			// set Timeout for async iteration
1310			setTimeout(doChunk, 1);
1311		}
1312	}
1313	doChunk();
1314}
1315
1316})(nunaliit2);

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.