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 '&': '&' 1104 ,'<': '<' 1105 ,'>': '>' 1106 ,'"': '"' 1107 ,"'": ''' 1108 ,'`': '`' 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.