vendor: 11,242 bytes, lines 1-432
1/*! 2 * jQuery JavaScript Library v1.10.2 3 * http://jquery.com/ 4 * 5 * Includes Sizzle.js 6 * http://sizzlejs.com/ 7 * 8 * Copyright 2005, 2013 jQuery Foundation, Inc. and other contributors 9 * Released under the MIT license 10 * http://jquery.org/license 11 * 12 * Date: 2013-07-03T13:48Z 13 */ 14 15(function( window, undefined ) { 16 17// Can't do this because several apps including ASP.NET trace 18// the stack via arguments.caller.callee and Firefox dies if 19// you try to trace through "use strict" call chains. (#13335) 20// Support: Firefox 18+ 21//"use strict"; 22var 23 // The deferred used on DOM ready 24 readyList, 25 26 // A central reference to the root jQuery(document) 27 rootjQuery, 28 29 // Support: IE<10 30 // For `typeof xmlNode.method` instead of `xmlNode.method !== undefined` 31 core_strundefined = typeof undefined, 32 33 // Use the correct document accordingly with window argument (sandbox) 34 location = window.location, 35 document = window.document, 36 docElem = document.documentElement, 37 38 // Map over jQuery in case of overwrite 39 _jQuery = window.jQuery, 40 41 // Map over the $ in case of overwrite 42 _$ = window.$, 43 44 // [[Class]] -> type pairs 45 class2type = {}, 46 47 // List of deleted data cache ids, so we can reuse them 48 core_deletedIds = [], 49 50 core_version = "1.10.2", 51 52 // Save a reference to some core methods 53 core_concat = core_deletedIds.concat, 54 core_push = core_deletedIds.push, 55 core_slice = core_deletedIds.slice, 56 core_indexOf = core_deletedIds.indexOf, 57 core_toString = class2type.toString, 58 core_hasOwn = class2type.hasOwnProperty, 59 core_trim = core_version.trim, 60 61 // Define a local copy of jQuery 62 jQuery = function( selector, context ) { 63 // The jQuery object is actually just the init constructor 'enhanced' 64 return new jQuery.fn.init( selector, context, rootjQuery ); 65 }, 66 67 // Used for matching numbers 68 core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source, 69 70 // Used for splitting on whitespace 71 core_rnotwhite = /\S+/g, 72 73 // Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE) 74 rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, 75 76 // A simple way to check for HTML strings 77 // Prioritize #id over <tag> to avoid XSS via location.hash (#9521) 78 // Strict HTML recognition (#11290: must start with <) 79 rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, 80 81 // Match a standalone tag 82 rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, 83 84 // JSON RegExp 85 rvalidchars = /^[\],:{}\s]*$/, 86 rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, 87 rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g, 88 rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g, 89 90 // Matches dashed string for camelizing 91 rmsPrefix = /^-ms-/, 92 rdashAlpha = /-([\da-z])/gi, 93 94 // Used by jQuery.camelCase as callback to replace() 95 fcamelCase = function( all, letter ) { 96 return letter.toUpperCase(); 97 }, 98 99 // The ready event handler 100 completed = function( event ) { 101 102 // readyState === "complete" is good enough for us to call the dom ready in oldIE 103 if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) { 104 detach(); 105 jQuery.ready(); 106 } 107 }, 108 // Clean-up method for dom ready events 109 detach = function() { 110 if ( document.addEventListener ) { 111 document.removeEventListener( "DOMContentLoaded", completed, false ); 112 window.removeEventListener( "load", completed, false ); 113 114 } else { 115 document.detachEvent( "onreadystatechange", completed ); 116 window.detachEvent( "onload", completed ); 117 } 118 }; 119 120jQuery.fn = jQuery.prototype = { 121 // The current version of jQuery being used 122 jquery: core_version, 123 124 constructor: jQuery, 125 init: function( selector, context, rootjQuery ) { 126 var match, elem; 127 128 // HANDLE: $(""), $(null), $(undefined), $(false) 129 if ( !selector ) { 130 return this; 131 } 132 133 // Handle HTML strings 134 if ( typeof selector === "string" ) { 135 if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { 136 // Assume that strings that start and end with <> are HTML and skip the regex check 137 match = [ null, selector, null ]; 138 139 } else { 140 match = rquickExpr.exec( selector ); 141 } 142 143 // Match html or make sure no context is specified for #id 144 if ( match && (match[1] || !context) ) { 145 146 // HANDLE: $(html) -> $(array) 147 if ( match[1] ) { 148 context = context instanceof jQuery ? context[0] : context; 149 150 // scripts is true for back-compat 151 jQuery.merge( this, jQuery.parseHTML( 152 match[1], 153 context && context.nodeType ? context.ownerDocument || context : document, 154 true 155 ) ); 156 157 // HANDLE: $(html, props) 158 if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { 159 for ( match in context ) { 160 // Properties of context are called as methods if possible 161 if ( jQuery.isFunction( this[ match ] ) ) { 162 this[ match ]( context[ match ] ); 163 164 // ...and otherwise set as attributes 165 } else { 166 this.attr( match, context[ match ] ); 167 } 168 } 169 } 170 171 return this; 172 173 // HANDLE: $(#id) 174 } else { 175 elem = document.getElementById( match[2] ); 176 177 // Check parentNode to catch when Blackberry 4.6 returns 178 // nodes that are no longer in the document #6963 179 if ( elem && elem.parentNode ) { 180 // Handle the case where IE and Opera return items 181 // by name instead of ID 182 if ( elem.id !== match[2] ) { 183 return rootjQuery.find( selector ); 184 } 185 186 // Otherwise, we inject the element directly into the jQuery object 187 this.length = 1; 188 this[0] = elem; 189 } 190 191 this.context = document; 192 this.selector = selector; 193 return this; 194 } 195 196 // HANDLE: $(expr, $(...)) 197 } else if ( !context || context.jquery ) { 198 return ( context || rootjQuery ).find( selector ); 199 200 // HANDLE: $(expr, context) 201 // (which is just equivalent to: $(context).find(expr) 202 } else { 203 return this.constructor( context ).find( selector ); 204 } 205 206 // HANDLE: $(DOMElement) 207 } else if ( selector.nodeType ) { 208 this.context = this[0] = selector; 209 this.length = 1; 210 return this; 211 212 // HANDLE: $(function) 213 // Shortcut for document ready 214 } else if ( jQuery.isFunction( selector ) ) { 215 return rootjQuery.ready( selector ); 216 } 217 218 if ( selector.selector !== undefined ) { 219 this.selector = selector.selector; 220 this.context = selector.context; 221 } 222 223 return jQuery.makeArray( selector, this ); 224 }, 225 226 // Start with an empty selector 227 selector: "", 228 229 // The default length of a jQuery object is 0 230 length: 0, 231 232 toArray: function() { 233 return core_slice.call( this ); 234 }, 235 236 // Get the Nth element in the matched element set OR 237 // Get the whole matched element set as a clean array 238 get: function( num ) { 239 return num == null ? 240 241 // Return a 'clean' array 242 this.toArray() : 243 244 // Return just the object 245 ( num < 0 ? this[ this.length + num ] : this[ num ] ); 246 }, 247 248 // Take an array of elements and push it onto the stack 249 // (returning the new matched element set) 250 pushStack: function( elems ) { 251 252 // Build a new jQuery matched element set 253 var ret = jQuery.merge( this.constructor(), elems ); 254 255 // Add the old object onto the stack (as a reference) 256 ret.prevObject = this; 257 ret.context = this.context; 258 259 // Return the newly-formed element set 260 return ret; 261 }, 262 263 // Execute a callback for every element in the matched set. 264 // (You can seed the arguments with an array of args, but this is 265 // only used internally.) 266 each: function( callback, args ) { 267 return jQuery.each( this, callback, args ); 268 }, 269 270 ready: function( fn ) { 271 // Add the callback 272 jQuery.ready.promise().done( fn ); 273 274 return this; 275 }, 276 277 slice: function() { 278 return this.pushStack( core_slice.apply( this, arguments ) ); 279 }, 280 281 first: function() { 282 return this.eq( 0 ); 283 }, 284 285 last: function() { 286 return this.eq( -1 ); 287 }, 288 289 eq: function( i ) { 290 var len = this.length, 291 j = +i + ( i < 0 ? len : 0 ); 292 return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] ); 293 }, 294 295 map: function( callback ) { 296 return this.pushStack( jQuery.map(this, function( elem, i ) { 297 return callback.call( elem, i, elem ); 298 })); 299 }, 300 301 end: function() { 302 return this.prevObject || this.constructor(null); 303 }, 304 305 // For internal use only. 306 // Behaves like an Array's method, not like a jQuery method. 307 push: core_push, 308 sort: [].sort, 309 splice: [].splice 310}; 311 312// Give the init function the jQuery prototype for later instantiation 313jQuery.fn.init.prototype = jQuery.fn; 314 315jQuery.extend = jQuery.fn.extend = function() { 316 var src, copyIsArray, copy, name, options, clone, 317 target = arguments[0] || {}, 318 i = 1, 319 length = arguments.length, 320 deep = false; 321 322 // Handle a deep copy situation 323 if ( typeof target === "boolean" ) { 324 deep = target; 325 target = arguments[1] || {}; 326 // skip the boolean and the target 327 i = 2; 328 } 329 330 // Handle case when target is a string or something (possible in deep copy) 331 if ( typeof target !== "object" && !jQuery.isFunction(target) ) { 332 target = {}; 333 } 334 335 // extend jQuery itself if only one argument is passed 336 if ( length === i ) { 337 target = this; 338 --i; 339 } 340 341 for ( ; i < length; i++ ) { 342 // Only deal with non-null/undefined values 343 if ( (options = arguments[ i ]) != null ) { 344 // Extend the base object 345 for ( name in options ) { 346 src = target[ name ]; 347 copy = options[ name ]; 348 349 // Prevent never-ending loop 350 if ( target === copy ) { 351 continue; 352 } 353 354 // Recurse if we're merging plain objects or arrays 355 if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { 356 if ( copyIsArray ) { 357 copyIsArray = false; 358 clone = src && jQuery.isArray(src) ? src : []; 359 360 } else { 361 clone = src && jQuery.isPlainObject(src) ? src : {}; 362 } 363 364 // Never move original objects, clone them 365 target[ name ] = jQuery.extend( deep, clone, copy ); 366 367 // Don't bring in undefined values 368 } else if ( copy !== undefined ) { 369 target[ name ] = copy; 370 } 371 } 372 } 373 } 374 375 // Return the modified object 376 return target; 377}; 378 379jQuery.extend({ 380 // Unique for each copy of jQuery on the page 381 // Non-digits removed to match rinlinejQuery 382 expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" ), 383 384 noConflict: function( deep ) { 385 if ( window.$ === jQuery ) { 386 window.$ = _$; 387 } 388 389 if ( deep && window.jQuery === jQuery ) { 390 window.jQuery = _jQuery; 391 } 392 393 return jQuery; 394 }, 395 396 // Is the DOM ready to be used? Set to true once it occurs. 397 isReady: false, 398 399 // A counter to track how many items to wait for before 400 // the ready event fires. See #6781 401 readyWait: 1, 402 403 // Hold (or release) the ready event 404 holdReady: function( hold ) { 405 if ( hold ) { 406 jQuery.readyWait++; 407 } else { 408 jQuery.ready( true ); 409 } 410 }, 411 412 // Handle when the DOM is ready 413 ready: function( wait ) { 414 415 // Abort if there are pending holds or we're already ready 416 if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { 417 return; 418 } 419 420 // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). 421 if ( !document.body ) { 422 return setTimeout( jQuery.ready ); 423 } 424 425 // Remember that the DOM is ready 426 jQuery.isReady = true; 427 428 // If a normal DOM Ready event fired, decrement, and wait if need be 429 if ( wait !== true && --jQuery.readyWait > 0 ) { 430 return; 431 } 432
433 // If there are functions bound, to execute 434 readyList.resolveWith( document, [ jQuery ] ); 435 436 // Trigger any bound ready events 437 if ( jQuery.fn.trigger ) { 438 jQuery( document ).trigger("ready").off("ready"); 439 } 440 }, 441 442 // See test/unit/core.js for details concerning isFunction. 443 // Since version 1.3, DOM methods and functions like alert 444 // aren't supported. They return false on IE (#2968). 445 isFunction: function( obj ) { 446 return jQuery.type(obj) === "function"; 447 }, 448 449 isArray: Array.isArray || function( obj ) { 450 return jQuery.type(obj) === "array"; 451 }, 452 453 isWindow: function( obj ) { 454 /* jshint eqeqeq: false */ 455 return obj != null && obj == obj.window; 456 }, 457 458 isNumeric: function( obj ) { 459 return !isNaN( parseFloat(obj) ) && isFinite( obj ); 460 }, 461 462 type: function( obj ) { 463 if ( obj == null ) { 464 return String( obj ); 465 } 466 return typeof obj === "object" || typeof obj === "function" ? 467 class2type[ core_toString.call(obj) ] || "object" : 468 typeof obj; 469 }, 470 471 isPlainObject: function( obj ) { 472 var key; 473 474 // Must be an Object. 475 // Because of IE, we also have to check the presence of the constructor property. 476 // Make sure that DOM nodes and window objects don't pass through, as well 477 if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { 478 return false; 479 } 480 481 try { 482 // Not own constructor property must be Object 483 if ( obj.constructor && 484 !core_hasOwn.call(obj, "constructor") && 485 !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { 486 return false; 487 } 488 } catch ( e ) { 489 // IE8,9 Will throw exceptions on certain host objects #9897 490 return false; 491 } 492 493 // Support: IE<9 494 // Handle iteration over inherited properties before own properties. 495 if ( jQuery.support.ownLast ) { 496 for ( key in obj ) { 497 return core_hasOwn.call( obj, key ); 498 } 499 } 500 501 // Own properties are enumerated firstly, so to speed up, 502 // if last one is own, then all properties are own. 503 for ( key in obj ) {} 504 505 return key === undefined || core_hasOwn.call( obj, key ); 506 }, 507 508 isEmptyObject: function( obj ) { 509 var name; 510 for ( name in obj ) { 511 return false; 512 } 513 return true; 514 }, 515 516 error: function( msg ) { 517 throw new Error( msg ); 518 }, 519 520 // data: string of html 521 // context (optional): If specified, the fragment will be created in this context, defaults to document 522 // keepScripts (optional): If true, will include scripts passed in the html string 523 parseHTML: function( data, context, keepScripts ) { 524 if ( !data || typeof data !== "string" ) { 525 return null; 526 } 527 if ( typeof context === "boolean" ) { 528 keepScripts = context; 529 context = false; 530 } 531 context = context || document; 532 533 var parsed = rsingleTag.exec( data ), 534 scripts = !keepScripts && []; 535 536 // Single tag 537 if ( parsed ) { 538 return [ context.createElement( parsed[1] ) ]; 539 } 540 541 parsed = jQuery.buildFragment( [ data ], context, scripts ); 542 if ( scripts ) { 543 jQuery( scripts ).remove(); 544 } 545 return jQuery.merge( [], parsed.childNodes ); 546 }, 547 548 parseJSON: function( data ) { 549 // Attempt to parse using the native JSON parser first 550 if ( window.JSON && window.JSON.parse ) { 551 return window.JSON.parse( data ); 552 } 553 554 if ( data === null ) { 555 return data; 556 } 557 558 if ( typeof data === "string" ) { 559 560 // Make sure leading/trailing whitespace is removed (IE can't handle it) 561 data = jQuery.trim( data ); 562 563 if ( data ) { 564 // Make sure the incoming data is actual JSON 565 // Logic borrowed from http://json.org/json2.js 566 if ( rvalidchars.test( data.replace( rvalidescape, "@" ) 567 .replace( rvalidtokens, "]" ) 568 .replace( rvalidbraces, "")) ) { 569 570 return ( new Function( "return " + data ) )(); 571 } 572 } 573 } 574 575 jQuery.error( "Invalid JSON: " + data ); 576 }, 577 578 // Cross-browser xml parsing 579 parseXML: function( data ) { 580 var xml, tmp; 581 if ( !data || typeof data !== "string" ) { 582 return null; 583 } 584 try { 585 if ( window.DOMParser ) { // Standard 586 tmp = new DOMParser(); 587 xml = tmp.parseFromString( data , "text/xml" ); 588 } else { // IE 589 xml = new ActiveXObject( "Microsoft.XMLDOM" ); 590 xml.async = "false"; 591 xml.loadXML( data ); 592 } 593 } catch( e ) { 594 xml = undefined; 595 } 596 if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { 597 jQuery.error( "Invalid XML: " + data ); 598 } 599 return xml; 600 }, 601 602 noop: function() {}, 603 604 // Evaluates a script in a global context 605 // Workarounds based on findings by Jim Driscoll 606 // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context 607 globalEval: function( data ) { 608 if ( data && jQuery.trim( data ) ) { 609 // We use execScript on Internet Explorer 610 // We use an anonymous function so that context is window 611 // rather than jQuery in Firefox 612 ( window.execScript || function( data ) { 613 window[ "eval" ].call( window, data ); 614 } )( data ); 615 } 616 }, 617 618 // Convert dashed to camelCase; used by the css and data modules 619 // Microsoft forgot to hump their vendor prefix (#9572) 620 camelCase: function( string ) { 621 return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); 622 }, 623
624 nodeName: function( elem, name ) { 625 return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); 626 }, 627 628 // args is for internal usage only 629 each: function( obj, callback, args ) { 630 var value, 631 i = 0, 632 length = obj.length, 633 isArray = isArraylike( obj ); 634 635 if ( args ) { 636 if ( isArray ) { 637 for ( ; i < length; i++ ) { 638 value = callback.apply( obj[ i ], args ); 639 640 if ( value === false ) { 641 break; 642 } 643 } 644 } else { 645 for ( i in obj ) { 646 value = callback.apply( obj[ i ], args ); 647 648 if ( value === false ) { 649 break; 650 } 651 } 652 } 653 654 // A special, fast, case for the most common use of each 655 } else { 656 if ( isArray ) { 657 for ( ; i < length; i++ ) { 658 value = callback.call( obj[ i ], i, obj[ i ] ); 659 660 if ( value === false ) { 661 break; 662 } 663 } 664 } else { 665 for ( i in obj ) { 666 value = callback.call( obj[ i ], i, obj[ i ] ); 667 668 if ( value === false ) { 669 break; 670 } 671 } 672 } 673 } 674 675 return obj; 676 }, 677 678 // Use native String.trim function wherever possible 679 trim: core_trim && !core_trim.call("\uFEFF\xA0") ? 680 function( text ) { 681 return text == null ? 682 "" : 683 core_trim.call( text ); 684 } : 685 686 // Otherwise use our own trimming functionality 687 function( text ) { 688 return text == null ? 689 "" : 690 ( text + "" ).replace( rtrim, "" ); 691 }, 692 693 // results is for internal usage only 694 makeArray: function( arr, results ) { 695 var ret = results || []; 696 697 if ( arr != null ) { 698 if ( isArraylike( Object(arr) ) ) { 699 jQuery.merge( ret, 700 typeof arr === "string" ? 701 [ arr ] : arr 702 ); 703 } else { 704 core_push.call( ret, arr ); 705 } 706 } 707 708 return ret; 709 }, 710 711 inArray: function( elem, arr, i ) { 712 var len; 713 714 if ( arr ) { 715 if ( core_indexOf ) { 716 return core_indexOf.call( arr, elem, i ); 717 } 718 719 len = arr.length; 720 i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; 721 722 for ( ; i < len; i++ ) { 723 // Skip accessing in sparse arrays 724 if ( i in arr && arr[ i ] === elem ) { 725 return i; 726 } 727 } 728 } 729 730 return -1; 731 }, 732 733 merge: function( first, second ) { 734 var l = second.length, 735 i = first.length, 736 j = 0; 737 738 if ( typeof l === "number" ) { 739 for ( ; j < l; j++ ) { 740 first[ i++ ] = second[ j ]; 741 } 742 } else { 743 while ( second[j] !== undefined ) { 744 first[ i++ ] = second[ j++ ]; 745 } 746 } 747 748 first.length = i; 749 750 return first; 751 }, 752 753 grep: function( elems, callback, inv ) { 754 var retVal, 755 ret = [], 756 i = 0, 757 length = elems.length; 758 inv = !!inv; 759 760 // Go through the array, only saving the items 761 // that pass the validator function 762 for ( ; i < length; i++ ) { 763 retVal = !!callback( elems[ i ], i ); 764 if ( inv !== retVal ) { 765 ret.push( elems[ i ] ); 766 } 767 } 768 769 return ret; 770 }, 771 772 // arg is for internal usage only 773 map: function( elems, callback, arg ) { 774 var value, 775 i = 0, 776 length = elems.length, 777 isArray = isArraylike( elems ), 778 ret = []; 779 780 // Go through the array, translating each of the items to their 781 if ( isArray ) { 782 for ( ; i < length; i++ ) { 783 value = callback( elems[ i ], i, arg ); 784 785 if ( value != null ) { 786 ret[ ret.length ] = value; 787 } 788 } 789 790 // Go through every key on the object, 791 } else { 792 for ( i in elems ) { 793 value = callback( elems[ i ], i, arg ); 794 795 if ( value != null ) { 796 ret[ ret.length ] = value; 797 } 798 } 799 } 800 801 // Flatten any nested arrays 802 return core_concat.apply( [], ret ); 803 }, 804 805 // A global GUID counter for objects 806 guid: 1, 807 808 // Bind a function to a context, optionally partially applying any 809 // arguments. 810 proxy: function( fn, context ) { 811 var args, proxy, tmp; 812 813 if ( typeof context === "string" ) { 814 tmp = fn[ context ]; 815 context = fn; 816 fn = tmp; 817 } 818 819 // Quick check to determine if target is callable, in the spec 820 // this throws a TypeError, but we will just return undefined. 821 if ( !jQuery.isFunction( fn ) ) { 822 return undefined; 823 } 824 825 // Simulated bind 826 args = core_slice.call( arguments, 2 ); 827 proxy = function() { 828 return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) ); 829 }; 830 831 // Set the guid of unique handler to the same of original handler, so it can be removed 832 proxy.guid = fn.guid = fn.guid || jQuery.guid++; 833 834 return proxy; 835 }, 836 837 // Multifunctional method to get and set values of a collection 838 // The value/s can optionally be executed if it's a function 839 access: function( elems, fn, key, value, chainable, emptyGet, raw ) { 840 var i = 0, 841 length = elems.length, 842 bulk = key == null; 843 844 // Sets many values 845 if ( jQuery.type( key ) === "object" ) { 846 chainable = true; 847 for ( i in key ) { 848 jQuery.access( elems, fn, i, key[i], true, emptyGet, raw ); 849 } 850 851 // Sets one value 852 } else if ( value !== undefined ) { 853 chainable = true; 854 855 if ( !jQuery.isFunction( value ) ) { 856 raw = true; 857 } 858 859 if ( bulk ) { 860 // Bulk operations run against the entire set 861 if ( raw ) { 862 fn.call( elems, value ); 863 fn = null; 864 865 // ...except when executing function values 866 } else { 867 bulk = fn; 868 fn = function( elem, key, value ) {
869 return bulk.call( jQuery( elem ), value ); 870 }; 871 } 872 } 873 874 if ( fn ) { 875 for ( ; i < length; i++ ) { 876 fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); 877 } 878 } 879 } 880 881 return chainable ? 882 elems : 883 884 // Gets 885 bulk ? 886 fn.call( elems ) : 887 length ? fn( elems[0], key ) : emptyGet; 888 }, 889 890 now: function() { 891 return ( new Date() ).getTime(); 892 }, 893 894 // A method for quickly swapping in/out CSS properties to get correct calculations. 895 // Note: this method belongs to the css module but it's needed here for the support module. 896 // If support gets modularized, this method should be moved back to the css module. 897 swap: function( elem, options, callback, args ) { 898 var ret, name, 899 old = {}; 900 901 // Remember the old values, and insert the new ones 902 for ( name in options ) { 903 old[ name ] = elem.style[ name ]; 904 elem.style[ name ] = options[ name ]; 905 } 906 907 ret = callback.apply( elem, args || [] ); 908 909 // Revert the old values 910 for ( name in options ) { 911 elem.style[ name ] = old[ name ]; 912 } 913 914 return ret; 915 } 916}); 917 918jQuery.ready.promise = function( obj ) { 919 if ( !readyList ) { 920 921 readyList = jQuery.Deferred(); 922 923 // Catch cases where $(document).ready() is called after the browser event has already occurred. 924 // we once tried to use readyState "interactive" here, but it caused issues like the one 925 // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 926 if ( document.readyState === "complete" ) { 927 // Handle it asynchronously to allow scripts the opportunity to delay ready 928 setTimeout( jQuery.ready ); 929 930 // Standards-based browsers support DOMContentLoaded 931 } else if ( document.addEventListener ) { 932 // Use the handy event callback 933 document.addEventListener( "DOMContentLoaded", completed, false ); 934 935 // A fallback to window.onload, that will always work 936 window.addEventListener( "load", completed, false ); 937 938 // If IE event model is used 939 } else { 940 // Ensure firing before onload, maybe late but safe also for iframes 941 document.attachEvent( "onreadystatechange", completed ); 942 943 // A fallback to window.onload, that will always work 944 window.attachEvent( "onload", completed ); 945 946 // If IE and not a frame 947 // continually check to see if the document is ready 948 var top = false; 949 950 try { 951 top = window.frameElement == null && document.documentElement; 952 } catch(e) {} 953 954 if ( top && top.doScroll ) { 955 (function doScrollCheck() { 956 if ( !jQuery.isReady ) { 957 958 try { 959 // Use the trick by Diego Perini 960 // http://javascript.nwbox.com/IEContentLoaded/ 961 top.doScroll("left"); 962 } catch(e) { 963 return setTimeout( doScrollCheck, 50 ); 964 } 965 966 // detach all dom ready events 967 detach(); 968 969 // and execute any waiting functions 970 jQuery.ready(); 971 } 972 })(); 973 } 974 } 975 } 976 return readyList.promise( obj ); 977}; 978 979// Populate the class2type map 980jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { 981 class2type[ "[object " + name + "]" ] = name.toLowerCase(); 982}); 983 984function isArraylike( obj ) { 985 var length = obj.length, 986 type = jQuery.type( obj ); 987 988 if ( jQuery.isWindow( obj ) ) { 989 return false; 990 } 991 992 if ( obj.nodeType === 1 && length ) { 993 return true; 994 } 995 996 return type === "array" || type !== "function" && 997 ( length === 0 || 998 typeof length === "number" && length > 0 && ( length - 1 ) in obj ); 999} 1000 1001// All jQuery objects should point back to these 1002rootjQuery = jQuery(document); 1003/*! 1004 * Sizzle CSS Selector Engine v1.10.2 1005 * http://sizzlejs.com/ 1006 * 1007 * Copyright 2013 jQuery Foundation, Inc. and other contributors 1008 * Released under the MIT license 1009 * http://jquery.org/license 1010 * 1011 * Date: 2013-07-03 1012 */ 1013(function( window, undefined ) { 1014 1015var i, 1016 support, 1017 cachedruns, 1018 Expr, 1019 getText, 1020 isXML, 1021 compile, 1022 outermostContext, 1023 sortInput, 1024 1025 // Local document vars 1026 setDocument, 1027 document, 1028 docElem, 1029 documentIsHTML, 1030 rbuggyQSA, 1031 rbuggyMatches, 1032 matches, 1033 contains, 1034 1035 // Instance-specific data 1036 expando = "sizzle" + -(new Date()), 1037 preferredDoc = window.document, 1038 dirruns = 0, 1039 done = 0, 1040 classCache = createCache(), 1041 tokenCache = createCache(), 1042 compilerCache = createCache(), 1043 hasDuplicate = false, 1044 sortOrder = function( a, b ) { 1045 if ( a === b ) { 1046 hasDuplicate = true; 1047 return 0; 1048 } 1049 return 0; 1050 }, 1051 1052 // General-purpose constants 1053 strundefined = typeof undefined, 1054 MAX_NEGATIVE = 1 << 31, 1055 1056 // Instance methods 1057 hasOwn = ({}).hasOwnProperty, 1058 arr = [], 1059 pop = arr.pop, 1060 push_native = arr.push, 1061 push = arr.push, 1062 slice = arr.slice, 1063 // Use a stripped-down indexOf if we can't use a native one 1064 indexOf = arr.indexOf || function( elem ) { 1065 var i = 0, 1066 len = this.length; 1067 for ( ; i < len; i++ ) { 1068 if ( this[i] === elem ) { 1069 return i; 1070 } 1071 } 1072 return -1; 1073 }, 1074 1075 booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", 1076 1077 // Regular expressions 1078 1079 // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace 1080 whitespace = "[\\x20\\t\\r\\n\\f]", 1081 // http://www.w3.org/TR/css3-syntax/#characters 1082 characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", 1083 1084 // Loosely modeled on CSS identifier characters 1085 // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors 1086 // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier 1087 identifier = characterEncoding.replace( "w", "w#" ), 1088 1089 // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors 1090 attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + 1091 "*(?:([*^$|!~]?=)" + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", 1092 1093 // Prefer arguments quoted, 1094 // then not containing pseudos/brackets, 1095 // then attribute selectors/non-parenthetical expressions, 1096 // then anything else 1097 // These preferences are here to reduce the number of selectors 1098 // needing tokenize in the PSEUDO preFilter 1099 pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)", 1100 1101 // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter 1102 rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), 1103 1104 rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), 1105 rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), 1106 1107 rsibling = new RegExp( whitespace + "*[+~]" ), 1108 rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*)" + whitespace + "*\\]", "g" ), 1109 1110 rpseudo = new RegExp( pseudos ), 1111 ridentifier = new RegExp( "^" + identifier + "$" ), 1112 1113 matchExpr = { 1114 "ID": new RegExp( "^#(" + characterEncoding + ")" ), 1115 "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), 1116 "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), 1117 "ATTR": new RegExp( "^" + attributes ), 1118 "PSEUDO": new RegExp( "^" + pseudos ), 1119 "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + 1120 "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + 1121 "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), 1122 "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), 1123 // For use in libraries implementing .is() 1124 // We use this for POS matching in `select` 1125 "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + 1126 whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) 1127 }, 1128 1129 rnative = /^[^{]+\{\s*\[native \w/, 1130
vendor: 21,121 bytes, lines 1131-1895
1131 // Easily-parseable/retrievable ID or TAG or CLASS selectors 1132 rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, 1133 1134 rinputs = /^(?:input|select|textarea|button)$/i, 1135 rheader = /^h\d$/i, 1136 1137 rescape = /'|\\/g, 1138 1139 // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters 1140 runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), 1141 funescape = function( _, escaped, escapedWhitespace ) { 1142 var high = "0x" + escaped - 0x10000; 1143 // NaN means non-codepoint 1144 // Support: Firefox 1145 // Workaround erroneous numeric interpretation of +"0x" 1146 return high !== high || escapedWhitespace ? 1147 escaped : 1148 // BMP codepoint 1149 high < 0 ? 1150 String.fromCharCode( high + 0x10000 ) : 1151 // Supplemental Plane codepoint (surrogate pair) 1152 String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); 1153 }; 1154 1155// Optimize for push.apply( _, NodeList ) 1156try { 1157 push.apply( 1158 (arr = slice.call( preferredDoc.childNodes )), 1159 preferredDoc.childNodes 1160 ); 1161 // Support: Android<4.0 1162 // Detect silently failing push.apply 1163 arr[ preferredDoc.childNodes.length ].nodeType; 1164} catch ( e ) { 1165 push = { apply: arr.length ? 1166 1167 // Leverage slice if possible 1168 function( target, els ) { 1169 push_native.apply( target, slice.call(els) ); 1170 } : 1171 1172 // Support: IE<9 1173 // Otherwise append directly 1174 function( target, els ) { 1175 var j = target.length, 1176 i = 0; 1177 // Can't trust NodeList.length 1178 while ( (target[j++] = els[i++]) ) {} 1179 target.length = j - 1; 1180 } 1181 }; 1182} 1183 1184function Sizzle( selector, context, results, seed ) { 1185 var match, elem, m, nodeType, 1186 // QSA vars 1187 i, groups, old, nid, newContext, newSelector; 1188 1189 if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { 1190 setDocument( context ); 1191 } 1192 1193 context = context || document; 1194 results = results || []; 1195 1196 if ( !selector || typeof selector !== "string" ) { 1197 return results; 1198 } 1199 1200 if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) { 1201 return []; 1202 } 1203 1204 if ( documentIsHTML && !seed ) { 1205 1206 // Shortcuts 1207 if ( (match = rquickExpr.exec( selector )) ) { 1208 // Speed-up: Sizzle("#ID") 1209 if ( (m = match[1]) ) { 1210 if ( nodeType === 9 ) { 1211 elem = context.getElementById( m ); 1212 // Check parentNode to catch when Blackberry 4.6 returns 1213 // nodes that are no longer in the document #6963 1214 if ( elem && elem.parentNode ) { 1215 // Handle the case where IE, Opera, and Webkit return items 1216 // by name instead of ID 1217 if ( elem.id === m ) { 1218 results.push( elem ); 1219 return results; 1220 } 1221 } else { 1222 return results; 1223 } 1224 } else { 1225 // Context is not a document 1226 if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && 1227 contains( context, elem ) && elem.id === m ) { 1228 results.push( elem ); 1229 return results; 1230 } 1231 } 1232 1233 // Speed-up: Sizzle("TAG") 1234 } else if ( match[2] ) { 1235 push.apply( results, context.getElementsByTagName( selector ) ); 1236 return results; 1237 1238 // Speed-up: Sizzle(".CLASS") 1239 } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) { 1240 push.apply( results, context.getElementsByClassName( m ) ); 1241 return results; 1242 } 1243 } 1244 1245 // QSA path 1246 if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { 1247 nid = old = expando; 1248 newContext = context; 1249 newSelector = nodeType === 9 && selector; 1250 1251 // qSA works strangely on Element-rooted queries 1252 // We can work around this by specifying an extra ID on the root 1253 // and working up from there (Thanks to Andrew Dupont for the technique) 1254 // IE 8 doesn't work on object elements 1255 if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { 1256 groups = tokenize( selector ); 1257 1258 if ( (old = context.getAttribute("id")) ) { 1259 nid = old.replace( rescape, "\\$&" ); 1260 } else { 1261 context.setAttribute( "id", nid ); 1262 } 1263 nid = "[id='" + nid + "'] "; 1264 1265 i = groups.length; 1266 while ( i-- ) { 1267 groups[i] = nid + toSelector( groups[i] ); 1268 } 1269 newContext = rsibling.test( selector ) && context.parentNode || context; 1270 newSelector = groups.join(","); 1271 } 1272 1273 if ( newSelector ) { 1274 try { 1275 push.apply( results, 1276 newContext.querySelectorAll( newSelector ) 1277 ); 1278 return results; 1279 } catch(qsaError) { 1280 } finally { 1281 if ( !old ) { 1282 context.removeAttribute("id"); 1283 } 1284 } 1285 } 1286 } 1287 } 1288 1289 // All others 1290 return select( selector.replace( rtrim, "$1" ), context, results, seed ); 1291} 1292 1293/** 1294 * Create key-value caches of limited size 1295 * @returns {Function(string, Object)} Returns the Object data after storing it on itself with 1296 * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) 1297 * deleting the oldest entry 1298 */ 1299function createCache() { 1300 var keys = []; 1301 1302 function cache( key, value ) { 1303 // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) 1304 if ( keys.push( key += " " ) > Expr.cacheLength ) { 1305 // Only keep the most recent entries 1306 delete cache[ keys.shift() ]; 1307 } 1308 return (cache[ key ] = value); 1309 } 1310 return cache; 1311} 1312 1313/** 1314 * Mark a function for special use by Sizzle 1315 * @param {Function} fn The function to mark 1316 */ 1317function markFunction( fn ) { 1318 fn[ expando ] = true; 1319 return fn; 1320} 1321 1322/** 1323 * Support testing using an element 1324 * @param {Function} fn Passed the created div and expects a boolean result 1325 */ 1326function assert( fn ) { 1327 var div = document.createElement("div"); 1328 1329 try { 1330 return !!fn( div ); 1331 } catch (e) { 1332 return false; 1333 } finally { 1334 // Remove from its parent by default 1335 if ( div.parentNode ) { 1336 div.parentNode.removeChild( div ); 1337 } 1338 // release memory in IE 1339 div = null; 1340 } 1341} 1342 1343/** 1344 * Adds the same handler for all of the specified attrs 1345 * @param {String} attrs Pipe-separated list of attributes 1346 * @param {Function} handler The method that will be applied 1347 */ 1348function addHandle( attrs, handler ) { 1349 var arr = attrs.split("|"), 1350 i = attrs.length; 1351 1352 while ( i-- ) { 1353 Expr.attrHandle[ arr[i] ] = handler; 1354 } 1355} 1356 1357/** 1358 * Checks document order of two siblings 1359 * @param {Element} a 1360 * @param {Element} b 1361 * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b 1362 */ 1363function siblingCheck( a, b ) { 1364 var cur = b && a, 1365 diff = cur && a.nodeType === 1 && b.nodeType === 1 && 1366 ( ~b.sourceIndex || MAX_NEGATIVE ) - 1367 ( ~a.sourceIndex || MAX_NEGATIVE ); 1368 1369 // Use IE sourceIndex if available on both nodes 1370 if ( diff ) { 1371 return diff; 1372 } 1373 1374 // Check if b follows a 1375 if ( cur ) { 1376 while ( (cur = cur.nextSibling) ) { 1377 if ( cur === b ) { 1378 return -1; 1379 } 1380 } 1381 } 1382 1383 return a ? 1 : -1; 1384} 1385 1386/** 1387 * Returns a function to use in pseudos for input types 1388 * @param {String} type 1389 */ 1390function createInputPseudo( type ) { 1391 return function( elem ) { 1392 var name = elem.nodeName.toLowerCase(); 1393 return name === "input" && elem.type === type; 1394 }; 1395} 1396 1397/** 1398 * Returns a function to use in pseudos for buttons 1399 * @param {String} type 1400 */ 1401function createButtonPseudo( type ) { 1402 return function( elem ) { 1403 var name = elem.nodeName.toLowerCase(); 1404 return (name === "input" || name === "button") && elem.type === type; 1405 }; 1406} 1407 1408/** 1409 * Returns a function to use in pseudos for positionals 1410 * @param {Function} fn 1411 */ 1412function createPositionalPseudo( fn ) { 1413 return markFunction(function( argument ) { 1414 argument = +argument; 1415 return markFunction(function( seed, matches ) { 1416 var j, 1417 matchIndexes = fn( [], seed.length, argument ), 1418 i = matchIndexes.length; 1419 1420 // Match elements found at the specified indexes 1421 while ( i-- ) { 1422 if ( seed[ (j = matchIndexes[i]) ] ) { 1423 seed[j] = !(matches[j] = seed[j]); 1424 } 1425 } 1426 }); 1427 }); 1428} 1429 1430/** 1431 * Detect xml 1432 * @param {Element|Object} elem An element or a document 1433 */ 1434isXML = Sizzle.isXML = function( elem ) { 1435 // documentElement is verified for cases where it doesn't yet exist 1436 // (such as loading iframes in IE - #4833) 1437 var documentElement = elem && (elem.ownerDocument || elem).documentElement; 1438 return documentElement ? documentElement.nodeName !== "HTML" : false; 1439}; 1440 1441// Expose support vars for convenience 1442support = Sizzle.support = {}; 1443 1444/** 1445 * Sets document-related variables once based on the current document 1446 * @param {Element|Object} [doc] An element or document object to use to set the document 1447 * @returns {Object} Returns the current document 1448 */ 1449setDocument = Sizzle.setDocument = function( node ) { 1450 var doc = node ? node.ownerDocument || node : preferredDoc, 1451 parent = doc.defaultView; 1452 1453 // If no document and documentElement is available, return 1454 if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { 1455 return document; 1456 } 1457 1458 // Set our document 1459 document = doc; 1460 docElem = doc.documentElement; 1461 1462 // Support tests 1463 documentIsHTML = !isXML( doc ); 1464 1465 // Support: IE>8 1466 // If iframe document is assigned to "document" variable and if iframe has been reloaded, 1467 // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936 1468 // IE6-8 do not support the defaultView property so parent will be undefined 1469 if ( parent && parent.attachEvent && parent !== parent.top ) { 1470 parent.attachEvent( "onbeforeunload", function() { 1471 setDocument(); 1472 }); 1473 } 1474 1475 /* Attributes 1476 ---------------------------------------------------------------------- */ 1477 1478 // Support: IE<8 1479 // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans) 1480 support.attributes = assert(function( div ) { 1481 div.className = "i"; 1482 return !div.getAttribute("className"); 1483 }); 1484 1485 /* getElement(s)By* 1486 ---------------------------------------------------------------------- */ 1487 1488 // Check if getElementsByTagName("*") returns only elements 1489 support.getElementsByTagName = assert(function( div ) { 1490 div.appendChild( doc.createComment("") ); 1491 return !div.getElementsByTagName("*").length; 1492 }); 1493 1494 // Check if getElementsByClassName can be trusted 1495 support.getElementsByClassName = assert(function( div ) { 1496 div.innerHTML = "<div class='a'></div><div class='a i'></div>"; 1497 1498 // Support: Safari<4 1499 // Catch class over-caching 1500 div.firstChild.className = "i"; 1501 // Support: Opera<10 1502 // Catch gEBCN failure to find non-leading classes 1503 return div.getElementsByClassName("i").length === 2; 1504 }); 1505 1506 // Support: IE<10 1507 // Check if getElementById returns elements by name 1508 // The broken getElementById methods don't pick up programatically-set names, 1509 // so use a roundabout getElementsByName test 1510 support.getById = assert(function( div ) { 1511 docElem.appendChild( div ).id = expando; 1512 return !doc.getElementsByName || !doc.getElementsByName( expando ).length; 1513 }); 1514 1515 // ID find and filter 1516 if ( support.getById ) { 1517 Expr.find["ID"] = function( id, context ) { 1518 if ( typeof context.getElementById !== strundefined && documentIsHTML ) { 1519 var m = context.getElementById( id ); 1520 // Check parentNode to catch when Blackberry 4.6 returns 1521 // nodes that are no longer in the document #6963 1522 return m && m.parentNode ? [m] : []; 1523 } 1524 }; 1525 Expr.filter["ID"] = function( id ) { 1526 var attrId = id.replace( runescape, funescape ); 1527 return function( elem ) { 1528 return elem.getAttribute("id") === attrId; 1529 }; 1530 }; 1531 } else { 1532 // Support: IE6/7 1533 // getElementById is not reliable as a find shortcut 1534 delete Expr.find["ID"]; 1535 1536 Expr.filter["ID"] = function( id ) { 1537 var attrId = id.replace( runescape, funescape ); 1538 return function( elem ) { 1539 var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); 1540 return node && node.value === attrId; 1541 }; 1542 }; 1543 } 1544 1545 // Tag 1546 Expr.find["TAG"] = support.getElementsByTagName ? 1547 function( tag, context ) { 1548 if ( typeof context.getElementsByTagName !== strundefined ) { 1549 return context.getElementsByTagName( tag ); 1550 } 1551 } : 1552 function( tag, context ) { 1553 var elem, 1554 tmp = [], 1555 i = 0, 1556 results = context.getElementsByTagName( tag ); 1557 1558 // Filter out possible comments 1559 if ( tag === "*" ) { 1560 while ( (elem = results[i++]) ) { 1561 if ( elem.nodeType === 1 ) { 1562 tmp.push( elem ); 1563 } 1564 } 1565 1566 return tmp; 1567 } 1568 return results; 1569 }; 1570 1571 // Class 1572 Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { 1573 if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) { 1574 return context.getElementsByClassName( className ); 1575 } 1576 }; 1577 1578 /* QSA/matchesSelector 1579 ---------------------------------------------------------------------- */ 1580 1581 // QSA and matchesSelector support 1582 1583 // matchesSelector(:active) reports false when true (IE9/Opera 11.5) 1584 rbuggyMatches = []; 1585 1586 // qSa(:focus) reports false when true (Chrome 21) 1587 // We allow this because of a bug in IE8/9 that throws an error 1588 // whenever `document.activeElement` is accessed on an iframe 1589 // So, we allow :focus to pass through QSA all the time to avoid the IE error 1590 // See http://bugs.jquery.com/ticket/13378 1591 rbuggyQSA = []; 1592 1593 if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) { 1594 // Build QSA regex 1595 // Regex strategy adopted from Diego Perini 1596 assert(function( div ) { 1597 // Select is set to empty string on purpose 1598 // This is to test IE's treatment of not explicitly 1599 // setting a boolean content attribute, 1600 // since its presence should be enough 1601 // http://bugs.jquery.com/ticket/12359 1602 div.innerHTML = "<select><option selected=''></option></select>"; 1603 1604 // Support: IE8 1605 // Boolean attributes and "value" are not treated correctly 1606 if ( !div.querySelectorAll("[selected]").length ) { 1607 rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); 1608 } 1609 1610 // Webkit/Opera - :checked should return selected option elements 1611 // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked 1612 // IE8 throws error here and will not see later tests 1613 if ( !div.querySelectorAll(":checked").length ) { 1614 rbuggyQSA.push(":checked"); 1615 } 1616 }); 1617 1618 assert(function( div ) { 1619 1620 // Support: Opera 10-12/IE8 1621 // ^= $= *= and empty values 1622 // Should not select anything 1623 // Support: Windows 8 Native Apps 1624 // The type attribute is restricted during .innerHTML assignment 1625 var input = doc.createElement("input"); 1626 input.setAttribute( "type", "hidden" ); 1627 div.appendChild( input ).setAttribute( "t", "" ); 1628 1629 if ( div.querySelectorAll("[t^='']").length ) { 1630 rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); 1631 } 1632 1633 // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) 1634 // IE8 throws error here and will not see later tests 1635 if ( !div.querySelectorAll(":enabled").length ) { 1636 rbuggyQSA.push( ":enabled", ":disabled" ); 1637 } 1638 1639 // Opera 10-11 does not throw on post-comma invalid pseudos 1640 div.querySelectorAll("*,:x"); 1641 rbuggyQSA.push(",.*:"); 1642 }); 1643 } 1644 1645 if ( (support.matchesSelector = rnative.test( (matches = docElem.webkitMatchesSelector || 1646 docElem.mozMatchesSelector || 1647 docElem.oMatchesSelector || 1648 docElem.msMatchesSelector) )) ) { 1649 1650 assert(function( div ) { 1651 // Check to see if it's possible to do matchesSelector 1652 // on a disconnected node (IE 9) 1653 support.disconnectedMatch = matches.call( div, "div" ); 1654 1655 // This should fail with an exception 1656 // Gecko does not error, returns false instead 1657 matches.call( div, "[s!='']:x" ); 1658 rbuggyMatches.push( "!=", pseudos ); 1659 }); 1660 } 1661 1662 rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); 1663 rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); 1664 1665 /* Contains 1666 ---------------------------------------------------------------------- */ 1667 1668 // Element contains another 1669 // Purposefully does not implement inclusive descendent 1670 // As in, an element does not contain itself 1671 contains = rnative.test( docElem.contains ) || docElem.compareDocumentPosition ? 1672 function( a, b ) { 1673 var adown = a.nodeType === 9 ? a.documentElement : a, 1674 bup = b && b.parentNode; 1675 return a === bup || !!( bup && bup.nodeType === 1 && ( 1676 adown.contains ? 1677 adown.contains( bup ) : 1678 a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 1679 )); 1680 } : 1681 function( a, b ) { 1682 if ( b ) { 1683 while ( (b = b.parentNode) ) { 1684 if ( b === a ) { 1685 return true; 1686 } 1687 } 1688 } 1689 return false; 1690 }; 1691 1692 /* Sorting 1693 ---------------------------------------------------------------------- */ 1694 1695 // Document order sorting 1696 sortOrder = docElem.compareDocumentPosition ? 1697 function( a, b ) { 1698 1699 // Flag for duplicate removal 1700 if ( a === b ) { 1701 hasDuplicate = true; 1702 return 0; 1703 } 1704 1705 var compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition( b ); 1706 1707 if ( compare ) { 1708 // Disconnected nodes 1709 if ( compare & 1 || 1710 (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { 1711 1712 // Choose the first element that is related to our preferred document 1713 if ( a === doc || contains(preferredDoc, a) ) { 1714 return -1; 1715 } 1716 if ( b === doc || contains(preferredDoc, b) ) { 1717 return 1; 1718 } 1719 1720 // Maintain original order 1721 return sortInput ? 1722 ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : 1723 0; 1724 } 1725 1726 return compare & 4 ? -1 : 1; 1727 } 1728 1729 // Not directly comparable, sort on existence of method 1730 return a.compareDocumentPosition ? -1 : 1; 1731 } : 1732 function( a, b ) { 1733 var cur, 1734 i = 0, 1735 aup = a.parentNode, 1736 bup = b.parentNode, 1737 ap = [ a ], 1738 bp = [ b ]; 1739 1740 // Exit early if the nodes are identical 1741 if ( a === b ) { 1742 hasDuplicate = true; 1743 return 0; 1744 1745 // Parentless nodes are either documents or disconnected 1746 } else if ( !aup || !bup ) { 1747 return a === doc ? -1 : 1748 b === doc ? 1 : 1749 aup ? -1 : 1750 bup ? 1 : 1751 sortInput ? 1752 ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : 1753 0; 1754 1755 // If the nodes are siblings, we can do a quick check 1756 } else if ( aup === bup ) { 1757 return siblingCheck( a, b ); 1758 } 1759 1760 // Otherwise we need full lists of their ancestors for comparison 1761 cur = a; 1762 while ( (cur = cur.parentNode) ) { 1763 ap.unshift( cur ); 1764 } 1765 cur = b; 1766 while ( (cur = cur.parentNode) ) { 1767 bp.unshift( cur ); 1768 } 1769 1770 // Walk down the tree looking for a discrepancy 1771 while ( ap[i] === bp[i] ) { 1772 i++; 1773 } 1774 1775 return i ? 1776 // Do a sibling check if the nodes have a common ancestor 1777 siblingCheck( ap[i], bp[i] ) : 1778 1779 // Otherwise nodes in our document sort first 1780 ap[i] === preferredDoc ? -1 : 1781 bp[i] === preferredDoc ? 1 : 1782 0; 1783 }; 1784 1785 return doc; 1786}; 1787 1788Sizzle.matches = function( expr, elements ) { 1789 return Sizzle( expr, null, null, elements ); 1790}; 1791 1792Sizzle.matchesSelector = function( elem, expr ) { 1793 // Set document vars if needed 1794 if ( ( elem.ownerDocument || elem ) !== document ) { 1795 setDocument( elem ); 1796 } 1797 1798 // Make sure that attribute selectors are quoted 1799 expr = expr.replace( rattributeQuotes, "='$1']" ); 1800 1801 if ( support.matchesSelector && documentIsHTML && 1802 ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && 1803 ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { 1804 1805 try { 1806 var ret = matches.call( elem, expr ); 1807 1808 // IE 9's matchesSelector returns false on disconnected nodes 1809 if ( ret || support.disconnectedMatch || 1810 // As well, disconnected nodes are said to be in a document 1811 // fragment in IE 9 1812 elem.document && elem.document.nodeType !== 11 ) { 1813 return ret; 1814 } 1815 } catch(e) {} 1816 } 1817 1818 return Sizzle( expr, document, null, [elem] ).length > 0; 1819}; 1820 1821Sizzle.contains = function( context, elem ) { 1822 // Set document vars if needed 1823 if ( ( context.ownerDocument || context ) !== document ) { 1824 setDocument( context ); 1825 } 1826 return contains( context, elem ); 1827}; 1828 1829Sizzle.attr = function( elem, name ) { 1830 // Set document vars if needed 1831 if ( ( elem.ownerDocument || elem ) !== document ) { 1832 setDocument( elem ); 1833 } 1834 1835 var fn = Expr.attrHandle[ name.toLowerCase() ], 1836 // Don't get fooled by Object.prototype properties (jQuery #13807) 1837 val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? 1838 fn( elem, name, !documentIsHTML ) : 1839 undefined; 1840 1841 return val === undefined ? 1842 support.attributes || !documentIsHTML ? 1843 elem.getAttribute( name ) : 1844 (val = elem.getAttributeNode(name)) && val.specified ? 1845 val.value : 1846 null : 1847 val; 1848}; 1849 1850Sizzle.error = function( msg ) { 1851 throw new Error( "Syntax error, unrecognized expression: " + msg ); 1852}; 1853 1854/** 1855 * Document sorting and removing duplicates 1856 * @param {ArrayLike} results 1857 */ 1858Sizzle.uniqueSort = function( results ) { 1859 var elem, 1860 duplicates = [], 1861 j = 0, 1862 i = 0; 1863 1864 // Unless we *know* we can detect duplicates, assume their presence 1865 hasDuplicate = !support.detectDuplicates; 1866 sortInput = !support.sortStable && results.slice( 0 ); 1867 results.sort( sortOrder ); 1868 1869 if ( hasDuplicate ) { 1870 while ( (elem = results[i++]) ) { 1871 if ( elem === results[ i ] ) { 1872 j = duplicates.push( i ); 1873 } 1874 } 1875 while ( j-- ) { 1876 results.splice( duplicates[ j ], 1 ); 1877 } 1878 } 1879 1880 return results; 1881}; 1882 1883/** 1884 * Utility function for retrieving the text value of an array of DOM nodes 1885 * @param {Array|Element} elem 1886 */ 1887getText = Sizzle.getText = function( elem ) { 1888 var node, 1889 ret = "", 1890 i = 0, 1891 nodeType = elem.nodeType; 1892 1893 if ( !nodeType ) { 1894 // If no nodeType, this is expected to be an array 1895 for ( ; (node = elem[i]); i++ ) {
vendor: 2,485 bytes, lines 1896-1994
1896 // Do not traverse comment nodes 1897 ret += getText( node ); 1898 } 1899 } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { 1900 // Use textContent for elements 1901 // innerText usage removed for consistency of new lines (see #11153) 1902 if ( typeof elem.textContent === "string" ) { 1903 return elem.textContent; 1904 } else { 1905 // Traverse its children 1906 for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { 1907 ret += getText( elem ); 1908 } 1909 } 1910 } else if ( nodeType === 3 || nodeType === 4 ) { 1911 return elem.nodeValue; 1912 } 1913 // Do not include comment or processing instruction nodes 1914 1915 return ret; 1916}; 1917 1918Expr = Sizzle.selectors = { 1919 1920 // Can be adjusted by the user 1921 cacheLength: 50, 1922 1923 createPseudo: markFunction, 1924 1925 match: matchExpr, 1926 1927 attrHandle: {}, 1928 1929 find: {}, 1930 1931 relative: { 1932 ">": { dir: "parentNode", first: true }, 1933 " ": { dir: "parentNode" }, 1934 "+": { dir: "previousSibling", first: true }, 1935 "~": { dir: "previousSibling" } 1936 }, 1937 1938 preFilter: { 1939 "ATTR": function( match ) { 1940 match[1] = match[1].replace( runescape, funescape ); 1941 1942 // Move the given value to match[3] whether quoted or unquoted 1943 match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape ); 1944 1945 if ( match[2] === "~=" ) { 1946 match[3] = " " + match[3] + " "; 1947 } 1948 1949 return match.slice( 0, 4 ); 1950 }, 1951 1952 "CHILD": function( match ) { 1953 /* matches from matchExpr["CHILD"] 1954 1 type (only|nth|...) 1955 2 what (child|of-type) 1956 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) 1957 4 xn-component of xn+y argument ([+-]?\d*n|) 1958 5 sign of xn-component 1959 6 x of xn-component 1960 7 sign of y-component 1961 8 y of y-component 1962 */ 1963 match[1] = match[1].toLowerCase(); 1964 1965 if ( match[1].slice( 0, 3 ) === "nth" ) { 1966 // nth-* requires argument 1967 if ( !match[3] ) { 1968 Sizzle.error( match[0] ); 1969 } 1970 1971 // numeric x and y parameters for Expr.filter.CHILD 1972 // remember that false/true cast respectively to 0/1 1973 match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); 1974 match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); 1975 1976 // other types prohibit arguments 1977 } else if ( match[3] ) { 1978 Sizzle.error( match[0] ); 1979 } 1980 1981 return match; 1982 }, 1983 1984 "PSEUDO": function( match ) { 1985 var excess, 1986 unquoted = !match[5] && match[2]; 1987 1988 if ( matchExpr["CHILD"].test( match[0] ) ) { 1989 return null; 1990 } 1991 1992 // Accept quoted arguments as-is 1993 if ( match[3] && match[4] !== undefined ) { 1994 match[2] = match[4]
vendor: 9,637 bytes, lines 1994-2298
1994; 1995 1996 // Strip excess characters from unquoted arguments 1997 } else if ( unquoted && rpseudo.test( unquoted ) && 1998 // Get excess from tokenize (recursively) 1999 (excess = tokenize( unquoted, true )) && 2000 // advance to the next closing parenthesis 2001 (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { 2002 2003 // excess is a negative index 2004 match[0] = match[0].slice( 0, excess ); 2005 match[2] = unquoted.slice( 0, excess ); 2006 } 2007 2008 // Return only captures needed by the pseudo filter method (type and argument) 2009 return match.slice( 0, 3 ); 2010 } 2011 }, 2012 2013 filter: { 2014 2015 "TAG": function( nodeNameSelector ) { 2016 var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); 2017 return nodeNameSelector === "*" ? 2018 function() { return true; } : 2019 function( elem ) { 2020 return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; 2021 }; 2022 }, 2023 2024 "CLASS": function( className ) { 2025 var pattern = classCache[ className + " " ]; 2026 2027 return pattern || 2028 (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && 2029 classCache( className, function( elem ) { 2030 return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" ); 2031 }); 2032 }, 2033 2034 "ATTR": function( name, operator, check ) { 2035 return function( elem ) { 2036 var result = Sizzle.attr( elem, name ); 2037 2038 if ( result == null ) { 2039 return operator === "!="; 2040 } 2041 if ( !operator ) { 2042 return true; 2043 } 2044 2045 result += ""; 2046 2047 return operator === "=" ? result === check : 2048 operator === "!=" ? result !== check : 2049 operator === "^=" ? check && result.indexOf( check ) === 0 : 2050 operator === "*=" ? check && result.indexOf( check ) > -1 : 2051 operator === "$=" ? check && result.slice( -check.length ) === check : 2052 operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : 2053 operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : 2054 false; 2055 }; 2056 }, 2057 2058 "CHILD": function( type, what, argument, first, last ) { 2059 var simple = type.slice( 0, 3 ) !== "nth", 2060 forward = type.slice( -4 ) !== "last", 2061 ofType = what === "of-type"; 2062 2063 return first === 1 && last === 0 ? 2064 2065 // Shortcut for :nth-*(n) 2066 function( elem ) { 2067 return !!elem.parentNode; 2068 } : 2069 2070 function( elem, context, xml ) { 2071 var cache, outerCache, node, diff, nodeIndex, start, 2072 dir = simple !== forward ? "nextSibling" : "previousSibling", 2073 parent = elem.parentNode, 2074 name = ofType && elem.nodeName.toLowerCase(), 2075 useCache = !xml && !ofType; 2076 2077 if ( parent ) { 2078 2079 // :(first|last|only)-(child|of-type) 2080 if ( simple ) { 2081 while ( dir ) { 2082 node = elem; 2083 while ( (node = node[ dir ]) ) { 2084 if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) { 2085 return false; 2086 } 2087 } 2088 // Reverse direction for :only-* (if we haven't yet done so) 2089 start = dir = type === "only" && !start && "nextSibling"; 2090 } 2091 return true; 2092 } 2093 2094 start = [ forward ? parent.firstChild : parent.lastChild ]; 2095 2096 // non-xml :nth-child(...) stores cache data on `parent` 2097 if ( forward && useCache ) { 2098 // Seek `elem` from a previously-cached index 2099 outerCache = parent[ expando ] || (parent[ expando ] = {}); 2100 cache = outerCache[ type ] || []; 2101 nodeIndex = cache[0] === dirruns && cache[1]; 2102 diff = cache[0] === dirruns && cache[2]; 2103 node = nodeIndex && parent.childNodes[ nodeIndex ]; 2104 2105 while ( (node = ++nodeIndex && node && node[ dir ] || 2106 2107 // Fallback to seeking `elem` from the start 2108 (diff = nodeIndex = 0) || start.pop()) ) { 2109 2110 // When found, cache indexes on `parent` and break 2111 if ( node.nodeType === 1 && ++diff && node === elem ) { 2112 outerCache[ type ] = [ dirruns, nodeIndex, diff ]; 2113 break; 2114 } 2115 } 2116 2117 // Use previously-cached element index if available 2118 } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) { 2119 diff = cache[1]; 2120 2121 // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) 2122 } else { 2123 // Use the same loop as above to seek `elem` from the start 2124 while ( (node = ++nodeIndex && node && node[ dir ] || 2125 (diff = nodeIndex = 0) || start.pop()) ) { 2126 2127 if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) { 2128 // Cache the index of each encountered element 2129 if ( useCache ) { 2130 (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ]; 2131 } 2132 2133 if ( node === elem ) { 2134 break; 2135 } 2136 } 2137 } 2138 } 2139 2140 // Incorporate the offset, then check against cycle size 2141 diff -= last; 2142 return diff === first || ( diff % first === 0 && diff / first >= 0 ); 2143 } 2144 }; 2145 }, 2146 2147 "PSEUDO": function( pseudo, argument ) { 2148 // pseudo-class names are case-insensitive 2149 // http://www.w3.org/TR/selectors/#pseudo-classes 2150 // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters 2151 // Remember that setFilters inherits from pseudos 2152 var args, 2153 fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || 2154 Sizzle.error( "unsupported pseudo: " + pseudo ); 2155 2156 // The user may use createPseudo to indicate that 2157 // arguments are needed to create the filter function 2158 // just as Sizzle does 2159 if ( fn[ expando ] ) { 2160 return fn( argument ); 2161 } 2162 2163 // But maintain support for old signatures 2164 if ( fn.length > 1 ) { 2165 args = [ pseudo, pseudo, "", argument ]; 2166 return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? 2167 markFunction(function( seed, matches ) { 2168 var idx, 2169 matched = fn( seed, argument ), 2170 i = matched.length; 2171 while ( i-- ) { 2172 idx = indexOf.call( seed, matched[i] ); 2173 seed[ idx ] = !( matches[ idx ] = matched[i] ); 2174 } 2175 }) : 2176 function( elem ) { 2177 return fn( elem, 0, args ); 2178 }; 2179 } 2180 2181 return fn; 2182 } 2183 }, 2184 2185 pseudos: { 2186 // Potentially complex pseudos 2187 "not": markFunction(function( selector ) { 2188 // Trim the selector passed to compile 2189 // to avoid treating leading and trailing 2190 // spaces as combinators 2191 var input = [], 2192 results = [], 2193 matcher = compile( selector.replace( rtrim, "$1" ) ); 2194 2195 return matcher[ expando ] ? 2196 markFunction(function( seed, matches, context, xml ) { 2197 var elem, 2198 unmatched = matcher( seed, null, xml, [] ), 2199 i = seed.length; 2200 2201 // Match elements unmatched by `matcher` 2202 while ( i-- ) { 2203 if ( (elem = unmatched[i]) ) { 2204 seed[i] = !(matches[i] = elem); 2205 } 2206 } 2207 }) : 2208 function( elem, context, xml ) { 2209 input[0] = elem; 2210 matcher( input, null, xml, results ); 2211 return !results.pop(); 2212 }; 2213 }), 2214 2215 "has": markFunction(function( selector ) { 2216 return function( elem ) { 2217 return Sizzle( selector, elem ).length > 0; 2218 }; 2219 }), 2220 2221 "contains": markFunction(function( text ) { 2222 return function( elem ) { 2223 return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; 2224 }; 2225 }), 2226 2227 // "Whether an element is represented by a :lang() selector 2228 // is based solely on the element's language value 2229 // being equal to the identifier C, 2230 // or beginning with the identifier C immediately followed by "-". 2231 // The matching of C against the element's language value is performed case-insensitively. 2232 // The identifier C does not have to be a valid language name." 2233 // http://www.w3.org/TR/selectors/#lang-pseudo 2234 "lang": markFunction( function( lang ) { 2235 // lang value must be a valid identifier 2236 if ( !ridentifier.test(lang || "") ) { 2237 Sizzle.error( "unsupported lang: " + lang ); 2238 } 2239 lang = lang.replace( runescape, funescape ).toLowerCase(); 2240 return function( elem ) { 2241 var elemLang; 2242 do { 2243 if ( (elemLang = documentIsHTML ? 2244 elem.lang : 2245 elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { 2246 2247 elemLang = elemLang.toLowerCase(); 2248 return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; 2249 } 2250 } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); 2251 return false; 2252 }; 2253 }), 2254 2255 // Miscellaneous 2256 "target": function( elem ) { 2257 var hash = window.location && window.location.hash; 2258 return hash && hash.slice( 1 ) === elem.id; 2259 }, 2260 2261 "root": function( elem ) { 2262 return elem === docElem; 2263 }, 2264 2265 "focus": function( elem ) { 2266 return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); 2267 }, 2268 2269 // Boolean properties 2270 "enabled": function( elem ) { 2271 return elem.disabled === false; 2272 }, 2273 2274 "disabled": function( elem ) { 2275 return elem.disabled === true; 2276 }, 2277 2278 "checked": function( elem ) { 2279 // In CSS3, :checked should return both checked and selected elements 2280 // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked 2281 var nodeName = elem.nodeName.toLowerCase(); 2282 return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); 2283 }, 2284 2285 "selected": function( elem ) { 2286 // Accessing this property makes selected-by-default 2287 // options in Safari work properly 2288 if ( elem.parentNode ) { 2289 elem.parentNode.selectedIndex; 2290 } 2291 2292 return elem.selected === true; 2293 }, 2294 2295 // Contents 2296 "empty": function( elem ) { 2297 // http://www.w3.org/TR/selectors/#empty-pseudo 2298 // :empty is
vendor: 3,404 bytes, lines 2298-2423
2298only affected by element nodes and content nodes(including text(3), cdata(4)), 2299 // not comment, processing instructions, or others 2300 // Thanks to Diego Perini for the nodeName shortcut 2301 // Greater than "@" means alpha characters (specifically not starting with "#" or "?") 2302 for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { 2303 if ( elem.nodeName > "@" || elem.nodeType === 3 || elem.nodeType === 4 ) { 2304 return false; 2305 } 2306 } 2307 return true; 2308 }, 2309 2310 "parent": function( elem ) { 2311 return !Expr.pseudos["empty"]( elem ); 2312 }, 2313 2314 // Element/input types 2315 "header": function( elem ) { 2316 return rheader.test( elem.nodeName ); 2317 }, 2318 2319 "input": function( elem ) { 2320 return rinputs.test( elem.nodeName ); 2321 }, 2322 2323 "button": function( elem ) { 2324 var name = elem.nodeName.toLowerCase(); 2325 return name === "input" && elem.type === "button" || name === "button"; 2326 }, 2327 2328 "text": function( elem ) { 2329 var attr; 2330 // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) 2331 // use getAttribute instead to test this case 2332 return elem.nodeName.toLowerCase() === "input" && 2333 elem.type === "text" && 2334 ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === elem.type ); 2335 }, 2336 2337 // Position-in-collection 2338 "first": createPositionalPseudo(function() { 2339 return [ 0 ]; 2340 }), 2341 2342 "last": createPositionalPseudo(function( matchIndexes, length ) { 2343 return [ length - 1 ]; 2344 }), 2345 2346 "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { 2347 return [ argument < 0 ? argument + length : argument ]; 2348 }), 2349 2350 "even": createPositionalPseudo(function( matchIndexes, length ) { 2351 var i = 0; 2352 for ( ; i < length; i += 2 ) { 2353 matchIndexes.push( i ); 2354 } 2355 return matchIndexes; 2356 }), 2357 2358 "odd": createPositionalPseudo(function( matchIndexes, length ) { 2359 var i = 1; 2360 for ( ; i < length; i += 2 ) { 2361 matchIndexes.push( i ); 2362 } 2363 return matchIndexes; 2364 }), 2365 2366 "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { 2367 var i = argument < 0 ? argument + length : argument; 2368 for ( ; --i >= 0; ) { 2369 matchIndexes.push( i ); 2370 } 2371 return matchIndexes; 2372 }), 2373 2374 "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { 2375 var i = argument < 0 ? argument + length : argument; 2376 for ( ; ++i < length; ) { 2377 matchIndexes.push( i ); 2378 } 2379 return matchIndexes; 2380 }) 2381 } 2382}; 2383 2384Expr.pseudos["nth"] = Expr.pseudos["eq"]; 2385 2386// Add button/input type pseudos 2387for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { 2388 Expr.pseudos[ i ] = createInputPseudo( i ); 2389} 2390for ( i in { submit: true, reset: true } ) { 2391 Expr.pseudos[ i ] = createButtonPseudo( i ); 2392} 2393 2394// Easy API for creating new setFilters 2395function setFilters() {} 2396setFilters.prototype = Expr.filters = Expr.pseudos; 2397Expr.setFilters = new setFilters(); 2398 2399function tokenize( selector, parseOnly ) { 2400 var matched, match, tokens, type, 2401 soFar, groups, preFilters, 2402 cached = tokenCache[ selector + " " ]; 2403 2404 if ( cached ) { 2405 return parseOnly ? 0 : cached.slice( 0 ); 2406 } 2407 2408 soFar = selector; 2409 groups = []; 2410 preFilters = Expr.preFilter; 2411 2412 while ( soFar ) { 2413 2414 // Comma and first run 2415 if ( !matched || (match = rcomma.exec( soFar )) ) { 2416 if ( match ) { 2417 // Don't consume trailing commas as valid 2418 soFar = soFar.slice( match[0].length ) || soFar; 2419 } 2420 groups.push( tokens = [] ); 2421 } 2422 2423 matched = false;
vendor: 25,111 bytes, lines 2424-3313
2424 2425 // Combinators 2426 if ( (match = rcombinators.exec( soFar )) ) { 2427 matched = match.shift(); 2428 tokens.push({ 2429 value: matched, 2430 // Cast descendant combinators to space 2431 type: match[0].replace( rtrim, " " ) 2432 }); 2433 soFar = soFar.slice( matched.length ); 2434 } 2435 2436 // Filters 2437 for ( type in Expr.filter ) { 2438 if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || 2439 (match = preFilters[ type ]( match ))) ) { 2440 matched = match.shift(); 2441 tokens.push({ 2442 value: matched, 2443 type: type, 2444 matches: match 2445 }); 2446 soFar = soFar.slice( matched.length ); 2447 } 2448 } 2449 2450 if ( !matched ) { 2451 break; 2452 } 2453 } 2454 2455 // Return the length of the invalid excess 2456 // if we're just parsing 2457 // Otherwise, throw an error or return tokens 2458 return parseOnly ? 2459 soFar.length : 2460 soFar ? 2461 Sizzle.error( selector ) : 2462 // Cache the tokens 2463 tokenCache( selector, groups ).slice( 0 ); 2464} 2465 2466function toSelector( tokens ) { 2467 var i = 0, 2468 len = tokens.length, 2469 selector = ""; 2470 for ( ; i < len; i++ ) { 2471 selector += tokens[i].value; 2472 } 2473 return selector; 2474} 2475 2476function addCombinator( matcher, combinator, base ) { 2477 var dir = combinator.dir, 2478 checkNonElements = base && dir === "parentNode", 2479 doneName = done++; 2480 2481 return combinator.first ? 2482 // Check against closest ancestor/preceding element 2483 function( elem, context, xml ) { 2484 while ( (elem = elem[ dir ]) ) { 2485 if ( elem.nodeType === 1 || checkNonElements ) { 2486 return matcher( elem, context, xml ); 2487 } 2488 } 2489 } : 2490 2491 // Check against all ancestor/preceding elements 2492 function( elem, context, xml ) { 2493 var data, cache, outerCache, 2494 dirkey = dirruns + " " + doneName; 2495 2496 // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching 2497 if ( xml ) { 2498 while ( (elem = elem[ dir ]) ) { 2499 if ( elem.nodeType === 1 || checkNonElements ) { 2500 if ( matcher( elem, context, xml ) ) { 2501 return true; 2502 } 2503 } 2504 } 2505 } else { 2506 while ( (elem = elem[ dir ]) ) { 2507 if ( elem.nodeType === 1 || checkNonElements ) { 2508 outerCache = elem[ expando ] || (elem[ expando ] = {}); 2509 if ( (cache = outerCache[ dir ]) && cache[0] === dirkey ) { 2510 if ( (data = cache[1]) === true || data === cachedruns ) { 2511 return data === true; 2512 } 2513 } else { 2514 cache = outerCache[ dir ] = [ dirkey ]; 2515 cache[1] = matcher( elem, context, xml ) || cachedruns; 2516 if ( cache[1] === true ) { 2517 return true; 2518 } 2519 } 2520 } 2521 } 2522 } 2523 }; 2524} 2525 2526function elementMatcher( matchers ) { 2527 return matchers.length > 1 ? 2528 function( elem, context, xml ) { 2529 var i = matchers.length; 2530 while ( i-- ) { 2531 if ( !matchers[i]( elem, context, xml ) ) { 2532 return false; 2533 } 2534 } 2535 return true; 2536 } : 2537 matchers[0]; 2538} 2539 2540function condense( unmatched, map, filter, context, xml ) { 2541 var elem, 2542 newUnmatched = [], 2543 i = 0, 2544 len = unmatched.length, 2545 mapped = map != null; 2546 2547 for ( ; i < len; i++ ) { 2548 if ( (elem = unmatched[i]) ) { 2549 if ( !filter || filter( elem, context, xml ) ) { 2550 newUnmatched.push( elem ); 2551 if ( mapped ) { 2552 map.push( i ); 2553 } 2554 } 2555 } 2556 } 2557 2558 return newUnmatched; 2559} 2560 2561function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { 2562 if ( postFilter && !postFilter[ expando ] ) { 2563 postFilter = setMatcher( postFilter ); 2564 } 2565 if ( postFinder && !postFinder[ expando ] ) { 2566 postFinder = setMatcher( postFinder, postSelector ); 2567 } 2568 return markFunction(function( seed, results, context, xml ) { 2569 var temp, i, elem, 2570 preMap = [], 2571 postMap = [], 2572 preexisting = results.length, 2573 2574 // Get initial elements from seed or context 2575 elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), 2576 2577 // Prefilter to get matcher input, preserving a map for seed-results synchronization 2578 matcherIn = preFilter && ( seed || !selector ) ? 2579 condense( elems, preMap, preFilter, context, xml ) : 2580 elems, 2581 2582 matcherOut = matcher ? 2583 // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, 2584 postFinder || ( seed ? preFilter : preexisting || postFilter ) ? 2585 2586 // ...intermediate processing is necessary 2587 [] : 2588 2589 // ...otherwise use results directly 2590 results : 2591 matcherIn; 2592 2593 // Find primary matches 2594 if ( matcher ) { 2595 matcher( matcherIn, matcherOut, context, xml ); 2596 } 2597 2598 // Apply postFilter 2599 if ( postFilter ) { 2600 temp = condense( matcherOut, postMap ); 2601 postFilter( temp, [], context, xml ); 2602 2603 // Un-match failing elements by moving them back to matcherIn 2604 i = temp.length; 2605 while ( i-- ) { 2606 if ( (elem = temp[i]) ) { 2607 matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); 2608 } 2609 } 2610 } 2611 2612 if ( seed ) { 2613 if ( postFinder || preFilter ) { 2614 if ( postFinder ) { 2615 // Get the final matcherOut by condensing this intermediate into postFinder contexts 2616 temp = []; 2617 i = matcherOut.length; 2618 while ( i-- ) { 2619 if ( (elem = matcherOut[i]) ) { 2620 // Restore matcherIn since elem is not yet a final match 2621 temp.push( (matcherIn[i] = elem) ); 2622 } 2623 } 2624 postFinder( null, (matcherOut = []), temp, xml ); 2625 } 2626 2627 // Move matched elements from seed to results to keep them synchronized 2628 i = matcherOut.length; 2629 while ( i-- ) { 2630 if ( (elem = matcherOut[i]) && 2631 (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { 2632 2633 seed[temp] = !(results[temp] = elem); 2634 } 2635 } 2636 } 2637 2638 // Add elements to results, through postFinder if defined 2639 } else { 2640 matcherOut = condense( 2641 matcherOut === results ? 2642 matcherOut.splice( preexisting, matcherOut.length ) : 2643 matcherOut 2644 ); 2645 if ( postFinder ) { 2646 postFinder( null, results, matcherOut, xml ); 2647 } else { 2648 push.apply( results, matcherOut ); 2649 } 2650 } 2651 }); 2652} 2653 2654function matcherFromTokens( tokens ) { 2655 var checkContext, matcher, j, 2656 len = tokens.length, 2657 leadingRelative = Expr.relative[ tokens[0].type ], 2658 implicitRelative = leadingRelative || Expr.relative[" "], 2659 i = leadingRelative ? 1 : 0, 2660 2661 // The foundational matcher ensures that elements are reachable from top-level context(s) 2662 matchContext = addCombinator( function( elem ) { 2663 return elem === checkContext; 2664 }, implicitRelative, true ), 2665 matchAnyContext = addCombinator( function( elem ) { 2666 return indexOf.call( checkContext, elem ) > -1; 2667 }, implicitRelative, true ), 2668 matchers = [ function( elem, context, xml ) { 2669 return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( 2670 (checkContext = context).nodeType ? 2671 matchContext( elem, context, xml ) : 2672 matchAnyContext( elem, context, xml ) ); 2673 } ]; 2674 2675 for ( ; i < len; i++ ) { 2676 if ( (matcher = Expr.relative[ tokens[i].type ]) ) { 2677 matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; 2678 } else { 2679 matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); 2680 2681 // Return special upon seeing a positional matcher 2682 if ( matcher[ expando ] ) { 2683 // Find the next relative operator (if any) for proper handling 2684 j = ++i; 2685 for ( ; j < len; j++ ) { 2686 if ( Expr.relative[ tokens[j].type ] ) { 2687 break; 2688 } 2689 } 2690 return setMatcher( 2691 i > 1 && elementMatcher( matchers ), 2692 i > 1 && toSelector( 2693 // If the preceding token was a descendant combinator, insert an implicit any-element `*` 2694 tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) 2695 ).replace( rtrim, "$1" ), 2696 matcher, 2697 i < j && matcherFromTokens( tokens.slice( i, j ) ), 2698 j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), 2699 j < len && toSelector( tokens ) 2700 ); 2701 } 2702 matchers.push( matcher ); 2703 } 2704 } 2705 2706 return elementMatcher( matchers ); 2707} 2708 2709function matcherFromGroupMatchers( elementMatchers, setMatchers ) { 2710 // A counter to specify which element is currently being matched 2711 var matcherCachedRuns = 0, 2712 bySet = setMatchers.length > 0, 2713 byElement = elementMatchers.length > 0, 2714 superMatcher = function( seed, context, xml, results, expandContext ) { 2715 var elem, j, matcher, 2716 setMatched = [], 2717 matchedCount = 0, 2718 i = "0", 2719 unmatched = seed && [], 2720 outermost = expandContext != null, 2721 contextBackup = outermostContext, 2722 // We must always have either seed elements or context 2723 elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ), 2724 // Use integer dirruns iff this is the outermost matcher 2725 dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1); 2726 2727 if ( outermost ) { 2728 outermostContext = context !== document && context; 2729 cachedruns = matcherCachedRuns; 2730 } 2731 2732 // Add elements passing elementMatchers directly to results 2733 // Keep `i` a string if there are no elements so `matchedCount` will be "00" below 2734 for ( ; (elem = elems[i]) != null; i++ ) { 2735 if ( byElement && elem ) { 2736 j = 0; 2737 while ( (matcher = elementMatchers[j++]) ) { 2738 if ( matcher( elem, context, xml ) ) { 2739 results.push( elem ); 2740 break; 2741 } 2742 } 2743 if ( outermost ) { 2744 dirruns = dirrunsUnique; 2745 cachedruns = ++matcherCachedRuns; 2746 } 2747 } 2748 2749 // Track unmatched elements for set filters 2750 if ( bySet ) { 2751 // They will have gone through all possible matchers 2752 if ( (elem = !matcher && elem) ) { 2753 matchedCount--; 2754 } 2755 2756 // Lengthen the array for every element, matched or not 2757 if ( seed ) { 2758 unmatched.push( elem ); 2759 } 2760 } 2761 } 2762 2763 // Apply set filters to unmatched elements 2764 matchedCount += i; 2765 if ( bySet && i !== matchedCount ) { 2766 j = 0; 2767 while ( (matcher = setMatchers[j++]) ) { 2768 matcher( unmatched, setMatched, context, xml ); 2769 } 2770 2771 if ( seed ) { 2772 // Reintegrate element matches to eliminate the need for sorting 2773 if ( matchedCount > 0 ) { 2774 while ( i-- ) { 2775 if ( !(unmatched[i] || setMatched[i]) ) { 2776 setMatched[i] = pop.call( results ); 2777 } 2778 } 2779 } 2780 2781 // Discard index placeholder values to get only actual matches 2782 setMatched = condense( setMatched ); 2783 } 2784 2785 // Add matches to results 2786 push.apply( results, setMatched ); 2787 2788 // Seedless set matches succeeding multiple successful matchers stipulate sorting 2789 if ( outermost && !seed && setMatched.length > 0 && 2790 ( matchedCount + setMatchers.length ) > 1 ) { 2791 2792 Sizzle.uniqueSort( results ); 2793 } 2794 } 2795 2796 // Override manipulation of globals by nested matchers 2797 if ( outermost ) { 2798 dirruns = dirrunsUnique; 2799 outermostContext = contextBackup; 2800 } 2801 2802 return unmatched; 2803 }; 2804 2805 return bySet ? 2806 markFunction( superMatcher ) : 2807 superMatcher; 2808} 2809 2810compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) { 2811 var i, 2812 setMatchers = [], 2813 elementMatchers = [], 2814 cached = compilerCache[ selector + " " ]; 2815 2816 if ( !cached ) { 2817 // Generate a function of recursive functions that can be used to check each element 2818 if ( !group ) { 2819 group = tokenize( selector ); 2820 } 2821 i = group.length; 2822 while ( i-- ) { 2823 cached = matcherFromTokens( group[i] ); 2824 if ( cached[ expando ] ) { 2825 setMatchers.push( cached ); 2826 } else { 2827 elementMatchers.push( cached ); 2828 } 2829 } 2830 2831 // Cache the compiled function 2832 cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); 2833 } 2834 return cached; 2835}; 2836 2837function multipleContexts( selector, contexts, results ) { 2838 var i = 0, 2839 len = contexts.length; 2840 for ( ; i < len; i++ ) { 2841 Sizzle( selector, contexts[i], results ); 2842 } 2843 return results; 2844} 2845 2846function select( selector, context, results, seed ) { 2847 var i, tokens, token, type, find, 2848 match = tokenize( selector ); 2849 2850 if ( !seed ) { 2851 // Try to minimize operations if there is only one group 2852 if ( match.length === 1 ) { 2853 2854 // Take a shortcut and set the context if the root selector is an ID 2855 tokens = match[0] = match[0].slice( 0 ); 2856 if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && 2857 support.getById && context.nodeType === 9 && documentIsHTML && 2858 Expr.relative[ tokens[1].type ] ) { 2859 2860 context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; 2861 if ( !context ) { 2862 return results; 2863 } 2864 selector = selector.slice( tokens.shift().value.length ); 2865 } 2866 2867 // Fetch a seed set for right-to-left matching 2868 i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; 2869 while ( i-- ) { 2870 token = tokens[i]; 2871 2872 // Abort if we hit a combinator 2873 if ( Expr.relative[ (type = token.type) ] ) { 2874 break; 2875 } 2876 if ( (find = Expr.find[ type ]) ) { 2877 // Search, expanding context for leading sibling combinators 2878 if ( (seed = find( 2879 token.matches[0].replace( runescape, funescape ), 2880 rsibling.test( tokens[0].type ) && context.parentNode || context 2881 )) ) { 2882 2883 // If seed is empty or no tokens remain, we can return early 2884 tokens.splice( i, 1 ); 2885 selector = seed.length && toSelector( tokens ); 2886 if ( !selector ) { 2887 push.apply( results, seed ); 2888 return results; 2889 } 2890 2891 break; 2892 } 2893 } 2894 } 2895 } 2896 } 2897 2898 // Compile and execute a filtering function 2899 // Provide `match` to avoid retokenization if we modified the selector above 2900 compile( selector, match )( 2901 seed, 2902 context, 2903 !documentIsHTML, 2904 results, 2905 rsibling.test( selector ) 2906 ); 2907 return results; 2908} 2909 2910// One-time assignments 2911 2912// Sort stability 2913support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; 2914 2915// Support: Chrome<14 2916// Always assume duplicates if they aren't passed to the comparison function 2917support.detectDuplicates = hasDuplicate; 2918 2919// Initialize against the default document 2920setDocument(); 2921 2922// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) 2923// Detached nodes confoundingly follow *each other* 2924support.sortDetached = assert(function( div1 ) { 2925 // Should return 1, but returns 4 (following) 2926 return div1.compareDocumentPosition( document.createElement("div") ) & 1; 2927}); 2928 2929// Support: IE<8 2930// Prevent attribute/property "interpolation" 2931// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx 2932if ( !assert(function( div ) { 2933 div.innerHTML = "<a href='#'></a>"; 2934 return div.firstChild.getAttribute("href") === "#" ; 2935}) ) { 2936 addHandle( "type|href|height|width", function( elem, name, isXML ) { 2937 if ( !isXML ) { 2938 return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); 2939 } 2940 }); 2941} 2942 2943// Support: IE<9 2944// Use defaultValue in place of getAttribute("value") 2945if ( !support.attributes || !assert(function( div ) { 2946 div.innerHTML = "<input/>"; 2947 div.firstChild.setAttribute( "value", "" ); 2948 return div.firstChild.getAttribute( "value" ) === ""; 2949}) ) { 2950 addHandle( "value", function( elem, name, isXML ) { 2951 if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { 2952 return elem.defaultValue; 2953 } 2954 }); 2955} 2956 2957// Support: IE<9 2958// Use getAttributeNode to fetch booleans when getAttribute lies 2959if ( !assert(function( div ) { 2960 return div.getAttribute("disabled") == null; 2961}) ) { 2962 addHandle( booleans, function( elem, name, isXML ) { 2963 var val; 2964 if ( !isXML ) { 2965 return (val = elem.getAttributeNode( name )) && val.specified ? 2966 val.value : 2967 elem[ name ] === true ? name.toLowerCase() : null; 2968 } 2969 }); 2970} 2971 2972jQuery.find = Sizzle; 2973jQuery.expr = Sizzle.selectors; 2974jQuery.expr[":"] = jQuery.expr.pseudos; 2975jQuery.unique = Sizzle.uniqueSort; 2976jQuery.text = Sizzle.getText; 2977jQuery.isXMLDoc = Sizzle.isXML; 2978jQuery.contains = Sizzle.contains; 2979 2980 2981})( window ); 2982// String to Object options format cache 2983var optionsCache = {}; 2984 2985// Convert String-formatted options into Object-formatted ones and store in cache 2986function createOptions( options ) { 2987 var object = optionsCache[ options ] = {}; 2988 jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) { 2989 object[ flag ] = true; 2990 }); 2991 return object; 2992} 2993 2994/* 2995 * Create a callback list using the following parameters: 2996 * 2997 * options: an optional list of space-separated options that will change how 2998 * the callback list behaves or a more traditional option object 2999 * 3000 * By default a callback list will act like an event callback list and can be 3001 * "fired" multiple times. 3002 * 3003 * Possible options: 3004 * 3005 * once: will ensure the callback list can only be fired once (like a Deferred) 3006 * 3007 * memory: will keep track of previous values and will call any callback added 3008 * after the list has been fired right away with the latest "memorized" 3009 * values (like a Deferred) 3010 * 3011 * unique: will ensure a callback can only be added once (no duplicate in the list) 3012 * 3013 * stopOnFalse: interrupt callings when a callback returns false 3014 * 3015 */ 3016jQuery.Callbacks = function( options ) { 3017 3018 // Convert options from String-formatted to Object-formatted if needed 3019 // (we check in cache first) 3020 options = typeof options === "string" ? 3021 ( optionsCache[ options ] || createOptions( options ) ) : 3022 jQuery.extend( {}, options ); 3023 3024 var // Flag to know if list is currently firing 3025 firing, 3026 // Last fire value (for non-forgettable lists) 3027 memory, 3028 // Flag to know if list was already fired 3029 fired, 3030 // End of the loop when firing 3031 firingLength, 3032 // Index of currently firing callback (modified by remove if needed) 3033 firingIndex, 3034 // First callback to fire (used internally by add and fireWith) 3035 firingStart, 3036 // Actual callback list 3037 list = [], 3038 // Stack of fire calls for repeatable lists 3039 stack = !options.once && [], 3040 // Fire callbacks 3041 fire = function( data ) { 3042 memory = options.memory && data; 3043 fired = true; 3044 firingIndex = firingStart || 0; 3045 firingStart = 0; 3046 firingLength = list.length; 3047 firing = true; 3048 for ( ; list && firingIndex < firingLength; firingIndex++ ) { 3049 if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { 3050 memory = false; // To prevent further calls using add 3051 break; 3052 } 3053 } 3054 firing = false; 3055 if ( list ) { 3056 if ( stack ) { 3057 if ( stack.length ) { 3058 fire( stack.shift() ); 3059 } 3060 } else if ( memory ) { 3061 list = []; 3062 } else { 3063 self.disable(); 3064 } 3065 } 3066 }, 3067 // Actual Callbacks object 3068 self = { 3069 // Add a callback or a collection of callbacks to the list 3070 add: function() { 3071 if ( list ) { 3072 // First, we save the current length 3073 var start = list.length; 3074 (function add( args ) { 3075 jQuery.each( args, function( _, arg ) { 3076 var type = jQuery.type( arg ); 3077 if ( type === "function" ) { 3078 if ( !options.unique || !self.has( arg ) ) { 3079 list.push( arg ); 3080 } 3081 } else if ( arg && arg.length && type !== "string" ) { 3082 // Inspect recursively 3083 add( arg ); 3084 } 3085 }); 3086 })( arguments ); 3087 // Do we need to add the callbacks to the 3088 // current firing batch? 3089 if ( firing ) { 3090 firingLength = list.length; 3091 // With memory, if we're not firing then 3092 // we should call right away 3093 } else if ( memory ) { 3094 firingStart = start; 3095 fire( memory ); 3096 } 3097 } 3098 return this; 3099 }, 3100 // Remove a callback from the list 3101 remove: function() { 3102 if ( list ) { 3103 jQuery.each( arguments, function( _, arg ) { 3104 var index; 3105 while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { 3106 list.splice( index, 1 ); 3107 // Handle firing indexes 3108 if ( firing ) { 3109 if ( index <= firingLength ) { 3110 firingLength--; 3111 } 3112 if ( index <= firingIndex ) { 3113 firingIndex--; 3114 } 3115 } 3116 } 3117 }); 3118 } 3119 return this; 3120 }, 3121 // Check if a given callback is in the list. 3122 // If no argument is given, return whether or not list has callbacks attached. 3123 has: function( fn ) { 3124 return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length ); 3125 }, 3126 // Remove all callbacks from the list 3127 empty: function() { 3128 list = []; 3129 firingLength = 0; 3130 return this; 3131 }, 3132 // Have the list do nothing anymore 3133 disable: function() { 3134 list = stack = memory = undefined; 3135 return this; 3136 }, 3137 // Is it disabled? 3138 disabled: function() { 3139 return !list; 3140 }, 3141 // Lock the list in its current state 3142 lock: function() { 3143 stack = undefined; 3144 if ( !memory ) { 3145 self.disable(); 3146 } 3147 return this; 3148 }, 3149 // Is it locked? 3150 locked: function() { 3151 return !stack; 3152 }, 3153 // Call all callbacks with the given context and arguments 3154 fireWith: function( context, args ) { 3155 if ( list && ( !fired || stack ) ) { 3156 args = args || []; 3157 args = [ context, args.slice ? args.slice() : args ]; 3158 if ( firing ) { 3159 stack.push( args ); 3160 } else { 3161 fire( args ); 3162 } 3163 } 3164 return this; 3165 }, 3166 // Call all the callbacks with the given arguments 3167 fire: function() { 3168 self.fireWith( this, arguments ); 3169 return this; 3170 }, 3171 // To know if the callbacks have already been called at least once 3172 fired: function() { 3173 return !!fired; 3174 } 3175 }; 3176 3177 return self; 3178}; 3179jQuery.extend({ 3180 3181 Deferred: function( func ) { 3182 var tuples = [ 3183 // action, add listener, listener list, final state 3184 [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], 3185 [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], 3186 [ "notify", "progress", jQuery.Callbacks("memory") ] 3187 ], 3188 state = "pending", 3189 promise = { 3190 state: function() { 3191 return state; 3192 }, 3193 always: function() { 3194 deferred.done( arguments ).fail( arguments ); 3195 return this; 3196 }, 3197 then: function( /* fnDone, fnFail, fnProgress */ ) { 3198 var fns = arguments; 3199 return jQuery.Deferred(function( newDefer ) { 3200 jQuery.each( tuples, function( i, tuple ) { 3201 var action = tuple[ 0 ], 3202 fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; 3203 // deferred[ done | fail | progress ] for forwarding actions to newDefer 3204 deferred[ tuple[1] ](function() { 3205 var returned = fn && fn.apply( this, arguments ); 3206 if ( returned && jQuery.isFunction( returned.promise ) ) { 3207 returned.promise() 3208 .done( newDefer.resolve ) 3209 .fail( newDefer.reject ) 3210 .progress( newDefer.notify ); 3211 } else { 3212 newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); 3213 } 3214 }); 3215 }); 3216 fns = null; 3217 }).promise(); 3218 }, 3219 // Get a promise for this deferred 3220 // If obj is provided, the promise aspect is added to the object 3221 promise: function( obj ) { 3222 return obj != null ? jQuery.extend( obj, promise ) : promise; 3223 } 3224 }, 3225 deferred = {}; 3226 3227 // Keep pipe for back-compat 3228 promise.pipe = promise.then; 3229 3230 // Add list-specific methods 3231 jQuery.each( tuples, function( i, tuple ) { 3232 var list = tuple[ 2 ], 3233 stateString = tuple[ 3 ]; 3234 3235 // promise[ done | fail | progress ] = list.add 3236 promise[ tuple[1] ] = list.add; 3237 3238 // Handle state 3239 if ( stateString ) { 3240 list.add(function() { 3241 // state = [ resolved | rejected ] 3242 state = stateString; 3243 3244 // [ reject_list | resolve_list ].disable; progress_list.lock 3245 }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); 3246 } 3247 3248 // deferred[ resolve | reject | notify ] 3249 deferred[ tuple[0] ] = function() { 3250 deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); 3251 return this; 3252 }; 3253 deferred[ tuple[0] + "With" ] = list.fireWith; 3254 }); 3255 3256 // Make the deferred a promise 3257 promise.promise( deferred ); 3258 3259 // Call given func if any 3260 if ( func ) { 3261 func.call( deferred, deferred ); 3262 } 3263 3264 // All done! 3265 return deferred; 3266 }, 3267 3268 // Deferred helper 3269 when: function( subordinate /* , ..., subordinateN */ ) { 3270 var i = 0, 3271 resolveValues = core_slice.call( arguments ), 3272 length = resolveValues.length, 3273 3274 // the count of uncompleted subordinates 3275 remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, 3276 3277 // the master Deferred. If resolveValues consist of only a single Deferred, just use that. 3278 deferred = remaining === 1 ? subordinate : jQuery.Deferred(), 3279 3280 // Update function for both resolve and progress values 3281 updateFunc = function( i, contexts, values ) { 3282 return function( value ) { 3283 contexts[ i ] = this; 3284 values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value; 3285 if( values === progressValues ) { 3286 deferred.notifyWith( contexts, values ); 3287 } else if ( !( --remaining ) ) { 3288 deferred.resolveWith( contexts, values ); 3289 } 3290 }; 3291 }, 3292 3293 progressValues, progressContexts, resolveContexts; 3294 3295 // add listeners to Deferred subordinates; treat others as resolved 3296 if ( length > 1 ) { 3297 progressValues = new Array( length ); 3298 progressContexts = new Array( length ); 3299 resolveContexts = new Array( length ); 3300 for ( ; i < length; i++ ) { 3301 if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { 3302 resolveValues[ i ].promise() 3303 .done( updateFunc( i, resolveContexts, resolveValues ) ) 3304 .fail( deferred.reject ) 3305 .progress( updateFunc( i, progressContexts, progressValues ) ); 3306 } else { 3307 --remaining; 3308 } 3309 } 3310 } 3311 3312 // if we're not waiting on anything, resolve the master 3313 if ( !remaining ) {
3314 deferred.resolveWith( resolveContexts, resolveValues ); 3315 } 3316 3317 return deferred.promise(); 3318 } 3319}); 3320jQuery.support = (function( support ) { 3321 3322 var all, a, input, select, fragment, opt, eventName, isSupported, i, 3323 div = document.createElement("div"); 3324 3325 // Setup 3326 div.setAttribute( "className", "t" ); 3327 div.innerHTML = " <link/><table></table><a href='/a'>a</a><input type='checkbox'/>"; 3328 3329 // Finish early in limited (non-browser) environments 3330 all = div.getElementsByTagName("*") || []; 3331 a = div.getElementsByTagName("a")[ 0 ]; 3332 if ( !a || !a.style || !all.length ) { 3333 return support; 3334 } 3335 3336 // First batch of tests 3337 select = document.createElement("select"); 3338 opt = select.appendChild( document.createElement("option") ); 3339 input = div.getElementsByTagName("input")[ 0 ]; 3340 3341 a.style.cssText = "top:1px;float:left;opacity:.5"; 3342 3343 // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) 3344 support.getSetAttribute = div.className !== "t"; 3345 3346 // IE strips leading whitespace when .innerHTML is used 3347 support.leadingWhitespace = div.firstChild.nodeType === 3; 3348 3349 // Make sure that tbody elements aren't automatically inserted 3350 // IE will insert them into empty tables 3351 support.tbody = !div.getElementsByTagName("tbody").length; 3352 3353 // Make sure that link elements get serialized correctly by innerHTML 3354 // This requires a wrapper element in IE 3355 support.htmlSerialize = !!div.getElementsByTagName("link").length; 3356 3357 // Get the style information from getAttribute 3358 // (IE uses .cssText instead)
3359 support.style = /top/.test( a.getAttribute("style") ); 3360 3361 // Make sure that URLs aren't manipulated 3362 // (IE normalizes it by default) 3363 support.hrefNormalized = a.getAttribute("href") === "/a"; 3364 3365 // Make sure that element opacity exists 3366 // (IE uses filter instead) 3367 // Use a regex to work around a WebKit issue. See #5145 3368 support.opacity = /^0.5/.test( a.style.opacity ); 3369 3370 // Verify style float existence 3371 // (IE uses styleFloat instead of cssFloat) 3372 support.cssFloat = !!a.style.cssFloat; 3373 3374 // Check the default checkbox/radio value ("" on WebKit; "on" elsewhere) 3375 support.checkOn = !!input.value; 3376 3377 // Make sure that a selected-by-default option has a working selected property. 3378 // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) 3379 support.optSelected = opt.selected; 3380 3381 // Tests for enctype support on a form (#6743) 3382 support.enctype = !!document.createElement("form").enctype; 3383 3384 // Makes sure cloning an html5 element does not cause problems 3385 // Where outerHTML is undefined, this still works 3386 support.html5Clone = document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav></:nav>"; 3387 3388 // Will be defined later 3389 support.inlineBlockNeedsLayout = false; 3390 support.shrinkWrapBlocks = false; 3391 support.pixelPosition = false; 3392 support.deleteExpando = true; 3393 support.noCloneEvent = true; 3394 support.reliableMarginRight = true; 3395 support.boxSizingReliable = true; 3396 3397 // Make sure checked status is properly cloned 3398 input.checked = true; 3399 support.noCloneChecked = input.cloneNode( true ).checked; 3400 3401 // Make sure that the options inside disabled selects aren't marked as disabled 3402 // (WebKit marks them as disabled) 3403 select.disabled = true; 3404 support.optDisabled = !opt.disabled; 3405 3406 // Support: IE<9 3407 try { 3408 delete div.test; 3409 } catch( e ) { 3410 support.deleteExpando = false; 3411 } 3412 3413 // Check if we can trust getAttribute("value") 3414 input = document.createElement("input"); 3415 input.setAttribute( "value", "" ); 3416 support.input = input.getAttribute( "value" ) === ""; 3417 3418 // Check if an input maintains its value after becoming a radio 3419 input.value = "t"; 3420 input.setAttribute( "type", "radio" ); 3421 support.radioValue = input.value === "t"; 3422 3423 // #11217 - WebKit loses check when the name is after the checked attribute 3424 input.setAttribute( "checked", "t" ); 3425 input.setAttribute( "name", "t" ); 3426 3427 fragment = document.createDocumentFragment(); 3428 fragment.appendChild( input ); 3429 3430 // Check if a disconnected checkbox will retain its checked 3431 // value of true after appended to the DOM (IE6/7) 3432 support.appendChecked = input.checked; 3433 3434 // WebKit doesn't clone checked state correctly in fragments 3435 support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; 3436 3437 // Support: IE<9 3438 // Opera does not clone events (and typeof div.attachEvent === undefined). 3439 // IE9-10 clones events bound via attachEvent, but they don't trigger with .click() 3440 if ( div.attachEvent ) { 3441 div.attachEvent( "onclick", function() { 3442 support.noCloneEvent = false; 3443 }); 3444 3445 div.cloneNode( true ).click(); 3446 } 3447 3448 // Support: IE<9 (lack submit/change bubble), Firefox 17+ (lack focusin event) 3449 // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP) 3450 for ( i in { submit: true, change: true, focusin: true }) { 3451 div.setAttribute( eventName = "on" + i, "t" ); 3452
3453 support[ i + "Bubbles" ] = eventName in window || div.attributes[ eventName ].expando === false; 3454 } 3455 3456 div.style.backgroundClip = "content-box"; 3457 div.cloneNode( true ).style.backgroundClip = ""; 3458 support.clearCloneStyle = div.style.backgroundClip === "content-box"; 3459 3460 // Support: IE<9 3461 // Iteration over object's inherited properties before its own. 3462 for ( i in jQuery( support ) ) { 3463 break; 3464 } 3465 support.ownLast = i !== "0"; 3466 3467 // Run tests that need a body at doc ready 3468 jQuery(function() { 3469 var container, marginDiv, tds, 3470 divReset = "padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;", 3471 body = document.getElementsByTagName("body")[0]; 3472 3473 if ( !body ) { 3474 // Return for frameset docs that don't have a body 3475 return; 3476 } 3477 3478 container = document.createElement("div"); 3479 container.style.cssText = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px"; 3480 3481 body.appendChild( container ).appendChild( div ); 3482 3483 // Support: IE8 3484 // Check if table cells still have offsetWidth/Height when they are set 3485 // to display:none and there are still other visible table cells in a 3486 // table row; if so, offsetWidth/Height are not reliable for use when 3487 // determining if an element has been hidden directly using 3488 // display:none (it is still safe to use offsets if a parent element is 3489 // hidden; don safety goggles and see bug #4512 for more information). 3490 div.innerHTML = "<table><tr><td></td><td>t</td></tr></table>"; 3491 tds = div.getElementsByTagName("td"); 3492 tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none"; 3493 isSupported = ( tds[ 0 ].offsetHeight === 0 ); 3494 3495 tds[ 0 ].style.display = ""; 3496 tds[ 1 ].style.display = "none"; 3497 3498 // Support: IE8 3499 // Check if empty table cells still have offsetWidth/Height 3500 support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); 3501 3502 // Check box-sizing and margin behavior. 3503 div.innerHTML = ""; 3504 div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;"; 3505 3506 // Workaround failing boxSizing test due to offsetWidth returning wrong value 3507 // with some non-1 values of body zoom, ticket #13543 3508 jQuery.swap( body, body.style.zoom != null ? { zoom: 1 } : {}, function() { 3509 support.boxSizing = div.offsetWidth === 4; 3510 }); 3511 3512 // Use window.getComputedStyle because jsdom on node.js will break without it. 3513 if ( window.getComputedStyle ) { 3514 support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%"; 3515 support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px"; 3516 3517 // Check if div with explicit width and no margin-right incorrectly 3518 // gets computed margin-right based on width of container. (#3333) 3519 // Fails in WebKit before Feb 2011 nightlies 3520 // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right 3521 marginDiv = div.appendChild( document.createElement("div") ); 3522 marginDiv.style.cssText = div.style.cssText = divReset; 3523 marginDiv.style.marginRight = marginDiv.style.width = "0"; 3524 div.style.width = "1px"; 3525 3526 support.reliableMarginRight = 3527 !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight ); 3528 } 3529 3530 if ( typeof div.style.zoom !== core_strundefined ) { 3531 // Support: IE<8 3532 // Check if natively block-level elements act like inline-block 3533 // elements when setting their display to 'inline' and giving 3534 // them layout 3535 div.innerHTML = ""; 3536 div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1"; 3537 support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 ); 3538 3539 // Support: IE6 3540 // Check if elements with layout shrink-wrap their children 3541 div.style.display = "block"; 3542 div.innerHTML = "<div></div>"; 3543 div.firstChild.style.width = "5px"; 3544 support.shrinkWrapBlocks = ( div.offsetWidth !== 3 ); 3545 3546 if ( support.inlineBlockNeedsLayout ) { 3547 // Prevent IE 6 from affecting layout for positioned elements #11048 3548 // Prevent IE from shrinking the body in IE 7 mode #12869
vendor: 4,821 bytes, lines 3549-3726
3549 // Support: IE<8 3550 body.style.zoom = 1; 3551 } 3552 } 3553 3554 body.removeChild( container ); 3555 3556 // Null elements to avoid leaks in IE 3557 container = div = tds = marginDiv = null; 3558 }); 3559 3560 // Null elements to avoid leaks in IE 3561 all = select = fragment = opt = a = input = null; 3562 3563 return support; 3564})({}); 3565 3566var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, 3567 rmultiDash = /([A-Z])/g; 3568 3569function internalData( elem, name, data, pvt /* Internal Use Only */ ){ 3570 if ( !jQuery.acceptData( elem ) ) { 3571 return; 3572 } 3573 3574 var ret, thisCache, 3575 internalKey = jQuery.expando, 3576 3577 // We have to handle DOM nodes and JS objects differently because IE6-7 3578 // can't GC object references properly across the DOM-JS boundary 3579 isNode = elem.nodeType, 3580 3581 // Only DOM nodes need the global jQuery cache; JS object data is 3582 // attached directly to the object so GC can occur automatically 3583 cache = isNode ? jQuery.cache : elem, 3584 3585 // Only defining an ID for JS objects if its cache already exists allows 3586 // the code to shortcut on the same path as a DOM node with no cache 3587 id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey; 3588 3589 // Avoid doing any more work than we need to when trying to get data on an 3590 // object that has no data at all 3591 if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && data === undefined && typeof name === "string" ) { 3592 return; 3593 } 3594 3595 if ( !id ) { 3596 // Only DOM nodes need a new unique ID for each element since their data 3597 // ends up in the global cache 3598 if ( isNode ) { 3599 id = elem[ internalKey ] = core_deletedIds.pop() || jQuery.guid++; 3600 } else { 3601 id = internalKey; 3602 } 3603 } 3604 3605 if ( !cache[ id ] ) { 3606 // Avoid exposing jQuery metadata on plain JS objects when the object 3607 // is serialized using JSON.stringify 3608 cache[ id ] = isNode ? {} : { toJSON: jQuery.noop }; 3609 } 3610 3611 // An object can be passed to jQuery.data instead of a key/value pair; this gets 3612 // shallow copied over onto the existing cache 3613 if ( typeof name === "object" || typeof name === "function" ) { 3614 if ( pvt ) { 3615 cache[ id ] = jQuery.extend( cache[ id ], name ); 3616 } else { 3617 cache[ id ].data = jQuery.extend( cache[ id ].data, name ); 3618 } 3619 } 3620 3621 thisCache = cache[ id ]; 3622 3623 // jQuery data() is stored in a separate object inside the object's internal data 3624 // cache in order to avoid key collisions between internal data and user-defined 3625 // data. 3626 if ( !pvt ) { 3627 if ( !thisCache.data ) { 3628 thisCache.data = {}; 3629 } 3630 3631 thisCache = thisCache.data; 3632 } 3633 3634 if ( data !== undefined ) { 3635 thisCache[ jQuery.camelCase( name ) ] = data; 3636 } 3637 3638 // Check for both converted-to-camel and non-converted data property names 3639 // If a data property was specified 3640 if ( typeof name === "string" ) { 3641 3642 // First Try to find as-is property data 3643 ret = thisCache[ name ]; 3644 3645 // Test for null|undefined property data 3646 if ( ret == null ) { 3647 3648 // Try to find the camelCased property 3649 ret = thisCache[ jQuery.camelCase( name ) ]; 3650 } 3651 } else { 3652 ret = thisCache; 3653 } 3654 3655 return ret; 3656} 3657 3658function internalRemoveData( elem, name, pvt ) { 3659 if ( !jQuery.acceptData( elem ) ) { 3660 return; 3661 } 3662 3663 var thisCache, i, 3664 isNode = elem.nodeType, 3665 3666 // See jQuery.data for more information 3667 cache = isNode ? jQuery.cache : elem, 3668 id = isNode ? elem[ jQuery.expando ] : jQuery.expando; 3669 3670 // If there is already no cache entry for this object, there is no 3671 // purpose in continuing 3672 if ( !cache[ id ] ) { 3673 return; 3674 } 3675 3676 if ( name ) { 3677 3678 thisCache = pvt ? cache[ id ] : cache[ id ].data; 3679 3680 if ( thisCache ) { 3681 3682 // Support array or space separated string names for data keys 3683 if ( !jQuery.isArray( name ) ) { 3684 3685 // try the string as a key before any manipulation 3686 if ( name in thisCache ) { 3687 name = [ name ]; 3688 } else { 3689 3690 // split the camel cased version by spaces unless a key with the spaces exists 3691 name = jQuery.camelCase( name ); 3692 if ( name in thisCache ) { 3693 name = [ name ]; 3694 } else { 3695 name = name.split(" "); 3696 } 3697 } 3698 } else { 3699 // If "name" is an array of keys... 3700 // When data is initially created, via ("key", "val") signature, 3701 // keys will be converted to camelCase. 3702 // Since there is no way to tell _how_ a key was added, remove 3703 // both plain key and camelCase key. #12786 3704 // This will only penalize the array argument path. 3705 name = name.concat( jQuery.map( name, jQuery.camelCase ) ); 3706 } 3707 3708 i = name.length; 3709 while ( i-- ) { 3710 delete thisCache[ name[i] ]; 3711 } 3712 3713 // If there is no data left in the cache, we want to continue 3714 // and let the cache object itself get destroyed 3715 if ( pvt ? !isEmptyDataObject(thisCache) : !jQuery.isEmptyObject(thisCache) ) { 3716 return; 3717 } 3718 } 3719 } 3720 3721 // See jQuery.data for more information 3722 if ( !pvt ) { 3723 delete cache[ id ].data; 3724 3725 // Don't destroy the parent cache unless the internal data object 3726 // had been the only thing left in it
3727 if ( !isEmptyDataObject( cache[ id ] ) ) { 3728 return; 3729 } 3730 } 3731 3732 // Destroy the cache 3733 if ( isNode ) { 3734 jQuery.cleanData( [ elem ], true ); 3735 3736 // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080) 3737 /* jshint eqeqeq: false */ 3738 } else if ( jQuery.support.deleteExpando || cache != cache.window ) { 3739 /* jshint eqeqeq: true */ 3740 delete cache[ id ]; 3741 3742 // When all else fails, null 3743 } else { 3744 cache[ id ] = null; 3745 } 3746} 3747 3748jQuery.extend({ 3749 cache: {}, 3750 3751 // The following elements throw uncatchable exceptions if you 3752 // attempt to add expando properties to them. 3753 noData: { 3754 "applet": true, 3755 "embed": true, 3756 // Ban all objects except for Flash (which handle expandos) 3757 "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" 3758 }, 3759 3760 hasData: function( elem ) { 3761 elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; 3762 return !!elem && !isEmptyDataObject( elem ); 3763 }, 3764 3765 data: function( elem, name, data ) { 3766 return internalData( elem, name, data ); 3767 }, 3768 3769 removeData: function( elem, name ) { 3770 return internalRemoveData( elem, name ); 3771 }, 3772 3773 // For internal use only. 3774 _data: function( elem, name, data ) { 3775 return internalData( elem, name, data, true ); 3776 }, 3777 3778 _removeData: function( elem, name ) { 3779 return internalRemoveData( elem, name, true ); 3780 }, 3781 3782 // A method for determining if a DOM node can handle the data expando 3783 acceptData: function( elem ) { 3784 // Do not set data on non-element because it will not be cleared (#8335). 3785 if ( elem.nodeType && elem.nodeType !== 1 && elem.nodeType !== 9 ) { 3786 return false; 3787 } 3788 3789 var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ]; 3790 3791 // nodes accept data unless otherwise specified; rejection can be conditional 3792 return !noData || noData !== true && elem.getAttribute("classid") === noData; 3793 } 3794}); 3795 3796jQuery.fn.extend({ 3797 data: function( key, value ) { 3798 var attrs, name, 3799 data = null, 3800 i = 0, 3801 elem = this[0]; 3802 3803 // Special expections of .data basically thwart jQuery.access, 3804 // so implement the relevant behavior ourselves 3805 3806 // Gets all values 3807 if ( key === undefined ) { 3808 if ( this.length ) { 3809 data = jQuery.data( elem ); 3810 3811 if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { 3812 attrs = elem.attributes; 3813 for ( ; i < attrs.length; i++ ) { 3814 name = attrs[i].name; 3815 3816 if ( name.indexOf("data-") === 0 ) { 3817 name = jQuery.camelCase( name.slice(5) ); 3818 3819 dataAttr( elem, name, data[ name ] ); 3820 } 3821 } 3822 jQuery._data( elem, "parsedAttrs", true ); 3823 } 3824 } 3825 3826 return data; 3827 } 3828 3829 // Sets multiple values 3830 if ( typeof key === "object" ) { 3831 return this.each(function() { 3832 jQuery.data( this, key ); 3833 }); 3834 } 3835 3836 return arguments.length > 1 ? 3837 3838 // Sets one value 3839 this.each(function() { 3840 jQuery.data( this, key, value ); 3841 }) : 3842 3843 // Gets one value 3844 // Try to fetch any internally stored data first 3845 elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : null; 3846 }, 3847 3848 removeData: function( key ) { 3849 return this.each(function() { 3850 jQuery.removeData( this, key ); 3851 }); 3852 } 3853}); 3854 3855function dataAttr( elem, key, data ) { 3856 // If nothing was found internally, try to fetch any 3857 // data from the HTML5 data-* attribute 3858 if ( data === undefined && elem.nodeType === 1 ) { 3859 3860 var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); 3861 3862 data = elem.getAttribute( name ); 3863 3864 if ( typeof data === "string" ) { 3865 try { 3866 data = data === "true" ? true : 3867 data === "false" ? false : 3868 data === "null" ? null : 3869 // Only convert to a number if it doesn't change the string 3870 +data + "" === data ? +data : 3871 rbrace.test( data ) ? jQuery.parseJSON( data ) : 3872 data; 3873 } catch( e ) {} 3874 3875 // Make sure we set the data so it isn't changed later 3876 jQuery.data( elem, key, data ); 3877 3878 } else { 3879 data = undefined; 3880 } 3881 } 3882 3883 return data; 3884} 3885 3886// checks a cache object for emptiness 3887function isEmptyDataObject( obj ) { 3888 var name; 3889 for ( name in obj ) { 3890 3891 // if the public data object is empty, the private is still empty 3892 if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { 3893 continue; 3894 } 3895 if ( name !== "toJSON" ) { 3896 return false;
vendor: 5,969 bytes, lines 3897-4146
3897 } 3898 } 3899 3900 return true; 3901} 3902jQuery.extend({ 3903 queue: function( elem, type, data ) { 3904 var queue; 3905 3906 if ( elem ) { 3907 type = ( type || "fx" ) + "queue"; 3908 queue = jQuery._data( elem, type ); 3909 3910 // Speed up dequeue by getting out quickly if this is just a lookup 3911 if ( data ) { 3912 if ( !queue || jQuery.isArray(data) ) { 3913 queue = jQuery._data( elem, type, jQuery.makeArray(data) ); 3914 } else { 3915 queue.push( data ); 3916 } 3917 } 3918 return queue || []; 3919 } 3920 }, 3921 3922 dequeue: function( elem, type ) { 3923 type = type || "fx"; 3924 3925 var queue = jQuery.queue( elem, type ), 3926 startLength = queue.length, 3927 fn = queue.shift(), 3928 hooks = jQuery._queueHooks( elem, type ), 3929 next = function() { 3930 jQuery.dequeue( elem, type ); 3931 }; 3932 3933 // If the fx queue is dequeued, always remove the progress sentinel 3934 if ( fn === "inprogress" ) { 3935 fn = queue.shift(); 3936 startLength--; 3937 } 3938 3939 if ( fn ) { 3940 3941 // Add a progress sentinel to prevent the fx queue from being 3942 // automatically dequeued 3943 if ( type === "fx" ) { 3944 queue.unshift( "inprogress" ); 3945 } 3946 3947 // clear up the last queue stop function 3948 delete hooks.stop; 3949 fn.call( elem, next, hooks ); 3950 } 3951 3952 if ( !startLength && hooks ) { 3953 hooks.empty.fire(); 3954 } 3955 }, 3956 3957 // not intended for public consumption - generates a queueHooks object, or returns the current one 3958 _queueHooks: function( elem, type ) { 3959 var key = type + "queueHooks"; 3960 return jQuery._data( elem, key ) || jQuery._data( elem, key, { 3961 empty: jQuery.Callbacks("once memory").add(function() { 3962 jQuery._removeData( elem, type + "queue" ); 3963 jQuery._removeData( elem, key ); 3964 }) 3965 }); 3966 } 3967}); 3968 3969jQuery.fn.extend({ 3970 queue: function( type, data ) { 3971 var setter = 2; 3972 3973 if ( typeof type !== "string" ) { 3974 data = type; 3975 type = "fx"; 3976 setter--; 3977 } 3978 3979 if ( arguments.length < setter ) { 3980 return jQuery.queue( this[0], type ); 3981 } 3982 3983 return data === undefined ? 3984 this : 3985 this.each(function() { 3986 var queue = jQuery.queue( this, type, data ); 3987 3988 // ensure a hooks for this queue 3989 jQuery._queueHooks( this, type ); 3990 3991 if ( type === "fx" && queue[0] !== "inprogress" ) { 3992 jQuery.dequeue( this, type ); 3993 } 3994 }); 3995 }, 3996 dequeue: function( type ) { 3997 return this.each(function() { 3998 jQuery.dequeue( this, type ); 3999 }); 4000 }, 4001 // Based off of the plugin by Clint Helfers, with permission. 4002 // http://blindsignals.com/index.php/2009/07/jquery-delay/ 4003 delay: function( time, type ) { 4004 time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; 4005 type = type || "fx"; 4006 4007 return this.queue( type, function( next, hooks ) { 4008 var timeout = setTimeout( next, time ); 4009 hooks.stop = function() { 4010 clearTimeout( timeout ); 4011 }; 4012 }); 4013 }, 4014 clearQueue: function( type ) { 4015 return this.queue( type || "fx", [] ); 4016 }, 4017 // Get a promise resolved when queues of a certain type 4018 // are emptied (fx is the type by default) 4019 promise: function( type, obj ) { 4020 var tmp, 4021 count = 1, 4022 defer = jQuery.Deferred(), 4023 elements = this, 4024 i = this.length, 4025 resolve = function() { 4026 if ( !( --count ) ) { 4027 defer.resolveWith( elements, [ elements ] ); 4028 } 4029 }; 4030 4031 if ( typeof type !== "string" ) { 4032 obj = type; 4033 type = undefined; 4034 } 4035 type = type || "fx"; 4036 4037 while( i-- ) { 4038 tmp = jQuery._data( elements[ i ], type + "queueHooks" ); 4039 if ( tmp && tmp.empty ) { 4040 count++; 4041 tmp.empty.add( resolve ); 4042 } 4043 } 4044 resolve(); 4045 return defer.promise( obj ); 4046 } 4047}); 4048var nodeHook, boolHook, 4049 rclass = /[\t\r\n\f]/g, 4050 rreturn = /\r/g, 4051 rfocusable = /^(?:input|select|textarea|button|object)$/i, 4052 rclickable = /^(?:a|area)$/i, 4053 ruseDefault = /^(?:checked|selected)$/i, 4054 getSetAttribute = jQuery.support.getSetAttribute, 4055 getSetInput = jQuery.support.input; 4056 4057jQuery.fn.extend({ 4058 attr: function( name, value ) { 4059 return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 ); 4060 }, 4061 4062 removeAttr: function( name ) { 4063 return this.each(function() { 4064 jQuery.removeAttr( this, name ); 4065 }); 4066 }, 4067 4068 prop: function( name, value ) { 4069 return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 ); 4070 }, 4071 4072 removeProp: function( name ) { 4073 name = jQuery.propFix[ name ] || name; 4074 return this.each(function() { 4075 // try/catch handles cases where IE balks (such as removing a property on window) 4076 try { 4077 this[ name ] = undefined; 4078 delete this[ name ]; 4079 } catch( e ) {} 4080 }); 4081 }, 4082 4083 addClass: function( value ) { 4084 var classes, elem, cur, clazz, j, 4085 i = 0, 4086 len = this.length, 4087 proceed = typeof value === "string" && value; 4088 4089 if ( jQuery.isFunction( value ) ) { 4090 return this.each(function( j ) { 4091 jQuery( this ).addClass( value.call( this, j, this.className ) ); 4092 }); 4093 } 4094 4095 if ( proceed ) { 4096 // The disjunction here is for better compressibility (see removeClass) 4097 classes = ( value || "" ).match( core_rnotwhite ) || []; 4098 4099 for ( ; i < len; i++ ) { 4100 elem = this[ i ]; 4101 cur = elem.nodeType === 1 && ( elem.className ? 4102 ( " " + elem.className + " " ).replace( rclass, " " ) : 4103 " " 4104 ); 4105 4106 if ( cur ) { 4107 j = 0; 4108 while ( (clazz = classes[j++]) ) { 4109 if ( cur.indexOf( " " + clazz + " " ) < 0 ) { 4110 cur += clazz + " "; 4111 } 4112 } 4113 elem.className = jQuery.trim( cur ); 4114 4115 } 4116 } 4117 } 4118 4119 return this; 4120 }, 4121 4122 removeClass: function( value ) { 4123 var classes, elem, cur, clazz, j, 4124 i = 0, 4125 len = this.length, 4126 proceed = arguments.length === 0 || typeof value === "string" && value; 4127 4128 if ( jQuery.isFunction( value ) ) { 4129 return this.each(function( j ) { 4130 jQuery( this ).removeClass( value.call( this, j, this.className ) ); 4131 }); 4132 } 4133 if ( proceed ) { 4134 classes = ( value || "" ).match( core_rnotwhite ) || []; 4135 4136 for ( ; i < len; i++ ) { 4137 elem = this[ i ]; 4138 // This expression is here for better compressibility (see addClass) 4139 cur = elem.nodeType === 1 && ( elem.className ? 4140 ( " " + elem.className + " " ).replace( rclass, " " ) : 4141 "" 4142 ); 4143 4144 if ( cur ) { 4145 j = 0; 4146 while ( (clazz = classes[j++]) ) {
vendor: 6,992 bytes, lines 4147-4411
4147 // Remove *all* instances 4148 while ( cur.indexOf( " " + clazz + " " ) >= 0 ) { 4149 cur = cur.replace( " " + clazz + " ", " " ); 4150 } 4151 } 4152 elem.className = value ? jQuery.trim( cur ) : ""; 4153 } 4154 } 4155 } 4156 4157 return this; 4158 }, 4159 4160 toggleClass: function( value, stateVal ) { 4161 var type = typeof value; 4162 4163 if ( typeof stateVal === "boolean" && type === "string" ) { 4164 return stateVal ? this.addClass( value ) : this.removeClass( value ); 4165 } 4166 4167 if ( jQuery.isFunction( value ) ) { 4168 return this.each(function( i ) { 4169 jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); 4170 }); 4171 } 4172 4173 return this.each(function() { 4174 if ( type === "string" ) { 4175 // toggle individual class names 4176 var className, 4177 i = 0, 4178 self = jQuery( this ), 4179 classNames = value.match( core_rnotwhite ) || []; 4180 4181 while ( (className = classNames[ i++ ]) ) { 4182 // check each className given, space separated list 4183 if ( self.hasClass( className ) ) { 4184 self.removeClass( className ); 4185 } else { 4186 self.addClass( className ); 4187 } 4188 } 4189 4190 // Toggle whole class name 4191 } else if ( type === core_strundefined || type === "boolean" ) { 4192 if ( this.className ) { 4193 // store className if set 4194 jQuery._data( this, "__className__", this.className ); 4195 } 4196 4197 // If the element has a class name or if we're passed "false", 4198 // then remove the whole classname (if there was one, the above saved it). 4199 // Otherwise bring back whatever was previously saved (if anything), 4200 // falling back to the empty string if nothing was stored. 4201 this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; 4202 } 4203 }); 4204 }, 4205 4206 hasClass: function( selector ) { 4207 var className = " " + selector + " ", 4208 i = 0, 4209 l = this.length; 4210 for ( ; i < l; i++ ) { 4211 if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) { 4212 return true; 4213 } 4214 } 4215 4216 return false; 4217 }, 4218 4219 val: function( value ) { 4220 var ret, hooks, isFunction, 4221 elem = this[0]; 4222 4223 if ( !arguments.length ) { 4224 if ( elem ) { 4225 hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; 4226 4227 if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { 4228 return ret; 4229 } 4230 4231 ret = elem.value; 4232 4233 return typeof ret === "string" ? 4234 // handle most common string cases 4235 ret.replace(rreturn, "") : 4236 // handle cases where value is null/undef or number 4237 ret == null ? "" : ret; 4238 } 4239 4240 return; 4241 } 4242 4243 isFunction = jQuery.isFunction( value ); 4244 4245 return this.each(function( i ) { 4246 var val; 4247 4248 if ( this.nodeType !== 1 ) { 4249 return; 4250 } 4251 4252 if ( isFunction ) { 4253 val = value.call( this, i, jQuery( this ).val() ); 4254 } else { 4255 val = value; 4256 } 4257 4258 // Treat null/undefined as ""; convert numbers to string 4259 if ( val == null ) { 4260 val = ""; 4261 } else if ( typeof val === "number" ) { 4262 val += ""; 4263 } else if ( jQuery.isArray( val ) ) { 4264 val = jQuery.map(val, function ( value ) { 4265 return value == null ? "" : value + ""; 4266 }); 4267 } 4268 4269 hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; 4270 4271 // If set returns undefined, fall back to normal setting 4272 if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { 4273 this.value = val; 4274 } 4275 }); 4276 } 4277}); 4278 4279jQuery.extend({ 4280 valHooks: { 4281 option: { 4282 get: function( elem ) { 4283 // Use proper attribute retrieval(#6932, #12072) 4284 var val = jQuery.find.attr( elem, "value" ); 4285 return val != null ? 4286 val : 4287 elem.text; 4288 } 4289 }, 4290 select: { 4291 get: function( elem ) { 4292 var value, option, 4293 options = elem.options, 4294 index = elem.selectedIndex, 4295 one = elem.type === "select-one" || index < 0, 4296 values = one ? null : [], 4297 max = one ? index + 1 : options.length, 4298 i = index < 0 ? 4299 max : 4300 one ? index : 0; 4301 4302 // Loop through all the selected options 4303 for ( ; i < max; i++ ) { 4304 option = options[ i ]; 4305 4306 // oldIE doesn't update selected after form reset (#2551) 4307 if ( ( option.selected || i === index ) && 4308 // Don't return options that are disabled or in a disabled optgroup 4309 ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) && 4310 ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) { 4311 4312 // Get the specific value for the option 4313 value = jQuery( option ).val(); 4314 4315 // We don't need an array for one selects 4316 if ( one ) { 4317 return value; 4318 } 4319 4320 // Multi-Selects return an array 4321 values.push( value ); 4322 } 4323 } 4324 4325 return values; 4326 }, 4327 4328 set: function( elem, value ) { 4329 var optionSet, option, 4330 options = elem.options, 4331 values = jQuery.makeArray( value ), 4332 i = options.length; 4333 4334 while ( i-- ) { 4335 option = options[ i ]; 4336 if ( (option.selected = jQuery.inArray( jQuery(option).val(), values ) >= 0) ) { 4337 optionSet = true; 4338 } 4339 } 4340 4341 // force browsers to behave consistently when non-matching value is set 4342 if ( !optionSet ) { 4343 elem.selectedIndex = -1; 4344 } 4345 return values; 4346 } 4347 } 4348 }, 4349 4350 attr: function( elem, name, value ) { 4351 var hooks, ret, 4352 nType = elem.nodeType; 4353 4354 // don't get/set attributes on text, comment and attribute nodes 4355 if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { 4356 return; 4357 } 4358 4359 // Fallback to prop when attributes are not supported 4360 if ( typeof elem.getAttribute === core_strundefined ) { 4361 return jQuery.prop( elem, name, value ); 4362 } 4363 4364 // All attributes are lowercase 4365 // Grab necessary hook if one is defined 4366 if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { 4367 name = name.toLowerCase(); 4368 hooks = jQuery.attrHooks[ name ] || 4369 ( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook ); 4370 } 4371 4372 if ( value !== undefined ) { 4373 4374 if ( value === null ) { 4375 jQuery.removeAttr( elem, name ); 4376 4377 } else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { 4378 return ret; 4379 4380 } else { 4381 elem.setAttribute( name, value + "" ); 4382 return value; 4383 } 4384 4385 } else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { 4386 return ret; 4387 4388 } else { 4389 ret = jQuery.find.attr( elem, name ); 4390 4391 // Non-existent attributes return null, we normalize to undefined 4392 return ret == null ? 4393 undefined : 4394 ret; 4395 } 4396 }, 4397 4398 removeAttr: function( elem, value ) { 4399 var name, propName, 4400 i = 0, 4401 attrNames = value && value.match( core_rnotwhite ); 4402 4403 if ( attrNames && elem.nodeType === 1 ) { 4404 while ( (name = attrNames[i++]) ) { 4405 propName = jQuery.propFix[ name ] || name; 4406 4407 // Boolean attributes get special treatment (#10870) 4408 if ( jQuery.expr.match.bool.test( name ) ) { 4409 // Set corresponding property to false 4410 if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) { 4411 elem[ propName ] = false;
4412 // Support: IE<9 4413 // Also clear defaultChecked/defaultSelected (if appropriate) 4414 } else { 4415 elem[ jQuery.camelCase( "default-" + name ) ] = 4416 elem[ propName ] = false; 4417 } 4418 4419 // See #9699 for explanation of this approach (setting first, then removal) 4420 } else { 4421 jQuery.attr( elem, name, "" ); 4422 } 4423 4424 elem.removeAttribute( getSetAttribute ? name : propName ); 4425 } 4426 } 4427 }, 4428 4429 attrHooks: { 4430 type: { 4431 set: function( elem, value ) { 4432 if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { 4433 // Setting the type on a radio button after the value resets the value in IE6-9 4434 // Reset value to default in case type is set after value during creation 4435 var val = elem.value; 4436 elem.setAttribute( "type", value ); 4437 if ( val ) { 4438 elem.value = val; 4439 } 4440 return value; 4441 } 4442 } 4443 } 4444 }, 4445 4446 propFix: { 4447 "for": "htmlFor", 4448 "class": "className" 4449 }, 4450 4451 prop: function( elem, name, value ) { 4452 var ret, hooks, notxml, 4453 nType = elem.nodeType; 4454 4455 // don't get/set properties on text, comment and attribute nodes 4456 if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { 4457 return; 4458 } 4459 4460 notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); 4461 4462 if ( notxml ) { 4463 // Fix name and attach hooks 4464 name = jQuery.propFix[ name ] || name; 4465 hooks = jQuery.propHooks[ name ]; 4466 } 4467 4468 if ( value !== undefined ) { 4469 return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ? 4470 ret : 4471 ( elem[ name ] = value ); 4472 4473 } else { 4474 return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ? 4475 ret : 4476 elem[ name ]; 4477 } 4478 }, 4479 4480 propHooks: { 4481 tabIndex: { 4482 get: function( elem ) { 4483 // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set 4484 // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ 4485 // Use proper attribute retrieval(#12072) 4486 var tabindex = jQuery.find.attr( elem, "tabindex" ); 4487 4488 return tabindex ? 4489 parseInt( tabindex, 10 ) : 4490 rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? 4491 0 : 4492 -1; 4493 } 4494 } 4495 } 4496}); 4497 4498// Hooks for boolean attributes 4499boolHook = { 4500 set: function( elem, value, name ) { 4501 if ( value === false ) { 4502 // Remove boolean attributes when set to false 4503 jQuery.removeAttr( elem, name ); 4504 } else if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) { 4505 // IE<8 needs the *property* name 4506 elem.setAttribute( !getSetAttribute && jQuery.propFix[ name ] || name, name ); 4507 4508 // Use defaultChecked and defaultSelected for oldIE 4509 } else { 4510 elem[ jQuery.camelCase( "default-" + name ) ] = elem[ name ] = true; 4511 } 4512 4513 return name; 4514 } 4515}; 4516jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) { 4517 var getter = jQuery.expr.attrHandle[ name ] || jQuery.find.attr; 4518 4519 jQuery.expr.attrHandle[ name ] = getSetInput && getSetAttribute || !ruseDefault.test( name ) ? 4520 function( elem, name, isXML ) { 4521 var fn = jQuery.expr.attrHandle[ name ], 4522 ret = isXML ? 4523 undefined : 4524 /* jshint eqeqeq: false */ 4525 (jQuery.expr.attrHandle[ name ] = undefined) != 4526 getter( elem, name, isXML ) ? 4527 4528 name.toLowerCase() : 4529 null; 4530 jQuery.expr.attrHandle[ name ] = fn; 4531 return ret; 4532 } : 4533 function( elem, name, isXML ) { 4534 return isXML ? 4535 undefined : 4536 elem[ jQuery.camelCase( "default-" + name ) ] ? 4537 name.toLowerCase() : 4538 null; 4539 }; 4540}); 4541 4542// fix oldIE attroperties 4543if ( !getSetInput || !getSetAttribute ) { 4544 jQuery.attrHooks.value = { 4545 set: function( elem, value, name ) { 4546 if ( jQuery.nodeName( elem, "input" ) ) { 4547 // Does not return so that setAttribute is also used 4548 elem.defaultValue = value; 4549 } else { 4550 // Use nodeHook if defined (#1954); otherwise setAttribute is fine 4551 return nodeHook && nodeHook.set( elem, value, name ); 4552 } 4553 } 4554 }; 4555} 4556 4557// IE6/7 do not support getting/setting some attributes with get/setAttribute 4558if ( !getSetAttribute ) { 4559 4560 // Use this for any attribute in IE6/7 4561 // This fixes almost every IE6/7 issue 4562 nodeHook = { 4563 set: function( elem, value, name ) { 4564 // Set the existing or create a new attribute node 4565 var ret = elem.getAttributeNode( name ); 4566 if ( !ret ) { 4567 elem.setAttributeNode( 4568 (ret = elem.ownerDocument.createAttribute( name )) 4569 ); 4570 } 4571 4572 ret.value = value += ""; 4573 4574 // Break association with cloned elements by also using setAttribute (#9646) 4575 return name === "value" || value === elem.getAttribute( name ) ? 4576 value : 4577 undefined; 4578 } 4579 }; 4580 jQuery.expr.attrHandle.id = jQuery.expr.attrHandle.name = jQuery.expr.attrHandle.coords = 4581 // Some attributes are constructed with empty-string values when not defined 4582 function( elem, name, isXML ) { 4583 var ret; 4584 return isXML ? 4585 undefined : 4586 (ret = elem.getAttributeNode( name )) && ret.value !== "" ? 4587 ret.value : 4588 null; 4589 }; 4590 jQuery.valHooks.button = { 4591 get: function( elem, name ) { 4592 var ret = elem.getAttributeNode( name ); 4593 return ret && ret.specified ? 4594 ret.value : 4595 undefined; 4596 }, 4597 set: nodeHook.set 4598 }; 4599 4600 // Set contenteditable to false on removals(#10429)
vendor: 13,769 bytes, lines 4601-5064
4601 // Setting to empty string throws an error as an invalid value 4602 jQuery.attrHooks.contenteditable = { 4603 set: function( elem, value, name ) { 4604 nodeHook.set( elem, value === "" ? false : value, name ); 4605 } 4606 }; 4607 4608 // Set width and height to auto instead of 0 on empty string( Bug #8150 ) 4609 // This is for removals 4610 jQuery.each([ "width", "height" ], function( i, name ) { 4611 jQuery.attrHooks[ name ] = { 4612 set: function( elem, value ) { 4613 if ( value === "" ) { 4614 elem.setAttribute( name, "auto" ); 4615 return value; 4616 } 4617 } 4618 }; 4619 }); 4620} 4621 4622 4623// Some attributes require a special call on IE 4624// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx 4625if ( !jQuery.support.hrefNormalized ) { 4626 // href/src property should get the full normalized URL (#10299/#12915) 4627 jQuery.each([ "href", "src" ], function( i, name ) { 4628 jQuery.propHooks[ name ] = { 4629 get: function( elem ) { 4630 return elem.getAttribute( name, 4 ); 4631 } 4632 }; 4633 }); 4634} 4635 4636if ( !jQuery.support.style ) { 4637 jQuery.attrHooks.style = { 4638 get: function( elem ) { 4639 // Return undefined in the case of empty string 4640 // Note: IE uppercases css property names, but if we were to .toLowerCase() 4641 // .cssText, that would destroy case senstitivity in URL's, like in "background" 4642 return elem.style.cssText || undefined; 4643 }, 4644 set: function( elem, value ) { 4645 return ( elem.style.cssText = value + "" ); 4646 } 4647 }; 4648} 4649 4650// Safari mis-reports the default selected property of an option 4651// Accessing the parent's selectedIndex property fixes it 4652if ( !jQuery.support.optSelected ) { 4653 jQuery.propHooks.selected = { 4654 get: function( elem ) { 4655 var parent = elem.parentNode; 4656 4657 if ( parent ) { 4658 parent.selectedIndex; 4659 4660 // Make sure that it also works with optgroups, see #5701 4661 if ( parent.parentNode ) { 4662 parent.parentNode.selectedIndex; 4663 } 4664 } 4665 return null; 4666 } 4667 }; 4668} 4669 4670jQuery.each([ 4671 "tabIndex", 4672 "readOnly", 4673 "maxLength", 4674 "cellSpacing", 4675 "cellPadding", 4676 "rowSpan", 4677 "colSpan", 4678 "useMap", 4679 "frameBorder", 4680 "contentEditable" 4681], function() { 4682 jQuery.propFix[ this.toLowerCase() ] = this; 4683}); 4684 4685// IE6/7 call enctype encoding 4686if ( !jQuery.support.enctype ) { 4687 jQuery.propFix.enctype = "encoding"; 4688} 4689 4690// Radios and checkboxes getter/setter 4691jQuery.each([ "radio", "checkbox" ], function() { 4692 jQuery.valHooks[ this ] = { 4693 set: function( elem, value ) { 4694 if ( jQuery.isArray( value ) ) { 4695 return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); 4696 } 4697 } 4698 }; 4699 if ( !jQuery.support.checkOn ) { 4700 jQuery.valHooks[ this ].get = function( elem ) { 4701 // Support: Webkit 4702 // "" is returned instead of "on" if a value isn't specified 4703 return elem.getAttribute("value") === null ? "on" : elem.value; 4704 }; 4705 } 4706}); 4707var rformElems = /^(?:input|select|textarea)$/i, 4708 rkeyEvent = /^key/, 4709 rmouseEvent = /^(?:mouse|contextmenu)|click/, 4710 rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, 4711 rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; 4712 4713function returnTrue() { 4714 return true; 4715} 4716 4717function returnFalse() { 4718 return false; 4719} 4720 4721function safeActiveElement() { 4722 try { 4723 return document.activeElement; 4724 } catch ( err ) { } 4725} 4726 4727/* 4728 * Helper functions for managing events -- not part of the public interface. 4729 * Props to Dean Edwards' addEvent library for many of the ideas. 4730 */ 4731jQuery.event = { 4732 4733 global: {}, 4734 4735 add: function( elem, types, handler, data, selector ) { 4736 var tmp, events, t, handleObjIn, 4737 special, eventHandle, handleObj, 4738 handlers, type, namespaces, origType, 4739 elemData = jQuery._data( elem ); 4740 4741 // Don't attach events to noData or text/comment nodes (but allow plain objects) 4742 if ( !elemData ) { 4743 return; 4744 } 4745 4746 // Caller can pass in an object of custom data in lieu of the handler 4747 if ( handler.handler ) { 4748 handleObjIn = handler; 4749 handler = handleObjIn.handler; 4750 selector = handleObjIn.selector; 4751 } 4752 4753 // Make sure that the handler has a unique ID, used to find/remove it later 4754 if ( !handler.guid ) { 4755 handler.guid = jQuery.guid++; 4756 } 4757 4758 // Init the element's event structure and main handler, if this is the first 4759 if ( !(events = elemData.events) ) { 4760 events = elemData.events = {}; 4761 } 4762 if ( !(eventHandle = elemData.handle) ) { 4763 eventHandle = elemData.handle = function( e ) { 4764 // Discard the second event of a jQuery.event.trigger() and 4765 // when an event is called after a page has unloaded 4766 return typeof jQuery !== core_strundefined && (!e || jQuery.event.triggered !== e.type) ? 4767 jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : 4768 undefined; 4769 }; 4770 // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events 4771 eventHandle.elem = elem; 4772 } 4773 4774 // Handle multiple events separated by a space 4775 types = ( types || "" ).match( core_rnotwhite ) || [""]; 4776 t = types.length; 4777 while ( t-- ) { 4778 tmp = rtypenamespace.exec( types[t] ) || []; 4779 type = origType = tmp[1]; 4780 namespaces = ( tmp[2] || "" ).split( "." ).sort(); 4781 4782 // There *must* be a type, no attaching namespace-only handlers 4783 if ( !type ) { 4784 continue; 4785 } 4786 4787 // If event changes its type, use the special event handlers for the changed type 4788 special = jQuery.event.special[ type ] || {}; 4789 4790 // If selector defined, determine special event api type, otherwise given type 4791 type = ( selector ? special.delegateType : special.bindType ) || type; 4792 4793 // Update special based on newly reset type 4794 special = jQuery.event.special[ type ] || {}; 4795 4796 // handleObj is passed to all event handlers 4797 handleObj = jQuery.extend({ 4798 type: type, 4799 origType: origType, 4800 data: data, 4801 handler: handler, 4802 guid: handler.guid, 4803 selector: selector, 4804 needsContext: selector && jQuery.expr.match.needsContext.test( selector ), 4805 namespace: namespaces.join(".") 4806 }, handleObjIn ); 4807 4808 // Init the event handler queue if we're the first 4809 if ( !(handlers = events[ type ]) ) { 4810 handlers = events[ type ] = []; 4811 handlers.delegateCount = 0; 4812 4813 // Only use addEventListener/attachEvent if the special events handler returns false 4814 if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { 4815 // Bind the global event handler to the element 4816 if ( elem.addEventListener ) { 4817 elem.addEventListener( type, eventHandle, false ); 4818 4819 } else if ( elem.attachEvent ) { 4820 elem.attachEvent( "on" + type, eventHandle ); 4821 } 4822 } 4823 } 4824 4825 if ( special.add ) { 4826 special.add.call( elem, handleObj ); 4827 4828 if ( !handleObj.handler.guid ) { 4829 handleObj.handler.guid = handler.guid; 4830 } 4831 } 4832 4833 // Add to the element's handler list, delegates in front 4834 if ( selector ) { 4835 handlers.splice( handlers.delegateCount++, 0, handleObj ); 4836 } else { 4837 handlers.push( handleObj ); 4838 } 4839 4840 // Keep track of which events have ever been used, for event optimization 4841 jQuery.event.global[ type ] = true; 4842 } 4843 4844 // Nullify elem to prevent memory leaks in IE 4845 elem = null; 4846 }, 4847 4848 // Detach an event or set of events from an element 4849 remove: function( elem, types, handler, selector, mappedTypes ) { 4850 var j, handleObj, tmp, 4851 origCount, t, events, 4852 special, handlers, type, 4853 namespaces, origType, 4854 elemData = jQuery.hasData( elem ) && jQuery._data( elem ); 4855 4856 if ( !elemData || !(events = elemData.events) ) { 4857 return; 4858 } 4859 4860 // Once for each type.namespace in types; type may be omitted 4861 types = ( types || "" ).match( core_rnotwhite ) || [""]; 4862 t = types.length; 4863 while ( t-- ) { 4864 tmp = rtypenamespace.exec( types[t] ) || []; 4865 type = origType = tmp[1]; 4866 namespaces = ( tmp[2] || "" ).split( "." ).sort(); 4867 4868 // Unbind all events (on this namespace, if provided) for the element 4869 if ( !type ) { 4870 for ( type in events ) { 4871 jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); 4872 } 4873 continue; 4874 } 4875 4876 special = jQuery.event.special[ type ] || {}; 4877 type = ( selector ? special.delegateType : special.bindType ) || type; 4878 handlers = events[ type ] || []; 4879 tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ); 4880 4881 // Remove matching events 4882 origCount = j = handlers.length; 4883 while ( j-- ) { 4884 handleObj = handlers[ j ]; 4885 4886 if ( ( mappedTypes || origType === handleObj.origType ) && 4887 ( !handler || handler.guid === handleObj.guid ) && 4888 ( !tmp || tmp.test( handleObj.namespace ) ) && 4889 ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { 4890 handlers.splice( j, 1 ); 4891 4892 if ( handleObj.selector ) { 4893 handlers.delegateCount--; 4894 } 4895 if ( special.remove ) { 4896 special.remove.call( elem, handleObj ); 4897 } 4898 } 4899 } 4900 4901 // Remove generic event handler if we removed something and no more handlers exist 4902 // (avoids potential for endless recursion during removal of special event handlers) 4903 if ( origCount && !handlers.length ) { 4904 if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { 4905 jQuery.removeEvent( elem, type, elemData.handle ); 4906 } 4907 4908 delete events[ type ]; 4909 } 4910 } 4911 4912 // Remove the expando if it's no longer used 4913 if ( jQuery.isEmptyObject( events ) ) { 4914 delete elemData.handle; 4915 4916 // removeData also checks for emptiness and clears the expando if empty 4917 // so use it instead of delete 4918 jQuery._removeData( elem, "events" ); 4919 } 4920 }, 4921 4922 trigger: function( event, data, elem, onlyHandlers ) { 4923 var handle, ontype, cur, 4924 bubbleType, special, tmp, i, 4925 eventPath = [ elem || document ], 4926 type = core_hasOwn.call( event, "type" ) ? event.type : event, 4927 namespaces = core_hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; 4928 4929 cur = tmp = elem = elem || document; 4930 4931 // Don't do events on text and comment nodes 4932 if ( elem.nodeType === 3 || elem.nodeType === 8 ) { 4933 return; 4934 } 4935 4936 // focus/blur morphs to focusin/out; ensure we're not firing them right now 4937 if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { 4938 return; 4939 } 4940 4941 if ( type.indexOf(".") >= 0 ) { 4942 // Namespaced trigger; create a regexp to match event type in handle() 4943 namespaces = type.split("."); 4944 type = namespaces.shift(); 4945 namespaces.sort(); 4946 } 4947 ontype = type.indexOf(":") < 0 && "on" + type; 4948 4949 // Caller can pass in a jQuery.Event object, Object, or just an event type string 4950 event = event[ jQuery.expando ] ? 4951 event : 4952 new jQuery.Event( type, typeof event === "object" && event ); 4953 4954 // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) 4955 event.isTrigger = onlyHandlers ? 2 : 3; 4956 event.namespace = namespaces.join("."); 4957 event.namespace_re = event.namespace ? 4958 new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) : 4959 null; 4960 4961 // Clean up the event in case it is being reused 4962 event.result = undefined; 4963 if ( !event.target ) { 4964 event.target = elem; 4965 } 4966 4967 // Clone any incoming data and prepend the event, creating the handler arg list 4968 data = data == null ? 4969 [ event ] : 4970 jQuery.makeArray( data, [ event ] ); 4971 4972 // Allow special events to draw outside the lines 4973 special = jQuery.event.special[ type ] || {}; 4974 if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { 4975 return; 4976 } 4977 4978 // Determine event propagation path in advance, per W3C events spec (#9951) 4979 // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) 4980 if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { 4981 4982 bubbleType = special.delegateType || type; 4983 if ( !rfocusMorph.test( bubbleType + type ) ) { 4984 cur = cur.parentNode; 4985 } 4986 for ( ; cur; cur = cur.parentNode ) { 4987 eventPath.push( cur ); 4988 tmp = cur; 4989 } 4990 4991 // Only add window if we got to document (e.g., not plain obj or detached DOM) 4992 if ( tmp === (elem.ownerDocument || document) ) { 4993 eventPath.push( tmp.defaultView || tmp.parentWindow || window ); 4994 } 4995 } 4996 4997 // Fire handlers on the event path 4998 i = 0; 4999 while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) { 5000 5001 event.type = i > 1 ? 5002 bubbleType : 5003 special.bindType || type; 5004 5005 // jQuery handler 5006 handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); 5007 if ( handle ) { 5008 handle.apply( cur, data ); 5009 } 5010 5011 // Native handler 5012 handle = ontype && cur[ ontype ]; 5013 if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) { 5014 event.preventDefault(); 5015 } 5016 } 5017 event.type = type; 5018 5019 // If nobody prevented the default action, do it now 5020 if ( !onlyHandlers && !event.isDefaultPrevented() ) { 5021 5022 if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) && 5023 jQuery.acceptData( elem ) ) { 5024 5025 // Call a native DOM method on the target with the same name name as the event. 5026 // Can't use an .isFunction() check here because IE6/7 fails that test. 5027 // Don't do default actions on window, that's where global variables be (#6170) 5028 if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) { 5029 5030 // Don't re-trigger an onFOO event when we call its FOO() method 5031 tmp = elem[ ontype ]; 5032 5033 if ( tmp ) { 5034 elem[ ontype ] = null; 5035 } 5036 5037 // Prevent re-triggering of the same event, since we already bubbled it above 5038 jQuery.event.triggered = type; 5039 try { 5040 elem[ type ](); 5041 } catch ( e ) { 5042 // IE<9 dies on focus/blur to hidden element (#1486,#12518) 5043 // only reproducible on winXP IE8 native, not IE9 in IE8 mode 5044 } 5045 jQuery.event.triggered = undefined; 5046 5047 if ( tmp ) { 5048 elem[ ontype ] = tmp; 5049 } 5050 } 5051 } 5052 } 5053 5054 return event.result; 5055 }, 5056 5057 dispatch: function( event ) { 5058 5059 // Make a writable jQuery.Event from the native event object 5060 event = jQuery.event.fix( event ); 5061 5062 var i, ret, handleObj, matched, j, 5063 handlerQueue = [], 5064 args = core_
vendor: 7,307 bytes, lines 5064-5309
5064slice.call( arguments ), 5065 handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [], 5066 special = jQuery.event.special[ event.type ] || {}; 5067 5068 // Use the fix-ed jQuery.Event rather than the (read-only) native event 5069 args[0] = event; 5070 event.delegateTarget = this; 5071 5072 // Call the preDispatch hook for the mapped type, and let it bail if desired 5073 if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { 5074 return; 5075 } 5076 5077 // Determine handlers 5078 handlerQueue = jQuery.event.handlers.call( this, event, handlers ); 5079 5080 // Run delegates first; they may want to stop propagation beneath us 5081 i = 0; 5082 while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) { 5083 event.currentTarget = matched.elem; 5084 5085 j = 0; 5086 while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { 5087 5088 // Triggered event must either 1) have no namespace, or 5089 // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). 5090 if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) { 5091 5092 event.handleObj = handleObj; 5093 event.data = handleObj.data; 5094 5095 ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) 5096 .apply( matched.elem, args ); 5097 5098 if ( ret !== undefined ) { 5099 if ( (event.result = ret) === false ) { 5100 event.preventDefault(); 5101 event.stopPropagation(); 5102 } 5103 } 5104 } 5105 } 5106 } 5107 5108 // Call the postDispatch hook for the mapped type 5109 if ( special.postDispatch ) { 5110 special.postDispatch.call( this, event ); 5111 } 5112 5113 return event.result; 5114 }, 5115 5116 handlers: function( event, handlers ) { 5117 var sel, handleObj, matches, i, 5118 handlerQueue = [], 5119 delegateCount = handlers.delegateCount, 5120 cur = event.target; 5121 5122 // Find delegate handlers 5123 // Black-hole SVG <use> instance trees (#13180) 5124 // Avoid non-left-click bubbling in Firefox (#3861) 5125 if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { 5126 5127 /* jshint eqeqeq: false */ 5128 for ( ; cur != this; cur = cur.parentNode || this ) { 5129 /* jshint eqeqeq: true */ 5130 5131 // Don't check non-elements (#13208) 5132 // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) 5133 if ( cur.nodeType === 1 && (cur.disabled !== true || event.type !== "click") ) { 5134 matches = []; 5135 for ( i = 0; i < delegateCount; i++ ) { 5136 handleObj = handlers[ i ]; 5137 5138 // Don't conflict with Object.prototype properties (#13203) 5139 sel = handleObj.selector + " "; 5140 5141 if ( matches[ sel ] === undefined ) { 5142 matches[ sel ] = handleObj.needsContext ? 5143 jQuery( sel, this ).index( cur ) >= 0 : 5144 jQuery.find( sel, this, null, [ cur ] ).length; 5145 } 5146 if ( matches[ sel ] ) { 5147 matches.push( handleObj ); 5148 } 5149 } 5150 if ( matches.length ) { 5151 handlerQueue.push({ elem: cur, handlers: matches }); 5152 } 5153 } 5154 } 5155 } 5156 5157 // Add the remaining (directly-bound) handlers 5158 if ( delegateCount < handlers.length ) { 5159 handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); 5160 } 5161 5162 return handlerQueue; 5163 }, 5164 5165 fix: function( event ) { 5166 if ( event[ jQuery.expando ] ) { 5167 return event; 5168 } 5169 5170 // Create a writable copy of the event object and normalize some properties 5171 var i, prop, copy, 5172 type = event.type, 5173 originalEvent = event, 5174 fixHook = this.fixHooks[ type ]; 5175 5176 if ( !fixHook ) { 5177 this.fixHooks[ type ] = fixHook = 5178 rmouseEvent.test( type ) ? this.mouseHooks : 5179 rkeyEvent.test( type ) ? this.keyHooks : 5180 {}; 5181 } 5182 copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; 5183 5184 event = new jQuery.Event( originalEvent ); 5185 5186 i = copy.length; 5187 while ( i-- ) { 5188 prop = copy[ i ]; 5189 event[ prop ] = originalEvent[ prop ]; 5190 } 5191 5192 // Support: IE<9 5193 // Fix target property (#1925) 5194 if ( !event.target ) { 5195 event.target = originalEvent.srcElement || document; 5196 } 5197 5198 // Support: Chrome 23+, Safari? 5199 // Target should not be a text node (#504, #13143) 5200 if ( event.target.nodeType === 3 ) { 5201 event.target = event.target.parentNode; 5202 } 5203 5204 // Support: IE<9 5205 // For mouse/key events, metaKey==false if it's undefined (#3368, #11328) 5206 event.metaKey = !!event.metaKey; 5207 5208 return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; 5209 }, 5210 5211 // Includes some event props shared by KeyEvent and MouseEvent 5212 props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), 5213 5214 fixHooks: {}, 5215 5216 keyHooks: { 5217 props: "char charCode key keyCode".split(" "), 5218 filter: function( event, original ) { 5219 5220 // Add which for key events 5221 if ( event.which == null ) { 5222 event.which = original.charCode != null ? original.charCode : original.keyCode; 5223 } 5224 5225 return event; 5226 } 5227 }, 5228 5229 mouseHooks: { 5230 props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), 5231 filter: function( event, original ) { 5232 var body, eventDoc, doc, 5233 button = original.button, 5234 fromElement = original.fromElement; 5235 5236 // Calculate pageX/Y if missing and clientX/Y available 5237 if ( event.pageX == null && original.clientX != null ) { 5238 eventDoc = event.target.ownerDocument || document; 5239 doc = eventDoc.documentElement; 5240 body = eventDoc.body; 5241 5242 event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); 5243 event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); 5244 } 5245 5246 // Add relatedTarget, if necessary 5247 if ( !event.relatedTarget && fromElement ) { 5248 event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; 5249 } 5250 5251 // Add which for click: 1 === left; 2 === middle; 3 === right 5252 // Note: button is not normalized, so don't use it 5253 if ( !event.which && button !== undefined ) { 5254 event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); 5255 } 5256 5257 return event; 5258 } 5259 }, 5260 5261 special: { 5262 load: { 5263 // Prevent triggered image.load events from bubbling to window.load 5264 noBubble: true 5265 }, 5266 focus: { 5267 // Fire native event if possible so blur/focus sequence is correct 5268 trigger: function() { 5269 if ( this !== safeActiveElement() && this.focus ) { 5270 try { 5271 this.focus(); 5272 return false; 5273 } catch ( e ) { 5274 // Support: IE<9 5275 // If we error on focus to hidden element (#1486, #12518), 5276 // let .trigger() run the handlers 5277 } 5278 } 5279 }, 5280 delegateType: "focusin" 5281 }, 5282 blur: { 5283 trigger: function() { 5284 if ( this === safeActiveElement() && this.blur ) { 5285 this.blur(); 5286 return false; 5287 } 5288 }, 5289 delegateType: "focusout" 5290 }, 5291 click: { 5292 // For checkbox, fire native event so checked state will be right 5293 trigger: function() { 5294 if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) { 5295 this.click(); 5296 return false; 5297 } 5298 }, 5299 5300 // For cross-browser consistency, don't fire native .click() on links 5301 _default: function( event ) { 5302 return jQuery.nodeName( event.target, "a" ); 5303 } 5304 }, 5305 5306 beforeunload: { 5307 postDispatch: function( event ) { 5308 5309 //
vendor: 4,409 bytes, lines 5309-5476
5309Even when returnValue equals to undefined Firefox will still show alert 5310 if ( event.result !== undefined ) { 5311 event.originalEvent.returnValue = event.result; 5312 } 5313 } 5314 } 5315 }, 5316 5317 simulate: function( type, elem, event, bubble ) { 5318 // Piggyback on a donor event to simulate a different one. 5319 // Fake originalEvent to avoid donor's stopPropagation, but if the 5320 // simulated event prevents default then we do the same on the donor. 5321 var e = jQuery.extend( 5322 new jQuery.Event(), 5323 event, 5324 { 5325 type: type, 5326 isSimulated: true, 5327 originalEvent: {} 5328 } 5329 ); 5330 if ( bubble ) { 5331 jQuery.event.trigger( e, null, elem ); 5332 } else { 5333 jQuery.event.dispatch.call( elem, e ); 5334 } 5335 if ( e.isDefaultPrevented() ) { 5336 event.preventDefault(); 5337 } 5338 } 5339}; 5340 5341jQuery.removeEvent = document.removeEventListener ? 5342 function( elem, type, handle ) { 5343 if ( elem.removeEventListener ) { 5344 elem.removeEventListener( type, handle, false ); 5345 } 5346 } : 5347 function( elem, type, handle ) { 5348 var name = "on" + type; 5349 5350 if ( elem.detachEvent ) { 5351 5352 // #8545, #7054, preventing memory leaks for custom events in IE6-8 5353 // detachEvent needed property on element, by name of that event, to properly expose it to GC 5354 if ( typeof elem[ name ] === core_strundefined ) { 5355 elem[ name ] = null; 5356 } 5357 5358 elem.detachEvent( name, handle ); 5359 } 5360 }; 5361 5362jQuery.Event = function( src, props ) { 5363 // Allow instantiation without the 'new' keyword 5364 if ( !(this instanceof jQuery.Event) ) { 5365 return new jQuery.Event( src, props ); 5366 } 5367 5368 // Event object 5369 if ( src && src.type ) { 5370 this.originalEvent = src; 5371 this.type = src.type; 5372 5373 // Events bubbling up the document may have been marked as prevented 5374 // by a handler lower down the tree; reflect the correct value. 5375 this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false || 5376 src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; 5377 5378 // Event type 5379 } else { 5380 this.type = src; 5381 } 5382 5383 // Put explicitly provided properties onto the event object 5384 if ( props ) { 5385 jQuery.extend( this, props ); 5386 } 5387 5388 // Create a timestamp if incoming event doesn't have one 5389 this.timeStamp = src && src.timeStamp || jQuery.now(); 5390 5391 // Mark it as fixed 5392 this[ jQuery.expando ] = true; 5393}; 5394 5395// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding 5396// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html 5397jQuery.Event.prototype = { 5398 isDefaultPrevented: returnFalse, 5399 isPropagationStopped: returnFalse, 5400 isImmediatePropagationStopped: returnFalse, 5401 5402 preventDefault: function() { 5403 var e = this.originalEvent; 5404 5405 this.isDefaultPrevented = returnTrue; 5406 if ( !e ) { 5407 return; 5408 } 5409 5410 // If preventDefault exists, run it on the original event 5411 if ( e.preventDefault ) { 5412 e.preventDefault(); 5413 5414 // Support: IE 5415 // Otherwise set the returnValue property of the original event to false 5416 } else { 5417 e.returnValue = false; 5418 } 5419 }, 5420 stopPropagation: function() { 5421 var e = this.originalEvent; 5422 5423 this.isPropagationStopped = returnTrue; 5424 if ( !e ) { 5425 return; 5426 } 5427 // If stopPropagation exists, run it on the original event 5428 if ( e.stopPropagation ) { 5429 e.stopPropagation(); 5430 } 5431 5432 // Support: IE 5433 // Set the cancelBubble property of the original event to true 5434 e.cancelBubble = true; 5435 }, 5436 stopImmediatePropagation: function() { 5437 this.isImmediatePropagationStopped = returnTrue; 5438 this.stopPropagation(); 5439 } 5440}; 5441 5442// Create mouseenter/leave events using mouseover/out and event-time checks 5443jQuery.each({ 5444 mouseenter: "mouseover", 5445 mouseleave: "mouseout" 5446}, function( orig, fix ) { 5447 jQuery.event.special[ orig ] = { 5448 delegateType: fix, 5449 bindType: fix, 5450 5451 handle: function( event ) { 5452 var ret, 5453 target = this, 5454 related = event.relatedTarget, 5455 handleObj = event.handleObj; 5456 5457 // For mousenter/leave call the handler if related is outside the target. 5458 // NB: No relatedTarget if the mouse left/entered the browser window 5459 if ( !related || (related !== target && !jQuery.contains( target, related )) ) { 5460 event.type = handleObj.origType; 5461 ret = handleObj.handler.apply( this, arguments ); 5462 event.type = fix; 5463 } 5464 return ret; 5465 } 5466 }; 5467}); 5468 5469// IE submit delegation 5470if ( !jQuery.support.submitBubbles ) { 5471 5472 jQuery.event.special.submit = { 5473 setup: function() { 5474 // Only need this for delegated form submit events 5475 if ( jQuery.nodeName( this, "form" ) ) { 5476 return false;
vendor: 6,944 bytes, lines 5477-5720
5477 } 5478 5479 // Lazy-add a submit handler when a descendant form may potentially be submitted 5480 jQuery.event.add( this, "click._submit keypress._submit", function( e ) { 5481 // Node name check avoids a VML-related crash in IE (#9807) 5482 var elem = e.target, 5483 form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; 5484 if ( form && !jQuery._data( form, "submitBubbles" ) ) { 5485 jQuery.event.add( form, "submit._submit", function( event ) { 5486 event._submit_bubble = true; 5487 }); 5488 jQuery._data( form, "submitBubbles", true ); 5489 } 5490 }); 5491 // return undefined since we don't need an event listener 5492 }, 5493 5494 postDispatch: function( event ) { 5495 // If form was submitted by the user, bubble the event up the tree 5496 if ( event._submit_bubble ) { 5497 delete event._submit_bubble; 5498 if ( this.parentNode && !event.isTrigger ) { 5499 jQuery.event.simulate( "submit", this.parentNode, event, true ); 5500 } 5501 } 5502 }, 5503 5504 teardown: function() { 5505 // Only need this for delegated form submit events 5506 if ( jQuery.nodeName( this, "form" ) ) { 5507 return false; 5508 } 5509 5510 // Remove delegated handlers; cleanData eventually reaps submit handlers attached above 5511 jQuery.event.remove( this, "._submit" ); 5512 } 5513 }; 5514} 5515 5516// IE change delegation and checkbox/radio fix 5517if ( !jQuery.support.changeBubbles ) { 5518 5519 jQuery.event.special.change = { 5520 5521 setup: function() { 5522 5523 if ( rformElems.test( this.nodeName ) ) { 5524 // IE doesn't fire change on a check/radio until blur; trigger it on click 5525 // after a propertychange. Eat the blur-change in special.change.handle. 5526 // This still fires onchange a second time for check/radio after blur. 5527 if ( this.type === "checkbox" || this.type === "radio" ) { 5528 jQuery.event.add( this, "propertychange._change", function( event ) { 5529 if ( event.originalEvent.propertyName === "checked" ) { 5530 this._just_changed = true; 5531 } 5532 }); 5533 jQuery.event.add( this, "click._change", function( event ) { 5534 if ( this._just_changed && !event.isTrigger ) { 5535 this._just_changed = false; 5536 } 5537 // Allow triggered, simulated change events (#11500) 5538 jQuery.event.simulate( "change", this, event, true ); 5539 }); 5540 } 5541 return false; 5542 } 5543 // Delegated event; lazy-add a change handler on descendant inputs 5544 jQuery.event.add( this, "beforeactivate._change", function( e ) { 5545 var elem = e.target; 5546 5547 if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "changeBubbles" ) ) { 5548 jQuery.event.add( elem, "change._change", function( event ) { 5549 if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { 5550 jQuery.event.simulate( "change", this.parentNode, event, true ); 5551 } 5552 }); 5553 jQuery._data( elem, "changeBubbles", true ); 5554 } 5555 }); 5556 }, 5557 5558 handle: function( event ) { 5559 var elem = event.target; 5560 5561 // Swallow native change events from checkbox/radio, we already triggered them above 5562 if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { 5563 return event.handleObj.handler.apply( this, arguments ); 5564 } 5565 }, 5566 5567 teardown: function() { 5568 jQuery.event.remove( this, "._change" ); 5569 5570 return !rformElems.test( this.nodeName ); 5571 } 5572 }; 5573} 5574 5575// Create "bubbling" focus and blur events 5576if ( !jQuery.support.focusinBubbles ) { 5577 jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { 5578 5579 // Attach a single capturing handler while someone wants focusin/focusout 5580 var attaches = 0, 5581 handler = function( event ) { 5582 jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); 5583 }; 5584 5585 jQuery.event.special[ fix ] = { 5586 setup: function() { 5587 if ( attaches++ === 0 ) { 5588 document.addEventListener( orig, handler, true ); 5589 } 5590 }, 5591 teardown: function() { 5592 if ( --attaches === 0 ) { 5593 document.removeEventListener( orig, handler, true ); 5594 } 5595 } 5596 }; 5597 }); 5598} 5599 5600jQuery.fn.extend({ 5601 5602 on: function( types, selector, data, fn, /*INTERNAL*/ one ) { 5603 var type, origFn; 5604 5605 // Types can be a map of types/handlers 5606 if ( typeof types === "object" ) { 5607 // ( types-Object, selector, data ) 5608 if ( typeof selector !== "string" ) { 5609 // ( types-Object, data ) 5610 data = data || selector; 5611 selector = undefined; 5612 } 5613 for ( type in types ) { 5614 this.on( type, selector, data, types[ type ], one ); 5615 } 5616 return this; 5617 } 5618 5619 if ( data == null && fn == null ) { 5620 // ( types, fn ) 5621 fn = selector; 5622 data = selector = undefined; 5623 } else if ( fn == null ) { 5624 if ( typeof selector === "string" ) { 5625 // ( types, selector, fn ) 5626 fn = data; 5627 data = undefined; 5628 } else { 5629 // ( types, data, fn ) 5630 fn = data; 5631 data = selector; 5632 selector = undefined; 5633 } 5634 } 5635 if ( fn === false ) { 5636 fn = returnFalse; 5637 } else if ( !fn ) { 5638 return this; 5639 } 5640 5641 if ( one === 1 ) { 5642 origFn = fn; 5643 fn = function( event ) { 5644 // Can use an empty set, since event contains the info 5645 jQuery().off( event ); 5646 return origFn.apply( this, arguments ); 5647 }; 5648 // Use same guid so caller can remove using origFn 5649 fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); 5650 } 5651 return this.each( function() { 5652 jQuery.event.add( this, types, fn, data, selector ); 5653 }); 5654 }, 5655 one: function( types, selector, data, fn ) { 5656 return this.on( types, selector, data, fn, 1 ); 5657 }, 5658 off: function( types, selector, fn ) { 5659 var handleObj, type; 5660 if ( types && types.preventDefault && types.handleObj ) { 5661 // ( event ) dispatched jQuery.Event 5662 handleObj = types.handleObj; 5663 jQuery( types.delegateTarget ).off( 5664 handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, 5665 handleObj.selector, 5666 handleObj.handler 5667 ); 5668 return this; 5669 } 5670 if ( typeof types === "object" ) { 5671 // ( types-object [, selector] ) 5672 for ( type in types ) { 5673 this.off( type, selector, types[ type ] ); 5674 } 5675 return this; 5676 } 5677 if ( selector === false || typeof selector === "function" ) { 5678 // ( types [, fn] ) 5679 fn = selector; 5680 selector = undefined; 5681 } 5682 if ( fn === false ) { 5683 fn = returnFalse; 5684 } 5685 return this.each(function() { 5686 jQuery.event.remove( this, types, fn, selector ); 5687 }); 5688 }, 5689 5690 trigger: function( type, data ) { 5691 return this.each(function() { 5692 jQuery.event.trigger( type, data, this ); 5693 }); 5694 }, 5695 triggerHandler: function( type, data ) { 5696 var elem = this[0]; 5697 if ( elem ) { 5698 return jQuery.event.trigger( type, data, elem, true ); 5699 } 5700 } 5701}); 5702var isSimple = /^.[^:#\[\.,]*$/, 5703 rparentsprev = /^(?:parents|prev(?:Until|All))/, 5704 rneedsContext = jQuery.expr.match.needsContext, 5705 // methods guaranteed to produce a unique set when starting from a unique set 5706 guaranteedUnique = { 5707 children: true, 5708 contents: true, 5709 next: true, 5710 prev: true 5711 }; 5712 5713jQuery.fn.extend({ 5714 find: function( selector ) { 5715 var i, 5716 ret = [], 5717 self = this, 5718 len = self.length; 5719 5720 if ( typeof selector !== "string" ) {
vendor: 37,017 bytes, lines 5721-7082
5721 return this.pushStack( jQuery( selector ).filter(function() { 5722 for ( i = 0; i < len; i++ ) { 5723 if ( jQuery.contains( self[ i ], this ) ) { 5724 return true; 5725 } 5726 } 5727 }) ); 5728 } 5729 5730 for ( i = 0; i < len; i++ ) { 5731 jQuery.find( selector, self[ i ], ret ); 5732 } 5733 5734 // Needed because $( selector, context ) becomes $( context ).find( selector ) 5735 ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); 5736 ret.selector = this.selector ? this.selector + " " + selector : selector; 5737 return ret; 5738 }, 5739 5740 has: function( target ) { 5741 var i, 5742 targets = jQuery( target, this ), 5743 len = targets.length; 5744 5745 return this.filter(function() { 5746 for ( i = 0; i < len; i++ ) { 5747 if ( jQuery.contains( this, targets[i] ) ) { 5748 return true; 5749 } 5750 } 5751 }); 5752 }, 5753 5754 not: function( selector ) { 5755 return this.pushStack( winnow(this, selector || [], true) ); 5756 }, 5757 5758 filter: function( selector ) { 5759 return this.pushStack( winnow(this, selector || [], false) ); 5760 }, 5761 5762 is: function( selector ) { 5763 return !!winnow( 5764 this, 5765 5766 // If this is a positional/relative selector, check membership in the returned set 5767 // so $("p:first").is("p:last") won't return true for a doc with two "p". 5768 typeof selector === "string" && rneedsContext.test( selector ) ? 5769 jQuery( selector ) : 5770 selector || [], 5771 false 5772 ).length; 5773 }, 5774 5775 closest: function( selectors, context ) { 5776 var cur, 5777 i = 0, 5778 l = this.length, 5779 ret = [], 5780 pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? 5781 jQuery( selectors, context || this.context ) : 5782 0; 5783 5784 for ( ; i < l; i++ ) { 5785 for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) { 5786 // Always skip document fragments 5787 if ( cur.nodeType < 11 && (pos ? 5788 pos.index(cur) > -1 : 5789 5790 // Don't pass non-elements to Sizzle 5791 cur.nodeType === 1 && 5792 jQuery.find.matchesSelector(cur, selectors)) ) { 5793 5794 cur = ret.push( cur ); 5795 break; 5796 } 5797 } 5798 } 5799 5800 return this.pushStack( ret.length > 1 ? jQuery.unique( ret ) : ret ); 5801 }, 5802 5803 // Determine the position of an element within 5804 // the matched set of elements 5805 index: function( elem ) { 5806 5807 // No argument, return index in parent 5808 if ( !elem ) { 5809 return ( this[0] && this[0].parentNode ) ? this.first().prevAll().length : -1; 5810 } 5811 5812 // index in selector 5813 if ( typeof elem === "string" ) { 5814 return jQuery.inArray( this[0], jQuery( elem ) ); 5815 } 5816 5817 // Locate the position of the desired element 5818 return jQuery.inArray( 5819 // If it receives a jQuery object, the first element is used 5820 elem.jquery ? elem[0] : elem, this ); 5821 }, 5822 5823 add: function( selector, context ) { 5824 var set = typeof selector === "string" ? 5825 jQuery( selector, context ) : 5826 jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), 5827 all = jQuery.merge( this.get(), set ); 5828 5829 return this.pushStack( jQuery.unique(all) ); 5830 }, 5831 5832 addBack: function( selector ) { 5833 return this.add( selector == null ? 5834 this.prevObject : this.prevObject.filter(selector) 5835 ); 5836 } 5837}); 5838 5839function sibling( cur, dir ) { 5840 do { 5841 cur = cur[ dir ]; 5842 } while ( cur && cur.nodeType !== 1 ); 5843 5844 return cur; 5845} 5846 5847jQuery.each({ 5848 parent: function( elem ) { 5849 var parent = elem.parentNode; 5850 return parent && parent.nodeType !== 11 ? parent : null; 5851 }, 5852 parents: function( elem ) { 5853 return jQuery.dir( elem, "parentNode" ); 5854 }, 5855 parentsUntil: function( elem, i, until ) { 5856 return jQuery.dir( elem, "parentNode", until ); 5857 }, 5858 next: function( elem ) { 5859 return sibling( elem, "nextSibling" ); 5860 }, 5861 prev: function( elem ) { 5862 return sibling( elem, "previousSibling" ); 5863 }, 5864 nextAll: function( elem ) { 5865 return jQuery.dir( elem, "nextSibling" ); 5866 }, 5867 prevAll: function( elem ) { 5868 return jQuery.dir( elem, "previousSibling" ); 5869 }, 5870 nextUntil: function( elem, i, until ) { 5871 return jQuery.dir( elem, "nextSibling", until ); 5872 }, 5873 prevUntil: function( elem, i, until ) { 5874 return jQuery.dir( elem, "previousSibling", until ); 5875 }, 5876 siblings: function( elem ) { 5877 return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); 5878 }, 5879 children: function( elem ) { 5880 return jQuery.sibling( elem.firstChild ); 5881 }, 5882 contents: function( elem ) { 5883 return jQuery.nodeName( elem, "iframe" ) ? 5884 elem.contentDocument || elem.contentWindow.document : 5885 jQuery.merge( [], elem.childNodes ); 5886 } 5887}, function( name, fn ) { 5888 jQuery.fn[ name ] = function( until, selector ) { 5889 var ret = jQuery.map( this, fn, until ); 5890 5891 if ( name.slice( -5 ) !== "Until" ) { 5892 selector = until; 5893 } 5894 5895 if ( selector && typeof selector === "string" ) { 5896 ret = jQuery.filter( selector, ret ); 5897 } 5898 5899 if ( this.length > 1 ) { 5900 // Remove duplicates 5901 if ( !guaranteedUnique[ name ] ) { 5902 ret = jQuery.unique( ret ); 5903 } 5904 5905 // Reverse order for parents* and prev-derivatives 5906 if ( rparentsprev.test( name ) ) { 5907 ret = ret.reverse(); 5908 } 5909 } 5910 5911 return this.pushStack( ret ); 5912 }; 5913}); 5914 5915jQuery.extend({ 5916 filter: function( expr, elems, not ) { 5917 var elem = elems[ 0 ]; 5918 5919 if ( not ) { 5920 expr = ":not(" + expr + ")"; 5921 } 5922 5923 return elems.length === 1 && elem.nodeType === 1 ? 5924 jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] : 5925 jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { 5926 return elem.nodeType === 1; 5927 })); 5928 }, 5929 5930 dir: function( elem, dir, until ) { 5931 var matched = [], 5932 cur = elem[ dir ]; 5933 5934 while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { 5935 if ( cur.nodeType === 1 ) { 5936 matched.push( cur ); 5937 } 5938 cur = cur[dir]; 5939 } 5940 return matched; 5941 }, 5942 5943 sibling: function( n, elem ) { 5944 var r = []; 5945 5946 for ( ; n; n = n.nextSibling ) { 5947 if ( n.nodeType === 1 && n !== elem ) { 5948 r.push( n ); 5949 } 5950 } 5951 5952 return r; 5953 } 5954}); 5955 5956// Implement the identical functionality for filter and not 5957function winnow( elements, qualifier, not ) { 5958 if ( jQuery.isFunction( qualifier ) ) { 5959 return jQuery.grep( elements, function( elem, i ) { 5960 /* jshint -W018 */ 5961 return !!qualifier.call( elem, i, elem ) !== not; 5962 }); 5963 5964 } 5965 5966 if ( qualifier.nodeType ) { 5967 return jQuery.grep( elements, function( elem ) { 5968 return ( elem === qualifier ) !== not; 5969 }); 5970 5971 } 5972 5973 if ( typeof qualifier === "string" ) { 5974 if ( isSimple.test( qualifier ) ) { 5975 return jQuery.filter( qualifier, elements, not ); 5976 } 5977 5978 qualifier = jQuery.filter( qualifier, elements ); 5979 } 5980 5981 return jQuery.grep( elements, function( elem ) { 5982 return ( jQuery.inArray( elem, qualifier ) >= 0 ) !== not; 5983 }); 5984} 5985function createSafeFragment( document ) { 5986 var list = nodeNames.split( "|" ), 5987 safeFrag = document.createDocumentFragment(); 5988 5989 if ( safeFrag.createElement ) { 5990 while ( list.length ) { 5991 safeFrag.createElement( 5992 list.pop() 5993 ); 5994 } 5995 } 5996 return safeFrag; 5997} 5998 5999var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" + 6000 "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", 6001 rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g, 6002 rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"), 6003 rleadingWhitespace = /^\s+/, 6004 rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, 6005 rtagName = /<([\w:]+)/, 6006 rtbody = /<tbody/i, 6007 rhtml = /<|&#?\w+;/, 6008 rnoInnerhtml = /<(?:script|style|link)/i, 6009 manipulation_rcheckableType = /^(?:checkbox|radio)$/i, 6010 // checked="checked" or checked 6011 rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, 6012 rscriptType = /^$|\/(?:java|ecma)script/i, 6013 rscriptTypeMasked = /^true\/(.*)/, 6014 rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g, 6015 6016 // We have to close these tags to support XHTML (#13200) 6017 wrapMap = { 6018 option: [ 1, "<select multiple='multiple'>", "</select>" ], 6019 legend: [ 1, "<fieldset>", "</fieldset>" ], 6020 area: [ 1, "<map>", "</map>" ], 6021 param: [ 1, "<object>", "</object>" ], 6022 thead: [ 1, "<table>", "</table>" ], 6023 tr: [ 2, "<table><tbody>", "</tbody></table>" ], 6024 col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ], 6025 td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ], 6026 6027 // IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags, 6028 // unless wrapped in a div with non-breaking characters in front of it. 6029 _default: jQuery.support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X<div>", "</div>" ] 6030 }, 6031 safeFragment = createSafeFragment( document ), 6032 fragmentDiv = safeFragment.appendChild( document.createElement("div") ); 6033 6034wrapMap.optgroup = wrapMap.option; 6035wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; 6036wrapMap.th = wrapMap.td; 6037 6038jQuery.fn.extend({ 6039 text: function( value ) { 6040 return jQuery.access( this, function( value ) { 6041 return value === undefined ? 6042 jQuery.text( this ) : 6043 this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) ); 6044 }, null, value, arguments.length ); 6045 }, 6046 6047 append: function() { 6048 return this.domManip( arguments, function( elem ) { 6049 if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { 6050 var target = manipulationTarget( this, elem ); 6051 target.appendChild( elem ); 6052 } 6053 }); 6054 }, 6055 6056 prepend: function() { 6057 return this.domManip( arguments, function( elem ) { 6058 if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { 6059 var target = manipulationTarget( this, elem ); 6060 target.insertBefore( elem, target.firstChild ); 6061 } 6062 }); 6063 }, 6064 6065 before: function() { 6066 return this.domManip( arguments, function( elem ) { 6067 if ( this.parentNode ) { 6068 this.parentNode.insertBefore( elem, this ); 6069 } 6070 }); 6071 }, 6072 6073 after: function() { 6074 return this.domManip( arguments, function( elem ) { 6075 if ( this.parentNode ) { 6076 this.parentNode.insertBefore( elem, this.nextSibling ); 6077 } 6078 }); 6079 }, 6080 6081 // keepData is for internal use only--do not document 6082 remove: function( selector, keepData ) { 6083 var elem, 6084 elems = selector ? jQuery.filter( selector, this ) : this, 6085 i = 0; 6086 6087 for ( ; (elem = elems[i]) != null; i++ ) { 6088 6089 if ( !keepData && elem.nodeType === 1 ) { 6090 jQuery.cleanData( getAll( elem ) ); 6091 } 6092 6093 if ( elem.parentNode ) { 6094 if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { 6095 setGlobalEval( getAll( elem, "script" ) ); 6096 } 6097 elem.parentNode.removeChild( elem ); 6098 } 6099 } 6100 6101 return this; 6102 }, 6103 6104 empty: function() { 6105 var elem, 6106 i = 0; 6107 6108 for ( ; (elem = this[i]) != null; i++ ) { 6109 // Remove element nodes and prevent memory leaks 6110 if ( elem.nodeType === 1 ) { 6111 jQuery.cleanData( getAll( elem, false ) ); 6112 } 6113 6114 // Remove any remaining nodes 6115 while ( elem.firstChild ) { 6116 elem.removeChild( elem.firstChild ); 6117 } 6118 6119 // If this is a select, ensure that it displays empty (#12336) 6120 // Support: IE<9 6121 if ( elem.options && jQuery.nodeName( elem, "select" ) ) { 6122 elem.options.length = 0; 6123 } 6124 } 6125 6126 return this; 6127 }, 6128 6129 clone: function( dataAndEvents, deepDataAndEvents ) { 6130 dataAndEvents = dataAndEvents == null ? false : dataAndEvents; 6131 deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; 6132 6133 return this.map( function () { 6134 return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); 6135 }); 6136 }, 6137 6138 html: function( value ) { 6139 return jQuery.access( this, function( value ) { 6140 var elem = this[0] || {}, 6141 i = 0, 6142 l = this.length; 6143 6144 if ( value === undefined ) { 6145 return elem.nodeType === 1 ? 6146 elem.innerHTML.replace( rinlinejQuery, "" ) : 6147 undefined; 6148 } 6149 6150 // See if we can take a shortcut and just use innerHTML 6151 if ( typeof value === "string" && !rnoInnerhtml.test( value ) && 6152 ( jQuery.support.htmlSerialize || !rnoshimcache.test( value ) ) && 6153 ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) && 6154 !wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) { 6155 6156 value = value.replace( rxhtmlTag, "<$1></$2>" ); 6157 6158 try { 6159 for (; i < l; i++ ) { 6160 // Remove element nodes and prevent memory leaks 6161 elem = this[i] || {}; 6162 if ( elem.nodeType === 1 ) { 6163 jQuery.cleanData( getAll( elem, false ) ); 6164 elem.innerHTML = value; 6165 } 6166 } 6167 6168 elem = 0; 6169 6170 // If using innerHTML throws an exception, use the fallback method 6171 } catch(e) {} 6172 } 6173 6174 if ( elem ) { 6175 this.empty().append( value ); 6176 } 6177 }, null, value, arguments.length ); 6178 }, 6179 6180 replaceWith: function() { 6181 var 6182 // Snapshot the DOM in case .domManip sweeps something relevant into its fragment 6183 args = jQuery.map( this, function( elem ) { 6184 return [ elem.nextSibling, elem.parentNode ]; 6185 }), 6186 i = 0; 6187 6188 // Make the changes, replacing each context element with the new content 6189 this.domManip( arguments, function( elem ) { 6190 var next = args[ i++ ], 6191 parent = args[ i++ ]; 6192 6193 if ( parent ) { 6194 // Don't use the snapshot next if it has moved (#13810) 6195 if ( next && next.parentNode !== parent ) { 6196 next = this.nextSibling; 6197 } 6198 jQuery( this ).remove(); 6199 parent.insertBefore( elem, next ); 6200 } 6201 // Allow new content to include elements from the context set 6202 }, true ); 6203 6204 // Force removal if there was no new content (e.g., from empty arguments) 6205 return i ? this : this.remove(); 6206 }, 6207 6208 detach: function( selector ) { 6209 return this.remove( selector, true ); 6210 }, 6211 6212 domManip: function( args, callback, allowIntersection ) { 6213 6214 // Flatten any nested arrays 6215 args = core_concat.apply( [], args ); 6216 6217 var first, node, hasScripts, 6218 scripts, doc, fragment, 6219 i = 0, 6220 l = this.length, 6221 set = this, 6222 iNoClone = l - 1, 6223 value = args[0], 6224 isFunction = jQuery.isFunction( value ); 6225 6226 // We can't cloneNode fragments that contain checked, in WebKit 6227 if ( isFunction || !( l <= 1 || typeof value !== "string" || jQuery.support.checkClone || !rchecked.test( value ) ) ) { 6228 return this.each(function( index ) { 6229 var self = set.eq( index ); 6230 if ( isFunction ) { 6231 args[0] = value.call( this, index, self.html() ); 6232 } 6233 self.domManip( args, callback, allowIntersection ); 6234 }); 6235 } 6236 6237 if ( l ) { 6238 fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, !allowIntersection && this ); 6239 first = fragment.firstChild; 6240 6241 if ( fragment.childNodes.length === 1 ) { 6242 fragment = first; 6243 } 6244 6245 if ( first ) { 6246 scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); 6247 hasScripts = scripts.length; 6248 6249 // Use the original fragment for the last item instead of the first because it can end up 6250 // being emptied incorrectly in certain situations (#8070). 6251 for ( ; i < l; i++ ) { 6252 node = fragment; 6253 6254 if ( i !== iNoClone ) { 6255 node = jQuery.clone( node, true, true ); 6256 6257 // Keep references to cloned scripts for later restoration 6258 if ( hasScripts ) { 6259 jQuery.merge( scripts, getAll( node, "script" ) ); 6260 } 6261 } 6262 6263 callback.call( this[i], node, i ); 6264 } 6265 6266 if ( hasScripts ) { 6267 doc = scripts[ scripts.length - 1 ].ownerDocument; 6268 6269 // Reenable scripts 6270 jQuery.map( scripts, restoreScript ); 6271 6272 // Evaluate executable scripts on first document insertion 6273 for ( i = 0; i < hasScripts; i++ ) { 6274 node = scripts[ i ]; 6275 if ( rscriptType.test( node.type || "" ) && 6276 !jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) { 6277 6278 if ( node.src ) { 6279 // Hope ajax is available... 6280 jQuery._evalUrl( node.src ); 6281 } else { 6282 jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) ); 6283 } 6284 } 6285 } 6286 } 6287 6288 // Fix #11809: Avoid leaking memory 6289 fragment = first = null; 6290 } 6291 } 6292 6293 return this; 6294 } 6295}); 6296 6297// Support: IE<8 6298// Manipulating tables requires a tbody 6299function manipulationTarget( elem, content ) { 6300 return jQuery.nodeName( elem, "table" ) && 6301 jQuery.nodeName( content.nodeType === 1 ? content : content.firstChild, "tr" ) ? 6302 6303 elem.getElementsByTagName("tbody")[0] || 6304 elem.appendChild( elem.ownerDocument.createElement("tbody") ) : 6305 elem; 6306} 6307 6308// Replace/restore the type attribute of script elements for safe DOM manipulation 6309function disableScript( elem ) { 6310 elem.type = (jQuery.find.attr( elem, "type" ) !== null) + "/" + elem.type; 6311 return elem; 6312} 6313function restoreScript( elem ) { 6314 var match = rscriptTypeMasked.exec( elem.type ); 6315 if ( match ) { 6316 elem.type = match[1]; 6317 } else { 6318 elem.removeAttribute("type"); 6319 } 6320 return elem; 6321} 6322 6323// Mark scripts as having already been evaluated 6324function setGlobalEval( elems, refElements ) { 6325 var elem, 6326 i = 0; 6327 for ( ; (elem = elems[i]) != null; i++ ) { 6328 jQuery._data( elem, "globalEval", !refElements || jQuery._data( refElements[i], "globalEval" ) ); 6329 } 6330} 6331 6332function cloneCopyEvent( src, dest ) { 6333 6334 if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) { 6335 return; 6336 } 6337 6338 var type, i, l, 6339 oldData = jQuery._data( src ), 6340 curData = jQuery._data( dest, oldData ), 6341 events = oldData.events; 6342 6343 if ( events ) { 6344 delete curData.handle; 6345 curData.events = {}; 6346 6347 for ( type in events ) { 6348 for ( i = 0, l = events[ type ].length; i < l; i++ ) { 6349 jQuery.event.add( dest, type, events[ type ][ i ] ); 6350 } 6351 } 6352 } 6353 6354 // make the cloned public data object a copy from the original 6355 if ( curData.data ) { 6356 curData.data = jQuery.extend( {}, curData.data ); 6357 } 6358} 6359 6360function fixCloneNodeIssues( src, dest ) { 6361 var nodeName, e, data; 6362 6363 // We do not need to do anything for non-Elements 6364 if ( dest.nodeType !== 1 ) { 6365 return; 6366 } 6367 6368 nodeName = dest.nodeName.toLowerCase(); 6369 6370 // IE6-8 copies events bound via attachEvent when using cloneNode. 6371 if ( !jQuery.support.noCloneEvent && dest[ jQuery.expando ] ) { 6372 data = jQuery._data( dest ); 6373 6374 for ( e in data.events ) { 6375 jQuery.removeEvent( dest, e, data.handle ); 6376 } 6377 6378 // Event data gets referenced instead of copied if the expando gets copied too 6379 dest.removeAttribute( jQuery.expando ); 6380 } 6381 6382 // IE blanks contents when cloning scripts, and tries to evaluate newly-set text 6383 if ( nodeName === "script" && dest.text !== src.text ) { 6384 disableScript( dest ).text = src.text; 6385 restoreScript( dest ); 6386 6387 // IE6-10 improperly clones children of object elements using classid. 6388 // IE10 throws NoModificationAllowedError if parent is null, #12132. 6389 } else if ( nodeName === "object" ) { 6390 if ( dest.parentNode ) { 6391 dest.outerHTML = src.outerHTML; 6392 } 6393 6394 // This path appears unavoidable for IE9. When cloning an object 6395 // element in IE9, the outerHTML strategy above is not sufficient. 6396 // If the src has innerHTML and the destination does not, 6397 // copy the src.innerHTML into the dest.innerHTML. #10324 6398 if ( jQuery.support.html5Clone && ( src.innerHTML && !jQuery.trim(dest.innerHTML) ) ) { 6399 dest.innerHTML = src.innerHTML; 6400 } 6401 6402 } else if ( nodeName === "input" && manipulation_rcheckableType.test( src.type ) ) { 6403 // IE6-8 fails to persist the checked state of a cloned checkbox 6404 // or radio button. Worse, IE6-7 fail to give the cloned element 6405 // a checked appearance if the defaultChecked value isn't also set 6406 6407 dest.defaultChecked = dest.checked = src.checked; 6408 6409 // IE6-7 get confused and end up setting the value of a cloned 6410 // checkbox/radio button to an empty string instead of "on" 6411 if ( dest.value !== src.value ) { 6412 dest.value = src.value; 6413 } 6414 6415 // IE6-8 fails to return the selected option to the default selected 6416 // state when cloning options 6417 } else if ( nodeName === "option" ) { 6418 dest.defaultSelected = dest.selected = src.defaultSelected; 6419 6420 // IE6-8 fails to set the defaultValue to the correct value when 6421 // cloning other types of input fields 6422 } else if ( nodeName === "input" || nodeName === "textarea" ) { 6423 dest.defaultValue = src.defaultValue; 6424 } 6425} 6426 6427jQuery.each({ 6428 appendTo: "append", 6429 prependTo: "prepend", 6430 insertBefore: "before", 6431 insertAfter: "after", 6432 replaceAll: "replaceWith" 6433}, function( name, original ) { 6434 jQuery.fn[ name ] = function( selector ) { 6435 var elems, 6436 i = 0, 6437 ret = [], 6438 insert = jQuery( selector ), 6439 last = insert.length - 1; 6440 6441 for ( ; i <= last; i++ ) { 6442 elems = i === last ? this : this.clone(true); 6443 jQuery( insert[i] )[ original ]( elems ); 6444 6445 // Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get() 6446 core_push.apply( ret, elems.get() ); 6447 } 6448 6449 return this.pushStack( ret ); 6450 }; 6451}); 6452 6453function getAll( context, tag ) { 6454 var elems, elem, 6455 i = 0, 6456 found = typeof context.getElementsByTagName !== core_strundefined ? context.getElementsByTagName( tag || "*" ) : 6457 typeof context.querySelectorAll !== core_strundefined ? context.querySelectorAll( tag || "*" ) : 6458 undefined; 6459 6460 if ( !found ) { 6461 for ( found = [], elems = context.childNodes || context; (elem = elems[i]) != null; i++ ) { 6462 if ( !tag || jQuery.nodeName( elem, tag ) ) { 6463 found.push( elem ); 6464 } else { 6465 jQuery.merge( found, getAll( elem, tag ) ); 6466 } 6467 } 6468 } 6469 6470 return tag === undefined || tag && jQuery.nodeName( context, tag ) ? 6471 jQuery.merge( [ context ], found ) : 6472 found; 6473} 6474 6475// Used in buildFragment, fixes the defaultChecked property 6476function fixDefaultChecked( elem ) { 6477 if ( manipulation_rcheckableType.test( elem.type ) ) { 6478 elem.defaultChecked = elem.checked; 6479 } 6480} 6481 6482jQuery.extend({ 6483 clone: function( elem, dataAndEvents, deepDataAndEvents ) { 6484 var destElements, node, clone, i, srcElements, 6485 inPage = jQuery.contains( elem.ownerDocument, elem ); 6486 6487 if ( jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) { 6488 clone = elem.cloneNode( true ); 6489 6490 // IE<=8 does not properly clone detached, unknown element nodes 6491 } else { 6492 fragmentDiv.innerHTML = elem.outerHTML; 6493 fragmentDiv.removeChild( clone = fragmentDiv.firstChild ); 6494 } 6495 6496 if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) && 6497 (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) { 6498 6499 // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 6500 destElements = getAll( clone ); 6501 srcElements = getAll( elem ); 6502 6503 // Fix all IE cloning issues 6504 for ( i = 0; (node = srcElements[i]) != null; ++i ) { 6505 // Ensure that the destination node is not null; Fixes #9587 6506 if ( destElements[i] ) { 6507 fixCloneNodeIssues( node, destElements[i] ); 6508 } 6509 } 6510 } 6511 6512 // Copy the events from the original to the clone 6513 if ( dataAndEvents ) { 6514 if ( deepDataAndEvents ) { 6515 srcElements = srcElements || getAll( elem ); 6516 destElements = destElements || getAll( clone ); 6517 6518 for ( i = 0; (node = srcElements[i]) != null; i++ ) { 6519 cloneCopyEvent( node, destElements[i] ); 6520 } 6521 } else { 6522 cloneCopyEvent( elem, clone ); 6523 } 6524 } 6525 6526 // Preserve script evaluation history 6527 destElements = getAll( clone, "script" ); 6528 if ( destElements.length > 0 ) { 6529 setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); 6530 } 6531 6532 destElements = srcElements = node = null; 6533 6534 // Return the cloned set 6535 return clone; 6536 }, 6537 6538 buildFragment: function( elems, context, scripts, selection ) { 6539 var j, elem, contains, 6540 tmp, tag, tbody, wrap, 6541 l = elems.length, 6542 6543 // Ensure a safe fragment 6544 safe = createSafeFragment( context ), 6545 6546 nodes = [], 6547 i = 0; 6548 6549 for ( ; i < l; i++ ) { 6550 elem = elems[ i ]; 6551 6552 if ( elem || elem === 0 ) { 6553 6554 // Add nodes directly 6555 if ( jQuery.type( elem ) === "object" ) { 6556 jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); 6557 6558 // Convert non-html into a text node 6559 } else if ( !rhtml.test( elem ) ) { 6560 nodes.push( context.createTextNode( elem ) ); 6561 6562 // Convert html into DOM nodes 6563 } else { 6564 tmp = tmp || safe.appendChild( context.createElement("div") ); 6565 6566 // Deserialize a standard representation 6567 tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase(); 6568 wrap = wrapMap[ tag ] || wrapMap._default; 6569 6570 tmp.innerHTML = wrap[1] + elem.replace( rxhtmlTag, "<$1></$2>" ) + wrap[2]; 6571 6572 // Descend through wrappers to the right content 6573 j = wrap[0]; 6574 while ( j-- ) { 6575 tmp = tmp.lastChild; 6576 } 6577 6578 // Manually add leading whitespace removed by IE 6579 if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) { 6580 nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) ); 6581 } 6582 6583 // Remove IE's autoinserted <tbody> from table fragments 6584 if ( !jQuery.support.tbody ) { 6585 6586 // String was a <table>, *may* have spurious <tbody> 6587 elem = tag === "table" && !rtbody.test( elem ) ? 6588 tmp.firstChild : 6589 6590 // String was a bare <thead> or <tfoot> 6591 wrap[1] === "<table>" && !rtbody.test( elem ) ? 6592 tmp : 6593 0; 6594 6595 j = elem && elem.childNodes.length; 6596 while ( j-- ) { 6597 if ( jQuery.nodeName( (tbody = elem.childNodes[j]), "tbody" ) && !tbody.childNodes.length ) { 6598 elem.removeChild( tbody ); 6599 } 6600 } 6601 } 6602 6603 jQuery.merge( nodes, tmp.childNodes ); 6604 6605 // Fix #12392 for WebKit and IE > 9 6606 tmp.textContent = ""; 6607 6608 // Fix #12392 for oldIE 6609 while ( tmp.firstChild ) { 6610 tmp.removeChild( tmp.firstChild ); 6611 } 6612 6613 // Remember the top-level container for proper cleanup 6614 tmp = safe.lastChild; 6615 } 6616 } 6617 } 6618 6619 // Fix #11356: Clear elements from fragment 6620 if ( tmp ) { 6621 safe.removeChild( tmp ); 6622 } 6623 6624 // Reset defaultChecked for any radios and checkboxes 6625 // about to be appended to the DOM in IE 6/7 (#8060) 6626 if ( !jQuery.support.appendChecked ) { 6627 jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked ); 6628 } 6629 6630 i = 0; 6631 while ( (elem = nodes[ i++ ]) ) { 6632 6633 // #4087 - If origin and destination elements are the same, and this is 6634 // that element, do not do anything 6635 if ( selection && jQuery.inArray( elem, selection ) !== -1 ) { 6636 continue; 6637 } 6638 6639 contains = jQuery.contains( elem.ownerDocument, elem ); 6640 6641 // Append to fragment 6642 tmp = getAll( safe.appendChild( elem ), "script" ); 6643 6644 // Preserve script evaluation history 6645 if ( contains ) { 6646 setGlobalEval( tmp ); 6647 } 6648 6649 // Capture executables 6650 if ( scripts ) { 6651 j = 0; 6652 while ( (elem = tmp[ j++ ]) ) { 6653 if ( rscriptType.test( elem.type || "" ) ) { 6654 scripts.push( elem ); 6655 } 6656 } 6657 } 6658 } 6659 6660 tmp = null; 6661 6662 return safe; 6663 }, 6664 6665 cleanData: function( elems, /* internal */ acceptData ) { 6666 var elem, type, id, data, 6667 i = 0, 6668 internalKey = jQuery.expando, 6669 cache = jQuery.cache, 6670 deleteExpando = jQuery.support.deleteExpando, 6671 special = jQuery.event.special; 6672 6673 for ( ; (elem = elems[i]) != null; i++ ) { 6674 6675 if ( acceptData || jQuery.acceptData( elem ) ) { 6676 6677 id = elem[ internalKey ]; 6678 data = id && cache[ id ]; 6679 6680 if ( data ) { 6681 if ( data.events ) { 6682 for ( type in data.events ) { 6683 if ( special[ type ] ) { 6684 jQuery.event.remove( elem, type ); 6685 6686 // This is a shortcut to avoid jQuery.event.remove's overhead 6687 } else { 6688 jQuery.removeEvent( elem, type, data.handle ); 6689 } 6690 } 6691 } 6692 6693 // Remove cache only if it was not already removed by jQuery.event.remove 6694 if ( cache[ id ] ) { 6695 6696 delete cache[ id ]; 6697 6698 // IE does not allow us to delete expando properties from nodes, 6699 // nor does it have a removeAttribute function on Document nodes; 6700 // we must handle all of these cases 6701 if ( deleteExpando ) { 6702 delete elem[ internalKey ]; 6703 6704 } else if ( typeof elem.removeAttribute !== core_strundefined ) { 6705 elem.removeAttribute( internalKey ); 6706 6707 } else { 6708 elem[ internalKey ] = null; 6709 } 6710 6711 core_deletedIds.push( id ); 6712 } 6713 } 6714 } 6715 } 6716 }, 6717 6718 _evalUrl: function( url ) { 6719 return jQuery.ajax({ 6720 url: url, 6721 type: "GET", 6722 dataType: "script", 6723 async: false, 6724 global: false, 6725 "throws": true 6726 }); 6727 } 6728}); 6729jQuery.fn.extend({ 6730 wrapAll: function( html ) { 6731 if ( jQuery.isFunction( html ) ) { 6732 return this.each(function(i) { 6733 jQuery(this).wrapAll( html.call(this, i) ); 6734 }); 6735 } 6736 6737 if ( this[0] ) { 6738 // The elements to wrap the target around 6739 var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true); 6740 6741 if ( this[0].parentNode ) { 6742 wrap.insertBefore( this[0] ); 6743 } 6744 6745 wrap.map(function() { 6746 var elem = this; 6747 6748 while ( elem.firstChild && elem.firstChild.nodeType === 1 ) { 6749 elem = elem.firstChild; 6750 } 6751 6752 return elem; 6753 }).append( this ); 6754 } 6755 6756 return this; 6757 }, 6758 6759 wrapInner: function( html ) { 6760 if ( jQuery.isFunction( html ) ) { 6761 return this.each(function(i) { 6762 jQuery(this).wrapInner( html.call(this, i) ); 6763 }); 6764 } 6765 6766 return this.each(function() { 6767 var self = jQuery( this ), 6768 contents = self.contents(); 6769 6770 if ( contents.length ) { 6771 contents.wrapAll( html ); 6772 6773 } else { 6774 self.append( html ); 6775 } 6776 }); 6777 }, 6778 6779 wrap: function( html ) { 6780 var isFunction = jQuery.isFunction( html ); 6781 6782 return this.each(function(i) { 6783 jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html ); 6784 }); 6785 }, 6786 6787 unwrap: function() { 6788 return this.parent().each(function() { 6789 if ( !jQuery.nodeName( this, "body" ) ) { 6790 jQuery( this ).replaceWith( this.childNodes ); 6791 } 6792 }).end(); 6793 } 6794}); 6795var iframe, getStyles, curCSS, 6796 ralpha = /alpha\([^)]*\)/i, 6797 ropacity = /opacity\s*=\s*([^)]*)/, 6798 rposition = /^(top|right|bottom|left)$/, 6799 // swappable if display is none or starts with table except "table", "table-cell", or "table-caption" 6800 // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display 6801 rdisplayswap = /^(none|table(?!-c[ea]).+)/, 6802 rmargin = /^margin/, 6803 rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ), 6804 rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ), 6805 rrelNum = new RegExp( "^([+-])=(" + core_pnum + ")", "i" ), 6806 elemdisplay = { BODY: "block" }, 6807 6808 cssShow = { position: "absolute", visibility: "hidden", display: "block" }, 6809 cssNormalTransform = { 6810 letterSpacing: 0, 6811 fontWeight: 400 6812 }, 6813 6814 cssExpand = [ "Top", "Right", "Bottom", "Left" ], 6815 cssPrefixes = [ "Webkit", "O", "Moz", "ms" ]; 6816 6817// return a css property mapped to a potentially vendor prefixed property 6818function vendorPropName( style, name ) { 6819 6820 // shortcut for names that are not vendor prefixed 6821 if ( name in style ) { 6822 return name; 6823 } 6824 6825 // check for vendor prefixed names 6826 var capName = name.charAt(0).toUpperCase() + name.slice(1), 6827 origName = name, 6828 i = cssPrefixes.length; 6829 6830 while ( i-- ) { 6831 name = cssPrefixes[ i ] + capName; 6832 if ( name in style ) { 6833 return name; 6834 } 6835 } 6836 6837 return origName; 6838} 6839 6840function isHidden( elem, el ) { 6841 // isHidden might be called from jQuery#filter function; 6842 // in that case, element will be second argument 6843 elem = el || elem; 6844 return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); 6845} 6846 6847function showHide( elements, show ) { 6848 var display, elem, hidden, 6849 values = [], 6850 index = 0, 6851 length = elements.length; 6852 6853 for ( ; index < length; index++ ) { 6854 elem = elements[ index ]; 6855 if ( !elem.style ) { 6856 continue; 6857 } 6858 6859 values[ index ] = jQuery._data( elem, "olddisplay" ); 6860 display = elem.style.display; 6861 if ( show ) { 6862 // Reset the inline display of this element to learn if it is 6863 // being hidden by cascaded rules or not 6864 if ( !values[ index ] && display === "none" ) { 6865 elem.style.display = ""; 6866 } 6867 6868 // Set elements which have been overridden with display: none 6869 // in a stylesheet to whatever the default browser style is 6870 // for such an element 6871 if ( elem.style.display === "" && isHidden( elem ) ) { 6872 values[ index ] = jQuery._data( elem, "olddisplay", css_defaultDisplay(elem.nodeName) ); 6873 } 6874 } else { 6875 6876 if ( !values[ index ] ) { 6877 hidden = isHidden( elem ); 6878 6879 if ( display && display !== "none" || !hidden ) { 6880 jQuery._data( elem, "olddisplay", hidden ? display : jQuery.css( elem, "display" ) ); 6881 } 6882 } 6883 } 6884 } 6885 6886 // Set the display of most of the elements in a second loop 6887 // to avoid the constant reflow 6888 for ( index = 0; index < length; index++ ) { 6889 elem = elements[ index ]; 6890 if ( !elem.style ) { 6891 continue; 6892 } 6893 if ( !show || elem.style.display === "none" || elem.style.display === "" ) { 6894 elem.style.display = show ? values[ index ] || "" : "none"; 6895 } 6896 } 6897 6898 return elements; 6899} 6900 6901jQuery.fn.extend({ 6902 css: function( name, value ) { 6903 return jQuery.access( this, function( elem, name, value ) { 6904 var len, styles, 6905 map = {}, 6906 i = 0; 6907 6908 if ( jQuery.isArray( name ) ) { 6909 styles = getStyles( elem ); 6910 len = name.length; 6911 6912 for ( ; i < len; i++ ) { 6913 map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); 6914 } 6915 6916 return map; 6917 } 6918 6919 return value !== undefined ? 6920 jQuery.style( elem, name, value ) : 6921 jQuery.css( elem, name ); 6922 }, name, value, arguments.length > 1 ); 6923 }, 6924 show: function() { 6925 return showHide( this, true ); 6926 }, 6927 hide: function() { 6928 return showHide( this ); 6929 }, 6930 toggle: function( state ) { 6931 if ( typeof state === "boolean" ) { 6932 return state ? this.show() : this.hide(); 6933 } 6934 6935 return this.each(function() { 6936 if ( isHidden( this ) ) { 6937 jQuery( this ).show(); 6938 } else { 6939 jQuery( this ).hide(); 6940 } 6941 }); 6942 } 6943}); 6944 6945jQuery.extend({ 6946 // Add in style property hooks for overriding the default 6947 // behavior of getting and setting a style property 6948 cssHooks: { 6949 opacity: { 6950 get: function( elem, computed ) { 6951 if ( computed ) { 6952 // We should always get a number back from opacity 6953 var ret = curCSS( elem, "opacity" ); 6954 return ret === "" ? "1" : ret; 6955 } 6956 } 6957 } 6958 }, 6959 6960 // Don't automatically add "px" to these possibly-unitless properties 6961 cssNumber: { 6962 "columnCount": true, 6963 "fillOpacity": true, 6964 "fontWeight": true, 6965 "lineHeight": true, 6966 "opacity": true, 6967 "order": true, 6968 "orphans": true, 6969 "widows": true, 6970 "zIndex": true, 6971 "zoom": true 6972 }, 6973 6974 // Add in properties whose names you wish to fix before 6975 // setting or getting the value 6976 cssProps: { 6977 // normalize float css property 6978 "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat" 6979 }, 6980 6981 // Get and set the style property on a DOM Node 6982 style: function( elem, name, value, extra ) { 6983 // Don't set styles on text and comment nodes 6984 if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { 6985 return; 6986 } 6987 6988 // Make sure that we're working with the right name 6989 var ret, type, hooks, 6990 origName = jQuery.camelCase( name ), 6991 style = elem.style; 6992 6993 name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) ); 6994 6995 // gets hook for the prefixed version 6996 // followed by the unprefixed version 6997 hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; 6998 6999 // Check if we're setting a value 7000 if ( value !== undefined ) { 7001 type = typeof value; 7002 7003 // convert relative number strings (+= or -=) to relative numbers. #7345 7004 if ( type === "string" && (ret = rrelNum.exec( value )) ) { 7005 value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) ); 7006 // Fixes bug #9237 7007 type = "number"; 7008 } 7009 7010 // Make sure that NaN and null values aren't set. See: #7116 7011 if ( value == null || type === "number" && isNaN( value ) ) { 7012 return; 7013 } 7014 7015 // If a number was passed in, add 'px' to the (except for certain CSS properties) 7016 if ( type === "number" && !jQuery.cssNumber[ origName ] ) { 7017 value += "px"; 7018 } 7019 7020 // Fixes #8908, it can be done more correctly by specifing setters in cssHooks, 7021 // but it would mean to define eight (for every problematic property) identical functions 7022 if ( !jQuery.support.clearCloneStyle && value === "" && name.indexOf("background") === 0 ) { 7023 style[ name ] = "inherit"; 7024 } 7025 7026 // If a hook was provided, use that value, otherwise just set the specified value 7027 if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) { 7028 7029 // Wrapped to prevent IE from throwing errors when 'invalid' values are provided 7030 // Fixes bug #5509 7031 try { 7032 style[ name ] = value; 7033 } catch(e) {} 7034 } 7035 7036 } else { 7037 // If a hook was provided get the non-computed value from there 7038 if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) { 7039 return ret; 7040 } 7041 7042 // Otherwise just get the value from the style object 7043 return style[ name ]; 7044 } 7045 }, 7046 7047 css: function( elem, name, extra, styles ) { 7048 var num, val, hooks, 7049 origName = jQuery.camelCase( name ); 7050 7051 // Make sure that we're working with the right name 7052 name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) ); 7053 7054 // gets hook for the prefixed version 7055 // followed by the unprefixed version 7056 hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; 7057 7058 // If a hook was provided get the computed value from there 7059 if ( hooks && "get" in hooks ) { 7060 val = hooks.get( elem, true, extra ); 7061 } 7062 7063 // Otherwise, if a way to get the computed value exists, use that 7064 if ( val === undefined ) { 7065 val = curCSS( elem, name, styles ); 7066 } 7067 7068 //convert "normal" to computed value 7069 if ( val === "normal" && name in cssNormalTransform ) { 7070 val = cssNormalTransform[ name ]; 7071 } 7072 7073 // Return, converting to number if forced or a qualifier was provided and val looks numeric 7074 if ( extra === "" || extra ) { 7075 num = parseFloat( val ); 7076 return extra === true || jQuery.isNumeric( num ) ? num || 0 : val; 7077 } 7078 return val; 7079 } 7080}); 7081 7082// NOTE: we've included the "window" in window.getComputedStyle
7083// because jsdom on node.js will break without it. 7084if ( window.getComputedStyle ) { 7085 getStyles = function( elem ) { 7086 return window.getComputedStyle( elem, null ); 7087 }; 7088 7089 curCSS = function( elem, name, _computed ) { 7090 var width, minWidth, maxWidth, 7091 computed = _computed || getStyles( elem ), 7092 7093 // getPropertyValue is only needed for .css('filter') in IE9, see #12537 7094 ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined, 7095 style = elem.style; 7096 7097 if ( computed ) { 7098 7099 if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { 7100 ret = jQuery.style( elem, name ); 7101 } 7102 7103 // A tribute to the "awesome hack by Dean Edwards" 7104 // Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right 7105 // Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels 7106 // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values 7107 if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) { 7108 7109 // Remember the original values 7110 width = style.width; 7111 minWidth = style.minWidth; 7112 maxWidth = style.maxWidth; 7113 7114 // Put in the new values to get a computed value out 7115 style.minWidth = style.maxWidth = style.width = ret; 7116 ret = computed.width; 7117 7118 // Revert the changed values 7119 style.width = width; 7120 style.minWidth = minWidth; 7121 style.maxWidth = maxWidth; 7122 } 7123 } 7124 7125 return ret; 7126 }; 7127} else if ( document.documentElement.currentStyle ) { 7128 getStyles = function( elem ) { 7129 return elem.currentStyle; 7130 }; 7131 7132 curCSS = function( elem, name, _computed ) { 7133 var left, rs, rsLeft, 7134 computed = _computed || getStyles( elem ), 7135 ret = computed ? computed[ name ] : undefined, 7136 style = elem.style; 7137 7138 // Avoid setting ret to empty string here 7139 // so we don't default to auto 7140 if ( ret == null && style && style[ name ] ) { 7141 ret = style[ name ]; 7142 } 7143 7144 // From the awesome hack by Dean Edwards 7145 // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 7146 7147 // If we're not dealing with a regular pixel number 7148 // but a number that has a weird ending, we need to convert it to pixels 7149 // but not position css attributes, as those are proportional to the parent element instead 7150 // and we can't measure the parent instead because it might trigger a "stacking dolls" problem 7151 if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) { 7152 7153 // Remember the original values 7154 left = style.left; 7155 rs = elem.runtimeStyle; 7156 rsLeft = rs && rs.left; 7157 7158 // Put in the new values to get a computed value out 7159 if ( rsLeft ) { 7160 rs.left = elem.currentStyle.left; 7161 } 7162 style.left = name === "fontSize" ? "1em" : ret; 7163 ret = style.pixelLeft + "px"; 7164 7165 // Revert the changed values 7166 style.left = left; 7167 if ( rsLeft ) { 7168 rs.left = rsLeft; 7169 } 7170 } 7171 7172 return ret === "" ? "auto" : ret; 7173 }; 7174} 7175 7176function setPositiveNumber( elem, value, subtract ) { 7177 var matches = rnumsplit.exec( value ); 7178 return matches ? 7179 // Guard against undefined "subtract", e.g., when used as in cssHooks 7180 Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) : 7181 value; 7182} 7183 7184function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) { 7185 var i = extra === ( isBorderBox ? "border" : "content" ) ? 7186 // If we already have the right measurement, avoid augmentation 7187 4 : 7188 // Otherwise initialize for horizontal or vertical properties 7189 name === "width" ? 1 : 0, 7190 7191 val = 0; 7192 7193 for ( ; i < 4; i += 2 ) { 7194 // both box models exclude margin, so add it if we want it 7195 if ( extra === "margin" ) { 7196 val += jQuery.css( elem, extra + cssExpand[ i ], true, styles ); 7197 } 7198 7199 if ( isBorderBox ) { 7200 // border-box includes padding, so remove it if we want content 7201 if ( extra === "content" ) { 7202 val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); 7203 } 7204 7205 // at this point, extra isn't border nor margin, so remove border 7206 if ( extra !== "margin" ) { 7207 val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); 7208 } 7209 } else { 7210 // at this point, extra isn't content, so add padding 7211 val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); 7212 7213 // at this point, extra isn't content nor padding, so add border 7214 if ( extra !== "padding" ) { 7215 val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); 7216 } 7217 } 7218 } 7219 7220 return val; 7221} 7222 7223function getWidthOrHeight( elem, name, extra ) { 7224 7225 // Start with offset property, which is equivalent to the border-box value 7226 var valueIsBorderBox = true, 7227 val = name === "width" ? elem.offsetWidth : elem.offsetHeight, 7228 styles = getStyles( elem ), 7229 isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; 7230 7231 // some non-html elements return undefined for offsetWidth, so check for null/undefined 7232 // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285 7233 // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668 7234 if ( val <= 0 || val == null ) { 7235 // Fall back to computed then uncomputed css if necessary 7236 val = curCSS( elem, name, styles ); 7237 if ( val < 0 || val == null ) { 7238 val = elem.style[ name ]; 7239 } 7240 7241 // Computed unit is not pixels. Stop here and return. 7242 if ( rnumnonpx.test(val) ) { 7243 return val; 7244 } 7245 7246 // we need the check for style in case a browser which returns unreliable values 7247 // for getComputedStyle silently falls back to the reliable elem.style 7248 valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] ); 7249 7250 // Normalize "", auto, and prepare for extra 7251 val = parseFloat( val ) || 0; 7252 } 7253 7254 // use the active box-sizing model to add/subtract irrelevant styles 7255 return ( val + 7256 augmentWidthOrHeight( 7257 elem, 7258 name, 7259 extra || ( isBorderBox ? "border" : "content" ), 7260 valueIsBorderBox, 7261 styles 7262 ) 7263 ) + "px"; 7264} 7265 7266// Try to determine the default display value of an element 7267function css_defaultDisplay( nodeName ) { 7268 var doc = document, 7269 display = elemdisplay[ nodeName ]; 7270 7271 if ( !display ) { 7272 display = actualDisplay( nodeName, doc ); 7273 7274 // If the simple way fails, read from inside an iframe 7275 if ( display === "none" || !display ) { 7276 // Use the already-created iframe if possible 7277 iframe = ( iframe || 7278 jQuery("<iframe frameborder='0' width='0' height='0'/>") 7279 .css( "cssText", "display:block !important" ) 7280 ).appendTo( doc.documentElement ); 7281
7282 // Always write a new HTML skeleton so Webkit and Firefox don't choke on reuse 7283 doc = ( iframe[0].contentWindow || iframe[0].contentDocument ).document; 7284 doc.write("<!doctype html><html><body>"); 7285 doc.close(); 7286 7287 display = actualDisplay( nodeName, doc ); 7288 iframe.detach(); 7289 } 7290 7291 // Store the correct default display 7292 elemdisplay[ nodeName ] = display; 7293 } 7294 7295 return display; 7296} 7297 7298// Called ONLY from within css_defaultDisplay 7299function actualDisplay( name, doc ) { 7300 var elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ), 7301 display = jQuery.css( elem[0], "display" ); 7302 elem.remove(); 7303 return display; 7304} 7305 7306jQuery.each([ "height", "width" ], function( i, name ) { 7307 jQuery.cssHooks[ name ] = { 7308 get: function( elem, computed, extra ) { 7309 if ( computed ) { 7310 // certain elements can have dimension info if we invisibly show them 7311 // however, it must have a current display style that would benefit from this 7312 return elem.offsetWidth === 0 && rdisplayswap.test( jQuery.css( elem, "display" ) ) ? 7313 jQuery.swap( elem, cssShow, function() { 7314 return getWidthOrHeight( elem, name, extra ); 7315 }) : 7316 getWidthOrHeight( elem, name, extra ); 7317 } 7318 }, 7319 7320 set: function( elem, value, extra ) { 7321 var styles = extra && getStyles( elem ); 7322 return setPositiveNumber( elem, value, extra ? 7323 augmentWidthOrHeight( 7324 elem, 7325 name, 7326 extra, 7327 jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box", 7328 styles 7329 ) : 0 7330 ); 7331 } 7332 }; 7333}); 7334 7335if ( !jQuery.support.opacity ) { 7336 jQuery.cssHooks.opacity = { 7337 get: function( elem, computed ) { 7338 // IE uses filters for opacity 7339 return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ? 7340 ( 0.01 * parseFloat( RegExp.$1 ) ) + "" : 7341 computed ? "1" : ""; 7342 }, 7343 7344 set: function( elem, value ) { 7345 var style = elem.style, 7346 currentStyle = elem.currentStyle, 7347 opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "", 7348 filter = currentStyle && currentStyle.filter || style.filter || ""; 7349 7350 // IE has trouble with opacity if it does not have layout 7351 // Force it by setting the zoom level 7352 style.zoom = 1; 7353 7354 // if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652 7355 // if value === "", then remove inline opacity #12685 7356 if ( ( value >= 1 || value === "" ) && 7357 jQuery.trim( filter.replace( ralpha, "" ) ) === "" && 7358 style.removeAttribute ) { 7359 7360 // Setting style.filter to null, "" & " " still leave "filter:" in the cssText 7361 // if "filter:" is present at all, clearType is disabled, we want to avoid this 7362 // style.removeAttribute is IE Only, but so apparently is this code path... 7363 style.removeAttribute( "filter" ); 7364 7365 // if there is no filter style applied in a css rule or unset inline opacity, we are done 7366 if ( value === "" || currentStyle && !currentStyle.filter ) { 7367 return; 7368 } 7369 } 7370 7371 // otherwise, set new filter values 7372 style.filter = ralpha.test( filter ) ? 7373 filter.replace( ralpha, opacity ) : 7374 filter + " " + opacity; 7375 } 7376 }; 7377} 7378 7379// These hooks cannot be added until DOM ready because the support test 7380// for it is not run until after DOM ready 7381jQuery(function() { 7382 if ( !jQuery.support.reliableMarginRight ) { 7383 jQuery.cssHooks.marginRight = { 7384 get: function( elem, computed ) { 7385 if ( computed ) { 7386 // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right 7387 // Work around by temporarily setting element display to inline-block 7388 return jQuery.swap( elem, { "display": "inline-block" }, 7389 curCSS, [ elem, "marginRight" ] ); 7390 } 7391 } 7392 }; 7393 } 7394 7395 // Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084 7396 // getComputedStyle returns percent when specified for top/left/bottom/right 7397 // rather than make the css module depend on the offset module, we just check for it here 7398 if ( !jQuery.support.pixelPosition && jQuery.fn.position ) { 7399 jQuery.each( [ "top", "left" ], function( i, prop ) { 7400 jQuery.cssHooks[ prop ] = { 7401 get: function( elem, computed ) { 7402 if ( computed ) { 7403 computed = curCSS( elem, prop ); 7404 // if curCSS returns percentage, fallback to offset 7405 return rnumnonpx.test( computed ) ? 7406 jQuery( elem ).position()[ prop ] + "px" : 7407 computed; 7408 } 7409 } 7410 }; 7411 }); 7412 } 7413 7414}); 7415 7416if ( jQuery.expr && jQuery.expr.filters ) { 7417 jQuery.expr.filters.hidden = function( elem ) {
vendor: 5,333 bytes, lines 7418-7596
7418 // Support: Opera <= 12.12 7419 // Opera reports offsetWidths and offsetHeights less than zero on some elements 7420 return elem.offsetWidth <= 0 && elem.offsetHeight <= 0 || 7421 (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || jQuery.css( elem, "display" )) === "none"); 7422 }; 7423 7424 jQuery.expr.filters.visible = function( elem ) { 7425 return !jQuery.expr.filters.hidden( elem ); 7426 }; 7427} 7428 7429// These hooks are used by animate to expand properties 7430jQuery.each({ 7431 margin: "", 7432 padding: "", 7433 border: "Width" 7434}, function( prefix, suffix ) { 7435 jQuery.cssHooks[ prefix + suffix ] = { 7436 expand: function( value ) { 7437 var i = 0, 7438 expanded = {}, 7439 7440 // assumes a single number if not a string 7441 parts = typeof value === "string" ? value.split(" ") : [ value ]; 7442 7443 for ( ; i < 4; i++ ) { 7444 expanded[ prefix + cssExpand[ i ] + suffix ] = 7445 parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; 7446 } 7447 7448 return expanded; 7449 } 7450 }; 7451 7452 if ( !rmargin.test( prefix ) ) { 7453 jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; 7454 } 7455}); 7456var r20 = /%20/g, 7457 rbracket = /\[\]$/, 7458 rCRLF = /\r?\n/g, 7459 rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, 7460 rsubmittable = /^(?:input|select|textarea|keygen)/i; 7461 7462jQuery.fn.extend({ 7463 serialize: function() { 7464 return jQuery.param( this.serializeArray() ); 7465 }, 7466 serializeArray: function() { 7467 return this.map(function(){ 7468 // Can add propHook for "elements" to filter or add form elements 7469 var elements = jQuery.prop( this, "elements" ); 7470 return elements ? jQuery.makeArray( elements ) : this; 7471 }) 7472 .filter(function(){ 7473 var type = this.type; 7474 // Use .is(":disabled") so that fieldset[disabled] works 7475 return this.name && !jQuery( this ).is( ":disabled" ) && 7476 rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && 7477 ( this.checked || !manipulation_rcheckableType.test( type ) ); 7478 }) 7479 .map(function( i, elem ){ 7480 var val = jQuery( this ).val(); 7481 7482 return val == null ? 7483 null : 7484 jQuery.isArray( val ) ? 7485 jQuery.map( val, function( val ){ 7486 return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; 7487 }) : 7488 { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; 7489 }).get(); 7490 } 7491}); 7492 7493//Serialize an array of form elements or a set of 7494//key/values into a query string 7495jQuery.param = function( a, traditional ) { 7496 var prefix, 7497 s = [], 7498 add = function( key, value ) { 7499 // If value is a function, invoke it and return its value 7500 value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value ); 7501 s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value ); 7502 }; 7503 7504 // Set traditional to true for jQuery <= 1.3.2 behavior. 7505 if ( traditional === undefined ) { 7506 traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional; 7507 } 7508 7509 // If an array was passed in, assume that it is an array of form elements. 7510 if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { 7511 // Serialize the form elements 7512 jQuery.each( a, function() { 7513 add( this.name, this.value ); 7514 }); 7515 7516 } else { 7517 // If traditional, encode the "old" way (the way 1.3.2 or older 7518 // did it), otherwise encode params recursively. 7519 for ( prefix in a ) { 7520 buildParams( prefix, a[ prefix ], traditional, add ); 7521 } 7522 } 7523 7524 // Return the resulting serialization 7525 return s.join( "&" ).replace( r20, "+" ); 7526}; 7527 7528function buildParams( prefix, obj, traditional, add ) { 7529 var name; 7530 7531 if ( jQuery.isArray( obj ) ) { 7532 // Serialize array item. 7533 jQuery.each( obj, function( i, v ) { 7534 if ( traditional || rbracket.test( prefix ) ) { 7535 // Treat each array item as a scalar. 7536 add( prefix, v ); 7537 7538 } else { 7539 // Item is non-scalar (array or object), encode its numeric index. 7540 buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add ); 7541 } 7542 }); 7543 7544 } else if ( !traditional && jQuery.type( obj ) === "object" ) { 7545 // Serialize object item. 7546 for ( name in obj ) { 7547 buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); 7548 } 7549 7550 } else { 7551 // Serialize scalar item. 7552 add( prefix, obj ); 7553 } 7554} 7555jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + 7556 "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + 7557 "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) { 7558 7559 // Handle event binding 7560 jQuery.fn[ name ] = function( data, fn ) { 7561 return arguments.length > 0 ? 7562 this.on( name, null, data, fn ) : 7563 this.trigger( name ); 7564 }; 7565}); 7566 7567jQuery.fn.extend({ 7568 hover: function( fnOver, fnOut ) { 7569 return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); 7570 }, 7571 7572 bind: function( types, data, fn ) { 7573 return this.on( types, null, data, fn ); 7574 }, 7575 unbind: function( types, fn ) { 7576 return this.off( types, null, fn ); 7577 }, 7578 7579 delegate: function( selector, types, data, fn ) { 7580 return this.on( types, selector, data, fn ); 7581 }, 7582 undelegate: function( selector, types, fn ) { 7583 // ( namespace ) or ( selector, types [, fn] ) 7584 return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn ); 7585 } 7586}); 7587var 7588 // Document location 7589 ajaxLocParts, 7590 ajaxLocation, 7591 ajax_nonce = jQuery.now(), 7592 7593 ajax_rquery = /\?/, 7594 rhash = /#.*$/, 7595 rts = /([?&])_=[^&]*/, 7596 rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL
vendor: 4,281 bytes, lines 7597-7740
7597 // #7653, #8125, #8152: local protocol detection 7598 rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, 7599 rnoContent = /^(?:GET|HEAD)$/, 7600 rprotocol = /^\/\//, 7601 rurl = /^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/, 7602 7603 // Keep a copy of the old load method 7604 _load = jQuery.fn.load, 7605 7606 /* Prefilters 7607 * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) 7608 * 2) These are called: 7609 * - BEFORE asking for a transport 7610 * - AFTER param serialization (s.data is a string if s.processData is true) 7611 * 3) key is the dataType 7612 * 4) the catchall symbol "*" can be used 7613 * 5) execution will start with transport dataType and THEN continue down to "*" if needed 7614 */ 7615 prefilters = {}, 7616 7617 /* Transports bindings 7618 * 1) key is the dataType 7619 * 2) the catchall symbol "*" can be used 7620 * 3) selection will start with transport dataType and THEN go to "*" if needed 7621 */ 7622 transports = {}, 7623 7624 // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression 7625 allTypes = "*/".concat("*"); 7626 7627// #8138, IE may throw an exception when accessing 7628// a field from window.location if document.domain has been set 7629try { 7630 ajaxLocation = location.href; 7631} catch( e ) { 7632 // Use the href attribute of an A element 7633 // since IE will modify it given document.location 7634 ajaxLocation = document.createElement( "a" ); 7635 ajaxLocation.href = ""; 7636 ajaxLocation = ajaxLocation.href; 7637} 7638 7639// Segment location into parts 7640ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || []; 7641 7642// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport 7643function addToPrefiltersOrTransports( structure ) { 7644 7645 // dataTypeExpression is optional and defaults to "*" 7646 return function( dataTypeExpression, func ) { 7647 7648 if ( typeof dataTypeExpression !== "string" ) { 7649 func = dataTypeExpression; 7650 dataTypeExpression = "*"; 7651 } 7652 7653 var dataType, 7654 i = 0, 7655 dataTypes = dataTypeExpression.toLowerCase().match( core_rnotwhite ) || []; 7656 7657 if ( jQuery.isFunction( func ) ) { 7658 // For each dataType in the dataTypeExpression 7659 while ( (dataType = dataTypes[i++]) ) { 7660 // Prepend if requested 7661 if ( dataType[0] === "+" ) { 7662 dataType = dataType.slice( 1 ) || "*"; 7663 (structure[ dataType ] = structure[ dataType ] || []).unshift( func ); 7664 7665 // Otherwise append 7666 } else { 7667 (structure[ dataType ] = structure[ dataType ] || []).push( func ); 7668 } 7669 } 7670 } 7671 }; 7672} 7673 7674// Base inspection function for prefilters and transports 7675function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { 7676 7677 var inspected = {}, 7678 seekingTransport = ( structure === transports ); 7679 7680 function inspect( dataType ) { 7681 var selected; 7682 inspected[ dataType ] = true; 7683 jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { 7684 var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); 7685 if( typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[ dataTypeOrTransport ] ) { 7686 options.dataTypes.unshift( dataTypeOrTransport ); 7687 inspect( dataTypeOrTransport ); 7688 return false; 7689 } else if ( seekingTransport ) { 7690 return !( selected = dataTypeOrTransport ); 7691 } 7692 }); 7693 return selected; 7694 } 7695 7696 return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); 7697} 7698 7699// A special extend for ajax options 7700// that takes "flat" options (not to be deep extended) 7701// Fixes #9887 7702function ajaxExtend( target, src ) { 7703 var deep, key, 7704 flatOptions = jQuery.ajaxSettings.flatOptions || {}; 7705 7706 for ( key in src ) { 7707 if ( src[ key ] !== undefined ) { 7708 ( flatOptions[ key ] ? target : ( deep || (deep = {}) ) )[ key ] = src[ key ]; 7709 } 7710 } 7711 if ( deep ) { 7712 jQuery.extend( true, target, deep ); 7713 } 7714 7715 return target; 7716} 7717 7718jQuery.fn.load = function( url, params, callback ) { 7719 if ( typeof url !== "string" && _load ) { 7720 return _load.apply( this, arguments ); 7721 } 7722 7723 var selector, response, type, 7724 self = this, 7725 off = url.indexOf(" "); 7726 7727 if ( off >= 0 ) { 7728 selector = url.slice( off, url.length ); 7729 url = url.slice( 0, off ); 7730 } 7731 7732 // If it's a function 7733 if ( jQuery.isFunction( params ) ) { 7734 7735 // We assume that it's the callback 7736 callback = params; 7737 params = undefined; 7738 7739 // Otherwise, build a param string 7740 } else if ( params && typeof params === "object" ) {
vendor: 15,508 bytes, lines 7741-8323
7741 type = "POST"; 7742 } 7743 7744 // If we have elements to modify, make the request 7745 if ( self.length > 0 ) { 7746 jQuery.ajax({ 7747 url: url, 7748 7749 // if "type" variable is undefined, then "GET" method will be used 7750 type: type, 7751 dataType: "html", 7752 data: params 7753 }).done(function( responseText ) { 7754 7755 // Save response for use in complete callback 7756 response = arguments; 7757 7758 self.html( selector ? 7759 7760 // If a selector was specified, locate the right elements in a dummy div 7761 // Exclude scripts to avoid IE 'Permission Denied' errors 7762 jQuery("<div>").append( jQuery.parseHTML( responseText ) ).find( selector ) : 7763 7764 // Otherwise use the full result 7765 responseText ); 7766 7767 }).complete( callback && function( jqXHR, status ) { 7768 self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] ); 7769 }); 7770 } 7771 7772 return this; 7773}; 7774 7775// Attach a bunch of functions for handling common AJAX events 7776jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ){ 7777 jQuery.fn[ type ] = function( fn ){ 7778 return this.on( type, fn ); 7779 }; 7780}); 7781 7782jQuery.extend({ 7783 7784 // Counter for holding the number of active queries 7785 active: 0, 7786 7787 // Last-Modified header cache for next request 7788 lastModified: {}, 7789 etag: {}, 7790 7791 ajaxSettings: { 7792 url: ajaxLocation, 7793 type: "GET", 7794 isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ), 7795 global: true, 7796 processData: true, 7797 async: true, 7798 contentType: "application/x-www-form-urlencoded; charset=UTF-8", 7799 /* 7800 timeout: 0, 7801 data: null, 7802 dataType: null, 7803 username: null, 7804 password: null, 7805 cache: null, 7806 throws: false, 7807 traditional: false, 7808 headers: {}, 7809 */ 7810 7811 accepts: { 7812 "*": allTypes, 7813 text: "text/plain", 7814 html: "text/html", 7815 xml: "application/xml, text/xml", 7816 json: "application/json, text/javascript" 7817 }, 7818 7819 contents: { 7820 xml: /xml/, 7821 html: /html/, 7822 json: /json/ 7823 }, 7824 7825 responseFields: { 7826 xml: "responseXML", 7827 text: "responseText", 7828 json: "responseJSON" 7829 }, 7830 7831 // Data converters 7832 // Keys separate source (or catchall "*") and destination types with a single space 7833 converters: { 7834 7835 // Convert anything to text 7836 "* text": String, 7837 7838 // Text to html (true = no transformation) 7839 "text html": true, 7840 7841 // Evaluate text as a json expression 7842 "text json": jQuery.parseJSON, 7843 7844 // Parse text as xml 7845 "text xml": jQuery.parseXML 7846 }, 7847 7848 // For options that shouldn't be deep extended: 7849 // you can add your own custom options here if 7850 // and when you create one that shouldn't be 7851 // deep extended (see ajaxExtend) 7852 flatOptions: { 7853 url: true, 7854 context: true 7855 } 7856 }, 7857 7858 // Creates a full fledged settings object into target 7859 // with both ajaxSettings and settings fields. 7860 // If target is omitted, writes into ajaxSettings. 7861 ajaxSetup: function( target, settings ) { 7862 return settings ? 7863 7864 // Building a settings object 7865 ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : 7866 7867 // Extending ajaxSettings 7868 ajaxExtend( jQuery.ajaxSettings, target ); 7869 }, 7870 7871 ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), 7872 ajaxTransport: addToPrefiltersOrTransports( transports ), 7873 7874 // Main method 7875 ajax: function( url, options ) { 7876 7877 // If url is an object, simulate pre-1.5 signature 7878 if ( typeof url === "object" ) { 7879 options = url; 7880 url = undefined; 7881 } 7882 7883 // Force options to be an object 7884 options = options || {}; 7885 7886 var // Cross-domain detection vars 7887 parts, 7888 // Loop variable 7889 i, 7890 // URL without anti-cache param 7891 cacheURL, 7892 // Response headers as string 7893 responseHeadersString, 7894 // timeout handle 7895 timeoutTimer, 7896 7897 // To know if global events are to be dispatched 7898 fireGlobals, 7899 7900 transport, 7901 // Response headers 7902 responseHeaders, 7903 // Create the final options object 7904 s = jQuery.ajaxSetup( {}, options ), 7905 // Callbacks context 7906 callbackContext = s.context || s, 7907 // Context for global events is callbackContext if it is a DOM node or jQuery collection 7908 globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ? 7909 jQuery( callbackContext ) : 7910 jQuery.event, 7911 // Deferreds 7912 deferred = jQuery.Deferred(), 7913 completeDeferred = jQuery.Callbacks("once memory"), 7914 // Status-dependent callbacks 7915 statusCode = s.statusCode || {}, 7916 // Headers (they are sent all at once) 7917 requestHeaders = {}, 7918 requestHeadersNames = {}, 7919 // The jqXHR state 7920 state = 0, 7921 // Default abort message 7922 strAbort = "canceled", 7923 // Fake xhr 7924 jqXHR = { 7925 readyState: 0, 7926 7927 // Builds headers hashtable if needed 7928 getResponseHeader: function( key ) { 7929 var match; 7930 if ( state === 2 ) { 7931 if ( !responseHeaders ) { 7932 responseHeaders = {}; 7933 while ( (match = rheaders.exec( responseHeadersString )) ) { 7934 responseHeaders[ match[1].toLowerCase() ] = match[ 2 ]; 7935 } 7936 } 7937 match = responseHeaders[ key.toLowerCase() ]; 7938 } 7939 return match == null ? null : match; 7940 }, 7941 7942 // Raw string 7943 getAllResponseHeaders: function() { 7944 return state === 2 ? responseHeadersString : null; 7945 }, 7946 7947 // Caches the header 7948 setRequestHeader: function( name, value ) { 7949 var lname = name.toLowerCase(); 7950 if ( !state ) { 7951 name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name; 7952 requestHeaders[ name ] = value; 7953 } 7954 return this; 7955 }, 7956 7957 // Overrides response content-type header 7958 overrideMimeType: function( type ) { 7959 if ( !state ) { 7960 s.mimeType = type; 7961 } 7962 return this; 7963 }, 7964 7965 // Status-dependent callbacks 7966 statusCode: function( map ) { 7967 var code; 7968 if ( map ) { 7969 if ( state < 2 ) { 7970 for ( code in map ) { 7971 // Lazy-add the new callback in a way that preserves old ones 7972 statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; 7973 } 7974 } else { 7975 // Execute the appropriate callbacks 7976 jqXHR.always( map[ jqXHR.status ] ); 7977 } 7978 } 7979 return this; 7980 }, 7981 7982 // Cancel the request 7983 abort: function( statusText ) { 7984 var finalText = statusText || strAbort; 7985 if ( transport ) { 7986 transport.abort( finalText ); 7987 } 7988 done( 0, finalText ); 7989 return this; 7990 } 7991 }; 7992 7993 // Attach deferreds 7994 deferred.promise( jqXHR ).complete = completeDeferred.add; 7995 jqXHR.success = jqXHR.done; 7996 jqXHR.error = jqXHR.fail; 7997 7998 // Remove hash character (#7531: and string promotion) 7999 // Add protocol if not provided (#5866: IE7 issue with protocol-less urls) 8000 // Handle falsy url in the settings object (#10093: consistency with old signature) 8001 // We also use the url parameter if available 8002 s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" ); 8003 8004 // Alias method option to type as per ticket #12004 8005 s.type = options.method || options.type || s.method || s.type; 8006 8007 // Extract dataTypes list 8008 s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( core_rnotwhite ) || [""]; 8009 8010 // A cross-domain request is in order when we have a protocol:host:port mismatch 8011 if ( s.crossDomain == null ) { 8012 parts = rurl.exec( s.url.toLowerCase() ); 8013 s.crossDomain = !!( parts && 8014 ( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] || 8015 ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? "80" : "443" ) ) !== 8016 ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? "80" : "443" ) ) ) 8017 ); 8018 } 8019 8020 // Convert data if not already a string 8021 if ( s.data && s.processData && typeof s.data !== "string" ) { 8022 s.data = jQuery.param( s.data, s.traditional ); 8023 } 8024 8025 // Apply prefilters 8026 inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); 8027 8028 // If request was aborted inside a prefilter, stop there 8029 if ( state === 2 ) { 8030 return jqXHR; 8031 } 8032 8033 // We can fire global events as of now if asked to 8034 fireGlobals = s.global; 8035 8036 // Watch for a new set of requests 8037 if ( fireGlobals && jQuery.active++ === 0 ) { 8038 jQuery.event.trigger("ajaxStart"); 8039 } 8040 8041 // Uppercase the type 8042 s.type = s.type.toUpperCase(); 8043 8044 // Determine if request has content 8045 s.hasContent = !rnoContent.test( s.type ); 8046 8047 // Save the URL in case we're toying with the If-Modified-Since 8048 // and/or If-None-Match header later on 8049 cacheURL = s.url; 8050 8051 // More options handling for requests with no content 8052 if ( !s.hasContent ) { 8053 8054 // If data is available, append data to url 8055 if ( s.data ) { 8056 cacheURL = ( s.url += ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + s.data ); 8057 // #9682: remove data so that it's not used in an eventual retry 8058 delete s.data; 8059 } 8060 8061 // Add anti-cache in url if needed 8062 if ( s.cache === false ) { 8063 s.url = rts.test( cacheURL ) ? 8064 8065 // If there is already a '_' parameter, set its value 8066 cacheURL.replace( rts, "$1_=" + ajax_nonce++ ) : 8067 8068 // Otherwise add one to the end 8069 cacheURL + ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ajax_nonce++; 8070 } 8071 } 8072 8073 // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. 8074 if ( s.ifModified ) { 8075 if ( jQuery.lastModified[ cacheURL ] ) { 8076 jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); 8077 } 8078 if ( jQuery.etag[ cacheURL ] ) { 8079 jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); 8080 } 8081 } 8082 8083 // Set the correct header, if data is being sent 8084 if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { 8085 jqXHR.setRequestHeader( "Content-Type", s.contentType ); 8086 } 8087 8088 // Set the Accepts header for the server, depending on the dataType 8089 jqXHR.setRequestHeader( 8090 "Accept", 8091 s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ? 8092 s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : 8093 s.accepts[ "*" ] 8094 ); 8095 8096 // Check for headers option 8097 for ( i in s.headers ) { 8098 jqXHR.setRequestHeader( i, s.headers[ i ] ); 8099 } 8100 8101 // Allow custom headers/mimetypes and early abort 8102 if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) { 8103 // Abort if not done already and return 8104 return jqXHR.abort(); 8105 } 8106 8107 // aborting is no longer a cancellation 8108 strAbort = "abort"; 8109 8110 // Install callbacks on deferreds 8111 for ( i in { success: 1, error: 1, complete: 1 } ) { 8112 jqXHR[ i ]( s[ i ] ); 8113 } 8114 8115 // Get transport 8116 transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); 8117 8118 // If no transport, we auto-abort 8119 if ( !transport ) { 8120 done( -1, "No Transport" ); 8121 } else { 8122 jqXHR.readyState = 1; 8123 8124 // Send global event 8125 if ( fireGlobals ) { 8126 globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); 8127 } 8128 // Timeout 8129 if ( s.async && s.timeout > 0 ) { 8130 timeoutTimer = setTimeout(function() { 8131 jqXHR.abort("timeout"); 8132 }, s.timeout ); 8133 } 8134 8135 try { 8136 state = 1; 8137 transport.send( requestHeaders, done ); 8138 } catch ( e ) { 8139 // Propagate exception as error if not done 8140 if ( state < 2 ) { 8141 done( -1, e ); 8142 // Simply rethrow otherwise 8143 } else { 8144 throw e; 8145 } 8146 } 8147 } 8148 8149 // Callback for when everything is done 8150 function done( status, nativeStatusText, responses, headers ) { 8151 var isSuccess, success, error, response, modified, 8152 statusText = nativeStatusText; 8153 8154 // Called once 8155 if ( state === 2 ) { 8156 return; 8157 } 8158 8159 // State is "done" now 8160 state = 2; 8161 8162 // Clear timeout if it exists 8163 if ( timeoutTimer ) { 8164 clearTimeout( timeoutTimer ); 8165 } 8166 8167 // Dereference transport for early garbage collection 8168 // (no matter how long the jqXHR object will be used) 8169 transport = undefined; 8170 8171 // Cache response headers 8172 responseHeadersString = headers || ""; 8173 8174 // Set readyState 8175 jqXHR.readyState = status > 0 ? 4 : 0; 8176 8177 // Determine if successful 8178 isSuccess = status >= 200 && status < 300 || status === 304; 8179 8180 // Get response data 8181 if ( responses ) { 8182 response = ajaxHandleResponses( s, jqXHR, responses ); 8183 } 8184 8185 // Convert no matter what (that way responseXXX fields are always set) 8186 response = ajaxConvert( s, response, jqXHR, isSuccess ); 8187 8188 // If successful, handle type chaining 8189 if ( isSuccess ) { 8190 8191 // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. 8192 if ( s.ifModified ) { 8193 modified = jqXHR.getResponseHeader("Last-Modified"); 8194 if ( modified ) { 8195 jQuery.lastModified[ cacheURL ] = modified; 8196 } 8197 modified = jqXHR.getResponseHeader("etag"); 8198 if ( modified ) { 8199 jQuery.etag[ cacheURL ] = modified; 8200 } 8201 } 8202 8203 // if no content 8204 if ( status === 204 || s.type === "HEAD" ) { 8205 statusText = "nocontent"; 8206 8207 // if not modified 8208 } else if ( status === 304 ) { 8209 statusText = "notmodified"; 8210 8211 // If we have data, let's convert it 8212 } else { 8213 statusText = response.state; 8214 success = response.data; 8215 error = response.error; 8216 isSuccess = !error; 8217 } 8218 } else { 8219 // We extract error from statusText 8220 // then normalize statusText and status for non-aborts 8221 error = statusText; 8222 if ( status || !statusText ) { 8223 statusText = "error"; 8224 if ( status < 0 ) { 8225 status = 0; 8226 } 8227 } 8228 } 8229 8230 // Set data for the fake xhr object 8231 jqXHR.status = status; 8232 jqXHR.statusText = ( nativeStatusText || statusText ) + ""; 8233 8234 // Success/Error 8235 if ( isSuccess ) { 8236 deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); 8237 } else { 8238 deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); 8239 } 8240 8241 // Status-dependent callbacks 8242 jqXHR.statusCode( statusCode ); 8243 statusCode = undefined; 8244 8245 if ( fireGlobals ) { 8246 globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", 8247 [ jqXHR, s, isSuccess ? success : error ] ); 8248 } 8249 8250 // Complete 8251 completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); 8252 8253 if ( fireGlobals ) { 8254 globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); 8255 // Handle the global AJAX counter 8256 if ( !( --jQuery.active ) ) { 8257 jQuery.event.trigger("ajaxStop"); 8258 } 8259 } 8260 } 8261 8262 return jqXHR; 8263 }, 8264 8265 getJSON: function( url, data, callback ) { 8266 return jQuery.get( url, data, callback, "json" ); 8267 }, 8268 8269 getScript: function( url, callback ) { 8270 return jQuery.get( url, undefined, callback, "script" ); 8271 } 8272}); 8273 8274jQuery.each( [ "get", "post" ], function( i, method ) { 8275 jQuery[ method ] = function( url, data, callback, type ) { 8276 // shift arguments if data argument was omitted 8277 if ( jQuery.isFunction( data ) ) { 8278 type = type || callback; 8279 callback = data; 8280 data = undefined; 8281 } 8282 8283 return jQuery.ajax({ 8284 url: url, 8285 type: method, 8286 dataType: type, 8287 data: data, 8288 success: callback 8289 }); 8290 }; 8291}); 8292 8293/* Handles responses to an ajax request: 8294 * - finds the right dataType (mediates between content-type and expected dataType) 8295 * - returns the corresponding response 8296 */ 8297function ajaxHandleResponses( s, jqXHR, responses ) { 8298 var firstDataType, ct, finalDataType, type, 8299 contents = s.contents, 8300 dataTypes = s.dataTypes; 8301 8302 // Remove auto dataType and get content-type in the process 8303 while( dataTypes[ 0 ] === "*" ) { 8304 dataTypes.shift(); 8305 if ( ct === undefined ) { 8306 ct = s.mimeType || jqXHR.getResponseHeader("Content-Type"); 8307 } 8308 } 8309 8310 // Check if we're dealing with a known content-type 8311 if ( ct ) { 8312 for ( type in contents ) { 8313 if ( contents[ type ] && contents[ type ].test( ct ) ) { 8314 dataTypes.unshift( type ); 8315 break; 8316 } 8317 } 8318 } 8319 8320 // Check to see if we have a response for the expected dataType 8321 if ( dataTypes[ 0 ] in responses ) { 8322 finalDataType = dataTypes[ 0 ]; 8323 } else {
vendor: 7,605 bytes, lines 8324-8613
8324 // Try convertible dataTypes 8325 for ( type in responses ) { 8326 if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) { 8327 finalDataType = type; 8328 break; 8329 } 8330 if ( !firstDataType ) { 8331 firstDataType = type; 8332 } 8333 } 8334 // Or just use first one 8335 finalDataType = finalDataType || firstDataType; 8336 } 8337 8338 // If we found a dataType 8339 // We add the dataType to the list if needed 8340 // and return the corresponding response 8341 if ( finalDataType ) { 8342 if ( finalDataType !== dataTypes[ 0 ] ) { 8343 dataTypes.unshift( finalDataType ); 8344 } 8345 return responses[ finalDataType ]; 8346 } 8347} 8348 8349/* Chain conversions given the request and the original response 8350 * Also sets the responseXXX fields on the jqXHR instance 8351 */ 8352function ajaxConvert( s, response, jqXHR, isSuccess ) { 8353 var conv2, current, conv, tmp, prev, 8354 converters = {}, 8355 // Work with a copy of dataTypes in case we need to modify it for conversion 8356 dataTypes = s.dataTypes.slice(); 8357 8358 // Create converters map with lowercased keys 8359 if ( dataTypes[ 1 ] ) { 8360 for ( conv in s.converters ) { 8361 converters[ conv.toLowerCase() ] = s.converters[ conv ]; 8362 } 8363 } 8364 8365 current = dataTypes.shift(); 8366 8367 // Convert to each sequential dataType 8368 while ( current ) { 8369 8370 if ( s.responseFields[ current ] ) { 8371 jqXHR[ s.responseFields[ current ] ] = response; 8372 } 8373 8374 // Apply the dataFilter if provided 8375 if ( !prev && isSuccess && s.dataFilter ) { 8376 response = s.dataFilter( response, s.dataType ); 8377 } 8378 8379 prev = current; 8380 current = dataTypes.shift(); 8381 8382 if ( current ) { 8383 8384 // There's only work to do if current dataType is non-auto 8385 if ( current === "*" ) { 8386 8387 current = prev; 8388 8389 // Convert response if prev dataType is non-auto and differs from current 8390 } else if ( prev !== "*" && prev !== current ) { 8391 8392 // Seek a direct converter 8393 conv = converters[ prev + " " + current ] || converters[ "* " + current ]; 8394 8395 // If none found, seek a pair 8396 if ( !conv ) { 8397 for ( conv2 in converters ) { 8398 8399 // If conv2 outputs current 8400 tmp = conv2.split( " " ); 8401 if ( tmp[ 1 ] === current ) { 8402 8403 // If prev can be converted to accepted input 8404 conv = converters[ prev + " " + tmp[ 0 ] ] || 8405 converters[ "* " + tmp[ 0 ] ]; 8406 if ( conv ) { 8407 // Condense equivalence converters 8408 if ( conv === true ) { 8409 conv = converters[ conv2 ]; 8410 8411 // Otherwise, insert the intermediate dataType 8412 } else if ( converters[ conv2 ] !== true ) { 8413 current = tmp[ 0 ]; 8414 dataTypes.unshift( tmp[ 1 ] ); 8415 } 8416 break; 8417 } 8418 } 8419 } 8420 } 8421 8422 // Apply converter (if not an equivalence) 8423 if ( conv !== true ) { 8424 8425 // Unless errors are allowed to bubble, catch and return them 8426 if ( conv && s[ "throws" ] ) { 8427 response = conv( response ); 8428 } else { 8429 try { 8430 response = conv( response ); 8431 } catch ( e ) { 8432 return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current }; 8433 } 8434 } 8435 } 8436 } 8437 } 8438 } 8439 8440 return { state: "success", data: response }; 8441} 8442// Install script dataType 8443jQuery.ajaxSetup({ 8444 accepts: { 8445 script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript" 8446 }, 8447 contents: { 8448 script: /(?:java|ecma)script/ 8449 }, 8450 converters: { 8451 "text script": function( text ) { 8452 jQuery.globalEval( text ); 8453 return text; 8454 } 8455 } 8456}); 8457 8458// Handle cache's special case and global 8459jQuery.ajaxPrefilter( "script", function( s ) { 8460 if ( s.cache === undefined ) { 8461 s.cache = false; 8462 } 8463 if ( s.crossDomain ) { 8464 s.type = "GET"; 8465 s.global = false; 8466 } 8467}); 8468 8469// Bind script tag hack transport 8470jQuery.ajaxTransport( "script", function(s) { 8471 8472 // This transport only deals with cross domain requests 8473 if ( s.crossDomain ) { 8474 8475 var script, 8476 head = document.head || jQuery("head")[0] || document.documentElement; 8477 8478 return { 8479 8480 send: function( _, callback ) { 8481 8482 script = document.createElement("script"); 8483 8484 script.async = true; 8485 8486 if ( s.scriptCharset ) { 8487 script.charset = s.scriptCharset; 8488 } 8489 8490 script.src = s.url; 8491 8492 // Attach handlers for all browsers 8493 script.onload = script.onreadystatechange = function( _, isAbort ) { 8494 8495 if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) { 8496 8497 // Handle memory leak in IE 8498 script.onload = script.onreadystatechange = null; 8499 8500 // Remove the script 8501 if ( script.parentNode ) { 8502 script.parentNode.removeChild( script ); 8503 } 8504 8505 // Dereference the script 8506 script = null; 8507 8508 // Callback if not abort 8509 if ( !isAbort ) { 8510 callback( 200, "success" ); 8511 } 8512 } 8513 }; 8514 8515 // Circumvent IE6 bugs with base elements (#2709 and #4378) by prepending 8516 // Use native DOM manipulation to avoid our domManip AJAX trickery 8517 head.insertBefore( script, head.firstChild ); 8518 }, 8519 8520 abort: function() { 8521 if ( script ) { 8522 script.onload( undefined, true ); 8523 } 8524 } 8525 }; 8526 } 8527}); 8528var oldCallbacks = [], 8529 rjsonp = /(=)\?(?=&|$)|\?\?/; 8530 8531// Default jsonp settings 8532jQuery.ajaxSetup({ 8533 jsonp: "callback", 8534 jsonpCallback: function() { 8535 var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( ajax_nonce++ ) ); 8536 this[ callback ] = true; 8537 return callback; 8538 } 8539}); 8540 8541// Detect, normalize options and install callbacks for jsonp requests 8542jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) { 8543 8544 var callbackName, overwritten, responseContainer, 8545 jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ? 8546 "url" : 8547 typeof s.data === "string" && !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") && rjsonp.test( s.data ) && "data" 8548 ); 8549 8550 // Handle iff the expected data type is "jsonp" or we have a parameter to set 8551 if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) { 8552 8553 // Get callback name, remembering preexisting value associated with it 8554 callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ? 8555 s.jsonpCallback() : 8556 s.jsonpCallback; 8557 8558 // Insert callback into url or form data 8559 if ( jsonProp ) { 8560 s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName ); 8561 } else if ( s.jsonp !== false ) { 8562 s.url += ( ajax_rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName; 8563 } 8564 8565 // Use data converter to retrieve json after script execution 8566 s.converters["script json"] = function() { 8567 if ( !responseContainer ) { 8568 jQuery.error( callbackName + " was not called" ); 8569 } 8570 return responseContainer[ 0 ]; 8571 }; 8572 8573 // force json dataType 8574 s.dataTypes[ 0 ] = "json"; 8575 8576 // Install callback 8577 overwritten = window[ callbackName ]; 8578 window[ callbackName ] = function() { 8579 responseContainer = arguments; 8580 }; 8581 8582 // Clean-up function (fires after converters) 8583 jqXHR.always(function() { 8584 // Restore preexisting value 8585 window[ callbackName ] = overwritten; 8586 8587 // Save back as free 8588 if ( s[ callbackName ] ) { 8589 // make sure that re-using the options doesn't screw things around 8590 s.jsonpCallback = originalSettings.jsonpCallback; 8591 8592 // save the callback name for future use 8593 oldCallbacks.push( callbackName ); 8594 } 8595 8596 // Call if it was a function and we have a response 8597 if ( responseContainer && jQuery.isFunction( overwritten ) ) { 8598 overwritten( responseContainer[ 0 ] ); 8599 } 8600 8601 responseContainer = overwritten = undefined; 8602 }); 8603 8604 // Delegate to script 8605 return "script"; 8606 } 8607}); 8608var xhrCallbacks, xhrSupported, 8609 xhrId = 0, 8610 // #5280: Internet Explorer will keep connections alive if we don't abort on unload 8611 xhrOnUnloadAbort = window.ActiveXObject && function() { 8612 // Abort all pending requests 8613 var key;
8614 for ( key in xhrCallbacks ) { 8615 xhrCallbacks[ key ]( undefined, true ); 8616 } 8617 }; 8618 8619// Functions to create xhrs 8620function createStandardXHR() { 8621 try { 8622 return new window.XMLHttpRequest(); 8623 } catch( e ) {} 8624} 8625 8626function createActiveXHR() { 8627 try { 8628 return new window.ActiveXObject("Microsoft.XMLHTTP"); 8629 } catch( e ) {} 8630} 8631 8632// Create the request object 8633// (This is still attached to ajaxSettings for backward compatibility) 8634jQuery.ajaxSettings.xhr = window.ActiveXObject ? 8635 /* Microsoft failed to properly 8636 * implement the XMLHttpRequest in IE7 (can't request local files), 8637 * so we use the ActiveXObject when it is available 8638 * Additionally XMLHttpRequest can be disabled in IE7/IE8 so 8639 * we need a fallback. 8640 */ 8641 function() { 8642 return !this.isLocal && createStandardXHR() || createActiveXHR(); 8643 } : 8644 // For all other browsers, use the standard XMLHttpRequest object 8645 createStandardXHR; 8646 8647// Determine support properties 8648xhrSupported = jQuery.ajaxSettings.xhr(); 8649jQuery.support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); 8650xhrSupported = jQuery.support.ajax = !!xhrSupported; 8651 8652// Create transport if the browser can provide an xhr 8653if ( xhrSupported ) { 8654 8655 jQuery.ajaxTransport(function( s ) { 8656 // Cross domain only allowed if supported through XMLHttpRequest 8657 if ( !s.crossDomain || jQuery.support.cors ) { 8658 8659 var callback; 8660 8661 return { 8662 send: function( headers, complete ) { 8663 8664 // Get a new xhr 8665 var handle, i, 8666 xhr = s.xhr(); 8667 8668 // Open the socket 8669 // Passing null username, generates a login popup on Opera (#2865) 8670 if ( s.username ) { 8671 xhr.open( s.type, s.url, s.async, s.username, s.password ); 8672 } else { 8673 xhr.open( s.type, s.url, s.async ); 8674 } 8675 8676 // Apply custom fields if provided 8677 if ( s.xhrFields ) { 8678 for ( i in s.xhrFields ) { 8679 xhr[ i ] = s.xhrFields[ i ]; 8680 } 8681 } 8682 8683 // Override mime type if needed 8684 if ( s.mimeType && xhr.overrideMimeType ) { 8685 xhr.overrideMimeType( s.mimeType ); 8686 } 8687 8688 // X-Requested-With header 8689 // For cross-domain requests, seeing as conditions for a preflight are 8690 // akin to a jigsaw puzzle, we simply never set it to be sure. 8691 // (it can always be set on a per-request basis or even using ajaxSetup)
8692 // For same-domain requests, won't change header if already provided. 8693 if ( !s.crossDomain && !headers["X-Requested-With"] ) { 8694 headers["X-Requested-With"] = "XMLHttpRequest"; 8695 } 8696 8697 // Need an extra try/catch for cross domain requests in Firefox 3 8698 try { 8699 for ( i in headers ) { 8700 xhr.setRequestHeader( i, headers[ i ] ); 8701 } 8702 } catch( err ) {} 8703 8704 // Do send the request 8705 // This may raise an exception which is actually 8706 // handled in jQuery.ajax (so no try/catch here) 8707 xhr.send( ( s.hasContent && s.data ) || null ); 8708 8709 // Listener 8710 callback = function( _, isAbort ) { 8711 var status, responseHeaders, statusText, responses; 8712 8713 // Firefox throws exceptions when accessing properties 8714 // of an xhr when a network error occurred 8715 // http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE) 8716 try { 8717 8718 // Was never called and is aborted or complete 8719 if ( callback && ( isAbort || xhr.readyState === 4 ) ) { 8720 8721 // Only called once 8722 callback = undefined; 8723 8724 // Do not keep as active anymore 8725 if ( handle ) { 8726 xhr.onreadystatechange = jQuery.noop; 8727 if ( xhrOnUnloadAbort ) { 8728 delete xhrCallbacks[ handle ]; 8729 } 8730 } 8731 8732 // If it's an abort 8733 if ( isAbort ) { 8734 // Abort it manually if needed 8735 if ( xhr.readyState !== 4 ) { 8736 xhr.abort(); 8737 } 8738 } else { 8739 responses = {}; 8740 status = xhr.status; 8741 responseHeaders = xhr.getAllResponseHeaders(); 8742 8743 // When requesting binary data, IE6-9 will throw an exception 8744 // on any attempt to access responseText (#11426) 8745 if ( typeof xhr.responseText === "string" ) { 8746 responses.text = xhr.responseText; 8747 } 8748 8749 // Firefox throws an exception when accessing 8750 // statusText for faulty cross-domain requests 8751 try { 8752 statusText = xhr.statusText; 8753 } catch( e ) { 8754 // We normalize with Webkit giving an empty statusText 8755 statusText = ""; 8756 } 8757 8758 // Filter status for non standard behaviors 8759 8760 // If the request is local and we have data: assume a success 8761 // (success with no data won't get notified, that's the best we 8762 // can do given current implementations) 8763 if ( !status && s.isLocal && !s.crossDomain ) { 8764 status = responses.text ? 200 : 404; 8765 // IE - #1450: sometimes returns 1223 when it should be 204 8766 } else if ( status === 1223 ) { 8767 status = 204; 8768 } 8769 } 8770 } 8771 } catch( firefoxAccessException ) { 8772 if ( !isAbort ) { 8773 complete( -1, firefoxAccessException ); 8774 } 8775 } 8776 8777 // Call complete if needed 8778 if ( responses ) { 8779 complete( status, statusText, responses, responseHeaders ); 8780 } 8781 }; 8782 8783 if ( !s.async ) { 8784 // if we're in sync mode we fire the callback 8785 callback(); 8786 } else if ( xhr.readyState === 4 ) { 8787 // (IE6 & IE7) if it's in cache and has been 8788 // retrieved directly we need to fire the callback 8789 setTimeout( callback ); 8790 } else { 8791 handle = ++xhrId; 8792 if ( xhrOnUnloadAbort ) { 8793 // Create the active xhrs callbacks list if needed 8794 // and attach the unload handler 8795 if ( !xhrCallbacks ) { 8796 xhrCallbacks = {}; 8797 jQuery( window ).unload( xhrOnUnloadAbort ); 8798 } 8799 // Add to list of active xhrs callbacks 8800 xhrCallbacks[ handle ] = callback; 8801 } 8802 xhr.onreadystatechange = callback; 8803 } 8804 }, 8805 8806 abort: function() { 8807 if ( callback ) { 8808 callback( undefined, true ); 8809 } 8810 } 8811 }; 8812 } 8813 }); 8814} 8815var fxNow, timerId, 8816 rfxtypes = /^(?:toggle|show|hide)$/, 8817 rfxnum = new RegExp( "^(?:([+-])=|)(" + core_pnum + ")([a-z%]*)$", "i" ), 8818 rrun = /queueHooks$/, 8819 animationPrefilters = [ defaultPrefilter ], 8820 tweeners = { 8821 "*": [function( prop, value ) { 8822 var tween = this.createTween( prop, value ), 8823 target = tween.cur(), 8824 parts = rfxnum.exec( value ), 8825 unit = parts && parts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),
vendor: 23,716 bytes, lines 8826-9717
8826 8827 // Starting value computation is required for potential unit mismatches 8828 start = ( jQuery.cssNumber[ prop ] || unit !== "px" && +target ) && 8829 rfxnum.exec( jQuery.css( tween.elem, prop ) ), 8830 scale = 1, 8831 maxIterations = 20; 8832 8833 if ( start && start[ 3 ] !== unit ) { 8834 // Trust units reported by jQuery.css 8835 unit = unit || start[ 3 ]; 8836 8837 // Make sure we update the tween properties later on 8838 parts = parts || []; 8839 8840 // Iteratively approximate from a nonzero starting point 8841 start = +target || 1; 8842 8843 do { 8844 // If previous iteration zeroed out, double until we get *something* 8845 // Use a string for doubling factor so we don't accidentally see scale as unchanged below 8846 scale = scale || ".5"; 8847 8848 // Adjust and apply 8849 start = start / scale; 8850 jQuery.style( tween.elem, prop, start + unit ); 8851 8852 // Update scale, tolerating zero or NaN from tween.cur() 8853 // And breaking the loop if scale is unchanged or perfect, or if we've just had enough 8854 } while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations ); 8855 } 8856 8857 // Update tween properties 8858 if ( parts ) { 8859 start = tween.start = +start || +target || 0; 8860 tween.unit = unit; 8861 // If a +=/-= token was provided, we're doing a relative animation 8862 tween.end = parts[ 1 ] ? 8863 start + ( parts[ 1 ] + 1 ) * parts[ 2 ] : 8864 +parts[ 2 ]; 8865 } 8866 8867 return tween; 8868 }] 8869 }; 8870 8871// Animations created synchronously will run synchronously 8872function createFxNow() { 8873 setTimeout(function() { 8874 fxNow = undefined; 8875 }); 8876 return ( fxNow = jQuery.now() ); 8877} 8878 8879function createTween( value, prop, animation ) { 8880 var tween, 8881 collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ), 8882 index = 0, 8883 length = collection.length; 8884 for ( ; index < length; index++ ) { 8885 if ( (tween = collection[ index ].call( animation, prop, value )) ) { 8886 8887 // we're done with this property 8888 return tween; 8889 } 8890 } 8891} 8892 8893function Animation( elem, properties, options ) { 8894 var result, 8895 stopped, 8896 index = 0, 8897 length = animationPrefilters.length, 8898 deferred = jQuery.Deferred().always( function() { 8899 // don't match elem in the :animated selector 8900 delete tick.elem; 8901 }), 8902 tick = function() { 8903 if ( stopped ) { 8904 return false; 8905 } 8906 var currentTime = fxNow || createFxNow(), 8907 remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), 8908 // archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497) 8909 temp = remaining / animation.duration || 0, 8910 percent = 1 - temp, 8911 index = 0, 8912 length = animation.tweens.length; 8913 8914 for ( ; index < length ; index++ ) { 8915 animation.tweens[ index ].run( percent ); 8916 } 8917 8918 deferred.notifyWith( elem, [ animation, percent, remaining ]); 8919 8920 if ( percent < 1 && length ) { 8921 return remaining; 8922 } else { 8923 deferred.resolveWith( elem, [ animation ] ); 8924 return false; 8925 } 8926 }, 8927 animation = deferred.promise({ 8928 elem: elem, 8929 props: jQuery.extend( {}, properties ), 8930 opts: jQuery.extend( true, { specialEasing: {} }, options ), 8931 originalProperties: properties, 8932 originalOptions: options, 8933 startTime: fxNow || createFxNow(), 8934 duration: options.duration, 8935 tweens: [], 8936 createTween: function( prop, end ) { 8937 var tween = jQuery.Tween( elem, animation.opts, prop, end, 8938 animation.opts.specialEasing[ prop ] || animation.opts.easing ); 8939 animation.tweens.push( tween ); 8940 return tween; 8941 }, 8942 stop: function( gotoEnd ) { 8943 var index = 0, 8944 // if we are going to the end, we want to run all the tweens 8945 // otherwise we skip this part 8946 length = gotoEnd ? animation.tweens.length : 0; 8947 if ( stopped ) { 8948 return this; 8949 } 8950 stopped = true; 8951 for ( ; index < length ; index++ ) { 8952 animation.tweens[ index ].run( 1 ); 8953 } 8954 8955 // resolve when we played the last frame 8956 // otherwise, reject 8957 if ( gotoEnd ) { 8958 deferred.resolveWith( elem, [ animation, gotoEnd ] ); 8959 } else { 8960 deferred.rejectWith( elem, [ animation, gotoEnd ] ); 8961 } 8962 return this; 8963 } 8964 }), 8965 props = animation.props; 8966 8967 propFilter( props, animation.opts.specialEasing ); 8968 8969 for ( ; index < length ; index++ ) { 8970 result = animationPrefilters[ index ].call( animation, elem, props, animation.opts ); 8971 if ( result ) { 8972 return result; 8973 } 8974 } 8975 8976 jQuery.map( props, createTween, animation ); 8977 8978 if ( jQuery.isFunction( animation.opts.start ) ) { 8979 animation.opts.start.call( elem, animation ); 8980 } 8981 8982 jQuery.fx.timer( 8983 jQuery.extend( tick, { 8984 elem: elem, 8985 anim: animation, 8986 queue: animation.opts.queue 8987 }) 8988 ); 8989 8990 // attach callbacks from options 8991 return animation.progress( animation.opts.progress ) 8992 .done( animation.opts.done, animation.opts.complete ) 8993 .fail( animation.opts.fail ) 8994 .always( animation.opts.always ); 8995} 8996 8997function propFilter( props, specialEasing ) { 8998 var index, name, easing, value, hooks; 8999 9000 // camelCase, specialEasing and expand cssHook pass 9001 for ( index in props ) { 9002 name = jQuery.camelCase( index ); 9003 easing = specialEasing[ name ]; 9004 value = props[ index ]; 9005 if ( jQuery.isArray( value ) ) { 9006 easing = value[ 1 ]; 9007 value = props[ index ] = value[ 0 ]; 9008 } 9009 9010 if ( index !== name ) { 9011 props[ name ] = value; 9012 delete props[ index ]; 9013 } 9014 9015 hooks = jQuery.cssHooks[ name ]; 9016 if ( hooks && "expand" in hooks ) { 9017 value = hooks.expand( value ); 9018 delete props[ name ]; 9019 9020 // not quite $.extend, this wont overwrite keys already present. 9021 // also - reusing 'index' from above because we have the correct "name" 9022 for ( index in value ) { 9023 if ( !( index in props ) ) { 9024 props[ index ] = value[ index ]; 9025 specialEasing[ index ] = easing; 9026 } 9027 } 9028 } else { 9029 specialEasing[ name ] = easing; 9030 } 9031 } 9032} 9033 9034jQuery.Animation = jQuery.extend( Animation, { 9035 9036 tweener: function( props, callback ) { 9037 if ( jQuery.isFunction( props ) ) { 9038 callback = props; 9039 props = [ "*" ]; 9040 } else { 9041 props = props.split(" "); 9042 } 9043 9044 var prop, 9045 index = 0, 9046 length = props.length; 9047 9048 for ( ; index < length ; index++ ) { 9049 prop = props[ index ]; 9050 tweeners[ prop ] = tweeners[ prop ] || []; 9051 tweeners[ prop ].unshift( callback ); 9052 } 9053 }, 9054 9055 prefilter: function( callback, prepend ) { 9056 if ( prepend ) { 9057 animationPrefilters.unshift( callback ); 9058 } else { 9059 animationPrefilters.push( callback ); 9060 } 9061 } 9062}); 9063 9064function defaultPrefilter( elem, props, opts ) { 9065 /* jshint validthis: true */ 9066 var prop, value, toggle, tween, hooks, oldfire, 9067 anim = this, 9068 orig = {}, 9069 style = elem.style, 9070 hidden = elem.nodeType && isHidden( elem ), 9071 dataShow = jQuery._data( elem, "fxshow" ); 9072 9073 // handle queue: false promises 9074 if ( !opts.queue ) { 9075 hooks = jQuery._queueHooks( elem, "fx" ); 9076 if ( hooks.unqueued == null ) { 9077 hooks.unqueued = 0; 9078 oldfire = hooks.empty.fire; 9079 hooks.empty.fire = function() { 9080 if ( !hooks.unqueued ) { 9081 oldfire(); 9082 } 9083 }; 9084 } 9085 hooks.unqueued++; 9086 9087 anim.always(function() { 9088 // doing this makes sure that the complete handler will be called 9089 // before this completes 9090 anim.always(function() { 9091 hooks.unqueued--; 9092 if ( !jQuery.queue( elem, "fx" ).length ) { 9093 hooks.empty.fire(); 9094 } 9095 }); 9096 }); 9097 } 9098 9099 // height/width overflow pass 9100 if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) { 9101 // Make sure that nothing sneaks out 9102 // Record all 3 overflow attributes because IE does not 9103 // change the overflow attribute when overflowX and 9104 // overflowY are set to the same value 9105 opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; 9106 9107 // Set display property to inline-block for height/width 9108 // animations on inline elements that are having width/height animated 9109 if ( jQuery.css( elem, "display" ) === "inline" && 9110 jQuery.css( elem, "float" ) === "none" ) { 9111 9112 // inline-level elements accept inline-block; 9113 // block-level elements need to be inline with layout 9114 if ( !jQuery.support.inlineBlockNeedsLayout || css_defaultDisplay( elem.nodeName ) === "inline" ) { 9115 style.display = "inline-block"; 9116 9117 } else { 9118 style.zoom = 1; 9119 } 9120 } 9121 } 9122 9123 if ( opts.overflow ) { 9124 style.overflow = "hidden"; 9125 if ( !jQuery.support.shrinkWrapBlocks ) { 9126 anim.always(function() { 9127 style.overflow = opts.overflow[ 0 ]; 9128 style.overflowX = opts.overflow[ 1 ]; 9129 style.overflowY = opts.overflow[ 2 ]; 9130 }); 9131 } 9132 } 9133 9134 9135 // show/hide pass 9136 for ( prop in props ) { 9137 value = props[ prop ]; 9138 if ( rfxtypes.exec( value ) ) { 9139 delete props[ prop ]; 9140 toggle = toggle || value === "toggle"; 9141 if ( value === ( hidden ? "hide" : "show" ) ) { 9142 continue; 9143 } 9144 orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); 9145 } 9146 } 9147 9148 if ( !jQuery.isEmptyObject( orig ) ) { 9149 if ( dataShow ) { 9150 if ( "hidden" in dataShow ) { 9151 hidden = dataShow.hidden; 9152 } 9153 } else { 9154 dataShow = jQuery._data( elem, "fxshow", {} ); 9155 } 9156 9157 // store state if its toggle - enables .stop().toggle() to "reverse" 9158 if ( toggle ) { 9159 dataShow.hidden = !hidden; 9160 } 9161 if ( hidden ) { 9162 jQuery( elem ).show(); 9163 } else { 9164 anim.done(function() { 9165 jQuery( elem ).hide(); 9166 }); 9167 } 9168 anim.done(function() { 9169 var prop; 9170 jQuery._removeData( elem, "fxshow" ); 9171 for ( prop in orig ) { 9172 jQuery.style( elem, prop, orig[ prop ] ); 9173 } 9174 }); 9175 for ( prop in orig ) { 9176 tween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); 9177 9178 if ( !( prop in dataShow ) ) { 9179 dataShow[ prop ] = tween.start; 9180 if ( hidden ) { 9181 tween.end = tween.start; 9182 tween.start = prop === "width" || prop === "height" ? 1 : 0; 9183 } 9184 } 9185 } 9186 } 9187} 9188 9189function Tween( elem, options, prop, end, easing ) { 9190 return new Tween.prototype.init( elem, options, prop, end, easing ); 9191} 9192jQuery.Tween = Tween; 9193 9194Tween.prototype = { 9195 constructor: Tween, 9196 init: function( elem, options, prop, end, easing, unit ) { 9197 this.elem = elem; 9198 this.prop = prop; 9199 this.easing = easing || "swing"; 9200 this.options = options; 9201 this.start = this.now = this.cur(); 9202 this.end = end; 9203 this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); 9204 }, 9205 cur: function() { 9206 var hooks = Tween.propHooks[ this.prop ]; 9207 9208 return hooks && hooks.get ? 9209 hooks.get( this ) : 9210 Tween.propHooks._default.get( this ); 9211 }, 9212 run: function( percent ) { 9213 var eased, 9214 hooks = Tween.propHooks[ this.prop ]; 9215 9216 if ( this.options.duration ) { 9217 this.pos = eased = jQuery.easing[ this.easing ]( 9218 percent, this.options.duration * percent, 0, 1, this.options.duration 9219 ); 9220 } else { 9221 this.pos = eased = percent; 9222 } 9223 this.now = ( this.end - this.start ) * eased + this.start; 9224 9225 if ( this.options.step ) { 9226 this.options.step.call( this.elem, this.now, this ); 9227 } 9228 9229 if ( hooks && hooks.set ) { 9230 hooks.set( this ); 9231 } else { 9232 Tween.propHooks._default.set( this ); 9233 } 9234 return this; 9235 } 9236}; 9237 9238Tween.prototype.init.prototype = Tween.prototype; 9239 9240Tween.propHooks = { 9241 _default: { 9242 get: function( tween ) { 9243 var result; 9244 9245 if ( tween.elem[ tween.prop ] != null && 9246 (!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) { 9247 return tween.elem[ tween.prop ]; 9248 } 9249 9250 // passing an empty string as a 3rd parameter to .css will automatically 9251 // attempt a parseFloat and fallback to a string if the parse fails 9252 // so, simple values such as "10px" are parsed to Float. 9253 // complex values such as "rotate(1rad)" are returned as is. 9254 result = jQuery.css( tween.elem, tween.prop, "" ); 9255 // Empty strings, null, undefined and "auto" are converted to 0. 9256 return !result || result === "auto" ? 0 : result; 9257 }, 9258 set: function( tween ) { 9259 // use step hook for back compat - use cssHook if its there - use .style if its 9260 // available and use plain properties where available 9261 if ( jQuery.fx.step[ tween.prop ] ) { 9262 jQuery.fx.step[ tween.prop ]( tween ); 9263 } else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) { 9264 jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); 9265 } else { 9266 tween.elem[ tween.prop ] = tween.now; 9267 } 9268 } 9269 } 9270}; 9271 9272// Support: IE <=9 9273// Panic based approach to setting things on disconnected nodes 9274 9275Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { 9276 set: function( tween ) { 9277 if ( tween.elem.nodeType && tween.elem.parentNode ) { 9278 tween.elem[ tween.prop ] = tween.now; 9279 } 9280 } 9281}; 9282 9283jQuery.each([ "toggle", "show", "hide" ], function( i, name ) { 9284 var cssFn = jQuery.fn[ name ]; 9285 jQuery.fn[ name ] = function( speed, easing, callback ) { 9286 return speed == null || typeof speed === "boolean" ? 9287 cssFn.apply( this, arguments ) : 9288 this.animate( genFx( name, true ), speed, easing, callback ); 9289 }; 9290}); 9291 9292jQuery.fn.extend({ 9293 fadeTo: function( speed, to, easing, callback ) { 9294 9295 // show any hidden elements after setting opacity to 0 9296 return this.filter( isHidden ).css( "opacity", 0 ).show() 9297 9298 // animate to the value specified 9299 .end().animate({ opacity: to }, speed, easing, callback ); 9300 }, 9301 animate: function( prop, speed, easing, callback ) { 9302 var empty = jQuery.isEmptyObject( prop ), 9303 optall = jQuery.speed( speed, easing, callback ), 9304 doAnimation = function() { 9305 // Operate on a copy of prop so per-property easing won't be lost 9306 var anim = Animation( this, jQuery.extend( {}, prop ), optall ); 9307 9308 // Empty animations, or finishing resolves immediately 9309 if ( empty || jQuery._data( this, "finish" ) ) { 9310 anim.stop( true ); 9311 } 9312 }; 9313 doAnimation.finish = doAnimation; 9314 9315 return empty || optall.queue === false ? 9316 this.each( doAnimation ) : 9317 this.queue( optall.queue, doAnimation ); 9318 }, 9319 stop: function( type, clearQueue, gotoEnd ) { 9320 var stopQueue = function( hooks ) { 9321 var stop = hooks.stop; 9322 delete hooks.stop; 9323 stop( gotoEnd ); 9324 }; 9325 9326 if ( typeof type !== "string" ) { 9327 gotoEnd = clearQueue; 9328 clearQueue = type; 9329 type = undefined; 9330 } 9331 if ( clearQueue && type !== false ) { 9332 this.queue( type || "fx", [] ); 9333 } 9334 9335 return this.each(function() { 9336 var dequeue = true, 9337 index = type != null && type + "queueHooks", 9338 timers = jQuery.timers, 9339 data = jQuery._data( this ); 9340 9341 if ( index ) { 9342 if ( data[ index ] && data[ index ].stop ) { 9343 stopQueue( data[ index ] ); 9344 } 9345 } else { 9346 for ( index in data ) { 9347 if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { 9348 stopQueue( data[ index ] ); 9349 } 9350 } 9351 } 9352 9353 for ( index = timers.length; index--; ) { 9354 if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) { 9355 timers[ index ].anim.stop( gotoEnd ); 9356 dequeue = false; 9357 timers.splice( index, 1 ); 9358 } 9359 } 9360 9361 // start the next in the queue if the last step wasn't forced 9362 // timers currently will call their complete callbacks, which will dequeue 9363 // but only if they were gotoEnd 9364 if ( dequeue || !gotoEnd ) { 9365 jQuery.dequeue( this, type ); 9366 } 9367 }); 9368 }, 9369 finish: function( type ) { 9370 if ( type !== false ) { 9371 type = type || "fx"; 9372 } 9373 return this.each(function() { 9374 var index, 9375 data = jQuery._data( this ), 9376 queue = data[ type + "queue" ], 9377 hooks = data[ type + "queueHooks" ], 9378 timers = jQuery.timers, 9379 length = queue ? queue.length : 0; 9380 9381 // enable finishing flag on private data 9382 data.finish = true; 9383 9384 // empty the queue first 9385 jQuery.queue( this, type, [] ); 9386 9387 if ( hooks && hooks.stop ) { 9388 hooks.stop.call( this, true ); 9389 } 9390 9391 // look for any active animations, and finish them 9392 for ( index = timers.length; index--; ) { 9393 if ( timers[ index ].elem === this && timers[ index ].queue === type ) { 9394 timers[ index ].anim.stop( true ); 9395 timers.splice( index, 1 ); 9396 } 9397 } 9398 9399 // look for any animations in the old queue and finish them 9400 for ( index = 0; index < length; index++ ) { 9401 if ( queue[ index ] && queue[ index ].finish ) { 9402 queue[ index ].finish.call( this ); 9403 } 9404 } 9405 9406 // turn off finishing flag 9407 delete data.finish; 9408 }); 9409 } 9410}); 9411 9412// Generate parameters to create a standard animation 9413function genFx( type, includeWidth ) { 9414 var which, 9415 attrs = { height: type }, 9416 i = 0; 9417 9418 // if we include width, step value is 1 to do all cssExpand values, 9419 // if we don't include width, step value is 2 to skip over Left and Right 9420 includeWidth = includeWidth? 1 : 0; 9421 for( ; i < 4 ; i += 2 - includeWidth ) { 9422 which = cssExpand[ i ]; 9423 attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; 9424 } 9425 9426 if ( includeWidth ) { 9427 attrs.opacity = attrs.width = type; 9428 } 9429 9430 return attrs; 9431} 9432 9433// Generate shortcuts for custom animations 9434jQuery.each({ 9435 slideDown: genFx("show"), 9436 slideUp: genFx("hide"), 9437 slideToggle: genFx("toggle"), 9438 fadeIn: { opacity: "show" }, 9439 fadeOut: { opacity: "hide" }, 9440 fadeToggle: { opacity: "toggle" } 9441}, function( name, props ) { 9442 jQuery.fn[ name ] = function( speed, easing, callback ) { 9443 return this.animate( props, speed, easing, callback ); 9444 }; 9445}); 9446 9447jQuery.speed = function( speed, easing, fn ) { 9448 var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { 9449 complete: fn || !fn && easing || 9450 jQuery.isFunction( speed ) && speed, 9451 duration: speed, 9452 easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing 9453 }; 9454 9455 opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration : 9456 opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default; 9457 9458 // normalize opt.queue - true/undefined/null -> "fx" 9459 if ( opt.queue == null || opt.queue === true ) { 9460 opt.queue = "fx"; 9461 } 9462 9463 // Queueing 9464 opt.old = opt.complete; 9465 9466 opt.complete = function() { 9467 if ( jQuery.isFunction( opt.old ) ) { 9468 opt.old.call( this ); 9469 } 9470 9471 if ( opt.queue ) { 9472 jQuery.dequeue( this, opt.queue ); 9473 } 9474 }; 9475 9476 return opt; 9477}; 9478 9479jQuery.easing = { 9480 linear: function( p ) { 9481 return p; 9482 }, 9483 swing: function( p ) { 9484 return 0.5 - Math.cos( p*Math.PI ) / 2; 9485 } 9486}; 9487 9488jQuery.timers = []; 9489jQuery.fx = Tween.prototype.init; 9490jQuery.fx.tick = function() { 9491 var timer, 9492 timers = jQuery.timers, 9493 i = 0; 9494 9495 fxNow = jQuery.now(); 9496 9497 for ( ; i < timers.length; i++ ) { 9498 timer = timers[ i ]; 9499 // Checks the timer has not already been removed 9500 if ( !timer() && timers[ i ] === timer ) { 9501 timers.splice( i--, 1 ); 9502 } 9503 } 9504 9505 if ( !timers.length ) { 9506 jQuery.fx.stop(); 9507 } 9508 fxNow = undefined; 9509}; 9510 9511jQuery.fx.timer = function( timer ) { 9512 if ( timer() && jQuery.timers.push( timer ) ) { 9513 jQuery.fx.start(); 9514 } 9515}; 9516 9517jQuery.fx.interval = 13; 9518 9519jQuery.fx.start = function() { 9520 if ( !timerId ) { 9521 timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval ); 9522 } 9523}; 9524 9525jQuery.fx.stop = function() { 9526 clearInterval( timerId ); 9527 timerId = null; 9528}; 9529 9530jQuery.fx.speeds = { 9531 slow: 600, 9532 fast: 200, 9533 // Default speed 9534 _default: 400 9535}; 9536 9537// Back Compat <1.8 extension point 9538jQuery.fx.step = {}; 9539 9540if ( jQuery.expr && jQuery.expr.filters ) { 9541 jQuery.expr.filters.animated = function( elem ) { 9542 return jQuery.grep(jQuery.timers, function( fn ) { 9543 return elem === fn.elem; 9544 }).length; 9545 }; 9546} 9547jQuery.fn.offset = function( options ) { 9548 if ( arguments.length ) { 9549 return options === undefined ? 9550 this : 9551 this.each(function( i ) { 9552 jQuery.offset.setOffset( this, options, i ); 9553 }); 9554 } 9555 9556 var docElem, win, 9557 box = { top: 0, left: 0 }, 9558 elem = this[ 0 ], 9559 doc = elem && elem.ownerDocument; 9560 9561 if ( !doc ) { 9562 return; 9563 } 9564 9565 docElem = doc.documentElement; 9566 9567 // Make sure it's not a disconnected DOM node 9568 if ( !jQuery.contains( docElem, elem ) ) { 9569 return box; 9570 } 9571 9572 // If we don't have gBCR, just use 0,0 rather than error 9573 // BlackBerry 5, iOS 3 (original iPhone) 9574 if ( typeof elem.getBoundingClientRect !== core_strundefined ) { 9575 box = elem.getBoundingClientRect(); 9576 } 9577 win = getWindow( doc ); 9578 return { 9579 top: box.top + ( win.pageYOffset || docElem.scrollTop ) - ( docElem.clientTop || 0 ), 9580 left: box.left + ( win.pageXOffset || docElem.scrollLeft ) - ( docElem.clientLeft || 0 ) 9581 }; 9582}; 9583 9584jQuery.offset = { 9585 9586 setOffset: function( elem, options, i ) { 9587 var position = jQuery.css( elem, "position" ); 9588 9589 // set position first, in-case top/left are set even on static elem 9590 if ( position === "static" ) { 9591 elem.style.position = "relative"; 9592 } 9593 9594 var curElem = jQuery( elem ), 9595 curOffset = curElem.offset(), 9596 curCSSTop = jQuery.css( elem, "top" ), 9597 curCSSLeft = jQuery.css( elem, "left" ), 9598 calculatePosition = ( position === "absolute" || position === "fixed" ) && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1, 9599 props = {}, curPosition = {}, curTop, curLeft; 9600 9601 // need to be able to calculate position if either top or left is auto and position is either absolute or fixed 9602 if ( calculatePosition ) { 9603 curPosition = curElem.position(); 9604 curTop = curPosition.top; 9605 curLeft = curPosition.left; 9606 } else { 9607 curTop = parseFloat( curCSSTop ) || 0; 9608 curLeft = parseFloat( curCSSLeft ) || 0; 9609 } 9610 9611 if ( jQuery.isFunction( options ) ) { 9612 options = options.call( elem, i, curOffset ); 9613 } 9614 9615 if ( options.top != null ) { 9616 props.top = ( options.top - curOffset.top ) + curTop; 9617 } 9618 if ( options.left != null ) { 9619 props.left = ( options.left - curOffset.left ) + curLeft; 9620 } 9621 9622 if ( "using" in options ) { 9623 options.using.call( elem, props ); 9624 } else { 9625 curElem.css( props ); 9626 } 9627 } 9628}; 9629 9630 9631jQuery.fn.extend({ 9632 9633 position: function() { 9634 if ( !this[ 0 ] ) { 9635 return; 9636 } 9637 9638 var offsetParent, offset, 9639 parentOffset = { top: 0, left: 0 }, 9640 elem = this[ 0 ]; 9641 9642 // fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is it's only offset parent 9643 if ( jQuery.css( elem, "position" ) === "fixed" ) { 9644 // we assume that getBoundingClientRect is available when computed position is fixed 9645 offset = elem.getBoundingClientRect(); 9646 } else { 9647 // Get *real* offsetParent 9648 offsetParent = this.offsetParent(); 9649 9650 // Get correct offsets 9651 offset = this.offset(); 9652 if ( !jQuery.nodeName( offsetParent[ 0 ], "html" ) ) { 9653 parentOffset = offsetParent.offset(); 9654 } 9655 9656 // Add offsetParent borders 9657 parentOffset.top += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true ); 9658 parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true ); 9659 } 9660 9661 // Subtract parent offsets and element margins 9662 // note: when an element has margin: auto the offsetLeft and marginLeft 9663 // are the same in Safari causing offset.left to incorrectly be 0 9664 return { 9665 top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ), 9666 left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true) 9667 }; 9668 }, 9669 9670 offsetParent: function() { 9671 return this.map(function() { 9672 var offsetParent = this.offsetParent || docElem; 9673 while ( offsetParent && ( !jQuery.nodeName( offsetParent, "html" ) && jQuery.css( offsetParent, "position") === "static" ) ) { 9674 offsetParent = offsetParent.offsetParent; 9675 } 9676 return offsetParent || docElem; 9677 }); 9678 } 9679}); 9680 9681 9682// Create scrollLeft and scrollTop methods 9683jQuery.each( {scrollLeft: "pageXOffset", scrollTop: "pageYOffset"}, function( method, prop ) { 9684 var top = /Y/.test( prop ); 9685 9686 jQuery.fn[ method ] = function( val ) { 9687 return jQuery.access( this, function( elem, method, val ) { 9688 var win = getWindow( elem ); 9689 9690 if ( val === undefined ) { 9691 return win ? (prop in win) ? win[ prop ] : 9692 win.document.documentElement[ method ] : 9693 elem[ method ]; 9694 } 9695 9696 if ( win ) { 9697 win.scrollTo( 9698 !top ? val : jQuery( win ).scrollLeft(), 9699 top ? val : jQuery( win ).scrollTop() 9700 ); 9701 9702 } else { 9703 elem[ method ] = val; 9704 } 9705 }, method, val, arguments.length, null ); 9706 }; 9707}); 9708 9709function getWindow( elem ) { 9710 return jQuery.isWindow( elem ) ? 9711 elem : 9712 elem.nodeType === 9 ? 9713 elem.defaultView || elem.parentWindow : 9714 false; 9715} 9716// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods 9717jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
9718 jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) { 9719 // margin is only for outerHeight, outerWidth 9720 jQuery.fn[ funcName ] = function( margin, value ) { 9721 var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ), 9722 extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" ); 9723 9724 return jQuery.access( this, function( elem, type, value ) { 9725 var doc; 9726 9727 if ( jQuery.isWindow( elem ) ) { 9728 // As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there 9729 // isn't a whole lot we can do. See pull request at this URL for discussion: 9730 // https://github.com/jquery/jquery/pull/764 9731 return elem.document.documentElement[ "client" + name ]; 9732 } 9733 9734 // Get document width or height 9735 if ( elem.nodeType === 9 ) { 9736 doc = elem.documentElement; 9737 9738 // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], whichever is greatest 9739 // unfortunately, this causes bug #3838 in IE6/8 only, but there is currently no good, small way to fix it. 9740 return Math.max( 9741 elem.body[ "scroll" + name ], doc[ "scroll" + name ], 9742 elem.body[ "offset" + name ], doc[ "offset" + name ], 9743 doc[ "client" + name ] 9744 ); 9745 } 9746 9747 return value === undefined ? 9748 // Get width or height on the element, requesting but not forcing parseFloat 9749 jQuery.css( elem, type, extra ) : 9750 9751 // Set width or height on the element 9752 jQuery.style( elem, type, value, extra ); 9753 }, type, chainable ? margin : undefined, chainable, null ); 9754 }; 9755 }); 9756}); 9757// Limit scope pollution from any deprecated API 9758// (function() { 9759 9760// The number of elements contained in the matched element set 9761jQuery.fn.size = function() { 9762 return this.length; 9763}; 9764 9765jQuery.fn.andSelf = jQuery.fn.addBack; 9766 9767// })(); 9768if ( typeof module === "object" && module && typeof module.exports === "object" ) { 9769 // Expose jQuery as module.exports in loaders that implement the Node 9770 // module pattern (including browserify). Do not create the global, since 9771 // the user will be storing it themselves locally, and globals are frowned 9772 // upon in the Node module world. 9773 module.exports = jQuery; 9774} else { 9775 // Otherwise expose jQuery to the global object as usual 9776 window.jQuery = window.$ = jQuery; 9777 9778 // Register as a named AMD module, since jQuery can be concatenated with other 9779 // files that may use define, but not via a proper concatenation script that 9780 // understands anonymous AMD modules. A named AMD is safest and most robust 9781 // way to register. Lowercase jquery is used because AMD module names are 9782 // derived from file names, and jQuery is normally delivered in a lowercase 9783 // file name. Do this after creating the global so that if an AMD module wants 9784 // to call noConflict to hide this version of jQuery, it will work. 9785 if ( typeof define === "function" && define.amd ) { 9786 define( "jquery", [], function () { return jQuery; } ); 9787 } 9788} 9789 9790})( window ); 9791/*! jQuery Migrate v1.2.1 | (c) 2005, 2013 jQuery Foundation, Inc. and other contributors | jquery.org/license */ 9792 9793jQuery.migrateMute===void 0&&(jQuery.migrateMute=!0),function(e,t,n){function r(n){var r=t.console;i[n]||(i[n]=!0,e.migrateWarnings.push(n),r&&r.warn&&!e.migrateMute&&(r.warn("JQMIGRATE: "+n),e.migrateTrace&&r.trace&&r.trace()))}function a(t,a,i,o){if(Object.defineProperty)try{return Object.defineProperty(t,a,{configurable:!0,enumerable:!0,get:function(){return r(o),i},set:function(e){r(o),i=e}}),n}catch(s){}e._definePropertyBroken=!0,t[a]=i}var i={};e.migrateWarnings=[],!e.migrateMute&&t.console&&t.console.log&&t.console.log("JQMIGRATE: Logging is active"),e.migrateTrace===n&&(e.migrateTrace=!0),e.migrateReset=function(){i={},e.migrateWarnings.length=0},"BackCompat"===document.compatMode&&r("jQuery is not compatible with Quirks Mode");var o=e("<input/>",{size:1}).attr("size")&&e.attrFn,s=e.attr,u=e.attrHooks.value&&e.attrHooks.value.get||function(){return null},c=e.attrHooks.value&&e.attrHooks.value.set||function(){return n},l=/^(?:input|button)$/i,d=/^[238]$/,p=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,f=/^(?:checked|selected)$/i;a(e,"attrFn",o||{},"jQuery.attrFn is deprecated"),e.attr=function(t,a,i,u){var c=a.toLowerCase(),g=t&&t.nodeType;return u&&(4>s.length&&r("jQuery.fn.attr( props, pass ) is deprecated"),t&&!d.test(g)&&(o?a in o:e.isFunction(e.fn[a])))?e(t)[a](i):("type"===a&&i!==n&&l.test(t.no
vendor: 5,981 bytes, lines 9793-9809
9793deName)&&t.parentNode&&r("Can't change the 'type' of an input or button in IE 6/7/8"),!e.attrHooks[c]&&p.test(c)&&(e.attrHooks[c]={get:function(t,r){var a,i=e.prop(t,r);return i===!0||"boolean"!=typeof i&&(a=t.getAttributeNode(r))&&a.nodeValue!==!1?r.toLowerCase():n},set:function(t,n,r){var a;return n===!1?e.removeAttr(t,r):(a=e.propFix[r]||r,a in t&&(t[a]=!0),t.setAttribute(r,r.toLowerCase())),r}},f.test(c)&&r("jQuery.fn.attr('"+c+"') may use property instead of attribute")),s.call(e,t,a,i))},e.attrHooks.value={get:function(e,t){var n=(e.nodeName||"").toLowerCase();return"button"===n?u.apply(this,arguments):("input"!==n&&"option"!==n&&r("jQuery.fn.attr('value') no longer gets properties"),t in e?e.value:null)},set:function(e,t){var a=(e.nodeName||"").toLowerCase();return"button"===a?c.apply(this,arguments):("input"!==a&&"option"!==a&&r("jQuery.fn.attr('value', val) no longer sets properties"),e.value=t,n)}};var g,h,v=e.fn.init,m=e.parseJSON,y=/^([^<]*)(<[\w\W]+>)([^>]*)$/;e.fn.init=function(t,n,a){var i;return t&&"string"==typeof t&&!e.isPlainObject(n)&&(i=y.exec(e.trim(t)))&&i[0]&&("<"!==t.charAt(0)&&r("$(html) HTML strings must start with '<' character"),i[3]&&r("$(html) HTML text after last tag is ignored"),"#"===i[0].charAt(0)&&(r("HTML string cannot start with a '#' character"),e.error("JQMIGRATE: Invalid selector string (XSS)")),n&&n.context&&(n=n.context),e.parseHTML)?v.call(this,e.parseHTML(i[2],n,!0),n,a):v.apply(this,arguments)},e.fn.init.prototype=e.fn,e.parseJSON=function(e){return e||null===e?m.apply(this,arguments):(r("jQuery.parseJSON requires a valid JSON string"),null)},e.uaMatch=function(e){e=e.toLowerCase();var t=/(chrome)[ \/]([\w.]+)/.exec(e)||/(webkit)[ \/]([\w.]+)/.exec(e)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(e)||/(msie) ([\w.]+)/.exec(e)||0>e.indexOf("compatible")&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(e)||[];return{browser:t[1]||"",version:t[2]||"0"}},e.browser||(g=e.uaMatch(navigator.userAgent),h={},g.browser&&(h[g.browser]=!0,h.version=g.version),h.chrome?h.webkit=!0:h.webkit&&(h.safari=!0),e.browser=h),a(e,"browser",e.browser,"jQuery.browser is deprecated"),e.sub=function(){function t(e,n){return new t.fn.init(e,n)}e.extend(!0,t,this),t.superclass=this,t.fn=t.prototype=this(),t.fn.constructor=t,t.sub=this.sub,t.fn.init=function(r,a){return a&&a instanceof e&&!(a instanceof t)&&(a=t(a)),e.fn.init.call(this,r,a,n)},t.fn.init.prototype=t.fn;var n=t(document);return r("jQuery.sub() is deprecated"),t},e.ajaxSetup({converters:{"text json":e.parseJSON}});var b=e.fn.data;e.fn.data=function(t){var a,i,o=this[0];return!o||"events"!==t||1!==arguments.length||(a=e.data(o,t),i=e._data(o,t),a!==n&&a!==i||i===n)?b.apply(this,arguments):(r("Use of jQuery.fn.data('events') is deprecated"),i)};var j=/\/(java|ecma)script/i,w=e.fn.andSelf||e.fn.addBack;e.fn.andSelf=function(){return r("jQuery.fn.andSelf() replaced by jQuery.fn.addBack()"),w.apply(this,arguments)},e.clean||(e.clean=function(t,a,i,o){a=a||document,a=!a.nodeType&&a[0]||a,a=a.ownerDocument||a,r("jQuery.clean() is deprecated");var s,u,c,l,d=[];if(e.merge(d,e.buildFragment(t,a).childNodes),i)for(c=function(e){return!e.type||j.test(e.type)?o?o.push(e.parentNode?e.parentNode.removeChild(e):e):i.appendChild(e):n},s=0;null!=(u=d[s]);s++)e.nodeName(u,"script")&&c(u)||(i.appendChild(u),u.getElementsByTagName!==n&&(l=e.grep(e.merge([],u.getElementsByTagName("script")),c),d.splice.apply(d,[s+1,0].concat(l)),s+=l.length));return d});var Q=e.event.add,x=e.event.remove,k=e.event.trigger,N=e.fn.toggle,T=e.fn.live,M=e.fn.die,S="ajaxStart|ajaxStop|ajaxSend|ajaxComplete|ajaxError|ajaxSuccess",C=RegExp("\\b(?:"+S+")\\b"),H=/(?:^|\s)hover(\.\S+|)\b/,A=function(t){return"string"!=typeof t||e.event.special.hover?t:(H.test(t)&&r("'hover' pseudo-event is deprecated, use 'mouseenter mouseleave'"),t&&t.replace(H,"mouseenter$1 mouseleave$1"))};e.event.props&&"attrChange"!==e.event.props[0]&&e.event.props.unshift("attrChange","attrName","relatedNode","srcElement"),e.event.dispatch&&a(e.event,"handle",e.event.dispatch,"jQuery.event.handle is undocumented and deprecated"),e.event.add=function(e,t,n,a,i){e!==document&&C.test(t)&&r("AJAX events should be attached to document: "+t),Q.call(this,e,A(t||""),n,a,i)},e.event.remove=function(e,t,n,r,a){x.call(this,e,A(t)||"",n,r,a)},e.fn.error=function(){var e=Array.prototype.slice.call(arguments,0);return r("jQuery.fn.error() is deprecated"),e.splice(0,0,"error"),arguments.length?this.bind.apply(this,e):(this.triggerHandler.apply(this,e),this)},e.fn.toggle=function(t,n){if(!e.isFunction(t)||!e.isFunction(n))return N.apply(this,arguments);r("jQuery.fn.toggle(handler, handler...) is deprecated");var a=arguments,i=t.guid||e.guid++,o=0,s=function(n){var r=(e._data(this,"lastToggle"+t.guid)||0)%o;return e._data(this,"lastToggle"+t.guid,r+1),n.preventDefault(),a[r].apply(this,arguments)||!1};for(s.guid=i;a.length>o;)a[o++].guid=i;return this.click(s)},e.fn.live=function(t,n,a){return r("jQuery.fn.live() is deprecated"),T?T.apply(this,arguments):(e(this.context).on(t,this.selector,n,a),this)},e.fn.die=function(t,n){return r("jQuery.fn.die() is deprecated"),M?M.apply(this,arguments):(e(this.context).off(t,this.selector||"**",n),this)},e.event.trigger=function(e,t,n,a){return n||C.test(e)||r("Global events are undocumented and deprecated"),k.call(this,e,t,n||document,a)},e.each(S.split("|"),function(t,n){e.event.special[n]={setup:function(){var t=this;return t!==document&&(e.event.add(document,n+"."+e.guid,function(){e.event.trigger(n,null,t,!0)}),e._data(this,n,e.guid++)),!1},teardown:function(){return this!==document&&e.event.remove(document,n+"."+e._data(this,n)),!1}}})}(jQuery,window); 9794/*! 9795 * jQuery UI Core 1.10.3 9796 * http://jqueryui.com 9797 * 9798 * Copyright 2013 jQuery Foundation and other contributors 9799 * Released under the MIT license. 9800 * http://jquery.org/license 9801 * 9802 * http://api.jqueryui.com/category/ui-core/ 9803 */ 9804 9805(function( $, undefined ) { 9806 9807var uuid = 0, 9808 runiqueId = /^ui-id-\d+$/; 9809
vendor: 5,040 bytes, lines 9810-9997
9810// $.ui might exist from components with no dependencies, e.g., $.ui.position 9811$.ui = $.ui || {}; 9812 9813$.extend( $.ui, { 9814 version: "1.10.3", 9815 9816 keyCode: { 9817 BACKSPACE: 8, 9818 COMMA: 188, 9819 DELETE: 46, 9820 DOWN: 40, 9821 END: 35, 9822 ENTER: 13, 9823 ESCAPE: 27, 9824 HOME: 36, 9825 LEFT: 37, 9826 NUMPAD_ADD: 107, 9827 NUMPAD_DECIMAL: 110, 9828 NUMPAD_DIVIDE: 111, 9829 NUMPAD_ENTER: 108, 9830 NUMPAD_MULTIPLY: 106, 9831 NUMPAD_SUBTRACT: 109, 9832 PAGE_DOWN: 34, 9833 PAGE_UP: 33, 9834 PERIOD: 190, 9835 RIGHT: 39, 9836 SPACE: 32, 9837 TAB: 9, 9838 UP: 38 9839 } 9840}); 9841 9842// plugins 9843$.fn.extend({ 9844 focus: (function( orig ) { 9845 return function( delay, fn ) { 9846 return typeof delay === "number" ? 9847 this.each(function() { 9848 var elem = this; 9849 setTimeout(function() { 9850 $( elem ).focus(); 9851 if ( fn ) { 9852 fn.call( elem ); 9853 } 9854 }, delay ); 9855 }) : 9856 orig.apply( this, arguments ); 9857 }; 9858 })( $.fn.focus ), 9859 9860 scrollParent: function() { 9861 var scrollParent; 9862 if (($.ui.ie && (/(static|relative)/).test(this.css("position"))) || (/absolute/).test(this.css("position"))) { 9863 scrollParent = this.parents().filter(function() { 9864 return (/(relative|absolute|fixed)/).test($.css(this,"position")) && (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x")); 9865 }).eq(0); 9866 } else { 9867 scrollParent = this.parents().filter(function() { 9868 return (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x")); 9869 }).eq(0); 9870 } 9871 9872 return (/fixed/).test(this.css("position")) || !scrollParent.length ? $(document) : scrollParent; 9873 }, 9874 9875 zIndex: function( zIndex ) { 9876 if ( zIndex !== undefined ) { 9877 return this.css( "zIndex", zIndex ); 9878 } 9879 9880 if ( this.length ) { 9881 var elem = $( this[ 0 ] ), position, value; 9882 while ( elem.length && elem[ 0 ] !== document ) { 9883 // Ignore z-index if position is set to a value where z-index is ignored by the browser 9884 // This makes behavior of this function consistent across browsers 9885 // WebKit always returns auto if the element is positioned 9886 position = elem.css( "position" ); 9887 if ( position === "absolute" || position === "relative" || position === "fixed" ) { 9888 // IE returns 0 when zIndex is not specified 9889 // other browsers return a string 9890 // we ignore the case of nested elements with an explicit value of 0 9891 // <div style="z-index: -10;"><div style="z-index: 0;"></div></div> 9892 value = parseInt( elem.css( "zIndex" ), 10 ); 9893 if ( !isNaN( value ) && value !== 0 ) { 9894 return value; 9895 } 9896 } 9897 elem = elem.parent(); 9898 } 9899 } 9900 9901 return 0; 9902 }, 9903 9904 uniqueId: function() { 9905 return this.each(function() { 9906 if ( !this.id ) { 9907 this.id = "ui-id-" + (++uuid); 9908 } 9909 }); 9910 }, 9911 9912 removeUniqueId: function() { 9913 return this.each(function() { 9914 if ( runiqueId.test( this.id ) ) { 9915 $( this ).removeAttr( "id" ); 9916 } 9917 }); 9918 } 9919}); 9920 9921// selectors 9922function focusable( element, isTabIndexNotNaN ) { 9923 var map, mapName, img, 9924 nodeName = element.nodeName.toLowerCase(); 9925 if ( "area" === nodeName ) { 9926 map = element.parentNode; 9927 mapName = map.name; 9928 if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) { 9929 return false; 9930 } 9931 img = $( "img[usemap=#" + mapName + "]" )[0]; 9932 return !!img && visible( img ); 9933 } 9934 return ( /input|select|textarea|button|object/.test( nodeName ) ? 9935 !element.disabled : 9936 "a" === nodeName ? 9937 element.href || isTabIndexNotNaN : 9938 isTabIndexNotNaN) && 9939 // the element and all of its ancestors must be visible 9940 visible( element ); 9941} 9942 9943function visible( element ) { 9944 return $.expr.filters.visible( element ) && 9945 !$( element ).parents().addBack().filter(function() { 9946 return $.css( this, "visibility" ) === "hidden"; 9947 }).length; 9948} 9949 9950$.extend( $.expr[ ":" ], { 9951 data: $.expr.createPseudo ? 9952 $.expr.createPseudo(function( dataName ) { 9953 return function( elem ) { 9954 return !!$.data( elem, dataName ); 9955 }; 9956 }) : 9957 // support: jQuery <1.8 9958 function( elem, i, match ) { 9959 return !!$.data( elem, match[ 3 ] ); 9960 }, 9961 9962 focusable: function( element ) { 9963 return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) ); 9964 }, 9965 9966 tabbable: function( element ) { 9967 var tabIndex = $.attr( element, "tabindex" ), 9968 isTabIndexNaN = isNaN( tabIndex ); 9969 return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN ); 9970 } 9971}); 9972 9973// support: jQuery <1.8 9974if ( !$( "<a>" ).outerWidth( 1 ).jquery ) { 9975 $.each( [ "Width", "Height" ], function( i, name ) { 9976 var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ], 9977 type = name.toLowerCase(), 9978 orig = { 9979 innerWidth: $.fn.innerWidth, 9980 innerHeight: $.fn.innerHeight, 9981 outerWidth: $.fn.outerWidth, 9982 outerHeight: $.fn.outerHeight 9983 }; 9984 9985 function reduce( elem, size, border, margin ) { 9986 $.each( side, function() { 9987 size -= parseFloat( $.css( elem, "padding" + this ) ) || 0; 9988 if ( border ) { 9989 size -= parseFloat( $.css( elem, "border" + this + "Width" ) ) || 0; 9990 } 9991 if ( margin ) { 9992 size -= parseFloat( $.css( elem, "margin" + this ) ) || 0; 9993 } 9994 }); 9995 return size; 9996 } 9997
9998 $.fn[ "inner" + name ] = function( size ) { 9999 if ( size === undefined ) { 10000 return orig[ "inner" + name ].call( this ); 10001 } 10002 10003 return this.each(function() { 10004 $( this ).css( type, reduce( this, size ) + "px" ); 10005 }); 10006 }; 10007 10008 $.fn[ "outer" + name] = function( size, margin ) { 10009 if ( typeof size !== "number" ) { 10010 return orig[ "outer" + name ].call( this, size ); 10011 } 10012 10013 return this.each(function() { 10014 $( this).css( type, reduce( this, size, true, margin ) + "px" ); 10015 }); 10016 }; 10017 }); 10018} 10019 10020// support: jQuery <1.8 10021if ( !$.fn.addBack ) { 10022 $.fn.addBack = function( selector ) { 10023 return this.add( selector == null ? 10024 this.prevObject : this.prevObject.filter( selector ) 10025 ); 10026 }; 10027} 10028 10029// support: jQuery 1.6.1, 1.6.2 (http://bugs.jquery.com/ticket/9413) 10030if ( $( "<a>" ).data( "a-b", "a" ).removeData( "a-b" ).data( "a-b" ) ) { 10031 $.fn.removeData = (function( removeData ) { 10032 return function( key ) { 10033 if ( arguments.length ) { 10034 return removeData.call( this, $.camelCase( key ) ); 10035 } else { 10036 return removeData.call( this ); 10037 } 10038 }; 10039 })( $.fn.removeData ); 10040} 10041 10042 10043 10044 10045 10046// deprecated 10047$.ui.ie = !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() ); 10048 10049$.support.selectstart = "onselectstart" in document.createElement( "div" ); 10050$.fn.extend({ 10051 disableSelection: function() { 10052 return this.bind( ( $.support.selectstart ? "selectstart" : "mousedown" ) + 10053 ".ui-disableSelection", function( event ) { 10054 event.preventDefault(); 10055 }); 10056 }, 10057 10058 enableSelection: function() { 10059 return this.unbind( ".ui-disableSelection" ); 10060 } 10061}); 10062 10063$.extend( $.ui, { 10064 // $.ui.plugin is deprecated. Use $.widget() extensions instead. 10065 plugin: { 10066 add: function( module, option, set ) { 10067 var i, 10068 proto = $.ui[ module ].prototype; 10069 for ( i in set ) { 10070 proto.plugins[ i ] = proto.plugins[ i ] || []; 10071 proto.plugins[ i ].push( [ option, set[ i ] ] ); 10072 } 10073 }, 10074 call: function( instance, name, args ) { 10075 var i, 10076 set = instance.plugins[ name ]; 10077 if ( !set || !instance.element[ 0 ].parentNode || instance.element[ 0 ].parentNode.nodeType === 11 ) { 10078 return; 10079 } 10080 10081 for ( i = 0; i < set.length; i++ ) { 10082 if ( instance.options[ set[ i ][ 0 ] ] ) { 10083 set[ i ][ 1 ].apply( instance.element, args ); 10084 } 10085 } 10086 } 10087 }, 10088 10089 // only used by resizable 10090 hasScroll: function( el, a ) { 10091 10092 //If overflow is hidden, the element might have extra content, but the user wants to hide it 10093 if ( $( el ).css( "overflow" ) === "hidden") { 10094 return false; 10095 } 10096 10097 var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop", 10098 has = false; 10099 10100 if ( el[ scroll ] > 0 ) { 10101 return true; 10102 } 10103 10104 // TODO: determine which cases actually cause this to happen 10105 // if the element doesn't have the scroll set, see if it's possible to 10106 // set the scroll 10107 el[ scroll ] = 1; 10108 has = ( el[ scroll ] > 0 ); 10109 el[ scroll ] = 0; 10110 return has; 10111 } 10112}); 10113 10114})( jQuery ); 10115/*! 10116 * jQuery UI Widget 1.10.3 10117 * http://jqueryui.com 10118 * 10119 * Copyright 2013 jQuery Foundation and other contributors 10120 * Released under the MIT license. 10121 * http://jquery.org/license 10122 * 10123 * http://api.jqueryui.com/jQuery.widget/ 10124 */ 10125 10126(function( $, undefined ) { 10127 10128var uuid = 0, 10129 slice = Array.prototype.slice, 10130 _cleanData = $.cleanData; 10131$.cleanData = function( elems ) { 10132 for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { 10133 try { 10134 $( elem ).triggerHandler( "remove" ); 10135 // http://bugs.jquery.com/ticket/8235 10136 } catch( e ) {} 10137 } 10138 _cleanData( elems ); 10139}; 10140 10141$.widget = function( name, base, prototype ) { 10142 var fullName, existingConstructor, constructor, basePrototype, 10143 // proxiedPrototype allows the provided prototype to remain unmodified 10144 // so that it can be used as a mixin for multiple widgets (#8876) 10145 proxiedPrototype = {}, 10146 namespace = name.split( "." )[ 0 ]; 10147 10148 name = name.split( "." )[ 1 ]; 10149 fullName = namespace + "-" + name; 10150 10151 if ( !prototype ) { 10152 prototype = base; 10153 base = $.Widget; 10154 } 10155 10156 // create selector for plugin 10157 $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) { 10158 return !!$.data( elem, fullName ); 10159 }; 10160 10161 $[ namespace ] = $[ namespace ] || {}; 10162 existingConstructor = $[ namespace ][ name ]; 10163 constructor = $[ namespace ][ name ] = function( options, element ) { 10164 // allow instantiation without "new" keyword 10165 if ( !this._createWidget ) { 10166 return new constructor( options, element ); 10167 } 10168 10169 // allow instantiation without initializing for simple inheritance 10170 // must use "new" keyword (the code above always passes args) 10171 if ( arguments.length ) { 10172 this._createWidget( options, element ); 10173 } 10174 }; 10175 // extend with the existing constructor to carry over any static properties 10176 $.extend( constructor, existingConstructor, { 10177 version: prototype.version, 10178 // copy the object used to create the prototype in case we need to 10179 // redefine the widget later 10180 _proto: $.extend( {}, prototype ), 10181 // track widgets that inherit from this widget in case this widget is 10182 // redefined after a widget inherits from it 10183 _childConstructors: [] 10184 }); 10185 10186 basePrototype = new base(); 10187 // we need to make the options hash a property directly on the new instance 10188 // otherwise we'll modify the options hash on the prototype that we're 10189 // inheriting from 10190 basePrototype.options = $.widget.extend( {}, basePrototype.options ); 10191 $.each( prototype, function( prop, value ) { 10192 if ( !$.isFunction( value ) ) { 10193 proxiedPrototype[ prop ] = value; 10194 return; 10195 } 10196 proxiedPrototype[ prop ] = (function() { 10197 var _super = function() { 10198 return base.prototype[ prop ].apply( this, arguments ); 10199 }, 10200 _superApply = function( args ) { 10201 return base.prototype[ prop ].apply( this, args ); 10202 }; 10203 return function() { 10204 var __super = this._super, 10205 __superApply = this._superApply, 10206 returnValue; 10207 10208 this._super = _super; 10209 this._superApply = _superApply; 10210
vendor: 11,913 bytes, lines 10211-10622
10211 returnValue = value.apply( this, arguments ); 10212 10213 this._super = __super; 10214 this._superApply = __superApply; 10215 10216 return returnValue; 10217 }; 10218 })(); 10219 }); 10220 constructor.prototype = $.widget.extend( basePrototype, { 10221 // TODO: remove support for widgetEventPrefix 10222 // always use the name + a colon as the prefix, e.g., draggable:start 10223 // don't prefix for widgets that aren't DOM-based 10224 widgetEventPrefix: existingConstructor ? basePrototype.widgetEventPrefix : name 10225 }, proxiedPrototype, { 10226 constructor: constructor, 10227 namespace: namespace, 10228 widgetName: name, 10229 widgetFullName: fullName 10230 }); 10231 10232 // If this widget is being redefined then we need to find all widgets that 10233 // are inheriting from it and redefine all of them so that they inherit from 10234 // the new version of this widget. We're essentially trying to replace one 10235 // level in the prototype chain. 10236 if ( existingConstructor ) { 10237 $.each( existingConstructor._childConstructors, function( i, child ) { 10238 var childPrototype = child.prototype; 10239 10240 // redefine the child widget using the same prototype that was 10241 // originally used, but inherit from the new version of the base 10242 $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto ); 10243 }); 10244 // remove the list of existing child constructors from the old constructor 10245 // so the old child constructors can be garbage collected 10246 delete existingConstructor._childConstructors; 10247 } else { 10248 base._childConstructors.push( constructor ); 10249 } 10250 10251 $.widget.bridge( name, constructor ); 10252}; 10253 10254$.widget.extend = function( target ) { 10255 var input = slice.call( arguments, 1 ), 10256 inputIndex = 0, 10257 inputLength = input.length, 10258 key, 10259 value; 10260 for ( ; inputIndex < inputLength; inputIndex++ ) { 10261 for ( key in input[ inputIndex ] ) { 10262 value = input[ inputIndex ][ key ]; 10263 if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) { 10264 // Clone objects 10265 if ( $.isPlainObject( value ) ) { 10266 target[ key ] = $.isPlainObject( target[ key ] ) ? 10267 $.widget.extend( {}, target[ key ], value ) : 10268 // Don't extend strings, arrays, etc. with objects 10269 $.widget.extend( {}, value ); 10270 // Copy everything else by reference 10271 } else { 10272 target[ key ] = value; 10273 } 10274 } 10275 } 10276 } 10277 return target; 10278}; 10279 10280$.widget.bridge = function( name, object ) { 10281 var fullName = object.prototype.widgetFullName || name; 10282 $.fn[ name ] = function( options ) { 10283 var isMethodCall = typeof options === "string", 10284 args = slice.call( arguments, 1 ), 10285 returnValue = this; 10286 10287 // allow multiple hashes to be passed on init 10288 options = !isMethodCall && args.length ? 10289 $.widget.extend.apply( null, [ options ].concat(args) ) : 10290 options; 10291 10292 if ( isMethodCall ) { 10293 this.each(function() { 10294 var methodValue, 10295 instance = $.data( this, fullName ); 10296 if ( !instance ) { 10297 return $.error( "cannot call methods on " + name + " prior to initialization; " + 10298 "attempted to call method '" + options + "'" ); 10299 } 10300 if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) { 10301 return $.error( "no such method '" + options + "' for " + name + " widget instance" ); 10302 } 10303 methodValue = instance[ options ].apply( instance, args ); 10304 if ( methodValue !== instance && methodValue !== undefined ) { 10305 returnValue = methodValue && methodValue.jquery ? 10306 returnValue.pushStack( methodValue.get() ) : 10307 methodValue; 10308 return false; 10309 } 10310 }); 10311 } else { 10312 this.each(function() { 10313 var instance = $.data( this, fullName ); 10314 if ( instance ) { 10315 instance.option( options || {} )._init(); 10316 } else { 10317 $.data( this, fullName, new object( options, this ) ); 10318 } 10319 }); 10320 } 10321 10322 return returnValue; 10323 }; 10324}; 10325 10326$.Widget = function( /* options, element */ ) {}; 10327$.Widget._childConstructors = []; 10328 10329$.Widget.prototype = { 10330 widgetName: "widget", 10331 widgetEventPrefix: "", 10332 defaultElement: "<div>", 10333 options: { 10334 disabled: false, 10335 10336 // callbacks 10337 create: null 10338 }, 10339 _createWidget: function( options, element ) { 10340 element = $( element || this.defaultElement || this )[ 0 ]; 10341 this.element = $( element ); 10342 this.uuid = uuid++; 10343 this.eventNamespace = "." + this.widgetName + this.uuid; 10344 this.options = $.widget.extend( {}, 10345 this.options, 10346 this._getCreateOptions(), 10347 options ); 10348 10349 this.bindings = $(); 10350 this.hoverable = $(); 10351 this.focusable = $(); 10352 10353 if ( element !== this ) { 10354 $.data( element, this.widgetFullName, this ); 10355 this._on( true, this.element, { 10356 remove: function( event ) { 10357 if ( event.target === element ) { 10358 this.destroy(); 10359 } 10360 } 10361 }); 10362 this.document = $( element.style ? 10363 // element within the document 10364 element.ownerDocument : 10365 // element is window or document 10366 element.document || element ); 10367 this.window = $( this.document[0].defaultView || this.document[0].parentWindow ); 10368 } 10369 10370 this._create(); 10371 this._trigger( "create", null, this._getCreateEventData() ); 10372 this._init(); 10373 }, 10374 _getCreateOptions: $.noop, 10375 _getCreateEventData: $.noop, 10376 _create: $.noop, 10377 _init: $.noop, 10378 10379 destroy: function() { 10380 this._destroy(); 10381 // we can probably remove the unbind calls in 2.0 10382 // all event bindings should go through this._on() 10383 this.element 10384 .unbind( this.eventNamespace ) 10385 // 1.9 BC for #7810 10386 // TODO remove dual storage 10387 .removeData( this.widgetName ) 10388 .removeData( this.widgetFullName ) 10389 // support: jquery <1.6.3 10390 // http://bugs.jquery.com/ticket/9413 10391 .removeData( $.camelCase( this.widgetFullName ) ); 10392 this.widget() 10393 .unbind( this.eventNamespace ) 10394 .removeAttr( "aria-disabled" ) 10395 .removeClass( 10396 this.widgetFullName + "-disabled " + 10397 "ui-state-disabled" ); 10398 10399 // clean up events and states 10400 this.bindings.unbind( this.eventNamespace ); 10401 this.hoverable.removeClass( "ui-state-hover" ); 10402 this.focusable.removeClass( "ui-state-focus" ); 10403 }, 10404 _destroy: $.noop, 10405 10406 widget: function() { 10407 return this.element; 10408 }, 10409 10410 option: function( key, value ) { 10411 var options = key, 10412 parts, 10413 curOption, 10414 i; 10415 10416 if ( arguments.length === 0 ) { 10417 // don't return a reference to the internal hash 10418 return $.widget.extend( {}, this.options ); 10419 } 10420 10421 if ( typeof key === "string" ) { 10422 // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } } 10423 options = {}; 10424 parts = key.split( "." ); 10425 key = parts.shift(); 10426 if ( parts.length ) { 10427 curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] ); 10428 for ( i = 0; i < parts.length - 1; i++ ) { 10429 curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {}; 10430 curOption = curOption[ parts[ i ] ]; 10431 } 10432 key = parts.pop(); 10433 if ( value === undefined ) { 10434 return curOption[ key ] === undefined ? null : curOption[ key ]; 10435 } 10436 curOption[ key ] = value; 10437 } else { 10438 if ( value === undefined ) { 10439 return this.options[ key ] === undefined ? null : this.options[ key ]; 10440 } 10441 options[ key ] = value; 10442 } 10443 } 10444 10445 this._setOptions( options ); 10446 10447 return this; 10448 }, 10449 _setOptions: function( options ) { 10450 var key; 10451 10452 for ( key in options ) { 10453 this._setOption( key, options[ key ] ); 10454 } 10455 10456 return this; 10457 }, 10458 _setOption: function( key, value ) { 10459 this.options[ key ] = value; 10460 10461 if ( key === "disabled" ) { 10462 this.widget() 10463 .toggleClass( this.widgetFullName + "-disabled ui-state-disabled", !!value ) 10464 .attr( "aria-disabled", value ); 10465 this.hoverable.removeClass( "ui-state-hover" ); 10466 this.focusable.removeClass( "ui-state-focus" ); 10467 } 10468 10469 return this; 10470 }, 10471 10472 enable: function() { 10473 return this._setOption( "disabled", false ); 10474 }, 10475 disable: function() { 10476 return this._setOption( "disabled", true ); 10477 }, 10478 10479 _on: function( suppressDisabledCheck, element, handlers ) { 10480 var delegateElement, 10481 instance = this; 10482 10483 // no suppressDisabledCheck flag, shuffle arguments 10484 if ( typeof suppressDisabledCheck !== "boolean" ) { 10485 handlers = element; 10486 element = suppressDisabledCheck; 10487 suppressDisabledCheck = false; 10488 } 10489 10490 // no element argument, shuffle and use this.element 10491 if ( !handlers ) { 10492 handlers = element; 10493 element = this.element; 10494 delegateElement = this.widget(); 10495 } else { 10496 // accept selectors, DOM elements 10497 element = delegateElement = $( element ); 10498 this.bindings = this.bindings.add( element ); 10499 } 10500 10501 $.each( handlers, function( event, handler ) { 10502 function handlerProxy() { 10503 // allow widgets to customize the disabled handling 10504 // - disabled as an array instead of boolean 10505 // - disabled class as method for disabling individual parts 10506 if ( !suppressDisabledCheck && 10507 ( instance.options.disabled === true || 10508 $( this ).hasClass( "ui-state-disabled" ) ) ) { 10509 return; 10510 } 10511 return ( typeof handler === "string" ? instance[ handler ] : handler ) 10512 .apply( instance, arguments ); 10513 } 10514 10515 // copy the guid so direct unbinding works 10516 if ( typeof handler !== "string" ) { 10517 handlerProxy.guid = handler.guid = 10518 handler.guid || handlerProxy.guid || $.guid++; 10519 } 10520 10521 var match = event.match( /^(\w+)\s*(.*)$/ ), 10522 eventName = match[1] + instance.eventNamespace, 10523 selector = match[2]; 10524 if ( selector ) { 10525 delegateElement.delegate( selector, eventName, handlerProxy ); 10526 } else { 10527 element.bind( eventName, handlerProxy ); 10528 } 10529 }); 10530 }, 10531 10532 _off: function( element, eventName ) { 10533 eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + this.eventNamespace; 10534 element.unbind( eventName ).undelegate( eventName ); 10535 }, 10536 10537 _delay: function( handler, delay ) { 10538 function handlerProxy() { 10539 return ( typeof handler === "string" ? instance[ handler ] : handler ) 10540 .apply( instance, arguments ); 10541 } 10542 var instance = this; 10543 return setTimeout( handlerProxy, delay || 0 ); 10544 }, 10545 10546 _hoverable: function( element ) { 10547 this.hoverable = this.hoverable.add( element ); 10548 this._on( element, { 10549 mouseenter: function( event ) { 10550 $( event.currentTarget ).addClass( "ui-state-hover" ); 10551 }, 10552 mouseleave: function( event ) { 10553 $( event.currentTarget ).removeClass( "ui-state-hover" ); 10554 } 10555 }); 10556 }, 10557 10558 _focusable: function( element ) { 10559 this.focusable = this.focusable.add( element ); 10560 this._on( element, { 10561 focusin: function( event ) { 10562 $( event.currentTarget ).addClass( "ui-state-focus" ); 10563 }, 10564 focusout: function( event ) { 10565 $( event.currentTarget ).removeClass( "ui-state-focus" ); 10566 } 10567 }); 10568 }, 10569 10570 _trigger: function( type, event, data ) { 10571 var prop, orig, 10572 callback = this.options[ type ]; 10573 10574 data = data || {}; 10575 event = $.Event( event ); 10576 event.type = ( type === this.widgetEventPrefix ? 10577 type : 10578 this.widgetEventPrefix + type ).toLowerCase(); 10579 // the original event may come from any element 10580 // so we need to reset the target on the new event 10581 event.target = this.element[ 0 ]; 10582 10583 // copy original event properties over to the new event 10584 orig = event.originalEvent; 10585 if ( orig ) { 10586 for ( prop in orig ) { 10587 if ( !( prop in event ) ) { 10588 event[ prop ] = orig[ prop ]; 10589 } 10590 } 10591 } 10592 10593 this.element.trigger( event, data ); 10594 return !( $.isFunction( callback ) && 10595 callback.apply( this.element[0], [ event ].concat( data ) ) === false || 10596 event.isDefaultPrevented() ); 10597 } 10598}; 10599 10600$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) { 10601 $.Widget.prototype[ "_" + method ] = function( element, options, callback ) { 10602 if ( typeof options === "string" ) { 10603 options = { effect: options }; 10604 } 10605 var hasOptions, 10606 effectName = !options ? 10607 method : 10608 options === true || typeof options === "number" ? 10609 defaultEffect : 10610 options.effect || defaultEffect; 10611 options = options || {}; 10612 if ( typeof options === "number" ) { 10613 options = { duration: options }; 10614 } 10615 hasOptions = !$.isEmptyObject( options ); 10616 options.complete = callback; 10617 if ( options.delay ) { 10618 element.delay( options.delay ); 10619 } 10620 if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) { 10621 element[ method ]( options ); 10622 } else if ( effectName !== method && element[ effectName ] ) {
vendor: 31,725 bytes, lines 10623-11758
10623 element[ effectName ]( options.duration, options.easing, callback ); 10624 } else { 10625 element.queue(function( next ) { 10626 $( this )[ method ](); 10627 if ( callback ) { 10628 callback.call( element[ 0 ] ); 10629 } 10630 next(); 10631 }); 10632 } 10633 }; 10634}); 10635 10636})( jQuery ); 10637 10638 10639 10640/*! 10641 * jQuery UI Accordion 1.10.3 10642 * http://jqueryui.com 10643 * 10644 * Copyright 2013 jQuery Foundation and other contributors 10645 * Released under the MIT license. 10646 * http://jquery.org/license 10647 * 10648 * http://api.jqueryui.com/accordion/ 10649 * 10650 * Depends: 10651 * jquery.ui.core.js 10652 * jquery.ui.widget.js 10653 */ 10654 10655(function( $, undefined ) { 10656 10657var uid = 0, 10658 hideProps = {}, 10659 showProps = {}; 10660 10661hideProps.height = hideProps.paddingTop = hideProps.paddingBottom = 10662 hideProps.borderTopWidth = hideProps.borderBottomWidth = "hide"; 10663showProps.height = showProps.paddingTop = showProps.paddingBottom = 10664 showProps.borderTopWidth = showProps.borderBottomWidth = "show"; 10665 10666$.widget( "ui.accordion", { 10667 version: "1.10.3", 10668 options: { 10669 active: 0, 10670 animate: {}, 10671 collapsible: false, 10672 event: "click", 10673 header: "> li > :first-child,> :not(li):even", 10674 heightStyle: "auto", 10675 icons: { 10676 activeHeader: "ui-icon-triangle-1-s", 10677 header: "ui-icon-triangle-1-e" 10678 }, 10679 10680 // callbacks 10681 activate: null, 10682 beforeActivate: null 10683 }, 10684 10685 _create: function() { 10686 var options = this.options; 10687 this.prevShow = this.prevHide = $(); 10688 this.element.addClass( "ui-accordion ui-widget ui-helper-reset" ) 10689 // ARIA 10690 .attr( "role", "tablist" ); 10691 10692 // don't allow collapsible: false and active: false / null 10693 if ( !options.collapsible && (options.active === false || options.active == null) ) { 10694 options.active = 0; 10695 } 10696 10697 this._processPanels(); 10698 // handle negative values 10699 if ( options.active < 0 ) { 10700 options.active += this.headers.length; 10701 } 10702 this._refresh(); 10703 }, 10704 10705 _getCreateEventData: function() { 10706 return { 10707 header: this.active, 10708 panel: !this.active.length ? $() : this.active.next(), 10709 content: !this.active.length ? $() : this.active.next() 10710 }; 10711 }, 10712 10713 _createIcons: function() { 10714 var icons = this.options.icons; 10715 if ( icons ) { 10716 $( "<span>" ) 10717 .addClass( "ui-accordion-header-icon ui-icon " + icons.header ) 10718 .prependTo( this.headers ); 10719 this.active.children( ".ui-accordion-header-icon" ) 10720 .removeClass( icons.header ) 10721 .addClass( icons.activeHeader ); 10722 this.headers.addClass( "ui-accordion-icons" ); 10723 } 10724 }, 10725 10726 _destroyIcons: function() { 10727 this.headers 10728 .removeClass( "ui-accordion-icons" ) 10729 .children( ".ui-accordion-header-icon" ) 10730 .remove(); 10731 }, 10732 10733 _destroy: function() { 10734 var contents; 10735 10736 // clean up main element 10737 this.element 10738 .removeClass( "ui-accordion ui-widget ui-helper-reset" ) 10739 .removeAttr( "role" ); 10740 10741 // clean up headers 10742 this.headers 10743 .removeClass( "ui-accordion-header ui-accordion-header-active ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top" ) 10744 .removeAttr( "role" ) 10745 .removeAttr( "aria-selected" ) 10746 .removeAttr( "aria-controls" ) 10747 .removeAttr( "tabIndex" ) 10748 .each(function() { 10749 if ( /^ui-accordion/.test( this.id ) ) { 10750 this.removeAttribute( "id" ); 10751 } 10752 }); 10753 this._destroyIcons(); 10754 10755 // clean up content panels 10756 contents = this.headers.next() 10757 .css( "display", "" ) 10758 .removeAttr( "role" ) 10759 .removeAttr( "aria-expanded" ) 10760 .removeAttr( "aria-hidden" ) 10761 .removeAttr( "aria-labelledby" ) 10762 .removeClass( "ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-state-disabled" ) 10763 .each(function() { 10764 if ( /^ui-accordion/.test( this.id ) ) { 10765 this.removeAttribute( "id" ); 10766 } 10767 }); 10768 if ( this.options.heightStyle !== "content" ) { 10769 contents.css( "height", "" ); 10770 } 10771 }, 10772 10773 _setOption: function( key, value ) { 10774 if ( key === "active" ) { 10775 // _activate() will handle invalid values and update this.options 10776 this._activate( value ); 10777 return; 10778 } 10779 10780 if ( key === "event" ) { 10781 if ( this.options.event ) { 10782 this._off( this.headers, this.options.event ); 10783 } 10784 this._setupEvents( value ); 10785 } 10786 10787 this._super( key, value ); 10788 10789 // setting collapsible: false while collapsed; open first panel 10790 if ( key === "collapsible" && !value && this.options.active === false ) { 10791 this._activate( 0 ); 10792 } 10793 10794 if ( key === "icons" ) { 10795 this._destroyIcons(); 10796 if ( value ) { 10797 this._createIcons(); 10798 } 10799 } 10800 10801 // #5332 - opacity doesn't cascade to positioned elements in IE 10802 // so we need to add the disabled class to the headers and panels 10803 if ( key === "disabled" ) { 10804 this.headers.add( this.headers.next() ) 10805 .toggleClass( "ui-state-disabled", !!value ); 10806 } 10807 }, 10808 10809 _keydown: function( event ) { 10810 /*jshint maxcomplexity:15*/ 10811 if ( event.altKey || event.ctrlKey ) { 10812 return; 10813 } 10814 10815 var keyCode = $.ui.keyCode, 10816 length = this.headers.length, 10817 currentIndex = this.headers.index( event.target ), 10818 toFocus = false; 10819 10820 switch ( event.keyCode ) { 10821 case keyCode.RIGHT: 10822 case keyCode.DOWN: 10823 toFocus = this.headers[ ( currentIndex + 1 ) % length ]; 10824 break; 10825 case keyCode.LEFT: 10826 case keyCode.UP: 10827 toFocus = this.headers[ ( currentIndex - 1 + length ) % length ]; 10828 break; 10829 case keyCode.SPACE: 10830 case keyCode.ENTER: 10831 this._eventHandler( event ); 10832 break; 10833 case keyCode.HOME: 10834 toFocus = this.headers[ 0 ]; 10835 break; 10836 case keyCode.END: 10837 toFocus = this.headers[ length - 1 ]; 10838 break; 10839 } 10840 10841 if ( toFocus ) { 10842 $( event.target ).attr( "tabIndex", -1 ); 10843 $( toFocus ).attr( "tabIndex", 0 ); 10844 toFocus.focus(); 10845 event.preventDefault(); 10846 } 10847 }, 10848 10849 _panelKeyDown : function( event ) { 10850 if ( event.keyCode === $.ui.keyCode.UP && event.ctrlKey ) { 10851 $( event.currentTarget ).prev().focus(); 10852 } 10853 }, 10854 10855 refresh: function() { 10856 var options = this.options; 10857 this._processPanels(); 10858 10859 // was collapsed or no panel 10860 if ( ( options.active === false && options.collapsible === true ) || !this.headers.length ) { 10861 options.active = false; 10862 this.active = $(); 10863 // active false only when collapsible is true 10864 } else if ( options.active === false ) { 10865 this._activate( 0 ); 10866 // was active, but active panel is gone 10867 } else if ( this.active.length && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) { 10868 // all remaining panel are disabled 10869 if ( this.headers.length === this.headers.find(".ui-state-disabled").length ) { 10870 options.active = false; 10871 this.active = $(); 10872 // activate previous panel 10873 } else { 10874 this._activate( Math.max( 0, options.active - 1 ) ); 10875 } 10876 // was active, active panel still exists 10877 } else { 10878 // make sure active index is correct 10879 options.active = this.headers.index( this.active ); 10880 } 10881 10882 this._destroyIcons(); 10883 10884 this._refresh(); 10885 }, 10886 10887 _processPanels: function() { 10888 this.headers = this.element.find( this.options.header ) 10889 .addClass( "ui-accordion-header ui-helper-reset ui-state-default ui-corner-all" ); 10890 10891 this.headers.next() 10892 .addClass( "ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom" ) 10893 .filter(":not(.ui-accordion-content-active)") 10894 .hide(); 10895 }, 10896 10897 _refresh: function() { 10898 var maxHeight, 10899 options = this.options, 10900 heightStyle = options.heightStyle, 10901 parent = this.element.parent(), 10902 accordionId = this.accordionId = "ui-accordion-" + 10903 (this.element.attr( "id" ) || ++uid); 10904 10905 this.active = this._findActive( options.active ) 10906 .addClass( "ui-accordion-header-active ui-state-active ui-corner-top" ) 10907 .removeClass( "ui-corner-all" ); 10908 this.active.next() 10909 .addClass( "ui-accordion-content-active" ) 10910 .show(); 10911 10912 this.headers 10913 .attr( "role", "tab" ) 10914 .each(function( i ) { 10915 var header = $( this ), 10916 headerId = header.attr( "id" ), 10917 panel = header.next(), 10918 panelId = panel.attr( "id" ); 10919 if ( !headerId ) { 10920 headerId = accordionId + "-header-" + i; 10921 header.attr( "id", headerId ); 10922 } 10923 if ( !panelId ) { 10924 panelId = accordionId + "-panel-" + i; 10925 panel.attr( "id", panelId ); 10926 } 10927 header.attr( "aria-controls", panelId ); 10928 panel.attr( "aria-labelledby", headerId ); 10929 }) 10930 .next() 10931 .attr( "role", "tabpanel" ); 10932 10933 this.headers 10934 .not( this.active ) 10935 .attr({ 10936 "aria-selected": "false", 10937 tabIndex: -1 10938 }) 10939 .next() 10940 .attr({ 10941 "aria-expanded": "false", 10942 "aria-hidden": "true" 10943 }) 10944 .hide(); 10945 10946 // make sure at least one header is in the tab order 10947 if ( !this.active.length ) { 10948 this.headers.eq( 0 ).attr( "tabIndex", 0 ); 10949 } else { 10950 this.active.attr({ 10951 "aria-selected": "true", 10952 tabIndex: 0 10953 }) 10954 .next() 10955 .attr({ 10956 "aria-expanded": "true", 10957 "aria-hidden": "false" 10958 }); 10959 } 10960 10961 this._createIcons(); 10962 10963 this._setupEvents( options.event ); 10964 10965 if ( heightStyle === "fill" ) { 10966 maxHeight = parent.height(); 10967 this.element.siblings( ":visible" ).each(function() { 10968 var elem = $( this ), 10969 position = elem.css( "position" ); 10970 10971 if ( position === "absolute" || position === "fixed" ) { 10972 return; 10973 } 10974 maxHeight -= elem.outerHeight( true ); 10975 }); 10976 10977 this.headers.each(function() { 10978 maxHeight -= $( this ).outerHeight( true ); 10979 }); 10980 10981 this.headers.next() 10982 .each(function() { 10983 $( this ).height( Math.max( 0, maxHeight - 10984 $( this ).innerHeight() + $( this ).height() ) ); 10985 }) 10986 .css( "overflow", "auto" ); 10987 } else if ( heightStyle === "auto" ) { 10988 maxHeight = 0; 10989 this.headers.next() 10990 .each(function() { 10991 maxHeight = Math.max( maxHeight, $( this ).css( "height", "" ).height() ); 10992 }) 10993 .height( maxHeight ); 10994 } 10995 }, 10996 10997 _activate: function( index ) { 10998 var active = this._findActive( index )[ 0 ]; 10999 11000 // trying to activate the already active panel 11001 if ( active === this.active[ 0 ] ) { 11002 return; 11003 } 11004 11005 // trying to collapse, simulate a click on the currently active header 11006 active = active || this.active[ 0 ]; 11007 11008 this._eventHandler({ 11009 target: active, 11010 currentTarget: active, 11011 preventDefault: $.noop 11012 }); 11013 }, 11014 11015 _findActive: function( selector ) { 11016 return typeof selector === "number" ? this.headers.eq( selector ) : $(); 11017 }, 11018 11019 _setupEvents: function( event ) { 11020 var events = { 11021 keydown: "_keydown" 11022 }; 11023 if ( event ) { 11024 $.each( event.split(" "), function( index, eventName ) { 11025 events[ eventName ] = "_eventHandler"; 11026 }); 11027 } 11028 11029 this._off( this.headers.add( this.headers.next() ) ); 11030 this._on( this.headers, events ); 11031 this._on( this.headers.next(), { keydown: "_panelKeyDown" }); 11032 this._hoverable( this.headers ); 11033 this._focusable( this.headers ); 11034 }, 11035 11036 _eventHandler: function( event ) { 11037 var options = this.options, 11038 active = this.active, 11039 clicked = $( event.currentTarget ), 11040 clickedIsActive = clicked[ 0 ] === active[ 0 ], 11041 collapsing = clickedIsActive && options.collapsible, 11042 toShow = collapsing ? $() : clicked.next(), 11043 toHide = active.next(), 11044 eventData = { 11045 oldHeader: active, 11046 oldPanel: toHide, 11047 newHeader: collapsing ? $() : clicked, 11048 newPanel: toShow 11049 }; 11050 11051 event.preventDefault(); 11052 11053 if ( 11054 // click on active header, but not collapsible 11055 ( clickedIsActive && !options.collapsible ) || 11056 // allow canceling activation 11057 ( this._trigger( "beforeActivate", event, eventData ) === false ) ) { 11058 return; 11059 } 11060 11061 options.active = collapsing ? false : this.headers.index( clicked ); 11062 11063 // when the call to ._toggle() comes after the class changes 11064 // it causes a very odd bug in IE 8 (see #6720) 11065 this.active = clickedIsActive ? $() : clicked; 11066 this._toggle( eventData ); 11067 11068 // switch classes 11069 // corner classes on the previously active header stay after the animation 11070 active.removeClass( "ui-accordion-header-active ui-state-active" ); 11071 if ( options.icons ) { 11072 active.children( ".ui-accordion-header-icon" ) 11073 .removeClass( options.icons.activeHeader ) 11074 .addClass( options.icons.header ); 11075 } 11076 11077 if ( !clickedIsActive ) { 11078 clicked 11079 .removeClass( "ui-corner-all" ) 11080 .addClass( "ui-accordion-header-active ui-state-active ui-corner-top" ); 11081 if ( options.icons ) { 11082 clicked.children( ".ui-accordion-header-icon" ) 11083 .removeClass( options.icons.header ) 11084 .addClass( options.icons.activeHeader ); 11085 } 11086 11087 clicked 11088 .next() 11089 .addClass( "ui-accordion-content-active" ); 11090 } 11091 }, 11092 11093 _toggle: function( data ) { 11094 var toShow = data.newPanel, 11095 toHide = this.prevShow.length ? this.prevShow : data.oldPanel; 11096 11097 // handle activating a panel during the animation for another activation 11098 this.prevShow.add( this.prevHide ).stop( true, true ); 11099 this.prevShow = toShow; 11100 this.prevHide = toHide; 11101 11102 if ( this.options.animate ) { 11103 this._animate( toShow, toHide, data ); 11104 } else { 11105 toHide.hide(); 11106 toShow.show(); 11107 this._toggleComplete( data ); 11108 } 11109 11110 toHide.attr({ 11111 "aria-expanded": "false", 11112 "aria-hidden": "true" 11113 }); 11114 toHide.prev().attr( "aria-selected", "false" ); 11115 // if we're switching panels, remove the old header from the tab order 11116 // if we're opening from collapsed state, remove the previous header from the tab order 11117 // if we're collapsing, then keep the collapsing header in the tab order 11118 if ( toShow.length && toHide.length ) { 11119 toHide.prev().attr( "tabIndex", -1 ); 11120 } else if ( toShow.length ) { 11121 this.headers.filter(function() { 11122 return $( this ).attr( "tabIndex" ) === 0; 11123 }) 11124 .attr( "tabIndex", -1 ); 11125 } 11126 11127 toShow 11128 .attr({ 11129 "aria-expanded": "true", 11130 "aria-hidden": "false" 11131 }) 11132 .prev() 11133 .attr({ 11134 "aria-selected": "true", 11135 tabIndex: 0 11136 }); 11137 }, 11138 11139 _animate: function( toShow, toHide, data ) { 11140 var total, easing, duration, 11141 that = this, 11142 adjust = 0, 11143 down = toShow.length && 11144 ( !toHide.length || ( toShow.index() < toHide.index() ) ), 11145 animate = this.options.animate || {}, 11146 options = down && animate.down || animate, 11147 complete = function() { 11148 that._toggleComplete( data ); 11149 }; 11150 11151 if ( typeof options === "number" ) { 11152 duration = options; 11153 } 11154 if ( typeof options === "string" ) { 11155 easing = options; 11156 } 11157 // fall back from options to animation in case of partial down settings 11158 easing = easing || options.easing || animate.easing; 11159 duration = duration || options.duration || animate.duration; 11160 11161 if ( !toHide.length ) { 11162 return toShow.animate( showProps, duration, easing, complete ); 11163 } 11164 if ( !toShow.length ) { 11165 return toHide.animate( hideProps, duration, easing, complete ); 11166 } 11167 11168 total = toShow.show().outerHeight(); 11169 toHide.animate( hideProps, { 11170 duration: duration, 11171 easing: easing, 11172 step: function( now, fx ) { 11173 fx.now = Math.round( now ); 11174 } 11175 }); 11176 toShow 11177 .hide() 11178 .animate( showProps, { 11179 duration: duration, 11180 easing: easing, 11181 complete: complete, 11182 step: function( now, fx ) { 11183 fx.now = Math.round( now ); 11184 if ( fx.prop !== "height" ) { 11185 adjust += fx.now; 11186 } else if ( that.options.heightStyle !== "content" ) { 11187 fx.now = Math.round( total - toHide.outerHeight() - adjust ); 11188 adjust = 0; 11189 } 11190 } 11191 }); 11192 }, 11193 11194 _toggleComplete: function( data ) { 11195 var toHide = data.oldPanel; 11196 11197 toHide 11198 .removeClass( "ui-accordion-content-active" ) 11199 .prev() 11200 .removeClass( "ui-corner-top" ) 11201 .addClass( "ui-corner-all" ); 11202 11203 // Work around for rendering bug in IE (#5421) 11204 if ( toHide.length ) { 11205 toHide.parent()[0].className = toHide.parent()[0].className; 11206 } 11207 11208 this._trigger( "activate", null, data ); 11209 } 11210}); 11211 11212})( jQuery ); 11213/*! 11214 * jQuery UI Position 1.10.3 11215 * http://jqueryui.com 11216 * 11217 * Copyright 2013 jQuery Foundation and other contributors 11218 * Released under the MIT license. 11219 * http://jquery.org/license 11220 * 11221 * http://api.jqueryui.com/position/ 11222 */ 11223 11224(function( $, undefined ) { 11225 11226$.ui = $.ui || {}; 11227 11228var cachedScrollbarWidth, 11229 max = Math.max, 11230 abs = Math.abs, 11231 round = Math.round, 11232 rhorizontal = /left|center|right/, 11233 rvertical = /top|center|bottom/, 11234 roffset = /[\+\-]\d+(\.[\d]+)?%?/, 11235 rposition = /^\w+/, 11236 rpercent = /%$/, 11237 _position = $.fn.position; 11238 11239function getOffsets( offsets, width, height ) { 11240 return [ 11241 parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ), 11242 parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 ) 11243 ]; 11244} 11245 11246function parseCss( element, property ) { 11247 return parseInt( $.css( element, property ), 10 ) || 0; 11248} 11249 11250function getDimensions( elem ) { 11251 var raw = elem[0]; 11252 if ( raw.nodeType === 9 ) { 11253 return { 11254 width: elem.width(), 11255 height: elem.height(), 11256 offset: { top: 0, left: 0 } 11257 }; 11258 } 11259 if ( $.isWindow( raw ) ) { 11260 return { 11261 width: elem.width(), 11262 height: elem.height(), 11263 offset: { top: elem.scrollTop(), left: elem.scrollLeft() } 11264 }; 11265 } 11266 if ( raw.preventDefault ) { 11267 return { 11268 width: 0, 11269 height: 0, 11270 offset: { top: raw.pageY, left: raw.pageX } 11271 }; 11272 } 11273 return { 11274 width: elem.outerWidth(), 11275 height: elem.outerHeight(), 11276 offset: elem.offset() 11277 }; 11278} 11279 11280$.position = { 11281 scrollbarWidth: function() { 11282 if ( cachedScrollbarWidth !== undefined ) { 11283 return cachedScrollbarWidth; 11284 } 11285 var w1, w2, 11286 div = $( "<div style='display:block;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>" ), 11287 innerDiv = div.children()[0]; 11288 11289 $( "body" ).append( div ); 11290 w1 = innerDiv.offsetWidth; 11291 div.css( "overflow", "scroll" ); 11292 11293 w2 = innerDiv.offsetWidth; 11294 11295 if ( w1 === w2 ) { 11296 w2 = div[0].clientWidth; 11297 } 11298 11299 div.remove(); 11300 11301 return (cachedScrollbarWidth = w1 - w2); 11302 }, 11303 getScrollInfo: function( within ) { 11304 var overflowX = within.isWindow ? "" : within.element.css( "overflow-x" ), 11305 overflowY = within.isWindow ? "" : within.element.css( "overflow-y" ), 11306 hasOverflowX = overflowX === "scroll" || 11307 ( overflowX === "auto" && within.width < within.element[0].scrollWidth ), 11308 hasOverflowY = overflowY === "scroll" || 11309 ( overflowY === "auto" && within.height < within.element[0].scrollHeight ); 11310 return { 11311 width: hasOverflowY ? $.position.scrollbarWidth() : 0, 11312 height: hasOverflowX ? $.position.scrollbarWidth() : 0 11313 }; 11314 }, 11315 getWithinInfo: function( element ) { 11316 var withinElement = $( element || window ), 11317 isWindow = $.isWindow( withinElement[0] ); 11318 return { 11319 element: withinElement, 11320 isWindow: isWindow, 11321 offset: withinElement.offset() || { left: 0, top: 0 }, 11322 scrollLeft: withinElement.scrollLeft(), 11323 scrollTop: withinElement.scrollTop(), 11324 width: isWindow ? withinElement.width() : withinElement.outerWidth(), 11325 height: isWindow ? withinElement.height() : withinElement.outerHeight() 11326 }; 11327 } 11328}; 11329 11330$.fn.position = function( options ) { 11331 if ( !options || !options.of ) { 11332 return _position.apply( this, arguments ); 11333 } 11334 11335 // make a copy, we don't want to modify arguments 11336 options = $.extend( {}, options ); 11337 11338 var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions, 11339 target = $( options.of ), 11340 within = $.position.getWithinInfo( options.within ), 11341 scrollInfo = $.position.getScrollInfo( within ), 11342 collision = ( options.collision || "flip" ).split( " " ), 11343 offsets = {}; 11344 11345 dimensions = getDimensions( target ); 11346 if ( target[0].preventDefault ) { 11347 // force left top to allow flipping 11348 options.at = "left top"; 11349 } 11350 targetWidth = dimensions.width; 11351 targetHeight = dimensions.height; 11352 targetOffset = dimensions.offset; 11353 // clone to reuse original targetOffset later 11354 basePosition = $.extend( {}, targetOffset ); 11355 11356 // force my and at to have valid horizontal and vertical positions 11357 // if a value is missing or invalid, it will be converted to center 11358 $.each( [ "my", "at" ], function() { 11359 var pos = ( options[ this ] || "" ).split( " " ), 11360 horizontalOffset, 11361 verticalOffset; 11362 11363 if ( pos.length === 1) { 11364 pos = rhorizontal.test( pos[ 0 ] ) ? 11365 pos.concat( [ "center" ] ) : 11366 rvertical.test( pos[ 0 ] ) ? 11367 [ "center" ].concat( pos ) : 11368 [ "center", "center" ]; 11369 } 11370 pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center"; 11371 pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center"; 11372 11373 // calculate offsets 11374 horizontalOffset = roffset.exec( pos[ 0 ] ); 11375 verticalOffset = roffset.exec( pos[ 1 ] ); 11376 offsets[ this ] = [ 11377 horizontalOffset ? horizontalOffset[ 0 ] : 0, 11378 verticalOffset ? verticalOffset[ 0 ] : 0 11379 ]; 11380 11381 // reduce to just the positions without the offsets 11382 options[ this ] = [ 11383 rposition.exec( pos[ 0 ] )[ 0 ], 11384 rposition.exec( pos[ 1 ] )[ 0 ] 11385 ]; 11386 }); 11387 11388 // normalize collision option 11389 if ( collision.length === 1 ) { 11390 collision[ 1 ] = collision[ 0 ]; 11391 } 11392 11393 if ( options.at[ 0 ] === "right" ) { 11394 basePosition.left += targetWidth; 11395 } else if ( options.at[ 0 ] === "center" ) { 11396 basePosition.left += targetWidth / 2; 11397 } 11398 11399 if ( options.at[ 1 ] === "bottom" ) { 11400 basePosition.top += targetHeight; 11401 } else if ( options.at[ 1 ] === "center" ) { 11402 basePosition.top += targetHeight / 2; 11403 } 11404 11405 atOffset = getOffsets( offsets.at, targetWidth, targetHeight ); 11406 basePosition.left += atOffset[ 0 ]; 11407 basePosition.top += atOffset[ 1 ]; 11408 11409 return this.each(function() { 11410 var collisionPosition, using, 11411 elem = $( this ), 11412 elemWidth = elem.outerWidth(), 11413 elemHeight = elem.outerHeight(), 11414 marginLeft = parseCss( this, "marginLeft" ), 11415 marginTop = parseCss( this, "marginTop" ), 11416 collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) + scrollInfo.width, 11417 collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) + scrollInfo.height, 11418 position = $.extend( {}, basePosition ), 11419 myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() ); 11420 11421 if ( options.my[ 0 ] === "right" ) { 11422 position.left -= elemWidth; 11423 } else if ( options.my[ 0 ] === "center" ) { 11424 position.left -= elemWidth / 2; 11425 } 11426 11427 if ( options.my[ 1 ] === "bottom" ) { 11428 position.top -= elemHeight; 11429 } else if ( options.my[ 1 ] === "center" ) { 11430 position.top -= elemHeight / 2; 11431 } 11432 11433 position.left += myOffset[ 0 ]; 11434 position.top += myOffset[ 1 ]; 11435 11436 // if the browser doesn't support fractions, then round for consistent results 11437 if ( !$.support.offsetFractions ) { 11438 position.left = round( position.left ); 11439 position.top = round( position.top ); 11440 } 11441 11442 collisionPosition = { 11443 marginLeft: marginLeft, 11444 marginTop: marginTop 11445 }; 11446 11447 $.each( [ "left", "top" ], function( i, dir ) { 11448 if ( $.ui.position[ collision[ i ] ] ) { 11449 $.ui.position[ collision[ i ] ][ dir ]( position, { 11450 targetWidth: targetWidth, 11451 targetHeight: targetHeight, 11452 elemWidth: elemWidth, 11453 elemHeight: elemHeight, 11454 collisionPosition: collisionPosition, 11455 collisionWidth: collisionWidth, 11456 collisionHeight: collisionHeight, 11457 offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ], 11458 my: options.my, 11459 at: options.at, 11460 within: within, 11461 elem : elem 11462 }); 11463 } 11464 }); 11465 11466 if ( options.using ) { 11467 // adds feedback as second argument to using callback, if present 11468 using = function( props ) { 11469 var left = targetOffset.left - position.left, 11470 right = left + targetWidth - elemWidth, 11471 top = targetOffset.top - position.top, 11472 bottom = top + targetHeight - elemHeight, 11473 feedback = { 11474 target: { 11475 element: target, 11476 left: targetOffset.left, 11477 top: targetOffset.top, 11478 width: targetWidth, 11479 height: targetHeight 11480 }, 11481 element: { 11482 element: elem, 11483 left: position.left, 11484 top: position.top, 11485 width: elemWidth, 11486 height: elemHeight 11487 }, 11488 horizontal: right < 0 ? "left" : left > 0 ? "right" : "center", 11489 vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle" 11490 }; 11491 if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) { 11492 feedback.horizontal = "center"; 11493 } 11494 if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) { 11495 feedback.vertical = "middle"; 11496 } 11497 if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) { 11498 feedback.important = "horizontal"; 11499 } else { 11500 feedback.important = "vertical"; 11501 } 11502 options.using.call( this, props, feedback ); 11503 }; 11504 } 11505 11506 elem.offset( $.extend( position, { using: using } ) ); 11507 }); 11508}; 11509 11510$.ui.position = { 11511 fit: { 11512 left: function( position, data ) { 11513 var within = data.within, 11514 withinOffset = within.isWindow ? within.scrollLeft : within.offset.left, 11515 outerWidth = within.width, 11516 collisionPosLeft = position.left - data.collisionPosition.marginLeft, 11517 overLeft = withinOffset - collisionPosLeft, 11518 overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset, 11519 newOverRight; 11520 11521 // element is wider than within 11522 if ( data.collisionWidth > outerWidth ) { 11523 // element is initially over the left side of within 11524 if ( overLeft > 0 && overRight <= 0 ) { 11525 newOverRight = position.left + overLeft + data.collisionWidth - outerWidth - withinOffset; 11526 position.left += overLeft - newOverRight; 11527 // element is initially over right side of within 11528 } else if ( overRight > 0 && overLeft <= 0 ) { 11529 position.left = withinOffset; 11530 // element is initially over both left and right sides of within 11531 } else { 11532 if ( overLeft > overRight ) { 11533 position.left = withinOffset + outerWidth - data.collisionWidth; 11534 } else { 11535 position.left = withinOffset; 11536 } 11537 } 11538 // too far left -> align with left edge 11539 } else if ( overLeft > 0 ) { 11540 position.left += overLeft; 11541 // too far right -> align with right edge 11542 } else if ( overRight > 0 ) { 11543 position.left -= overRight; 11544 // adjust based on position and margin 11545 } else { 11546 position.left = max( position.left - collisionPosLeft, position.left ); 11547 } 11548 }, 11549 top: function( position, data ) { 11550 var within = data.within, 11551 withinOffset = within.isWindow ? within.scrollTop : within.offset.top, 11552 outerHeight = data.within.height, 11553 collisionPosTop = position.top - data.collisionPosition.marginTop, 11554 overTop = withinOffset - collisionPosTop, 11555 overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset, 11556 newOverBottom; 11557 11558 // element is taller than within 11559 if ( data.collisionHeight > outerHeight ) { 11560 // element is initially over the top of within 11561 if ( overTop > 0 && overBottom <= 0 ) { 11562 newOverBottom = position.top + overTop + data.collisionHeight - outerHeight - withinOffset; 11563 position.top += overTop - newOverBottom; 11564 // element is initially over bottom of within 11565 } else if ( overBottom > 0 && overTop <= 0 ) { 11566 position.top = withinOffset; 11567 // element is initially over both top and bottom of within 11568 } else { 11569 if ( overTop > overBottom ) { 11570 position.top = withinOffset + outerHeight - data.collisionHeight; 11571 } else { 11572 position.top = withinOffset; 11573 } 11574 } 11575 // too far up -> align with top 11576 } else if ( overTop > 0 ) { 11577 position.top += overTop; 11578 // too far down -> align with bottom edge 11579 } else if ( overBottom > 0 ) { 11580 position.top -= overBottom; 11581 // adjust based on position and margin 11582 } else { 11583 position.top = max( position.top - collisionPosTop, position.top ); 11584 } 11585 } 11586 }, 11587 flip: { 11588 left: function( position, data ) { 11589 var within = data.within, 11590 withinOffset = within.offset.left + within.scrollLeft, 11591 outerWidth = within.width, 11592 offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left, 11593 collisionPosLeft = position.left - data.collisionPosition.marginLeft, 11594 overLeft = collisionPosLeft - offsetLeft, 11595 overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft, 11596 myOffset = data.my[ 0 ] === "left" ? 11597 -data.elemWidth : 11598 data.my[ 0 ] === "right" ? 11599 data.elemWidth : 11600 0, 11601 atOffset = data.at[ 0 ] === "left" ? 11602 data.targetWidth : 11603 data.at[ 0 ] === "right" ? 11604 -data.targetWidth : 11605 0, 11606 offset = -2 * data.offset[ 0 ], 11607 newOverRight, 11608 newOverLeft; 11609 11610 if ( overLeft < 0 ) { 11611 newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth - outerWidth - withinOffset; 11612 if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) { 11613 position.left += myOffset + atOffset + offset; 11614 } 11615 } 11616 else if ( overRight > 0 ) { 11617 newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset + atOffset + offset - offsetLeft; 11618 if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) { 11619 position.left += myOffset + atOffset + offset; 11620 } 11621 } 11622 }, 11623 top: function( position, data ) { 11624 var within = data.within, 11625 withinOffset = within.offset.top + within.scrollTop, 11626 outerHeight = within.height, 11627 offsetTop = within.isWindow ? within.scrollTop : within.offset.top, 11628 collisionPosTop = position.top - data.collisionPosition.marginTop, 11629 overTop = collisionPosTop - offsetTop, 11630 overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop, 11631 top = data.my[ 1 ] === "top", 11632 myOffset = top ? 11633 -data.elemHeight : 11634 data.my[ 1 ] === "bottom" ? 11635 data.elemHeight : 11636 0, 11637 atOffset = data.at[ 1 ] === "top" ? 11638 data.targetHeight : 11639 data.at[ 1 ] === "bottom" ? 11640 -data.targetHeight : 11641 0, 11642 offset = -2 * data.offset[ 1 ], 11643 newOverTop, 11644 newOverBottom; 11645 if ( overTop < 0 ) { 11646 newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight - outerHeight - withinOffset; 11647 if ( ( position.top + myOffset + atOffset + offset) > overTop && ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) ) { 11648 position.top += myOffset + atOffset + offset; 11649 } 11650 } 11651 else if ( overBottom > 0 ) { 11652 newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + offset - offsetTop; 11653 if ( ( position.top + myOffset + atOffset + offset) > overBottom && ( newOverTop > 0 || abs( newOverTop ) < overBottom ) ) { 11654 position.top += myOffset + atOffset + offset; 11655 } 11656 } 11657 } 11658 }, 11659 flipfit: { 11660 left: function() { 11661 $.ui.position.flip.left.apply( this, arguments ); 11662 $.ui.position.fit.left.apply( this, arguments ); 11663 }, 11664 top: function() { 11665 $.ui.position.flip.top.apply( this, arguments ); 11666 $.ui.position.fit.top.apply( this, arguments ); 11667 } 11668 } 11669}; 11670 11671// fraction support test 11672(function () { 11673 var testElement, testElementParent, testElementStyle, offsetLeft, i, 11674 body = document.getElementsByTagName( "body" )[ 0 ], 11675 div = document.createElement( "div" ); 11676 11677 //Create a "fake body" for testing based on method used in jQuery.support 11678 testElement = document.createElement( body ? "div" : "body" ); 11679 testElementStyle = { 11680 visibility: "hidden", 11681 width: 0, 11682 height: 0, 11683 border: 0, 11684 margin: 0, 11685 background: "none" 11686 }; 11687 if ( body ) { 11688 $.extend( testElementStyle, { 11689 position: "absolute", 11690 left: "-1000px", 11691 top: "-1000px" 11692 }); 11693 } 11694 for ( i in testElementStyle ) { 11695 testElement.style[ i ] = testElementStyle[ i ]; 11696 } 11697 testElement.appendChild( div ); 11698 testElementParent = body || document.documentElement; 11699 testElementParent.insertBefore( testElement, testElementParent.firstChild ); 11700 11701 div.style.cssText = "position: absolute; left: 10.7432222px;"; 11702 11703 offsetLeft = $( div ).offset().left; 11704 $.support.offsetFractions = offsetLeft > 10 && offsetLeft < 11; 11705 11706 testElement.innerHTML = ""; 11707 testElementParent.removeChild( testElement ); 11708})(); 11709 11710}( jQuery ) ); 11711 11712 11713 11714 11715/*! 11716 * jQuery UI Menu 1.10.3 11717 * http://jqueryui.com 11718 * 11719 * Copyright 2013 jQuery Foundation and other contributors 11720 * Released under the MIT license. 11721 * http://jquery.org/license 11722 * 11723 * http://api.jqueryui.com/menu/ 11724 * 11725 * Depends: 11726 * jquery.ui.core.js 11727 * jquery.ui.widget.js 11728 * jquery.ui.position.js 11729 */ 11730 11731(function( $, undefined ) { 11732 11733$.widget( "ui.menu", { 11734 version: "1.10.3", 11735 defaultElement: "<ul>", 11736 delay: 300, 11737 options: { 11738 icons: { 11739 submenu: "ui-icon-carat-1-e" 11740 }, 11741 menus: "ul", 11742 position: { 11743 my: "left top", 11744 at: "right top" 11745 }, 11746 role: "menu", 11747 11748 // callbacks 11749 blur: null, 11750 focus: null, 11751 select: null 11752 }, 11753 11754 _create: function() { 11755 this.activeMenu = this.element; 11756 // flag used to prevent firing of the click handler 11757 // as the event bubbles up through nested menus 11758 this.mouseHandled = false;
11759 this.element 11760 .uniqueId() 11761 .addClass( "ui-menu ui-widget ui-widget-content ui-corner-all" ) 11762 .toggleClass( "ui-menu-icons", !!this.element.find( ".ui-icon" ).length ) 11763 .attr({ 11764 role: this.options.role, 11765 tabIndex: 0 11766 }) 11767 // need to catch all clicks on disabled menu 11768 // not possible through _on 11769 .bind( "click" + this.eventNamespace, $.proxy(function( event ) { 11770 if ( this.options.disabled ) { 11771 event.preventDefault(); 11772 } 11773 }, this )); 11774 11775 if ( this.options.disabled ) { 11776 this.element 11777 .addClass( "ui-state-disabled" ) 11778 .attr( "aria-disabled", "true" ); 11779 } 11780 11781 this._on({ 11782 // Prevent focus from sticking to links inside menu after clicking 11783 // them (focus should always stay on UL during navigation). 11784 "mousedown .ui-menu-item > a": function( event ) { 11785 event.preventDefault(); 11786 }, 11787 "click .ui-state-disabled > a": function( event ) { 11788 event.preventDefault(); 11789 }, 11790 "click .ui-menu-item:has(a)": function( event ) { 11791 var target = $( event.target ).closest( ".ui-menu-item" ); 11792 if ( !this.mouseHandled && target.not( ".ui-state-disabled" ).length ) { 11793 this.mouseHandled = true; 11794 11795 this.select( event ); 11796 // Open submenu on click 11797 if ( target.has( ".ui-menu" ).length ) { 11798 this.expand( event ); 11799 } else if ( !this.element.is( ":focus" ) ) { 11800 // Redirect focus to the menu 11801 this.element.trigger( "focus", [ true ] ); 11802 11803 // If the active item is on the top level, let it stay active. 11804 // Otherwise, blur the active item since it is no longer visible. 11805 if ( this.active && this.active.parents( ".ui-menu" ).length === 1 ) { 11806 clearTimeout( this.timer ); 11807 } 11808 } 11809 } 11810 }, 11811 "mouseenter .ui-menu-item": function( event ) { 11812 var target = $( event.currentTarget ); 11813 // Remove ui-state-active class from siblings of the newly focused menu item 11814 // to avoid a jump caused by adjacent elements both having a class with a border 11815 target.siblings().children( ".ui-state-active" ).removeClass( "ui-state-active" ); 11816 this.focus( event, target ); 11817 }, 11818 mouseleave: "collapseAll", 11819 "mouseleave .ui-menu": "collapseAll", 11820 focus: function( event, keepActiveItem ) { 11821 // If there's already an active item, keep it active 11822 // If not, activate the first item 11823 var item = this.active || this.element.children( ".ui-menu-item" ).eq( 0 ); 11824 11825 if ( !keepActiveItem ) { 11826 this.focus( event, item ); 11827 } 11828 }, 11829 blur: function( event ) { 11830 this._delay(function() { 11831 if ( !$.contains( this.element[0], this.document[0].activeElement ) ) { 11832 this.collapseAll( event ); 11833 } 11834 }); 11835 }, 11836 keydown: "_keydown" 11837 }); 11838 11839 this.refresh(); 11840
vendor: 36,920 bytes, lines 11841-13224
11841 // Clicks outside of a menu collapse any open menus 11842 this._on( this.document, { 11843 click: function( event ) { 11844 if ( !$( event.target ).closest( ".ui-menu" ).length ) { 11845 this.collapseAll( event ); 11846 } 11847 11848 // Reset the mouseHandled flag 11849 this.mouseHandled = false; 11850 } 11851 }); 11852 }, 11853 11854 _destroy: function() { 11855 // Destroy (sub)menus 11856 this.element 11857 .removeAttr( "aria-activedescendant" ) 11858 .find( ".ui-menu" ).addBack() 11859 .removeClass( "ui-menu ui-widget ui-widget-content ui-corner-all ui-menu-icons" ) 11860 .removeAttr( "role" ) 11861 .removeAttr( "tabIndex" ) 11862 .removeAttr( "aria-labelledby" ) 11863 .removeAttr( "aria-expanded" ) 11864 .removeAttr( "aria-hidden" ) 11865 .removeAttr( "aria-disabled" ) 11866 .removeUniqueId() 11867 .show(); 11868 11869 // Destroy menu items 11870 this.element.find( ".ui-menu-item" ) 11871 .removeClass( "ui-menu-item" ) 11872 .removeAttr( "role" ) 11873 .removeAttr( "aria-disabled" ) 11874 .children( "a" ) 11875 .removeUniqueId() 11876 .removeClass( "ui-corner-all ui-state-hover" ) 11877 .removeAttr( "tabIndex" ) 11878 .removeAttr( "role" ) 11879 .removeAttr( "aria-haspopup" ) 11880 .children().each( function() { 11881 var elem = $( this ); 11882 if ( elem.data( "ui-menu-submenu-carat" ) ) { 11883 elem.remove(); 11884 } 11885 }); 11886 11887 // Destroy menu dividers 11888 this.element.find( ".ui-menu-divider" ).removeClass( "ui-menu-divider ui-widget-content" ); 11889 }, 11890 11891 _keydown: function( event ) { 11892 /*jshint maxcomplexity:20*/ 11893 var match, prev, character, skip, regex, 11894 preventDefault = true; 11895 11896 function escape( value ) { 11897 return value.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" ); 11898 } 11899 11900 switch ( event.keyCode ) { 11901 case $.ui.keyCode.PAGE_UP: 11902 this.previousPage( event ); 11903 break; 11904 case $.ui.keyCode.PAGE_DOWN: 11905 this.nextPage( event ); 11906 break; 11907 case $.ui.keyCode.HOME: 11908 this._move( "first", "first", event ); 11909 break; 11910 case $.ui.keyCode.END: 11911 this._move( "last", "last", event ); 11912 break; 11913 case $.ui.keyCode.UP: 11914 this.previous( event ); 11915 break; 11916 case $.ui.keyCode.DOWN: 11917 this.next( event ); 11918 break; 11919 case $.ui.keyCode.LEFT: 11920 this.collapse( event ); 11921 break; 11922 case $.ui.keyCode.RIGHT: 11923 if ( this.active && !this.active.is( ".ui-state-disabled" ) ) { 11924 this.expand( event ); 11925 } 11926 break; 11927 case $.ui.keyCode.ENTER: 11928 case $.ui.keyCode.SPACE: 11929 this._activate( event ); 11930 break; 11931 case $.ui.keyCode.ESCAPE: 11932 this.collapse( event ); 11933 break; 11934 default: 11935 preventDefault = false; 11936 prev = this.previousFilter || ""; 11937 character = String.fromCharCode( event.keyCode ); 11938 skip = false; 11939 11940 clearTimeout( this.filterTimer ); 11941 11942 if ( character === prev ) { 11943 skip = true; 11944 } else { 11945 character = prev + character; 11946 } 11947 11948 regex = new RegExp( "^" + escape( character ), "i" ); 11949 match = this.activeMenu.children( ".ui-menu-item" ).filter(function() { 11950 return regex.test( $( this ).children( "a" ).text() ); 11951 }); 11952 match = skip && match.index( this.active.next() ) !== -1 ? 11953 this.active.nextAll( ".ui-menu-item" ) : 11954 match; 11955 11956 // If no matches on the current filter, reset to the last character pressed 11957 // to move down the menu to the first item that starts with that character 11958 if ( !match.length ) { 11959 character = String.fromCharCode( event.keyCode ); 11960 regex = new RegExp( "^" + escape( character ), "i" ); 11961 match = this.activeMenu.children( ".ui-menu-item" ).filter(function() { 11962 return regex.test( $( this ).children( "a" ).text() ); 11963 }); 11964 } 11965 11966 if ( match.length ) { 11967 this.focus( event, match ); 11968 if ( match.length > 1 ) { 11969 this.previousFilter = character; 11970 this.filterTimer = this._delay(function() { 11971 delete this.previousFilter; 11972 }, 1000 ); 11973 } else { 11974 delete this.previousFilter; 11975 } 11976 } else { 11977 delete this.previousFilter; 11978 } 11979 } 11980 11981 if ( preventDefault ) { 11982 event.preventDefault(); 11983 } 11984 }, 11985 11986 _activate: function( event ) { 11987 if ( !this.active.is( ".ui-state-disabled" ) ) { 11988 if ( this.active.children( "a[aria-haspopup='true']" ).length ) { 11989 this.expand( event ); 11990 } else { 11991 this.select( event ); 11992 } 11993 } 11994 }, 11995 11996 refresh: function() { 11997 var menus, 11998 icon = this.options.icons.submenu, 11999 submenus = this.element.find( this.options.menus ); 12000 12001 // Initialize nested menus 12002 submenus.filter( ":not(.ui-menu)" ) 12003 .addClass( "ui-menu ui-widget ui-widget-content ui-corner-all" ) 12004 .hide() 12005 .attr({ 12006 role: this.options.role, 12007 "aria-hidden": "true", 12008 "aria-expanded": "false" 12009 }) 12010 .each(function() { 12011 var menu = $( this ), 12012 item = menu.prev( "a" ), 12013 submenuCarat = $( "<span>" ) 12014 .addClass( "ui-menu-icon ui-icon " + icon ) 12015 .data( "ui-menu-submenu-carat", true ); 12016 12017 item 12018 .attr( "aria-haspopup", "true" ) 12019 .prepend( submenuCarat ); 12020 menu.attr( "aria-labelledby", item.attr( "id" ) ); 12021 }); 12022 12023 menus = submenus.add( this.element ); 12024 12025 // Don't refresh list items that are already adapted 12026 menus.children( ":not(.ui-menu-item):has(a)" ) 12027 .addClass( "ui-menu-item" ) 12028 .attr( "role", "presentation" ) 12029 .children( "a" ) 12030 .uniqueId() 12031 .addClass( "ui-corner-all" ) 12032 .attr({ 12033 tabIndex: -1, 12034 role: this._itemRole() 12035 }); 12036 12037 // Initialize unlinked menu-items containing spaces and/or dashes only as dividers 12038 menus.children( ":not(.ui-menu-item)" ).each(function() { 12039 var item = $( this ); 12040 // hyphen, em dash, en dash 12041 if ( !/[^\-\u2014\u2013\s]/.test( item.text() ) ) { 12042 item.addClass( "ui-widget-content ui-menu-divider" ); 12043 } 12044 }); 12045 12046 // Add aria-disabled attribute to any disabled menu item 12047 menus.children( ".ui-state-disabled" ).attr( "aria-disabled", "true" ); 12048 12049 // If the active item has been removed, blur the menu 12050 if ( this.active && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) { 12051 this.blur(); 12052 } 12053 }, 12054 12055 _itemRole: function() { 12056 return { 12057 menu: "menuitem", 12058 listbox: "option" 12059 }[ this.options.role ]; 12060 }, 12061 12062 _setOption: function( key, value ) { 12063 if ( key === "icons" ) { 12064 this.element.find( ".ui-menu-icon" ) 12065 .removeClass( this.options.icons.submenu ) 12066 .addClass( value.submenu ); 12067 } 12068 this._super( key, value ); 12069 }, 12070 12071 focus: function( event, item ) { 12072 var nested, focused; 12073 this.blur( event, event && event.type === "focus" ); 12074 12075 this._scrollIntoView( item ); 12076 12077 this.active = item.first(); 12078 focused = this.active.children( "a" ).addClass( "ui-state-focus" ); 12079 // Only update aria-activedescendant if there's a role 12080 // otherwise we assume focus is managed elsewhere 12081 if ( this.options.role ) { 12082 this.element.attr( "aria-activedescendant", focused.attr( "id" ) ); 12083 } 12084 12085 // Highlight active parent menu item, if any 12086 this.active 12087 .parent() 12088 .closest( ".ui-menu-item" ) 12089 .children( "a:first" ) 12090 .addClass( "ui-state-active" ); 12091 12092 if ( event && event.type === "keydown" ) { 12093 this._close(); 12094 } else { 12095 this.timer = this._delay(function() { 12096 this._close(); 12097 }, this.delay ); 12098 } 12099 12100 nested = item.children( ".ui-menu" ); 12101 if ( nested.length && ( /^mouse/.test( event.type ) ) ) { 12102 this._startOpening(nested); 12103 } 12104 this.activeMenu = item.parent(); 12105 12106 this._trigger( "focus", event, { item: item } ); 12107 }, 12108 12109 _scrollIntoView: function( item ) { 12110 var borderTop, paddingTop, offset, scroll, elementHeight, itemHeight; 12111 if ( this._hasScroll() ) { 12112 borderTop = parseFloat( $.css( this.activeMenu[0], "borderTopWidth" ) ) || 0; 12113 paddingTop = parseFloat( $.css( this.activeMenu[0], "paddingTop" ) ) || 0; 12114 offset = item.offset().top - this.activeMenu.offset().top - borderTop - paddingTop; 12115 scroll = this.activeMenu.scrollTop(); 12116 elementHeight = this.activeMenu.height(); 12117 itemHeight = item.height(); 12118 12119 if ( offset < 0 ) { 12120 this.activeMenu.scrollTop( scroll + offset ); 12121 } else if ( offset + itemHeight > elementHeight ) { 12122 this.activeMenu.scrollTop( scroll + offset - elementHeight + itemHeight ); 12123 } 12124 } 12125 }, 12126 12127 blur: function( event, fromFocus ) { 12128 if ( !fromFocus ) { 12129 clearTimeout( this.timer ); 12130 } 12131 12132 if ( !this.active ) { 12133 return; 12134 } 12135 12136 this.active.children( "a" ).removeClass( "ui-state-focus" ); 12137 this.active = null; 12138 12139 this._trigger( "blur", event, { item: this.active } ); 12140 }, 12141 12142 _startOpening: function( submenu ) { 12143 clearTimeout( this.timer ); 12144 12145 // Don't open if already open fixes a Firefox bug that caused a .5 pixel 12146 // shift in the submenu position when mousing over the carat icon 12147 if ( submenu.attr( "aria-hidden" ) !== "true" ) { 12148 return; 12149 } 12150 12151 this.timer = this._delay(function() { 12152 this._close(); 12153 this._open( submenu ); 12154 }, this.delay ); 12155 }, 12156 12157 _open: function( submenu ) { 12158 var position = $.extend({ 12159 of: this.active 12160 }, this.options.position ); 12161 12162 clearTimeout( this.timer ); 12163 this.element.find( ".ui-menu" ).not( submenu.parents( ".ui-menu" ) ) 12164 .hide() 12165 .attr( "aria-hidden", "true" ); 12166 12167 submenu 12168 .show() 12169 .removeAttr( "aria-hidden" ) 12170 .attr( "aria-expanded", "true" ) 12171 .position( position ); 12172 }, 12173 12174 collapseAll: function( event, all ) { 12175 clearTimeout( this.timer ); 12176 this.timer = this._delay(function() { 12177 // If we were passed an event, look for the submenu that contains the event 12178 var currentMenu = all ? this.element : 12179 $( event && event.target ).closest( this.element.find( ".ui-menu" ) ); 12180 12181 // If we found no valid submenu ancestor, use the main menu to close all sub menus anyway 12182 if ( !currentMenu.length ) { 12183 currentMenu = this.element; 12184 } 12185 12186 this._close( currentMenu ); 12187 12188 this.blur( event ); 12189 this.activeMenu = currentMenu; 12190 }, this.delay ); 12191 }, 12192 12193 // With no arguments, closes the currently active menu - if nothing is active 12194 // it closes all menus. If passed an argument, it will search for menus BELOW 12195 _close: function( startMenu ) { 12196 if ( !startMenu ) { 12197 startMenu = this.active ? this.active.parent() : this.element; 12198 } 12199 12200 startMenu 12201 .find( ".ui-menu" ) 12202 .hide() 12203 .attr( "aria-hidden", "true" ) 12204 .attr( "aria-expanded", "false" ) 12205 .end() 12206 .find( "a.ui-state-active" ) 12207 .removeClass( "ui-state-active" ); 12208 }, 12209 12210 collapse: function( event ) { 12211 var newItem = this.active && 12212 this.active.parent().closest( ".ui-menu-item", this.element ); 12213 if ( newItem && newItem.length ) { 12214 this._close(); 12215 this.focus( event, newItem ); 12216 } 12217 }, 12218 12219 expand: function( event ) { 12220 var newItem = this.active && 12221 this.active 12222 .children( ".ui-menu " ) 12223 .children( ".ui-menu-item" ) 12224 .first(); 12225 12226 if ( newItem && newItem.length ) { 12227 this._open( newItem.parent() ); 12228 12229 // Delay so Firefox will not hide activedescendant change in expanding submenu from AT 12230 this._delay(function() { 12231 this.focus( event, newItem ); 12232 }); 12233 } 12234 }, 12235 12236 next: function( event ) { 12237 this._move( "next", "first", event ); 12238 }, 12239 12240 previous: function( event ) { 12241 this._move( "prev", "last", event ); 12242 }, 12243 12244 isFirstItem: function() { 12245 return this.active && !this.active.prevAll( ".ui-menu-item" ).length; 12246 }, 12247 12248 isLastItem: function() { 12249 return this.active && !this.active.nextAll( ".ui-menu-item" ).length; 12250 }, 12251 12252 _move: function( direction, filter, event ) { 12253 var next; 12254 if ( this.active ) { 12255 if ( direction === "first" || direction === "last" ) { 12256 next = this.active 12257 [ direction === "first" ? "prevAll" : "nextAll" ]( ".ui-menu-item" ) 12258 .eq( -1 ); 12259 } else { 12260 next = this.active 12261 [ direction + "All" ]( ".ui-menu-item" ) 12262 .eq( 0 ); 12263 } 12264 } 12265 if ( !next || !next.length || !this.active ) { 12266 next = this.activeMenu.children( ".ui-menu-item" )[ filter ](); 12267 } 12268 12269 this.focus( event, next ); 12270 }, 12271 12272 nextPage: function( event ) { 12273 var item, base, height; 12274 12275 if ( !this.active ) { 12276 this.next( event ); 12277 return; 12278 } 12279 if ( this.isLastItem() ) { 12280 return; 12281 } 12282 if ( this._hasScroll() ) { 12283 base = this.active.offset().top; 12284 height = this.element.height(); 12285 this.active.nextAll( ".ui-menu-item" ).each(function() { 12286 item = $( this ); 12287 return item.offset().top - base - height < 0; 12288 }); 12289 12290 this.focus( event, item ); 12291 } else { 12292 this.focus( event, this.activeMenu.children( ".ui-menu-item" ) 12293 [ !this.active ? "first" : "last" ]() ); 12294 } 12295 }, 12296 12297 previousPage: function( event ) { 12298 var item, base, height; 12299 if ( !this.active ) { 12300 this.next( event ); 12301 return; 12302 } 12303 if ( this.isFirstItem() ) { 12304 return; 12305 } 12306 if ( this._hasScroll() ) { 12307 base = this.active.offset().top; 12308 height = this.element.height(); 12309 this.active.prevAll( ".ui-menu-item" ).each(function() { 12310 item = $( this ); 12311 return item.offset().top - base + height > 0; 12312 }); 12313 12314 this.focus( event, item ); 12315 } else { 12316 this.focus( event, this.activeMenu.children( ".ui-menu-item" ).first() ); 12317 } 12318 }, 12319 12320 _hasScroll: function() { 12321 return this.element.outerHeight() < this.element.prop( "scrollHeight" ); 12322 }, 12323 12324 select: function( event ) { 12325 // TODO: It should never be possible to not have an active item at this 12326 // point, but the tests don't trigger mouseenter before click. 12327 this.active = this.active || $( event.target ).closest( ".ui-menu-item" ); 12328 var ui = { item: this.active }; 12329 if ( !this.active.has( ".ui-menu" ).length ) { 12330 this.collapseAll( event, true ); 12331 } 12332 this._trigger( "select", event, ui ); 12333 } 12334}); 12335 12336}( jQuery )); 12337 12338 12339 12340 12341 12342/*! 12343 * jQuery UI Autocomplete 1.10.3 12344 * http://jqueryui.com 12345 * 12346 * Copyright 2013 jQuery Foundation and other contributors 12347 * Released under the MIT license. 12348 * http://jquery.org/license 12349 * 12350 * http://api.jqueryui.com/autocomplete/ 12351 * 12352 * Depends: 12353 * jquery.ui.core.js 12354 * jquery.ui.widget.js 12355 * jquery.ui.position.js 12356 * jquery.ui.menu.js 12357 */ 12358 12359(function( $, undefined ) { 12360 12361// used to prevent race conditions with remote data sources 12362var requestIndex = 0; 12363 12364$.widget( "ui.autocomplete", { 12365 version: "1.10.3", 12366 defaultElement: "<input>", 12367 options: { 12368 appendTo: null, 12369 autoFocus: false, 12370 delay: 300, 12371 minLength: 1, 12372 position: { 12373 my: "left top", 12374 at: "left bottom", 12375 collision: "none" 12376 }, 12377 source: null, 12378 12379 // callbacks 12380 change: null, 12381 close: null, 12382 focus: null, 12383 open: null, 12384 response: null, 12385 search: null, 12386 select: null 12387 }, 12388 12389 pending: 0, 12390 12391 _create: function() { 12392 // Some browsers only repeat keydown events, not keypress events, 12393 // so we use the suppressKeyPress flag to determine if we've already 12394 // handled the keydown event. #7269 12395 // Unfortunately the code for & in keypress is the same as the up arrow, 12396 // so we use the suppressKeyPressRepeat flag to avoid handling keypress 12397 // events when we know the keydown event was used to modify the 12398 // search term. #7799 12399 var suppressKeyPress, suppressKeyPressRepeat, suppressInput, 12400 nodeName = this.element[0].nodeName.toLowerCase(), 12401 isTextarea = nodeName === "textarea", 12402 isInput = nodeName === "input"; 12403 12404 this.isMultiLine = 12405 // Textareas are always multi-line 12406 isTextarea ? true : 12407 // Inputs are always single-line, even if inside a contentEditable element 12408 // IE also treats inputs as contentEditable 12409 isInput ? false : 12410 // All other element types are determined by whether or not they're contentEditable 12411 this.element.prop( "isContentEditable" ); 12412 12413 this.valueMethod = this.element[ isTextarea || isInput ? "val" : "text" ]; 12414 this.isNewMenu = true; 12415 12416 this.element 12417 .addClass( "ui-autocomplete-input" ) 12418 .attr( "autocomplete", "off" ); 12419 12420 this._on( this.element, { 12421 keydown: function( event ) { 12422 /*jshint maxcomplexity:15*/ 12423 if ( this.element.prop( "readOnly" ) ) { 12424 suppressKeyPress = true; 12425 suppressInput = true; 12426 suppressKeyPressRepeat = true; 12427 return; 12428 } 12429 12430 suppressKeyPress = false; 12431 suppressInput = false; 12432 suppressKeyPressRepeat = false; 12433 var keyCode = $.ui.keyCode; 12434 switch( event.keyCode ) { 12435 case keyCode.PAGE_UP: 12436 suppressKeyPress = true; 12437 this._move( "previousPage", event ); 12438 break; 12439 case keyCode.PAGE_DOWN: 12440 suppressKeyPress = true; 12441 this._move( "nextPage", event ); 12442 break; 12443 case keyCode.UP: 12444 suppressKeyPress = true; 12445 this._keyEvent( "previous", event ); 12446 break; 12447 case keyCode.DOWN: 12448 suppressKeyPress = true; 12449 this._keyEvent( "next", event ); 12450 break; 12451 case keyCode.ENTER: 12452 case keyCode.NUMPAD_ENTER: 12453 // when menu is open and has focus 12454 if ( this.menu.active ) { 12455 // #6055 - Opera still allows the keypress to occur 12456 // which causes forms to submit 12457 suppressKeyPress = true; 12458 event.preventDefault(); 12459 this.menu.select( event ); 12460 } 12461 break; 12462 case keyCode.TAB: 12463 if ( this.menu.active ) { 12464 this.menu.select( event ); 12465 } 12466 break; 12467 case keyCode.ESCAPE: 12468 if ( this.menu.element.is( ":visible" ) ) { 12469 this._value( this.term ); 12470 this.close( event ); 12471 // Different browsers have different default behavior for escape 12472 // Single press can mean undo or clear 12473 // Double press in IE means clear the whole form 12474 event.preventDefault(); 12475 } 12476 break; 12477 default: 12478 suppressKeyPressRepeat = true; 12479 // search timeout should be triggered before the input value is changed 12480 this._searchTimeout( event ); 12481 break; 12482 } 12483 }, 12484 keypress: function( event ) { 12485 if ( suppressKeyPress ) { 12486 suppressKeyPress = false; 12487 if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) { 12488 event.preventDefault(); 12489 } 12490 return; 12491 } 12492 if ( suppressKeyPressRepeat ) { 12493 return; 12494 } 12495 12496 // replicate some key handlers to allow them to repeat in Firefox and Opera 12497 var keyCode = $.ui.keyCode; 12498 switch( event.keyCode ) { 12499 case keyCode.PAGE_UP: 12500 this._move( "previousPage", event ); 12501 break; 12502 case keyCode.PAGE_DOWN: 12503 this._move( "nextPage", event ); 12504 break; 12505 case keyCode.UP: 12506 this._keyEvent( "previous", event ); 12507 break; 12508 case keyCode.DOWN: 12509 this._keyEvent( "next", event ); 12510 break; 12511 } 12512 }, 12513 input: function( event ) { 12514 if ( suppressInput ) { 12515 suppressInput = false; 12516 event.preventDefault(); 12517 return; 12518 } 12519 this._searchTimeout( event ); 12520 }, 12521 focus: function() { 12522 this.selectedItem = null; 12523 this.previous = this._value(); 12524 }, 12525 blur: function( event ) { 12526 if ( this.cancelBlur ) { 12527 delete this.cancelBlur; 12528 return; 12529 } 12530 12531 clearTimeout( this.searching ); 12532 this.close( event ); 12533 this._change( event ); 12534 } 12535 }); 12536 12537 this._initSource(); 12538 this.menu = $( "<ul>" ) 12539 .addClass( "ui-autocomplete ui-front" ) 12540 .appendTo( this._appendTo() ) 12541 .menu({ 12542 // disable ARIA support, the live region takes care of that 12543 role: null 12544 }) 12545 .hide() 12546 .data( "ui-menu" ); 12547 12548 this._on( this.menu.element, { 12549 mousedown: function( event ) { 12550 // prevent moving focus out of the text field 12551 event.preventDefault(); 12552 12553 // IE doesn't prevent moving focus even with event.preventDefault() 12554 // so we set a flag to know when we should ignore the blur event 12555 this.cancelBlur = true; 12556 this._delay(function() { 12557 delete this.cancelBlur; 12558 }); 12559 12560 // clicking on the scrollbar causes focus to shift to the body 12561 // but we can't detect a mouseup or a click immediately afterward 12562 // so we have to track the next mousedown and close the menu if 12563 // the user clicks somewhere outside of the autocomplete 12564 var menuElement = this.menu.element[ 0 ]; 12565 if ( !$( event.target ).closest( ".ui-menu-item" ).length ) { 12566 this._delay(function() { 12567 var that = this; 12568 this.document.one( "mousedown", function( event ) { 12569 if ( event.target !== that.element[ 0 ] && 12570 event.target !== menuElement && 12571 !$.contains( menuElement, event.target ) ) { 12572 that.close(); 12573 } 12574 }); 12575 }); 12576 } 12577 }, 12578 menufocus: function( event, ui ) { 12579 // support: Firefox 12580 // Prevent accidental activation of menu items in Firefox (#7024 #9118) 12581 if ( this.isNewMenu ) { 12582 this.isNewMenu = false; 12583 if ( event.originalEvent && /^mouse/.test( event.originalEvent.type ) ) { 12584 this.menu.blur(); 12585 12586 this.document.one( "mousemove", function() { 12587 $( event.target ).trigger( event.originalEvent ); 12588 }); 12589 12590 return; 12591 } 12592 } 12593 12594 var item = ui.item.data( "ui-autocomplete-item" ); 12595 if ( false !== this._trigger( "focus", event, { item: item } ) ) { 12596 // use value to match what will end up in the input, if it was a key event 12597 if ( event.originalEvent && /^key/.test( event.originalEvent.type ) ) { 12598 this._value( item.value ); 12599 } 12600 } else { 12601 // Normally the input is populated with the item's value as the 12602 // menu is navigated, causing screen readers to notice a change and 12603 // announce the item. Since the focus event was canceled, this doesn't 12604 // happen, so we update the live region so that screen readers can 12605 // still notice the change and announce it. 12606 this.liveRegion.text( item.value ); 12607 } 12608 }, 12609 menuselect: function( event, ui ) { 12610 var item = ui.item.data( "ui-autocomplete-item" ), 12611 previous = this.previous; 12612 12613 // only trigger when focus was lost (click on menu) 12614 if ( this.element[0] !== this.document[0].activeElement ) { 12615 this.element.focus(); 12616 this.previous = previous; 12617 // #6109 - IE triggers two focus events and the second 12618 // is asynchronous, so we need to reset the previous 12619 // term synchronously and asynchronously :-( 12620 this._delay(function() { 12621 this.previous = previous; 12622 this.selectedItem = item; 12623 }); 12624 } 12625 12626 if ( false !== this._trigger( "select", event, { item: item } ) ) { 12627 this._value( item.value ); 12628 } 12629 // reset the term after the select event 12630 // this allows custom select handling to work properly 12631 this.term = this._value(); 12632 12633 this.close( event ); 12634 this.selectedItem = item; 12635 } 12636 }); 12637 12638 this.liveRegion = $( "<span>", { 12639 role: "status", 12640 "aria-live": "polite" 12641 }) 12642 .addClass( "ui-helper-hidden-accessible" ) 12643 .insertBefore( this.element ); 12644 12645 // turning off autocomplete prevents the browser from remembering the 12646 // value when navigating through history, so we re-enable autocomplete 12647 // if the page is unloaded before the widget is destroyed. #7790 12648 this._on( this.window, { 12649 beforeunload: function() { 12650 this.element.removeAttr( "autocomplete" ); 12651 } 12652 }); 12653 }, 12654 12655 _destroy: function() { 12656 clearTimeout( this.searching ); 12657 this.element 12658 .removeClass( "ui-autocomplete-input" ) 12659 .removeAttr( "autocomplete" ); 12660 this.menu.element.remove(); 12661 this.liveRegion.remove(); 12662 }, 12663 12664 _setOption: function( key, value ) { 12665 this._super( key, value ); 12666 if ( key === "source" ) { 12667 this._initSource(); 12668 } 12669 if ( key === "appendTo" ) { 12670 this.menu.element.appendTo( this._appendTo() ); 12671 } 12672 if ( key === "disabled" && value && this.xhr ) { 12673 this.xhr.abort(); 12674 } 12675 }, 12676 12677 _appendTo: function() { 12678 var element = this.options.appendTo; 12679 12680 if ( element ) { 12681 element = element.jquery || element.nodeType ? 12682 $( element ) : 12683 this.document.find( element ).eq( 0 ); 12684 } 12685 12686 if ( !element ) { 12687 element = this.element.closest( ".ui-front" ); 12688 } 12689 12690 if ( !element.length ) { 12691 element = this.document[0].body; 12692 } 12693 12694 return element; 12695 }, 12696 12697 _initSource: function() { 12698 var array, url, 12699 that = this; 12700 if ( $.isArray(this.options.source) ) { 12701 array = this.options.source; 12702 this.source = function( request, response ) { 12703 response( $.ui.autocomplete.filter( array, request.term ) ); 12704 }; 12705 } else if ( typeof this.options.source === "string" ) { 12706 url = this.options.source; 12707 this.source = function( request, response ) { 12708 if ( that.xhr ) { 12709 that.xhr.abort(); 12710 } 12711 that.xhr = $.ajax({ 12712 url: url, 12713 data: request, 12714 dataType: "json", 12715 success: function( data ) { 12716 response( data ); 12717 }, 12718 error: function() { 12719 response( [] ); 12720 } 12721 }); 12722 }; 12723 } else { 12724 this.source = this.options.source; 12725 } 12726 }, 12727 12728 _searchTimeout: function( event ) { 12729 clearTimeout( this.searching ); 12730 this.searching = this._delay(function() { 12731 // only search if the value has changed 12732 if ( this.term !== this._value() ) { 12733 this.selectedItem = null; 12734 this.search( null, event ); 12735 } 12736 }, this.options.delay ); 12737 }, 12738 12739 search: function( value, event ) { 12740 value = value != null ? value : this._value(); 12741 12742 // always save the actual value, not the one passed as an argument 12743 this.term = this._value(); 12744 12745 if ( value.length < this.options.minLength ) { 12746 return this.close( event ); 12747 } 12748 12749 if ( this._trigger( "search", event ) === false ) { 12750 return; 12751 } 12752 12753 return this._search( value ); 12754 }, 12755 12756 _search: function( value ) { 12757 this.pending++; 12758 this.element.addClass( "ui-autocomplete-loading" ); 12759 this.cancelSearch = false; 12760 12761 this.source( { term: value }, this._response() ); 12762 }, 12763 12764 _response: function() { 12765 var that = this, 12766 index = ++requestIndex; 12767 12768 return function( content ) { 12769 if ( index === requestIndex ) { 12770 that.__response( content ); 12771 } 12772 12773 that.pending--; 12774 if ( !that.pending ) { 12775 that.element.removeClass( "ui-autocomplete-loading" ); 12776 } 12777 }; 12778 }, 12779 12780 __response: function( content ) { 12781 if ( content ) { 12782 content = this._normalize( content ); 12783 } 12784 this._trigger( "response", null, { content: content } ); 12785 if ( !this.options.disabled && content && content.length && !this.cancelSearch ) { 12786 this._suggest( content ); 12787 this._trigger( "open" ); 12788 } else { 12789 // use ._close() instead of .close() so we don't cancel future searches 12790 this._close(); 12791 } 12792 }, 12793 12794 close: function( event ) { 12795 this.cancelSearch = true; 12796 this._close( event ); 12797 }, 12798 12799 _close: function( event ) { 12800 if ( this.menu.element.is( ":visible" ) ) { 12801 this.menu.element.hide(); 12802 this.menu.blur(); 12803 this.isNewMenu = true; 12804 this._trigger( "close", event ); 12805 } 12806 }, 12807 12808 _change: function( event ) { 12809 if ( this.previous !== this._value() ) { 12810 this._trigger( "change", event, { item: this.selectedItem } ); 12811 } 12812 }, 12813 12814 _normalize: function( items ) { 12815 // assume all items have the right format when the first item is complete 12816 if ( items.length && items[0].label && items[0].value ) { 12817 return items; 12818 } 12819 return $.map( items, function( item ) { 12820 if ( typeof item === "string" ) { 12821 return { 12822 label: item, 12823 value: item 12824 }; 12825 } 12826 return $.extend({ 12827 label: item.label || item.value, 12828 value: item.value || item.label 12829 }, item ); 12830 }); 12831 }, 12832 12833 _suggest: function( items ) { 12834 var ul = this.menu.element.empty(); 12835 this._renderMenu( ul, items ); 12836 this.isNewMenu = true; 12837 this.menu.refresh(); 12838 12839 // size and position menu 12840 ul.show(); 12841 this._resizeMenu(); 12842 ul.position( $.extend({ 12843 of: this.element 12844 }, this.options.position )); 12845 12846 if ( this.options.autoFocus ) { 12847 this.menu.next(); 12848 } 12849 }, 12850 12851 _resizeMenu: function() { 12852 var ul = this.menu.element; 12853 ul.outerWidth( Math.max( 12854 // Firefox wraps long text (possibly a rounding bug) 12855 // so we add 1px to avoid the wrapping (#7513) 12856 ul.width( "" ).outerWidth() + 1, 12857 this.element.outerWidth() 12858 ) ); 12859 }, 12860 12861 _renderMenu: function( ul, items ) { 12862 var that = this; 12863 $.each( items, function( index, item ) { 12864 that._renderItemData( ul, item ); 12865 }); 12866 }, 12867 12868 _renderItemData: function( ul, item ) { 12869 return this._renderItem( ul, item ).data( "ui-autocomplete-item", item ); 12870 }, 12871 12872 _renderItem: function( ul, item ) { 12873 return $( "<li>" ) 12874 .append( $( "<a>" ).text( item.label ) ) 12875 .appendTo( ul ); 12876 }, 12877 12878 _move: function( direction, event ) { 12879 if ( !this.menu.element.is( ":visible" ) ) { 12880 this.search( null, event ); 12881 return; 12882 } 12883 if ( this.menu.isFirstItem() && /^previous/.test( direction ) || 12884 this.menu.isLastItem() && /^next/.test( direction ) ) { 12885 this._value( this.term ); 12886 this.menu.blur(); 12887 return; 12888 } 12889 this.menu[ direction ]( event ); 12890 }, 12891 12892 widget: function() { 12893 return this.menu.element; 12894 }, 12895 12896 _value: function() { 12897 return this.valueMethod.apply( this.element, arguments ); 12898 }, 12899 12900 _keyEvent: function( keyEvent, event ) { 12901 if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) { 12902 this._move( keyEvent, event ); 12903 12904 // prevents moving cursor to beginning/end of the text field in some browsers 12905 event.preventDefault(); 12906 } 12907 } 12908}); 12909 12910$.extend( $.ui.autocomplete, { 12911 escapeRegex: function( value ) { 12912 return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&"); 12913 }, 12914 filter: function(array, term) { 12915 var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" ); 12916 return $.grep( array, function(value) { 12917 return matcher.test( value.label || value.value || value ); 12918 }); 12919 } 12920}); 12921 12922 12923// live region extension, adding a `messages` option 12924// NOTE: This is an experimental API. We are still investigating 12925// a full solution for string manipulation and internationalization. 12926$.widget( "ui.autocomplete", $.ui.autocomplete, { 12927 options: { 12928 messages: { 12929 noResults: "No search results.", 12930 results: function( amount ) { 12931 return amount + ( amount > 1 ? " results are" : " result is" ) + 12932 " available, use up and down arrow keys to navigate."; 12933 } 12934 } 12935 }, 12936 12937 __response: function( content ) { 12938 var message; 12939 this._superApply( arguments ); 12940 if ( this.options.disabled || this.cancelSearch ) { 12941 return; 12942 } 12943 if ( content && content.length ) { 12944 message = this.options.messages.results( content.length ); 12945 } else { 12946 message = this.options.messages.noResults; 12947 } 12948 this.liveRegion.text( message ); 12949 } 12950}); 12951 12952}( jQuery )); 12953 12954 12955 12956/*! 12957 * jQuery UI Button 1.10.3 12958 * http://jqueryui.com 12959 * 12960 * Copyright 2013 jQuery Foundation and other contributors 12961 * Released under the MIT license. 12962 * http://jquery.org/license 12963 * 12964 * http://api.jqueryui.com/button/ 12965 * 12966 * Depends: 12967 * jquery.ui.core.js 12968 * jquery.ui.widget.js 12969 */ 12970 12971(function( $, undefined ) { 12972 12973var lastActive, startXPos, startYPos, clickDragged, 12974 baseClasses = "ui-button ui-widget ui-state-default ui-corner-all", 12975 stateClasses = "ui-state-hover ui-state-active ", 12976 typeClasses = "ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only", 12977 formResetHandler = function() { 12978 var form = $( this ); 12979 setTimeout(function() { 12980 form.find( ":ui-button" ).button( "refresh" ); 12981 }, 1 ); 12982 }, 12983 radioGroup = function( radio ) { 12984 var name = radio.name, 12985 form = radio.form, 12986 radios = $( [] ); 12987 if ( name ) { 12988 name = name.replace( /'/g, "\\'" ); 12989 if ( form ) { 12990 radios = $( form ).find( "[name='" + name + "']" ); 12991 } else { 12992 radios = $( "[name='" + name + "']", radio.ownerDocument ) 12993 .filter(function() { 12994 return !this.form; 12995 }); 12996 } 12997 } 12998 return radios; 12999 }; 13000 13001$.widget( "ui.button", { 13002 version: "1.10.3", 13003 defaultElement: "<button>", 13004 options: { 13005 disabled: null, 13006 text: true, 13007 label: null, 13008 icons: { 13009 primary: null, 13010 secondary: null 13011 } 13012 }, 13013 _create: function() { 13014 this.element.closest( "form" ) 13015 .unbind( "reset" + this.eventNamespace ) 13016 .bind( "reset" + this.eventNamespace, formResetHandler ); 13017 13018 if ( typeof this.options.disabled !== "boolean" ) { 13019 this.options.disabled = !!this.element.prop( "disabled" ); 13020 } else { 13021 this.element.prop( "disabled", this.options.disabled ); 13022 } 13023 13024 this._determineButtonType(); 13025 this.hasTitle = !!this.buttonElement.attr( "title" ); 13026 13027 var that = this, 13028 options = this.options, 13029 toggleButton = this.type === "checkbox" || this.type === "radio", 13030 activeClass = !toggleButton ? "ui-state-active" : "", 13031 focusClass = "ui-state-focus"; 13032 13033 if ( options.label === null ) { 13034 options.label = (this.type === "input" ? this.buttonElement.val() : this.buttonElement.html()); 13035 } 13036 13037 this._hoverable( this.buttonElement ); 13038 13039 this.buttonElement 13040 .addClass( baseClasses ) 13041 .attr( "role", "button" ) 13042 .bind( "mouseenter" + this.eventNamespace, function() { 13043 if ( options.disabled ) { 13044 return; 13045 } 13046 if ( this === lastActive ) { 13047 $( this ).addClass( "ui-state-active" ); 13048 } 13049 }) 13050 .bind( "mouseleave" + this.eventNamespace, function() { 13051 if ( options.disabled ) { 13052 return; 13053 } 13054 $( this ).removeClass( activeClass ); 13055 }) 13056 .bind( "click" + this.eventNamespace, function( event ) { 13057 if ( options.disabled ) { 13058 event.preventDefault(); 13059 event.stopImmediatePropagation(); 13060 } 13061 }); 13062 13063 this.element 13064 .bind( "focus" + this.eventNamespace, function() { 13065 // no need to check disabled, focus won't be triggered anyway 13066 that.buttonElement.addClass( focusClass ); 13067 }) 13068 .bind( "blur" + this.eventNamespace, function() { 13069 that.buttonElement.removeClass( focusClass ); 13070 }); 13071 13072 if ( toggleButton ) { 13073 this.element.bind( "change" + this.eventNamespace, function() { 13074 if ( clickDragged ) { 13075 return; 13076 } 13077 that.refresh(); 13078 }); 13079 // if mouse moves between mousedown and mouseup (drag) set clickDragged flag 13080 // prevents issue where button state changes but checkbox/radio checked state 13081 // does not in Firefox (see ticket #6970) 13082 this.buttonElement 13083 .bind( "mousedown" + this.eventNamespace, function( event ) { 13084 if ( options.disabled ) { 13085 return; 13086 } 13087 clickDragged = false; 13088 startXPos = event.pageX; 13089 startYPos = event.pageY; 13090 }) 13091 .bind( "mouseup" + this.eventNamespace, function( event ) { 13092 if ( options.disabled ) { 13093 return; 13094 } 13095 if ( startXPos !== event.pageX || startYPos !== event.pageY ) { 13096 clickDragged = true; 13097 } 13098 }); 13099 } 13100 13101 if ( this.type === "checkbox" ) { 13102 this.buttonElement.bind( "click" + this.eventNamespace, function() { 13103 if ( options.disabled || clickDragged ) { 13104 return false; 13105 } 13106 }); 13107 } else if ( this.type === "radio" ) { 13108 this.buttonElement.bind( "click" + this.eventNamespace, function() { 13109 if ( options.disabled || clickDragged ) { 13110 return false; 13111 } 13112 $( this ).addClass( "ui-state-active" ); 13113 that.buttonElement.attr( "aria-pressed", "true" ); 13114 13115 var radio = that.element[ 0 ]; 13116 radioGroup( radio ) 13117 .not( radio ) 13118 .map(function() { 13119 return $( this ).button( "widget" )[ 0 ]; 13120 }) 13121 .removeClass( "ui-state-active" ) 13122 .attr( "aria-pressed", "false" ); 13123 }); 13124 } else { 13125 this.buttonElement 13126 .bind( "mousedown" + this.eventNamespace, function() { 13127 if ( options.disabled ) { 13128 return false; 13129 } 13130 $( this ).addClass( "ui-state-active" ); 13131 lastActive = this; 13132 that.document.one( "mouseup", function() { 13133 lastActive = null; 13134 }); 13135 }) 13136 .bind( "mouseup" + this.eventNamespace, function() { 13137 if ( options.disabled ) { 13138 return false; 13139 } 13140 $( this ).removeClass( "ui-state-active" ); 13141 }) 13142 .bind( "keydown" + this.eventNamespace, function(event) { 13143 if ( options.disabled ) { 13144 return false; 13145 } 13146 if ( event.keyCode === $.ui.keyCode.SPACE || event.keyCode === $.ui.keyCode.ENTER ) { 13147 $( this ).addClass( "ui-state-active" ); 13148 } 13149 }) 13150 // see #8559, we bind to blur here in case the button element loses 13151 // focus between keydown and keyup, it would be left in an "active" state 13152 .bind( "keyup" + this.eventNamespace + " blur" + this.eventNamespace, function() { 13153 $( this ).removeClass( "ui-state-active" ); 13154 }); 13155 13156 if ( this.buttonElement.is("a") ) { 13157 this.buttonElement.keyup(function(event) { 13158 if ( event.keyCode === $.ui.keyCode.SPACE ) { 13159 // TODO pass through original event correctly (just as 2nd argument doesn't work) 13160 $( this ).click(); 13161 } 13162 }); 13163 } 13164 } 13165 13166 // TODO: pull out $.Widget's handling for the disabled option into 13167 // $.Widget.prototype._setOptionDisabled so it's easy to proxy and can 13168 // be overridden by individual plugins 13169 this._setOption( "disabled", options.disabled ); 13170 this._resetButton(); 13171 }, 13172 13173 _determineButtonType: function() { 13174 var ancestor, labelSelector, checked; 13175 13176 if ( this.element.is("[type=checkbox]") ) { 13177 this.type = "checkbox"; 13178 } else if ( this.element.is("[type=radio]") ) { 13179 this.type = "radio"; 13180 } else if ( this.element.is("input") ) { 13181 this.type = "input"; 13182 } else { 13183 this.type = "button"; 13184 } 13185 13186 if ( this.type === "checkbox" || this.type === "radio" ) { 13187 // we don't search against the document in case the element 13188 // is disconnected from the DOM 13189 ancestor = this.element.parents().last(); 13190 labelSelector = "label[for='" + this.element.attr("id") + "']"; 13191 this.buttonElement = ancestor.find( labelSelector ); 13192 if ( !this.buttonElement.length ) { 13193 ancestor = ancestor.length ? ancestor.siblings() : this.element.siblings(); 13194 this.buttonElement = ancestor.filter( labelSelector ); 13195 if ( !this.buttonElement.length ) { 13196 this.buttonElement = ancestor.find( labelSelector ); 13197 } 13198 } 13199 this.element.addClass( "ui-helper-hidden-accessible" ); 13200 13201 checked = this.element.is( ":checked" ); 13202 if ( checked ) { 13203 this.buttonElement.addClass( "ui-state-active" ); 13204 } 13205 this.buttonElement.prop( "aria-pressed", checked ); 13206 } else { 13207 this.buttonElement = this.element; 13208 } 13209 }, 13210 13211 widget: function() { 13212 return this.buttonElement; 13213 }, 13214 13215 _destroy: function() { 13216 this.element 13217 .removeClass( "ui-helper-hidden-accessible" ); 13218 this.buttonElement 13219 .removeClass( baseClasses + " " + stateClasses + " " + typeClasses ) 13220 .removeAttr( "role" ) 13221 .removeAttr( "aria-pressed" ) 13222 .html( this.buttonElement.find(".ui-button-text").html() ); 13223 13224 if ( !this.hasTitle ) {
vendor: 4,220 bytes, lines 13225-13390
13225 this.buttonElement.removeAttr( "title" ); 13226 } 13227 }, 13228 13229 _setOption: function( key, value ) { 13230 this._super( key, value ); 13231 if ( key === "disabled" ) { 13232 if ( value ) { 13233 this.element.prop( "disabled", true ); 13234 } else { 13235 this.element.prop( "disabled", false ); 13236 } 13237 return; 13238 } 13239 this._resetButton(); 13240 }, 13241 13242 refresh: function() { 13243 //See #8237 & #8828 13244 var isDisabled = this.element.is( "input, button" ) ? this.element.is( ":disabled" ) : this.element.hasClass( "ui-button-disabled" ); 13245 13246 if ( isDisabled !== this.options.disabled ) { 13247 this._setOption( "disabled", isDisabled ); 13248 } 13249 if ( this.type === "radio" ) { 13250 radioGroup( this.element[0] ).each(function() { 13251 if ( $( this ).is( ":checked" ) ) { 13252 $( this ).button( "widget" ) 13253 .addClass( "ui-state-active" ) 13254 .attr( "aria-pressed", "true" ); 13255 } else { 13256 $( this ).button( "widget" ) 13257 .removeClass( "ui-state-active" ) 13258 .attr( "aria-pressed", "false" ); 13259 } 13260 }); 13261 } else if ( this.type === "checkbox" ) { 13262 if ( this.element.is( ":checked" ) ) { 13263 this.buttonElement 13264 .addClass( "ui-state-active" ) 13265 .attr( "aria-pressed", "true" ); 13266 } else { 13267 this.buttonElement 13268 .removeClass( "ui-state-active" ) 13269 .attr( "aria-pressed", "false" ); 13270 } 13271 } 13272 }, 13273 13274 _resetButton: function() { 13275 if ( this.type === "input" ) { 13276 if ( this.options.label ) { 13277 this.element.val( this.options.label ); 13278 } 13279 return; 13280 } 13281 var buttonElement = this.buttonElement.removeClass( typeClasses ), 13282 buttonText = $( "<span></span>", this.document[0] ) 13283 .addClass( "ui-button-text" ) 13284 .html( this.options.label ) 13285 .appendTo( buttonElement.empty() ) 13286 .text(), 13287 icons = this.options.icons, 13288 multipleIcons = icons.primary && icons.secondary, 13289 buttonClasses = []; 13290 13291 if ( icons.primary || icons.secondary ) { 13292 if ( this.options.text ) { 13293 buttonClasses.push( "ui-button-text-icon" + ( multipleIcons ? "s" : ( icons.primary ? "-primary" : "-secondary" ) ) ); 13294 } 13295 13296 if ( icons.primary ) { 13297 buttonElement.prepend( "<span class='ui-button-icon-primary ui-icon " + icons.primary + "'></span>" ); 13298 } 13299 13300 if ( icons.secondary ) { 13301 buttonElement.append( "<span class='ui-button-icon-secondary ui-icon " + icons.secondary + "'></span>" ); 13302 } 13303 13304 if ( !this.options.text ) { 13305 buttonClasses.push( multipleIcons ? "ui-button-icons-only" : "ui-button-icon-only" ); 13306 13307 if ( !this.hasTitle ) { 13308 buttonElement.attr( "title", $.trim( buttonText ) ); 13309 } 13310 } 13311 } else { 13312 buttonClasses.push( "ui-button-text-only" ); 13313 } 13314 buttonElement.addClass( buttonClasses.join( " " ) ); 13315 } 13316}); 13317 13318$.widget( "ui.buttonset", { 13319 version: "1.10.3", 13320 options: { 13321 items: "button, input[type=button], input[type=submit], input[type=reset], input[type=checkbox], input[type=radio], a, :data(ui-button)" 13322 }, 13323 13324 _create: function() { 13325 this.element.addClass( "ui-buttonset" ); 13326 }, 13327 13328 _init: function() { 13329 this.refresh(); 13330 }, 13331 13332 _setOption: function( key, value ) { 13333 if ( key === "disabled" ) { 13334 this.buttons.button( "option", key, value ); 13335 } 13336 13337 this._super( key, value ); 13338 }, 13339 13340 refresh: function() { 13341 var rtl = this.element.css( "direction" ) === "rtl"; 13342 13343 this.buttons = this.element.find( this.options.items ) 13344 .filter( ":ui-button" ) 13345 .button( "refresh" ) 13346 .end() 13347 .not( ":ui-button" ) 13348 .button() 13349 .end() 13350 .map(function() { 13351 return $( this ).button( "widget" )[ 0 ]; 13352 }) 13353 .removeClass( "ui-corner-all ui-corner-left ui-corner-right" ) 13354 .filter( ":first" ) 13355 .addClass( rtl ? "ui-corner-right" : "ui-corner-left" ) 13356 .end() 13357 .filter( ":last" ) 13358 .addClass( rtl ? "ui-corner-left" : "ui-corner-right" ) 13359 .end() 13360 .end(); 13361 }, 13362 13363 _destroy: function() { 13364 this.element.removeClass( "ui-buttonset" ); 13365 this.buttons 13366 .map(function() { 13367 return $( this ).button( "widget" )[ 0 ]; 13368 }) 13369 .removeClass( "ui-corner-left ui-corner-right" ) 13370 .end() 13371 .button( "destroy" ); 13372 } 13373}); 13374 13375}( jQuery ) ); 13376 13377 13378/*! 13379 * jQuery UI Datepicker 1.10.3 13380 * http://jqueryui.com 13381 * 13382 * Copyright 2013 jQuery Foundation and other contributors 13383 * Released under the MIT license. 13384 * http://jquery.org/license 13385 * 13386 * http://api.jqueryui.com/datepicker/ 13387 * 13388 * Depends: 13389 * jquery.ui.core.js 13390 */
vendor: 76,059 bytes, lines 13391-15416
13391 13392(function( $, undefined ) { 13393 13394$.extend($.ui, { datepicker: { version: "1.10.3" } }); 13395 13396var PROP_NAME = "datepicker", 13397 instActive; 13398 13399/* Date picker manager. 13400 Use the singleton instance of this class, $.datepicker, to interact with the date picker. 13401 Settings for (groups of) date pickers are maintained in an instance object, 13402 allowing multiple different settings on the same page. */ 13403 13404function Datepicker() { 13405 this._curInst = null; // The current instance in use 13406 this._keyEvent = false; // If the last event was a key event 13407 this._disabledInputs = []; // List of date picker inputs that have been disabled 13408 this._datepickerShowing = false; // True if the popup picker is showing , false if not 13409 this._inDialog = false; // True if showing within a "dialog", false if not 13410 this._mainDivId = "ui-datepicker-div"; // The ID of the main datepicker division 13411 this._inlineClass = "ui-datepicker-inline"; // The name of the inline marker class 13412 this._appendClass = "ui-datepicker-append"; // The name of the append marker class 13413 this._triggerClass = "ui-datepicker-trigger"; // The name of the trigger marker class 13414 this._dialogClass = "ui-datepicker-dialog"; // The name of the dialog marker class 13415 this._disableClass = "ui-datepicker-disabled"; // The name of the disabled covering marker class 13416 this._unselectableClass = "ui-datepicker-unselectable"; // The name of the unselectable cell marker class 13417 this._currentClass = "ui-datepicker-current-day"; // The name of the current day marker class 13418 this._dayOverClass = "ui-datepicker-days-cell-over"; // The name of the day hover marker class 13419 this.regional = []; // Available regional settings, indexed by language code 13420 this.regional[""] = { // Default regional settings 13421 closeText: "Done", // Display text for close link 13422 prevText: "Prev", // Display text for previous month link 13423 nextText: "Next", // Display text for next month link 13424 currentText: "Today", // Display text for current month link 13425 monthNames: ["January","February","March","April","May","June", 13426 "July","August","September","October","November","December"], // Names of months for drop-down and formatting 13427 monthNamesShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], // For formatting 13428 dayNames: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], // For formatting 13429 dayNamesShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], // For formatting 13430 dayNamesMin: ["Su","Mo","Tu","We","Th","Fr","Sa"], // Column headings for days starting at Sunday 13431 weekHeader: "Wk", // Column header for week of the year 13432 dateFormat: "mm/dd/yy", // See format options on parseDate 13433 firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ... 13434 isRTL: false, // True if right-to-left language, false if left-to-right 13435 showMonthAfterYear: false, // True if the year select precedes month, false for month then year 13436 yearSuffix: "" // Additional text to append to the year in the month headers 13437 }; 13438 this._defaults = { // Global defaults for all the date picker instances 13439 showOn: "focus", // "focus" for popup on focus, 13440 // "button" for trigger button, or "both" for either 13441 showAnim: "fadeIn", // Name of jQuery animation for popup 13442 showOptions: {}, // Options for enhanced animations 13443 defaultDate: null, // Used when field is blank: actual date, 13444 // +/-number for offset from today, null for today 13445 appendText: "", // Display text following the input box, e.g. showing the format 13446 buttonText: "...", // Text for trigger button 13447 buttonImage: "", // URL for trigger button image 13448 buttonImageOnly: false, // True if the image appears alone, false if it appears on a button 13449 hideIfNoPrevNext: false, // True to hide next/previous month links 13450 // if not applicable, false to just disable them 13451 navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links 13452 gotoCurrent: false, // True if today link goes back to current selection instead 13453 changeMonth: false, // True if month can be selected directly, false if only prev/next 13454 changeYear: false, // True if year can be selected directly, false if only prev/next 13455 yearRange: "c-10:c+10", // Range of years to display in drop-down, 13456 // either relative to today's year (-nn:+nn), relative to currently displayed year 13457 // (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n) 13458 showOtherMonths: false, // True to show dates in other months, false to leave blank 13459 selectOtherMonths: false, // True to allow selection of dates in other months, false for unselectable 13460 showWeek: false, // True to show week of the year, false to not show it 13461 calculateWeek: this.iso8601Week, // How to calculate the week of the year, 13462 // takes a Date and returns the number of the week for it 13463 shortYearCutoff: "+10", // Short year values < this are in the current century, 13464 // > this are in the previous century, 13465 // string value starting with "+" for current year + value 13466 minDate: null, // The earliest selectable date, or null for no limit 13467 maxDate: null, // The latest selectable date, or null for no limit 13468 duration: "fast", // Duration of display/closure 13469 beforeShowDay: null, // Function that takes a date and returns an array with 13470 // [0] = true if selectable, false if not, [1] = custom CSS class name(s) or "", 13471 // [2] = cell title (optional), e.g. $.datepicker.noWeekends 13472 beforeShow: null, // Function that takes an input field and 13473 // returns a set of custom settings for the date picker 13474 onSelect: null, // Define a callback function when a date is selected 13475 onChangeMonthYear: null, // Define a callback function when the month or year is changed 13476 onClose: null, // Define a callback function when the datepicker is closed 13477 numberOfMonths: 1, // Number of months to show at a time 13478 showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0) 13479 stepMonths: 1, // Number of months to step back/forward 13480 stepBigMonths: 12, // Number of months to step back/forward for the big links 13481 altField: "", // Selector for an alternate field to store selected dates into 13482 altFormat: "", // The date format to use for the alternate field 13483 constrainInput: true, // The input is constrained by the current date format 13484 showButtonPanel: false, // True to show button panel, false to not show it 13485 autoSize: false, // True to size the input for the date format, false to leave as is 13486 disabled: false // The initial disabled state 13487 }; 13488 $.extend(this._defaults, this.regional[""]); 13489 this.dpDiv = bindHover($("<div id='" + this._mainDivId + "' class='ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>")); 13490} 13491 13492$.extend(Datepicker.prototype, { 13493 /* Class name added to elements to indicate already configured with a date picker. */ 13494 markerClassName: "hasDatepicker", 13495 13496 //Keep track of the maximum number of rows displayed (see #7043) 13497 maxRows: 4, 13498 13499 // TODO rename to "widget" when switching to widget factory 13500 _widgetDatepicker: function() { 13501 return this.dpDiv; 13502 }, 13503 13504 /* Override the default settings for all instances of the date picker. 13505 * @param settings object - the new settings to use as defaults (anonymous object) 13506 * @return the manager object 13507 */ 13508 setDefaults: function(settings) { 13509 extendRemove(this._defaults, settings || {}); 13510 return this; 13511 }, 13512 13513 /* Attach the date picker to a jQuery selection. 13514 * @param target element - the target input field or division or span 13515 * @param settings object - the new settings to use for this date picker instance (anonymous) 13516 */ 13517 _attachDatepicker: function(target, settings) { 13518 var nodeName, inline, inst; 13519 nodeName = target.nodeName.toLowerCase(); 13520 inline = (nodeName === "div" || nodeName === "span"); 13521 if (!target.id) { 13522 this.uuid += 1; 13523 target.id = "dp" + this.uuid; 13524 } 13525 inst = this._newInst($(target), inline); 13526 inst.settings = $.extend({}, settings || {}); 13527 if (nodeName === "input") { 13528 this._connectDatepicker(target, inst); 13529 } else if (inline) { 13530 this._inlineDatepicker(target, inst); 13531 } 13532 }, 13533 13534 /* Create a new instance object. */ 13535 _newInst: function(target, inline) { 13536 var id = target[0].id.replace(/([^A-Za-z0-9_\-])/g, "\\\\$1"); // escape jQuery meta chars 13537 return {id: id, input: target, // associated target 13538 selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection 13539 drawMonth: 0, drawYear: 0, // month being drawn 13540 inline: inline, // is datepicker inline or not 13541 dpDiv: (!inline ? this.dpDiv : // presentation div 13542 bindHover($("<div class='" + this._inlineClass + " ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>")))}; 13543 }, 13544 13545 /* Attach the date picker to an input field. */ 13546 _connectDatepicker: function(target, inst) { 13547 var input = $(target); 13548 inst.append = $([]); 13549 inst.trigger = $([]); 13550 if (input.hasClass(this.markerClassName)) { 13551 return; 13552 } 13553 this._attachments(input, inst); 13554 input.addClass(this.markerClassName).keydown(this._doKeyDown). 13555 keypress(this._doKeyPress).keyup(this._doKeyUp); 13556 this._autoSize(inst); 13557 $.data(target, PROP_NAME, inst); 13558 //If disabled option is true, disable the datepicker once it has been attached to the input (see ticket #5665) 13559 if( inst.settings.disabled ) { 13560 this._disableDatepicker( target ); 13561 } 13562 }, 13563 13564 /* Make attachments based on settings. */ 13565 _attachments: function(input, inst) { 13566 var showOn, buttonText, buttonImage, 13567 appendText = this._get(inst, "appendText"), 13568 isRTL = this._get(inst, "isRTL"); 13569 13570 if (inst.append) { 13571 inst.append.remove(); 13572 } 13573 if (appendText) { 13574 inst.append = $("<span class='" + this._appendClass + "'>" + appendText + "</span>"); 13575 input[isRTL ? "before" : "after"](inst.append); 13576 } 13577 13578 input.unbind("focus", this._showDatepicker); 13579 13580 if (inst.trigger) { 13581 inst.trigger.remove(); 13582 } 13583 13584 showOn = this._get(inst, "showOn"); 13585 if (showOn === "focus" || showOn === "both") { // pop-up date picker when in the marked field 13586 input.focus(this._showDatepicker); 13587 } 13588 if (showOn === "button" || showOn === "both") { // pop-up date picker when button clicked 13589 buttonText = this._get(inst, "buttonText"); 13590 buttonImage = this._get(inst, "buttonImage"); 13591 inst.trigger = $(this._get(inst, "buttonImageOnly") ? 13592 $("<img/>").addClass(this._triggerClass). 13593 attr({ src: buttonImage, alt: buttonText, title: buttonText }) : 13594 $("<button type='button'></button>").addClass(this._triggerClass). 13595 html(!buttonImage ? buttonText : $("<img/>").attr( 13596 { src:buttonImage, alt:buttonText, title:buttonText }))); 13597 input[isRTL ? "before" : "after"](inst.trigger); 13598 inst.trigger.click(function() { 13599 if ($.datepicker._datepickerShowing && $.datepicker._lastInput === input[0]) { 13600 $.datepicker._hideDatepicker(); 13601 } else if ($.datepicker._datepickerShowing && $.datepicker._lastInput !== input[0]) { 13602 $.datepicker._hideDatepicker(); 13603 $.datepicker._showDatepicker(input[0]); 13604 } else { 13605 $.datepicker._showDatepicker(input[0]); 13606 } 13607 return false; 13608 }); 13609 } 13610 }, 13611 13612 /* Apply the maximum length for the date format. */ 13613 _autoSize: function(inst) { 13614 if (this._get(inst, "autoSize") && !inst.inline) { 13615 var findMax, max, maxI, i, 13616 date = new Date(2009, 12 - 1, 20), // Ensure double digits 13617 dateFormat = this._get(inst, "dateFormat"); 13618 13619 if (dateFormat.match(/[DM]/)) { 13620 findMax = function(names) { 13621 max = 0; 13622 maxI = 0; 13623 for (i = 0; i < names.length; i++) { 13624 if (names[i].length > max) { 13625 max = names[i].length; 13626 maxI = i; 13627 } 13628 } 13629 return maxI; 13630 }; 13631 date.setMonth(findMax(this._get(inst, (dateFormat.match(/MM/) ? 13632 "monthNames" : "monthNamesShort")))); 13633 date.setDate(findMax(this._get(inst, (dateFormat.match(/DD/) ? 13634 "dayNames" : "dayNamesShort"))) + 20 - date.getDay()); 13635 } 13636 inst.input.attr("size", this._formatDate(inst, date).length); 13637 } 13638 }, 13639 13640 /* Attach an inline date picker to a div. */ 13641 _inlineDatepicker: function(target, inst) { 13642 var divSpan = $(target); 13643 if (divSpan.hasClass(this.markerClassName)) { 13644 return; 13645 } 13646 divSpan.addClass(this.markerClassName).append(inst.dpDiv); 13647 $.data(target, PROP_NAME, inst); 13648 this._setDate(inst, this._getDefaultDate(inst), true); 13649 this._updateDatepicker(inst); 13650 this._updateAlternate(inst); 13651 //If disabled option is true, disable the datepicker before showing it (see ticket #5665) 13652 if( inst.settings.disabled ) { 13653 this._disableDatepicker( target ); 13654 } 13655 // Set display:block in place of inst.dpDiv.show() which won't work on disconnected elements 13656 // http://bugs.jqueryui.com/ticket/7552 - A Datepicker created on a detached div has zero height 13657 inst.dpDiv.css( "display", "block" ); 13658 }, 13659 13660 /* Pop-up the date picker in a "dialog" box. 13661 * @param input element - ignored 13662 * @param date string or Date - the initial date to display 13663 * @param onSelect function - the function to call when a date is selected 13664 * @param settings object - update the dialog date picker instance's settings (anonymous object) 13665 * @param pos int[2] - coordinates for the dialog's position within the screen or 13666 * event - with x/y coordinates or 13667 * leave empty for default (screen centre) 13668 * @return the manager object 13669 */ 13670 _dialogDatepicker: function(input, date, onSelect, settings, pos) { 13671 var id, browserWidth, browserHeight, scrollX, scrollY, 13672 inst = this._dialogInst; // internal instance 13673 13674 if (!inst) { 13675 this.uuid += 1; 13676 id = "dp" + this.uuid; 13677 this._dialogInput = $("<input type='text' id='" + id + 13678 "' style='position: absolute; top: -100px; width: 0px;'/>"); 13679 this._dialogInput.keydown(this._doKeyDown); 13680 $("body").append(this._dialogInput); 13681 inst = this._dialogInst = this._newInst(this._dialogInput, false); 13682 inst.settings = {}; 13683 $.data(this._dialogInput[0], PROP_NAME, inst); 13684 } 13685 extendRemove(inst.settings, settings || {}); 13686 date = (date && date.constructor === Date ? this._formatDate(inst, date) : date); 13687 this._dialogInput.val(date); 13688 13689 this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null); 13690 if (!this._pos) { 13691 browserWidth = document.documentElement.clientWidth; 13692 browserHeight = document.documentElement.clientHeight; 13693 scrollX = document.documentElement.scrollLeft || document.body.scrollLeft; 13694 scrollY = document.documentElement.scrollTop || document.body.scrollTop; 13695 this._pos = // should use actual width/height below 13696 [(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY]; 13697 } 13698 13699 // move input on screen for focus, but hidden behind dialog 13700 this._dialogInput.css("left", (this._pos[0] + 20) + "px").css("top", this._pos[1] + "px"); 13701 inst.settings.onSelect = onSelect; 13702 this._inDialog = true; 13703 this.dpDiv.addClass(this._dialogClass); 13704 this._showDatepicker(this._dialogInput[0]); 13705 if ($.blockUI) { 13706 $.blockUI(this.dpDiv); 13707 } 13708 $.data(this._dialogInput[0], PROP_NAME, inst); 13709 return this; 13710 }, 13711 13712 /* Detach a datepicker from its control. 13713 * @param target element - the target input field or division or span 13714 */ 13715 _destroyDatepicker: function(target) { 13716 var nodeName, 13717 $target = $(target), 13718 inst = $.data(target, PROP_NAME); 13719 13720 if (!$target.hasClass(this.markerClassName)) { 13721 return; 13722 } 13723 13724 nodeName = target.nodeName.toLowerCase(); 13725 $.removeData(target, PROP_NAME); 13726 if (nodeName === "input") { 13727 inst.append.remove(); 13728 inst.trigger.remove(); 13729 $target.removeClass(this.markerClassName). 13730 unbind("focus", this._showDatepicker). 13731 unbind("keydown", this._doKeyDown). 13732 unbind("keypress", this._doKeyPress). 13733 unbind("keyup", this._doKeyUp); 13734 } else if (nodeName === "div" || nodeName === "span") { 13735 $target.removeClass(this.markerClassName).empty(); 13736 } 13737 }, 13738 13739 /* Enable the date picker to a jQuery selection. 13740 * @param target element - the target input field or division or span 13741 */ 13742 _enableDatepicker: function(target) { 13743 var nodeName, inline, 13744 $target = $(target), 13745 inst = $.data(target, PROP_NAME); 13746 13747 if (!$target.hasClass(this.markerClassName)) { 13748 return; 13749 } 13750 13751 nodeName = target.nodeName.toLowerCase(); 13752 if (nodeName === "input") { 13753 target.disabled = false; 13754 inst.trigger.filter("button"). 13755 each(function() { this.disabled = false; }).end(). 13756 filter("img").css({opacity: "1.0", cursor: ""}); 13757 } else if (nodeName === "div" || nodeName === "span") { 13758 inline = $target.children("." + this._inlineClass); 13759 inline.children().removeClass("ui-state-disabled"); 13760 inline.find("select.ui-datepicker-month, select.ui-datepicker-year"). 13761 prop("disabled", false); 13762 } 13763 this._disabledInputs = $.map(this._disabledInputs, 13764 function(value) { return (value === target ? null : value); }); // delete entry 13765 }, 13766 13767 /* Disable the date picker to a jQuery selection. 13768 * @param target element - the target input field or division or span 13769 */ 13770 _disableDatepicker: function(target) { 13771 var nodeName, inline, 13772 $target = $(target), 13773 inst = $.data(target, PROP_NAME); 13774 13775 if (!$target.hasClass(this.markerClassName)) { 13776 return; 13777 } 13778 13779 nodeName = target.nodeName.toLowerCase(); 13780 if (nodeName === "input") { 13781 target.disabled = true; 13782 inst.trigger.filter("button"). 13783 each(function() { this.disabled = true; }).end(). 13784 filter("img").css({opacity: "0.5", cursor: "default"}); 13785 } else if (nodeName === "div" || nodeName === "span") { 13786 inline = $target.children("." + this._inlineClass); 13787 inline.children().addClass("ui-state-disabled"); 13788 inline.find("select.ui-datepicker-month, select.ui-datepicker-year"). 13789 prop("disabled", true); 13790 } 13791 this._disabledInputs = $.map(this._disabledInputs, 13792 function(value) { return (value === target ? null : value); }); // delete entry 13793 this._disabledInputs[this._disabledInputs.length] = target; 13794 }, 13795 13796 /* Is the first field in a jQuery collection disabled as a datepicker? 13797 * @param target element - the target input field or division or span 13798 * @return boolean - true if disabled, false if enabled 13799 */ 13800 _isDisabledDatepicker: function(target) { 13801 if (!target) { 13802 return false; 13803 } 13804 for (var i = 0; i < this._disabledInputs.length; i++) { 13805 if (this._disabledInputs[i] === target) { 13806 return true; 13807 } 13808 } 13809 return false; 13810 }, 13811 13812 /* Retrieve the instance data for the target control. 13813 * @param target element - the target input field or division or span 13814 * @return object - the associated instance data 13815 * @throws error if a jQuery problem getting data 13816 */ 13817 _getInst: function(target) { 13818 try { 13819 return $.data(target, PROP_NAME); 13820 } 13821 catch (err) { 13822 throw "Missing instance data for this datepicker"; 13823 } 13824 }, 13825 13826 /* Update or retrieve the settings for a date picker attached to an input field or division. 13827 * @param target element - the target input field or division or span 13828 * @param name object - the new settings to update or 13829 * string - the name of the setting to change or retrieve, 13830 * when retrieving also "all" for all instance settings or 13831 * "defaults" for all global defaults 13832 * @param value any - the new value for the setting 13833 * (omit if above is an object or to retrieve a value) 13834 */ 13835 _optionDatepicker: function(target, name, value) { 13836 var settings, date, minDate, maxDate, 13837 inst = this._getInst(target); 13838 13839 if (arguments.length === 2 && typeof name === "string") { 13840 return (name === "defaults" ? $.extend({}, $.datepicker._defaults) : 13841 (inst ? (name === "all" ? $.extend({}, inst.settings) : 13842 this._get(inst, name)) : null)); 13843 } 13844 13845 settings = name || {}; 13846 if (typeof name === "string") { 13847 settings = {}; 13848 settings[name] = value; 13849 } 13850 13851 if (inst) { 13852 if (this._curInst === inst) { 13853 this._hideDatepicker(); 13854 } 13855 13856 date = this._getDateDatepicker(target, true); 13857 minDate = this._getMinMaxDate(inst, "min"); 13858 maxDate = this._getMinMaxDate(inst, "max"); 13859 extendRemove(inst.settings, settings); 13860 // reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided 13861 if (minDate !== null && settings.dateFormat !== undefined && settings.minDate === undefined) { 13862 inst.settings.minDate = this._formatDate(inst, minDate); 13863 } 13864 if (maxDate !== null && settings.dateFormat !== undefined && settings.maxDate === undefined) { 13865 inst.settings.maxDate = this._formatDate(inst, maxDate); 13866 } 13867 if ( "disabled" in settings ) { 13868 if ( settings.disabled ) { 13869 this._disableDatepicker(target); 13870 } else { 13871 this._enableDatepicker(target); 13872 } 13873 } 13874 this._attachments($(target), inst); 13875 this._autoSize(inst); 13876 this._setDate(inst, date); 13877 this._updateAlternate(inst); 13878 this._updateDatepicker(inst); 13879 } 13880 }, 13881 13882 // change method deprecated 13883 _changeDatepicker: function(target, name, value) { 13884 this._optionDatepicker(target, name, value); 13885 }, 13886 13887 /* Redraw the date picker attached to an input field or division. 13888 * @param target element - the target input field or division or span 13889 */ 13890 _refreshDatepicker: function(target) { 13891 var inst = this._getInst(target); 13892 if (inst) { 13893 this._updateDatepicker(inst); 13894 } 13895 }, 13896 13897 /* Set the dates for a jQuery selection. 13898 * @param target element - the target input field or division or span 13899 * @param date Date - the new date 13900 */ 13901 _setDateDatepicker: function(target, date) { 13902 var inst = this._getInst(target); 13903 if (inst) { 13904 this._setDate(inst, date); 13905 this._updateDatepicker(inst); 13906 this._updateAlternate(inst); 13907 } 13908 }, 13909 13910 /* Get the date(s) for the first entry in a jQuery selection. 13911 * @param target element - the target input field or division or span 13912 * @param noDefault boolean - true if no default date is to be used 13913 * @return Date - the current date 13914 */ 13915 _getDateDatepicker: function(target, noDefault) { 13916 var inst = this._getInst(target); 13917 if (inst && !inst.inline) { 13918 this._setDateFromField(inst, noDefault); 13919 } 13920 return (inst ? this._getDate(inst) : null); 13921 }, 13922 13923 /* Handle keystrokes. */ 13924 _doKeyDown: function(event) { 13925 var onSelect, dateStr, sel, 13926 inst = $.datepicker._getInst(event.target), 13927 handled = true, 13928 isRTL = inst.dpDiv.is(".ui-datepicker-rtl"); 13929 13930 inst._keyEvent = true; 13931 if ($.datepicker._datepickerShowing) { 13932 switch (event.keyCode) { 13933 case 9: $.datepicker._hideDatepicker(); 13934 handled = false; 13935 break; // hide on tab out 13936 case 13: sel = $("td." + $.datepicker._dayOverClass + ":not(." + 13937 $.datepicker._currentClass + ")", inst.dpDiv); 13938 if (sel[0]) { 13939 $.datepicker._selectDay(event.target, inst.selectedMonth, inst.selectedYear, sel[0]); 13940 } 13941 13942 onSelect = $.datepicker._get(inst, "onSelect"); 13943 if (onSelect) { 13944 dateStr = $.datepicker._formatDate(inst); 13945 13946 // trigger custom callback 13947 onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]); 13948 } else { 13949 $.datepicker._hideDatepicker(); 13950 } 13951 13952 return false; // don't submit the form 13953 case 27: $.datepicker._hideDatepicker(); 13954 break; // hide on escape 13955 case 33: $.datepicker._adjustDate(event.target, (event.ctrlKey ? 13956 -$.datepicker._get(inst, "stepBigMonths") : 13957 -$.datepicker._get(inst, "stepMonths")), "M"); 13958 break; // previous month/year on page up/+ ctrl 13959 case 34: $.datepicker._adjustDate(event.target, (event.ctrlKey ? 13960 +$.datepicker._get(inst, "stepBigMonths") : 13961 +$.datepicker._get(inst, "stepMonths")), "M"); 13962 break; // next month/year on page down/+ ctrl 13963 case 35: if (event.ctrlKey || event.metaKey) { 13964 $.datepicker._clearDate(event.target); 13965 } 13966 handled = event.ctrlKey || event.metaKey; 13967 break; // clear on ctrl or command +end 13968 case 36: if (event.ctrlKey || event.metaKey) { 13969 $.datepicker._gotoToday(event.target); 13970 } 13971 handled = event.ctrlKey || event.metaKey; 13972 break; // current on ctrl or command +home 13973 case 37: if (event.ctrlKey || event.metaKey) { 13974 $.datepicker._adjustDate(event.target, (isRTL ? +1 : -1), "D"); 13975 } 13976 handled = event.ctrlKey || event.metaKey; 13977 // -1 day on ctrl or command +left 13978 if (event.originalEvent.altKey) { 13979 $.datepicker._adjustDate(event.target, (event.ctrlKey ? 13980 -$.datepicker._get(inst, "stepBigMonths") : 13981 -$.datepicker._get(inst, "stepMonths")), "M"); 13982 } 13983 // next month/year on alt +left on Mac 13984 break; 13985 case 38: if (event.ctrlKey || event.metaKey) { 13986 $.datepicker._adjustDate(event.target, -7, "D"); 13987 } 13988 handled = event.ctrlKey || event.metaKey; 13989 break; // -1 week on ctrl or command +up 13990 case 39: if (event.ctrlKey || event.metaKey) { 13991 $.datepicker._adjustDate(event.target, (isRTL ? -1 : +1), "D"); 13992 } 13993 handled = event.ctrlKey || event.metaKey; 13994 // +1 day on ctrl or command +right 13995 if (event.originalEvent.altKey) { 13996 $.datepicker._adjustDate(event.target, (event.ctrlKey ? 13997 +$.datepicker._get(inst, "stepBigMonths") : 13998 +$.datepicker._get(inst, "stepMonths")), "M"); 13999 } 14000 // next month/year on alt +right 14001 break; 14002 case 40: if (event.ctrlKey || event.metaKey) { 14003 $.datepicker._adjustDate(event.target, +7, "D"); 14004 } 14005 handled = event.ctrlKey || event.metaKey; 14006 break; // +1 week on ctrl or command +down 14007 default: handled = false; 14008 } 14009 } else if (event.keyCode === 36 && event.ctrlKey) { // display the date picker on ctrl+home 14010 $.datepicker._showDatepicker(this); 14011 } else { 14012 handled = false; 14013 } 14014 14015 if (handled) { 14016 event.preventDefault(); 14017 event.stopPropagation(); 14018 } 14019 }, 14020 14021 /* Filter entered characters - based on date format. */ 14022 _doKeyPress: function(event) { 14023 var chars, chr, 14024 inst = $.datepicker._getInst(event.target); 14025 14026 if ($.datepicker._get(inst, "constrainInput")) { 14027 chars = $.datepicker._possibleChars($.datepicker._get(inst, "dateFormat")); 14028 chr = String.fromCharCode(event.charCode == null ? event.keyCode : event.charCode); 14029 return event.ctrlKey || event.metaKey || (chr < " " || !chars || chars.indexOf(chr) > -1); 14030 } 14031 }, 14032 14033 /* Synchronise manual entry and field/alternate field. */ 14034 _doKeyUp: function(event) { 14035 var date, 14036 inst = $.datepicker._getInst(event.target); 14037 14038 if (inst.input.val() !== inst.lastVal) { 14039 try { 14040 date = $.datepicker.parseDate($.datepicker._get(inst, "dateFormat"), 14041 (inst.input ? inst.input.val() : null), 14042 $.datepicker._getFormatConfig(inst)); 14043 14044 if (date) { // only if valid 14045 $.datepicker._setDateFromField(inst); 14046 $.datepicker._updateAlternate(inst); 14047 $.datepicker._updateDatepicker(inst); 14048 } 14049 } 14050 catch (err) { 14051 } 14052 } 14053 return true; 14054 }, 14055 14056 /* Pop-up the date picker for a given input field. 14057 * If false returned from beforeShow event handler do not show. 14058 * @param input element - the input field attached to the date picker or 14059 * event - if triggered by focus 14060 */ 14061 _showDatepicker: function(input) { 14062 input = input.target || input; 14063 if (input.nodeName.toLowerCase() !== "input") { // find from button/image trigger 14064 input = $("input", input.parentNode)[0]; 14065 } 14066 14067 if ($.datepicker._isDisabledDatepicker(input) || $.datepicker._lastInput === input) { // already here 14068 return; 14069 } 14070 14071 var inst, beforeShow, beforeShowSettings, isFixed, 14072 offset, showAnim, duration; 14073 14074 inst = $.datepicker._getInst(input); 14075 if ($.datepicker._curInst && $.datepicker._curInst !== inst) { 14076 $.datepicker._curInst.dpDiv.stop(true, true); 14077 if ( inst && $.datepicker._datepickerShowing ) { 14078 $.datepicker._hideDatepicker( $.datepicker._curInst.input[0] ); 14079 } 14080 } 14081 14082 beforeShow = $.datepicker._get(inst, "beforeShow"); 14083 beforeShowSettings = beforeShow ? beforeShow.apply(input, [input, inst]) : {}; 14084 if(beforeShowSettings === false){ 14085 return; 14086 } 14087 extendRemove(inst.settings, beforeShowSettings); 14088 14089 inst.lastVal = null; 14090 $.datepicker._lastInput = input; 14091 $.datepicker._setDateFromField(inst); 14092 14093 if ($.datepicker._inDialog) { // hide cursor 14094 input.value = ""; 14095 } 14096 if (!$.datepicker._pos) { // position below input 14097 $.datepicker._pos = $.datepicker._findPos(input); 14098 $.datepicker._pos[1] += input.offsetHeight; // add the height 14099 } 14100 14101 isFixed = false; 14102 $(input).parents().each(function() { 14103 isFixed |= $(this).css("position") === "fixed"; 14104 return !isFixed; 14105 }); 14106 14107 offset = {left: $.datepicker._pos[0], top: $.datepicker._pos[1]}; 14108 $.datepicker._pos = null; 14109 //to avoid flashes on Firefox 14110 inst.dpDiv.empty(); 14111 // determine sizing offscreen 14112 inst.dpDiv.css({position: "absolute", display: "block", top: "-1000px"}); 14113 $.datepicker._updateDatepicker(inst); 14114 // fix width for dynamic number of date pickers 14115 // and adjust position before showing 14116 offset = $.datepicker._checkOffset(inst, offset, isFixed); 14117 inst.dpDiv.css({position: ($.datepicker._inDialog && $.blockUI ? 14118 "static" : (isFixed ? "fixed" : "absolute")), display: "none", 14119 left: offset.left + "px", top: offset.top + "px"}); 14120 14121 if (!inst.inline) { 14122 showAnim = $.datepicker._get(inst, "showAnim"); 14123 duration = $.datepicker._get(inst, "duration"); 14124 inst.dpDiv.zIndex($(input).zIndex()+1); 14125 $.datepicker._datepickerShowing = true; 14126 14127 if ( $.effects && $.effects.effect[ showAnim ] ) { 14128 inst.dpDiv.show(showAnim, $.datepicker._get(inst, "showOptions"), duration); 14129 } else { 14130 inst.dpDiv[showAnim || "show"](showAnim ? duration : null); 14131 } 14132 14133 if ( $.datepicker._shouldFocusInput( inst ) ) { 14134 inst.input.focus(); 14135 } 14136 14137 $.datepicker._curInst = inst; 14138 } 14139 }, 14140 14141 /* Generate the date picker content. */ 14142 _updateDatepicker: function(inst) { 14143 this.maxRows = 4; //Reset the max number of rows being displayed (see #7043) 14144 instActive = inst; // for delegate hover events 14145 inst.dpDiv.empty().append(this._generateHTML(inst)); 14146 this._attachHandlers(inst); 14147 inst.dpDiv.find("." + this._dayOverClass + " a").mouseover(); 14148 14149 var origyearshtml, 14150 numMonths = this._getNumberOfMonths(inst), 14151 cols = numMonths[1], 14152 width = 17; 14153 14154 inst.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width(""); 14155 if (cols > 1) { 14156 inst.dpDiv.addClass("ui-datepicker-multi-" + cols).css("width", (width * cols) + "em"); 14157 } 14158 inst.dpDiv[(numMonths[0] !== 1 || numMonths[1] !== 1 ? "add" : "remove") + 14159 "Class"]("ui-datepicker-multi"); 14160 inst.dpDiv[(this._get(inst, "isRTL") ? "add" : "remove") + 14161 "Class"]("ui-datepicker-rtl"); 14162 14163 if (inst === $.datepicker._curInst && $.datepicker._datepickerShowing && $.datepicker._shouldFocusInput( inst ) ) { 14164 inst.input.focus(); 14165 } 14166 14167 // deffered render of the years select (to avoid flashes on Firefox) 14168 if( inst.yearshtml ){ 14169 origyearshtml = inst.yearshtml; 14170 setTimeout(function(){ 14171 //assure that inst.yearshtml didn't change. 14172 if( origyearshtml === inst.yearshtml && inst.yearshtml ){ 14173 inst.dpDiv.find("select.ui-datepicker-year:first").replaceWith(inst.yearshtml); 14174 } 14175 origyearshtml = inst.yearshtml = null; 14176 }, 0); 14177 } 14178 }, 14179 14180 // #6694 - don't focus the input if it's already focused 14181 // this breaks the change event in IE 14182 // Support: IE and jQuery <1.9 14183 _shouldFocusInput: function( inst ) { 14184 return inst.input && inst.input.is( ":visible" ) && !inst.input.is( ":disabled" ) && !inst.input.is( ":focus" ); 14185 }, 14186 14187 /* Check positioning to remain on screen. */ 14188 _checkOffset: function(inst, offset, isFixed) { 14189 var dpWidth = inst.dpDiv.outerWidth(), 14190 dpHeight = inst.dpDiv.outerHeight(), 14191 inputWidth = inst.input ? inst.input.outerWidth() : 0, 14192 inputHeight = inst.input ? inst.input.outerHeight() : 0, 14193 viewWidth = document.documentElement.clientWidth + (isFixed ? 0 : $(document).scrollLeft()), 14194 viewHeight = document.documentElement.clientHeight + (isFixed ? 0 : $(document).scrollTop()); 14195 14196 offset.left -= (this._get(inst, "isRTL") ? (dpWidth - inputWidth) : 0); 14197 offset.left -= (isFixed && offset.left === inst.input.offset().left) ? $(document).scrollLeft() : 0; 14198 offset.top -= (isFixed && offset.top === (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0; 14199 14200 // now check if datepicker is showing outside window viewport - move to a better place if so. 14201 offset.left -= Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ? 14202 Math.abs(offset.left + dpWidth - viewWidth) : 0); 14203 offset.top -= Math.min(offset.top, (offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ? 14204 Math.abs(dpHeight + inputHeight) : 0); 14205 14206 return offset; 14207 }, 14208 14209 /* Find an object's position on the screen. */ 14210 _findPos: function(obj) { 14211 var position, 14212 inst = this._getInst(obj), 14213 isRTL = this._get(inst, "isRTL"); 14214 14215 while (obj && (obj.type === "hidden" || obj.nodeType !== 1 || $.expr.filters.hidden(obj))) { 14216 obj = obj[isRTL ? "previousSibling" : "nextSibling"]; 14217 } 14218 14219 position = $(obj).offset(); 14220 return [position.left, position.top]; 14221 }, 14222 14223 /* Hide the date picker from view. 14224 * @param input element - the input field attached to the date picker 14225 */ 14226 _hideDatepicker: function(input) { 14227 var showAnim, duration, postProcess, onClose, 14228 inst = this._curInst; 14229 14230 if (!inst || (input && inst !== $.data(input, PROP_NAME))) { 14231 return; 14232 } 14233 14234 if (this._datepickerShowing) { 14235 showAnim = this._get(inst, "showAnim"); 14236 duration = this._get(inst, "duration"); 14237 postProcess = function() { 14238 $.datepicker._tidyDialog(inst); 14239 }; 14240 14241 // DEPRECATED: after BC for 1.8.x $.effects[ showAnim ] is not needed 14242 if ( $.effects && ( $.effects.effect[ showAnim ] || $.effects[ showAnim ] ) ) { 14243 inst.dpDiv.hide(showAnim, $.datepicker._get(inst, "showOptions"), duration, postProcess); 14244 } else { 14245 inst.dpDiv[(showAnim === "slideDown" ? "slideUp" : 14246 (showAnim === "fadeIn" ? "fadeOut" : "hide"))]((showAnim ? duration : null), postProcess); 14247 } 14248 14249 if (!showAnim) { 14250 postProcess(); 14251 } 14252 this._datepickerShowing = false; 14253 14254 onClose = this._get(inst, "onClose"); 14255 if (onClose) { 14256 onClose.apply((inst.input ? inst.input[0] : null), [(inst.input ? inst.input.val() : ""), inst]); 14257 } 14258 14259 this._lastInput = null; 14260 if (this._inDialog) { 14261 this._dialogInput.css({ position: "absolute", left: "0", top: "-100px" }); 14262 if ($.blockUI) { 14263 $.unblockUI(); 14264 $("body").append(this.dpDiv); 14265 } 14266 } 14267 this._inDialog = false; 14268 } 14269 }, 14270 14271 /* Tidy up after a dialog display. */ 14272 _tidyDialog: function(inst) { 14273 inst.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar"); 14274 }, 14275 14276 /* Close date picker if clicked elsewhere. */ 14277 _checkExternalClick: function(event) { 14278 if (!$.datepicker._curInst) { 14279 return; 14280 } 14281 14282 var $target = $(event.target), 14283 inst = $.datepicker._getInst($target[0]); 14284 14285 if ( ( ( $target[0].id !== $.datepicker._mainDivId && 14286 $target.parents("#" + $.datepicker._mainDivId).length === 0 && 14287 !$target.hasClass($.datepicker.markerClassName) && 14288 !$target.closest("." + $.datepicker._triggerClass).length && 14289 $.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI) ) ) || 14290 ( $target.hasClass($.datepicker.markerClassName) && $.datepicker._curInst !== inst ) ) { 14291 $.datepicker._hideDatepicker(); 14292 } 14293 }, 14294 14295 /* Adjust one of the date sub-fields. */ 14296 _adjustDate: function(id, offset, period) { 14297 var target = $(id), 14298 inst = this._getInst(target[0]); 14299 14300 if (this._isDisabledDatepicker(target[0])) { 14301 return; 14302 } 14303 this._adjustInstDate(inst, offset + 14304 (period === "M" ? this._get(inst, "showCurrentAtPos") : 0), // undo positioning 14305 period); 14306 this._updateDatepicker(inst); 14307 }, 14308 14309 /* Action for current link. */ 14310 _gotoToday: function(id) { 14311 var date, 14312 target = $(id), 14313 inst = this._getInst(target[0]); 14314 14315 if (this._get(inst, "gotoCurrent") && inst.currentDay) { 14316 inst.selectedDay = inst.currentDay; 14317 inst.drawMonth = inst.selectedMonth = inst.currentMonth; 14318 inst.drawYear = inst.selectedYear = inst.currentYear; 14319 } else { 14320 date = new Date(); 14321 inst.selectedDay = date.getDate(); 14322 inst.drawMonth = inst.selectedMonth = date.getMonth(); 14323 inst.drawYear = inst.selectedYear = date.getFullYear(); 14324 } 14325 this._notifyChange(inst); 14326 this._adjustDate(target); 14327 }, 14328 14329 /* Action for selecting a new month/year. */ 14330 _selectMonthYear: function(id, select, period) { 14331 var target = $(id), 14332 inst = this._getInst(target[0]); 14333 14334 inst["selected" + (period === "M" ? "Month" : "Year")] = 14335 inst["draw" + (period === "M" ? "Month" : "Year")] = 14336 parseInt(select.options[select.selectedIndex].value,10); 14337 14338 this._notifyChange(inst); 14339 this._adjustDate(target); 14340 }, 14341 14342 /* Action for selecting a day. */ 14343 _selectDay: function(id, month, year, td) { 14344 var inst, 14345 target = $(id); 14346 14347 if ($(td).hasClass(this._unselectableClass) || this._isDisabledDatepicker(target[0])) { 14348 return; 14349 } 14350 14351 inst = this._getInst(target[0]); 14352 inst.selectedDay = inst.currentDay = $("a", td).html(); 14353 inst.selectedMonth = inst.currentMonth = month; 14354 inst.selectedYear = inst.currentYear = year; 14355 this._selectDate(id, this._formatDate(inst, 14356 inst.currentDay, inst.currentMonth, inst.currentYear)); 14357 }, 14358 14359 /* Erase the input field and hide the date picker. */ 14360 _clearDate: function(id) { 14361 var target = $(id); 14362 this._selectDate(target, ""); 14363 }, 14364 14365 /* Update the input field with the selected date. */ 14366 _selectDate: function(id, dateStr) { 14367 var onSelect, 14368 target = $(id), 14369 inst = this._getInst(target[0]); 14370 14371 dateStr = (dateStr != null ? dateStr : this._formatDate(inst)); 14372 if (inst.input) { 14373 inst.input.val(dateStr); 14374 } 14375 this._updateAlternate(inst); 14376 14377 onSelect = this._get(inst, "onSelect"); 14378 if (onSelect) { 14379 onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]); // trigger custom callback 14380 } else if (inst.input) { 14381 inst.input.trigger("change"); // fire the change event 14382 } 14383 14384 if (inst.inline){ 14385 this._updateDatepicker(inst); 14386 } else { 14387 this._hideDatepicker(); 14388 this._lastInput = inst.input[0]; 14389 if (typeof(inst.input[0]) !== "object") { 14390 inst.input.focus(); // restore focus 14391 } 14392 this._lastInput = null; 14393 } 14394 }, 14395 14396 /* Update any alternate field to synchronise with the main field. */ 14397 _updateAlternate: function(inst) { 14398 var altFormat, date, dateStr, 14399 altField = this._get(inst, "altField"); 14400 14401 if (altField) { // update alternate field too 14402 altFormat = this._get(inst, "altFormat") || this._get(inst, "dateFormat"); 14403 date = this._getDate(inst); 14404 dateStr = this.formatDate(altFormat, date, this._getFormatConfig(inst)); 14405 $(altField).each(function() { $(this).val(dateStr); }); 14406 } 14407 }, 14408 14409 /* Set as beforeShowDay function to prevent selection of weekends. 14410 * @param date Date - the date to customise 14411 * @return [boolean, string] - is this date selectable?, what is its CSS class? 14412 */ 14413 noWeekends: function(date) { 14414 var day = date.getDay(); 14415 return [(day > 0 && day < 6), ""]; 14416 }, 14417 14418 /* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition. 14419 * @param date Date - the date to get the week for 14420 * @return number - the number of the week within the year that contains this date 14421 */ 14422 iso8601Week: function(date) { 14423 var time, 14424 checkDate = new Date(date.getTime()); 14425 14426 // Find Thursday of this week starting on Monday 14427 checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); 14428 14429 time = checkDate.getTime(); 14430 checkDate.setMonth(0); // Compare with Jan 1 14431 checkDate.setDate(1); 14432 return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1; 14433 }, 14434 14435 /* Parse a string value into a date object. 14436 * See formatDate below for the possible formats. 14437 * 14438 * @param format string - the expected format of the date 14439 * @param value string - the date in the above format 14440 * @param settings Object - attributes include: 14441 * shortYearCutoff number - the cutoff year for determining the century (optional) 14442 * dayNamesShort string[7] - abbreviated names of the days from Sunday (optional) 14443 * dayNames string[7] - names of the days from Sunday (optional) 14444 * monthNamesShort string[12] - abbreviated names of the months (optional) 14445 * monthNames string[12] - names of the months (optional) 14446 * @return Date - the extracted date value or null if value is blank 14447 */ 14448 parseDate: function (format, value, settings) { 14449 if (format == null || value == null) { 14450 throw "Invalid arguments"; 14451 } 14452 14453 value = (typeof value === "object" ? value.toString() : value + ""); 14454 if (value === "") { 14455 return null; 14456 } 14457 14458 var iFormat, dim, extra, 14459 iValue = 0, 14460 shortYearCutoffTemp = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff, 14461 shortYearCutoff = (typeof shortYearCutoffTemp !== "string" ? shortYearCutoffTemp : 14462 new Date().getFullYear() % 100 + parseInt(shortYearCutoffTemp, 10)), 14463 dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort, 14464 dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames, 14465 monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort, 14466 monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames, 14467 year = -1, 14468 month = -1, 14469 day = -1, 14470 doy = -1, 14471 literal = false, 14472 date, 14473 // Check whether a format character is doubled 14474 lookAhead = function(match) { 14475 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match); 14476 if (matches) { 14477 iFormat++; 14478 } 14479 return matches; 14480 }, 14481 // Extract a number from the string value 14482 getNumber = function(match) { 14483 var isDoubled = lookAhead(match), 14484 size = (match === "@" ? 14 : (match === "!" ? 20 : 14485 (match === "y" && isDoubled ? 4 : (match === "o" ? 3 : 2)))), 14486 digits = new RegExp("^\\d{1," + size + "}"), 14487 num = value.substring(iValue).match(digits); 14488 if (!num) { 14489 throw "Missing number at position " + iValue; 14490 } 14491 iValue += num[0].length; 14492 return parseInt(num[0], 10); 14493 }, 14494 // Extract a name from the string value and convert to an index 14495 getName = function(match, shortNames, longNames) { 14496 var index = -1, 14497 names = $.map(lookAhead(match) ? longNames : shortNames, function (v, k) { 14498 return [ [k, v] ]; 14499 }).sort(function (a, b) { 14500 return -(a[1].length - b[1].length); 14501 }); 14502 14503 $.each(names, function (i, pair) { 14504 var name = pair[1]; 14505 if (value.substr(iValue, name.length).toLowerCase() === name.toLowerCase()) { 14506 index = pair[0]; 14507 iValue += name.length; 14508 return false; 14509 } 14510 }); 14511 if (index !== -1) { 14512 return index + 1; 14513 } else { 14514 throw "Unknown name at position " + iValue; 14515 } 14516 }, 14517 // Confirm that a literal character matches the string value 14518 checkLiteral = function() { 14519 if (value.charAt(iValue) !== format.charAt(iFormat)) { 14520 throw "Unexpected literal at position " + iValue; 14521 } 14522 iValue++; 14523 }; 14524 14525 for (iFormat = 0; iFormat < format.length; iFormat++) { 14526 if (literal) { 14527 if (format.charAt(iFormat) === "'" && !lookAhead("'")) { 14528 literal = false; 14529 } else { 14530 checkLiteral(); 14531 } 14532 } else { 14533 switch (format.charAt(iFormat)) { 14534 case "d": 14535 day = getNumber("d"); 14536 break; 14537 case "D": 14538 getName("D", dayNamesShort, dayNames); 14539 break; 14540 case "o": 14541 doy = getNumber("o"); 14542 break; 14543 case "m": 14544 month = getNumber("m"); 14545 break; 14546 case "M": 14547 month = getName("M", monthNamesShort, monthNames); 14548 break; 14549 case "y": 14550 year = getNumber("y"); 14551 break; 14552 case "@": 14553 date = new Date(getNumber("@")); 14554 year = date.getFullYear(); 14555 month = date.getMonth() + 1; 14556 day = date.getDate(); 14557 break; 14558 case "!": 14559 date = new Date((getNumber("!") - this._ticksTo1970) / 10000); 14560 year = date.getFullYear(); 14561 month = date.getMonth() + 1; 14562 day = date.getDate(); 14563 break; 14564 case "'": 14565 if (lookAhead("'")){ 14566 checkLiteral(); 14567 } else { 14568 literal = true; 14569 } 14570 break; 14571 default: 14572 checkLiteral(); 14573 } 14574 } 14575 } 14576 14577 if (iValue < value.length){ 14578 extra = value.substr(iValue); 14579 if (!/^\s+/.test(extra)) { 14580 throw "Extra/unparsed characters found in date: " + extra; 14581 } 14582 } 14583 14584 if (year === -1) { 14585 year = new Date().getFullYear(); 14586 } else if (year < 100) { 14587 year += new Date().getFullYear() - new Date().getFullYear() % 100 + 14588 (year <= shortYearCutoff ? 0 : -100); 14589 } 14590 14591 if (doy > -1) { 14592 month = 1; 14593 day = doy; 14594 do { 14595 dim = this._getDaysInMonth(year, month - 1); 14596 if (day <= dim) { 14597 break; 14598 } 14599 month++; 14600 day -= dim; 14601 } while (true); 14602 } 14603 14604 date = this._daylightSavingAdjust(new Date(year, month - 1, day)); 14605 if (date.getFullYear() !== year || date.getMonth() + 1 !== month || date.getDate() !== day) { 14606 throw "Invalid date"; // E.g. 31/02/00 14607 } 14608 return date; 14609 }, 14610 14611 /* Standard date formats. */ 14612 ATOM: "yy-mm-dd", // RFC 3339 (ISO 8601) 14613 COOKIE: "D, dd M yy", 14614 ISO_8601: "yy-mm-dd", 14615 RFC_822: "D, d M y", 14616 RFC_850: "DD, dd-M-y", 14617 RFC_1036: "D, d M y", 14618 RFC_1123: "D, d M yy", 14619 RFC_2822: "D, d M yy", 14620 RSS: "D, d M y", // RFC 822 14621 TICKS: "!", 14622 TIMESTAMP: "@", 14623 W3C: "yy-mm-dd", // ISO 8601 14624 14625 _ticksTo1970: (((1970 - 1) * 365 + Math.floor(1970 / 4) - Math.floor(1970 / 100) + 14626 Math.floor(1970 / 400)) * 24 * 60 * 60 * 10000000), 14627 14628 /* Format a date object into a string value. 14629 * The format can be combinations of the following: 14630 * d - day of month (no leading zero) 14631 * dd - day of month (two digit) 14632 * o - day of year (no leading zeros) 14633 * oo - day of year (three digit) 14634 * D - day name short 14635 * DD - day name long 14636 * m - month of year (no leading zero) 14637 * mm - month of year (two digit) 14638 * M - month name short 14639 * MM - month name long 14640 * y - year (two digit) 14641 * yy - year (four digit) 14642 * @ - Unix timestamp (ms since 01/01/1970) 14643 * ! - Windows ticks (100ns since 01/01/0001) 14644 * "..." - literal text 14645 * '' - single quote 14646 * 14647 * @param format string - the desired format of the date 14648 * @param date Date - the date value to format 14649 * @param settings Object - attributes include: 14650 * dayNamesShort string[7] - abbreviated names of the days from Sunday (optional) 14651 * dayNames string[7] - names of the days from Sunday (optional) 14652 * monthNamesShort string[12] - abbreviated names of the months (optional) 14653 * monthNames string[12] - names of the months (optional) 14654 * @return string - the date in the above format 14655 */ 14656 formatDate: function (format, date, settings) { 14657 if (!date) { 14658 return ""; 14659 } 14660 14661 var iFormat, 14662 dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort, 14663 dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames, 14664 monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort, 14665 monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames, 14666 // Check whether a format character is doubled 14667 lookAhead = function(match) { 14668 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match); 14669 if (matches) { 14670 iFormat++; 14671 } 14672 return matches; 14673 }, 14674 // Format a number, with leading zero if necessary 14675 formatNumber = function(match, value, len) { 14676 var num = "" + value; 14677 if (lookAhead(match)) { 14678 while (num.length < len) { 14679 num = "0" + num; 14680 } 14681 } 14682 return num; 14683 }, 14684 // Format a name, short or long as requested 14685 formatName = function(match, value, shortNames, longNames) { 14686 return (lookAhead(match) ? longNames[value] : shortNames[value]); 14687 }, 14688 output = "", 14689 literal = false; 14690 14691 if (date) { 14692 for (iFormat = 0; iFormat < format.length; iFormat++) { 14693 if (literal) { 14694 if (format.charAt(iFormat) === "'" && !lookAhead("'")) { 14695 literal = false; 14696 } else { 14697 output += format.charAt(iFormat); 14698 } 14699 } else { 14700 switch (format.charAt(iFormat)) { 14701 case "d": 14702 output += formatNumber("d", date.getDate(), 2); 14703 break; 14704 case "D": 14705 output += formatName("D", date.getDay(), dayNamesShort, dayNames); 14706 break; 14707 case "o": 14708 output += formatNumber("o", 14709 Math.round((new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000), 3); 14710 break; 14711 case "m": 14712 output += formatNumber("m", date.getMonth() + 1, 2); 14713 break; 14714 case "M": 14715 output += formatName("M", date.getMonth(), monthNamesShort, monthNames); 14716 break; 14717 case "y": 14718 output += (lookAhead("y") ? date.getFullYear() : 14719 (date.getYear() % 100 < 10 ? "0" : "") + date.getYear() % 100); 14720 break; 14721 case "@": 14722 output += date.getTime(); 14723 break; 14724 case "!": 14725 output += date.getTime() * 10000 + this._ticksTo1970; 14726 break; 14727 case "'": 14728 if (lookAhead("'")) { 14729 output += "'"; 14730 } else { 14731 literal = true; 14732 } 14733 break; 14734 default: 14735 output += format.charAt(iFormat); 14736 } 14737 } 14738 } 14739 } 14740 return output; 14741 }, 14742 14743 /* Extract all possible characters from the date format. */ 14744 _possibleChars: function (format) { 14745 var iFormat, 14746 chars = "", 14747 literal = false, 14748 // Check whether a format character is doubled 14749 lookAhead = function(match) { 14750 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match); 14751 if (matches) { 14752 iFormat++; 14753 } 14754 return matches; 14755 }; 14756 14757 for (iFormat = 0; iFormat < format.length; iFormat++) { 14758 if (literal) { 14759 if (format.charAt(iFormat) === "'" && !lookAhead("'")) { 14760 literal = false; 14761 } else { 14762 chars += format.charAt(iFormat); 14763 } 14764 } else { 14765 switch (format.charAt(iFormat)) { 14766 case "d": case "m": case "y": case "@": 14767 chars += "0123456789"; 14768 break; 14769 case "D": case "M": 14770 return null; // Accept anything 14771 case "'": 14772 if (lookAhead("'")) { 14773 chars += "'"; 14774 } else { 14775 literal = true; 14776 } 14777 break; 14778 default: 14779 chars += format.charAt(iFormat); 14780 } 14781 } 14782 } 14783 return chars; 14784 }, 14785 14786 /* Get a setting value, defaulting if necessary. */ 14787 _get: function(inst, name) { 14788 return inst.settings[name] !== undefined ? 14789 inst.settings[name] : this._defaults[name]; 14790 }, 14791 14792 /* Parse existing date and initialise date picker. */ 14793 _setDateFromField: function(inst, noDefault) { 14794 if (inst.input.val() === inst.lastVal) { 14795 return; 14796 } 14797 14798 var dateFormat = this._get(inst, "dateFormat"), 14799 dates = inst.lastVal = inst.input ? inst.input.val() : null, 14800 defaultDate = this._getDefaultDate(inst), 14801 date = defaultDate, 14802 settings = this._getFormatConfig(inst); 14803 14804 try { 14805 date = this.parseDate(dateFormat, dates, settings) || defaultDate; 14806 } catch (event) { 14807 dates = (noDefault ? "" : dates); 14808 } 14809 inst.selectedDay = date.getDate(); 14810 inst.drawMonth = inst.selectedMonth = date.getMonth(); 14811 inst.drawYear = inst.selectedYear = date.getFullYear(); 14812 inst.currentDay = (dates ? date.getDate() : 0); 14813 inst.currentMonth = (dates ? date.getMonth() : 0); 14814 inst.currentYear = (dates ? date.getFullYear() : 0); 14815 this._adjustInstDate(inst); 14816 }, 14817 14818 /* Retrieve the default date shown on opening. */ 14819 _getDefaultDate: function(inst) { 14820 return this._restrictMinMax(inst, 14821 this._determineDate(inst, this._get(inst, "defaultDate"), new Date())); 14822 }, 14823 14824 /* A date may be specified as an exact value or a relative one. */ 14825 _determineDate: function(inst, date, defaultDate) { 14826 var offsetNumeric = function(offset) { 14827 var date = new Date(); 14828 date.setDate(date.getDate() + offset); 14829 return date; 14830 }, 14831 offsetString = function(offset) { 14832 try { 14833 return $.datepicker.parseDate($.datepicker._get(inst, "dateFormat"), 14834 offset, $.datepicker._getFormatConfig(inst)); 14835 } 14836 catch (e) { 14837 // Ignore 14838 } 14839 14840 var date = (offset.toLowerCase().match(/^c/) ? 14841 $.datepicker._getDate(inst) : null) || new Date(), 14842 year = date.getFullYear(), 14843 month = date.getMonth(), 14844 day = date.getDate(), 14845 pattern = /([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g, 14846 matches = pattern.exec(offset); 14847 14848 while (matches) { 14849 switch (matches[2] || "d") { 14850 case "d" : case "D" : 14851 day += parseInt(matches[1],10); break; 14852 case "w" : case "W" : 14853 day += parseInt(matches[1],10) * 7; break; 14854 case "m" : case "M" : 14855 month += parseInt(matches[1],10); 14856 day = Math.min(day, $.datepicker._getDaysInMonth(year, month)); 14857 break; 14858 case "y": case "Y" : 14859 year += parseInt(matches[1],10); 14860 day = Math.min(day, $.datepicker._getDaysInMonth(year, month)); 14861 break; 14862 } 14863 matches = pattern.exec(offset); 14864 } 14865 return new Date(year, month, day); 14866 }, 14867 newDate = (date == null || date === "" ? defaultDate : (typeof date === "string" ? offsetString(date) : 14868 (typeof date === "number" ? (isNaN(date) ? defaultDate : offsetNumeric(date)) : new Date(date.getTime())))); 14869 14870 newDate = (newDate && newDate.toString() === "Invalid Date" ? defaultDate : newDate); 14871 if (newDate) { 14872 newDate.setHours(0); 14873 newDate.setMinutes(0); 14874 newDate.setSeconds(0); 14875 newDate.setMilliseconds(0); 14876 } 14877 return this._daylightSavingAdjust(newDate); 14878 }, 14879 14880 /* Handle switch to/from daylight saving. 14881 * Hours may be non-zero on daylight saving cut-over: 14882 * > 12 when midnight changeover, but then cannot generate 14883 * midnight datetime, so jump to 1AM, otherwise reset. 14884 * @param date (Date) the date to check 14885 * @return (Date) the corrected date 14886 */ 14887 _daylightSavingAdjust: function(date) { 14888 if (!date) { 14889 return null; 14890 } 14891 date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0); 14892 return date; 14893 }, 14894 14895 /* Set the date(s) directly. */ 14896 _setDate: function(inst, date, noChange) { 14897 var clear = !date, 14898 origMonth = inst.selectedMonth, 14899 origYear = inst.selectedYear, 14900 newDate = this._restrictMinMax(inst, this._determineDate(inst, date, new Date())); 14901 14902 inst.selectedDay = inst.currentDay = newDate.getDate(); 14903 inst.drawMonth = inst.selectedMonth = inst.currentMonth = newDate.getMonth(); 14904 inst.drawYear = inst.selectedYear = inst.currentYear = newDate.getFullYear(); 14905 if ((origMonth !== inst.selectedMonth || origYear !== inst.selectedYear) && !noChange) { 14906 this._notifyChange(inst); 14907 } 14908 this._adjustInstDate(inst); 14909 if (inst.input) { 14910 inst.input.val(clear ? "" : this._formatDate(inst)); 14911 } 14912 }, 14913 14914 /* Retrieve the date(s) directly. */ 14915 _getDate: function(inst) { 14916 var startDate = (!inst.currentYear || (inst.input && inst.input.val() === "") ? null : 14917 this._daylightSavingAdjust(new Date( 14918 inst.currentYear, inst.currentMonth, inst.currentDay))); 14919 return startDate; 14920 }, 14921 14922 /* Attach the onxxx handlers. These are declared statically so 14923 * they work with static code transformers like Caja. 14924 */ 14925 _attachHandlers: function(inst) { 14926 var stepMonths = this._get(inst, "stepMonths"), 14927 id = "#" + inst.id.replace( /\\\\/g, "\\" ); 14928 inst.dpDiv.find("[data-handler]").map(function () { 14929 var handler = { 14930 prev: function () { 14931 $.datepicker._adjustDate(id, -stepMonths, "M"); 14932 }, 14933 next: function () { 14934 $.datepicker._adjustDate(id, +stepMonths, "M"); 14935 }, 14936 hide: function () { 14937 $.datepicker._hideDatepicker(); 14938 }, 14939 today: function () { 14940 $.datepicker._gotoToday(id); 14941 }, 14942 selectDay: function () { 14943 $.datepicker._selectDay(id, +this.getAttribute("data-month"), +this.getAttribute("data-year"), this); 14944 return false; 14945 }, 14946 selectMonth: function () { 14947 $.datepicker._selectMonthYear(id, this, "M"); 14948 return false; 14949 }, 14950 selectYear: function () { 14951 $.datepicker._selectMonthYear(id, this, "Y"); 14952 return false; 14953 } 14954 }; 14955 $(this).bind(this.getAttribute("data-event"), handler[this.getAttribute("data-handler")]); 14956 }); 14957 }, 14958 14959 /* Generate the HTML for the current state of the date picker. */ 14960 _generateHTML: function(inst) { 14961 var maxDraw, prevText, prev, nextText, next, currentText, gotoDate, 14962 controls, buttonPanel, firstDay, showWeek, dayNames, dayNamesMin, 14963 monthNames, monthNamesShort, beforeShowDay, showOtherMonths, 14964 selectOtherMonths, defaultDate, html, dow, row, group, col, selectedDate, 14965 cornerClass, calender, thead, day, daysInMonth, leadDays, curRows, numRows, 14966 printDate, dRow, tbody, daySettings, otherMonth, unselectable, 14967 tempDate = new Date(), 14968 today = this._daylightSavingAdjust( 14969 new Date(tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate())), // clear time 14970 isRTL = this._get(inst, "isRTL"), 14971 showButtonPanel = this._get(inst, "showButtonPanel"), 14972 hideIfNoPrevNext = this._get(inst, "hideIfNoPrevNext"), 14973 navigationAsDateFormat = this._get(inst, "navigationAsDateFormat"), 14974 numMonths = this._getNumberOfMonths(inst), 14975 showCurrentAtPos = this._get(inst, "showCurrentAtPos"), 14976 stepMonths = this._get(inst, "stepMonths"), 14977 isMultiMonth = (numMonths[0] !== 1 || numMonths[1] !== 1), 14978 currentDate = this._daylightSavingAdjust((!inst.currentDay ? new Date(9999, 9, 9) : 14979 new Date(inst.currentYear, inst.currentMonth, inst.currentDay))), 14980 minDate = this._getMinMaxDate(inst, "min"), 14981 maxDate = this._getMinMaxDate(inst, "max"), 14982 drawMonth = inst.drawMonth - showCurrentAtPos, 14983 drawYear = inst.drawYear; 14984 14985 if (drawMonth < 0) { 14986 drawMonth += 12; 14987 drawYear--; 14988 } 14989 if (maxDate) { 14990 maxDraw = this._daylightSavingAdjust(new Date(maxDate.getFullYear(), 14991 maxDate.getMonth() - (numMonths[0] * numMonths[1]) + 1, maxDate.getDate())); 14992 maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw); 14993 while (this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1)) > maxDraw) { 14994 drawMonth--; 14995 if (drawMonth < 0) { 14996 drawMonth = 11; 14997 drawYear--; 14998 } 14999 } 15000 } 15001 inst.drawMonth = drawMonth; 15002 inst.drawYear = drawYear; 15003 15004 prevText = this._get(inst, "prevText"); 15005 prevText = (!navigationAsDateFormat ? prevText : this.formatDate(prevText, 15006 this._daylightSavingAdjust(new Date(drawYear, drawMonth - stepMonths, 1)), 15007 this._getFormatConfig(inst))); 15008 15009 prev = (this._canAdjustMonth(inst, -1, drawYear, drawMonth) ? 15010 "<a class='ui-datepicker-prev ui-corner-all' data-handler='prev' data-event='click'" + 15011 " title='" + prevText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "e" : "w") + "'>" + prevText + "</span></a>" : 15012 (hideIfNoPrevNext ? "" : "<a class='ui-datepicker-prev ui-corner-all ui-state-disabled' title='"+ prevText +"'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "e" : "w") + "'>" + prevText + "</span></a>")); 15013 15014 nextText = this._get(inst, "nextText"); 15015 nextText = (!navigationAsDateFormat ? nextText : this.formatDate(nextText, 15016 this._daylightSavingAdjust(new Date(drawYear, drawMonth + stepMonths, 1)), 15017 this._getFormatConfig(inst))); 15018 15019 next = (this._canAdjustMonth(inst, +1, drawYear, drawMonth) ? 15020 "<a class='ui-datepicker-next ui-corner-all' data-handler='next' data-event='click'" + 15021 " title='" + nextText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "w" : "e") + "'>" + nextText + "</span></a>" : 15022 (hideIfNoPrevNext ? "" : "<a class='ui-datepicker-next ui-corner-all ui-state-disabled' title='"+ nextText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "w" : "e") + "'>" + nextText + "</span></a>")); 15023 15024 currentText = this._get(inst, "currentText"); 15025 gotoDate = (this._get(inst, "gotoCurrent") && inst.currentDay ? currentDate : today); 15026 currentText = (!navigationAsDateFormat ? currentText : 15027 this.formatDate(currentText, gotoDate, this._getFormatConfig(inst))); 15028 15029 controls = (!inst.inline ? "<button type='button' class='ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all' data-handler='hide' data-event='click'>" + 15030 this._get(inst, "closeText") + "</button>" : ""); 15031 15032 buttonPanel = (showButtonPanel) ? "<div class='ui-datepicker-buttonpane ui-widget-content'>" + (isRTL ? controls : "") + 15033 (this._isInRange(inst, gotoDate) ? "<button type='button' class='ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all' data-handler='today' data-event='click'" + 15034 ">" + currentText + "</button>" : "") + (isRTL ? "" : controls) + "</div>" : ""; 15035 15036 firstDay = parseInt(this._get(inst, "firstDay"),10); 15037 firstDay = (isNaN(firstDay) ? 0 : firstDay); 15038 15039 showWeek = this._get(inst, "showWeek"); 15040 dayNames = this._get(inst, "dayNames"); 15041 dayNamesMin = this._get(inst, "dayNamesMin"); 15042 monthNames = this._get(inst, "monthNames"); 15043 monthNamesShort = this._get(inst, "monthNamesShort"); 15044 beforeShowDay = this._get(inst, "beforeShowDay"); 15045 showOtherMonths = this._get(inst, "showOtherMonths"); 15046 selectOtherMonths = this._get(inst, "selectOtherMonths"); 15047 defaultDate = this._getDefaultDate(inst); 15048 html = ""; 15049 dow; 15050 for (row = 0; row < numMonths[0]; row++) { 15051 group = ""; 15052 this.maxRows = 4; 15053 for (col = 0; col < numMonths[1]; col++) { 15054 selectedDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, inst.selectedDay)); 15055 cornerClass = " ui-corner-all"; 15056 calender = ""; 15057 if (isMultiMonth) { 15058 calender += "<div class='ui-datepicker-group"; 15059 if (numMonths[1] > 1) { 15060 switch (col) { 15061 case 0: calender += " ui-datepicker-group-first"; 15062 cornerClass = " ui-corner-" + (isRTL ? "right" : "left"); break; 15063 case numMonths[1]-1: calender += " ui-datepicker-group-last"; 15064 cornerClass = " ui-corner-" + (isRTL ? "left" : "right"); break; 15065 default: calender += " ui-datepicker-group-middle"; cornerClass = ""; break; 15066 } 15067 } 15068 calender += "'>"; 15069 } 15070 calender += "<div class='ui-datepicker-header ui-widget-header ui-helper-clearfix" + cornerClass + "'>" + 15071 (/all|left/.test(cornerClass) && row === 0 ? (isRTL ? next : prev) : "") + 15072 (/all|right/.test(cornerClass) && row === 0 ? (isRTL ? prev : next) : "") + 15073 this._generateMonthYearHeader(inst, drawMonth, drawYear, minDate, maxDate, 15074 row > 0 || col > 0, monthNames, monthNamesShort) + // draw month headers 15075 "</div><table class='ui-datepicker-calendar'><thead>" + 15076 "<tr>"; 15077 thead = (showWeek ? "<th class='ui-datepicker-week-col'>" + this._get(inst, "weekHeader") + "</th>" : ""); 15078 for (dow = 0; dow < 7; dow++) { // days of the week 15079 day = (dow + firstDay) % 7; 15080 thead += "<th" + ((dow + firstDay + 6) % 7 >= 5 ? " class='ui-datepicker-week-end'" : "") + ">" + 15081 "<span title='" + dayNames[day] + "'>" + dayNamesMin[day] + "</span></th>"; 15082 } 15083 calender += thead + "</tr></thead><tbody>"; 15084 daysInMonth = this._getDaysInMonth(drawYear, drawMonth); 15085 if (drawYear === inst.selectedYear && drawMonth === inst.selectedMonth) { 15086 inst.selectedDay = Math.min(inst.selectedDay, daysInMonth); 15087 } 15088 leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7; 15089 curRows = Math.ceil((leadDays + daysInMonth) / 7); // calculate the number of rows to generate 15090 numRows = (isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows); //If multiple months, use the higher number of rows (see #7043) 15091 this.maxRows = numRows; 15092 printDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1 - leadDays)); 15093 for (dRow = 0; dRow < numRows; dRow++) { // create date picker rows 15094 calender += "<tr>"; 15095 tbody = (!showWeek ? "" : "<td class='ui-datepicker-week-col'>" + 15096 this._get(inst, "calculateWeek")(printDate) + "</td>"); 15097 for (dow = 0; dow < 7; dow++) { // create date picker days 15098 daySettings = (beforeShowDay ? 15099 beforeShowDay.apply((inst.input ? inst.input[0] : null), [printDate]) : [true, ""]); 15100 otherMonth = (printDate.getMonth() !== drawMonth); 15101 unselectable = (otherMonth && !selectOtherMonths) || !daySettings[0] || 15102 (minDate && printDate < minDate) || (maxDate && printDate > maxDate); 15103 tbody += "<td class='" + 15104 ((dow + firstDay + 6) % 7 >= 5 ? " ui-datepicker-week-end" : "") + // highlight weekends 15105 (otherMonth ? " ui-datepicker-other-month" : "") + // highlight days from other months 15106 ((printDate.getTime() === selectedDate.getTime() && drawMonth === inst.selectedMonth && inst._keyEvent) || // user pressed key 15107 (defaultDate.getTime() === printDate.getTime() && defaultDate.getTime() === selectedDate.getTime()) ? 15108 // or defaultDate is current printedDate and defaultDate is selectedDate 15109 " " + this._dayOverClass : "") + // highlight selected day 15110 (unselectable ? " " + this._unselectableClass + " ui-state-disabled": "") + // highlight unselectable days 15111 (otherMonth && !showOtherMonths ? "" : " " + daySettings[1] + // highlight custom dates 15112 (printDate.getTime() === currentDate.getTime() ? " " + this._currentClass : "") + // highlight selected day 15113 (printDate.getTime() === today.getTime() ? " ui-datepicker-today" : "")) + "'" + // highlight today (if different) 15114 ((!otherMonth || showOtherMonths) && daySettings[2] ? " title='" + daySettings[2].replace(/'/g, "'") + "'" : "") + // cell title 15115 (unselectable ? "" : " data-handler='selectDay' data-event='click' data-month='" + printDate.getMonth() + "' data-year='" + printDate.getFullYear() + "'") + ">" + // actions 15116 (otherMonth && !showOtherMonths ? " " : // display for other months 15117 (unselectable ? "<span class='ui-state-default'>" + printDate.getDate() + "</span>" : "<a class='ui-state-default" + 15118 (printDate.getTime() === today.getTime() ? " ui-state-highlight" : "") + 15119 (printDate.getTime() === currentDate.getTime() ? " ui-state-active" : "") + // highlight selected day 15120 (otherMonth ? " ui-priority-secondary" : "") + // distinguish dates from other months 15121 "' href='#'>" + printDate.getDate() + "</a>")) + "</td>"; // display selectable date 15122 printDate.setDate(printDate.getDate() + 1); 15123 printDate = this._daylightSavingAdjust(printDate); 15124 } 15125 calender += tbody + "</tr>"; 15126 } 15127 drawMonth++; 15128 if (drawMonth > 11) { 15129 drawMonth = 0; 15130 drawYear++; 15131 } 15132 calender += "</tbody></table>" + (isMultiMonth ? "</div>" + 15133 ((numMonths[0] > 0 && col === numMonths[1]-1) ? "<div class='ui-datepicker-row-break'></div>" : "") : ""); 15134 group += calender; 15135 } 15136 html += group; 15137 } 15138 html += buttonPanel; 15139 inst._keyEvent = false; 15140 return html; 15141 }, 15142 15143 /* Generate the month and year header. */ 15144 _generateMonthYearHeader: function(inst, drawMonth, drawYear, minDate, maxDate, 15145 secondary, monthNames, monthNamesShort) { 15146 15147 var inMinYear, inMaxYear, month, years, thisYear, determineYear, year, endYear, 15148 changeMonth = this._get(inst, "changeMonth"), 15149 changeYear = this._get(inst, "changeYear"), 15150 showMonthAfterYear = this._get(inst, "showMonthAfterYear"), 15151 html = "<div class='ui-datepicker-title'>", 15152 monthHtml = ""; 15153 15154 // month selection 15155 if (secondary || !changeMonth) { 15156 monthHtml += "<span class='ui-datepicker-month'>" + monthNames[drawMonth] + "</span>"; 15157 } else { 15158 inMinYear = (minDate && minDate.getFullYear() === drawYear); 15159 inMaxYear = (maxDate && maxDate.getFullYear() === drawYear); 15160 monthHtml += "<select class='ui-datepicker-month' data-handler='selectMonth' data-event='change'>"; 15161 for ( month = 0; month < 12; month++) { 15162 if ((!inMinYear || month >= minDate.getMonth()) && (!inMaxYear || month <= maxDate.getMonth())) { 15163 monthHtml += "<option value='" + month + "'" + 15164 (month === drawMonth ? " selected='selected'" : "") + 15165 ">" + monthNamesShort[month] + "</option>"; 15166 } 15167 } 15168 monthHtml += "</select>"; 15169 } 15170 15171 if (!showMonthAfterYear) { 15172 html += monthHtml + (secondary || !(changeMonth && changeYear) ? " " : ""); 15173 } 15174 15175 // year selection 15176 if ( !inst.yearshtml ) { 15177 inst.yearshtml = ""; 15178 if (secondary || !changeYear) { 15179 html += "<span class='ui-datepicker-year'>" + drawYear + "</span>"; 15180 } else { 15181 // determine range of years to display 15182 years = this._get(inst, "yearRange").split(":"); 15183 thisYear = new Date().getFullYear(); 15184 determineYear = function(value) { 15185 var year = (value.match(/c[+\-].*/) ? drawYear + parseInt(value.substring(1), 10) : 15186 (value.match(/[+\-].*/) ? thisYear + parseInt(value, 10) : 15187 parseInt(value, 10))); 15188 return (isNaN(year) ? thisYear : year); 15189 }; 15190 year = determineYear(years[0]); 15191 endYear = Math.max(year, determineYear(years[1] || "")); 15192 year = (minDate ? Math.max(year, minDate.getFullYear()) : year); 15193 endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear); 15194 inst.yearshtml += "<select class='ui-datepicker-year' data-handler='selectYear' data-event='change'>"; 15195 for (; year <= endYear; year++) { 15196 inst.yearshtml += "<option value='" + year + "'" + 15197 (year === drawYear ? " selected='selected'" : "") + 15198 ">" + year + "</option>"; 15199 } 15200 inst.yearshtml += "</select>"; 15201 15202 html += inst.yearshtml; 15203 inst.yearshtml = null; 15204 } 15205 } 15206 15207 html += this._get(inst, "yearSuffix"); 15208 if (showMonthAfterYear) { 15209 html += (secondary || !(changeMonth && changeYear) ? " " : "") + monthHtml; 15210 } 15211 html += "</div>"; // Close datepicker_header 15212 return html; 15213 }, 15214 15215 /* Adjust one of the date sub-fields. */ 15216 _adjustInstDate: function(inst, offset, period) { 15217 var year = inst.drawYear + (period === "Y" ? offset : 0), 15218 month = inst.drawMonth + (period === "M" ? offset : 0), 15219 day = Math.min(inst.selectedDay, this._getDaysInMonth(year, month)) + (period === "D" ? offset : 0), 15220 date = this._restrictMinMax(inst, this._daylightSavingAdjust(new Date(year, month, day))); 15221 15222 inst.selectedDay = date.getDate(); 15223 inst.drawMonth = inst.selectedMonth = date.getMonth(); 15224 inst.drawYear = inst.selectedYear = date.getFullYear(); 15225 if (period === "M" || period === "Y") { 15226 this._notifyChange(inst); 15227 } 15228 }, 15229 15230 /* Ensure a date is within any min/max bounds. */ 15231 _restrictMinMax: function(inst, date) { 15232 var minDate = this._getMinMaxDate(inst, "min"), 15233 maxDate = this._getMinMaxDate(inst, "max"), 15234 newDate = (minDate && date < minDate ? minDate : date); 15235 return (maxDate && newDate > maxDate ? maxDate : newDate); 15236 }, 15237 15238 /* Notify change of month/year. */ 15239 _notifyChange: function(inst) { 15240 var onChange = this._get(inst, "onChangeMonthYear"); 15241 if (onChange) { 15242 onChange.apply((inst.input ? inst.input[0] : null), 15243 [inst.selectedYear, inst.selectedMonth + 1, inst]); 15244 } 15245 }, 15246 15247 /* Determine the number of months to show. */ 15248 _getNumberOfMonths: function(inst) { 15249 var numMonths = this._get(inst, "numberOfMonths"); 15250 return (numMonths == null ? [1, 1] : (typeof numMonths === "number" ? [1, numMonths] : numMonths)); 15251 }, 15252 15253 /* Determine the current maximum date - ensure no time components are set. */ 15254 _getMinMaxDate: function(inst, minMax) { 15255 return this._determineDate(inst, this._get(inst, minMax + "Date"), null); 15256 }, 15257 15258 /* Find the number of days in a given month. */ 15259 _getDaysInMonth: function(year, month) { 15260 return 32 - this._daylightSavingAdjust(new Date(year, month, 32)).getDate(); 15261 }, 15262 15263 /* Find the day of the week of the first of a month. */ 15264 _getFirstDayOfMonth: function(year, month) { 15265 return new Date(year, month, 1).getDay(); 15266 }, 15267 15268 /* Determines if we should allow a "next/prev" month display change. */ 15269 _canAdjustMonth: function(inst, offset, curYear, curMonth) { 15270 var numMonths = this._getNumberOfMonths(inst), 15271 date = this._daylightSavingAdjust(new Date(curYear, 15272 curMonth + (offset < 0 ? offset : numMonths[0] * numMonths[1]), 1)); 15273 15274 if (offset < 0) { 15275 date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth())); 15276 } 15277 return this._isInRange(inst, date); 15278 }, 15279 15280 /* Is the given date in the accepted range? */ 15281 _isInRange: function(inst, date) { 15282 var yearSplit, currentYear, 15283 minDate = this._getMinMaxDate(inst, "min"), 15284 maxDate = this._getMinMaxDate(inst, "max"), 15285 minYear = null, 15286 maxYear = null, 15287 years = this._get(inst, "yearRange"); 15288 if (years){ 15289 yearSplit = years.split(":"); 15290 currentYear = new Date().getFullYear(); 15291 minYear = parseInt(yearSplit[0], 10); 15292 maxYear = parseInt(yearSplit[1], 10); 15293 if ( yearSplit[0].match(/[+\-].*/) ) { 15294 minYear += currentYear; 15295 } 15296 if ( yearSplit[1].match(/[+\-].*/) ) { 15297 maxYear += currentYear; 15298 } 15299 } 15300 15301 return ((!minDate || date.getTime() >= minDate.getTime()) && 15302 (!maxDate || date.getTime() <= maxDate.getTime()) && 15303 (!minYear || date.getFullYear() >= minYear) && 15304 (!maxYear || date.getFullYear() <= maxYear)); 15305 }, 15306 15307 /* Provide the configuration settings for formatting/parsing. */ 15308 _getFormatConfig: function(inst) { 15309 var shortYearCutoff = this._get(inst, "shortYearCutoff"); 15310 shortYearCutoff = (typeof shortYearCutoff !== "string" ? shortYearCutoff : 15311 new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10)); 15312 return {shortYearCutoff: shortYearCutoff, 15313 dayNamesShort: this._get(inst, "dayNamesShort"), dayNames: this._get(inst, "dayNames"), 15314 monthNamesShort: this._get(inst, "monthNamesShort"), monthNames: this._get(inst, "monthNames")}; 15315 }, 15316 15317 /* Format the given date for display. */ 15318 _formatDate: function(inst, day, month, year) { 15319 if (!day) { 15320 inst.currentDay = inst.selectedDay; 15321 inst.currentMonth = inst.selectedMonth; 15322 inst.currentYear = inst.selectedYear; 15323 } 15324 var date = (day ? (typeof day === "object" ? day : 15325 this._daylightSavingAdjust(new Date(year, month, day))) : 15326 this._daylightSavingAdjust(new Date(inst.currentYear, inst.currentMonth, inst.currentDay))); 15327 return this.formatDate(this._get(inst, "dateFormat"), date, this._getFormatConfig(inst)); 15328 } 15329}); 15330 15331/* 15332 * Bind hover events for datepicker elements. 15333 * Done via delegate so the binding only occurs once in the lifetime of the parent div. 15334 * Global instActive, set by _updateDatepicker allows the handlers to find their way back to the active picker. 15335 */ 15336function bindHover(dpDiv) { 15337 var selector = "button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a"; 15338 return dpDiv.delegate(selector, "mouseout", function() { 15339 $(this).removeClass("ui-state-hover"); 15340 if (this.className.indexOf("ui-datepicker-prev") !== -1) { 15341 $(this).removeClass("ui-datepicker-prev-hover"); 15342 } 15343 if (this.className.indexOf("ui-datepicker-next") !== -1) { 15344 $(this).removeClass("ui-datepicker-next-hover"); 15345 } 15346 }) 15347 .delegate(selector, "mouseover", function(){ 15348 if (!$.datepicker._isDisabledDatepicker( instActive.inline ? dpDiv.parent()[0] : instActive.input[0])) { 15349 $(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"); 15350 $(this).addClass("ui-state-hover"); 15351 if (this.className.indexOf("ui-datepicker-prev") !== -1) { 15352 $(this).addClass("ui-datepicker-prev-hover"); 15353 } 15354 if (this.className.indexOf("ui-datepicker-next") !== -1) { 15355 $(this).addClass("ui-datepicker-next-hover"); 15356 } 15357 } 15358 }); 15359} 15360 15361/* jQuery extend now ignores nulls! */ 15362function extendRemove(target, props) { 15363 $.extend(target, props); 15364 for (var name in props) { 15365 if (props[name] == null) { 15366 target[name] = props[name]; 15367 } 15368 } 15369 return target; 15370} 15371 15372/* Invoke the datepicker functionality. 15373 @param options string - a command, optionally followed by additional parameters or 15374 Object - settings for attaching new datepicker functionality 15375 @return jQuery object */ 15376$.fn.datepicker = function(options){ 15377 15378 /* Verify an empty collection wasn't passed - Fixes #6976 */ 15379 if ( !this.length ) { 15380 return this; 15381 } 15382 15383 /* Initialise the date picker. */ 15384 if (!$.datepicker.initialized) { 15385 $(document).mousedown($.datepicker._checkExternalClick); 15386 $.datepicker.initialized = true; 15387 } 15388 15389 /* Append datepicker main container to body if not exist. */ 15390 if ($("#"+$.datepicker._mainDivId).length === 0) { 15391 $("body").append($.datepicker.dpDiv); 15392 } 15393 15394 var otherArgs = Array.prototype.slice.call(arguments, 1); 15395 if (typeof options === "string" && (options === "isDisabled" || options === "getDate" || options === "widget")) { 15396 return $.datepicker["_" + options + "Datepicker"]. 15397 apply($.datepicker, [this[0]].concat(otherArgs)); 15398 } 15399 if (options === "option" && arguments.length === 2 && typeof arguments[1] === "string") { 15400 return $.datepicker["_" + options + "Datepicker"]. 15401 apply($.datepicker, [this[0]].concat(otherArgs)); 15402 } 15403 return this.each(function() { 15404 typeof options === "string" ? 15405 $.datepicker["_" + options + "Datepicker"]. 15406 apply($.datepicker, [this].concat(otherArgs)) : 15407 $.datepicker._attachDatepicker(this, options); 15408 }); 15409}; 15410 15411$.datepicker = new Datepicker(); // singleton instance 15412$.datepicker.initialized = false; 15413$.datepicker.uuid = new Date().getTime(); 15414$.datepicker.version = "1.10.3"; 15415 15416})(jQuery);
vendor: 55,650 bytes, lines 15417-17269
15417 15418 15419 15420/*! 15421 * jQuery UI Mouse 1.10.3 15422 * http://jqueryui.com 15423 * 15424 * Copyright 2013 jQuery Foundation and other contributors 15425 * Released under the MIT license. 15426 * http://jquery.org/license 15427 * 15428 * http://api.jqueryui.com/mouse/ 15429 * 15430 * Depends: 15431 * jquery.ui.widget.js 15432 */ 15433 15434(function( $, undefined ) { 15435 15436var mouseHandled = false; 15437$( document ).mouseup( function() { 15438 mouseHandled = false; 15439}); 15440 15441$.widget("ui.mouse", { 15442 version: "1.10.3", 15443 options: { 15444 cancel: "input,textarea,button,select,option", 15445 distance: 1, 15446 delay: 0 15447 }, 15448 _mouseInit: function() { 15449 var that = this; 15450 15451 this.element 15452 .bind("mousedown."+this.widgetName, function(event) { 15453 return that._mouseDown(event); 15454 }) 15455 .bind("click."+this.widgetName, function(event) { 15456 if (true === $.data(event.target, that.widgetName + ".preventClickEvent")) { 15457 $.removeData(event.target, that.widgetName + ".preventClickEvent"); 15458 event.stopImmediatePropagation(); 15459 return false; 15460 } 15461 }); 15462 15463 this.started = false; 15464 }, 15465 15466 // TODO: make sure destroying one instance of mouse doesn't mess with 15467 // other instances of mouse 15468 _mouseDestroy: function() { 15469 this.element.unbind("."+this.widgetName); 15470 if ( this._mouseMoveDelegate ) { 15471 $(document) 15472 .unbind("mousemove."+this.widgetName, this._mouseMoveDelegate) 15473 .unbind("mouseup."+this.widgetName, this._mouseUpDelegate); 15474 } 15475 }, 15476 15477 _mouseDown: function(event) { 15478 // don't let more than one widget handle mouseStart 15479 if( mouseHandled ) { return; } 15480 15481 // we may have missed mouseup (out of window) 15482 (this._mouseStarted && this._mouseUp(event)); 15483 15484 this._mouseDownEvent = event; 15485 15486 var that = this, 15487 btnIsLeft = (event.which === 1), 15488 // event.target.nodeName works around a bug in IE 8 with 15489 // disabled inputs (#7620) 15490 elIsCancel = (typeof this.options.cancel === "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false); 15491 if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) { 15492 return true; 15493 } 15494 15495 this.mouseDelayMet = !this.options.delay; 15496 if (!this.mouseDelayMet) { 15497 this._mouseDelayTimer = setTimeout(function() { 15498 that.mouseDelayMet = true; 15499 }, this.options.delay); 15500 } 15501 15502 if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) { 15503 this._mouseStarted = (this._mouseStart(event) !== false); 15504 if (!this._mouseStarted) { 15505 event.preventDefault(); 15506 return true; 15507 } 15508 } 15509 15510 // Click event may never have fired (Gecko & Opera) 15511 if (true === $.data(event.target, this.widgetName + ".preventClickEvent")) { 15512 $.removeData(event.target, this.widgetName + ".preventClickEvent"); 15513 } 15514 15515 // these delegates are required to keep context 15516 this._mouseMoveDelegate = function(event) { 15517 return that._mouseMove(event); 15518 }; 15519 this._mouseUpDelegate = function(event) { 15520 return that._mouseUp(event); 15521 }; 15522 $(document) 15523 .bind("mousemove."+this.widgetName, this._mouseMoveDelegate) 15524 .bind("mouseup."+this.widgetName, this._mouseUpDelegate); 15525 15526 event.preventDefault(); 15527 15528 mouseHandled = true; 15529 return true; 15530 }, 15531 15532 _mouseMove: function(event) { 15533 // IE mouseup check - mouseup happened when mouse was out of window 15534 if ($.ui.ie && ( !document.documentMode || document.documentMode < 9 ) && !event.button) { 15535 return this._mouseUp(event); 15536 } 15537 15538 if (this._mouseStarted) { 15539 this._mouseDrag(event); 15540 return event.preventDefault(); 15541 } 15542 15543 if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) { 15544 this._mouseStarted = 15545 (this._mouseStart(this._mouseDownEvent, event) !== false); 15546 (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event)); 15547 } 15548 15549 return !this._mouseStarted; 15550 }, 15551 15552 _mouseUp: function(event) { 15553 $(document) 15554 .unbind("mousemove."+this.widgetName, this._mouseMoveDelegate) 15555 .unbind("mouseup."+this.widgetName, this._mouseUpDelegate); 15556 15557 if (this._mouseStarted) { 15558 this._mouseStarted = false; 15559 15560 if (event.target === this._mouseDownEvent.target) { 15561 $.data(event.target, this.widgetName + ".preventClickEvent", true); 15562 } 15563 15564 this._mouseStop(event); 15565 } 15566 15567 return false; 15568 }, 15569 15570 _mouseDistanceMet: function(event) { 15571 return (Math.max( 15572 Math.abs(this._mouseDownEvent.pageX - event.pageX), 15573 Math.abs(this._mouseDownEvent.pageY - event.pageY) 15574 ) >= this.options.distance 15575 ); 15576 }, 15577 15578 _mouseDelayMet: function(/* event */) { 15579 return this.mouseDelayMet; 15580 }, 15581 15582 // These are placeholder methods, to be overriden by extending plugin 15583 _mouseStart: function(/* event */) {}, 15584 _mouseDrag: function(/* event */) {}, 15585 _mouseStop: function(/* event */) {}, 15586 _mouseCapture: function(/* event */) { return true; } 15587}); 15588 15589})(jQuery); 15590 15591 15592 15593 15594/*! 15595 * jQuery UI Draggable 1.10.3 15596 * http://jqueryui.com 15597 * 15598 * Copyright 2013 jQuery Foundation and other contributors 15599 * Released under the MIT license. 15600 * http://jquery.org/license 15601 * 15602 * http://api.jqueryui.com/draggable/ 15603 * 15604 * Depends: 15605 * jquery.ui.core.js 15606 * jquery.ui.mouse.js 15607 * jquery.ui.widget.js 15608 */ 15609 15610(function( $, undefined ) { 15611 15612$.widget("ui.draggable", $.ui.mouse, { 15613 version: "1.10.3", 15614 widgetEventPrefix: "drag", 15615 options: { 15616 addClasses: true, 15617 appendTo: "parent", 15618 axis: false, 15619 connectToSortable: false, 15620 containment: false, 15621 cursor: "auto", 15622 cursorAt: false, 15623 grid: false, 15624 handle: false, 15625 helper: "original", 15626 iframeFix: false, 15627 opacity: false, 15628 refreshPositions: false, 15629 revert: false, 15630 revertDuration: 500, 15631 scope: "default", 15632 scroll: true, 15633 scrollSensitivity: 20, 15634 scrollSpeed: 20, 15635 snap: false, 15636 snapMode: "both", 15637 snapTolerance: 20, 15638 stack: false, 15639 zIndex: false, 15640 15641 // callbacks 15642 drag: null, 15643 start: null, 15644 stop: null 15645 }, 15646 _create: function() { 15647 15648 if (this.options.helper === "original" && !(/^(?:r|a|f)/).test(this.element.css("position"))) { 15649 this.element[0].style.position = "relative"; 15650 } 15651 if (this.options.addClasses){ 15652 this.element.addClass("ui-draggable"); 15653 } 15654 if (this.options.disabled){ 15655 this.element.addClass("ui-draggable-disabled"); 15656 } 15657 15658 this._mouseInit(); 15659 15660 }, 15661 15662 _destroy: function() { 15663 this.element.removeClass( "ui-draggable ui-draggable-dragging ui-draggable-disabled" ); 15664 this._mouseDestroy(); 15665 }, 15666 15667 _mouseCapture: function(event) { 15668 15669 var o = this.options; 15670 15671 // among others, prevent a drag on a resizable-handle 15672 if (this.helper || o.disabled || $(event.target).closest(".ui-resizable-handle").length > 0) { 15673 return false; 15674 } 15675 15676 //Quit if we're not on a valid handle 15677 this.handle = this._getHandle(event); 15678 if (!this.handle) { 15679 return false; 15680 } 15681 15682 $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() { 15683 $("<div class='ui-draggable-iframeFix' style='background: #fff;'></div>") 15684 .css({ 15685 width: this.offsetWidth+"px", height: this.offsetHeight+"px", 15686 position: "absolute", opacity: "0.001", zIndex: 1000 15687 }) 15688 .css($(this).offset()) 15689 .appendTo("body"); 15690 }); 15691 15692 return true; 15693 15694 }, 15695 15696 _mouseStart: function(event) { 15697 15698 var o = this.options; 15699 15700 //Create and append the visible helper 15701 this.helper = this._createHelper(event); 15702 15703 this.helper.addClass("ui-draggable-dragging"); 15704 15705 //Cache the helper size 15706 this._cacheHelperProportions(); 15707 15708 //If ddmanager is used for droppables, set the global draggable 15709 if($.ui.ddmanager) { 15710 $.ui.ddmanager.current = this; 15711 } 15712 15713 /* 15714 * - Position generation - 15715 * This block generates everything position related - it's the core of draggables. 15716 */ 15717 15718 //Cache the margins of the original element 15719 this._cacheMargins(); 15720 15721 //Store the helper's css position 15722 this.cssPosition = this.helper.css( "position" ); 15723 this.scrollParent = this.helper.scrollParent(); 15724 this.offsetParent = this.helper.offsetParent(); 15725 this.offsetParentCssPosition = this.offsetParent.css( "position" ); 15726 15727 //The element's absolute position on the page minus margins 15728 this.offset = this.positionAbs = this.element.offset(); 15729 this.offset = { 15730 top: this.offset.top - this.margins.top, 15731 left: this.offset.left - this.margins.left 15732 }; 15733 15734 //Reset scroll cache 15735 this.offset.scroll = false; 15736 15737 $.extend(this.offset, { 15738 click: { //Where the click happened, relative to the element 15739 left: event.pageX - this.offset.left, 15740 top: event.pageY - this.offset.top 15741 }, 15742 parent: this._getParentOffset(), 15743 relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper 15744 }); 15745 15746 //Generate the original position 15747 this.originalPosition = this.position = this._generatePosition(event); 15748 this.originalPageX = event.pageX; 15749 this.originalPageY = event.pageY; 15750 15751 //Adjust the mouse offset relative to the helper if "cursorAt" is supplied 15752 (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt)); 15753 15754 //Set a containment if given in the options 15755 this._setContainment(); 15756 15757 //Trigger event + callbacks 15758 if(this._trigger("start", event) === false) { 15759 this._clear(); 15760 return false; 15761 } 15762 15763 //Recache the helper size 15764 this._cacheHelperProportions(); 15765 15766 //Prepare the droppable offsets 15767 if ($.ui.ddmanager && !o.dropBehaviour) { 15768 $.ui.ddmanager.prepareOffsets(this, event); 15769 } 15770 15771 15772 this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position 15773 15774 //If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003) 15775 if ( $.ui.ddmanager ) { 15776 $.ui.ddmanager.dragStart(this, event); 15777 } 15778 15779 return true; 15780 }, 15781 15782 _mouseDrag: function(event, noPropagation) { 15783 // reset any necessary cached properties (see #5009) 15784 if ( this.offsetParentCssPosition === "fixed" ) { 15785 this.offset.parent = this._getParentOffset(); 15786 } 15787 15788 //Compute the helpers position 15789 this.position = this._generatePosition(event); 15790 this.positionAbs = this._convertPositionTo("absolute"); 15791 15792 //Call plugins and callbacks and use the resulting position if something is returned 15793 if (!noPropagation) { 15794 var ui = this._uiHash(); 15795 if(this._trigger("drag", event, ui) === false) { 15796 this._mouseUp({}); 15797 return false; 15798 } 15799 this.position = ui.position; 15800 } 15801 15802 if(!this.options.axis || this.options.axis !== "y") { 15803 this.helper[0].style.left = this.position.left+"px"; 15804 } 15805 if(!this.options.axis || this.options.axis !== "x") { 15806 this.helper[0].style.top = this.position.top+"px"; 15807 } 15808 if($.ui.ddmanager) { 15809 $.ui.ddmanager.drag(this, event); 15810 } 15811 15812 return false; 15813 }, 15814 15815 _mouseStop: function(event) { 15816 15817 //If we are using droppables, inform the manager about the drop 15818 var that = this, 15819 dropped = false; 15820 if ($.ui.ddmanager && !this.options.dropBehaviour) { 15821 dropped = $.ui.ddmanager.drop(this, event); 15822 } 15823 15824 //if a drop comes from outside (a sortable) 15825 if(this.dropped) { 15826 dropped = this.dropped; 15827 this.dropped = false; 15828 } 15829 15830 //if the original element is no longer in the DOM don't bother to continue (see #8269) 15831 if ( this.options.helper === "original" && !$.contains( this.element[ 0 ].ownerDocument, this.element[ 0 ] ) ) { 15832 return false; 15833 } 15834 15835 if((this.options.revert === "invalid" && !dropped) || (this.options.revert === "valid" && dropped) || this.options.revert === true || ($.isFunction(this.options.revert) && this.options.revert.call(this.element, dropped))) { 15836 $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() { 15837 if(that._trigger("stop", event) !== false) { 15838 that._clear(); 15839 } 15840 }); 15841 } else { 15842 if(this._trigger("stop", event) !== false) { 15843 this._clear(); 15844 } 15845 } 15846 15847 return false; 15848 }, 15849 15850 _mouseUp: function(event) { 15851 //Remove frame helpers 15852 $("div.ui-draggable-iframeFix").each(function() { 15853 this.parentNode.removeChild(this); 15854 }); 15855 15856 //If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003) 15857 if( $.ui.ddmanager ) { 15858 $.ui.ddmanager.dragStop(this, event); 15859 } 15860 15861 return $.ui.mouse.prototype._mouseUp.call(this, event); 15862 }, 15863 15864 cancel: function() { 15865 15866 if(this.helper.is(".ui-draggable-dragging")) { 15867 this._mouseUp({}); 15868 } else { 15869 this._clear(); 15870 } 15871 15872 return this; 15873 15874 }, 15875 15876 _getHandle: function(event) { 15877 return this.options.handle ? 15878 !!$( event.target ).closest( this.element.find( this.options.handle ) ).length : 15879 true; 15880 }, 15881 15882 _createHelper: function(event) { 15883 15884 var o = this.options, 15885 helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper === "clone" ? this.element.clone().removeAttr("id") : this.element); 15886 15887 if(!helper.parents("body").length) { 15888 helper.appendTo((o.appendTo === "parent" ? this.element[0].parentNode : o.appendTo)); 15889 } 15890 15891 if(helper[0] !== this.element[0] && !(/(fixed|absolute)/).test(helper.css("position"))) { 15892 helper.css("position", "absolute"); 15893 } 15894 15895 return helper; 15896 15897 }, 15898 15899 _adjustOffsetFromHelper: function(obj) { 15900 if (typeof obj === "string") { 15901 obj = obj.split(" "); 15902 } 15903 if ($.isArray(obj)) { 15904 obj = {left: +obj[0], top: +obj[1] || 0}; 15905 } 15906 if ("left" in obj) { 15907 this.offset.click.left = obj.left + this.margins.left; 15908 } 15909 if ("right" in obj) { 15910 this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left; 15911 } 15912 if ("top" in obj) { 15913 this.offset.click.top = obj.top + this.margins.top; 15914 } 15915 if ("bottom" in obj) { 15916 this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top; 15917 } 15918 }, 15919 15920 _getParentOffset: function() { 15921 15922 //Get the offsetParent and cache its position 15923 var po = this.offsetParent.offset(); 15924 15925 // This is a special case where we need to modify a offset calculated on start, since the following happened: 15926 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent 15927 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that 15928 // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag 15929 if(this.cssPosition === "absolute" && this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) { 15930 po.left += this.scrollParent.scrollLeft(); 15931 po.top += this.scrollParent.scrollTop(); 15932 } 15933 15934 //This needs to be actually done for all browsers, since pageX/pageY includes this information 15935 //Ugly IE fix 15936 if((this.offsetParent[0] === document.body) || 15937 (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() === "html" && $.ui.ie)) { 15938 po = { top: 0, left: 0 }; 15939 } 15940 15941 return { 15942 top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0), 15943 left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0) 15944 }; 15945 15946 }, 15947 15948 _getRelativeOffset: function() { 15949 15950 if(this.cssPosition === "relative") { 15951 var p = this.element.position(); 15952 return { 15953 top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(), 15954 left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft() 15955 }; 15956 } else { 15957 return { top: 0, left: 0 }; 15958 } 15959 15960 }, 15961 15962 _cacheMargins: function() { 15963 this.margins = { 15964 left: (parseInt(this.element.css("marginLeft"),10) || 0), 15965 top: (parseInt(this.element.css("marginTop"),10) || 0), 15966 right: (parseInt(this.element.css("marginRight"),10) || 0), 15967 bottom: (parseInt(this.element.css("marginBottom"),10) || 0) 15968 }; 15969 }, 15970 15971 _cacheHelperProportions: function() { 15972 this.helperProportions = { 15973 width: this.helper.outerWidth(), 15974 height: this.helper.outerHeight() 15975 }; 15976 }, 15977 15978 _setContainment: function() { 15979 15980 var over, c, ce, 15981 o = this.options; 15982 15983 if ( !o.containment ) { 15984 this.containment = null; 15985 return; 15986 } 15987 15988 if ( o.containment === "window" ) { 15989 this.containment = [ 15990 $( window ).scrollLeft() - this.offset.relative.left - this.offset.parent.left, 15991 $( window ).scrollTop() - this.offset.relative.top - this.offset.parent.top, 15992 $( window ).scrollLeft() + $( window ).width() - this.helperProportions.width - this.margins.left, 15993 $( window ).scrollTop() + ( $( window ).height() || document.body.parentNode.scrollHeight ) - this.helperProportions.height - this.margins.top 15994 ]; 15995 return; 15996 } 15997 15998 if ( o.containment === "document") { 15999 this.containment = [ 16000 0, 16001 0, 16002 $( document ).width() - this.helperProportions.width - this.margins.left, 16003 ( $( document ).height() || document.body.parentNode.scrollHeight ) - this.helperProportions.height - this.margins.top 16004 ]; 16005 return; 16006 } 16007 16008 if ( o.containment.constructor === Array ) { 16009 this.containment = o.containment; 16010 return; 16011 } 16012 16013 if ( o.containment === "parent" ) { 16014 o.containment = this.helper[ 0 ].parentNode; 16015 } 16016 16017 c = $( o.containment ); 16018 ce = c[ 0 ]; 16019 16020 if( !ce ) { 16021 return; 16022 } 16023 16024 over = c.css( "overflow" ) !== "hidden"; 16025 16026 this.containment = [ 16027 ( parseInt( c.css( "borderLeftWidth" ), 10 ) || 0 ) + ( parseInt( c.css( "paddingLeft" ), 10 ) || 0 ), 16028 ( parseInt( c.css( "borderTopWidth" ), 10 ) || 0 ) + ( parseInt( c.css( "paddingTop" ), 10 ) || 0 ) , 16029 ( over ? Math.max( ce.scrollWidth, ce.offsetWidth ) : ce.offsetWidth ) - ( parseInt( c.css( "borderRightWidth" ), 10 ) || 0 ) - ( parseInt( c.css( "paddingRight" ), 10 ) || 0 ) - this.helperProportions.width - this.margins.left - this.margins.right, 16030 ( over ? Math.max( ce.scrollHeight, ce.offsetHeight ) : ce.offsetHeight ) - ( parseInt( c.css( "borderBottomWidth" ), 10 ) || 0 ) - ( parseInt( c.css( "paddingBottom" ), 10 ) || 0 ) - this.helperProportions.height - this.margins.top - this.margins.bottom 16031 ]; 16032 this.relative_container = c; 16033 }, 16034 16035 _convertPositionTo: function(d, pos) { 16036 16037 if(!pos) { 16038 pos = this.position; 16039 } 16040 16041 var mod = d === "absolute" ? 1 : -1, 16042 scroll = this.cssPosition === "absolute" && !( this.scrollParent[ 0 ] !== document && $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ? this.offsetParent : this.scrollParent; 16043 16044 //Cache the scroll 16045 if (!this.offset.scroll) { 16046 this.offset.scroll = {top : scroll.scrollTop(), left : scroll.scrollLeft()}; 16047 } 16048 16049 return { 16050 top: ( 16051 pos.top + // The absolute mouse position 16052 this.offset.relative.top * mod + // Only for relative positioned nodes: Relative offset from element to offset parent 16053 this.offset.parent.top * mod - // The offsetParent's offset without borders (offset + border) 16054 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : this.offset.scroll.top ) * mod ) 16055 ), 16056 left: ( 16057 pos.left + // The absolute mouse position 16058 this.offset.relative.left * mod + // Only for relative positioned nodes: Relative offset from element to offset parent 16059 this.offset.parent.left * mod - // The offsetParent's offset without borders (offset + border) 16060 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : this.offset.scroll.left ) * mod ) 16061 ) 16062 }; 16063 16064 }, 16065 16066 _generatePosition: function(event) { 16067 16068 var containment, co, top, left, 16069 o = this.options, 16070 scroll = this.cssPosition === "absolute" && !( this.scrollParent[ 0 ] !== document && $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ? this.offsetParent : this.scrollParent, 16071 pageX = event.pageX, 16072 pageY = event.pageY; 16073 16074 //Cache the scroll 16075 if (!this.offset.scroll) { 16076 this.offset.scroll = {top : scroll.scrollTop(), left : scroll.scrollLeft()}; 16077 } 16078 16079 /* 16080 * - Position constraining - 16081 * Constrain the position to a mix of grid, containment. 16082 */ 16083 16084 // If we are not dragging yet, we won't check for options 16085 if ( this.originalPosition ) { 16086 if ( this.containment ) { 16087 if ( this.relative_container ){ 16088 co = this.relative_container.offset(); 16089 containment = [ 16090 this.containment[ 0 ] + co.left, 16091 this.containment[ 1 ] + co.top, 16092 this.containment[ 2 ] + co.left, 16093 this.containment[ 3 ] + co.top 16094 ]; 16095 } 16096 else { 16097 containment = this.containment; 16098 } 16099 16100 if(event.pageX - this.offset.click.left < containment[0]) { 16101 pageX = containment[0] + this.offset.click.left; 16102 } 16103 if(event.pageY - this.offset.click.top < containment[1]) { 16104 pageY = containment[1] + this.offset.click.top; 16105 } 16106 if(event.pageX - this.offset.click.left > containment[2]) { 16107 pageX = containment[2] + this.offset.click.left; 16108 } 16109 if(event.pageY - this.offset.click.top > containment[3]) { 16110 pageY = containment[3] + this.offset.click.top; 16111 } 16112 } 16113 16114 if(o.grid) { 16115 //Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950) 16116 top = o.grid[1] ? this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1] : this.originalPageY; 16117 pageY = containment ? ((top - this.offset.click.top >= containment[1] || top - this.offset.click.top > containment[3]) ? top : ((top - this.offset.click.top >= containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top; 16118 16119 left = o.grid[0] ? this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0] : this.originalPageX; 16120 pageX = containment ? ((left - this.offset.click.left >= containment[0] || left - this.offset.click.left > containment[2]) ? left : ((left - this.offset.click.left >= containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left; 16121 } 16122 16123 } 16124 16125 return { 16126 top: ( 16127 pageY - // The absolute mouse position 16128 this.offset.click.top - // Click offset (relative to the element) 16129 this.offset.relative.top - // Only for relative positioned nodes: Relative offset from element to offset parent 16130 this.offset.parent.top + // The offsetParent's offset without borders (offset + border) 16131 ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : this.offset.scroll.top ) 16132 ), 16133 left: ( 16134 pageX - // The absolute mouse position 16135 this.offset.click.left - // Click offset (relative to the element) 16136 this.offset.relative.left - // Only for relative positioned nodes: Relative offset from element to offset parent 16137 this.offset.parent.left + // The offsetParent's offset without borders (offset + border) 16138 ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : this.offset.scroll.left ) 16139 ) 16140 }; 16141 16142 }, 16143 16144 _clear: function() { 16145 this.helper.removeClass("ui-draggable-dragging"); 16146 if(this.helper[0] !== this.element[0] && !this.cancelHelperRemoval) { 16147 this.helper.remove(); 16148 } 16149 this.helper = null; 16150 this.cancelHelperRemoval = false; 16151 }, 16152 16153 // From now on bulk stuff - mainly helpers 16154 16155 _trigger: function(type, event, ui) { 16156 ui = ui || this._uiHash(); 16157 $.ui.plugin.call(this, type, [event, ui]); 16158 //The absolute position has to be recalculated after plugins 16159 if(type === "drag") { 16160 this.positionAbs = this._convertPositionTo("absolute"); 16161 } 16162 return $.Widget.prototype._trigger.call(this, type, event, ui); 16163 }, 16164 16165 plugins: {}, 16166 16167 _uiHash: function() { 16168 return { 16169 helper: this.helper, 16170 position: this.position, 16171 originalPosition: this.originalPosition, 16172 offset: this.positionAbs 16173 }; 16174 } 16175 16176}); 16177 16178$.ui.plugin.add("draggable", "connectToSortable", { 16179 start: function(event, ui) { 16180 16181 var inst = $(this).data("ui-draggable"), o = inst.options, 16182 uiSortable = $.extend({}, ui, { item: inst.element }); 16183 inst.sortables = []; 16184 $(o.connectToSortable).each(function() { 16185 var sortable = $.data(this, "ui-sortable"); 16186 if (sortable && !sortable.options.disabled) { 16187 inst.sortables.push({ 16188 instance: sortable, 16189 shouldRevert: sortable.options.revert 16190 }); 16191 sortable.refreshPositions(); // Call the sortable's refreshPositions at drag start to refresh the containerCache since the sortable container cache is used in drag and needs to be up to date (this will ensure it's initialised as well as being kept in step with any changes that might have happened on the page). 16192 sortable._trigger("activate", event, uiSortable); 16193 } 16194 }); 16195 16196 }, 16197 stop: function(event, ui) { 16198 16199 //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper 16200 var inst = $(this).data("ui-draggable"), 16201 uiSortable = $.extend({}, ui, { item: inst.element }); 16202 16203 $.each(inst.sortables, function() { 16204 if(this.instance.isOver) { 16205 16206 this.instance.isOver = 0; 16207 16208 inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance 16209 this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work) 16210 16211 //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: "valid/invalid" 16212 if(this.shouldRevert) { 16213 this.instance.options.revert = this.shouldRevert; 16214 } 16215 16216 //Trigger the stop of the sortable 16217 this.instance._mouseStop(event); 16218 16219 this.instance.options.helper = this.instance.options._helper; 16220 16221 //If the helper has been the original item, restore properties in the sortable 16222 if(inst.options.helper === "original") { 16223 this.instance.currentItem.css({ top: "auto", left: "auto" }); 16224 } 16225 16226 } else { 16227 this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance 16228 this.instance._trigger("deactivate", event, uiSortable); 16229 } 16230 16231 }); 16232 16233 }, 16234 drag: function(event, ui) { 16235 16236 var inst = $(this).data("ui-draggable"), that = this; 16237 16238 $.each(inst.sortables, function() { 16239 16240 var innermostIntersecting = false, 16241 thisSortable = this; 16242 16243 //Copy over some variables to allow calling the sortable's native _intersectsWith 16244 this.instance.positionAbs = inst.positionAbs; 16245 this.instance.helperProportions = inst.helperProportions; 16246 this.instance.offset.click = inst.offset.click; 16247 16248 if(this.instance._intersectsWith(this.instance.containerCache)) { 16249 innermostIntersecting = true; 16250 $.each(inst.sortables, function () { 16251 this.instance.positionAbs = inst.positionAbs; 16252 this.instance.helperProportions = inst.helperProportions; 16253 this.instance.offset.click = inst.offset.click; 16254 if (this !== thisSortable && 16255 this.instance._intersectsWith(this.instance.containerCache) && 16256 $.contains(thisSortable.instance.element[0], this.instance.element[0]) 16257 ) { 16258 innermostIntersecting = false; 16259 } 16260 return innermostIntersecting; 16261 }); 16262 } 16263 16264 16265 if(innermostIntersecting) { 16266 //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once 16267 if(!this.instance.isOver) { 16268 16269 this.instance.isOver = 1; 16270 //Now we fake the start of dragging for the sortable instance, 16271 //by cloning the list group item, appending it to the sortable and using it as inst.currentItem 16272 //We can then fire the start event of the sortable with our passed browser event, and our own helper (so it doesn't create a new one) 16273 this.instance.currentItem = $(that).clone().removeAttr("id").appendTo(this.instance.element).data("ui-sortable-item", true); 16274 this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it 16275 this.instance.options.helper = function() { return ui.helper[0]; }; 16276 16277 event.target = this.instance.currentItem[0]; 16278 this.instance._mouseCapture(event, true); 16279 this.instance._mouseStart(event, true, true); 16280 16281 //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes 16282 this.instance.offset.click.top = inst.offset.click.top; 16283 this.instance.offset.click.left = inst.offset.click.left; 16284 this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left; 16285 this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top; 16286 16287 inst._trigger("toSortable", event); 16288 inst.dropped = this.instance.element; //draggable revert needs that 16289 //hack so receive/update callbacks work (mostly) 16290 inst.currentItem = inst.element; 16291 this.instance.fromOutside = inst; 16292 16293 } 16294 16295 //Provided we did all the previous steps, we can fire the drag event of the sortable on every draggable drag, when it intersects with the sortable 16296 if(this.instance.currentItem) { 16297 this.instance._mouseDrag(event); 16298 } 16299 16300 } else { 16301 16302 //If it doesn't intersect with the sortable, and it intersected before, 16303 //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval 16304 if(this.instance.isOver) { 16305 16306 this.instance.isOver = 0; 16307 this.instance.cancelHelperRemoval = true; 16308 16309 //Prevent reverting on this forced stop 16310 this.instance.options.revert = false; 16311 16312 // The out event needs to be triggered independently 16313 this.instance._trigger("out", event, this.instance._uiHash(this.instance)); 16314 16315 this.instance._mouseStop(event, true); 16316 this.instance.options.helper = this.instance.options._helper; 16317 16318 //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size 16319 this.instance.currentItem.remove(); 16320 if(this.instance.placeholder) { 16321 this.instance.placeholder.remove(); 16322 } 16323 16324 inst._trigger("fromSortable", event); 16325 inst.dropped = false; //draggable revert needs that 16326 } 16327 16328 } 16329 16330 }); 16331 16332 } 16333}); 16334 16335$.ui.plugin.add("draggable", "cursor", { 16336 start: function() { 16337 var t = $("body"), o = $(this).data("ui-draggable").options; 16338 if (t.css("cursor")) { 16339 o._cursor = t.css("cursor"); 16340 } 16341 t.css("cursor", o.cursor); 16342 }, 16343 stop: function() { 16344 var o = $(this).data("ui-draggable").options; 16345 if (o._cursor) { 16346 $("body").css("cursor", o._cursor); 16347 } 16348 } 16349}); 16350 16351$.ui.plugin.add("draggable", "opacity", { 16352 start: function(event, ui) { 16353 var t = $(ui.helper), o = $(this).data("ui-draggable").options; 16354 if(t.css("opacity")) { 16355 o._opacity = t.css("opacity"); 16356 } 16357 t.css("opacity", o.opacity); 16358 }, 16359 stop: function(event, ui) { 16360 var o = $(this).data("ui-draggable").options; 16361 if(o._opacity) { 16362 $(ui.helper).css("opacity", o._opacity); 16363 } 16364 } 16365}); 16366 16367$.ui.plugin.add("draggable", "scroll", { 16368 start: function() { 16369 var i = $(this).data("ui-draggable"); 16370 if(i.scrollParent[0] !== document && i.scrollParent[0].tagName !== "HTML") { 16371 i.overflowOffset = i.scrollParent.offset(); 16372 } 16373 }, 16374 drag: function( event ) { 16375 16376 var i = $(this).data("ui-draggable"), o = i.options, scrolled = false; 16377 16378 if(i.scrollParent[0] !== document && i.scrollParent[0].tagName !== "HTML") { 16379 16380 if(!o.axis || o.axis !== "x") { 16381 if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) { 16382 i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed; 16383 } else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity) { 16384 i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed; 16385 } 16386 } 16387 16388 if(!o.axis || o.axis !== "y") { 16389 if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) { 16390 i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed; 16391 } else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity) { 16392 i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed; 16393 } 16394 } 16395 16396 } else { 16397 16398 if(!o.axis || o.axis !== "x") { 16399 if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) { 16400 scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed); 16401 } else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) { 16402 scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed); 16403 } 16404 } 16405 16406 if(!o.axis || o.axis !== "y") { 16407 if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) { 16408 scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed); 16409 } else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) { 16410 scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed); 16411 } 16412 } 16413 16414 } 16415 16416 if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) { 16417 $.ui.ddmanager.prepareOffsets(i, event); 16418 } 16419 16420 } 16421}); 16422 16423$.ui.plugin.add("draggable", "snap", { 16424 start: function() { 16425 16426 var i = $(this).data("ui-draggable"), 16427 o = i.options; 16428 16429 i.snapElements = []; 16430 16431 $(o.snap.constructor !== String ? ( o.snap.items || ":data(ui-draggable)" ) : o.snap).each(function() { 16432 var $t = $(this), 16433 $o = $t.offset(); 16434 if(this !== i.element[0]) { 16435 i.snapElements.push({ 16436 item: this, 16437 width: $t.outerWidth(), height: $t.outerHeight(), 16438 top: $o.top, left: $o.left 16439 }); 16440 } 16441 }); 16442 16443 }, 16444 drag: function(event, ui) { 16445 16446 var ts, bs, ls, rs, l, r, t, b, i, first, 16447 inst = $(this).data("ui-draggable"), 16448 o = inst.options, 16449 d = o.snapTolerance, 16450 x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width, 16451 y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height; 16452 16453 for (i = inst.snapElements.length - 1; i >= 0; i--){ 16454 16455 l = inst.snapElements[i].left; 16456 r = l + inst.snapElements[i].width; 16457 t = inst.snapElements[i].top; 16458 b = t + inst.snapElements[i].height; 16459 16460 if ( x2 < l - d || x1 > r + d || y2 < t - d || y1 > b + d || !$.contains( inst.snapElements[ i ].item.ownerDocument, inst.snapElements[ i ].item ) ) { 16461 if(inst.snapElements[i].snapping) { 16462 (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item }))); 16463 } 16464 inst.snapElements[i].snapping = false; 16465 continue; 16466 } 16467 16468 if(o.snapMode !== "inner") { 16469 ts = Math.abs(t - y2) <= d; 16470 bs = Math.abs(b - y1) <= d; 16471 ls = Math.abs(l - x2) <= d; 16472 rs = Math.abs(r - x1) <= d; 16473 if(ts) { 16474 ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top; 16475 } 16476 if(bs) { 16477 ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top; 16478 } 16479 if(ls) { 16480 ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left; 16481 } 16482 if(rs) { 16483 ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left; 16484 } 16485 } 16486 16487 first = (ts || bs || ls || rs); 16488 16489 if(o.snapMode !== "outer") { 16490 ts = Math.abs(t - y1) <= d; 16491 bs = Math.abs(b - y2) <= d; 16492 ls = Math.abs(l - x1) <= d; 16493 rs = Math.abs(r - x2) <= d; 16494 if(ts) { 16495 ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top; 16496 } 16497 if(bs) { 16498 ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top; 16499 } 16500 if(ls) { 16501 ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left; 16502 } 16503 if(rs) { 16504 ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left; 16505 } 16506 } 16507 16508 if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first)) { 16509 (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item }))); 16510 } 16511 inst.snapElements[i].snapping = (ts || bs || ls || rs || first); 16512 16513 } 16514 16515 } 16516}); 16517 16518$.ui.plugin.add("draggable", "stack", { 16519 start: function() { 16520 var min, 16521 o = this.data("ui-draggable").options, 16522 group = $.makeArray($(o.stack)).sort(function(a,b) { 16523 return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0); 16524 }); 16525 16526 if (!group.length) { return; } 16527 16528 min = parseInt($(group[0]).css("zIndex"), 10) || 0; 16529 $(group).each(function(i) { 16530 $(this).css("zIndex", min + i); 16531 }); 16532 this.css("zIndex", (min + group.length)); 16533 } 16534}); 16535 16536$.ui.plugin.add("draggable", "zIndex", { 16537 start: function(event, ui) { 16538 var t = $(ui.helper), o = $(this).data("ui-draggable").options; 16539 if(t.css("zIndex")) { 16540 o._zIndex = t.css("zIndex"); 16541 } 16542 t.css("zIndex", o.zIndex); 16543 }, 16544 stop: function(event, ui) { 16545 var o = $(this).data("ui-draggable").options; 16546 if(o._zIndex) { 16547 $(ui.helper).css("zIndex", o._zIndex); 16548 } 16549 } 16550}); 16551 16552})(jQuery); 16553 16554 16555 16556 16557/*! 16558 * jQuery UI Resizable 1.10.3 16559 * http://jqueryui.com 16560 * 16561 * Copyright 2013 jQuery Foundation and other contributors 16562 * Released under the MIT license. 16563 * http://jquery.org/license 16564 * 16565 * http://api.jqueryui.com/resizable/ 16566 * 16567 * Depends: 16568 * jquery.ui.core.js 16569 * jquery.ui.mouse.js 16570 * jquery.ui.widget.js 16571 */ 16572 16573(function( $, undefined ) { 16574 16575function num(v) { 16576 return parseInt(v, 10) || 0; 16577} 16578 16579function isNumber(value) { 16580 return !isNaN(parseInt(value, 10)); 16581} 16582 16583$.widget("ui.resizable", $.ui.mouse, { 16584 version: "1.10.3", 16585 widgetEventPrefix: "resize", 16586 options: { 16587 alsoResize: false, 16588 animate: false, 16589 animateDuration: "slow", 16590 animateEasing: "swing", 16591 aspectRatio: false, 16592 autoHide: false, 16593 containment: false, 16594 ghost: false, 16595 grid: false, 16596 handles: "e,s,se", 16597 helper: false, 16598 maxHeight: null, 16599 maxWidth: null, 16600 minHeight: 10, 16601 minWidth: 10, 16602 // See #7960 16603 zIndex: 90, 16604 16605 // callbacks 16606 resize: null, 16607 start: null, 16608 stop: null 16609 }, 16610 _create: function() { 16611 16612 var n, i, handle, axis, hname, 16613 that = this, 16614 o = this.options; 16615 this.element.addClass("ui-resizable"); 16616 16617 $.extend(this, { 16618 _aspectRatio: !!(o.aspectRatio), 16619 aspectRatio: o.aspectRatio, 16620 originalElement: this.element, 16621 _proportionallyResizeElements: [], 16622 _helper: o.helper || o.ghost || o.animate ? o.helper || "ui-resizable-helper" : null 16623 }); 16624 16625 //Wrap the element if it cannot hold child nodes 16626 if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)) { 16627 16628 //Create a wrapper element and set the wrapper to the new current internal element 16629 this.element.wrap( 16630 $("<div class='ui-wrapper' style='overflow: hidden;'></div>").css({ 16631 position: this.element.css("position"), 16632 width: this.element.outerWidth(), 16633 height: this.element.outerHeight(), 16634 top: this.element.css("top"), 16635 left: this.element.css("left") 16636 }) 16637 ); 16638 16639 //Overwrite the original this.element 16640 this.element = this.element.parent().data( 16641 "ui-resizable", this.element.data("ui-resizable") 16642 ); 16643 16644 this.elementIsWrapper = true; 16645 16646 //Move margins to the wrapper 16647 this.element.css({ marginLeft: this.originalElement.css("marginLeft"), marginTop: this.originalElement.css("marginTop"), marginRight: this.originalElement.css("marginRight"), marginBottom: this.originalElement.css("marginBottom") }); 16648 this.originalElement.css({ marginLeft: 0, marginTop: 0, marginRight: 0, marginBottom: 0}); 16649 16650 //Prevent Safari textarea resize 16651 this.originalResizeStyle = this.originalElement.css("resize"); 16652 this.originalElement.css("resize", "none"); 16653 16654 //Push the actual element to our proportionallyResize internal array 16655 this._proportionallyResizeElements.push(this.originalElement.css({ position: "static", zoom: 1, display: "block" })); 16656 16657 // avoid IE jump (hard set the margin) 16658 this.originalElement.css({ margin: this.originalElement.css("margin") }); 16659 16660 // fix handlers offset 16661 this._proportionallyResize(); 16662 16663 } 16664 16665 this.handles = o.handles || (!$(".ui-resizable-handle", this.element).length ? "e,s,se" : { n: ".ui-resizable-n", e: ".ui-resizable-e", s: ".ui-resizable-s", w: ".ui-resizable-w", se: ".ui-resizable-se", sw: ".ui-resizable-sw", ne: ".ui-resizable-ne", nw: ".ui-resizable-nw" }); 16666 if(this.handles.constructor === String) { 16667 16668 if ( this.handles === "all") { 16669 this.handles = "n,e,s,w,se,sw,ne,nw"; 16670 } 16671 16672 n = this.handles.split(","); 16673 this.handles = {}; 16674 16675 for(i = 0; i < n.length; i++) { 16676 16677 handle = $.trim(n[i]); 16678 hname = "ui-resizable-"+handle; 16679 axis = $("<div class='ui-resizable-handle " + hname + "'></div>"); 16680 16681 // Apply zIndex to all handles - see #7960 16682 axis.css({ zIndex: o.zIndex }); 16683 16684 //TODO : What's going on here? 16685 if ("se" === handle) { 16686 axis.addClass("ui-icon ui-icon-gripsmall-diagonal-se"); 16687 } 16688 16689 //Insert into internal handles object and append to element 16690 this.handles[handle] = ".ui-resizable-"+handle; 16691 this.element.append(axis); 16692 } 16693 16694 } 16695 16696 this._renderAxis = function(target) { 16697 16698 var i, axis, padPos, padWrapper; 16699 16700 target = target || this.element; 16701 16702 for(i in this.handles) { 16703 16704 if(this.handles[i].constructor === String) { 16705 this.handles[i] = $(this.handles[i], this.element).show(); 16706 } 16707 16708 //Apply pad to wrapper element, needed to fix axis position (textarea, inputs, scrolls) 16709 if (this.elementIsWrapper && this.originalElement[0].nodeName.match(/textarea|input|select|button/i)) { 16710 16711 axis = $(this.handles[i], this.element); 16712 16713 //Checking the correct pad and border 16714 padWrapper = /sw|ne|nw|se|n|s/.test(i) ? axis.outerHeight() : axis.outerWidth(); 16715 16716 //The padding type i have to apply... 16717 padPos = [ "padding", 16718 /ne|nw|n/.test(i) ? "Top" : 16719 /se|sw|s/.test(i) ? "Bottom" : 16720 /^e$/.test(i) ? "Right" : "Left" ].join(""); 16721 16722 target.css(padPos, padWrapper); 16723 16724 this._proportionallyResize(); 16725 16726 } 16727 16728 //TODO: What's that good for? There's not anything to be executed left 16729 if(!$(this.handles[i]).length) { 16730 continue; 16731 } 16732 } 16733 }; 16734 16735 //TODO: make renderAxis a prototype function 16736 this._renderAxis(this.element); 16737 16738 this._handles = $(".ui-resizable-handle", this.element) 16739 .disableSelection(); 16740 16741 //Matching axis name 16742 this._handles.mouseover(function() { 16743 if (!that.resizing) { 16744 if (this.className) { 16745 axis = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i); 16746 } 16747 //Axis, default = se 16748 that.axis = axis && axis[1] ? axis[1] : "se"; 16749 } 16750 }); 16751 16752 //If we want to auto hide the elements 16753 if (o.autoHide) { 16754 this._handles.hide(); 16755 $(this.element) 16756 .addClass("ui-resizable-autohide") 16757 .mouseenter(function() { 16758 if (o.disabled) { 16759 return; 16760 } 16761 $(this).removeClass("ui-resizable-autohide"); 16762 that._handles.show(); 16763 }) 16764 .mouseleave(function(){ 16765 if (o.disabled) { 16766 return; 16767 } 16768 if (!that.resizing) { 16769 $(this).addClass("ui-resizable-autohide"); 16770 that._handles.hide(); 16771 } 16772 }); 16773 } 16774 16775 //Initialize the mouse interaction 16776 this._mouseInit(); 16777 16778 }, 16779 16780 _destroy: function() { 16781 16782 this._mouseDestroy(); 16783 16784 var wrapper, 16785 _destroy = function(exp) { 16786 $(exp).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing") 16787 .removeData("resizable").removeData("ui-resizable").unbind(".resizable").find(".ui-resizable-handle").remove(); 16788 }; 16789 16790 //TODO: Unwrap at same DOM position 16791 if (this.elementIsWrapper) { 16792 _destroy(this.element); 16793 wrapper = this.element; 16794 this.originalElement.css({ 16795 position: wrapper.css("position"), 16796 width: wrapper.outerWidth(), 16797 height: wrapper.outerHeight(), 16798 top: wrapper.css("top"), 16799 left: wrapper.css("left") 16800 }).insertAfter( wrapper ); 16801 wrapper.remove(); 16802 } 16803 16804 this.originalElement.css("resize", this.originalResizeStyle); 16805 _destroy(this.originalElement); 16806 16807 return this; 16808 }, 16809 16810 _mouseCapture: function(event) { 16811 var i, handle, 16812 capture = false; 16813 16814 for (i in this.handles) { 16815 handle = $(this.handles[i])[0]; 16816 if (handle === event.target || $.contains(handle, event.target)) { 16817 capture = true; 16818 } 16819 } 16820 16821 return !this.options.disabled && capture; 16822 }, 16823 16824 _mouseStart: function(event) { 16825 16826 var curleft, curtop, cursor, 16827 o = this.options, 16828 iniPos = this.element.position(), 16829 el = this.element; 16830 16831 this.resizing = true; 16832 16833 // bugfix for http://dev.jquery.com/ticket/1749 16834 if ( (/absolute/).test( el.css("position") ) ) { 16835 el.css({ position: "absolute", top: el.css("top"), left: el.css("left") }); 16836 } else if (el.is(".ui-draggable")) { 16837 el.css({ position: "absolute", top: iniPos.top, left: iniPos.left }); 16838 } 16839 16840 this._renderProxy(); 16841 16842 curleft = num(this.helper.css("left")); 16843 curtop = num(this.helper.css("top")); 16844 16845 if (o.containment) { 16846 curleft += $(o.containment).scrollLeft() || 0; 16847 curtop += $(o.containment).scrollTop() || 0; 16848 } 16849 16850 //Store needed variables 16851 this.offset = this.helper.offset(); 16852 this.position = { left: curleft, top: curtop }; 16853 this.size = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() }; 16854 this.originalSize = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() }; 16855 this.originalPosition = { left: curleft, top: curtop }; 16856 this.sizeDiff = { width: el.outerWidth() - el.width(), height: el.outerHeight() - el.height() }; 16857 this.originalMousePosition = { left: event.pageX, top: event.pageY }; 16858 16859 //Aspect Ratio 16860 this.aspectRatio = (typeof o.aspectRatio === "number") ? o.aspectRatio : ((this.originalSize.width / this.originalSize.height) || 1); 16861 16862 cursor = $(".ui-resizable-" + this.axis).css("cursor"); 16863 $("body").css("cursor", cursor === "auto" ? this.axis + "-resize" : cursor); 16864 16865 el.addClass("ui-resizable-resizing"); 16866 this._propagate("start", event); 16867 return true; 16868 }, 16869 16870 _mouseDrag: function(event) { 16871 16872 //Increase performance, avoid regex 16873 var data, 16874 el = this.helper, props = {}, 16875 smp = this.originalMousePosition, 16876 a = this.axis, 16877 prevTop = this.position.top, 16878 prevLeft = this.position.left, 16879 prevWidth = this.size.width, 16880 prevHeight = this.size.height, 16881 dx = (event.pageX-smp.left)||0, 16882 dy = (event.pageY-smp.top)||0, 16883 trigger = this._change[a]; 16884 16885 if (!trigger) { 16886 return false; 16887 } 16888 16889 // Calculate the attrs that will be change 16890 data = trigger.apply(this, [event, dx, dy]); 16891 16892 // Put this in the mouseDrag handler since the user can start pressing shift while resizing 16893 this._updateVirtualBoundaries(event.shiftKey); 16894 if (this._aspectRatio || event.shiftKey) { 16895 data = this._updateRatio(data, event); 16896 } 16897 16898 data = this._respectSize(data, event); 16899 16900 this._updateCache(data); 16901 16902 // plugins callbacks need to be called first 16903 this._propagate("resize", event); 16904 16905 if (this.position.top !== prevTop) { 16906 props.top = this.position.top + "px"; 16907 } 16908 if (this.position.left !== prevLeft) { 16909 props.left = this.position.left + "px"; 16910 } 16911 if (this.size.width !== prevWidth) { 16912 props.width = this.size.width + "px"; 16913 } 16914 if (this.size.height !== prevHeight) { 16915 props.height = this.size.height + "px"; 16916 } 16917 el.css(props); 16918 16919 if (!this._helper && this._proportionallyResizeElements.length) { 16920 this._proportionallyResize(); 16921 } 16922 16923 // Call the user callback if the element was resized 16924 if ( ! $.isEmptyObject(props) ) { 16925 this._trigger("resize", event, this.ui()); 16926 } 16927 16928 return false; 16929 }, 16930 16931 _mouseStop: function(event) { 16932 16933 this.resizing = false; 16934 var pr, ista, soffseth, soffsetw, s, left, top, 16935 o = this.options, that = this; 16936 16937 if(this._helper) { 16938 16939 pr = this._proportionallyResizeElements; 16940 ista = pr.length && (/textarea/i).test(pr[0].nodeName); 16941 soffseth = ista && $.ui.hasScroll(pr[0], "left") /* TODO - jump height */ ? 0 : that.sizeDiff.height; 16942 soffsetw = ista ? 0 : that.sizeDiff.width; 16943 16944 s = { width: (that.helper.width() - soffsetw), height: (that.helper.height() - soffseth) }; 16945 left = (parseInt(that.element.css("left"), 10) + (that.position.left - that.originalPosition.left)) || null; 16946 top = (parseInt(that.element.css("top"), 10) + (that.position.top - that.originalPosition.top)) || null; 16947 16948 if (!o.animate) { 16949 this.element.css($.extend(s, { top: top, left: left })); 16950 } 16951 16952 that.helper.height(that.size.height); 16953 that.helper.width(that.size.width); 16954 16955 if (this._helper && !o.animate) { 16956 this._proportionallyResize(); 16957 } 16958 } 16959 16960 $("body").css("cursor", "auto"); 16961 16962 this.element.removeClass("ui-resizable-resizing"); 16963 16964 this._propagate("stop", event); 16965 16966 if (this._helper) { 16967 this.helper.remove(); 16968 } 16969 16970 return false; 16971 16972 }, 16973 16974 _updateVirtualBoundaries: function(forceAspectRatio) { 16975 var pMinWidth, pMaxWidth, pMinHeight, pMaxHeight, b, 16976 o = this.options; 16977 16978 b = { 16979 minWidth: isNumber(o.minWidth) ? o.minWidth : 0, 16980 maxWidth: isNumber(o.maxWidth) ? o.maxWidth : Infinity, 16981 minHeight: isNumber(o.minHeight) ? o.minHeight : 0, 16982 maxHeight: isNumber(o.maxHeight) ? o.maxHeight : Infinity 16983 }; 16984 16985 if(this._aspectRatio || forceAspectRatio) { 16986 // We want to create an enclosing box whose aspect ration is the requested one 16987 // First, compute the "projected" size for each dimension based on the aspect ratio and other dimension 16988 pMinWidth = b.minHeight * this.aspectRatio; 16989 pMinHeight = b.minWidth / this.aspectRatio; 16990 pMaxWidth = b.maxHeight * this.aspectRatio; 16991 pMaxHeight = b.maxWidth / this.aspectRatio; 16992 16993 if(pMinWidth > b.minWidth) { 16994 b.minWidth = pMinWidth; 16995 } 16996 if(pMinHeight > b.minHeight) { 16997 b.minHeight = pMinHeight; 16998 } 16999 if(pMaxWidth < b.maxWidth) { 17000 b.maxWidth = pMaxWidth; 17001 } 17002 if(pMaxHeight < b.maxHeight) { 17003 b.maxHeight = pMaxHeight; 17004 } 17005 } 17006 this._vBoundaries = b; 17007 }, 17008 17009 _updateCache: function(data) { 17010 this.offset = this.helper.offset(); 17011 if (isNumber(data.left)) { 17012 this.position.left = data.left; 17013 } 17014 if (isNumber(data.top)) { 17015 this.position.top = data.top; 17016 } 17017 if (isNumber(data.height)) { 17018 this.size.height = data.height; 17019 } 17020 if (isNumber(data.width)) { 17021 this.size.width = data.width; 17022 } 17023 }, 17024 17025 _updateRatio: function( data ) { 17026 17027 var cpos = this.position, 17028 csize = this.size, 17029 a = this.axis; 17030 17031 if (isNumber(data.height)) { 17032 data.width = (data.height * this.aspectRatio); 17033 } else if (isNumber(data.width)) { 17034 data.height = (data.width / this.aspectRatio); 17035 } 17036 17037 if (a === "sw") { 17038 data.left = cpos.left + (csize.width - data.width); 17039 data.top = null; 17040 } 17041 if (a === "nw") { 17042 data.top = cpos.top + (csize.height - data.height); 17043 data.left = cpos.left + (csize.width - data.width); 17044 } 17045 17046 return data; 17047 }, 17048 17049 _respectSize: function( data ) { 17050 17051 var o = this._vBoundaries, 17052 a = this.axis, 17053 ismaxw = isNumber(data.width) && o.maxWidth && (o.maxWidth < data.width), ismaxh = isNumber(data.height) && o.maxHeight && (o.maxHeight < data.height), 17054 isminw = isNumber(data.width) && o.minWidth && (o.minWidth > data.width), isminh = isNumber(data.height) && o.minHeight && (o.minHeight > data.height), 17055 dw = this.originalPosition.left + this.originalSize.width, 17056 dh = this.position.top + this.size.height, 17057 cw = /sw|nw|w/.test(a), ch = /nw|ne|n/.test(a); 17058 if (isminw) { 17059 data.width = o.minWidth; 17060 } 17061 if (isminh) { 17062 data.height = o.minHeight; 17063 } 17064 if (ismaxw) { 17065 data.width = o.maxWidth; 17066 } 17067 if (ismaxh) { 17068 data.height = o.maxHeight; 17069 } 17070 17071 if (isminw && cw) { 17072 data.left = dw - o.minWidth; 17073 } 17074 if (ismaxw && cw) { 17075 data.left = dw - o.maxWidth; 17076 } 17077 if (isminh && ch) { 17078 data.top = dh - o.minHeight; 17079 } 17080 if (ismaxh && ch) { 17081 data.top = dh - o.maxHeight; 17082 } 17083 17084 // fixing jump error on top/left - bug #2330 17085 if (!data.width && !data.height && !data.left && data.top) { 17086 data.top = null; 17087 } else if (!data.width && !data.height && !data.top && data.left) { 17088 data.left = null; 17089 } 17090 17091 return data; 17092 }, 17093 17094 _proportionallyResize: function() { 17095 17096 if (!this._proportionallyResizeElements.length) { 17097 return; 17098 } 17099 17100 var i, j, borders, paddings, prel, 17101 element = this.helper || this.element; 17102 17103 for ( i=0; i < this._proportionallyResizeElements.length; i++) { 17104 17105 prel = this._proportionallyResizeElements[i]; 17106 17107 if (!this.borderDif) { 17108 this.borderDif = []; 17109 borders = [prel.css("borderTopWidth"), prel.css("borderRightWidth"), prel.css("borderBottomWidth"), prel.css("borderLeftWidth")]; 17110 paddings = [prel.css("paddingTop"), prel.css("paddingRight"), prel.css("paddingBottom"), prel.css("paddingLeft")]; 17111 17112 for ( j = 0; j < borders.length; j++ ) { 17113 this.borderDif[ j ] = ( parseInt( borders[ j ], 10 ) || 0 ) + ( parseInt( paddings[ j ], 10 ) || 0 ); 17114 } 17115 } 17116 17117 prel.css({ 17118 height: (element.height() - this.borderDif[0] - this.borderDif[2]) || 0, 17119 width: (element.width() - this.borderDif[1] - this.borderDif[3]) || 0 17120 }); 17121 17122 } 17123 17124 }, 17125 17126 _renderProxy: function() { 17127 17128 var el = this.element, o = this.options; 17129 this.elementOffset = el.offset(); 17130 17131 if(this._helper) { 17132 17133 this.helper = this.helper || $("<div style='overflow:hidden;'></div>"); 17134 17135 this.helper.addClass(this._helper).css({ 17136 width: this.element.outerWidth() - 1, 17137 height: this.element.outerHeight() - 1, 17138 position: "absolute", 17139 left: this.elementOffset.left +"px", 17140 top: this.elementOffset.top +"px", 17141 zIndex: ++o.zIndex //TODO: Don't modify option 17142 }); 17143 17144 this.helper 17145 .appendTo("body") 17146 .disableSelection(); 17147 17148 } else { 17149 this.helper = this.element; 17150 } 17151 17152 }, 17153 17154 _change: { 17155 e: function(event, dx) { 17156 return { width: this.originalSize.width + dx }; 17157 }, 17158 w: function(event, dx) { 17159 var cs = this.originalSize, sp = this.originalPosition; 17160 return { left: sp.left + dx, width: cs.width - dx }; 17161 }, 17162 n: function(event, dx, dy) { 17163 var cs = this.originalSize, sp = this.originalPosition; 17164 return { top: sp.top + dy, height: cs.height - dy }; 17165 }, 17166 s: function(event, dx, dy) { 17167 return { height: this.originalSize.height + dy }; 17168 }, 17169 se: function(event, dx, dy) { 17170 return $.extend(this._change.s.apply(this, arguments), this._change.e.apply(this, [event, dx, dy])); 17171 }, 17172 sw: function(event, dx, dy) { 17173 return $.extend(this._change.s.apply(this, arguments), this._change.w.apply(this, [event, dx, dy])); 17174 }, 17175 ne: function(event, dx, dy) { 17176 return $.extend(this._change.n.apply(this, arguments), this._change.e.apply(this, [event, dx, dy])); 17177 }, 17178 nw: function(event, dx, dy) { 17179 return $.extend(this._change.n.apply(this, arguments), this._change.w.apply(this, [event, dx, dy])); 17180 } 17181 }, 17182 17183 _propagate: function(n, event) { 17184 $.ui.plugin.call(this, n, [event, this.ui()]); 17185 (n !== "resize" && this._trigger(n, event, this.ui())); 17186 }, 17187 17188 plugins: {}, 17189 17190 ui: function() { 17191 return { 17192 originalElement: this.originalElement, 17193 element: this.element, 17194 helper: this.helper, 17195 position: this.position, 17196 size: this.size, 17197 originalSize: this.originalSize, 17198 originalPosition: this.originalPosition 17199 }; 17200 } 17201 17202}); 17203 17204/* 17205 * Resizable Extensions 17206 */ 17207 17208$.ui.plugin.add("resizable", "animate", { 17209 17210 stop: function( event ) { 17211 var that = $(this).data("ui-resizable"), 17212 o = that.options, 17213 pr = that._proportionallyResizeElements, 17214 ista = pr.length && (/textarea/i).test(pr[0].nodeName), 17215 soffseth = ista && $.ui.hasScroll(pr[0], "left") /* TODO - jump height */ ? 0 : that.sizeDiff.height, 17216 soffsetw = ista ? 0 : that.sizeDiff.width, 17217 style = { width: (that.size.width - soffsetw), height: (that.size.height - soffseth) }, 17218 left = (parseInt(that.element.css("left"), 10) + (that.position.left - that.originalPosition.left)) || null, 17219 top = (parseInt(that.element.css("top"), 10) + (that.position.top - that.originalPosition.top)) || null; 17220 17221 that.element.animate( 17222 $.extend(style, top && left ? { top: top, left: left } : {}), { 17223 duration: o.animateDuration, 17224 easing: o.animateEasing, 17225 step: function() { 17226 17227 var data = { 17228 width: parseInt(that.element.css("width"), 10), 17229 height: parseInt(that.element.css("height"), 10), 17230 top: parseInt(that.element.css("top"), 10), 17231 left: parseInt(that.element.css("left"), 10) 17232 }; 17233 17234 if (pr && pr.length) { 17235 $(pr[0]).css({ width: data.width, height: data.height }); 17236 } 17237 17238 // propagating resize, and updating values for each animation step 17239 that._updateCache(data); 17240 that._propagate("resize", event); 17241 17242 } 17243 } 17244 ); 17245 } 17246 17247}); 17248 17249$.ui.plugin.add("resizable", "containment", { 17250 17251 start: function() { 17252 var element, p, co, ch, cw, width, height, 17253 that = $(this).data("ui-resizable"), 17254 o = that.options, 17255 el = that.element, 17256 oc = o.containment, 17257 ce = (oc instanceof $) ? oc.get(0) : (/parent/.test(oc)) ? el.parent().get(0) : oc; 17258 17259 if (!ce) { 17260 return; 17261 } 17262 17263 that.containerElement = $(ce); 17264 17265 if (/document/.test(oc) || oc === document) { 17266 that.containerOffset = { left: 0, top: 0 }; 17267 that.containerPosition = { left: 0, top: 0 }; 17268 17269 that.parentData = {
17270 element: $(document), left: 0, top: 0, 17271 width: $(document).width(), height: $(document).height() || document.body.parentNode.scrollHeight 17272 }; 17273 } 17274 17275 // i'm a node, so compute top, left, right, bottom 17276 else { 17277 element = $(ce); 17278 p = []; 17279 $([ "Top", "Right", "Left", "Bottom" ]).each(function(i, name) { p[i] = num(element.css("padding" + name)); }); 17280 17281 that.containerOffset = element.offset(); 17282 that.containerPosition = element.position(); 17283 that.containerSize = { height: (element.innerHeight() - p[3]), width: (element.innerWidth() - p[1]) }; 17284 17285 co = that.containerOffset; 17286 ch = that.containerSize.height; 17287 cw = that.containerSize.width; 17288 width = ($.ui.hasScroll(ce, "left") ? ce.scrollWidth : cw ); 17289 height = ($.ui.hasScroll(ce) ? ce.scrollHeight : ch); 17290 17291 that.parentData = { 17292 element: ce, left: co.left, top: co.top, width: width, height: height 17293 }; 17294 } 17295 }, 17296 17297 resize: function( event ) { 17298 var woset, hoset, isParent, isOffsetRelative, 17299 that = $(this).data("ui-resizable"), 17300 o = that.options, 17301 co = that.containerOffset, cp = that.position, 17302 pRatio = that._aspectRatio || event.shiftKey, 17303 cop = { top:0, left:0 }, ce = that.containerElement; 17304 17305 if (ce[0] !== document && (/static/).test(ce.css("position"))) { 17306 cop = co; 17307 } 17308 17309 if (cp.left < (that._helper ? co.left : 0)) { 17310 that.size.width = that.size.width + (that._helper ? (that.position.left - co.left) : (that.position.left - cop.left)); 17311 if (pRatio) { 17312 that.size.height = that.size.width / that.aspectRatio; 17313 } 17314 that.position.left = o.helper ? co.left : 0; 17315 } 17316 17317 if (cp.top < (that._helper ? co.top : 0)) { 17318 that.size.height = that.size.height + (that._helper ? (that.position.top - co.top) : that.position.top); 17319 if (pRatio) { 17320 that.size.width = that.size.height * that.aspectRatio; 17321 } 17322 that.position.top = that._helper ? co.top : 0; 17323 } 17324 17325 that.offset.left = that.parentData.left+that.position.left; 17326 that.offset.top = that.parentData.top+that.position.top; 17327 17328 woset = Math.abs( (that._helper ? that.offset.left - cop.left : (that.offset.left - cop.left)) + that.sizeDiff.width ); 17329 hoset = Math.abs( (that._helper ? that.offset.top - cop.top : (that.offset.top - co.top)) + that.sizeDiff.height ); 17330 17331 isParent = that.containerElement.get(0) === that.element.parent().get(0); 17332 isOffsetRelative = /relative|absolute/.test(that.containerElement.css("position")); 17333 17334 if(isParent && isOffsetRelative) { 17335 woset -= that.parentData.left; 17336 } 17337 17338 if (woset + that.size.width >= that.parentData.width) { 17339 that.size.width = that.parentData.width - woset; 17340 if (pRatio) { 17341 that.size.height = that.size.width / that.aspectRatio; 17342 } 17343 } 17344 17345 if (hoset + that.size.height >= that.parentData.height) { 17346 that.size.height = that.parentData.height - hoset; 17347 if (pRatio) { 17348 that.size.width = that.size.height * that.aspectRatio; 17349 } 17350 } 17351 }, 17352 17353 stop: function(){ 17354 var that = $(this).data("ui-resizable"), 17355 o = that.options, 17356 co = that.containerOffset, 17357 cop = that.containerPosition, 17358 ce = that.containerElement, 17359 helper = $(that.helper), 17360 ho = helper.offset(), 17361 w = helper.outerWidth() - that.sizeDiff.width, 17362 h = helper.outerHeight() - that.sizeDiff.height; 17363 17364 if (that._helper && !o.animate && (/relative/).test(ce.css("position"))) { 17365 $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h }); 17366 } 17367 17368 if (that._helper && !o.animate && (/static/).test(ce.css("position"))) { 17369 $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h }); 17370 } 17371 17372 } 17373}); 17374 17375$.ui.plugin.add("resizable", "alsoResize", { 17376 17377 start: function () { 17378 var that = $(this).data("ui-resizable"), 17379 o = that.options, 17380 _store = function (exp) { 17381 $(exp).each(function() { 17382 var el = $(this); 17383 el.data("ui-resizable-alsoresize", { 17384 width: parseInt(el.width(), 10), height: parseInt(el.height(), 10), 17385 left: parseInt(el.css("left"), 10), top: parseInt(el.css("top"), 10) 17386 }); 17387 }); 17388 }; 17389 17390 if (typeof(o.alsoResize) === "object" && !o.alsoResize.parentNode) { 17391 if (o.alsoResize.length) { o.alsoResize = o.alsoResize[0]; _store(o.alsoResize); } 17392 else { $.each(o.alsoResize, function (exp) { _store(exp); }); } 17393 }else{ 17394 _store(o.alsoResize); 17395 } 17396 }, 17397 17398 resize: function (event, ui) { 17399 var that = $(this).data("ui-resizable"), 17400 o = that.options, 17401 os = that.originalSize, 17402 op = that.originalPosition, 17403 delta = { 17404 height: (that.size.height - os.height) || 0, width: (that.size.width - os.width) || 0, 17405 top: (that.position.top - op.top) || 0, left: (that.position.left - op.left) || 0 17406 }, 17407 17408 _alsoResize = function (exp, c) { 17409 $(exp).each(function() { 17410 var el = $(this), start = $(this).data("ui-resizable-alsoresize"), style = {}, 17411 css = c && c.length ? c : el.parents(ui.originalElement[0]).length ? ["width", "height"] : ["width", "height", "top", "left"]; 17412
vendor: 1,574 bytes, lines 17413-17477
17413 $.each(css, function (i, prop) { 17414 var sum = (start[prop]||0) + (delta[prop]||0); 17415 if (sum && sum >= 0) { 17416 style[prop] = sum || null; 17417 } 17418 }); 17419 17420 el.css(style); 17421 }); 17422 }; 17423 17424 if (typeof(o.alsoResize) === "object" && !o.alsoResize.nodeType) { 17425 $.each(o.alsoResize, function (exp, c) { _alsoResize(exp, c); }); 17426 }else{ 17427 _alsoResize(o.alsoResize); 17428 } 17429 }, 17430 17431 stop: function () { 17432 $(this).removeData("resizable-alsoresize"); 17433 } 17434}); 17435 17436$.ui.plugin.add("resizable", "ghost", { 17437 17438 start: function() { 17439 17440 var that = $(this).data("ui-resizable"), o = that.options, cs = that.size; 17441 17442 that.ghost = that.originalElement.clone(); 17443 that.ghost 17444 .css({ opacity: 0.25, display: "block", position: "relative", height: cs.height, width: cs.width, margin: 0, left: 0, top: 0 }) 17445 .addClass("ui-resizable-ghost") 17446 .addClass(typeof o.ghost === "string" ? o.ghost : ""); 17447 17448 that.ghost.appendTo(that.helper); 17449 17450 }, 17451 17452 resize: function(){ 17453 var that = $(this).data("ui-resizable"); 17454 if (that.ghost) { 17455 that.ghost.css({ position: "relative", height: that.size.height, width: that.size.width }); 17456 } 17457 }, 17458 17459 stop: function() { 17460 var that = $(this).data("ui-resizable"); 17461 if (that.ghost && that.helper) { 17462 that.helper.get(0).removeChild(that.ghost.get(0)); 17463 } 17464 } 17465 17466}); 17467 17468$.ui.plugin.add("resizable", "grid", { 17469 17470 resize: function() { 17471 var that = $(this).data("ui-resizable"), 17472 o = that.options, 17473 cs = that.size, 17474 os = that.originalSize, 17475 op = that.originalPosition, 17476 a = that.axis, 17477 grid = typeof o.grid === "number" ? [o.grid, o.grid] : o.grid,
17478 gridX = (grid[0]||1), 17479 gridY = (grid[1]||1), 17480 ox = Math.round((cs.width - os.width) / gridX) * gridX, 17481 oy = Math.round((cs.height - os.height) / gridY) * gridY, 17482 newWidth = os.width + ox, 17483 newHeight = os.height + oy, 17484 isMaxWidth = o.maxWidth && (o.maxWidth < newWidth), 17485 isMaxHeight = o.maxHeight && (o.maxHeight < newHeight), 17486 isMinWidth = o.minWidth && (o.minWidth > newWidth), 17487 isMinHeight = o.minHeight && (o.minHeight > newHeight); 17488 17489 o.grid = grid; 17490 17491 if (isMinWidth) { 17492 newWidth = newWidth + gridX; 17493 } 17494 if (isMinHeight) { 17495 newHeight = newHeight + gridY; 17496 } 17497 if (isMaxWidth) { 17498 newWidth = newWidth - gridX; 17499 } 17500 if (isMaxHeight) { 17501 newHeight = newHeight - gridY; 17502 } 17503 17504 if (/^(se|s|e)$/.test(a)) { 17505 that.size.width = newWidth; 17506 that.size.height = newHeight; 17507 } else if (/^(ne)$/.test(a)) { 17508 that.size.width = newWidth; 17509 that.size.height = newHeight; 17510 that.position.top = op.top - oy; 17511 } else if (/^(sw)$/.test(a)) { 17512 that.size.width = newWidth; 17513 that.size.height = newHeight; 17514 that.position.left = op.left - ox; 17515 } else { 17516 that.size.width = newWidth; 17517 that.size.height = newHeight; 17518 that.position.top = op.top - oy; 17519 that.position.left = op.left - ox; 17520 } 17521 } 17522 17523}); 17524 17525})(jQuery); 17526 17527 17528 17529 17530 17531 17532 17533/*! 17534 * jQuery UI Dialog 1.10.3 17535 * http://jqueryui.com 17536 * 17537 * Copyright 2013 jQuery Foundation and other contributors 17538 * Released under the MIT license. 17539 * http://jquery.org/license 17540 * 17541 * http://api.jqueryui.com/dialog/ 17542 * 17543 * Depends: 17544 * jquery.ui.core.js 17545 * jquery.ui.widget.js 17546 * jquery.ui.button.js 17547 * jquery.ui.draggable.js 17548 * jquery.ui.mouse.js 17549 * jquery.ui.position.js 17550 * jquery.ui.resizable.js 17551 */ 17552 17553(function( $, undefined ) { 17554 17555var sizeRelatedOptions = { 17556 buttons: true, 17557 height: true, 17558 maxHeight: true, 17559 maxWidth: true, 17560 minHeight: true, 17561 minWidth: true, 17562 width: true 17563 }, 17564 resizableRelatedOptions = { 17565 maxHeight: true, 17566 maxWidth: true, 17567 minHeight: true, 17568 minWidth: true 17569 }; 17570 17571$.widget( "ui.dialog", { 17572 version: "1.10.3", 17573 options: { 17574 appendTo: "body", 17575 autoOpen: true, 17576 buttons: [], 17577 closeOnEscape: true, 17578 closeText: "close", 17579 dialogClass: "", 17580 draggable: true, 17581 hide: null, 17582 height: "auto", 17583 maxHeight: null, 17584 maxWidth: null, 17585 minHeight: 150, 17586 minWidth: 150, 17587 modal: false, 17588 position: { 17589 my: "center", 17590 at: "center", 17591 of: window, 17592 collision: "fit", 17593 // Ensure the titlebar is always visible 17594 using: function( pos ) { 17595 var topOffset = $( this ).css( pos ).offset().top; 17596 if ( topOffset < 0 ) { 17597 $( this ).css( "top", pos.top - topOffset ); 17598 } 17599 } 17600 }, 17601 resizable: true, 17602 show: null, 17603 title: null, 17604 width: 300, 17605 17606 // callbacks 17607 beforeClose: null, 17608 close: null, 17609 drag: null, 17610 dragStart: null, 17611 dragStop: null, 17612 focus: null, 17613 open: null, 17614 resize: null, 17615 resizeStart: null, 17616 resizeStop: null 17617 }, 17618 17619 _create: function() { 17620 this.originalCss = { 17621 display: this.element[0].style.display, 17622 width: this.element[0].style.width, 17623 minHeight: this.element[0].style.minHeight, 17624 maxHeight: this.element[0].style.maxHeight, 17625 height: this.element[0].style.height 17626 }; 17627 this.originalPosition = { 17628 parent: this.element.parent(), 17629 index: this.element.parent().children().index( this.element ) 17630 }; 17631 this.originalTitle = this.element.attr("title"); 17632 this.options.title = this.options.title || this.originalTitle; 17633 17634 this._createWrapper(); 17635 17636 this.element 17637 .show() 17638 .removeAttr("title") 17639 .addClass("ui-dialog-content ui-widget-content") 17640 .appendTo( this.uiDialog ); 17641 17642 this._createTitlebar(); 17643 this._createButtonPane(); 17644 17645 if ( this.options.draggable && $.fn.draggable ) { 17646 this._makeDraggable(); 17647 } 17648 if ( this.options.resizable && $.fn.resizable ) { 17649 this._makeResizable(); 17650 } 17651 17652 this._isOpen = false; 17653 }, 17654 17655 _init: function() { 17656 if ( this.options.autoOpen ) { 17657 this.open(); 17658 } 17659 }, 17660 17661 _appendTo: function() { 17662 var element = this.options.appendTo; 17663 if ( element && (element.jquery || element.nodeType) ) { 17664 return $( element ); 17665 } 17666 return this.document.find( element || "body" ).eq( 0 ); 17667 }, 17668 17669 _destroy: function() { 17670 var next, 17671 originalPosition = this.originalPosition; 17672 17673 this._destroyOverlay(); 17674 17675 this.element 17676 .removeUniqueId() 17677 .removeClass("ui-dialog-content ui-widget-content") 17678 .css( this.originalCss ) 17679 // Without detaching first, the following becomes really slow 17680 .detach(); 17681 17682 this.uiDialog.stop( true, true ).remove(); 17683 17684 if ( this.originalTitle ) { 17685 this.element.attr( "title", this.originalTitle ); 17686 } 17687 17688 next = originalPosition.parent.children().eq( originalPosition.index ); 17689 // Don't try to place the dialog next to itself (#8613) 17690 if ( next.length && next[0] !== this.element[0] ) { 17691 next.before( this.element ); 17692 } else { 17693 originalPosition.parent.append( this.element ); 17694 } 17695 }, 17696 17697 widget: function() { 17698 return this.uiDialog; 17699 }, 17700 17701 disable: $.noop, 17702 enable: $.noop, 17703 17704 close: function( event ) { 17705 var that = this; 17706 17707 if ( !this._isOpen || this._trigger( "beforeClose", event ) === false ) { 17708 return; 17709 } 17710 17711 this._isOpen = false;
vendor: 14,295 bytes, lines 17712-18262
17712 this._destroyOverlay(); 17713 17714 if ( !this.opener.filter(":focusable").focus().length ) { 17715 // Hiding a focused element doesn't trigger blur in WebKit 17716 // so in case we have nothing to focus on, explicitly blur the active element 17717 // https://bugs.webkit.org/show_bug.cgi?id=47182 17718 $( this.document[0].activeElement ).blur(); 17719 } 17720 17721 this._hide( this.uiDialog, this.options.hide, function() { 17722 that._trigger( "close", event ); 17723 }); 17724 }, 17725 17726 isOpen: function() { 17727 return this._isOpen; 17728 }, 17729 17730 moveToTop: function() { 17731 this._moveToTop(); 17732 }, 17733 17734 _moveToTop: function( event, silent ) { 17735 var moved = !!this.uiDialog.nextAll(":visible").insertBefore( this.uiDialog ).length; 17736 if ( moved && !silent ) { 17737 this._trigger( "focus", event ); 17738 } 17739 return moved; 17740 }, 17741 17742 open: function() { 17743 var that = this; 17744 if ( this._isOpen ) { 17745 if ( this._moveToTop() ) { 17746 this._focusTabbable(); 17747 } 17748 return; 17749 } 17750 17751 this._isOpen = true; 17752 this.opener = $( this.document[0].activeElement ); 17753 17754 this._size(); 17755 this._position(); 17756 this._createOverlay(); 17757 this._moveToTop( null, true ); 17758 this._show( this.uiDialog, this.options.show, function() { 17759 that._focusTabbable(); 17760 that._trigger("focus"); 17761 }); 17762 17763 this._trigger("open"); 17764 }, 17765 17766 _focusTabbable: function() { 17767 // Set focus to the first match: 17768 // 1. First element inside the dialog matching [autofocus] 17769 // 2. Tabbable element inside the content element 17770 // 3. Tabbable element inside the buttonpane 17771 // 4. The close button 17772 // 5. The dialog itself 17773 var hasFocus = this.element.find("[autofocus]"); 17774 if ( !hasFocus.length ) { 17775 hasFocus = this.element.find(":tabbable"); 17776 } 17777 if ( !hasFocus.length ) { 17778 hasFocus = this.uiDialogButtonPane.find(":tabbable"); 17779 } 17780 if ( !hasFocus.length ) { 17781 hasFocus = this.uiDialogTitlebarClose.filter(":tabbable"); 17782 } 17783 if ( !hasFocus.length ) { 17784 hasFocus = this.uiDialog; 17785 } 17786 hasFocus.eq( 0 ).focus(); 17787 }, 17788 17789 _keepFocus: function( event ) { 17790 function checkFocus() { 17791 var activeElement = this.document[0].activeElement, 17792 isActive = this.uiDialog[0] === activeElement || 17793 $.contains( this.uiDialog[0], activeElement ); 17794 if ( !isActive ) { 17795 this._focusTabbable(); 17796 } 17797 } 17798 event.preventDefault(); 17799 checkFocus.call( this ); 17800 // support: IE 17801 // IE <= 8 doesn't prevent moving focus even with event.preventDefault() 17802 // so we check again later 17803 this._delay( checkFocus ); 17804 }, 17805 17806 _createWrapper: function() { 17807 this.uiDialog = $("<div>") 17808 .addClass( "ui-dialog ui-widget ui-widget-content ui-corner-all ui-front " + 17809 this.options.dialogClass ) 17810 .hide() 17811 .attr({ 17812 // Setting tabIndex makes the div focusable 17813 tabIndex: -1, 17814 role: "dialog" 17815 }) 17816 .appendTo( this._appendTo() ); 17817 17818 this._on( this.uiDialog, { 17819 keydown: function( event ) { 17820 if ( this.options.closeOnEscape && !event.isDefaultPrevented() && event.keyCode && 17821 event.keyCode === $.ui.keyCode.ESCAPE ) { 17822 event.preventDefault(); 17823 this.close( event ); 17824 return; 17825 } 17826 17827 // prevent tabbing out of dialogs 17828 if ( event.keyCode !== $.ui.keyCode.TAB ) { 17829 return; 17830 } 17831 var tabbables = this.uiDialog.find(":tabbable"), 17832 first = tabbables.filter(":first"), 17833 last = tabbables.filter(":last"); 17834 17835 if ( ( event.target === last[0] || event.target === this.uiDialog[0] ) && !event.shiftKey ) { 17836 first.focus( 1 ); 17837 event.preventDefault(); 17838 } else if ( ( event.target === first[0] || event.target === this.uiDialog[0] ) && event.shiftKey ) { 17839 last.focus( 1 ); 17840 event.preventDefault(); 17841 } 17842 }, 17843 mousedown: function( event ) { 17844 if ( this._moveToTop( event ) ) { 17845 this._focusTabbable(); 17846 } 17847 } 17848 }); 17849 17850 // We assume that any existing aria-describedby attribute means 17851 // that the dialog content is marked up properly 17852 // otherwise we brute force the content as the description 17853 if ( !this.element.find("[aria-describedby]").length ) { 17854 this.uiDialog.attr({ 17855 "aria-describedby": this.element.uniqueId().attr("id") 17856 }); 17857 } 17858 }, 17859 17860 _createTitlebar: function() { 17861 var uiDialogTitle; 17862 17863 this.uiDialogTitlebar = $("<div>") 17864 .addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix") 17865 .prependTo( this.uiDialog ); 17866 this._on( this.uiDialogTitlebar, { 17867 mousedown: function( event ) { 17868 // Don't prevent click on close button (#8838) 17869 // Focusing a dialog that is partially scrolled out of view 17870 // causes the browser to scroll it into view, preventing the click event 17871 if ( !$( event.target ).closest(".ui-dialog-titlebar-close") ) { 17872 // Dialog isn't getting focus when dragging (#8063) 17873 this.uiDialog.focus(); 17874 } 17875 } 17876 }); 17877 17878 this.uiDialogTitlebarClose = $("<button></button>") 17879 .button({ 17880 label: this.options.closeText, 17881 icons: { 17882 primary: "ui-icon-closethick" 17883 }, 17884 text: false 17885 }) 17886 .addClass("ui-dialog-titlebar-close") 17887 .appendTo( this.uiDialogTitlebar ); 17888 this._on( this.uiDialogTitlebarClose, { 17889 click: function( event ) { 17890 event.preventDefault(); 17891 this.close( event ); 17892 } 17893 }); 17894 17895 uiDialogTitle = $("<span>") 17896 .uniqueId() 17897 .addClass("ui-dialog-title") 17898 .prependTo( this.uiDialogTitlebar ); 17899 this._title( uiDialogTitle ); 17900 17901 this.uiDialog.attr({ 17902 "aria-labelledby": uiDialogTitle.attr("id") 17903 }); 17904 }, 17905 17906 _title: function( title ) { 17907 if ( !this.options.title ) { 17908 title.html(" "); 17909 } 17910 title.text( this.options.title ); 17911 }, 17912 17913 _createButtonPane: function() { 17914 this.uiDialogButtonPane = $("<div>") 17915 .addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"); 17916 17917 this.uiButtonSet = $("<div>") 17918 .addClass("ui-dialog-buttonset") 17919 .appendTo( this.uiDialogButtonPane ); 17920 17921 this._createButtons(); 17922 }, 17923 17924 _createButtons: function() { 17925 var that = this, 17926 buttons = this.options.buttons; 17927 17928 // if we already have a button pane, remove it 17929 this.uiDialogButtonPane.remove(); 17930 this.uiButtonSet.empty(); 17931 17932 if ( $.isEmptyObject( buttons ) || ($.isArray( buttons ) && !buttons.length) ) { 17933 this.uiDialog.removeClass("ui-dialog-buttons"); 17934 return; 17935 } 17936 17937 $.each( buttons, function( name, props ) { 17938 var click, buttonOptions; 17939 props = $.isFunction( props ) ? 17940 { click: props, text: name } : 17941 props; 17942 // Default to a non-submitting button 17943 props = $.extend( { type: "button" }, props ); 17944 // Change the context for the click callback to be the main element 17945 click = props.click; 17946 props.click = function() { 17947 click.apply( that.element[0], arguments ); 17948 }; 17949 buttonOptions = { 17950 icons: props.icons, 17951 text: props.showText 17952 }; 17953 delete props.icons; 17954 delete props.showText; 17955 $( "<button></button>", props ) 17956 .button( buttonOptions ) 17957 .appendTo( that.uiButtonSet ); 17958 }); 17959 this.uiDialog.addClass("ui-dialog-buttons"); 17960 this.uiDialogButtonPane.appendTo( this.uiDialog ); 17961 }, 17962 17963 _makeDraggable: function() { 17964 var that = this, 17965 options = this.options; 17966 17967 function filteredUi( ui ) { 17968 return { 17969 position: ui.position, 17970 offset: ui.offset 17971 }; 17972 } 17973 17974 this.uiDialog.draggable({ 17975 cancel: ".ui-dialog-content, .ui-dialog-titlebar-close", 17976 handle: ".ui-dialog-titlebar", 17977 containment: "document", 17978 start: function( event, ui ) { 17979 $( this ).addClass("ui-dialog-dragging"); 17980 that._blockFrames(); 17981 that._trigger( "dragStart", event, filteredUi( ui ) ); 17982 }, 17983 drag: function( event, ui ) { 17984 that._trigger( "drag", event, filteredUi( ui ) ); 17985 }, 17986 stop: function( event, ui ) { 17987 options.position = [ 17988 ui.position.left - that.document.scrollLeft(), 17989 ui.position.top - that.document.scrollTop() 17990 ]; 17991 $( this ).removeClass("ui-dialog-dragging"); 17992 that._unblockFrames(); 17993 that._trigger( "dragStop", event, filteredUi( ui ) ); 17994 } 17995 }); 17996 }, 17997 17998 _makeResizable: function() { 17999 var that = this, 18000 options = this.options, 18001 handles = options.resizable, 18002 // .ui-resizable has position: relative defined in the stylesheet 18003 // but dialogs have to use absolute or fixed positioning 18004 position = this.uiDialog.css("position"), 18005 resizeHandles = typeof handles === "string" ? 18006 handles : 18007 "n,e,s,w,se,sw,ne,nw"; 18008 18009 function filteredUi( ui ) { 18010 return { 18011 originalPosition: ui.originalPosition, 18012 originalSize: ui.originalSize, 18013 position: ui.position, 18014 size: ui.size 18015 }; 18016 } 18017 18018 this.uiDialog.resizable({ 18019 cancel: ".ui-dialog-content", 18020 containment: "document", 18021 alsoResize: this.element, 18022 maxWidth: options.maxWidth, 18023 maxHeight: options.maxHeight, 18024 minWidth: options.minWidth, 18025 minHeight: this._minHeight(), 18026 handles: resizeHandles, 18027 start: function( event, ui ) { 18028 $( this ).addClass("ui-dialog-resizing"); 18029 that._blockFrames(); 18030 that._trigger( "resizeStart", event, filteredUi( ui ) ); 18031 }, 18032 resize: function( event, ui ) { 18033 that._trigger( "resize", event, filteredUi( ui ) ); 18034 }, 18035 stop: function( event, ui ) { 18036 options.height = $( this ).height(); 18037 options.width = $( this ).width(); 18038 $( this ).removeClass("ui-dialog-resizing"); 18039 that._unblockFrames(); 18040 that._trigger( "resizeStop", event, filteredUi( ui ) ); 18041 } 18042 }) 18043 .css( "position", position ); 18044 }, 18045 18046 _minHeight: function() { 18047 var options = this.options; 18048 18049 return options.height === "auto" ? 18050 options.minHeight : 18051 Math.min( options.minHeight, options.height ); 18052 }, 18053 18054 _position: function() { 18055 // Need to show the dialog to get the actual offset in the position plugin 18056 var isVisible = this.uiDialog.is(":visible"); 18057 if ( !isVisible ) { 18058 this.uiDialog.show(); 18059 } 18060 this.uiDialog.position( this.options.position ); 18061 if ( !isVisible ) { 18062 this.uiDialog.hide(); 18063 } 18064 }, 18065 18066 _setOptions: function( options ) { 18067 var that = this, 18068 resize = false, 18069 resizableOptions = {}; 18070 18071 $.each( options, function( key, value ) { 18072 that._setOption( key, value ); 18073 18074 if ( key in sizeRelatedOptions ) { 18075 resize = true; 18076 } 18077 if ( key in resizableRelatedOptions ) { 18078 resizableOptions[ key ] = value; 18079 } 18080 }); 18081 18082 if ( resize ) { 18083 this._size(); 18084 this._position(); 18085 } 18086 if ( this.uiDialog.is(":data(ui-resizable)") ) { 18087 this.uiDialog.resizable( "option", resizableOptions ); 18088 } 18089 }, 18090 18091 _setOption: function( key, value ) { 18092 /*jshint maxcomplexity:15*/ 18093 var isDraggable, isResizable, 18094 uiDialog = this.uiDialog; 18095 18096 if ( key === "dialogClass" ) { 18097 uiDialog 18098 .removeClass( this.options.dialogClass ) 18099 .addClass( value ); 18100 } 18101 18102 if ( key === "disabled" ) { 18103 return; 18104 } 18105 18106 this._super( key, value ); 18107 18108 if ( key === "appendTo" ) { 18109 this.uiDialog.appendTo( this._appendTo() ); 18110 } 18111 18112 if ( key === "buttons" ) { 18113 this._createButtons(); 18114 } 18115 18116 if ( key === "closeText" ) { 18117 this.uiDialogTitlebarClose.button({ 18118 // Ensure that we always pass a string 18119 label: "" + value 18120 }); 18121 } 18122 18123 if ( key === "draggable" ) { 18124 isDraggable = uiDialog.is(":data(ui-draggable)"); 18125 if ( isDraggable && !value ) { 18126 uiDialog.draggable("destroy"); 18127 } 18128 18129 if ( !isDraggable && value ) { 18130 this._makeDraggable(); 18131 } 18132 } 18133 18134 if ( key === "position" ) { 18135 this._position(); 18136 } 18137 18138 if ( key === "resizable" ) { 18139 // currently resizable, becoming non-resizable 18140 isResizable = uiDialog.is(":data(ui-resizable)"); 18141 if ( isResizable && !value ) { 18142 uiDialog.resizable("destroy"); 18143 } 18144 18145 // currently resizable, changing handles 18146 if ( isResizable && typeof value === "string" ) { 18147 uiDialog.resizable( "option", "handles", value ); 18148 } 18149 18150 // currently non-resizable, becoming resizable 18151 if ( !isResizable && value !== false ) { 18152 this._makeResizable(); 18153 } 18154 } 18155 18156 if ( key === "title" ) { 18157 this._title( this.uiDialogTitlebar.find(".ui-dialog-title") ); 18158 } 18159 }, 18160 18161 _size: function() { 18162 // If the user has resized the dialog, the .ui-dialog and .ui-dialog-content 18163 // divs will both have width and height set, so we need to reset them 18164 var nonContentHeight, minContentHeight, maxContentHeight, 18165 options = this.options; 18166 18167 // Reset content sizing 18168 this.element.show().css({ 18169 width: "auto", 18170 minHeight: 0, 18171 maxHeight: "none", 18172 height: 0 18173 }); 18174 18175 if ( options.minWidth > options.width ) { 18176 options.width = options.minWidth; 18177 } 18178 18179 // reset wrapper sizing 18180 // determine the height of all the non-content elements 18181 nonContentHeight = this.uiDialog.css({ 18182 height: "auto", 18183 width: options.width 18184 }) 18185 .outerHeight(); 18186 minContentHeight = Math.max( 0, options.minHeight - nonContentHeight ); 18187 maxContentHeight = typeof options.maxHeight === "number" ? 18188 Math.max( 0, options.maxHeight - nonContentHeight ) : 18189 "none"; 18190 18191 if ( options.height === "auto" ) { 18192 this.element.css({ 18193 minHeight: minContentHeight, 18194 maxHeight: maxContentHeight, 18195 height: "auto" 18196 }); 18197 } else { 18198 this.element.height( Math.max( 0, options.height - nonContentHeight ) ); 18199 } 18200 18201 if (this.uiDialog.is(":data(ui-resizable)") ) { 18202 this.uiDialog.resizable( "option", "minHeight", this._minHeight() ); 18203 } 18204 }, 18205 18206 _blockFrames: function() { 18207 this.iframeBlocks = this.document.find( "iframe" ).map(function() { 18208 var iframe = $( this ); 18209 18210 return $( "<div>" ) 18211 .css({ 18212 position: "absolute", 18213 width: iframe.outerWidth(), 18214 height: iframe.outerHeight() 18215 }) 18216 .appendTo( iframe.parent() ) 18217 .offset( iframe.offset() )[0]; 18218 }); 18219 }, 18220 18221 _unblockFrames: function() { 18222 if ( this.iframeBlocks ) { 18223 this.iframeBlocks.remove(); 18224 delete this.iframeBlocks; 18225 } 18226 }, 18227 18228 _allowInteraction: function( event ) { 18229 if ( $( event.target ).closest(".ui-dialog").length ) { 18230 return true; 18231 } 18232 18233 // TODO: Remove hack when datepicker implements 18234 // the .ui-front logic (#8989) 18235 return !!$( event.target ).closest(".ui-datepicker").length; 18236 }, 18237 18238 _createOverlay: function() { 18239 if ( !this.options.modal ) { 18240 return; 18241 } 18242 18243 var that = this, 18244 widgetFullName = this.widgetFullName; 18245 if ( !$.ui.dialog.overlayInstances ) { 18246 // Prevent use of anchors and inputs. 18247 // We use a delay in case the overlay is created from an 18248 // event that we're going to be cancelling. (#2804) 18249 this._delay(function() { 18250 // Handle .dialog().dialog("close") (#4065) 18251 if ( $.ui.dialog.overlayInstances ) { 18252 this.document.bind( "focusin.dialog", function( event ) { 18253 if ( !that._allowInteraction( event ) ) { 18254 event.preventDefault(); 18255 $(".ui-dialog:visible:last .ui-dialog-content") 18256 .data( widgetFullName )._focusTabbable(); 18257 } 18258 }); 18259 } 18260 }); 18261 } 18262
18263 this.overlay = $("<div>") 18264 .addClass("ui-widget-overlay ui-front") 18265 .appendTo( this._appendTo() ); 18266 this._on( this.overlay, { 18267 mousedown: "_keepFocus" 18268 }); 18269 $.ui.dialog.overlayInstances++; 18270 }, 18271 18272 _destroyOverlay: function() { 18273 if ( !this.options.modal ) { 18274 return; 18275 } 18276 18277 if ( this.overlay ) { 18278 $.ui.dialog.overlayInstances--; 18279 18280 if ( !$.ui.dialog.overlayInstances ) { 18281 this.document.unbind( "focusin.dialog" ); 18282 } 18283 this.overlay.remove(); 18284 this.overlay = null; 18285 } 18286 } 18287}); 18288 18289$.ui.dialog.overlayInstances = 0; 18290 18291// DEPRECATED 18292if ( $.uiBackCompat !== false ) { 18293 // position option with array notation 18294 // just override with old implementation 18295 $.widget( "ui.dialog", $.ui.dialog, { 18296 _position: function() { 18297 var position = this.options.position, 18298 myAt = [], 18299 offset = [ 0, 0 ], 18300 isVisible; 18301 18302 if ( position ) { 18303 if ( typeof position === "string" || (typeof position === "object" && "0" in position ) ) { 18304 myAt = position.split ? position.split(" ") : [ position[0], position[1] ]; 18305 if ( myAt.length === 1 ) { 18306 myAt[1] = myAt[0]; 18307 } 18308 18309 $.each( [ "left", "top" ], function( i, offsetPosition ) { 18310 if ( +myAt[ i ] === myAt[ i ] ) { 18311 offset[ i ] = myAt[ i ]; 18312 myAt[ i ] = offsetPosition; 18313 } 18314 }); 18315 18316 position = { 18317 my: myAt[0] + (offset[0] < 0 ? offset[0] : "+" + offset[0]) + " " + 18318 myAt[1] + (offset[1] < 0 ? offset[1] : "+" + offset[1]), 18319 at: myAt.join(" ") 18320 }; 18321 } 18322 18323 position = $.extend( {}, $.ui.dialog.prototype.options.position, position ); 18324 } else { 18325 position = $.ui.dialog.prototype.options.position; 18326 } 18327 18328 // need to show the dialog to get the actual offset in the position plugin 18329 isVisible = this.uiDialog.is(":visible"); 18330 if ( !isVisible ) { 18331 this.uiDialog.show(); 18332 } 18333 this.uiDialog.position( position ); 18334 if ( !isVisible ) { 18335 this.uiDialog.hide(); 18336 } 18337 } 18338 }); 18339} 18340 18341}( jQuery ) ); 18342 18343 18344 18345 18346 18347/*! 18348 * jQuery UI Droppable 1.10.3 18349 * http://jqueryui.com 18350 * 18351 * Copyright 2013 jQuery Foundation and other contributors 18352 * Released under the MIT license. 18353 * http://jquery.org/license 18354 * 18355 * http://api.jqueryui.com/droppable/ 18356 * 18357 * Depends: 18358 * jquery.ui.core.js 18359 * jquery.ui.widget.js 18360 * jquery.ui.mouse.js 18361 * jquery.ui.draggable.js 18362 */ 18363 18364(function( $, undefined ) { 18365 18366function isOverAxis( x, reference, size ) { 18367 return ( x > reference ) && ( x < ( reference + size ) ); 18368} 18369 18370$.widget("ui.droppable", { 18371 version: "1.10.3", 18372 widgetEventPrefix: "drop", 18373 options: { 18374 accept: "*", 18375 activeClass: false, 18376 addClasses: true, 18377 greedy: false, 18378 hoverClass: false, 18379 scope: "default", 18380 tolerance: "intersect", 18381 18382 // callbacks 18383 activate: null, 18384 deactivate: null, 18385 drop: null, 18386 out: null, 18387 over: null 18388 }, 18389 _create: function() { 18390 18391 var o = this.options, 18392 accept = o.accept; 18393 18394 this.isover = false; 18395 this.isout = true; 18396 18397 this.accept = $.isFunction(accept) ? accept : function(d) { 18398 return d.is(accept); 18399 }; 18400 18401 //Store the droppable's proportions 18402 this.proportions = { width: this.element[0].offsetWidth, height: this.element[0].offsetHeight }; 18403 18404 // Add the reference and positions to the manager 18405 $.ui.ddmanager.droppables[o.scope] = $.ui.ddmanager.droppables[o.scope] || []; 18406 $.ui.ddmanager.droppables[o.scope].push(this); 18407 18408 (o.addClasses && this.element.addClass("ui-droppable")); 18409 18410 }, 18411 18412 _destroy: function() { 18413 var i = 0, 18414 drop = $.ui.ddmanager.droppables[this.options.scope]; 18415 18416 for ( ; i < drop.length; i++ ) { 18417 if ( drop[i] === this ) { 18418 drop.splice(i, 1); 18419 } 18420 } 18421 18422 this.element.removeClass("ui-droppable ui-droppable-disabled"); 18423 }, 18424 18425 _setOption: function(key, value) { 18426 18427 if(key === "accept") { 18428 this.accept = $.isFunction(value) ? value : function(d) { 18429 return d.is(value); 18430 }; 18431 } 18432 $.Widget.prototype._setOption.apply(this, arguments); 18433 }, 18434 18435 _activate: function(event) { 18436 var draggable = $.ui.ddmanager.current; 18437 if(this.options.activeClass) { 18438 this.element.addClass(this.options.activeClass); 18439 } 18440 if(draggable){ 18441 this._trigger("activate", event, this.ui(draggable)); 18442 } 18443 }, 18444 18445 _deactivate: function(event) { 18446 var draggable = $.ui.ddmanager.current; 18447 if(this.options.activeClass) { 18448 this.element.removeClass(this.options.activeClass); 18449 } 18450 if(draggable){ 18451 this._trigger("deactivate", event, this.ui(draggable)); 18452 } 18453 }, 18454 18455 _over: function(event) { 18456 18457 var draggable = $.ui.ddmanager.current; 18458 18459 // Bail if draggable and droppable are same element 18460 if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) { 18461 return; 18462 } 18463 18464 if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) { 18465 if(this.options.hoverClass) { 18466 this.element.addClass(this.options.hoverClass); 18467 } 18468 this._trigger("over", event, this.ui(draggable)); 18469 } 18470 18471 }, 18472 18473 _out: function(event) { 18474 18475 var draggable = $.ui.ddmanager.current; 18476 18477 // Bail if draggable and droppable are same element 18478 if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) { 18479 return; 18480 } 18481 18482 if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) { 18483 if(this.options.hoverClass) { 18484 this.element.removeClass(this.options.hoverClass); 18485 } 18486 this._trigger("out", event, this.ui(draggable)); 18487 } 18488 18489 }, 18490 18491 _drop: function(event,custom) { 18492 18493 var draggable = custom || $.ui.ddmanager.current, 18494 childrenIntersection = false;
18495 18496 // Bail if draggable and droppable are same element 18497 if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) { 18498 return false; 18499 } 18500 18501 this.element.find(":data(ui-droppable)").not(".ui-draggable-dragging").each(function() { 18502 var inst = $.data(this, "ui-droppable"); 18503 if( 18504 inst.options.greedy && 18505 !inst.options.disabled && 18506 inst.options.scope === draggable.options.scope && 18507 inst.accept.call(inst.element[0], (draggable.currentItem || draggable.element)) && 18508 $.ui.intersect(draggable, $.extend(inst, { offset: inst.element.offset() }), inst.options.tolerance) 18509 ) { childrenIntersection = true; return false; } 18510 }); 18511 if(childrenIntersection) { 18512 return false; 18513 } 18514 18515 if(this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) { 18516 if(this.options.activeClass) { 18517 this.element.removeClass(this.options.activeClass); 18518 } 18519 if(this.options.hoverClass) { 18520 this.element.removeClass(this.options.hoverClass); 18521 } 18522 this._trigger("drop", event, this.ui(draggable)); 18523 return this.element; 18524 } 18525 18526 return false; 18527 18528 }, 18529 18530 ui: function(c) { 18531 return { 18532 draggable: (c.currentItem || c.element), 18533 helper: c.helper, 18534 position: c.position, 18535 offset: c.positionAbs 18536 }; 18537 } 18538 18539}); 18540 18541$.ui.intersect = function(draggable, droppable, toleranceMode) { 18542 18543 if (!droppable.offset) { 18544 return false; 18545 } 18546 18547 var draggableLeft, draggableTop, 18548 x1 = (draggable.positionAbs || draggable.position.absolute).left, x2 = x1 + draggable.helperProportions.width, 18549 y1 = (draggable.positionAbs || draggable.position.absolute).top, y2 = y1 + draggable.helperProportions.height, 18550 l = droppable.offset.left, r = l + droppable.proportions.width, 18551 t = droppable.offset.top, b = t + droppable.proportions.height; 18552 18553 switch (toleranceMode) { 18554 case "fit": 18555 return (l <= x1 && x2 <= r && t <= y1 && y2 <= b); 18556 case "intersect": 18557 return (l < x1 + (draggable.helperProportions.width / 2) && // Right Half 18558 x2 - (draggable.helperProportions.width / 2) < r && // Left Half 18559 t < y1 + (draggable.helperProportions.height / 2) && // Bottom Half 18560 y2 - (draggable.helperProportions.height / 2) < b ); // Top Half 18561 case "pointer": 18562 draggableLeft = ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left); 18563 draggableTop = ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top); 18564 return isOverAxis( draggableTop, t, droppable.proportions.height ) && isOverAxis( draggableLeft, l, droppable.proportions.width ); 18565 case "touch": 18566 return ( 18567 (y1 >= t && y1 <= b) || // Top edge touching 18568 (y2 >= t && y2 <= b) || // Bottom edge touching 18569 (y1 < t && y2 > b) // Surrounded vertically 18570 ) && ( 18571 (x1 >= l && x1 <= r) || // Left edge touching 18572 (x2 >= l && x2 <= r) || // Right edge touching 18573 (x1 < l && x2 > r) // Surrounded horizontally 18574 ); 18575 default: 18576 return false; 18577 } 18578 18579}; 18580 18581/* 18582 This manager tracks offsets of draggables and droppables 18583*/ 18584$.ui.ddmanager = { 18585 current: null, 18586 droppables: { "default": [] }, 18587 prepareOffsets: function(t, event) { 18588 18589 var i, j, 18590 m = $.ui.ddmanager.droppables[t.options.scope] || [], 18591 type = event ? event.type : null, // workaround for #2317 18592 list = (t.currentItem || t.element).find(":data(ui-droppable)").addBack(); 18593 18594 droppablesLoop: for (i = 0; i < m.length; i++) { 18595 18596 //No disabled and non-accepted 18597 if(m[i].options.disabled || (t && !m[i].accept.call(m[i].element[0],(t.currentItem || t.element)))) { 18598 continue; 18599 } 18600 18601 // Filter out elements in the current dragged item 18602 for (j=0; j < list.length; j++) { 18603 if(list[j] === m[i].element[0]) { 18604 m[i].proportions.height = 0; 18605 continue droppablesLoop; 18606 } 18607 } 18608 18609 m[i].visible = m[i].element.css("display") !== "none"; 18610 if(!m[i].visible) { 18611 continue; 18612 } 18613 18614 //Activate the droppable if used directly from draggables 18615 if(type === "mousedown") { 18616 m[i]._activate.call(m[i], event); 18617 } 18618 18619 m[i].offset = m[i].element.offset(); 18620 m[i].proportions = { width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight }; 18621 18622 } 18623 18624 }, 18625 drop: function(draggable, event) { 18626 18627 var dropped = false;
vendor: 61,266 bytes, lines 18628-21132
18628 // Create a copy of the droppables in case the list changes during the drop (#9116) 18629 $.each(($.ui.ddmanager.droppables[draggable.options.scope] || []).slice(), function() { 18630 18631 if(!this.options) { 18632 return; 18633 } 18634 if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance)) { 18635 dropped = this._drop.call(this, event) || dropped; 18636 } 18637 18638 if (!this.options.disabled && this.visible && this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) { 18639 this.isout = true; 18640 this.isover = false; 18641 this._deactivate.call(this, event); 18642 } 18643 18644 }); 18645 return dropped; 18646 18647 }, 18648 dragStart: function( draggable, event ) { 18649 //Listen for scrolling so that if the dragging causes scrolling the position of the droppables can be recalculated (see #5003) 18650 draggable.element.parentsUntil( "body" ).bind( "scroll.droppable", function() { 18651 if( !draggable.options.refreshPositions ) { 18652 $.ui.ddmanager.prepareOffsets( draggable, event ); 18653 } 18654 }); 18655 }, 18656 drag: function(draggable, event) { 18657 18658 //If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse. 18659 if(draggable.options.refreshPositions) { 18660 $.ui.ddmanager.prepareOffsets(draggable, event); 18661 } 18662 18663 //Run through all droppables and check their positions based on specific tolerance options 18664 $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() { 18665 18666 if(this.options.disabled || this.greedyChild || !this.visible) { 18667 return; 18668 } 18669 18670 var parentInstance, scope, parent, 18671 intersects = $.ui.intersect(draggable, this, this.options.tolerance), 18672 c = !intersects && this.isover ? "isout" : (intersects && !this.isover ? "isover" : null); 18673 if(!c) { 18674 return; 18675 } 18676 18677 if (this.options.greedy) { 18678 // find droppable parents with same scope 18679 scope = this.options.scope; 18680 parent = this.element.parents(":data(ui-droppable)").filter(function () { 18681 return $.data(this, "ui-droppable").options.scope === scope; 18682 }); 18683 18684 if (parent.length) { 18685 parentInstance = $.data(parent[0], "ui-droppable"); 18686 parentInstance.greedyChild = (c === "isover"); 18687 } 18688 } 18689 18690 // we just moved into a greedy child 18691 if (parentInstance && c === "isover") { 18692 parentInstance.isover = false; 18693 parentInstance.isout = true; 18694 parentInstance._out.call(parentInstance, event); 18695 } 18696 18697 this[c] = true; 18698 this[c === "isout" ? "isover" : "isout"] = false; 18699 this[c === "isover" ? "_over" : "_out"].call(this, event); 18700 18701 // we just moved out of a greedy child 18702 if (parentInstance && c === "isout") { 18703 parentInstance.isout = false; 18704 parentInstance.isover = true; 18705 parentInstance._over.call(parentInstance, event); 18706 } 18707 }); 18708 18709 }, 18710 dragStop: function( draggable, event ) { 18711 draggable.element.parentsUntil( "body" ).unbind( "scroll.droppable" ); 18712 //Call prepareOffsets one final time since IE does not fire return scroll events when overflow was caused by drag (see #5003) 18713 if( !draggable.options.refreshPositions ) { 18714 $.ui.ddmanager.prepareOffsets( draggable, event ); 18715 } 18716 } 18717}; 18718 18719})(jQuery); 18720/*! 18721 * jQuery UI Effects 1.10.3 18722 * http://jqueryui.com 18723 * 18724 * Copyright 2013 jQuery Foundation and other contributors 18725 * Released under the MIT license. 18726 * http://jquery.org/license 18727 * 18728 * http://api.jqueryui.com/category/effects-core/ 18729 */ 18730 18731(function($, undefined) { 18732 18733var dataSpace = "ui-effects-"; 18734 18735$.effects = { 18736 effect: {} 18737}; 18738 18739/*! 18740 * jQuery Color Animations v2.1.2 18741 * https://github.com/jquery/jquery-color 18742 * 18743 * Copyright 2013 jQuery Foundation and other contributors 18744 * Released under the MIT license. 18745 * http://jquery.org/license 18746 * 18747 * Date: Wed Jan 16 08:47:09 2013 -0600 18748 */ 18749(function( jQuery, undefined ) { 18750 18751 var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor", 18752 18753 // plusequals test for += 100 -= 100 18754 rplusequals = /^([\-+])=\s*(\d+\.?\d*)/, 18755 // a set of RE's that can match strings and generate color tuples. 18756 stringParsers = [{ 18757 re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/, 18758 parse: function( execResult ) { 18759 return [ 18760 execResult[ 1 ], 18761 execResult[ 2 ], 18762 execResult[ 3 ], 18763 execResult[ 4 ] 18764 ]; 18765 } 18766 }, { 18767 re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/, 18768 parse: function( execResult ) { 18769 return [ 18770 execResult[ 1 ] * 2.55, 18771 execResult[ 2 ] * 2.55, 18772 execResult[ 3 ] * 2.55, 18773 execResult[ 4 ] 18774 ]; 18775 } 18776 }, { 18777 // this regex ignores A-F because it's compared against an already lowercased string 18778 re: /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/, 18779 parse: function( execResult ) { 18780 return [ 18781 parseInt( execResult[ 1 ], 16 ), 18782 parseInt( execResult[ 2 ], 16 ), 18783 parseInt( execResult[ 3 ], 16 ) 18784 ]; 18785 } 18786 }, { 18787 // this regex ignores A-F because it's compared against an already lowercased string 18788 re: /#([a-f0-9])([a-f0-9])([a-f0-9])/, 18789 parse: function( execResult ) { 18790 return [ 18791 parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ), 18792 parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ), 18793 parseInt( execResult[ 3 ] + execResult[ 3 ], 16 ) 18794 ]; 18795 } 18796 }, { 18797 re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/, 18798 space: "hsla", 18799 parse: function( execResult ) { 18800 return [ 18801 execResult[ 1 ], 18802 execResult[ 2 ] / 100, 18803 execResult[ 3 ] / 100, 18804 execResult[ 4 ] 18805 ]; 18806 } 18807 }], 18808 18809 // jQuery.Color( ) 18810 color = jQuery.Color = function( color, green, blue, alpha ) { 18811 return new jQuery.Color.fn.parse( color, green, blue, alpha ); 18812 }, 18813 spaces = { 18814 rgba: { 18815 props: { 18816 red: { 18817 idx: 0, 18818 type: "byte" 18819 }, 18820 green: { 18821 idx: 1, 18822 type: "byte" 18823 }, 18824 blue: { 18825 idx: 2, 18826 type: "byte" 18827 } 18828 } 18829 }, 18830 18831 hsla: { 18832 props: { 18833 hue: { 18834 idx: 0, 18835 type: "degrees" 18836 }, 18837 saturation: { 18838 idx: 1, 18839 type: "percent" 18840 }, 18841 lightness: { 18842 idx: 2, 18843 type: "percent" 18844 } 18845 } 18846 } 18847 }, 18848 propTypes = { 18849 "byte": { 18850 floor: true, 18851 max: 255 18852 }, 18853 "percent": { 18854 max: 1 18855 }, 18856 "degrees": { 18857 mod: 360, 18858 floor: true 18859 } 18860 }, 18861 support = color.support = {}, 18862 18863 // element for support tests 18864 supportElem = jQuery( "<p>" )[ 0 ], 18865 18866 // colors = jQuery.Color.names 18867 colors, 18868 18869 // local aliases of functions called often 18870 each = jQuery.each; 18871 18872// determine rgba support immediately 18873supportElem.style.cssText = "background-color:rgba(1,1,1,.5)"; 18874support.rgba = supportElem.style.backgroundColor.indexOf( "rgba" ) > -1; 18875 18876// define cache name and alpha properties 18877// for rgba and hsla spaces 18878each( spaces, function( spaceName, space ) { 18879 space.cache = "_" + spaceName; 18880 space.props.alpha = { 18881 idx: 3, 18882 type: "percent", 18883 def: 1 18884 }; 18885}); 18886 18887function clamp( value, prop, allowEmpty ) { 18888 var type = propTypes[ prop.type ] || {}; 18889 18890 if ( value == null ) { 18891 return (allowEmpty || !prop.def) ? null : prop.def; 18892 } 18893 18894 // ~~ is an short way of doing floor for positive numbers 18895 value = type.floor ? ~~value : parseFloat( value ); 18896 18897 // IE will pass in empty strings as value for alpha, 18898 // which will hit this case 18899 if ( isNaN( value ) ) { 18900 return prop.def; 18901 } 18902 18903 if ( type.mod ) { 18904 // we add mod before modding to make sure that negatives values 18905 // get converted properly: -10 -> 350 18906 return (value + type.mod) % type.mod; 18907 } 18908 18909 // for now all property types without mod have min and max 18910 return 0 > value ? 0 : type.max < value ? type.max : value; 18911} 18912 18913function stringParse( string ) { 18914 var inst = color(), 18915 rgba = inst._rgba = []; 18916 18917 string = string.toLowerCase(); 18918 18919 each( stringParsers, function( i, parser ) { 18920 var parsed, 18921 match = parser.re.exec( string ), 18922 values = match && parser.parse( match ), 18923 spaceName = parser.space || "rgba"; 18924 18925 if ( values ) { 18926 parsed = inst[ spaceName ]( values ); 18927 18928 // if this was an rgba parse the assignment might happen twice 18929 // oh well.... 18930 inst[ spaces[ spaceName ].cache ] = parsed[ spaces[ spaceName ].cache ]; 18931 rgba = inst._rgba = parsed._rgba; 18932 18933 // exit each( stringParsers ) here because we matched 18934 return false; 18935 } 18936 }); 18937 18938 // Found a stringParser that handled it 18939 if ( rgba.length ) { 18940 18941 // if this came from a parsed string, force "transparent" when alpha is 0 18942 // chrome, (and maybe others) return "transparent" as rgba(0,0,0,0) 18943 if ( rgba.join() === "0,0,0,0" ) { 18944 jQuery.extend( rgba, colors.transparent ); 18945 } 18946 return inst; 18947 } 18948 18949 // named colors 18950 return colors[ string ]; 18951} 18952 18953color.fn = jQuery.extend( color.prototype, { 18954 parse: function( red, green, blue, alpha ) { 18955 if ( red === undefined ) { 18956 this._rgba = [ null, null, null, null ]; 18957 return this; 18958 } 18959 if ( red.jquery || red.nodeType ) { 18960 red = jQuery( red ).css( green ); 18961 green = undefined; 18962 } 18963 18964 var inst = this, 18965 type = jQuery.type( red ), 18966 rgba = this._rgba = []; 18967 18968 // more than 1 argument specified - assume ( red, green, blue, alpha ) 18969 if ( green !== undefined ) { 18970 red = [ red, green, blue, alpha ]; 18971 type = "array"; 18972 } 18973 18974 if ( type === "string" ) { 18975 return this.parse( stringParse( red ) || colors._default ); 18976 } 18977 18978 if ( type === "array" ) { 18979 each( spaces.rgba.props, function( key, prop ) { 18980 rgba[ prop.idx ] = clamp( red[ prop.idx ], prop ); 18981 }); 18982 return this; 18983 } 18984 18985 if ( type === "object" ) { 18986 if ( red instanceof color ) { 18987 each( spaces, function( spaceName, space ) { 18988 if ( red[ space.cache ] ) { 18989 inst[ space.cache ] = red[ space.cache ].slice(); 18990 } 18991 }); 18992 } else { 18993 each( spaces, function( spaceName, space ) { 18994 var cache = space.cache; 18995 each( space.props, function( key, prop ) { 18996 18997 // if the cache doesn't exist, and we know how to convert 18998 if ( !inst[ cache ] && space.to ) { 18999 19000 // if the value was null, we don't need to copy it 19001 // if the key was alpha, we don't need to copy it either 19002 if ( key === "alpha" || red[ key ] == null ) { 19003 return; 19004 } 19005 inst[ cache ] = space.to( inst._rgba ); 19006 } 19007 19008 // this is the only case where we allow nulls for ALL properties. 19009 // call clamp with alwaysAllowEmpty 19010 inst[ cache ][ prop.idx ] = clamp( red[ key ], prop, true ); 19011 }); 19012 19013 // everything defined but alpha? 19014 if ( inst[ cache ] && jQuery.inArray( null, inst[ cache ].slice( 0, 3 ) ) < 0 ) { 19015 // use the default of 1 19016 inst[ cache ][ 3 ] = 1; 19017 if ( space.from ) { 19018 inst._rgba = space.from( inst[ cache ] ); 19019 } 19020 } 19021 }); 19022 } 19023 return this; 19024 } 19025 }, 19026 is: function( compare ) { 19027 var is = color( compare ), 19028 same = true, 19029 inst = this; 19030 19031 each( spaces, function( _, space ) { 19032 var localCache, 19033 isCache = is[ space.cache ]; 19034 if (isCache) { 19035 localCache = inst[ space.cache ] || space.to && space.to( inst._rgba ) || []; 19036 each( space.props, function( _, prop ) { 19037 if ( isCache[ prop.idx ] != null ) { 19038 same = ( isCache[ prop.idx ] === localCache[ prop.idx ] ); 19039 return same; 19040 } 19041 }); 19042 } 19043 return same; 19044 }); 19045 return same; 19046 }, 19047 _space: function() { 19048 var used = [], 19049 inst = this; 19050 each( spaces, function( spaceName, space ) { 19051 if ( inst[ space.cache ] ) { 19052 used.push( spaceName ); 19053 } 19054 }); 19055 return used.pop(); 19056 }, 19057 transition: function( other, distance ) { 19058 var end = color( other ), 19059 spaceName = end._space(), 19060 space = spaces[ spaceName ], 19061 startColor = this.alpha() === 0 ? color( "transparent" ) : this, 19062 start = startColor[ space.cache ] || space.to( startColor._rgba ), 19063 result = start.slice(); 19064 19065 end = end[ space.cache ]; 19066 each( space.props, function( key, prop ) { 19067 var index = prop.idx, 19068 startValue = start[ index ], 19069 endValue = end[ index ], 19070 type = propTypes[ prop.type ] || {}; 19071 19072 // if null, don't override start value 19073 if ( endValue === null ) { 19074 return; 19075 } 19076 // if null - use end 19077 if ( startValue === null ) { 19078 result[ index ] = endValue; 19079 } else { 19080 if ( type.mod ) { 19081 if ( endValue - startValue > type.mod / 2 ) { 19082 startValue += type.mod; 19083 } else if ( startValue - endValue > type.mod / 2 ) { 19084 startValue -= type.mod; 19085 } 19086 } 19087 result[ index ] = clamp( ( endValue - startValue ) * distance + startValue, prop ); 19088 } 19089 }); 19090 return this[ spaceName ]( result ); 19091 }, 19092 blend: function( opaque ) { 19093 // if we are already opaque - return ourself 19094 if ( this._rgba[ 3 ] === 1 ) { 19095 return this; 19096 } 19097 19098 var rgb = this._rgba.slice(), 19099 a = rgb.pop(), 19100 blend = color( opaque )._rgba; 19101 19102 return color( jQuery.map( rgb, function( v, i ) { 19103 return ( 1 - a ) * blend[ i ] + a * v; 19104 })); 19105 }, 19106 toRgbaString: function() { 19107 var prefix = "rgba(", 19108 rgba = jQuery.map( this._rgba, function( v, i ) { 19109 return v == null ? ( i > 2 ? 1 : 0 ) : v; 19110 }); 19111 19112 if ( rgba[ 3 ] === 1 ) { 19113 rgba.pop(); 19114 prefix = "rgb("; 19115 } 19116 19117 return prefix + rgba.join() + ")"; 19118 }, 19119 toHslaString: function() { 19120 var prefix = "hsla(", 19121 hsla = jQuery.map( this.hsla(), function( v, i ) { 19122 if ( v == null ) { 19123 v = i > 2 ? 1 : 0; 19124 } 19125 19126 // catch 1 and 2 19127 if ( i && i < 3 ) { 19128 v = Math.round( v * 100 ) + "%"; 19129 } 19130 return v; 19131 }); 19132 19133 if ( hsla[ 3 ] === 1 ) { 19134 hsla.pop(); 19135 prefix = "hsl("; 19136 } 19137 return prefix + hsla.join() + ")"; 19138 }, 19139 toHexString: function( includeAlpha ) { 19140 var rgba = this._rgba.slice(), 19141 alpha = rgba.pop(); 19142 19143 if ( includeAlpha ) { 19144 rgba.push( ~~( alpha * 255 ) ); 19145 } 19146 19147 return "#" + jQuery.map( rgba, function( v ) { 19148 19149 // default to 0 when nulls exist 19150 v = ( v || 0 ).toString( 16 ); 19151 return v.length === 1 ? "0" + v : v; 19152 }).join(""); 19153 }, 19154 toString: function() { 19155 return this._rgba[ 3 ] === 0 ? "transparent" : this.toRgbaString(); 19156 } 19157}); 19158color.fn.parse.prototype = color.fn; 19159 19160// hsla conversions adapted from: 19161// https://code.google.com/p/maashaack/source/browse/packages/graphics/trunk/src/graphics/colors/HUE2RGB.as?r=5021 19162 19163function hue2rgb( p, q, h ) { 19164 h = ( h + 1 ) % 1; 19165 if ( h * 6 < 1 ) { 19166 return p + (q - p) * h * 6; 19167 } 19168 if ( h * 2 < 1) { 19169 return q; 19170 } 19171 if ( h * 3 < 2 ) { 19172 return p + (q - p) * ((2/3) - h) * 6; 19173 } 19174 return p; 19175} 19176 19177spaces.hsla.to = function ( rgba ) { 19178 if ( rgba[ 0 ] == null || rgba[ 1 ] == null || rgba[ 2 ] == null ) { 19179 return [ null, null, null, rgba[ 3 ] ]; 19180 } 19181 var r = rgba[ 0 ] / 255, 19182 g = rgba[ 1 ] / 255, 19183 b = rgba[ 2 ] / 255, 19184 a = rgba[ 3 ], 19185 max = Math.max( r, g, b ), 19186 min = Math.min( r, g, b ), 19187 diff = max - min, 19188 add = max + min, 19189 l = add * 0.5, 19190 h, s; 19191 19192 if ( min === max ) { 19193 h = 0; 19194 } else if ( r === max ) { 19195 h = ( 60 * ( g - b ) / diff ) + 360; 19196 } else if ( g === max ) { 19197 h = ( 60 * ( b - r ) / diff ) + 120; 19198 } else { 19199 h = ( 60 * ( r - g ) / diff ) + 240; 19200 } 19201 19202 // chroma (diff) == 0 means greyscale which, by definition, saturation = 0% 19203 // otherwise, saturation is based on the ratio of chroma (diff) to lightness (add) 19204 if ( diff === 0 ) { 19205 s = 0; 19206 } else if ( l <= 0.5 ) { 19207 s = diff / add; 19208 } else { 19209 s = diff / ( 2 - add ); 19210 } 19211 return [ Math.round(h) % 360, s, l, a == null ? 1 : a ]; 19212}; 19213 19214spaces.hsla.from = function ( hsla ) { 19215 if ( hsla[ 0 ] == null || hsla[ 1 ] == null || hsla[ 2 ] == null ) { 19216 return [ null, null, null, hsla[ 3 ] ]; 19217 } 19218 var h = hsla[ 0 ] / 360, 19219 s = hsla[ 1 ], 19220 l = hsla[ 2 ], 19221 a = hsla[ 3 ], 19222 q = l <= 0.5 ? l * ( 1 + s ) : l + s - l * s, 19223 p = 2 * l - q; 19224 19225 return [ 19226 Math.round( hue2rgb( p, q, h + ( 1 / 3 ) ) * 255 ), 19227 Math.round( hue2rgb( p, q, h ) * 255 ), 19228 Math.round( hue2rgb( p, q, h - ( 1 / 3 ) ) * 255 ), 19229 a 19230 ]; 19231}; 19232 19233 19234each( spaces, function( spaceName, space ) { 19235 var props = space.props, 19236 cache = space.cache, 19237 to = space.to, 19238 from = space.from; 19239 19240 // makes rgba() and hsla() 19241 color.fn[ spaceName ] = function( value ) { 19242 19243 // generate a cache for this space if it doesn't exist 19244 if ( to && !this[ cache ] ) { 19245 this[ cache ] = to( this._rgba ); 19246 } 19247 if ( value === undefined ) { 19248 return this[ cache ].slice(); 19249 } 19250 19251 var ret, 19252 type = jQuery.type( value ), 19253 arr = ( type === "array" || type === "object" ) ? value : arguments, 19254 local = this[ cache ].slice(); 19255 19256 each( props, function( key, prop ) { 19257 var val = arr[ type === "object" ? key : prop.idx ]; 19258 if ( val == null ) { 19259 val = local[ prop.idx ]; 19260 } 19261 local[ prop.idx ] = clamp( val, prop ); 19262 }); 19263 19264 if ( from ) { 19265 ret = color( from( local ) ); 19266 ret[ cache ] = local; 19267 return ret; 19268 } else { 19269 return color( local ); 19270 } 19271 }; 19272 19273 // makes red() green() blue() alpha() hue() saturation() lightness() 19274 each( props, function( key, prop ) { 19275 // alpha is included in more than one space 19276 if ( color.fn[ key ] ) { 19277 return; 19278 } 19279 color.fn[ key ] = function( value ) { 19280 var vtype = jQuery.type( value ), 19281 fn = ( key === "alpha" ? ( this._hsla ? "hsla" : "rgba" ) : spaceName ), 19282 local = this[ fn ](), 19283 cur = local[ prop.idx ], 19284 match; 19285 19286 if ( vtype === "undefined" ) { 19287 return cur; 19288 } 19289 19290 if ( vtype === "function" ) { 19291 value = value.call( this, cur ); 19292 vtype = jQuery.type( value ); 19293 } 19294 if ( value == null && prop.empty ) { 19295 return this; 19296 } 19297 if ( vtype === "string" ) { 19298 match = rplusequals.exec( value ); 19299 if ( match ) { 19300 value = cur + parseFloat( match[ 2 ] ) * ( match[ 1 ] === "+" ? 1 : -1 ); 19301 } 19302 } 19303 local[ prop.idx ] = value; 19304 return this[ fn ]( local ); 19305 }; 19306 }); 19307}); 19308 19309// add cssHook and .fx.step function for each named hook. 19310// accept a space separated string of properties 19311color.hook = function( hook ) { 19312 var hooks = hook.split( " " ); 19313 each( hooks, function( i, hook ) { 19314 jQuery.cssHooks[ hook ] = { 19315 set: function( elem, value ) { 19316 var parsed, curElem, 19317 backgroundColor = ""; 19318 19319 if ( value !== "transparent" && ( jQuery.type( value ) !== "string" || ( parsed = stringParse( value ) ) ) ) { 19320 value = color( parsed || value ); 19321 if ( !support.rgba && value._rgba[ 3 ] !== 1 ) { 19322 curElem = hook === "backgroundColor" ? elem.parentNode : elem; 19323 while ( 19324 (backgroundColor === "" || backgroundColor === "transparent") && 19325 curElem && curElem.style 19326 ) { 19327 try { 19328 backgroundColor = jQuery.css( curElem, "backgroundColor" ); 19329 curElem = curElem.parentNode; 19330 } catch ( e ) { 19331 } 19332 } 19333 19334 value = value.blend( backgroundColor && backgroundColor !== "transparent" ? 19335 backgroundColor : 19336 "_default" ); 19337 } 19338 19339 value = value.toRgbaString(); 19340 } 19341 try { 19342 elem.style[ hook ] = value; 19343 } catch( e ) { 19344 // wrapped to prevent IE from throwing errors on "invalid" values like 'auto' or 'inherit' 19345 } 19346 } 19347 }; 19348 jQuery.fx.step[ hook ] = function( fx ) { 19349 if ( !fx.colorInit ) { 19350 fx.start = color( fx.elem, hook ); 19351 fx.end = color( fx.end ); 19352 fx.colorInit = true; 19353 } 19354 jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) ); 19355 }; 19356 }); 19357 19358}; 19359 19360color.hook( stepHooks ); 19361 19362jQuery.cssHooks.borderColor = { 19363 expand: function( value ) { 19364 var expanded = {}; 19365 19366 each( [ "Top", "Right", "Bottom", "Left" ], function( i, part ) { 19367 expanded[ "border" + part + "Color" ] = value; 19368 }); 19369 return expanded; 19370 } 19371}; 19372 19373// Basic color names only. 19374// Usage of any of the other color names requires adding yourself or including 19375// jquery.color.svg-names.js. 19376colors = jQuery.Color.names = { 19377 // 4.1. Basic color keywords 19378 aqua: "#00ffff", 19379 black: "#000000", 19380 blue: "#0000ff", 19381 fuchsia: "#ff00ff", 19382 gray: "#808080", 19383 green: "#008000", 19384 lime: "#00ff00", 19385 maroon: "#800000", 19386 navy: "#000080", 19387 olive: "#808000", 19388 purple: "#800080", 19389 red: "#ff0000", 19390 silver: "#c0c0c0", 19391 teal: "#008080", 19392 white: "#ffffff", 19393 yellow: "#ffff00", 19394 19395 // 4.2.3. "transparent" color keyword 19396 transparent: [ null, null, null, 0 ], 19397 19398 _default: "#ffffff" 19399}; 19400 19401})( jQuery ); 19402 19403 19404/******************************************************************************/ 19405/****************************** CLASS ANIMATIONS ******************************/ 19406/******************************************************************************/ 19407(function() { 19408 19409var classAnimationActions = [ "add", "remove", "toggle" ], 19410 shorthandStyles = { 19411 border: 1, 19412 borderBottom: 1, 19413 borderColor: 1, 19414 borderLeft: 1, 19415 borderRight: 1, 19416 borderTop: 1, 19417 borderWidth: 1, 19418 margin: 1, 19419 padding: 1 19420 }; 19421 19422$.each([ "borderLeftStyle", "borderRightStyle", "borderBottomStyle", "borderTopStyle" ], function( _, prop ) { 19423 $.fx.step[ prop ] = function( fx ) { 19424 if ( fx.end !== "none" && !fx.setAttr || fx.pos === 1 && !fx.setAttr ) { 19425 jQuery.style( fx.elem, prop, fx.end ); 19426 fx.setAttr = true; 19427 } 19428 }; 19429}); 19430 19431function getElementStyles( elem ) { 19432 var key, len, 19433 style = elem.ownerDocument.defaultView ? 19434 elem.ownerDocument.defaultView.getComputedStyle( elem, null ) : 19435 elem.currentStyle, 19436 styles = {}; 19437 19438 if ( style && style.length && style[ 0 ] && style[ style[ 0 ] ] ) { 19439 len = style.length; 19440 while ( len-- ) { 19441 key = style[ len ]; 19442 if ( typeof style[ key ] === "string" ) { 19443 styles[ $.camelCase( key ) ] = style[ key ]; 19444 } 19445 } 19446 // support: Opera, IE <9 19447 } else { 19448 for ( key in style ) { 19449 if ( typeof style[ key ] === "string" ) { 19450 styles[ key ] = style[ key ]; 19451 } 19452 } 19453 } 19454 19455 return styles; 19456} 19457 19458 19459function styleDifference( oldStyle, newStyle ) { 19460 var diff = {}, 19461 name, value; 19462 19463 for ( name in newStyle ) { 19464 value = newStyle[ name ]; 19465 if ( oldStyle[ name ] !== value ) { 19466 if ( !shorthandStyles[ name ] ) { 19467 if ( $.fx.step[ name ] || !isNaN( parseFloat( value ) ) ) { 19468 diff[ name ] = value; 19469 } 19470 } 19471 } 19472 } 19473 19474 return diff; 19475} 19476 19477// support: jQuery <1.8 19478if ( !$.fn.addBack ) { 19479 $.fn.addBack = function( selector ) { 19480 return this.add( selector == null ? 19481 this.prevObject : this.prevObject.filter( selector ) 19482 ); 19483 }; 19484} 19485 19486$.effects.animateClass = function( value, duration, easing, callback ) { 19487 var o = $.speed( duration, easing, callback ); 19488 19489 return this.queue( function() { 19490 var animated = $( this ), 19491 baseClass = animated.attr( "class" ) || "", 19492 applyClassChange, 19493 allAnimations = o.children ? animated.find( "*" ).addBack() : animated; 19494 19495 // map the animated objects to store the original styles. 19496 allAnimations = allAnimations.map(function() { 19497 var el = $( this ); 19498 return { 19499 el: el, 19500 start: getElementStyles( this ) 19501 }; 19502 }); 19503 19504 // apply class change 19505 applyClassChange = function() { 19506 $.each( classAnimationActions, function(i, action) { 19507 if ( value[ action ] ) { 19508 animated[ action + "Class" ]( value[ action ] ); 19509 } 19510 }); 19511 }; 19512 applyClassChange(); 19513 19514 // map all animated objects again - calculate new styles and diff 19515 allAnimations = allAnimations.map(function() { 19516 this.end = getElementStyles( this.el[ 0 ] ); 19517 this.diff = styleDifference( this.start, this.end ); 19518 return this; 19519 }); 19520 19521 // apply original class 19522 animated.attr( "class", baseClass ); 19523 19524 // map all animated objects again - this time collecting a promise 19525 allAnimations = allAnimations.map(function() { 19526 var styleInfo = this, 19527 dfd = $.Deferred(), 19528 opts = $.extend({}, o, { 19529 queue: false, 19530 complete: function() { 19531 dfd.resolve( styleInfo ); 19532 } 19533 }); 19534 19535 this.el.animate( this.diff, opts ); 19536 return dfd.promise(); 19537 }); 19538 19539 // once all animations have completed: 19540 $.when.apply( $, allAnimations.get() ).done(function() { 19541 19542 // set the final class 19543 applyClassChange(); 19544 19545 // for each animated element, 19546 // clear all css properties that were animated 19547 $.each( arguments, function() { 19548 var el = this.el; 19549 $.each( this.diff, function(key) { 19550 el.css( key, "" ); 19551 }); 19552 }); 19553 19554 // this is guarnteed to be there if you use jQuery.speed() 19555 // it also handles dequeuing the next anim... 19556 o.complete.call( animated[ 0 ] ); 19557 }); 19558 }); 19559}; 19560 19561$.fn.extend({ 19562 addClass: (function( orig ) { 19563 return function( classNames, speed, easing, callback ) { 19564 return speed ? 19565 $.effects.animateClass.call( this, 19566 { add: classNames }, speed, easing, callback ) : 19567 orig.apply( this, arguments ); 19568 }; 19569 })( $.fn.addClass ), 19570 19571 removeClass: (function( orig ) { 19572 return function( classNames, speed, easing, callback ) { 19573 return arguments.length > 1 ? 19574 $.effects.animateClass.call( this, 19575 { remove: classNames }, speed, easing, callback ) : 19576 orig.apply( this, arguments ); 19577 }; 19578 })( $.fn.removeClass ), 19579 19580 toggleClass: (function( orig ) { 19581 return function( classNames, force, speed, easing, callback ) { 19582 if ( typeof force === "boolean" || force === undefined ) { 19583 if ( !speed ) { 19584 // without speed parameter 19585 return orig.apply( this, arguments ); 19586 } else { 19587 return $.effects.animateClass.call( this, 19588 (force ? { add: classNames } : { remove: classNames }), 19589 speed, easing, callback ); 19590 } 19591 } else { 19592 // without force parameter 19593 return $.effects.animateClass.call( this, 19594 { toggle: classNames }, force, speed, easing ); 19595 } 19596 }; 19597 })( $.fn.toggleClass ), 19598 19599 switchClass: function( remove, add, speed, easing, callback) { 19600 return $.effects.animateClass.call( this, { 19601 add: add, 19602 remove: remove 19603 }, speed, easing, callback ); 19604 } 19605}); 19606 19607})(); 19608 19609/******************************************************************************/ 19610/*********************************** EFFECTS **********************************/ 19611/******************************************************************************/ 19612 19613(function() { 19614 19615$.extend( $.effects, { 19616 version: "1.10.3", 19617 19618 // Saves a set of properties in a data storage 19619 save: function( element, set ) { 19620 for( var i=0; i < set.length; i++ ) { 19621 if ( set[ i ] !== null ) { 19622 element.data( dataSpace + set[ i ], element[ 0 ].style[ set[ i ] ] ); 19623 } 19624 } 19625 }, 19626 19627 // Restores a set of previously saved properties from a data storage 19628 restore: function( element, set ) { 19629 var val, i; 19630 for( i=0; i < set.length; i++ ) { 19631 if ( set[ i ] !== null ) { 19632 val = element.data( dataSpace + set[ i ] ); 19633 // support: jQuery 1.6.2 19634 // http://bugs.jquery.com/ticket/9917 19635 // jQuery 1.6.2 incorrectly returns undefined for any falsy value. 19636 // We can't differentiate between "" and 0 here, so we just assume 19637 // empty string since it's likely to be a more common value... 19638 if ( val === undefined ) { 19639 val = ""; 19640 } 19641 element.css( set[ i ], val ); 19642 } 19643 } 19644 }, 19645 19646 setMode: function( el, mode ) { 19647 if (mode === "toggle") { 19648 mode = el.is( ":hidden" ) ? "show" : "hide"; 19649 } 19650 return mode; 19651 }, 19652 19653 // Translates a [top,left] array into a baseline value 19654 // this should be a little more flexible in the future to handle a string & hash 19655 getBaseline: function( origin, original ) { 19656 var y, x; 19657 switch ( origin[ 0 ] ) { 19658 case "top": y = 0; break; 19659 case "middle": y = 0.5; break; 19660 case "bottom": y = 1; break; 19661 default: y = origin[ 0 ] / original.height; 19662 } 19663 switch ( origin[ 1 ] ) { 19664 case "left": x = 0; break; 19665 case "center": x = 0.5; break; 19666 case "right": x = 1; break; 19667 default: x = origin[ 1 ] / original.width; 19668 } 19669 return { 19670 x: x, 19671 y: y 19672 }; 19673 }, 19674 19675 // Wraps the element around a wrapper that copies position properties 19676 createWrapper: function( element ) { 19677 19678 // if the element is already wrapped, return it 19679 if ( element.parent().is( ".ui-effects-wrapper" )) { 19680 return element.parent(); 19681 } 19682 19683 // wrap the element 19684 var props = { 19685 width: element.outerWidth(true), 19686 height: element.outerHeight(true), 19687 "float": element.css( "float" ) 19688 }, 19689 wrapper = $( "<div></div>" ) 19690 .addClass( "ui-effects-wrapper" ) 19691 .css({ 19692 fontSize: "100%", 19693 background: "transparent", 19694 border: "none", 19695 margin: 0, 19696 padding: 0 19697 }), 19698 // Store the size in case width/height are defined in % - Fixes #5245 19699 size = { 19700 width: element.width(), 19701 height: element.height() 19702 }, 19703 active = document.activeElement; 19704 19705 // support: Firefox 19706 // Firefox incorrectly exposes anonymous content 19707 // https://bugzilla.mozilla.org/show_bug.cgi?id=561664 19708 try { 19709 active.id; 19710 } catch( e ) { 19711 active = document.body; 19712 } 19713 19714 element.wrap( wrapper ); 19715 19716 // Fixes #7595 - Elements lose focus when wrapped. 19717 if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) { 19718 $( active ).focus(); 19719 } 19720 19721 wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually lose the reference to the wrapped element 19722 19723 // transfer positioning properties to the wrapper 19724 if ( element.css( "position" ) === "static" ) { 19725 wrapper.css({ position: "relative" }); 19726 element.css({ position: "relative" }); 19727 } else { 19728 $.extend( props, { 19729 position: element.css( "position" ), 19730 zIndex: element.css( "z-index" ) 19731 }); 19732 $.each([ "top", "left", "bottom", "right" ], function(i, pos) { 19733 props[ pos ] = element.css( pos ); 19734 if ( isNaN( parseInt( props[ pos ], 10 ) ) ) { 19735 props[ pos ] = "auto"; 19736 } 19737 }); 19738 element.css({ 19739 position: "relative", 19740 top: 0, 19741 left: 0, 19742 right: "auto", 19743 bottom: "auto" 19744 }); 19745 } 19746 element.css(size); 19747 19748 return wrapper.css( props ).show(); 19749 }, 19750 19751 removeWrapper: function( element ) { 19752 var active = document.activeElement; 19753 19754 if ( element.parent().is( ".ui-effects-wrapper" ) ) { 19755 element.parent().replaceWith( element ); 19756 19757 // Fixes #7595 - Elements lose focus when wrapped. 19758 if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) { 19759 $( active ).focus(); 19760 } 19761 } 19762 19763 19764 return element; 19765 }, 19766 19767 setTransition: function( element, list, factor, value ) { 19768 value = value || {}; 19769 $.each( list, function( i, x ) { 19770 var unit = element.cssUnit( x ); 19771 if ( unit[ 0 ] > 0 ) { 19772 value[ x ] = unit[ 0 ] * factor + unit[ 1 ]; 19773 } 19774 }); 19775 return value; 19776 } 19777}); 19778 19779// return an effect options object for the given parameters: 19780function _normalizeArguments( effect, options, speed, callback ) { 19781 19782 // allow passing all options as the first parameter 19783 if ( $.isPlainObject( effect ) ) { 19784 options = effect; 19785 effect = effect.effect; 19786 } 19787 19788 // convert to an object 19789 effect = { effect: effect }; 19790 19791 // catch (effect, null, ...) 19792 if ( options == null ) { 19793 options = {}; 19794 } 19795 19796 // catch (effect, callback) 19797 if ( $.isFunction( options ) ) { 19798 callback = options; 19799 speed = null; 19800 options = {}; 19801 } 19802 19803 // catch (effect, speed, ?) 19804 if ( typeof options === "number" || $.fx.speeds[ options ] ) { 19805 callback = speed; 19806 speed = options; 19807 options = {}; 19808 } 19809 19810 // catch (effect, options, callback) 19811 if ( $.isFunction( speed ) ) { 19812 callback = speed; 19813 speed = null; 19814 } 19815 19816 // add options to effect 19817 if ( options ) { 19818 $.extend( effect, options ); 19819 } 19820 19821 speed = speed || options.duration; 19822 effect.duration = $.fx.off ? 0 : 19823 typeof speed === "number" ? speed : 19824 speed in $.fx.speeds ? $.fx.speeds[ speed ] : 19825 $.fx.speeds._default; 19826 19827 effect.complete = callback || options.complete; 19828 19829 return effect; 19830} 19831 19832function standardAnimationOption( option ) { 19833 // Valid standard speeds (nothing, number, named speed) 19834 if ( !option || typeof option === "number" || $.fx.speeds[ option ] ) { 19835 return true; 19836 } 19837 19838 // Invalid strings - treat as "normal" speed 19839 if ( typeof option === "string" && !$.effects.effect[ option ] ) { 19840 return true; 19841 } 19842 19843 // Complete callback 19844 if ( $.isFunction( option ) ) { 19845 return true; 19846 } 19847 19848 // Options hash (but not naming an effect) 19849 if ( typeof option === "object" && !option.effect ) { 19850 return true; 19851 } 19852 19853 // Didn't match any standard API 19854 return false; 19855} 19856 19857$.fn.extend({ 19858 effect: function( /* effect, options, speed, callback */ ) { 19859 var args = _normalizeArguments.apply( this, arguments ), 19860 mode = args.mode, 19861 queue = args.queue, 19862 effectMethod = $.effects.effect[ args.effect ]; 19863 19864 if ( $.fx.off || !effectMethod ) { 19865 // delegate to the original method (e.g., .show()) if possible 19866 if ( mode ) { 19867 return this[ mode ]( args.duration, args.complete ); 19868 } else { 19869 return this.each( function() { 19870 if ( args.complete ) { 19871 args.complete.call( this ); 19872 } 19873 }); 19874 } 19875 } 19876 19877 function run( next ) { 19878 var elem = $( this ), 19879 complete = args.complete, 19880 mode = args.mode; 19881 19882 function done() { 19883 if ( $.isFunction( complete ) ) { 19884 complete.call( elem[0] ); 19885 } 19886 if ( $.isFunction( next ) ) { 19887 next(); 19888 } 19889 } 19890 19891 // If the element already has the correct final state, delegate to 19892 // the core methods so the internal tracking of "olddisplay" works. 19893 if ( elem.is( ":hidden" ) ? mode === "hide" : mode === "show" ) { 19894 elem[ mode ](); 19895 done(); 19896 } else { 19897 effectMethod.call( elem[0], args, done ); 19898 } 19899 } 19900 19901 return queue === false ? this.each( run ) : this.queue( queue || "fx", run ); 19902 }, 19903 19904 show: (function( orig ) { 19905 return function( option ) { 19906 if ( standardAnimationOption( option ) ) { 19907 return orig.apply( this, arguments ); 19908 } else { 19909 var args = _normalizeArguments.apply( this, arguments ); 19910 args.mode = "show"; 19911 return this.effect.call( this, args ); 19912 } 19913 }; 19914 })( $.fn.show ), 19915 19916 hide: (function( orig ) { 19917 return function( option ) { 19918 if ( standardAnimationOption( option ) ) { 19919 return orig.apply( this, arguments ); 19920 } else { 19921 var args = _normalizeArguments.apply( this, arguments ); 19922 args.mode = "hide"; 19923 return this.effect.call( this, args ); 19924 } 19925 }; 19926 })( $.fn.hide ), 19927 19928 toggle: (function( orig ) { 19929 return function( option ) { 19930 if ( standardAnimationOption( option ) || typeof option === "boolean" ) { 19931 return orig.apply( this, arguments ); 19932 } else { 19933 var args = _normalizeArguments.apply( this, arguments ); 19934 args.mode = "toggle"; 19935 return this.effect.call( this, args ); 19936 } 19937 }; 19938 })( $.fn.toggle ), 19939 19940 // helper functions 19941 cssUnit: function(key) { 19942 var style = this.css( key ), 19943 val = []; 19944 19945 $.each( [ "em", "px", "%", "pt" ], function( i, unit ) { 19946 if ( style.indexOf( unit ) > 0 ) { 19947 val = [ parseFloat( style ), unit ]; 19948 } 19949 }); 19950 return val; 19951 } 19952}); 19953 19954})(); 19955 19956/******************************************************************************/ 19957/*********************************** EASING ***********************************/ 19958/******************************************************************************/ 19959 19960(function() { 19961 19962// based on easing equations from Robert Penner (http://www.robertpenner.com/easing) 19963 19964var baseEasings = {}; 19965 19966$.each( [ "Quad", "Cubic", "Quart", "Quint", "Expo" ], function( i, name ) { 19967 baseEasings[ name ] = function( p ) { 19968 return Math.pow( p, i + 2 ); 19969 }; 19970}); 19971 19972$.extend( baseEasings, { 19973 Sine: function ( p ) { 19974 return 1 - Math.cos( p * Math.PI / 2 ); 19975 }, 19976 Circ: function ( p ) { 19977 return 1 - Math.sqrt( 1 - p * p ); 19978 }, 19979 Elastic: function( p ) { 19980 return p === 0 || p === 1 ? p : 19981 -Math.pow( 2, 8 * (p - 1) ) * Math.sin( ( (p - 1) * 80 - 7.5 ) * Math.PI / 15 ); 19982 }, 19983 Back: function( p ) { 19984 return p * p * ( 3 * p - 2 ); 19985 }, 19986 Bounce: function ( p ) { 19987 var pow2, 19988 bounce = 4; 19989 19990 while ( p < ( ( pow2 = Math.pow( 2, --bounce ) ) - 1 ) / 11 ) {} 19991 return 1 / Math.pow( 4, 3 - bounce ) - 7.5625 * Math.pow( ( pow2 * 3 - 2 ) / 22 - p, 2 ); 19992 } 19993}); 19994 19995$.each( baseEasings, function( name, easeIn ) { 19996 $.easing[ "easeIn" + name ] = easeIn; 19997 $.easing[ "easeOut" + name ] = function( p ) { 19998 return 1 - easeIn( 1 - p ); 19999 }; 20000 $.easing[ "easeInOut" + name ] = function( p ) { 20001 return p < 0.5 ? 20002 easeIn( p * 2 ) / 2 : 20003 1 - easeIn( p * -2 + 2 ) / 2; 20004 }; 20005}); 20006 20007})(); 20008 20009})(jQuery); 20010 20011 20012/*! 20013 * jQuery UI Effects Blind 1.10.3 20014 * http://jqueryui.com 20015 * 20016 * Copyright 2013 jQuery Foundation and other contributors 20017 * Released under the MIT license. 20018 * http://jquery.org/license 20019 * 20020 * http://api.jqueryui.com/blind-effect/ 20021 * 20022 * Depends: 20023 * jquery.ui.effect.js 20024 */ 20025 20026(function( $, undefined ) { 20027 20028var rvertical = /up|down|vertical/, 20029 rpositivemotion = /up|left|vertical|horizontal/; 20030 20031$.effects.effect.blind = function( o, done ) { 20032 // Create element 20033 var el = $( this ), 20034 props = [ "position", "top", "bottom", "left", "right", "height", "width" ], 20035 mode = $.effects.setMode( el, o.mode || "hide" ), 20036 direction = o.direction || "up", 20037 vertical = rvertical.test( direction ), 20038 ref = vertical ? "height" : "width", 20039 ref2 = vertical ? "top" : "left", 20040 motion = rpositivemotion.test( direction ), 20041 animation = {}, 20042 show = mode === "show", 20043 wrapper, distance, margin; 20044 20045 // if already wrapped, the wrapper's properties are my property. #6245 20046 if ( el.parent().is( ".ui-effects-wrapper" ) ) { 20047 $.effects.save( el.parent(), props ); 20048 } else { 20049 $.effects.save( el, props ); 20050 } 20051 el.show(); 20052 wrapper = $.effects.createWrapper( el ).css({ 20053 overflow: "hidden" 20054 }); 20055 20056 distance = wrapper[ ref ](); 20057 margin = parseFloat( wrapper.css( ref2 ) ) || 0; 20058 20059 animation[ ref ] = show ? distance : 0; 20060 if ( !motion ) { 20061 el 20062 .css( vertical ? "bottom" : "right", 0 ) 20063 .css( vertical ? "top" : "left", "auto" ) 20064 .css({ position: "absolute" }); 20065 20066 animation[ ref2 ] = show ? margin : distance + margin; 20067 } 20068 20069 // start at 0 if we are showing 20070 if ( show ) { 20071 wrapper.css( ref, 0 ); 20072 if ( ! motion ) { 20073 wrapper.css( ref2, margin + distance ); 20074 } 20075 } 20076 20077 // Animate 20078 wrapper.animate( animation, { 20079 duration: o.duration, 20080 easing: o.easing, 20081 queue: false, 20082 complete: function() { 20083 if ( mode === "hide" ) { 20084 el.hide(); 20085 } 20086 $.effects.restore( el, props ); 20087 $.effects.removeWrapper( el ); 20088 done(); 20089 } 20090 }); 20091 20092}; 20093 20094})(jQuery); 20095 20096 20097/*! 20098 * jQuery UI Effects Bounce 1.10.3 20099 * http://jqueryui.com 20100 * 20101 * Copyright 2013 jQuery Foundation and other contributors 20102 * Released under the MIT license. 20103 * http://jquery.org/license 20104 * 20105 * http://api.jqueryui.com/bounce-effect/ 20106 * 20107 * Depends: 20108 * jquery.ui.effect.js 20109 */ 20110 20111(function( $, undefined ) { 20112 20113$.effects.effect.bounce = function( o, done ) { 20114 var el = $( this ), 20115 props = [ "position", "top", "bottom", "left", "right", "height", "width" ], 20116 20117 // defaults: 20118 mode = $.effects.setMode( el, o.mode || "effect" ), 20119 hide = mode === "hide", 20120 show = mode === "show", 20121 direction = o.direction || "up", 20122 distance = o.distance, 20123 times = o.times || 5, 20124 20125 // number of internal animations 20126 anims = times * 2 + ( show || hide ? 1 : 0 ), 20127 speed = o.duration / anims, 20128 easing = o.easing, 20129 20130 // utility: 20131 ref = ( direction === "up" || direction === "down" ) ? "top" : "left", 20132 motion = ( direction === "up" || direction === "left" ), 20133 i, 20134 upAnim, 20135 downAnim, 20136 20137 // we will need to re-assemble the queue to stack our animations in place 20138 queue = el.queue(), 20139 queuelen = queue.length; 20140 20141 // Avoid touching opacity to prevent clearType and PNG issues in IE 20142 if ( show || hide ) { 20143 props.push( "opacity" ); 20144 } 20145 20146 $.effects.save( el, props ); 20147 el.show(); 20148 $.effects.createWrapper( el ); // Create Wrapper 20149 20150 // default distance for the BIGGEST bounce is the outer Distance / 3 20151 if ( !distance ) { 20152 distance = el[ ref === "top" ? "outerHeight" : "outerWidth" ]() / 3; 20153 } 20154 20155 if ( show ) { 20156 downAnim = { opacity: 1 }; 20157 downAnim[ ref ] = 0; 20158 20159 // if we are showing, force opacity 0 and set the initial position 20160 // then do the "first" animation 20161 el.css( "opacity", 0 ) 20162 .css( ref, motion ? -distance * 2 : distance * 2 ) 20163 .animate( downAnim, speed, easing ); 20164 } 20165 20166 // start at the smallest distance if we are hiding 20167 if ( hide ) { 20168 distance = distance / Math.pow( 2, times - 1 ); 20169 } 20170 20171 downAnim = {}; 20172 downAnim[ ref ] = 0; 20173 // Bounces up/down/left/right then back to 0 -- times * 2 animations happen here 20174 for ( i = 0; i < times; i++ ) { 20175 upAnim = {}; 20176 upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance; 20177 20178 el.animate( upAnim, speed, easing ) 20179 .animate( downAnim, speed, easing ); 20180 20181 distance = hide ? distance * 2 : distance / 2; 20182 } 20183 20184 // Last Bounce when Hiding 20185 if ( hide ) { 20186 upAnim = { opacity: 0 }; 20187 upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance; 20188 20189 el.animate( upAnim, speed, easing ); 20190 } 20191 20192 el.queue(function() { 20193 if ( hide ) { 20194 el.hide(); 20195 } 20196 $.effects.restore( el, props ); 20197 $.effects.removeWrapper( el ); 20198 done(); 20199 }); 20200 20201 // inject all the animations we just queued to be first in line (after "inprogress") 20202 if ( queuelen > 1) { 20203 queue.splice.apply( queue, 20204 [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) ); 20205 } 20206 el.dequeue(); 20207 20208}; 20209 20210})(jQuery); 20211 20212 20213/*! 20214 * jQuery UI Effects Clip 1.10.3 20215 * http://jqueryui.com 20216 * 20217 * Copyright 2013 jQuery Foundation and other contributors 20218 * Released under the MIT license. 20219 * http://jquery.org/license 20220 * 20221 * http://api.jqueryui.com/clip-effect/ 20222 * 20223 * Depends: 20224 * jquery.ui.effect.js 20225 */ 20226 20227(function( $, undefined ) { 20228 20229$.effects.effect.clip = function( o, done ) { 20230 // Create element 20231 var el = $( this ), 20232 props = [ "position", "top", "bottom", "left", "right", "height", "width" ], 20233 mode = $.effects.setMode( el, o.mode || "hide" ), 20234 show = mode === "show", 20235 direction = o.direction || "vertical", 20236 vert = direction === "vertical", 20237 size = vert ? "height" : "width", 20238 position = vert ? "top" : "left", 20239 animation = {}, 20240 wrapper, animate, distance; 20241 20242 // Save & Show 20243 $.effects.save( el, props ); 20244 el.show(); 20245 20246 // Create Wrapper 20247 wrapper = $.effects.createWrapper( el ).css({ 20248 overflow: "hidden" 20249 }); 20250 animate = ( el[0].tagName === "IMG" ) ? wrapper : el; 20251 distance = animate[ size ](); 20252 20253 // Shift 20254 if ( show ) { 20255 animate.css( size, 0 ); 20256 animate.css( position, distance / 2 ); 20257 } 20258 20259 // Create Animation Object: 20260 animation[ size ] = show ? distance : 0; 20261 animation[ position ] = show ? 0 : distance / 2; 20262 20263 // Animate 20264 animate.animate( animation, { 20265 queue: false, 20266 duration: o.duration, 20267 easing: o.easing, 20268 complete: function() { 20269 if ( !show ) { 20270 el.hide(); 20271 } 20272 $.effects.restore( el, props ); 20273 $.effects.removeWrapper( el ); 20274 done(); 20275 } 20276 }); 20277 20278}; 20279 20280})(jQuery); 20281 20282 20283/*! 20284 * jQuery UI Effects Drop 1.10.3 20285 * http://jqueryui.com 20286 * 20287 * Copyright 2013 jQuery Foundation and other contributors 20288 * Released under the MIT license. 20289 * http://jquery.org/license 20290 * 20291 * http://api.jqueryui.com/drop-effect/ 20292 * 20293 * Depends: 20294 * jquery.ui.effect.js 20295 */ 20296 20297(function( $, undefined ) { 20298 20299$.effects.effect.drop = function( o, done ) { 20300 20301 var el = $( this ), 20302 props = [ "position", "top", "bottom", "left", "right", "opacity", "height", "width" ], 20303 mode = $.effects.setMode( el, o.mode || "hide" ), 20304 show = mode === "show", 20305 direction = o.direction || "left", 20306 ref = ( direction === "up" || direction === "down" ) ? "top" : "left", 20307 motion = ( direction === "up" || direction === "left" ) ? "pos" : "neg", 20308 animation = { 20309 opacity: show ? 1 : 0 20310 }, 20311 distance; 20312 20313 // Adjust 20314 $.effects.save( el, props ); 20315 el.show(); 20316 $.effects.createWrapper( el ); 20317 20318 distance = o.distance || el[ ref === "top" ? "outerHeight": "outerWidth" ]( true ) / 2; 20319 20320 if ( show ) { 20321 el 20322 .css( "opacity", 0 ) 20323 .css( ref, motion === "pos" ? -distance : distance ); 20324 } 20325 20326 // Animation 20327 animation[ ref ] = ( show ? 20328 ( motion === "pos" ? "+=" : "-=" ) : 20329 ( motion === "pos" ? "-=" : "+=" ) ) + 20330 distance; 20331 20332 // Animate 20333 el.animate( animation, { 20334 queue: false, 20335 duration: o.duration, 20336 easing: o.easing, 20337 complete: function() { 20338 if ( mode === "hide" ) { 20339 el.hide(); 20340 } 20341 $.effects.restore( el, props ); 20342 $.effects.removeWrapper( el ); 20343 done(); 20344 } 20345 }); 20346}; 20347 20348})(jQuery); 20349 20350 20351/*! 20352 * jQuery UI Effects Explode 1.10.3 20353 * http://jqueryui.com 20354 * 20355 * Copyright 2013 jQuery Foundation and other contributors 20356 * Released under the MIT license. 20357 * http://jquery.org/license 20358 * 20359 * http://api.jqueryui.com/explode-effect/ 20360 * 20361 * Depends: 20362 * jquery.ui.effect.js 20363 */ 20364 20365(function( $, undefined ) { 20366 20367$.effects.effect.explode = function( o, done ) { 20368 20369 var rows = o.pieces ? Math.round( Math.sqrt( o.pieces ) ) : 3, 20370 cells = rows, 20371 el = $( this ), 20372 mode = $.effects.setMode( el, o.mode || "hide" ), 20373 show = mode === "show", 20374 20375 // show and then visibility:hidden the element before calculating offset 20376 offset = el.show().css( "visibility", "hidden" ).offset(), 20377 20378 // width and height of a piece 20379 width = Math.ceil( el.outerWidth() / cells ), 20380 height = Math.ceil( el.outerHeight() / rows ), 20381 pieces = [], 20382 20383 // loop 20384 i, j, left, top, mx, my; 20385 20386 // children animate complete: 20387 function childComplete() { 20388 pieces.push( this ); 20389 if ( pieces.length === rows * cells ) { 20390 animComplete(); 20391 } 20392 } 20393 20394 // clone the element for each row and cell. 20395 for( i = 0; i < rows ; i++ ) { // ===> 20396 top = offset.top + i * height; 20397 my = i - ( rows - 1 ) / 2 ; 20398 20399 for( j = 0; j < cells ; j++ ) { // ||| 20400 left = offset.left + j * width; 20401 mx = j - ( cells - 1 ) / 2 ; 20402 20403 // Create a clone of the now hidden main element that will be absolute positioned 20404 // within a wrapper div off the -left and -top equal to size of our pieces 20405 el 20406 .clone() 20407 .appendTo( "body" ) 20408 .wrap( "<div></div>" ) 20409 .css({ 20410 position: "absolute", 20411 visibility: "visible", 20412 left: -j * width, 20413 top: -i * height 20414 }) 20415 20416 // select the wrapper - make it overflow: hidden and absolute positioned based on 20417 // where the original was located +left and +top equal to the size of pieces 20418 .parent() 20419 .addClass( "ui-effects-explode" ) 20420 .css({ 20421 position: "absolute", 20422 overflow: "hidden", 20423 width: width, 20424 height: height, 20425 left: left + ( show ? mx * width : 0 ), 20426 top: top + ( show ? my * height : 0 ), 20427 opacity: show ? 0 : 1 20428 }).animate({ 20429 left: left + ( show ? 0 : mx * width ), 20430 top: top + ( show ? 0 : my * height ), 20431 opacity: show ? 1 : 0 20432 }, o.duration || 500, o.easing, childComplete ); 20433 } 20434 } 20435 20436 function animComplete() { 20437 el.css({ 20438 visibility: "visible" 20439 }); 20440 $( pieces ).remove(); 20441 if ( !show ) { 20442 el.hide(); 20443 } 20444 done(); 20445 } 20446}; 20447 20448})(jQuery); 20449 20450 20451/*! 20452 * jQuery UI Effects Fade 1.10.3 20453 * http://jqueryui.com 20454 * 20455 * Copyright 2013 jQuery Foundation and other contributors 20456 * Released under the MIT license. 20457 * http://jquery.org/license 20458 * 20459 * http://api.jqueryui.com/fade-effect/ 20460 * 20461 * Depends: 20462 * jquery.ui.effect.js 20463 */ 20464 20465(function( $, undefined ) { 20466 20467$.effects.effect.fade = function( o, done ) { 20468 var el = $( this ), 20469 mode = $.effects.setMode( el, o.mode || "toggle" ); 20470 20471 el.animate({ 20472 opacity: mode 20473 }, { 20474 queue: false, 20475 duration: o.duration, 20476 easing: o.easing, 20477 complete: done 20478 }); 20479}; 20480 20481})( jQuery ); 20482 20483 20484/*! 20485 * jQuery UI Effects Fold 1.10.3 20486 * http://jqueryui.com 20487 * 20488 * Copyright 2013 jQuery Foundation and other contributors 20489 * Released under the MIT license. 20490 * http://jquery.org/license 20491 * 20492 * http://api.jqueryui.com/fold-effect/ 20493 * 20494 * Depends: 20495 * jquery.ui.effect.js 20496 */ 20497 20498(function( $, undefined ) { 20499 20500$.effects.effect.fold = function( o, done ) { 20501 20502 // Create element 20503 var el = $( this ), 20504 props = [ "position", "top", "bottom", "left", "right", "height", "width" ], 20505 mode = $.effects.setMode( el, o.mode || "hide" ), 20506 show = mode === "show", 20507 hide = mode === "hide", 20508 size = o.size || 15, 20509 percent = /([0-9]+)%/.exec( size ), 20510 horizFirst = !!o.horizFirst, 20511 widthFirst = show !== horizFirst, 20512 ref = widthFirst ? [ "width", "height" ] : [ "height", "width" ], 20513 duration = o.duration / 2, 20514 wrapper, distance, 20515 animation1 = {}, 20516 animation2 = {}; 20517 20518 $.effects.save( el, props ); 20519 el.show(); 20520 20521 // Create Wrapper 20522 wrapper = $.effects.createWrapper( el ).css({ 20523 overflow: "hidden" 20524 }); 20525 distance = widthFirst ? 20526 [ wrapper.width(), wrapper.height() ] : 20527 [ wrapper.height(), wrapper.width() ]; 20528 20529 if ( percent ) { 20530 size = parseInt( percent[ 1 ], 10 ) / 100 * distance[ hide ? 0 : 1 ]; 20531 } 20532 if ( show ) { 20533 wrapper.css( horizFirst ? { 20534 height: 0, 20535 width: size 20536 } : { 20537 height: size, 20538 width: 0 20539 }); 20540 } 20541 20542 // Animation 20543 animation1[ ref[ 0 ] ] = show ? distance[ 0 ] : size; 20544 animation2[ ref[ 1 ] ] = show ? distance[ 1 ] : 0; 20545 20546 // Animate 20547 wrapper 20548 .animate( animation1, duration, o.easing ) 20549 .animate( animation2, duration, o.easing, function() { 20550 if ( hide ) { 20551 el.hide(); 20552 } 20553 $.effects.restore( el, props ); 20554 $.effects.removeWrapper( el ); 20555 done(); 20556 }); 20557 20558}; 20559 20560})(jQuery); 20561 20562 20563/*! 20564 * jQuery UI Effects Highlight 1.10.3 20565 * http://jqueryui.com 20566 * 20567 * Copyright 2013 jQuery Foundation and other contributors 20568 * Released under the MIT license. 20569 * http://jquery.org/license 20570 * 20571 * http://api.jqueryui.com/highlight-effect/ 20572 * 20573 * Depends: 20574 * jquery.ui.effect.js 20575 */ 20576 20577(function( $, undefined ) { 20578 20579$.effects.effect.highlight = function( o, done ) { 20580 var elem = $( this ), 20581 props = [ "backgroundImage", "backgroundColor", "opacity" ], 20582 mode = $.effects.setMode( elem, o.mode || "show" ), 20583 animation = { 20584 backgroundColor: elem.css( "backgroundColor" ) 20585 }; 20586 20587 if (mode === "hide") { 20588 animation.opacity = 0; 20589 } 20590 20591 $.effects.save( elem, props ); 20592 20593 elem 20594 .show() 20595 .css({ 20596 backgroundImage: "none", 20597 backgroundColor: o.color || "#ffff99" 20598 }) 20599 .animate( animation, { 20600 queue: false, 20601 duration: o.duration, 20602 easing: o.easing, 20603 complete: function() { 20604 if ( mode === "hide" ) { 20605 elem.hide(); 20606 } 20607 $.effects.restore( elem, props ); 20608 done(); 20609 } 20610 }); 20611}; 20612 20613})(jQuery); 20614 20615 20616/*! 20617 * jQuery UI Effects Pulsate 1.10.3 20618 * http://jqueryui.com 20619 * 20620 * Copyright 2013 jQuery Foundation and other contributors 20621 * Released under the MIT license. 20622 * http://jquery.org/license 20623 * 20624 * http://api.jqueryui.com/pulsate-effect/ 20625 * 20626 * Depends: 20627 * jquery.ui.effect.js 20628 */ 20629 20630(function( $, undefined ) { 20631 20632$.effects.effect.pulsate = function( o, done ) { 20633 var elem = $( this ), 20634 mode = $.effects.setMode( elem, o.mode || "show" ), 20635 show = mode === "show", 20636 hide = mode === "hide", 20637 showhide = ( show || mode === "hide" ), 20638 20639 // showing or hiding leaves of the "last" animation 20640 anims = ( ( o.times || 5 ) * 2 ) + ( showhide ? 1 : 0 ), 20641 duration = o.duration / anims, 20642 animateTo = 0, 20643 queue = elem.queue(), 20644 queuelen = queue.length, 20645 i; 20646 20647 if ( show || !elem.is(":visible")) { 20648 elem.css( "opacity", 0 ).show(); 20649 animateTo = 1; 20650 } 20651 20652 // anims - 1 opacity "toggles" 20653 for ( i = 1; i < anims; i++ ) { 20654 elem.animate({ 20655 opacity: animateTo 20656 }, duration, o.easing ); 20657 animateTo = 1 - animateTo; 20658 } 20659 20660 elem.animate({ 20661 opacity: animateTo 20662 }, duration, o.easing); 20663 20664 elem.queue(function() { 20665 if ( hide ) { 20666 elem.hide(); 20667 } 20668 done(); 20669 }); 20670 20671 // We just queued up "anims" animations, we need to put them next in the queue 20672 if ( queuelen > 1 ) { 20673 queue.splice.apply( queue, 20674 [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) ); 20675 } 20676 elem.dequeue(); 20677}; 20678 20679})(jQuery); 20680 20681 20682/*! 20683 * jQuery UI Effects Scale 1.10.3 20684 * http://jqueryui.com 20685 * 20686 * Copyright 2013 jQuery Foundation and other contributors 20687 * Released under the MIT license. 20688 * http://jquery.org/license 20689 * 20690 * http://api.jqueryui.com/scale-effect/ 20691 * 20692 * Depends: 20693 * jquery.ui.effect.js 20694 */ 20695 20696(function( $, undefined ) { 20697 20698$.effects.effect.puff = function( o, done ) { 20699 var elem = $( this ), 20700 mode = $.effects.setMode( elem, o.mode || "hide" ), 20701 hide = mode === "hide", 20702 percent = parseInt( o.percent, 10 ) || 150, 20703 factor = percent / 100, 20704 original = { 20705 height: elem.height(), 20706 width: elem.width(), 20707 outerHeight: elem.outerHeight(), 20708 outerWidth: elem.outerWidth() 20709 }; 20710 20711 $.extend( o, { 20712 effect: "scale", 20713 queue: false, 20714 fade: true, 20715 mode: mode, 20716 complete: done, 20717 percent: hide ? percent : 100, 20718 from: hide ? 20719 original : 20720 { 20721 height: original.height * factor, 20722 width: original.width * factor, 20723 outerHeight: original.outerHeight * factor, 20724 outerWidth: original.outerWidth * factor 20725 } 20726 }); 20727 20728 elem.effect( o ); 20729}; 20730 20731$.effects.effect.scale = function( o, done ) { 20732 20733 // Create element 20734 var el = $( this ), 20735 options = $.extend( true, {}, o ), 20736 mode = $.effects.setMode( el, o.mode || "effect" ), 20737 percent = parseInt( o.percent, 10 ) || 20738 ( parseInt( o.percent, 10 ) === 0 ? 0 : ( mode === "hide" ? 0 : 100 ) ), 20739 direction = o.direction || "both", 20740 origin = o.origin, 20741 original = { 20742 height: el.height(), 20743 width: el.width(), 20744 outerHeight: el.outerHeight(), 20745 outerWidth: el.outerWidth() 20746 }, 20747 factor = { 20748 y: direction !== "horizontal" ? (percent / 100) : 1, 20749 x: direction !== "vertical" ? (percent / 100) : 1 20750 }; 20751 20752 // We are going to pass this effect to the size effect: 20753 options.effect = "size"; 20754 options.queue = false; 20755 options.complete = done; 20756 20757 // Set default origin and restore for show/hide 20758 if ( mode !== "effect" ) { 20759 options.origin = origin || ["middle","center"]; 20760 options.restore = true; 20761 } 20762 20763 options.from = o.from || ( mode === "show" ? { 20764 height: 0, 20765 width: 0, 20766 outerHeight: 0, 20767 outerWidth: 0 20768 } : original ); 20769 options.to = { 20770 height: original.height * factor.y, 20771 width: original.width * factor.x, 20772 outerHeight: original.outerHeight * factor.y, 20773 outerWidth: original.outerWidth * factor.x 20774 }; 20775 20776 // Fade option to support puff 20777 if ( options.fade ) { 20778 if ( mode === "show" ) { 20779 options.from.opacity = 0; 20780 options.to.opacity = 1; 20781 } 20782 if ( mode === "hide" ) { 20783 options.from.opacity = 1; 20784 options.to.opacity = 0; 20785 } 20786 } 20787 20788 // Animate 20789 el.effect( options ); 20790 20791}; 20792 20793$.effects.effect.size = function( o, done ) { 20794 20795 // Create element 20796 var original, baseline, factor, 20797 el = $( this ), 20798 props0 = [ "position", "top", "bottom", "left", "right", "width", "height", "overflow", "opacity" ], 20799 20800 // Always restore 20801 props1 = [ "position", "top", "bottom", "left", "right", "overflow", "opacity" ], 20802 20803 // Copy for children 20804 props2 = [ "width", "height", "overflow" ], 20805 cProps = [ "fontSize" ], 20806 vProps = [ "borderTopWidth", "borderBottomWidth", "paddingTop", "paddingBottom" ], 20807 hProps = [ "borderLeftWidth", "borderRightWidth", "paddingLeft", "paddingRight" ], 20808 20809 // Set options 20810 mode = $.effects.setMode( el, o.mode || "effect" ), 20811 restore = o.restore || mode !== "effect", 20812 scale = o.scale || "both", 20813 origin = o.origin || [ "middle", "center" ], 20814 position = el.css( "position" ), 20815 props = restore ? props0 : props1, 20816 zero = { 20817 height: 0, 20818 width: 0, 20819 outerHeight: 0, 20820 outerWidth: 0 20821 }; 20822 20823 if ( mode === "show" ) { 20824 el.show(); 20825 } 20826 original = { 20827 height: el.height(), 20828 width: el.width(), 20829 outerHeight: el.outerHeight(), 20830 outerWidth: el.outerWidth() 20831 }; 20832 20833 if ( o.mode === "toggle" && mode === "show" ) { 20834 el.from = o.to || zero; 20835 el.to = o.from || original; 20836 } else { 20837 el.from = o.from || ( mode === "show" ? zero : original ); 20838 el.to = o.to || ( mode === "hide" ? zero : original ); 20839 } 20840 20841 // Set scaling factor 20842 factor = { 20843 from: { 20844 y: el.from.height / original.height, 20845 x: el.from.width / original.width 20846 }, 20847 to: { 20848 y: el.to.height / original.height, 20849 x: el.to.width / original.width 20850 } 20851 }; 20852 20853 // Scale the css box 20854 if ( scale === "box" || scale === "both" ) { 20855 20856 // Vertical props scaling 20857 if ( factor.from.y !== factor.to.y ) { 20858 props = props.concat( vProps ); 20859 el.from = $.effects.setTransition( el, vProps, factor.from.y, el.from ); 20860 el.to = $.effects.setTransition( el, vProps, factor.to.y, el.to ); 20861 } 20862 20863 // Horizontal props scaling 20864 if ( factor.from.x !== factor.to.x ) { 20865 props = props.concat( hProps ); 20866 el.from = $.effects.setTransition( el, hProps, factor.from.x, el.from ); 20867 el.to = $.effects.setTransition( el, hProps, factor.to.x, el.to ); 20868 } 20869 } 20870 20871 // Scale the content 20872 if ( scale === "content" || scale === "both" ) { 20873 20874 // Vertical props scaling 20875 if ( factor.from.y !== factor.to.y ) { 20876 props = props.concat( cProps ).concat( props2 ); 20877 el.from = $.effects.setTransition( el, cProps, factor.from.y, el.from ); 20878 el.to = $.effects.setTransition( el, cProps, factor.to.y, el.to ); 20879 } 20880 } 20881 20882 $.effects.save( el, props ); 20883 el.show(); 20884 $.effects.createWrapper( el ); 20885 el.css( "overflow", "hidden" ).css( el.from ); 20886 20887 // Adjust 20888 if (origin) { // Calculate baseline shifts 20889 baseline = $.effects.getBaseline( origin, original ); 20890 el.from.top = ( original.outerHeight - el.outerHeight() ) * baseline.y; 20891 el.from.left = ( original.outerWidth - el.outerWidth() ) * baseline.x; 20892 el.to.top = ( original.outerHeight - el.to.outerHeight ) * baseline.y; 20893 el.to.left = ( original.outerWidth - el.to.outerWidth ) * baseline.x; 20894 } 20895 el.css( el.from ); // set top & left 20896 20897 // Animate 20898 if ( scale === "content" || scale === "both" ) { // Scale the children 20899 20900 // Add margins/font-size 20901 vProps = vProps.concat([ "marginTop", "marginBottom" ]).concat(cProps); 20902 hProps = hProps.concat([ "marginLeft", "marginRight" ]); 20903 props2 = props0.concat(vProps).concat(hProps); 20904 20905 el.find( "*[width]" ).each( function(){ 20906 var child = $( this ), 20907 c_original = { 20908 height: child.height(), 20909 width: child.width(), 20910 outerHeight: child.outerHeight(), 20911 outerWidth: child.outerWidth() 20912 }; 20913 if (restore) { 20914 $.effects.save(child, props2); 20915 } 20916 20917 child.from = { 20918 height: c_original.height * factor.from.y, 20919 width: c_original.width * factor.from.x, 20920 outerHeight: c_original.outerHeight * factor.from.y, 20921 outerWidth: c_original.outerWidth * factor.from.x 20922 }; 20923 child.to = { 20924 height: c_original.height * factor.to.y, 20925 width: c_original.width * factor.to.x, 20926 outerHeight: c_original.height * factor.to.y, 20927 outerWidth: c_original.width * factor.to.x 20928 }; 20929 20930 // Vertical props scaling 20931 if ( factor.from.y !== factor.to.y ) { 20932 child.from = $.effects.setTransition( child, vProps, factor.from.y, child.from ); 20933 child.to = $.effects.setTransition( child, vProps, factor.to.y, child.to ); 20934 } 20935 20936 // Horizontal props scaling 20937 if ( factor.from.x !== factor.to.x ) { 20938 child.from = $.effects.setTransition( child, hProps, factor.from.x, child.from ); 20939 child.to = $.effects.setTransition( child, hProps, factor.to.x, child.to ); 20940 } 20941 20942 // Animate children 20943 child.css( child.from ); 20944 child.animate( child.to, o.duration, o.easing, function() { 20945 20946 // Restore children 20947 if ( restore ) { 20948 $.effects.restore( child, props2 ); 20949 } 20950 }); 20951 }); 20952 } 20953 20954 // Animate 20955 el.animate( el.to, { 20956 queue: false, 20957 duration: o.duration, 20958 easing: o.easing, 20959 complete: function() { 20960 if ( el.to.opacity === 0 ) { 20961 el.css( "opacity", el.from.opacity ); 20962 } 20963 if( mode === "hide" ) { 20964 el.hide(); 20965 } 20966 $.effects.restore( el, props ); 20967 if ( !restore ) { 20968 20969 // we need to calculate our new positioning based on the scaling 20970 if ( position === "static" ) { 20971 el.css({ 20972 position: "relative", 20973 top: el.to.top, 20974 left: el.to.left 20975 }); 20976 } else { 20977 $.each([ "top", "left" ], function( idx, pos ) { 20978 el.css( pos, function( _, str ) { 20979 var val = parseInt( str, 10 ), 20980 toRef = idx ? el.to.left : el.to.top; 20981 20982 // if original was "auto", recalculate the new value from wrapper 20983 if ( str === "auto" ) { 20984 return toRef + "px"; 20985 } 20986 20987 return val + toRef + "px"; 20988 }); 20989 }); 20990 } 20991 } 20992 20993 $.effects.removeWrapper( el ); 20994 done(); 20995 } 20996 }); 20997 20998}; 20999 21000})(jQuery); 21001 21002 21003/*! 21004 * jQuery UI Effects Shake 1.10.3 21005 * http://jqueryui.com 21006 * 21007 * Copyright 2013 jQuery Foundation and other contributors 21008 * Released under the MIT license. 21009 * http://jquery.org/license 21010 * 21011 * http://api.jqueryui.com/shake-effect/ 21012 * 21013 * Depends: 21014 * jquery.ui.effect.js 21015 */ 21016 21017(function( $, undefined ) { 21018 21019$.effects.effect.shake = function( o, done ) { 21020 21021 var el = $( this ), 21022 props = [ "position", "top", "bottom", "left", "right", "height", "width" ], 21023 mode = $.effects.setMode( el, o.mode || "effect" ), 21024 direction = o.direction || "left", 21025 distance = o.distance || 20, 21026 times = o.times || 3, 21027 anims = times * 2 + 1, 21028 speed = Math.round(o.duration/anims), 21029 ref = (direction === "up" || direction === "down") ? "top" : "left", 21030 positiveMotion = (direction === "up" || direction === "left"), 21031 animation = {}, 21032 animation1 = {}, 21033 animation2 = {}, 21034 i, 21035 21036 // we will need to re-assemble the queue to stack our animations in place 21037 queue = el.queue(), 21038 queuelen = queue.length; 21039 21040 $.effects.save( el, props ); 21041 el.show(); 21042 $.effects.createWrapper( el ); 21043 21044 // Animation 21045 animation[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance; 21046 animation1[ ref ] = ( positiveMotion ? "+=" : "-=" ) + distance * 2; 21047 animation2[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance * 2; 21048 21049 // Animate 21050 el.animate( animation, speed, o.easing ); 21051 21052 // Shakes 21053 for ( i = 1; i < times; i++ ) { 21054 el.animate( animation1, speed, o.easing ).animate( animation2, speed, o.easing ); 21055 } 21056 el 21057 .animate( animation1, speed, o.easing ) 21058 .animate( animation, speed / 2, o.easing ) 21059 .queue(function() { 21060 if ( mode === "hide" ) { 21061 el.hide(); 21062 } 21063 $.effects.restore( el, props ); 21064 $.effects.removeWrapper( el ); 21065 done(); 21066 }); 21067 21068 // inject all the animations we just queued to be first in line (after "inprogress") 21069 if ( queuelen > 1) { 21070 queue.splice.apply( queue, 21071 [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) ); 21072 } 21073 el.dequeue(); 21074 21075}; 21076 21077})(jQuery); 21078 21079 21080/*! 21081 * jQuery UI Effects Slide 1.10.3 21082 * http://jqueryui.com 21083 * 21084 * Copyright 2013 jQuery Foundation and other contributors 21085 * Released under the MIT license. 21086 * http://jquery.org/license 21087 * 21088 * http://api.jqueryui.com/slide-effect/ 21089 * 21090 * Depends: 21091 * jquery.ui.effect.js 21092 */ 21093 21094(function( $, undefined ) { 21095 21096$.effects.effect.slide = function( o, done ) { 21097 21098 // Create element 21099 var el = $( this ), 21100 props = [ "position", "top", "bottom", "left", "right", "width", "height" ], 21101 mode = $.effects.setMode( el, o.mode || "show" ), 21102 show = mode === "show", 21103 direction = o.direction || "left", 21104 ref = (direction === "up" || direction === "down") ? "top" : "left", 21105 positiveMotion = (direction === "up" || direction === "left"), 21106 distance, 21107 animation = {}; 21108 21109 // Adjust 21110 $.effects.save( el, props ); 21111 el.show(); 21112 distance = o.distance || el[ ref === "top" ? "outerHeight" : "outerWidth" ]( true ); 21113 21114 $.effects.createWrapper( el ).css({ 21115 overflow: "hidden" 21116 }); 21117 21118 if ( show ) { 21119 el.css( ref, positiveMotion ? (isNaN(distance) ? "-" + distance : -distance) : distance ); 21120 } 21121 21122 // Animation 21123 animation[ ref ] = ( show ? 21124 ( positiveMotion ? "+=" : "-=") : 21125 ( positiveMotion ? "-=" : "+=")) + 21126 distance; 21127 21128 // Animate 21129 el.animate( animation, { 21130 queue: false, 21131 duration: o.duration, 21132 easing: o.easing,
21133 complete: function() { 21134 if ( mode === "hide" ) { 21135 el.hide(); 21136 } 21137 $.effects.restore( el, props ); 21138 $.effects.removeWrapper( el ); 21139 done(); 21140 } 21141 }); 21142}; 21143 21144})(jQuery); 21145 21146 21147/*! 21148 * jQuery UI Effects Transfer 1.10.3 21149 * http://jqueryui.com 21150 * 21151 * Copyright 2013 jQuery Foundation and other contributors 21152 * Released under the MIT license. 21153 * http://jquery.org/license 21154 * 21155 * http://api.jqueryui.com/transfer-effect/ 21156 * 21157 * Depends: 21158 * jquery.ui.effect.js 21159 */ 21160 21161(function( $, undefined ) { 21162 21163$.effects.effect.transfer = function( o, done ) { 21164 var elem = $( this ), 21165 target = $( o.to ), 21166 targetFixed = target.css( "position" ) === "fixed", 21167 body = $("body"), 21168 fixTop = targetFixed ? body.scrollTop() : 0, 21169 fixLeft = targetFixed ? body.scrollLeft() : 0, 21170 endPosition = target.offset(), 21171 animation = { 21172 top: endPosition.top - fixTop , 21173 left: endPosition.left - fixLeft , 21174 height: target.innerHeight(), 21175 width: target.innerWidth() 21176 }, 21177 startPosition = elem.offset(), 21178 transfer = $( "<div class='ui-effects-transfer'></div>" ) 21179 .appendTo( document.body ) 21180 .addClass( o.className ) 21181 .css({ 21182 top: startPosition.top - fixTop , 21183 left: startPosition.left - fixLeft ,
vendor: 52,232 bytes, lines 21184-23101
21184 height: elem.innerHeight(), 21185 width: elem.innerWidth(), 21186 position: targetFixed ? "fixed" : "absolute" 21187 }) 21188 .animate( animation, o.duration, o.easing, function() { 21189 transfer.remove(); 21190 done(); 21191 }); 21192}; 21193 21194})(jQuery); 21195 21196 21197 21198/*! 21199 * jQuery UI Progressbar 1.10.3 21200 * http://jqueryui.com 21201 * 21202 * Copyright 2013 jQuery Foundation and other contributors 21203 * Released under the MIT license. 21204 * http://jquery.org/license 21205 * 21206 * http://api.jqueryui.com/progressbar/ 21207 * 21208 * Depends: 21209 * jquery.ui.core.js 21210 * jquery.ui.widget.js 21211 */ 21212 21213(function( $, undefined ) { 21214 21215$.widget( "ui.progressbar", { 21216 version: "1.10.3", 21217 options: { 21218 max: 100, 21219 value: 0, 21220 21221 change: null, 21222 complete: null 21223 }, 21224 21225 min: 0, 21226 21227 _create: function() { 21228 // Constrain initial value 21229 this.oldValue = this.options.value = this._constrainedValue(); 21230 21231 this.element 21232 .addClass( "ui-progressbar ui-widget ui-widget-content ui-corner-all" ) 21233 .attr({ 21234 // Only set static values, aria-valuenow and aria-valuemax are 21235 // set inside _refreshValue() 21236 role: "progressbar", 21237 "aria-valuemin": this.min 21238 }); 21239 21240 this.valueDiv = $( "<div class='ui-progressbar-value ui-widget-header ui-corner-left'></div>" ) 21241 .appendTo( this.element ); 21242 21243 this._refreshValue(); 21244 }, 21245 21246 _destroy: function() { 21247 this.element 21248 .removeClass( "ui-progressbar ui-widget ui-widget-content ui-corner-all" ) 21249 .removeAttr( "role" ) 21250 .removeAttr( "aria-valuemin" ) 21251 .removeAttr( "aria-valuemax" ) 21252 .removeAttr( "aria-valuenow" ); 21253 21254 this.valueDiv.remove(); 21255 }, 21256 21257 value: function( newValue ) { 21258 if ( newValue === undefined ) { 21259 return this.options.value; 21260 } 21261 21262 this.options.value = this._constrainedValue( newValue ); 21263 this._refreshValue(); 21264 }, 21265 21266 _constrainedValue: function( newValue ) { 21267 if ( newValue === undefined ) { 21268 newValue = this.options.value; 21269 } 21270 21271 this.indeterminate = newValue === false; 21272 21273 // sanitize value 21274 if ( typeof newValue !== "number" ) { 21275 newValue = 0; 21276 } 21277 21278 return this.indeterminate ? false : 21279 Math.min( this.options.max, Math.max( this.min, newValue ) ); 21280 }, 21281 21282 _setOptions: function( options ) { 21283 // Ensure "value" option is set after other values (like max) 21284 var value = options.value; 21285 delete options.value; 21286 21287 this._super( options ); 21288 21289 this.options.value = this._constrainedValue( value ); 21290 this._refreshValue(); 21291 }, 21292 21293 _setOption: function( key, value ) { 21294 if ( key === "max" ) { 21295 // Don't allow a max less than min 21296 value = Math.max( this.min, value ); 21297 } 21298 21299 this._super( key, value ); 21300 }, 21301 21302 _percentage: function() { 21303 return this.indeterminate ? 100 : 100 * ( this.options.value - this.min ) / ( this.options.max - this.min ); 21304 }, 21305 21306 _refreshValue: function() { 21307 var value = this.options.value, 21308 percentage = this._percentage(); 21309 21310 this.valueDiv 21311 .toggle( this.indeterminate || value > this.min ) 21312 .toggleClass( "ui-corner-right", value === this.options.max ) 21313 .width( percentage.toFixed(0) + "%" ); 21314 21315 this.element.toggleClass( "ui-progressbar-indeterminate", this.indeterminate ); 21316 21317 if ( this.indeterminate ) { 21318 this.element.removeAttr( "aria-valuenow" ); 21319 if ( !this.overlayDiv ) { 21320 this.overlayDiv = $( "<div class='ui-progressbar-overlay'></div>" ).appendTo( this.valueDiv ); 21321 } 21322 } else { 21323 this.element.attr({ 21324 "aria-valuemax": this.options.max, 21325 "aria-valuenow": value 21326 }); 21327 if ( this.overlayDiv ) { 21328 this.overlayDiv.remove(); 21329 this.overlayDiv = null; 21330 } 21331 } 21332 21333 if ( this.oldValue !== value ) { 21334 this.oldValue = value; 21335 this._trigger( "change" ); 21336 } 21337 if ( value === this.options.max ) { 21338 this._trigger( "complete" ); 21339 } 21340 } 21341}); 21342 21343})( jQuery ); 21344 21345 21346 21347 21348/*! 21349 * jQuery UI Selectable 1.10.3 21350 * http://jqueryui.com 21351 * 21352 * Copyright 2013 jQuery Foundation and other contributors 21353 * Released under the MIT license. 21354 * http://jquery.org/license 21355 * 21356 * http://api.jqueryui.com/selectable/ 21357 * 21358 * Depends: 21359 * jquery.ui.core.js 21360 * jquery.ui.mouse.js 21361 * jquery.ui.widget.js 21362 */ 21363 21364(function( $, undefined ) { 21365 21366$.widget("ui.selectable", $.ui.mouse, { 21367 version: "1.10.3", 21368 options: { 21369 appendTo: "body", 21370 autoRefresh: true, 21371 distance: 0, 21372 filter: "*", 21373 tolerance: "touch", 21374 21375 // callbacks 21376 selected: null, 21377 selecting: null, 21378 start: null, 21379 stop: null, 21380 unselected: null, 21381 unselecting: null 21382 }, 21383 _create: function() { 21384 var selectees, 21385 that = this; 21386 21387 this.element.addClass("ui-selectable"); 21388 21389 this.dragged = false; 21390 21391 // cache selectee children based on filter 21392 this.refresh = function() { 21393 selectees = $(that.options.filter, that.element[0]); 21394 selectees.addClass("ui-selectee"); 21395 selectees.each(function() { 21396 var $this = $(this), 21397 pos = $this.offset(); 21398 $.data(this, "selectable-item", { 21399 element: this, 21400 $element: $this, 21401 left: pos.left, 21402 top: pos.top, 21403 right: pos.left + $this.outerWidth(), 21404 bottom: pos.top + $this.outerHeight(), 21405 startselected: false, 21406 selected: $this.hasClass("ui-selected"), 21407 selecting: $this.hasClass("ui-selecting"), 21408 unselecting: $this.hasClass("ui-unselecting") 21409 }); 21410 }); 21411 }; 21412 this.refresh(); 21413 21414 this.selectees = selectees.addClass("ui-selectee"); 21415 21416 this._mouseInit(); 21417 21418 this.helper = $("<div class='ui-selectable-helper'></div>"); 21419 }, 21420 21421 _destroy: function() { 21422 this.selectees 21423 .removeClass("ui-selectee") 21424 .removeData("selectable-item"); 21425 this.element 21426 .removeClass("ui-selectable ui-selectable-disabled"); 21427 this._mouseDestroy(); 21428 }, 21429 21430 _mouseStart: function(event) { 21431 var that = this, 21432 options = this.options; 21433 21434 this.opos = [event.pageX, event.pageY]; 21435 21436 if (this.options.disabled) { 21437 return; 21438 } 21439 21440 this.selectees = $(options.filter, this.element[0]); 21441 21442 this._trigger("start", event); 21443 21444 $(options.appendTo).append(this.helper); 21445 // position helper (lasso) 21446 this.helper.css({ 21447 "left": event.pageX, 21448 "top": event.pageY, 21449 "width": 0, 21450 "height": 0 21451 }); 21452 21453 if (options.autoRefresh) { 21454 this.refresh(); 21455 } 21456 21457 this.selectees.filter(".ui-selected").each(function() { 21458 var selectee = $.data(this, "selectable-item"); 21459 selectee.startselected = true; 21460 if (!event.metaKey && !event.ctrlKey) { 21461 selectee.$element.removeClass("ui-selected"); 21462 selectee.selected = false; 21463 selectee.$element.addClass("ui-unselecting"); 21464 selectee.unselecting = true; 21465 // selectable UNSELECTING callback 21466 that._trigger("unselecting", event, { 21467 unselecting: selectee.element 21468 }); 21469 } 21470 }); 21471 21472 $(event.target).parents().addBack().each(function() { 21473 var doSelect, 21474 selectee = $.data(this, "selectable-item"); 21475 if (selectee) { 21476 doSelect = (!event.metaKey && !event.ctrlKey) || !selectee.$element.hasClass("ui-selected"); 21477 selectee.$element 21478 .removeClass(doSelect ? "ui-unselecting" : "ui-selected") 21479 .addClass(doSelect ? "ui-selecting" : "ui-unselecting"); 21480 selectee.unselecting = !doSelect; 21481 selectee.selecting = doSelect; 21482 selectee.selected = doSelect; 21483 // selectable (UN)SELECTING callback 21484 if (doSelect) { 21485 that._trigger("selecting", event, { 21486 selecting: selectee.element 21487 }); 21488 } else { 21489 that._trigger("unselecting", event, { 21490 unselecting: selectee.element 21491 }); 21492 } 21493 return false; 21494 } 21495 }); 21496 21497 }, 21498 21499 _mouseDrag: function(event) { 21500 21501 this.dragged = true; 21502 21503 if (this.options.disabled) { 21504 return; 21505 } 21506 21507 var tmp, 21508 that = this, 21509 options = this.options, 21510 x1 = this.opos[0], 21511 y1 = this.opos[1], 21512 x2 = event.pageX, 21513 y2 = event.pageY; 21514 21515 if (x1 > x2) { tmp = x2; x2 = x1; x1 = tmp; } 21516 if (y1 > y2) { tmp = y2; y2 = y1; y1 = tmp; } 21517 this.helper.css({left: x1, top: y1, width: x2-x1, height: y2-y1}); 21518 21519 this.selectees.each(function() { 21520 var selectee = $.data(this, "selectable-item"), 21521 hit = false; 21522 21523 //prevent helper from being selected if appendTo: selectable 21524 if (!selectee || selectee.element === that.element[0]) { 21525 return; 21526 } 21527 21528 if (options.tolerance === "touch") { 21529 hit = ( !(selectee.left > x2 || selectee.right < x1 || selectee.top > y2 || selectee.bottom < y1) ); 21530 } else if (options.tolerance === "fit") { 21531 hit = (selectee.left > x1 && selectee.right < x2 && selectee.top > y1 && selectee.bottom < y2); 21532 } 21533 21534 if (hit) { 21535 // SELECT 21536 if (selectee.selected) { 21537 selectee.$element.removeClass("ui-selected"); 21538 selectee.selected = false; 21539 } 21540 if (selectee.unselecting) { 21541 selectee.$element.removeClass("ui-unselecting"); 21542 selectee.unselecting = false; 21543 } 21544 if (!selectee.selecting) { 21545 selectee.$element.addClass("ui-selecting"); 21546 selectee.selecting = true; 21547 // selectable SELECTING callback 21548 that._trigger("selecting", event, { 21549 selecting: selectee.element 21550 }); 21551 } 21552 } else { 21553 // UNSELECT 21554 if (selectee.selecting) { 21555 if ((event.metaKey || event.ctrlKey) && selectee.startselected) { 21556 selectee.$element.removeClass("ui-selecting"); 21557 selectee.selecting = false; 21558 selectee.$element.addClass("ui-selected"); 21559 selectee.selected = true; 21560 } else { 21561 selectee.$element.removeClass("ui-selecting"); 21562 selectee.selecting = false; 21563 if (selectee.startselected) { 21564 selectee.$element.addClass("ui-unselecting"); 21565 selectee.unselecting = true; 21566 } 21567 // selectable UNSELECTING callback 21568 that._trigger("unselecting", event, { 21569 unselecting: selectee.element 21570 }); 21571 } 21572 } 21573 if (selectee.selected) { 21574 if (!event.metaKey && !event.ctrlKey && !selectee.startselected) { 21575 selectee.$element.removeClass("ui-selected"); 21576 selectee.selected = false; 21577 21578 selectee.$element.addClass("ui-unselecting"); 21579 selectee.unselecting = true; 21580 // selectable UNSELECTING callback 21581 that._trigger("unselecting", event, { 21582 unselecting: selectee.element 21583 }); 21584 } 21585 } 21586 } 21587 }); 21588 21589 return false; 21590 }, 21591 21592 _mouseStop: function(event) { 21593 var that = this; 21594 21595 this.dragged = false; 21596 21597 $(".ui-unselecting", this.element[0]).each(function() { 21598 var selectee = $.data(this, "selectable-item"); 21599 selectee.$element.removeClass("ui-unselecting"); 21600 selectee.unselecting = false; 21601 selectee.startselected = false; 21602 that._trigger("unselected", event, { 21603 unselected: selectee.element 21604 }); 21605 }); 21606 $(".ui-selecting", this.element[0]).each(function() { 21607 var selectee = $.data(this, "selectable-item"); 21608 selectee.$element.removeClass("ui-selecting").addClass("ui-selected"); 21609 selectee.selecting = false; 21610 selectee.selected = true; 21611 selectee.startselected = true; 21612 that._trigger("selected", event, { 21613 selected: selectee.element 21614 }); 21615 }); 21616 this._trigger("stop", event); 21617 21618 this.helper.remove(); 21619 21620 return false; 21621 } 21622 21623}); 21624 21625})(jQuery); 21626 21627 21628 21629 21630/*! 21631 * jQuery UI Slider 1.10.3 21632 * http://jqueryui.com 21633 * 21634 * Copyright 2013 jQuery Foundation and other contributors 21635 * Released under the MIT license. 21636 * http://jquery.org/license 21637 * 21638 * http://api.jqueryui.com/slider/ 21639 * 21640 * Depends: 21641 * jquery.ui.core.js 21642 * jquery.ui.mouse.js 21643 * jquery.ui.widget.js 21644 */ 21645 21646(function( $, undefined ) { 21647 21648// number of pages in a slider 21649// (how many times can you page up/down to go through the whole range) 21650var numPages = 5; 21651 21652$.widget( "ui.slider", $.ui.mouse, { 21653 version: "1.10.3", 21654 widgetEventPrefix: "slide", 21655 21656 options: { 21657 animate: false, 21658 distance: 0, 21659 max: 100, 21660 min: 0, 21661 orientation: "horizontal", 21662 range: false, 21663 step: 1, 21664 value: 0, 21665 values: null, 21666 21667 // callbacks 21668 change: null, 21669 slide: null, 21670 start: null, 21671 stop: null 21672 }, 21673 21674 _create: function() { 21675 this._keySliding = false; 21676 this._mouseSliding = false; 21677 this._animateOff = true; 21678 this._handleIndex = null; 21679 this._detectOrientation(); 21680 this._mouseInit(); 21681 21682 this.element 21683 .addClass( "ui-slider" + 21684 " ui-slider-" + this.orientation + 21685 " ui-widget" + 21686 " ui-widget-content" + 21687 " ui-corner-all"); 21688 21689 this._refresh(); 21690 this._setOption( "disabled", this.options.disabled ); 21691 21692 this._animateOff = false; 21693 }, 21694 21695 _refresh: function() { 21696 this._createRange(); 21697 this._createHandles(); 21698 this._setupEvents(); 21699 this._refreshValue(); 21700 }, 21701 21702 _createHandles: function() { 21703 var i, handleCount, 21704 options = this.options, 21705 existingHandles = this.element.find( ".ui-slider-handle" ).addClass( "ui-state-default ui-corner-all" ), 21706 handle = "<a class='ui-slider-handle ui-state-default ui-corner-all' href='#'></a>", 21707 handles = []; 21708 21709 handleCount = ( options.values && options.values.length ) || 1; 21710 21711 if ( existingHandles.length > handleCount ) { 21712 existingHandles.slice( handleCount ).remove(); 21713 existingHandles = existingHandles.slice( 0, handleCount ); 21714 } 21715 21716 for ( i = existingHandles.length; i < handleCount; i++ ) { 21717 handles.push( handle ); 21718 } 21719 21720 this.handles = existingHandles.add( $( handles.join( "" ) ).appendTo( this.element ) ); 21721 21722 this.handle = this.handles.eq( 0 ); 21723 21724 this.handles.each(function( i ) { 21725 $( this ).data( "ui-slider-handle-index", i ); 21726 }); 21727 }, 21728 21729 _createRange: function() { 21730 var options = this.options, 21731 classes = ""; 21732 21733 if ( options.range ) { 21734 if ( options.range === true ) { 21735 if ( !options.values ) { 21736 options.values = [ this._valueMin(), this._valueMin() ]; 21737 } else if ( options.values.length && options.values.length !== 2 ) { 21738 options.values = [ options.values[0], options.values[0] ]; 21739 } else if ( $.isArray( options.values ) ) { 21740 options.values = options.values.slice(0); 21741 } 21742 } 21743 21744 if ( !this.range || !this.range.length ) { 21745 this.range = $( "<div></div>" ) 21746 .appendTo( this.element ); 21747 21748 classes = "ui-slider-range" + 21749 // note: this isn't the most fittingly semantic framework class for this element, 21750 // but worked best visually with a variety of themes 21751 " ui-widget-header ui-corner-all"; 21752 } else { 21753 this.range.removeClass( "ui-slider-range-min ui-slider-range-max" ) 21754 // Handle range switching from true to min/max 21755 .css({ 21756 "left": "", 21757 "bottom": "" 21758 }); 21759 } 21760 21761 this.range.addClass( classes + 21762 ( ( options.range === "min" || options.range === "max" ) ? " ui-slider-range-" + options.range : "" ) ); 21763 } else { 21764 this.range = $([]); 21765 } 21766 }, 21767 21768 _setupEvents: function() { 21769 var elements = this.handles.add( this.range ).filter( "a" ); 21770 this._off( elements ); 21771 this._on( elements, this._handleEvents ); 21772 this._hoverable( elements ); 21773 this._focusable( elements ); 21774 }, 21775 21776 _destroy: function() { 21777 this.handles.remove(); 21778 this.range.remove(); 21779 21780 this.element 21781 .removeClass( "ui-slider" + 21782 " ui-slider-horizontal" + 21783 " ui-slider-vertical" + 21784 " ui-widget" + 21785 " ui-widget-content" + 21786 " ui-corner-all" ); 21787 21788 this._mouseDestroy(); 21789 }, 21790 21791 _mouseCapture: function( event ) { 21792 var position, normValue, distance, closestHandle, index, allowed, offset, mouseOverHandle, 21793 that = this, 21794 o = this.options; 21795 21796 if ( o.disabled ) { 21797 return false; 21798 } 21799 21800 this.elementSize = { 21801 width: this.element.outerWidth(), 21802 height: this.element.outerHeight() 21803 }; 21804 this.elementOffset = this.element.offset(); 21805 21806 position = { x: event.pageX, y: event.pageY }; 21807 normValue = this._normValueFromMouse( position ); 21808 distance = this._valueMax() - this._valueMin() + 1; 21809 this.handles.each(function( i ) { 21810 var thisDistance = Math.abs( normValue - that.values(i) ); 21811 if (( distance > thisDistance ) || 21812 ( distance === thisDistance && 21813 (i === that._lastChangedValue || that.values(i) === o.min ))) { 21814 distance = thisDistance; 21815 closestHandle = $( this ); 21816 index = i; 21817 } 21818 }); 21819 21820 allowed = this._start( event, index ); 21821 if ( allowed === false ) { 21822 return false; 21823 } 21824 this._mouseSliding = true; 21825 21826 this._handleIndex = index; 21827 21828 closestHandle 21829 .addClass( "ui-state-active" ) 21830 .focus(); 21831 21832 offset = closestHandle.offset(); 21833 mouseOverHandle = !$( event.target ).parents().addBack().is( ".ui-slider-handle" ); 21834 this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : { 21835 left: event.pageX - offset.left - ( closestHandle.width() / 2 ), 21836 top: event.pageY - offset.top - 21837 ( closestHandle.height() / 2 ) - 21838 ( parseInt( closestHandle.css("borderTopWidth"), 10 ) || 0 ) - 21839 ( parseInt( closestHandle.css("borderBottomWidth"), 10 ) || 0) + 21840 ( parseInt( closestHandle.css("marginTop"), 10 ) || 0) 21841 }; 21842 21843 if ( !this.handles.hasClass( "ui-state-hover" ) ) { 21844 this._slide( event, index, normValue ); 21845 } 21846 this._animateOff = true; 21847 return true; 21848 }, 21849 21850 _mouseStart: function() { 21851 return true; 21852 }, 21853 21854 _mouseDrag: function( event ) { 21855 var position = { x: event.pageX, y: event.pageY }, 21856 normValue = this._normValueFromMouse( position ); 21857 21858 this._slide( event, this._handleIndex, normValue ); 21859 21860 return false; 21861 }, 21862 21863 _mouseStop: function( event ) { 21864 this.handles.removeClass( "ui-state-active" ); 21865 this._mouseSliding = false; 21866 21867 this._stop( event, this._handleIndex ); 21868 this._change( event, this._handleIndex ); 21869 21870 this._handleIndex = null; 21871 this._clickOffset = null; 21872 this._animateOff = false; 21873 21874 return false; 21875 }, 21876 21877 _detectOrientation: function() { 21878 this.orientation = ( this.options.orientation === "vertical" ) ? "vertical" : "horizontal"; 21879 }, 21880 21881 _normValueFromMouse: function( position ) { 21882 var pixelTotal, 21883 pixelMouse, 21884 percentMouse, 21885 valueTotal, 21886 valueMouse; 21887 21888 if ( this.orientation === "horizontal" ) { 21889 pixelTotal = this.elementSize.width; 21890 pixelMouse = position.x - this.elementOffset.left - ( this._clickOffset ? this._clickOffset.left : 0 ); 21891 } else { 21892 pixelTotal = this.elementSize.height; 21893 pixelMouse = position.y - this.elementOffset.top - ( this._clickOffset ? this._clickOffset.top : 0 ); 21894 } 21895 21896 percentMouse = ( pixelMouse / pixelTotal ); 21897 if ( percentMouse > 1 ) { 21898 percentMouse = 1; 21899 } 21900 if ( percentMouse < 0 ) { 21901 percentMouse = 0; 21902 } 21903 if ( this.orientation === "vertical" ) { 21904 percentMouse = 1 - percentMouse; 21905 } 21906 21907 valueTotal = this._valueMax() - this._valueMin(); 21908 valueMouse = this._valueMin() + percentMouse * valueTotal; 21909 21910 return this._trimAlignValue( valueMouse ); 21911 }, 21912 21913 _start: function( event, index ) { 21914 var uiHash = { 21915 handle: this.handles[ index ], 21916 value: this.value() 21917 }; 21918 if ( this.options.values && this.options.values.length ) { 21919 uiHash.value = this.values( index ); 21920 uiHash.values = this.values(); 21921 } 21922 return this._trigger( "start", event, uiHash ); 21923 }, 21924 21925 _slide: function( event, index, newVal ) { 21926 var otherVal, 21927 newValues, 21928 allowed; 21929 21930 if ( this.options.values && this.options.values.length ) { 21931 otherVal = this.values( index ? 0 : 1 ); 21932 21933 if ( ( this.options.values.length === 2 && this.options.range === true ) && 21934 ( ( index === 0 && newVal > otherVal) || ( index === 1 && newVal < otherVal ) ) 21935 ) { 21936 newVal = otherVal; 21937 } 21938 21939 if ( newVal !== this.values( index ) ) { 21940 newValues = this.values(); 21941 newValues[ index ] = newVal; 21942 // A slide can be canceled by returning false from the slide callback 21943 allowed = this._trigger( "slide", event, { 21944 handle: this.handles[ index ], 21945 value: newVal, 21946 values: newValues 21947 } ); 21948 otherVal = this.values( index ? 0 : 1 ); 21949 if ( allowed !== false ) { 21950 this.values( index, newVal, true ); 21951 } 21952 } 21953 } else { 21954 if ( newVal !== this.value() ) { 21955 // A slide can be canceled by returning false from the slide callback 21956 allowed = this._trigger( "slide", event, { 21957 handle: this.handles[ index ], 21958 value: newVal 21959 } ); 21960 if ( allowed !== false ) { 21961 this.value( newVal ); 21962 } 21963 } 21964 } 21965 }, 21966 21967 _stop: function( event, index ) { 21968 var uiHash = { 21969 handle: this.handles[ index ], 21970 value: this.value() 21971 }; 21972 if ( this.options.values && this.options.values.length ) { 21973 uiHash.value = this.values( index ); 21974 uiHash.values = this.values(); 21975 } 21976 21977 this._trigger( "stop", event, uiHash ); 21978 }, 21979 21980 _change: function( event, index ) { 21981 if ( !this._keySliding && !this._mouseSliding ) { 21982 var uiHash = { 21983 handle: this.handles[ index ], 21984 value: this.value() 21985 }; 21986 if ( this.options.values && this.options.values.length ) { 21987 uiHash.value = this.values( index ); 21988 uiHash.values = this.values(); 21989 } 21990 21991 //store the last changed value index for reference when handles overlap 21992 this._lastChangedValue = index; 21993 21994 this._trigger( "change", event, uiHash ); 21995 } 21996 }, 21997 21998 value: function( newValue ) { 21999 if ( arguments.length ) { 22000 this.options.value = this._trimAlignValue( newValue ); 22001 this._refreshValue(); 22002 this._change( null, 0 ); 22003 return; 22004 } 22005 22006 return this._value(); 22007 }, 22008 22009 values: function( index, newValue ) { 22010 var vals, 22011 newValues, 22012 i; 22013 22014 if ( arguments.length > 1 ) { 22015 this.options.values[ index ] = this._trimAlignValue( newValue ); 22016 this._refreshValue(); 22017 this._change( null, index ); 22018 return; 22019 } 22020 22021 if ( arguments.length ) { 22022 if ( $.isArray( arguments[ 0 ] ) ) { 22023 vals = this.options.values; 22024 newValues = arguments[ 0 ]; 22025 for ( i = 0; i < vals.length; i += 1 ) { 22026 vals[ i ] = this._trimAlignValue( newValues[ i ] ); 22027 this._change( null, i ); 22028 } 22029 this._refreshValue(); 22030 } else { 22031 if ( this.options.values && this.options.values.length ) { 22032 return this._values( index ); 22033 } else { 22034 return this.value(); 22035 } 22036 } 22037 } else { 22038 return this._values(); 22039 } 22040 }, 22041 22042 _setOption: function( key, value ) { 22043 var i, 22044 valsLength = 0; 22045 22046 if ( key === "range" && this.options.range === true ) { 22047 if ( value === "min" ) { 22048 this.options.value = this._values( 0 ); 22049 this.options.values = null; 22050 } else if ( value === "max" ) { 22051 this.options.value = this._values( this.options.values.length-1 ); 22052 this.options.values = null; 22053 } 22054 } 22055 22056 if ( $.isArray( this.options.values ) ) { 22057 valsLength = this.options.values.length; 22058 } 22059 22060 $.Widget.prototype._setOption.apply( this, arguments ); 22061 22062 switch ( key ) { 22063 case "orientation": 22064 this._detectOrientation(); 22065 this.element 22066 .removeClass( "ui-slider-horizontal ui-slider-vertical" ) 22067 .addClass( "ui-slider-" + this.orientation ); 22068 this._refreshValue(); 22069 break; 22070 case "value": 22071 this._animateOff = true; 22072 this._refreshValue(); 22073 this._change( null, 0 ); 22074 this._animateOff = false; 22075 break; 22076 case "values": 22077 this._animateOff = true; 22078 this._refreshValue(); 22079 for ( i = 0; i < valsLength; i += 1 ) { 22080 this._change( null, i ); 22081 } 22082 this._animateOff = false; 22083 break; 22084 case "min": 22085 case "max": 22086 this._animateOff = true; 22087 this._refreshValue(); 22088 this._animateOff = false; 22089 break; 22090 case "range": 22091 this._animateOff = true; 22092 this._refresh(); 22093 this._animateOff = false; 22094 break; 22095 } 22096 }, 22097 22098 //internal value getter 22099 // _value() returns value trimmed by min and max, aligned by step 22100 _value: function() { 22101 var val = this.options.value; 22102 val = this._trimAlignValue( val ); 22103 22104 return val; 22105 }, 22106 22107 //internal values getter 22108 // _values() returns array of values trimmed by min and max, aligned by step 22109 // _values( index ) returns single value trimmed by min and max, aligned by step 22110 _values: function( index ) { 22111 var val, 22112 vals, 22113 i; 22114 22115 if ( arguments.length ) { 22116 val = this.options.values[ index ]; 22117 val = this._trimAlignValue( val ); 22118 22119 return val; 22120 } else if ( this.options.values && this.options.values.length ) { 22121 // .slice() creates a copy of the array 22122 // this copy gets trimmed by min and max and then returned 22123 vals = this.options.values.slice(); 22124 for ( i = 0; i < vals.length; i+= 1) { 22125 vals[ i ] = this._trimAlignValue( vals[ i ] ); 22126 } 22127 22128 return vals; 22129 } else { 22130 return []; 22131 } 22132 }, 22133 22134 // returns the step-aligned value that val is closest to, between (inclusive) min and max 22135 _trimAlignValue: function( val ) { 22136 if ( val <= this._valueMin() ) { 22137 return this._valueMin(); 22138 } 22139 if ( val >= this._valueMax() ) { 22140 return this._valueMax(); 22141 } 22142 var step = ( this.options.step > 0 ) ? this.options.step : 1, 22143 valModStep = (val - this._valueMin()) % step, 22144 alignValue = val - valModStep; 22145 22146 if ( Math.abs(valModStep) * 2 >= step ) { 22147 alignValue += ( valModStep > 0 ) ? step : ( -step ); 22148 } 22149 22150 // Since JavaScript has problems with large floats, round 22151 // the final value to 5 digits after the decimal point (see #4124) 22152 return parseFloat( alignValue.toFixed(5) ); 22153 }, 22154 22155 _valueMin: function() { 22156 return this.options.min; 22157 }, 22158 22159 _valueMax: function() { 22160 return this.options.max; 22161 }, 22162 22163 _refreshValue: function() { 22164 var lastValPercent, valPercent, value, valueMin, valueMax, 22165 oRange = this.options.range, 22166 o = this.options, 22167 that = this, 22168 animate = ( !this._animateOff ) ? o.animate : false, 22169 _set = {}; 22170 22171 if ( this.options.values && this.options.values.length ) { 22172 this.handles.each(function( i ) { 22173 valPercent = ( that.values(i) - that._valueMin() ) / ( that._valueMax() - that._valueMin() ) * 100; 22174 _set[ that.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%"; 22175 $( this ).stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate ); 22176 if ( that.options.range === true ) { 22177 if ( that.orientation === "horizontal" ) { 22178 if ( i === 0 ) { 22179 that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { left: valPercent + "%" }, o.animate ); 22180 } 22181 if ( i === 1 ) { 22182 that.range[ animate ? "animate" : "css" ]( { width: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } ); 22183 } 22184 } else { 22185 if ( i === 0 ) { 22186 that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { bottom: ( valPercent ) + "%" }, o.animate ); 22187 } 22188 if ( i === 1 ) { 22189 that.range[ animate ? "animate" : "css" ]( { height: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } ); 22190 } 22191 } 22192 } 22193 lastValPercent = valPercent; 22194 }); 22195 } else { 22196 value = this.value(); 22197 valueMin = this._valueMin(); 22198 valueMax = this._valueMax(); 22199 valPercent = ( valueMax !== valueMin ) ? 22200 ( value - valueMin ) / ( valueMax - valueMin ) * 100 : 22201 0; 22202 _set[ this.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%"; 22203 this.handle.stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate ); 22204 22205 if ( oRange === "min" && this.orientation === "horizontal" ) { 22206 this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { width: valPercent + "%" }, o.animate ); 22207 } 22208 if ( oRange === "max" && this.orientation === "horizontal" ) { 22209 this.range[ animate ? "animate" : "css" ]( { width: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } ); 22210 } 22211 if ( oRange === "min" && this.orientation === "vertical" ) { 22212 this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { height: valPercent + "%" }, o.animate ); 22213 } 22214 if ( oRange === "max" && this.orientation === "vertical" ) { 22215 this.range[ animate ? "animate" : "css" ]( { height: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } ); 22216 } 22217 } 22218 }, 22219 22220 _handleEvents: { 22221 keydown: function( event ) { 22222 /*jshint maxcomplexity:25*/ 22223 var allowed, curVal, newVal, step, 22224 index = $( event.target ).data( "ui-slider-handle-index" ); 22225 22226 switch ( event.keyCode ) { 22227 case $.ui.keyCode.HOME: 22228 case $.ui.keyCode.END: 22229 case $.ui.keyCode.PAGE_UP: 22230 case $.ui.keyCode.PAGE_DOWN: 22231 case $.ui.keyCode.UP: 22232 case $.ui.keyCode.RIGHT: 22233 case $.ui.keyCode.DOWN: 22234 case $.ui.keyCode.LEFT: 22235 event.preventDefault(); 22236 if ( !this._keySliding ) { 22237 this._keySliding = true; 22238 $( event.target ).addClass( "ui-state-active" ); 22239 allowed = this._start( event, index ); 22240 if ( allowed === false ) { 22241 return; 22242 } 22243 } 22244 break; 22245 } 22246 22247 step = this.options.step; 22248 if ( this.options.values && this.options.values.length ) { 22249 curVal = newVal = this.values( index ); 22250 } else { 22251 curVal = newVal = this.value(); 22252 } 22253 22254 switch ( event.keyCode ) { 22255 case $.ui.keyCode.HOME: 22256 newVal = this._valueMin(); 22257 break; 22258 case $.ui.keyCode.END: 22259 newVal = this._valueMax(); 22260 break; 22261 case $.ui.keyCode.PAGE_UP: 22262 newVal = this._trimAlignValue( curVal + ( (this._valueMax() - this._valueMin()) / numPages ) ); 22263 break; 22264 case $.ui.keyCode.PAGE_DOWN: 22265 newVal = this._trimAlignValue( curVal - ( (this._valueMax() - this._valueMin()) / numPages ) ); 22266 break; 22267 case $.ui.keyCode.UP: 22268 case $.ui.keyCode.RIGHT: 22269 if ( curVal === this._valueMax() ) { 22270 return; 22271 } 22272 newVal = this._trimAlignValue( curVal + step ); 22273 break; 22274 case $.ui.keyCode.DOWN: 22275 case $.ui.keyCode.LEFT: 22276 if ( curVal === this._valueMin() ) { 22277 return; 22278 } 22279 newVal = this._trimAlignValue( curVal - step ); 22280 break; 22281 } 22282 22283 this._slide( event, index, newVal ); 22284 }, 22285 click: function( event ) { 22286 event.preventDefault(); 22287 }, 22288 keyup: function( event ) { 22289 var index = $( event.target ).data( "ui-slider-handle-index" ); 22290 22291 if ( this._keySliding ) { 22292 this._keySliding = false; 22293 this._stop( event, index ); 22294 this._change( event, index ); 22295 $( event.target ).removeClass( "ui-state-active" ); 22296 } 22297 } 22298 } 22299 22300}); 22301 22302}(jQuery)); 22303 22304 22305 22306 22307/*! 22308 * jQuery UI Sortable 1.10.3 22309 * http://jqueryui.com 22310 * 22311 * Copyright 2013 jQuery Foundation and other contributors 22312 * Released under the MIT license. 22313 * http://jquery.org/license 22314 * 22315 * http://api.jqueryui.com/sortable/ 22316 * 22317 * Depends: 22318 * jquery.ui.core.js 22319 * jquery.ui.mouse.js 22320 * jquery.ui.widget.js 22321 */ 22322 22323(function( $, undefined ) { 22324 22325/*jshint loopfunc: true */ 22326 22327function isOverAxis( x, reference, size ) { 22328 return ( x > reference ) && ( x < ( reference + size ) ); 22329} 22330 22331function isFloating(item) { 22332 return (/left|right/).test(item.css("float")) || (/inline|table-cell/).test(item.css("display")); 22333} 22334 22335$.widget("ui.sortable", $.ui.mouse, { 22336 version: "1.10.3", 22337 widgetEventPrefix: "sort", 22338 ready: false, 22339 options: { 22340 appendTo: "parent", 22341 axis: false, 22342 connectWith: false, 22343 containment: false, 22344 cursor: "auto", 22345 cursorAt: false, 22346 dropOnEmpty: true, 22347 forcePlaceholderSize: false, 22348 forceHelperSize: false, 22349 grid: false, 22350 handle: false, 22351 helper: "original", 22352 items: "> *", 22353 opacity: false, 22354 placeholder: false, 22355 revert: false, 22356 scroll: true, 22357 scrollSensitivity: 20, 22358 scrollSpeed: 20, 22359 scope: "default", 22360 tolerance: "intersect", 22361 zIndex: 1000, 22362 22363 // callbacks 22364 activate: null, 22365 beforeStop: null, 22366 change: null, 22367 deactivate: null, 22368 out: null, 22369 over: null, 22370 receive: null, 22371 remove: null, 22372 sort: null, 22373 start: null, 22374 stop: null, 22375 update: null 22376 }, 22377 _create: function() { 22378 22379 var o = this.options; 22380 this.containerCache = {}; 22381 this.element.addClass("ui-sortable"); 22382 22383 //Get the items 22384 this.refresh(); 22385 22386 //Let's determine if the items are being displayed horizontally 22387 this.floating = this.items.length ? o.axis === "x" || isFloating(this.items[0].item) : false; 22388 22389 //Let's determine the parent's offset 22390 this.offset = this.element.offset(); 22391 22392 //Initialize mouse events for interaction 22393 this._mouseInit(); 22394 22395 //We're ready to go 22396 this.ready = true; 22397 22398 }, 22399 22400 _destroy: function() { 22401 this.element 22402 .removeClass("ui-sortable ui-sortable-disabled"); 22403 this._mouseDestroy(); 22404 22405 for ( var i = this.items.length - 1; i >= 0; i-- ) { 22406 this.items[i].item.removeData(this.widgetName + "-item"); 22407 } 22408 22409 return this; 22410 }, 22411 22412 _setOption: function(key, value){ 22413 if ( key === "disabled" ) { 22414 this.options[ key ] = value; 22415 22416 this.widget().toggleClass( "ui-sortable-disabled", !!value ); 22417 } else { 22418 // Don't call widget base _setOption for disable as it adds ui-state-disabled class 22419 $.Widget.prototype._setOption.apply(this, arguments); 22420 } 22421 }, 22422 22423 _mouseCapture: function(event, overrideHandle) { 22424 var currentItem = null, 22425 validHandle = false, 22426 that = this; 22427 22428 if (this.reverting) { 22429 return false; 22430 } 22431 22432 if(this.options.disabled || this.options.type === "static") { 22433 return false; 22434 } 22435 22436 //We have to refresh the items data once first 22437 this._refreshItems(event); 22438 22439 //Find out if the clicked node (or one of its parents) is a actual item in this.items 22440 $(event.target).parents().each(function() { 22441 if($.data(this, that.widgetName + "-item") === that) { 22442 currentItem = $(this); 22443 return false; 22444 } 22445 }); 22446 if($.data(event.target, that.widgetName + "-item") === that) { 22447 currentItem = $(event.target); 22448 } 22449 22450 if(!currentItem) { 22451 return false; 22452 } 22453 if(this.options.handle && !overrideHandle) { 22454 $(this.options.handle, currentItem).find("*").addBack().each(function() { 22455 if(this === event.target) { 22456 validHandle = true; 22457 } 22458 }); 22459 if(!validHandle) { 22460 return false; 22461 } 22462 } 22463 22464 this.currentItem = currentItem; 22465 this._removeCurrentsFromItems(); 22466 return true; 22467 22468 }, 22469 22470 _mouseStart: function(event, overrideHandle, noActivation) { 22471 22472 var i, body, 22473 o = this.options; 22474 22475 this.currentContainer = this; 22476 22477 //We only need to call refreshPositions, because the refreshItems call has been moved to mouseCapture 22478 this.refreshPositions(); 22479 22480 //Create and append the visible helper 22481 this.helper = this._createHelper(event); 22482 22483 //Cache the helper size 22484 this._cacheHelperProportions(); 22485 22486 /* 22487 * - Position generation - 22488 * This block generates everything position related - it's the core of draggables. 22489 */ 22490 22491 //Cache the margins of the original element 22492 this._cacheMargins(); 22493 22494 //Get the next scrolling parent 22495 this.scrollParent = this.helper.scrollParent(); 22496 22497 //The element's absolute position on the page minus margins 22498 this.offset = this.currentItem.offset(); 22499 this.offset = { 22500 top: this.offset.top - this.margins.top, 22501 left: this.offset.left - this.margins.left 22502 }; 22503 22504 $.extend(this.offset, { 22505 click: { //Where the click happened, relative to the element 22506 left: event.pageX - this.offset.left, 22507 top: event.pageY - this.offset.top 22508 }, 22509 parent: this._getParentOffset(), 22510 relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper 22511 }); 22512 22513 // Only after we got the offset, we can change the helper's position to absolute 22514 // TODO: Still need to figure out a way to make relative sorting possible 22515 this.helper.css("position", "absolute"); 22516 this.cssPosition = this.helper.css("position"); 22517 22518 //Generate the original position 22519 this.originalPosition = this._generatePosition(event); 22520 this.originalPageX = event.pageX; 22521 this.originalPageY = event.pageY; 22522 22523 //Adjust the mouse offset relative to the helper if "cursorAt" is supplied 22524 (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt)); 22525 22526 //Cache the former DOM position 22527 this.domPosition = { prev: this.currentItem.prev()[0], parent: this.currentItem.parent()[0] }; 22528 22529 //If the helper is not the original, hide the original so it's not playing any role during the drag, won't cause anything bad this way 22530 if(this.helper[0] !== this.currentItem[0]) { 22531 this.currentItem.hide(); 22532 } 22533 22534 //Create the placeholder 22535 this._createPlaceholder(); 22536 22537 //Set a containment if given in the options 22538 if(o.containment) { 22539 this._setContainment(); 22540 } 22541 22542 if( o.cursor && o.cursor !== "auto" ) { // cursor option 22543 body = this.document.find( "body" ); 22544 22545 // support: IE 22546 this.storedCursor = body.css( "cursor" ); 22547 body.css( "cursor", o.cursor ); 22548 22549 this.storedStylesheet = $( "<style>*{ cursor: "+o.cursor+" !important; }</style>" ).appendTo( body ); 22550 } 22551 22552 if(o.opacity) { // opacity option 22553 if (this.helper.css("opacity")) { 22554 this._storedOpacity = this.helper.css("opacity"); 22555 } 22556 this.helper.css("opacity", o.opacity); 22557 } 22558 22559 if(o.zIndex) { // zIndex option 22560 if (this.helper.css("zIndex")) { 22561 this._storedZIndex = this.helper.css("zIndex"); 22562 } 22563 this.helper.css("zIndex", o.zIndex); 22564 } 22565 22566 //Prepare scrolling 22567 if(this.scrollParent[0] !== document && this.scrollParent[0].tagName !== "HTML") { 22568 this.overflowOffset = this.scrollParent.offset(); 22569 } 22570 22571 //Call callbacks 22572 this._trigger("start", event, this._uiHash()); 22573 22574 //Recache the helper size 22575 if(!this._preserveHelperProportions) { 22576 this._cacheHelperProportions(); 22577 } 22578 22579 22580 //Post "activate" events to possible containers 22581 if( !noActivation ) { 22582 for ( i = this.containers.length - 1; i >= 0; i-- ) { 22583 this.containers[ i ]._trigger( "activate", event, this._uiHash( this ) ); 22584 } 22585 } 22586 22587 //Prepare possible droppables 22588 if($.ui.ddmanager) { 22589 $.ui.ddmanager.current = this; 22590 } 22591 22592 if ($.ui.ddmanager && !o.dropBehaviour) { 22593 $.ui.ddmanager.prepareOffsets(this, event); 22594 } 22595 22596 this.dragging = true; 22597 22598 this.helper.addClass("ui-sortable-helper"); 22599 this._mouseDrag(event); //Execute the drag once - this causes the helper not to be visible before getting its correct position 22600 return true; 22601 22602 }, 22603 22604 _mouseDrag: function(event) { 22605 var i, item, itemElement, intersection, 22606 o = this.options, 22607 scrolled = false; 22608 22609 //Compute the helpers position 22610 this.position = this._generatePosition(event); 22611 this.positionAbs = this._convertPositionTo("absolute"); 22612 22613 if (!this.lastPositionAbs) { 22614 this.lastPositionAbs = this.positionAbs; 22615 } 22616 22617 //Do scrolling 22618 if(this.options.scroll) { 22619 if(this.scrollParent[0] !== document && this.scrollParent[0].tagName !== "HTML") { 22620 22621 if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) { 22622 this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed; 22623 } else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity) { 22624 this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed; 22625 } 22626 22627 if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) { 22628 this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed; 22629 } else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity) { 22630 this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed; 22631 } 22632 22633 } else { 22634 22635 if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) { 22636 scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed); 22637 } else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) { 22638 scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed); 22639 } 22640 22641 if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) { 22642 scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed); 22643 } else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) { 22644 scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed); 22645 } 22646 22647 } 22648 22649 if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) { 22650 $.ui.ddmanager.prepareOffsets(this, event); 22651 } 22652 } 22653 22654 //Regenerate the absolute position used for position checks 22655 this.positionAbs = this._convertPositionTo("absolute"); 22656 22657 //Set the helper position 22658 if(!this.options.axis || this.options.axis !== "y") { 22659 this.helper[0].style.left = this.position.left+"px"; 22660 } 22661 if(!this.options.axis || this.options.axis !== "x") { 22662 this.helper[0].style.top = this.position.top+"px"; 22663 } 22664 22665 //Rearrange 22666 for (i = this.items.length - 1; i >= 0; i--) { 22667 22668 //Cache variables and intersection, continue if no intersection 22669 item = this.items[i]; 22670 itemElement = item.item[0]; 22671 intersection = this._intersectsWithPointer(item); 22672 if (!intersection) { 22673 continue; 22674 } 22675 22676 // Only put the placeholder inside the current Container, skip all 22677 // items form other containers. This works because when moving 22678 // an item from one container to another the 22679 // currentContainer is switched before the placeholder is moved. 22680 // 22681 // Without this moving items in "sub-sortables" can cause the placeholder to jitter 22682 // beetween the outer and inner container. 22683 if (item.instance !== this.currentContainer) { 22684 continue; 22685 } 22686 22687 // cannot intersect with itself 22688 // no useless actions that have been done before 22689 // no action if the item moved is the parent of the item checked 22690 if (itemElement !== this.currentItem[0] && 22691 this.placeholder[intersection === 1 ? "next" : "prev"]()[0] !== itemElement && 22692 !$.contains(this.placeholder[0], itemElement) && 22693 (this.options.type === "semi-dynamic" ? !$.contains(this.element[0], itemElement) : true) 22694 ) { 22695 22696 this.direction = intersection === 1 ? "down" : "up"; 22697 22698 if (this.options.tolerance === "pointer" || this._intersectsWithSides(item)) { 22699 this._rearrange(event, item); 22700 } else { 22701 break; 22702 } 22703 22704 this._trigger("change", event, this._uiHash()); 22705 break; 22706 } 22707 } 22708 22709 //Post events to containers 22710 this._contactContainers(event); 22711 22712 //Interconnect with droppables 22713 if($.ui.ddmanager) { 22714 $.ui.ddmanager.drag(this, event); 22715 } 22716 22717 //Call callbacks 22718 this._trigger("sort", event, this._uiHash()); 22719 22720 this.lastPositionAbs = this.positionAbs; 22721 return false; 22722 22723 }, 22724 22725 _mouseStop: function(event, noPropagation) { 22726 22727 if(!event) { 22728 return; 22729 } 22730 22731 //If we are using droppables, inform the manager about the drop 22732 if ($.ui.ddmanager && !this.options.dropBehaviour) { 22733 $.ui.ddmanager.drop(this, event); 22734 } 22735 22736 if(this.options.revert) { 22737 var that = this, 22738 cur = this.placeholder.offset(), 22739 axis = this.options.axis, 22740 animation = {}; 22741 22742 if ( !axis || axis === "x" ) { 22743 animation.left = cur.left - this.offset.parent.left - this.margins.left + (this.offsetParent[0] === document.body ? 0 : this.offsetParent[0].scrollLeft); 22744 } 22745 if ( !axis || axis === "y" ) { 22746 animation.top = cur.top - this.offset.parent.top - this.margins.top + (this.offsetParent[0] === document.body ? 0 : this.offsetParent[0].scrollTop); 22747 } 22748 this.reverting = true; 22749 $(this.helper).animate( animation, parseInt(this.options.revert, 10) || 500, function() { 22750 that._clear(event); 22751 }); 22752 } else { 22753 this._clear(event, noPropagation); 22754 } 22755 22756 return false; 22757 22758 }, 22759 22760 cancel: function() { 22761 22762 if(this.dragging) { 22763 22764 this._mouseUp({ target: null }); 22765 22766 if(this.options.helper === "original") { 22767 this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"); 22768 } else { 22769 this.currentItem.show(); 22770 } 22771 22772 //Post deactivating events to containers 22773 for (var i = this.containers.length - 1; i >= 0; i--){ 22774 this.containers[i]._trigger("deactivate", null, this._uiHash(this)); 22775 if(this.containers[i].containerCache.over) { 22776 this.containers[i]._trigger("out", null, this._uiHash(this)); 22777 this.containers[i].containerCache.over = 0; 22778 } 22779 } 22780 22781 } 22782 22783 if (this.placeholder) { 22784 //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node! 22785 if(this.placeholder[0].parentNode) { 22786 this.placeholder[0].parentNode.removeChild(this.placeholder[0]); 22787 } 22788 if(this.options.helper !== "original" && this.helper && this.helper[0].parentNode) { 22789 this.helper.remove(); 22790 } 22791 22792 $.extend(this, { 22793 helper: null, 22794 dragging: false, 22795 reverting: false, 22796 _noFinalSort: null 22797 }); 22798 22799 if(this.domPosition.prev) { 22800 $(this.domPosition.prev).after(this.currentItem); 22801 } else { 22802 $(this.domPosition.parent).prepend(this.currentItem); 22803 } 22804 } 22805 22806 return this; 22807 22808 }, 22809 22810 serialize: function(o) { 22811 22812 var items = this._getItemsAsjQuery(o && o.connected), 22813 str = []; 22814 o = o || {}; 22815 22816 $(items).each(function() { 22817 var res = ($(o.item || this).attr(o.attribute || "id") || "").match(o.expression || (/(.+)[\-=_](.+)/)); 22818 if (res) { 22819 str.push((o.key || res[1]+"[]")+"="+(o.key && o.expression ? res[1] : res[2])); 22820 } 22821 }); 22822 22823 if(!str.length && o.key) { 22824 str.push(o.key + "="); 22825 } 22826 22827 return str.join("&"); 22828 22829 }, 22830 22831 toArray: function(o) { 22832 22833 var items = this._getItemsAsjQuery(o && o.connected), 22834 ret = []; 22835 22836 o = o || {}; 22837 22838 items.each(function() { ret.push($(o.item || this).attr(o.attribute || "id") || ""); }); 22839 return ret; 22840 22841 }, 22842 22843 /* Be careful with the following core functions */ 22844 _intersectsWith: function(item) { 22845 22846 var x1 = this.positionAbs.left, 22847 x2 = x1 + this.helperProportions.width, 22848 y1 = this.positionAbs.top, 22849 y2 = y1 + this.helperProportions.height, 22850 l = item.left, 22851 r = l + item.width, 22852 t = item.top, 22853 b = t + item.height, 22854 dyClick = this.offset.click.top, 22855 dxClick = this.offset.click.left, 22856 isOverElementHeight = ( this.options.axis === "x" ) || ( ( y1 + dyClick ) > t && ( y1 + dyClick ) < b ), 22857 isOverElementWidth = ( this.options.axis === "y" ) || ( ( x1 + dxClick ) > l && ( x1 + dxClick ) < r ), 22858 isOverElement = isOverElementHeight && isOverElementWidth; 22859 22860 if ( this.options.tolerance === "pointer" || 22861 this.options.forcePointerForContainers || 22862 (this.options.tolerance !== "pointer" && this.helperProportions[this.floating ? "width" : "height"] > item[this.floating ? "width" : "height"]) 22863 ) { 22864 return isOverElement; 22865 } else { 22866 22867 return (l < x1 + (this.helperProportions.width / 2) && // Right Half 22868 x2 - (this.helperProportions.width / 2) < r && // Left Half 22869 t < y1 + (this.helperProportions.height / 2) && // Bottom Half 22870 y2 - (this.helperProportions.height / 2) < b ); // Top Half 22871 22872 } 22873 }, 22874 22875 _intersectsWithPointer: function(item) { 22876 22877 var isOverElementHeight = (this.options.axis === "x") || isOverAxis(this.positionAbs.top + this.offset.click.top, item.top, item.height), 22878 isOverElementWidth = (this.options.axis === "y") || isOverAxis(this.positionAbs.left + this.offset.click.left, item.left, item.width), 22879 isOverElement = isOverElementHeight && isOverElementWidth, 22880 verticalDirection = this._getDragVerticalDirection(), 22881 horizontalDirection = this._getDragHorizontalDirection(); 22882 22883 if (!isOverElement) { 22884 return false; 22885 } 22886 22887 return this.floating ? 22888 ( ((horizontalDirection && horizontalDirection === "right") || verticalDirection === "down") ? 2 : 1 ) 22889 : ( verticalDirection && (verticalDirection === "down" ? 2 : 1) ); 22890 22891 }, 22892 22893 _intersectsWithSides: function(item) { 22894 22895 var isOverBottomHalf = isOverAxis(this.positionAbs.top + this.offset.click.top, item.top + (item.height/2), item.height), 22896 isOverRightHalf = isOverAxis(this.positionAbs.left + this.offset.click.left, item.left + (item.width/2), item.width), 22897 verticalDirection = this._getDragVerticalDirection(), 22898 horizontalDirection = this._getDragHorizontalDirection(); 22899 22900 if (this.floating && horizontalDirection) { 22901 return ((horizontalDirection === "right" && isOverRightHalf) || (horizontalDirection === "left" && !isOverRightHalf)); 22902 } else { 22903 return verticalDirection && ((verticalDirection === "down" && isOverBottomHalf) || (verticalDirection === "up" && !isOverBottomHalf)); 22904 } 22905 22906 }, 22907 22908 _getDragVerticalDirection: function() { 22909 var delta = this.positionAbs.top - this.lastPositionAbs.top; 22910 return delta !== 0 && (delta > 0 ? "down" : "up"); 22911 }, 22912 22913 _getDragHorizontalDirection: function() { 22914 var delta = this.positionAbs.left - this.lastPositionAbs.left; 22915 return delta !== 0 && (delta > 0 ? "right" : "left"); 22916 }, 22917 22918 refresh: function(event) { 22919 this._refreshItems(event); 22920 this.refreshPositions(); 22921 return this; 22922 }, 22923 22924 _connectWith: function() { 22925 var options = this.options; 22926 return options.connectWith.constructor === String ? [options.connectWith] : options.connectWith; 22927 }, 22928 22929 _getItemsAsjQuery: function(connected) { 22930 22931 var i, j, cur, inst, 22932 items = [], 22933 queries = [], 22934 connectWith = this._connectWith(); 22935 22936 if(connectWith && connected) { 22937 for (i = connectWith.length - 1; i >= 0; i--){ 22938 cur = $(connectWith[i]); 22939 for ( j = cur.length - 1; j >= 0; j--){ 22940 inst = $.data(cur[j], this.widgetFullName); 22941 if(inst && inst !== this && !inst.options.disabled) { 22942 queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element) : $(inst.options.items, inst.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"), inst]); 22943 } 22944 } 22945 } 22946 } 22947 22948 queries.push([$.isFunction(this.options.items) ? this.options.items.call(this.element, null, { options: this.options, item: this.currentItem }) : $(this.options.items, this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"), this]); 22949 22950 for (i = queries.length - 1; i >= 0; i--){ 22951 queries[i][0].each(function() { 22952 items.push(this); 22953 }); 22954 } 22955 22956 return $(items); 22957 22958 }, 22959 22960 _removeCurrentsFromItems: function() { 22961 22962 var list = this.currentItem.find(":data(" + this.widgetName + "-item)"); 22963 22964 this.items = $.grep(this.items, function (item) { 22965 for (var j=0; j < list.length; j++) { 22966 if(list[j] === item.item[0]) { 22967 return false; 22968 } 22969 } 22970 return true; 22971 }); 22972 22973 }, 22974 22975 _refreshItems: function(event) { 22976 22977 this.items = []; 22978 this.containers = [this]; 22979 22980 var i, j, cur, inst, targetData, _queries, item, queriesLength, 22981 items = this.items, 22982 queries = [[$.isFunction(this.options.items) ? this.options.items.call(this.element[0], event, { item: this.currentItem }) : $(this.options.items, this.element), this]], 22983 connectWith = this._connectWith(); 22984 22985 if(connectWith && this.ready) { //Shouldn't be run the first time through due to massive slow-down 22986 for (i = connectWith.length - 1; i >= 0; i--){ 22987 cur = $(connectWith[i]); 22988 for (j = cur.length - 1; j >= 0; j--){ 22989 inst = $.data(cur[j], this.widgetFullName); 22990 if(inst && inst !== this && !inst.options.disabled) { 22991 queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element[0], event, { item: this.currentItem }) : $(inst.options.items, inst.element), inst]); 22992 this.containers.push(inst); 22993 } 22994 } 22995 } 22996 } 22997 22998 for (i = queries.length - 1; i >= 0; i--) { 22999 targetData = queries[i][1]; 23000 _queries = queries[i][0]; 23001 23002 for (j=0, queriesLength = _queries.length; j < queriesLength; j++) { 23003 item = $(_queries[j]); 23004 23005 item.data(this.widgetName + "-item", targetData); // Data for target checking (mouse manager) 23006 23007 items.push({ 23008 item: item, 23009 instance: targetData, 23010 width: 0, height: 0, 23011 left: 0, top: 0 23012 }); 23013 } 23014 } 23015 23016 }, 23017 23018 refreshPositions: function(fast) { 23019 23020 //This has to be redone because due to the item being moved out/into the offsetParent, the offsetParent's position will change 23021 if(this.offsetParent && this.helper) { 23022 this.offset.parent = this._getParentOffset(); 23023 } 23024 23025 var i, item, t, p; 23026 23027 for (i = this.items.length - 1; i >= 0; i--){ 23028 item = this.items[i]; 23029 23030 //We ignore calculating positions of all connected containers when we're not over them 23031 if(item.instance !== this.currentContainer && this.currentContainer && item.item[0] !== this.currentItem[0]) { 23032 continue; 23033 } 23034 23035 t = this.options.toleranceElement ? $(this.options.toleranceElement, item.item) : item.item; 23036 23037 if (!fast) { 23038 item.width = t.outerWidth(); 23039 item.height = t.outerHeight(); 23040 } 23041 23042 p = t.offset(); 23043 item.left = p.left; 23044 item.top = p.top; 23045 } 23046 23047 if(this.options.custom && this.options.custom.refreshContainers) { 23048 this.options.custom.refreshContainers.call(this); 23049 } else { 23050 for (i = this.containers.length - 1; i >= 0; i--){ 23051 p = this.containers[i].element.offset(); 23052 this.containers[i].containerCache.left = p.left; 23053 this.containers[i].containerCache.top = p.top; 23054 this.containers[i].containerCache.width = this.containers[i].element.outerWidth(); 23055 this.containers[i].containerCache.height = this.containers[i].element.outerHeight(); 23056 } 23057 } 23058 23059 return this; 23060 }, 23061 23062 _createPlaceholder: function(that) { 23063 that = that || this; 23064 var className, 23065 o = that.options; 23066 23067 if(!o.placeholder || o.placeholder.constructor === String) { 23068 className = o.placeholder; 23069 o.placeholder = { 23070 element: function() { 23071 23072 var nodeName = that.currentItem[0].nodeName.toLowerCase(), 23073 element = $( "<" + nodeName + ">", that.document[0] ) 23074 .addClass(className || that.currentItem[0].className+" ui-sortable-placeholder") 23075 .removeClass("ui-sortable-helper"); 23076 23077 if ( nodeName === "tr" ) { 23078 that.currentItem.children().each(function() { 23079 $( "<td> </td>", that.document[0] ) 23080 .attr( "colspan", $( this ).attr( "colspan" ) || 1 ) 23081 .appendTo( element ); 23082 }); 23083 } else if ( nodeName === "img" ) { 23084 element.attr( "src", that.currentItem.attr( "src" ) ); 23085 } 23086 23087 if ( !className ) { 23088 element.css( "visibility", "hidden" ); 23089 } 23090 23091 return element; 23092 }, 23093 update: function(container, p) { 23094 23095 // 1. If a className is set as 'placeholder option, we don't force sizes - the class is responsible for that 23096 // 2. The option 'forcePlaceholderSize can be enabled to force it even if a class name is specified 23097 if(className && !o.forcePlaceholderSize) { 23098 return; 23099 } 23100 23101 //If the element doesn't have a actual height by itself (without styles coming from a stylesheet), it receives the inline height from the dragged item
23102 if(!p.height()) { p.height(that.currentItem.innerHeight() - parseInt(that.currentItem.css("paddingTop")||0, 10) - parseInt(that.currentItem.css("paddingBottom")||0, 10)); } 23103 if(!p.width()) { p.width(that.currentItem.innerWidth() - parseInt(that.currentItem.css("paddingLeft")||0, 10) - parseInt(that.currentItem.css("paddingRight")||0, 10)); } 23104 } 23105 }; 23106 } 23107 23108 //Create the placeholder 23109 that.placeholder = $(o.placeholder.element.call(that.element, that.currentItem)); 23110 23111 //Append it after the actual current item 23112 that.currentItem.after(that.placeholder); 23113 23114 //Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317) 23115 o.placeholder.update(that, that.placeholder); 23116 23117 }, 23118 23119 _contactContainers: function(event) { 23120 var i, j, dist, itemWithLeastDistance, posProperty, sizeProperty, base, cur, nearBottom, floating, 23121 innermostContainer = null, 23122 innermostIndex = null; 23123 23124 // get innermost container that intersects with item 23125 for (i = this.containers.length - 1; i >= 0; i--) { 23126 23127 // never consider a container that's located within the item itself 23128 if($.contains(this.currentItem[0], this.containers[i].element[0])) { 23129 continue; 23130 } 23131 23132 if(this._intersectsWith(this.containers[i].containerCache)) { 23133 23134 // if we've already found a container and it's more "inner" than this, then continue 23135 if(innermostContainer && $.contains(this.containers[i].element[0], innermostContainer.element[0])) { 23136 continue; 23137 } 23138 23139 innermostContainer = this.containers[i]; 23140 innermostIndex = i; 23141 23142 } else { 23143 // container doesn't intersect. trigger "out" event if necessary 23144 if(this.containers[i].containerCache.over) { 23145 this.containers[i]._trigger("out", event, this._uiHash(this)); 23146 this.containers[i].containerCache.over = 0; 23147 } 23148 } 23149 23150 } 23151 23152 // if no intersecting containers found, return 23153 if(!innermostContainer) { 23154 return; 23155 } 23156 23157 // move the item into the container if it's not there already 23158 if(this.containers.length === 1) { 23159 if (!this.containers[innermostIndex].containerCache.over) { 23160 this.containers[innermostIndex]._trigger("over", event, this._uiHash(this)); 23161 this.containers[innermostIndex].containerCache.over = 1; 23162 } 23163 } else { 23164 23165 //When entering a new container, we will find the item with the least distance and append our item near it 23166 dist = 10000; 23167 itemWithLeastDistance = null; 23168 floating = innermostContainer.floating || isFloating(this.currentItem); 23169 posProperty = floating ? "left" : "top"; 23170 sizeProperty = floating ? "width" : "height"; 23171 base = this.positionAbs[posProperty] + this.offset.click[posProperty]; 23172 for (j = this.items.length - 1; j >= 0; j--) { 23173 if(!$.contains(this.containers[innermostIndex].element[0], this.items[j].item[0])) { 23174 continue; 23175 } 23176 if(this.items[j].item[0] === this.currentItem[0]) { 23177 continue; 23178 } 23179 if (floating && !isOverAxis(this.positionAbs.top + this.offset.click.top, this.items[j].top, this.items[j].height)) { 23180 continue; 23181 } 23182 cur = this.items[j].item.offset()[posProperty]; 23183 nearBottom = false; 23184 if(Math.abs(cur - base) > Math.abs(cur + this.items[j][sizeProperty] - base)){ 23185 nearBottom = true; 23186 cur += this.items[j][sizeProperty]; 23187 } 23188 23189 if(Math.abs(cur - base) < dist) { 23190 dist = Math.abs(cur - base); itemWithLeastDistance = this.items[j
vendor: 13,544 bytes, lines 23190-23511
23190]; 23191 this.direction = nearBottom ? "up": "down"; 23192 } 23193 } 23194 23195 //Check if dropOnEmpty is enabled 23196 if(!itemWithLeastDistance && !this.options.dropOnEmpty) { 23197 return; 23198 } 23199 23200 if(this.currentContainer === this.containers[innermostIndex]) { 23201 return; 23202 } 23203 23204 itemWithLeastDistance ? this._rearrange(event, itemWithLeastDistance, null, true) : this._rearrange(event, null, this.containers[innermostIndex].element, true); 23205 this._trigger("change", event, this._uiHash()); 23206 this.containers[innermostIndex]._trigger("change", event, this._uiHash(this)); 23207 this.currentContainer = this.containers[innermostIndex]; 23208 23209 //Update the placeholder 23210 this.options.placeholder.update(this.currentContainer, this.placeholder); 23211 23212 this.containers[innermostIndex]._trigger("over", event, this._uiHash(this)); 23213 this.containers[innermostIndex].containerCache.over = 1; 23214 } 23215 23216 23217 }, 23218 23219 _createHelper: function(event) { 23220 23221 var o = this.options, 23222 helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event, this.currentItem])) : (o.helper === "clone" ? this.currentItem.clone() : this.currentItem); 23223 23224 //Add the helper to the DOM if that didn't happen already 23225 if(!helper.parents("body").length) { 23226 $(o.appendTo !== "parent" ? o.appendTo : this.currentItem[0].parentNode)[0].appendChild(helper[0]); 23227 } 23228 23229 if(helper[0] === this.currentItem[0]) { 23230 this._storedCSS = { width: this.currentItem[0].style.width, height: this.currentItem[0].style.height, position: this.currentItem.css("position"), top: this.currentItem.css("top"), left: this.currentItem.css("left") }; 23231 } 23232 23233 if(!helper[0].style.width || o.forceHelperSize) { 23234 helper.width(this.currentItem.width()); 23235 } 23236 if(!helper[0].style.height || o.forceHelperSize) { 23237 helper.height(this.currentItem.height()); 23238 } 23239 23240 return helper; 23241 23242 }, 23243 23244 _adjustOffsetFromHelper: function(obj) { 23245 if (typeof obj === "string") { 23246 obj = obj.split(" "); 23247 } 23248 if ($.isArray(obj)) { 23249 obj = {left: +obj[0], top: +obj[1] || 0}; 23250 } 23251 if ("left" in obj) { 23252 this.offset.click.left = obj.left + this.margins.left; 23253 } 23254 if ("right" in obj) { 23255 this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left; 23256 } 23257 if ("top" in obj) { 23258 this.offset.click.top = obj.top + this.margins.top; 23259 } 23260 if ("bottom" in obj) { 23261 this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top; 23262 } 23263 }, 23264 23265 _getParentOffset: function() { 23266 23267 23268 //Get the offsetParent and cache its position 23269 this.offsetParent = this.helper.offsetParent(); 23270 var po = this.offsetParent.offset(); 23271 23272 // This is a special case where we need to modify a offset calculated on start, since the following happened: 23273 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent 23274 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that 23275 // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag 23276 if(this.cssPosition === "absolute" && this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) { 23277 po.left += this.scrollParent.scrollLeft(); 23278 po.top += this.scrollParent.scrollTop(); 23279 } 23280 23281 // This needs to be actually done for all browsers, since pageX/pageY includes this information 23282 // with an ugly IE fix 23283 if( this.offsetParent[0] === document.body || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() === "html" && $.ui.ie)) { 23284 po = { top: 0, left: 0 }; 23285 } 23286 23287 return { 23288 top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0), 23289 left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0) 23290 }; 23291 23292 }, 23293 23294 _getRelativeOffset: function() { 23295 23296 if(this.cssPosition === "relative") { 23297 var p = this.currentItem.position(); 23298 return { 23299 top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(), 23300 left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft() 23301 }; 23302 } else { 23303 return { top: 0, left: 0 }; 23304 } 23305 23306 }, 23307 23308 _cacheMargins: function() { 23309 this.margins = { 23310 left: (parseInt(this.currentItem.css("marginLeft"),10) || 0), 23311 top: (parseInt(this.currentItem.css("marginTop"),10) || 0) 23312 }; 23313 }, 23314 23315 _cacheHelperProportions: function() { 23316 this.helperProportions = { 23317 width: this.helper.outerWidth(), 23318 height: this.helper.outerHeight() 23319 }; 23320 }, 23321 23322 _setContainment: function() { 23323 23324 var ce, co, over, 23325 o = this.options; 23326 if(o.containment === "parent") { 23327 o.containment = this.helper[0].parentNode; 23328 } 23329 if(o.containment === "document" || o.containment === "window") { 23330 this.containment = [ 23331 0 - this.offset.relative.left - this.offset.parent.left, 23332 0 - this.offset.relative.top - this.offset.parent.top, 23333 $(o.containment === "document" ? document : window).width() - this.helperProportions.width - this.margins.left, 23334 ($(o.containment === "document" ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top 23335 ]; 23336 } 23337 23338 if(!(/^(document|window|parent)$/).test(o.containment)) { 23339 ce = $(o.containment)[0]; 23340 co = $(o.containment).offset(); 23341 over = ($(ce).css("overflow") !== "hidden"); 23342 23343 this.containment = [ 23344 co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0) - this.margins.left, 23345 co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0) - this.margins.top, 23346 co.left+(over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left, 23347 co.top+(over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top 23348 ]; 23349 } 23350 23351 }, 23352 23353 _convertPositionTo: function(d, pos) { 23354 23355 if(!pos) { 23356 pos = this.position; 23357 } 23358 var mod = d === "absolute" ? 1 : -1, 23359 scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, 23360 scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName); 23361 23362 return { 23363 top: ( 23364 pos.top + // The absolute mouse position 23365 this.offset.relative.top * mod + // Only for relative positioned nodes: Relative offset from element to offset parent 23366 this.offset.parent.top * mod - // The offsetParent's offset without borders (offset + border) 23367 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod) 23368 ), 23369 left: ( 23370 pos.left + // The absolute mouse position 23371 this.offset.relative.left * mod + // Only for relative positioned nodes: Relative offset from element to offset parent 23372 this.offset.parent.left * mod - // The offsetParent's offset without borders (offset + border) 23373 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod) 23374 ) 23375 }; 23376 23377 }, 23378 23379 _generatePosition: function(event) { 23380 23381 var top, left, 23382 o = this.options, 23383 pageX = event.pageX, 23384 pageY = event.pageY, 23385 scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName); 23386 23387 // This is another very weird special case that only happens for relative elements: 23388 // 1. If the css position is relative 23389 // 2. and the scroll parent is the document or similar to the offset parent 23390 // we have to refresh the relative offset during the scroll so there are no jumps 23391 if(this.cssPosition === "relative" && !(this.scrollParent[0] !== document && this.scrollParent[0] !== this.offsetParent[0])) { 23392 this.offset.relative = this._getRelativeOffset(); 23393 } 23394 23395 /* 23396 * - Position constraining - 23397 * Constrain the position to a mix of grid, containment. 23398 */ 23399 23400 if(this.originalPosition) { //If we are not dragging yet, we won't check for options 23401 23402 if(this.containment) { 23403 if(event.pageX - this.offset.click.left < this.containment[0]) { 23404 pageX = this.containment[0] + this.offset.click.left; 23405 } 23406 if(event.pageY - this.offset.click.top < this.containment[1]) { 23407 pageY = this.containment[1] + this.offset.click.top; 23408 } 23409 if(event.pageX - this.offset.click.left > this.containment[2]) { 23410 pageX = this.containment[2] + this.offset.click.left; 23411 } 23412 if(event.pageY - this.offset.click.top > this.containment[3]) { 23413 pageY = this.containment[3] + this.offset.click.top; 23414 } 23415 } 23416 23417 if(o.grid) { 23418 top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1]; 23419 pageY = this.containment ? ( (top - this.offset.click.top >= this.containment[1] && top - this.offset.click.top <= this.containment[3]) ? top : ((top - this.offset.click.top >= this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top; 23420 23421 left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0]; 23422 pageX = this.containment ? ( (left - this.offset.click.left >= this.containment[0] && left - this.offset.click.left <= this.containment[2]) ? left : ((left - this.offset.click.left >= this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left; 23423 } 23424 23425 } 23426 23427 return { 23428 top: ( 23429 pageY - // The absolute mouse position 23430 this.offset.click.top - // Click offset (relative to the element) 23431 this.offset.relative.top - // Only for relative positioned nodes: Relative offset from element to offset parent 23432 this.offset.parent.top + // The offsetParent's offset without borders (offset + border) 23433 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) )) 23434 ), 23435 left: ( 23436 pageX - // The absolute mouse position 23437 this.offset.click.left - // Click offset (relative to the element) 23438 this.offset.relative.left - // Only for relative positioned nodes: Relative offset from element to offset parent 23439 this.offset.parent.left + // The offsetParent's offset without borders (offset + border) 23440 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() )) 23441 ) 23442 }; 23443 23444 }, 23445 23446 _rearrange: function(event, i, a, hardRefresh) { 23447 23448 a ? a[0].appendChild(this.placeholder[0]) : i.item[0].parentNode.insertBefore(this.placeholder[0], (this.direction === "down" ? i.item[0] : i.item[0].nextSibling)); 23449 23450 //Various things done here to improve the performance: 23451 // 1. we create a setTimeout, that calls refreshPositions 23452 // 2. on the instance, we have a counter variable, that get's higher after every append 23453 // 3. on the local scope, we copy the counter variable, and check in the timeout, if it's still the same 23454 // 4. this lets only the last addition to the timeout stack through 23455 this.counter = this.counter ? ++this.counter : 1; 23456 var counter = this.counter; 23457 23458 this._delay(function() { 23459 if(counter === this.counter) { 23460 this.refreshPositions(!hardRefresh); //Precompute after each DOM insertion, NOT on mousemove 23461 } 23462 }); 23463 23464 }, 23465 23466 _clear: function(event, noPropagation) { 23467 23468 this.reverting = false; 23469 // We delay all events that have to be triggered to after the point where the placeholder has been removed and 23470 // everything else normalized again 23471 var i, 23472 delayedTriggers = []; 23473 23474 // We first have to update the dom position of the actual currentItem 23475 // Note: don't do it if the current item is already removed (by a user), or it gets reappended (see #4088) 23476 if(!this._noFinalSort && this.currentItem.parent().length) { 23477 this.placeholder.before(this.currentItem); 23478 } 23479 this._noFinalSort = null; 23480 23481 if(this.helper[0] === this.currentItem[0]) { 23482 for(i in this._storedCSS) { 23483 if(this._storedCSS[i] === "auto" || this._storedCSS[i] === "static") { 23484 this._storedCSS[i] = ""; 23485 } 23486 } 23487 this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"); 23488 } else { 23489 this.currentItem.show(); 23490 } 23491 23492 if(this.fromOutside && !noPropagation) { 23493 delayedTriggers.push(function(event) { this._trigger("receive", event, this._uiHash(this.fromOutside)); }); 23494 } 23495 if((this.fromOutside || this.domPosition.prev !== this.currentItem.prev().not(".ui-sortable-helper")[0] || this.domPosition.parent !== this.currentItem.parent()[0]) && !noPropagation) { 23496 delayedTriggers.push(function(event) { this._trigger("update", event, this._uiHash()); }); //Trigger update callback if the DOM position has changed 23497 } 23498 23499 // Check if the items Container has Changed and trigger appropriate 23500 // events. 23501 if (this !== this.currentContainer) { 23502 if(!noPropagation) { 23503 delayedTriggers.push(function(event) { this._trigger("remove", event, this._uiHash()); }); 23504 delayedTriggers.push((function(c) { return function(event) { c._trigger("receive", event, this._uiHash(this)); }; }).call(this, this.currentContainer)); 23505 delayedTriggers.push((function(c) { return function(event) { c._trigger("update", event, this._uiHash(this)); }; }).call(this, this.currentContainer)); 23506 } 23507 } 23508 23509 23510 //Post events to containers 23511 f
vendor: 11,400 bytes, lines 23511-23952
23511or (i = this.containers.length - 1; i >= 0; i--){ 23512 if(!noPropagation) { 23513 delayedTriggers.push((function(c) { return function(event) { c._trigger("deactivate", event, this._uiHash(this)); }; }).call(this, this.containers[i])); 23514 } 23515 if(this.containers[i].containerCache.over) { 23516 delayedTriggers.push((function(c) { return function(event) { c._trigger("out", event, this._uiHash(this)); }; }).call(this, this.containers[i])); 23517 this.containers[i].containerCache.over = 0; 23518 } 23519 } 23520 23521 //Do what was originally in plugins 23522 if ( this.storedCursor ) { 23523 this.document.find( "body" ).css( "cursor", this.storedCursor ); 23524 this.storedStylesheet.remove(); 23525 } 23526 if(this._storedOpacity) { 23527 this.helper.css("opacity", this._storedOpacity); 23528 } 23529 if(this._storedZIndex) { 23530 this.helper.css("zIndex", this._storedZIndex === "auto" ? "" : this._storedZIndex); 23531 } 23532 23533 this.dragging = false; 23534 if(this.cancelHelperRemoval) { 23535 if(!noPropagation) { 23536 this._trigger("beforeStop", event, this._uiHash()); 23537 for (i=0; i < delayedTriggers.length; i++) { 23538 delayedTriggers[i].call(this, event); 23539 } //Trigger all delayed events 23540 this._trigger("stop", event, this._uiHash()); 23541 } 23542 23543 this.fromOutside = false; 23544 return false; 23545 } 23546 23547 if(!noPropagation) { 23548 this._trigger("beforeStop", event, this._uiHash()); 23549 } 23550 23551 //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node! 23552 this.placeholder[0].parentNode.removeChild(this.placeholder[0]); 23553 23554 if(this.helper[0] !== this.currentItem[0]) { 23555 this.helper.remove(); 23556 } 23557 this.helper = null; 23558 23559 if(!noPropagation) { 23560 for (i=0; i < delayedTriggers.length; i++) { 23561 delayedTriggers[i].call(this, event); 23562 } //Trigger all delayed events 23563 this._trigger("stop", event, this._uiHash()); 23564 } 23565 23566 this.fromOutside = false; 23567 return true; 23568 23569 }, 23570 23571 _trigger: function() { 23572 if ($.Widget.prototype._trigger.apply(this, arguments) === false) { 23573 this.cancel(); 23574 } 23575 }, 23576 23577 _uiHash: function(_inst) { 23578 var inst = _inst || this; 23579 return { 23580 helper: inst.helper, 23581 placeholder: inst.placeholder || $([]), 23582 position: inst.position, 23583 originalPosition: inst.originalPosition, 23584 offset: inst.positionAbs, 23585 item: inst.currentItem, 23586 sender: _inst ? _inst.element : null 23587 }; 23588 } 23589 23590}); 23591 23592})(jQuery); 23593 23594 23595 23596 23597/*! 23598 * jQuery UI Spinner 1.10.3 23599 * http://jqueryui.com 23600 * 23601 * Copyright 2013 jQuery Foundation and other contributors 23602 * Released under the MIT license. 23603 * http://jquery.org/license 23604 * 23605 * http://api.jqueryui.com/spinner/ 23606 * 23607 * Depends: 23608 * jquery.ui.core.js 23609 * jquery.ui.widget.js 23610 * jquery.ui.button.js 23611 */ 23612 23613(function( $ ) { 23614 23615function modifier( fn ) { 23616 return function() { 23617 var previous = this.element.val(); 23618 fn.apply( this, arguments ); 23619 this._refresh(); 23620 if ( previous !== this.element.val() ) { 23621 this._trigger( "change" ); 23622 } 23623 }; 23624} 23625 23626$.widget( "ui.spinner", { 23627 version: "1.10.3", 23628 defaultElement: "<input>", 23629 widgetEventPrefix: "spin", 23630 options: { 23631 culture: null, 23632 icons: { 23633 down: "ui-icon-triangle-1-s", 23634 up: "ui-icon-triangle-1-n" 23635 }, 23636 incremental: true, 23637 max: null, 23638 min: null, 23639 numberFormat: null, 23640 page: 10, 23641 step: 1, 23642 23643 change: null, 23644 spin: null, 23645 start: null, 23646 stop: null 23647 }, 23648 23649 _create: function() { 23650 // handle string values that need to be parsed 23651 this._setOption( "max", this.options.max ); 23652 this._setOption( "min", this.options.min ); 23653 this._setOption( "step", this.options.step ); 23654 23655 // format the value, but don't constrain 23656 this._value( this.element.val(), true ); 23657 23658 this._draw(); 23659 this._on( this._events ); 23660 this._refresh(); 23661 23662 // turning off autocomplete prevents the browser from remembering the 23663 // value when navigating through history, so we re-enable autocomplete 23664 // if the page is unloaded before the widget is destroyed. #7790 23665 this._on( this.window, { 23666 beforeunload: function() { 23667 this.element.removeAttr( "autocomplete" ); 23668 } 23669 }); 23670 }, 23671 23672 _getCreateOptions: function() { 23673 var options = {}, 23674 element = this.element; 23675 23676 $.each( [ "min", "max", "step" ], function( i, option ) { 23677 var value = element.attr( option ); 23678 if ( value !== undefined && value.length ) { 23679 options[ option ] = value; 23680 } 23681 }); 23682 23683 return options; 23684 }, 23685 23686 _events: { 23687 keydown: function( event ) { 23688 if ( this._start( event ) && this._keydown( event ) ) { 23689 event.preventDefault(); 23690 } 23691 }, 23692 keyup: "_stop", 23693 focus: function() { 23694 this.previous = this.element.val(); 23695 }, 23696 blur: function( event ) { 23697 if ( this.cancelBlur ) { 23698 delete this.cancelBlur; 23699 return; 23700 } 23701 23702 this._stop(); 23703 this._refresh(); 23704 if ( this.previous !== this.element.val() ) { 23705 this._trigger( "change", event ); 23706 } 23707 }, 23708 mousewheel: function( event, delta ) { 23709 if ( !delta ) { 23710 return; 23711 } 23712 if ( !this.spinning && !this._start( event ) ) { 23713 return false; 23714 } 23715 23716 this._spin( (delta > 0 ? 1 : -1) * this.options.step, event ); 23717 clearTimeout( this.mousewheelTimer ); 23718 this.mousewheelTimer = this._delay(function() { 23719 if ( this.spinning ) { 23720 this._stop( event ); 23721 } 23722 }, 100 ); 23723 event.preventDefault(); 23724 }, 23725 "mousedown .ui-spinner-button": function( event ) { 23726 var previous; 23727 23728 // We never want the buttons to have focus; whenever the user is 23729 // interacting with the spinner, the focus should be on the input. 23730 // If the input is focused then this.previous is properly set from 23731 // when the input first received focus. If the input is not focused 23732 // then we need to set this.previous based on the value before spinning. 23733 previous = this.element[0] === this.document[0].activeElement ? 23734 this.previous : this.element.val(); 23735 function checkFocus() { 23736 var isActive = this.element[0] === this.document[0].activeElement; 23737 if ( !isActive ) { 23738 this.element.focus(); 23739 this.previous = previous; 23740 // support: IE 23741 // IE sets focus asynchronously, so we need to check if focus 23742 // moved off of the input because the user clicked on the button. 23743 this._delay(function() { 23744 this.previous = previous; 23745 }); 23746 } 23747 } 23748 23749 // ensure focus is on (or stays on) the text field 23750 event.preventDefault(); 23751 checkFocus.call( this ); 23752 23753 // support: IE 23754 // IE doesn't prevent moving focus even with event.preventDefault() 23755 // so we set a flag to know when we should ignore the blur event 23756 // and check (again) if focus moved off of the input. 23757 this.cancelBlur = true; 23758 this._delay(function() { 23759 delete this.cancelBlur; 23760 checkFocus.call( this ); 23761 }); 23762 23763 if ( this._start( event ) === false ) { 23764 return; 23765 } 23766 23767 this._repeat( null, $( event.currentTarget ).hasClass( "ui-spinner-up" ) ? 1 : -1, event ); 23768 }, 23769 "mouseup .ui-spinner-button": "_stop", 23770 "mouseenter .ui-spinner-button": function( event ) { 23771 // button will add ui-state-active if mouse was down while mouseleave and kept down 23772 if ( !$( event.currentTarget ).hasClass( "ui-state-active" ) ) { 23773 return; 23774 } 23775 23776 if ( this._start( event ) === false ) { 23777 return false; 23778 } 23779 this._repeat( null, $( event.currentTarget ).hasClass( "ui-spinner-up" ) ? 1 : -1, event ); 23780 }, 23781 // TODO: do we really want to consider this a stop? 23782 // shouldn't we just stop the repeater and wait until mouseup before 23783 // we trigger the stop event? 23784 "mouseleave .ui-spinner-button": "_stop" 23785 }, 23786 23787 _draw: function() { 23788 var uiSpinner = this.uiSpinner = this.element 23789 .addClass( "ui-spinner-input" ) 23790 .attr( "autocomplete", "off" ) 23791 .wrap( this._uiSpinnerHtml() ) 23792 .parent() 23793 // add buttons 23794 .append( this._buttonHtml() ); 23795 23796 this.element.attr( "role", "spinbutton" ); 23797 23798 // button bindings 23799 this.buttons = uiSpinner.find( ".ui-spinner-button" ) 23800 .attr( "tabIndex", -1 ) 23801 .button() 23802 .removeClass( "ui-corner-all" ); 23803 23804 // IE 6 doesn't understand height: 50% for the buttons 23805 // unless the wrapper has an explicit height 23806 if ( this.buttons.height() > Math.ceil( uiSpinner.height() * 0.5 ) && 23807 uiSpinner.height() > 0 ) { 23808 uiSpinner.height( uiSpinner.height() ); 23809 } 23810 23811 // disable spinner if element was already disabled 23812 if ( this.options.disabled ) { 23813 this.disable(); 23814 } 23815 }, 23816 23817 _keydown: function( event ) { 23818 var options = this.options, 23819 keyCode = $.ui.keyCode; 23820 23821 switch ( event.keyCode ) { 23822 case keyCode.UP: 23823 this._repeat( null, 1, event ); 23824 return true; 23825 case keyCode.DOWN: 23826 this._repeat( null, -1, event ); 23827 return true; 23828 case keyCode.PAGE_UP: 23829 this._repeat( null, options.page, event ); 23830 return true; 23831 case keyCode.PAGE_DOWN: 23832 this._repeat( null, -options.page, event ); 23833 return true; 23834 } 23835 23836 return false; 23837 }, 23838 23839 _uiSpinnerHtml: function() { 23840 return "<span class='ui-spinner ui-widget ui-widget-content ui-corner-all'></span>"; 23841 }, 23842 23843 _buttonHtml: function() { 23844 return "" + 23845 "<a class='ui-spinner-button ui-spinner-up ui-corner-tr'>" + 23846 "<span class='ui-icon " + this.options.icons.up + "'>▲</span>" + 23847 "</a>" + 23848 "<a class='ui-spinner-button ui-spinner-down ui-corner-br'>" + 23849 "<span class='ui-icon " + this.options.icons.down + "'>▼</span>" + 23850 "</a>"; 23851 }, 23852 23853 _start: function( event ) { 23854 if ( !this.spinning && this._trigger( "start", event ) === false ) { 23855 return false; 23856 } 23857 23858 if ( !this.counter ) { 23859 this.counter = 1; 23860 } 23861 this.spinning = true; 23862 return true; 23863 }, 23864 23865 _repeat: function( i, steps, event ) { 23866 i = i || 500; 23867 23868 clearTimeout( this.timer ); 23869 this.timer = this._delay(function() { 23870 this._repeat( 40, steps, event ); 23871 }, i ); 23872 23873 this._spin( steps * this.options.step, event ); 23874 }, 23875 23876 _spin: function( step, event ) { 23877 var value = this.value() || 0; 23878 23879 if ( !this.counter ) { 23880 this.counter = 1; 23881 } 23882 23883 value = this._adjustValue( value + step * this._increment( this.counter ) ); 23884 23885 if ( !this.spinning || this._trigger( "spin", event, { value: value } ) !== false) { 23886 this._value( value ); 23887 this.counter++; 23888 } 23889 }, 23890 23891 _increment: function( i ) { 23892 var incremental = this.options.incremental; 23893 23894 if ( incremental ) { 23895 return $.isFunction( incremental ) ? 23896 incremental( i ) : 23897 Math.floor( i*i*i/50000 - i*i/500 + 17*i/200 + 1 ); 23898 } 23899 23900 return 1; 23901 }, 23902 23903 _precision: function() { 23904 var precision = this._precisionOf( this.options.step ); 23905 if ( this.options.min !== null ) { 23906 precision = Math.max( precision, this._precisionOf( this.options.min ) ); 23907 } 23908 return precision; 23909 }, 23910 23911 _precisionOf: function( num ) { 23912 var str = num.toString(), 23913 decimal = str.indexOf( "." ); 23914 return decimal === -1 ? 0 : str.length - decimal - 1; 23915 }, 23916 23917 _adjustValue: function( value ) { 23918 var base, aboveMin, 23919 options = this.options; 23920 23921 // make sure we're at a valid step 23922 // - find out where we are relative to the base (min or 0) 23923 base = options.min !== null ? options.min : 0; 23924 aboveMin = value - base; 23925 // - round to the nearest step 23926 aboveMin = Math.round(aboveMin / options.step) * options.step; 23927 // - rounding is based on 0, so adjust back to our base 23928 value = base + aboveMin; 23929 23930 // fix precision from bad JS floating point math 23931 value = parseFloat( value.toFixed( this._precision() ) ); 23932 23933 // clamp the value 23934 if ( options.max !== null && value > options.max) { 23935 return options.max; 23936 } 23937 if ( options.min !== null && value < options.min ) { 23938 return options.min; 23939 } 23940 23941 return value; 23942 }, 23943 23944 _stop: function( event ) { 23945 if ( !this.spinning ) { 23946 return; 23947 } 23948 23949 clearTimeout( this.timer ); 23950 clearTimeout( this.mousewheelTimer ); 23951 this.counter = 0; 23952 this.spinning = false;
23953 this._trigger( "stop", event ); 23954 }, 23955 23956 _setOption: function( key, value ) { 23957 if ( key === "culture" || key === "numberFormat" ) { 23958 var prevValue = this._parse( this.element.val() ); 23959 this.options[ key ] = value; 23960 this.element.val( this._format( prevValue ) ); 23961 return; 23962 } 23963 23964 if ( key === "max" || key === "min" || key === "step" ) { 23965 if ( typeof value === "string" ) { 23966 value = this._parse( value ); 23967 } 23968 } 23969 if ( key === "icons" ) { 23970 this.buttons.first().find( ".ui-icon" ) 23971 .removeClass( this.options.icons.up ) 23972 .addClass( value.up ); 23973 this.buttons.last().find( ".ui-icon" ) 23974 .removeClass( this.options.icons.down ) 23975 .addClass( value.down ); 23976 } 23977 23978 this._super( key, value ); 23979 23980 if ( key === "disabled" ) { 23981 if ( value ) { 23982 this.element.prop( "disabled", true ); 23983 this.buttons.button( "disable" ); 23984 } else { 23985 this.element.prop( "disabled", false ); 23986 this.buttons.button( "enable" ); 23987 } 23988 } 23989 }, 23990 23991 _setOptions: modifier(function( options ) { 23992 this._super( options ); 23993 this._value( this.element.val() ); 23994 }), 23995 23996 _parse: function( val ) { 23997 if ( typeof val === "string" && val !== "" ) { 23998 val = window.Globalize && this.options.numberFormat ? 23999 Globalize.parseFloat( val, 10, this.options.culture ) : +val; 24000 } 24001 return val === "" || isNaN( val ) ? null : val; 24002 }, 24003 24004 _format: function( value ) { 24005 if ( value === "" ) { 24006 return ""; 24007 } 24008 return window.Globalize && this.options.numberFormat ? 24009 Globalize.format( value, this.options.numberFormat, this.options.culture ) : 24010 value; 24011 }, 24012 24013 _refresh: function() { 24014 this.element.attr({ 24015 "aria-valuemin": this.options.min, 24016 "aria-valuemax": this.options.max, 24017 // TODO: what should we do with values that can't be parsed? 24018 "aria-valuenow": this._parse( this.element.val() ) 24019 }); 24020 }, 24021 24022 // update the value without triggering change 24023 _value: function( value, allowAny ) { 24024 var parsed; 24025 if ( value !== "" ) { 24026 parsed = this._parse( value ); 24027 if ( parsed !== null ) { 24028 if ( !allowAny ) { 24029 parsed = this._adjustValue( parsed ); 24030 } 24031 value = this._format( parsed ); 24032 } 24033 } 24034 this.element.val( value ); 24035 this._refresh(); 24036 }, 24037 24038 _destroy: function() { 24039 this.element 24040 .removeClass( "ui-spinner-input" ) 24041 .prop( "disabled", false ) 24042 .removeAttr( "autocomplete" ) 24043 .removeAttr( "role" ) 24044 .removeAttr( "aria-valuemin" ) 24045 .removeAttr( "aria-valuemax" ) 24046 .removeAttr( "aria-valuenow" ); 24047 this.uiSpinner.replaceWith( this.element ); 24048 }, 24049 24050 stepUp: modifier(function( steps ) { 24051 this._stepUp( steps ); 24052 }), 24053 _stepUp: function( steps ) { 24054 if ( this._start() ) { 24055 this._spin( (steps || 1) * this.options.step ); 24056 this._stop(); 24057 } 24058 }, 24059 24060 stepDown: modifier(function( steps ) { 24061 this._stepDown( steps ); 24062 }), 24063 _stepDown: function( steps ) { 24064 if ( this._start() ) { 24065 this._spin( (steps || 1) * -this.options.step ); 24066 this._stop(); 24067 } 24068 }, 24069 24070 pageUp: modifier(function( pages ) { 24071 this._stepUp( (pages || 1) * this.options.page ); 24072 }), 24073 24074 pageDown: modifier(function( pages ) { 24075 this._stepDown( (pages || 1) * this.options.page ); 24076 }), 24077 24078 value: function( newVal ) { 24079 if ( !arguments.length ) { 24080 return this._parse( this.element.val() ); 24081 } 24082 modifier( this._value ).call( this, newVal ); 24083 }, 24084 24085 widget: function() { 24086 return this.uiSpinner; 24087 } 24088}); 24089 24090}( jQuery ) ); 24091 24092 24093 24094/*! 24095 * jQuery UI Tabs 1.10.3 24096 * http://jqueryui.com 24097 * 24098 * Copyright 2013 jQuery Foundation and other contributors 24099 * Released under the MIT license. 24100 * http://jquery.org/license 24101 * 24102 * http://api.jqueryui.com/tabs/ 24103 * 24104 * Depends: 24105 * jquery.ui.core.js 24106 * jquery.ui.widget.js 24107 */ 24108 24109(function( $, undefined ) { 24110 24111var tabId = 0, 24112 rhash = /#.*$/; 24113 24114function getNextTabId() { 24115 return ++tabId; 24116} 24117 24118function isLocal( anchor ) { 24119 return anchor.hash.length > 1 && 24120 decodeURIComponent( anchor.href.replace( rhash, "" ) ) === 24121 decodeURIComponent( location.href.replace( rhash, "" ) ); 24122} 24123 24124$.widget( "ui.tabs", { 24125 version: "1.10.3", 24126 delay: 300, 24127 options: { 24128 active: null, 24129 collapsible: false, 24130 event: "click", 24131 heightStyle: "content", 24132 hide: null, 24133 show: null, 24134 24135 // callbacks 24136 activate: null, 24137 beforeActivate: null, 24138 beforeLoad: null, 24139 load: null 24140 }, 24141 24142 _create: function() { 24143 var that = this, 24144 options = this.options; 24145 24146 this.running = false;
vendor: 27,525 bytes, lines 24147-25185
24147 24148 this.element 24149 .addClass( "ui-tabs ui-widget ui-widget-content ui-corner-all" ) 24150 .toggleClass( "ui-tabs-collapsible", options.collapsible ) 24151 // Prevent users from focusing disabled tabs via click 24152 .delegate( ".ui-tabs-nav > li", "mousedown" + this.eventNamespace, function( event ) { 24153 if ( $( this ).is( ".ui-state-disabled" ) ) { 24154 event.preventDefault(); 24155 } 24156 }) 24157 // support: IE <9 24158 // Preventing the default action in mousedown doesn't prevent IE 24159 // from focusing the element, so if the anchor gets focused, blur. 24160 // We don't have to worry about focusing the previously focused 24161 // element since clicking on a non-focusable element should focus 24162 // the body anyway. 24163 .delegate( ".ui-tabs-anchor", "focus" + this.eventNamespace, function() { 24164 if ( $( this ).closest( "li" ).is( ".ui-state-disabled" ) ) { 24165 this.blur(); 24166 } 24167 }); 24168 24169 this._processTabs(); 24170 options.active = this._initialActive(); 24171 24172 // Take disabling tabs via class attribute from HTML 24173 // into account and update option properly. 24174 if ( $.isArray( options.disabled ) ) { 24175 options.disabled = $.unique( options.disabled.concat( 24176 $.map( this.tabs.filter( ".ui-state-disabled" ), function( li ) { 24177 return that.tabs.index( li ); 24178 }) 24179 ) ).sort(); 24180 } 24181 24182 // check for length avoids error when initializing empty list 24183 if ( this.options.active !== false && this.anchors.length ) { 24184 this.active = this._findActive( options.active ); 24185 } else { 24186 this.active = $(); 24187 } 24188 24189 this._refresh(); 24190 24191 if ( this.active.length ) { 24192 this.load( options.active ); 24193 } 24194 }, 24195 24196 _initialActive: function() { 24197 var active = this.options.active, 24198 collapsible = this.options.collapsible, 24199 locationHash = location.hash.substring( 1 ); 24200 24201 if ( active === null ) { 24202 // check the fragment identifier in the URL 24203 if ( locationHash ) { 24204 this.tabs.each(function( i, tab ) { 24205 if ( $( tab ).attr( "aria-controls" ) === locationHash ) { 24206 active = i; 24207 return false; 24208 } 24209 }); 24210 } 24211 24212 // check for a tab marked active via a class 24213 if ( active === null ) { 24214 active = this.tabs.index( this.tabs.filter( ".ui-tabs-active" ) ); 24215 } 24216 24217 // no active tab, set to false 24218 if ( active === null || active === -1 ) { 24219 active = this.tabs.length ? 0 : false; 24220 } 24221 } 24222 24223 // handle numbers: negative, out of range 24224 if ( active !== false ) { 24225 active = this.tabs.index( this.tabs.eq( active ) ); 24226 if ( active === -1 ) { 24227 active = collapsible ? false : 0; 24228 } 24229 } 24230 24231 // don't allow collapsible: false and active: false 24232 if ( !collapsible && active === false && this.anchors.length ) { 24233 active = 0; 24234 } 24235 24236 return active; 24237 }, 24238 24239 _getCreateEventData: function() { 24240 return { 24241 tab: this.active, 24242 panel: !this.active.length ? $() : this._getPanelForTab( this.active ) 24243 }; 24244 }, 24245 24246 _tabKeydown: function( event ) { 24247 /*jshint maxcomplexity:15*/ 24248 var focusedTab = $( this.document[0].activeElement ).closest( "li" ), 24249 selectedIndex = this.tabs.index( focusedTab ), 24250 goingForward = true; 24251 24252 if ( this._handlePageNav( event ) ) { 24253 return; 24254 } 24255 24256 switch ( event.keyCode ) { 24257 case $.ui.keyCode.RIGHT: 24258 case $.ui.keyCode.DOWN: 24259 selectedIndex++; 24260 break; 24261 case $.ui.keyCode.UP: 24262 case $.ui.keyCode.LEFT: 24263 goingForward = false; 24264 selectedIndex--; 24265 break; 24266 case $.ui.keyCode.END: 24267 selectedIndex = this.anchors.length - 1; 24268 break; 24269 case $.ui.keyCode.HOME: 24270 selectedIndex = 0; 24271 break; 24272 case $.ui.keyCode.SPACE: 24273 // Activate only, no collapsing 24274 event.preventDefault(); 24275 clearTimeout( this.activating ); 24276 this._activate( selectedIndex ); 24277 return; 24278 case $.ui.keyCode.ENTER: 24279 // Toggle (cancel delayed activation, allow collapsing) 24280 event.preventDefault(); 24281 clearTimeout( this.activating ); 24282 // Determine if we should collapse or activate 24283 this._activate( selectedIndex === this.options.active ? false : selectedIndex ); 24284 return; 24285 default: 24286 return; 24287 } 24288 24289 // Focus the appropriate tab, based on which key was pressed 24290 event.preventDefault(); 24291 clearTimeout( this.activating ); 24292 selectedIndex = this._focusNextTab( selectedIndex, goingForward ); 24293 24294 // Navigating with control key will prevent automatic activation 24295 if ( !event.ctrlKey ) { 24296 // Update aria-selected immediately so that AT think the tab is already selected. 24297 // Otherwise AT may confuse the user by stating that they need to activate the tab, 24298 // but the tab will already be activated by the time the announcement finishes. 24299 focusedTab.attr( "aria-selected", "false" ); 24300 this.tabs.eq( selectedIndex ).attr( "aria-selected", "true" ); 24301 24302 this.activating = this._delay(function() { 24303 this.option( "active", selectedIndex ); 24304 }, this.delay ); 24305 } 24306 }, 24307 24308 _panelKeydown: function( event ) { 24309 if ( this._handlePageNav( event ) ) { 24310 return; 24311 } 24312 24313 // Ctrl+up moves focus to the current tab 24314 if ( event.ctrlKey && event.keyCode === $.ui.keyCode.UP ) { 24315 event.preventDefault(); 24316 this.active.focus(); 24317 } 24318 }, 24319 24320 // Alt+page up/down moves focus to the previous/next tab (and activates) 24321 _handlePageNav: function( event ) { 24322 if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_UP ) { 24323 this._activate( this._focusNextTab( this.options.active - 1, false ) ); 24324 return true; 24325 } 24326 if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_DOWN ) { 24327 this._activate( this._focusNextTab( this.options.active + 1, true ) ); 24328 return true; 24329 } 24330 }, 24331 24332 _findNextTab: function( index, goingForward ) { 24333 var lastTabIndex = this.tabs.length - 1; 24334 24335 function constrain() { 24336 if ( index > lastTabIndex ) { 24337 index = 0; 24338 } 24339 if ( index < 0 ) { 24340 index = lastTabIndex; 24341 } 24342 return index; 24343 } 24344 24345 while ( $.inArray( constrain(), this.options.disabled ) !== -1 ) { 24346 index = goingForward ? index + 1 : index - 1; 24347 } 24348 24349 return index; 24350 }, 24351 24352 _focusNextTab: function( index, goingForward ) { 24353 index = this._findNextTab( index, goingForward ); 24354 this.tabs.eq( index ).focus(); 24355 return index; 24356 }, 24357 24358 _setOption: function( key, value ) { 24359 if ( key === "active" ) { 24360 // _activate() will handle invalid values and update this.options 24361 this._activate( value ); 24362 return; 24363 } 24364 24365 if ( key === "disabled" ) { 24366 // don't use the widget factory's disabled handling 24367 this._setupDisabled( value ); 24368 return; 24369 } 24370 24371 this._super( key, value); 24372 24373 if ( key === "collapsible" ) { 24374 this.element.toggleClass( "ui-tabs-collapsible", value ); 24375 // Setting collapsible: false while collapsed; open first panel 24376 if ( !value && this.options.active === false ) { 24377 this._activate( 0 ); 24378 } 24379 } 24380 24381 if ( key === "event" ) { 24382 this._setupEvents( value ); 24383 } 24384 24385 if ( key === "heightStyle" ) { 24386 this._setupHeightStyle( value ); 24387 } 24388 }, 24389 24390 _tabId: function( tab ) { 24391 return tab.attr( "aria-controls" ) || "ui-tabs-" + getNextTabId(); 24392 }, 24393 24394 _sanitizeSelector: function( hash ) { 24395 return hash ? hash.replace( /[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g, "\\$&" ) : ""; 24396 }, 24397 24398 refresh: function() { 24399 var options = this.options, 24400 lis = this.tablist.children( ":has(a[href])" ); 24401 24402 // get disabled tabs from class attribute from HTML 24403 // this will get converted to a boolean if needed in _refresh() 24404 options.disabled = $.map( lis.filter( ".ui-state-disabled" ), function( tab ) { 24405 return lis.index( tab ); 24406 }); 24407 24408 this._processTabs(); 24409 24410 // was collapsed or no tabs 24411 if ( options.active === false || !this.anchors.length ) { 24412 options.active = false; 24413 this.active = $(); 24414 // was active, but active tab is gone 24415 } else if ( this.active.length && !$.contains( this.tablist[ 0 ], this.active[ 0 ] ) ) { 24416 // all remaining tabs are disabled 24417 if ( this.tabs.length === options.disabled.length ) { 24418 options.active = false; 24419 this.active = $(); 24420 // activate previous tab 24421 } else { 24422 this._activate( this._findNextTab( Math.max( 0, options.active - 1 ), false ) ); 24423 } 24424 // was active, active tab still exists 24425 } else { 24426 // make sure active index is correct 24427 options.active = this.tabs.index( this.active ); 24428 } 24429 24430 this._refresh(); 24431 }, 24432 24433 _refresh: function() { 24434 this._setupDisabled( this.options.disabled ); 24435 this._setupEvents( this.options.event ); 24436 this._setupHeightStyle( this.options.heightStyle ); 24437 24438 this.tabs.not( this.active ).attr({ 24439 "aria-selected": "false", 24440 tabIndex: -1 24441 }); 24442 this.panels.not( this._getPanelForTab( this.active ) ) 24443 .hide() 24444 .attr({ 24445 "aria-expanded": "false", 24446 "aria-hidden": "true" 24447 }); 24448 24449 // Make sure one tab is in the tab order 24450 if ( !this.active.length ) { 24451 this.tabs.eq( 0 ).attr( "tabIndex", 0 ); 24452 } else { 24453 this.active 24454 .addClass( "ui-tabs-active ui-state-active" ) 24455 .attr({ 24456 "aria-selected": "true", 24457 tabIndex: 0 24458 }); 24459 this._getPanelForTab( this.active ) 24460 .show() 24461 .attr({ 24462 "aria-expanded": "true", 24463 "aria-hidden": "false" 24464 }); 24465 } 24466 }, 24467 24468 _processTabs: function() { 24469 var that = this; 24470 24471 this.tablist = this._getList() 24472 .addClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" ) 24473 .attr( "role", "tablist" ); 24474 24475 this.tabs = this.tablist.find( "> li:has(a[href])" ) 24476 .addClass( "ui-state-default ui-corner-top" ) 24477 .attr({ 24478 role: "tab", 24479 tabIndex: -1 24480 }); 24481 24482 this.anchors = this.tabs.map(function() { 24483 return $( "a", this )[ 0 ]; 24484 }) 24485 .addClass( "ui-tabs-anchor" ) 24486 .attr({ 24487 role: "presentation", 24488 tabIndex: -1 24489 }); 24490 24491 this.panels = $(); 24492 24493 this.anchors.each(function( i, anchor ) { 24494 var selector, panel, panelId, 24495 anchorId = $( anchor ).uniqueId().attr( "id" ), 24496 tab = $( anchor ).closest( "li" ), 24497 originalAriaControls = tab.attr( "aria-controls" ); 24498 24499 // inline tab 24500 if ( isLocal( anchor ) ) { 24501 selector = anchor.hash; 24502 panel = that.element.find( that._sanitizeSelector( selector ) ); 24503 // remote tab 24504 } else { 24505 panelId = that._tabId( tab ); 24506 selector = "#" + panelId; 24507 panel = that.element.find( selector ); 24508 if ( !panel.length ) { 24509 panel = that._createPanel( panelId ); 24510 panel.insertAfter( that.panels[ i - 1 ] || that.tablist ); 24511 } 24512 panel.attr( "aria-live", "polite" ); 24513 } 24514 24515 if ( panel.length) { 24516 that.panels = that.panels.add( panel ); 24517 } 24518 if ( originalAriaControls ) { 24519 tab.data( "ui-tabs-aria-controls", originalAriaControls ); 24520 } 24521 tab.attr({ 24522 "aria-controls": selector.substring( 1 ), 24523 "aria-labelledby": anchorId 24524 }); 24525 panel.attr( "aria-labelledby", anchorId ); 24526 }); 24527 24528 this.panels 24529 .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" ) 24530 .attr( "role", "tabpanel" ); 24531 }, 24532 24533 // allow overriding how to find the list for rare usage scenarios (#7715) 24534 _getList: function() { 24535 return this.element.find( "ol,ul" ).eq( 0 ); 24536 }, 24537 24538 _createPanel: function( id ) { 24539 return $( "<div>" ) 24540 .attr( "id", id ) 24541 .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" ) 24542 .data( "ui-tabs-destroy", true ); 24543 }, 24544 24545 _setupDisabled: function( disabled ) { 24546 if ( $.isArray( disabled ) ) { 24547 if ( !disabled.length ) { 24548 disabled = false; 24549 } else if ( disabled.length === this.anchors.length ) { 24550 disabled = true; 24551 } 24552 } 24553 24554 // disable tabs 24555 for ( var i = 0, li; ( li = this.tabs[ i ] ); i++ ) { 24556 if ( disabled === true || $.inArray( i, disabled ) !== -1 ) { 24557 $( li ) 24558 .addClass( "ui-state-disabled" ) 24559 .attr( "aria-disabled", "true" ); 24560 } else { 24561 $( li ) 24562 .removeClass( "ui-state-disabled" ) 24563 .removeAttr( "aria-disabled" ); 24564 } 24565 } 24566 24567 this.options.disabled = disabled; 24568 }, 24569 24570 _setupEvents: function( event ) { 24571 var events = { 24572 click: function( event ) { 24573 event.preventDefault(); 24574 } 24575 }; 24576 if ( event ) { 24577 $.each( event.split(" "), function( index, eventName ) { 24578 events[ eventName ] = "_eventHandler"; 24579 }); 24580 } 24581 24582 this._off( this.anchors.add( this.tabs ).add( this.panels ) ); 24583 this._on( this.anchors, events ); 24584 this._on( this.tabs, { keydown: "_tabKeydown" } ); 24585 this._on( this.panels, { keydown: "_panelKeydown" } ); 24586 24587 this._focusable( this.tabs ); 24588 this._hoverable( this.tabs ); 24589 }, 24590 24591 _setupHeightStyle: function( heightStyle ) { 24592 var maxHeight, 24593 parent = this.element.parent(); 24594 24595 if ( heightStyle === "fill" ) { 24596 maxHeight = parent.height(); 24597 maxHeight -= this.element.outerHeight() - this.element.height(); 24598 24599 this.element.siblings( ":visible" ).each(function() { 24600 var elem = $( this ), 24601 position = elem.css( "position" ); 24602 24603 if ( position === "absolute" || position === "fixed" ) { 24604 return; 24605 } 24606 maxHeight -= elem.outerHeight( true ); 24607 }); 24608 24609 this.element.children().not( this.panels ).each(function() { 24610 maxHeight -= $( this ).outerHeight( true ); 24611 }); 24612 24613 this.panels.each(function() { 24614 $( this ).height( Math.max( 0, maxHeight - 24615 $( this ).innerHeight() + $( this ).height() ) ); 24616 }) 24617 .css( "overflow", "auto" ); 24618 } else if ( heightStyle === "auto" ) { 24619 maxHeight = 0; 24620 this.panels.each(function() { 24621 maxHeight = Math.max( maxHeight, $( this ).height( "" ).height() ); 24622 }).height( maxHeight ); 24623 } 24624 }, 24625 24626 _eventHandler: function( event ) { 24627 var options = this.options, 24628 active = this.active, 24629 anchor = $( event.currentTarget ), 24630 tab = anchor.closest( "li" ), 24631 clickedIsActive = tab[ 0 ] === active[ 0 ], 24632 collapsing = clickedIsActive && options.collapsible, 24633 toShow = collapsing ? $() : this._getPanelForTab( tab ), 24634 toHide = !active.length ? $() : this._getPanelForTab( active ), 24635 eventData = { 24636 oldTab: active, 24637 oldPanel: toHide, 24638 newTab: collapsing ? $() : tab, 24639 newPanel: toShow 24640 }; 24641 24642 event.preventDefault(); 24643 24644 if ( tab.hasClass( "ui-state-disabled" ) || 24645 // tab is already loading 24646 tab.hasClass( "ui-tabs-loading" ) || 24647 // can't switch durning an animation 24648 this.running || 24649 // click on active header, but not collapsible 24650 ( clickedIsActive && !options.collapsible ) || 24651 // allow canceling activation 24652 ( this._trigger( "beforeActivate", event, eventData ) === false ) ) { 24653 return; 24654 } 24655 24656 options.active = collapsing ? false : this.tabs.index( tab ); 24657 24658 this.active = clickedIsActive ? $() : tab; 24659 if ( this.xhr ) { 24660 this.xhr.abort(); 24661 } 24662 24663 if ( !toHide.length && !toShow.length ) { 24664 $.error( "jQuery UI Tabs: Mismatching fragment identifier." ); 24665 } 24666 24667 if ( toShow.length ) { 24668 this.load( this.tabs.index( tab ), event ); 24669 } 24670 this._toggle( event, eventData ); 24671 }, 24672 24673 // handles show/hide for selecting tabs 24674 _toggle: function( event, eventData ) { 24675 var that = this, 24676 toShow = eventData.newPanel, 24677 toHide = eventData.oldPanel; 24678 24679 this.running = true; 24680 24681 function complete() { 24682 that.running = false; 24683 that._trigger( "activate", event, eventData ); 24684 } 24685 24686 function show() { 24687 eventData.newTab.closest( "li" ).addClass( "ui-tabs-active ui-state-active" ); 24688 24689 if ( toShow.length && that.options.show ) { 24690 that._show( toShow, that.options.show, complete ); 24691 } else { 24692 toShow.show(); 24693 complete(); 24694 } 24695 } 24696 24697 // start out by hiding, then showing, then completing 24698 if ( toHide.length && this.options.hide ) { 24699 this._hide( toHide, this.options.hide, function() { 24700 eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" ); 24701 show(); 24702 }); 24703 } else { 24704 eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" ); 24705 toHide.hide(); 24706 show(); 24707 } 24708 24709 toHide.attr({ 24710 "aria-expanded": "false", 24711 "aria-hidden": "true" 24712 }); 24713 eventData.oldTab.attr( "aria-selected", "false" ); 24714 // If we're switching tabs, remove the old tab from the tab order. 24715 // If we're opening from collapsed state, remove the previous tab from the tab order. 24716 // If we're collapsing, then keep the collapsing tab in the tab order. 24717 if ( toShow.length && toHide.length ) { 24718 eventData.oldTab.attr( "tabIndex", -1 ); 24719 } else if ( toShow.length ) { 24720 this.tabs.filter(function() { 24721 return $( this ).attr( "tabIndex" ) === 0; 24722 }) 24723 .attr( "tabIndex", -1 ); 24724 } 24725 24726 toShow.attr({ 24727 "aria-expanded": "true", 24728 "aria-hidden": "false" 24729 }); 24730 eventData.newTab.attr({ 24731 "aria-selected": "true", 24732 tabIndex: 0 24733 }); 24734 }, 24735 24736 _activate: function( index ) { 24737 var anchor, 24738 active = this._findActive( index ); 24739 24740 // trying to activate the already active panel 24741 if ( active[ 0 ] === this.active[ 0 ] ) { 24742 return; 24743 } 24744 24745 // trying to collapse, simulate a click on the current active header 24746 if ( !active.length ) { 24747 active = this.active; 24748 } 24749 24750 anchor = active.find( ".ui-tabs-anchor" )[ 0 ]; 24751 this._eventHandler({ 24752 target: anchor, 24753 currentTarget: anchor, 24754 preventDefault: $.noop 24755 }); 24756 }, 24757 24758 _findActive: function( index ) { 24759 return index === false ? $() : this.tabs.eq( index ); 24760 }, 24761 24762 _getIndex: function( index ) { 24763 // meta-function to give users option to provide a href string instead of a numerical index. 24764 if ( typeof index === "string" ) { 24765 index = this.anchors.index( this.anchors.filter( "[href$='" + index + "']" ) ); 24766 } 24767 24768 return index; 24769 }, 24770 24771 _destroy: function() { 24772 if ( this.xhr ) { 24773 this.xhr.abort(); 24774 } 24775 24776 this.element.removeClass( "ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible" ); 24777 24778 this.tablist 24779 .removeClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" ) 24780 .removeAttr( "role" ); 24781 24782 this.anchors 24783 .removeClass( "ui-tabs-anchor" ) 24784 .removeAttr( "role" ) 24785 .removeAttr( "tabIndex" ) 24786 .removeUniqueId(); 24787 24788 this.tabs.add( this.panels ).each(function() { 24789 if ( $.data( this, "ui-tabs-destroy" ) ) { 24790 $( this ).remove(); 24791 } else { 24792 $( this ) 24793 .removeClass( "ui-state-default ui-state-active ui-state-disabled " + 24794 "ui-corner-top ui-corner-bottom ui-widget-content ui-tabs-active ui-tabs-panel" ) 24795 .removeAttr( "tabIndex" ) 24796 .removeAttr( "aria-live" ) 24797 .removeAttr( "aria-busy" ) 24798 .removeAttr( "aria-selected" ) 24799 .removeAttr( "aria-labelledby" ) 24800 .removeAttr( "aria-hidden" ) 24801 .removeAttr( "aria-expanded" ) 24802 .removeAttr( "role" ); 24803 } 24804 }); 24805 24806 this.tabs.each(function() { 24807 var li = $( this ), 24808 prev = li.data( "ui-tabs-aria-controls" ); 24809 if ( prev ) { 24810 li 24811 .attr( "aria-controls", prev ) 24812 .removeData( "ui-tabs-aria-controls" ); 24813 } else { 24814 li.removeAttr( "aria-controls" ); 24815 } 24816 }); 24817 24818 this.panels.show(); 24819 24820 if ( this.options.heightStyle !== "content" ) { 24821 this.panels.css( "height", "" ); 24822 } 24823 }, 24824 24825 enable: function( index ) { 24826 var disabled = this.options.disabled; 24827 if ( disabled === false ) { 24828 return; 24829 } 24830 24831 if ( index === undefined ) { 24832 disabled = false; 24833 } else { 24834 index = this._getIndex( index ); 24835 if ( $.isArray( disabled ) ) { 24836 disabled = $.map( disabled, function( num ) { 24837 return num !== index ? num : null; 24838 }); 24839 } else { 24840 disabled = $.map( this.tabs, function( li, num ) { 24841 return num !== index ? num : null; 24842 }); 24843 } 24844 } 24845 this._setupDisabled( disabled ); 24846 }, 24847 24848 disable: function( index ) { 24849 var disabled = this.options.disabled; 24850 if ( disabled === true ) { 24851 return; 24852 } 24853 24854 if ( index === undefined ) { 24855 disabled = true; 24856 } else { 24857 index = this._getIndex( index ); 24858 if ( $.inArray( index, disabled ) !== -1 ) { 24859 return; 24860 } 24861 if ( $.isArray( disabled ) ) { 24862 disabled = $.merge( [ index ], disabled ).sort(); 24863 } else { 24864 disabled = [ index ]; 24865 } 24866 } 24867 this._setupDisabled( disabled ); 24868 }, 24869 24870 load: function( index, event ) { 24871 index = this._getIndex( index ); 24872 var that = this, 24873 tab = this.tabs.eq( index ), 24874 anchor = tab.find( ".ui-tabs-anchor" ), 24875 panel = this._getPanelForTab( tab ), 24876 eventData = { 24877 tab: tab, 24878 panel: panel 24879 }; 24880 24881 // not remote 24882 if ( isLocal( anchor[ 0 ] ) ) { 24883 return; 24884 } 24885 24886 this.xhr = $.ajax( this._ajaxSettings( anchor, event, eventData ) ); 24887 24888 // support: jQuery <1.8 24889 // jQuery <1.8 returns false if the request is canceled in beforeSend, 24890 // but as of 1.8, $.ajax() always returns a jqXHR object. 24891 if ( this.xhr && this.xhr.statusText !== "canceled" ) { 24892 tab.addClass( "ui-tabs-loading" ); 24893 panel.attr( "aria-busy", "true" ); 24894 24895 this.xhr 24896 .success(function( response ) { 24897 // support: jQuery <1.8 24898 // http://bugs.jquery.com/ticket/11778 24899 setTimeout(function() { 24900 panel.html( response ); 24901 that._trigger( "load", event, eventData ); 24902 }, 1 ); 24903 }) 24904 .complete(function( jqXHR, status ) { 24905 // support: jQuery <1.8 24906 // http://bugs.jquery.com/ticket/11778 24907 setTimeout(function() { 24908 if ( status === "abort" ) { 24909 that.panels.stop( false, true ); 24910 } 24911 24912 tab.removeClass( "ui-tabs-loading" ); 24913 panel.removeAttr( "aria-busy" ); 24914 24915 if ( jqXHR === that.xhr ) { 24916 delete that.xhr; 24917 } 24918 }, 1 ); 24919 }); 24920 } 24921 }, 24922 24923 _ajaxSettings: function( anchor, event, eventData ) { 24924 var that = this; 24925 return { 24926 url: anchor.attr( "href" ), 24927 beforeSend: function( jqXHR, settings ) { 24928 return that._trigger( "beforeLoad", event, 24929 $.extend( { jqXHR : jqXHR, ajaxSettings: settings }, eventData ) ); 24930 } 24931 }; 24932 }, 24933 24934 _getPanelForTab: function( tab ) { 24935 var id = $( tab ).attr( "aria-controls" ); 24936 return this.element.find( this._sanitizeSelector( "#" + id ) ); 24937 } 24938}); 24939 24940})( jQuery ); 24941 24942 24943 24944 24945/*! 24946 * jQuery UI Tooltip 1.10.3 24947 * http://jqueryui.com 24948 * 24949 * Copyright 2013 jQuery Foundation and other contributors 24950 * Released under the MIT license. 24951 * http://jquery.org/license 24952 * 24953 * http://api.jqueryui.com/tooltip/ 24954 * 24955 * Depends: 24956 * jquery.ui.core.js 24957 * jquery.ui.widget.js 24958 * jquery.ui.position.js 24959 */ 24960 24961(function( $ ) { 24962 24963var increments = 0; 24964 24965function addDescribedBy( elem, id ) { 24966 var describedby = (elem.attr( "aria-describedby" ) || "").split( /\s+/ ); 24967 describedby.push( id ); 24968 elem 24969 .data( "ui-tooltip-id", id ) 24970 .attr( "aria-describedby", $.trim( describedby.join( " " ) ) ); 24971} 24972 24973function removeDescribedBy( elem ) { 24974 var id = elem.data( "ui-tooltip-id" ), 24975 describedby = (elem.attr( "aria-describedby" ) || "").split( /\s+/ ), 24976 index = $.inArray( id, describedby ); 24977 if ( index !== -1 ) { 24978 describedby.splice( index, 1 ); 24979 } 24980 24981 elem.removeData( "ui-tooltip-id" ); 24982 describedby = $.trim( describedby.join( " " ) ); 24983 if ( describedby ) { 24984 elem.attr( "aria-describedby", describedby ); 24985 } else { 24986 elem.removeAttr( "aria-describedby" ); 24987 } 24988} 24989 24990$.widget( "ui.tooltip", { 24991 version: "1.10.3", 24992 options: { 24993 content: function() { 24994 // support: IE<9, Opera in jQuery <1.7 24995 // .text() can't accept undefined, so coerce to a string 24996 var title = $( this ).attr( "title" ) || ""; 24997 // Escape title, since we're going from an attribute to raw HTML 24998 return $( "<a>" ).text( title ).html(); 24999 }, 25000 hide: true, 25001 // Disabled elements have inconsistent behavior across browsers (#8661) 25002 items: "[title]:not([disabled])", 25003 position: { 25004 my: "left top+15", 25005 at: "left bottom", 25006 collision: "flipfit flip" 25007 }, 25008 show: true, 25009 tooltipClass: null, 25010 track: false, 25011 25012 // callbacks 25013 close: null, 25014 open: null 25015 }, 25016 25017 _create: function() { 25018 this._on({ 25019 mouseover: "open", 25020 focusin: "open" 25021 }); 25022 25023 // IDs of generated tooltips, needed for destroy 25024 this.tooltips = {}; 25025 // IDs of parent tooltips where we removed the title attribute 25026 this.parents = {}; 25027 25028 if ( this.options.disabled ) { 25029 this._disable(); 25030 } 25031 }, 25032 25033 _setOption: function( key, value ) { 25034 var that = this; 25035 25036 if ( key === "disabled" ) { 25037 this[ value ? "_disable" : "_enable" ](); 25038 this.options[ key ] = value; 25039 // disable element style changes 25040 return; 25041 } 25042 25043 this._super( key, value ); 25044 25045 if ( key === "content" ) { 25046 $.each( this.tooltips, function( id, element ) { 25047 that._updateContent( element ); 25048 }); 25049 } 25050 }, 25051 25052 _disable: function() { 25053 var that = this; 25054 25055 // close open tooltips 25056 $.each( this.tooltips, function( id, element ) { 25057 var event = $.Event( "blur" ); 25058 event.target = event.currentTarget = element[0]; 25059 that.close( event, true ); 25060 }); 25061 25062 // remove title attributes to prevent native tooltips 25063 this.element.find( this.options.items ).addBack().each(function() { 25064 var element = $( this ); 25065 if ( element.is( "[title]" ) ) { 25066 element 25067 .data( "ui-tooltip-title", element.attr( "title" ) ) 25068 .attr( "title", "" ); 25069 } 25070 }); 25071 }, 25072 25073 _enable: function() { 25074 // restore title attributes 25075 this.element.find( this.options.items ).addBack().each(function() { 25076 var element = $( this ); 25077 if ( element.data( "ui-tooltip-title" ) ) { 25078 element.attr( "title", element.data( "ui-tooltip-title" ) ); 25079 } 25080 }); 25081 }, 25082 25083 open: function( event ) { 25084 var that = this, 25085 target = $( event ? event.target : this.element ) 25086 // we need closest here due to mouseover bubbling, 25087 // but always pointing at the same event target 25088 .closest( this.options.items ); 25089 25090 // No element to show a tooltip for or the tooltip is already open 25091 if ( !target.length || target.data( "ui-tooltip-id" ) ) { 25092 return; 25093 } 25094 25095 if ( target.attr( "title" ) ) { 25096 target.data( "ui-tooltip-title", target.attr( "title" ) ); 25097 } 25098 25099 target.data( "ui-tooltip-open", true ); 25100 25101 // kill parent tooltips, custom or native, for hover 25102 if ( event && event.type === "mouseover" ) { 25103 target.parents().each(function() { 25104 var parent = $( this ), 25105 blurEvent; 25106 if ( parent.data( "ui-tooltip-open" ) ) { 25107 blurEvent = $.Event( "blur" ); 25108 blurEvent.target = blurEvent.currentTarget = this; 25109 that.close( blurEvent, true ); 25110 } 25111 if ( parent.attr( "title" ) ) { 25112 parent.uniqueId(); 25113 that.parents[ this.id ] = { 25114 element: this, 25115 title: parent.attr( "title" ) 25116 }; 25117 parent.attr( "title", "" ); 25118 } 25119 }); 25120 } 25121 25122 this._updateContent( target, event ); 25123 }, 25124 25125 _updateContent: function( target, event ) { 25126 var content, 25127 contentOption = this.options.content, 25128 that = this, 25129 eventType = event ? event.type : null; 25130 25131 if ( typeof contentOption === "string" ) { 25132 return this._open( event, target, contentOption ); 25133 } 25134 25135 content = contentOption.call( target[0], function( response ) { 25136 // ignore async response if tooltip was closed already 25137 if ( !target.data( "ui-tooltip-open" ) ) { 25138 return; 25139 } 25140 // IE may instantly serve a cached response for ajax requests 25141 // delay this call to _open so the other call to _open runs first 25142 that._delay(function() { 25143 // jQuery creates a special event for focusin when it doesn't 25144 // exist natively. To improve performance, the native event 25145 // object is reused and the type is changed. Therefore, we can't 25146 // rely on the type being correct after the event finished 25147 // bubbling, so we set it back to the previous value. (#8740) 25148 if ( event ) { 25149 event.type = eventType; 25150 } 25151 this._open( event, target, response ); 25152 }); 25153 }); 25154 if ( content ) { 25155 this._open( event, target, content ); 25156 } 25157 }, 25158 25159 _open: function( event, target, content ) { 25160 var tooltip, events, delayedShow, 25161 positionOption = $.extend( {}, this.options.position ); 25162 25163 if ( !content ) { 25164 return; 25165 } 25166 25167 // Content can be updated multiple times. If the tooltip already 25168 // exists, then just update the content and bail. 25169 tooltip = this._find( target ); 25170 if ( tooltip.length ) { 25171 tooltip.find( ".ui-tooltip-content" ).html( content ); 25172 return; 25173 } 25174 25175 // if we have a title, clear it to prevent the native tooltip 25176 // we have to check first to avoid defining a title if none exists 25177 // (we don't want to cause an element to start matching [title]) 25178 // 25179 // We use removeAttr only for key events, to allow IE to export the correct 25180 // accessible attributes. For mouse events, set to empty string to avoid 25181 // native tooltip showing up (happens only when removing inside mouseover). 25182 if ( target.is( "[title]" ) ) { 25183 if ( event && event.type === "mouseover" ) { 25184 target.attr( "title", "" ); 25185 } else {
25186 target.removeAttr( "title" ); 25187 } 25188 } 25189 25190 tooltip = this._tooltip( target ); 25191 addDescribedBy( target, tooltip.attr( "id" ) ); 25192 tooltip.find( ".ui-tooltip-content" ).html( content ); 25193 25194 function position( event ) { 25195 positionOption.of = event; 25196 if ( tooltip.is( ":hidden" ) ) { 25197 return; 25198 } 25199 tooltip.position( positionOption ); 25200 } 25201 if ( this.options.track && event && /^mouse/.test( event.type ) ) { 25202 this._on( this.document, { 25203 mousemove: position 25204 }); 25205 // trigger once to override element-relative positioning 25206 position( event ); 25207 } else { 25208 tooltip.position( $.extend({ 25209 of: target 25210 }, this.options.position ) ); 25211 } 25212 25213 tooltip.hide(); 25214 25215 this._show( tooltip, this.options.show ); 25216 // Handle tracking tooltips that are shown with a delay (#8644). As soon 25217 // as the tooltip is visible, position the tooltip using the most recent 25218 // event. 25219 if ( this.options.show && this.options.show.delay ) { 25220 delayedShow = this.delayedShow = setInterval(function() { 25221 if ( tooltip.is( ":visible" ) ) { 25222 position( positionOption.of ); 25223 clearInterval( delayedShow ); 25224 } 25225 }, $.fx.interval ); 25226 } 25227 25228 this._trigger( "open", event, { tooltip: tooltip } ); 25229 25230 events = { 25231 keyup: function( event ) { 25232 if ( event.keyCode === $.ui.keyCode.ESCAPE ) { 25233 var fakeEvent = $.Event(event); 25234 fakeEvent.currentTarget = target[0]; 25235 this.close( fakeEvent, true ); 25236 } 25237 }, 25238 remove: function() { 25239 this._removeTooltip( tooltip ); 25240 } 25241 }; 25242 if ( !event || event.type === "mouseover" ) { 25243 events.mouseleave = "close"; 25244 } 25245 if ( !event || event.type === "focusin" ) { 25246 events.focusout = "close"; 25247 } 25248 this._on( true, target, events ); 25249 }, 25250 25251 close: function( event ) { 25252 var that = this, 25253 target = $( event ? event.currentTarget : this.element ), 25254 tooltip = this._find( target ); 25255 25256 // disabling closes the tooltip, so we need to track when we're closing 25257 // to avoid an infinite loop in case the tooltip becomes disabled on close 25258 if ( this.closing ) { 25259 return; 25260 } 25261 25262 // Clear the interval for delayed tracking tooltips 25263 clearInterval( this.delayedShow ); 25264 25265 // only set title if we had one before (see comment in _open()) 25266 if ( target.data( "ui-tooltip-title" ) ) { 25267 target.attr( "title", target.data( "ui-tooltip-title" ) ); 25268 } 25269 25270 removeDescribedBy( target ); 25271 25272 tooltip.stop( true ); 25273 this._hide( tooltip, this.options.hide, function() { 25274 that._removeTooltip( $( this ) ); 25275 }); 25276 25277 target.removeData( "ui-tooltip-open" ); 25278 this._off( target, "mouseleave focusout keyup" ); 25279 // Remove 'remove' binding only on delegated targets 25280 if ( target[0] !== this.element[0] ) { 25281 this._off( target, "remove" ); 25282 } 25283 this._off( this.document, "mousemove" ); 25284 25285 if ( event && event.type === "mouseleave" ) { 25286 $.each( this.parents, function( id, parent ) { 25287 $( parent.element ).attr( "title", parent.title ); 25288 delete that.parents[ id ]; 25289 }); 25290 } 25291 25292 this.closing = true; 25293 this._trigger( "close", event, { tooltip: tooltip } ); 25294 this.closing = false; 25295 }, 25296 25297 _tooltip: function( element ) { 25298 var id = "ui-tooltip-" + increments++, 25299 tooltip = $( "<div>" ) 25300 .attr({ 25301 id: id, 25302 role: "tooltip" 25303 }) 25304 .addClass( "ui-tooltip ui-widget ui-corner-all ui-widget-content " + 25305 ( this.options.tooltipClass || "" ) ); 25306 $( "<div>" ) 25307 .addClass( "ui-tooltip-content" ) 25308 .appendTo( tooltip ); 25309 tooltip.appendTo( this.document[0].body ); 25310 this.tooltips[ id ] = element; 25311 return tooltip; 25312 }, 25313 25314 _find: function( target ) { 25315 var id = target.data( "ui-tooltip-id" ); 25316 return id ? $( "#" + id ) : $(); 25317 }, 25318 25319 _removeTooltip: function( tooltip ) { 25320 tooltip.remove(); 25321 delete this.tooltips[ tooltip.attr( "id" ) ]; 25322 }, 25323 25324 _destroy: function() { 25325 var that = this; 25326 25327 // close open tooltips 25328 $.each( this.tooltips, function( id, element ) { 25329 // Delegate to close method to handle common cleanup 25330 var event = $.Event( "blur" ); 25331 event.target = event.currentTarget = element[0]; 25332 that.close( event, true ); 25333 25334 // Remove immediately; destroying an open tooltip doesn't use the 25335 // hide animation 25336 $( "#" + id ).remove(); 25337 25338 // Restore the title 25339 if ( element.data( "ui-tooltip-title" ) ) { 25340 element.attr( "title", element.data( "ui-tooltip-title" ) ); 25341 element.removeData( "ui-tooltip-title" ); 25342 } 25343 }); 25344 } 25345}); 25346 25347}( jQuery ) ); 25348 25349 25350 25351 25352 25353 25354 25355 25356 25357 25358 25359 25360 25361 25362 25363 25364 25365 25366 25367 25368 25369 25370 25371 25372 25373 25374 25375 25376 25377 25378 25379 25380 25381 25382var $j = jQuery.noConflict(); 25383 25384/** 25385 * Copyright(c) The Allen Institute for Brain Science 2011 25386 * 25387 * 25388 * Tim Dolbeare 25389 */ 25390 25391/** 25392 * The root of the javascript libraries for working with the Allen Institute 25393 * warehouse API. 25394 * <p></p> 25395 * The namespace is divided into two main sections-- 25396 * <p></p> 25397 * DATA, which not surprisingly handles 25398 * low-level communication with the various services as well as some higher-level fetching and 25399 * persistence. 25400 * <p></p> 25401 * UI, which also not surprisingly deals with displaying data and capturing user input. It's 25402 * intended that the UI bits be as de-coupled from the data and its schema as practical. 25403 * <p></p> 25404 * 25405 * @namespace 25406 */ 25407 25408var AIBS = {}; 25409 25410 25411/** 25412 * The root of the Data javascript libraries for working with the Allen Institute 25413 * analysis services. 25414 * <p></p> 25415 * 25416 * @namespace 25417 */ 25418AIBS.Analysis = {}; 25419 25420/** 25421 * The root of the Data javascript libraries for working with the Allen Institute 25422 * warehouse data. 25423 * <p></p> 25424 * 25425 * @namespace 25426 */ 25427AIBS.DATA = {}; 25428 25429 25430/** 25431 * The root of the UI javascript libraries for working with the Allen Institute 25432 * warehouse data. 25433 * <p></p> 25434 * It's intended that the UI bits be as de-coupled from the data and its schema as practical. 25435 * This provokes some repeating patterns in the widgets; often a data map is supplied to a widget 25436 * constructor, defining what data goes where. 25437 * <p></p> 25438 * 25439 * @namespace 25440 */ 25441AIBS.UI = {}; 25442 25443 25444/** 25445 * A container for Components, which are defined as a set of classes required to manage both the 25446 * data and UI for a particular task. 25447 * <p></p> 25448 * 25449 * @namespace 25450 */ 25451AIBS.Components = {}; 25452 25453 25454/** 25455 * Any code that is coupled up with what we call the framework, and scoped to be usable 25456 * throughout an application. 25457 * 25458 * @namespace 25459 */ 25460AIBS.Application = {}; 25461 25462 25463function application(options) { 25464
25465 // default settings, can be overridden by options 25466 this.settings = { 25467 theme:'projection', 25468 urls:{ 25469 data_service:'/api/v2/data/', 25470 analysis_service:'/mouse/data/', 25471 mapping_service:'/api/v2/' 25472 }, 25473 experiment_list_key:'experiment_lists', 25474 default_experiment_list:'default', 25475 config_name: 'default', 25476 config_location:'/application.json', 25477 on_error:do_json_file_error 25478 }; 25479 25480 $j.extend(true, this.settings, options); 25481 25482 function inspect(prefix, obj) { 25483 var s = ""; 25484 for(key in obj) { 25485 if((typeof obj[key]) == "function") 25486 s = s; 25487 else if((typeof obj[key]) == "object" && obj[key] !== null & key != "element") 25488 s += "\n" + prefix + "key: " + key + inspect(prefix + " ", obj[key]); 25489 else 25490 s += "\n" + prefix + "key: " + key + ", value: " + obj[key]; 25491 } 25492 return(s); 25493 } 25494 25495 this.version = function() { 25496 return(_version); 25497 } 25498 25499 this.current_config_name = function() { 25500 return(_config.current_config); 25501 } 25502 25503 this.app_config = function() { 25504 return(_app_config.application); 25505 } 25506 25507 /** 25508 * 25509 * @param theme_name optional argument; it allows you to access info from any of the defined themes 25510 * @return {*} 25511 */ 25512 this.theme_config = function(theme_name) { 25513 var tname = theme_name == undefined ? this.settings.theme : theme_name; 25514 return(_app_config.themes[tname]); 25515 } 25516 25517 /** 25518 * 25519 * @param page_name name of page within the theme 25520 * @param theme_name optional argument; it allows you to access page info from any of the defined themes 25521 * @return {*} 25522 */ 25523 this.page_config = function(page_name, theme_name) { 25524 var tname = theme_name == undefined ? this.settings.theme : theme_name; 25525 return(_app_config.themes[tname].pages[page_name]); 25526 } 25527 25528 this.DAOs = function() { 25529 return(_the_daos); 25530 } 25531 25532 this.change_config = function(config_name) { 25533 do_change_config(config_name); 25534 } 25535 25536 this.set_user_data = function(key, value) { 25537 _user_data_store.set(key, value); 25538 } 25539 25540 this.get_user_data = function(key) { 25541 return(_user_data_store.get(key)); 25542 } 25543 25544 this.experiment_id_store = function() { 25545 return(_experiment_id_store); 25546 } 25547 25548 this.app_data = function() { 25549 return(_app_data); 25550 } 25551 25552 this.on_ready = options ? options.on_ready ? options.on_ready : null : null; 25553 25554 25555 25556 var _self = this; 25557 var _current_config_name = ""; 25558 var _current_experiment_list = ""; 25559 var _config = {}; 25560 var _app_config = {}; 25561 25562 var _title = "Application Title"; 25563 var _version = "Unknown"; 25564 var _app_data = {}; 25565 var _the_daos = {}; 25566 25567 var _user_data_store = null; 25568 var _experiment_id_store = null; 25569 25570 var _parse_filenames = true; // do this when parsing main application.json, but not for included .json files. 25571 var _include_files = []; 25572 25573 25574 // loads up the app configuration data from the main application.json file and any extra config files 25575 function load_config() { 25576 // note: jQuery.getJSON does not call error handlers, use jQuery.ajax instead. See http://stackoverflow.com/questions/5388934/jquery-handle-errors-in-getjson 25577 $j.ajax({ 25578 url: _self.settings.config_location, 25579 dataType: 'json', 25580 dataFilter: do_parse_comments, 25581 success: function(data) { 25582 if (_include_files.length > 0) { 25583 // _include_files is populated in do_parse_comments 25584 data = do_include_configs(data); 25585 } 25586 _config = data; 25587 _version = data.version; 25588 _app_data = data.data; 25589 _current_experiment_list = data.current_experiment_list; // not used by app code, it's for testing 25590 do_change_config(_config.current_config); 25591 if(_self.on_ready) 25592 _self.on_ready(_self); 25593 }, 25594 error: function(data) { 25595 _self.settings.on_error("JSON file access error: " + data.statusText); 25596 } 25597 }); 25598 } 25599 25600 /**
25601 * Reentrant function that reads in and merges local JSON config files into the main JSON configuration. 25602 * These additional config files are specified within the main config file (application.json) by the use 25603 * of special comment lines: "// include=/the.name.of.the.file.json" 25604 * Will continue until it iterates over the entire _include_files array. 25605 * 25606 * @param {hash} json_data 25607 * @return {hash} JSON data, augmented with elements from the included files 25608 */ 25609 function do_include_configs(json_data) { 25610 if (_include_files.length > 0) { 25611 var file_path = _include_files[0]; 25612 _include_files.splice(0, 1); // remove the file path from the array 25613 load_additional_config(json_data, file_path); 25614 } 25615 25616 return json_data; 25617 } 25618 25619 /** 25620 * Synchronous merge of extra config files (JSON format) into current JSON config. 25621 * Recursively calls do_include_configs() until there are no more config files to load and merge. 25622 * 25623 * @param {hash} json_data 25624 * @param {string} file_path 25625 * @returns {hash} JSON data merged with extra config 25626 */ 25627 function load_additional_config(json_data, file_path) { 25628 _parse_filenames = false; // avoid loading config files within config files 25629 25630 $j.ajax({ 25631 async: false, 25632 url: file_path, 25633 cache: true, 25634 dataType: 'json', 25635 dataFilter: do_parse_comments, 25636 success: function(data) { 25637 $j.extend(true, json_data, data); 25638 do_include_configs(json_data); 25639 }, 25640 error: function(data) { 25641 _self.settings.on_error("JSON file access error: " + data.statusText); 25642 } 25643 }); 25644 25645 return json_data; 25646 } 25647 25648 /** 25649 * Go thru the raw results of the Ajax call, parsing all single line javascipt comments. 25650 * Lines with '// include=' are parsed for JSON filenames, which are saved for later loading in _include_files. 25651 * After the initial pass to collect filenames, all comment content is removed to ensure the file meet JSON requirements. 25652 * 25653 * @param {string} data raw data from ajax call 25654 * @param {string} type data type, probably 'json' 25655 * @return {string} data stripped of single-line comments. This makes the data JSON-compatible 25656 */ 25657 function do_parse_comments(data, type) { 25658 var re, 25659 match, 25660 index; 25661 25662 if (_parse_filenames) { 25663 re = /\/\/\s*include=(.*\.json)/gm; 25664 match = data.match(re); 25665 if (match) { 25666 for (var i = 0, len = match.length; i < len; ++i) { 25667 // example match data: "// include=/foo.bar.json", only want "/foo.bar.json" 25668 index = match[i].indexOf('=') + 1; 25669 _include_files.push(match[i].slice(index)); 25670 } 25671 } 25672 } 25673 25674 // remove all line content from '//' to the end of line 25675 return data.replace(/^\/\/.*|[ ]\/\/.*/gm, ''); 25676 } 25677 25678 function do_change_config(config_name) { 25679 _app_config = _config.configs[config_name]; 25680 _title = _app_config.application.title; 25681 _current_config_name = config_name; 25682 25683 build_daos(); 25684 setup_user_data(); 25685 } 25686 25687 function build_daos() { 25688 25689 var opts = { 25690 product:_app_config.themes[_self.settings.theme].application.data_sets.join(','), 25691 timeout:_app_config.themes[_self.settings.theme].application.timeout 25692 }; 25693 25694 // for convenience, an attempt is made to load these 3 daos. 25695 // For additional/alternative dao objects, configure the dao-name/class-name 25696 // in application.json app.themes.application.daos{} 25697 var daos = { 25698 data: "AIBS.DATA.ISH_DAO", 25699 analysis: "AIBS.Analysis.DAO", 25700 mapping: "AIBS.DATA.Mapping_DAO" 25701 }; 25702 var dao_options = _app_config.themes[_self.settings.theme].application.daos ? _app_config.themes[_self.settings.theme].application.daos : {} 25703 $j.extend(true, daos, dao_options); 25704 25705 // If the configured dao class exists, it will be loaded. 25706 // NOTE that it is assumed that for each named dao (such as data, analysis, mapping, etc.) 25707 // there is a corresponding service URL. For example, if a dao n
25707amed 'blargh' is configured 25708 // there should be a blargh_service entry in the options.urls hash used to instantiate this app object. 25709 for(var dao_name in daos) { 25710 if(eval(daos[dao_name])) 25711 _the_daos[dao_name] = eval("new " + daos[dao_name] + "(_self.settings.urls." + dao_name + "_service, opts);"); 25712 else 25713 if(window.console) 25714 console.log("The configured DAO '" + dao_name + "' is not available and has not been loaded.") 25715 } 25716 } 25717 25718 function setup_user_data() { 25719 _user_data_store = new user_data(); 25720 25721 // make sure the key 'experiment_lists' is populated with at least 'default' 25722 var exp_list = _user_data_store.get(_self.settings.experiment_list_key); 25723 if(!exp_list || !exp_list['default']) 25724 _user_data_store.set(_self.settings.experiment_list_key, {'default':{}}); 25725 25726 _experiment_id_store = new experiment_list(_user_data_store, _self.settings.experiment_list_key, _current_experiment_list); 25727 25728 } 25729 25730 // this is primary for the use of the developer, to flag application.json formatting errors 25731 function do_json_file_error(error) { 25732 alert(error); 25733 } 25734 25735 function init() { 25736 25737 load_config(); 25738 25739 25740 } 25741 25742 init(); 25743 25744} 25745; 25746/** 25747 * @class 25748 * 25749 * The AIBS.Application.controller is intended to be almost an abstract class; that is it doesn't do 25750 * very much on its own, but can be useful when used with overrides or delegates. This class might be 25751 * used as a page controller, or as a controller for a widget or component. 25752 * <p/> 25753 * The controller's job is to glue together the various components under its control. When 25754 * one component raises an event that might change the page or component state, and a different 25755 * component should respond, the controller will mediate. 25756 * <p/> 25757 * One of the optional settings provided to the controller at construction is a hash of widget_config hashes. 25758 * It is expected that a derived or composing class will provide a 'widget_properties' delegate 25759 * function that will supplement each widget_config with additional runtime properties, such as 25760 * resource locations or callback/delegate functions. 25761 * <p/> 25762 * A controller is usually used in conjunction with a view class. Another optional setting for this 25763 * controller class is a 'view_settings' hash. If provided, the view settings should include 25764 * a view_class field that is the name of a view class that has a prototype that 25765 * is at least a subset of that defined by <a href="AIBS.Application.view.html">AIBS.Application.view</a>. 25766 * If provided, the view will be instantiated and the widget_configs passed in to its constructor. The 25767 * view_settings hash can of course contain anything else necessary for the view, and will be passed 25768 * to the view's constructor. 25769 * <p/> 25770 * When used in a page controller the composing class will also probably make use of the <a href="AIBS.Application.page.html"> 25771 * AIBS.Application.page</a> class, which encapsulates page state. A View class will probably be defined and 25772 * configured at startup, and a widget_properties_delegate will probably be provided to set event 25773 * handler functions to perform the mediation between the Page object and other controlled components. 25774 * <p/> 25775 * 25776 * @param container The jQuery object representing the HTML container within which all 25777 * of the controlled UI will live. 25778 * <p/> 25779 * @param settings <pre>Settings include: 25780 * 25781 * view_settings: Optional. A hash containing settings for a view to be associated with this controller. 25782 * If the view_settings hash includes a "view_class" member, the given class will 25783 * be instantiated with two arguments; the first argument will be the container, 25784 * the second will be a hash containing widget_configs (see below) and the contents 25785 * of view_settings. 25786 * 25787 * widget_configs: A hash of named widget_config objects. The widget_config object should look like this: 25788 * 25789 * "widget_name":{ 25790 * "class_name":"some_class_name", // Required. The name of the widget class 25791 * "group":"1", // Required. Could be used by the View to place 25792 * // widgets in different areas of the layout. 25793 * "order":10, // Required. Relative to the group.
25794 * "container_class":"some_css_class_name" // Required (can be blank). A css class that will 25795 * // be added to the container constructed for this widget. 25796 * "settings":{}, // Required. Any other settings the widget needs. 25797 * "build_delegate":some_function(container, settings, view) 25798 * // Optional. If provided this delegate will be called to 25799 * // instantiate the widget. It must return 25800 * // a reference to the widget instance. 25801 * } 25802 * 25803 * delegates: A hash containing delegate references. Currently the only delegate 25804 * defined is widget_properties_delegate. If provided, this delegate will be 25805 * called with a single argument, which is a hash of named widgets_config 25806 * objects. The delegate will be called prior to the View being constructed 25807 * to allow the delegate to add any additional runtime configuration to each 25808 * widget. 25809 * </pre> 25810 * 25811 */ 25812 25813AIBS.Application.controller = (function () { 25814 25815 return( 25816 function (container, settings) { 25817 25818 this.container = $j(container); 25819 this.settings = { 25820 view_settings:{ 25821 view_class:null 25822 }, 25823 widget_config:{}, 25824 delegates:{ 25825 widget_properties_delegate:null 25826 } 25827 }; 25828 $j.extend(true, this.settings, settings); 25829 25830 25831 /** 25832 * 25833 * @return {Object} A reference to the instance of a AIBS.Application.view-derived object 25834 * managed by this controller. 25835 */ 25836 this.get_view = function () { 25837 25838 return(_the_view); 25839 }; 25840 25841 25842 /** 25843 * @private 25844 * @scope AIBS.Application.controller 25845 */ 25846 var _self = this; 25847 var _the_view = null; 25848 25849 25850 // this routine first calls 'set_widget_properties' to, you know, 25851 // set the properties of the UI widgets. The idea is that the 25852 // View is strictly layout and user interface. Any connection 25853 // between the data, the configuration, or between UI elements 25854 // happens here in the controller. 25855 function build_view() { 25856 25857 set_widget_properties(_self.settings.widget_config); 25858 25859 if (!_self.settings.view_settings.view_class) 25860 return; 25861 25862 var options = { 25863 widget_config:{} 25864 }; 25865 $j.extend(true, options, _self.settings.view_settings); 25866 $j.extend(true, options.widget_config, _self.settings.widget_config); 25867 25868 _the_view = new (eval(_self.settings.view_settings.view_class))(_self.container, options); 25869 } 25870 25871 25872 // Some of the widgets need to have run-time configuration data set, like 25873 // resource locations, event handlers, etc. Or there may be config decisions 25874 // that will be based on this particular data set. This routine sets these 25875 // config options for each of the widgets prior to handing them off to the 25876 // view, where they are constructed. 25877 function set_widget_properties(widgets) { 25878 25879 if (_self.settings.delegates.widget_properties_delegate) 25880 _self.settings.delegates.widget_properties_delegate(widgets); 25881 } 25882 25883 function init() { 25884 25885 build_view(); 25886 } 25887 25888 init(); 25889 }); 25890})(); 25891 25892/* 25893 * A wrapper around the user_data object to simplify getting/setting experiment lists. 25894 * This assumes a data store with methods to get & set key/value pairs. It's also assumed 25895 * that the data store contains an object that looks like this: 25896 storage = { 25897 <root_name>:{ 25898 experiment_lists:{ 25899 Default:{} 25900 } 25901 } 25902 }; 25903 */ 25904 25905function experiment_list(store, root_name, initial_list_name) { 25906 25907// public properties 25908 this.data_store = store; 25909 this.root_name = root_name; 25910 this.list_name = initial_list_name; 25911 25912// events 25913 this.on_add = null; 25914 this.on_remove = null; 25915 this.on_clear = null; 25916 25917// public methods 25918 this.exists = function(id) { 25919 return(check_exists(this.list_name, id)); 25920 } 25921 25922 // note that if the current list_name does not exist, it will be created 25923 // when something is added to it with this method. 25924 this.add = function(id) { 25925 var ret = add_to_list(this.list_name, id); 25926 if(this.on_add) 25927 this.on_add(this, id); 25928 25929 return(ret); 25930 } 25931 25932 this.remove = function(id) { 25933 var ret = remove_from_list(this.list_name, id); 25934 if(this.on_remove) 25935 this.on_remove(this, id); 25936 25937 return(ret); 25938 } 25939 25940 this.clear = function() { 25941 var ret = clear_list(this.list_name); 25942 if(this.on_clear) 25943 this.on_clear(this, this.list_name); 25944 25945 return(ret); 25946 } 25947 25948 this.get_list = function() { 25949 var root = this.data_store.get(this.root_name); 25950 if(!root || root.length == 0 || !root || root.length == 0) 25951 return(null); 25952 25953 return(root[this.list_name]); 25954 } 25955 25956 this.set_list = function(list_name) { 25957 get_or_create_list(list_name); 25958 this.list_name = list_name; 25959 } 25960 25961 this.get_list_names = function() { 25962 var root = this.data_store.get(this.root_name); 25963 var lists = root; 25964 25965 var names = []; 25966 for(key in lists) 25967 names.push(key); 25968 25969 return(names); 25970 } 25971 25972 this.count = function() { 25973 var root = this.data_store.get(this.root_name); 25974 var list = root[this.list_name]; 25975 var i = 0; 25976 for(key in list) 25977 i++; 25978 25979 return(i); 25980 } 25981 25982 25983// private properties 25984 var _self = this; 25985 25986 25987// private methods 25988 function check_exists(list_name, id) { 25989 var root = _self.data_store.get(_self.root_name); 25990 if(!root || root.length == 0 || !root || root.length == 0 || !root[list_name]) 25991 return(false); 25992 25993 return(root[list_name][id] != undefined); 25994 } 25995 25996 function add_to_list(list_name, id) { 25997 var root = _self.data_store.get(_self.root_name); 25998 if(!root || root.length == 0 || !root || root.length == 0) 25999 return(false); 26000 26001 if(!root[list_name]) 26002 root[list_name] = {}; 26003 26004 root[list_name][id] = true; 26005 _self.data_store.set(_self.root_name, root); 26006 return(true); 26007 } 26008 26009 function get_or_create_list(list_name) { 26010 var list = root[list_name]; 26011 if(!list) 26012 root[list_name] = {}; 26013 26014 return(list); 26015 } 26016 26017 function remove_from_list(list_name, id) { 26018 var root = _self.data_store.get(_self.root_name); 26019 if(!root || root.length == 0 || !root || root.length == 0 || !root[list_name]) 26020 return(false); 26021 26022 delete root[list_name][id]; 26023 _self.data_store.set(_self.root_name, root); 26024 return(true); 26025 } 26026 26027 function clear_list(list_name) { 26028 var root = _self.data_store.get(_self.root_name); 26029 if(!root || root.length == 0 || !root || root.length == 0 || !root[list_name]) 26030 return(false); 26031 26032 delete root[list_name]; 26033 root[list_name] = {}; 26034 _self.data_store.set(_self.root_name, root); 26035 26036 return(true); 26037 } 26038 26039} 26040; 26041 26042 26043 26044function app_page(container, name, app, options) { 26045 26046 26047 this.container = container; 26048 this.page_name = name; 26049 this.application = app; 26050 26051 this.settings = { 26052 on_page_change:null 26053 }; 26054 26055 $j.extend(true, this.settings, options); 26056 26057 26058 this.set_busy_container = function(new_container) { 26059 position_busy_indicator(new_container); 26060 } 26061 26062 this.show_busy = function(busy) { 26063 do_show_busy(busy); 26064 } 26065 26066 this.set_state = function(state) { 26067 do_push_state(state); 26068 } 26069 26070 this.get_state = function() { 26071 return(_state); 26072 } 26073 26074 this.get_config = function() { 26075 return(_config); 26076 } 26077 26078 26079 var _self = this; 26080 var _events_setup = false;
26081 var _config = {}; 26082 var _state = {}; 26083 var _busy_indicator = {}; 26084 var _history = null; 26085 var _changing_state = false; 26086 26087 26088 function position_busy_indicator(container) { 26089 26090 if(!_busy_indicator.panel) 26091 setup_busy_indicator(container); 26092 26093 var pos = container.offset(); 26094 var container_width = container.outerWidth(); 26095 var container_height = container.outerHeight(); 26096 26097 _busy_indicator.panel.css({ 26098 position:'absolute', 26099 background: "url('/assets/aiclient/ajax-loader.gif') no-repeat center center", 26100 top:pos.top, 26101 left:pos.left, 26102 'background-color':'#FFF', 26103 opacity:0.7, 26104 width:container_width, 26105 height:container_height, 26106 display:'none' 26107 }); 26108/* 26109 _busy_indicator.indicator.css({ 26110 position:'absolute', 26111 top:pos.top + (container_height/2), 26112 left:pos.left + (container_width/2), 26113 display:'none' 26114 }); 26115*/ 26116 } 26117 26118 function setup_busy_indicator(container) { 26119 26120 _self.container = container; 26121 26122 var html = "<div id='busy_panel'></div>"; 26123 _busy_indicator.panel = $j(html); 26124 _busy_indicator.panel.appendTo(container); 26125/* 26126 var indicator_html = "<img id='busy_indicator' src='/assets/ajax-loader.gif'/>"; 26127 _busy_indicator.indicator = $j(indicator_html); 26128 _busy_indicator.indicator.appendTo(container); 26129*/ 26130 //position_busy_indicator(container); 26131 setup_events(); 26132 } 26133 26134 function do_show_busy(busy) { 26135 26136 if(!_busy_indicator.panel) { 26137 setup_busy_indicator(_self.container); 26138 } 26139 26140 if(busy) { 26141 26142 position_busy_indicator(_self.container); 26143 _busy_indicator.panel.show(); 26144// _busy_indicator.indicator.show(); 26145 } 26146 else { 26147 26148 _busy_indicator.panel.hide(); 26149// _busy_indicator.indicator.hide(); 26150 } 26151 } 26152 26153 function load_state() { 26154 26155 var state = {}; 26156 var url_state = split_url(window.location.href); 26157 26158 if(!$j.isEmptyObject(url_state)) { 26159 for(var key in url_state) { 26160 if(!key || key === "") continue; 26161 state[key] = decode_term(url_state[key]); 26162 } 26163 26164 // convert the true or false strings into true or false values 26165 if(state.exact_match) 26166 state.exact_match = state.exact_match == 'true'; 26167 if(state.no_paging) 26168 state.no_paging = state.no_paging == 'true'; 26169 } 26170 26171 return(state); 26172 } 26173 26174 function setup_history() { 26175 _history = window.history; 26176 if (!_history.enabled) 26177 return; 26178 26179 // setup the baseline url so we can compare changes and raise an event is necessary 26180 _history.Adapter.bind(window, 'statechange', check_state_change); 26181 } 26182 26183 function check_state_change() { 26184 if(_changing_state) { 26185 _changing_state = false; 26186 return; 26187 } 26188 26189 _state = load_state(); 26190 if(_self.on_page_change) 26191 _self.on_page_change(_state); 26192 } 26193 26194 function inspect(prefix, obj) { 26195 var s = ""; 26196 for(key in obj) { 26197 if((typeof obj[key]) == "function") 26198 s = s; 26199 else if((typeof obj[key]) == "object" && obj[key] !== null & key != "element") 26200 s += "\n" + prefix + "key: " + key + inspect(prefix + " ", obj[key]); 26201 else 26202 s += "\n" + prefix + "key: " + key + ", value: " + obj[key]; 26203 } 26204 return(s); 26205 } 26206 26207 function do_push_state(state) { 26208 _state = state; 26209 var state_string = "?"; 26210 var count = 0; 26211 var val = ""; 26212 26213 for(var key in state) { 26214 if(count++) 26215 state_string += "&"; 26216 26217 if( (typeof state[key]) == "object" && ( state[key] instanceof Array ) && state[key] !== null ){ 26218 state_string += encode_array(state[key], key); 26219 } 26220 else if((typeof state[key]) == "object" && state[key] !== null) { 26221 state_string += encode_object(state[key]); 26222 } else { 26223 val = encode_term(new String(state[key])); 26224 state_string += key + "=" + val; 26225 } 26226 26227 } 26228 _changing_state = true; 26229 History.pushState({state:1}, _config.title, state_string); 26230 } 26231 26232 function encode_object(obj) { 26233 var val = '' 26234 for(var key in obj) { 26235 if(val.length) 26236 val += '&'; 26237 val += key + '=' + new String(obj[key]); 26238 } 26239 return(encodeURIComponent(val)); 26240 } 26241 26242 function encode_array(arr,attr_key) { 26243 var target_str = ''; 26244 for(var i=0; i < arr.length; i++) { 26245 if(i>0) 26246 target_str += ','; 26247 else 26248 target_str += attr_key + '='; 26249 target_str += arr[i]; 26250 } 26251 return target_str; 26252 } 26253 26254 function split_url(url) { 26255 var param_hash = {}; 26256 26257 url = url.replace('?&', '?'); 26258 if(!url || !url.split("?") || url.split("?").length < 2) 26259 return({}); 26260 26261 // Edge case: if this is an IE url that is carrying history info on the url, it will look like http://foobar/show?arg1=moo#show?arg1=moo2 etc. 26262 // For this kind of url, you want to collect the original args, and overwrite with newer history arg values. 26263 var numQ = url.split("?").length - 1; 26264 if (numQ > 1) { 26265 // take into account edge case, where original url had no args, but new url has args: url like http://foobar/show?#show?arg1=moo2 26266 var oldAndNew = url.split("#"); 26267 var paramsOld = oldAndNew[0].split("?"); 26268 if (paramsOld.length == 2) 26269 paramsOld = paramsOld[1].split("&"); 26270 else 26271 paramsOld = []; 26272 26273 var paramsNew = oldAndNew[1].split("?"); 26274 if (paramsNew.length == 2) 26275 paramsNew = paramsNew[1].split("&"); 26276 else 26277 paramsNew = []; 26278 26279 var terms; 26280 for (var i = 0; i < paramsOld.length; i++) { 26281 terms = paramsOld[i].split("="); 26282 param_hash[terms[0]] = terms[1]; 26283 } 26284 // overwrite existing term values, add new terms and values 26285 for (i = 0; i < paramsNew.length; i++) { 26286 terms = paramsNew[i].split("="); 26287 param_hash[terms[0]] = terms[1]; 26288 } 26289 } 26290 else { 26291 // normal url processing 26292 var params = url.split("?")[1].split("&"); 26293 26294 for(var i = 0; i < params.length; i++) { 26295 var terms = params[i].split("="); 26296 param_hash[terms[0]] = terms[1]; 26297 } 26298 } 26299 26300 return(param_hash); 26301 } 26302 26303 26304 function decode_term(term) { 26305 26306 // decode our private encoding for newlines-- \n == $ 26307 var new_term = term.replace(/\$/g,'\n'); 26308 // url decoding 26309 new_term = decodeURIComponent(new_term); 26310 26311 return(new_term); 26312 } 26313 26314 function encode_each_term(term, delimiter) { 26315 var terms = term.split(delimiter); 26316 26317 if(terms.length == 1) 26318 return(encodeURIComponent(trim(term))); 26319 26320 var term_string = ''; 26321 for(var i = 0; i < terms.length; i++) 26322 term_string += trim(terms[i]) + '$'; 26323 26324 var new_term = encodeURIComponent(term_string); 26325 26326 return(new_term); 26327 } 26328 26329 function encode_term(term) { 26330 return(encode_each_term(encode_each_term(term, '\n'), '\t')); 26331 } 26332 26333 function trim(s) 26334 { 26335 var l = 0; 26336 var r = s.length - 1; 26337
26338 while(l < s.length && s[l] == ' ') l++; 26339 while(r > l && s[r] == ' ') r -= 1; 26340 26341 return s.substring(l, r+1); 26342 } 26343 26344 function setup_events() { 26345 if(_events_setup) 26346 return; 26347 26348 $j(window).bind('resize', function(){position_busy_indicator(_self.container)}); 26349 _events_setup = true; 26350 } 26351 26352 function init() { 26353 _config = _self.application.page_config(_self.page_name); 26354 setup_history(); 26355 //setup_busy_indicator(_self.container); 26356 _state = load_state(); 26357 //setup_events(); 26358 } 26359 26360 init(); 26361} 26362; 26363 26364/* 26365 * Peristent key/value storage. If initialized with default values the defaults 26366 * will be returned when the data store does not have the given key. 26367 */ 26368 26369 26370function user_data(defaults) { 26371 26372 this.defaults = defaults ? defaults : {}; 26373 26374 this.set = function(name, value) { 26375 // Backward-compatibility priority given to toJSON use, if defined. Otherwise, use more generally-acceptable stringify 26376 if (Object.toJSON) // Prototype javascript lib, see http://jira.corp.alleninstitute.org/browse/IT-7265 26377 set_data(name, Object.toJSON(value)); 26378 else 26379 set_data(name, JSON.stringify(value)); 26380 } 26381 26382 this.get = function(name) { 26383 26384 var data = get_data(name); 26385 if(data && data.length > 0 && data != '""') { 26386 var value = eval("("+data+")"); 26387 if(value == 'false' || value == 'true') 26388 value = (value == 'true'); 26389 26390 return(value); 26391 } 26392 else 26393 data = this.defaults[name]; 26394 26395 return(data); 26396 } 26397 26398 function set_data(name, value) { 26399 localStorage.setItem(name, encodeURI(value)); 26400 } 26401 26402 function get_data(name) { 26403 26404 var data = localStorage.getItem(name); 26405 if(data) 26406 return(decodeURI(data)); 26407 26408 return(null); 26409 } 26410} 26411 26412; 26413 26414 26415AIBS.Application.__view_count__ = AIBS.Application.__view_count__ || 0; 26416 26417/** 26418 * @class 26419 * 26420 * The AIBS.Application.view is intended primarily to be used as a base or composition class for component 26421 * or page views. The primary work performed by this view is to create a container for and instantiate each 26422 * of the list of components given in the settings.widget_configs param. It is expected that the derived or 26423 * composing class will provide a layout delegate that will construct the HTML scaffold in which the widgets 26424 * will live. 26425 * <p/> 26426 * See also <a href="AIBS.Application.controller.html">AIBS.Application.controller</a> for a Controller 26427 * class designed to work with this View class. 26428 * 26429 * 26430 * @param container The jQuery object representing the HTML container within which all 26431 * of the UI will live. 26432 * <p/> 26433 * @param settings <pre>Settings include: 26434 * 26435 * widget_configs: A hash of named widget_config objects. The widget_config object should look like this: 26436 * 26437 * "widget_name":{ 26438 * "class_name":"some_class_name", // Required. The name of the widget class 26439 * "group":"1", // Required. Could be used by the View to place 26440 * // widgets in different areas of the layout. 26441 * "order":10, // Required. Relative to the group. 26442 * "settings":{ 26443 * on_ready:null 26444 * }, // Required. The settings hash should include any settings a build 26445 * // delegate would need to construct the widget. 26446 * // One requied key in the settings hash is an on_ready callback. 26447 * // The on_ready callback must be invoked by the widget builder 26448 * // when the widget is ready for programatic and/or user interaction. 26449 * // The callback should be invoked with a reference to the widget itself, 26450 * // as in _self.settings.on_ready(_self); 26451 * 26452 * "build_delegate":some_builder(container_id, widget_settings, view_reference) 26453 * // Optional. If provided, the widget's build_delegate will be called 26454 * // to instantiate the widget. The delegate should return a 26455 * // reference to the widget instance. All widgets should 26456 * } 26457 * 26458 * delegates: A hash containing delegate references. Currently the only delegate 26459 * defined is the layout_delegate. 26460 * 26461 * layout_delegate: It's expected that a delegate will be supplied for this. The delegate will be invoked with 26462 * three arguments: 26463 * container: A jQuery element that is the container for everything that is part of this view. 26464 * widget_config: A hash of widget configuration objects, keyed by widget name. 26465 * id: An ID value that is unique within this page scope which can be used as a prefix 26466 * to uniquely identify widgets belonging to this view. 26467 * 26468 * The primary requirement of a layout_delegate is to make sure that each widget_config.container_id 26469 * has a corresponding element with html id = the appropriate widget.container_id. This view will use the 26470 * jQuery selector ('#' + widget_config.container_id) when constructing (or invoking a build_delegate) 26471 * for each widget. 26472 * 26473 * the layout delegate is responsible for attaching any new content to the given container. 26474 * 26475 * If no delegate is provided, a default layout is created consisting of a container div 26476 * and one contained div per widget. 26477 * </pre> 26478 * 26479 */ 26480AIBS.Application.view = (function() { 26481 26482 return( 26483 function(container, settings) { 26484 26485// public properties 26486 this.container = $j(container); 26487 this.options = { 26488 on_ready:null, 26489 delegates:{ 26490 layout_delegate:null 26491 }, 26492 widget_config:{} 26493 }; 26494 $j.extend(true, this.options, settings); 26495 26496// public methods 26497 26498 /** 26499 * Returns the instance ID for this view. 26500 * @return {*} 26501 */ 26502 this.id = function () { 26503 return(_id); 26504 }; 26505 26506 /** 26507 * Set an error message to be displayed in the view. The error 26508 * message will be displayed in place of the named widget. 26509 * 26510 * @param widget_name 26511 * @param msg 26512 */ 26513 this.set_error = function (widget_name, msg) { 26514 26515 var widget_container = $j('#' + _widget_list[widget_name].container_id);
26516 container_error($j(widget_container[0].parentNode), msg); 26517 }; 26518 26519 /** 26520 * Finds and returns the named widget. The name value is a key given 26521 * in settings.widget_configs. 26522 * 26523 * @param name 26524 * @return {Object} A widget_config object 26525 */ 26526 this.get_widget_by_name = function (name) { 26527 26528 if (_widget_list[name]) 26529 return(_widget_list[name].instance); 26530 26531 return(null); 26532 }; 26533 26534 /** 26535 * The same widget_config hash as settings.widget_configs. 26536 * 26537 * @return {Object} The same widget_config hash as settings.widget_configs. 26538 */ 26539 this.get_widgets = function() { 26540 26541 return(_widget_list); 26542 }; 26543 26544 this.destroy = function () { 26545 26546 for(var name in _widget_list) 26547 if(_widget_list[name].destroy) 26548 _widget_list[name].destroy(); 26549 26550 if(_own_layout) 26551 _own_layout.remove(); 26552 }; 26553 26554 26555// private properties 26556 26557 /** 26558 * @private 26559 * @scope AIBS.Application.view 26560 */ 26561 var _self = this; 26562 var _id = null; 26563 var _widget_list = null; 26564 var _own_layout = false; 26565 26566 26567 function container_error(container, msg) { 26568 26569 var err_html = "<div class='err_notice ui-state-error'>" + msg + "</div>"; 26570 container.html(err_html); 26571 } 26572 26573 // It's expected that a delegate will be supplied for this. The delegate will be invoked with 26574 // three arguments: 26575 // container: A jQuery element that is the container for everything that is part of this view. 26576 // widget_config: A hash of widget configuration objects, keyed by widget name. 26577 // id: An ID value that is unique within this page scope which can be used as a prefix 26578 // to uniquely identify widgets belonging to this view. 26579 // 26580 // The primary requirement of a layout_delegate is to make sure that each widget_config.container_id 26581 // has a corresponding element with html id = the appropriate widget.container_id. This view will use the 26582 // jQuery selector ('#' + widget_config.container_id) when constructing (or invoking a build_delegate) 26583 // for each widget. 26584 // 26585 // the layout delegate is responsible for attaching any new content to the given container. 26586 // 26587 // If no delegate is provided, a default layout is created consisting of a container div 26588 // and one contained div per widget. 26589 function build_layout() { 26590 26591 if (_self.options.delegates.layout_delegate) { 26592 _self.options.delegates.layout_delegate(_self.container, _self.options.widget_config, _id); 26593 return; 26594 } 26595 26596 // the default layout is just a container full of divs. 26597 var html = "<div id='" + _id + "_page_view_wrapper' class='_page_view_wrapper'>"; 26598 for(var name in _self.options.widget_config) { 26599 26600 var widget = _self.options.widget_config[name]; 26601 widget.container_id = _id + "_" + name; 26602 html += "<div id='" + widget.container_id + "' class='" + widget.container_class + "'></div>"; 26603 } 26604 26605 html += "</div>"; 26606 _own_layout = $j(html).appendTo(_self.container); 26607 } 26608 26609 function build_widgets() { 26610 26611 _widget_list = {}; 26612 for (var name in _self.options.widget_config) { 26613 26614 var widget = _self.options.widget_config[name]; 26615 _widget_list[name] = widget; 26616 26617 // many oddball widgets will need build delegates 26618 if (widget.build_delegate) { 26619 26620 // Some widgets do some asynchronous construction; if so they should invoke an on_ready 26621 // call back when they are ready enough to have their 'set_state' method called. 26622 widget.settings.on_ready = function(the_instance) { _widget_list[name].instance = the_instance; widget.ready = true; check_ready(); } 26623 _widget_list[name].instance = widget.build_delegate('#' + widget.container_id, widget.settings, _self); 26624 } 26625 // the info_widgets are numerous and similar enough to be built directly 26626 else { 26627 26628 // this is a syncronous process, so mark them ready immediately 26629 _widget_list[name].instance = new (eval(widget.class_name))('#'+widget.container_id, widget.settings, _self); 26630 widget.ready = true; 26631 } 26632 } 26633 26634 // maybe everything's already ready? 26635 check_ready(); 26636 } 26637 26638 function check_ready() { 26639 26640 for(var name in _widget_list) 26641 if(!_widget_list[name].ready) 26642 return; 26643 26644 trace(_id + " is ready."); 26645 if(_self.options.on_ready) 26646 _self.options.on_ready(_self); 26647 } 26648
26649 function init() { 26650 26651 _id = 'view' + AIBS.Application.__view_count__++; 26652trace("initializing " + _id); 26653 build_layout(); 26654 build_widgets(); 26655 } 26656 26657 init(); 26658 }); 26659})(); 26660 26661 26662 26663 26664 26665 26666/*! 26667 * jquery.event.drag - v 2.0.0 26668 * Copyright (c) 2010 Three Dub Media - http://threedubmedia.com 26669 * Open Source MIT License - http://threedubmedia.com/code/license 26670 */ 26671// Created: 2008-06-04 26672// Updated: 2010-06-07 26673// REQUIRES: jquery 1.4.2+ 26674 26675;(function( $ ){ 26676 26677// add the jquery instance method 26678$.fn.drag = function( str, arg, opts ){ 26679 // figure out the event type 26680 var type = typeof str == "string" ? str : "", 26681 // figure out the event handler... 26682 fn = $.isFunction( str ) ? str : $.isFunction( arg ) ? arg : null; 26683 // fix the event type 26684 if ( type.indexOf("drag") !== 0 ) 26685 type = "drag"+ type; 26686 // were options passed 26687 opts = ( str == fn ? arg : opts ) || {}; 26688 // trigger or bind event handler 26689 return fn ? this.bind( type, opts, fn ) : this.trigger( type ); 26690}; 26691 26692// local refs (increase compression) 26693var $event = $.event, 26694$special = $event.special, 26695// configure the drag special event 26696drag = $special.drag = { 26697 26698 // these are the default settings 26699 defaults: { 26700 which: 1, // mouse button pressed to start drag sequence 26701 distance: 0, // distance dragged before dragstart 26702 not: ':input', // selector to suppress dragging on target elements 26703 handle: null, // selector to match handle target elements 26704 relative: false, // true to use "position", false to use "offset" 26705 drop: true, // false to suppress drop events, true or selector to allow 26706 click: false // false to suppress click events after dragend (no proxy) 26707 }, 26708 26709 // the key name for stored drag data 26710 datakey: "dragdata", 26711 26712 // the namespace for internal live events 26713 livekey: "livedrag", 26714 26715 // count bound related events 26716 add: function( obj ){ 26717 // read the interaction data 26718 var data = $.data( this, drag.datakey ), 26719 // read any passed options 26720 opts = obj.data || {}; 26721 // count another realted event 26722 data.related += 1; 26723 // bind the live "draginit" delegator 26724 if ( !data.live && obj.selector ){ 26725 data.live = true; 26726 $event.add( this, "draginit."+ drag.livekey, drag.delegate ); 26727 } 26728 // extend data options bound with this event 26729 // don't iterate "opts" in case it is a node 26730 $.each( drag.defaults, function( key, def ){ 26731 if ( opts[ key ] !== undefined ) 26732 data[ key ] = opts[ key ]; 26733 }); 26734 }, 26735 26736 // forget unbound related events 26737 remove: function(){ 26738 $.data( this, drag.datakey ).related -= 1; 26739 }, 26740 26741 // configure interaction, capture settings 26742 setup: function(){ 26743 // check for related events 26744 if ( $.data( this, drag.datakey ) ) 26745 return; 26746 // initialize the drag data with copied defaults 26747 var data = $.extend({ related:0 }, drag.defaults ); 26748 // store the interaction data 26749 $.data( this, drag.datakey, data ); 26750 // bind the mousedown event, which starts drag interactions 26751 $event.add( this, "mousedown", drag.init, data ); 26752 // prevent image dragging in IE... 26753 if ( this.attachEvent ) 26754 this.attachEvent("ondragstart", drag.dontstart ); 26755 }, 26756 26757 // destroy configured interaction 26758 teardown: function(){ 26759 // check for related events 26760 if ( $.data( this, drag.datakey ).related ) 26761 return; 26762 // remove the stored data 26763 $.removeData( this, drag.datakey ); 26764 // remove the mousedown event 26765 $event.remove( this, "mousedown", drag.init ); 26766 // remove the "live" delegation 26767 $event.remove( this, "draginit", drag.delegate ); 26768 // enable text selection 26769 drag.textselect( true ); 26770 // un-prevent image dragging in IE... 26771 if ( this.detachEvent ) 26772 this.detachEvent("ondragstart", drag.dontstart ); 26773 }, 26774 26775 // initialize the interaction 26776 init: function( event ){ 26777 // the drag/drop interaction data 26778 var dd = event.data, results; 26779 // check the which directive 26780 if ( dd.which > 0 && event.which != dd.which ) 26781 return; 26782 // check for suppressed selector 26783 if ( $( event.target ).is( dd.not ) ) 26784 return; 26785 // check for handle selector 26786 if ( dd.handle && !$( event.target ).closest( dd.handle, event.currentTarget ).length ) 26787 return; 26788 // store/reset some initial attributes 26789 dd.propagates = 1; 26790 dd.interactions = [ drag.interaction( this, dd ) ]; 26791 dd.target = event.target; 26792 dd.pageX = event.pageX; 26793 dd.pageY = event.pageY; 26794 dd.dragging = null; 26795 // handle draginit event... 26796 results = drag.hijack( event, "draginit", dd ); 26797 // early cancel 26798 if ( !dd.propagates ) 26799 return; 26800 // flatten the result set 26801 results = drag.flatten( results ); 26802 // insert new interaction elements 26803 if ( results && results.length ){ 26804 dd.interactions = []; 26805 $.each( results, function(){ 26806 dd.interactions.push( drag.interaction( this, dd ) ); 26807 }); 26808 } 26809 // remember how many interactions are propagating 26810 dd.propagates = dd.interactions.length; 26811 // locate and init the drop targets 26812 if ( dd.drop !== false && $special.drop ) 26813 $special.drop.handler( event, dd ); 26814 // disable text selection 26815 drag.textselect( false ); 26816 // bind additional events... 26817 $event.add( document, "mousemove mouseup", drag.handler, dd ); 26818 // helps prevent text selection 26819 return false; 26820 }, 26821 // returns an interaction object 26822 interaction: function( elem, dd ){ 26823 return { 26824 drag: elem, 26825 callback: new drag.callback(), 26826 droppable: [], 26827 offset: $( elem )[ dd.relative ? "position" : "offset" ]() || { top:0, left
26827:0 } 26828 }; 26829 }, 26830 // handle drag-releatd DOM events 26831 handler: function( event ){ 26832 // read the data before hijacking anything 26833 var dd = event.data; 26834 // handle various events 26835 switch ( event.type ){ 26836 // mousemove, check distance, start dragging 26837 case !dd.dragging && 'mousemove': 26838 // drag tolerance, x² + y² = distance² 26839 if ( Math.pow( event.pageX-dd.pageX, 2 ) + Math.pow( event.pageY-dd.pageY, 2 ) < Math.pow( dd.distance, 2 ) ) 26840 break; // distance tolerance not reached 26841 event.target = dd.target; // force target from "mousedown" event (fix distance issue) 26842 drag.hijack( event, "dragstart", dd ); // trigger "dragstart" 26843 if ( dd.propagates ) // "dragstart" not rejected 26844 dd.dragging = true; // activate interaction 26845 // mousemove, dragging 26846 case 'mousemove': 26847 if ( dd.dragging ){ 26848 // trigger "drag" 26849 drag.hijack( event, "drag", dd ); 26850 if ( dd.propagates ){ 26851 // manage drop events 26852 if ( dd.drop !== false && $special.drop ) 26853 $special.drop.handler( event, dd ); // "dropstart", "dropend" 26854 break; // "drag" not rejected, stop 26855 } 26856 event.type = "mouseup"; // helps "drop" handler behave 26857 } 26858 // mouseup, stop dragging 26859 case 'mouseup': 26860 $event.remove( document, "mousemove mouseup", drag.handler ); // remove page events 26861 if ( dd.dragging ){ 26862 if ( dd.drop !== false && $special.drop ) 26863 $special.drop.handler( event, dd ); // "drop" 26864 drag.hijack( event, "dragend", dd ); // trigger "dragend" 26865 } 26866 drag.textselect( true ); // enable text selection 26867 26868 // if suppressing click events... 26869 if ( dd.click === false && dd.dragging ){ 26870 jQuery.event.triggered = true; 26871 setTimeout(function(){ 26872 jQuery.event.triggered = false; 26873 }, 20 ); 26874 dd.dragging = false; // deactivate element 26875 } 26876 break; 26877 } 26878 }, 26879 26880 // identify potential delegate elements 26881 delegate: function( event ){ 26882 // local refs 26883 var elems = [], target, 26884 // element event structure 26885 events = $.data( this, "events" ) || {}; 26886 // query live events 26887 $.each( events.live || [], function( i, obj ){ 26888 // no event type matches 26889 if ( obj.preType.indexOf("drag") !== 0 ) 26890 return; 26891 // locate the element to delegate 26892 target = $( event.target ).closest( obj.selector, event.currentTarget )[0]; 26893 // no element found 26894 if ( !target ) 26895 return; 26896 // add an event handler 26897 $event.add( target, obj.origType+'.'+drag.livekey, obj.origHandler, obj.data ); 26898 // remember new elements 26899 if ( $.inArray( target, elems ) < 0 ) 26900 elems.push( target ); 26901 }); 26902 // if there are no elements, break 26903 if ( !elems.length ) 26904 return false; 26905 // return the matched results, and clenup when complete 26906 return $( elems ).bind("dragend."+ drag.livekey, function(){ 26907 $event.remove( this, "."+ drag.livekey ); // cleanup delegation 26908 }); 26909 }, 26910 26911 // re-use event object for custom events 26912 hijack: function( event, type, dd, x, elem ){ 26913 // not configured 26914 if ( !dd ) 26915 return; 26916 // remember the original event and type 26917 var orig = { event:event.originalEvent, type: event.type }, 26918 // is the event drag related or drog related? 26919 mode = type.indexOf("drop") ? "drag" : "drop", 26920 // iteration vars 26921 result, i = x || 0, ia, $elems, callback, 26922 len = !isNaN( x ) ? x : dd.interactions.length; 26923 // modify the event type 26924 event.type = type; 26925 // remove the original event 26926 event.originalEvent = null; 26927 // initialize the results 26928 dd.results = []; 26929 // handle each interacted element 26930 do if ( ia = dd.interactions[ i ] ){ 26931 // validate the interaction 26932 if ( type !== "dragend" && ia.cancelled ) 26933 continue; 26934 // set the dragdrop properties on the event object 26935 callback = drag.properties( event, dd, ia ); 26936 // prepare for more results 26937 ia.results = []; 26938 // handle each element 26939 $( elem || ia[ mode ] || dd.droppable ).each(function( p, subject ){ 26940 // identify drag or drop targets individually 26941 callback.target = subject; 26942 // handle the event 26943 result = subject ? $event.handle.call( subject, event, callback ) : null; 26944 // stop the drag interaction for this element 26945 if ( result === false ){ 26946 if ( mode == "drag" ){ 26947 ia.cancelled = true; 26948 dd.propagates -= 1; 26949 } 26950 if ( type == "drop" ){ 26951 ia[ mode ][p] = null; 26952 } 26953 } 26954 // assign any dropinit elements 26955 else if ( type == "dropinit" ) 26956 ia.droppable.push( drag.element( result ) || subject ); 26957 // accept a returned proxy element 26958 if ( type == "dragstart" ) 26959 ia.proxy = $( drag.element( result ) || ia.drag )[0]; 26960 // remember this result 26961 ia.results.push( result ); 26962 // forget the event result, for recycling 26963 delete event.result; 26964 // break on cancelled handler 26965 if ( type !== "dropinit" ) 26966 return result; 26967 }); 26968 // flatten the results 26969 dd.results[ i ] = drag.flatten( ia.results ); 26970 // accept a set of valid drop targets 26971 if ( type == "dropinit" ) 26972 ia.droppable = drag.flatten( ia.droppable ); 26973 // locate drop targets 26974 if ( type == "dragstart" && !ia.cancelled ) 26975 callback.update(); 26976 } 26977 while ( ++i < len ) 26978 // restore the original event & type 26979 event.type = orig.type; 26980 event.originalEvent = orig.event; 26981 // return all handler results 26982 return drag.flatten( dd.results ); 26983 }, 26984 26985 // extend the callback object with drag/drop properties... 26986 properties: function( event, dd, ia ){ 26987 var obj = ia.callback; 26988 // elements 26989 obj.drag = ia.drag; 26990 obj.proxy = ia.proxy || ia.drag; 26991 // starting mouse position 26992 obj.startX = dd.pageX; 26993 obj.startY = dd.pageY; 26994 // current distance dragged 26995 obj.deltaX = event.pageX - dd.pageX; 26996 obj.deltaY = event.pageY - dd.pageY; 26997 // original element position 26998 obj.originalX = ia.offset.left; 26999 obj.originalY = ia.offset.top; 27000 // adjusted element position 27001 obj.offsetX = event.pageX - ( dd.pageX - obj.originalX ); 27002 obj.offsetY = event.pageY - ( dd.pageY - obj.originalY ); 27003 // assign the drop targets information 27004 obj.drop = drag.flatten( ( ia.drop || [] ).slice() ); 27005 obj.available = drag.flatten( ( ia.droppable || [] ).slice() ); 27006 return obj; 27007 }, 27008 27009 // determine is the argument is an element or jquery instance 27010 element: function( arg ){ 27011 if ( arg && ( arg.jquery || arg.nodeType == 1 ) ) 27012 return arg; 27013 }, 27014 27015 // flatten nested jquery objects and arrays into a single dimension array 27016 flatten: function( arr ){ 27017 return $.map( arr, function( member ){ 27018 return member && member.jquery ? $.makeArray( member ) : 27019 member && member.length ? drag.flatten( member ) : member; 27020 }); 27021 }, 27022 27023 // toggles text selection attributes ON (true) or OFF (false) 27024 textselect: function( bool ){ 27025 $( document )[ bool ? "unbind" : "bind" ]("selectstart", drag.dontstart ) 27026 .attr("unselectable", bool ? "off" : "on" ) 27027 .css("MozUserSelect", bool ? "" : "none" ); 27028 }, 27029 27030 // suppress "selectstart" and "ondragstart" events 27031 dontstart: function(){ 27032 return false; 27033 }, 27034 27035 // a callback instance contructor 27036 callback: function(){} 27037 27038}; 27039 27040// callback methods 27041drag.callback.prototype = { 27042 update: function(){ 27043 if ( $special.drop && this.available.length ) 27044 $.each( this.available, function( i ){ 27045 $special.drop.locate( this, i ); 27046 }); 27047 } 27048}; 27049 27050// share the same special event configuration with related events... 27051$special.draginit = $special.dragstart = $special.dragend = drag; 27052 27053})( jQuery ); 27054/*! 27055 * jquery.event.drop - v 2.0.0 27056 * Copyright (c) 2010 Three Dub Media - http://threedubmedia.com 27057 * Open Source MIT License - http://threedubmedia.com/code/license 27058 */ 27059// Created: 2008-06-04 27060// Updated: 2010-06-08 27061// REQUIRES: jquery 1.4.2+, jquery.event.drag 2+ 27062 27063;(function($){ // secure $ jQuery alias 27064 27065// Events: drop, dropinit, dropstart, dropend 27066 27067// add the jquery instance method
27068$.fn.drop = function( str, arg, opts ){ 27069 // figure out the event type 27070 var type = typeof str == "string" ? str : "", 27071 // figure out the event handler... 27072 fn = $.isFunction( str ) ? str : $.isFunction( arg ) ? arg : null; 27073 // fix the event type 27074 if ( type.indexOf("drop") !== 0 ) 27075 type = "drop"+ type; 27076 // were options passed 27077 opts = ( str == fn ? arg : opts ) || {}; 27078 // trigger or bind event handler 27079 return fn ? this.bind( type, opts, fn ) : this.trigger( type ); 27080}; 27081 27082// DROP MANAGEMENT UTILITY 27083// returns filtered drop target elements, caches their positions 27084$.drop = function( opts ){ 27085 opts = opts || {}; 27086 // safely set new options... 27087 drop.multi = opts.multi === true ? Infinity : 27088 opts.multi === false ? 1 : !isNaN( opts.multi ) ? opts.multi : drop.multi; 27089 drop.delay = opts.delay || drop.delay; 27090 drop.tolerance = $.isFunction( opts.tolerance ) ? opts.tolerance : 27091 opts.tolerance === null ? null : drop.tolerance; 27092 drop.mode = opts.mode || drop.mode || 'intersect'; 27093}; 27094 27095// local refs (increase compression) 27096var $event = $.event, 27097$special = $event.special, 27098// configure the drop special event 27099drop = $.event.special.drop = { 27100 27101 // these are the default settings 27102 multi: 1, // allow multiple drop winners per dragged element 27103 delay: 20, // async timeout delay 27104 mode: 'overlap', // drop tolerance mode 27105 27106 // internal cache 27107 targets: [], 27108 27109 // the key name for stored drop data 27110 datakey: "dropdata", 27111 27112 // the namespace for internal live events 27113 livekey: "livedrop", 27114 27115 // count bound related events 27116 add: function( obj ){ 27117 // read the interaction data 27118 var data = $.data( this, drop.datakey ); 27119 // count another realted event 27120 data.related += 1; 27121 // bind the live "dropinit" delegator 27122 if ( !data.live && obj.selector ){ 27123 data.live = true; 27124 $event.add( this, "dropinit."+ drop.livekey, drop.delegate ); 27125 } 27126 }, 27127 27128 // forget unbound related events 27129 remove: function(){ 27130 $.data( this, drop.datakey ).related -= 1; 27131 }, 27132 27133 // configure the interactions 27134 setup: function(){ 27135 // check for related events 27136 if ( $.data( this, drop.datakey ) ) 27137 return; 27138 // initialize the drop element data 27139 var data = { 27140 related: 0, 27141 active: [], 27142 anyactive: 0, 27143 winner: 0, 27144 location: {} 27145 }; 27146 // store the drop data on the element 27147 $.data( this, drop.datakey, data ); 27148 // store the drop target in internal cache 27149 drop.targets.push( this ); 27150 }, 27151 27152 // destroy the configure interaction 27153 teardown: function(){ 27154 // check for related events 27155 if ( $.data( this, drop.datakey ).related ) 27156 return; 27157 // remove the stored data 27158 $.removeData( this, drop.datakey ); 27159 // remove the "live" delegation 27160 $event.remove( this, "dropinit", drop.delegate ); 27161 // reference the targeted element 27162 var element = this; 27163 // remove from the internal cache 27164 drop.targets = $.grep( drop.targets, function( target ){ 27165 return ( target !== element ); 27166 }); 27167 }, 27168 27169 // shared event handler 27170 handler: function( event, dd ){ 27171 // local vars 27172 var results, $targets; 27173 // make sure the right data is available 27174 if ( !dd ) 27175 return; 27176 // handle various events 27177 switch ( event.type ){ 27178 // draginit, from $.event.special.drag 27179 case 'mousedown': // DROPINIT >> 27180 // collect and assign the drop targets 27181 $targets = $( drop.targets ); 27182 if ( typeof dd.drop == "string" ) 27183 $targets = $targets.filter( dd.drop ); 27184 // reset drop data winner properties 27185 $targets.each(function(){ 27186 var data = $.data( this, drop.datakey ); 27187 data.active = []; 27188 data.anyactive = 0; 27189 data.winner = 0; 27190 }); 27191 // set available target elements 27192 dd.droppable = $targets; 27193 // hold any live elements 27194 drop.delegates = []; 27195 // activate drop targets for the initial element being dragged 27196 $special.drag.hijack( event, "dropinit", dd ); 27197 // flatten all the live elements 27198 drop.delegates = $.unique( $special.drag.flatten( drop.delegates ) ); 27199 break; 27200 // drag, from $.event.special.drag 27201 case 'mousemove': // TOLERATE >> 27202 drop.event = event; // store the mousemove event 27203 if ( !drop.timer ) 27204 // monitor drop targets 27205 drop.tolerate( dd ); 27206 break; 27207 // dragend, from $.event.special.drag 27208 case 'mouseup': // DROP >> DROPEND >> 27209 drop.timer = clearTimeout( drop.timer ); // delete timer 27210 if ( dd.propagates ){ 27211 $special.drag.hijack( event, "drop", dd ); 27212 $special.drag.hijack( event, "dropend", dd ); 27213 // cleanout live events... 27214 $.each( drop.delegates || [], function(){ 27215 $event.remove( this, '.'+ drop.livekey ); 27216 });
27217 } 27218 break; 27219 } 27220 }, 27221 27222 // identify potential delegate elements 27223 delegate: function( event ){ 27224 // local refs 27225 var elems = [], $targets, 27226 // element event structure 27227 events = $.data( this, "events" ) || {}; 27228 // query live events 27229 $.each( events.live || [], function( i, obj ){ 27230 // no event type matches 27231 if ( obj.preType.indexOf("drop") !== 0 ) 27232 return; 27233 // locate the elements to delegate 27234 $targets = $( event.currentTarget ).find( obj.selector ); 27235 // no element found 27236 if ( !$targets.length ) 27237 return; 27238 // take each target... 27239 $targets.each(function(){ 27240 // add an event handler 27241 $event.add( this, obj.origType +'.'+ drop.livekey, obj.origHandler, obj.data ); 27242 // remember new elements 27243 if ( $.inArray( this, elems ) < 0 ) 27244 elems.push( this ); 27245 }); 27246 }); 27247 drop.delegates.push( elems ); 27248 return elems.length ? $( elems ) : false; 27249 }, 27250 27251 // returns the location positions of an element 27252 locate: function( elem, index ){ 27253 var data = $.data( elem, drop.datakey ), 27254 $elem = $( elem ), 27255 posi = $elem.offset() || {}, 27256 height = $elem.outerHeight(), 27257 width = $elem.outerWidth(), 27258 location = { 27259 elem: elem, 27260 width: width, 27261 height: height, 27262 top: posi.top, 27263 left: posi.left, 27264 right: posi.left + width, 27265 bottom: posi.top + height 27266 }; 27267 // drag elements might not have dropdata 27268 if ( data ){ 27269 data.location = location; 27270 data.index = index; 27271 data.elem = elem; 27272 } 27273 return location; 27274 }, 27275 27276 // test the location positions of an element against another OR an X,Y coord 27277 contains: function( target, test ){ // target { location } contains test [x,y] or { location } 27278 return ( ( test[0] || test.left ) >= target.left && ( test[0] || test.right ) <= target.right 27279 && ( test[1] || test.top ) >= target.top && ( test[1] || test.bottom ) <= target.bottom ); 27280 }, 27281 27282 // stored tolerance modes 27283 modes: { // fn scope: "$.event.special.drop" object 27284 // target with mouse wins, else target with most overlap wins 27285 'intersect': function( event, proxy, target ){ 27286 return this.contains( target, [ event.pageX, event.pageY ] ) ? // check cursor 27287 1e9 : this.modes.overlap.apply( this, arguments ); // check overlap 27288 }, 27289 // target with most overlap wins 27290 'overlap': function( event, proxy, target ){ 27291 // calculate the area of overlap... 27292 return Math.max( 0, Math.min( target.bottom, proxy.bottom ) - Math.max( target.top, proxy.top ) ) 27293 * Math.max( 0, Math.min( target.right, proxy.right ) - Math.max( target.left, proxy.left ) ); 27294 }, 27295 // proxy is completely contained within target bounds 27296 'fit': function( event, proxy, target ){ 27297 return this.contains( target, proxy ) ? 1 : 0; 27298 }, 27299 // center of the proxy is contained within target bounds 27300 'middle': function( event, proxy, target ){ 27301 return this.contains( target, [ proxy.left + proxy.width * .5, proxy.top + proxy.height * .5 ] ) ? 1 : 0; 27302 } 27303 }, 27304 27305 // sort drop target cache by by winner (dsc), then index (asc) 27306 sort: function( a, b ){ 27307 return ( b.winner - a.winner ) || ( a.index - b.index ); 27308 }, 27309 27310 // async, recursive tolerance execution 27311 tolerate: function( dd ){ 27312 // declare local refs 27313 var i, drp, drg, data, arr, len, elem, 27314 // interaction iteration variables 27315 x = 0, ia, end = dd.interactions.length, 27316 // determine the mouse coords 27317 xy = [ drop.event.pageX, drop.event.pageY ], 27318 // custom or stored tolerance fn 27319 tolerance = drop.tolerance || drop.modes[ drop.mode ]; 27320 // go through each passed interaction... 27321 do if ( ia = dd.interactions[x] ){ 27322 // check valid interaction 27323 if ( !ia ) 27324 return; 27325 // initialize or clear the drop data 27326 ia.drop = []; 27327 // holds the drop elements 27328 arr = []; 27329 len = ia.droppable.length; 27330 // determine the proxy location, if needed 27331 if ( tolerance ) 27332 drg = drop.locate( ia.proxy ); 27333 // reset the loop 27334 i = 0; 27335 // loop each stored drop target 27336 do if ( elem = ia.droppable[i] ){ 27337 data = $.data( elem, drop.datakey ); 27338 drp = data.location; 27339 if ( !drp ) continue; 27340 // find a winner: tolerance function is defined, call it 27341 data.winner = tolerance ? tolerance.call( drop, drop.event, drg, drp ) 27342 // mouse position is always the fallback 27343 : drop.contains( drp, xy ) ? 1 : 0; 27344 arr.push( data ); 27345 } while ( ++i < len ); // loop 27346 // sort the drop targets 27347 arr.sort( drop.sort ); 27348 // reset the loop 27349 i = 0; 27350 // loop through all of the targets again 27351 do if ( data = arr[ i ] ){ 27352 // winners... 27353 if ( data.winner && ia.drop.length < drop.multi ){ 27354 // new winner... dropstart 27355 if ( !data.active[x] && !data.anyactive ){ 27356 // check to make sure that this is not prevented 27357 if ( $special.drag.hijack( drop.event, "dropstart", dd, x, data.elem )[0] !== false ){ 27358 data.active[x] = 1; 27359 data.anyactive += 1; 27360 } 27361 // if false, it is not a winner 27362 else 27363 data.winner = 0; 27364 } 27365 // if it is still a winner 27366 if ( data.winner ) 27367 ia.drop.push( data.elem ); 27368 } 27369 // losers... 27370 else if ( data.active[x] && data.anyactive == 1 ){ 27371 // former winner... dropend 27372 $special.drag.hijack( drop.event, "dropend", dd, x, data.elem ); 27373 data.active[x] = 0; 27374 data.anyactive -= 1;
27375 } 27376 } while ( ++i < len ); // loop 27377 } while ( ++x < end ) // loop 27378 // check if the mouse is still moving or is idle 27379 if ( drop.last && xy[0] == drop.last.pageX && xy[1] == drop.last.pageY ) 27380 delete drop.timer; // idle, don't recurse 27381 else // recurse 27382 drop.timer = setTimeout(function(){ 27383 drop.tolerate( dd ); 27384 }, drop.delay ); 27385 // remember event, to compare idleness 27386 drop.last = drop.event; 27387 } 27388 27389}; 27390 27391// share the same special event configuration with related events... 27392$special.dropinit = $special.dropstart = $special.dropend = drop; 27393 27394})(jQuery); // confine scope 27395; 27396/** 27397 * @license 27398 * (c) 2009-2010 Michael Leibman ([email protected]) 27399 * http://github.com/mleibman/slickgrid 27400 * Distributed under MIT license. 27401 * All rights reserved. 27402 * 27403 * SlickGrid v1.4.3 27404 * 27405 * TODO: 27406 * - frozen columns 27407 * - consistent events (EventHelper? jQuery events?) 27408 * 27409 * 27410 * OPTIONS: 27411 * rowHeight - (default 25px) Row height in pixels. 27412 * enableAddRow - (default false) If true, a blank row will be displayed at the bottom - typing values in that row will add a new one. 27413 * leaveSpaceForNewRows - (default false) 27414 * editable - (default false) If false, no cells will be switched into edit mode. 27415 * autoEdit - (default true) Cell will not automatically go into edit mode when selected. 27416 * enableCellNavigation - (default true) If false, no cells will be selectable. 27417 * enableCellRangeSelection - (default false) If true, user will be able to select a cell range. onCellRangeSelected event will be fired. 27418 * defaultColumnWidth - (default 80px) Default column width in pixels (if columns[cell].width is not specified). 27419 * enableColumnReorder - (default true) Allows the user to reorder columns. 27420 * asyncEditorLoading - (default false) Makes cell editors load asynchronously after a small delay. 27421 * This greatly increases keyboard navigation speed. 27422 * asyncEditorLoadDelay - (default 100msec) Delay after which cell editor is loaded. Ignored unless asyncEditorLoading is true. 27423 * forceFitColumns - (default false) Force column sizes to fit into the viewport (avoid horizontal scrolling). 27424 * enableAsyncPostRender - (default false) If true, async post rendering will occur and asyncPostRender delegates on columns will be called. 27425 * asyncPostRenderDelay - (default 60msec) Delay after which async post renderer delegate is called. 27426 * autoHeight - (default false) If true, vertically resizes to fit all rows. 27427 * editorLock - (default Slick.GlobalEditorLock) A Slick.EditorLock instance to use for controlling concurrent data edits. 27428 * showSecondaryHeaderRow - (default false) If true, an extra blank (to be populated externally) row will be displayed just below the header columns. 27429 * secondaryHeaderRowHeight - (default 25px) The height of the secondary header row. 27430 * syncColumnCellResize - (default false) Synchronously resize column cells when column headers are resized 27431 * rowCssClasses - (default null) A function which (given a row's data item as an argument) returns a space-delimited string of CSS classes that will be applied to the slick-row element. Note that this should be fast, as it is called every time a row is displayed. 27432 * cellHighlightCssClass - (default "highlighted") A CSS class to apply to cells highlighted via setHighlightedCells(). 27433 * cellFlashingCssClass - (default "flashing") A CSS class to apply to flashing cells (flashCell()). 27434 * formatterFactory - (default null) A factory object responsible to creating a formatter for a given cell. 27435 * Must implement getFormatter(column). 27436 * editorFactory - (default null) A factory object responsible to creating an editor for a given cell. 27437 * Must implement getEditor(column). 27438 * multiSelect - (default true) Enable multiple row selection. 27439 * 27440 * COLUMN DEFINITION (columns) OPTIONS: 27441 * id - Column ID. 27442 * name - Column name to put in the header. 27443 * toolTip - Tooltip (if different from name). 27444 * field - Property of the data context to bind to. 27445 * formatter - (default 'return value || ""') Function responsible for rendering the contents of a cell. Signature: function formatter(row, cell, value, columnDef, dataContext) { ... return "..."; } 27446 * editor - An Editor class. 27447 * validator - An extra validation function to be passed to the editor. 27448 * unselectable - If true, the cell cannot be selected (and therefore edited). 27449 * cannotTriggerInsert - If true, a new row cannot be created from just the value of this cell. 27450 * width - Width of the column in pixels. 27451 * resizable - (default true) If false, the column cannot be resized. 27452 * sortable - (default false) If true, the column can be sorted (onSort will be called). 27453 * minWidth - Minimum allowed column width for resizing. 27454 * maxWidth - Maximum allowed column width for resizing. 27455 * cssClass - A CSS class to add to the cell. 27456 * rerenderOnResize - Rerender the column when it is resized (useful for columns relying on cell width or adaptive formatters). 27457 * asyncPostRender - Function responsible for manipulating the cell DOM node after it has been rendered (called in the background). 27458 * behavior - Configures the column with one of several available predefined behaviors: "select", "move", "selectAndMove". 27459 * 27460 * 27461 * EVENTS: 27462 * onSort - 27463 * onHeaderContextMenu - 27464 * onHeaderClick - 27465 * onClick - 27466 * onDblClick - 27467 * onContextMenu - 27468 * onKeyDown - 27469 * onAddNewRow - 27470 * onValidationError - 27471 * onViewportChanged - 27472 * onSelectedRowsChanged - 27473 * onColumnsReordered - 27474 * onColumnsResized - 27475 * onBeforeMoveRows - 27476 * onMoveRows - 27477 * onCellChange - Raised when cell has been edited. Args: row,cell,dataContext. 27478 * onBeforeEditCell - Raised before a cell goes into edit mode. Return false to cancel. Arg
27478s: row,cell,dataContext. 27479 * onBeforeCellEditorDestroy - Raised before a cell editor is destroyed. Args: current cell editor. 27480 * onBeforeDestroy - Raised just before the grid control is destroyed (part of the destroy() method). 27481 * onCurrentCellChanged - Raised when the selected (active) cell changed. Args: {row:currentRow, cell:currentCell}. 27482 * onCellRangeSelected - Raised when a user selects a range of cells. Args: {from:{row,cell}, to:{row,cell}}. 27483 * 27484 * NOTES: 27485 * Cell/row DOM manipulations are done directly bypassing jQuery's DOM manipulation methods. 27486 * This increases the speed dramatically, but can only be done safely because there are no event handlers 27487 * or data associated with any cell/row DOM nodes. Cell editors must make sure they implement .destroy() 27488 * and do proper cleanup. 27489 * 27490 * 27491 * @param {Node} container Container node to create the grid in. 27492 * @param {Array} or {Object} data An array of objects for databinding. 27493 * @param {Array} columns An array of column definitions. 27494 * @param {Object} options Grid options. 27495 */ 27496 27497// make sure required JavaScript modules are loaded 27498if (typeof jQuery === "undefined") { 27499 throw new Error("SlickGrid requires jquery module to be loaded"); 27500} 27501if (!jQuery.fn.drag) { 27502 throw new Error("SlickGrid requires jquery.event.drag module to be loaded"); 27503} 27504 27505(function($) { 27506 var scrollbarDimensions; // shared across all grids on this page 27507 27508 27509 ////////////////////////////////////////////////////////////////////////////////////////////// 27510 // EditorLock class implementation (available as Slick.EditorLock) 27511 27512 /** @constructor */ 27513 function EditorLock() { 27514 /// <summary> 27515 /// Track currently active edit controller and ensure 27516 /// that onle a single controller can be active at a time. 27517 /// Edit controller is an object that is responsible for 27518 /// gory details of looking after editor in the browser, 27519 /// and allowing EditorLock clients to either accept 27520 /// or cancel editor changes without knowing any of the 27521 /// implementation details. SlickGrid instance is used 27522 /// as edit controller for cell editors. 27523 /// </summary> 27524 27525 var currentEditController = null; 27526 27527 this.isActive = function isActive(editController) { 27528 /// <summary> 27529 /// Return true if the specified editController 27530 /// is currently active in this lock instance 27531 /// (i.e. if that controller acquired edit lock). 27532 /// If invoked without parameters ("editorLock.isActive()"), 27533 /// return true if any editController is currently 27534 /// active in this lock instance. 27535 /// </summary> 27536 return (editController ? currentEditController === editController : currentEditController !== null); 27537 }; 27538 27539 this.activate = function activate(editController) { 27540 /// <summary> 27541 /// Set the specified editController as the active 27542 /// controller in this lock instance (acquire edit lock). 27543 /// If another editController is already active, 27544 /// an error will be thrown (i.e. before calling 27545 /// this method isActive() must be false, 27546 /// afterwards isActive() will be true). 27547 /// </summary> 27548 if (editController === currentEditController) { // already activated? 27549 return; 27550 } 27551 if (currentEditController !== null) { 27552 throw "SlickGrid.EditorLock.activate: an editController is still active, can't activate another editController"; 27553 } 27554 if (!editController.commitCurrentEdit) { 27555 throw "SlickGrid.EditorLock.activate: editController must implement .commitCurrentEdit()"; 27556 } 27557 if (!editController.cancelCurrentEdit) { 27558 throw "SlickGrid.EditorLock.activate: editController must implement .cancelCurrentEdit()"; 27559 } 27560 currentEditController = editController; 27561 }; 27562 27563 this.deactivate = function deactivate(editController) { 27564 /// <summary> 27565 /// Unset the specified editController as the active 27566 /// controller in this lock instance (release edit lock). 27567 /// If the specified editController is not the editController 27568 /// that is currently active in this lock instance, 27569 /// an error will be thrown. 27570 /// </summary> 27571 if (currentEditController !== editController) { 27572 throw "SlickGrid.EditorLock.deactivate: specified editController is not the currently active one"; 27573 } 27574 currentEditController = null; 27575 }; 27576 27577 this.commitCurrentEdit = function commitCurrentEdit() { 27578 /// <summary> 27579 /// Invoke the "commitCurrentEdit" method on the 27580 /// editController that is active in this lock 27581 /// instance and return the return value of that method 27582 /// (if no controller is active, return true). 27583 /// "commitCurrentEdit" is expected to return true 27584 /// to indicate successful commit, false otherwise. 27585 /// </summary> 27586 return (currentEditController ? currentEditController.commitCurrentEdit() : true); 27587 }; 27588 27589 this.cancelCurrentEdit = function cancelCurrentEdit() { 27590 /// <summary> 27591 /// Invoke the "cancelCurrentEdit" method on the 27592 /// editController that is active in this lock 27593 /// instance (if no controller is active, do nothing). 27594 /// Returns true if the edit was succesfully cancelled. 27595 /// </summary> 27596 return (currentEditController ? currentEditController.cancelCurrentEdit() : true); 27597 }; 27598 } // end of EditorLock function (class) 27599 27600 27601
27602 ////////////////////////////////////////////////////////////////////////////////////////////// 27603 // SlickGrid class implementation (available as Slick.Grid) 27604 27605 /** @constructor */ 27606 function SlickGrid(container,data,columns,options) { 27607 /// <summary> 27608 /// Create and manage virtual grid in the specified $container, 27609 /// connecting it to the specified data source. Data is presented 27610 /// as a grid with the specified columns and data.length rows. 27611 /// Options alter behaviour of the grid. 27612 /// </summary> 27613 27614 // settings 27615 var defaults = { 27616 rowHeight: 25, 27617 defaultColumnWidth: 80, 27618 enableAddRow: false, 27619 leaveSpaceForNewRows: false, 27620 editable: false, 27621 autoEdit: true, 27622 enableCellNavigation: true, 27623 enableCellRangeSelection: false, 27624 enableColumnReorder: true, 27625 asyncEditorLoading: false, 27626 asyncEditorLoadDelay: 100, 27627 forceFitColumns: false, 27628 enableAsyncPostRender: false, 27629 asyncPostRenderDelay: 60, 27630 autoHeight: false, 27631 editorLock: Slick.GlobalEditorLock, 27632 showSecondaryHeaderRow: false, 27633 secondaryHeaderRowHeight: 25, 27634 syncColumnCellResize: false, 27635 enableAutoTooltips: true, 27636 toolTipMaxLength: null, 27637 formatterFactory: null, 27638 editorFactory: null, 27639 cellHighlightCssClass: "highlighted", 27640 cellFlashingCssClass: "flashing", 27641 multiSelect: true 27642 }, 27643 gridData, gridDataGetLength, gridDataGetItem; 27644 27645 var columnDefaults = { 27646 name: "", 27647 resizable: true, 27648 sortable: false, 27649 minWidth: 30 27650 }; 27651 27652 // scroller 27653 var maxSupportedCssHeight; // browser's breaking point 27654 var th; // virtual height 27655 var h; // real scrollable height 27656 var ph; // page height 27657 var n; // number of pages 27658 var cj; // "jumpiness" coefficient 27659 27660 var page = 0; // current page 27661 var offset = 0; // current page offset 27662 var scrollDir = 1; 27663 27664 // private 27665 var $container; 27666 var uid = "slickgrid_" + Math.round(1000000 * Math.random()); 27667 var self = this; 27668 var $headerScroller; 27669 var $headers; 27670 var $secondaryHeaderScroller; 27671 var $secondaryHeaders; 27672 var $viewport; 27673 var $canvas; 27674 var $style; 27675 var stylesheet; 27676 var viewportH, viewportW; 27677 var viewportHasHScroll; 27678 var headerColumnWidthDiff, headerColumnHeightDiff, cellWidthDiff, cellHeightDiff; // padding+border 27679 var absoluteColumnMinWidth; 27680 27681 var currentRow, currentCell; 27682 var currentCellNode = null; 27683 var currentEditor = null; 27684 var serializedEditorValue; 27685 var editController; 27686 27687 var rowsCache = {}; 27688 var renderedRows = 0; 27689 var numVisibleRows; 27690 var prevScrollTop = 0; 27691 var scrollTop = 0; 27692 var lastRenderedScrollTop = 0; 27693 var prevScrollLeft = 0; 27694 var avgRowRenderTime = 10; 27695 27696 var selectedRows = []; 27697 var selectedRowsLookup = {}; 27698 var columnsById = {}; 27699 var highlightedCells; 27700 var sortColumnId; 27701 var sortAsc = true; 27702 27703 // async call handles 27704 var h_editorLoader = null; 27705 var h_render = null; 27706 var h_postrender = null; 27707 var postProcessedRows = {}; 27708 var postProcessToRow = null; 27709 var postProcessFromRow = null; 27710 27711 // perf counters 27712 var counter_rows_rendered = 0; 27713 var counter_rows_removed = 0; 27714 27715
27716 ////////////////////////////////////////////////////////////////////////////////////////////// 27717 // Initialization 27718 27719 function init() { 27720 /// <summary> 27721 /// Initialize 'this' (self) instance of a SlickGrid. 27722 /// This function is called by the constructor. 27723 /// </summary> 27724 27725 $container = $(container); 27726 27727 gridData = data; 27728 gridDataGetLength = gridData.getLength || defaultGetLength; 27729 gridDataGetItem = gridData.getItem || defaultGetItem; 27730 27731 maxSupportedCssHeight = getMaxSupportedCssHeight(); 27732 27733 scrollbarDimensions = scrollbarDimensions || measureScrollbar(); // skip measurement if already have dimensions 27734 options = $.extend({},defaults,options); 27735 columnDefaults.width = options.defaultColumnWidth; 27736 27737 // validate loaded JavaScript modules against requested options 27738 if (options.enableColumnReorder && !$.fn.sortable) { 27739 throw new Error("SlickGrid's \"enableColumnReorder = true\" option requires jquery-ui.sortable module to be loaded"); 27740 } 27741 27742 editController = { 27743 "commitCurrentEdit": commitCurrentEdit, 27744 "cancelCurrentEdit": cancelCurrentEdit 27745 }; 27746 27747 $container 27748 .empty() 27749 .attr("tabIndex",0) 27750 .attr("hideFocus",true) 27751 .css("overflow","hidden") 27752 .css("outline",0) 27753 .addClass(uid) 27754 .addClass("ui-widget"); 27755 27756 // set up a positioning container if needed 27757 if (!/relative|absolute|fixed/.test($container.css("position"))) 27758 $container.css("position","relative"); 27759 27760 $headerScroller = $("<div class='slick-header ui-state-default' style='overflow:hidden;position:relative;' />").appendTo($container); 27761 $headers = $("<div class='slick-header-columns' style='width:100000px; left:-10000px' />").appendTo($headerScroller); 27762 27763 $secondaryHeaderScroller = $("<div class='slick-header-secondary ui-state-default' style='overflow:hidden;position:relative;' />").appendTo($container); 27764 $secondaryHeaders = $("<div class='slick-header-columns-secondary' style='width:100000px' />").appendTo($secondaryHeaderScroller); 27765 27766 if (!options.showSecondaryHeaderRow) { 27767 $secondaryHeaderScroller.hide(); 27768 } 27769 27770 $viewport = $("<div class='slick-viewport' tabIndex='0' hideFocus style='width:100%;overflow-x:auto;outline:0;position:relative;overflow-y:auto;'>").appendTo($container); 27771 $canvas = $("<div class='grid-canvas' tabIndex='0' hideFocus />").appendTo($viewport); 27772 27773 // header columns and cells may have different padding/border skewing width calculations (box-sizing, hello?) 27774 // calculate the diff so we can set consistent sizes 27775 measureCellPaddingAndBorder(); 27776 27777 $viewport.height( 27778 $container.innerHeight() - 27779 $headerScroller.outerHeight() - 27780 (options.showSecondaryHeaderRow ? $secondaryHeaderScroller.outerHeight() : 0)); 27781 27782 // for usability reasons, all text selection in SlickGrid is disabled 27783 // with the exception of input and textarea elements (selection must 27784 // be enabled there so that editors work as expected); note that 27785 // selection in grid cells (grid body) is already unavailable in 27786 // all browsers except IE 27787 disableSelection($headers); // disable all text selection in header (including input and textarea) 27788 $viewport.bind("selectstart.ui", function (event) { return $(event.target).is("input,textarea"); }); // disable text selection in grid cells except in input and textarea elements (this is IE-specific, because selectstart event will only fire in IE) 27789 27790 createColumnHeaders(); 27791 setupColumnSort(); 27792 setupDragEvents(); 27793 createCssRules(); 27794 27795 resizeAndRender(); 27796
27797 bindAncestorScrollEvents(); 27798 $viewport.bind("scroll.slickgrid", handleScroll); 27799 $container.bind("resize.slickgrid", resizeAndRender); 27800 $canvas.bind("keydown.slickgrid", handleKeyDown); 27801 $canvas.bind("click.slickgrid", handleClick); 27802 $canvas.bind("dblclick.slickgrid", handleDblClick); 27803 $canvas.bind("contextmenu.slickgrid", handleContextMenu); 27804 $canvas.bind("mouseover.slickgrid", handleHover); 27805 $headerScroller.bind("contextmenu.slickgrid", handleHeaderContextMenu); 27806 $headerScroller.bind("click.slickgrid", handleHeaderClick); 27807 } 27808 27809 function measureScrollbar() { 27810 /// <summary> 27811 /// Measure width of a vertical scrollbar 27812 /// and height of a horizontal scrollbar. 27813 /// </summary 27814 /// <returns> 27815 /// { width: pixelWidth, height: pixelHeight } 27816 /// </returns> 27817 var $c = $("<div style='position:absolute; top:-10000px; left:-10000px; width:100px; height:100px; overflow:scroll;'></div>").appendTo("body"); 27818 var dim = { width: $c.width() - $c[0].clientWidth, height: $c.height() - $c[0].clientHeight }; 27819 $c.remove(); 27820 return dim; 27821 } 27822 27823 function setCanvasWidth(width) { 27824 $canvas.width(width); 27825 viewportHasHScroll = (width > viewportW - scrollbarDimensions.width); 27826 } 27827 27828 function disableSelection($target) { 27829 /// <summary> 27830 /// Disable text selection (using mouse) in 27831 /// the specified target. 27832 /// </summary 27833 if ($target && $target.jquery) { 27834 $target.attr('unselectable', 'on').css('MozUserSelect', 'none').bind('selectstart.ui', function() { return false; }); // from jquery:ui.core.js 1.7.2 27835 } 27836 } 27837 27838 function defaultGetLength() { 27839 /// <summary> 27840 /// Default implementation of getLength method 27841 /// returns the length of the array. 27842 /// </summary 27843 return gridData.length; 27844 } 27845 27846 function defaultGetItem(i) { 27847 /// <summary> 27848 /// Default implementation of getItem method 27849 /// returns the item at specified position in 27850 /// the array. 27851 /// </summary 27852 return gridData[i]; 27853 } 27854 27855 function getMaxSupportedCssHeight() { 27856 var increment = 1000000; 27857 var supportedHeight = 0; 27858 // FF reports the height back but still renders blank after ~6M px 27859 var testUpTo = ($.browser.mozilla) ? 5000000 : 1000000000; 27860 var div = $("<div style='display:none' />").appendTo(document.body); 27861 27862 while (supportedHeight <= testUpTo) { 27863 div.css("height", supportedHeight + increment); 27864 if (div.height() !== supportedHeight + increment) 27865 break; 27866 else 27867 supportedHeight += increment; 27868 } 27869 27870 div.remove(); 27871 return supportedHeight; 27872 } 27873 27874 // TODO: this is static. need to handle page mutation. 27875 function bindAncestorScrollEvents() { 27876 var elem = $canvas[0]; 27877 while ((elem = elem.parentNode) != document.body) { 27878 // bind to scroll containers only 27879 if (elem == $viewport[0] || elem.scrollWidth != elem.clientWidth || elem.scrollHeight != elem.clientHeight)
27880 $(elem).bind("scroll.slickgrid", handleCurrentCellPositionChange); 27881 } 27882 } 27883 27884 function unbindAncestorScrollEvents() { 27885 $canvas.parents().unbind("scroll.slickgrid"); 27886 } 27887 27888 function createColumnHeaders() { 27889 var i; 27890 27891 function hoverBegin() { 27892 $(this).addClass("ui-state-hover"); 27893 } 27894 function hoverEnd() { 27895 $(this).removeClass("ui-state-hover"); 27896 } 27897 27898 $headers.empty(); 27899 columnsById = {}; 27900 27901 for (i = 0; i < columns.length; i++) { 27902 var m = columns[i] = $.extend({},columnDefaults,columns[i]); 27903 columnsById[m.id] = i; 27904 27905 var header = $("<div class='ui-state-default slick-header-column' id='" + uid + m.id + "' />") 27906 .html("<span class='slick-column-name'>" + m.name + "</span>") 27907 .width((m.currentWidth || m.width) - headerColumnWidthDiff) 27908 .attr("title", m.toolTip || m.name || "") 27909 .data("fieldId", m.id) 27910 .appendTo($headers); 27911 27912 if (options.enableColumnReorder || m.sortable) { 27913 header.hover(hoverBegin, hoverEnd); 27914 } 27915 27916 if (m.sortable) { 27917 header.append("<span class='slick-sort-indicator' />"); 27918 } 27919 } 27920 27921 setSortColumn(sortColumnId,sortAsc); 27922 setupColumnResize(); 27923 if (options.enableColumnReorder) { 27924 setupColumnReorder(); 27925 } 27926 } 27927 27928 function setupColumnSort() { 27929 $headers.click(function(e) { 27930 if ($(e.target).hasClass("slick-resizable-handle")) { 27931 return; 27932 } 27933 27934 if (self.onSort) { 27935 var $col = $(e.target).closest(".slick-header-column"); 27936 if (!$col.length) 27937 return; 27938 27939 var column = columns[getSiblingIndex($col[0])]; 27940 if (column.sortable) { 27941 if (!options.editorLock.commitCurrentEdit()) 27942 return; 27943 27944 if (column.id === sortColumnId) { 27945 sortAsc = !sortAsc; 27946 } 27947 else { 27948 sortColumnId = column.id; 27949 sortAsc = true; 27950 } 27951 27952 setSortColumn(sortColumnId,sortAsc); 27953 self.onSort(column,sortAsc); 27954 } 27955 } 27956 }); 27957 } 27958 27959 function setupColumnReorder() { 27960 $headers.sortable({ 27961 containment: "parent", 27962 axis: "x", 27963 cursor: "default", 27964 tolerance: "intersection", 27965 helper: "clone", 27966 placeholder: "slick-sortable-placeholder ui-state-default slick-header-column", 27967 forcePlaceholderSize: true, 27968 start: function(e, ui) { $(ui.helper).addClass("slick-header-column-active"); }, 27969 beforeStop: function(e, ui) { $(ui.helper).removeClass("slick-header-column-active"); }, 27970 stop: function(e) { 27971 if (!options.editorLock.commitCurrentEdit()) { 27972 $(this).sortable("cancel"); 27973 return; 27974 } 27975 27976 var reorderedIds = $headers.sortable("toArray"); 27977 var reorderedColumns = []; 27978 for (var i=0; i<reorderedIds.length; i++) { 27979 reorderedColumns.push(columns[getColumnIndex(reorderedIds[i].replace(uid,""))]); 27980 } 27981 setColumns(reorderedColumns); 27982 27983 if (self.onColumnsReordered) { 27984 self.onColumnsReordered(); 27985 } 27986 e.stopPropagation(); 27987 setupColumnResize(); 27988 } 27989 }); 27990 } 27991 27992 function setupColumnResize() { 27993 var $col, j, c, pageX, columnElements, minPageX, maxPageX, firstResizable, lastResizable, originalCanvasWidth; 27994 columnElements = $headers.children(); 27995 columnElements.find(".slick-resizable-handle").remove(); 27996 columnElements.each(function(i,e) { 27997 if (columns[i].resizable) { 27998 if (firstResizable === undefined) { firstResizable = i; } 27999 lastResizable = i; 28000 } 28001 }); 28002 columnElements.each(function(i,e) {
28003 if ((firstResizable !== undefined && i < firstResizable) || (options.forceFitColumns && i >= lastResizable)) { return; } 28004 $col = $(e); 28005 $("<div class='slick-resizable-handle' />") 28006 .appendTo(e) 28007 .bind("dragstart", function(e,dd) { 28008 if (!options.editorLock.commitCurrentEdit()) { return false; } 28009 pageX = e.pageX; 28010 $(this).parent().addClass("slick-header-column-active"); 28011 var shrinkLeewayOnRight = null, stretchLeewayOnRight = null; 28012 // lock each column's width option to current width 28013 columnElements.each(function(i,e) { columns[i].previousWidth = $(e).outerWidth(); }); 28014 if (options.forceFitColumns) { 28015 shrinkLeewayOnRight = 0; 28016 stretchLeewayOnRight = 0; 28017 // colums on right affect maxPageX/minPageX 28018 for (j = i + 1; j < columnElements.length; j++) { 28019 c = columns[j]; 28020 if (c.resizable) { 28021 if (stretchLeewayOnRight !== null) { 28022 if (c.maxWidth) { 28023 stretchLeewayOnRight += c.maxWidth - c.previousWidth; 28024 } 28025 else { 28026 stretchLeewayOnRight = null; 28027 } 28028 } 28029 shrinkLeewayOnRight += c.previousWidth - Math.max(c.minWidth || 0, absoluteColumnMinWidth); 28030 } 28031 } 28032 } 28033 var shrinkLeewayOnLeft = 0, stretchLeewayOnLeft = 0; 28034 for (j = 0; j <= i; j++) { 28035 // columns on left only affect minPageX 28036 c = columns[j]; 28037 if (c.resizable) { 28038 if (stretchLeewayOnLeft !== null) { 28039 if (c.maxWidth) { 28040 stretchLeewayOnLeft += c.maxWidth - c.previousWidth; 28041 } 28042 else { 28043 stretchLeewayOnLeft = null; 28044 } 28045 } 28046 shrinkLeewayOnLeft += c.previousWidth - Math.max(c.minWidth || 0, absoluteColumnMinWidth); 28047 } 28048 } 28049 if (shrinkLeewayOnRight === null) { shrinkLeewayOnRight = 100000; } 28050 if (shrinkLeewayOnLeft === null) { shrinkLeewayOnLeft = 100000; } 28051 if (stretchLeewayOnRight === null) { stretchLeewayOnRight = 100000; } 28052 if (stretchLeewayOnLeft === null) { stretchLeewayOnLeft = 100000; } 28053 maxPageX = pageX + Math.min(shrinkLeewayOnRight, stretchLeewayOnLeft); 28054 minPageX = pageX - Math.min(shrinkLeewayOnLeft, stretchLeewayOnRight); 28055 originalCanvasWidth = $canvas.width(); 28056 }) 28057 .bind("drag", function(e,dd) { 28058 var actualMinWidth, d = Math.min(maxPageX, Math.max(minPageX, e.pageX)) - pageX, x, ci; 28059 if (d < 0) { // shrink column 28060 x = d; 28061 for (j = i; j >= 0; j--) { 28062 c = columns[j]; 28063 if (c.resizable) { 28064 actualMinWidth = Math.max(c.minWidth || 0, absoluteColumnMinWidth); 28065 if (x && c.previousWidth + x < actualMinWidth) { 28066 x += c.previousWidth - actualMinWidth; 28067 styleColumnWidth(j, actualMinWidth, options.syncColumnCellResize); 28068 } else { 28069 styleColumnWidth(j, c.previousWidth + x, options.syncColumnCellResize); 28070 x = 0; 28071 } 28072 } 28073 } 28074 28075 if (options.forceFitColumns) { 28076 x = -d; 28077 for (j = i + 1; j < columnElements.length; j++) { 28078 c = columns[j]; 28079 if (c.resizable) { 28080 if (x && c.maxWidth && (c.maxWidth - c.previousWidth < x)) { 28081 x -= c.maxWidth - c.previousWidth; 28082 styleColumnWidth(j, c.maxWidth, options.syncColumnCellResize); 28083 } else { 28084 styleColumnWidth(j, c.previousWidth + x, options.syncColumnCellResize); 28085 x = 0; 28086 } 28087 } 28088 } 28089 } else if (options.syncColumnCellResize) { 28090 setCanvasWidth(originalCanvasWidth + d); 28091 } 28092 } else { // stretch column 28093 x = d; 28094 for (j = i; j >= 0; j--) { 28095 c = columns[j]; 28096 if (c.resizable) { 28097 if (x && c.maxWidth && (c.maxWidth - c.previousWidth < x)) { 28098 x -= c.maxWidth - c.previousWidth; 28099 styleColumnWidth(j, c.maxWidth, options.syncColumnCellResize); 28100 } else { 28101 styleColumnWidth(j, c.previousWidth + x, options.syncColumnCellResize); 28102 x = 0; 28103 } 28104 } 28105 } 28106 28107 if (options.forceFitColumns) { 28108 x = -d; 28109 for (j = i + 1; j < columnElements.length; j++) { 28110 c = columns[j]; 28111 if (c.resizable) { 28112 actualMinWidth = Math.max(c.minWidth || 0, absoluteColumnMinWidth); 28113 if (x && c.previousWidth + x < actualMinWidth) { 28114 x += c.previousWidth - actualMinWidth; 28115 styleColumnWidth(j, actualMinWidth, options.syncColumnCellResize); 28116 } else { 28117 styleColumnWidth(j, c.previousWidth + x, options.syncColumnCellResize); 28118 x = 0; 28119 } 28120 } 28121 } 28122 } else if (options.syncColumnCellResize) { 28123 setCanvasWidth(originalCanvasWidth + d); 28124 } 28125 } 28126 }) 28127 .bind("dragend", function(e,dd) { 28128 var newWidth; 28129 $(this).parent().removeClass("slick-header-column-active"); 28130 for (j = 0; j < columnElements.length; j++) { 28131 c = columns[j]; 28132 newWidth = $(columnElements[j]).outerWidth(); 28133 28134 if (c.previousWidth !== newWidth && c.rerenderOnResize) { 28135 removeAllRows(); 28136 } 28137 if (options.forceFitColumns) { 28138 c.width = Math.floor(c.width * (newWidth - c.previousWidth) / c.previousWidth) + c.width; 28139 } else { 28140 c.width = newWidth; 28141 } 28142 if (!options.syncColumnCellResize && c.previousWidth !== newWidth) { 28143 styleColumnWidth(j, newWidth, true); 28144 } 28145 } 28146 resizeCanvas(); 28147 if (self.onColumnsResized) { 28148 self.onColumnsResized(); 28149 } 28150 }); 28151 }); 28152 } 28153 28154 function setupDragEvents() { 28155 var MOVE_ROWS = 1; 28156 var SELECT_CELLS = 2; 28157 28158 function fixUpRange(range) { 28159 var r1 = Math.min(range.start.row,range.end.row); 28160 var c1 = Math.min(range.start.cell,range.end.cell); 28161 var r2 = Math.max(range.start.row,range.end.row); 28162 var c2 = Math.max(range.start.cell,range.end.cell); 28163 return { 28164 start: {row:r1, cell:c1}, 28165 end: {row:r2, cell:c2} 28166 }; 28167 } 28168 28169 $canvas 28170 .bind("draginit", function(e,dd) { 28171 var $cell = $(e.target).closest(".slick-cell"); 28172 if ($cell.length === 0) { return false; } 28173 if (parseInt($cell.parent().attr("row"), 10) >= gridDataGetLength()) 28174 return false;
28175 28176 var colDef = columns[getSiblingIndex($cell[0])]; 28177 if (colDef.behavior == "move" || colDef.behavior == "selectAndMove") { 28178 dd.mode = MOVE_ROWS; 28179 } 28180 else if (options.enableCellRangeSelection) { 28181 dd.mode = SELECT_CELLS; 28182 } 28183 else 28184 return false; 28185 }) 28186 .bind("dragstart", function(e,dd) { 28187 if (!options.editorLock.commitCurrentEdit()) { return false; } 28188 var row = parseInt($(e.target).closest(".slick-row").attr("row"), 10); 28189 28190 if (dd.mode == MOVE_ROWS) { 28191 if (!selectedRowsLookup[row]) { 28192 setSelectedRows([row]); 28193 } 28194 28195 dd.selectionProxy = $("<div class='slick-reorder-proxy'/>") 28196 .css("position", "absolute") 28197 .css("zIndex", "99999") 28198 .css("width", $(this).innerWidth()) 28199 .css("height", options.rowHeight*selectedRows.length) 28200 .appendTo($viewport); 28201 28202 dd.guide = $("<div class='slick-reorder-guide'/>") 28203 .css("position", "absolute") 28204 .css("zIndex", "99998") 28205 .css("width", $(this).innerWidth()) 28206 .css("top", -1000) 28207 .appendTo($viewport); 28208 28209 dd.insertBefore = -1; 28210 } 28211 28212 if (dd.mode == SELECT_CELLS) { 28213 var start = getCellFromPoint(dd.startX - $canvas.offset().left, dd.startY - $canvas.offset().top); 28214 if (!cellExists(start.row,start.cell)) 28215 return false; 28216 28217 dd.range = {start:start,end:{}}; 28218 return $("<div class='slick-selection'></div>").appendTo($canvas); 28219 } 28220 }) 28221 .bind("drag", function(e,dd) { 28222 if (dd.mode == MOVE_ROWS) { 28223 var top = e.pageY - $(this).offset().top; 28224 dd.selectionProxy.css("top",top-5); 28225 28226 var insertBefore = Math.max(0,Math.min(Math.round(top/options.rowHeight),gridDataGetLength())); 28227 if (insertBefore !== dd.insertBefore) { 28228 if (self.onBeforeMoveRows && self.onBeforeMoveRows(getSelectedRows(),insertBefore) === false) { 28229 dd.guide.css("top", -1000); 28230 dd.canMove = false; 28231 } 28232 else { 28233 dd.guide.css("top",insertBefore*options.rowHeight); 28234 dd.canMove = true; 28235 } 28236 dd.insertBefore = insertBefore; 28237 } 28238 } 28239 28240 if (dd.mode == SELECT_CELLS) { 28241 var end = getCellFromPoint(e.clientX - $canvas.offset().left, e.clientY - $canvas.offset().top); 28242 if (!cellExists(end.row,end.cell)) 28243 return; 28244 28245 dd.range.end = end; 28246 var r = fixUpRange(dd.range); 28247 var from = getCellNodeBox(r.start.row,r.start.cell); 28248 var to = getCellNodeBox(r.end.row,r.end.cell); 28249 $(dd.proxy).css({ 28250 top: from.top, 28251 left: from.left, 28252 height: to.bottom - from.top - 2, 28253 width: to.right - from.left - 2 28254 }); 28255 } 28256 }) 28257 .bind("dragend", function(e,dd) { 28258 if (dd.mode == MOVE_ROWS) { 28259 dd.guide.remove(); 28260 dd.selectionProxy.remove(); 28261 if (self.onMoveRows && dd.canMove) { 28262 self.onMoveRows(getSelectedRows(),dd.insertBefore); 28263 } 28264 } 28265 28266 if (dd.mode == SELECT_CELLS) { 28267 $(dd.proxy).remove(); 28268 28269 if (self.onCellRangeSelected) 28270 self.onCellRangeSelected(fixUpRange(dd.range)); 28271 } 28272 }); 28273 } 28274 28275 function measureCellPaddingAndBorder() { 28276 var tmp = $("<div class='ui-state-default slick-header-column' style='visibility:hidden'>-</div>").appendTo($headers); 28277 headerColumnWidthDiff = tmp.outerWidth() - tmp.width(); 28278 headerColumnHeightDiff = tmp.outerHeight() - tmp.height(); 28279 tmp.remove(); 28280 28281 var r = $("<div class='slick-row' />").appendTo($canvas); 28282 tmp = $("<div class='slick-cell' id='' style='visibility:hidden'>-</div>").appendTo(r); 28283 cellWidthDiff = tmp.outerWidth() - tmp.width(); 28284 cellHeightDiff = tmp.outerHeight() - tmp.height(); 28285 r.remove(); 28286 28287 absoluteColumnMinWidth = Math.max(headerColumnWidthDiff,cellWidthDiff); 28288 } 28289 28290 function createCssRules() { 28291 $style = $("<style type='text/css' rel='stylesheet' />").appendTo($("head")); 28292 var rowHeight = (options.rowHeight - cellHeightDiff); 28293 28294 var rules = [ 28295 "." + uid + " .slick-header-column { left: 10000px; }", 28296 "." + uid + " .slick-header-columns-secondary { height:" + options.secondaryHeaderRowHeight + "px; }", 28297 "." + uid + " .slick-cell { height:" + rowHeight + "px; }" 28298 ]; 28299 28300 for (var i=0; i<columns.length; i++) { 28301 rules.push( 28302 "." + uid + " .c" + i + " { " +
28303 "width:" + ((columns[i].currentWidth || columns[i].width) - cellWidthDiff) + "px; " + 28304 " } "); 28305 } 28306 28307 if ($style[0].styleSheet) { // IE 28308 $style[0].styleSheet.cssText = rules.join(""); 28309 } 28310 else { 28311 $style[0].appendChild(document.createTextNode(rules.join(" "))); 28312 } 28313 28314 var sheets = document.styleSheets; 28315 for (var i=0; i<sheets.length; i++) { 28316 if ((sheets[i].ownerNode || sheets[i].owningElement) == $style[0]) { 28317 stylesheet = sheets[i]; 28318 break; 28319 } 28320 } 28321 } 28322 28323 function findCssRule(selector) { 28324 var rules = (stylesheet.cssRules || stylesheet.rules); 28325 28326 for (var i=0; i<rules.length; i++) { 28327 if (rules[i].selectorText == selector) 28328 return rules[i]; 28329 } 28330 28331 return null; 28332 } 28333 28334 function findCssRuleForCell(index) { 28335 return findCssRule("." + uid + " .c" + index); 28336 } 28337 28338 function removeCssRules() { 28339 $style.remove(); 28340 } 28341 28342 function destroy() { 28343 options.editorLock.cancelCurrentEdit(); 28344 28345 if (self.onBeforeDestroy) 28346 self.onBeforeDestroy(); 28347 28348 if (options.enableColumnReorder && $headers.sortable) 28349 $headers.sortable("destroy"); 28350 28351 unbindAncestorScrollEvents(); 28352 $container.unbind(".slickgrid"); 28353 removeCssRules(); 28354 28355 $canvas.unbind("draginit dragstart dragend drag"); 28356 $container.empty().removeClass(uid); 28357 } 28358 28359
28360 ////////////////////////////////////////////////////////////////////////////////////////////// 28361 // General 28362 28363 function getEditController() { 28364 return editController; 28365 } 28366 28367 function getColumnIndex(id) { 28368 return columnsById[id]; 28369 } 28370 28371 function autosizeColumns() { 28372 var i, c, 28373 widths = [], 28374 shrinkLeeway = 0, 28375 viewportW = $viewport.innerWidth(), // may not be initialized yet 28376 availWidth = (options.autoHeight ? viewportW : viewportW - scrollbarDimensions.width), // with AutoHeight, we do not need to accomodate the vertical scroll bar 28377 total = 0, 28378 existingTotal = 0; 28379 28380 for (i = 0; i < columns.length; i++) { 28381 c = columns[i]; 28382 widths.push(c.width); 28383 existingTotal += c.width; 28384 shrinkLeeway += c.width - Math.max(c.minWidth || 0, absoluteColumnMinWidth); 28385 } 28386 28387 total = existingTotal; 28388 28389 removeAllRows(); 28390 28391 // shrink 28392 while (total > availWidth) { 28393 if (!shrinkLeeway) { return; } 28394 var shrinkProportion = (total - availWidth) / shrinkLeeway; 28395 for (i = 0; i < columns.length && total > availWidth; i++) { 28396 c = columns[i]; 28397 if (!c.resizable || c.minWidth === c.width || c.width === absoluteColumnMinWidth) { continue; } 28398 var shrinkSize = Math.floor(shrinkProportion * (c.width - Math.max(c.minWidth || 0, absoluteColumnMinWidth))) || 1; 28399 total -= shrinkSize; 28400 widths[i] -= shrinkSize; 28401 } 28402 } 28403 28404 // grow 28405 var previousTotal = total; 28406 while (total < availWidth) { 28407 var growProportion = availWidth / total; 28408 for (i = 0; i < columns.length && total < availWidth; i++) { 28409 c = columns[i]; 28410 if (!c.resizable || c.maxWidth <= c.width) { continue; } 28411 var growSize = Math.min(Math.floor(growProportion * c.width) - c.width, (c.maxWidth - c.width) || 1000000) || 1; 28412 total += growSize; 28413 widths[i] += growSize; 28414 } 28415 if (previousTotal == total) break; // if total is not changing, will result in infinite loop 28416 previousTotal = total; 28417 } 28418 28419 for (i=0; i<columns.length; i++) { 28420 styleColumnWidth(i, columns[i].currentWidth = widths[i], true); 28421 } 28422 28423 resizeCanvas(); 28424 } 28425 28426 function styleColumnWidth(index,width,styleCells) { 28427 columns[index].currentWidth = width; 28428 $headers.children().eq(index).css("width", width - headerColumnWidthDiff); 28429 if (styleCells) { 28430 findCssRuleForCell(index).style.width = (width - cellWidthDiff) + "px"; 28431 } 28432 } 28433 28434 function setSortColumn(columnId, ascending) { 28435 sortColumnId = columnId; 28436 sortAsc = ascending; 28437 var columnIndex = getColumnIndex(sortColumnId); 28438 28439 $headers.children().removeClass("slick-header-column-sorted"); 28440 $headers.find(".slick-sort-indicator").removeClass("slick-sort-indicator-asc slick-sort-indicator-desc"); 28441 28442 if (columnIndex != null) { 28443 $headers.children().eq(columnIndex) 28444 .addClass("slick-header-column-sorted") 28445 .find(".slick-sort-indicator") 28446 .addClass(sortAsc ? "slick-sort-indicator-asc" : "slick-sort-indicator-desc"); 28447 } 28448 } 28449 28450 function getSelectedRows() { 28451 return selectedRows.concat(); 28452 } 28453 28454 function setSelectedRows(rows) { 28455 var i, row; 28456 var lookup = {}; 28457 for (i=0; i<rows.length; i++) { 28458 lookup[rows[i]] = true; 28459 } 28460 28461 // unselect old rows 28462 for (i=0; i<selectedRows.length; i++) { 28463 row = selectedRows[i]; 28464 if (rowsCache[row] && !lookup[row]) { 28465 $(rowsCache[row]).removeClass("ui-state-active selected"); 28466 } 28467 } 28468 28469 // select new ones 28470 for (i=0; i<rows.length; i++) { 28471 row = rows[i]; 28472 if (rowsCache[row] && !selectedRowsLookup[row]) { 28473 $(rowsCache[row]).addClass("ui-state-active selected"); 28474 } 28475 } 28476 28477 selectedRows = rows.concat(); 28478 selectedRowsLookup = lookup; 28479 } 28480 28481 function getColumns() { 28482 return columns; 28483 } 28484 28485 function setColumns(columnDefinitions) { 28486 columns = columnDefinitions; 28487 removeAllRows(); 28488 createColumnHeaders(); 28489 removeCssRules(); 28490 createCssRules(); 28491 resizeAndRender(); 28492 handleScroll(); 28493 } 28494 28495 function getOptions() { 28496 return options; 28497 } 28498 28499 function setOptions(args) { 28500 if (!options.editorLock.commitCurrentEdit()) { 28501 return; 28502 } 28503 28504 makeSelectedCellNormal(); 28505 28506 if (options.enableAddRow !== args.enableAddRow) { 28507 removeRow(gridDataGetLength()); 28508 } 28509
28510 options = $.extend(options,args); 28511 28512 render(); 28513 } 28514 28515 function setData(newData,scrollToTop) { 28516 removeAllRows(); 28517 data = newData; 28518 gridData = data; 28519 gridDataGetLength = gridData.getLength || defaultGetLength; 28520 gridDataGetItem = gridData.getItem || defaultGetItem; 28521 if (scrollToTop) 28522 scrollTo(0); 28523 } 28524 28525 function getData() { 28526 return gridData; 28527 } 28528 28529 function getSecondaryHeaderRow() { 28530 return $secondaryHeaders[0]; 28531 } 28532 28533 function showSecondaryHeaderRow() { 28534 options.showSecondaryHeaderRow = true; 28535 $secondaryHeaderScroller.slideDown("fast", resizeCanvas); 28536 } 28537 28538 function hideSecondaryHeaderRow() { 28539 options.showSecondaryHeaderRow = false; 28540 $secondaryHeaderScroller.slideUp("fast", resizeCanvas); 28541 } 28542 28543 ////////////////////////////////////////////////////////////////////////////////////////////// 28544 // Rendering / Scrolling 28545 28546 function scrollTo(y) { 28547 var oldOffset = offset; 28548 28549 page = Math.min(n-1, Math.floor(y / ph)); 28550 offset = Math.round(page * cj); 28551 var newScrollTop = y - offset; 28552 28553 if (offset != oldOffset) { 28554 var range = getVisibleRange(newScrollTop); 28555 cleanupRows(range.top,range.bottom); 28556 updateRowPositions(); 28557 } 28558 28559 if (prevScrollTop != newScrollTop) { 28560 scrollDir = (prevScrollTop + oldOffset < newScrollTop + offset) ? 1 : -1; 28561 $viewport[0].scrollTop = (lastRenderedScrollTop = scrollTop = prevScrollTop = newScrollTop); 28562 28563 if (self.onViewportChanged) { 28564 self.onViewportChanged(); 28565 } 28566 } 28567 } 28568 28569 function defaultFormatter(row, cell, value, columnDef, dataContext) { 28570 return (value === null || value === undefined) ? "" : value; 28571 } 28572 28573 function getFormatter(column) { 28574 return column.formatter || 28575 (options.formatterFactory && options.formatterFactory.getFormatter(column)) || 28576 defaultFormatter; 28577 } 28578 28579 function getEditor(column) { 28580 return column.editor || (options.editorFactory && options.editorFactory.getEditor(column)); 28581 } 28582
28583 function appendRowHtml(stringArray,row) { 28584 var d = gridDataGetItem(row); 28585 var dataLoading = row < gridDataGetLength() && !d; 28586 var cellCss; 28587 var css = "slick-row " + 28588 (dataLoading ? " loading" : "") + 28589 (selectedRowsLookup[row] ? " selected ui-state-active" : "") + 28590 (row % 2 == 1 ? ' odd' : ' even'); 28591 28592 // if the user has specified a function to provide additional per-row css classes, call it here 28593 if (options.rowCssClasses) { 28594 css += ' ' + options.rowCssClasses(d); 28595 } 28596 28597 stringArray.push("<div class='ui-widget-content " + css + "' row='" + row + "' style='top:" + (options.rowHeight*row-offset) + "px'>"); 28598 28599 for (var i=0, cols=columns.length; i<cols; i++) { 28600 var m = columns[i]; 28601 28602 cellCss = "slick-cell c" + i + (m.cssClass ? " " + m.cssClass : ""); 28603 if (highlightedCells && highlightedCells[row] && highlightedCells[row][m.id]) 28604 cellCss += (" " + options.cellHighlightCssClass); 28605 28606 stringArray.push("<div class='" + cellCss + "'>"); 28607 28608 // if there is a corresponding row (if not, this is the Add New row or this data hasn't been loaded yet) 28609 if (d) { 28610 stringArray.push(getFormatter(m)(row, i, d[m.field], m, d)); 28611 } 28612 28613 stringArray.push("</div>"); 28614 } 28615 28616 stringArray.push("</div>"); 28617 } 28618 28619 function cleanupRows(rangeToKeep) { 28620 for (var i in rowsCache) { 28621 if (((i = parseInt(i, 10)) !== currentRow) && (i < rangeToKeep.top || i > rangeToKeep.bottom)) { 28622 removeRowFromCache(i); 28623 } 28624 } 28625 } 28626
28627 function invalidate() { 28628 updateRowCount(); 28629 removeAllRows(); 28630 render(); 28631 } 28632 28633 function removeAllRows() { 28634 if (currentEditor) { 28635 makeSelectedCellNormal(); 28636 } 28637// modified 9/26/2011 timd 28638 if($canvas && $canvas.length) 28639 $canvas[0].innerHTML = ""; 28640// end modification 28641 rowsCache= {}; 28642 postProcessedRows = {}; 28643 counter_rows_removed += renderedRows; 28644 renderedRows = 0; 28645 } 28646 28647 function removeRowFromCache(row) { 28648 var node = rowsCache[row]; 28649 if (!node) { return; } 28650 $canvas[0].removeChild(node); 28651 28652 delete rowsCache[row]; 28653 delete postProcessedRows[row]; 28654 renderedRows--; 28655 counter_rows_removed++; 28656 } 28657 28658 function removeRows(rows) { 28659 var i, rl, nl; 28660 if (!rows || !rows.length) { return; } 28661 scrollDir = 0; 28662 var nodes = []; 28663 for (i=0, rl=rows.length; i<rl; i++) { 28664 if (currentEditor && currentRow === i) { 28665 makeSelectedCellNormal(); 28666 } 28667 28668 if (rowsCache[rows[i]]) { 28669 nodes.push(rows[i]); 28670 } 28671 } 28672 28673 if (renderedRows > 10 && nodes.length === renderedRows) { 28674 removeAllRows(); 28675 } 28676 else { 28677 for (i=0, nl=nodes.length; i<nl; i++) { 28678 removeRowFromCache(nodes[i]); 28679 } 28680 } 28681 } 28682 28683 function removeRow(row) { 28684 removeRows([row]); 28685 } 28686 28687 function updateCell(row,cell) { 28688 if (!rowsCache[row]) { return; } 28689 var $cell = $(rowsCache[row]).children().eq(cell); 28690 if ($cell.length === 0) { return; } 28691 28692 var m = columns[cell], d = gridDataGetItem(row); 28693 if (currentEditor && currentRow === row && currentCell === cell) { 28694 currentEditor.loadValue(d); 28695 } 28696 else { 28697 $cell[0].innerHTML = d ? getFormatter(m)(row, cell, d[m.field], m, d) : ""; 28698 invalidatePostProcessingResults(row); 28699 } 28700 } 28701 28702 function updateRow(row) { 28703 if (!rowsCache[row]) { return; } 28704 28705 $(rowsCache[row]).children().each(function(i) { 28706 var m = columns[i]; 28707 if (row === currentRow && i === currentCell && currentEditor) { 28708 currentEditor.loadValue(gridDataGetItem(currentRow)); 28709 } 28710 else if (gridDataGetItem(row)) { 28711 this.innerHTML = getFormatter(m)(row, i, gridDataGetItem(row)[m.field], m, gridDataGetItem(row)); 28712 } 28713 else { 28714 this.innerHTML = ""; 28715 } 28716 }); 28717 28718 invalidatePostProcessingResults(row); 28719 } 28720 28721 function resizeCanvas() { 28722 var newViewportH = options.rowHeight * (gridDataGetLength() + (options.enableAddRow ? 1 : 0) + (options.leaveSpaceForNewRows? numVisibleRows - 1 : 0)); 28723 if (options.autoHeight) { // use computed height to set both canvas _and_ divMainScroller, effectively hiding scroll bars. 28724 $viewport.height(newViewportH); 28725 } 28726 else { 28727 $viewport.height( 28728 $container.innerHeight() - 28729 $headerScroller.outerHeight() - 28730 (options.showSecondaryHeaderRow ? $secondaryHeaderScroller.outerHeight() : 0)); 28731 } 28732 28733 viewportW = $viewport.innerWidth(); 28734 viewportH = $viewport.innerHeight(); 28735 numVisibleRows = Math.ceil(viewportH / options.rowHeight); 28736 28737 var totalWidth = 0; 28738 $headers.find(".slick-header-column").each(function() { 28739 totalWidth += $(this).outerWidth(); 28740 }); 28741 setCanvasWidth(totalWidth); 28742 28743 updateRowCount(); 28744 render(); 28745 } 28746 28747 function resizeAndRender() { 28748 if (options.forceFitColumns) { 28749 autosizeColumns(); 28750 } else { 28751 resizeCanvas(); 28752 } 28753 } 28754 28755 function updateRowCount() { 28756 var newRowCount = gridDataGetLength() + (options.enableAddRow?1:0) + (options.leaveSpaceForNewRows?numVisibleRows-1:0); 28757 var oldH = h; 28758 28759 // remove the rows that are now outside of the data range 28760 // this helps avoid redundant calls to .removeRow() when the size of the data decreased by thousands of rows 28761 var l = options.enableAddRow ? gridDataGetLength() : gridDataGetLength() - 1; 28762 for (var i in rowsCache) { 28763 if (i >= l) {
28764 removeRowFromCache(i); 28765 } 28766 } 28767 th = Math.max(options.rowHeight * newRowCount, viewportH - scrollbarDimensions.height); 28768 if (th < maxSupportedCssHeight) { 28769 // just one page 28770 h = ph = th; 28771 n = 1; 28772 cj = 0; 28773 } 28774 else { 28775 // break into pages 28776 h = maxSupportedCssHeight; 28777 ph = h / 100; 28778 n = Math.floor(th / ph); 28779 cj = (th - h) / (n - 1); 28780 } 28781 28782 if (h !== oldH) { 28783 $canvas.css("height",h); 28784 scrollTop = $viewport[0].scrollTop; 28785 } 28786 28787 var oldScrollTopInRange = (scrollTop + offset <= th - viewportH); 28788 28789 if (th == 0 || scrollTop == 0) { 28790 page = offset = 0; 28791 } 28792 else if (oldScrollTopInRange) { 28793 // maintain virtual position 28794 scrollTo(scrollTop+offset); 28795 } 28796 else { 28797 // scroll to bottom 28798 scrollTo(th-viewportH); 28799 } 28800 28801 if (h != oldH && options.autoHeight) { 28802 resizeCanvas(); 28803 } 28804 } 28805 28806 function getVisibleRange(viewportTop) { 28807 if (viewportTop == null) 28808 viewportTop = scrollTop; 28809 28810 return { 28811 top: Math.floor((scrollTop+offset)/options.rowHeight), 28812 bottom: Math.ceil((scrollTop+offset+viewportH)/options.rowHeight) 28813 }; 28814 } 28815 28816 function getRenderedRange(viewportTop) { 28817 var range = getVisibleRange(viewportTop); 28818 var buffer = Math.round(viewportH/options.rowHeight); 28819 var minBuffer = 3; 28820 28821 if (scrollDir == -1) { 28822 range.top -= buffer; 28823 range.bottom += minBuffer; 28824 } 28825 else if (scrollDir == 1) { 28826 range.top -= minBuffer; 28827 range.bottom += buffer; 28828 } 28829 else { 28830 range.top -= minBuffer; 28831 range.bottom += minBuffer; 28832 } 28833 28834 range.top = Math.max(0,range.top); 28835 range.bottom = Math.min(options.enableAddRow ? gridDataGetLength() : gridDataGetLength() - 1,range.bottom); 28836 28837 return range; 28838 } 28839 28840 function renderRows(range) { 28841 var i, l, 28842 parentNode = $canvas[0], 28843 rowsBefore = renderedRows, 28844 stringArray = [], 28845 rows = [], 28846 startTimestamp = new Date(), 28847 needToReselectCell = false; 28848 28849 for (i = range.top; i <= range.bottom; i++) { 28850 if (rowsCache[i]) { continue; } 28851 renderedRows++; 28852 rows.push(i); 28853 appendRowHtml(stringArray,i); 28854 if (currentCellNode && currentRow === i) 28855 needToReselectCell = true; 28856 counter_rows_rendered++; 28857 } 28858 28859 var x = document.createElement("div"); 28860 x.innerHTML = stringArray.join(""); 28861 28862 for (i = 0, l = x.childNodes.length; i < l; i++) { 28863 rowsCache[rows[i]] = parentNode.appendChild(x.firstChild); 28864 } 28865 28866 if (needToReselectCell) { 28867 currentCellNode = $(rowsCache[currentRow]).children().eq(currentCell)[0]; 28868 setSelectedCell(currentCellNode,false); 28869 } 28870 28871 if (renderedRows - rowsBefore > 5) { 28872 avgRowRenderTime = (new Date() - startTimestamp) / (renderedRows - rowsBefore); 28873 } 28874 } 28875 28876 function startPostProcessing() { 28877 if (!options.enableAsyncPostRender) { return; } 28878 clearTimeout(h_postrender); 28879 h_postrender = setTimeout(asyncPostProcessRows, options.asyncPostRenderDelay); 28880 } 28881
28882 function invalidatePostProcessingResults(row) { 28883 delete postProcessedRows[row]; 28884 postProcessFromRow = Math.min(postProcessFromRow,row); 28885 postProcessToRow = Math.max(postProcessToRow,row); 28886 startPostProcessing(); 28887 } 28888 28889 function updateRowPositions() { 28890 for (var row in rowsCache) { 28891 rowsCache[row].style.top = (row*options.rowHeight-offset) + "px"; 28892 } 28893 } 28894 28895 function render() { 28896 var visible = getVisibleRange(); 28897 var rendered = getRenderedRange(); 28898 28899 // remove rows no longer in the viewport 28900 cleanupRows(rendered); 28901 28902 // add new rows 28903 renderRows(rendered); 28904 28905 postProcessFromRow = visible.top; 28906 postProcessToRow = Math.min(options.enableAddRow ? gridDataGetLength() : gridDataGetLength() - 1, visible.bottom); 28907 startPostProcessing(); 28908 28909 lastRenderedScrollTop = scrollTop; 28910 h_render = null; 28911 } 28912 28913 function handleScroll() { 28914 scrollTop = $viewport[0].scrollTop; 28915 var scrollLeft = $viewport[0].scrollLeft; 28916 var scrollDist = Math.abs(scrollTop - prevScrollTop); 28917 28918 if (scrollLeft !== prevScrollLeft) { 28919 prevScrollLeft = scrollLeft; 28920 $headerScroller[0].scrollLeft = scrollLeft; 28921 $secondaryHeaderScroller[0].scrollLeft = scrollLeft; 28922 } 28923 28924 if (!scrollDist) return; 28925 28926 scrollDir = prevScrollTop < scrollTop ? 1 : -1; 28927 prevScrollTop = scrollTop; 28928 28929 // switch virtual pages if needed 28930 if (scrollDist < viewportH) { 28931 scrollTo(scrollTop + offset); 28932 } 28933 else { 28934 var oldOffset = offset; 28935 page = Math.min(n - 1, Math.floor(scrollTop * ((th - viewportH) / (h - viewportH)) * (1 / ph))); 28936 offset = Math.round(page * cj); 28937 if (oldOffset != offset) 28938 removeAllRows(); 28939 } 28940 28941 if (h_render) 28942 clearTimeout(h_render); 28943 28944 if (Math.abs(lastRenderedScrollTop - scrollTop) < viewportH) 28945 render(); 28946 else 28947 h_render = setTimeout(render, 50); 28948 28949 if (self.onViewportChanged) { 28950 self.onViewportChanged(); 28951 } 28952 } 28953 28954 function asyncPostProcessRows() { 28955 while (postProcessFromRow <= postProcessToRow) { 28956 var row = (scrollDir >= 0) ? postProcessFromRow++ : postProcessToRow--; 28957 var rowNode = rowsCache[row]; 28958 if (!rowNode || postProcessedRows[row] || row>=gridDataGetLength()) { continue; } 28959 28960 var d = gridDataGetItem(row), cellNodes = rowNode.childNodes; 28961 for (var i=0, j=0, l=columns.length; i<l; ++i) { 28962 var m = columns[i]; 28963 if (m.asyncPostRender) { m.asyncPostRender(cellNodes[j], postProcessFromRow, d, m); } 28964 ++j; 28965 } 28966 28967 postProcessedRows[row] = true; 28968 h_postrender = setTimeout(asyncPostProcessRows, options.asyncPostRenderDelay); 28969 return; 28970 } 28971 } 28972 28973 function setHighlightedCells(cellsToHighlight) { 28974 var i, $cell, hasHighlight, hadHighlight; 28975 28976 for (var row in rowsCache) { 28977 for (i=0; i<columns.length; i++) { 28978 hadHighlight = highlightedCells && highlightedCells[row] && highlightedCells[row][columns[i].id]; 28979 hasHighlight = cellsToHighlight && cellsToHighlight[row] && cellsToHighlight[row][columns[i].id]; 28980 28981 if (hadHighlight != hasHighlight) { 28982 $cell = $(rowsCache[row]).children().eq(i); 28983 if ($cell.length) { 28984 $cell.toggleClass(options.cellHighlightCssClass); 28985 } 28986 } 28987 } 28988 } 28989 28990 highlightedCells = cellsToHighlight; 28991 } 28992 28993 function flashCell(row, cell, speed) { 28994 speed = speed || 100; 28995 if (rowsCache[row]) { 28996 var $cell = $(rowsCache[row]).children().eq(cell); 28997 28998 function toggleCellClass(times) { 28999 if (!times) return; 29000 setTimeout(function() { 29001 $cell.queue(function() { 29002 $cell.toggleClass(options.cellFlashingCssClass).dequeue(); 29003 toggleCellClass(times-1); 29004 }); 29005 }, 29006 speed); 29007 } 29008 29009 toggleCellClass(4); 29010 } 29011 } 29012
29013 ////////////////////////////////////////////////////////////////////////////////////////////// 29014 // Interactivity 29015 29016 function getSiblingIndex(node) { 29017 var idx = 0; 29018 while (node && node.previousSibling) { 29019 idx++; 29020 node = node.previousSibling; 29021 } 29022 return idx; 29023 } 29024 29025 function handleKeyDown(e) { 29026 // give registered handler chance to process the keyboard event 29027 var handled = (self.onKeyDown && // a handler must be registered 29028 !options.editorLock.isActive() && // grid must not be in edit mode; 29029 self.onKeyDown(e, currentRow, currentCell)); // handler must return truthy-value to indicate it handled the event 29030 29031 if (!handled) { 29032 if (!e.shiftKey && !e.altKey && !e.ctrlKey) { 29033 if (e.which == 27) { 29034 if (!options.editorLock.isActive()) { 29035 return; // no editing mode to cancel, allow bubbling and default processing (exit without cancelling the event) 29036 } 29037 cancelEditAndSetFocus(); 29038 } 29039 else if (e.which == 37) { 29040 navigateLeft(); 29041 } 29042 else if (e.which == 39) { 29043 navigateRight(); 29044 } 29045 else if (e.which == 38) { 29046 navigateUp(); 29047 } 29048 else if (e.which == 40) { 29049 navigateDown(); 29050 } 29051 else if (e.which == 9) { 29052 navigateNext(); 29053 } 29054 else if (e.which == 13) { 29055 if (options.editable) { 29056 if (currentEditor) { 29057 // adding new row 29058 if (currentRow === defaultGetLength()) { 29059 navigateDown(); 29060 } 29061 else { 29062 commitEditAndSetFocus(); 29063 } 29064 } else { 29065 if (options.editorLock.commitCurrentEdit()) { 29066 makeSelectedCellEditable(); 29067 } 29068 } 29069 } 29070 } 29071 else 29072 return; 29073 } 29074 else if (e.which == 9 && e.shiftKey && !e.ctrlKey && !e.altKey) { 29075 navigatePrev(); 29076 } 29077 else 29078 return; 29079 } 29080 29081 // the event has been handled so don't let parent element (bubbling/propagation) or browser (default) handle it 29082 e.stopPropagation(); 29083 e.preventDefault(); 29084 try { 29085 e.originalEvent.keyCode = 0; // prevent default behaviour for special keys in IE browsers (F3, F5, etc.) 29086 } 29087 catch (error) {} // ignore exceptions - setting the original event's keycode throws access denied exception for "Ctrl" (hitting control key only, nothing else), "Shift" (maybe others) 29088 } 29089 29090 function handleClick(e) { 29091 29092//MODIFIED 8/19/2011 timd to allow ctrl-click to open link in a new tab 29093 if(e.ctrlKey && e.target.tagName.toLowerCase() == 'a') 29094 return(true); 29095// end modified 29096 29097 var $cell = $(e.target).closest(".slick-cell", $canvas); 29098 if ($cell.length === 0) { return; } 29099 29100 // are we editing this cell? 29101 if (currentCellNode === $cell[0] && currentEditor !== null) { return; } 29102 29103 var row = parseInt($cell.parent().attr("row"), 10); 29104 var cell = getSiblingIndex($cell[0]); 29105 var validated = null; 29106 var c = columns[cell]; 29107 var item = gridDataGetItem(row); 29108 29109 // is this a 'select' column or a Ctrl/Shift-click? 29110 if (item && (c.behavior === "selectAndMove" || c.behavior === "select" || (e.ctrlKey || e.shiftKey))) { 29111 // grid must not be in edit mode 29112 validated = options.editorLock.commitCurrentEdit(); 29113 if (validated) { 29114 var selection = getSelectedRows(); 29115 var idx = $.inArray(row, selection); 29116 29117 if (!e.ctrlKey && !e.shiftKey && !e.metaKey) { 29118 selection = [row]; 29119 } 29120 else if (options.multiSelect) { 29121 if (idx === -1 && (e.ctrlKey || e.metaKey)) { 29122 selection.push(row); 29123 } 29124 else if (idx !== -1 && (e.ctrlKey || e.metaKey)) { 29125 selection = $.grep(selection, function(o, i) { return (o !== row); }); 29126 } 29127 else if (selection.length && e.shiftKey) { 29128 var last = selection.pop(); 29129 var from = Math.min(row, last); 29130 var to = Math.max(row, last); 29131 selection = []; 29132 for (var i = from; i <= to; i++) { 29133 if (i !== last) { 29134 selection.push(i); 29135 } 29136 } 29137 selection.push(last); 29138 } 29139 }
29140 resetCurrentCell(); 29141 setSelectedRows(selection); 29142 if (self.onSelectedRowsChanged) { 29143 self.onSelectedRowsChanged(); 29144 } 29145 29146 if (!$.browser.msie) { 29147 $canvas[0].focus(); 29148 } 29149 29150 return false; 29151 } 29152 } 29153 29154 // do we have any registered handlers? 29155 if (item && self.onClick) { 29156 // grid must not be in edit mode 29157 validated = options.editorLock.commitCurrentEdit(); 29158 if (validated) { 29159 // handler will return true if the event was handled 29160 if (self.onClick(e, row, cell)) { 29161 e.stopPropagation(); 29162 e.preventDefault(); 29163 return false; 29164 } 29165 } 29166 } 29167 29168 if (options.enableCellNavigation && !columns[cell].unselectable) { 29169 // commit current edit before proceeding 29170 if (validated === true || (validated === null && options.editorLock.commitCurrentEdit())) { 29171 scrollRowIntoView(row,false); 29172 setSelectedCellAndRow($cell[0], (row === defaultGetLength()) || options.autoEdit); 29173 } 29174 } 29175 } 29176 29177 function handleContextMenu(e) { 29178 var $cell = $(e.target).closest(".slick-cell", $canvas); 29179 if ($cell.length === 0) { return; } 29180 29181 // are we editing this cell? 29182 if (currentCellNode === $cell[0] && currentEditor !== null) { return; } 29183 29184 var row = parseInt($cell.parent().attr("row"), 10); 29185 var cell = getSiblingIndex($cell[0]); 29186 var validated = null; 29187 29188 // do we have any registered handlers? 29189 if (gridDataGetItem(row) && self.onContextMenu) { 29190 // grid must not be in edit mode 29191 validated = options.editorLock.commitCurrentEdit(); 29192 if (validated) { 29193 // handler will return true if the event was handled 29194 if (self.onContextMenu(e, row, cell)) { 29195 e.stopPropagation(); 29196 e.preventDefault(); 29197 return false; 29198 } 29199 } 29200 } 29201 } 29202 29203 function handleDblClick(e) { 29204 var $cell = $(e.target).closest(".slick-cell", $canvas); 29205 if ($cell.length === 0) { return; } 29206 29207 // are we editing this cell? 29208 if (currentCellNode === $cell[0] && currentEditor !== null) { return; } 29209 29210 var row = parseInt($cell.parent().attr("row"), 10); 29211 var cell = getSiblingIndex($cell[0]); 29212 var validated = null; 29213 29214 // do we have any registered handlers? 29215 if (gridDataGetItem(row) && self.onDblClick) { 29216 // grid must not be in edit mode 29217 validated = options.editorLock.commitCurrentEdit(); 29218 if (validated) { 29219 // handler will return true if the event was handled 29220 if (self.onDblClick(e, row, cell)) { 29221 e.stopPropagation(); 29222 e.preventDefault(); 29223 return false; 29224 } 29225 } 29226 } 29227 29228 if (options.editable) { 29229 gotoCell(row, cell, true); 29230 } 29231 } 29232 29233 function handleHeaderContextMenu(e) { 29234 if (self.onHeaderContextMenu && options.editorLock.commitCurrentEdit()) { 29235 e.preventDefault(); 29236 var selectedElement = $(e.target).closest(".slick-header-column", ".slick-header-columns"); 29237 self.onHeaderContextMenu(e, columns[self.getColumnIndex(selectedElement.data("fieldId"))]); 29238 } 29239 } 29240 29241 function handleHeaderClick(e) { 29242 var $col = $(e.target).closest(".slick-header-column"); 29243 if ($col.length ==0) { return; } 29244 var column = columns[getSiblingIndex($col[0])]; 29245 29246 if (self.onHeaderClick && options.editorLock.commitCurrentEdit()) { 29247 e.preventDefault(); 29248 self.onHeaderClick(e, column); 29249 } 29250 } 29251 29252 function handleHover(e) { 29253 if (!options.enableAutoTooltips) return; 29254 var $cell = $(e.target).closest(".slick-cell",$canvas); 29255 if ($cell.length) { 29256 if ($cell.innerWidth() < $cell[0].scrollWidth) { 29257 var text = $.trim($cell.text()); 29258 $cell.attr("title", (options.toolTipMaxLength && text.length > options.toolTipMaxLength) ? text.substr(0, options.toolTipMaxLength - 3) + "..." : text); 29259 } 29260 else { 29261 $cell.attr("title",""); 29262 } 29263 } 29264 } 29265 29266 function cellExists(row,cell) { 29267 return !(row < 0 || row >= gridDataGetLength() || cell < 0 || cell >= columns.length); 29268 } 29269 29270 function getCellFromPoint(x,y) { 29271 var row = Math.floor((y+offset)/options.rowHeight); 29272 var cell = 0; 29273 29274 var w = 0; 29275 for (var i=0; i<columns.length && w<x; i++) { 29276 w += columns[i].width; 29277 cell++; 29278 } 29279 29280 return {row:row,cell:cell-1}; 29281 } 29282 29283 function getCellFromEvent(e) { 29284 var $cell = $(e.target).closest(".slick-cell", $canvas); 29285 if (!$cell.length) 29286 return null; 29287 29288 return { 29289 row: $cell.parent().attr("row") | 0, 29290 cell: getSiblingIndex($cell[0]) 29291 }; 29292 } 29293 29294 function getCellNodeBox(row,cell) { 29295 if (!cellExists(row,cell)) 29296 return null; 29297 29298 var y1 = row * options.rowHeight - offset;
29299 var y2 = y1 + options.rowHeight - 1; 29300 var x1 = 0; 29301 for (var i=0; i<cell; i++) { 29302 x1 += columns[i].width; 29303 } 29304 var x2 = x1 + columns[cell].width; 29305 29306 return { 29307 top: y1, 29308 left: x1, 29309 bottom: y2, 29310 right: x2 29311 }; 29312 } 29313 29314 ////////////////////////////////////////////////////////////////////////////////////////////// 29315 // Cell switching 29316 29317 function resetCurrentCell() { 29318 setSelectedCell(null,false); 29319 } 29320 29321 function focusOnCurrentCell() { 29322 // lazily enable the cell to receive keyboard focus 29323 $(currentCellNode) 29324 .attr("tabIndex",0) 29325 .attr("hideFocus",true); 29326 29327 // IE7 tries to scroll the viewport so that the item being focused is aligned to the left border 29328 // IE-specific .setActive() sets the focus, but doesn't scroll 29329 if ($.browser.msie && parseInt($.browser.version) < 8) 29330 currentCellNode.setActive(); 29331 else 29332 currentCellNode.focus(); 29333 29334 var left = $(currentCellNode).position().left, 29335 right = left + $(currentCellNode).outerWidth(), 29336 scrollLeft = $viewport.scrollLeft(), 29337 scrollRight = scrollLeft + $viewport.width(); 29338 29339 if (left < scrollLeft) 29340 $viewport.scrollLeft(left); 29341 else if (right > scrollRight) 29342 $viewport.scrollLeft(Math.min(left, right - $viewport[0].clientWidth)); 29343 } 29344 29345 function setSelectedCell(newCell,editMode) { 29346 if (currentCellNode !== null) { 29347 makeSelectedCellNormal(); 29348 $(currentCellNode).removeClass("selected"); 29349 } 29350 29351 currentCellNode = newCell; 29352 29353 if (currentCellNode != null) { 29354 currentRow = parseInt($(currentCellNode).parent().attr("row"), 10); 29355 currentCell = getSiblingIndex(currentCellNode); 29356 29357 $(currentCellNode).addClass("selected"); 29358 29359 if (options.editable && editMode && isCellPotentiallyEditable(currentRow,currentCell)) { 29360 clearTimeout(h_editorLoader); 29361 29362 if (options.asyncEditorLoading) { 29363 h_editorLoader = setTimeout(makeSelectedCellEditable, options.asyncEditorLoadDelay); 29364 } 29365 else { 29366 makeSelectedCellEditable(); 29367 } 29368 } 29369 else { 29370 focusOnCurrentCell() 29371 } 29372 if (self.onCurrentCellChanged) 29373 self.onCurrentCellChanged(getCurrentCell()); 29374 } 29375 else { 29376 currentRow = null; 29377 currentCell = null; 29378 } 29379 } 29380 29381 function setSelectedCellAndRow(newCell,editMode) { 29382 setSelectedCell(newCell,editMode); 29383 29384 if (newCell) { 29385 setSelectedRows([currentRow]); 29386 } 29387 else { 29388 setSelectedRows([]); 29389 } 29390 29391 if (self.onSelectedRowsChanged) { 29392 self.onSelectedRowsChanged(); 29393 } 29394 } 29395 29396 function clearTextSelection() { 29397 if (document.selection && document.selection.empty) { 29398 document.selection.empty(); 29399 } 29400 else if (window.getSelection) { 29401 var sel = window.getSelection(); 29402 if (sel && sel.removeAllRanges) { 29403 sel.removeAllRanges(); 29404 } 29405 } 29406 } 29407 29408 function isCellPotentiallyEditable(row,cell) { 29409 // is the data for this row loaded? 29410 if (row < gridDataGetLength() && !gridDataGetItem(row)) { 29411 return false; 29412 } 29413 29414 // are we in the Add New row? can we create new from this cell? 29415 if (columns[cell].cannotTriggerInsert && row >= gridDataGetLength()) { 29416 return false;
29417 } 29418 29419 // does this cell have an editor? 29420 if (!getEditor(columns[cell])) { 29421 return false; 29422 } 29423 29424 return true; 29425 } 29426 29427 function makeSelectedCellNormal() { 29428 if (!currentEditor) { return; } 29429 29430 if (self.onBeforeCellEditorDestroy) { 29431 self.onBeforeCellEditorDestroy(currentEditor); 29432 } 29433 currentEditor.destroy(); 29434 currentEditor = null; 29435 29436 if (currentCellNode) { 29437 $(currentCellNode).removeClass("editable invalid"); 29438 29439 if (gridDataGetItem(currentRow)) { 29440 var column = columns[currentCell]; 29441 currentCellNode.innerHTML = getFormatter(column)(currentRow, currentCell, gridDataGetItem(currentRow)[column.field], column, gridDataGetItem(currentRow)); 29442 invalidatePostProcessingResults(currentRow); 29443 } 29444 } 29445 29446 // if there previously was text selected on a page (such as selected text in the edit cell just removed), 29447 // IE can't set focus to anything else correctly 29448 if ($.browser.msie) { clearTextSelection(); } 29449 29450 options.editorLock.deactivate(editController); 29451 } 29452 29453 function makeSelectedCellEditable() { 29454 if (!currentCellNode) { return; } 29455 if (!options.editable) { 29456 throw "Grid : makeSelectedCellEditable : should never get called when options.editable is false"; 29457 } 29458 29459 // cancel pending async call if there is one 29460 clearTimeout(h_editorLoader); 29461 29462 if (!isCellPotentiallyEditable(currentRow,currentCell)) { 29463 return; 29464 } 29465 29466 if (self.onBeforeEditCell && self.onBeforeEditCell(currentRow,currentCell,gridDataGetItem(currentRow)) === false) { 29467 focusOnCurrentCell(); 29468 return; 29469 } 29470 29471 options.editorLock.activate(editController); 29472 $(currentCellNode).addClass("editable"); 29473 29474 currentCellNode.innerHTML = ""; 29475 29476 var columnDef = columns[currentCell]; 29477 var item = gridDataGetItem(currentRow); 29478 29479 currentEditor = new (getEditor(columnDef))({ 29480 grid: self, 29481 gridPosition: absBox($container[0]), 29482 position: absBox(currentCellNode), 29483 container: currentCellNode, 29484 column: columnDef, 29485 item: item || {}, 29486 commitChanges: commitEditAndSetFocus, 29487 cancelChanges: cancelEditAndSetFocus 29488 }); 29489 29490 if (item) 29491 currentEditor.loadValue(item); 29492 29493 serializedEditorValue = currentEditor.serializeValue(); 29494 29495 if (currentEditor.position) 29496 handleCurrentCellPositionChange(); 29497 } 29498 29499 function commitEditAndSetFocus() { 29500 // if the commit fails, it would do so due to a validation error 29501 // if so, do not steal the focus from the editor 29502 if (options.editorLock.commitCurrentEdit()) { 29503 focusOnCurrentCell(); 29504 29505 if (options.autoEdit) { 29506 navigateDown(); 29507 } 29508 } 29509 } 29510 29511 function cancelEditAndSetFocus() { 29512 if (options.editorLock.cancelCurrentEdit()) { 29513 focusOnCurrentCell(); 29514 } 29515 } 29516 29517 function absBox(elem) { 29518 var box = {top:elem.offsetTop, left:elem.offsetLeft, bottom:0, right:0, width:$(elem).outerWidth(), height:$(elem).outerHeight(), visible:true}; 29519 box.bottom = box.top + box.height; 29520 box.right = box.left + box.width; 29521 29522 // walk up the tree 29523 var offsetParent = elem.offsetParent; 29524 while ((elem = elem.parentNode) != document.body) { 29525 if (box.visible && elem.scrollHeight != elem.offsetHeight && $(elem).css("overflowY") != "visible") 29526 box.visible = box.bottom > elem.scrollTop && box.top < elem.scrollTop + elem.clientHeight; 29527 29528 if (box.visible && elem.scrollWidth != elem.offsetWidth && $(elem).css("overflowX") != "visible") 29529 box.visible = box.right > elem.scrollLeft && box.left < elem.scrollLeft + elem.clientWidth; 29530 29531 box.left -= elem.scrollLeft; 29532 box.top -= elem.scrollTop; 29533 29534 if (elem === offsetParent) { 29535 box.left += elem.offsetLeft; 29536 box.top += elem.offsetTop; 29537 offsetParent = elem.offsetParent; 29538 } 29539 29540 box.bottom = box.top + box.height; 29541 box.right = box.left + box.width; 29542 } 29543 29544 return box; 29545 } 29546 29547 function getCurrentCellPosition(){ 29548 return absBox(currentCellNode); 29549 } 29550 29551 function getGridPosition(){ 29552 return absBox($container[0]) 29553 } 29554 29555 function handleCurrentCellPositionChange() { 29556 if (!currentCellNode) return; 29557 var cellBox; 29558 29559 if (self.onCurrentCellPositionChanged){ 29560 cellBox = getCurrentCellPosition(); 29561 self.onCurrentCellPositionChanged(cellBox); 29562 } 29563 29564 if (currentEditor) { 29565 cellBox = cellBox || getCurrentCellPosition(); 29566 if (currentEditor.show && currentEditor.hide) { 29567 if (!cellBox.visible) 29568 currentEditor.hide(); 29569 else 29570 currentEditor.show(); 29571 } 29572 29573 if (currentEditor.position) 29574 currentEditor.position(cellBox); 29575 } 29576 } 29577 29578 function getCellEditor() { 29579 return currentEditor; 29580 } 29581
29582 function getCurrentCell() { 29583 if (!currentCellNode) 29584 return null; 29585 else 29586 return {row: currentRow, cell: currentCell}; 29587 } 29588 29589 function getCurrentCellNode() { 29590 return currentCellNode; 29591 } 29592 29593 function scrollRowIntoView(row, doPaging) { 29594 var rowAtTop = row * options.rowHeight; 29595 var rowAtBottom = (row + 1) * options.rowHeight - viewportH + (viewportHasHScroll?scrollbarDimensions.height:0); 29596 29597 // need to page down? 29598 if ((row + 1) * options.rowHeight > scrollTop + viewportH + offset) { 29599 scrollTo(doPaging ? rowAtTop : rowAtBottom); 29600 render(); 29601 } 29602 29603 // or page up? 29604 else if (row * options.rowHeight < scrollTop + offset) { 29605 scrollTo(doPaging ? rowAtBottom : rowAtTop); 29606 render(); 29607 } 29608 } 29609 29610 function gotoDir(dy, dx, rollover) { 29611 if (!currentCellNode || !options.enableCellNavigation) { return; } 29612 if (!options.editorLock.commitCurrentEdit()) { return; } 29613 29614 function selectableCellFilter() { 29615 return !columns[getSiblingIndex(this)].unselectable 29616 } 29617 29618 var nextRow = rowsCache[currentRow + dy]; 29619 var nextCell = (nextRow && currentCell + dx >= 0) 29620 ? $(nextRow).children().eq(currentCell+dx).filter(selectableCellFilter) 29621 : null; 29622 29623 if (nextCell && !nextCell.length) { 29624 var nodes = $(nextRow).children() 29625 .filter(function(index) { return (dx>0) ? index > currentCell + dx : index < currentCell + dx }) 29626 .filter(selectableCellFilter); 29627 29628 if (nodes && nodes.length) { 29629 nextCell = (dx>0) 29630 ? nodes.eq(0) 29631 : nodes.eq(nodes.length-1); 29632 } 29633 } 29634 29635 if (rollover && dy === 0 && !(nextRow && nextCell && nextCell.length)) { 29636 if (!nextCell || !nextCell.length) { 29637 nextRow = rowsCache[currentRow + dy + ((dx>0)?1:-1)]; 29638 var nodes = $(nextRow).children().filter(selectableCellFilter); 29639 if (dx > 0) { 29640 nextCell = nextRow 29641 ? nodes.eq(0) 29642 : null; 29643 } 29644 else { 29645 nextCell = nextRow 29646 ? nodes.eq(nodes.length-1) 29647 : null; 29648 } 29649 } 29650 } 29651 29652 if (nextRow && nextCell && nextCell.length) { 29653 // if selecting the 'add new' row, start editing right away 29654 var row = parseInt($(nextRow).attr("row"), 10); 29655 var isAddNewRow = (row == defaultGetLength()); 29656 scrollRowIntoView(row,!isAddNewRow); 29657 setSelectedCellAndRow(nextCell[0], isAddNewRow || options.autoEdit); 29658 29659 // if no editor was created, set the focus back on the cell 29660 if (!currentEditor) { 29661 focusOnCurrentCell(); 29662 } 29663 } 29664 else { 29665 focusOnCurrentCell(); 29666 } 29667 } 29668 29669 function gotoCell(row, cell, forceEdit) { 29670 if (row > gridDataGetLength() || row < 0 || cell >= columns.length || cell < 0) { return; } 29671 if (!options.enableCellNavigation || columns[cell].unselectable) { return; } 29672 29673 if (!options.editorLock.commitCurrentEdit()) { return; } 29674 29675 scrollRowIntoView(row,false); 29676 29677 var newCell = null; 29678 if (!columns[cell].unselectable) { 29679 newCell = $(rowsCache[row]).children().eq(cell)[0]; 29680 } 29681 29682 // if selecting the 'add new' row, start editing right away 29683 setSelectedCellAndRow(newCell, forceEdit || (row === gridDataGetLength()) || options.autoEdit); 29684 29685 // if no editor was created, set the focus back on the cell 29686 if (!currentEditor) {
29687 focusOnCurrentCell(); 29688 } 29689 } 29690 29691 function navigateUp() { 29692 gotoDir(-1, 0, false); 29693 } 29694 29695 function navigateDown() { 29696 gotoDir(1, 0, false); 29697 } 29698 29699 function navigateLeft() { 29700 gotoDir(0, -1, false); 29701 } 29702 29703 function navigateRight() { 29704 gotoDir(0, 1, false); 29705 } 29706 29707 function navigatePrev() { 29708 gotoDir(0, -1, true); 29709 } 29710 29711 function navigateNext() { 29712 gotoDir(0, 1, true); 29713 } 29714 29715 ////////////////////////////////////////////////////////////////////////////////////////////// 29716 // IEditor implementation for the editor lock 29717 29718 function commitCurrentEdit() { 29719 var item = gridDataGetItem(currentRow); 29720 var column = columns[currentCell]; 29721 29722 if (currentEditor) { 29723 if (currentEditor.isValueChanged()) { 29724 var validationResults = currentEditor.validate(); 29725 29726 if (validationResults.valid) { 29727 if (currentRow < gridDataGetLength()) { 29728 var editCommand = { 29729 row: currentRow, 29730 cell: currentCell, 29731 editor: currentEditor, 29732 serializedValue: currentEditor.serializeValue(), 29733 prevSerializedValue: serializedEditorValue, 29734 execute: function() { 29735 this.editor.applyValue(item,this.serializedValue); 29736 updateRow(this.row); 29737 }, 29738 undo: function() { 29739 this.editor.applyValue(item,this.prevSerializedValue); 29740 updateRow(this.row); 29741 } 29742 }; 29743 29744 if (options.editCommandHandler) { 29745 makeSelectedCellNormal(); 29746 options.editCommandHandler(item,column,editCommand); 29747 29748 } 29749 else { 29750 editCommand.execute(); 29751 makeSelectedCellNormal(); 29752 } 29753 29754 if (self.onCellChange) { 29755 self.onCellChange(currentRow,currentCell,item); 29756 } 29757 } 29758 else if (self.onAddNewRow) { 29759 var newItem = {}; 29760 currentEditor.applyValue(newItem,currentEditor.serializeValue()); 29761 makeSelectedCellNormal(); 29762 self.onAddNewRow(newItem,column); 29763 } 29764 29765 // check whether the lock has been re-acquired by event handlers 29766 return !options.editorLock.isActive(); 29767 } 29768 else { 29769 // TODO: remove and put in onValidationError handlers in examples 29770 $(currentCellNode).addClass("invalid"); 29771 $(currentCellNode).stop(true,true).effect("highlight", {color:"red"}, 300); 29772 29773 if (self.onValidationError) { 29774 self.onValidationError(currentCellNode, validationResults, currentRow, currentCell, column); 29775 } 29776 29777 currentEditor.focus(); 29778 return false; 29779 } 29780 } 29781 29782 makeSelectedCellNormal(); 29783 } 29784 return true; 29785 } 29786 29787 function cancelCurrentEdit() { 29788 makeSelectedCellNormal(); 29789 return true; 29790 } 29791 29792
29793 ////////////////////////////////////////////////////////////////////////////////////////////// 29794 // Debug 29795 29796 this.debug = function() { 29797 var s = ""; 29798 29799 s += ("\n" + "counter_rows_rendered: " + counter_rows_rendered); 29800 s += ("\n" + "counter_rows_removed: " + counter_rows_removed); 29801 s += ("\n" + "renderedRows: " + renderedRows); 29802 s += ("\n" + "numVisibleRows: " + numVisibleRows); 29803 s += ("\n" + "maxSupportedCssHeight: " + maxSupportedCssHeight); 29804 s += ("\n" + "n(umber of pages): " + n); 29805 s += ("\n" + "(current) page: " + page); 29806 s += ("\n" + "page height (ph): " + ph); 29807 s += ("\n" + "scrollDir: " + scrollDir); 29808 29809 alert(s); 29810 }; 29811 29812 // a debug helper to be able to access private members 29813 this.eval = function(expr) { 29814 return eval(expr); 29815 }; 29816 29817 init(); 29818 29819 29820 ////////////////////////////////////////////////////////////////////////////////////////////// 29821 // Public API 29822 29823 $.extend(this, { 29824 "slickGridVersion": "1.4.3", 29825 29826 // Events 29827 "onSort": null, 29828 "onHeaderContextMenu": null, 29829 "onClick": null, 29830 "onDblClick": null, 29831 "onContextMenu": null, 29832 "onKeyDown": null, 29833 "onAddNewRow": null, 29834 "onValidationError": null, 29835 "onViewportChanged": null, 29836 "onSelectedRowsChanged": null, 29837 "onColumnsReordered": null, 29838 "onColumnsResized": null, 29839 "onBeforeMoveRows": null, 29840 "onMoveRows": null, 29841 "onCellChange": null, 29842 "onBeforeEditCell": null, 29843 "onBeforeCellEditorDestroy": null, 29844 "onBeforeDestroy": null, 29845 "onCurrentCellChanged": null, 29846 "onCurrentCellPositionChanged": null, 29847 "onCellRangeSelected": null, 29848 29849 // Methods 29850 "getColumns": getColumns, 29851 "setColumns": setColumns, 29852 "getOptions": getOptions, 29853 "setOptions": setOptions, 29854 "getData": getData, 29855 "setData": setData, 29856 "destroy": destroy, 29857 "getColumnIndex": getColumnIndex, 29858 "autosizeColumns": autosizeColumns, 29859 "updateCell": updateCell, 29860 "updateRow": updateRow, 29861 "removeRow": removeRow, 29862 "removeRows": removeRows, 29863 "removeAllRows": removeAllRows, 29864 "render": render, 29865 "invalidate": invalidate, 29866 "setHighlightedCells": setHighlightedCells, 29867 "flashCell": flashCell, 29868 "getViewport": getVisibleRange, 29869 "resizeCanvas": resizeCanvas, 29870 "updateRowCount": updateRowCount, 29871 "getCellFromPoint": getCellFromPoint, 29872 "getCellFromEvent": getCellFromEvent, 29873 "getCurrentCell": getCurrentCell, 29874 "getCurrentCellNode": getCurrentCellNode, 29875 "resetCurrentCell": resetCurrentCell, 29876 "navigatePrev": navigatePrev, 29877 "navigateNext": navigateNext, 29878 "navigateUp": navigateUp, 29879 "navigateDown": navigateDown, 29880 "navigateLeft": navigateLeft, 29881 "navigateRight": navigateRight, 29882 "gotoCell": gotoCell, 29883 "editCurrentCell": makeSelectedCellEditable, 29884 "getCellEditor": getCellEditor, 29885 "scrollRowIntoView": scrollRowIntoView, 29886 "getSelectedRows": getSelectedRows, 29887 "setSelectedRows": setSelectedRows, 29888 "getSecondaryHeaderRow": getSecondaryHeaderRow, 29889 "showSecondaryHeaderRow": showSecondaryHeaderRow,
29890 "hideSecondaryHeaderRow": hideSecondaryHeaderRow, 29891 "setSortColumn": setSortColumn, 29892 "getCurrentCellPosition" : getCurrentCellPosition, 29893 "getGridPosition": getGridPosition, 29894 29895 // IEditor implementation 29896 "getEditController": getEditController 29897 }); 29898 } 29899 29900 // Slick.Grid 29901 $.extend(true, window, { 29902 Slick: { 29903 Grid: SlickGrid, 29904 EditorLock: EditorLock, 29905 GlobalEditorLock: new EditorLock() 29906 } 29907 }); 29908}(jQuery)); 29909/*** 29910 * A simple observer pattern implementation. 29911 */ 29912 29913function EventHelper() { 29914 this.handlers = []; 29915 29916 this.subscribe = function(fn) { 29917 this.handlers.push(fn); 29918 }; 29919 29920 this.notify = function(args) { 29921 for (var i = 0; i < this.handlers.length; i++) { 29922 this.handlers[i].call(this, args); 29923 } 29924 }; 29925 29926 return this; 29927} 29928 29929 29930(function($) { 29931 /*** 29932 * A sample Model implementation. 29933 * Provides a filtered view of the underlying data. 29934 * 29935 * Relies on the data item having an "id" property uniquely identifying it. 29936 */ 29937 function DataView() { 29938 var self = this; 29939 29940 // private 29941 var idProperty = "id"; // property holding a unique row id 29942 var items = []; // data by index 29943 var rows = []; // data by row 29944 var idxById = {}; // indexes by id 29945 var rowsById = null; // rows by id; lazy-calculated 29946 var filter = null; // filter function 29947 var updated = null; // updated item ids 29948 var suspend = false; // suspends the recalculation 29949 var sortAsc = true; 29950 var sortComparer = null; 29951 var fastSortField = null; 29952 29953 var pagesize = 0; 29954 var pagenum = 0; 29955 var totalRows = 0; 29956 29957 // events 29958 var onRowCountChanged = new EventHelper(); 29959 var onRowsChanged = new EventHelper(); 29960 var onPagingInfoChanged = new EventHelper(); 29961 29962 29963 function beginUpdate() { 29964 suspend = true; 29965 } 29966 29967 function endUpdate() { 29968 suspend = false; 29969 refresh(); 29970 } 29971 29972 function refreshIdxById() { 29973 idxById = {}; 29974 for (var i = 0,l = items.length; i < l; i++) { 29975 var id = items[i][idProperty]; 29976 if (id == undefined || idxById[id] != undefined) 29977 throw "Each data element must implement a unique 'id' property"; 29978 idxById[id] = i; 29979 } 29980 } 29981 29982 function getItems() { 29983 return items; 29984 } 29985 29986 function setItems(data, objectIdProperty) { 29987 if (objectIdProperty !== undefined) idProperty = objectIdProperty; 29988 items = data; 29989 refreshIdxById(); 29990 refresh(); 29991 } 29992 29993 function setPagingOptions(args) { 29994 if (args.pageSize != undefined) 29995 pagesize = args.pageSize; 29996 29997 if (args.pageNum != undefined) 29998 pagenum = Math.min(args.pageNum, Math.ceil(totalRows / pagesize)); 29999 30000 onPagingInfoChanged.notify(getPagingInfo()); 30001 30002 refresh(); 30003 } 30004 30005 function getPagingInfo() { 30006 return {pageSize:pagesize, pageNum:pagenum, totalRows:totalRows}; 30007 } 30008 30009 function sort(comparer, ascending) { 30010 sortAsc = ascending; 30011 sortComparer = comparer; 30012 fastSortField = null; 30013 if (ascending === false) items.reverse(); 30014 items.sort(comparer); 30015 if (ascending === false) items.reverse(); 30016 refreshIdxById(); 30017 refresh(); 30018 } 30019 30020 /*** 30021 * Provides a workaround for the extremely slow sorting in IE. 30022 * Does a [lexicographic] sort on a give column by temporarily overriding Object.prototype.toString 30023 * to return the value of that field and then doing a native Array.sort(). 30024 */ 30025 function fastSort(field, ascending) { 30026 sortAsc = ascending; 30027 fastSortField = field; 30028 sortComparer = null; 30029 var oldToString = Object.prototype.toString; 30030 Object.prototype.toString = (typeof field == "function")?field:function() { return this[field] }; 30031 // an extra reversal for descending sort keeps the sort stable 30032 // (assuming a stable native sort implementation, which isn't true in some cases) 30033 if (ascending === false) items.reverse(); 30034 items.sort(); 30035 Object.prototype.toString = oldToString; 30036 if (ascending === false) items.reverse(); 30037 refreshIdxById(); 30038 refresh(); 30039 } 30040 30041 function reSort() { 30042 if (sortComparer) 30043 sort(sortComparer,sortAsc); 30044 else if (fastSortField) 30045 fastSort(fastSortField,sortAsc); 30046 } 30047 30048 function setFilter(filterFn) {
30049 filter = filterFn; 30050 refresh(); 30051 } 30052 30053 function getItemByIdx(i) { 30054 return items[i]; 30055 } 30056 30057 function getIdxById(id) { 30058 return idxById[id]; 30059 } 30060 30061 // calculate the lookup table on first call 30062 function getRowById(id) { 30063 if (!rowsById) { 30064 rowsById = {}; 30065 for (var i = 0, l = rows.length; i < l; ++i) { 30066 rowsById[rows[i][idProperty]] = i; 30067 } 30068 } 30069 30070 return rowsById[id]; 30071 } 30072 30073 function getItemById(id) { 30074 return items[idxById[id]]; 30075 } 30076 30077 function updateItem(id, item) { 30078 if (idxById[id] === undefined || id !== item[idProperty]) 30079 throw "Invalid or non-matching id"; 30080 items[idxById[id]] = item; 30081 if (!updated) updated = {}; 30082 updated[id] = true; 30083 refresh(); 30084 } 30085 30086 function insertItem(insertBefore, item) { 30087 items.splice(insertBefore, 0, item); 30088 refreshIdxById(); // TODO: optimize 30089 refresh(); 30090 } 30091 30092 function addItem(item) { 30093 items.push(item); 30094 refreshIdxById(); // TODO: optimize 30095 refresh(); 30096 } 30097 30098 function deleteItem(id) { 30099 if (idxById[id] === undefined) 30100 throw "Invalid id"; 30101 items.splice(idxById[id], 1); 30102 refreshIdxById(); // TODO: optimize 30103 refresh(); 30104 } 30105 30106 function recalc(_items, _rows, _filter, _updated) { 30107 var diff = []; 30108 var items = _items, rows = _rows, filter = _filter, updated = _updated; // cache as local vars 30109 30110 rowsById = null; 30111 30112 // go over all items remapping them to rows on the fly 30113 // while keeping track of the differences and updating indexes 30114 var rl = rows.length; 30115 var currentRowIndex = 0; 30116 var currentPageIndex = 0; 30117 var item,id; 30118 30119 for (var i = 0, il = items.length; i < il; ++i) { 30120 item = items[i]; 30121 30122 if (!filter || filter(item)) { 30123 id = item[idProperty]; 30124 30125 if (!pagesize || (currentRowIndex >= pagesize * pagenum && currentRowIndex < pagesize * (pagenum + 1))) { 30126 if (currentPageIndex >= rl || id != rows[currentPageIndex][idProperty] || (updated && updated[id])) { 30127 diff[diff.length] = currentPageIndex; 30128 rows[currentPageIndex] = item; 30129 } 30130 30131 currentPageIndex++; 30132 } 30133 30134 currentRowIndex++; 30135 } 30136 } 30137 30138 if (rl > currentPageIndex) 30139 rows.splice(currentPageIndex, rl - currentPageIndex); 30140 30141 totalRows = currentRowIndex; 30142 30143 return diff; 30144 } 30145 30146 function refresh() { 30147 if (suspend) return; 30148 30149 var countBefore = rows.length; 30150 var totalRowsBefore = totalRows; 30151 30152 var diff = recalc(items, rows, filter, updated); // pass as direct refs to avoid closure perf hit 30153 30154 // if the current page is no longer valid, go to last page and recalc 30155 // we suffer a performance penalty here, but the main loop (recalc) remains highly optimized 30156 if (pagesize && totalRows < pagenum * pagesize) { 30157 pagenum = Math.floor(totalRows / pagesize); 30158 diff = recalc(items, rows, filter, updated); 30159 } 30160 30161 updated = null; 30162 30163 if (totalRowsBefore != totalRows) onPagingInfoChanged.notify(getPagingInfo()); 30164 if (countBefore != rows.length) onRowCountChanged.notify({previous:countBefore, current:rows.length}); 30165 if (diff.length > 0) onRowsChanged.notify(diff); 30166 } 30167 30168 30169 return { 30170 // properties 30171 "rows": rows, // note: neither the array or the data in it should be modified directly 30172 30173 // methods 30174 "beginUpdate": beginUpdate, 30175 "endUpdate": endUpdate, 30176 "setPagingOptions": setPagingOptions, 30177 "getPagingInfo": getPagingInfo, 30178 "getItems": getItems, 30179 "setItems": setItems, 30180 "setFilter": setFilter, 30181 "sort": sort, 30182 "fastSort": fastSort, 30183 "reSort": reSort, 30184 "getIdxById": getIdxById, 30185 "getRowById": getRowById, 30186 "getItemById": getItemById, 30187 "getItemByIdx": getItemByIdx, 30188 "refresh": refresh, 30189 "updateItem": updateItem, 30190 "insertItem": insertItem, 30191 "addItem": addItem, 30192 "deleteItem": deleteItem, 30193 30194 // events 30195 "onRowCountChanged": onRowCountChanged, 30196 "onRowsChanged": onRowsChanged, 30197 "onPagingInfoChanged": onPagingInfoChanged 30198 }; 30199 } 30200 30201 // Slick.Data.DataView 30202 $.extend(true, window, { Slick: { Data: { DataView: DataView }}}); 30203})(jQuery); 30204(function($) { 30205 function SlickGridPager(dataView, grid, $container) 30206 { 30207 var $status, $contextMenu; 30208
30209 function init() 30210 { 30211 dataView.onPagingInfoChanged.subscribe(function(pagingInfo) { 30212 updatePager(pagingInfo); 30213 }); 30214 30215 constructPagerUI(); 30216 updatePager(dataView.getPagingInfo()); 30217 } 30218 30219 function getNavState() 30220 { 30221 var cannotLeaveEditMode = !Slick.GlobalEditorLock.commitCurrentEdit(); 30222 var pagingInfo = dataView.getPagingInfo(); 30223 var lastPage = Math.floor(pagingInfo.totalRows/pagingInfo.pageSize); 30224 30225 return { 30226 canGotoFirst: !cannotLeaveEditMode && pagingInfo.pageSize != 0 && pagingInfo.pageNum > 0, 30227 canGotoLast: !cannotLeaveEditMode && pagingInfo.pageSize != 0 && pagingInfo.pageNum != lastPage, 30228 canGotoPrev: !cannotLeaveEditMode && pagingInfo.pageSize != 0 && pagingInfo.pageNum > 0, 30229 canGotoNext: !cannotLeaveEditMode && pagingInfo.pageSize != 0 && pagingInfo.pageNum < lastPage, 30230 pagingInfo: pagingInfo, 30231 lastPage: lastPage 30232 } 30233 } 30234 30235 function setPageSize(n) 30236 { 30237 dataView.setPagingOptions({pageSize:n}); 30238 } 30239 30240 function gotoFirst() 30241 { 30242 if (getNavState().canGotoFirst) 30243 dataView.setPagingOptions({pageNum: 0}); 30244 } 30245 30246 function gotoLast() 30247 { 30248 var state = getNavState(); 30249 if (state.canGotoLast) 30250 dataView.setPagingOptions({pageNum: state.lastPage}); 30251 } 30252 30253 function gotoPrev() 30254 { 30255 var state = getNavState(); 30256 if (state.canGotoPrev) 30257 dataView.setPagingOptions({pageNum: state.pagingInfo.pageNum-1}); 30258 } 30259 30260 function gotoNext() 30261 { 30262 var state = getNavState(); 30263 if (state.canGotoNext) 30264 dataView.setPagingOptions({pageNum: state.pagingInfo.pageNum+1}); 30265 } 30266 30267 function constructPagerUI() 30268 { 30269 $container.empty(); 30270 30271 30272 var $nav = $("<span class='slick-pager-nav' />").appendTo($container); 30273 var $settings = $("<span class='slick-pager-settings' />").appendTo($container); 30274 $status = $("<span class='slick-pager-status' />").appendTo($container); 30275 30276 $settings 30277 .append("<span class='slick-pager-settings-expanded' style='display:none'>Show: <a data='-1'>Auto</a><a data=25>25</a><a data=50>50</a><a data=100>100</a></span>"); 30278 30279 $settings.find("a[data]").click(function(e) { 30280 var pagesize = $(e.target).attr("data"); 30281 if (pagesize != undefined) 30282 { 30283 if (pagesize == -1) 30284 { 30285 var vp = grid.getViewport(); 30286 setPageSize((vp.bottom-vp.top) - 1); 30287 } 30288 else 30289 setPageSize(parseInt(pagesize)); 30290 } 30291 }); 30292 30293 var icon_prefix = "<span class='ui-state-default ui-corner-all ui-icon-container'><span class='ui-icon "; 30294 var icon_suffix = "' /></span>"; 30295 30296 $(icon_prefix + "ui-icon-lightbulb" + icon_suffix) 30297 .click(function() { $(".slick-pager-settings-expanded").toggle() }) 30298 .appendTo($settings); 30299 30300 $(icon_prefix + "ui-icon-seek-first" + icon_suffix) 30301 .click(gotoFirst) 30302 .appendTo($nav); 30303 30304 $(icon_prefix + "ui-icon-seek-prev" + icon_suffix) 30305 .click(gotoPrev) 30306 .appendTo($nav); 30307 30308 $(icon_prefix + "ui-icon-seek-next" + icon_suffix) 30309 .click(gotoNext) 30310 .appendTo($nav); 30311 30312 $(icon_prefix + "ui-icon-seek-end" + icon_suffix) 30313 .click(gotoLast) 30314 .appendTo($nav); 30315 30316 $container.find(".ui-icon-container") 30317 .hover(function() { 30318 $(this).toggleClass("ui-state-hover"); 30319 }); 30320 30321 $container.children().wrapAll("<div class='slick-pager' />"); 30322 } 30323 30324 30325 function updatePager(pagingInfo) 30326 { 30327 var state = getNavState(); 30328 30329 $container.find(".slick-pager-nav span").removeClass("ui-state-disabled"); 30330 if (!state.canGotoFirst) $container.find(".ui-icon-seek-first").addClass("ui-state-disabled"); 30331 if (!state.canGotoLast) $container.find(".ui-icon-seek-end").addClass("ui-state-disabled"); 30332 if (!state.canGotoNext) $container.find(".ui-icon-seek-next").addClass("ui-state-disabled"); 30333 if (!state.canGotoPrev) $container.find(".ui-icon-seek-prev").addClass("ui-state-disabled"); 30334 30335 30336 if (pagingInfo.showAll) 30337 $status.text("Showing all " + pagingInfo.totalRows + " rows"); 30338 else 30339 $status.text("Showing page " + (pagingInfo.pageNum+1) + " of " + (Math.floor(pagingInfo.totalRows/pagingInfo.pageSize)+1)); 30340 } 30341 30342 30343 30344 init(); 30345 } 30346 30347 // Slick.Controls.Pager 30348 $.extend(true, window, { Slick: { Controls: { Pager: SlickGridPager }}}); 30349})(jQuery); 30350 30351 30352 30353/*! 30354 * Amplify Store - Persistent Client-Side Storage @VERSION 30355 * 30356 * Copyright 2011 appendTo LLC. (http://appendto.com/team) 30357 * Dual licensed under the MIT or GPL licenses. 30358 * http://appendto.com/open-source-licenses 30359 * 30360 * http://amplifyjs.com 30361 */ 30362 30363(function( amplify, undefined ) { 30364 30365// MooTools Compatibility 30366JSON.stringify = JSON.stringify||JSON.encode; 30367JSON.parse = JSON.parse||JSON.decode; 30368 30369var store = amplify.store = function( key, value, options, type ) { 30370 var type = store.type; 30371 if ( options && options.type && options.type in store.types ) { 30372 type = options.type; 30373 } 30374 return store.types[ type ]( key, value, options || {} ); 30375}; 30376 30377store.types = {}; 30378store.type = null; 30379store.addType = function( type, storage ) { 30380 if ( !store.type ) { 30381 store.type = type; 30382 } 30383 30384 store.types[ type ] = storage; 30385 store[ type ] = function( key, value, options ) { 30386 options = options || {}; 30387 options.type = type; 30388 return store( key, value, options ); 30389 }; 30390} 30391store.error = function() { 30392 return "amplify.store quota exceeded"; 30393}; 30394 30395function createSimpleStorage( storageType, storage ) { 30396 var values = storage.__amplify__ ? JSON.parse( storage.__amplify__ ) : {}; 30397 store.addType( storageType, function( key, value, options ) { 30398 var ret = value, 30399 now = (new Date()).getTime(), 30400 storedValue, 30401 parsed; 30402 30403 if ( !key ) { 30404 ret = {}; 30405 for ( key in values ) { 30406 storedValue = storage[ key ]; 30407 parsed = storedValue ? JSON.parse( storedValue ) : { expires: -1 }; 30408 if ( parsed.expires && parsed.expires <= now ) { 30409 delete storage[ key ]; 30410 delete values[ key ]; 30411 } else { 30412 ret[ key.replace( /^__amplify__/, "" ) ] = parsed.data; 30413 } 30414 } 30415 storage.__amplify__ = JSON.stringify( values ); 30416 return ret; 30417 } 30418 30419 // protect against overwriting built-in properties 30420 key = "__amplify__" + key; 30421 30422 if ( value === undefined ) { 30423 if ( values[ key ] ) { 30424 storedValue = storage[ key ]; 30425 parsed = storedValue ? JSON.parse( storedValue ) : { expires: -1 }; 30426 if ( parsed.expires && parsed.expires <= now ) { 30427 delete storage[ key ]; 30428 delete values[ key ]; 30429 } else { 30430 return parsed.data; 30431 } 30432 } 30433 } else { 30434 if ( value === null ) { 30435 delete storage[ key ]; 30436 delete values[ key ]; 30437 } else { 30438 parsed = JSON.stringify({ 30439 data: value, 30440 expires: options.expires ? now + options.expires : null 30441 });
30442 try { 30443 storage[ key ] = parsed; 30444 values[ key ] = true; 30445 // quota exceeded 30446 } catch( error ) { 30447 // expire old data and try again 30448 store[ storageType ](); 30449 try { 30450 storage[ key ] = parsed; 30451 values[ key ] = true; 30452 } catch( error ) { 30453 throw store.error(); 30454 } 30455 } 30456 } 30457 } 30458 30459 storage.__amplify__ = JSON.stringify( values ); 30460 return ret; 30461 }); 30462} 30463 30464// localStorage + sessionStorage 30465// IE 8+, Firefox 3.5+, Safari 4+, Chrome 4+, Opera 10.5+, iPhone 2+, Android 2+ 30466for ( var webStorageType in { localStorage: 1, sessionStorage: 1 } ) { 30467 // try/catch for file protocol in Firefox 30468 try { 30469 if ( window[ webStorageType ].getItem ) { 30470 createSimpleStorage( webStorageType, window[ webStorageType ] ); 30471 } 30472 } catch( e ) {} 30473} 30474 30475// globalStorage 30476// non-standard: Firefox 2+ 30477// https://developer.mozilla.org/en/dom/storage#globalStorage 30478if ( window.globalStorage ) { 30479 createSimpleStorage( "globalStorage", 30480 window.globalStorage[ window.location.hostname ] ); 30481 // Firefox 2.0 and 3.0 have sessionStorage and globalStorage 30482 // make sure we defualt to globalStorage 30483 // but don't default to globalStorage in 3.5+ which also has localStorage 30484 if ( store.type === "sessionStorage" ) { 30485 store.type = "globalStorage"; 30486 } 30487} 30488 30489// userData 30490// non-standard: IE 5+ 30491// http://msdn.microsoft.com/en-us/library/ms531424(v=vs.85).aspx 30492(function() { 30493 // append to html instead of body so we can do this from the head 30494 var div = document.createElement( "div" ), 30495 attrKey = "amplify", 30496 attrs; 30497 div.style.display = "none"; 30498 document.getElementsByTagName( "head" )[ 0 ].appendChild( div ); 30499 if ( div.addBehavior ) { 30500 div.addBehavior( "#default#userdata" ); 30501 div.load( attrKey ); 30502 attrs = div.getAttribute( attrKey ) ? JSON.parse( div.getAttribute( attrKey ) ) : {}; 30503 30504 store.addType( "userData", function( key, value, options ) { 30505 var ret = value, 30506 now = (new Date()).getTime(), 30507 attr, 30508 parsed, 30509 prevValue; 30510 30511 if ( !key ) { 30512 ret = {}; 30513 for ( key in attrs ) { 30514 attr = div.getAttribute( key ); 30515 parsed = attr ? JSON.parse( attr ) : { expires: -1 }; 30516 if ( parsed.expires && parsed.expires <= now ) { 30517 div.removeAttribute( key ); 30518 delete attrs[ key ]; 30519 } else { 30520 ret[ key ] = parsed.data; 30521 } 30522 } 30523 div.setAttribute( attrKey, JSON.stringify( attrs ) ); 30524 div.save( attrKey ); 30525 return ret; 30526 } 30527 30528 // convert invalid characters to dashes 30529 // http://www.w3.org/TR/REC-xml/#NT-Name 30530 // simplified to assume the starting character is valid 30531 // also removed colon as it is invalid in HTML attribute names 30532 key = key.replace( /[^-._0-9A-Za-z\xb7\xc0-\xd6\xd8-\xf6\xf8-\u037d\u37f-\u1fff\u200c-\u200d\u203f\u2040\u2070-\u218f]/g, "-" ); 30533 30534 if ( value === undefined ) { 30535 if ( key in attrs ) { 30536 attr = div.getAttribute( key ); 30537 parsed = attr ? JSON.parse( attr ) : { expires: -1 }; 30538 if ( parsed.expires && parsed.expires <= now ) { 30539 div.removeAttribute( key ); 30540 delete attrs[ key ]; 30541 } else { 30542 return parsed.data; 30543 } 30544 } 30545 } else { 30546 if ( value === null ) { 30547 div.removeAttribute( key ); 30548 delete attrs[ key ]; 30549 } else { 30550 // we need to get the previous value in case we need to rollback 30551 prevValue = div.getAttribute( key ); 30552 parsed = JSON.stringify({ 30553 data: value, 30554 expires: (options.expires ? (now + options.expires) : null) 30555 }); 30556 div.setAttribute( key, parsed ); 30557 attrs[ key ] = true; 30558 } 30559 } 30560 30561 div.setAttribute( attrKey, JSON.stringify( attrs ) ); 30562 try { 30563 div.save( attrKey ); 30564 // quota exceeded 30565 } catch ( error ) { 30566 // roll the value back to the previous value 30567 if ( prevValue === null ) { 30568 div.removeAttribute( key ); 30569 delete attrs[ key ]; 30570 } else { 30571 div.setAttribute( key, prevValue ); 30572 } 30573 30574 // expire old data and try again 30575 store.userData(); 30576 try { 30577 div.setAttribute( key, parsed ); 30578 attrs[ key ] = true; 30579 div.save( attrKey ); 30580 } catch ( error ) { 30581 // roll the value back to the previous value 30582 if ( prevValue === null ) { 30583 div.removeAttribute( key ); 30584 delete attrs[ key ]; 30585 } else { 30586 div.setAttribute( key, prevValue ); 30587 } 30588 throw store.error(); 30589 } 30590 } 30591 return ret; 30592 }); 30593 } 30594}() ); 30595 30596// in-memory storage 30597// fallback for all browsers to enable the API even if we can't persist data 30598createSimpleStorage( "memory", {} ); 30599 30600}( this.amplify = this.amplify || {} ) ); 30601/** 30602 * History.js jQuery Adapter 30603 * @author Benjamin Arthur Lupton <[email protected]> 30604 * @copyright 2010-2011 Benjamin Arthur Lupton <[email protected]> 30605 * @license New BSD License <http://creativecommons.org/licenses/BSD/> 30606 */ 30607 30608// Closure 30609(function(window,undefined){ 30610 // Localise Globals 30611 var 30612 History = window.History = window.History||{}, 30613 jQuery = window.jQuery; 30614 30615 // Check Existence 30616 if ( typeof History.Adapter !== 'undefined' ) { 30617 throw new Error('History.js Adapter has already been loaded...'); 30618 } 30619 30620 // Add the Adapter 30621 History.Adapter = { 30622 /** 30623 * History.Adapter.bind(el,event,callback) 30624 * @param {Element|Selector} el 30625 * @param {String} event - custom and standard events 30626 * @param {Function} callback 30627 * @return 30628 */ 30629 bind: function(el,event,callback){ 30630 jQuery(el).bind(event,callback); 30631 }, 30632 30633 /** 30634 * History.Adapter.trigger(el,event) 30635 * @param {Element|Selector} el 30636 * @param {String} event - custom and standard events 30637 * @return 30638 */ 30639 trigger: function(el,event){ 30640 jQuery(el).trigger(event); 30641 }, 30642 30643 /** 30644 * History.Adapter.trigger(el,event,data) 30645 * @param {Function} callback 30646 * @return 30647 */ 30648 onDomLoad: function(callback) { 30649 jQuery(callback); 30650 } 30651 }; 30652 30653 // Try and Initialise History 30654 if ( typeof History.init !== 'undefined' ) { 30655 History.init(); 30656 } 30657 30658})(window); 30659/** 30660 * History.js Core 30661 * @author Benjamin Arthur Lupton <[email protected]> 30662 * @copyright 2010-2011 Benjamin Arthur Lupton <[email protected]> 30663 * @license New BSD License <http://creativecommons.org/licenses/BSD/> 30664 */ 30665 30666 30667(function(window,undefined){ 30668 "use strict"; 30669 30670 // -------------------------------------------------------------------------- 30671 // Initialise 30672 30673 // Localise Globals 30674 var 30675 console = window.console||undefined, // Prevent a JSLint complain 30676 document = window.document, // Make sure we are using the correct document 30677 navigator = window.navigator, // Make sure we are using the correct navigator 30678 amplify = window.amplify||false, // Amplify.js 30679 setTimeout = window.setTimeout, 30680 clearTimeout = window.clearTimeout, 30681 setInterval = window.setInterval, 30682 clearInterval = window.clearInterval, 30683 JSON = window.JSON, 30684 History = window.History = window.History||{}, // Public History Object 30685 history = window.history; // Old History Object 30686 30687 // MooTools Compatibility 30688 JSON.stringify = JSON.stringify||JSON.encode; 30689 JSON.parse = JSON.parse||JSON.decode; 30690 30691 // Check Existence 30692 if ( typeof History.init !== 'undefined' ) { 30693 throw new Error('History.js Core has already been loaded...'); 30694 } 30695 30696 // Initialise History 30697 History.init = function(){
vendor: 14,162 bytes, lines 30698-31222
30698 // Check Load Status of Adapter 30699 if ( typeof History.Adapter === 'undefined' ) { 30700 return false; 30701 } 30702 30703 // Check Load Status of Core 30704 if ( typeof History.initCore !== 'undefined' ) { 30705 History.initCore(); 30706 } 30707 30708 // Check Load Status of HTML4 Support 30709 if ( typeof History.initHtml4 !== 'undefined' ) { 30710 History.initHtml4(); 30711 } 30712 30713 // Return true 30714 return true; 30715 }; 30716 30717 // -------------------------------------------------------------------------- 30718 // Initialise Core 30719 30720 // Initialise Core 30721 History.initCore = function(){ 30722 // Initialise 30723 if ( typeof History.initCore.initialized !== 'undefined' ) { 30724 // Already Loaded 30725 return false; 30726 } 30727 else { 30728 History.initCore.initialized = true; 30729 } 30730 30731 // ---------------------------------------------------------------------- 30732 // Options 30733 30734 /** 30735 * History.options 30736 * Configurable options 30737 */ 30738 History.options = History.options||{}; 30739 30740 /** 30741 * History.options.hashChangeInterval 30742 * How long should the interval be before hashchange checks 30743 */ 30744 History.options.hashChangeInterval = History.options.hashChangeInterval || 100; 30745 30746 /** 30747 * History.options.safariPollInterval 30748 * How long should the interval be before safari poll checks 30749 */ 30750 History.options.safariPollInterval = History.options.safariPollInterval || 500; 30751 30752 /** 30753 * History.options.doubleCheckInterval 30754 * How long should the interval be before we perform a double check 30755 */ 30756 History.options.doubleCheckInterval = History.options.doubleCheckInterval || 500; 30757 30758 /** 30759 * History.options.storeInterval 30760 * How long should we wait between store calls 30761 */ 30762 History.options.storeInterval = History.options.storeInterval || 1000; 30763 30764 /** 30765 * History.options.busyDelay 30766 * How long should we wait between busy events 30767 */ 30768 History.options.busyDelay = History.options.busyDelay || 250; 30769 30770 /** 30771 * History.options.debug 30772 * If true will enable debug messages to be logged 30773 */ 30774 History.options.debug = History.options.debug || false; 30775 30776 /** 30777 * History.options.initialTitle 30778 * What is the title of the initial state 30779 */ 30780 History.options.initialTitle = History.options.initialTitle || document.title; 30781 30782 30783 // ---------------------------------------------------------------------- 30784 // Interval record 30785 30786 /** 30787 * History.intervalList 30788 * List of intervals set, to be cleared when document is unloaded. 30789 */ 30790 History.intervalList = []; 30791 30792 /** 30793 * History.clearAllIntervals 30794 * Clears all setInterval instances. 30795 */ 30796 History.clearAllIntervals = function(){ 30797 var i, il = History.intervalList; 30798 if (typeof il !== "undefined" && il !== null) { 30799 for (i = 0; i < il.length; i++) { 30800 clearInterval(il[i]); 30801 } 30802 History.intervalList = null; 30803 } 30804 }; 30805 History.Adapter.bind(window,"beforeunload",History.clearAllIntervals); 30806 History.Adapter.bind(window,"unload",History.clearAllIntervals); 30807 30808 30809 // ---------------------------------------------------------------------- 30810 // Debug 30811 30812 /** 30813 * History.debug(message,...) 30814 * Logs the passed arguments if debug enabled 30815 */ 30816 History.debug = function(){ 30817 if ( (History.options.debug||false) ) { 30818 History.log.apply(History,arguments); 30819 } 30820 }; 30821 30822 /** 30823 * History.log(message,...) 30824 * Logs the passed arguments 30825 */ 30826 History.log = function(){ 30827 // Prepare 30828 var 30829 consoleExists = !(typeof console === 'undefined' || typeof console.log === 'undefined' || typeof console.log.apply === 'undefined'), 30830 textarea = document.getElementById('log'), 30831 message, 30832 i,n 30833 ; 30834 30835 // Write to Console 30836 if ( consoleExists ) { 30837 var args = Array.prototype.slice.call(arguments); 30838 message = args.shift(); 30839 if ( typeof console.debug !== 'undefined' ) { 30840 console.debug.apply(console,[message,args]); 30841 } 30842 else { 30843 console.log.apply(console,[message,args]); 30844 } 30845 } 30846 else { 30847 message = ("\n"+arguments[0]+"\n"); 30848 } 30849 30850 // Write to log 30851 for ( i=1,n=arguments.length; i<n; ++i ) { 30852 var arg = arguments[i]; 30853 if ( typeof arg === 'object' && typeof JSON !== 'undefined' ) { 30854 try { 30855 arg = JSON.stringify(arg); 30856 } 30857 catch ( Exception ) { 30858 // Recursive Object 30859 } 30860 } 30861 message += "\n"+arg+"\n"; 30862 } 30863 30864 // Textarea 30865 if ( textarea ) { 30866 textarea.value += message+"\n-----\n"; 30867 textarea.scrollTop = textarea.scrollHeight - textarea.clientHeight; 30868 } 30869 // No Textarea, No Console 30870 else if ( !consoleExists ) { 30871 alert(message); 30872 } 30873 30874 // Return true 30875 return true; 30876 }; 30877 30878 // ---------------------------------------------------------------------- 30879 // Emulated Status 30880 30881 /** 30882 * History.getInternetExplorerMajorVersion() 30883 * Get's the major version of Internet Explorer 30884 * @return {integer} 30885 * @license Public Domain 30886 * @author Benjamin Arthur Lupton <[email protected]> 30887 * @author James Padolsey <https://gist.github.com/527683> 30888 */ 30889 History.getInternetExplorerMajorVersion = function(){ 30890 var result = History.getInternetExplorerMajorVersion.cached = 30891 (typeof History.getInternetExplorerMajorVersion.cached !== 'undefined') 30892 ? History.getInternetExplorerMajorVersion.cached 30893 : (function(){ 30894 var v = 3, 30895 div = document.createElement('div'), 30896 all = div.getElementsByTagName('i'); 30897 while ( (div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->') && all[0] ) {} 30898 return (v > 4) ? v : false; 30899 })() 30900 ; 30901 return result; 30902 }; 30903 30904 /** 30905 * History.isInternetExplorer() 30906 * Are we using Internet Explorer? 30907 * @return {boolean} 30908 * @license Public Domain 30909 * @author Benjamin Arthur Lupton <[email protected]> 30910 */ 30911 History.isInternetExplorer = function(){ 30912 var result = 30913 History.isInternetExplorer.cached = 30914 (typeof History.isInternetExplorer.cached !== 'undefined') 30915 ? History.isInternetExplorer.cached 30916 : Boolean(History.getInternetExplorerMajorVersion()) 30917 ; 30918 return result; 30919 }; 30920 30921 /** 30922 * History.emulated 30923 * Which features require emulating? 30924 */ 30925 History.emulated = { 30926 pushState: !Boolean( 30927 window.history && window.history.pushState && window.history.replaceState 30928 && !( 30929 (/ Mobile\/([1-7][a-z]|(8([abcde]|f(1[0-8]))))/i).test(navigator.userAgent) /* disable for versions of iOS before version 4.3 (8F190) */ 30930 || (/AppleWebKit\/5([0-2]|3[0-2])/i).test(navigator.userAgent) /* disable for the mercury iOS browser, or at least older versions of the webkit engine */ 30931 ) 30932 ), 30933 hashChange: Boolean( 30934 !(('onhashchange' in window) || ('onhashchange' in document)) 30935 || 30936 (History.isInternetExplorer() && History.getInternetExplorerMajorVersion() < 8) 30937 ) 30938 }; 30939 30940 /** 30941 * History.enabled 30942 * Is History enabled? 30943 */ 30944 History.enabled = !History.emulated.pushState; 30945 30946 /** 30947 * History.bugs 30948 * Which bugs are present 30949 */ 30950 History.bugs = { 30951 /** 30952 * Safari 5 and Safari iOS 4 fail to return to the correct state once a hash is replaced by a `replaceState` call 30953 * https://bugs.webkit.org/show_bug.cgi?id=56249 30954 */ 30955 setHash: Boolean(!History.emulated.pushState && navigator.vendor === 'Apple Computer, Inc.' && /AppleWebKit\/5([0-2]|3[0-3])/.test(navigator.userAgent)), 30956 30957 /** 30958 * Safari 5 and Safari iOS 4 sometimes fail to apply the state change under busy conditions 30959 * https://bugs.webkit.org/show_bug.cgi?id=42940 30960 */ 30961 safariPoll: Boolean(!History.emulated.pushState && navigator.vendor === 'Apple Computer, Inc.' && /AppleWebKit\/5([0-2]|3[0-3])/.test(navigator.userAgent)), 30962 30963 /** 30964 * MSIE 6 and 7 sometimes do not apply a hash even it was told to (requiring a second call to the apply function) 30965 */ 30966 ieDoubleCheck: Boolean(History.isInternetExplorer() && History.getInternetExplorerMajorVersion() < 8), 30967 30968 /** 30969 * MSIE 6 requires the entire hash to be encoded for the hashes to trigger the onHashChange event 30970 */ 30971 hashEscape: Boolean(History.isInternetExplorer() && History.getInternetExplorerMajorVersion() < 7) 30972 }; 30973 30974 /** 30975 * History.isEmptyObject(obj) 30976 * Checks to see if the Object is Empty 30977 * @param {Object} obj 30978 * @return {boolean} 30979 */ 30980 History.isEmptyObject = function(obj) { 30981 for ( var name in obj ) { 30982 return false; 30983 } 30984 return true; 30985 }; 30986 30987 /** 30988 * History.cloneObject(obj) 30989 * Clones a object 30990 * @param {Object} obj 30991 * @return {Object} 30992 */ 30993 History.cloneObject = function(obj) { 30994 var hash,newObj; 30995 if ( obj ) { 30996 hash = JSON.stringify(obj); 30997 newObj = JSON.parse(hash); 30998 } 30999 else { 31000 newObj = {}; 31001 } 31002 return newObj; 31003 }; 31004 31005 // ---------------------------------------------------------------------- 31006 // URL Helpers 31007 31008 /** 31009 * History.getRootUrl() 31010 * Turns "http://mysite.com/dir/page.html?asd" into "http://mysite.com" 31011 * @return {String} rootUrl 31012 */ 31013 History.getRootUrl = function(){ 31014 // Create 31015 var rootUrl = document.location.protocol+'//'+(document.location.hostname||document.location.host); 31016 if ( document.location.port||false ) { 31017 rootUrl += ':'+document.location.port; 31018 } 31019 rootUrl += '/'; 31020 31021 // Return 31022 return rootUrl; 31023 }; 31024 31025 /** 31026 * History.getBaseHref() 31027 * Fetches the `href` attribute of the `<base href="...">` element if it exists 31028 * @return {String} baseHref 31029 */ 31030 History.getBaseHref = function(){ 31031 // Create 31032 var 31033 baseElements = document.getElementsByTagName('base'), 31034 baseElement = null, 31035 baseHref = ''; 31036 31037 // Test for Base Element 31038 if ( baseElements.length === 1 ) { 31039 // Prepare for Base Element 31040 baseElement = baseElements[0]; 31041 baseHref = baseElement.href.replace(/[^\/]+$/,''); 31042 } 31043 31044 // Adjust trailing slash 31045 baseHref = baseHref.replace(/\/+$/,''); 31046 if ( baseHref ) baseHref += '/'; 31047 31048 // Return 31049 return baseHref; 31050 }; 31051 31052 /** 31053 * History.getBaseUrl() 31054 * Fetches the baseHref or basePageUrl or rootUrl (whichever one exists first) 31055 * @return {String} baseUrl 31056 */ 31057 History.getBaseUrl = function(){ 31058 // Create 31059 var baseUrl = History.getBaseHref()||History.getBasePageUrl()||History.getRootUrl(); 31060 31061 // Return 31062 return baseUrl; 31063 }; 31064 31065 /** 31066 * History.getPageUrl() 31067 * Fetches the URL of the current page 31068 * @return {String} pageUrl 31069 */ 31070 History.getPageUrl = function(){ 31071 // Fetch 31072 var 31073 State = History.getState(false,false), 31074 stateUrl = (State||{}).url||document.location.href; 31075 31076 // Create 31077 var pageUrl = stateUrl.replace(/\/+$/,'').replace(/[^\/]+$/,function(part,index,string){ 31078 return (/\./).test(part) ? part : part+'/'; 31079 }); 31080 31081 // Return 31082 return pageUrl; 31083 }; 31084 31085 /** 31086 * History.getBasePageUrl() 31087 * Fetches the Url of the directory of the current page 31088 * @return {String} basePageUrl 31089 */ 31090 History.getBasePageUrl = function(){ 31091 // Create 31092 var basePageUrl = document.location.href.replace(/[#\?].*/,'').replace(/[^\/]+$/,function(part,index,string){ 31093 return (/[^\/]$/).test(part) ? '' : part; 31094 }).replace(/\/+$/,'')+'/'; 31095 31096 // Return 31097 return basePageUrl; 31098 }; 31099 31100 /** 31101 * History.getFullUrl(url) 31102 * Ensures that we have an absolute URL and not a relative URL 31103 * @param {string} url 31104 * @param {Boolean} allowBaseHref 31105 * @return {string} fullUrl 31106 */ 31107 History.getFullUrl = function(url,allowBaseHref){ 31108 // Prepare 31109 var fullUrl = url, firstChar = url.substring(0,1); 31110 allowBaseHref = (typeof allowBaseHref === 'undefined') ? true : allowBaseHref; 31111 31112 // Check 31113 if ( /[a-z]+\:\/\//.test(url) ) { 31114 // Full URL 31115 } 31116 else if ( firstChar === '/' ) { 31117 // Root URL 31118 fullUrl = History.getRootUrl()+url.replace(/^\/+/,''); 31119 } 31120 else if ( firstChar === '#' ) { 31121 // Anchor URL 31122 fullUrl = History.getPageUrl().replace(/#.*/,'')+url; 31123 } 31124 else if ( firstChar === '?' ) { 31125 // Query URL 31126 fullUrl = History.getPageUrl().replace(/[\?#].*/,'')+url; 31127 } 31128 else { 31129 // Relative URL 31130 if ( allowBaseHref ) { 31131 fullUrl = History.getBaseUrl()+url.replace(/^(\.\/)+/,''); 31132 } else { 31133 fullUrl = History.getBasePageUrl()+url.replace(/^(\.\/)+/,''); 31134 } 31135 // We have an if condition above as we do not want hashes 31136 // which are relative to the baseHref in our URLs 31137 // as if the baseHref changes, then all our bookmarks 31138 // would now point to different locations 31139 // whereas the basePageUrl will always stay the same 31140 } 31141 31142 // Return 31143 return fullUrl.replace(/\#$/,''); 31144 }; 31145 31146 /** 31147 * History.getShortUrl(url) 31148 * Ensures that we have a relative URL and not a absolute URL 31149 * @param {string} url 31150 * @return {string} url 31151 */ 31152 History.getShortUrl = function(url){ 31153 // Prepare 31154 var shortUrl = url, baseUrl = History.getBaseUrl(), rootUrl = History.getRootUrl(); 31155 31156 // Trim baseUrl 31157 if ( History.emulated.pushState ) { 31158 // We are in a if statement as when pushState is not emulated 31159 // The actual url these short urls are relative to can change 31160 // So within the same session, we the url may end up somewhere different 31161 shortUrl = shortUrl.replace(baseUrl,''); 31162 } 31163 31164 // Trim rootUrl 31165 shortUrl = shortUrl.replace(rootUrl,'/'); 31166 31167 // Ensure we can still detect it as a state 31168 if ( History.isTraditionalAnchor(shortUrl) ) { 31169 shortUrl = './'+shortUrl; 31170 } 31171 31172 // Clean It 31173 shortUrl = shortUrl.replace(/^(\.\/)+/g,'./').replace(/\#$/,''); 31174 31175 // Return 31176 return shortUrl; 31177 }; 31178 31179 // ---------------------------------------------------------------------- 31180 // State Storage 31181 31182 /** 31183 * History.store 31184 * The store for all session specific data 31185 */ 31186 History.store = amplify ? (amplify.store('History.store')||{}) : {}; 31187 History.store.idToState = History.store.idToState||{}; 31188 History.store.urlToId = History.store.urlToId||{}; 31189 History.store.stateToId = History.store.stateToId||{}; 31190 31191 /** 31192 * History.idToState 31193 * 1-1: State ID to State Object 31194 */ 31195 History.idToState = History.idToState||{}; 31196 31197 /** 31198 * History.stateToId 31199 * 1-1: State String to State ID 31200 */ 31201 History.stateToId = History.stateToId||{}; 31202 31203 /** 31204 * History.urlToId 31205 * 1-1: State URL to State ID 31206 */ 31207 History.urlToId = History.urlToId||{}; 31208 31209 /** 31210 * History.storedStates 31211 * Store the states in an array 31212 */ 31213 History.storedStates = History.storedStates||[]; 31214 31215 /** 31216 * History.savedStates 31217 * Saved the states in an array 31218 */ 31219 History.savedStates = History.savedStates||[]; 31220 31221 /** 31222 * History.getState()
31223 * Get an object containing the data, title and url of the current state 31224 * @param {Boolean} friendly 31225 * @param {Boolean} create 31226 * @return {Object} State 31227 */ 31228 History.getState = function(friendly,create){ 31229 // Prepare 31230 if ( typeof friendly === 'undefined' ) { friendly = true; } 31231 if ( typeof create === 'undefined' ) { create = true; } 31232 31233 // Fetch 31234 var State = History.getLastSavedState(); 31235 31236 // Create 31237 if ( !State && create ) { 31238 State = History.createStateObject(); 31239 } 31240 31241 // Adjust 31242 if ( friendly ) { 31243 State = History.cloneObject(State); 31244 State.url = State.cleanUrl||State.url; 31245 } 31246 31247 // Return 31248 return State; 31249 }; 31250 31251 /** 31252 * History.getIdByState(State) 31253 * Gets a ID for a State 31254 * @param {State} newState 31255 * @return {String} id 31256 */ 31257 History.getIdByState = function(newState){ 31258 31259 // Fetch ID 31260 var id = History.extractId(newState.url); 31261 if ( !id ) { 31262 // Find ID via State String 31263 var str = History.getStateString(newState); 31264 if ( typeof History.stateToId[str] !== 'undefined' ) { 31265 id = History.stateToId[str]; 31266 } 31267 else if ( typeof History.store.stateToId[str] !== 'undefined' ) { 31268 id = History.store.stateToId[str]; 31269 } 31270 else { 31271 // Generate a new ID 31272 while ( true ) { 31273 id = String(Math.floor(Math.random()*1000)); 31274 if ( typeof History.idToState[id] === 'undefined' && typeof History.store.idToState[id] === 'undefined' ) { 31275 break; 31276 } 31277 } 31278 31279 // Apply the new State to the ID 31280 History.stateToId[str] = id; 31281 History.idToState[id] = newState; 31282 } 31283 } 31284 31285 // Return ID 31286 return id; 31287 }; 31288 31289 /** 31290 * History.normalizeState(State) 31291 * Expands a State Object 31292 * @param {object} State 31293 * @return {object} 31294 */ 31295 History.normalizeState = function(oldState){ 31296 // Prepare 31297 if ( !oldState || (typeof oldState !== 'object') ) { 31298 oldState = {}; 31299 } 31300 31301 // Check 31302 if ( typeof oldState.normalized !== 'undefined' ) { 31303 return oldState; 31304 } 31305 31306 // Adjust 31307 if ( !oldState.data || (typeof oldState.data !== 'object') ) { 31308 oldState.data = {}; 31309 } 31310 31311 // ---------------------------------------------------------------------- 31312 31313 // Create 31314 var newState = {}; 31315 newState.normalized = true; 31316 newState.title = oldState.title||''; 31317 newState.url = History.getFullUrl(History.unescapeString(oldState.url||document.location.href)); 31318 newState.hash = History.getShortUrl(newState.url); 31319 newState.data = History.cloneObject(oldState.data); 31320 31321 // Fetch ID 31322 newState.id = History.getIdByState(newState); 31323 31324 // ---------------------------------------------------------------------- 31325 31326 // Clean the URL 31327 newState.cleanUrl = newState.url.replace(/\??\&_suid.*/,''); 31328 newState.url = newState.cleanUrl; 31329 31330 // Check to see if we have more than just a url 31331 var dataNotEmpty = !History.isEmptyObject(newState.data); 31332 31333 // Apply 31334 if ( newState.title || dataNotEmpty ) { 31335 // Add ID to Hash 31336 newState.hash = History.getShortUrl(newState.url).replace(/\??\&_suid.*/,''); 31337 if ( !/\?/.test(newState.hash) ) { 31338 newState.hash += '?'; 31339 } 31340 newState.hash += '&_suid='+newState.id; 31341 } 31342 31343 // Create the Hashed URL 31344 newState.hashedUrl = History.getFullUrl(newState.hash); 31345 31346 // ---------------------------------------------------------------------- 31347 31348 // Update the URL if we have a duplicate 31349 if ( (History.emulated.pushState || History.bugs.safariPoll) && History.hasUrlDuplicate(newState) ) { 31350 newState.url = newState.hashedUrl; 31351 } 31352 31353 // ---------------------------------------------------------------------- 31354 31355 // Return 31356 return newState; 31357 }; 31358 31359 /** 31360 * History.createStateObject(data,title,url) 31361 * Creates a object based on the data, title and url state params 31362 * @param {object} data 31363 * @param {string} title 31364 * @param {string} url 31365 * @return {object} 31366 */ 31367 History.createStateObject = function(data,title,url){ 31368 // Hashify 31369 var State = { 31370 'data': data, 31371 'title': title, 31372 'url': url 31373 }; 31374 31375 // Expand the State 31376 State = History.normalizeState(State); 31377 31378 // Return object 31379 return State; 31380 }; 31381 31382 /** 31383 * History.getStateById(id) 31384 * Get a state by it's UID 31385 * @param {String} id 31386 */ 31387 History.getStateById = function(id){ 31388 // Prepare 31389 id = String(id); 31390 31391 // Retrieve 31392 var State = History.idToState[id] || History.store.idToState[id] || undefined; 31393 31394 // Return State 31395 return State; 31396 }; 31397 31398 /** 31399 * Get a State's String 31400 * @param {State} passedState 31401 */ 31402 History.getStateString = function(passedState){ 31403 // Prepare 31404 var State = History.normalizeState(passedState); 31405 31406 // Clean 31407 var cleanedState = { 31408 data: State.data, 31409 title: passedState.title, 31410 url: passedState.url 31411 }; 31412 31413 // Fetch 31414 var str = JSON.stringify(cleanedState); 31415 31416 // Return 31417 return str; 31418 }; 31419 31420 /** 31421 * Get a State's ID 31422 * @param {State} passedState 31423 * @return {String} id 31424 */ 31425 History.getStateId = function(passedState){ 31426 // Prepare 31427 var State = History.normalizeState(passedState); 31428 31429 // Fetch 31430 var id = State.id; 31431 31432 // Return 31433 return id; 31434 }; 31435 31436 /** 31437 * History.getHashByState(State) 31438 * Creates a Hash for the State Object 31439 * @param {State} passedState 31440 * @return {String} hash 31441 */ 31442 History.getHashByState = function(passedState){ 31443 // Prepare 31444 var hash, State = History.normalizeState(passedState); 31445 31446 // Fetch 31447 hash = State.hash; 31448 31449 // Return 31450 return hash; 31451 }; 31452 31453 /** 31454 * History.extractId(url_or_hash) 31455 * Get a State ID by it's URL or Hash 31456 * @param {string} url_or_hash 31457 * @return {string} id 31458 */ 31459 History.extractId = function ( url_or_hash ) { 31460 // Prepare 31461 var id; 31462 31463 // Extract 31464 var parts,url; 31465 parts = /(.*)\&_suid=([0-9]+)$/.exec(url_or_hash); 31466 url = parts ? (parts[1]||url_or_hash) : url_or_hash; 31467 id = parts ? String(parts[2]||'') : ''; 31468 31469 // Return 31470 return id||false; 31471 }; 31472 31473 /** 31474 * History.isTraditionalAnchor 31475 * Checks to see if the url is a traditional anchor or not 31476 * @param {String} url_or_hash 31477 * @return {Boolean} 31478 */ 31479 History.isTraditionalAnchor = function(url_or_hash){ 31480 // Check 31481 var isTraditional = !(/[\/\?\.]/.test(url_or_hash)); 31482 31483 // Return 31484 return isTraditional; 31485 }; 31486 31487 /** 31488 * History.extractState 31489 * Get a State by it's URL or Hash 31490 * @param {String} url_or_hash 31491 * @return {State|null} 31492 */ 31493 History.extractState = function(url_or_hash,create){ 31494 // Prepare 31495 var State = null; 31496 create = create||false; 31497 31498 // Fetch SUID 31499 var id = History.extractId(url_or_hash); 31500 if ( id ) { 31501 State = History.getStateById(id); 31502 } 31503 31504 // Fetch SUID returned no State 31505 if ( !State ) { 31506 // Fetch URL 31507 var url = History.getFullUrl(url_or_hash); 31508 31509 // Check URL 31510 id = History.getIdByUrl(url)||false; 31511 if ( id ) { 31512 State = History.getStateById(id); 31513 } 31514 31515 // Create State 31516 if ( !State && create && !History.isTraditionalAnchor(url_or_hash) ) { 31517 State = History.createStateObject(null,null,url); 31518 } 31519 } 31520 31521 // Return 31522 return State; 31523 }; 31524 31525 /** 31526 * History.getIdByUrl() 31527 * Get a State ID by a State URL 31528 */ 31529 History.getIdByUrl = function(url){ 31530 // Fetch 31531 var id = History.urlToId[url] || History.store.urlToId[url] || undefined; 31532 31533 // Return 31534 return id; 31535 }; 31536 31537 /** 31538 * History.getLastSavedState()
31539 * Get an object containing the data, title and url of the current state 31540 * @return {Object} State 31541 */ 31542 History.getLastSavedState = function(){ 31543 return History.savedStates[History.savedStates.length-1]||undefined; 31544 }; 31545 31546 /** 31547 * History.getLastStoredState() 31548 * Get an object containing the data, title and url of the current state 31549 * @return {Object} State 31550 */ 31551 History.getLastStoredState = function(){ 31552 return History.storedStates[History.storedStates.length-1]||undefined; 31553 }; 31554 31555 /** 31556 * History.hasUrlDuplicate 31557 * Checks if a Url will have a url conflict 31558 * @param {Object} newState 31559 * @return {Boolean} hasDuplicate 31560 */ 31561 History.hasUrlDuplicate = function(newState) { 31562 // Prepare 31563 var hasDuplicate = false; 31564 31565 // Fetch 31566 var oldState = History.extractState(newState.url); 31567 31568 // Check 31569 hasDuplicate = oldState && oldState.id !== newState.id; 31570 31571 // Return 31572 return hasDuplicate; 31573 }; 31574 31575 /** 31576 * History.storeState 31577 * Store a State 31578 * @param {Object} newState 31579 * @return {Object} newState 31580 */ 31581 History.storeState = function(newState){ 31582 // Store the State 31583 History.urlToId[newState.url] = newState.id; 31584 31585 // Push the State 31586 History.storedStates.push(History.cloneObject(newState)); 31587 31588 // Return newState 31589 return newState; 31590 }; 31591 31592 /** 31593 * History.isLastSavedState(newState) 31594 * Tests to see if the state is the last state 31595 * @param {Object} newState 31596 * @return {boolean} isLast 31597 */ 31598 History.isLastSavedState = function(newState){ 31599 // Prepare 31600 var isLast = false; 31601 31602 // Check 31603 if ( History.savedStates.length ) { 31604 var 31605 newId = newState.id, 31606 oldState = History.getLastSavedState(), 31607 oldId = oldState.id; 31608 31609 // Check 31610 isLast = (newId === oldId); 31611 } 31612 31613 // Return 31614 return isLast; 31615 }; 31616 31617 /** 31618 * History.saveState 31619 * Push a State 31620 * @param {Object} newState 31621 * @return {boolean} changed 31622 */ 31623 History.saveState = function(newState){ 31624 // Check Hash 31625 if ( History.isLastSavedState(newState) ) { 31626 return false; 31627 } 31628 31629 // Push the State 31630 History.savedStates.push(History.cloneObject(newState)); 31631 31632 // Return true 31633 return true; 31634 }; 31635 31636 /** 31637 * History.getStateByIndex() 31638 * Gets a state by the index 31639 * @param {integer} index 31640 * @return {Object} 31641 */ 31642 History.getStateByIndex = function(index){ 31643 // Prepare 31644 var State = null; 31645 31646 // Handle 31647 if ( typeof index === 'undefined' ) { 31648 // Get the last inserted 31649 State = History.savedStates[History.savedStates.length-1]; 31650 } 31651 else if ( index < 0 ) { 31652 // Get from the end 31653 State = History.savedStates[History.savedStates.length+index]; 31654 } 31655 else { 31656 // Get from the beginning 31657 State = History.savedStates[index]; 31658 } 31659 31660 // Return State 31661 return State; 31662 }; 31663 31664 // ---------------------------------------------------------------------- 31665 // Hash Helpers 31666 31667 /** 31668 * History.getHash() 31669 * Gets the current document hash 31670 * @return {string} 31671 */ 31672 History.getHash = function(){ 31673 var hash = History.unescapeHash(document.location.hash); 31674 return hash; 31675 }; 31676 31677 /** 31678 * History.unescapeString() 31679 * Unescape a string 31680 * @param {String} str 31681 * @return {string} 31682 */ 31683 History.unescapeString = function(str){ 31684 // Prepare 31685 var result = str; 31686 31687 // Unescape hash 31688 var tmp; 31689 while ( true ) { 31690 tmp = window.unescape(result); 31691 if ( tmp === result ) { 31692 break; 31693 } 31694 result = tmp; 31695 } 31696 31697 // Return result 31698 return result; 31699 }; 31700 31701 /** 31702 * History.unescapeHash() 31703 * normalize and Unescape a Hash 31704 * @param {String} hash 31705 * @return {string} 31706 */ 31707 History.unescapeHash = function(hash){ 31708 // Prepare 31709 var result = History.normalizeHash(hash); 31710 31711 // Unescape hash 31712 result = History.unescapeString(result); 31713 31714 // Return result 31715 return result; 31716 }; 31717 31718 /** 31719 * History.normalizeHash() 31720 * normalize a hash across browsers 31721 * @return {string} 31722 */ 31723 History.normalizeHash = function(hash){ 31724 var result = hash.replace(/[^#]*#/,'').replace(/#.*/, ''); 31725 31726 // Return result 31727 return result; 31728 }; 31729 31730 /** 31731 * History.setHash(hash) 31732 * Sets the document hash 31733 * @param {string} hash 31734 * @return {History} 31735 */ 31736 History.setHash = function(hash,queue){ 31737 // Handle Queueing 31738 if ( queue !== false && History.busy() ) { 31739 // Wait + Push to Queue 31740 //History.debug('History.setHash: we must wait', arguments); 31741 History.pushQueue({ 31742 scope: History, 31743 callback: History.setHash, 31744 args: arguments, 31745 queue: queue 31746 }); 31747 return false;
31748 } 31749 31750 // Log 31751 //History.debug('History.setHash: called',hash); 31752 31753 // Prepare 31754 var adjustedHash = History.escapeHash(hash); 31755 31756 // Make Busy + Continue 31757 History.busy(true); 31758 31759 // Check if hash is a state 31760 var State = History.extractState(hash,true); 31761 if ( State && !History.emulated.pushState ) { 31762 // Hash is a state so skip the setHash 31763 //History.debug('History.setHash: Hash is a state so skipping the hash set with a direct pushState call',arguments); 31764 31765 // PushState 31766 History.pushState(State.data,State.title,State.url,false); 31767 } 31768 else if ( document.location.hash !== adjustedHash ) { 31769 // Hash is a proper hash, so apply it 31770 31771 // Handle browser bugs 31772 if ( History.bugs.setHash ) { 31773 // Fix Safari Bug https://bugs.webkit.org/show_bug.cgi?id=56249 31774 31775 // Fetch the base page 31776 var pageUrl = History.getPageUrl(); 31777 31778 // Safari hash apply 31779 History.pushState(null,null,pageUrl+'#'+adjustedHash,false); 31780 } 31781 else { 31782 // Normal hash apply 31783 document.location.hash = adjustedHash; 31784 } 31785 } 31786 31787 // Chain 31788 return History; 31789 }; 31790 31791 /** 31792 * History.escape() 31793 * normalize and Escape a Hash 31794 * @return {string} 31795 */ 31796 History.escapeHash = function(hash){ 31797 var result = History.normalizeHash(hash); 31798 31799 // Escape hash 31800 result = window.escape(result); 31801 31802 // IE6 Escape Bug 31803 if ( !History.bugs.hashEscape ) { 31804 // Restore common parts 31805 result = result 31806 .replace(/\%21/g,'!') 31807 .replace(/\%26/g,'&') 31808 .replace(/\%3D/g,'=') 31809 .replace(/\%3F/g,'?'); 31810 } 31811 31812 // Return result 31813 return result; 31814 }; 31815 31816 /** 31817 * History.getHashByUrl(url) 31818 * Extracts the Hash from a URL 31819 * @param {string} url 31820 * @return {string} url 31821 */ 31822 History.getHashByUrl = function(url){ 31823 // Extract the hash 31824 var hash = String(url) 31825 .replace(/([^#]*)#?([^#]*)#?(.*)/, '$2') 31826 ; 31827 31828 // Unescape hash 31829 hash = History.unescapeHash(hash); 31830 31831 // Return hash 31832 return hash; 31833 }; 31834 31835 /** 31836 * History.setTitle(title) 31837 * Applies the title to the document 31838 * @param {State} newState 31839 * @return {Boolean} 31840 */ 31841 History.setTitle = function(newState){ 31842 // Prepare 31843 var title = newState.title; 31844 31845 // Initial 31846 if ( !title ) { 31847 var firstState = History.getStateByIndex(0); 31848 if ( firstState && firstState.url === newState.url ) { 31849 title = firstState.title||History.options.initialTitle; 31850 } 31851 } 31852 31853 // Apply 31854 try { 31855 document.getElementsByTagName('title')[0].innerHTML = title.replace('<','<').replace('>','>').replace(' & ',' & '); 31856 } 31857 catch ( Exception ) { } 31858 document.title = title; 31859 31860 // Chain 31861 return History; 31862 }; 31863 31864 // ---------------------------------------------------------------------- 31865 // Queueing 31866 31867 /** 31868 * History.queues 31869 * The list of queues to use 31870 * First In, First Out 31871 */ 31872 History.queues = []; 31873 31874 /** 31875 * History.busy(value) 31876 * @param {boolean} value [optional] 31877 * @return {boolean} busy 31878 */ 31879 History.busy = function(value){ 31880 // Apply 31881 if ( typeof value !== 'undefined' ) { 31882 //History.debug('History.busy: changing ['+(History.busy.flag||false)+'] to ['+(value||false)+']', History.queues.length); 31883 History.busy.flag = value; 31884 } 31885 // Default 31886 else if ( typeof History.busy.flag === 'undefined' ) { 31887 History.busy.flag = false; 31888 } 31889 31890 // Queue 31891 if ( !History.busy.flag ) { 31892 // Execute the next item in the queue 31893 clearTimeout(History.busy.timeout); 31894 var fireNext = function(){ 31895 if ( History.busy.flag ) return; 31896 for ( var i=History.queues.length-1; i >= 0; --i ) { 31897 var queue = History.queues[i]; 31898 if ( queue.length === 0 ) continue; 31899 var item = queue.shift(); 31900 History.fireQueueItem(item); 31901 History.busy.timeout = setTimeout(fireNext,History.options.busyDelay); 31902 } 31903 }; 31904 History.busy.timeout = setTimeout(fireNext,History.options.busyDelay); 31905 } 31906 31907 // Return 31908 return History.busy.flag; 31909 }; 31910 31911 /** 31912 * History.fireQueueItem(item) 31913 * Fire a Queue Item 31914 * @param {Object} item 31915 * @return {Mixed} result 31916 */ 31917 History.fireQueueItem = function(item){ 31918 return item.callback.apply(item.scope||History,item.args||[]); 31919 }; 31920 31921 /** 31922 * History.pushQueue(callback,args) 31923 * Add an item to the queue 31924 * @param {Object} item [scope,callback,args,queue] 31925 */ 31926 History.pushQueue = function(item){ 31927 // Prepare the queue 31928 History.queues[item.queue||0] = History.queues[item.queue||0]||[]; 31929 31930 // Add to the queue 31931 History.queues[item.queue||0].push(item); 31932 31933 // Chain 31934 return History; 31935 }; 31936 31937 /** 31938 * History.queue (item,queue), (func,queue), (func), (item) 31939 * Either firs the item now if not busy, or adds it to the queue 31940 */ 31941 History.queue = function(item,queue){ 31942 // Prepare 31943 if ( typeof item === 'function' ) { 31944 item = { 31945 callback: item 31946 }; 31947 } 31948 if ( typeof queue !== 'undefined' ) { 31949 item.queue = queue; 31950 } 31951 31952 // Handle 31953 if ( History.busy() ) { 31954 History.pushQueue(item); 31955 } else { 31956 History.fireQueueItem(item); 31957 } 31958 31959 // Chain 31960 return History; 31961 }; 31962 31963 /** 31964 * History.clearQueue() 31965 * Clears the Queue 31966 */ 31967 History.clearQueue = function(){ 31968 History.busy.flag = false;
31969 History.queues = []; 31970 return History; 31971 }; 31972 31973 31974 // ---------------------------------------------------------------------- 31975 // IE Bug Fix 31976 31977 /** 31978 * History.stateChanged 31979 * States whether or not the state has changed since the last double check was initialised 31980 */ 31981 History.stateChanged = false; 31982 31983 /** 31984 * History.doubleChecker 31985 * Contains the timeout used for the double checks 31986 */ 31987 History.doubleChecker = false; 31988 31989 /** 31990 * History.doubleCheckComplete() 31991 * Complete a double check 31992 * @return {History} 31993 */ 31994 History.doubleCheckComplete = function(){ 31995 // Update 31996 History.stateChanged = true; 31997 31998 // Clear 31999 History.doubleCheckClear(); 32000 32001 // Chain 32002 return History; 32003 }; 32004 32005 /** 32006 * History.doubleCheckClear() 32007 * Clear a double check 32008 * @return {History} 32009 */ 32010 History.doubleCheckClear = function(){ 32011 // Clear 32012 if ( History.doubleChecker ) { 32013 clearTimeout(History.doubleChecker); 32014 History.doubleChecker = false; 32015 } 32016 32017 // Chain 32018 return History; 32019 }; 32020 32021 /** 32022 * History.doubleCheck() 32023 * Create a double check 32024 * @return {History} 32025 */ 32026 History.doubleCheck = function(tryAgain){ 32027 // Reset 32028 History.stateChanged = false; 32029 History.doubleCheckClear(); 32030 32031 // Fix IE6,IE7 bug where calling history.back or history.forward does not actually change the hash (whereas doing it manually does) 32032 // Fix Safari 5 bug where sometimes the state does not change: https://bugs.webkit.org/show_bug.cgi?id=42940 32033 if ( History.bugs.ieDoubleCheck ) { 32034 // Apply Check 32035 History.doubleChecker = setTimeout( 32036 function(){ 32037 History.doubleCheckClear(); 32038 if ( !History.stateChanged ) { 32039 //History.debug('History.doubleCheck: State has not yet changed, trying again', arguments); 32040 // Re-Attempt 32041 tryAgain(); 32042 } 32043 return true; 32044 }, 32045 History.options.doubleCheckInterval 32046 ); 32047 } 32048 32049 // Chain 32050 return History; 32051 }; 32052 32053 // ---------------------------------------------------------------------- 32054 // Safari Bug Fix 32055 32056 /** 32057 * History.safariStatePoll() 32058 * Poll the current state 32059 * @return {History} 32060 */ 32061 History.safariStatePoll = function(){ 32062 // Poll the URL 32063 32064 // Get the Last State which has the new URL 32065 var 32066 urlState = History.extractState(document.location.href), 32067 newState; 32068 32069 // Check for a difference 32070 if ( !History.isLastSavedState(urlState) ) { 32071 newState = urlState; 32072 } 32073 else { 32074 return; 32075 } 32076 32077 // Check if we have a state with that url 32078 // If not create it 32079 if ( !newState ) { 32080 //History.debug('History.safariStatePoll: new'); 32081 newState = History.createStateObject(); 32082 } 32083 32084 // Apply the New State 32085 //History.debug('History.safariStatePoll: trigger'); 32086 History.Adapter.trigger(window,'popstate'); 32087 32088 // Chain 32089 return History; 32090 }; 32091 32092 // ---------------------------------------------------------------------- 32093 // State Aliases 32094 32095 /** 32096 * History.back(queue) 32097 * Send the browser history back one item 32098 * @param {Integer} queue [optional] 32099 */ 32100 History.back = function(queue){ 32101 //History.debug('History.back: called', arguments); 32102 32103 // Handle Queueing 32104 if ( queue !== false && History.busy() ) { 32105 // Wait + Push to Queue 32106 //History.debug('History.back: we must wait', arguments); 32107 History.pushQueue({ 32108 scope: History, 32109 callback: History.back, 32110 args: arguments, 32111 queue: queue 32112 }); 32113 return false; 32114 } 32115 32116 // Make Busy + Continue 32117 History.busy(true); 32118 32119 // Fix certain browser bugs that prevent the state from changing 32120 History.doubleCheck(function(){ 32121 History.back(false); 32122 }); 32123 32124 // Go back 32125 history.go(-1); 32126 32127 // End back closure 32128 return true; 32129 }; 32130 32131 /** 32132 * History.forward(queue) 32133 * Send the browser history forward one item 32134 * @param {Integer} queue [optional] 32135 */ 32136 History.forward = function(queue){ 32137 //History.debug('History.forward: called', arguments); 32138 32139 // Handle Queueing 32140 if ( queue !== false && History.busy() ) { 32141 // Wait + Push to Queue 32142 //History.debug('History.forward: we must wait', arguments); 32143 History.pushQueue({ 32144 scope: History, 32145 callback: History.forward, 32146 args: arguments, 32147 queue: queue 32148 }); 32149 return false;
32150 } 32151 32152 // Make Busy + Continue 32153 History.busy(true); 32154 32155 // Fix certain browser bugs that prevent the state from changing 32156 History.doubleCheck(function(){ 32157 History.forward(false); 32158 }); 32159 32160 // Go forward 32161 history.go(1); 32162 32163 // End forward closure 32164 return true; 32165 }; 32166 32167 /** 32168 * History.go(index,queue) 32169 * Send the browser history back or forward index times 32170 * @param {Integer} queue [optional] 32171 */ 32172 History.go = function(index,queue){ 32173 //History.debug('History.go: called', arguments); 32174 32175 // Prepare 32176 var i; 32177 32178 // Handle 32179 if ( index > 0 ) { 32180 // Forward 32181 for ( i=1; i<=index; ++i ) { 32182 History.forward(queue); 32183 } 32184 } 32185 else if ( index < 0 ) { 32186 // Backward 32187 for ( i=-1; i>=index; --i ) { 32188 History.back(queue); 32189 } 32190 } 32191 else { 32192 throw new Error('History.go: History.go requires a positive or negative integer passed.'); 32193 } 32194 32195 // Chain 32196 return History; 32197 }; 32198 32199 32200 // ---------------------------------------------------------------------- 32201 // Initialise 32202 32203 /** 32204 * Create the initial State 32205 */ 32206 History.saveState(History.storeState(History.extractState(document.location.href,true))); 32207 32208 /** 32209 * Bind for Saving Store 32210 */ 32211 if ( amplify ) { 32212 History.onUnload = function(){ 32213 // Prepare 32214 var 32215 currentStore = amplify.store('History.store')||{}, 32216 item; 32217 32218 // Ensure 32219 currentStore.idToState = currentStore.idToState || {}; 32220 currentStore.urlToId = currentStore.urlToId || {}; 32221 currentStore.stateToId = currentStore.stateToId || {}; 32222 32223 // Sync 32224 for ( item in History.idToState ) { 32225 if ( !History.idToState.hasOwnProperty(item) ) { 32226 continue; 32227 } 32228 currentStore.idToState[item] = History.idToState[item]; 32229 } 32230 for ( item in History.urlToId ) { 32231 if ( !History.urlToId.hasOwnProperty(item) ) { 32232 continue; 32233 } 32234 currentStore.urlToId[item] = History.urlToId[item]; 32235 } 32236 for ( item in History.stateToId ) { 32237 if ( !History.stateToId.hasOwnProperty(item) ) { 32238 continue; 32239 } 32240 currentStore.stateToId[item] = History.stateToId[item]; 32241 } 32242 32243 // Update 32244 History.store = currentStore; 32245 32246 // Store 32247 amplify.store('History.store',currentStore); 32248 }; 32249 // For Internet Explorer 32250 History.intervalList.push(setInterval(History.onUnload,History.options.storeInterval)); 32251 // For Other Browsers 32252 History.Adapter.bind(window,'beforeunload',History.onUnload); 32253 History.Adapter.bind(window,'unload',History.onUnload); 32254 // Both are enabled for consistency 32255 } 32256 32257 32258 // ---------------------------------------------------------------------- 32259 // HTML5 State Support 32260 32261 if ( History.emulated.pushState ) { 32262 /* 32263 * Provide Skeleton for HTML4 Browsers 32264 */ 32265 32266 // Prepare 32267 var emptyFunction = function(){}; 32268 History.pushState = History.pushState||emptyFunction; 32269 History.replaceState = History.replaceState||emptyFunction; 32270 } 32271 else { 32272 /* 32273 * Use native HTML5 History API Implementation 32274 */ 32275 32276 /** 32277 * History.onPopState(event,extra) 32278 * Refresh the Current State 32279 */ 32280 History.onPopState = function(event){ 32281 // Reset the double check 32282 History.doubleCheckComplete(); 32283 32284 // Check for a Hash, and handle apporiatly 32285 var currentHash = History.getHash(); 32286 if ( currentHash ) { 32287 // Expand Hash 32288 var currentState = History.extractState(currentHash||document.location.href,true); 32289 if ( currentState ) { 32290 // We were able to parse it, it must be a State! 32291 // Let's forward to replaceState 32292 //History.debug('History.onPopState: state anchor', currentHash, currentState); 32293 History.replaceState(currentState.data, currentState.title, currentState.url, false); 32294 } 32295 else { 32296 // Traditional Anchor 32297 //History.debug('History.onPopState: traditional anchor', currentHash); 32298 History.Adapter.trigger(window,'anchorchange'); 32299 History.busy(false); 32300 } 32301 32302 // We don't care for hashes 32303 History.expectedStateId = false; 32304 return false; 32305 } 32306 32307 // Prepare 32308 var newState = false; 32309 32310 // Prepare 32311 event = event||{}; 32312 if ( typeof event.state === 'undefined' ) { 32313 // jQuery 32314 if ( typeof event.originalEvent !== 'undefined' && typeof event.originalEvent.state !== 'undefined' ) { 32315 event.state = event.originalEvent.state||false; 32316 } 32317 // MooTools 32318 else if ( typeof event.event !== 'undefined' && typeof event.event.state !== 'undefined' ) { 32319 event.state = event.event.state||false; 32320 } 32321 } 32322 32323 // Ensure 32324 event.state = (event.state||false); 32325 32326 // Fetch State 32327 if ( event.state ) { 32328 // Vanilla: Back/forward button was used 32329 newState = History.getStateById(event.state); 32330 } 32331 else if ( History.expectedStateId ) { 32332 // Vanilla: A new state was pushed, and popstate was called manually 32333 newState = History.getStateById(History.expectedStateId); 32334 } 32335 else { 32336 // Initial State 32337 newState = History.extractState(document.location.href); 32338 } 32339
32340 // The State did not exist in our store 32341 if ( !newState ) { 32342 // Regenerate the State 32343 newState = History.createStateObject(null,null,document.location.href); 32344 } 32345 32346 // Clean 32347 History.expectedStateId = false; 32348 32349 // Check if we are the same state 32350 if ( History.isLastSavedState(newState) ) { 32351 // There has been no change (just the page's hash has finally propagated) 32352 //History.debug('History.onPopState: no change', newState, History.savedStates); 32353 History.busy(false); 32354 return false; 32355 } 32356 32357 // Store the State 32358 History.storeState(newState); 32359 History.saveState(newState); 32360 32361 // Force update of the title 32362 History.setTitle(newState); 32363 32364 // Fire Our Event 32365 History.Adapter.trigger(window,'statechange'); 32366 History.busy(false); 32367 32368 // Return true 32369 return true; 32370 }; 32371 History.Adapter.bind(window,'popstate',History.onPopState); 32372 32373 /** 32374 * History.pushState(data,title,url) 32375 * Add a new State to the history object, become it, and trigger onpopstate 32376 * We have to trigger for HTML4 compatibility 32377 * @param {object} data 32378 * @param {string} title 32379 * @param {string} url 32380 * @return {true} 32381 */ 32382 History.pushState = function(data,title,url,queue){ 32383 //History.debug('History.pushState: called', arguments); 32384 32385 // Check the State 32386 if ( History.getHashByUrl(url) && History.emulated.pushState ) { 32387 throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).'); 32388 } 32389 32390 // Handle Queueing 32391 if ( queue !== false && History.busy() ) { 32392 // Wait + Push to Queue 32393 //History.debug('History.pushState: we must wait', arguments); 32394 History.pushQueue({ 32395 scope: History, 32396 callback: History.pushState, 32397 args: arguments, 32398 queue: queue 32399 }); 32400 return false; 32401 } 32402 32403 // Make Busy + Continue 32404 History.busy(true); 32405 32406 // Create the newState 32407 var newState = History.createStateObject(data,title,url); 32408 32409 // Check it 32410 if ( History.isLastSavedState(newState) ) { 32411 // Won't be a change 32412 History.busy(false); 32413 } 32414 else { 32415 // Store the newState 32416 History.storeState(newState); 32417 History.expectedStateId = newState.id; 32418 32419 // Push the newState 32420 history.pushState(newState.id,newState.title,newState.url); 32421 32422 // Fire HTML5 Event 32423 History.Adapter.trigger(window,'popstate'); 32424 } 32425 32426 // End pushState closure 32427 return true; 32428 }; 32429 32430 /** 32431 * History.replaceState(data,title,url) 32432 * Replace the State and trigger onpopstate 32433 * We have to trigger for HTML4 compatibility 32434 * @param {object} data 32435 * @param {string} title 32436 * @param {string} url 32437 * @return {true} 32438 */ 32439 History.replaceState = function(data,title,url,queue){ 32440 //History.debug('History.replaceState: called', arguments); 32441 32442 // Check the State 32443 if ( History.getHashByUrl(url) && History.emulated.pushState ) { 32444 throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).'); 32445 } 32446 32447 // Handle Queueing 32448 if ( queue !== false && History.busy() ) { 32449 // Wait + Push to Queue 32450 //History.debug('History.replaceState: we must wait', arguments); 32451 History.pushQueue({ 32452 scope: History, 32453 callback: History.replaceState, 32454 args: arguments, 32455 queue: queue 32456 }); 32457 return false; 32458 } 32459 32460 // Make Busy + Continue 32461 History.busy(true); 32462 32463 // Create the newState 32464 var newState = History.createStateObject(data,title,url); 32465 32466 // Check it 32467 if ( History.isLastSavedState(newState) ) { 32468 // Won't be a change 32469 History.busy(false); 32470 } 32471 else { 32472 // Store the newState 32473 History.storeState(newState); 32474 History.expectedStateId = newState.id; 32475 32476 // Push the newState 32477 history.replaceState(newState.id,newState.title,newState.url); 32478 32479 // Fire HTML5 Event 32480 History.Adapter.trigger(window,'popstate'); 32481 } 32482 32483 // End replaceState closure 32484 return true; 32485 }; 32486 32487 // Be aware, the following is only for native pushState implementations 32488 // If you are wanting to include something for all browsers 32489 // Then include it above this if block 32490 32491 /** 32492 * Setup Safari Fix 32493 */ 32494 if ( History.bugs.safariPoll ) { 32495 History.intervalList.push(setInterval(History.safariStatePoll, History.options.safariPollInterval)); 32496 } 32497 32498 /** 32499 * Ensure Cross Browser Compatibility 32500 */
32501 if ( navigator.vendor === 'Apple Computer, Inc.' || (navigator.appCodeName||'') === 'Mozilla' ) { 32502 /** 32503 * Fix Safari HashChange Issue 32504 */ 32505 32506 // Setup Alias 32507 History.Adapter.bind(window,'hashchange',function(){ 32508 History.Adapter.trigger(window,'popstate'); 32509 }); 32510 32511 // Initialise Alias 32512 if ( History.getHash() ) { 32513 History.Adapter.onDomLoad(function(){ 32514 History.Adapter.trigger(window,'hashchange'); 32515 }); 32516 } 32517 } 32518 32519 } // !History.emulated.pushState 32520 32521 }; // History.initCore 32522 32523 // Try and Initialise History 32524 History.init(); 32525 32526})(window); 32527/** 32528 * History.js HTML4 Support 32529 * Depends on the HTML5 Support 32530 * @author Benjamin Arthur Lupton <[email protected]> 32531 * @copyright 2010-2011 Benjamin Arthur Lupton <[email protected]> 32532 * @license New BSD License <http://creativecommons.org/licenses/BSD/> 32533 */ 32534 32535 32536(function(window,undefined){ 32537 "use strict"; 32538 32539 // -------------------------------------------------------------------------- 32540 // Initialise 32541 32542 // Localise Globals 32543 var 32544 document = window.document, // Make sure we are using the correct document 32545 setTimeout = window.setTimeout||setTimeout, 32546 clearTimeout = window.clearTimeout||clearTimeout, 32547 setInterval = window.setInterval||setInterval, 32548 History = window.History = window.History||{}; // Public History Object 32549 32550 // Check Existence 32551 if ( typeof History.initHtml4 !== 'undefined' ) { 32552 throw new Error('History.js HTML4 Support has already been loaded...'); 32553 } 32554 32555 // -------------------------------------------------------------------------- 32556 // Initialise HTML4 Support 32557 32558 // Initialise HTML4 Support 32559 History.initHtml4 = function(){ 32560 // Initialise 32561 if ( typeof History.initHtml4.initialized !== 'undefined' ) { 32562 // Already Loaded 32563 return false; 32564 } 32565 else { 32566 History.initHtml4.initialized = true; 32567 } 32568 32569 // ---------------------------------------------------------------------- 32570 // Properties 32571 32572 /** 32573 * History.enabled 32574 * Is History enabled? 32575 */ 32576 History.enabled = true; 32577 32578 32579 // ---------------------------------------------------------------------- 32580 // Hash Storage 32581 32582 /** 32583 * History.savedHashes 32584 * Store the hashes in an array 32585 */ 32586 History.savedHashes = []; 32587 32588 /** 32589 * History.isLastHash(newHash) 32590 * Checks if the hash is the last hash 32591 * @param {string} newHash 32592 * @return {boolean} true 32593 */ 32594 History.isLastHash = function(newHash){ 32595 // Prepare 32596 var oldHash = History.getHashByIndex(); 32597 32598 // Check 32599 var isLast = newHash === oldHash; 32600 32601 // Return isLast 32602 return isLast; 32603 }; 32604 32605 /** 32606 * History.saveHash(newHash) 32607 * Push a Hash 32608 * @param {string} newHash 32609 * @return {boolean} true 32610 */ 32611 History.saveHash = function(newHash){ 32612 // Check Hash 32613 if ( History.isLastHash(newHash) ) { 32614 return false; 32615 } 32616 32617 // Push the Hash 32618 History.savedHashes.push(newHash); 32619 32620 // Return true 32621 return true; 32622 }; 32623 32624 /** 32625 * History.getHashByIndex() 32626 * Gets a hash by the index 32627 * @param {integer} index 32628 * @return {string} 32629 */ 32630 History.getHashByIndex = function(index){ 32631 // Prepare 32632 var hash = null; 32633 32634 // Handle 32635 if ( typeof index === 'undefined' ) { 32636 // Get the last inserted 32637 hash = History.savedHashes[History.savedHashes.length-1]; 32638 } 32639 else if ( index < 0 ) { 32640 // Get from the end 32641 hash = History.savedHashes[History.savedHashes.length+index]; 32642 } 32643 else { 32644 // Get from the beginning 32645 hash = History.savedHashes[index]; 32646 } 32647 32648 // Return hash 32649 return hash; 32650 }; 32651 32652 // ---------------------------------------------------------------------- 32653 // Discarded States 32654 32655 /** 32656 * History.discardedHashes 32657 * A hashed array of discarded hashes 32658 */ 32659 History.discardedHashes = {}; 32660 32661 /** 32662 * History.discardedStates 32663 * A hashed array of discarded states 32664 */ 32665 History.discardedStates = {}; 32666 32667 /** 32668 * History.discardState(State) 32669 * Discards the state by ignoring it through History 32670 * @param {object} State 32671 * @return {true} 32672 */ 32673 History.discardState = function(discardedState,forwardState,backState){ 32674 //History.debug('History.discardState', arguments); 32675 // Prepare 32676 var discardedStateHash = History.getHashByState(discardedState); 32677 32678 // Create Discard Object 32679 var discardObject = { 32680 'discardedState': discardedState, 32681 'backState': backState, 32682 'forwardState': forwardState 32683 }; 32684 32685 // Add to DiscardedStates 32686 History.discardedStates[discardedStateHash] = discardObject; 32687 32688 // Return true 32689 return true; 32690 }; 32691 32692 /** 32693 * History.discardHash(hash) 32694 * Discards the hash by ignoring it through History 32695 * @param {string} hash 32696 * @return {true} 32697 */ 32698 History.discardHash = function(discardedHash,forwardState,backState){ 32699 //History.debug('History.discardState', arguments); 32700 // Create Discard Object 32701 var discardObject = { 32702 'discardedHash': discardedHash, 32703 'backState': backState, 32704 'forwardState': forwardState 32705 }; 32706 32707 // Add to discardedHash 32708 History.discardedHashes[discardedHash] = discardObject; 32709 32710 // Return true 32711 return true; 32712 }; 32713 32714 /** 32715 * History.discardState(State) 32716 * Checks to see if the state is discarded 32717 * @param {object} State 32718 * @return {bool} 32719 */ 32720 History.discardedState = function(State){ 32721 // Prepare 32722 var StateHash = History.getHashByState(State); 32723 32724 // Check 32725 var discarded = History.discardedStates[StateHash]||false; 32726 32727 // Return true 32728 return discarded; 32729 }; 32730 32731 /** 32732 * History.discardedHash(hash) 32733 * Checks to see if the state is discarded 32734 * @param {string} State 32735 * @return {bool} 32736 */ 32737 History.discardedHash = function(hash){ 32738 // Check 32739 var discarded = History.discardedHashes[hash]||false; 32740 32741 // Return true 32742 return discarded; 32743 }; 32744 32745 /** 32746 * History.recycleState(State) 32747 * Allows a discarded state to be used again 32748 * @param {object} data 32749 * @param {string} title 32750 * @param {string} url 32751 * @return {true} 32752 */ 32753 History.recycleState = function(State){ 32754 //History.debug('History.recycleState', arguments); 32755 // Prepare 32756 var StateHash = History.getHashByState(State); 32757 32758 // Remove from DiscardedStates 32759 if ( History.discardedState(State) ) { 32760 delete History.discardedStates[StateHash]; 32761 } 32762 32763 // Return true 32764 return true; 32765 }; 32766 32767 // ---------------------------------------------------------------------- 32768 // HTML4 HashChange Support 32769 32770 if ( History.emulated.hashChange ) { 32771 /* 32772 * We must emulate the HTML4 HashChange Support by manually checking for hash changes 32773 */ 32774 32775 /** 32776 * History.hashChangeInit() 32777 * Init the HashChange Emulation 32778 */ 32779 History.hashChangeInit = function(){ 32780 // Define our Checker Function 32781 History.checkerFunction = null; 32782 32783 // Define some variables that will help in our checker function 32784 var 32785 lastDocumentHash = ''; 32786 32787 // Handle depending on the browser 32788 if ( History.isInternetExplorer() ) { 32789 // IE6 and IE7 32790 // We need to use an iframe to emulate the back and forward buttons 32791 32792 // Create iFrame 32793 var 32794 iframeId = 'historyjs-iframe', 32795 iframe = document.createElement('iframe'); 32796 32797 // Adjust iFarme 32798 iframe.setAttribute('id', iframeId); 32799 iframe.style.display = 'none'; 32800 32801 // Append iFrame 32802 document.body.appendChild(iframe); 32803 32804 // Create initial history entry 32805 iframe.contentWindow.document.open(); 32806 iframe.contentWindow.document.close(); 32807 32808 // Define some variables that will help in our checker function 32809 var 32810 lastIframeHash = '', 32811 checkerRunning = false;
32812 32813 // Define the checker function 32814 History.checkerFunction = function(){ 32815 // Check Running 32816 if ( checkerRunning ) { 32817 return false; 32818 } 32819 32820 // Update Running 32821 checkerRunning = true; 32822 32823 // Fetch 32824 var 32825 documentHash = History.getHash()||'', 32826 iframeHash = History.unescapeHash(iframe.contentWindow.document.location.hash)||''; 32827 32828 // The Document Hash has changed (application caused) 32829 if ( documentHash !== lastDocumentHash ) { 32830 // Equalise 32831 lastDocumentHash = documentHash; 32832 32833 // Create a history entry in the iframe 32834 if ( iframeHash !== documentHash ) { 32835 //History.debug('hashchange.checker: iframe hash change', 'documentHash (new):', documentHash, 'iframeHash (old):', iframeHash); 32836 32837 // Equalise 32838 lastIframeHash = iframeHash = documentHash; 32839 32840 // Create History Entry 32841 iframe.contentWindow.document.open(); 32842 iframe.contentWindow.document.close(); 32843 32844 // Update the iframe's hash 32845 iframe.contentWindow.document.location.hash = History.escapeHash(documentHash); 32846 } 32847 32848 // Trigger Hashchange Event 32849 History.Adapter.trigger(window,'hashchange'); 32850 } 32851 32852 // The iFrame Hash has changed (back button caused) 32853 else if ( iframeHash !== lastIframeHash ) { 32854 //History.debug('hashchange.checker: iframe hash out of sync', 'iframeHash (new):', iframeHash, 'documentHash (old):', documentHash); 32855 32856 // Equalise 32857 lastIframeHash = iframeHash; 32858 32859 // Update the Hash 32860 History.setHash(iframeHash,false); 32861 } 32862 32863 // Reset Running 32864 checkerRunning = false; 32865 32866 // Return true 32867 return true; 32868 }; 32869 } 32870 else { 32871 // We are not IE 32872 // Firefox 1 or 2, Opera 32873 32874 // Define the checker function 32875 History.checkerFunction = function(){ 32876 // Prepare 32877 var documentHash = History.getHash(); 32878 32879 // The Document Hash has changed (application caused) 32880 if ( documentHash !== lastDocumentHash ) { 32881 // Equalise 32882 lastDocumentHash = documentHash; 32883 32884 // Trigger Hashchange Event 32885 History.Adapter.trigger(window,'hashchange'); 32886 } 32887 32888 // Return true 32889 return true; 32890 }; 32891 } 32892 32893 // Apply the checker function 32894 History.intervalList.push(setInterval(History.checkerFunction, History.options.hashChangeInterval)); 32895 32896 // Done 32897 return true; 32898 }; // History.hashChangeInit 32899 32900 // Bind hashChangeInit 32901 History.Adapter.onDomLoad(History.hashChangeInit); 32902 32903 } // History.emulated.hashChange 32904 32905 32906 // ---------------------------------------------------------------------- 32907 // HTML5 State Support 32908 32909 if ( History.emulated.pushState ) { 32910 /* 32911 * We must emulate the HTML5 State Management by using HTML4 HashChange 32912 */ 32913 32914 /** 32915 * History.onHashChange(event) 32916 * Trigger HTML5's window.onpopstate via HTML4 HashChange Support 32917 */ 32918 History.onHashChange = function(event){ 32919 //History.debug('History.onHashChange', arguments); 32920 32921 // Prepare 32922 var 32923 currentUrl = ((event && event.newURL) || document.location.href), 32924 currentHash = History.getHashByUrl(currentUrl), 32925 currentState = null, 32926 currentStateHash = null, 32927 currentStateHashExits = null; 32928 32929 // Check if we are the same state 32930 if ( History.isLastHash(currentHash) ) { 32931 // There has been no change (just the page's hash has finally propagated) 32932 //History.debug('History.onHashChange: no change'); 32933 History.busy(false); 32934 return false; 32935 } 32936 32937 // Reset the double check 32938 History.doubleCheckComplete(); 32939 32940 // Store our location for use in detecting back/forward direction 32941 History.saveHash(currentHash); 32942 32943 // Expand Hash 32944 if ( currentHash && History.isTraditionalAnchor(currentHash) ) { 32945 //History.debug('History.onHashChange: traditional anchor', currentHash); 32946 // Traditional Anchor Hash 32947 History.Adapter.trigger(window,'anchorchange'); 32948 History.busy(false); 32949 return false; 32950 } 32951 32952 // Create State 32953 currentState = History.extractState(History.getFullUrl(currentHash||document.location.href,false),true); 32954 32955 // Check if we are the same state 32956 if ( History.isLastSavedState(currentState) ) { 32957 //History.debug('History.onHashChange: no change'); 32958 // There has been no change (just the page's hash has finally propagated) 32959 History.busy(false); 32960 return false;
32961 } 32962 32963 // Create the state Hash 32964 currentStateHash = History.getHashByState(currentState); 32965 32966 // Check if we are DiscardedState 32967 var discardObject = History.discardedState(currentState); 32968 if ( discardObject ) { 32969 // Ignore this state as it has been discarded and go back to the state before it 32970 if ( History.getHashByIndex(-2) === History.getHashByState(discardObject.forwardState) ) { 32971 // We are going backwards 32972 //History.debug('History.onHashChange: go backwards'); 32973 History.back(false); 32974 } else { 32975 // We are going forwards 32976 //History.debug('History.onHashChange: go forwards'); 32977 History.forward(false); 32978 } 32979 return false; 32980 } 32981 32982 // Push the new HTML5 State 32983 //History.debug('History.onHashChange: success hashchange'); 32984 History.pushState(currentState.data,currentState.title,currentState.url,false); 32985 32986 // End onHashChange closure 32987 return true; 32988 }; 32989 History.Adapter.bind(window,'hashchange',History.onHashChange); 32990 32991 /** 32992 * History.pushState(data,title,url) 32993 * Add a new State to the history object, become it, and trigger onpopstate 32994 * We have to trigger for HTML4 compatibility 32995 * @param {object} data 32996 * @param {string} title 32997 * @param {string} url 32998 * @return {true} 32999 */ 33000 History.pushState = function(data,title,url,queue){ 33001 //History.debug('History.pushState: called', arguments); 33002 33003 // Check the State 33004 if ( History.getHashByUrl(url) ) { 33005 throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).'); 33006 } 33007 33008 // Handle Queueing 33009 if ( queue !== false && History.busy() ) { 33010 // Wait + Push to Queue 33011 //History.debug('History.pushState: we must wait', arguments); 33012 History.pushQueue({ 33013 scope: History, 33014 callback: History.pushState, 33015 args: arguments, 33016 queue: queue 33017 }); 33018 return false; 33019 } 33020 33021 // Make Busy 33022 History.busy(true); 33023 33024 // Fetch the State Object 33025 var 33026 newState = History.createStateObject(data,title,url), 33027 newStateHash = History.getHashByState(newState), 33028 oldState = History.getState(false), 33029 oldStateHash = History.getHashByState(oldState), 33030 html4Hash = History.getHash(); 33031 33032 // Store the newState 33033 History.storeState(newState); 33034 History.expectedStateId = newState.id; 33035 33036 // Recycle the State 33037 History.recycleState(newState); 33038 33039 // Force update of the title 33040 History.setTitle(newState); 33041 33042 // Check if we are the same State 33043 if ( newStateHash === oldStateHash ) { 33044 //History.debug('History.pushState: no change', newStateHash); 33045 History.busy(false); 33046 return false; 33047 } 33048 33049 // Update HTML4 Hash 33050 if ( newStateHash !== html4Hash && newStateHash !== History.getShortUrl(document.location.href) ) { 33051 //History.debug('History.pushState: update hash', newStateHash, html4Hash); 33052 History.setHash(newStateHash,false); 33053 return false; 33054 } 33055 33056 // Update HTML5 State 33057 History.saveState(newState); 33058 33059 // Fire HTML5 Event 33060 //History.debug('History.pushState: trigger popstate'); 33061 History.Adapter.trigger(window,'statechange'); 33062 History.busy(false); 33063 33064 // End pushState closure 33065 return true; 33066 }; 33067 33068 /** 33069 * History.replaceState(data,title,url) 33070 * Replace the State and trigger onpopstate 33071 * We have to trigger for HTML4 compatibility 33072 * @param {object} data 33073 * @param {string} title 33074 * @param {string} url 33075 * @return {true} 33076 */ 33077 History.replaceState = function(data,title,url,queue){ 33078 //History.debug('History.replaceState: called', arguments); 33079 33080 // Check the State 33081 if ( History.getHashByUrl(url) ) { 33082 throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).'); 33083 } 33084 33085 // Handle Queueing 33086 if ( queue !== false && History.busy() ) { 33087 // Wait + Push to Queue 33088 //History.debug('History.replaceState: we must wait', arguments); 33089 History.pushQueue({ 33090 scope: History, 33091 callback: History.replaceState, 33092 args: arguments, 33093 queue: queue 33094 }); 33095 return false; 33096 } 33097 33098 // Make Busy 33099 History.busy(true); 33100 33101 // Fetch the State Objects 33102 var 33103 newState = History.createStateObject(data,title,url), 33104 oldState = History.getState(false), 33105 previousState = History.getStateByIndex(-2); 33106 33107 // Discard Old State 33108 History.discardState(oldState,newState,previousState); 33109 33110 // Alias to PushState 33111 History.pushState(newState.data,newState.title,newState.url,false); 33112 33113 // End replaceState closure 33114 return true; 33115 }; 33116 33117 /** 33118 * Ensure initial state is handled correctly 33119 */ 33120 if ( History.getHash() && !History.emulated.hashChange ) { 33121 History.Adapter.onDomLoad(function(){ 33122 History.Adapter.trigger(window,'hashchange'); 33123 }); 33124 } 33125 33126 } // History.emulated.pushState 33127 33128 }; // History.initHtml4 33129 33130 // Try and Initialise History 33131 History.init(); 33132 33133}
33133)(window); 33134 33135 33136 33137 33138 33139/* 33140 33141 jQuery Tags Input Plugin 1.3.3 33142 33143 Copyright (c) 2011 XOXCO, Inc 33144 33145 Documentation for this plugin lives here: 33146 http://xoxco.com/clickable/jquery-tags-input 33147 33148 Licensed under the MIT license: 33149 http://www.opensource.org/licenses/mit-license.php 33150 33151 [email protected] 33152 33153*/ 33154 33155 33156(function($) { 33157 33158 var delimiter = new Array(); 33159 var tags_callbacks = new Array(); 33160 $.fn.doAutosize = function(o){ 33161 var minWidth = $(this).data('minwidth'), 33162 maxWidth = $(this).data('maxwidth'), 33163 val = '', 33164 input = $(this), 33165 testSubject = $('#'+$(this).data('tester_id')); 33166 33167 if (val === (val = input.val())) {return;} 33168 33169 // Enter new content into testSubject 33170 var escaped = val.replace(/&/g, '&').replace(/\s/g,' ').replace(/</g, '<').replace(/>/g, '>'); 33171 testSubject.html(escaped); 33172 // Calculate new width + whether to change 33173 var testerWidth = testSubject.width(), 33174 newWidth = (testerWidth + o.comfortZone) >= minWidth ? testerWidth + o.comfortZone : minWidth, 33175 currentWidth = input.width(), 33176 isValidWidthChange = (newWidth < currentWidth && newWidth >= minWidth) 33177 || (newWidth > minWidth && newWidth < maxWidth); 33178 33179 // Animate width 33180 if (isValidWidthChange) { 33181 input.width(newWidth); 33182 } 33183 33184 33185 }; 33186 $.fn.resetAutosize = function(options){ 33187 // alert(JSON.stringify(options)); 33188 var minWidth = $(this).data('minwidth') || options.minInputWidth || $(this).width(), 33189 maxWidth = $(this).data('maxwidth') || options.maxInputWidth || ($(this).closest('.tagsinput').width() - options.inputPadding), 33190 val = '', 33191 input = $(this), 33192 testSubject = $('<tester/>').css({ 33193 position: 'absolute', 33194 top: -9999, 33195 left: -9999, 33196 width: 'auto', 33197 fontSize: input.css('fontSize'), 33198 fontFamily: input.css('fontFamily'), 33199 fontWeight: input.css('fontWeight'), 33200 letterSpacing: input.css('letterSpacing'), 33201 whiteSpace: 'nowrap' 33202 }), 33203 testerId = $(this).attr('id')+'_autosize_tester'; 33204 if(! $('#'+testerId).length > 0){ 33205 testSubject.attr('id', testerId); 33206 testSubject.appendTo('body'); 33207 } 33208 33209 input.data('minwidth', minWidth); 33210 input.data('maxwidth', maxWidth); 33211 input.data('tester_id', testerId); 33212 input.css('width', minWidth); 33213 }; 33214 33215 $.fn.addTag = function(value,options) { 33216 options = jQuery.extend({focus:false,callback:true},options); 33217 this.each(function() { 33218 var id = $(this).attr('id'); 33219 33220 var tagslist = $(this).val().split(delimiter[id]); 33221 if (tagslist[0] == '') { 33222 tagslist = new Array(); 33223 } 33224 33225 value = jQuery.trim(value); 33226 33227 if (options.unique) { 33228 var skipTag = $(this).tagExist(value); 33229 if(skipTag == true) { 33230 //Marks fake input as not_valid to let styling it 33231 $('#'+id+'_tag').addClass('not_valid'); 33232 } 33233 } else { 33234 var skipTag = false; 33235 } 33236 33237 if (value !='' && skipTag != true) { 33238 $('<span>').addClass('tag').append( 33239 $('<span>').text(value).append(' '), 33240 $('<a>', { 33241 href : '#', 33242 title : 'Removing tag', 33243 text : 'x' 33244 }).click(function () { 33245 return $('#' + id).removeTag(escape(value)); 33246 }) 33247 ).insertBefore('#' + id + '_addTag'); 33248 33249 tagslist.push(value); 33250 33251 $('#'+id+'_tag').val(''); 33252 if (options.focus) { 33253 $('#'+id+'_tag').focus(); 33254 } else { 33255 $('#'+id+'_tag').blur(); 33256 } 33257 33258 $.fn.tagsInput.updateTagsField(this,tagslist); 33259 33260 if (options.callback && tags_callbacks[id] && tags_callbacks[id]['onAddTag']) { 33261 var f = tags_callbacks[id]['onAddTag']; 33262 f.call(this, value); 33263 } 33264 if(tags_callbacks[id] && tags_callbacks[id]['onChange']) 33265 { 33266 var i = tagslist.length; 33267 var f = tags_callbacks[id]['onChange']; 33268 f.call(this, $(this), tagslist[i-1]); 33269 } 33270 } 33271 33272 }); 33273 33274 return false;
33275 }; 33276 33277 $.fn.removeTag = function(value) { 33278 value = unescape(value); 33279 this.each(function() { 33280 var id = $(this).attr('id'); 33281 33282 var old = $(this).val().split(delimiter[id]); 33283 33284 $('#'+id+'_tagsinput .tag').remove(); 33285 str = ''; 33286 for (i=0; i< old.length; i++) { 33287 if (old[i]!=value) { 33288 str = str + delimiter[id] +old[i]; 33289 } 33290 } 33291 33292 $.fn.tagsInput.importTags(this,str); 33293 33294 if (tags_callbacks[id] && tags_callbacks[id]['onRemoveTag']) { 33295 var f = tags_callbacks[id]['onRemoveTag']; 33296 f.call(this, value); 33297 } 33298 33299 // davidf: making default text visible again. 33300 if ($(this).val().length == 0) { 33301 var fake_input = $j("#" + $j(this).attr('id') + "_tag"); 33302 fake_input.blur(); 33303 } 33304 }); 33305 33306 return false; 33307 }; 33308 33309 $.fn.tagExist = function(val) { 33310 var id = $(this).attr('id'); 33311 var tagslist = $(this).val().split(delimiter[id]); 33312 return (jQuery.inArray(val, tagslist) >= 0); //true when tag exists, false when not 33313 }; 33314 33315 // clear all existing tags and import new ones from a string 33316 $.fn.importTags = function(str) { 33317 id = $(this).attr('id'); 33318 $('#'+id+'_tagsinput .tag').remove(); 33319 $.fn.tagsInput.importTags(this,str); 33320 } 33321 33322 $.fn.tagsInput = function(options) { 33323 var settings = jQuery.extend({ 33324 interactive:true, 33325 defaultText:'add a tag', 33326 minChars:0, 33327 width:'300px', 33328 height:'100px', 33329 autocomplete: {selectFirst: false }, 33330 'hide':true, 33331 'delimiter':',', 33332 'unique':true, 33333 removeWithBackspace:true, 33334 placeholderColor:'#666666', 33335 autosize: true, 33336 comfortZone: 20, 33337 inputPadding: 6*2 33338 },options); 33339 33340 this.each(function() { 33341 if (settings.hide) { 33342 $(this).hide(); 33343 } 33344 var id = $(this).attr('id'); 33345 if (!id || delimiter[$(this).attr('id')]) { 33346 id = $(this).attr('id', 'tags' + new Date().getTime()).attr('id'); 33347 } 33348 33349 var data = jQuery.extend({ 33350 pid:id, 33351 real_input: '#'+id, 33352 holder: '#'+id+'_tagsinput', 33353 input_wrapper: '#'+id+'_addTag', 33354 fake_input: '#'+id+'_tag' 33355 },settings); 33356 33357 delimiter[id] = data.delimiter; 33358 33359 if (settings.onAddTag || settings.onRemoveTag || settings.onChange) { 33360 tags_callbacks[id] = new Array(); 33361 tags_callbacks[id]['onAddTag'] = settings.onAddTag; 33362 tags_callbacks[id]['onRemoveTag'] = settings.onRemoveTag; 33363 tags_callbacks[id]['onChange'] = settings.onChange; 33364 } 33365 33366 var markup = '<div id="'+id+'_tagsinput" class="tagsinput"><div id="'+id+'_addTag">'; 33367 33368 if (settings.interactive) { 33369 markup = markup + '<input id="'+id+'_tag" value="" data-default="'+settings.defaultText+'" />'; 33370 } 33371 33372 markup = markup + '</div><div class="tags_clear"></div></div>'; 33373 33374 $(markup).insertAfter(this); 33375 33376 $(data.holder).css('width',settings.width); 33377 $(data.holder).css('min-height',settings.height); 33378 $(data.holder).css('height','100%'); 33379 33380 if ($(data.real_input).val()!='') { 33381 $.fn.tagsInput.importTags($(data.real_input),$(data.real_input).val()); 33382 } 33383 if (settings.interactive) { 33384 $(data.fake_input).val($(data.fake_input).attr('data-default')); 33385 $(data.fake_input).css('color',settings.placeholderColor); 33386 $(data.fake_input).resetAutosize(settings); 33387 33388 $(data.holder).bind('click',data,function(event) { 33389 $(event.data.fake_input).focus(); 33390 }); 33391 33392 $(data.fake_input).bind('focus',data,function(event) { 33393 if ($(event.data.fake_input).val()==$(event.data.fake_input).attr('data-default')) { 33394 $(event.data.fake_input).val(''); 33395 } 33396 $(event.data.fake_input).css('color','#000000'); 33397 }); 33398 33399 // davidf: reinstate the default text 33400 $(data.fake_input).bind('blur',data,function(event) { 33401 var last_tag = $(this).closest('.tagsinput').find('.tag:last').text(); 33402 if (last_tag == '') { 33403 $(event.data.fake_input).val($(event.data.fake_input).attr('data-default')); 33404 $(data.fake_input).css('color',settings.placeholderColor); 33405 } 33406 }); 33407 33408 if (settings.autocomplete_url != undefined) { 33409 autocomplete_options = {source: settings.autocomplete_url}; 33410 for (attrname in settings.autocomplete) { 33411 autocomplete_options[attrname] = settings.autocomplete[attrname]; 33412 } 33413 33414 if (jQuery.Autocompleter !== undefined) { 33415 $(data.fake_input).autocomplete(settings.autocomplete_url, settings.autocomplete); 33416 $(data.fake_input).bind('result',data,function(event,data,formatted) { 33417 if (data) { 33418 $('#'+id).addTag(data[0] + "",{focus:true,unique:(settings.unique)}); 33419 } 33420 }); 33421 } else if (jQuery.ui.autocomplete !== undefined) { 33422 $(data.fake_input).autocomplete(autocomplete_options); 33423 $(data.fake_input).bind('autocompleteselect',data,function(event,ui) { 33424 $(event.data.real_input).addTag(ui.item.value,{focus:true,unique:(settings.unique)}); 33425 return false;
33426 }); 33427 } 33428 33429 33430 } else { 33431 // if a user tabs out of the field, create a new tag 33432 // this is only available if autocomplete is not used. 33433 $(data.fake_input).bind('blur',data,function(event) { 33434 var d = $(this).attr('data-default'); 33435 if ($(event.data.fake_input).val()!='' && $(event.data.fake_input).val()!=d) { 33436 if( (event.data.minChars <= $(event.data.fake_input).val().length) && (!event.data.maxChars || (event.data.maxChars >= $(event.data.fake_input).val().length)) ) 33437 $(event.data.real_input).addTag($(event.data.fake_input).val(),{focus:true,unique:(settings.unique)}); 33438 } else { 33439 $(event.data.fake_input).val($(event.data.fake_input).attr('data-default')); 33440 $(event.data.fake_input).css('color',settings.placeholderColor); 33441 } 33442 return false; 33443 }); 33444 33445 } 33446 // if user types a comma, create a new tag 33447 $(data.fake_input).bind('keypress',data,function(event) { 33448 if (event.which==event.data.delimiter.charCodeAt(0) || event.which==13 ) { 33449 event.preventDefault(); 33450 if( (event.data.minChars <= $(event.data.fake_input).val().length) && (!event.data.maxChars || (event.data.maxChars >= $(event.data.fake_input).val().length)) ) 33451 $(event.data.real_input).addTag($(event.data.fake_input).val(),{focus:true,unique:(settings.unique)}); 33452 $(event.data.fake_input).resetAutosize(settings); 33453 return false; 33454 } else if (event.data.autosize) { 33455 $(event.data.fake_input).doAutosize(settings); 33456 33457 } 33458 }); 33459 //Delete last tag on backspace 33460 data.removeWithBackspace && $(data.fake_input).bind('keydown', function(event) 33461 { 33462 if(event.keyCode == 8 && $(this).val() == '') 33463 { 33464 event.preventDefault(); 33465 var last_tag = $(this).closest('.tagsinput').find('.tag:last').text(); 33466 var id = $(this).attr('id').replace(/_tag$/, ''); 33467 last_tag = last_tag.replace(/[\s]+x$/, ''); 33468 $('#' + id).removeTag(escape(last_tag)); 33469 $(this).trigger('focus'); 33470 } 33471 }); 33472 $(data.fake_input).blur(); 33473 33474 //Removes the not_valid class when user changes the value of the fake input 33475 if(data.unique) { 33476 $(data.fake_input).keydown(function(event){ 33477 if(event.keyCode == 8 || String.fromCharCode(event.which).match(/\w+|[áéÃóúÃÃÃÃÃñÃ,/]+/)) { 33478 $(this).removeClass('not_valid'); 33479 } 33480 }); 33481 } 33482 } // if settings.interactive 33483 }); 33484 33485 return this; 33486 33487 }; 33488 33489 $.fn.tagsInput.updateTagsField = function(obj,tagslist) { 33490 var id = $(obj).attr('id'); 33491 $(obj).val(tagslist.join(delimiter[id])); 33492 }; 33493 33494 $.fn.tagsInput.importTags = function(obj,val) { 33495 $(obj).val(''); 33496 var id = $(obj).attr('id'); 33497 var tags = val.split(delimiter[id]); 33498 for (i=0; i<tags.length; i++) { 33499 $(obj).addTag(tags[i],{focus:false,callback:false}); 33500 } 33501 if(tags_callbacks[id] && tags_callbacks[id]['onChange']) 33502 { 33503 var f = tags_callbacks[id]['onChange']; 33504 f.call(obj, obj, tags[i]); 33505 } 33506 }; 33507 33508 $.fn.tagsInput.destroy = function( ) { 33509 33510 delimiter = new Array(); 33511 tags_callbacks = new Array(); 33512 }; 33513 33514})(jQuery); 33515/* 33516 * jsTree 1.0-rc3 33517 * http://jstree.com/ 33518 * 33519 * Copyright (c) 2010 Ivan Bozhanov (vakata.com) 33520 * 33521 * Licensed same as jquery - under the terms of either the MIT License or the GPL Version 2 License 33522 * http://www.opensource.org/licenses/mit-license.php 33523 * http://www.gnu.org/licenses/gpl.html 33524 * 33525 * $Date: 2011-02-09 01:17:14 +0200 (ÑÑ, 09 ÑÐµÐ²Ñ 2011) $ 33526 * $Revision: 236 $ 33527 */ 33528 33529/*jslint browser: true, onevar: true, undef: true, bitwise: true, strict: true */ 33530/*global window : false, clearInterval: false, clearTimeout: false, document: false, setInterval: false, setTimeout: false, jQuery: false, navigator: false, XSLTProcessor: false, DOMParser: false, XMLSerializer: false*/ 33531 33532 33533"use strict"; 33534 33535// top wrapper to prevent multiple inclusion (is this OK?) 33536(function () { if(jQuery && jQuery.jstree) { return; } 33537 var is_ie6 = false, is_ie7 = false, is_ff2 = false; 33538 33539/* 33540 * jsTree core 33541 */ 33542(function ($) { 33543 // Common functions not related to jsTree 33544 // decided to move them to a `vakata` "namespace" 33545 $.vakata = {}; 33546 // CSS related functions 33547 $.vakata.css = { 33548 get_css : function(rule_name, delete_flag, sheet) { 33549 rule_name = rule_name.toLowerCase(); 33550 var css_rules = sheet.cssRules || sheet.rules, 33551 j = 0; 33552 do { 33553 if(css_rules.length && j > css_rules.length + 5) { return false; } 33554 if(css_rules[j].selectorText && css_rules[j].selectorText.toLowerCase() == rule_name) { 33555 if(delete_flag === true) { 33556 if(sheet.removeRule) { sheet.removeRule(j); } 33557 if(sheet.deleteRule) { sheet.deleteRule(j); } 33558 return true; 33559 } 33560 else { return css_rules[j]; } 33561 } 33562 } 33563 while (css_rules[++j]); 33564 return false; 33565 }, 33566 add_css : function(rule_name, sheet) { 33567 if($.jstree.css.get_css(rule_name, false, sheet)) { return false; } 33568 if(sheet.insertRule) { sheet.insertRule(rule_name + ' { }', 0); } else { sheet.addRule(rule_name, null, 0); } 33569 return $.vakata.css.get_css(rule_name); 33570 }, 33571 remove_css : function(rule_name, sheet) { 33572 return $.vakata.css.get_css(rule_name, true, sheet); 33573 }, 33574 add_sheet : function(opts) { 33575 var tmp = false, is_new = true; 33576 if(opts.str) { 33577 if(opts.title) { tmp = $("style[id='" + opts.title + "-stylesheet']")[0]; } 33578 if(tmp) { is_new = false; } 33579 else { 33580 tmp = document.createElement("style"); 33581 tmp.setAttribute('type',"text/css"); 33582 if(opts.title) { tmp.setAttribute("id", opts.title + "-stylesheet"); } 33583 } 33584 if(tmp.styleSheet) { 33585 if(is_new) { 33586 document.getElementsByTagName("head")[0].appendChild(tmp); 33587 tmp.styleSheet.cssText = opts.str; 33588 } 33589 else { 33590 tmp.styleSheet.cssText = tmp.styleSheet.cssText + " " + opts.str; 33591 } 33592 } 33593 else { 33594 tmp.appendChild(document.createTextNode(opts.str)); 33595 document.getElementsByTagName("head")[0].appendChild(tmp); 33596 } 33597 return tmp.sheet || tmp.styleSheet; 33598 } 33599 if(opts.url) { 33600 if(document.createStyleSheet) { 33601 try { tmp = document.createStyleSheet(opts.url); } catch (e) { } 33602 } 33603 else { 33604 tmp = document.createElement('link'); 33605 tmp.rel = 'stylesheet'; 33606 tmp.type = 'text/css'; 33607 tmp.media = "all"; 33608 tmp.href = opts.url; 33609 document.getElementsByTagName("head")[0].appendChild(tmp); 33610 return tmp.styleSheet; 33611 } 33612 } 33613 } 33614 }; 33615 33616 // private variables
33617 var instances = [], // instance array (used by $.jstree.reference/create/focused) 33618 focused_instance = -1, // the index in the instance array of the currently focused instance 33619 plugins = {}, // list of included plugins 33620 prepared_move = {}; // for the move_node function 33621 33622 // jQuery plugin wrapper (thanks to jquery UI widget function) 33623 $.fn.jstree = function (settings) { 33624 var isMethodCall = (typeof settings == 'string'), // is this a method call like $().jstree("open_node") 33625 args = Array.prototype.slice.call(arguments, 1), 33626 returnValue = this; 33627 33628 // if a method call execute the method on all selected instances 33629 if(isMethodCall) { 33630 if(settings.substring(0, 1) == '_') { return returnValue; } 33631 this.each(function() { 33632 var instance = instances[$.data(this, "jstree_instance_id")], 33633 methodValue = (instance && $.isFunction(instance[settings])) ? instance[settings].apply(instance, args) : instance; 33634 if(typeof methodValue !== "undefined" && (settings.indexOf("is_") === 0 || (methodValue !== true && methodValue !== false))) { returnValue = methodValue; return false; } 33635 }); 33636 } 33637 else { 33638 this.each(function() { 33639 // extend settings and allow for multiple hashes and $.data 33640 var instance_id = $.data(this, "jstree_instance_id"), 33641 a = [], 33642 b = settings ? $.extend({}, true, settings) : {}, 33643 c = $(this), 33644 s = false, 33645 t = []; 33646 a = a.concat(args); 33647 if(c.data("jstree")) { a.push(c.data("jstree")); } 33648 b = a.length ? $.extend.apply(null, [true, b].concat(a)) : b; 33649 33650 // if an instance already exists, destroy it first 33651 if(typeof instance_id !== "undefined" && instances[instance_id]) { instances[instance_id].destroy(); } 33652 // push a new empty object to the instances array 33653 instance_id = parseInt(instances.push({}),10) - 1; 33654 // store the jstree instance id to the container element 33655 $.data(this, "jstree_instance_id", instance_id); 33656 // clean up all plugins 33657 b.plugins = $.isArray(b.plugins) ? b.plugins : $.jstree.defaults.plugins.slice(); 33658 b.plugins.unshift("core"); 33659 // only unique plugins 33660 b.plugins = b.plugins.sort().join(",,").replace(/(,|^)([^,]+)(,,\2)+(,|$)/g,"$1$2$4").replace(/,,+/g,",").replace(/,$/,"").split(","); 33661 33662 // extend defaults with passed data 33663 s = $.extend(true, {}, $.jstree.defaults, b); 33664 s.plugins = b.plugins; 33665 $.each(plugins, function (i, val) { 33666 if($.inArray(i, s.plugins) === -1) { s[i] = null; delete s[i]; } 33667 else { t.push(i); } 33668 }); 33669 s.plugins = t; 33670 33671 // push the new object to the instances array (at the same time set the default classes to the container) and init 33672 instances[instance_id] = new $.jstree._instance(instance_id, $(this).addClass("jstree jstree-" + instance_id), s); 33673 // init all activated plugins for this instance 33674 $.each(instances[instance_id]._get_settings().plugins, function (i, val) { instances[instance_id].data[val] = {}; }); 33675 $.each(instances[instance_id]._get_settings().plugins, function (i, val) { if(plugins[val]) { plugins[val].__init.apply(instances[instance_id]); } }); 33676 // initialize the instance 33677 setTimeout(function() { if(instances[instance_id]) { instances[instance_id].init(); } }, 0); 33678 }); 33679 } 33680 // return the jquery selection (or if it was a method call that returned a value - the returned value) 33681 return returnValue; 33682 }; 33683 // object to store exposed functions and objects 33684 $.jstree = { 33685 defaults : { 33686 plugins : [] 33687 }, 33688 _focused : function () { return instances[focused_instance] || null; }, 33689 _reference : function (needle) { 33690 // get by instance id 33691 if(instances[needle]) { return instances[needle]; } 33692 // get by DOM (if still no luck - return null 33693 var o = $(needle); 33694 if(!o.length && typeof needle === "string") { o = $("#" + needle); } 33695 if(!o.length) { return null; } 33696 return instances[o.closest(".jstree").data("jstree_instance_id")] || null; 33697 }, 33698 _instance : function (index, container, settings) { 33699 // for plugins to store data in 33700 this.data = { core : {} }; 33701 this.get_settings = function () { return $.extend(true, {}, settings); }; 33702 this._get_settings = function () { return settings; }; 33703 this.get_index = function () { return index; }; 33704 this.get_container = function () { return container; }; 33705 this.get_container_ul = function () { return container.children("ul:eq(0)"); }; 33706 this._set_settings = function (s) { 33707 settings = $.extend(true, {}, settings, s); 33708 }; 33709 }, 33710 _fn : { }, 33711 plugin : function (pname, pdata) { 33712 pdata = $.extend({}, { 33713 __init : $.noop, 33714 __destroy : $.noop, 33715 _fn : {}, 33716 defaults : false 33717 }, pdata); 33718 plugins[pname] = pdata; 33719 33720 $.jstree.defaults[pname] = pdata.defaults; 33721 $.each(pdata._fn, function (i, val) { 33722 val.plugin = pname; 33723 val.old = $.jstree._fn[i];
33724 $.jstree._fn[i] = function () { 33725 var rslt, 33726 func = val, 33727 args = Array.prototype.slice.call(arguments), 33728 evnt = new $.Event("before.jstree"), 33729 rlbk = false; 33730 33731 if(this.data.core.locked === true && i !== "unlock" && i !== "is_locked") { return; } 33732 33733 // Check if function belongs to the included plugins of this instance 33734 do { 33735 if(func && func.plugin && $.inArray(func.plugin, this._get_settings().plugins) !== -1) { break; } 33736 func = func.old; 33737 } while(func); 33738 if(!func) { return; } 33739 33740 // context and function to trigger events, then finally call the function 33741 if(i.indexOf("_") === 0) { 33742 rslt = func.apply(this, args); 33743 } 33744 else { 33745 rslt = this.get_container().triggerHandler(evnt, { "func" : i, "inst" : this, "args" : args, "plugin" : func.plugin }); 33746 if(rslt === false) { return; } 33747 if(typeof rslt !== "undefined") { args = rslt; } 33748 33749 rslt = func.apply( 33750 $.extend({}, this, { 33751 __callback : function (data) { 33752 this.get_container().triggerHandler( i + '.jstree', { "inst" : this, "args" : args, "rslt" : data, "rlbk" : rlbk }); 33753 }, 33754 __rollback : function () { 33755 rlbk = this.get_rollback(); 33756 return rlbk; 33757 }, 33758 __call_old : function (replace_arguments) { 33759 return func.old.apply(this, (replace_arguments ? Array.prototype.slice.call(arguments, 1) : args ) ); 33760 } 33761 }), args); 33762 } 33763 33764 // return the result 33765 return rslt; 33766 }; 33767 $.jstree._fn[i].old = val.old; 33768 $.jstree._fn[i].plugin = pname; 33769 }); 33770 }, 33771 rollback : function (rb) { 33772 if(rb) { 33773 if(!$.isArray(rb)) { rb = [ rb ]; } 33774 $.each(rb, function (i, val) { 33775 instances[val.i].set_rollback(val.h, val.d); 33776 }); 33777 } 33778 } 33779 }; 33780 // set the prototype for all instances 33781 $.jstree._fn = $.jstree._instance.prototype = {}; 33782 33783 // load the css when DOM is ready 33784 $(function() { 33785 // code is copied from jQuery ($.browser is deprecated + there is a bug in IE) 33786 var u = navigator.userAgent.toLowerCase(), 33787 v = (u.match( /.+?(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,'0'])[1], 33788 css_string = '' + 33789 '.jstree ul, .jstree li { display:block; margin:0 0 0 0; padding:0 0 0 0; list-style-type:none; } ' + 33790 '.jstree li { display:block; min-height:18px; line-height:18px; white-space:nowrap; margin-left:18px; min-width:18px; } ' + 33791 '.jstree-rtl li { margin-left:0; margin-right:18px; } ' + 33792 '.jstree > ul > li { margin-left:0px; } ' + 33793 '.jstree-rtl > ul > li { margin-right:0px; } ' + 33794 '.jstree ins { display:inline-block; text-decoration:none; width:18px; height:18px; margin:0 0 0 0; padding:0; } ' + 33795 '.jstree a { display:inline-block; line-height:16px; height:16px; color:black; white-space:nowrap; text-decoration:none; padding:1px 2px; margin:0; } ' + 33796 '.jstree a:focus { outline: none; } ' + 33797 '.jstree a > ins { height:16px; width:16px; } ' + 33798 '.jstree a > .jstree-icon { margin-right:3px; } ' + 33799 '.jstree-rtl a > .jstree-icon { margin-left:3px; margin-right:0; } ' + 33800 'li.jstree-open > ul { display:block; } ' + 33801 'li.jstree-closed > ul { display:none; } '; 33802 // Correct IE 6 (does not support the > CSS selector) 33803 if(/msie/.test(u) && parseInt(v, 10) == 6) { 33804 is_ie6 = true; 33805 33806 // fix image flicker and lack of caching 33807 try { 33808 document.execCommand("BackgroundImageCache", false, true); 33809 } catch (err) { } 33810 33811 css_string += '' + 33812 '.jstree li { height:18px; margin-left:0; margin-right:0; } ' + 33813 '.jstree li li { margin-left:18px; } ' + 33814 '.jstree-rtl li li { margin-left:0px; margin-right:18px; } ' + 33815 'li.jstree-open ul { display:block; } ' + 33816 'li.jstree-closed ul { display:none !important; } ' + 33817 '.jstree li a { display:inline; border-width:0 !important; padding:0px 2px !important; } ' + 33818 '.jstree li a ins { height:16px; width:16px; margin-right:3px; } ' + 33819 '.jstree-rtl li a ins { margin-right:0px; margin-left:3px; } '; 33820 } 33821 // Correct IE 7 (shifts anchor nodes onhover) 33822 if(/msie/.test(u) && parseInt(v, 10) == 7) { 33823 is_ie7 = true; 33824 css_string += '.jstree li a { border-width:0 !important; padding:0px 2px !important; } '; 33825 } 33826 // correct ff2 lack of display:inline-block 33827 if(!/compatible/.test(u) && /mozilla/.test(u) && parseFloat(v, 10) < 1.9) { 33828 is_ff2 = true; 33829 css_string += '' + 33830 '.jstree ins { display:-moz-inline-box; } ' + 33831 '.jstree li { line-height:12px; } ' + // WHY?? 33832 '.jstree a { display:-moz-inline-box; } ' + 33833 '.jstree .jstree-no-icons .jstree-checkbox { display:-moz-inline-stack !important; } '; 33834 /* this shouldn't be here as it is theme specific */ 33835 } 33836 // the default stylesheet 33837 $.vakata.css.add_sheet({ str : css_string, title : "jstree" }); 33838 }); 33839 33840 // core functions (open, close, create, update, delete) 33841 $.jstree.plugin("core", { 33842 __init : function () { 33843 this.data.core.locked = false; 33844 this.data.core.to_open = this.get_settings().core.initially_open; 33845 this.data.core.to_load = this.get_settings().core.initially_load; 33846 }, 33847 defaults : { 33848 html_titles : false, 33849 animation : 500, 33850 initially_open : [], 33851 initially_load : [], 33852 open_parents : true, 33853 notify_plugins : true, 33854 rtl : false, 33855 load_open : false, 33856 strings : { 33857 loading : "Loading ...", 33858 new_node : "New node", 33859 multiple_selection : "Multiple selection" 33860 } 33861 }, 33862 _fn : { 33863 init : function () { 33864 this.set_focus(); 33865 if(this._get_settings().core.rtl) { 33866 this.get_container().addClass("jstree-rtl").css("direction", "rtl"); 33867 } 33868 this.get_container().html("<ul><li class='jstree-last jstree-leaf'><ins> </ins><a class='jstree-loading' href='#'><ins class='jstree-icon'> </ins>" + this._get_string("loading") + "</a></li></ul>"); 33869 this.data.core.li_height = this.get_container_ul().find("li.jstree-closed, li.jstree-leaf").eq(0).height() || 18; 33870 33871 this.get_container() 33872 .delegate("li > ins", "click.jstree", $.proxy(function (event) { 33873 var trgt = $(event.target); 33874 // if(trgt.is("ins") && event.pageY - trgt.offset().top < this.data.core.li_height) { this.toggle_node(trgt); } 33875 this.toggle_node(trgt); 33876 }, this)) 33877 .bind("mousedown.jstree", $.proxy(function () { 33878 this.set_focus(); // This used to be setTimeout(set_focus,0) - why? 33879 }, this)) 33880 .bind("dblclick.jstree", function (event) { 33881 var sel; 33882 if(document.selection && document.selection.empty) { document.selection.empty(); } 33883 else { 33884 if(window.getSelection) { 33885 sel = window.getSelection(); 33886 try { 33887 sel.removeAllRanges(); 33888 sel.collapse(); 33889 } catch (err) { } 33890 } 33891 } 33892 });
33893 if(this._get_settings().core.notify_plugins) { 33894 this.get_container() 33895 .bind("load_node.jstree", $.proxy(function (e, data) { 33896 var o = this._get_node(data.rslt.obj), 33897 t = this; 33898 if(o === -1) { o = this.get_container_ul(); } 33899 if(!o.length) { return; } 33900 o.find("li").each(function () { 33901 var th = $(this); 33902 if(th.data("jstree")) { 33903 $.each(th.data("jstree"), function (plugin, values) { 33904 if(t.data[plugin] && $.isFunction(t["_" + plugin + "_notify"])) { 33905 t["_" + plugin + "_notify"].call(t, th, values); 33906 } 33907 }); 33908 } 33909 }); 33910 }, this)); 33911 } 33912 if(this._get_settings().core.load_open) { 33913 this.get_container() 33914 .bind("load_node.jstree", $.proxy(function (e, data) { 33915 var o = this._get_node(data.rslt.obj), 33916 t = this; 33917 if(o === -1) { o = this.get_container_ul(); } 33918 if(!o.length) { return; } 33919 o.find("li.jstree-open:not(:has(ul))").each(function () { 33920 t.load_node(this, $.noop, $.noop); 33921 });
33922 }, this)); 33923 } 33924 this.__callback(); 33925 this.load_node(-1, function () { this.loaded(); this.reload_nodes(); }); 33926 }, 33927 destroy : function () { 33928 var i, 33929 n = this.get_index(), 33930 s = this._get_settings(), 33931 _this = this; 33932 33933 $.each(s.plugins, function (i, val) { 33934 try { plugins[val].__destroy.apply(_this); } catch(err) { } 33935 }); 33936 this.__callback(); 33937 // set focus to another instance if this one is focused 33938 if(this.is_focused()) { 33939 for(i in instances) { 33940 if(instances.hasOwnProperty(i) && i != n) { 33941 instances[i].set_focus(); 33942 break; 33943 } 33944 } 33945 } 33946 // if no other instance found 33947 if(n === focused_instance) { focused_instance = -1; } 33948 // remove all traces of jstree in the DOM (only the ones set using jstree*) and cleans all events 33949 this.get_container() 33950 .unbind(".jstree") 33951 .undelegate(".jstree") 33952 .removeData("jstree_instance_id") 33953 .find("[class^='jstree']") 33954 .andSelf() 33955 .attr("class", function () { return this.className.replace(/jstree[^ ]*|$/ig,''); }); 33956 $(document) 33957 .unbind(".jstree-" + n) 33958 .undelegate(".jstree-" + n); 33959 // remove the actual data 33960 instances[n] = null; 33961 delete instances[n]; 33962 }, 33963 33964 _core_notify : function (n, data) { 33965 if(data.opened) { 33966 this.open_node(n, false, true); 33967 } 33968 }, 33969 33970 lock : function () { 33971 this.data.core.locked = true; 33972 this.get_container().children("ul").addClass("jstree-locked").css("opacity","0.7"); 33973 this.__callback({}); 33974 }, 33975 unlock : function () { 33976 this.data.core.locked = false; 33977 this.get_container().children("ul").removeClass("jstree-locked").css("opacity","1"); 33978 this.__callback({}); 33979 }, 33980 is_locked : function () { return this.data.core.locked; }, 33981 save_opened : function () { 33982 var _this = this; 33983 this.data.core.to_open = []; 33984 this.get_container_ul().find("li.jstree-open").each(function () { 33985 if(this.id) { _this.data.core.to_open.push("#" + this.id.toString().replace(/^#/,"").replace(/\\\//g,"/").replace(/\//g,"\\\/").replace(/\\\./g,".").replace(/\./g,"\\.").replace(/\:/g,"\\:")); } 33986 }); 33987 this.__callback(_this.data.core.to_open); 33988 }, 33989 save_loaded : function () { }, 33990 reload_nodes : function (is_callback) { 33991 var _this = this, 33992 done = true, 33993 current = [], 33994 remaining = []; 33995 if(!is_callback) { 33996 this.data.core.reopen = false; 33997 this.data.core.refreshing = true; 33998 this.data.core.to_open = $.map($.makeArray(this.data.core.to_open), function (n) { return "#" + n.toString().replace(/^#/,"").replace(/\\\//g,"/").replace(/\//g,"\\\/").replace(/\\\./g,".").replace(/\./g,"\\.").replace(/\:/g,"\\:"); }); 33999 this.data.core.to_load = $.map($.makeArray(this.data.core.to_load), function (n) { return "#" + n.toString().replace(/^#/,"").replace(/\\\//g,"/").replace(/\//g,"\\\/").replace(/\\\./g,".").replace(/\./g,"\\.").replace(/\:/g,"\\:"); }); 34000 if(this.data.core.to_open.length) { 34001 this.data.core.to_load = this.data.core.to_load.concat(this.data.core.to_open); 34002 } 34003 } 34004 if(this.data.core.to_load.length) { 34005 $.each(this.data.core.to_load, function (i, val) { 34006 if(val == "#") { return true; } 34007 if($(val).length) { current.push(val); } 34008 else { remaining.push(val); } 34009 }); 34010 if(current.length) { 34011 this.data.core.to_load = remaining; 34012 $.each(current, function (i, val) { 34013 if(!_this._is_loaded(val)) { 34014 _this.load_node(val, function () { _this.reload_nodes(true); }, function () { _this.reload_nodes(true); }); 34015 done = false; 34016 } 34017 }); 34018 } 34019 } 34020 if(this.data.core.to_open.length) { 34021 $.each(this.data.core.to_open, function (i, val) { 34022 _this.open_node(val, false, true); 34023 }); 34024 } 34025 if(done) {
34026 // TODO: find a more elegant approach to syncronizing returning requests 34027 if(this.data.core.reopen) { clearTimeout(this.data.core.reopen); } 34028 this.data.core.reopen = setTimeout(function () { _this.__callback({}, _this); }, 50); 34029 this.data.core.refreshing = false; 34030 this.reopen(); 34031 } 34032 }, 34033 reopen : function () { 34034 var _this = this; 34035 if(this.data.core.to_open.length) { 34036 $.each(this.data.core.to_open, function (i, val) { 34037 _this.open_node(val, false, true); 34038 }); 34039 } 34040 this.__callback({}); 34041 }, 34042 refresh : function (obj) { 34043 var _this = this; 34044 this.save_opened(); 34045 if(!obj) { obj = -1; } 34046 obj = this._get_node(obj); 34047 if(!obj) { obj = -1; } 34048 if(obj !== -1) { obj.children("UL").remove(); } 34049 else { this.get_container_ul().empty(); } 34050 this.load_node(obj, function () { _this.__callback({ "obj" : obj}); _this.reload_nodes(); }); 34051 }, 34052 // Dummy function to fire after the first load (so that there is a jstree.loaded event) 34053 loaded : function () { 34054 this.__callback(); 34055 }, 34056 // deal with focus 34057 set_focus : function () { 34058 if(this.is_focused()) { return; } 34059 var f = $.jstree._focused(); 34060 if(f) { f.unset_focus(); } 34061 34062 this.get_container().addClass("jstree-focused"); 34063 focused_instance = this.get_index(); 34064 this.__callback(); 34065 }, 34066 is_focused : function () { 34067 return focused_instance == this.get_index(); 34068 }, 34069 unset_focus : function () { 34070 if(this.is_focused()) { 34071 this.get_container().removeClass("jstree-focused"); 34072 focused_instance = -1; 34073 } 34074 this.__callback(); 34075 }, 34076 34077 // traverse 34078 _get_node : function (obj) { 34079 var $obj = $(obj, this.get_container()); 34080 if($obj.is(".jstree") || obj == -1) { return -1; } 34081 $obj = $obj.closest("li", this.get_container()); 34082 return $obj.length ? $obj : false; 34083 }, 34084 _get_next : function (obj, strict) { 34085 obj = this._get_node(obj); 34086 if(obj === -1) { return this.get_container().find("> ul > li:first-child"); } 34087 if(!obj.length) { return false; } 34088 if(strict) { return (obj.nextAll("li").size() > 0) ? obj.nextAll("li:eq(0)") : false; } 34089 34090 if(obj.hasClass("jstree-open")) { return obj.find("li:eq(0)"); } 34091 else if(obj.nextAll("li").size() > 0) { return obj.nextAll("li:eq(0)"); } 34092 else { return obj.parentsUntil(".jstree","li").next("li").eq(0); } 34093 }, 34094 _get_prev : function (obj, strict) { 34095 obj = this._get_node(obj); 34096 if(obj === -1) { return this.get_container().find("> ul > li:last-child"); } 34097 if(!obj.length) { return false; } 34098 if(strict) { return (obj.prevAll("li").length > 0) ? obj.prevAll("li:eq(0)") : false; } 34099 34100 if(obj.prev("li").length) { 34101 obj = obj.prev("li").eq(0); 34102 while(obj.hasClass("jstree-open")) { obj = obj.children("ul:eq(0)").children("li:last"); } 34103 return obj; 34104 } 34105 else { var o = obj.parentsUntil(".jstree","li:eq(0)"); return o.length ? o : false; } 34106 }, 34107 _get_parent : function (obj) { 34108 obj = this._get_node(obj); 34109 if(obj == -1 || !obj.length) { return false; } 34110 var o = obj.parentsUntil(".jstree", "li:eq(0)"); 34111 return o.length ? o : -1; 34112 }, 34113 _get_children : function (obj) { 34114 obj = this._get_node(obj); 34115 if(obj === -1) { return this.get_container().children("ul:eq(0)").children("li"); } 34116 if(!obj.length) { return false; } 34117 return obj.children("ul:eq(0)").children("li"); 34118 }, 34119 get_path : function (obj, id_mode) { 34120 var p = [], 34121 _this = this; 34122 obj = this._get_node(obj); 34123 if(obj === -1 || !obj || !obj.length) { return false; } 34124 obj.parentsUntil(".jstree", "li").each(function () { 34125 p.push( id_mode ? this.id : _this.get_text(this) ); 34126 }); 34127 p.reverse(); 34128 p.push( id_mode ? obj.attr("id") : this.get_text(obj) ); 34129 return p; 34130 }, 34131 34132 // string functions 34133 _get_string : function (key) { 34134 return this._get_settings().core.strings[key] || key; 34135 }, 34136 34137 is_open : function (obj) { obj = this._get_node(obj); return obj && obj !== -1 && obj.hasClass("jstree-open"); }, 34138 is_closed : function (obj) { obj = this._get_node(obj); return obj && obj !== -1 && obj.hasClass("jstree-closed"); }, 34139 is_leaf : function (obj) { obj = this._get_node(obj); return obj && obj !== -1 && obj.hasClass("jstree-leaf"); }, 34140 correct_state : function (obj) { 34141 obj = this._get_node(obj); 34142 if(!obj || obj === -1) { return false; } 34143 obj.removeClass("jstree-closed jstree-open").addClass("jstree-leaf").children("ul").remove(); 34144 this.__callback({ "obj" : obj }); 34145 }, 34146 // open/close 34147 open_node : function (obj, callback, skip_animation) { 34148 34149 obj = this._get_node(obj); 34150 if(!obj.length) { return false; } 34151 if(!obj.hasClass("jstree-closed")) { if(callback) { callback.call(); } return false; } 34152 var s = skip_animation || is_ie6 ? 0 : this._get_settings().core.animation, 34153 t = this; 34154 if(!this._is_loaded(obj)) { 34155 obj.children("a").addClass("jstree-loading"); 34156 this.load_node(obj, function () { t.open_node(obj, callback, skip_animation); }, callback); 34157 } 34158 else { 34159 if(this._get_settings().core.open_parents) { 34160 obj.parentsUntil(".jstree",".jstree-closed").each(function () { 34161 t.open_node(this, false, true); 34162 });
34163 } 34164 if(s) { obj.children("ul").css("display","none"); } 34165 obj.removeClass("jstree-closed").addClass("jstree-open").children("a").removeClass("jstree-loading"); 34166 if(s) { obj.children("ul").stop(true, true).slideDown(s, function () { this.style.display = ""; t.after_open(obj); }); } 34167 else { t.after_open(obj); } 34168 this.__callback({ "obj" : obj }); 34169 if(callback) { callback.call(); } 34170 } 34171 }, 34172 after_open : function (obj) { this.__callback({ "obj" : obj }); }, 34173 close_node : function (obj, skip_animation) { 34174 obj = this._get_node(obj); 34175 var s = skip_animation || is_ie6 ? 0 : this._get_settings().core.animation, 34176 t = this; 34177 if(!obj.length || !obj.hasClass("jstree-open")) { return false; } 34178 if(s) { obj.children("ul").attr("style","display:block !important"); } 34179 obj.removeClass("jstree-open").addClass("jstree-closed"); 34180 if(s) { obj.children("ul").stop(true, true).slideUp(s, function () { this.style.display = ""; t.after_close(obj); }); } 34181 else { t.after_close(obj); } 34182 this.__callback({ "obj" : obj }); 34183 }, 34184 after_close : function (obj) { this.__callback({ "obj" : obj }); }, 34185 toggle_node : function (obj) { 34186 obj = this._get_node(obj); 34187 if(obj.hasClass("jstree-closed")) { return this.open_node(obj); } 34188 if(obj.hasClass("jstree-open")) { return this.close_node(obj); } 34189 }, 34190 open_all : function (obj, do_animation, original_obj) { 34191 obj = obj ? this._get_node(obj) : -1; 34192 if(!obj || obj === -1) { obj = this.get_container_ul(); } 34193 if(original_obj) { 34194 obj = obj.find("li.jstree-closed"); 34195 } 34196 else { 34197 original_obj = obj; 34198 if(obj.is(".jstree-closed")) { obj = obj.find("li.jstree-closed").andSelf(); } 34199 else { obj = obj.find("li.jstree-closed"); } 34200 } 34201 var _this = this; 34202 obj.each(function () { 34203 var __this = this; 34204 if(!_this._is_loaded(this)) { _this.open_node(this, function() { _this.open_all(__this, do_animation, original_obj); }, !do_animation); } 34205 else { _this.open_node(this, false, !do_animation); } 34206 }); 34207 // so that callback is fired AFTER all nodes are open 34208 if(original_obj.find('li.jstree-closed').length === 0) { this.__callback({ "obj" : original_obj }); } 34209 }, 34210 close_all : function (obj, do_animation) { 34211 var _this = this; 34212 obj = obj ? this._get_node(obj) : this.get_container(); 34213 if(!obj || obj === -1) { obj = this.get_container_ul(); } 34214 obj.find("li.jstree-open").andSelf().each(function () { _this.close_node(this, !do_animation); }); 34215 this.__callback({ "obj" : obj }); 34216 }, 34217 clean_node : function (obj) { 34218 obj = obj && obj != -1 ? $(obj) : this.get_container_ul(); 34219 obj = obj.is("li") ? obj.find("li").andSelf() : obj.find("li"); 34220 obj.removeClass("jstree-last") 34221 .filter("li:last-child").addClass("jstree-last").end() 34222 .filter(":has(li)") 34223 .not(".jstree-open").removeClass("jstree-leaf").addClass("jstree-closed"); 34224 obj.not(".jstree-open, .jstree-closed").addClass("jstree-leaf").children("ul").remove(); 34225 this.__callback({ "obj" : obj }); 34226 }, 34227 // rollback 34228 get_rollback : function () { 34229 this.__callback(); 34230 return { i : this.get_index(), h : this.get_container().children("ul").clone(true), d : this.data }; 34231 }, 34232 set_rollback : function (html, data) { 34233 this.get_container().empty().append(html); 34234 this.data = data; 34235 this.__callback(); 34236 }, 34237 // Dummy functions to be overwritten by any datastore plugin included 34238 load_node : function (obj, s_call, e_call) { this.__callback({ "obj" : obj }); }, 34239 _is_loaded : function (obj) { return true; }, 34240 34241 // Basic operations: create 34242 create_node : function (obj, position, js, callback, is_loaded) { 34243 obj = this._get_node(obj); 34244 position = typeof position === "undefined" ? "last" : position; 34245 var d = $("<li />"), 34246 s = this._get_settings().core, 34247 tmp; 34248 34249 if(obj !== -1 && !obj.length) { return false; } 34250 if(!is_loaded && !this._is_loaded(obj)) { this.load_node(obj, function () { this.create_node(obj, position, js, callback, true); }); return false; } 34251 34252 this.__rollback(); 34253 34254 if(typeof js === "string") { js = { "data" : js }; } 34255 if(!js) { js = {}; } 34256 if(js.attr) { d.attr(js.attr); } 34257 if(js.metadata) { d.data(js.metadata); } 34258 if(js.state) { d.addClass("jstree-" + js.state); } 34259 if(!js.data) { js.data = this._get_string("new_node"); } 34260 if(!$.isArray(js.data)) { tmp = js.data; js.data = []; js.data.push(tmp); } 34261 $.each(js.data, function (i, m) { 34262 tmp = $("<a />"); 34263 if($.isFunction(m)) { m = m.call(this, js); } 34264 if(typeof m == "string") { tmp.attr('href','#')[ s.html_titles ? "html" : "text" ](m); } 34265 else { 34266 if(!m.attr) { m.attr = {}; } 34267 if(!m.attr.href) { m.attr.href = '#'; } 34268 tmp.attr(m.attr)[ s.html_titles ? "html" : "text" ](m.title); 34269 if(m.language) { tmp.addClass(m.language); } 34270 } 34271 tmp.prepend("<ins class='jstree-icon'> </ins>"); 34272 if(!m.icon && js.icon) { m.icon = js.icon; } 34273 if(m.icon) { 34274 if(m.icon.indexOf("/") === -1) { tmp.children("ins").addClass(m.icon); }
34275 else { tmp.children("ins").css("background","url('" + m.icon + "') center center no-repeat"); } 34276 } 34277 d.append(tmp); 34278 }); 34279 d.prepend("<ins class='jstree-icon'> </ins>"); 34280 if(obj === -1) { 34281 obj = this.get_container(); 34282 if(position === "before") { position = "first"; } 34283 if(position === "after") { position = "last"; } 34284 } 34285 switch(position) { 34286 case "before": obj.before(d); tmp = this._get_parent(obj); break; 34287 case "after" : obj.after(d); tmp = this._get_parent(obj); break; 34288 case "inside": 34289 case "first" : 34290 if(!obj.children("ul").length) { obj.append("<ul />"); } 34291 obj.children("ul").prepend(d); 34292 tmp = obj; 34293 break; 34294 case "last": 34295 if(!obj.children("ul").length) { obj.append("<ul />"); } 34296 obj.children("ul").append(d); 34297 tmp = obj; 34298 break; 34299 default: 34300 if(!obj.children("ul").length) { obj.append("<ul />"); } 34301 if(!position) { position = 0; } 34302 tmp = obj.children("ul").children("li").eq(position); 34303 if(tmp.length) { tmp.before(d); } 34304 else { obj.children("ul").append(d); } 34305 tmp = obj; 34306 break; 34307 } 34308 if(tmp === -1 || tmp.get(0) === this.get_container().get(0)) { tmp = -1; } 34309 this.clean_node(tmp); 34310 this.__callback({ "obj" : d, "parent" : tmp }); 34311 if(callback) { callback.call(this, d); } 34312 return d; 34313 }, 34314 // Basic operations: rename (deal with text) 34315 get_text : function (obj) { 34316 obj = this._get_node(obj); 34317 if(!obj.length) { return false; } 34318 var s = this._get_settings().core.html_titles;
34319 obj = obj.children("a:eq(0)"); 34320 if(s) { 34321 obj = obj.clone(); 34322 obj.children("INS").remove(); 34323 return obj.html(); 34324 } 34325 else { 34326 obj = obj.contents().filter(function() { return this.nodeType == 3; })[0]; 34327 return obj.nodeValue; 34328 } 34329 }, 34330 set_text : function (obj, val) { 34331 obj = this._get_node(obj); 34332 if(!obj.length) { return false; } 34333 obj = obj.children("a:eq(0)"); 34334 if(this._get_settings().core.html_titles) { 34335 var tmp = obj.children("INS").clone(); 34336 obj.html(val).prepend(tmp); 34337 this.__callback({ "obj" : obj, "name" : val }); 34338 return true; 34339 } 34340 else { 34341 obj = obj.contents().filter(function() { return this.nodeType == 3; })[0]; 34342 this.__callback({ "obj" : obj, "name" : val }); 34343 return (obj.nodeValue = val); 34344 } 34345 }, 34346 rename_node : function (obj, val) { 34347 obj = this._get_node(obj); 34348 this.__rollback(); 34349 if(obj && obj.length && this.set_text.apply(this, Array.prototype.slice.call(arguments))) { this.__callback({ "obj" : obj, "name" : val }); } 34350 }, 34351 // Basic operations: deleting nodes 34352 delete_node : function (obj) { 34353 obj = this._get_node(obj); 34354 if(!obj.length) { return false; } 34355 this.__rollback(); 34356 var p = this._get_parent(obj), prev = $([]), t = this; 34357 obj.each(function () { 34358 prev = prev.add(t._get_prev(this)); 34359 }); 34360 obj = obj.detach(); 34361 if(p !== -1 && p.find("> ul > li").length === 0) { 34362 p.removeClass("jstree-open jstree-closed").addClass("jstree-leaf"); 34363 } 34364 this.clean_node(p); 34365 this.__callback({ "obj" : obj, "prev" : prev, "parent" : p }); 34366 return obj; 34367 }, 34368 prepare_move : function (o, r, pos, cb, is_cb) { 34369 var p = {}; 34370 34371 p.ot = $.jstree._reference(o) || this; 34372 p.o = p.ot._get_node(o); 34373 p.r = r === - 1 ? -1 : this._get_node(r); 34374 p.p = (typeof pos === "undefined" || pos === false) ? "last" : pos; // TODO: move to a setting 34375 if(!is_cb && prepared_move.o && prepared_move.o[0] === p.o[0] && prepared_move.r[0] === p.r[0] && prepared_move.p === p.p) { 34376 this.__callback(prepared_move); 34377 if(cb) { cb.call(this, prepared_move); } 34378 return; 34379 } 34380 p.ot = $.jstree._reference(p.o) || this; 34381 p.rt = $.jstree._reference(p.r) || this; // r === -1 ? p.ot : $.jstree._reference(p.r) || this 34382 if(p.r === -1 || !p.r) { 34383 p.cr = -1; 34384 switch(p.p) { 34385 case "first": 34386 case "before": 34387 case "inside": 34388 p.cp = 0; 34389 break; 34390 case "after": 34391 case "last": 34392 p.cp = p.rt.get_container().find(" > ul > li").length; 34393 break; 34394 default: 34395 p.cp = p.p; 34396 break; 34397 } 34398 } 34399 else { 34400 if(!/^(before|after)$/.test(p.p) && !this._is_loaded(p.r)) { 34401 return this.load_node(p.r, function () { this.prepare_move(o, r, pos, cb, true); }); 34402 } 34403 switch(p.p) { 34404 case "before": 34405 p.cp = p.r.index(); 34406 p.cr = p.rt._get_parent(p.r); 34407 break; 34408 case "after": 34409 p.cp = p.r.index() + 1; 34410 p.cr = p.rt._get_parent(p.r); 34411 break; 34412 case "inside": 34413 case "first": 34414 p.cp = 0; 34415 p.cr = p.r; 34416 break; 34417 case "last": 34418 p.cp = p.r.find(" > ul > li").length; 34419 p.cr = p.r; 34420 break; 34421 default: 34422 p.cp = p.p; 34423 p.cr = p.r; 34424 break; 34425 } 34426 } 34427 p.np = p.cr == -1 ? p.rt.get_container() : p.cr; 34428 p.op = p.ot._get_parent(p.o); 34429 p.cop = p.o.index(); 34430 if(p.op === -1) { p.op = p.ot ? p.ot.get_container() : this.get_container(); } 34431 if(!/^(before|after)$/.test(p.p) && p.op && p.np && p.op[0] === p.np[0] && p.o.index() < p.cp) { p.cp++; } 34432 //if(p.p === "before" && p.op && p.np && p.op[0] === p.np[0] && p.o.index() < p.cp) { p.cp--; } 34433 p.or = p.np.find(" > ul > li:nth-child(" + (p.cp + 1) + ")"); 34434 prepared_move = p; 34435 this.__callback(prepared_move); 34436 if(cb) { cb.call(this, prepared_move); } 34437 }, 34438 check_move : function () { 34439 var obj = prepared_move, ret = true, r = obj.r === -1 ? this.get_container() : obj.r; 34440 if(!obj || !obj.o || obj.or[0] === obj.o[0]) { return false; } 34441 if(obj.op && obj.np && obj.op[0] === obj.np[0] && obj.cp - 1 === obj.o.index()) { return false; } 34442 obj.o.each(function () { 34443 if(r.parentsUntil(".jstree", "li").andSelf().index(this) !== -1) { ret = false; return false; } 34444 }); 34445 return ret; 34446 }, 34447 move_node : function (obj, ref, position, is_copy, is_prepared, skip_check) { 34448 if(!is_prepared) { 34449 return this.prepare_move(obj, ref, position, function (p) { 34450 this.move_node(p, false, false, is_copy, true, skip_check); 34451 });
34452 } 34453 if(is_copy) { 34454 prepared_move.cy = true; 34455 } 34456 if(!skip_check && !this.check_move()) { return false; } 34457 34458 this.__rollback(); 34459 var o = false; 34460 if(is_copy) { 34461 o = obj.o.clone(true); 34462 o.find("*[id]").andSelf().each(function () { 34463 if(this.id) { this.id = "copy_" + this.id; } 34464 }); 34465 } 34466 else { o = obj.o; } 34467 34468 if(obj.or.length) { obj.or.before(o); } 34469 else { 34470 if(!obj.np.children("ul").length) { $("<ul />").appendTo(obj.np); } 34471 obj.np.children("ul:eq(0)").append(o); 34472 } 34473 34474 try { 34475 obj.ot.clean_node(obj.op); 34476 obj.rt.clean_node(obj.np); 34477 if(!obj.op.find("> ul > li").length) { 34478 obj.op.removeClass("jstree-open jstree-closed").addClass("jstree-leaf").children("ul").remove(); 34479 } 34480 } catch (e) { } 34481 34482 if(is_copy) { 34483 prepared_move.cy = true; 34484 prepared_move.oc = o; 34485 } 34486 this.__callback(prepared_move); 34487 return prepared_move; 34488 }, 34489 _get_move : function () { return prepared_move; } 34490 } 34491 }); 34492})(jQuery); 34493//*/ 34494 34495/* 34496 * jsTree ui plugin 34497 * This plugins handles selecting/deselecting/hovering/dehovering nodes 34498 */ 34499(function ($) { 34500 var scrollbar_width, e1, e2; 34501 $(function() { 34502 if (/msie/.test(navigator.userAgent.toLowerCase())) { 34503 e1 = $('<textarea cols="10" rows="2"></textarea>').css({ position: 'absolute', top: -1000, left: 0 }).appendTo('body'); 34504 e2 = $('<textarea cols="10" rows="2" style="overflow: hidden;"></textarea>').css({ position: 'absolute', top: -1000, left: 0 }).appendTo('body'); 34505 scrollbar_width = e1.width() - e2.width(); 34506 e1.add(e2).remove(); 34507 } 34508 else { 34509 e1 = $('<div />').css({ width: 100, height: 100, overflow: 'auto', position: 'absolute', top: -1000, left: 0 }) 34510 .prependTo('body').append('<div />').find('div').css({ width: '100%', height: 200 }); 34511 scrollbar_width = 100 - e1.width(); 34512 e1.parent().remove(); 34513 } 34514 }); 34515 $.jstree.plugin("ui", { 34516 __init : function () { 34517 this.data.ui.selected = $(); 34518 this.data.ui.last_selected = false; 34519 this.data.ui.hovered = null; 34520 this.data.ui.to_select = this.get_settings().ui.initially_select; 34521 34522 this.get_container() 34523 .delegate("a", "click.jstree", $.proxy(function (event) { 34524 event.preventDefault(); 34525 event.currentTarget.blur(); 34526 if(!$(event.currentTarget).hasClass("jstree-loading")) { 34527 this.select_node(event.currentTarget, true, event); 34528 } 34529 }, this)) 34530 .delegate("a", "mouseenter.jstree", $.proxy(function (event) { 34531 if(!$(event.currentTarget).hasClass("jstree-loading")) { 34532 this.hover_node(event.target); 34533 } 34534 }, this)) 34535 .delegate("a", "mouseleave.jstree", $.proxy(function (event) { 34536 if(!$(event.currentTarget).hasClass("jstree-loading")) { 34537 this.dehover_node(event.target); 34538 } 34539 }, this)) 34540 .bind("reopen.jstree", $.proxy(function () { 34541 this.reselect(); 34542 }, this)) 34543 .bind("get_rollback.jstree", $.proxy(function () { 34544 this.dehover_node(); 34545 this.save_selected(); 34546 }, this)) 34547 .bind("set_rollback.jstree", $.proxy(function () { 34548 this.reselect(); 34549 }, this)) 34550 .bind("close_node.jstree", $.proxy(function (event, data) { 34551 var s = this._get_settings().ui, 34552 obj = this._get_node(data.rslt.obj), 34553 clk = (obj && obj.length) ? obj.children("ul").find("a.jstree-clicked") : $(), 34554 _this = this; 34555 if(s.selected_parent_close === false || !clk.length) { return; } 34556 clk.each(function () { 34557 _this.deselect_node(this); 34558 if(s.selected_parent_close === "select_parent") { _this.select_node(obj); } 34559 }); 34560 }, this)) 34561 .bind("delete_node.jstree", $.proxy(function (event, data) { 34562 var s = this._get_settings().ui.select_prev_on_delete, 34563 obj = this._get_node(data.rslt.obj), 34564 clk = (obj && obj.length) ? obj.find("a.jstree-clicked") : [], 34565 _this = this; 34566 clk.each(function () { _this.deselect_node(this); }); 34567 if(s && clk.length) { 34568 data.rslt.prev.each(function () { 34569 if(this.parentNode) { _this.select_node(this); return false; /* if return false is removed all prev nodes will be selected */} 34570 });
34571 } 34572 }, this)) 34573 .bind("move_node.jstree", $.proxy(function (event, data) { 34574 if(data.rslt.cy) { 34575 data.rslt.oc.find("a.jstree-clicked").removeClass("jstree-clicked"); 34576 } 34577 }, this)); 34578 }, 34579 defaults : { 34580 select_limit : -1, // 0, 1, 2 ... or -1 for unlimited 34581 select_multiple_modifier : "ctrl", // on, or ctrl, shift, alt 34582 select_range_modifier : "shift", 34583 selected_parent_close : "select_parent", // false, "deselect", "select_parent" 34584 selected_parent_open : true, 34585 select_prev_on_delete : true, 34586 disable_selecting_children : false, 34587 initially_select : [] 34588 }, 34589 _fn : { 34590 _get_node : function (obj, allow_multiple) { 34591 if(typeof obj === "undefined" || obj === null) { return allow_multiple ? this.data.ui.selected : this.data.ui.last_selected; } 34592 var $obj = $(obj, this.get_container()); 34593 if($obj.is(".jstree") || obj == -1) { return -1; } 34594 $obj = $obj.closest("li", this.get_container()); 34595 return $obj.length ? $obj : false; 34596 }, 34597 _ui_notify : function (n, data) { 34598 if(data.selected) { 34599 this.select_node(n, false); 34600 } 34601 }, 34602 save_selected : function () { 34603 var _this = this; 34604 this.data.ui.to_select = []; 34605 this.data.ui.selected.each(function () { if(this.id) { _this.data.ui.to_select.push("#" + this.id.toString().replace(/^#/,"").replace(/\\\//g,"/").replace(/\//g,"\\\/").replace(/\\\./g,".").replace(/\./g,"\\.").replace(/\:/g,"\\:")); } }); 34606 this.__callback(this.data.ui.to_select); 34607 }, 34608 reselect : function () { 34609 var _this = this, 34610 s = this.data.ui.to_select; 34611 s = $.map($.makeArray(s), function (n) { return "#" + n.toString().replace(/^#/,"").replace(/\\\//g,"/").replace(/\//g,"\\\/").replace(/\\\./g,".").replace(/\./g,"\\.").replace(/\:/g,"\\:"); }); 34612 // this.deselect_all(); WHY deselect, breaks plugin state notifier? 34613 $.each(s, function (i, val) { if(val && val !== "#") { _this.select_node(val); } }); 34614 this.data.ui.selected = this.data.ui.selected.filter(function () { return this.parentNode; }); 34615 this.__callback(); 34616 }, 34617 refresh : function (obj) { 34618 this.save_selected(); 34619 return this.__call_old(); 34620 }, 34621 hover_node : function (obj) { 34622 obj = this._get_node(obj); 34623 if(!obj.length) { return false; } 34624 //if(this.data.ui.hovered && obj.get(0) === this.data.ui.hovered.get(0)) { return; } 34625 if(!obj.hasClass("jstree-hovered")) { this.dehover_node(); } 34626 this.data.ui.hovered = obj.children("a").addClass("jstree-hovered").parent(); 34627 this._fix_scroll(obj); 34628 this.__callback({ "obj" : obj }); 34629 }, 34630 dehover_node : function () { 34631 var obj = this.data.ui.hovered, p; 34632 if(!obj || !obj.length) { return false; } 34633 p = obj.children("a").removeClass("jstree-hovered").parent(); 34634 if(this.data.ui.hovered[0] === p[0]) { this.data.ui.hovered = null; } 34635 this.__callback({ "obj" : obj }); 34636 }, 34637 select_node : function (obj, check, e) { 34638 obj = this._get_node(obj); 34639 if(obj == -1 || !obj || !obj.length) { return false; } 34640 var s = this._get_settings().ui, 34641 is_multiple = (s.select_multiple_modifier == "on" || (s.select_multiple_modifier !== false && e && e[s.select_multiple_modifier + "Key"])), 34642 is_range = (s.select_range_modifier !== false && e && e[s.select_range_modifier + "Key"] && this.data.ui.last_selected && this.data.ui.last_selected[0] !== obj[0] && this.data.ui.last_selected.parent()[0] === obj.parent()[0]), 34643 is_selected = this.is_selected(obj), 34644 proceed = true, 34645 t = this; 34646 if(check) { 34647 if(s.disable_selecting_children && is_multiple && 34648 ( 34649 (obj.parentsUntil(".jstree","li").children("a.jstree-clicked").length) || 34650 (obj.children("ul").find("a.jstree-clicked:eq(0)").length) 34651 ) 34652 ) { 34653 return false; 34654 } 34655 proceed = false; 34656 switch(!0) { 34657 case (is_range): 34658 this.data.ui.last_selected.addClass("jstree-last-selected"); 34659 obj = obj[ obj.index() < this.data.ui.last_selected.index() ? "nextUntil" : "prevUntil" ](".jstree-last-selected").andSelf(); 34660 if(s.select_limit == -1 || obj.length < s.select_limit) { 34661 this.data.ui.last_selected.removeClass("jstree-last-selected"); 34662 this.data.ui.selected.each(function () { 34663 if(this !== t.data.ui.last_selected[0]) { t.deselect_node(this); } 34664 });
34665 is_selected = false; 34666 proceed = true; 34667 } 34668 else { 34669 proceed = false; 34670 } 34671 break; 34672 case (is_selected && !is_multiple): 34673 this.deselect_all(); 34674 is_selected = false; 34675 proceed = true; 34676 break; 34677 case (!is_selected && !is_multiple): 34678 if(s.select_limit == -1 || s.select_limit > 0) { 34679 this.deselect_all(); 34680 proceed = true; 34681 } 34682 break; 34683 case (is_selected && is_multiple): 34684 this.deselect_node(obj); 34685 break; 34686 case (!is_selected && is_multiple): 34687 if(s.select_limit == -1 || this.data.ui.selected.length + 1 <= s.select_limit) { 34688 proceed = true; 34689 } 34690 break; 34691 } 34692 } 34693 if(proceed && !is_selected) { 34694 if(!is_range) { this.data.ui.last_selected = obj; } 34695 obj.children("a").addClass("jstree-clicked"); 34696 if(s.selected_parent_open) { 34697 obj.parents(".jstree-closed").each(function () { t.open_node(this, false, true); }); 34698 } 34699 this.data.ui.selected = this.data.ui.selected.add(obj); 34700 this._fix_scroll(obj.eq(0)); 34701 this.__callback({ "obj" : obj, "e" : e }); 34702 } 34703 }, 34704 _fix_scroll : function (obj) { 34705 var c = this.get_container()[0], t; 34706 if(c.scrollHeight > c.offsetHeight) { 34707 obj = this._get_node(obj); 34708 if(!obj || obj === -1 || !obj.length || !obj.is(":visible")) { return; } 34709 t = obj.offset().top - this.get_container().offset().top; 34710 if(t < 0) { 34711 c.scrollTop = c.scrollTop + t - 1; 34712 } 34713 if(t + this.data.core.li_height + (c.scrollWidth > c.offsetWidth ? scrollbar_width : 0) > c.offsetHeight) { 34714 c.scrollTop = c.scrollTop + (t - c.offsetHeight + this.data.core.li_height + 1 + (c.scrollWidth > c.offsetWidth ? scrollbar_width : 0)); 34715 } 34716 } 34717 }, 34718 deselect_node : function (obj) { 34719 obj = this._get_node(obj); 34720 if(!obj.length) { return false; } 34721 if(this.is_selected(obj)) { 34722 obj.children("a").removeClass("jstree-clicked"); 34723 this.data.ui.selected = this.data.ui.selected.not(obj); 34724 if(this.data.ui.last_selected.get(0) === obj.get(0)) { this.data.ui.last_selected = this.data.ui.selected.eq(0); } 34725 this.__callback({ "obj" : obj }); 34726 } 34727 }, 34728 toggle_select : function (obj) { 34729 obj = this._get_node(obj); 34730 if(!obj.length) { return false; } 34731 if(this.is_selected(obj)) { this.deselect_node(obj); } 34732 else { this.select_node(obj); } 34733 }, 34734 is_selected : function (obj) { return this.data.ui.selected.index(this._get_node(obj)) >= 0; }, 34735 get_selected : function (context) { 34736 return context ? $(context).find("a.jstree-clicked").parent() : this.data.ui.selected; 34737 }, 34738 deselect_all : function (context) { 34739 var ret = context ? $(context).find("a.jstree-clicked").parent() : this.get_container().find("a.jstree-clicked").parent(); 34740 ret.children("a.jstree-clicked").removeClass("jstree-clicked"); 34741 this.data.ui.selected = $([]); 34742 this.data.ui.last_selected = false; 34743 this.__callback({ "obj" : ret }); 34744 } 34745 } 34746 }); 34747 // include the selection plugin by default 34748 $.jstree.defaults.plugins.push("ui"); 34749})(jQuery); 34750//*/ 34751 34752/* 34753 * jsTree CRRM plugin 34754 * Handles creating/renaming/removing/moving nodes by user interaction. 34755 */ 34756(function ($) { 34757 $.jstree.plugin("crrm", { 34758 __init : function () { 34759 this.get_container() 34760 .bind("move_node.jstree", $.proxy(function (e, data) { 34761 if(this._get_settings().crrm.move.open_onmove) { 34762 var t = this; 34763 data.rslt.np.parentsUntil(".jstree").andSelf().filter(".jstree-closed").each(function () { 34764 t.open_node(this, false, true); 34765 }); 34766 } 34767 }, this)); 34768 }, 34769 defaults : { 34770 input_width_limit : 200, 34771 move : { 34772 always_copy : false, // false, true or "multitree" 34773 open_onmove : true, 34774 default_position : "last", 34775 check_move : function (m) { return true; } 34776 } 34777 }, 34778 _fn : { 34779 _show_input : function (obj, callback) { 34780 obj = this._get_node(obj); 34781 var rtl = this._get_settings().core.rtl, 34782 w = this._get_settings().crrm.input_width_limit, 34783 w1 = obj.children("ins").width(), 34784 w2 = obj.find("> a:visible > ins").width() * obj.find("> a:visible > ins").length, 34785 t = this.get_text(obj), 34786 h1 = $("<div />", { css : { "position" : "absolute", "top" : "-200px", "left" : (rtl ? "0px" : "-1000px"), "visibility" : "hidden" } }).appendTo("body"), 34787 h2 = obj.css("position","relative").append( 34788 $("<input />", { 34789 "value" : t, 34790 "class" : "jstree-rename-input", 34791 // "size" : t.length, 34792 "css" : { 34793 "padding" : "0", 34794 "border" : "1px solid silver", 34795 "position" : "absolute",
34796 "left" : (rtl ? "auto" : (w1 + w2 + 4) + "px"), 34797 "right" : (rtl ? (w1 + w2 + 4) + "px" : "auto"), 34798 "top" : "0px", 34799 "height" : (this.data.core.li_height - 2) + "px", 34800 "lineHeight" : (this.data.core.li_height - 2) + "px", 34801 "width" : "150px" // will be set a bit further down 34802 }, 34803 "blur" : $.proxy(function () { 34804 var i = obj.children(".jstree-rename-input"), 34805 v = i.val(); 34806 if(v === "") { v = t; } 34807 h1.remove(); 34808 i.remove(); // rollback purposes 34809 this.set_text(obj,t); // rollback purposes 34810 this.rename_node(obj, v); 34811 callback.call(this, obj, v, t); 34812 obj.css("position",""); 34813 }, this), 34814 "keyup" : function (event) { 34815 var key = event.keyCode || event.which; 34816 if(key == 27) { this.value = t; this.blur(); return; } 34817 else if(key == 13) { this.blur(); return; } 34818 else { 34819 h2.width(Math.min(h1.text("pW" + this.value).width(),w)); 34820 } 34821 }, 34822 "keypress" : function(event) { 34823 var key = event.keyCode || event.which; 34824 if(key == 13) { return false; } 34825 } 34826 }) 34827 ).children(".jstree-rename-input"); 34828 this.set_text(obj, ""); 34829 h1.css({ 34830 fontFamily : h2.css('fontFamily') || '', 34831 fontSize : h2.css('fontSize') || '', 34832 fontWeight : h2.css('fontWeight') || '', 34833 fontStyle : h2.css('fontStyle') || '', 34834 fontStretch : h2.css('fontStretch') || '', 34835 fontVariant : h2.css('fontVariant') || '', 34836 letterSpacing : h2.css('letterSpacing') || '', 34837 wordSpacing : h2.css('wordSpacing') || '' 34838 }); 34839 h2.width(Math.min(h1.text("pW" + h2[0].value).width(),w))[0].select(); 34840 }, 34841 rename : function (obj) { 34842 obj = this._get_node(obj); 34843 this.__rollback(); 34844 var f = this.__callback; 34845 this._show_input(obj, function (obj, new_name, old_name) { 34846 f.call(this, { "obj" : obj, "new_name" : new_name, "old_name" : old_name }); 34847 }); 34848 }, 34849 create : function (obj, position, js, callback, skip_rename) { 34850 var t, _this = this; 34851 obj = this._get_node(obj); 34852 if(!obj) { obj = -1; } 34853 this.__rollback(); 34854 t = this.create_node(obj, position, js, function (t) { 34855 var p = this._get_parent(t), 34856 pos = $(t).index(); 34857 if(callback) { callback.call(this, t); } 34858 if(p.length && p.hasClass("jstree-closed")) { this.open_node(p, false, true); } 34859 if(!skip_rename) { 34860 this._show_input(t, function (obj, new_name, old_name) { 34861 _this.__callback({ "obj" : obj, "name" : new_name, "parent" : p, "position" : pos }); 34862 }); 34863 } 34864 else { _this.__callback({ "obj" : t, "name" : this.get_text(t), "parent" : p, "position" : pos }); } 34865 }); 34866 return t; 34867 }, 34868 remove : function (obj) { 34869 obj = this._get_node(obj, true); 34870 var p = this._get_parent(obj), prev = this._get_prev(obj); 34871 this.__rollback(); 34872 obj = this.delete_node(obj); 34873 if(obj !== false) { this.__callback({ "obj" : obj, "prev" : prev, "parent" : p }); } 34874 }, 34875 check_move : function () { 34876 if(!this.__call_old()) { return false; } 34877 var s = this._get_settings().crrm.move; 34878 if(!s.check_move.call(this, this._get_move())) { return false; } 34879 return true; 34880 }, 34881 move_node : function (obj, ref, position, is_copy, is_prepared, skip_check) { 34882 var s = this._get_settings().crrm.move; 34883 if(!is_prepared) { 34884 if(typeof position === "undefined") { position = s.default_position; } 34885 if(position === "inside" && !s.default_position.match(/^(before|after)$/)) { position = s.default_position; } 34886 return this.__call_old(true, obj, ref, position, is_copy, false, skip_check); 34887 } 34888 // if the move is already prepared 34889 if(s.always_copy === true || (s.always_copy === "multitree" && obj.rt.get_index() !== obj.ot.get_index() )) { 34890 is_copy = true; 34891 } 34892 this.__call_old(true, obj, ref, position, is_copy, true, skip_check); 34893 }, 34894 34895 cut : function (obj) { 34896 obj = this._get_node(obj, true); 34897 if(!obj || !obj.length) { return false; } 34898 this.data.crrm.cp_nodes = false; 34899 this.data.crrm.ct_nodes = obj;
34900 this.__callback({ "obj" : obj }); 34901 }, 34902 copy : function (obj) { 34903 obj = this._get_node(obj, true); 34904 if(!obj || !obj.length) { return false; } 34905 this.data.crrm.ct_nodes = false; 34906 this.data.crrm.cp_nodes = obj; 34907 this.__callback({ "obj" : obj }); 34908 }, 34909 paste : function (obj) { 34910 obj = this._get_node(obj); 34911 if(!obj || !obj.length) { return false; } 34912 var nodes = this.data.crrm.ct_nodes ? this.data.crrm.ct_nodes : this.data.crrm.cp_nodes; 34913 if(!this.data.crrm.ct_nodes && !this.data.crrm.cp_nodes) { return false; } 34914 if(this.data.crrm.ct_nodes) { this.move_node(this.data.crrm.ct_nodes, obj); this.data.crrm.ct_nodes = false; } 34915 if(this.data.crrm.cp_nodes) { this.move_node(this.data.crrm.cp_nodes, obj, false, true); } 34916 this.__callback({ "obj" : obj, "nodes" : nodes }); 34917 } 34918 } 34919 }); 34920 // include the crr plugin by default 34921 // $.jstree.defaults.plugins.push("crrm"); 34922})(jQuery); 34923//*/ 34924 34925/* 34926 * jsTree themes plugin 34927 * Handles loading and setting themes, as well as detecting path to themes, etc. 34928 */ 34929(function ($) { 34930 var themes_loaded = []; 34931 // this variable stores the path to the themes folder - if left as false - it will be autodetected 34932 $.jstree._themes = false; 34933 $.jstree.plugin("themes", { 34934 __init : function () { 34935 this.get_container() 34936 .bind("init.jstree", $.proxy(function () { 34937 var s = this._get_settings().themes; 34938 this.data.themes.dots = s.dots; 34939 this.data.themes.icons = s.icons; 34940 this.set_theme(s.theme, s.url); 34941 }, this)) 34942 .bind("loaded.jstree", $.proxy(function () { 34943 // bound here too, as simple HTML tree's won't honor dots & icons otherwise 34944 if(!this.data.themes.dots) { this.hide_dots(); } 34945 else { this.show_dots(); } 34946 if(!this.data.themes.icons) { this.hide_icons(); } 34947 else { this.show_icons(); } 34948 }, this)); 34949 }, 34950 defaults : { 34951 theme : "default", 34952 url : false, 34953 dots : true, 34954 icons : true 34955 }, 34956 _fn : { 34957 set_theme : function (theme_name, theme_url) { 34958 if(!theme_name) { return false; } 34959 if(!theme_url) { theme_url = $.jstree._themes + theme_name + '/style.css'; } 34960 if($.inArray(theme_url, themes_loaded) == -1) { 34961// $.vakata.css.add_sheet({ "url" : theme_url }); 34962// themes_loaded.push(theme_url); 34963 } 34964 if(this.data.themes.theme != theme_name) { 34965 this.get_container().removeClass('jstree-' + this.data.themes.theme); 34966 this.data.themes.theme = theme_name; 34967 } 34968 this.get_container().addClass('jstree-' + theme_name); 34969 if(!this.data.themes.dots) { this.hide_dots(); } 34970 else { this.show_dots(); } 34971 if(!this.data.themes.icons) { this.hide_icons(); } 34972 else { this.show_icons(); } 34973 this.__callback(); 34974 }, 34975 get_theme : function () { return this.data.themes.theme; }, 34976 34977 show_dots : function () { this.data.themes.dots = true; this.get_container().children("ul").removeClass("jstree-no-dots"); }, 34978 hide_dots : function () { this.data.themes.dots = false; this.get_container().children("ul").addClass("jstree-no-dots"); }, 34979 toggle_dots : function () { if(this.data.themes.dots) { this.hide_dots(); } else { this.show_dots(); } }, 34980 34981 show_icons : function () { this.data.themes.icons = true; this.get_container().children("ul").removeClass("jstree-no-icons"); }, 34982 hide_icons : function () { this.data.themes.icons = false; this.get_container().children("ul").addClass("jstree-no-icons"); }, 34983 toggle_icons: function () { if(this.data.themes.icons) { this.hide_icons(); } else { this.show_icons(); } } 34984 } 34985 }); 34986 // autodetect themes path 34987 $(function () { 34988 if($.jstree._themes === false) { 34989 $("script").each(function () { 34990 if(this.src.toString().match(/jquery\.jstree[^\/]*?\.js(\?.*)?$/)) { 34991 $.jstree._themes = this.src.toString().replace(/jquery\.jstree[^\/]*?\.js(\?.*)?$/, "") + 'themes/'; 34992 return false; 34993 } 34994 }); 34995 } 34996 if($.jstree._themes === false) { $.jstree._themes = "themes/"; } 34997 }); 34998 // include the themes plugin by default 34999 $.jstree.defaults.plugins.push("themes"); 35000})(jQuery);
35001//*/ 35002 35003/* 35004 * jsTree hotkeys plugin 35005 * Enables keyboard navigation for all tree instances 35006 * Depends on the jstree ui & jquery hotkeys plugins 35007 */ 35008(function ($) { 35009 var bound = []; 35010 function exec(i, event) { 35011 var f = $.jstree._focused(), tmp; 35012 if(f && f.data && f.data.hotkeys && f.data.hotkeys.enabled) { 35013 tmp = f._get_settings().hotkeys[i]; 35014 if(tmp) { return tmp.call(f, event); } 35015 } 35016 } 35017 $.jstree.plugin("hotkeys", { 35018 __init : function () { 35019 if(typeof $.hotkeys === "undefined") { throw "jsTree hotkeys: jQuery hotkeys plugin not included."; } 35020 if(!this.data.ui) { throw "jsTree hotkeys: jsTree UI plugin not included."; } 35021 $.each(this._get_settings().hotkeys, function (i, v) { 35022 if(v !== false && $.inArray(i, bound) == -1) { 35023 $(document).bind("keydown", i, function (event) { return exec(i, event); }); 35024 bound.push(i); 35025 } 35026 }); 35027 this.get_container() 35028 .bind("lock.jstree", $.proxy(function () { 35029 if(this.data.hotkeys.enabled) { this.data.hotkeys.enabled = false; this.data.hotkeys.revert = true; } 35030 }, this)) 35031 .bind("unlock.jstree", $.proxy(function () { 35032 if(this.data.hotkeys.revert) { this.data.hotkeys.enabled = true; } 35033 }, this)); 35034 this.enable_hotkeys(); 35035 }, 35036 defaults : { 35037 "up" : function () { 35038 var o = this.data.ui.hovered || this.data.ui.last_selected || -1; 35039 this.hover_node(this._get_prev(o)); 35040 return false; 35041 }, 35042 "ctrl+up" : function () { 35043 var o = this.data.ui.hovered || this.data.ui.last_selected || -1; 35044 this.hover_node(this._get_prev(o)); 35045 return false; 35046 }, 35047 "shift+up" : function () { 35048 var o = this.data.ui.hovered || this.data.ui.last_selected || -1; 35049 this.hover_node(this._get_prev(o)); 35050 return false; 35051 }, 35052 "down" : function () { 35053 var o = this.data.ui.hovered || this.data.ui.last_selected || -1; 35054 this.hover_node(this._get_next(o)); 35055 return false; 35056 }, 35057 "ctrl+down" : function () { 35058 var o = this.data.ui.hovered || this.data.ui.last_selected || -1; 35059 this.hover_node(this._get_next(o)); 35060 return false; 35061 }, 35062 "shift+down" : function () { 35063 var o = this.data.ui.hovered || this.data.ui.last_selected || -1; 35064 this.hover_node(this._get_next(o)); 35065 return false; 35066 }, 35067 "left" : function () { 35068 var o = this.data.ui.hovered || this.data.ui.last_selected; 35069 if(o) { 35070 if(o.hasClass("jstree-open")) { this.close_node(o); } 35071 else { this.hover_node(this._get_prev(o)); } 35072 } 35073 return false; 35074 }, 35075 "ctrl+left" : function () { 35076 var o = this.data.ui.hovered || this.data.ui.last_selected; 35077 if(o) { 35078 if(o.hasClass("jstree-open")) { this.close_node(o); } 35079 else { this.hover_node(this._get_prev(o)); } 35080 } 35081 return false; 35082 }, 35083 "shift+left" : function () { 35084 var o = this.data.ui.hovered || this.data.ui.last_selected; 35085 if(o) { 35086 if(o.hasClass("jstree-open")) { this.close_node(o); } 35087 else { this.hover_node(this._get_prev(o)); } 35088 } 35089 return false; 35090 }, 35091 "right" : function () { 35092 var o = this.data.ui.hovered || this.data.ui.last_selected; 35093 if(o && o.length) { 35094 if(o.hasClass("jstree-closed")) { this.open_node(o); } 35095 else { this.hover_node(this._get_next(o)); } 35096 } 35097 return false; 35098 }, 35099 "ctrl+right" : function () { 35100 var o = this.data.ui.hovered || this.data.ui.last_selected; 35101 if(o && o.length) { 35102 if(o.hasClass("jstree-closed")) { this.open_node(o); } 35103 else { this.hover_node(this._get_next(o)); } 35104 } 35105 return false; 35106 }, 35107 "shift+right" : function () { 35108 var o = this.data.ui.hovered || this.data.ui.last_selected; 35109 if(o && o.length) { 35110 if(o.hasClass("jstree-closed")) { this.open_node(o); } 35111 else { this.hover_node(this._get_next(o)); } 35112 } 35113 return false; 35114 }, 35115 "space" : function () { 35116 if(this.data.ui.hovered) { this.data.ui.hovered.children("a:eq(0)").click(); } 35117 return false; 35118 }, 35119 "ctrl+space" : function (event) { 35120 event.type = "click"; 35121 if(this.data.ui.hovered) { this.data.ui.hovered.children("a:eq(0)").trigger(event); } 35122 return false; 35123 }, 35124 "shift+space" : function (event) { 35125 event.type = "click"; 35126 if(this.data.ui.hovered) { this.data.ui.hovered.children("a:eq(0)").trigger(event); } 35127 return false; 35128 }, 35129 "f2" : function () { this.rename(this.data.ui.hovered || this.data.ui.last_selected); }, 35130 "del" : function () { this.remove(this.data.ui.hovered || this._get_node(null)); } 35131 }, 35132 _fn : { 35133 enable_hotkeys : function () { 35134 this.data.hotkeys.enabled = true; 35135 }, 35136 disable_hotkeys : function () { 35137 this.data.hotkeys.enabled = false; 35138 } 35139 } 35140 }); 35141})(jQuery);
35142//*/ 35143 35144/* 35145 * jsTree JSON plugin 35146 * The JSON data store. Datastores are build by overriding the `load_node` and `_is_loaded` functions. 35147 */ 35148(function ($) { 35149 $.jstree.plugin("json_data", { 35150 __init : function() { 35151 var s = this._get_settings().json_data; 35152 if(s.progressive_unload) { 35153 this.get_container().bind("after_close.jstree", function (e, data) { 35154 data.rslt.obj.children("ul").remove(); 35155 }); 35156 } 35157 }, 35158 defaults : { 35159 // `data` can be a function: 35160 // * accepts two arguments - node being loaded and a callback to pass the result to 35161 // * will be executed in the current tree's scope & ajax won't be supported 35162 data : false, 35163 ajax : false, 35164 correct_state : true, 35165 progressive_render : false, 35166 progressive_unload : false 35167 }, 35168 _fn : { 35169 load_node : function (obj, s_call, e_call) { var _this = this; this.load_node_json(obj, function () { _this.__callback({ "obj" : _this._get_node(obj) }); s_call.call(this); }, e_call); }, 35170 _is_loaded : function (obj) { 35171 var s = this._get_settings().json_data; 35172 obj = this._get_node(obj); 35173 return obj == -1 || !obj || (!s.ajax && !s.progressive_render && !$.isFunction(s.data)) || obj.is(".jstree-open, .jstree-leaf") || obj.children("ul").children("li").length > 0; 35174 }, 35175 refresh : function (obj) { 35176 obj = this._get_node(obj); 35177 var s = this._get_settings().json_data; 35178 if(obj && obj !== -1 && s.progressive_unload && ($.isFunction(s.data) || !!s.ajax)) { 35179 obj.removeData("jstree_children"); 35180 } 35181 return this.__call_old(); 35182 }, 35183 load_node_json : function (obj, s_call, e_call) { 35184 var s = this.get_settings().json_data, d, 35185 error_func = function () {}, 35186 success_func = function () {}; 35187 obj = this._get_node(obj); 35188 35189 if(obj && obj !== -1 && (s.progressive_render || s.progressive_unload) && !obj.is(".jstree-open, .jstree-leaf") && obj.children("ul").children("li").length === 0 && obj.data("jstree_children")) { 35190 d = this._parse_json(obj.data("jstree_children"), obj); 35191 if(d) { 35192 obj.append(d); 35193 if(!s.progressive_unload) { obj.removeData("jstree_children"); } 35194 } 35195 this.clean_node(obj); 35196 if(s_call) { s_call.call(this); } 35197 return; 35198 } 35199 35200 if(obj && obj !== -1) { 35201 if(obj.data("jstree_is_loading")) { return; } 35202 else { obj.data("jstree_is_loading",true); } 35203 } 35204 switch(!0) { 35205 case (!s.data && !s.ajax): throw "Neither data nor ajax settings supplied."; 35206 // function option added here for easier model integration (also supporting async - see callback) 35207 case ($.isFunction(s.data)): 35208 s.data.call(this, obj, $.proxy(function (d) { 35209 d = this._parse_json(d, obj); 35210 if(!d) { 35211 if(obj === -1 || !obj) { 35212 if(s.correct_state) { this.get_container().children("ul").empty(); } 35213 } 35214 else { 35215 obj.children("a.jstree-loading").removeClass("jstree-loading"); 35216 obj.removeData("jstree_is_loading"); 35217 if(s.correct_state) { this.correct_state(obj); } 35218 } 35219 if(e_call) { e_call.call(this); } 35220 } 35221 else { 35222 if(obj === -1 || !obj) { this.get_container().children("ul").empty().append(d.children()); } 35223 else { obj.append(d).children("a.jstree-loading").removeClass("jstree-loading"); obj.removeData("jstree_is_loading"); } 35224 this.clean_node(obj); 35225 if(s_call) { s_call.call(this); } 35226 } 35227 }, this)); 35228 break; 35229 case (!!s.data && !s.ajax) || (!!s.data && !!s.ajax && (!obj || obj === -1)): 35230 if(!obj || obj == -1) { 35231 d = this._parse_json(s.data, obj); 35232 if(d) { 35233 this.get_container().children("ul").empty().append(d.children()); 35234 this.clean_node(); 35235 } 35236 else { 35237 if(s.correct_state) { this.get_container().children("ul").empty(); } 35238 } 35239 } 35240 if(s_call) { s_call.call(this); } 35241 break; 35242 case (!s.data && !!s.ajax) || (!!s.data && !!s.ajax && obj && obj !== -1): 35243 error_func = function (x, t, e) { 35244 var ef = this.get_settings().json_data.ajax.error; 35245 if(ef) { ef.call(this, x, t, e); } 35246 if(obj != -1 && obj.length) { 35247 obj.children("a.jstree-loading").removeClass("jstree-loading"); 35248 obj.removeData("jstree_is_loading"); 35249 if(t === "success" && s.correct_state) { this.correct_state(
35249obj); } 35250 } 35251 else { 35252 if(t === "success" && s.correct_state) { this.get_container().children("ul").empty(); } 35253 } 35254 if(e_call) { e_call.call(this); } 35255 }; 35256 success_func = function (d, t, x) { 35257 var sf = this.get_settings().json_data.ajax.success; 35258 if(sf) { d = sf.call(this,d,t,x) || d; } 35259 if(d === "" || (d && d.toString && d.toString().replace(/^[\s\n]+$/,"") === "") || (!$.isArray(d) && !$.isPlainObject(d))) { 35260 return error_func.call(this, x, t, ""); 35261 } 35262 d = this._parse_json(d, obj); 35263 if(d) { 35264 if(obj === -1 || !obj) { this.get_container().children("ul").empty().append(d.children()); } 35265 else { obj.append(d).children("a.jstree-loading").removeClass("jstree-loading"); obj.removeData("jstree_is_loading"); } 35266 this.clean_node(obj); 35267 if(s_call) { s_call.call(this); } 35268 } 35269 else { 35270 if(obj === -1 || !obj) { 35271 if(s.correct_state) { 35272 this.get_container().children("ul").empty(); 35273 if(s_call) { s_call.call(this); } 35274 } 35275 } 35276 else { 35277 obj.children("a.jstree-loading").removeClass("jstree-loading"); 35278 obj.removeData("jstree_is_loading"); 35279 if(s.correct_state) { 35280 this.correct_state(obj); 35281 if(s_call) { s_call.call(this); } 35282 } 35283 } 35284 } 35285 }; 35286 s.ajax.context = this; 35287 s.ajax.error = error_func; 35288 s.ajax.success = success_func; 35289 if(!s.ajax.dataType) { s.ajax.dataType = "json"; } 35290 if($.isFunction(s.ajax.url)) { s.ajax.url = s.ajax.url.call(this, obj); } 35291 if($.isFunction(s.ajax.data)) { s.ajax.data = s.ajax.data.call(this, obj); } 35292 $.ajax(s.ajax); 35293 break; 35294 } 35295 }, 35296 _parse_json : function (js, obj, is_callback) { 35297 var d = false, 35298 p = this._get_settings(), 35299 s = p.json_data, 35300 t = p.core.html_titles, 35301 tmp, i, j, ul1, ul2; 35302 35303 if(!js) { return d; } 35304 if(s.progressive_unload && obj && obj !== -1) { 35305 obj.data("jstree_children", d); 35306 } 35307 if($.isArray(js)) { 35308 d = $(); 35309 if(!js.length) { return false; } 35310 for(i = 0, j = js.length; i < j; i++) { 35311 tmp = this._parse_json(js[i], obj, true); 35312 if(tmp.length) { d = d.add(tmp); } 35313 } 35314 } 35315 else { 35316 if(typeof js == "string") { js = { data : js }; } 35317 if(!js.data && js.data !== "") { return d; } 35318 d = $("<li />"); 35319 if(js.attr) { d.attr(js.attr); } 35320 if(js.metadata) { d.data(js.metadata); } 35321 if(js.state) { d.addClass("jstree-" + js.state); } 35322 if(!$.isArray(js.data)) { tmp = js.data; js.data = []; js.data.push(tmp); } 35323 $.each(js.data, function (i, m) { 35324 tmp = $("<a />"); 35325 if($.isFunction(m)) { m = m.call(this, js); } 35326 if(typeof m == "string") { tmp.attr('href','#')[ t ? "html" : "text" ](m); } 35327 else { 35328 if(!m.attr) { m.attr = {}; } 35329 if(!m.attr.href) { m.attr.href = '#'; } 35330 tmp.attr(m.attr)[ t ? "html" : "text" ](m.title); 35331 if(m.language) { tmp.addClass(m.language); } 35332 } 35333 tmp.prepend("<ins class='jstree-icon'> </ins>"); 35334 if(!m.icon && js.icon) { m.icon = js.icon; } 35335 if(m.icon) { 35336 if(m.icon.indexOf("/") === -1) { tmp.children("ins").addClass(m.icon); } 35337 else { tmp.children("ins").css("background","url('" + m.icon + "') center center no-repeat"); } 35338 } 35339 d.append(tmp); 35340 }); 35341 d.prepend("<ins class='jstree-icon'> </ins>"); 35342 if(js.children) { 35343 if(s.progressive_render && js.state !== "open") { 35344 d.addClass("jstree-closed").data("jstree_children", js.children); 35345 } 35346 else { 35347 if(s.progressive_unload) { d.data("jstree_children", js.children); } 35348 if($.isArray(js.children) && js.children.length) { 35349 tmp = this._parse_json(js.children, obj, true); 35350 if(tmp.length) { 35351 ul2 = $("<ul />"); 35352 ul2.append(tmp); 35353 d.append(ul2); 35354 } 35355 } 35356 } 35357 } 35358 } 35359 if(!is_callback) { 35360 ul1 = $("<ul />"); 35361 ul1.append(d); 35362 d = ul1; 35363 } 35364 return d; 35365 }, 35366 get_json : function (obj, li_attr, a_attr, is_callback) { 35367 var result = [], 35368 s = this._get_settings(), 35369 _this = this, 35370 tmp1, tmp2, li, a, t, lang; 35371 obj = this._get_node(obj); 35372 if(!obj || obj === -1) { obj = this.get_container().find("> ul > li"); } 35373 li_attr = $.isArray(li_attr) ? li_attr : [ "id", "class" ]; 35374 if(!is_callback && this.data.types) { li_attr.push(s.types.type_attr); } 35375 a_attr = $.isArray(a_attr) ? a_attr : [ ]; 35376 35377 obj.each(function () { 35378 li = $(this); 35379 tmp1 = { data : [] }; 35380 if(li_attr.length) { tmp1.attr = { }; } 35381 $.each(li_attr, function (i, v) { 35382 tmp2 = li.attr(v); 35383 if(tmp2 && tmp2.length && tmp2.replace(/jstree[^ ]*/ig,'').length) { 35384 tmp1.attr[v] = (" " + tmp2).replace(/ jstree[^ ]*/ig,'').replace(/\s+$/ig," ").replace(/^ /,"").replace(/ $/,""); 35385 } 35386 });
35387 if(li.hasClass("jstree-open")) { tmp1.state = "open"; } 35388 if(li.hasClass("jstree-closed")) { tmp1.state = "closed"; } 35389 if(li.data()) { tmp1.metadata = li.data(); } 35390 a = li.children("a"); 35391 a.each(function () { 35392 t = $(this); 35393 if( 35394 a_attr.length || 35395 $.inArray("languages", s.plugins) !== -1 || 35396 t.children("ins").get(0).style.backgroundImage.length || 35397 (t.children("ins").get(0).className && t.children("ins").get(0).className.replace(/jstree[^ ]*|$/ig,'').length) 35398 ) { 35399 lang = false; 35400 if($.inArray("languages", s.plugins) !== -1 && $.isArray(s.languages) && s.languages.length) { 35401 $.each(s.languages, function (l, lv) { 35402 if(t.hasClass(lv)) { 35403 lang = lv; 35404 return false; 35405 } 35406 }); 35407 } 35408 tmp2 = { attr : { }, title : _this.get_text(t, lang) }; 35409 $.each(a_attr, function (k, z) { 35410 tmp2.attr[z] = (" " + (t.attr(z) || "")).replace(/ jstree[^ ]*/ig,'').replace(/\s+$/ig," ").replace(/^ /,"").replace(/ $/,""); 35411 }); 35412 if($.inArray("languages", s.plugins) !== -1 && $.isArray(s.languages) && s.languages.length) { 35413 $.each(s.languages, function (k, z) { 35414 if(t.hasClass(z)) { tmp2.language = z; return true; } 35415 });
35416 } 35417 if(t.children("ins").get(0).className.replace(/jstree[^ ]*|$/ig,'').replace(/^\s+$/ig,"").length) { 35418 tmp2.icon = t.children("ins").get(0).className.replace(/jstree[^ ]*|$/ig,'').replace(/\s+$/ig," ").replace(/^ /,"").replace(/ $/,""); 35419 } 35420 if(t.children("ins").get(0).style.backgroundImage.length) { 35421 tmp2.icon = t.children("ins").get(0).style.backgroundImage.replace("url(","").replace(")",""); 35422 } 35423 } 35424 else { 35425 tmp2 = _this.get_text(t); 35426 } 35427 if(a.length > 1) { tmp1.data.push(tmp2); } 35428 else { tmp1.data = tmp2; } 35429 }); 35430 li = li.find("> ul > li"); 35431 if(li.length) { tmp1.children = _this.get_json(li, li_attr, a_attr, true); } 35432 result.push(tmp1); 35433 }); 35434 return result; 35435 } 35436 } 35437 }); 35438})(jQuery); 35439//*/ 35440 35441/* 35442 * jsTree languages plugin 35443 * Adds support for multiple language versions in one tree 35444 * This basically allows for many titles coexisting in one node, but only one of them being visible at any given time 35445 * This is useful for maintaining the same structure in many languages (hence the name of the plugin) 35446 */ 35447(function ($) { 35448 $.jstree.plugin("languages", { 35449 __init : function () { this._load_css(); }, 35450 defaults : [], 35451 _fn : { 35452 set_lang : function (i) { 35453 var langs = this._get_settings().languages, 35454 st = false, 35455 selector = ".jstree-" + this.get_index() + ' a'; 35456 if(!$.isArray(langs) || langs.length === 0) { return false; } 35457 if($.inArray(i,langs) == -1) { 35458 if(!!langs[i]) { i = langs[i]; } 35459 else { return false; } 35460 } 35461 if(i == this.data.languages.current_language) { return true; } 35462 st = $.vakata.css.get_css(selector + "." + this.data.languages.current_language, false, this.data.languages.language_css); 35463 if(st !== false) { st.style.display = "none"; } 35464 st = $.vakata.css.get_css(selector + "." + i, false, this.data.languages.language_css); 35465 if(st !== false) { st.style.display = ""; } 35466 this.data.languages.current_language = i; 35467 this.__callback(i); 35468 return true; 35469 }, 35470 get_lang : function () { 35471 return this.data.languages.current_language; 35472 }, 35473 _get_string : function (key, lang) { 35474 var langs = this._get_settings().languages, 35475 s = this._get_settings().core.strings; 35476 if($.isArray(langs) && langs.length) { 35477 lang = (lang && $.inArray(lang,langs) != -1) ? lang : this.data.languages.current_language; 35478 } 35479 if(s[lang] && s[lang][key]) { return s[lang][key]; } 35480 if(s[key]) { return s[key]; } 35481 return key; 35482 }, 35483 get_text : function (obj, lang) { 35484 obj = this._get_node(obj) || this.data.ui.last_selected; 35485 if(!obj.size()) { return false; } 35486 var langs = this._get_settings().languages, 35487 s = this._get_settings().core.html_titles;
35488 if($.isArray(langs) && langs.length) { 35489 lang = (lang && $.inArray(lang,langs) != -1) ? lang : this.data.languages.current_language; 35490 obj = obj.children("a." + lang); 35491 } 35492 else { obj = obj.children("a:eq(0)"); } 35493 if(s) { 35494 obj = obj.clone(); 35495 obj.children("INS").remove(); 35496 return obj.html(); 35497 } 35498 else { 35499 obj = obj.contents().filter(function() { return this.nodeType == 3; })[0]; 35500 return obj.nodeValue; 35501 } 35502 }, 35503 set_text : function (obj, val, lang) { 35504 obj = this._get_node(obj) || this.data.ui.last_selected; 35505 if(!obj.size()) { return false; } 35506 var langs = this._get_settings().languages, 35507 s = this._get_settings().core.html_titles, 35508 tmp; 35509 if($.isArray(langs) && langs.length) { 35510 lang = (lang && $.inArray(lang,langs) != -1) ? lang : this.data.languages.current_language; 35511 obj = obj.children("a." + lang); 35512 } 35513 else { obj = obj.children("a:eq(0)"); } 35514 if(s) { 35515 tmp = obj.children("INS").clone(); 35516 obj.html(val).prepend(tmp); 35517 this.__callback({ "obj" : obj, "name" : val, "lang" : lang }); 35518 return true; 35519 } 35520 else { 35521 obj = obj.contents().filter(function() { return this.nodeType == 3; })[0]; 35522 this.__callback({ "obj" : obj, "name" : val, "lang" : lang }); 35523 return (obj.nodeValue = val); 35524 } 35525 }, 35526 _load_css : function () { 35527 var langs = this._get_settings().languages, 35528 str = "/* languages css */", 35529 selector = ".jstree-" + this.get_index() + ' a', 35530 ln; 35531 if($.isArray(langs) && langs.length) { 35532 this.data.languages.current_language = langs[0]; 35533 for(ln = 0; ln < langs.length; ln++) { 35534 str += selector + "." + langs[ln] + " {"; 35535 if(langs[ln] != this.data.languages.current_language) { str += " display:none; "; } 35536 str += " } "; 35537 } 35538 this.data.languages.language_css = $.vakata.css.add_sheet({ 'str' : str, 'title' : "jstree-languages" }); 35539 } 35540 }, 35541 create_node : function (obj, position, js, callback) { 35542 var t = this.__call_old(true, obj, position, js, function (t) { 35543 var langs = this._get_settings().languages, 35544 a = t.children("a"), 35545 ln; 35546 if($.isArray(langs) && langs.length) { 35547 for(ln = 0; ln < langs.length; ln++) { 35548 if(!a.is("." + langs[ln])) { 35549 t.append(a.eq(0).clone().removeClass(langs.join(" ")).addClass(langs[ln])); 35550 } 35551 } 35552 a.not("." + langs.join(", .")).remove(); 35553 } 35554 if(callback) { callback.call(this, t); } 35555 }); 35556 return t; 35557 } 35558 } 35559 }); 35560})(jQuery); 35561//*/ 35562 35563/* 35564 * jsTree cookies plugin 35565 * Stores the currently opened/selected nodes in a cookie and then restores them 35566 * Depends on the jquery.cookie plugin 35567 */ 35568(function ($) { 35569 $.jstree.plugin("cookies", { 35570 __init : function () { 35571 if(typeof $.cookie === "undefined") { throw "jsTree cookie: jQuery cookie plugin not included."; } 35572 35573 var s = this._get_settings().cookies, 35574 tmp; 35575 if(!!s.save_loaded) { 35576 tmp = $.cookie(s.save_loaded); 35577 if(tmp && tmp.length) { this.data.core.to_load = tmp.split(","); } 35578 } 35579 if(!!s.save_opened) { 35580 tmp = $.cookie(s.save_opened); 35581 if(tmp && tmp.length) { this.data.core.to_open = tmp.split(","); } 35582 } 35583 if(!!s.save_selected) { 35584 tmp = $.cookie(s.save_selected); 35585 if(tmp && tmp.length && this.data.ui) { this.data.ui.to_select = tmp.split(","); } 35586 } 35587 this.get_container() 35588 .one( ( this.data.ui ? "reselect" : "reopen" ) + ".jstree", $.proxy(function () { 35589 this.get_container() 35590 .bind("open_node.jstree close_node.jstree select_node.jstree deselect_node.jstree", $.proxy(function (e) { 35591 if(this._get_settings().cookies.auto_save) { this.save_cookie((e.handleObj.namespace + e.handleObj.type).replace("jstree","")); } 35592 }, this)); 35593 }, this)); 35594 }, 35595 defaults : { 35596 save_loaded : "jstree_load", 35597 save_opened : "jstree_open", 35598 save_selected : "jstree_select", 35599 auto_save : true, 35600 cookie_options : {} 35601 }, 35602 _fn : { 35603 save_cookie : function (c) { 35604 if(this.data.core.refreshing) { return; } 35605 var s = this._get_settings().cookies; 35606 if(!c) { // if called manually and not by event 35607 if(s.save_loaded) { 35608 this.save_loaded(); 35609 $.cookie(s.save_loaded, this.data.core.to_load.join(","), s.cookie_options); 35610 } 35611 if(s.save_opened) { 35612 this.save_opened(); 35613 $.cookie(s.save_opened, this.data.core.to_open.join(","), s.cookie_options); 35614 } 35615 if(s.save_selected && this.data.ui) { 35616 this.save_selected(); 35617 $.cookie(s.save_selected, this.data.ui.to_select.join(","), s.cookie_options); 35618 } 35619 return; 35620 } 35621 switch(c) { 35622 case "open_node": 35623 case "close_node": 35624 if(!!s.save_opened) { 35625 this.save_opened(); 35626 $.cookie(s.save_opened, this.data.core.to_open.join(","), s.cookie_options); 35627 } 35628 if(!!s.save_loaded) { 35629 this.save_loaded(); 35630 $.cookie(s.save_loaded, this.data.core.to_load.join(","), s.cookie_options); 35631 } 35632 break; 35633 case "select_node": 35634 case "deselect_node": 35635 if(!!s.save_selected && this.data.ui) { 35636 this.save_selected(); 35637 $.cookie(s.save_selected, this.data.ui.to_select.join(","), s.cookie_options); 35638 } 35639 break; 35640 } 35641 } 35642 } 35643 }); 35644 // include cookies by default 35645 // $.jstree.defaults.plugins.push("cookies"); 35646})(jQuery);
35647//*/ 35648 35649/* 35650 * jsTree sort plugin 35651 * Sorts items alphabetically (or using any other function) 35652 */ 35653(function ($) { 35654 $.jstree.plugin("sort", { 35655 __init : function () { 35656 this.get_container() 35657 .bind("load_node.jstree", $.proxy(function (e, data) { 35658 var obj = this._get_node(data.rslt.obj); 35659 obj = obj === -1 ? this.get_container().children("ul") : obj.children("ul"); 35660 this.sort(obj); 35661 }, this)) 35662 .bind("rename_node.jstree create_node.jstree create.jstree", $.proxy(function (e, data) { 35663 this.sort(data.rslt.obj.parent()); 35664 }, this)) 35665 .bind("move_node.jstree", $.proxy(function (e, data) { 35666 var m = data.rslt.np == -1 ? this.get_container() : data.rslt.np; 35667 this.sort(m.children("ul")); 35668 }, this)); 35669 }, 35670 defaults : function (a, b) { return this.get_text(a) > this.get_text(b) ? 1 : -1; }, 35671 _fn : { 35672 sort : function (obj) { 35673 var s = this._get_settings().sort, 35674 t = this; 35675 obj.append($.makeArray(obj.children("li")).sort($.proxy(s, t))); 35676 obj.find("> li > ul").each(function() { t.sort($(this)); }); 35677 this.clean_node(obj); 35678 } 35679 } 35680 }); 35681})(jQuery); 35682//*/ 35683 35684/* 35685 * jsTree DND plugin 35686 * Drag and drop plugin for moving/copying nodes 35687 */ 35688(function ($) { 35689 var o = false, 35690 r = false, 35691 m = false, 35692 ml = false, 35693 sli = false, 35694 sti = false, 35695 dir1 = false, 35696 dir2 = false, 35697 last_pos = false; 35698 $.vakata.dnd = { 35699 is_down : false, 35700 is_drag : false, 35701 helper : false, 35702 scroll_spd : 10, 35703 init_x : 0, 35704 init_y : 0, 35705 threshold : 5, 35706 helper_left : 5, 35707 helper_top : 10, 35708 user_data : {}, 35709 35710 drag_start : function (e, data, html) { 35711 if($.vakata.dnd.is_drag) { $.vakata.drag_stop({}); } 35712 try { 35713 e.currentTarget.unselectable = "on"; 35714 e.currentTarget.onselectstart = function() { return false; }; 35715 if(e.currentTarget.style) { e.currentTarget.style.MozUserSelect = "none"; } 35716 } catch(err) { } 35717 $.vakata.dnd.init_x = e.pageX; 35718 $.vakata.dnd.init_y = e.pageY; 35719 $.vakata.dnd.user_data = data; 35720 $.vakata.dnd.is_down = true; 35721 $.vakata.dnd.helper = $("<div id='vakata-dragged' />").html(html); //.fadeTo(10,0.25); 35722 $(document).bind("mousemove", $.vakata.dnd.drag); 35723 $(document).bind("mouseup", $.vakata.dnd.drag_stop); 35724 return false; 35725 }, 35726 drag : function (e) { 35727 if(!$.vakata.dnd.is_down) { return; } 35728 if(!$.vakata.dnd.is_drag) { 35729 if(Math.abs(e.pageX - $.vakata.dnd.init_x) > 5 || Math.abs(e.pageY - $.vakata.dnd.init_y) > 5) { 35730 $.vakata.dnd.helper.appendTo("body"); 35731 $.vakata.dnd.is_drag = true; 35732 $(document).triggerHandler("drag_start.vakata", { "event" : e, "data" : $.vakata.dnd.user_data }); 35733 } 35734 else { return; } 35735 } 35736 35737 // maybe use a scrolling parent element instead of document? 35738 if(e.type === "mousemove") { // thought of adding scroll in order to move the helper, but mouse poisition is n/a 35739 var d = $(document), t = d.scrollTop(), l = d.scrollLeft(); 35740 if(e.pageY - t < 20) { 35741 if(sti && dir1 === "down") { clearInterval(sti); sti = false; } 35742 if(!sti) { dir1 = "up"; sti = setInterval(function () { $(document).scrollTop($(document).scrollTop() - $.vakata.dnd.scroll_spd); }, 150); } 35743 } 35744 else { 35745 if(sti && dir1 === "up") { clearInterval(sti); sti = false; } 35746 } 35747 if($(window).height() - (e.pageY - t) < 20) { 35748 if(sti && dir1 === "up") { clearInterval(sti); sti = false; } 35749 if(!sti) { dir1 = "down"; sti = setInterval(function () { $(document).scrollTop($(document).scrollTop() + $.vakata.dnd.scroll_spd); }, 150); } 35750 } 35751 else { 35752 if(sti && dir1 === "down") { clearInterval(sti); sti = false; } 35753 } 35754 35755 if(e.pageX - l < 20) { 35756 if(sli && dir2 === "right") { clearInterval(sli); sli = false; } 35757 if(!sli) { dir2 = "left"; sli = setInterval(function () { $(document).scrollLeft($(document).scrollLeft() - $.vakata.dnd.scroll_spd); }, 150); } 35758 } 35759 else { 35760 if(sli && dir2 === "left") { clearInterval(sli); sli = false; } 35761 } 35762 if($(window).width() - (e.pageX - l) < 20) { 35763 if(sli && dir2 === "left") { clearInterval(sli); sli = false; } 35764 if(!sli) { dir2 = "right"; sli = setInterval(function () { $(document).scrollLeft($(document).scrollLeft() + $.vakata.dnd.scroll_spd); }, 150); } 35765 } 35766 else { 35767 if(sli && dir2 === "right") { clearInterval(sli); sli = false; } 35768 } 35769 } 35770 35771 $.vakata.dnd.helper.css({ left : (e.pageX + $.vakata.dnd.helper_left) + "px", top : (e.pageY + $.vakata.dnd.helper_top) + "px" }); 35772 $(document).triggerHandler("drag.vakata", { "event" : e, "data" : $.vakata.dnd.user_data }); 35773 }, 35774 drag_stop : function (e) { 35775 if(sli) { clearInterval(sli); } 35776 if(sti) { clearInterval(sti); } 35777 $(document).unbind("mousemove", $.vakata.dnd.drag); 35778 $(document).unbind("mouseup", $.vakata.dnd.drag_stop); 35779 $(document).triggerHandler("drag_stop.vakata", { "event" : e, "data" : $.vakata.dnd.user_data }); 35780 $.vakata.dnd.helper.remove(); 35781 $.vakata.dnd.init_x = 0; 35782 $.vakata.dnd.init_y = 0; 35783 $.vakata.dnd.user_data = {}; 35784 $.vakata.dnd.is_down = false; 35785 $.vakata.dnd.is_drag = false; 35786 } 35787 }; 35788 $(function() { 35789 var css_string = '#vakata-dragged { display:block; margin:0 0 0 0; padding:4px 4px 4px 24px; position:absolute; top:-2000px; line-height:16px; z-index:10000; } '; 35790 $.vakata.css.add_sheet({ str : css_string, title : "vakata" });
35791 }); 35792 35793 $.jstree.plugin("dnd", { 35794 __init : function () { 35795 this.data.dnd = { 35796 active : false, 35797 after : false, 35798 inside : false, 35799 before : false, 35800 off : false, 35801 prepared : false, 35802 w : 0, 35803 to1 : false, 35804 to2 : false, 35805 cof : false, 35806 cw : false, 35807 ch : false, 35808 i1 : false, 35809 i2 : false, 35810 mto : false 35811 }; 35812 this.get_container() 35813 .bind("mouseenter.jstree", $.proxy(function (e) { 35814 if($.vakata.dnd.is_drag && $.vakata.dnd.user_data.jstree) { 35815 if(this.data.themes) { 35816 m.attr("class", "jstree-" + this.data.themes.theme); 35817 if(ml) { ml.attr("class", "jstree-" + this.data.themes.theme); } 35818 $.vakata.dnd.helper.attr("class", "jstree-dnd-helper jstree-" + this.data.themes.theme); 35819 } 35820 //if($(e.currentTarget).find("> ul > li").length === 0) { 35821 if(e.currentTarget === e.target && $.vakata.dnd.user_data.obj && $($.vakata.dnd.user_data.obj).length && $($.vakata.dnd.user_data.obj).parents(".jstree:eq(0)")[0] !== e.target) { // node should not be from the same tree 35822 var tr = $.jstree._reference(e.target), dc; 35823 if(tr.data.dnd.foreign) { 35824 dc = tr._get_settings().dnd.drag_check.call(this, { "o" : o, "r" : tr.get_container(), is_root : true }); 35825 if(dc === true || dc.inside === true || dc.before === true || dc.after === true) { 35826 $.vakata.dnd.helper.children("ins").attr("class","jstree-ok"); 35827 } 35828 } 35829 else { 35830 tr.prepare_move(o, tr.get_container(), "last"); 35831 if(tr.check_move()) { 35832 $.vakata.dnd.helper.children("ins").attr("class","jstree-ok"); 35833 } 35834 } 35835 } 35836 } 35837 }, this)) 35838 .bind("mouseup.jstree", $.proxy(function (e) { 35839 //if($.vakata.dnd.is_drag && $.vakata.dnd.user_data.jstree && $(e.currentTarget).find("> ul > li").length === 0) { 35840 if($.vakata.dnd.is_drag && $.vakata.dnd.user_data.jstree && e.currentTarget === e.target && $.vakata.dnd.user_data.obj && $($.vakata.dnd.user_data.obj).length && $($.vakata.dnd.user_data.obj).parents(".jstree:eq(0)")[0] !== e.target) { // node should not be from the same tree 35841 var tr = $.jstree._reference(e.currentTarget), dc; 35842 if(tr.data.dnd.foreign) { 35843 dc = tr._get_settings().dnd.drag_check.call(this, { "o" : o, "r" : tr.get_container(), is_root : true }); 35844 if(dc === true || dc.inside === true || dc.before === true || dc.after === true) { 35845 tr._get_settings().dnd.drag_finish.call(this, { "o" : o, "r" : tr.get_container(), is_root : true }); 35846 } 35847 } 35848 else { 35849 tr.move_node(o, tr.get_container(), "last", e[tr._get_settings().dnd.copy_modifier + "Key"]); 35850 } 35851 } 35852 }, this)) 35853 .bind("mouseleave.jstree", $.proxy(function (e) { 35854 if(e.relatedTarget && e.relatedTarget.id && e.relatedTarget.id === "jstree-marker-line") { 35855 return false; 35856 } 35857 if($.vakata.dnd.is_drag && $.vakata.dnd.user_data.jstree) { 35858 if(this.data.dnd.i1) { clearInterval(this.data.dnd.i1); } 35859 if(this.data.dnd.i2) { clearInterval(this.data.dnd.i2); } 35860 if(this.data.dnd.to1) { clearTimeout(this.data.dnd.to1); } 35861 if(this.data.dnd.to2) { clearTimeout(this.data.dnd.to2); } 35862 if($.vakata.dnd.helper.children("ins").hasClass("jstree-ok")) { 35863 $.vakata.dnd.helper.children("ins").attr("class","jstree-invalid"); 35864 } 35865 } 35866 }, this)) 35867 .bind("mousemove.jstree", $.proxy(function (e) { 35868 if($.vakata.dnd.is_drag && $.vakata.dnd.user_data.jstree) { 35869 var cnt = this.get_container()[0]; 35870 35871 // Horizontal scroll 35872 if(e.pageX + 24 > this.data.dnd.cof.left + this.data.dnd.cw) { 35873 if(this.data.dnd.i1) { clearInterval(this.data.dnd.i1); } 35874 this.data.dnd.i1 = setInterval($.proxy(function () { this.scrollLeft += $.vakata.dnd.scroll_spd; }, cnt), 100); 35875 } 35876 else if(e.pageX - 24 < this.data.dnd.cof.left) { 35877 if(this.data.dnd.i1) { clearInterval(this.data.dnd.i1); } 35878 this.data.dnd.i1 = setInterval($.proxy(function () { this.scrollLeft -= $.vakata.dnd.scroll_spd; }, cnt), 100); 35879 } 35880 else { 35881 if(this.data.dnd.i1) { clearInterval(this.data.dnd.i1); } 35882 } 35883 35884 // Vertical scroll 35885 if(e.pageY + 24 > this.data.dnd.cof.top + this.data.dnd.ch) { 35886 if(this.data.dnd.i2) { clearInterval(this.data.dnd.i2); } 35887 this.data.dnd.i2 = setInterval($.proxy(function () { this.scrollTop += $.vakata.dnd.scroll_spd; }, cnt), 100); 35888 } 35889 else if(e.pageY - 24 < this.data.dnd.cof.top) { 35890 if(this.data.dnd.i2) { clearInterval(this.data.dnd.i2); } 35891 this.data.dnd.i2 = setInterval($.proxy(function () { this.scrollTop -= $.vakata.dnd.scroll_spd; }, cnt), 100); 35892 } 35893 else { 35894 if(this.data.dnd.i2) { clearInterval(this.data.dnd.i2); } 35895 } 35896 35897 } 35898 }, this)) 35899 .bind("scroll.jstree", $.proxy(function (e) { 35900 if($.vakata.dnd.is_drag && $.vakata.dnd.user_data.jstree && m && ml) { 35901 m.hide(); 35902 ml.hide(); 35903 } 35904 }, this)) 35905 .delegate("a", "mousedown.jstree", $.proxy(function (e) { 35906 if(e.which === 1) { 35907 this.start_drag(e.currentTarget, e); 35908 return false; 35909 } 35910 }, this)) 35911 .delegate("a", "mouseenter.jstree", $.proxy(function (e) { 35912 if($.vakata.dnd.is_drag && $.vakata.dnd.user_data.jstree) { 35913 this.dnd_enter(e.currentTarget); 35914 } 35915 }, this)) 35916 .delegate("a", "mousemove.jstree", $.proxy(function (e) { 35917 if($.vakata.dnd.is_drag && $.vakata.dnd.user_data.jstree) { 35918 if(!r || !r.length || r.children("a")[0] !== e.currentTarget) { 35919 this.dnd_enter(e.currentTarget); 35920 } 35921 if(typeof this.data.dnd.off.top === "undefined") { this.data.dnd.off = $(e.target).offset(); } 35922 this.data.dnd.w = (e.pageY - (this.data.dnd.off.top || 0)) % this.data.core.li_height; 35923 if(this.data.dnd.w < 0) { this.data.dnd.w += this.data.core.li_height; } 35924 this.dnd_show(); 35925 } 35926 }, this)) 35927 .delegate("a", "mouseleave.jstree", $.proxy(function (e) { 35928 if($.vakata.dnd.is_drag && $.vakata.dnd.user_data.jstree) { 35929 if(e.relatedTarget && e.relatedTarget.id && e.relatedTarget.id === "jstree-marker-line") { 35930 return false; 35931 } 35932 if(m) { m.hide(); } 35933 if(ml) { ml.hide(); } 35934 /* 35935 var ec = $(e.currentTarget).closest("li"), 35936 er = $(e.relatedTarget).closest("li"); 35937 if(er[0] !== ec.prev()[0] && er[0] !== ec.next()[0]) { 35938 if(m) { m.hide(); } 35939 if(ml) { ml.hide(); } 35940 } 35941 */ 35942 this.data.dnd.mto = setTimeout( 35943 (function (t) { return function () { t.dnd_leave(e); }; })(this), 35944 0); 35945 } 35946 }, this)) 35947 .delegate("a", "mouseup.jstree", $.proxy(function (e) { 35948 if($.vakata.dnd.is_drag && $.vakata.dnd.user_data.jstree) { 35949 this.dnd_finish(e); 35950 } 35951 }, this)); 35952 35953 $(document) 35954 .bind("drag_stop.vakata", $.proxy(function () { 35955 if(this.data.dnd.to1) { clearTimeout(this.data.dnd.to1); } 35956 if(this.data.dnd.to2) { clearTimeout(this.data.dnd.to2); } 35957 if(this.data.dnd.i1) { clearInterval(this.data.dnd.i1); } 35958 if(this.data.dnd.i2) { clearInterval(this.data.dnd.i2); } 35959 this.data.dnd.after = false; 35960 this.data.dnd.before = false; 35961 this.data.dnd.inside = false; 35962 this.data.dnd.off = false; 35963 this.data.dnd.prepared = false; 35964 this.data.dnd.w = false; 35965 this.data.dnd.to1 = false; 35966 this.data.dnd.to2 = false; 35967 this.data.dnd.i1 = false; 35968 this.data.dnd.i2 = false; 35969 this.data.dnd.active = false; 35970 this.data.dnd.foreign = false; 35971 if(m) { m.css({ "top" : "-2000px" }); } 35972 if(ml) { ml.css({ "top" : "-2000px" }); } 35973 }, this)) 35974 .bind("drag_start.vakata", $.proxy(function (e, data) { 35975 if(data.data.jstree) { 35976 var et = $(data.event.target);
35977 if(et.closest(".jstree").hasClass("jstree-" + this.get_index())) { 35978 this.dnd_enter(et); 35979 } 35980 } 35981 }, this)); 35982 /* 35983 .bind("keydown.jstree-" + this.get_index() + " keyup.jstree-" + this.get_index(), $.proxy(function(e) { 35984 if($.vakata.dnd.is_drag && $.vakata.dnd.user_data.jstree && !this.data.dnd.foreign) { 35985 var h = $.vakata.dnd.helper.children("ins"); 35986 if(e[this._get_settings().dnd.copy_modifier + "Key"] && h.hasClass("jstree-ok")) { 35987 h.parent().html(h.parent().html().replace(/ \(Copy\)$/, "") + " (Copy)"); 35988 } 35989 else { 35990 h.parent().html(h.parent().html().replace(/ \(Copy\)$/, "")); 35991 } 35992 } 35993 }, this)); */ 35994 35995 35996 35997 var s = this._get_settings().dnd; 35998 if(s.drag_target) { 35999 $(document) 36000 .delegate(s.drag_target, "mousedown.jstree-" + this.get_index(), $.proxy(function (e) { 36001 o = e.target; 36002 $.vakata.dnd.drag_start(e, { jstree : true, obj : e.target }, "<ins class='jstree-icon'></ins>" + $(e.target).text() ); 36003 if(this.data.themes) { 36004 if(m) { m.attr("class", "jstree-" + this.data.themes.theme); } 36005 if(ml) { ml.attr("class", "jstree-" + this.data.themes.theme); } 36006 $.vakata.dnd.helper.attr("class", "jstree-dnd-helper jstree-" + this.data.themes.theme); 36007 } 36008 $.vakata.dnd.helper.children("ins").attr("class","jstree-invalid"); 36009 var cnt = this.get_container(); 36010 this.data.dnd.cof = cnt.offset(); 36011 this.data.dnd.cw = parseInt(cnt.width(),10); 36012 this.data.dnd.ch = parseInt(cnt.height(),10); 36013 this.data.dnd.foreign = true; 36014 e.preventDefault(); 36015 }, this)); 36016 } 36017 if(s.drop_target) { 36018 $(document) 36019 .delegate(s.drop_target, "mouseenter.jstree-" + this.get_index(), $.proxy(function (e) { 36020 if(this.data.dnd.active && this._get_settings().dnd.drop_check.call(this, { "o" : o, "r" : $(e.target), "e" : e })) { 36021 $.vakata.dnd.helper.children("ins").attr("class","jstree-ok"); 36022 } 36023 }, this)) 36024 .delegate(s.drop_target, "mouseleave.jstree-" + this.get_index(), $.proxy(function (e) { 36025 if(this.data.dnd.active) { 36026 $.vakata.dnd.helper.children("ins").attr("class","jstree-invalid"); 36027 } 36028 }, this)) 36029 .delegate(s.drop_target, "mouseup.jstree-" + this.get_index(), $.proxy(function (e) { 36030 if(this.data.dnd.active && $.vakata.dnd.helper.children("ins").hasClass("jstree-ok")) { 36031 this._get_settings().dnd.drop_finish.call(this, { "o" : o, "r" : $(e.target), "e" : e }); 36032 } 36033 }, this)); 36034 } 36035 }, 36036 defaults : { 36037 copy_modifier : "ctrl", 36038 check_timeout : 100, 36039 open_timeout : 500, 36040 drop_target : ".jstree-drop", 36041 drop_check : function (data) { return true; }, 36042 drop_finish : $.noop, 36043 drag_target : ".jstree-draggable", 36044 drag_finish : $.noop, 36045 drag_check : function (data) { return { after : false, before : false, inside : true }; } 36046 }, 36047 _fn : { 36048 dnd_prepare : function () { 36049 if(!r || !r.length) { return; } 36050 this.data.dnd.off = r.offset(); 36051 if(this._get_settings().core.rtl) { 36052 this.data.dnd.off.right = this.data.dnd.off.left + r.width(); 36053 } 36054 if(this.data.dnd.foreign) { 36055 var a = this._get_settings().dnd.drag_check.call(this, { "o" : o, "r" : r }); 36056 this.data.dnd.after = a.after; 36057 this.data.dnd.before = a.before; 36058 this.data.dnd.inside = a.inside; 36059 this.data.dnd.prepared = true; 36060 return this.dnd_show(); 36061 } 36062 this.prepare_move(o, r, "before"); 36063 this.data.dnd.before = this.check_move(); 36064 this.prepare_move(o, r, "after"); 36065 this.data.dnd.after = this.check_move(); 36066 if(this._is_loaded(r)) { 36067 this.prepare_move(o, r, "inside"); 36068 this.data.dnd.inside = this.check_move(); 36069 } 36070 else { 36071 this.data.dnd.inside = false; 36072 } 36073 this.data.dnd.prepared = true; 36074 return this.dnd_show(); 36075 }, 36076 dnd_show : function () { 36077 if(!this.data.dnd.prepared) { return; } 36078 var o = ["before","inside","after"], 36079 r = false, 36080 rtl = this._get_settings().core.rtl, 36081 pos; 36082 if(this.data.dnd.w < this.data.core.li_height/3) { o = ["before","inside","after"]; } 36083 else if(this.data.dnd.w <= this.data.core.li_height*2/3) { 36084 o = this.data.dnd.w < this.data.core.li_height/2 ? ["inside","before","after"] : ["inside","after","before"]; 36085 } 36086 else { o = ["after","inside","before"]; } 36087 $.each(o, $.proxy(function (i, val) { 36088 if(this.data.dnd[val]) { 36089 $.vakata.dnd.helper.children("ins").attr("class","jstree-ok"); 36090 r = val; 36091 return false; 36092 } 36093 }, this)); 36094 if(r === false) { $.vakata.dnd.helper.children("ins").attr("class","jstree-invalid"); } 36095 36096 pos = rtl ? (this.data.dnd.off.right - 18) : (this.data.dnd.off.left + 10); 36097 switch(r) { 36098 case "before": 36099 m.css({ "left" : pos + "px", "top" : (this.data.dnd.off.top - 6) + "px" }).show(); 36100 if(ml) { ml.css({ "left" : (pos + 8) + "px", "top" : (this.data.dnd.off.top - 1) + "px" }).show(); } 36101 break; 36102 case "after": 36103 m.css({ "left" : pos + "px", "top" : (this.data.dnd.off.top + this.data.core.li_height - 6) + "px" }).show(); 36104 if(ml) { ml.css({ "left" : (pos + 8) + "px", "top" : (this.data.dnd.off.top + this.data.core.li_height - 1) + "px" }).show(); } 36105 break; 36106 case "inside": 36107 m.css({ "left" : pos + ( rtl ? -4 : 4) + "px", "top" : (this.data.dnd.off.top + this.data.core.li_height/2 - 5) + "px" }).show(); 36108 if(ml) { ml.hide(); } 36109 break; 36110 default: 36111 m.hide(); 36112 if(ml) { ml.hide(); } 36113 break; 36114 } 36115 last_pos = r; 36116 return r; 36117 }, 36118 dnd_open : function () { 36119 this.data.dnd.to2 = false; 36120 this.open_node(r, $.proxy(this.dnd_prepare,this), true); 36121 }, 36122 dnd_finish : function (e) { 36123 if(this.data.dnd.foreign) { 36124 if(this.data.dnd.after || this.data.dnd.before || this.data.dnd.inside) { 36125 this._get_settings().dnd.drag_finish.call(this, { "o" : o, "r" : r, "p" : last_pos }); 36126 } 36127 } 36128 else { 36129 this.dnd_prepare(); 36130 this.move_node(o, r, last_pos, e[this._get_settings().dnd.copy_modifier + "Key"]);
36131 } 36132 o = false; 36133 r = false; 36134 m.hide(); 36135 if(ml) { ml.hide(); } 36136 }, 36137 dnd_enter : function (obj) { 36138 if(this.data.dnd.mto) { 36139 clearTimeout(this.data.dnd.mto); 36140 this.data.dnd.mto = false; 36141 } 36142 var s = this._get_settings().dnd; 36143 this.data.dnd.prepared = false; 36144 r = this._get_node(obj); 36145 if(s.check_timeout) { 36146 // do the calculations after a minimal timeout (users tend to drag quickly to the desired location) 36147 if(this.data.dnd.to1) { clearTimeout(this.data.dnd.to1); } 36148 this.data.dnd.to1 = setTimeout($.proxy(this.dnd_prepare, this), s.check_timeout); 36149 } 36150 else { 36151 this.dnd_prepare(); 36152 } 36153 if(s.open_timeout) { 36154 if(this.data.dnd.to2) { clearTimeout(this.data.dnd.to2); } 36155 if(r && r.length && r.hasClass("jstree-closed")) { 36156 // if the node is closed - open it, then recalculate 36157 this.data.dnd.to2 = setTimeout($.proxy(this.dnd_open, this), s.open_timeout); 36158 } 36159 } 36160 else { 36161 if(r && r.length && r.hasClass("jstree-closed")) { 36162 this.dnd_open(); 36163 } 36164 } 36165 }, 36166 dnd_leave : function (e) { 36167 this.data.dnd.after = false; 36168 this.data.dnd.before = false; 36169 this.data.dnd.inside = false; 36170 $.vakata.dnd.helper.children("ins").attr("class","jstree-invalid"); 36171 m.hide(); 36172 if(ml) { ml.hide(); } 36173 if(r && r[0] === e.target.parentNode) { 36174 if(this.data.dnd.to1) { 36175 clearTimeout(this.data.dnd.to1); 36176 this.data.dnd.to1 = false; 36177 } 36178 if(this.data.dnd.to2) { 36179 clearTimeout(this.data.dnd.to2); 36180 this.data.dnd.to2 = false; 36181 } 36182 } 36183 }, 36184 start_drag : function (obj, e) { 36185 o = this._get_node(obj); 36186 if(this.data.ui && this.is_selected(o)) { o = this._get_node(null, true); } 36187 var dt = o.length > 1 ? this._get_string("multiple_selection") : this.get_text(o), 36188 cnt = this.get_container(); 36189 if(!this._get_settings().core.html_titles) { dt = dt.replace(/</ig,"<").replace(/>/ig,">"); } 36190 $.vakata.dnd.drag_start(e, { jstree : true, obj : o }, "<ins class='jstree-icon'></ins>" + dt ); 36191 if(this.data.themes) { 36192 if(m) { m.attr("class", "jstree-" + this.data.themes.theme); } 36193 if(ml) { ml.attr("class", "jstree-" + this.data.themes.theme); } 36194 $.vakata.dnd.helper.attr("class", "jstree-dnd-helper jstree-" + this.data.themes.theme); 36195 } 36196 this.data.dnd.cof = cnt.offset(); 36197 this.data.dnd.cw = parseInt(cnt.width(),10); 36198 this.data.dnd.ch = parseInt(cnt.height(),10); 36199 this.data.dnd.active = true; 36200 } 36201 } 36202 }); 36203 $(function() { 36204 var css_string = '' + 36205 '#vakata-dragged ins { display:block; text-decoration:none; width:16px; height:16px; margin:0 0 0 0; padding:0; position:absolute; top:4px; left:4px; ' + 36206 ' -moz-border-radius:4px; border-radius:4px; -webkit-border-radius:4px; ' + 36207 '} ' + 36208 '#vakata-dragged .jstree-ok { background:green; } ' + 36209 '#vakata-dragged .jstree-invalid { background:red; } ' + 36210 '#jstree-marker { padding:0; margin:0; font-size:12px; overflow:hidden; height:12px; width:8px; position:absolute; top:-30px; z-index:10001; background-repeat:no-repeat; display:none; background-color:transparent; text-shadow:1px 1px 1px white; color:black; line-height:10px; } ' + 36211 '#jstree-marker-line { padding:0; margin:0; line-height:0%; font-size:1px; overflow:hidden; height:1px; width:100px; position:absolute; top:-30px; z-index:10000; background-repeat:no-repeat; display:none; background-color:#456c43; ' + 36212 ' cursor:pointer; border:1px solid #eeeeee; border-left:0; -moz-box-shadow: 0px 0px 2px #666; -webkit-box-shadow: 0px 0px 2px #666; box-shadow: 0px 0px 2px #666; ' + 36213 ' -moz-border-radius:1px; border-radius:1px; -webkit-border-radius:1px; ' + 36214 '}' + 36215 ''; 36216 $.vakata.css.add_sheet({ str : css_string, title : "jstree" }); 36217 m = $("<div />").attr({ id : "jstree-marker" }).hide().html("»") 36218 .bind("mouseleave mouseenter", function (e) { 36219 m.hide(); 36220 ml.hide(); 36221 e.preventDefault(); 36222 e.stopImmediatePropagation(); 36223 return false; 36224 }) 36225 .appendTo("body"); 36226 ml = $("<div />").attr({ id : "jstree-marker-line" }).hide() 36227 .bind("mouseup", function (e) { 36228 if(r && r.length) { 36229 r.children("a").trigger(e); 36230 e.preventDefault(); 36231 e.stopImmediatePropagation(); 36232 return false; 36233 } 36234 }) 36235 .bind("mouseleave", function (e) { 36236 var rt = $(e.relatedTarget);
36237 if(rt.is(".jstree") || rt.closest(".jstree").length === 0) { 36238 if(r && r.length) { 36239 r.children("a").trigger(e); 36240 m.hide(); 36241 ml.hide(); 36242 e.preventDefault(); 36243 e.stopImmediatePropagation(); 36244 return false; 36245 } 36246 } 36247 }) 36248 .appendTo("body"); 36249 $(document).bind("drag_start.vakata", function (e, data) { 36250 if(data.data.jstree) { m.show(); if(ml) { ml.show(); } } 36251 }); 36252 $(document).bind("drag_stop.vakata", function (e, data) { 36253 if(data.data.jstree) { m.hide(); if(ml) { ml.hide(); } } 36254 }); 36255 }); 36256})(jQuery); 36257//*/ 36258 36259/* 36260 * jsTree checkbox plugin 36261 * Inserts checkboxes in front of every node 36262 * Depends on the ui plugin 36263 * DOES NOT WORK NICELY WITH MULTITREE DRAG'N'DROP 36264 */ 36265(function ($) { 36266 $.jstree.plugin("checkbox", { 36267 __init : function () { 36268 this.data.checkbox.noui = this._get_settings().checkbox.override_ui; 36269 if(this.data.ui && this.data.checkbox.noui) { 36270 this.select_node = this.deselect_node = this.deselect_all = $.noop; 36271 this.get_selected = this.get_checked; 36272 } 36273 36274 this.get_container() 36275 .bind("open_node.jstree create_node.jstree clean_node.jstree refresh.jstree", $.proxy(function (e, data) { 36276 this._prepare_checkboxes(data.rslt.obj); 36277 }, this)) 36278 .bind("loaded.jstree", $.proxy(function (e) { 36279 this._prepare_checkboxes(); 36280 }, this)) 36281 .delegate( (this.data.ui && this.data.checkbox.noui ? "a" : "ins.jstree-checkbox") , "click.jstree", $.proxy(function (e) { 36282 e.preventDefault(); 36283 if(this._get_node(e.target).hasClass("jstree-checked")) { this.uncheck_node(e.target); } 36284 else { this.check_node(e.target); } 36285 if(this.data.ui && this.data.checkbox.noui) { 36286 this.save_selected(); 36287 if(this.data.cookies) { this.save_cookie("select_node"); } 36288 } 36289 else { 36290 e.stopImmediatePropagation(); 36291 return false; 36292 } 36293 }, this)); 36294 }, 36295 defaults : { 36296 override_ui : false, 36297 two_state : false, 36298 real_checkboxes : false, 36299 checked_parent_open : true, 36300 real_checkboxes_names : function (n) { return [ ("check_" + (n[0].id || Math.ceil(Math.random() * 10000))) , 1]; } 36301 }, 36302 __destroy : function () { 36303 this.get_container() 36304 .find("input.jstree-real-checkbox").removeClass("jstree-real-checkbox").end() 36305 .find("ins.jstree-checkbox").remove(); 36306 }, 36307 _fn : { 36308 _checkbox_notify : function (n, data) { 36309 if(data.checked) { 36310 this.check_node(n, false); 36311 } 36312 }, 36313 _prepare_checkboxes : function (obj) { 36314 obj = !obj || obj == -1 ? this.get_container().find("> ul > li") : this._get_node(obj); 36315 if(obj === false) { return; } // added for removing root nodes 36316 var c, _this = this, t, ts = this._get_settings().checkbox.two_state, rc = this._get_settings().checkbox.real_checkboxes, rcn = this._get_settings().checkbox.real_checkboxes_names; 36317 obj.each(function () { 36318 t = $(this); 36319 c = t.is("li") && (t.hasClass("jstree-checked") || (rc && t.children(":checked").length)) ? "jstree-checked" : "jstree-unchecked"; 36320 t.find("li").andSelf().each(function () { 36321 var $t = $(this), nm; 36322 $t.children("a" + (_this.data.languages ? "" : ":eq(0)") ).not(":has(.jstree-checkbox)").prepend("<ins class='jstree-checkbox'> </ins>").parent().not(".jstree-checked, .jstree-unchecked").addClass( ts ? "jstree-unchecked" : c ); 36323 if(rc) { 36324 if(!$t.children(":checkbox").length) { 36325 nm = rcn.call(_this, $t); 36326 $t.prepend("<input type='checkbox' class='jstree-real-checkbox' id='" + nm[0] + "' name='" + nm[0] + "' value='" + nm[1] + "' />"); 36327 } 36328 else { 36329 $t.children(":checkbox").addClass("jstree-real-checkbox"); 36330 } 36331 } 36332 if(!ts) { 36333 if(c === "jstree-checked" || $t.hasClass("jstree-checked") || $t.children(':checked').length) { 36334 $t.find("li").andSelf().addClass("jstree-checked").children(":checkbox").prop("checked", true); 36335 } 36336 } 36337 else { 36338 if($t.hasClass("jstree-checked") || $t.children(':checked').length) { 36339 $t.addClass("jstree-checked").children(":checkbox").prop("checked", true); 36340 } 36341 } 36342 });
36343 }); 36344 if(!ts) { 36345 obj.find(".jstree-checked").parent().parent().each(function () { _this._repair_state(this); }); 36346 } 36347 }, 36348 change_state : function (obj, state) { 36349 obj = this._get_node(obj); 36350 var coll = false, rc = this._get_settings().checkbox.real_checkboxes; 36351 if(!obj || obj === -1) { return false; } 36352 state = (state === false || state === true) ? state : obj.hasClass("jstree-checked"); 36353 if(this._get_settings().checkbox.two_state) { 36354 if(state) { 36355 obj.removeClass("jstree-checked").addClass("jstree-unchecked"); 36356 if(rc) { obj.children(":checkbox").prop("checked", false); } 36357 } 36358 else { 36359 obj.removeClass("jstree-unchecked").addClass("jstree-checked"); 36360 if(rc) { obj.children(":checkbox").prop("checked", true); } 36361 } 36362 } 36363 else { 36364 if(state) { 36365 coll = obj.find("li").andSelf(); 36366 if(!coll.filter(".jstree-checked, .jstree-undetermined").length) { return false; } 36367 coll.removeClass("jstree-checked jstree-undetermined").addClass("jstree-unchecked"); 36368 if(rc) { coll.children(":checkbox").prop("checked", false); } 36369 } 36370 else { 36371 coll = obj.find("li").andSelf(); 36372 if(!coll.filter(".jstree-unchecked, .jstree-undetermined").length) { return false; } 36373 coll.removeClass("jstree-unchecked jstree-undetermined").addClass("jstree-checked"); 36374 if(rc) { coll.children(":checkbox").prop("checked", true); } 36375 if(this.data.ui) { this.data.ui.last_selected = obj; } 36376 this.data.checkbox.last_selected = obj; 36377 } 36378 obj.parentsUntil(".jstree", "li").each(function () { 36379 var $this = $(this); 36380 if(state) { 36381 if($this.children("ul").children("li.jstree-checked, li.jstree-undetermined").length) { 36382 $this.parentsUntil(".jstree", "li").andSelf().removeClass("jstree-checked jstree-unchecked").addClass("jstree-undetermined"); 36383 if(rc) { $this.parentsUntil(".jstree", "li").andSelf().children(":checkbox").prop("checked", false); } 36384 return false; 36385 } 36386 else { 36387 $this.removeClass("jstree-checked jstree-undetermined").addClass("jstree-unchecked"); 36388 if(rc) { $this.children(":checkbox").prop("checked", false); } 36389 } 36390 } 36391 else { 36392 if($this.children("ul").children("li.jstree-unchecked, li.jstree-undetermined").length) { 36393 $this.parentsUntil(".jstree", "li").andSelf().removeClass("jstree-checked jstree-unchecked").addClass("jstree-undetermined"); 36394 if(rc) { $this.parentsUntil(".jstree", "li").andSelf().children(":checkbox").prop("checked", false); } 36395 return false; 36396 } 36397 else { 36398 $this.removeClass("jstree-unchecked jstree-undetermined").addClass("jstree-checked"); 36399 if(rc) { $this.children(":checkbox").prop("checked", true); } 36400 } 36401 } 36402 }); 36403 } 36404 if(this.data.ui && this.data.checkbox.noui) { this.data.ui.selected = this.get_checked(); } 36405 this.__callback(obj); 36406 return true; 36407 }, 36408 check_node : function (obj) { 36409 if(this.change_state(obj, false)) { 36410 obj = this._get_node(obj); 36411 if(this._get_settings().checkbox.checked_parent_open) { 36412 var t = this; 36413 obj.parents(".jstree-closed").each(function () { t.open_node(this, false, true); }); 36414 } 36415 this.__callback({ "obj" : obj }); 36416 } 36417 }, 36418 uncheck_node : function (obj) { 36419 if(this.change_state(obj, true)) { this.__callback({ "obj" : this._get_node(obj) }); } 36420 }, 36421 check_all : function () { 36422 var _this = this, 36423 coll = this._get_settings().checkbox.two_state ? this.get_container_ul().find("li") : this.get_container_ul().children("li"); 36424 coll.each(function () { 36425 _this.change_state(this, false); 36426 }); 36427 this.__callback(); 36428 }, 36429 uncheck_all : function () { 36430 var _this = this, 36431 coll = this._get_settings().checkbox.two_state ? this.get_container_ul().find("li") : this.get_container_ul().children("li"); 36432 coll.each(function () { 36433 _this.change_state(this, true); 36434 }); 36435 this.__callback(); 36436 }, 36437 36438 is_checked : function(obj) { 36439 obj = this._get_node(obj); 36440 return obj.length ? obj.is(".jstree-checked") : false; 36441 }, 36442 get_checked : function (obj, get_all) { 36443 obj = !obj || obj === -1 ? this.get_container() : this._get_node(obj);
36444 return get_all || this._get_settings().checkbox.two_state ? obj.find(".jstree-checked") : obj.find("> ul > .jstree-checked, .jstree-undetermined > ul > .jstree-checked"); 36445 }, 36446 get_unchecked : function (obj, get_all) { 36447 obj = !obj || obj === -1 ? this.get_container() : this._get_node(obj); 36448 return get_all || this._get_settings().checkbox.two_state ? obj.find(".jstree-unchecked") : obj.find("> ul > .jstree-unchecked, .jstree-undetermined > ul > .jstree-unchecked"); 36449 }, 36450 36451 show_checkboxes : function () { this.get_container().children("ul").removeClass("jstree-no-checkboxes"); }, 36452 hide_checkboxes : function () { this.get_container().children("ul").addClass("jstree-no-checkboxes"); }, 36453 36454 _repair_state : function (obj) { 36455 obj = this._get_node(obj); 36456 if(!obj.length) { return; } 36457 if(this._get_settings().checkbox.two_state) { 36458 obj.find('li').andSelf().not('.jstree-checked').removeClass('jstree-undetermined').addClass('jstree-unchecked').children(':checkbox').prop('checked', true); 36459 return; 36460 } 36461 var rc = this._get_settings().checkbox.real_checkboxes, 36462 a = obj.find("> ul > .jstree-checked").length, 36463 b = obj.find("> ul > .jstree-undetermined").length, 36464 c = obj.find("> ul > li").length; 36465 if(c === 0) { if(obj.hasClass("jstree-undetermined")) { this.change_state(obj, false); } } 36466 else if(a === 0 && b === 0) { this.change_state(obj, true); } 36467 else if(a === c) { this.change_state(obj, false); } 36468 else { 36469 obj.parentsUntil(".jstree","li").andSelf().removeClass("jstree-checked jstree-unchecked").addClass("jstree-undetermined"); 36470 if(rc) { obj.parentsUntil(".jstree", "li").andSelf().children(":checkbox").prop("checked", false); } 36471 } 36472 }, 36473 reselect : function () { 36474 if(this.data.ui && this.data.checkbox.noui) { 36475 var _this = this, 36476 s = this.data.ui.to_select; 36477 s = $.map($.makeArray(s), function (n) { return "#" + n.toString().replace(/^#/,"").replace(/\\\//g,"/").replace(/\//g,"\\\/").replace(/\\\./g,".").replace(/\./g,"\\.").replace(/\:/g,"\\:"); }); 36478 this.deselect_all(); 36479 $.each(s, function (i, val) { _this.check_node(val); }); 36480 this.__callback(); 36481 } 36482 else { 36483 this.__call_old(); 36484 } 36485 }, 36486 save_loaded : function () { 36487 var _this = this; 36488 this.data.core.to_load = []; 36489 this.get_container_ul().find("li.jstree-closed.jstree-undetermined").each(function () { 36490 if(this.id) { _this.data.core.to_load.push("#" + this.id); } 36491 }); 36492 } 36493 } 36494 }); 36495 $(function() { 36496 var css_string = '.jstree .jstree-real-checkbox { display:none; } '; 36497 $.vakata.css.add_sheet({ str : css_string, title : "jstree" }); 36498 }); 36499})(jQuery); 36500//*/ 36501 36502/* 36503 * jsTree XML plugin 36504 * The XML data store. Datastores are build by overriding the `load_node` and `_is_loaded` functions. 36505 */ 36506(function ($) { 36507 $.vakata.xslt = function (xml, xsl, callback) { 36508 var rs = "", xm, xs, processor, support; 36509 // TODO: IE9 no XSLTProcessor, no document.recalc 36510 if(document.recalc) { 36511 xm = document.createElement('xml'); 36512 xs = document.createElement('xml'); 36513 xm.innerHTML = xml; 36514 xs.innerHTML = xsl; 36515 $("body").append(xm).append(xs); 36516 setTimeout( (function (xm, xs, callback) { 36517 return function () { 36518 callback.call(null, xm.transformNode(xs.XMLDocument)); 36519 setTimeout( (function (xm, xs) { return function () { $(xm).remove(); $(xs).remove(); }; })(xm, xs), 200); 36520 }; 36521 })(xm, xs, callback), 100); 36522 return true; 36523 } 36524 if(typeof window.DOMParser !== "undefined" && typeof window.XMLHttpRequest !== "undefined" && typeof window.XSLTProcessor === "undefined") { 36525 xml = new DOMParser().parseFromString(xml, "text/xml"); 36526 xsl = new DOMParser().parseFromString(xsl, "text/xml"); 36527 // alert(xml.transformNode()); 36528 // callback.call(null, new XMLSerializer().serializeToString(rs)); 36529 36530 } 36531 if(typeof window.DOMParser !== "undefined" && typeof window.XMLHttpRequest !== "undefined" && typeof window.XSLTProcessor !== "undefined") { 36532 processor = new XSLTProcessor(); 36533 support = $.isFunction(processor.transformDocument) ? (typeof window.XMLSerializer !== "undefined") : true; 36534 if(!support) { return false; } 36535 xml = new DOMParser().parseFromString(xml, "text/xml"); 36536 xsl = new DOMParser().parseFromString(xsl, "text/xml"); 36537 if($.isFunction(processor.transformDocument)) { 36538 rs = document.implementation.createDocument("", "", null); 36539 processor.transformDocument(xml, xsl, rs, null); 36540 callback.call(null, new XMLSerializer().serializeToString(rs)); 36541 return true; 36542 } 36543 else { 36544 processor.importStylesheet(xsl); 36545 rs = processor.transformToFragment(xml, document); 36546 callback.call(null, $("<div />").append(rs).html()); 36547 return true; 36548 } 36549 } 36550 return false; 36551 }; 36552 var xsl = { 36553 'nest' : '<' + '?xml version="1.0" encoding="utf-8" ?>' + 36554 '<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >' + 36555 '<xsl:output method="html" encoding="utf-8" omit-xml-declaration="yes" standalone="no" indent="no" media-type="text/html" />' + 36556 '<xsl:template match="/">' + 36557 ' <xsl:call-template name="nodes">' + 36558 ' <xsl:with-param name="node" select="/root" />' + 36559 ' </xsl:call-template>' + 36560 '</xsl:template>' + 36561 '<xsl:template name="nodes">' + 36562 ' <xsl:param name="node" />' + 36563 ' <ul>' + 36564 ' <xsl:for-each select="$node/item">' + 36565 ' <xsl:variable name="children" select="count(./item) > 0" />' + 36566 ' <li>' + 36567 ' <xsl:attribute name="class">' + 36568 ' <xsl:if test="position() = last()">jstree-last </xsl:if>' + 36569 ' <xsl:choose>' + 36570 ' <xsl:when test="@state = \'open\'">jstree-open </xsl:when>' + 36571 ' <xsl:when test="$children or @hasChildren or @state = \'closed\'">jstree-closed </xsl:when>' + 36572 ' <xsl:otherwise>jstree-leaf </xsl:otherwise>' + 36573 ' </xsl:choose>' + 36574 ' <xsl:value-of select="@class" />' + 36575 ' </xsl:attribute>' + 36576 ' <xsl:for-each select="@*">' + 36577 ' <xsl:if test="name() != \'class\' and name() != \'state\' and name() != \'hasChildren\'">' + 36578 ' <xsl:attribute name="{name()}"><xsl:value-of select="." /></xsl:attribute>' + 36579 ' </xsl:if>' + 36580 ' </xsl:for-each>' + 36581 ' <ins class="jstree-icon"><xsl:text> </xsl:text></ins>' + 36582 ' <xsl:for-each select="content/name">' + 36583 ' <a>' + 36584 ' <xsl:attribute name="href">' + 36585 ' <xsl:choose>' + 36586 ' <xsl:when test="@href"><xsl:value-of select="@href" /></xsl:when>' + 36587 ' <xsl:otherwise>#</xsl:otherwise>' + 36588 ' </xsl:choose>' + 36589 ' </xsl:attribute>' + 36590 ' <xsl:attribute name="class"><xsl:value-of select="@lang" /> <xsl:value-of select="@class" /></xsl:attribute>' + 36591 ' <xsl:attribute name="style"><xsl:value-of select="@style" />
36591</xsl:attribute>' + 36592 ' <xsl:for-each select="@*">' + 36593 ' <xsl:if test="name() != \'style\' and name() != \'class\' and name() != \'href\'">' + 36594 ' <xsl:attribute name="{name()}"><xsl:value-of select="." /></xsl:attribute>' + 36595 ' </xsl:if>' + 36596 ' </xsl:for-each>' + 36597 ' <ins>' + 36598 ' <xsl:attribute name="class">jstree-icon ' + 36599 ' <xsl:if test="string-length(attribute::icon) > 0 and not(contains(@icon,\'/\'))"><xsl:value-of select="@icon" /></xsl:if>' + 36600 ' </xsl:attribute>' + 36601 ' <xsl:if test="string-length(attribute::icon) > 0 and contains(@icon,\'/\')"><xsl:attribute name="style">background:url(<xsl:value-of select="@icon" />) center center no-repeat;</xsl:attribute></xsl:if>' + 36602 ' <xsl:text> </xsl:text>' + 36603 ' </ins>' + 36604 ' <xsl:copy-of select="./child::node()" />' + 36605 ' </a>' + 36606 ' </xsl:for-each>' + 36607 ' <xsl:if test="$children or @hasChildren"><xsl:call-template name="nodes"><xsl:with-param name="node" select="current()" /></xsl:call-template></xsl:if>' + 36608 ' </li>' + 36609 ' </xsl:for-each>' + 36610 ' </ul>' + 36611 '</xsl:template>' + 36612 '</xsl:stylesheet>', 36613 36614 'flat' : '<' + '?xml version="1.0" encoding="utf-8" ?>' + 36615 '<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >' + 36616 '<xsl:output method="html" encoding="utf-8" omit-xml-declaration="yes" standalone="no" indent="no" media-type="text/xml" />' + 36617 '<xsl:template match="/">' + 36618 ' <ul>' + 36619 ' <xsl:for-each select="//item[not(@parent_id) or @parent_id=0 or not(@parent_id = //item/@id)]">' + /* the last `or` may be removed */ 36620 ' <xsl:call-template name="nodes">' + 36621 ' <xsl:with-param name="node" select="." />' + 36622 ' <xsl:with-param name="is_last" select="number(position() = last())" />' + 36623 ' </xsl:call-template>' + 36624 ' </xsl:for-each>' + 36625 ' </ul>' + 36626 '</xsl:template>' + 36627 '<xsl:template name="nodes">' + 36628 ' <xsl:param name="node" />' + 36629 ' <xsl:param name="is_last" />' + 36630 ' <xsl:variable name="children" select="count(//item[@parent_id=$node/attribute::id]) > 0" />' + 36631 ' <li>' + 36632 ' <xsl:attribute name="class">' + 36633 ' <xsl:if test="$is_last = true()">jstree-last </xsl:if>' + 36634 ' <xsl:choose>' + 36635 ' <xsl:when test="@state = \'open\'">jstree-open </xsl:when>' + 36636 ' <xsl:when test="$children or @hasChildren or @state = \'closed\'">jstree-closed </xsl:when>' + 36637 ' <xsl:otherwise>jstree-leaf </xsl:otherwise>' + 36638 ' </xsl:choose>' + 36639 ' <xsl:value-of select="@class" />' + 36640 ' </xsl:attribute>' + 36641 ' <xsl:for-each select="@*">' + 36642 ' <xsl:if test="name() != \'parent_id\' and name() != \'hasChildren\' and name() != \'class\' and name() != \'state\'">' + 36643 ' <xsl:attribute name="{name()}"><xsl:value-of select="." /></xsl:attribute>' + 36644 ' </xsl:if>' + 36645 ' </xsl:for-each>' + 36646 ' <ins class="jstree-icon"><xsl:text> </xsl:text></ins>' + 36647 ' <xsl:for-each select="content/name">' + 36648 ' <a>' + 36649 ' <xsl:attribute name="href">' + 36650 ' <xsl:choose>' + 36651 ' <xsl:when test="@href"><xsl:value-of select="@href" /></xsl:when>' + 36652 ' <xsl:otherwise>#</xsl:otherwise>' + 36653 ' </xsl:choose>' + 36654 ' </xsl:attribute>' + 36655 ' <xsl:attribute name="class"><xsl:value-of select="@lang" /> <xsl:value-of select="@class" /></xsl:attribute>' + 36656 ' <xsl:attribute name="style"><xsl:value-of select="@style" /></xsl:attribute>' + 36657 ' <xsl:for-each select="@*">' + 36658 ' <xsl:if test="name() != \'style\' and name() != \'class\' and name() != \'href\'">' + 36659 ' <xsl:attribute name="{name()}"><xsl:value-of select="." /></xsl:attribute>' + 36660 ' </xsl:if>' + 36661 ' </xsl:for-each>' + 36662 ' <ins>' + 36663 ' <xsl:attribute name="class">jstree-icon ' + 36664 ' <xsl:if test="string-length(attribute::icon) > 0 and not(contains(@icon,\'/\'))"><xsl:value-of select="@icon" /></xsl:if>' + 36665 ' </xsl:attribute>' + 36666 ' <xsl:if test="string-length(attribute::icon) > 0 and contains(@icon,\'/\')"><xsl:attribute name="style">background:url(<xsl:value-of select="@icon" />) center center no-repeat;</xsl:attribute></xsl:if>' + 36667 ' <xsl:text> </xsl:text>' + 36668 ' </ins>' + 36669 ' <xsl:copy-of select="./child::node()" />' + 36670 ' </a>' + 36671 ' </xsl:for-each>' + 36672 ' <xsl:if test="$children">' + 36673 ' <ul>' + 36674 ' <xsl:for-each select="//item[@parent_id=$node/attribute::id]">' + 36675 ' <xsl:call-template name="nodes">' + 36676 ' <xsl:with-param name="node" select="." />' + 36677 ' <xsl:with-param name="is_last" select="number(position() = last())" />' + 36678 ' </xsl:call-template>' + 36679 ' </xsl:for-each>' + 36680 ' </ul>' + 36681 ' </xsl:if>' + 36682 ' </li>' + 36683 '</xsl:template>' + 36684 '</xsl:stylesheet>' 36685 }, 36686 escape_xml = function(string) { 36687 return string 36688 .toString() 36689 .replace(/&/g, '&') 36690 .replace(/</g, '<') 36691 .replace(/>/g, '>') 36692 .replace(/"/g, '"') 36693 .replace(/'/g, '''); 36694 }; 36695 $.jstree.plugin("xml_data", { 36696 defaults : { 36697 data : false, 36698 ajax : false, 36699 xsl : "flat", 36700 clean_node : false, 36701 correct_state : true, 36702 get_skip_empty : false, 36703 get_include_preamble : true 36704 }, 36705 _fn : { 36706 load_node : function (obj, s_call, e_call) { var _this = this; this.load_node_xml(obj, function () { _this.__callback({ "obj" : _this._get_node(obj) }); s_call.call(this); }, e_call); }, 36707 _is_loaded : function (obj) { 36708 var s = this._get_settings().xml_data; 36709 obj = this._get_node(obj); 36710 return obj == -1 || !obj || (!s.ajax && !$.isFunction(s.data)) || obj.is(".jstree-open, .jstree-leaf") || obj.children("ul").children("li").size() > 0; 36711 }, 36712 load_node_xml : function (obj, s_call, e_call) { 36713 var s = this.get_settings().xml_data, 36714 error_func = function () {}, 36715 success_func = function () {}; 36716 36717 obj = this._get_node(obj); 36718 if(obj && obj !== -1) { 36719 if(obj.data("jstree_is_loading")) { return; } 36720 else { obj.data("jstree_is_loading",true); } 36721 } 36722 switch(!0) { 36723 case (!s.data && !s.ajax): throw "Neither data nor ajax settings supplied."; 36724 case ($.isFunction(s.data)): 36725 s.data.call(this, obj, $.proxy(function (d) {
36726 this.parse_xml(d, $.proxy(function (d) { 36727 if(d) { 36728 d = d.replace(/ ?xmlns="[^"]*"/ig, ""); 36729 if(d.length > 10) { 36730 d = $(d); 36731 if(obj === -1 || !obj) { this.get_container().children("ul").empty().append(d.children()); } 36732 else { obj.children("a.jstree-loading").removeClass("jstree-loading"); obj.append(d); obj.removeData("jstree_is_loading"); } 36733 if(s.clean_node) { this.clean_node(obj); } 36734 if(s_call) { s_call.call(this); } 36735 } 36736 else { 36737 if(obj && obj !== -1) { 36738 obj.children("a.jstree-loading").removeClass("jstree-loading"); 36739 obj.removeData("jstree_is_loading"); 36740 if(s.correct_state) { 36741 this.correct_state(obj); 36742 if(s_call) { s_call.call(this); } 36743 } 36744 } 36745 else { 36746 if(s.correct_state) { 36747 this.get_container().children("ul").empty(); 36748 if(s_call) { s_call.call(this); } 36749 } 36750 } 36751 } 36752 } 36753 }, this)); 36754 }, this)); 36755 break; 36756 case (!!s.data && !s.ajax) || (!!s.data && !!s.ajax && (!obj || obj === -1)): 36757 if(!obj || obj == -1) { 36758 this.parse_xml(s.data, $.proxy(function (d) { 36759 if(d) { 36760 d = d.replace(/ ?xmlns="[^"]*"/ig, ""); 36761 if(d.length > 10) { 36762 d = $(d); 36763 this.get_container().children("ul").empty().append(d.children()); 36764 if(s.clean_node) { this.clean_node(obj); } 36765 if(s_call) { s_call.call(this); } 36766 } 36767 } 36768 else { 36769 if(s.correct_state) { 36770 this.get_container().children("ul").empty(); 36771 if(s_call) { s_call.call(this); } 36772 } 36773 } 36774 }, this)); 36775 } 36776 break; 36777 case (!s.data && !!s.ajax) || (!!s.data && !!s.ajax && obj && obj !== -1): 36778 error_func = function (x, t, e) { 36779 var ef = this.get_settings().xml_data.ajax.error; 36780 if(ef) { ef.call(this, x, t, e); } 36781 if(obj !== -1 && obj.length) { 36782 obj.children("a.jstree-loading").removeClass("jstree-loading"); 36783 obj.removeData("jstree_is_loading"); 36784 if(t === "success" && s.correct_state) { this.correct_state(obj); } 36785 } 36786 else { 36787 if(t === "success" && s.correct_state) { this.get_container().children("ul").empty(); } 36788 } 36789 if(e_call) { e_call.call(this); } 36790 }; 36791 success_func = function (d, t, x) { 36792 d = x.responseText; 36793 var sf = this.get_settings().xml_data.ajax.success; 36794 if(sf) { d = sf.call(this,d,t,x) || d; } 36795 if(d === "" || (d && d.toString && d.toString().replace(/^[\s\n]+$/,"") === "")) { 36796 return error_func.call(this, x, t, ""); 36797 } 36798 this.parse_xml(d, $.proxy(function (d) { 36799 if(d) { 36800 d = d.replace(/ ?xmlns="[^"]*"/ig, ""); 36801 if(d.length > 10) { 36802 d = $(d); 36803 if(obj === -1 || !obj) { this.get_container().children("ul").empty().append(d.children()); } 36804 else { obj.children("a.jstree-loading").removeClass("jstree-loading"); obj.append(d); obj.removeData("jstree_is_loading"); } 36805 if(s.clean_node) { this.clean_node(obj); } 36806 if(s_call) { s_call.call(this); } 36807 } 36808 else { 36809 if(obj && obj !== -1) { 36810 obj.children("a.jstree-loading").removeClass("jstree-loading"); 36811 obj.removeData("jstree_is_loading"); 36812 if(s.correct_state) { 36813 this.correct_state(obj); 36814 if(s_call) { s_call.call(this); } 36815 } 36816 } 36817 else { 36818 if(s.correct_state) { 36819 this.get_container().children("ul").empty(); 36820 if(s_call) { s_call.call(this); } 36821 } 36822 } 36823 } 36824 } 36825 }, this)); 36826 }; 36827 s.ajax.context = this; 36828 s.ajax.error = error_func; 36829 s.ajax.success = success_func; 36830 if(!s.ajax.dataType) { s.ajax.dataType = "xml"; } 36831 if($.isFunction(s.ajax.url)) { s.ajax.url = s.ajax.url.call(this, obj); } 36832 if($.isFunction(s.ajax.data)) { s.ajax.data = s.ajax.data.call(this, obj); } 36833 $.ajax(s.ajax); 36834 break; 36835 } 36836 }, 36837 parse_xml : function (xml, callback) { 36838 var s = this._get_settings().xml_data; 36839 $.vakata.xslt(xml, xsl[s.xsl], callback); 36840 }, 36841 get_xml : function (tp, obj, li_attr, a_attr, is_callback) { 36842 var result = "", 36843 s = this._get_settings(), 36844 _this = this, 36845 tmp1, tmp2, li, a, lang; 36846 if(!tp) { tp = "flat"; } 36847 if(!is_callback) { is_callback = 0; } 36848 obj = this._get_node(obj); 36849 if(!obj || obj === -1) { obj = this.get_container().find("> ul > li"); } 36850 li_attr = $.isArray(li_attr) ? li_attr : [ "id", "class" ]; 36851 if(!is_callback && this.data.types && $.inArray(s.types.type_attr, li_attr) === -1) { li_attr.push(s.types.type_attr); } 36852 36853 a_attr = $.isArray(a_attr) ? a_attr : [ ]; 36854 36855 if(!is_callback) { 36856 if(s.xml_data.get_include_preamble) { 36857 result += '<' + '?xml version="1.0" encoding="UTF-8"?' + '>'; 36858 } 36859 result += "<root>"; 36860 } 36861 obj.each(function () { 36862 result += "<item"; 36863 li = $(this); 36864 $.each(li_attr, function (i, v) { 36865 var t = li.attr(v); 36866 if(!s.xml_data.get_skip_empty || typeof t !== "undefined") {
36867 result += " " + v + "=\"" + escape_xml((" " + (t || "")).replace(/ jstree[^ ]*/ig,'').replace(/\s+$/ig," ").replace(/^ /,"").replace(/ $/,"")) + "\""; 36868 } 36869 }); 36870 if(li.hasClass("jstree-open")) { result += " state=\"open\""; } 36871 if(li.hasClass("jstree-closed")) { result += " state=\"closed\""; } 36872 if(tp === "flat") { result += " parent_id=\"" + escape_xml(is_callback) + "\""; } 36873 result += ">"; 36874 result += "<content>"; 36875 a = li.children("a"); 36876 a.each(function () { 36877 tmp1 = $(this); 36878 lang = false; 36879 result += "<name"; 36880 if($.inArray("languages", s.plugins) !== -1) { 36881 $.each(s.languages, function (k, z) { 36882 if(tmp1.hasClass(z)) { result += " lang=\"" + escape_xml(z) + "\""; lang = z; return false; } 36883 }); 36884 } 36885 if(a_attr.length) { 36886 $.each(a_attr, function (k, z) { 36887 var t = tmp1.attr(z); 36888 if(!s.xml_data.get_skip_empty || typeof t !== "undefined") { 36889 result += " " + z + "=\"" + escape_xml((" " + t || "").replace(/ jstree[^ ]*/ig,'').replace(/\s+$/ig," ").replace(/^ /,"").replace(/ $/,"")) + "\""; 36890 } 36891 }); 36892 } 36893 if(tmp1.children("ins").get(0).className.replace(/jstree[^ ]*|$/ig,'').replace(/^\s+$/ig,"").length) { 36894 result += ' icon="' + escape_xml(tmp1.children("ins").get(0).className.replace(/jstree[^ ]*|$/ig,'').replace(/\s+$/ig," ").replace(/^ /,"").replace(/ $/,"")) + '"'; 36895 } 36896 if(tmp1.children("ins").get(0).style.backgroundImage.length) { 36897 result += ' icon="' + escape_xml(tmp1.children("ins").get(0).style.backgroundImage.replace("url(","").replace(")","").replace(/'/ig,"").replace(/"/ig,"")) + '"'; 36898 } 36899 result += ">"; 36900 result += "<![CDATA[" + _this.get_text(tmp1, lang) + "]]>"; 36901 result += "</name>"; 36902 }); 36903 result += "</content>"; 36904 tmp2 = li[0].id || true; 36905 li = li.find("> ul > li"); 36906 if(li.length) { tmp2 = _this.get_xml(tp, li, li_attr, a_attr, tmp2); } 36907 else { tmp2 = ""; } 36908 if(tp == "nest") { result += tmp2; } 36909 result += "</item>"; 36910 if(tp == "flat") { result += tmp2; } 36911 }); 36912 if(!is_callback) { result += "</root>"; } 36913 return result; 36914 } 36915 } 36916 }); 36917})(jQuery); 36918//*/ 36919 36920/* 36921 * jsTree search plugin 36922 * Enables both sync and async search on the tree 36923 * DOES NOT WORK WITH JSON PROGRESSIVE RENDER 36924 */ 36925(function ($) { 36926 $.expr[':'].jstree_contains = function(a,i,m){ 36927 return (a.textContent || a.innerText || "").toLowerCase().indexOf(m[3].toLowerCase())>=0; 36928 }; 36929 $.expr[':'].jstree_title_contains = function(a,i,m) { 36930 return (a.getAttribute("title") || "").toLowerCase().indexOf(m[3].toLowerCase())>=0; 36931 }; 36932 $.jstree.plugin("search", { 36933 __init : function () { 36934 this.data.search.str = ""; 36935 this.data.search.result = $(); 36936 if(this._get_settings().search.show_only_matches) { 36937 this.get_container() 36938 .bind("search.jstree", function (e, data) { 36939 $(this).children("ul").find("li").hide().removeClass("jstree-last"); 36940 data.rslt.nodes.parentsUntil(".jstree").andSelf().show() 36941 .filter("ul").each(function () { $(this).children("li:visible").eq(-1).addClass("jstree-last"); }); 36942 }) 36943 .bind("clear_search.jstree", function () { 36944 $(this).children("ul").find("li").css("display","").end().end().jstree("clean_node", -1); 36945 }); 36946 } 36947 }, 36948 defaults : { 36949 ajax : false, 36950 search_method : "jstree_contains", // for case insensitive - jstree_contains 36951 show_only_matches : false 36952 }, 36953 _fn : { 36954 search : function (str, skip_async) { 36955 if($.trim(str) === "") { this.clear_search(); return; } 36956 var s = this.get_settings().search, 36957 t = this, 36958 error_func = function () { }, 36959 success_func = function () { }; 36960 this.data.search.str = str; 36961 36962 if(!skip_async && s.ajax !== false && this.get_container_ul().find("li.jstree-closed:not(:has(ul)):eq(0)").length > 0) { 36963 this.search.supress_callback = true; 36964 error_func = function () { }; 36965 success_func = function (d, t, x) { 36966 var sf = this.get_settings().search.ajax.success; 36967 if(sf) { d = sf.call(this,d,t,x) || d; } 36968 this.data.search.to_open = d; 36969 this._search_open(); 36970 }; 36971 s.ajax.context = this; 36972 s.ajax.error = error_func; 36973 s.ajax.success = success_func; 36974 if($.isFunction(s.ajax.url)) { s.ajax.url = s.ajax.url.call(this, str); } 36975 if($.isFunction(s.ajax.data)) { s.ajax.data = s.ajax.data.call(this, str); } 36976 if(!s.ajax.data) { s.ajax.data = { "search_string" : str }; } 36977 if(!s.ajax.dataType || /^json/.exec(s.ajax.dataType)) { s.ajax.dataType = "json"; } 36978 $.ajax(s.ajax); 36979 return; 36980 } 36981 if(this.data.search.result.length) { this.clear_search(); } 36982 this.data.search.result = this.get_container().find("a" + (this.data.languages ? "." + this.get_lang() : "" ) + ":" + (s.search_method) + "(" + this.data.search.str + ")"); 36983 this.data.search.result.addClass("jstree-search").parent().parents(".jstree-closed").each(function () { 36984 t.open_node(this, false, true); 36985 }); 36986 this.__callback({ nodes : this.data.search.result, str : str }); 36987 }, 36988 clear_search : function (str) { 36989 this.data.search.result.removeClass("jstree-search"); 36990 this.__callback(this.data.search.result); 36991 this.data.search.result = $(); 36992 }, 36993 _search_open : function (is_callback) { 36994 var _this = this, 36995 done = true, 36996 current = [], 36997 remaining = []; 36998 if(this.data.search.to_open.length) { 36999 $.each(this.data.search.to_open, function (i, val) { 37000 if(val == "#") { return true; } 37001 if($(val).length && $(val).is(".jstree-closed")) { current.push(val); } 37002 else { remaining.push(val); } 37003 });
37004 if(current.length) { 37005 this.data.search.to_open = remaining; 37006 $.each(current, function (i, val) { 37007 _this.open_node(val, function () { _this._search_open(true); }); 37008 }); 37009 done = false; 37010 } 37011 } 37012 if(done) { this.search(this.data.search.str, true); } 37013 } 37014 } 37015 }); 37016})(jQuery); 37017//*/ 37018 37019/* 37020 * jsTree contextmenu plugin 37021 */ 37022(function ($) { 37023 $.vakata.context = { 37024 hide_on_mouseleave : false, 37025 37026 cnt : $("<div id='vakata-contextmenu' />"), 37027 vis : false, 37028 tgt : false, 37029 par : false, 37030 func : false, 37031 data : false, 37032 rtl : false, 37033 show : function (s, t, x, y, d, p, rtl) { 37034 $.vakata.context.rtl = !!rtl; 37035 var html = $.vakata.context.parse(s), h, w; 37036 if(!html) { return; } 37037 $.vakata.context.vis = true; 37038 $.vakata.context.tgt = t; 37039 $.vakata.context.par = p || t || null; 37040 $.vakata.context.data = d || null; 37041 $.vakata.context.cnt 37042 .html(html) 37043 .css({ "visibility" : "hidden", "display" : "block", "left" : 0, "top" : 0 }); 37044 37045 if($.vakata.context.hide_on_mouseleave) { 37046 $.vakata.context.cnt 37047 .one("mouseleave", function(e) { $.vakata.context.hide(); }); 37048 } 37049 37050 h = $.vakata.context.cnt.height(); 37051 w = $.vakata.context.cnt.width(); 37052 if(x + w > $(document).width()) { 37053 x = $(document).width() - (w + 5); 37054 $.vakata.context.cnt.find("li > ul").addClass("right"); 37055 } 37056 if(y + h > $(document).height()) { 37057 y = y - (h + t[0].offsetHeight); 37058 $.vakata.context.cnt.find("li > ul").addClass("bottom"); 37059 } 37060 37061 $.vakata.context.cnt 37062 .css({ "left" : x, "top" : y }) 37063 .find("li:has(ul)") 37064 .bind("mouseenter", function (e) { 37065 var w = $(document).width(), 37066 h = $(document).height(), 37067 ul = $(this).children("ul").show(); 37068 if(w !== $(document).width()) { ul.toggleClass("right"); } 37069 if(h !== $(document).height()) { ul.toggleClass("bottom"); } 37070 }) 37071 .bind("mouseleave", function (e) { 37072 $(this).children("ul").hide(); 37073 }) 37074 .end() 37075 .css({ "visibility" : "visible" }) 37076 .show(); 37077 $(document).triggerHandler("context_show.vakata"); 37078 }, 37079 hide : function () { 37080 $.vakata.context.vis = false; 37081 $.vakata.context.cnt.attr("class","").css({ "visibility" : "hidden" }); 37082 $(document).triggerHandler("context_hide.vakata"); 37083 }, 37084 parse : function (s, is_callback) { 37085 if(!s) { return false; } 37086 var str = "", 37087 tmp = false, 37088 was_sep = true; 37089 if(!is_callback) { $.vakata.context.func = {}; } 37090 str += "<ul>"; 37091 $.each(s, function (i, val) { 37092 if(!val) { return true; } 37093 $.vakata.context.func[i] = val.action; 37094 if(!was_sep && val.separator_before) { 37095 str += "<li class='vakata-separator vakata-separator-before'></li>"; 37096 } 37097 was_sep = false; 37098 str += "<li class='" + (val._class || "") + (val._disabled ? " jstree-contextmenu-disabled " : "") + "'><ins "; 37099 if(val.icon && val.icon.indexOf("/") === -1) { str += " class='" + val.icon + "' "; } 37100 if(val.icon && val.icon.indexOf("/") !== -1) { str += " style='background:url(" + val.icon + ") center center no-repeat;' "; } 37101 str += "> </ins><a href='#' rel='" + i + "'>"; 37102 if(val.submenu) { 37103 str += "<span style='float:" + ($.vakata.context.rtl ? "left" : "right") + ";'>»</span>"; 37104 } 37105 str += val.label + "</a>"; 37106 if(val.submenu) { 37107 tmp = $.vakata.context.parse(val.submenu, true); 37108 if(tmp) { str += tmp; } 37109 } 37110 str += "</li>"; 37111 if(val.separator_after) { 37112 str += "<li class='vakata-separator vakata-separator-after'></li>"; 37113 was_sep = true; 37114 } 37115 }); 37116 str = str.replace(/<li class\='vakata-separator vakata-separator-after'\><\/li\>$/,""); 37117 str += "</ul>"; 37118 $(document).triggerHandler("context_parse.vakata"); 37119 return str.length > 10 ? str : false; 37120 }, 37121 exec : function (i) { 37122 if($.isFunction($.vakata.context.func[i])) { 37123 // if is string - eval and call it! 37124 $.vakata.context.func[i].call($.vakata.context.data, $.vakata.context.par); 37125 return true; 37126 } 37127 else { return false; } 37128 } 37129 }; 37130 $(function () { 37131 var css_string = '' + 37132 '#vakata-contextmenu { display:block; visibility:hidden; left:0; top:-200px; position:absolute; margin:0; padding:0; min-width:180px; background:#ebebeb; border:1px solid silver; z-index:10000; *width:180px; } ' + 37133 '#vakata-contextmenu ul { min-width:180px; *width:180px; } ' + 37134 '#vakata-contextmenu ul, #vakata-contextmenu li { margin:0; padding:0; list-style-type:none; display:block; } ' + 37135 '#vakata-contextmenu li { line-height:20px; min-height:20px; position:relative; padding:0px; } ' + 37136 '#vakata-contextmenu li a { padding:1px 6px; line-height:17px; display:block; text-decoration:none; margin:1px 1px 0 1px; } ' + 37137 '#vakata-contextmenu li ins { float:left; width:16px; height:16px; text-decoration:none; margin-right:2px; } ' + 37138 '#vakata-contextmenu li a:hover, #vakata-contextmenu li.vakata-hover > a { background:gray; color:white; } ' + 37139 '#vakata-contextmenu li ul { display:none; position:absolute; top:-2px; left:100%; background:#ebebeb; border:1px solid gray; } ' + 37140 '#vakata-contextmenu .right { right:100%; left:auto; } ' + 37141 '#vakata-contextmenu .bottom { bottom:-1px; top:auto; } ' + 37142 '#vakata-contextmenu li.vakata-separator { min-height:0; height:1px; line-height:1px; font-size:1px; overflow:hidden; margin:0 2px; background:silver; /* border-top:1px solid #fefefe; */ padding:0; } '; 37143 $.vakata.css.add_sheet({ str : css_string, title : "vakata" });
37144 $.vakata.context.cnt 37145 .delegate("a","click", function (e) { e.preventDefault(); }) 37146 .delegate("a","mouseup", function (e) { 37147 if(!$(this).parent().hasClass("jstree-contextmenu-disabled") && $.vakata.context.exec($(this).attr("rel"))) { 37148 $.vakata.context.hide(); 37149 } 37150 else { $(this).blur(); } 37151 }) 37152 .delegate("a","mouseover", function () { 37153 $.vakata.context.cnt.find(".vakata-hover").removeClass("vakata-hover"); 37154 }) 37155 .appendTo("body"); 37156 $(document).bind("mousedown", function (e) { if($.vakata.context.vis && !$.contains($.vakata.context.cnt[0], e.target)) { $.vakata.context.hide(); } }); 37157 if(typeof $.hotkeys !== "undefined") { 37158 $(document) 37159 .bind("keydown", "up", function (e) { 37160 if($.vakata.context.vis) { 37161 var o = $.vakata.context.cnt.find("ul:visible").last().children(".vakata-hover").removeClass("vakata-hover").prevAll("li:not(.vakata-separator)").first(); 37162 if(!o.length) { o = $.vakata.context.cnt.find("ul:visible").last().children("li:not(.vakata-separator)").last(); } 37163 o.addClass("vakata-hover"); 37164 e.stopImmediatePropagation(); 37165 e.preventDefault(); 37166 } 37167 }) 37168 .bind("keydown", "down", function (e) { 37169 if($.vakata.context.vis) { 37170 var o = $.vakata.context.cnt.find("ul:visible").last().children(".vakata-hover").removeClass("vakata-hover").nextAll("li:not(.vakata-separator)").first(); 37171 if(!o.length) { o = $.vakata.context.cnt.find("ul:visible").last().children("li:not(.vakata-separator)").first(); } 37172 o.addClass("vakata-hover"); 37173 e.stopImmediatePropagation(); 37174 e.preventDefault(); 37175 } 37176 }) 37177 .bind("keydown", "right", function (e) { 37178 if($.vakata.context.vis) { 37179 $.vakata.context.cnt.find(".vakata-hover").children("ul").show().children("li:not(.vakata-separator)").removeClass("vakata-hover").first().addClass("vakata-hover"); 37180 e.stopImmediatePropagation(); 37181 e.preventDefault(); 37182 } 37183 }) 37184 .bind("keydown", "left", function (e) { 37185 if($.vakata.context.vis) { 37186 $.vakata.context.cnt.find(".vakata-hover").children("ul").hide().children(".vakata-separator").removeClass("vakata-hover"); 37187 e.stopImmediatePropagation(); 37188 e.preventDefault(); 37189 } 37190 }) 37191 .bind("keydown", "esc", function (e) { 37192 $.vakata.context.hide(); 37193 e.preventDefault(); 37194 }) 37195 .bind("keydown", "space", function (e) { 37196 $.vakata.context.cnt.find(".vakata-hover").last().children("a").click(); 37197 e.preventDefault(); 37198 }); 37199 } 37200 }); 37201 37202 $.jstree.plugin("contextmenu", { 37203 __init : function () { 37204 this.get_container() 37205 .delegate("a", "contextmenu.jstree", $.proxy(function (e) { 37206 e.preventDefault(); 37207 if(!$(e.currentTarget).hasClass("jstree-loading")) { 37208 this.show_contextmenu(e.currentTarget, e.pageX, e.pageY); 37209 } 37210 }, this)) 37211 .delegate("a", "click.jstree", $.proxy(function (e) { 37212 if(this.data.contextmenu) { 37213 $.vakata.context.hide(); 37214 } 37215 }, this)) 37216 .bind("destroy.jstree", $.proxy(function () { 37217 // TODO: move this to descruct method 37218 if(this.data.contextmenu) { 37219 $.vakata.context.hide(); 37220 } 37221 }, this)); 37222 $(document).bind("context_hide.vakata", $.proxy(function () { this.data.contextmenu = false; }, this)); 37223 }, 37224 defaults : { 37225 select_node : false, // requires UI plugin 37226 show_at_node : true, 37227 items : { // Could be a function that should return an object like this one 37228 "create" : { 37229 "separator_before" : false, 37230 "separator_after" : true, 37231 "label" : "Create", 37232 "action" : function (obj) { this.create(obj); } 37233 }, 37234 "rename" : { 37235 "separator_before" : false, 37236 "separator_after" : false, 37237 "label" : "Rename", 37238 "action" : function (obj) { this.rename(obj); } 37239 }, 37240 "remove" : { 37241 "separator_before" : false, 37242 "icon" : false, 37243 "separator_after" : false, 37244 "label" : "Delete", 37245 "action" : function (obj) { if(this.is_selected(obj)) { this.remove(); } else { this.remove(obj); } } 37246 }, 37247 "ccp" : { 37248 "separator_before" : true, 37249 "icon" : false, 37250 "separator_after" : false, 37251 "label" : "Edit", 37252 "action" : false, 37253 "submenu" : { 37254 "cut" : { 37255 "separator_before" : false, 37256 "separator_after" : false, 37257 "label" : "Cut", 37258 "action" : function (obj) { this.cut(obj); } 37259 }, 37260 "copy" : { 37261 "separator_before" : false, 37262 "icon" : false, 37263 "separator_after" : false, 37264 "label" : "Copy", 37265 "action" : function (obj) { this.copy(obj); } 37266 }, 37267 "paste" : { 37268 "separator_before" : false, 37269 "icon" : false, 37270 "separator_after" : false, 37271 "label" : "Paste", 37272 "action" : function (obj) { this.paste(obj); } 37273 } 37274 } 37275 } 37276 } 37277 }, 37278 _fn : { 37279 show_contextmenu : function (obj, x, y) { 37280 obj = this._get_node(obj); 37281 var s = this.get_settings().contextmenu, 37282 a = obj.children("a:visible:eq(0)"), 37283 o = false, 37284 i = false; 37285 if(s.select_node && this.data.ui && !this.is_selected(obj)) { 37286 this.deselect_all(); 37287 this.select_node(obj, true); 37288 } 37289 if(s.show_at_node || typeof x === "undefined" || typeof y === "undefined") { 37290 o = a.offset(); 37291 x = o.left; 37292 y = o.top + this.data.core.li_height; 37293 } 37294 i = obj.data("jstree") && obj.data("jstree").contextmenu ? obj.data("jstree").contextmenu : s.items; 37295 if($.isFunction(i)) { i = i.call(this, obj); } 37296 this.data.contextmenu = true; 37297 $.vakata.context.show(i, a, x, y, this, obj, this._get_settings().core.rtl); 37298 if(this.data.themes) { $.vakata.context.cnt.attr("class", "jstree-" + this.data.themes.theme + "-context"); } 37299 } 37300 } 37301 }); 37302})(jQuery);
37303//*/ 37304 37305/* 37306 * jsTree types plugin 37307 * Adds support types of nodes 37308 * You can set an attribute on each li node, that represents its type. 37309 * According to the type setting the node may get custom icon/validation rules 37310 */ 37311(function ($) { 37312 $.jstree.plugin("types", { 37313 __init : function () { 37314 var s = this._get_settings().types; 37315 this.data.types.attach_to = []; 37316 this.get_container() 37317 .bind("init.jstree", $.proxy(function () { 37318 var types = s.types, 37319 attr = s.type_attr, 37320 icons_css = "", 37321 _this = this; 37322 37323 $.each(types, function (i, tp) { 37324 $.each(tp, function (k, v) { 37325 if(!/^(max_depth|max_children|icon|valid_children)$/.test(k)) { _this.data.types.attach_to.push(k); } 37326 }); 37327 if(!tp.icon) { return true; } 37328 if( tp.icon.image || tp.icon.position) { 37329 if(i == "default") { icons_css += '.jstree-' + _this.get_index() + ' a > .jstree-icon { '; } 37330 else { icons_css += '.jstree-' + _this.get_index() + ' li[' + attr + '="' + i + '"] > a > .jstree-icon { '; } 37331 if(tp.icon.image) { icons_css += ' background-image:url(' + tp.icon.image + '); '; } 37332 if(tp.icon.position){ icons_css += ' background-position:' + tp.icon.position + '; '; } 37333 else { icons_css += ' background-position:0 0; '; } 37334 icons_css += '} '; 37335 } 37336 }); 37337 if(icons_css !== "") { $.vakata.css.add_sheet({ 'str' : icons_css, title : "jstree-types" }); } 37338 }, this)) 37339 .bind("before.jstree", $.proxy(function (e, data) { 37340 var s, t, 37341 o = this._get_settings().types.use_data ? this._get_node(data.args[0]) : false, 37342 d = o && o !== -1 && o.length ? o.data("jstree") : false; 37343 if(d && d.types && d.types[data.func] === false) { e.stopImmediatePropagation(); return false; } 37344 if($.inArray(data.func, this.data.types.attach_to) !== -1) { 37345 if(!data.args[0] || (!data.args[0].tagName && !data.args[0].jquery)) { return; } 37346 s = this._get_settings().types.types; 37347 t = this._get_type(data.args[0]); 37348 if( 37349 ( 37350 (s[t] && typeof s[t][data.func] !== "undefined") || 37351 (s["default"] && typeof s["default"][data.func] !== "undefined") 37352 ) && this._check(data.func, data.args[0]) === false 37353 ) { 37354 e.stopImmediatePropagation(); 37355 return false; 37356 } 37357 } 37358 }, this)); 37359 if(is_ie6) { 37360 this.get_container() 37361 .bind("load_node.jstree set_type.jstree", $.proxy(function (e, data) { 37362 var r = data && data.rslt && data.rslt.obj && data.rslt.obj !== -1 ? this._get_node(data.rslt.obj).parent() : this.get_container_ul(), 37363 c = false, 37364 s = this._get_settings().types; 37365 $.each(s.types, function (i, tp) { 37366 if(tp.icon && (tp.icon.image || tp.icon.position)) { 37367 c = i === "default" ? r.find("li > a > .jstree-icon") : r.find("li[" + s.type_attr + "='" + i + "'] > a > .jstree-icon"); 37368 if(tp.icon.image) { c.css("backgroundImage","url(" + tp.icon.image + ")"); } 37369 c.css("backgroundPosition", tp.icon.position || "0 0"); 37370 } 37371 }); 37372 }, this)); 37373 } 37374 }, 37375 defaults : { 37376 // defines maximum number of root nodes (-1 means unlimited, -2 means disable max_children checking) 37377 max_children : -1, 37378 // defines the maximum depth of the tree (-1 means unlimited, -2 means disable max_depth checking) 37379 max_depth : -1, 37380 // defines valid node types for the root nodes 37381 valid_children : "all", 37382 37383 // whether to use $.data 37384 use_data : false, 37385 // where is the type stores (the rel attribute of the LI element) 37386 type_attr : "rel", 37387 // a list of types 37388 types : { 37389 // the default type 37390 "default" : { 37391 "max_children" : -1, 37392 "max_depth" : -1, 37393 "valid_children": "all" 37394 37395 // Bound functions - you can bind any other function here (using boolean or function) 37396 //"select_node" : true 37397 } 37398 } 37399 }, 37400 _fn : { 37401 _types_notify : function (n, data) { 37402 if(data.type && this._get_settings().types.use_data) { 37403 this.set_type(data.type, n); 37404 } 37405 }, 37406 _get_type : function (obj) { 37407 obj = this._get_node(obj); 37408 return (!obj || !obj.length) ? false : obj.attr(this._get_settings().types.type_attr) || "default"; 37409 }, 37410 set_type : function (str, obj) { 37411 obj = this._get_node(obj); 37412 var ret = (!obj.length || !str) ? false : obj.attr(this._get_settings().types.type_attr, str); 37413 if(ret) { this.__callback({ obj : obj, type : str}); } 37414 return ret; 37415 }, 37416 _check : function (rule, obj, opts) { 37417 obj = this._get_node(obj); 37418 var v = false, t = this._get_type(obj), d = 0, _this = this, s = this._get_settings().types, data = false; 37419 if(obj === -1) { 37420 if(!!s[rule]) { v = s[rule]; } 37421 else { return; } 37422 } 37423 else { 37424 if(t === false) { return; } 37425 data = s.use_data ? obj.data("jstree") : false; 37426 if(data && data.types && typeof data.types[rule] !== "undefined") { v = data.types[rule]; } 37427 else if(!!s.types[t] && typeof s.types[t][rule] !== "undefined") { v = s.types[t][rule]; } 37428 else if(!!s.types["default"] && typeof s.types["default"][rule] !== "undefined") { v = s.types["default"][rule]; } 37429 } 37430 if($.isFunction(v)) { v = v.call(this, obj); } 37431 if(rule === "max_depth" && obj !== -1 && opts !== false && s.max_depth !== -2 && v !== 0) { 37432 // also include the node itself - otherwise if root node it is not checked 37433 obj.children("a:eq(0)").parentsUntil(".jstree","li").each(function (i) { 37434 // check if current depth already exceeds global tree depth 37435 if(s.max_depth !== -1 && s.max_depth - (i + 1) <= 0) { v = 0; return false; } 37436 d = (i === 0) ? v : _this._check(rule, this, false); 37437 // check if current node max depth is already matched or exceeded 37438 if(d !== -1 && d - (i + 1) <= 0) { v = 0; return false; } 37439 // otherwise - set the max depth to the current value minus current depth 37440 if(d >= 0 && (d - (i + 1) < v || v < 0) ) { v = d - (i + 1); } 37441 // if the global tree depth exists and it minus the nodes calculated so far is less than `v` or `v` is unlimited 37442 if(s.max_depth >= 0 && (s.max_depth - (i + 1) < v || v < 0) ) { v = s.max_depth - (i + 1); } 37443 });
37444 } 37445 return v; 37446 }, 37447 check_move : function () { 37448 if(!this.__call_old()) { return false; } 37449 var m = this._get_move(), 37450 s = m.rt._get_settings().types, 37451 mc = m.rt._check("max_children", m.cr), 37452 md = m.rt._check("max_depth", m.cr), 37453 vc = m.rt._check("valid_children", m.cr), 37454 ch = 0, d = 1, t; 37455 37456 if(vc === "none") { return false; } 37457 if($.isArray(vc) && m.ot && m.ot._get_type) { 37458 m.o.each(function () { 37459 if($.inArray(m.ot._get_type(this), vc) === -1) { d = false; return false; } 37460 }); 37461 if(d === false) { return false; } 37462 } 37463 if(s.max_children !== -2 && mc !== -1) { 37464 ch = m.cr === -1 ? this.get_container().find("> ul > li").not(m.o).length : m.cr.find("> ul > li").not(m.o).length; 37465 if(ch + m.o.length > mc) { return false; } 37466 } 37467 if(s.max_depth !== -2 && md !== -1) { 37468 d = 0; 37469 if(md === 0) { return false; } 37470 if(typeof m.o.d === "undefined") { 37471 // TODO: deal with progressive rendering and async when checking max_depth (how to know the depth of the moved node) 37472 t = m.o; 37473 while(t.length > 0) { 37474 t = t.find("> ul > li"); 37475 d ++; 37476 } 37477 m.o.d = d; 37478 } 37479 if(md - m.o.d < 0) { return false; } 37480 } 37481 return true; 37482 }, 37483 create_node : function (obj, position, js, callback, is_loaded, skip_check) { 37484 if(!skip_check && (is_loaded || this._is_loaded(obj))) { 37485 var p = (typeof position == "string" && position.match(/^before|after$/i) && obj !== -1) ? this._get_parent(obj) : this._get_node(obj), 37486 s = this._get_settings().types, 37487 mc = this._check("max_children", p), 37488 md = this._check("max_depth", p), 37489 vc = this._check("valid_children", p), 37490 ch; 37491 if(typeof js === "string") { js = { data : js }; } 37492 if(!js) { js = {}; } 37493 if(vc === "none") { return false; } 37494 if($.isArray(vc)) { 37495 if(!js.attr || !js.attr[s.type_attr]) { 37496 if(!js.attr) { js.attr = {}; } 37497 js.attr[s.type_attr] = vc[0]; 37498 } 37499 else { 37500 if($.inArray(js.attr[s.type_attr], vc) === -1) { return false; } 37501 } 37502 } 37503 if(s.max_children !== -2 && mc !== -1) { 37504 ch = p === -1 ? this.get_container().find("> ul > li").length : p.find("> ul > li").length; 37505 if(ch + 1 > mc) { return false; } 37506 } 37507 if(s.max_depth !== -2 && md !== -1 && (md - 1) < 0) { return false; } 37508 } 37509 return this.__call_old(true, obj, position, js, callback, is_loaded, skip_check); 37510 } 37511 } 37512 }); 37513})(jQuery); 37514//*/ 37515 37516/* 37517 * jsTree HTML plugin 37518 * The HTML data store. Datastores are build by replacing the `load_node` and `_is_loaded` functions. 37519 */ 37520(function ($) { 37521 $.jstree.plugin("html_data", { 37522 __init : function () { 37523 // this used to use html() and clean the whitespace, but this way any attached data was lost 37524 this.data.html_data.original_container_html = this.get_container().find(" > ul > li").clone(true); 37525 // remove white space from LI node - otherwise nodes appear a bit to the right 37526 this.data.html_data.original_container_html.find("li").andSelf().contents().filter(function() { return this.nodeType == 3; }).remove(); 37527 }, 37528 defaults : { 37529 data : false, 37530 ajax : false, 37531 correct_state : true 37532 }, 37533 _fn : { 37534 load_node : function (obj, s_call, e_call) { var _this = this; this.load_node_html(obj, function () { _this.__callback({ "obj" : _this._get_node(obj) }); s_call.call(this); }, e_call); }, 37535 _is_loaded : function (obj) { 37536 obj = this._get_node(obj); 37537 return obj == -1 || !obj || (!this._get_settings().html_data.ajax && !$.isFunction(this._get_settings().html_data.data)) || obj.is(".jstree-open, .jstree-leaf") || obj.children("ul").children("li").size() > 0; 37538 }, 37539 load_node_html : function (obj, s_call, e_call) { 37540 var d, 37541 s = this.get_settings().html_data, 37542 error_func = function () {}, 37543 success_func = function () {}; 37544 obj = this._get_node(obj); 37545 if(obj && obj !== -1) { 37546 if(obj.data("jstree_is_loading")) { return; } 37547 else { obj.data("jstree_is_loading",true); } 37548 } 37549 switch(!0) { 37550 case ($.isFunction(s.data)): 37551 s.data.call(this, obj, $.proxy(function (d) { 37552 if(d && d !== "" && d.toString && d.toString().replace(/^[\s\n]+$/,"") !== "") { 37553 d = $(d); 37554 if(!d.is("ul")) { d = $("<ul />").append(d); } 37555 if(obj == -1 || !obj) { this.get_container().children("ul").empty().append(d.children()).find("li, a").filter(function () { return !this.firstChild || !this.firstChild.tagName || this.firstChild.tagName !== "INS"; }).prepend("<ins class='jstree-icon'> </ins>").end().filter("a").children("ins:first-child").not(".jstree-icon").addClass("jstree-icon"); } 37556 else { obj.children("a.jstree-loading").removeClass("jstree-loading"); obj.append(d).children("ul").find("li, a").filter(function () { return !this.firstChild || !this.firstChild.tagName || this.firstChild.tagName !== "INS"; }).prepend("<ins class='jstree-icon'> </ins>").end().filter("a").children("ins:first-child").not(".jstree-icon").addClass("jstree-icon"); obj.removeData("jstree_is_loading"); } 37557 this.clean_node(obj); 37558 if(s_call) { s_call.call(this); } 37559 } 37560 else { 37561 if(obj && obj !== -1) { 37562 obj.children("a.jstree-loading").removeClass("jstree-loading"); 37563 obj.removeData("jstree_is_loading"); 37564 if(s.correct_state) {
37565 this.correct_state(obj); 37566 if(s_call) { s_call.call(this); } 37567 } 37568 } 37569 else { 37570 if(s.correct_state) { 37571 this.get_container().children("ul").empty(); 37572 if(s_call) { s_call.call(this); } 37573 } 37574 } 37575 } 37576 }, this)); 37577 break; 37578 case (!s.data && !s.ajax): 37579 if(!obj || obj == -1) { 37580 this.get_container() 37581 .children("ul").empty() 37582 .append(this.data.html_data.original_container_html) 37583 .find("li, a").filter(function () { return !this.firstChild || !this.firstChild.tagName || this.firstChild.tagName !== "INS"; }).prepend("<ins class='jstree-icon'> </ins>").end() 37584 .filter("a").children("ins:first-child").not(".jstree-icon").addClass("jstree-icon"); 37585 this.clean_node(); 37586 } 37587 if(s_call) { s_call.call(this); } 37588 break; 37589 case (!!s.data && !s.ajax) || (!!s.data && !!s.ajax && (!obj || obj === -1)): 37590 if(!obj || obj == -1) { 37591 d = $(s.data); 37592 if(!d.is("ul")) { d = $("<ul />").append(d); } 37593 this.get_container() 37594 .children("ul").empty().append(d.children()) 37595 .find("li, a").filter(function () { return !this.firstChild || !this.firstChild.tagName || this.firstChild.tagName !== "INS"; }).prepend("<ins class='jstree-icon'> </ins>").end() 37596 .filter("a").children("ins:first-child").not(".jstree-icon").addClass("jstree-icon"); 37597 this.clean_node(); 37598 } 37599 if(s_call) { s_call.call(this); } 37600 break; 37601 case (!s.data && !!s.ajax) || (!!s.data && !!s.ajax && obj && obj !== -1): 37602 obj = this._get_node(obj); 37603 error_func = function (x, t, e) { 37604 var ef = this.get_settings().html_data.ajax.error; 37605 if(ef) { ef.call(this, x, t, e); } 37606 if(obj != -1 && obj.length) { 37607 obj.children("a.jstree-loading").removeClass("jstree-loading"); 37608 obj.removeData("jstree_is_loading"); 37609 if(t === "success" && s.correct_state) { this.correct_state(obj); } 37610 } 37611 else { 37612 if(t === "success" && s.correct_state) { this.get_container().children("ul").empty(); } 37613 } 37614 if(e_call) { e_call.call(this); } 37615 }; 37616 success_func = function (d, t, x) { 37617 var sf = this.get_settings().html_data.ajax.success; 37618 if(sf) { d = sf.call(this,d,t,x) || d; } 37619 if(d === "" || (d && d.toString && d.toString().replace(/^[\s\n]+$/,"") === "")) { 37620 return error_func.call(this, x, t, ""); 37621 } 37622 if(d) { 37623 d = $(d); 37624 if(!d.is("ul")) { d = $("<ul />").append(d); } 37625 if(obj == -1 || !obj) { this.get_container().children("ul").empty().append(d.children()).find("li, a").filter(function () { return !this.firstChild || !this.firstChild.tagName || this.firstChild.tagName !== "INS"; }).prepend("<ins class='jstree-icon'> </ins>").end().filter("a").children("ins:first-child").not(".jstree-icon").addClass("jstree-icon"); } 37626 else { obj.children("a.jstree-loading").removeClass("jstree-loading"); obj.append(d).children("ul").find("li, a").filter(function () { return !this.firstChild || !this.firstChild.tagName || this.firstChild.tagName !== "INS"; }).prepend("<ins class='jstree-icon'> </ins>").end().filter("a").children("ins:first-child").not(".jstree-icon").addClass("jstree-icon"); obj.removeData("jstree_is_loading"); } 37627 this.clean_node(obj); 37628 if(s_call) { s_call.call(this); } 37629 } 37630 else { 37631 if(obj && obj !== -1) { 37632 obj.children("a.jstree-loading").removeClass("jstree-loading"); 37633 obj.removeData("jstree_is_loading"); 37634 if(s.correct_state) { 37635 this.correct_state(obj); 37636 if(s_call) { s_call.call(this); } 37637 } 37638 } 37639 else { 37640 if(s.correct_state) { 37641 this.get_container().children("ul").empty(); 37642 if(s_call) { s_call.call(this); } 37643 } 37644 } 37645 } 37646 }; 37647 s.ajax.context = this; 37648 s.ajax.error = error_func; 37649 s.ajax.success = success_func; 37650 if(!s.ajax.dataType) { s.ajax.dataType = "html"; } 37651 if($.isFunction(s.ajax.url)) { s.ajax.url = s.ajax.url.call(this, obj); } 37652 if($.isFunction(s.ajax.data)) { s.ajax.data = s.ajax.data.call(this, obj); } 37653 $.ajax(s.ajax); 37654 break; 37655 } 37656 } 37657 } 37658 }); 37659 // include the HTML data plugin by default 37660 $.jstree.defaults.plugins.push("html_data"); 37661})(jQuery);
37662//*/ 37663 37664/* 37665 * jsTree themeroller plugin 37666 * Adds support for jQuery UI themes. Include this at the end of your plugins list, also make sure "themes" is not included. 37667 */ 37668(function ($) { 37669 $.jstree.plugin("themeroller", { 37670 __init : function () { 37671 var s = this._get_settings().themeroller; 37672 this.get_container() 37673 .addClass("ui-widget-content") 37674 .addClass("jstree-themeroller") 37675 .delegate("a","mouseenter.jstree", function (e) { 37676 if(!$(e.currentTarget).hasClass("jstree-loading")) { 37677 $(this).addClass(s.item_h); 37678 } 37679 }) 37680 .delegate("a","mouseleave.jstree", function () { 37681 $(this).removeClass(s.item_h); 37682 }) 37683 .bind("init.jstree", $.proxy(function (e, data) { 37684 data.inst.get_container().find("> ul > li > .jstree-loading > ins").addClass("ui-icon-refresh"); 37685 this._themeroller(data.inst.get_container().find("> ul > li")); 37686 }, this)) 37687 .bind("open_node.jstree create_node.jstree", $.proxy(function (e, data) { 37688 this._themeroller(data.rslt.obj); 37689 }, this)) 37690 .bind("loaded.jstree refresh.jstree", $.proxy(function (e) { 37691 this._themeroller(); 37692 }, this)) 37693 .bind("close_node.jstree", $.proxy(function (e, data) { 37694 this._themeroller(data.rslt.obj); 37695 }, this)) 37696 .bind("delete_node.jstree", $.proxy(function (e, data) { 37697 this._themeroller(data.rslt.parent); 37698 }, this)) 37699 .bind("correct_state.jstree", $.proxy(function (e, data) { 37700 data.rslt.obj 37701 .children("ins.jstree-icon").removeClass(s.opened + " " + s.closed + " ui-icon").end() 37702 .find("> a > ins.ui-icon") 37703 .filter(function() { 37704 return this.className.toString() 37705 .replace(s.item_clsd,"").replace(s.item_open,"").replace(s.item_leaf,"") 37706 .indexOf("ui-icon-") === -1; 37707 }).removeClass(s.item_open + " " + s.item_clsd).addClass(s.item_leaf || "jstree-no-icon"); 37708 }, this)) 37709 .bind("select_node.jstree", $.proxy(function (e, data) { 37710 data.rslt.obj.children("a").addClass(s.item_a); 37711 }, this)) 37712 .bind("deselect_node.jstree deselect_all.jstree", $.proxy(function (e, data) { 37713 this.get_container() 37714 .find("a." + s.item_a).removeClass(s.item_a).end() 37715 .find("a.jstree-clicked").addClass(s.item_a); 37716 }, this)) 37717 .bind("dehover_node.jstree", $.proxy(function (e, data) { 37718 data.rslt.obj.children("a").removeClass(s.item_h); 37719 }, this)) 37720 .bind("hover_node.jstree", $.proxy(function (e, data) { 37721 this.get_container() 37722 .find("a." + s.item_h).not(data.rslt.obj).removeClass(s.item_h); 37723 data.rslt.obj.children("a").addClass(s.item_h); 37724 }, this)) 37725 .bind("move_node.jstree", $.proxy(function (e, data) { 37726 this._themeroller(data.rslt.o); 37727 this._themeroller(data.rslt.op); 37728 }, this)); 37729 }, 37730 __destroy : function () { 37731 var s = this._get_settings().themeroller, 37732 c = [ "ui-icon" ]; 37733 $.each(s, function (i, v) { 37734 v = v.split(" "); 37735 if(v.length) { c = c.concat(v); } 37736 }); 37737 this.get_container() 37738 .removeClass("ui-widget-content") 37739 .find("." + c.join(", .")).removeClass(c.join(" ")); 37740 }, 37741 _fn : { 37742 _themeroller : function (obj) { 37743 var s = this._get_settings().themeroller; 37744 obj = !obj || obj == -1 ? this.get_container_ul() : this._get_node(obj).parent(); 37745 obj 37746 .find("li.jstree-closed") 37747 .children("ins.jstree-icon").removeClass(s.opened).addClass("ui-icon " + s.closed).end() 37748 .children("a").addClass(s.item) 37749 .children("ins.jstree-icon").addClass("ui-icon") 37750 .filter(function() { 37751 return this.className.toString() 37752 .replace(s.item_clsd,"").replace(s.item_open,"").replace(s.item_leaf,"") 37753 .indexOf("ui-icon-") === -1; 37754 }).removeClass(s.item_leaf + " " + s.item_open).addClass(s.item_clsd || "jstree-no-icon") 37755 .end() 37756 .end() 37757 .end() 37758 .end() 37759 .find("li.jstree-open") 37760 .children("ins.jstree-icon").removeClass(s.closed).addClass("ui-icon " + s.opened).end() 37761 .children("a").addClass(s.item) 37762 .children("ins.jstree-icon").addClass("ui-icon") 37763 .filter(function() { 37764 return this.className.toString() 37765 .replace(s.item_clsd,"").replace(s.item_open,"").replace(s.item_leaf,"") 37766 .indexOf("ui-icon-") === -1; 37767 }).removeClass(s.item_leaf + " " + s.item_clsd).addClass(s.item_open || "jstree-no-icon") 37768 .end() 37769 .end() 37770 .end() 37771 .end() 37772 .find("li.jstree-leaf") 37773 .children("ins.jstree-icon").removeClass(s.closed + " ui-icon " + s.opened).end() 37774 .children("a").addClass(s.item) 37775 .children("ins.jstree-icon").addClass("ui-icon") 37776 .filter(function() { 37777 return this.className.toString() 37778 .replace(s.item_clsd,"").replace(s.item_open,"").replace(s.item_leaf,"") 37779 .indexOf("ui-icon-") === -1; 37780 }).removeClass(s.item_clsd + " " + s.item_open).addClass(s.item_leaf || "jstree-no-icon"); 37781 } 37782 }, 37783 defaults : { 37784 "opened" : "ui-icon-triangle-1-se", 37785 "closed" : "ui-icon-triangle-1-e", 37786 "item" : "ui-state-default", 37787 "item_h" : "ui-state-hover", 37788 "item_a" : "ui-state-active", 37789 "item_open" : "ui-icon-folder-open", 37790 "item_clsd" : "ui-icon-folder-collapsed", 37791 "item_leaf" : "ui-icon-document" 37792 } 37793 }); 37794 $(function() { 37795 var css_string = '' + 37796 '.jstree-themeroller .ui-icon { overflow:visible; } ' + 37797 '.jstree-themeroller a { padding:0 2px; } ' +
37798 '.jstree-themeroller .jstree-no-icon { display:none; }'; 37799 $.vakata.css.add_sheet({ str : css_string, title : "jstree" }); 37800 }); 37801})(jQuery); 37802//*/ 37803 37804/* 37805 * jsTree unique plugin 37806 * Forces different names amongst siblings (still a bit experimental) 37807 * NOTE: does not check language versions (it will not be possible to have nodes with the same title, even in different languages) 37808 */ 37809(function ($) { 37810 $.jstree.plugin("unique", { 37811 __init : function () { 37812 this.get_container() 37813 .bind("before.jstree", $.proxy(function (e, data) { 37814 var nms = [], res = true, p, t; 37815 if(data.func == "move_node") { 37816 // obj, ref, position, is_copy, is_prepared, skip_check 37817 if(data.args[4] === true) { 37818 if(data.args[0].o && data.args[0].o.length) { 37819 data.args[0].o.children("a").each(function () { nms.push($(this).text().replace(/^\s+/g,"")); }); 37820 res = this._check_unique(nms, data.args[0].np.find("> ul > li").not(data.args[0].o), "move_node"); 37821 } 37822 } 37823 } 37824 if(data.func == "create_node") { 37825 // obj, position, js, callback, is_loaded 37826 if(data.args[4] || this._is_loaded(data.args[0])) { 37827 p = this._get_node(data.args[0]); 37828 if(data.args[1] && (data.args[1] === "before" || data.args[1] === "after")) { 37829 p = this._get_parent(data.args[0]); 37830 if(!p || p === -1) { p = this.get_container(); } 37831 } 37832 if(typeof data.args[2] === "string") { nms.push(data.args[2]); } 37833 else if(!data.args[2] || !data.args[2].data) { nms.push(this._get_string("new_node")); } 37834 else { nms.push(data.args[2].data); } 37835 res = this._check_unique(nms, p.find("> ul > li"), "create_node"); 37836 } 37837 } 37838 if(data.func == "rename_node") { 37839 // obj, val 37840 nms.push(data.args[1]); 37841 t = this._get_node(data.args[0]); 37842 p = this._get_parent(t); 37843 if(!p || p === -1) { p = this.get_container(); } 37844 res = this._check_unique(nms, p.find("> ul > li").not(t), "rename_node"); 37845 } 37846 if(!res) { 37847 e.stopPropagation(); 37848 return false; 37849 } 37850 }, this)); 37851 }, 37852 defaults : { 37853 error_callback : $.noop 37854 }, 37855 _fn : { 37856 _check_unique : function (nms, p, func) { 37857 var cnms = []; 37858 p.children("a").each(function () { cnms.push($(this).text().replace(/^\s+/g,"")); }); 37859 if(!cnms.length || !nms.length) { return true; } 37860 cnms = cnms.sort().join(",,").replace(/(,|^)([^,]+)(,,\2)+(,|$)/g,"$1$2$4").replace(/,,+/g,",").replace(/,$/,"").split(","); 37861 if((cnms.length + nms.length) != cnms.concat(nms).sort().join(",,").replace(/(,|^)([^,]+)(,,\2)+(,|$)/g,"$1$2$4").replace(/,,+/g,",").replace(/,$/,"").split(",").length) { 37862 this._get_settings().unique.error_callback.call(null, nms, p, func); 37863 return false; 37864 } 37865 return true; 37866 }, 37867 check_move : function () { 37868 if(!this.__call_old()) { return false; } 37869 var p = this._get_move(), nms = []; 37870 if(p.o && p.o.length) { 37871 p.o.children("a").each(function () { nms.push($(this).text().replace(/^\s+/g,"")); }); 37872 return this._check_unique(nms, p.np.find("> ul > li").not(p.o), "check_move"); 37873 } 37874 return true; 37875 } 37876 } 37877 }); 37878})(jQuery); 37879//*/ 37880 37881/* 37882 * jsTree wholerow plugin 37883 * Makes select and hover work on the entire width of the node 37884 * MAY BE HEAVY IN LARGE DOM 37885 */ 37886(function ($) { 37887 $.jstree.plugin("wholerow", { 37888 __init : function () { 37889 if(!this.data.ui) { throw "jsTree wholerow: jsTree UI plugin not included."; } 37890 this.data.wholerow.html = false; 37891 this.data.wholerow.to = false; 37892 this.get_container() 37893 .bind("init.jstree", $.proxy(function (e, data) { 37894 this._get_settings().core.animation = 0; 37895 }, this)) 37896 .bind("open_node.jstree create_node.jstree clean_node.jstree loaded.jstree", $.proxy(function (e, data) { 37897 this._prepare_wholerow_span( data && data.rslt && data.rslt.obj ? data.rslt.obj : -1 ); 37898 }, this)) 37899 .bind("search.jstree clear_search.jstree reopen.jstree after_open.jstree after_close.jstree create_node.jstree delete_node.jstree clean_node.jstree", $.proxy(function (e, data) { 37900 if(this.data.to) { clearTimeout(this.data.to); } 37901 this.data.to = setTimeout( (function (t, o) { return function() { t._prepare_wholerow_ul(o); }; })(this, data && data.rslt && data.rslt.obj ? data.rslt.obj : -1), 0); 37902 }, this)) 37903 .bind("deselect_all.jstree", $.proxy(function (e, data) { 37904 this.get_container().find(" > .jstree-wholerow .jstree-clicked").removeClass("jstree-clicked " + (this.data.themeroller ? this._get_settings().themeroller.item_a : "" )); 37905 }, this)) 37906 .bind("select_node.jstree deselect_node.jstree ", $.proxy(function (e, data) { 37907 data.rslt.obj.each(function () { 37908 var ref = data.inst.get_container().find(" > .jstree-wholerow li:visible:eq(" + ( parseInt((($(this).offset().top - data.inst.get_container().offset().top + data.inst.get_container()[0].scrollTop) / data.inst.data.core.li_height),10)) + ")"); 37909 // ref.children("a")[e.type === "select_node" ? "addClass" : "removeClass"]("jstree-clicked"); 37910 ref.children("a").attr("class",data.rslt.obj.children("a").attr("class")); 37911 });
37912 }, this)) 37913 .bind("hover_node.jstree dehover_node.jstree", $.proxy(function (e, data) { 37914 this.get_container().find(" > .jstree-wholerow .jstree-hovered").removeClass("jstree-hovered " + (this.data.themeroller ? this._get_settings().themeroller.item_h : "" )); 37915 if(e.type === "hover_node") { 37916 var ref = this.get_container().find(" > .jstree-wholerow li:visible:eq(" + ( parseInt(((data.rslt.obj.offset().top - this.get_container().offset().top + this.get_container()[0].scrollTop) / this.data.core.li_height),10)) + ")"); 37917 // ref.children("a").addClass("jstree-hovered"); 37918 ref.children("a").attr("class",data.rslt.obj.children(".jstree-hovered").attr("class")); 37919 } 37920 }, this)) 37921 .delegate(".jstree-wholerow-span, ins.jstree-icon, li", "click.jstree", function (e) { 37922 var n = $(e.currentTarget); 37923 if(e.target.tagName === "A" || (e.target.tagName === "INS" && n.closest("li").is(".jstree-open, .jstree-closed"))) { return; } 37924 n.closest("li").children("a:visible:eq(0)").click(); 37925 e.stopImmediatePropagation(); 37926 }) 37927 .delegate("li", "mouseover.jstree", $.proxy(function (e) { 37928 e.stopImmediatePropagation(); 37929 if($(e.currentTarget).children(".jstree-hovered, .jstree-clicked").length) { return false; } 37930 this.hover_node(e.currentTarget); 37931 return false; 37932 }, this)) 37933 .delegate("li", "mouseleave.jstree", $.proxy(function (e) { 37934 if($(e.currentTarget).children("a").hasClass("jstree-hovered").length) { return; } 37935 this.dehover_node(e.currentTarget); 37936 }, this)); 37937 if(is_ie7 || is_ie6) { 37938 $.vakata.css.add_sheet({ str : ".jstree-" + this.get_index() + " { position:relative; } ", title : "jstree" }); 37939 } 37940 }, 37941 defaults : { 37942 }, 37943 __destroy : function () { 37944 this.get_container().children(".jstree-wholerow").remove(); 37945 this.get_container().find(".jstree-wholerow-span").remove(); 37946 }, 37947 _fn : { 37948 _prepare_wholerow_span : function (obj) { 37949 obj = !obj || obj == -1 ? this.get_container().find("> ul > li") : this._get_node(obj); 37950 if(obj === false) { return; } // added for removing root nodes 37951 obj.each(function () { 37952 $(this).find("li").andSelf().each(function () { 37953 var $t = $(this); 37954 if($t.children(".jstree-wholerow-span").length) { return true; } 37955 $t.prepend("<span class='jstree-wholerow-span' style='width:" + ($t.parentsUntil(".jstree","li").length * 18) + "px;'> </span>"); 37956 }); 37957 }); 37958 }, 37959 _prepare_wholerow_ul : function () { 37960 var o = this.get_container().children("ul").eq(0), h = o.html(); 37961 o.addClass("jstree-wholerow-real"); 37962 if(this.data.wholerow.last_html !== h) { 37963 this.data.wholerow.last_html = h; 37964 this.get_container().children(".jstree-wholerow").remove(); 37965 this.get_container().append( 37966 o.clone().removeClass("jstree-wholerow-real") 37967 .wrapAll("<div class='jstree-wholerow' />").parent() 37968 .width(o.parent()[0].scrollWidth) 37969 .css("top", (o.height() + ( is_ie7 ? 5 : 0)) * -1 ) 37970 .find("li[id]").each(function () { this.removeAttribute("id"); }).end() 37971 ); 37972 } 37973 } 37974 } 37975 }); 37976 $(function() { 37977 var css_string = '' + 37978 '.jstree .jstree-wholerow-real { position:relative; z-index:1; } ' + 37979 '.jstree .jstree-wholerow-real li { cursor:pointer; } ' + 37980 '.jstree .jstree-wholerow-real a { border-left-color:transparent !important; border-right-color:transparent !important; } ' + 37981 '.jstree .jstree-wholerow { position:relative; z-index:0; height:0; } ' + 37982 '.jstree .jstree-wholerow ul, .jstree .jstree-wholerow li { width:100%; } ' + 37983 '.jstree .jstree-wholerow, .jstree .jstree-wholerow ul, .jstree .jstree-wholerow li, .jstree .jstree-wholerow a { margin:0 !important; padding:0 !important; } ' + 37984 '.jstree .jstree-wholerow, .jstree .jstree-wholerow ul, .jstree .jstree-wholerow li { background:transparent !important; }' + 37985 '.jstree .jstree-wholerow ins, .jstree .jstree-wholerow span, .jstree .jstree-wholerow input { display:none !important; }' +
37986 '.jstree .jstree-wholerow a, .jstree .jstree-wholerow a:hover { text-indent:-9999px; !important; width:100%; padding:0 !important; border-right-width:0px !important; border-left-width:0px !important; } ' + 37987 '.jstree .jstree-wholerow-span { position:absolute; left:0; margin:0px; padding:0; height:18px; border-width:0; padding:0; z-index:0; }'; 37988 if(is_ff2) { 37989 css_string += '' + 37990 '.jstree .jstree-wholerow a { display:block; height:18px; margin:0; padding:0; border:0; } ' + 37991 '.jstree .jstree-wholerow-real a { border-color:transparent !important; } '; 37992 } 37993 if(is_ie7 || is_ie6) { 37994 css_string += '' + 37995 '.jstree .jstree-wholerow, .jstree .jstree-wholerow li, .jstree .jstree-wholerow ul, .jstree .jstree-wholerow a { margin:0; padding:0; line-height:18px; } ' + 37996 '.jstree .jstree-wholerow a { display:block; height:18px; line-height:18px; overflow:hidden; } '; 37997 } 37998 $.vakata.css.add_sheet({ str : css_string, title : "jstree" }); 37999 }); 38000})(jQuery); 38001//*/ 38002 38003/* 38004* jsTree model plugin 38005* This plugin gets jstree to use a class model to retrieve data, creating great dynamism 38006*/ 38007(function ($) { 38008 var nodeInterface = ["getChildren","getChildrenCount","getAttr","getName","getProps"], 38009 validateInterface = function(obj, inter) { 38010 var valid = true; 38011 obj = obj || {}; 38012 inter = [].concat(inter); 38013 $.each(inter, function (i, v) { 38014 if(!$.isFunction(obj[v])) { valid = false; return false; } 38015 }); 38016 return valid; 38017 }; 38018 $.jstree.plugin("model", { 38019 __init : function () { 38020 if(!this.data.json_data) { throw "jsTree model: jsTree json_data plugin not included."; } 38021 this._get_settings().json_data.data = function (n, b) { 38022 var obj = (n == -1) ? this._get_settings().model.object : n.data("jstree_model"); 38023 if(!validateInterface(obj, nodeInterface)) { return b.call(null, false); } 38024 if(this._get_settings().model.async) { 38025 obj.getChildren($.proxy(function (data) { 38026 this.model_done(data, b); 38027 }, this)); 38028 } 38029 else { 38030 this.model_done(obj.getChildren(), b); 38031 } 38032 }; 38033 }, 38034 defaults : { 38035 object : false, 38036 id_prefix : false, 38037 async : false 38038 }, 38039 _fn : { 38040 model_done : function (data, callback) { 38041 var ret = [], 38042 s = this._get_settings(), 38043 _this = this; 38044 38045 if(!$.isArray(data)) { data = [data]; } 38046 $.each(data, function (i, nd) { 38047 var r = nd.getProps() || {}; 38048 r.attr = nd.getAttr() || {}; 38049 if(nd.getChildrenCount()) { r.state = "closed"; } 38050 r.data = nd.getName(); 38051 if(!$.isArray(r.data)) { r.data = [r.data]; } 38052 if(_this.data.types && $.isFunction(nd.getType)) { 38053 r.attr[s.types.type_attr] = nd.getType(); 38054 } 38055 if(r.attr.id && s.model.id_prefix) { r.attr.id = s.model.id_prefix + r.attr.id; } 38056 if(!r.metadata) { r.metadata = { }; } 38057 r.metadata.jstree_model = nd; 38058 ret.push(r); 38059 }); 38060 callback.call(null, ret); 38061 } 38062 } 38063 }); 38064})(jQuery); 38065//*/ 38066 38067})(); 38068/** 38069 * Provides UI & data management for an ontology tree view. 38070 * 38071 * Requires AIBS.DATA.API_DAO and will throw if it is not present. 38072 * 38073 * All of the data access methods are contained within this widget. 38074 * 38075 * <p></p> 38076 * @class 38077 * @param container: The jquery selector for (or jquery object that is) the container for this widget. 38078 * @param ontology: An object that should contain at least these fields: 38079 * ontology_id: The database ID of the ontology to show. 38080 * root_node_id:The database ID of the node that is the parent of all the nodes to be displayed. 38081 * in this control. The root node will not itself be visible. 38082 * @param data_service_url The base url for the data service API 38083 * @param options: A hash of the following (work in progress): 38084 * <ul> 38085 * <li> 38086 * <b>included_fields</b>: Fields that are included in the RMA request for structure nodes. Default = "acronym,color_hex_triplet,hemisphere_id,id,name" 38087 * </li> 38088 * <li> 38089 * <b>animation_time</b>: The time in milliseconds to animate a node opening. Default = 100. 38090 * </li> 38091 * <li> 38092 * <b>on_ready</b>: A callback function invoked when data has been fetched and is ready to use. on_ready(this, data). 38093 * </li> 38094 * <li> 38095 * <b>on_error</b>: A callback function invoked when an error is encountered while fetching data. on_error(this, error). 38096 * </li> 38097 * <li> 38098 * <b>section_thickness_in_microns</b>: Default = 100. 38099 * </li> 38100 * <li> 38101 * <b>sort_field</b>: Default = section_number 38102 * </li> 38103 * <li> 38104 * <b>sort_direction</b>: Default = 'asc' 38105 * </li> 38106 * <li> 38107 * <b>others</b>: TBD 38108 * </li> 38109 * </ul> 38110**/ 38111 38112function ontology_widget(container, ontology, data_service_url, options) { 38113 38114 this.container = $j(container); 38115 this.ontology_id = ontology.id; 38116 this.root_node_id = ontology.root_structure_id; 38117 this.data_service_url = data_service_url; 38118 this.settings = { 38119 38120 "multi_select":true, 38121 "ontology_data":null, 38122 "included_fields":"acronym,color_hex_triplet,hemisphere_id,id,name", 38123 "animation_time":1000, 38124 38125 "themes":{ 38126 "theme":"classic", 38127 "url":"/assets/aiclient/vendor/jstree/themes/classic/style.css", 38128 "dots":true, 38129 "icons":false 38130 }, 38131 38132 on_node_error:null, 38133 on_node_select:null, 38134 on_node_load:null, 38135 on_node_open:null, 38136 on_tree_create:null, 38137 on_tree_load:null 38138 }; 38139 $j.extend(true, this.settings, options); 38140 38141 38142 /** 38143 * Open the tree to the given structure and select it. A node_select event will 38144 * be raised. 38145 * 38146 * @param structure_id 38147 */ 38148 this.open_structure = function(structure_id) { 38149 38150 38151 }; 38152 38153 /**
38154 * Apply class names to apply to nodes by structure_id 38155 * 38156 * @param structure_id A hash indexed by structure_id and returning class name. 38157 */ 38158 this.set_structure_states = function(structure_states) { 38159 38160 38161 }; 38162 38163 this.select_none = function() { 38164 38165 if(_inner_container) 38166 _inner_container.jstree('deselect_all'); 38167 }; 38168 38169 this.scroll_to_structure = function() { 38170 38171 }; 38172 38173 this.highlight_none = function() { 38174 38175 }; 38176 38177 38178 this.select_at_highlight = function() { 38179 38180 }; 38181 38182 38183 this.remove_highlight = function() { 38184 38185 38186 }; 38187 38188 /** 38189 * Opens the given path. The final node will be selected and a select event will be raised. 38190 * 38191 * @param path It is expected that this path will have the form '/1/2/3/4/'. Note 38192 * the leading and trailing slashes. 38193 * 38194 * @param target_id The ID of the final structure in the path. This structure will be 38195 * selected when it is exposed. 38196 */ 38197 this.open_structure_path = function(path, target_id) { 38198 38199 _center_select = true; 38200 _prevent_select_signal = true; 38201 _opening_target_structure = target_id; 38202 open_structure_id_path(path); 38203 38204 } 38205 38206 this.deselect_structure = function(structure_id) { 38207 38208 var node = $j('#'+structure_id + _id); 38209 _inner_container.jstree('deselect_node', node); 38210 } 38211 38212 38213 var _self = this; 38214 var _id = this.container.attr('id'); 38215 38216 var _outer_container = null; 38217 var _inner_container = null; 38218 var _search_container = null; 38219 38220 var _opening_path = null; 38221 var _opening_target_structure = 0; 38222 var _select_node = null; 38223 var _center_select = false; 38224 var _prevent_select_signal = false; 38225 38226 38227 38228 function build_scaffold() { 38229 38230 var html = "<div id='" + _id + "_outer_container' class='_ontology_outer_container'>"; 38231 html += "<div id='" + _id + "_inner_container' class='_ontology_inner_container'></div>"; 38232 html += "</div>"; 38233 38234 $j(html).appendTo(_self.container); 38235 _outer_container = $j("#" + _id + "_outer_container"); 38236 _inner_container = $j("#" + _id + "_inner_container"); 38237 } 38238 38239 function open_structure_id_path(path, prevent_signal) { 38240 38241 if(prevent_signal) 38242 _prevent_select_signal = true; 38243 38244 var path_len = path.length; 38245 if(!path || path_len < 3) 38246 return; 38247 38248 // get rid of the leading and trailing slashes and turn the string into an array. 38249 _opening_path = path.slice(1,path_len-1).split('/'); 38250 38251 // all of the paths start with the root node which is always open, so get rid of it. 38252 _opening_path.shift(); 38253 open_branch(); 38254 } 38255 38256 // opens (& removes) the top node in the _opening_path array. 38257 // Because fetching the node may happen asynchronously, 38258 // we can't just recurse through the tree here. This function 38259 // will be called again when the node's 'opened' event fires 38260 // so that the next node in the _opening_path array can 38261 // also be opened, and so forth. 38262 function open_branch() { 38263 38264 if(!_opening_path || !_opening_path.length) 38265 return; 38266 38267 var structure_id = _opening_path.shift(); 38268 var node = $j('#'+structure_id + _id); 38269 38270 // if we've reached the target structure we don't 38271 // need to see it's children, so stop here. 38272 if(structure_id == _opening_target_structure) { 38273 38274 if(!_self.settings.multi_select) 38275 _inner_container.jstree('deselect_all'); 38276 38277 // davidf: jstree will deselect a node that is already 38278 // selected. assuming that's not what we want here. 38279 if (!_inner_container.jstree('is_selected', node)) { 38280 _inner_container.jstree('select_node', node, true); 38281 } 38282 38283 if(_center_select) { 38284 center_selection(node); 38285 _center_select = false; 38286 } 38287 38288 _opening_path = null; 38289 _select_node = null; 38290 _opening_target_structure = 0; 38291 return; 38292 } 38293 38294 // we got this far, so open the node. This may invoke an 38295 // ajax call to fetch mode node data. If it does, this open_branch 38296 // function will be called again by the node's 'open' event handler. 38297 var is_open = _inner_container.jstree('is_open', node); 38298 _inner_container.jstree('open_node', node); 38299 38300 // if the node was already open we're not going to get an 'opened' 38301 // event, so keep opening here 38302 if(is_open) 38303 open_branch(); 38304 } 38305 38306 // Create the jsTree widget, bind handlers, etc. 38307 function build_ontology() { 38308 38309 var config = { 38310 "json_data":{}, 38311 "core":{ 38312 "html_titles":true, 38313 "animation":80 38314 }, 38315 "ui":{ 38316 "select_limit":_self.settings.multi_select ? -1 : 1, 38317 "select_multiple_modifier":_self.settings.multi_select ? "on" : "ctrl" 38318 }, 38319 "themes":_self.settings.themes, 38320 "plugins":[ "themes", "json_data", "ui"] 38321 }; 38322 38323 if (_self.settings.ontology_data) 38324 config.json_data.data = do_data;//_self.settings.ontology_data; 38325 38326 _inner_container.jstree(config) 38327 .bind("select_node.jstree", function (e, data) { 38328
38329 if (_self.settings.on_node_select && !_prevent_select_signal) 38330 _self.settings.on_node_select(data.rslt.obj.data("id").replace(_id,''), true); 38331 38332 _prevent_select_signal = false; 38333 }) 38334 .bind("deselect_node.jstree", function (e, data) { 38335 38336 if (_self.settings.on_node_select && !_prevent_select_signal) 38337 _self.settings.on_node_select(data.rslt.obj.data("id").replace(_id,''), false); 38338 38339 _prevent_select_signal = false; 38340 }) 38341 .bind("load_node.jstree", function (e, data) { 38342 38343 var container; 38344 38345 if (data.rslt.obj != -1) 38346 container = data.rslt.obj; 38347 else 38348 container = data.inst.get_container(); 38349 38350 // we need some info from the node data to properly color it. 38351 decorate_nodes(container); 38352 38353 if (_self.settings.on_node_load) 38354 _self.settings.on_node_load(container); 38355 }) 38356 .bind("open_node.jstree", function (e, data) { 38357 38358 open_branch(); 38359 if (_self.settings.on_node_open) 38360 _self.settings.on_node_open(data.rslt.obj.attr("id")); 38361 }) 38362 .bind("create.jstree", function (e, data) { 38363 38364 if (_self.settings.on_tree_create) 38365 _self.settings.on_tree_create(data); 38366 }) 38367 .bind("loaded.jstree", function (e, data) { 38368 38369 if (_self.settings.on_tree_load) 38370 _self.settings.on_tree_load(data); 38371 }); 38372 } 38373 38374 function do_data(node, callback) { 38375 38376 var id = node == -1 ? _self.root_node_id : node.attr("id").replace(_id,''); 38377 var data = _self.settings.ontology_data(id); 38378 38379 callback(format_node_data(data, [])); 38380 } 38381 38382 function center_selection(node) { 38383 38384 var pos = node.position(); 38385 if(!pos) 38386 return; 38387 38388 var scroll = _self.container.scrollTop(); 38389 var center_pos = _self.container.height()/2; 38390 var node_top = pos.top; 38391 // ignoring the height of the element; it's only 10px or so. 38392 var new_scroll = scroll - (center_pos - node_top); 38393 _self.container.scrollTop(new_scroll); 38394 } 38395 38396 38397 // Sets the color of the node. The color value is available as an attribute of 38398 // the node because we included the color_hex_triplet field in our RMA for fetching nodes, 38399 // and then we specified that the color_hex_triplet data be attached to the node as an 38400 // attribute in the format_node_data callback (see below.) 38401 function decorate_nodes(container) { 38402 38403 $j("#" + container.attr("id") + " ins").each(function(index, el) { 38404 38405 var a = $j(el.parentNode).find("a").first(); 38406 if(a && a.length) { 38407 38408 var color = a.attr("color"); 38409 if(color) 38410 $j(el).css({backgroundColor:color, border:'0px solid #f00', width:'14px', height:'14px'}); 38411 38412 var anno_state = a.attr('annotation_state'); 38413 if(anno_state) 38414 $j(a).addClass(anno_state); 38415 } 38416 }); 38417 } 38418 38419 // Transforms the data returned by the ajax call into 38420 // the format expected by the jsTree widget. 38421 function format_node_data(data) { 38422 38423 if(!data) 38424 return([]); 38425 38426 var formatted_nodes = []; 38427 for(var i = 0; i < data.length; i++) { 38428 38429 var state = data[i].child_structures && data[i].child_structures.length ? "closed" : "none"; 38430 formatted_nodes.push({ 38431 data:{ 38432 title:"<b>"+data[i].acronym+"</b> " + data[i].name, 38433 // include the color as an attribute. 38434 attr:{ 38435 color:'#'+data[i].color, 38436 annotation_state:false//data[i].annotation_state 38437 } 38438 }, 38439 state:state, 38440 attr:{id:(data[i].structure_id+_id)}, 38441 metadata:{ 38442 id:(data[i].structure_id+_id) 38443 } 38444 }); 38445 } 38446 38447 return(formatted_nodes); 38448 } 38449
38450 function init() { 38451 38452 build_scaffold(); 38453 build_ontology(); 38454 } 38455 38456 init(); 38457} 38458; 38459 38460AIBS.UI.ontology_tag_container = (function($j) { 38461 38462 return ( 38463 38464 function(container, options) { 38465 38466 this.container = $j(container); 38467 this.settings = { 38468 on_autocomplete:null, 38469 on_tag_added:null, 38470 on_tag_removed:null, 38471 default_text:'' 38472 }; 38473 $j.extend(true, this.settings, options); 38474 38475 38476 /** 38477 * add a tag to the container 38478 * @param tag {object} Like this: {label:'foo', value:'bar'} 38479 */ 38480 this.add_tag = function(tag) { 38481 38482 this.container.addTag(tag); 38483 } 38484 38485 /** 38486 * remove a tag from the container 38487 * @param tag {object} Like this: {label:'foo', value:'bar'} 38488 */ 38489 this.remove_tag = function(tag) { 38490 38491 this.container.removeTag(tag); 38492 } 38493 38494 /** 38495 * get an array of all the tags in the container 38496 * @returns {array} Array of tag objects 38497 */ 38498 this.get_tags = function() { 38499 38500 return(this.get_displayed_text().split(';')); 38501 } 38502 38503 /** 38504 * returns the text that's displayed in the container, which in some renderings 38505 * might be different than the set of labels in the tag collection. 38506 */ 38507 this.get_displayed_text = function() { 38508 38509 return(this.container.val()); 38510 } 38511 38512 /** 38513 * removes all tags from the collection 38514 */ 38515 this.clear = function() { 38516 38517 this.container.importTags(''); 38518 } 38519 38520 this.get_container = function() { 38521 38522 // after the tagsinput control is instantiated the original container is 38523 // no longer viable. 38524 return(this.actual_container); 38525 } 38526 38527 this.destroy = function() { 38528 38529 _self.container.tagsInput.destroy(); 38530 } 38531 38532 /** 38533 * @private 38534 * @scope AIBS.UI.tag_container 38535 */ 38536 38537 var _self = this; 38538 38539 38540 function init() { 38541 _self.container.tagsInput({ 38542 width:_self.container.outerWidth(), 38543 height:_self.container.outerHeight(), 38544 defaultText:_self.settings.default_text, 38545 delimiter:';', 38546 onAddTag:do_add_tag, 38547 onRemoveTag:do_remove_tag, 38548 // the autocomplete_url field must be included or the autocomplete:{} field will be ignored. 38549 autocomplete_url:'', 38550 autocomplete:{ 38551 source:_self.settings.on_autocomplete, 38552 autoFocus:false, 38553 position:{ 38554 collision:'flip' 38555 } 38556 } 38557 }); 38558 // the tagsinput control takes over the original control, so re-adjust here to reflect reality. 38559 _self.actual_container = $j('#' + _self.container.attr('id') + '_tagsinput'); 38560 } 38561 38562 function do_add_tag(tag_list) { 38563 38564 if(_self.settings.on_tag_added) 38565 _self.settings.on_tag_added(tag_list); 38566 } 38567 38568 function do_remove_tag(tag_list) { 38569 38570 if(_self.settings.on_tag_removed) 38571 _self.settings.on_tag_removed(tag_list); 38572 } 38573 38574 init(); 38575 } 38576 38577 ); 38578 38579 38580})(jQuery); 38581(function ($) { 38582 38583 var methods = { 38584 init:function (ontology_model, options) { 38585 38586 var settings = { 38587 height:400, 38588 width:null, 38589 view_options:{}, 38590 default_text:'', 38591 title_text:'Click below to select', 38592 close_text:'Close', 38593 delimiters:[';', ','], 38594 contains:true, 38595 initial_acronyms:'', 38596 horizontal_align:'left', 38597 closer:'icon', 38598 close_icon:'ui-icon-closethick', 38599 limit_single_selection:false, 38600 default_open_nodes:{"TH":true, "CTX":true, "MBmot":true, "MY":true}, 38601 on_open:null, 38602 on_close:null, 38603 on_add_structure:null, 38604 on_remove_structure:null, 38605 on_ready:null, 38606 tag_key:'acronym', 38607 zIndex: 2000 38608 }; 38609 if (options) 38610 $.extend(settings, options); 38611 38612 38613 return this.each(function () { 38614 38615 var $this = $(this); 38616 var _data = $this.data('ontology_autocomplete'); 38617 38618 if (!_data) { 38619 38620 var _id = Math.floor(Math.random() * 10001); 38621 var _list_class = "_ontology_autocomplete_div"; 38622 var _list_container = null; 38623 var _inner_container = null; 38624 var _visible = false;
38625 var _ontology_view = null; 38626 var _model = ontology_model; 38627 var _tag_container = null; 38628 var _self_click = false; 38629 var _adding_tag = false; 38630 var _first_open = true; 38631 var _previous_top = 0; 38632 var _scrolling = false; 38633 var _scroll_count = 0; 38634 var _close_button = null; 38635 var _replacement_container = null; 38636 38637 38638 function destroy() { 38639 38640 if(_tag_container) { 38641 _tag_container.destroy(); 38642 delete _tag_container; 38643 } 38644 } 38645 38646 function setup_events() { 38647 38648 _list_container.bind('click', do_self_click); 38649 38650 /* This bit sets up listeners for focus and click events in order 38651 to close the ontology drop down if the user clicks outside of this control. 38652 */ 38653 38654 // IE <= 8 sauce. Old IE does not support this property, 38655 // neither does it support window.click. Note that this 38656 // property is not strictly related to this implementation; it's 38657 // just a reliable indicator of old IE. 38658 if($.support.changeBubbles) 38659 $(window).bind('click', do_blur); 38660 else 38661 $(document).bind('click', do_blur); 38662 38663 // multiple input elements are created and destroyed as the users 38664 // enters text in the tag container. We don't want to close the dropdown in these cases. 38665 $(_tag_container.container[0].parentNode).find('input').bind('focus', show); 38666 $(_tag_container.container[0].parentNode).find('input').bind('click', do_self_click); 38667 _replacement_container = $('#'+_tag_container.container.attr('id')+'_tagsinput'); 38668 _replacement_container.live('focus', show); 38669 _replacement_container.live('click', do_self_click); 38670 38671 // If the browser size changes we need to reposition the drop downs, which are 38672 // absolutely positioned. 38673 $(window).bind('resize', set_position); 38674 38675 /* the input is typically ~20px high, but the default scroll is 40px. This bit resets the 38676 scroll top so that it's 20 * number_of_scrolls. 38677 */ 38678 _replacement_container.bind('scroll', function(event) { 38679 38680 if(_scrolling) 38681 return; 38682 38683 var cont = $(this); 38684 var new_top = cont.scrollTop(); 38685 38686 if(new_top > _previous_top) 38687 _scroll_count++; 38688 else if(new_top < _previous_top) 38689 _scroll_count--; 38690 38691 var wanted_top = _scroll_count*20; 38692 38693 if(new_top != wanted_top) { 38694 38695 _scrolling = true; 38696 cont.scrollTop(wanted_top); 38697 _scrolling = false; 38698 } 38699 38700 _previous_top = cont.scrollTop(); 38701 }); 38702 } 38703 38704 function setup_view() { 38705 38706 _ontology_view = new ontology_widget(_inner_container, {}, '', { 38707 ontology_data:_model.get_structure_children, 38708 multi_select:!settings.limit_single_selection, 38709 on_node_select:do_node_select, 38710 on_tree_load:do_tree_load 38711 }); 38712 _tag_container = new AIBS.UI.ontology_tag_container($this, { 38713 foo:'goo', 38714 on_autocomplete:_model.structure_autocomplete, 38715 on_tag_added:do_add_tag, 38716 on_tag_removed:do_remove_tag, 38717 default_text: settings.default_text 38718 }); 38719 } 38720 38721 function do_add_tag(tag) { 38722 38723 if(_adding_tag) 38724 return; 38725 38726 _adding_tag = true; 38727 38728 if(check_tags(tag)) { 38729 var node = _model.node_by_acronym(tag); 38730 38731 // the node_by_acronym is case-insensitive, but we want the ' 38732 // actual tag to be properly cased, so remove the user's tag and create a new one. 38733 // But if we're in single-select mode, just clear the tag container. 38734 if(settings.limit_single_selection) 38735 _tag_container.clear(); 38736 else 38737 _tag_container.remove_tag(tag); 38738 38739 _tag_container.add_tag(node[settings.tag_key]);//.acronym); 38740 38741 _ontology_view.open_structure_path(node.structure_id_path, node.structure_id); 38742 38743 structure_selection_change(node, true); 38744 } 38745 _adding_tag = false;
38746 } 38747 38748 function do_remove_tag(tag) { 38749 38750 if(_adding_tag) 38751 return; 38752 38753 var node = _model.node_by_acronym(tag); 38754 if(node) { 38755 _ontology_view.deselect_structure(node.structure_id); 38756 structure_selection_change(node, false); 38757 } 38758 } 38759 38760 function get_control_id() { 38761 38762 return("_oa_wrapper" + _id); 38763 } 38764 38765 function build_scaffold() { 38766 38767 // this is the containing div for the drop-down list 38768 var html = "<div id='_oa_wrapper" + _id + "' class='ui-widget " + _list_class + "'>"; 38769 html += "<div id='_oa_" + _id + "' class='ui-widget _ontology_autocomplete_inner_div'>"; 38770 html += "<div id='_oa_" + _id + "_closer' title='" + settings.close_text + "'></div>"; 38771 html += "</div>"; 38772 38773 _list_container = $(html); 38774 _list_container.appendTo(document.body) 38775 .hide(); 38776 38777 _inner_container = $('#_oa_' + _id).scroll(do_list_scroll); 38778 38779 _close_button = $('#_oa_' + _id + '_closer'); 38780 _close_button.button({ 38781 icons:{ 38782 primary:settings.close_icon 38783 }, 38784 text:false 38785 }).css({ 38786 height:'20px', 38787 width:'20px', 38788 margin:'2px', 38789 backgroundColor:'#fff', 38790 position:'absolute', 38791 right: '0px' 38792 }).bind('click', hide); 38793 38794 38795 } 38796 38797 function do_list_scroll(event, ui) { 38798 _close_button.css({ 38799 top:(_inner_container.scrollTop() )+'px', 38800 left:(_inner_container.scrollLeft() + _inner_container.width() - _close_button.width() - 21)+'px' 38801 }); 38802 } 38803 38804 function do_self_click() { 38805 _self_click = true; 38806 } 38807 38808 function do_blur(evt) { 38809 38810 if(!_self_click) 38811 hide(); 38812 38813 _self_click = false; 38814 } 38815 38816 function set_position() { 38817 38818 var container = _tag_container ? _tag_container.get_container() : $this; 38819 var pos; 38820 try { 38821 //pos = container.position(); 38822 pos = container.offset(); 38823 } catch(e) { 38824 // This is IE8 sauce. 38825 pos = {left:0, right:0, top:0, left:0}; 38826 } 38827 38828 var left = 0; 38829 var width = settings.width ? settings.width : (container.outerWidth() + 100); 38830 if (settings.horizontal_align == 'left') 38831 left = pos.left; 38832 else 38833 left = (pos.left + container.outerWidth()) - width; 38834 38835 var viewport_height = $j(window).height(); 38836 //var computed_top_y = (pos.top > viewport_height / 2) ? pos.top - settings.height : pos.top + container.outerHeight(); 38837 var computed_top_y = pos.top + container.outerHeight(); 38838 38839 var style = { 38840 'position':'absolute', 38841 'background-color':'#fff', 38842 'border':'1px solid #ddd', 38843 'top':computed_top_y, 38844 'left':left, 38845 'width':width + 'px', 38846 'height':settings.height, 38847 'overflow':'hidden', 38848 'z-index':settings.zIndex 38849 } 38850 _list_container.css(style); 38851 38852 var style = { 38853 'position':'relative', 38854 'background-color':'#fff', 38855 'border-top':'1px solid #ddd', 38856 'top':'0em', 38857 'left':'0px', 38858 'width':'100%', 38859 'height':(settings.height), 38860 'overflow':'auto' 38861 } 38862 _inner_container.css(style); 38863 } 38864 38865 function clear_selections(event, ui) { 38866 _ontology_view.select_none(); 38867 } 38868 38869 function do_node_select(node_id, add) { 38870 // when a user selects a tag from autocomplete it is added, 38871 // which fires the select event. We want to avoid that loop. 38872 if(_adding_tag) { 38873 _adding_tag = false;
38874 return; 38875 } 38876 38877 var node = _model.node_by_id(node_id); 38878 if(add) { 38879 38880 if(settings.limit_single_selection && !settings.load) 38881 _tag_container.clear(); 38882 38883 settings.load = false; // reset load flag 38884 _tag_container.add_tag(node.acronym); 38885 } 38886 else 38887 _tag_container.remove_tag(node.acronym); 38888 } 38889 38890 function do_tree_load(data) { 38891 if (settings.on_ready) { 38892 settings.on_ready(data); 38893 } 38894 } 38895 38896 function set_ids(structure_ids) { 38897 38898 _tag_container.clear(); 38899 _ontology_view.select_none(); 38900 38901 var len = structure_ids.length; 38902 for (var i = 0; i < len; i++) { 38903 var node = _model.node_by_id(structure_ids[i]); 38904 if(node) 38905 do_add_tag(node.acronym); 38906 } 38907 } 38908 38909 function set_tags(tags) { 38910 38911 var tag_array = tags.split(';'); 38912 var len = tag_array.length; 38913 38914 _tag_container.clear(); 38915 _ontology_view.select_none(); 38916 for (var i = 0; i < len; i++) { 38917 var node = _model.node_by_acronym(tag_array[i]); 38918 if(node) 38919 do_add_tag(node.acronym); 38920 } 38921 } 38922 38923 function get_structure_by_tag(tag) { 38924 38925 if (settings.tag_key == 'acronym') 38926 return _model.node_by_acronym(tag); 38927 else if (settings.tag_key == 'id') 38928 return _model.node_by_id(tag); 38929 else { 38930 var all_nodes = _model.get_all(); 38931 for (var j = 0; j < all_nodes.length; j++) { 38932 var n = all_nodes[j]; 38933 if (n[settings.tag_key] == tag) { 38934 return n; 38935 } 38936 } 38937 } 38938 return null; 38939 } 38940 38941 function get_tag_set() { 38942 38943 var tag_set = []; 38944 var tags = _tag_container.get_tags(); 38945 38946 for(var i = 0; i < tags.length; i++) { 38947 var struct = get_structure_by_tag(tags[i]); 38948 38949 if(!struct) 38950 trace("no record for struct '" + tags[i] + "'"); 38951 else 38952 tag_set.push({ 38953 label:tags[i], 38954 value:struct.structure_id 38955 }); 38956 } 38957 38958 return(tag_set); 38959 } 38960 38961 38962 function structure_selection_change(structure, add) { 38963 38964 if(_first_open) 38965 return; 38966 38967 if(add && settings.on_add_structure) 38968 settings.on_add_structure(structure); 38969 else if(!add && settings.on_remove_structure) 38970 settings.on_remove_structure(structure); 38971 } 38972 38973 /** 38974 * Checks to make sure all of the text in the input really represents 38975 * valid structures. Notifies the user if some tokens don't, 38976 * and removes them. 38977 */ 38978 function check_tags(tag_string) { 38979 38980 if (!tag_string.length) 38981 return; 38982 38983 // get the array of tokens in the input; 38984 // if there are multiple delimiters allowed, convert them all into 38985 // the first before splitting 38986 var first_delim = settings.delimiters[0]; 38987 for (var i = 1; i < settings.delimiters.length; i++) 38988 tag_string.replace(settings.delimiters[i], first_delim); 38989 var acronyms = tag_string.split(first_delim); 38990 38991 // check to make sure each token is an acronym 38992 var good_tokens = []; 38993 var bad_tokens = []; 38994 var len = acronyms.length; 38995 for (i = 0; i < len; i++) { 38996 var acronym = $.trim(acronyms[i]); 38997 var node = _model.node_by_acronym(acronym); 38998 if (!node) 38999 bad_tokens.push(acronym); 39000 else 39001 good_tokens.push(node); 39002 } 39003 39004 for (i = 0; i < bad_tokens.length; i++) { 39005 trace("bad token: " + inspect('', bad_tokens[i])); 39006 _tag_container.remove_tag(bad_tokens[i]); 39007 } 39008 39009 return(bad_tokens.length == 0); 39010 } 39011 39012 function show() { 39013 39014 if (_visible) 39015 return; 39016 39017 var tags = _tag_container.get_tags(); 39018 _ontology_view.select_none(); 39019 39020 _adding_tag = true; 39021 39022 if(_first_open) 39023 on_first_open(tags); 39024 39025 // davidf: the container has to be visible for jstree's auto-scroll-into-view to work 39026 _list_container.show(); 39027 39028 for(var i = 0; i < tags.length; i++) { 39029 var node = get_structure_by_tag(tags[i]); 39030 39031 if(node) 39032 _ontology_view.open_structure_path(node.structure_id_path, node.structure_id); 39033 } 39034 _adding_tag = false;
39035 39036 39037 set_position(); 39038 39039 _visible = true; 39040 if(settings.on_open) 39041 settings.on_open(); 39042 } 39043 39044 function on_first_open(tags) { 39045 39046 if(settings.default_open_nodes) { 39047 39048 for(var acronym in settings.default_open_nodes) { 39049 39050 // the tags arg is the list of tags that will be shown once the 39051 // ontology is opened. We don't want to open them here if we're going 39052 // to do it again anyway. 39053 if($.inArray(acronym,tags) < 0) { 39054 var node = _model.node_by_acronym(acronym); 39055 if(node) { 39056 _ontology_view.open_structure_path(node.structure_id_path, node.structure_id, false); 39057 _ontology_view.deselect_structure(node.structure_id); 39058 } 39059 } 39060 } 39061 } 39062 _first_open = false; 39063 } 39064 39065 function hide() { 39066 39067 if (!_visible) 39068 return; 39069 39070 _list_container.hide(); 39071 _visible = false; 39072 39073 _scrolling = true; 39074 _replacement_container.scrollTop(0); 39075 //_scroll_count = 0; 39076 _scrolling = false; 39077 39078 if(settings.on_close) 39079 settings.on_close(); 39080 } 39081 39082 function set_data() { 39083 39084 $this.data('ontology_autocomplete', { 39085 list_class:_list_class, 39086 tag_container:_tag_container, 39087 check_tags:check_tags, 39088 get_tag_set:get_tag_set, 39089 set_ids:set_ids, 39090 set_tags:set_tags, 39091 destroy_all:destroy, 39092 get_control_id:get_control_id 39093 }); 39094 } 39095 39096 function init() { 39097 39098 build_scaffold(); 39099 set_position(); 39100 setup_view(); 39101 setup_events(); 39102 check_tags(settings.initial_acronyms); 39103 set_data(); 39104 } 39105 39106 init(); 39107 39108 } // !_data 39109 39110 }); 39111 }, 39112 39113 set_structure_string:function (method, structs) { 39114 39115 return this.each(function () { 39116 var $this = $(this); 39117 var _data = $this.data('ontology_autocomplete'); 39118 _data.set_tags(structs); 39119 }); 39120 }, 39121 39122 set_structure_ids:function (method, struct_ids) { 39123 39124 return this.each(function () { 39125 var $this = $(this); 39126 var _data = $this.data('ontology_autocomplete'); 39127 _data.set_ids(struct_ids); 39128 }); 39129 }, 39130 39131 get_structure_string:function () { 39132 39133 var $this = $(this); 39134 var _data = $this.data('ontology_autocomplete'); 39135 return(_data.tag_container.get_displayed_text()); 39136 }, 39137 39138 get_tags:function () { 39139 39140 var $this = $(this); 39141 var _data = $this.data('ontology_autocomplete'); 39142 return(_data.get_tag_set()); 39143 }, 39144 39145 destroy : function( ) { 39146 39147 return this.each(function(){ 39148 39149 var $this = $(this); 39150 var _data = $this.data('ontology_autocomplete'); 39151 39152 // remove the DOM nodes for this instance 39153 if (_data) { 39154 jQuery("#" + _data.get_control_id()).remove(); 39155 $this.remove(); 39156 $this.removeData(); 39157 } 39158 }) 39159 39160 } 39161 }; 39162 39163 39164 $.fn.ontology_autocomplete = function (method) { 39165 if (methods[method]) { 39166 return methods[ method ].apply(this, Array.prototype.slice.call(arguments)); 39167 } else if (typeof method === 'object' || !method) { 39168 return methods.init.apply(this, arguments); 39169 } else { 39170 $.error('Method ' + method + ' does not exist on jQuery.ontology_autocomplete'); 39171 } 39172 } 39173 39174})(jQuery); 39175/** 39176 * Encapsulates an ontology graph and provides simple searches. By default the entire graph is loaded 39177 * when this object is instantiated. 39178 * @param dao {object} Not used, can be null 39179 * @param graph_id {integer} The is of the structure graph to model 39180 * @param options {object} options include: 39181 * 39182 * <br/>ontology_filename: Not actually optional. For backward compatibility this lives in the options hash. 39183 * The path to the ontology file to be loaded and displayed. 39184 * <br/>async: Default = true. Set to false to load the ontology data synchronously. 39185 * <br/>on_error: Assign a callback function to be called when the request for 39186 * the given ontology graph fails. fn(this, error). Default throws. 39187 * <br/>on_ready: Default = null. Assign a callback function to be called when the model has fetched 39188 * the ontology graph. fn(this). 39189 * <br/>defer_init: Default = false. If defer_init is set to true, the caller must explicitly call ontology_model.init(); 39190 */ 39191 39192function ontology_model(dao, graph_id, options) { 39193 39194 this.the_dao = dao; 39195 this.graph_id = graph_id; 39196 this.options = options; 39197 39198 this.settings = { 39199 ontology_filename:'ontology.json', 39200 defer_init:false, 39201 search_limit:20, 39202 async:true, // default value used for ajax call, can be overriden by optiohs 39203 on_error:do_error, 39204 on_ready:null, 39205 snippet_length: 0 39206 }; 39207 $j.extend(true, this.settings, this.options); 39208 39209 /** 39210 * Returns a single node. The first that matches the given search term. Wraps around if none are found 39211 * 'below' the previous find position. 39212 * @param term {string} The term for which to search. 39213 * @param contains {boolean} If true, does a 'contains' search. If false, does a 'starts with' search. 39214 */ 39215 this.find_next = function(term, contains) { 39216 39217 return(do_find_next(term, contains)); 39218 } 39219 39220 /** 39221 * resets the index of the last successful find_first() to 0 so subsequent searches start at the beginning. 39222 */ 39223 this.reset_find_index = function() { 39224 _last_find = 0; 39225 } 39226 39227 /** 39228 * Returns an array of all the nodes that match the given term 39229 * @param term 39230 * @param contains 39231 */ 39232 this.find_all = function(term, contains) { 39233 39234 return(do_find_all(term, contains)); 39235 } 39236 39237 39238 /** 39239 * Returns the list of ontology nodes 39240 */ 39241 this.get_all = function() { 39242 return(_the_nodes); 39243 } 39244 39245 39246 /** 39247 * returns the ontology node matching the given acronym, if it exists. Otherwise returns null; 39248 * @param {string} acronym 39249 */ 39250 this.node_by_acronym = function(acronym) { 39251 39252 return(_acronym_index[acronym]); 39253 } 39254 39255 /** 39256 * returns the ontology node matching the given ID, if it exists. Otherwise returns null; 39257 * @param {integer} structure ID 39258 */ 39259 this.node_by_id = function(id) { 39260 39261 return(_id_index[id]); 39262 } 39263 39264 this.size = function() { 39265 return(_size); 39266 } 39267 39268 /** 39269 * If the defer_init option is set to true, call this method to have the ontology_model request and load 39270 * the ontology_graph data from the AIBS warehouse service. 39271 */ 39272 this.init = function() { 39273 do_init(); 39274 } 39275 39276 39277 this.get_structure_children = function(structure_id) { 39278 39279 if(!structure_id) 39280 structure_id = _the_nodes[0].parent_structure_id; 39281 39282 var kids = _structure_parent_index[structure_id]; 39283 39284 //see if these have any children of their own 39285 if(kids) 39286 for(var i = 0; i < kids.length; i++) { 39287 // kids[i].annotation_state = get_structure_annotation_state(kids[i].id); 39288 kids[i].child_structures = _structure_parent_index[kids[i].structure_id]; 39289 } 39290 39291 return(kids); 39292 }; 39293 39294 this.structure_autocomplete = function(term, callback) { 39295 39296 39297 structure_search(term, callback); 39298 } 39299 39300 39301 /** 39302 * Returns an array of nodes descended from the given node. 39303 * @param acronym 39304 * @return {Array} 39305 */ 39306 this.get_descendants = function(structure_id) { 39307 39308 var node = this.node_by_id(structure_id); 39309 if(!node) 39310 return([]); 39311 39312 var d = []; 39313 for(var i = 0; i < _size; i++) { 39314
39315 // if we find a node where the path starts with the path of our starting node, 39316 // we have found a descendant. 39317 if(_the_nodes[i].structure_id_path.lastIndexOf(node.structure_id_path, 0) === 0) 39318 d.push(_the_nodes[i]); 39319 } 39320 return(d); 39321 } 39322 39323 39324 /** 39325 * Given a list of structure IDs, remove all but the parent nodes. 39326 * 39327 * @param ids. AN array of structure IDs 39328 * @return {Array} 39329 */ 39330 this.prune = function(ids) { 39331 39332 var parent_path = ""; 39333 var stump = []; 39334 var nodes = []; 39335 var len = ids.length; 39336 39337 // get all the nodes for the given IDs 39338 for(var i = 0; i < len; i++) 39339 nodes.push( this.node_by_id(ids[i])); 39340 39341 // sort by 'path' 39342 nodes.sort(function(a, b) {return(a.structure_id_path.localeCompare(b.structure_id_path));}); 39343 if(!nodes || !nodes.length) 39344 return(stump); 39345 39346 // the first entry must be a parent 39347 stump.push(nodes[0].structure_id); 39348 parent_path = nodes[0].structure_id_path; 39349 39350 // descend the sorted list 39351 for(var j = 1; j < len; j++) { 39352 // if the current node path does not start with the previous path, 39353 if(nodes[j].structure_id_path.lastIndexOf(parent_path, 0) != 0) { 39354 // it is a parent 39355 stump.push(nodes[j].structure_id); 39356 parent_path = nodes[j].structure_id_path; 39357 } 39358 } 39359 39360 return(stump); 39361 } 39362 39363 39364 /** 39365 * @private 39366 * @scope AIBS.DATA.ontology_model 39367 */ 39368 var _self = this; 39369 var _the_nodes = []; 39370 var _last_find = 0; 39371 var _size = 0; 39372 var _acronym_index = null; 39373 var _id_index = null; 39374 var _structure_parent_index = null; 39375 39376 /** 39377 * Convert a string into a snippet with _self.settings.snippet_length words. 39378 * 39379 * @param index string index around which the snippet will be made 39380 * @param str the string being snippeted 39381 */ 39382 function make_snippet(index, str) { 39383 // no snippet? no-op 39384 if (_self.settings.snippet_length <= 0) 39385 return str; 39386 39387 // break the snippet into words. 39388 var words = str.split(" "); 39389 39390 // if the match index is 0, just return the first N words 39391 if (index == 0) { 39392 var snippet_words = words.slice(0, _self.settings.snippet_length); 39393 return snippet_words.join(" ") + (words.length == snippet_words.length ? "" : "..."); 39394 } else { 39395 39396 // figure out which word the term appears in 39397 var word_index; 39398 var num_chars = 0; 39399 for (word_index = 0; word_index < words.length; word_index++) { 39400 num_chars += words[word_index].length + 1; 39401 if (num_chars > index) 39402 break; 39403 } 39404 39405 // the snippet will always have the term in it 39406 var snippet_words = [ words[word_index] ]; 39407 var before_index = word_index - 1; 39408 var after_index = word_index + 1; 39409 39410 // add words before and after the term word until we have enough words 39411 while (snippet_words.length < _self.settings.snippet_length) { 39412 if (before_index < 0 && after_index >= words.length) 39413 break; 39414 39415 if (before_index >= 0) { 39416 snippet_words.unshift(words[before_index]); 39417 } 39418 39419 if (after_index < words.length) { 39420 snippet_words.push(words[after_index]); 39421 } 39422 39423 before_index--; 39424 after_index++; 39425 } 39426 39427 return (before_index < 0 ? "" : "..." ) + snippet_words.join(' ') + (after_index >= words.length ? "" : "..."); 39428 } 39429 } 39430 39431 /** 39432 * 39433 * @param term 39434 * @param complete If true, first does a 'starts with' search and then 39435 * if the _self.settings.search_limit number of matches 39436 * is not found, also does a 'contains search'. 39437 * If false or undefined, only a 'starts with' search is done. 39438 */ 39439 function structure_search(term, callback) { 39440 39441 var matches = []; 39442 var match_count = 0; 39443 var matched_ids = {}; 39444 // first do the starts-with search 39445 var m = null; 39446 var start_patt = new RegExp(("^" + term.term), 'i'); 39447 for(var i = 0; i < _size; i++) { 39448 39449 if((m = _the_nodes[i].acronym.match(start_patt)) || (m = _the_nodes[i].name.match(start_patt))) { 39450 39451 var name = (_self.settings.snippet_length > 0) ? make_snippet(0, _the_nodes[i].name) : _the_nodes[i].name; 39452 39453 matched_ids[i] = true; 39454 matches[match_count++] = { 39455 label:_the_nodes[i].acronym + ' - ' + name, 39456 value:_the_nodes[i].acronym, 39457 id:_the_nodes[i].structure_id 39458 }; 39459 if(match_count >= _self.settings.search_limit) 39460 break; 39461 } 39462 } 39463 39464 // if necessary do the 'includes' search 39465 if(match_count < _self.settings.search_limit) { 39466 39467 var patt = new RegExp(term.term, 'i'); 39468 39469 39470 for(var i = 0; i < _size; i++) { 39471 if (i in matched_ids) 39472 continue; 39473 39474 // if it's included in the string, but not starting 39475 // the string (because we already matched that above...) 39476 if((m = _the_nodes[i].acronym.match(patt)) || (m = _the_nodes[i].name.match(patt)) 39477 && 39478 (!start_patt.test(_the_nodes[i].acronym) && !start_patt.test(_the_nodes[i].name)) 39479 ) { 39480 matched_ids[i] = true; 39481 var name = (_self.settings.snippet_length > 0) ? make_snippet(m.index, _the_nodes[i].name) : _the_nodes[i].name; 39482 39483 matches[match_count++] = { 39484 label:_the_nodes[i].acronym + ' - ' + name, 39485 value:_the_nodes[i].acronym, 39486 id:_the_nodes[i].structure_id 39487 }; 39488 if(match_count >= _self.settings.search_limit) 39489 break; 39490 } 39491 } 39492 } 39493 39494 callback(matches); 39495 } 39496 39497 // create three different indices into the data. 39498 function index_data() { 39499 39500 _acronym_index = {}; 39501 _id_index = {}; 39502 _structure_parent_index = {}; 39503 39504 var len = _the_nodes.length; 39505 for(var i = 0; i < len; i++) { 39506 39507 // straight-forward indices by id and acronym 39508 _acronym_index[_the_nodes[i].acronym] = _the_nodes[i]; 39509 _id_index[_the_nodes[i].structure_id] = _the_nodes[i]; 39510 39511 // an index of children by parent. For each parent_structure_id we want to list all children 39512 39513 // if this is the first instance of this parent_structure_id, create a new array for its members 39514 if(!_structure_parent_index[_the_nodes[i].parent_structure_id]) 39515 _structure_parent_index[_the_nodes[i].parent_structure_id] = []; 39516 39517 _structure_parent_index[_the_nodes[i].parent_structure_id].push(_the_nodes[i]); 39518 39519 } 39520 } 39521 39522 39523 function load_graph() { 39524 39525 // note: jQuery.getJSON does not call error handlers, use jQuery.ajax instead. See http://stackoverflow.com/questions/5388934/jquery-handle-errors-in-getjson 39526 $j.ajax({ 39527 url: '/' + _self.settings.ontology_filename + '?' + (Date.parse(new Date())), 39528 dataType: 'json', 39529 async: _self.settings.async, 39530 success: function(data) { 39531 _the_nodes = data; 39532 _size = data.length; 39533 index_data(); 39534 if(_self.settings.on_ready) 39535 _self.settings.on_ready(_self); 39536 }, 39537 error: _self.settings.on_error 39538 }); 39539 } 39540 39541 function do_error(xhdr, text_status, error) { 39542 var err = xhdr.msg == undefined ? error : xhdr.msg; 39543 39544 if(_self.settings.on_error) 39545 return(_self.settings.on_error(_self, err)); 39546 39547 throw "Error fetching ontology graph: " + err; 39548 } 39549 39550 function do_init() { 39551 39552 if(!_self.settings.ontology_filename || !_self.settings.ontology_filename.length) 39553 throw "Error loading ontology model. no ontology file name provided."; 39554 39555 load_graph(); 39556 } 39557 39558 if(!_self.settings.defer_init) 39559 do_init(); 39560 39561} 39562; 39563 39564 39565var __ONTOLOGY_VIEW_COUNT__ = 0; 39566 39567 39568/** 39569 * This is the UI portion of displaying all or part of an ontology tree. It's assumed that the 39570 * ontology adheres to the AIBS warehouse API OntologyNode schema, or that node objects contain at least 39571 * these fields: 39572 * { 39573 * depth:integer, 39574 * st_order:integer, 39575 * color:string, 39576 * acronym:string, 39577 * name:string, 39578 * structure_id:integer 39579 * } 39580 * @param container 39581 * @param node_array 39582 * @param options 39583 */ 39584function ontology_view(container, node_array, options) { 39585 39586 this.container = $j(container); 39587 this.node_array = node_array; 39588 this.options = options; 39589 39590 39591 this.on_node_over = options ? options.on_node_over ? options.on_node_over : null : null; 39592 this.on_node_out = options ? options.on_node_out ? options.on_node_out : null : null; 39593 this.on_node_select = options ? options.on_node_select ? options.on_node_select : null : null; 39594 this.render_node = options ? options.render_node ? options.render_node : null : null; 39595 this.scroll_speed = options ? options.scroll_speed ? options.scroll_speed : 0 : 0; 39596 39597 39598 this.scroll_to_structure = function(structure_id, highlight) { 39599 var $node = node_for_structure(structure_id); 39600 if($node && $node.length > 0) 39601 $node.scrollintoview(); 39602 39603 if(highlight) 39604 this.highlight_structure(structure_id); 39605 } 39606 39607 this.scroll_to_top = function() { 39608 first_item().scrollintoview(); 39609 } 39610 39611 this.highlight_structure = function(structure_id) {
39612 var $node = item_for_structure_id(structure_id); 39613 if($node && $node.length > 0) { 39614 if(_current_highlight_node) 39615 _current_highlight_node.removeClass('ui-state-hover'); 39616 39617 $node.addClass('ui-state-hover'); 39618 _current_highlight_node = $node; 39619 } 39620 } 39621 39622 this.highlight_prev = function() { 39623 39624 if(_current_highlight_node) { 39625 var prev = _current_highlight_node.prev(); 39626 this.highlight_none(); 39627 if(prev && prev.length) { 39628 prev.scrollintoview(); 39629 highlight_node(prev); 39630 } 39631 } 39632 else 39633 this.highlight_none(); 39634 39635 } 39636 39637 this.highlight_next = function() { 39638 39639 var next = null; 39640 if(_current_highlight_node) { 39641 next = _current_highlight_node.next(); 39642 this.highlight_none(); 39643 } 39644 else 39645 next = first_item(); 39646 39647 if(next && next.length) { 39648 next.scrollintoview(); 39649 highlight_node(next); 39650 } 39651 } 39652 39653 this.highlight_none = function() { 39654 if(_current_highlight_node) 39655 _current_highlight_node.removeClass('ui-state-hover'); 39656 39657 _current_highlight_node = null; 39658 } 39659 39660 this.select_at_highlight = function() { 39661 if(!_current_highlight_node) 39662 return; 39663 39664 var item = _current_highlight_node.find('._ov_chk_chk'); 39665 toggle_selected(item); 39666 } 39667 39668 this.select_at_structure = function(structure_id) { 39669 var item = node_for_structure(structure_id); 39670 toggle_selected(item); 39671 } 39672 39673 this.select_none = function() { 39674 var $selections = $j('#'+_id_prefix + '_ov_wrapper ._ov_checked'); 39675 $selections.each( function(index) {select($j(this), false);} ) 39676 } 39677 39678 // remove selection highlight from an ontology row 39679 this.remove_highlight = function(structure_id) { 39680 var $node = node_for_structure(structure_id); 39681 var item = item_for_structure_id(structure_id); 39682 39683 $node.removeClass('_ov_checked'); 39684 item.removeClass('_ov_selected'); 39685 } 39686 39687 39688 var _self = this; 39689 var _view_class = options ? options.view_class ? options.view_class : '' : ''; 39690 39691 var _indent_multiplier = options ? options._indent_multiplier ? options._indent_multiplier : 12 : 12; 39692 var _id_prefix = '_ov_' + __ONTOLOGY_VIEW_COUNT__++; 39693 39694 var _wrapper = null; 39695 var _ulist = null; 39696 39697 var _current_highlight_node = null; 39698 39699 39700 function node_for_structure(structure_id) { 39701 return( $j('#' + _id_prefix + '_ov_wrapper [struct_id="' + structure_id + '"]') ); 39702 } 39703 39704 function item_for_structure_id(structure_id) { 39705 return( $j('#' + _id_prefix + '_' +structure_id) ); 39706 } 39707 39708 function highlight_node(node) { 39709 if(!node) 39710 return; 39711 39712 node.addClass('ui-state-hover'); 39713 _current_highlight_node = node; 39714 } 39715 39716 function first_item() { 39717 return( _self.container.find('li').first()); 39718 } 39719 39720 function build_scaffold() { 39721 39722 var id = _id_prefix + '_ov_wrapper'; 39723 var class_names = '_ov_wrapper ' + _view_class; 39724 39725 var html = "<div id='" + id + "' class='" + class_names + "'>"; 39726 html += "<ul id='" + (id+'_ul') + "' class='_ov_wrapper_ul'></ul>" 39727 html += "</div>"; 39728 39729 $j(html).appendTo(_self.container); 39730 39731 //objectify 39732 _wrapper = $j('#'+id); 39733 _ulist = $j('#'+id+'_ul'); 39734 } 39735 39736 function render_node(node) { 39737 39738 if(_self.render_node) 39739 return(_self.render_node(node)); 39740 39741 39742 var id = _id_prefix + '_' + node.structure_id 39743 var html = "<li id='" + id + "' class='_ov_li'>"; 39744 html += "<div class='_ov_li_spacer' style='width:" + (node.depth*_indent_multiplier) + "px'></div>"; 39745 html += "<div class='_ov_chk_cont' >"; 39746 html += "<div struct_id='" + node.structure_id + "' acronym='" + node.acronym + "' struct_name='" + node.name + "' class='_ov_chk_chk _ov_unchecked' style='background-color:#"+ node.color + "'></div>"; 39747 html += "</div>"; 39748 html += "<div class='_ov_li_content' title='" + node.name + "'><b>
39748" + node.acronym + "</b> " + node.name + "</div>"; 39749 html += "</li>"; 39750 39751 return(html); 39752 } 39753 39754 function populate_view() { 39755 var len = _self.node_array.length; 39756 var html = ""; 39757 39758 for(var i = 0; i < len; i++) 39759 html += render_node(_self.node_array[i]); 39760 39761 _ulist[0].innerHTML = html; 39762 $j('#'+_id_prefix + '_ov_wrapper ._ov_li').bind('click', selector); 39763 } 39764 39765 function selector(event, ui) { 39766 var node = $j(this).find('._ov_chk_chk'); 39767 toggle_selected(node); 39768 } 39769 39770 function checker(event, ui) { 39771 39772 var node = $j(this); 39773 toggle_selected(node) 39774 _self.highlight_structure(node.attr('struct_id')); 39775 event.stopPropagation(); 39776 39777 return(false); 39778 } 39779 39780 function toggle_selected($node) { 39781 var selected = $node.hasClass('_ov_checked'); 39782 select($node, !selected); 39783 } 39784 39785 function select($node, select) { 39786 39787 var item = item_for_structure_id($node.attr('struct_id')); 39788 if(select) { 39789 $node.addClass('_ov_checked'); 39790 item.addClass('_ov_selected'); 39791 item.removeClass('_ov_highlight'); 39792 item.removeClass('ui-state-hover'); 39793 } 39794 else { 39795 $node.removeClass('_ov_checked'); 39796 item.removeClass('_ov_selected'); 39797 } 39798 39799 var info = { 39800 id:$node.attr('struct_id'), 39801 acronym:$node.attr('acronym'), 39802 name:$node.attr('struct_name') 39803 }; 39804 39805 if(_self.on_node_select) 39806 _self.on_node_select(_self, select, info); 39807 } 39808 39809 function init() { 39810 build_scaffold(); 39811 populate_view(); 39812 39813 } 39814 39815 init(); 39816} 39817; 39818 39819 39820 39821 39822 39823 39824 39825 39826 39827/*! 39828 * jQuery scrollintoview() plugin and :scrollable selector filter 39829 * 39830 * Version 1.8 (14 Jul 2011) 39831 * Requires jQuery 1.4 or newer 39832 * 39833 * Copyright (c) 2011 Robert Koritnik 39834 * Licensed under the terms of the MIT license 39835 * http://www.opensource.org/licenses/mit-license.php 39836 */ 39837 39838 39839(function ($) { 39840 var converter = { 39841 vertical: { x: false, y: true }, 39842 horizontal: { x: true, y: false }, 39843 both: { x: true, y: true }, 39844 x: { x: true, y: false }, 39845 y: { x: false, y: true } 39846 }; 39847 39848 var settings = { 39849 duration: "fast", 39850 direction: "both" 39851 }; 39852 39853 var rootrx = /^(?:html)$/i; 39854 39855 // gets border dimensions 39856 var borders = function (domElement, styles) { 39857 styles = styles || (document.defaultView && document.defaultView.getComputedStyle ? document.defaultView.getComputedStyle(domElement, null) : domElement.currentStyle); 39858 var px = document.defaultView && document.defaultView.getComputedStyle ? true : false; 39859 var b = { 39860 top: (parseFloat(px ? styles.borderTopWidth : $.css(domElement, "borderTopWidth")) || 0), 39861 left: (parseFloat(px ? styles.borderLeftWidth : $.css(domElement, "borderLeftWidth")) || 0), 39862 bottom: (parseFloat(px ? styles.borderBottomWidth : $.css(domElement, "borderBottomWidth")) || 0), 39863 right: (parseFloat(px ? styles.borderRightWidth : $.css(domElement, "borderRightWidth")) || 0) 39864 }; 39865 return { 39866 top: b.top, 39867 left: b.left, 39868 bottom: b.bottom, 39869 right: b.right, 39870 vertical: b.top + b.bottom, 39871 horizontal: b.left + b.right 39872 }; 39873 }; 39874 39875 var dimensions = function ($element) { 39876 var win = $(window); 39877 var isRoot = rootrx.test($element[0].nodeName); 39878 return { 39879 border: isRoot ? { top: 0, left: 0, bottom: 0, right: 0} : borders($element[0]), 39880 scroll: { 39881 top: (isRoot ? win : $element).scrollTop(), 39882 left: (isRoot ? win : $element).scrollLeft() 39883 }, 39884 scrollbar: { 39885 right: isRoot ? 0 : $element.innerWidth() - $element[0].clientWidth, 39886 bottom: isRoot ? 0 : $element.innerHeight() - $element[0].clientHeight 39887 }, 39888 rect: (function () { 39889 var r = $element[0].getBoundingClientRect(); 39890 return { 39891 top: isRoot ? 0 : r.top, 39892 left: isRoot ? 0 : r.left, 39893 bottom: isRoot ? $element[0].clientHeight : r.bottom, 39894 right: isRoot ? $element[0].clientWidth : r.right 39895 }; 39896 })() 39897 }; 39898 }; 39899 39900 $.fn.extend({ 39901 scrollintoview: function (options) { 39902 /// <summary>Scrolls the first element in the set into view by scrolling its closest scrollable parent.</summary> 39903 /// <param name="options" type="Object">Additional options that can configure scrolling: 39904 /// duration (default: "fast") - jQuery animation speed (can be a duration string or number of milliseconds) 39905 /// direction (default: "both") - select possible scrollings ("vertical" or "y", "horizontal" or "x", "both") 39906 /// complete (default: none) - a function to call when scrolling completes (called in context of the DOM element being scrolled) 39907 /// </param> 39908 /// <return type="jQuery">Returns the same jQuery set that this function was run on.</return> 39909 39910 options = $.extend({}, settings, options); 39911 options.direction = converter[typeof (options.direction) === "string" && options.direction.toLowerCase()] || converter.both; 39912 39913 var dirStr = ""; 39914 if (options.direction.x === true) dirStr = "horizontal"; 39915 if (options.direction.y === true) dirStr = dirStr ? "both" : "vertical"; 39916 39917 var el = this.eq(0); 39918 var scroller = el.closest(":scrollable(" + dirStr + ")"); 39919 39920 // check if there's anything to scroll in the first place 39921 if (scroller.length > 0) 39922 { 39923 scroller = scroller.eq(0); 39924 39925 var dim = { 39926 e: dimensions(el), 39927 s: dimensions(scroller) 39928 }; 39929 39930 var rel = {
39931 top: dim.e.rect.top - (dim.s.rect.top + dim.s.border.top), 39932 bottom: dim.s.rect.bottom - dim.s.border.bottom - dim.s.scrollbar.bottom - dim.e.rect.bottom, 39933 left: dim.e.rect.left - (dim.s.rect.left + dim.s.border.left), 39934 right: dim.s.rect.right - dim.s.border.right - dim.s.scrollbar.right - dim.e.rect.right 39935 }; 39936 39937 var animOptions = {}; 39938 39939 // vertical scroll 39940 if (options.direction.y === true) 39941 { 39942 if (rel.top < 0) 39943 { 39944 animOptions.scrollTop = dim.s.scroll.top + rel.top; 39945 } 39946 else if (rel.top > 0 && rel.bottom < 0) 39947 { 39948 animOptions.scrollTop = dim.s.scroll.top + Math.min(rel.top, -rel.bottom); 39949 } 39950 } 39951 39952 // horizontal scroll 39953 if (options.direction.x === true) 39954 { 39955 if (rel.left < 0) 39956 { 39957 animOptions.scrollLeft = dim.s.scroll.left + rel.left; 39958 } 39959 else if (rel.left > 0 && rel.right < 0) 39960 { 39961 animOptions.scrollLeft = dim.s.scroll.left + Math.min(rel.left, -rel.right); 39962 } 39963 } 39964 39965 // scroll if needed 39966 if (!$.isEmptyObject(animOptions)) 39967 { 39968 if (rootrx.test(scroller[0].nodeName)) 39969 { 39970 scroller = $("html,body"); 39971 } 39972 scroller 39973 .animate(animOptions, options.duration) 39974 .eq(0) // we want function to be called just once (ref. "html,body") 39975 .queue(function (next) { 39976 $.isFunction(options.complete) && options.complete.call(scroller[0]); 39977 next(); 39978 }); 39979 } 39980 else 39981 { 39982 // when there's nothing to scroll, just call the "complete" function 39983 $.isFunction(options.complete) && options.complete.call(scroller[0]); 39984 } 39985 } 39986 39987 // return set back 39988 return this; 39989 } 39990 }); 39991 39992 var scrollValue = { 39993 auto: true, 39994 scroll: true, 39995 visible: false, 39996 hidden: false 39997 }; 39998 39999 $.extend($.expr[":"], { 40000 scrollable: function (element, index, meta, stack) { 40001 var direction = converter[typeof (meta[3]) === "string" && meta[3].toLowerCase()] || converter.both; 40002 var styles = (document.defaultView && document.defaultView.getComputedStyle ? document.defaultView.getComputedStyle(element, null) : element.currentStyle); 40003 var overflow = { 40004 x: scrollValue[styles.overflowX.toLowerCase()] || false, 40005 y: scrollValue[styles.overflowY.toLowerCase()] || false, 40006 isRoot: rootrx.test(element.nodeName) 40007 }; 40008 40009 // check if completely unscrollable (exclude HTML element because it's special) 40010 if (!overflow.x && !overflow.y && !overflow.isRoot) 40011 { 40012 return false; 40013 } 40014 40015 var size = { 40016 height: { 40017 scroll: element.scrollHeight, 40018 client: element.clientHeight 40019 }, 40020 width: { 40021 scroll: element.scrollWidth, 40022 client: element.clientWidth 40023 }, 40024 // check overflow.x/y because iPad (and possibly other tablets) don't dislay scrollbars 40025 scrollableX: function () { 40026 return (overflow.x || overflow.isRoot) && this.width.scroll > this.width.client; 40027 }, 40028 scrollableY: function () { 40029 return (overflow.y || overflow.isRoot) && this.height.scroll > this.height.client; 40030 } 40031 }; 40032 return direction.y && size.scrollableY() || direction.x && size.scrollableX(); 40033 } 40034 }); 40035})(jQuery); 40036(function(a){a.widget("ui.dropdownchecklist",{version:function(){alert("DropDownCheckList v1.4")},_appendDropContainer:function(b){var d=a("<div/>");d.addClass("ui-dropdownchecklist ui-dropdownchecklist-dropcontainer-wrapper");d.addClass("ui-widget");d.attr("id",b.attr("id")+"-ddw");d.css({position:"absolute",left:"-33000px",top:"-33000px"});var c=a("<div/>");c.addClass("ui-dropdownchecklist-dropcontainer ui-widget-content");c.css("overflow-y","auto");d.append(c);d.insertAfter(b);d.isOpen=false;return d},_isDropDownKeyShortcut:function(c,b){return c.altKey&&(a.ui.keyCode.DOWN==b)},_isDropDownCloseKey:function(c,b){return(a.ui.keyCode.ESCAPE==b)||(a.ui.keyCode.ENTER==b)},_keyFocusChange:function(f,i,c){var g=a(":focusable");var d=g.index(f);if(d>=0){d+=i;if(c){var e=this.dropWrapper.find("input:not([disabled])");var b=g.index(e.get(0));var h=g.index(e.get(e.length-1));if(d<b){d=h}else{if(d>h){d=b}}}g.get(d).focus()}},_handleKeyboard:function(d){var b=this;var c=(d.keyCode||d.which);if(!b.dropWrapper.isOpen&&b._isDropDownKeyShortcut(d,c)){d.stopImmediatePropagation();b._toggleDropContainer(true)}else{if(b.dropWrapper.isOpen&&b._isDropDownCloseKey(d,c)){d.stopImmediatePropagation();b._toggleDropContainer(false);b.controlSelector.focus()}else{if(b.dropWrapper.isOpen&&(d.target.type=="checkbox")&&((c==a.ui.keyCode.DOWN)||(c==a.ui.keyCode.UP))){d.stopImmediatePropagation();b._keyFocusChange(d.target,(c==a.ui.keyCode.DOWN)?1:-1,true)}else{if(b.dropWrapper.isOpen&&(c==a.ui.keyCode.TAB)){}}}}},_handleFocus:function(f,d,b){var c=this;if(b&&!c.dropWrapper.isOpen){f.stopImmediatePropagation();if(d){c.controlSelector.addClass("ui-state-hover");if(a.ui.dropdownchecklist.gLastOpened!=null){a.ui.dropdownchecklist.gLastOpened._toggleDropContainer(false)}}else{c.controlSelector.removeClass("ui-state-hover")}}else{if(!b&&!d){if(f!=null){f.stopImmediatePropagation()}c.controlSelector.removeClass("ui-state-hover");c._toggleDropContainer(false)}}},_cancelBlur:function(c){var b=this;if(b.blurringItem!=null){clearTimeout(b.blurringItem);b.blurringItem=null}},_appendControl:function(){var j=this,c=this.sourceSelect,k=this.options;var b=a("<span/>");b.addClass("ui-dropdownchecklist ui-dropdownchecklist-selector-wrapper ui-widget");b.css({display:"inline-block",cursor:"default",overflow:"hidden"});var f=c.attr("id");
40036if((f==null)||(f=="")){f="ddcl-"+a.ui.dropdownchecklist.gIDCounter++}else{f="ddcl-"+f}b.attr("id",f);var h=a("<span/>");h.addClass("ui-dropdownchecklist-selector ui-state-default");h.css({display:"inline-block",overflow:"hidden","white-space":"nowrap"});var d=c.attr("tabIndex");if(d==null){d=0}else{d=parseInt(d);if(d<0){d=0}}h.attr("tabIndex",d);h.keyup(function(l){j._handleKeyboard(l)});h.focus(function(l){j._handleFocus(l,true,true)});h.blur(function(l){j._handleFocus(l,false,true)});b.append(h);if(k.icon!=null){var i=(k.icon.placement==null)?"left":k.icon.placement;var g=a("<div/>");g.addClass("ui-icon");g.addClass((k.icon.toOpen!=null)?k.icon.toOpen:"ui-icon-triangle-1-e");g.css({"float":i});h.append(g)}var e=a("<span/>");e.addClass("ui-dropdownchecklist-text");e.css({display:"inline-block","white-space":"nowrap",overflow:"hidden"});h.append(e);b.hover(function(){if(!j.disabled){h.addClass("ui-state-hover")}},function(){if(!j.disabled){h.removeClass("ui-state-hover")}});b.click(function(l){if(!j.disabled){l.stopImmediatePropagation();j._toggleDropContainer(!j.dropWrapper.isOpen)}});b.insertAfter(c);a(window).resize(function(){if(!j.disabled&&j.dropWrapper.isOpen){j._toggleDropContainer(true)}});return b},_createDropItem:function(g,f,o,l,q,h,e,k){var m=this,c=this.options,d=this.sourceSelect,p=this.controlWrapper;var t=a("<div/>");t.addClass("ui-dropdownchecklist-item");t.css({"white-space":"nowrap"});var r=h?' checked="checked"':"";var j=e?' class="inactive"':' class="active"';var b=p.attr("id");var n=b+"-i"+g;var s;if(m.isMultiple){s=a('<input disabled type="checkbox" id="'+n+'"'+r+j+' tabindex="'+f+'" />')}else{s=a('<input disabled type="radio" id="'+n+'" name="'+b+'"'+r+j+' tabindex="'+f+'" />')}s=s.attr("index",g).val(o);t.append(s);var i=a("<label for="+n+"/>");i.addClass("ui-dropdownchecklist-text");if(q!=null){i.attr("style",q)}i.css({cursor:"default"});i.html(l);if(k){t.addClass("ui-dropdownchecklist-indent")}t.addClass("ui-state-default");if(e){t.addClass("ui-state-disabled")}i.click(function(u){u.stopImmediatePropagation()});t.append(i);t.hover(function(v){var u=a(this);if(!u.hasClass("ui-state-disabled")){u.addClass("ui-state-hover")}},function(v){var u=a(this);u.removeClass("ui-state-hover")});s.click(function(w){var v=a(this);w.stopImmediatePropagation();if(v.hasClass("active")){var x=m.options.onItemClick;if(a.isFunction(x)){try{x.call(m,v,d.get(0))}catch(u){v.prop("checked",!v.prop("checked"));m._syncSelected(v);return}}m._syncSelected(v);m.sourceSelect.trigger("change","ddcl_internal");if(!m.isMultiple&&c.closeRadioOnClick){m._toggleDropContainer(false)}}});t.click(function(y){var x=a(this);y.stopImmediatePropagation();if(!x.hasClass("ui-state-disabled")){var v=x.find("input");var w=v.prop("checked");v.prop("checked",!w);var z=m.options.onItemClick;if(a.isFunction(z)){try{z.call(m,v,d.get(0))}catch(u){v.prop("checked",w);m._syncSelected(v);return}}m._syncSelected(v);m.sourceSelect.trigger("change","ddcl_internal");if(!w&&!m.isMultiple&&c.closeRadioOnClick){m._toggleDropContainer(false)}}else{x.focus();m._cancelBlur()}});t.focus(function(v){var u=a(this);v.stopImmediatePropagation()});t.keyup(function(u){m._handleKeyboard(u)});return t},_createGroupItem:function(f,d){var b=this;var e=a("<div />");e.addClass("ui-dropdownchecklist-group ui-widget-header");if(d){e.addClass("ui-state-disabled")}e.css({"white-space":"nowrap"});var c=a("<span/>");c.addClass("ui-dropdownchecklist-text");c.css({cursor:"default"});c.text(f);e.append(c);e.click(function(h){var g=a(this);h.stopImmediatePropagation();g.focus();b._cancelBlur()});e.focus(function(h){var g=a(this);h.stopImmediatePropagation()});return e},_createCloseItem:function(e){var b=this;var d=a("<div />");d.addClass("ui-state-default ui-dropdownchecklist-close ui-dropdownchecklist-item");d.css({"white-space":"nowrap","text-align":"right"});var c=a("<span/>");c.addClass("ui-dropdownchecklist-text");c.css({cursor:"default"});c.html(e);d.append(c);d.click(function(g){var f=a(this);g.stopImmediatePropagation();f.focus();b._toggleDropContainer(false)});d.hover(function(f){a(this).addClass("ui-state-hover")},function(f){a(this).removeClass("ui-state-hover")});d.focus(function(g){var f=a(this);g.stopImmediatePropagation()});return d},_appendItems:function(){var d=this,f=this.options,h=this.sourceSelect,g=this.dropWrapper;var b=g.find(".ui-dropdownchecklist-dropcontainer");h.children().each(function(j){var k=a(this);if(k.is("option")){d._appendOption(k,b,j,false,false)}else{if(k.is("optgroup")){var l=k.prop("disabled");var n=k.attr("label");if(n!=""){var m=d._createGroupItem(n,l);b.append(m)}d._appendOptions(k,b,j,true,l)}}});if(f.explicitClose!=null){var i=d._createCloseItem(f.explicitClose);b.append(i)}var c=b.outerWidth();var e=b.outerHeight();return{width:c,height:e}},_appendOptions:function(g,d,f,c,b){var e=this;g.children("option").each(function(h){var i=a(this);var j=(f+"."+h);e._appendOption(i,d,j,c,b)})},_appendOption:function(g,b,h,d,n){var m=this;var k=g.html();if((k!=null)&&(k!="")){var j=g.val();var i=g.attr("style");var f=g.prop("selected");var e=(n||g.prop("disabled"));var c=m.controlSelector.attr("tabindex");var l=m._createDropItem(h,c,j,k,i,f,e,d);b.append(l)}},_syncSelected:function(h){var i=this,l=this.options,b=this.sourceSelect,d=this.dropWrapper;var c=b.get(0).options;var g=d.find("input.active");if(l.firstItemChecksAll=="exclusive"){if((h==null)&&a(c[0]).prop("selected")){g.prop("checked",false);a(g[0]).prop("checked",true)}
40036else{if((h!=null)&&(h.attr("index")==0)){var e=h.prop("checked");g.prop("checked",false);a(g[0]).prop("checked",e)}else{var f=true;var k=null;g.each(function(m){if(m>0){var n=a(this).prop("checked");if(!n){f=false}}else{k=a(this)}});if(k!=null){if(f){g.prop("checked",false)}k.prop("checked",f)}}}}else{if(l.firstItemChecksAll){if((h==null)&&a(c[0]).prop("selected")){g.prop("checked",true)}else{if((h!=null)&&(h.attr("index")==0)){g.prop("checked",h.prop("checked"))}else{var f=true;var k=null;g.each(function(m){if(m>0){var n=a(this).prop("checked");if(!n){f=false}}else{k=a(this)}});if(k!=null){k.prop("checked",f)}}}}}var j=0;g=d.find("input");g.each(function(n){var m=a(c[n+j]);var o=m.html();if((o==null)||(o=="")){j+=1;m=a(c[n+j])}m.prop("selected",a(this).prop("checked"))});i._updateControlText();if(h!=null){h.focus()}},_sourceSelectChangeHandler:function(c){var b=this,d=this.dropWrapper;d.find("input").val(b.sourceSelect.val());b._updateControlText()},_updateControlText:function(){var c=this,g=this.sourceSelect,d=this.options,f=this.controlWrapper;var h=g.find("option:first");var b=g.find("option");var i=c._formatText(b,d.firstItemChecksAll,h);var e=f.find(".ui-dropdownchecklist-text");e.html(i);e.attr("title",e.text())},_formatText:function(b,d,e){var f;if(a.isFunction(this.options.textFormatFunction)){try{f=this.options.textFormatFunction(b)}catch(c){alert("textFormatFunction failed: "+c)}}else{if(d&&(e!=null)&&e.prop("selected")){f=e.html()}else{f="";b.each(function(){if(a(this).prop("selected")){if(f!=""){f+=", "}var g=a(this).attr("style");var h=a("<span/>");h.html(a(this).html());if(g==null){f+=h.html()}else{h.attr("style",g);f+=a("<span/>").append(h).html()}}});if(f==""){f=(this.options.emptyText!=null)?this.options.emptyText:" "}}}return f},_toggleDropContainer:function(e){var c=this;var d=function(f){if((f!=null)&&f.dropWrapper.isOpen){f.dropWrapper.isOpen=false;a.ui.dropdownchecklist.gLastOpened=null;var h=f.options;f.dropWrapper.css({top:"-33000px",left:"-33000px"});var g=f.controlSelector;g.removeClass("ui-state-active");g.removeClass("ui-state-hover");var j=f.controlWrapper.find(".ui-icon");if(j.length>0){j.removeClass((h.icon.toClose!=null)?h.icon.toClose:"ui-icon-triangle-1-s");j.addClass((h.icon.toOpen!=null)?h.icon.toOpen:"ui-icon-triangle-1-e")}a(document).unbind("click",d);f.dropWrapper.find("input.active").prop("disabled",true);if(a.isFunction(h.onComplete)){try{h.onComplete.call(f,f.sourceSelect.get(0))}catch(i){alert("callback failed: "+i)}}}};var b=function(n){if(!n.dropWrapper.isOpen){n.dropWrapper.isOpen=true;a.ui.dropdownchecklist.gLastOpened=n;var g=n.options;if((g.positionHow==null)||(g.positionHow=="absolute")){n.dropWrapper.css({position:"absolute",top:n.controlWrapper.position().top+n.controlWrapper.outerHeight()+"px",left:n.controlWrapper.position().left+"px"})}else{if(g.positionHow=="relative"){n.dropWrapper.css({position:"relative",top:"0px",left:"0px"})}}var m=0;if(g.zIndex==null){var l=n.controlWrapper.parents().map(function(){var o=a(this).css("z-index");return isNaN(o)?0:o}).get();var i=Math.max.apply(Math,l);if(i>=0){m=i+1}}else{m=parseInt(g.zIndex)}if(m>0){n.dropWrapper.css({"z-index":m})}var j=n.controlSelector;j.addClass("ui-state-active");j.removeClass("ui-state-hover");var h=n.controlWrapper.find(".ui-icon");if(h.length>0){h.removeClass((g.icon.toOpen!=null)?g.icon.toOpen:"ui-icon-triangle-1-e");h.addClass((g.icon.toClose!=null)?g.icon.toClose:"ui-icon-triangle-1-s")}a(document).bind("click",function(o){d(n)});var f=n.dropWrapper.find("input.active");f.prop("disabled",false);var k=f.get(0);if(k!=null){k.focus()}}};if(e){d(a.ui.dropdownchecklist.gLastOpened);b(c)}else{d(c)}},_setSize:function(b){var m=this.options,f=this.dropWrapper,l=this.controlWrapper;var k=b.width;if(m.width!=null){k=parseInt(m.width)}else{if(m.minWidth!=null){var c=parseInt(m.minWidth);if(k<c){k=c}}}var i=this.controlSelector;i.css({width:k+"px"});var g=i.find(".ui-dropdownchecklist-text");var d=i.find(".ui-icon");if(d!=null){k-=(d.outerWidth()+4);g.css({width:k+"px"})}k=l.outerWidth();var j=(m.maxDropHeight!=null)?parseInt(m.maxDropHeight):-1;var h=((j>0)&&(b.height>j))?j:b.height;var e=b.width<k?k:b.width;a(f).css({height:h+"px",width:e+"px"});f.find(".ui-dropdownchecklist-dropcontainer").css({height:h+"px"})},_init:function(){var c=this,d=this.options;if(a.ui.dropdownchecklist.gIDCounter==null){a.ui.dropdownchecklist.gIDCounter=1}c.blurringItem=null;var g=c.element;c.initialDisplay=g.css("display");g.css("display","none");c.initialMultiple=g.prop("multiple");c.isMultiple=c.initialMultiple;if(d.forceMultiple!=null){c.isMultiple=d.forceMultiple}g.prop("multiple",true);c.sourceSelect=g;var e=c._appendControl();c.controlWrapper=e;c.controlSelector=e.find(".ui-dropdownchecklist-selector");var f=c._appendDropContainer(e);c.dropWrapper=f;var b=c._appendItems();c._updateControlText(e,f,g);c._setSize(b);if(d.firstItemChecksAll){c._syncSelected(null)}if(d.bgiframe&&typeof c.dropWrapper.bgiframe=="function"){c.dropWrapper.bgiframe()}c.sourceSelect.change(function(i,h){if(h!="ddcl_internal"){c._sourceSelectChangeHandler(i)}})},_refreshOption:function(e,d,c){var b=e.parent();if(d){e.prop("disabled",true);e.removeClass("active");e.addClass("inactive");b.addClass("ui-state-disabled")}
40036else{e.prop("disabled",false);e.removeClass("inactive");e.addClass("active");b.removeClass("ui-state-disabled")}e.prop("checked",c)},_refreshGroup:function(c,b){if(b){c.addClass("ui-state-disabled")}else{c.removeClass("ui-state-disabled")}},close:function(){this._toggleDropContainer(false)},refresh:function(){var b=this,e=this.sourceSelect,d=this.dropWrapper;var c=d.find("input");var g=d.find(".ui-dropdownchecklist-group");var h=0;var f=0;e.children().each(function(i){var j=a(this);var l=j.prop("disabled");if(j.is("option")){var k=j.prop("selected");var n=a(c[f]);b._refreshOption(n,l,k);f+=1}else{if(j.is("optgroup")){var o=j.attr("label");if(o!=""){var m=a(g[h]);b._refreshGroup(m,l);h+=1}j.children("option").each(function(){var p=a(this);var r=(l||p.prop("disabled"));var q=p.prop("selected");var s=a(c[f]);b._refreshOption(s,r,q);f+=1})}}});b._syncSelected(null)},enable:function(){this.controlSelector.removeClass("ui-state-disabled");this.disabled=false},disable:function(){this.controlSelector.addClass("ui-state-disabled");this.disabled=true},destroy:function(){a.Widget.prototype.destroy.apply(this,arguments);this.sourceSelect.css("display",this.initialDisplay);this.sourceSelect.prop("multiple",this.initialMultiple);this.controlWrapper.unbind().remove();this.dropWrapper.remove()}});a.extend(a.ui.dropdownchecklist,{defaults:{width:null,maxDropHeight:null,firstItemChecksAll:false,closeRadioOnClick:false,minWidth:50,positionHow:"absolute",bgiframe:false,explicitClose:null}})})(jQuery); 40037/*! 40038 *jQuery UI Spinner 1.25 40039 * 40040 *Copyright (c) 2009-2010 Brant Burnett 40041 *Copyright (c) 2010 Johannes Geppert http://www.jgeppert.com 40042 *Dual licensed under the MIT or GPL Version 2 licenses. 40043 * 40044 * Depends: 40045 * jquery.ui.core.js 40046 * jquery.ui.widget.js 40047 * jquery.ui.mouse.js 40048 * jquery.ui.position.js 40049 */ 40050 40051(function(g,h){var i="ui-state-active",s="ui-state-hover",e="ui-state-disabled",k=g.ui.keyCode,m=k.UP,n=k.DOWN,r=k.RIGHT,f=k.LEFT,j=k.PAGE_UP,p=k.PAGE_DOWN,q=k.HOME,d=k.END,l=g.browser.msie,c=g.browser.mozilla?"DOMMouseScroll":"mousewheel",o=".uispinner",a=[m,n,r,f,j,p,q,d,k.BACKSPACE,k.DELETE,k.TAB],b;g.widget("ui.spinner",{options:{min:null,max:null,allowNull:false,group:"",point:".",prefix:"",suffix:"",places:null,defaultStep:1,largeStep:10,mouseWheel:true,increment:"slow",className:null,showOn:"always",width:16,upIconClass:"ui-icon-triangle-1-n",downIconClass:"ui-icon-triangle-1-s",format:function(w,v){var u=this,x=/(\d+)(\d{3})/,t=((isNaN(w)?0:Math.abs(w)).toFixed(v))+"";for(t=t.replace(".",u.point);x.test(t)&&u.group;t=t.replace(x,"$1"+u.group+"$2")){}return(w<0?"-":"")+u.prefix+t+u.suffix},parse:function(u){var t=this;if(t.group=="."){u=u.replace(".","")}if(t.point!="."){u=u.replace(t.point,".")}return parseFloat(u.replace(/[^0-9\-\.]/g,""))}},_create:function(){var u=this,t=u.element,v=t.attr("type");if(!t.is("input")||((v!="text")&&(v!="number"))){console.error("Invalid target for ui.spinner");return}u._procOptions(true);u._createButtons(t);if(!t.is(":enabled")){u.disable()}},_createButtons:function(E){function D(W){return W=="auto"?0:parseInt(W,10)}var H=this,x=H.options,v=x.className,N=x.width,y=x.showOn,C=g.support.boxModel,I=E.outerHeight(),R=H.oMargin=D(E.css("margin-right")),A=H.wrapper=E.css({width:(H.oWidth=(C?E.width():E.outerWidth()))-N,marginRight:R+N,textAlign:"right"}).after("<span class='ui-spinner ui-widget' style='top:0;left:0;'></span>").next(),L=H.btnContainer=g("<div class='ui-spinner-buttons'><div class='ui-spinner-up ui-spinner-button ui-state-default ui-corner-tr'><span class='ui-icon "+x.upIconClass+"'> </span></div><div class='ui-spinner-down ui-spinner-button ui-state-default ui-corner-br'><span class='ui-icon "+x.downIconClass+"'> </span></div></div>"),w,u,Q,V,F,B,G,P,J,O,z=E[0].dir=="rtl";if(v){A.addClass(v)}A.append(L.css({height:I,left:-N-R,top:(E.offset().top-A.offset().top)+"px"}));Q=H.buttons=L.find(".ui-spinner-button");Q.css({width:N-(C?Q.outerWidth()-Q.width():0),height:I/2-(C?Q.outerHeight()-Q.height():0)});w=Q[0];u=Q[1];V=Q.find(".ui-icon");V.css({marginLeft:(Q.innerWidth()-V.width())/2,marginTop:(Q.innerHeight()-V.height())/2});L.width(Q.outerWidth());if(y!="always"){L.css("opacity",0)}E.addClass("ui-spinner-input").attr({role:"textbox"});A.position({of:this.element,my:"left center",at:"right center",offset:"0 0",collision:"flip flip"});if(y=="hover"||y=="both"){Q.add(E).bind("mouseenter"+o,function(){T(function(){G=true;if(!H.focused||(y=="hover")){H.showButtons()}})}).bind("mouseleave"+o,function K(){T(function(){G=false;if(!H.focused||(y=="hover")){H.hideButtons()}})})}function U(){if(!x.disabled){var W=H.element[0],X=(this===w?1:-1);W.focus();W.select();g(this).addClass(i);O=true;H._startSpin(X)}return false}function S(){if(O){g(this).removeClass(i);H._stopSpin();O=false}return false}Q.hover(function(){H.buttons.removeClass(s);if(!x.disabled){g(this).addClass(s)}},function(){g(this).removeClass(s)}).mousedown(U).mouseup(S).mouseout(S);if(l){Q.dblclick(function(){if(!x.disabled){H._change();H._doSpin((this===w?1:-1)*x.step)}return false}).bind("selectstart",function(){return false})}E.bind("keydown"+o,function(aa){var X,Y,W,Z=aa.keyCode;if(aa.ctrl||aa.alt){return true}if(t(Z)){J=true}if(P){return false}switch(Z){case m:case j:X=1;Y=Z==j;break;case n:case p:X=-1;Y=Z==p;break;case r:case f:X=(Z==r)^z?1:-1;break;case q:W=H.options.min;if(W!==null){H._setValue(W)}return false;case d:W=H.options.max;W=H.options.max;if(W!==null){H._setValue(W)}return false}
40051if(X){if(!P&&!x.disabled){keyDir=X;g(X>0?w:u).addClass(i);P=true;H._startSpin(X,Y)}return false}}).bind("keyup"+o,function(W){if(W.ctrl||W.alt){return true}if(t(k)){J=false}switch(W.keyCode){case m:case r:case j:case n:case f:case p:Q.removeClass(i);H._stopSpin();P=false;return false}}).bind("change"+o,function(){H._change()}).bind("focus"+o,function(){function W(){H.element.select()}if(l){W()}else{setTimeout(W,0)}H.focused=true;b=H;if(!G&&(y=="focus"||y=="both")){H.showButtons()}}).bind("blur"+o,function(){H.focused=false;if(!G&&(y=="focus"||y=="both")){H.hideButtons()}});function t(X){for(var W=0;W<a.length;W++){if(a[W]==X){return true}}return false}function M(Z,W){if(J){return false}var Y=String.fromCharCode(W||Z),X=H.options;if((Y>="0")&&(Y<="9")||(Y=="-")){return false}if(((H.places>0)&&(Y==X.point))||(Y==X.group)){return false}return true}function T(X){if(F){if(X===B){return}clearTimeout(F)}function W(){F=0;X()}B=X;F=setTimeout(W,100)}},_procOptions:function(A){var B=this,y=B.element,C=B.options,w=C.min,x=C.max,v=C.step,t=C.places,u=-1,z;if(C.increment=="slow"){C.increment=[{count:1,mult:1,delay:250},{count:3,mult:1,delay:100},{count:0,mult:1,delay:50}]}else{if(C.increment=="fast"){C.increment=[{count:1,mult:1,delay:250},{count:19,mult:1,delay:100},{count:80,mult:1,delay:20},{count:100,mult:10,delay:20},{count:0,mult:100,delay:20}]}}if((w===null)&&((z=y.attr("min"))!==null)){w=parseFloat(z)}if((x===null)&&((z=y.attr("max"))!==null)){x=parseFloat(z)}if(!v&&((z=y.attr("step"))!==null)){if(z!="any"){v=parseFloat(z);C.largeStep*=v}}C.step=v=v||C.defaultStep;if((t===null)&&((z=v+"").indexOf(".")!=-1)){t=z.length-z.indexOf(".")-1}B.places=t;if((x!==null)&&(w!==null)){if(w>x){w=x}u=Math.max(Math.max(u,C.format(x,t,y).length),C.format(w,t,y).length)}if(A){B.inputMaxLength=y[0].maxLength}z=B.inputMaxLength;if(z>0){u=u>0?Math.min(z,u):z;z=Math.pow(10,u)-1;if((x===null)||(x>z)){x=z}z=-(z+1)/10+1;if((w===null)||(w<z)){w=z}}if(u>0){y.attr("maxlength",u)}C.min=w;C.max=x;B._change();y.unbind(c+o);if(C.mouseWheel){y.bind(c+o,B._mouseWheel)}},_mouseWheel:function(u){var t=g.data(this,"spinner");if(!t.options.disabled&&t.focused&&(b===t)){t._change();t._doSpin(((u.wheelDelta||-u.detail)>0?1:-1)*t.options.step);return false}},_setTimer:function(v,u,x){function w(){t._spin(u,x)}var t=this;t._stopSpin();t.timer=setInterval(w,v)},_stopSpin:function(){if(this.timer){clearInterval(this.timer);this.timer=0}},_startSpin:function(w,x){var u=this,v=u.options,t=v.increment;u._change();u._doSpin(w*(x?u.options.largeStep:u.options.step));if(t&&t.length>0){u.counter=0;u.incCounter=0;u._setTimer(t[0].delay,w,x)}},_spin:function(v,w){var u=this,t=u.options.increment,x=t[u.incCounter];u._doSpin(v*x.mult*(w?u.options.largeStep:u.options.step));u.counter++;if((u.counter>x.count)&&(u.incCounter<t.length-1)){u.counter=0;x=t[++u.incCounter];u._setTimer(x.delay,v,w)}},_doSpin:function(u){var t=this,v=t.curvalue;if(v===null){v=(u>0?t.options.min:t.options.max)||0}t._setValue(v+u)},_parseValue:function(){var t=this.element.val();return t?this.options.parse(t,this.element):null},_validate:function(w){var u=this.options,v=u.min,t=u.max;if((w===null)&&!u.allowNull){w=this.curvalue!==null?this.curvalue:v||t||0}if((t!==null)&&(w>t)){return t}else{if((v!==null)&&(w<v)){return v}else{return w}}},_change:function(){var u=this,w=u._parseValue(),v=u.options.min,t=u.options.max;if(!u.selfChange){if(isNaN(w)){w=u.curvalue}u._setValue(w,true)}},_setOption:function(t,u){g.Widget.prototype._setOption.call(this,t,u);this._procOptions()},increment:function(){this._doSpin(this.options.step)},decrement:function(){this._doSpin(-this.options.step)},showButtons:function(u){var t=this.btnContainer.stop();if(u){t.css("opacity",1)}else{t.fadeTo("fast",1)}},hideButtons:function(u){var t=this.btnContainer.stop();if(u){t.css("opacity",0)}else{t.fadeTo("fast",0)}this.buttons.removeClass(s)},_setValue:function(v,t){var u=this;u.curvalue=v=u._validate(v);u.element.val(v!==null?u.options.format(v,u.places,u.element):"");if(!t){u.selfChange=true;u.element.change();u.selfChange=false}},value:function(t){if(arguments.length){this._setValue(t);return this.element}return this.curvalue},enable:function(){this.buttons.removeClass(e);this.element[0].disabled=false;g.Widget.prototype.enable.call(this)},disable:function(){this.buttons.addClass(e).removeClass(s);this.element[0].disabled=true;g.Widget.prototype.disable.call(this)},destroy:function(t){this.wrapper.remove();this.element.unbind(o).css({width:this.oWidth,marginRight:this.oMargin});g.Widget.prototype.destroy.call(this)}})})(jQuery); 40052/** 40053 * This jQuery plugin displays pagination links inside the selected elements. 40054 * 40055 * This plugin needs at least jQuery 1.4.2 40056 * 40057 * @author Gabriel Birke (birke *at* d-scribe *dot* de) 40058 * @version 2.2 40059 * @param {int} maxentries Number of entries to paginate 40060 * @param {Object} opts Several options (see README for documentation) 40061 * @return {Object} jQuery Object 40062 */ 40063 40064 (function($){ 40065 /** 40066 * @class Class for calculating pagination values 40067 */ 40068 $.PaginationCalculator = function(maxentries, opts) { 40069 this.maxentries = maxentries; 40070 this.opts = opts; 40071 } 40072 40073 $.extend($.PaginationCalculator.prototype, { 40074 /** 40075 * Calculate the maximum number of pages 40076 * @method 40077 * @returns {Number} 40078 */ 40079 numPages:function() { 40080 return Math.ceil(this.maxentries/this.opts.items_per_page); 40081 }, 40082 /** 40083 * Calculate start and end point of pagination links depending on 40084 * current_page and num_display_entries. 40085 * @returns {Array} 40086 */ 40087 getInterval:function(current_page) { 40088 var ne_half = Math.floor(this.opts.num_display_entries/2); 40089 var np = this.numPages(); 40090 var upper_limit = np - this.opts.num_display_entries; 40091 var start = current_page > ne_half ? Math.max( Math.min(current_page - ne_half, upper_limit), 0 ) : 0; 40092 var end = current_page > ne_half?Math.min(current_page+ne_half + (this.opts.num_display_entries % 2), np):Math.min(this.opts.num_display_entries, np); 40093 return {start:start, end:end}; 40094 } 40095 }); 40096
40097 // Initialize jQuery object container for pagination renderers 40098 $.PaginationRenderers = {} 40099 40100 /** 40101 * @class Default renderer for rendering pagination links 40102 */ 40103 $.PaginationRenderers.defaultRenderer = function(maxentries, opts) { 40104 this.maxentries = maxentries; 40105 this.opts = opts; 40106 this.pc = new $.PaginationCalculator(maxentries, opts); 40107 } 40108 $.extend($.PaginationRenderers.defaultRenderer.prototype, { 40109 /** 40110 * Helper function for generating a single link (or a span tag if it's the current page) 40111 * @param {Number} page_id The page id for the new item 40112 * @param {Number} current_page 40113 * @param {Object} appendopts Options for the new item: text and classes 40114 * @returns {jQuery} jQuery object containing the link 40115 */ 40116 createLink:function(page_id, current_page, appendopts){ 40117 var lnk, np = this.pc.numPages(); 40118 page_id = page_id<0?0:(page_id<np?page_id:np-1); // Normalize page id to sane value 40119 appendopts = $.extend({text:page_id+1, classes:""}, appendopts||{}); 40120 if(page_id == current_page){ 40121 lnk = $("<span class='current'>" + appendopts.text + "</span>"); 40122 } 40123 else 40124 { 40125 lnk = $("<a>" + appendopts.text + "</a>") 40126 .attr('href', this.opts.link_to.replace(/__id__/,page_id)); 40127 } 40128 if(appendopts.classes){ lnk.addClass(appendopts.classes); } 40129 lnk.data('page_id', page_id); 40130 return lnk; 40131 }, 40132 // Generate a range of numeric links 40133 appendRange:function(container, current_page, start, end, opts) { 40134 var i; 40135 for(i=start; i<end; i++) { 40136 this.createLink(i, current_page, opts).appendTo(container); 40137 } 40138 }, 40139 getLinks:function(current_page, eventHandler) { 40140 var begin, end, 40141 interval = this.pc.getInterval(current_page), 40142 np = this.pc.numPages(), 40143 fragment = $("<div class='pagination'></div>"); 40144 40145 // Generate "Previous"-Link 40146 if(this.opts.prev_text && (current_page > 0 || this.opts.prev_show_always)){ 40147 fragment.append(this.createLink(current_page-1, current_page, {text:this.opts.prev_text, classes:"prev"})); 40148 } 40149 // Generate starting points 40150 if (interval.start > 0 && this.opts.num_edge_entries > 0) 40151 { 40152 end = Math.min(this.opts.num_edge_entries, interval.start); 40153 this.appendRange(fragment, current_page, 0, end, {classes:'sp'}); 40154 if(this.opts.num_edge_entries < interval.start && this.opts.ellipse_text) 40155 { 40156 jQuery("<span>"+this.opts.ellipse_text+"</span>").appendTo(fragment); 40157 } 40158 } 40159 // Generate interval links 40160 this.appendRange(fragment, current_page, interval.start, interval.end); 40161 // Generate ending points 40162 if (interval.end < np && this.opts.num_edge_entries > 0) 40163 { 40164 if(np-this.opts.num_edge_entries > interval.end && this.opts.ellipse_text) 40165 { 40166 jQuery("<span>"+this.opts.ellipse_text+"</span>").appendTo(fragment); 40167 } 40168 begin = Math.max(np-this.opts.num_edge_entries, interval.end); 40169 this.appendRange(fragment, current_page, begin, np, {classes:'ep'}); 40170 40171 } 40172 // Generate "Next"-Link 40173 if(this.opts.next_text && (current_page < np-1 || this.opts.next_show_always)){ 40174 fragment.append(this.createLink(current_page+1, current_page, {text:this.opts.next_text, classes:"next"})); 40175 } 40176 $('a', fragment).click(eventHandler); 40177 return fragment; 40178 } 40179 }); 40180 40181 // Extend jQuery 40182 $.fn.pagination = function(maxentries, opts){ 40183 40184 // Initialize options with default values 40185 opts = jQuery.extend({ 40186 items_per_page:10, 40187 num_display_entries:11, 40188 current_page:0, 40189 num_edge_entries:0, 40190 link_to:"#", 40191 prev_text:"Prev", 40192 next_text:"Next", 40193 ellipse_text:"...", 40194 prev_show_always:true, 40195 next_show_always:true, 40196 renderer:"defaultRenderer", 40197 load_first_page:false, 40198 callback:function(){return false;} 40199 },opts||{}); 40200 40201 var containers = this, 40202 renderer, links, current_page; 40203 40204 /** 40205 * This is the event handling function for the pagination links. 40206 * NOTE: added evt.preventDefault() to the original code to prevent loading the link 40207 * @param {int} page_id The new page number 40208 */ 40209 function paginationClickHandler(evt){ 40210 40211 // the default is for browser to load the URL at the link 40212 // However even the link generated here such as "http://original_url#" will cause jquery history to 40213 // think there is a state change in IE8/9. This will cause the app to load state. 40214 evt.preventDefault(); 40215 40216 var links, 40217 new_current_page = $(evt.target).data('page_id'), 40218 continuePropagation = selectPage(new_current_page); 40219 if (!continuePropagation) { 40220 evt.stopPropagation(); 40221 } 40222 return continuePropagation; 40223 } 40224 40225 /** 40226 * This is a utility function for the internal event handlers. 40227 * It sets the new current page on the pagination container objects, 40228 * generates a new HTMl fragment for the pagination links and calls 40229 * the callback function. 40230 */ 40231 function selectPage(new_current_page) { 40232 // update the link display of a all containers 40233 containers.data('current_page', new_current_page); 40234 links = renderer.getLinks(new_current_page, paginationClickHandler); 40235 containers.empty();
40236 links.appendTo(containers); 40237 // call the callback and propagate the event if it does not return false 40238 var continuePropagation = opts.callback(new_current_page, containers); 40239 return continuePropagation; 40240 } 40241 40242 // ----------------------------------- 40243 // Initialize containers 40244 // ----------------------------------- 40245 current_page = opts.current_page; 40246 containers.data('current_page', current_page); 40247 // Create a sane value for maxentries and items_per_page 40248 maxentries = (!maxentries || maxentries < 0)?1:maxentries; 40249 opts.items_per_page = (!opts.items_per_page || opts.items_per_page < 0)?1:opts.items_per_page; 40250 40251 if(!$.PaginationRenderers[opts.renderer]) 40252 { 40253 throw new ReferenceError("Pagination renderer '" + opts.renderer + "' was not found in jQuery.PaginationRenderers object."); 40254 } 40255 renderer = new $.PaginationRenderers[opts.renderer](maxentries, opts); 40256 40257 // Attach control events to the DOM elements 40258 var pc = new $.PaginationCalculator(maxentries, opts); 40259 var np = pc.numPages(); 40260 containers.bind('setPage', {numPages:np}, function(evt, page_id) { 40261 if(page_id >= 0 && page_id < evt.data.numPages) { 40262 selectPage(page_id); return false; 40263 } 40264 }); 40265 containers.bind('prevPage', function(evt){ 40266 var current_page = $(this).data('current_page'); 40267 if (current_page > 0) { 40268 selectPage(current_page - 1); 40269 } 40270 return false; 40271 }); 40272 containers.bind('nextPage', {numPages:np}, function(evt){ 40273 var current_page = $(this).data('current_page'); 40274 if(current_page < evt.data.numPages - 1) { 40275 selectPage(current_page + 1); 40276 } 40277 return false; 40278 }); 40279 40280 // When all initialisation is done, draw the links 40281 links = renderer.getLinks(current_page, paginationClickHandler); 40282 containers.empty(); 40283 links.appendTo(containers); 40284 // call callback function 40285 if(opts.load_first_page) { 40286 opts.callback(current_page, containers); 40287 } 40288 } // End of $.fn.pagination block 40289 40290})(jQuery); 40291(function($){ 40292 $.fn.clonePosition = function(element, options){ 40293 var options = $.extend({ 40294 setLeft : true, 40295 setTop : true, 40296 setWidth: false, 40297 setHeight: false, 40298 offsetLeft: 0, 40299 offsetTop: 0 40300 }, (options || {})); 40301 40302 var el = $(this); //what to position 40303 var src = $(element); //where to position 40304 var offsets = el.offset(); 40305 var p = [src.offset().left, src.offset().top]; 40306 var delta = [0, 0]; 40307 //difference in size between element and source (to align centers, not top-left corners) 40308 var delta_size =[0, 0]; 40309 var parent = null; 40310 40311 if(el.css('position') == 'absolute') { 40312 d = el.offset();//relative to the parent 40313 delta[0]=d.left; 40314 delta[1]=d.top; 40315 } 40316 //use outer width to account for borders and padding 40317 delta_size[0] = (el.outerWidth()-src.outerWidth())/2; 40318 delta_size[1] = (el.outerHeight()-src.outerHeight())/2; 40319 40320 if(parent == document.body){ 40321 delta[0] -= document.body.offsetLeft; 40322 delta[1] -= document.body.offsetTop; 40323 } 40324 40325 if(options.setLeft) el.css('left',(p[0] - delta[0] + options.offsetLeft - delta_size[0]) + 'px'); 40326 if(options.setTop) el.css('top', (p[1] - delta[1] + options.offsetTop - delta_size[1]) + 'px'); 40327 if(options.setWidth) el.width(src.width() + 'px'); 40328 if(options.setHeight) el.height(src.height() + 'px'); 40329 40330 return el; 40331 } 40332})(jQuery); 40333/* Prototype JavaScript framework, version 1.6.1 40334 * (c) 2005-2009 Sam Stephenson 40335 * 40336 * Prototype is freely distributable under the terms of an MIT-style license. 40337 * For details, see the Prototype web site: http://www.prototypejs.org/ 40338 * 40339 *--------------------------------------------------------------------------*/ 40340 40341 40342var Prototype = { 40343 Version: '1.6.1', 40344 40345 Browser: (function(){ 40346 var ua = navigator.userAgent; 40347 var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]'; 40348 return { 40349 IE: !!window.attachEvent && !isOpera, 40350 Opera: isOpera, 40351 WebKit: ua.indexOf('AppleWebKit/') > -1, 40352 Gecko: ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1, 40353 MobileSafari: /Apple.*Mobile.*Safari/.test(ua) 40354 } 40355 })(), 40356 40357 BrowserFeatures: { 40358 XPath: !!document.evaluate, 40359 SelectorsAPI: !!document.querySelector, 40360 ElementExtensions: (function() { 40361 var constructor = window.Element || window.HTMLElement; 40362 return !!(constructor && constructor.prototype); 40363 })(), 40364 SpecificElementExtensions: (function() { 40365 if (typeof window.HTMLDivElement !== 'undefined') 40366 return true; 40367 40368 var div = document.createElement('div'); 40369 var form = document.createElement('form'); 40370 var isSupported = false;
40371 40372 if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) { 40373 isSupported = true; 40374 } 40375 40376 div = form = null; 40377 40378 return isSupported; 40379 })() 40380 }, 40381 40382 ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>', 40383 JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/, 40384 40385 emptyFunction: function() { }, 40386 K: function(x) { return x } 40387}; 40388 40389if (Prototype.Browser.MobileSafari) 40390 Prototype.BrowserFeatures.SpecificElementExtensions = false; 40391 40392 40393var Abstract = { }; 40394 40395 40396var Try = { 40397 these: function() { 40398 var returnValue; 40399 40400 for (var i = 0, length = arguments.length; i < length; i++) { 40401 var lambda = arguments[i]; 40402 try { 40403 returnValue = lambda(); 40404 break; 40405 } catch (e) { } 40406 } 40407 40408 return returnValue; 40409 } 40410}; 40411 40412/* Based on Alex Arnell's inheritance implementation. */ 40413 40414var Class = (function() { 40415 function subclass() {}; 40416 function create() { 40417 var parent = null, properties = $A(arguments); 40418 if (Object.isFunction(properties[0])) 40419 parent = properties.shift(); 40420 40421 function klass() { 40422 this.initialize.apply(this, arguments); 40423 } 40424 40425 Object.extend(klass, Class.Methods); 40426 klass.superclass = parent; 40427 klass.subclasses = []; 40428 40429 if (parent) { 40430 subclass.prototype = parent.prototype; 40431 klass.prototype = new subclass; 40432 parent.subclasses.push(klass); 40433 } 40434 40435 for (var i = 0; i < properties.length; i++) 40436 klass.addMethods(properties[i]); 40437 40438 if (!klass.prototype.initialize) 40439 klass.prototype.initialize = Prototype.emptyFunction; 40440 40441 klass.prototype.constructor = klass; 40442 return klass; 40443 } 40444 40445 function addMethods(source) { 40446 var ancestor = this.superclass && this.superclass.prototype; 40447 var properties = Object.keys(source); 40448 40449 if (!Object.keys({ toString: true }).length) { 40450 if (source.toString != Object.prototype.toString) 40451 properties.push("toString"); 40452 if (source.valueOf != Object.prototype.valueOf) 40453 properties.push("valueOf"); 40454 } 40455 40456 for (var i = 0, length = properties.length; i < length; i++) { 40457 var property = properties[i], value = source[property]; 40458 if (ancestor && Object.isFunction(value) && 40459 value.argumentNames().first() == "$super") { 40460 var method = value; 40461 value = (function(m) { 40462 return function() { return ancestor[m].apply(this, arguments); }; 40463 })(property).wrap(method); 40464 40465 value.valueOf = method.valueOf.bind(method); 40466 value.toString = method.toString.bind(method); 40467 } 40468 this.prototype[property] = value; 40469 } 40470 40471 return this; 40472 } 40473 40474 return { 40475 create: create, 40476 Methods: { 40477 addMethods: addMethods 40478 } 40479 }; 40480})(); 40481(function() { 40482 40483 var _toString = Object.prototype.toString; 40484 40485 function extend(destination, source) { 40486 for (var property in source) 40487 destination[property] = source[property]; 40488 return destination; 40489 } 40490 40491 function inspect(object) { 40492 try { 40493 if (isUndefined(object)) return 'undefined'; 40494 if (object === null) return 'null'; 40495 return object.inspect ? object.inspect() : String(object); 40496 } catch (e) { 40497 if (e instanceof RangeError) return '...'; 40498 throw e; 40499 } 40500 } 40501 40502 function toJSON(object) { 40503 var type = typeof object; 40504 switch (type) { 40505 case 'undefined': 40506 case 'function': 40507 case 'unknown': return; 40508 case 'boolean': return object.toString(); 40509 } 40510 40511 if (object === null) return 'null'; 40512 if (object.toJSON) return object.toJSON(); 40513 if (isElement(object)) return; 40514 40515 var results = []; 40516 for (var property in object) { 40517 var value = toJSON(object[property]); 40518 if (!isUndefined(value)) 40519 results.push(property.toJSON() + ': ' + value); 40520 } 40521 40522 return '{' + results.join(', ') + '}'; 40523 } 40524 40525 function toQueryString(object) { 40526 return $H(object).toQueryString(); 40527 } 40528 40529 function toHTML(object) { 40530 return object && object.toHTML ? object.toHTML() : String.interpret(object); 40531 } 40532 40533 function keys(object) { 40534 var results = []; 40535 for (var property in object) 40536 results.push(property); 40537 return results; 40538 } 40539 40540 function values(object) { 40541 var results = []; 40542 for (var property in object) 40543 results.push(object[property]); 40544 return results; 40545 } 40546 40547 function clone(object) { 40548 return extend({ }, object); 40549 } 40550 40551 function isElement(object) { 40552 return !!(object && object.nodeType == 1); 40553 } 40554 40555 function isArray(object) { 40556 return _toString.call(object) == "[object Array]"; 40557 } 40558 40559 40560 function isHash(object) { 40561 return object instanceof Hash; 40562 } 40563 40564 function isFunction(object) { 40565 return typeof object === "function"; 40566 } 40567 40568 function isString(object) { 40569 return _toString.call(object) == "[object String]"; 40570 } 40571 40572 function isNumber(object) { 40573 return _toString.call(object) == "[object Number]"; 40574 } 40575 40576 function isUndefined(object) { 40577 return typeof object === "undefined"; 40578 } 40579 40580 extend(Object, { 40581 extend: extend, 40582 inspect: inspect, 40583 toJSON: toJSON, 40584 toQueryString: toQueryString, 40585 toHTML: toHTML, 40586 keys: keys, 40587 values: values, 40588 clone: clone, 40589 isElement: isElement, 40590 isArray: isArray, 40591 isHash: isHash, 40592 isFunction: isFunction, 40593 isString: isString, 40594 isNumber: isNumber, 40595 isUndefined: isUndefined 40596 }); 40597})(); 40598Object.extend(Function.prototype, (function() { 40599 var slice = Array.prototype.slice; 40600 40601 function update(array, args) { 40602 var arrayLength = array.length, length = args.length; 40603 while (length--) array[arrayLength + length] = args[length]; 40604 return array; 40605 } 40606 40607 function merge(array, args) { 40608 array = slice.call(array, 0); 40609 return update(array, args); 40610 } 40611 40612 function argumentNames() {
40613 var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1] 40614 .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '') 40615 .replace(/\s+/g, '').split(','); 40616 return names.length == 1 && !names[0] ? [] : names; 40617 } 40618 40619 function bind(context) { 40620 if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this; 40621 var __method = this, args = slice.call(arguments, 1); 40622 return function() { 40623 var a = merge(args, arguments); 40624 return __method.apply(context, a); 40625 } 40626 } 40627 40628 function bindAsEventListener(context) { 40629 var __method = this, args = slice.call(arguments, 1); 40630 return function(event) { 40631 var a = update([event || window.event], args); 40632 return __method.apply(context, a); 40633 } 40634 } 40635 40636 function curry() { 40637 if (!arguments.length) return this; 40638 var __method = this, args = slice.call(arguments, 0); 40639 return function() { 40640 var a = merge(args, arguments); 40641 return __method.apply(this, a); 40642 } 40643 } 40644 40645 function delay(timeout) { 40646 var __method = this, args = slice.call(arguments, 1); 40647 timeout = timeout * 1000 40648 return window.setTimeout(function() { 40649 return __method.apply(__method, args); 40650 }, timeout); 40651 } 40652 40653 function defer() { 40654 var args = update([0.01], arguments); 40655 return this.delay.apply(this, args); 40656 } 40657 40658 function wrap(wrapper) { 40659 var __method = this; 40660 return function() { 40661 var a = update([__method.bind(this)], arguments); 40662 return wrapper.apply(this, a); 40663 } 40664 } 40665 40666 function methodize() { 40667 if (this._methodized) return this._methodized; 40668 var __method = this; 40669 return this._methodized = function() { 40670 var a = update([this], arguments); 40671 return __method.apply(null, a); 40672 }; 40673 } 40674 40675 return { 40676 argumentNames: argumentNames, 40677 bind: bind, 40678 bindAsEventListener: bindAsEventListener, 40679 curry: curry, 40680 delay: delay, 40681 defer: defer, 40682 wrap: wrap, 40683 methodize: methodize 40684 } 40685})()); 40686 40687 40688Date.prototype.toJSON = function() { 40689 return '"' + this.getUTCFullYear() + '-' + 40690 (this.getUTCMonth() + 1).toPaddedString(2) + '-' + 40691 this.getUTCDate().toPaddedString(2) + 'T' + 40692 this.getUTCHours().toPaddedString(2) + ':' + 40693 this.getUTCMinutes().toPaddedString(2) + ':' + 40694 this.getUTCSeconds().toPaddedString(2) + 'Z"'; 40695}; 40696 40697 40698RegExp.prototype.match = RegExp.prototype.test; 40699 40700RegExp.escape = function(str) { 40701 return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'); 40702}; 40703var PeriodicalExecuter = Class.create({ 40704 initialize: function(callback, frequency) { 40705 this.callback = callback; 40706 this.frequency = frequency; 40707 this.currentlyExecuting = false; 40708 40709 this.registerCallback(); 40710 }, 40711 40712 registerCallback: function() { 40713 this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); 40714 }, 40715 40716 execute: function() { 40717 this.callback(this); 40718 }, 40719 40720 stop: function() { 40721 if (!this.timer) return; 40722 clearInterval(this.timer); 40723 this.timer = null; 40724 }, 40725 40726 onTimerEvent: function() { 40727 if (!this.currentlyExecuting) { 40728 try { 40729 this.currentlyExecuting = true; 40730 this.execute(); 40731 this.currentlyExecuting = false; 40732 } catch(e) { 40733 this.currentlyExecuting = false; 40734 throw e; 40735 } 40736 } 40737 } 40738}); 40739Object.extend(String, { 40740 interpret: function(value) { 40741 return value == null ? '' : String(value); 40742 }, 40743 specialChar: { 40744 '\b': '\\b', 40745 '\t': '\\t', 40746 '\n': '\\n', 40747 '\f': '\\f', 40748 '\r': '\\r', 40749 '\\': '\\\\' 40750 } 40751}); 40752 40753Object.extend(String.prototype, (function() { 40754 40755 function prepareReplacement(replacement) { 40756 if (Object.isFunction(replacement)) return replacement; 40757 var template = new Template(replacement); 40758 return function(match) { return template.evaluate(match) }; 40759 } 40760 40761 function gsub(pattern, replacement) { 40762 var result = '', source = this, match; 40763 replacement = prepareReplacement(replacement); 40764 40765 if (Object.isString(pattern)) 40766 pattern = RegExp.escape(pattern); 40767 40768 if (!(pattern.length || pattern.source)) { 40769 replacement = replacement(''); 40770 return replacement + source.split('').join(replacement) + replacement; 40771 } 40772 40773 while (source.length > 0) { 40774 if (match = source.match(pattern)) { 40775 result += source.slice(0, match.index); 40776 result += String.interpret(replacement(match)); 40777 source = source.slice(match.index + match[0].length); 40778 } else { 40779 result += source, source = ''; 40780 } 40781 } 40782 return result; 40783 } 40784 40785 function sub(pattern, replacement, count) { 40786 replacement = prepareReplacement(replacement); 40787 count = Object.isUndefined(count) ? 1 : count; 40788 40789 return this.gsub(pattern, function(match) { 40790 if (--count < 0) return match[0]; 40791 return replacement(match); 40792 }); 40793 } 40794 40795 function scan(pattern, iterator) { 40796 this.gsub(pattern, iterator); 40797 return String(this); 40798 } 40799 40800 function truncate(length, truncation) { 40801 length = length || 30; 40802 truncation = Object.isUndefined(truncation) ? '...' : truncation; 40803 return this.length > length ? 40804 this.slice(0, length - truncation.length) + truncation : String(this); 40805 } 40806 40807 function strip() { 40808 return this.replace(/^\s+/, '').replace(/\s+$/, ''); 40809 } 40810 40811 function stripTags() { 40812 return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?>|<\/\w+>/gi, ''); 40813 } 40814 40815 function stripScripts() { 40816 return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), ''); 40817 } 40818 40819 function extractScripts() { 40820 var matchAll = new RegExp(Prototype.ScriptFragment, 'img'); 40821 var matchOne = new RegExp(Prototype.ScriptFragment, 'im'); 40822 return (this.match(matchAll) || []).map(function(scriptTag) { 40823 return (scriptTag.match(matchOne) || ['', ''])[1]; 40824 }); 40825 } 40826 40827 function evalScripts() { 40828 return this.extractScripts().map(function(script) { return eval(script) }); 40829 } 40830 40831 function escapeHTML() { 40832 return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); 40833 } 40834 40835 function unescapeHTML() { 40836 return this.stripTags().replace(/</g,'<').replace(/>/g,'>').replace(/&/g,'&'); 40837 } 40838 40839 40840 function toQueryParams(separator) { 40841 var match = this.strip().match(/([^?#]*)(#.*)?$/); 40842 if (!match) return { }; 40843 40844 return match[1].split(separator || '&').inject({ }, function(hash, pair) { 40845 if ((pair = pair.split('='))[0]) { 40846 var key = decodeURIComponent(pair.shift()); 40847 var value = pair.length > 1 ? pair.join('=') : pair[0]; 40848 if (value != undefined) value = decodeURIComponent(value); 40849 40850 if (key in hash) { 40851 if (!Object.isArray(hash[key])) hash[key] = [hash[key]]; 40852 hash[key].push(value); 40853 } 40854 else hash[key] = value; 40855 } 40856 return hash; 40857 }); 40858 } 40859 40860 function toArray() { 40861 return this.split(''); 40862 } 40863 40864 function succ() { 40865 return this.slice(0, this.length - 1) + 40866 String.fromCharCode(this.charCodeAt(this.length - 1) + 1); 40867 } 40868 40869 function times(count) { 40870 return count < 1 ? '' : new Array(count + 1).join(this); 40871 } 40872 40873 function camelize() { 40874 var parts = this.split('-'), len = parts.length; 40875 if (len == 1) return parts[0]; 40876 40877 var camelized = this.charAt(0) == '-' 40878 ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1) 40879 : parts[0]; 40880 40881 for (var i = 1; i < len; i++) 40882 camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1); 40883 40884 return camelized; 40885 } 40886 40887 function capitalize() { 40888 return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase(); 40889 } 40890 40891 function underscore() { 40892 return this.replace(/::/g, '/') 40893 .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2') 40894 .replace(/([a-z\d])([A-Z])/g, '$1_$2') 40895 .replace(/-/g, '_') 40896 .toLowerCase(); 40897 } 40898 40899 function dasherize() { 40900 return this.replace(/_/g, '-'); 40901 } 40902 40903 function inspect(useDoubleQuotes) { 40904 var escapedString = this.replace(/[\x00-\x1f\\]/g, function(ch
40904aracter) { 40905 if (character in String.specialChar) { 40906 return String.specialChar[character]; 40907 } 40908 return '\\u00' + character.charCodeAt().toPaddedString(2, 16); 40909 }); 40910 if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"'; 40911 return "'" + escapedString.replace(/'/g, '\\\'') + "'"; 40912 } 40913 40914 function toJSON() { 40915 return this.inspect(true); 40916 } 40917 40918 function unfilterJSON(filter) { 40919 return this.replace(filter || Prototype.JSONFilter, '$1'); 40920 } 40921 40922 function isJSON() { 40923 var str = this; 40924 if (str.blank()) return false; 40925 str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''); 40926 return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str); 40927 } 40928 40929 function evalJSON(sanitize) { 40930 var json = this.unfilterJSON(); 40931 try { 40932 if (!sanitize || json.isJSON()) return eval('(' + json + ')'); 40933 } catch (e) { } 40934 throw new SyntaxError('Badly formed JSON string: ' + this.inspect()); 40935 } 40936 40937 function include(pattern) { 40938 return this.indexOf(pattern) > -1; 40939 } 40940 40941 function startsWith(pattern) { 40942 return this.indexOf(pattern) === 0; 40943 } 40944 40945 function endsWith(pattern) { 40946 var d = this.length - pattern.length; 40947 return d >= 0 && this.lastIndexOf(pattern) === d; 40948 } 40949 40950 function empty() { 40951 return this == ''; 40952 } 40953 40954 function blank() { 40955 return /^\s*$/.test(this); 40956 } 40957 40958 function interpolate(object, pattern) { 40959 return new Template(this, pattern).evaluate(object); 40960 } 40961 40962 return { 40963 gsub: gsub, 40964 sub: sub, 40965 scan: scan, 40966 truncate: truncate, 40967 strip: String.prototype.trim ? String.prototype.trim : strip, 40968 stripTags: stripTags, 40969 stripScripts: stripScripts, 40970 extractScripts: extractScripts, 40971 evalScripts: evalScripts, 40972 escapeHTML: escapeHTML, 40973 unescapeHTML: unescapeHTML, 40974 toQueryParams: toQueryParams, 40975 parseQuery: toQueryParams, 40976 toArray: toArray, 40977 succ: succ, 40978 times: times, 40979 camelize: camelize, 40980 capitalize: capitalize, 40981 underscore: underscore, 40982 dasherize: dasherize, 40983 inspect: inspect, 40984 toJSON: toJSON, 40985 unfilterJSON: unfilterJSON, 40986 isJSON: isJSON, 40987 evalJSON: evalJSON, 40988 include: include, 40989 startsWith: startsWith, 40990 endsWith: endsWith, 40991 empty: empty, 40992 blank: blank, 40993 interpolate: interpolate 40994 }; 40995})()); 40996 40997var Template = Class.create({ 40998 initialize: function(template, pattern) { 40999 this.template = template.toString(); 41000 this.pattern = pattern || Template.Pattern; 41001 }, 41002 41003 evaluate: function(object) { 41004 if (object && Object.isFunction(object.toTemplateReplacements)) 41005 object = object.toTemplateReplacements(); 41006 41007 return this.template.gsub(this.pattern, function(match) { 41008 if (object == null) return (match[1] + ''); 41009 41010 var before = match[1] || ''; 41011 if (before == '\\') return match[2]; 41012 41013 var ctx = object, expr = match[3]; 41014 var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/; 41015 match = pattern.exec(expr); 41016 if (match == null) return before; 41017 41018 while (match != null) { 41019 var comp = match[1].startsWith('[') ? match[2].replace(/\\\\]/g, ']') : match[1]; 41020 ctx = ctx[comp]; 41021 if (null == ctx || '' == match[3]) break; 41022 expr = expr.substring('[' == match[3] ? match[1].length : match[0].length); 41023 match = pattern.exec(expr); 41024 } 41025 41026 return before + String.interpret(ctx); 41027 }); 41028 } 41029}); 41030Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; 41031 41032var $break = { }; 41033 41034var Enumerable = (function() { 41035 function each(iterator, context) { 41036 var index = 0; 41037 try { 41038 this._each(function(value) { 41039 iterator.call(context, value, index++); 41040 }); 41041 } catch (e) { 41042 if (e != $break) throw e; 41043 } 41044 return this; 41045 } 41046 41047 function eachSlice(number, iterator, context) { 41048 var index = -number, slices = [], array = this.toArray(); 41049 if (number < 1) return array; 41050 while ((index += number) < array.length) 41051 slices.push(array.slice(index, index+number)); 41052 return slices.collect(iterator, context); 41053 } 41054 41055 function all(iterator, context) { 41056 iterator = iterator || Prototype.K; 41057 var result = true; 41058 this.each(function(value, index) { 41059 result = result && !!iterator.call(context, value, index); 41060 if (!result) throw $break; 41061 }); 41062 return result; 41063 } 41064 41065 function any(iterator, context) { 41066 iterator = iterator || Prototype.K; 41067 var result = false;
41068 this.each(function(value, index) { 41069 if (result = !!iterator.call(context, value, index)) 41070 throw $break; 41071 }); 41072 return result; 41073 } 41074 41075 function collect(iterator, context) { 41076 iterator = iterator || Prototype.K; 41077 var results = []; 41078 this.each(function(value, index) { 41079 results.push(iterator.call(context, value, index)); 41080 }); 41081 return results; 41082 } 41083 41084 function detect(iterator, context) { 41085 var result; 41086 this.each(function(value, index) { 41087 if (iterator.call(context, value, index)) { 41088 result = value; 41089 throw $break; 41090 } 41091 }); 41092 return result; 41093 } 41094 41095 function findAll(iterator, context) { 41096 var results = []; 41097 this.each(function(value, index) { 41098 if (iterator.call(context, value, index)) 41099 results.push(value); 41100 }); 41101 return results; 41102 } 41103 41104 function grep(filter, iterator, context) { 41105 iterator = iterator || Prototype.K; 41106 var results = []; 41107 41108 if (Object.isString(filter)) 41109 filter = new RegExp(RegExp.escape(filter)); 41110 41111 this.each(function(value, index) { 41112 if (filter.match(value)) 41113 results.push(iterator.call(context, value, index)); 41114 }); 41115 return results; 41116 } 41117 41118 function include(object) { 41119 if (Object.isFunction(this.indexOf)) 41120 if (this.indexOf(object) != -1) return true; 41121 41122 var found = false; 41123 this.each(function(value) { 41124 if (value == object) { 41125 found = true; 41126 throw $break; 41127 } 41128 }); 41129 return found; 41130 } 41131 41132 function inGroupsOf(number, fillWith) { 41133 fillWith = Object.isUndefined(fillWith) ? null : fillWith; 41134 return this.eachSlice(number, function(slice) { 41135 while(slice.length < number) slice.push(fillWith); 41136 return slice; 41137 }); 41138 } 41139 41140 function inject(memo, iterator, context) { 41141 this.each(function(value, index) { 41142 memo = iterator.call(context, memo, value, index); 41143 }); 41144 return memo; 41145 } 41146 41147 function invoke(method) { 41148 var args = $A(arguments).slice(1); 41149 return this.map(function(value) { 41150 return value[method].apply(value, args); 41151 }); 41152 } 41153 41154 function max(iterator, context) { 41155 iterator = iterator || Prototype.K; 41156 var result; 41157 this.each(function(value, index) { 41158 value = iterator.call(context, value, index); 41159 if (result == null || value >= result) 41160 result = value; 41161 }); 41162 return result; 41163 } 41164 41165 function min(iterator, context) { 41166 iterator = iterator || Prototype.K; 41167 var result; 41168 this.each(function(value, index) { 41169 value = iterator.call(context, value, index); 41170 if (result == null || value < result) 41171 result = value; 41172 }); 41173 return result; 41174 } 41175 41176 function partition(iterator, context) { 41177 iterator = iterator || Prototype.K; 41178 var trues = [], falses = []; 41179 this.each(function(value, index) { 41180 (iterator.call(context, value, index) ? 41181 trues : falses).push(value); 41182 }); 41183 return [trues, falses]; 41184 } 41185 41186 function pluck(property) { 41187 var results = []; 41188 this.each(function(value) { 41189 results.push(value[property]); 41190 }); 41191 return results; 41192 } 41193 41194 function reject(iterator, context) { 41195 var results = []; 41196 this.each(function(value, index) { 41197 if (!iterator.call(context, value, index)) 41198 results.push(value); 41199 }); 41200 return results; 41201 } 41202 41203 function sortBy(iterator, context) { 41204 return this.map(function(value, index) { 41205 return { 41206 value: value, 41207 criteria: iterator.call(context, value, index) 41208 }; 41209 }).sort(function(left, right) { 41210 var a = left.criteria, b = right.criteria; 41211 return a < b ? -1 : a > b ? 1 : 0; 41212 }).pluck('value'); 41213 } 41214 41215 function toArray() { 41216 return this.map(); 41217 } 41218 41219 function zip() { 41220 var iterator = Prototype.K, args = $A(arguments); 41221 if (Object.isFunction(args.last())) 41222 iterator = args.pop(); 41223 41224 var collections = [this].concat(args).map($A); 41225 return this.map(function(value, index) { 41226 return iterator(collections.pluck(index)); 41227 }); 41228 } 41229 41230 function size() { 41231 return this.toArray().length; 41232 } 41233 41234 function inspect() { 41235 return '#<Enumerable:' + this.toArray().inspect() + '>'; 41236 } 41237 41238 41239 41240 41241 41242 41243 41244 41245 41246 return { 41247 each: each, 41248 eachSlice: eachSlice, 41249 all: all, 41250 every: all, 41251 any: any, 41252 some: any, 41253 collect: collect, 41254 map: collect, 41255 detect: detect, 41256 findAll: findAll, 41257 select: findAll, 41258 filter: findAll, 41259 grep: grep, 41260 include: include, 41261 member: include, 41262 inGroupsOf: inGroupsOf, 41263 inject: inject, 41264 invoke: invoke, 41265 max: max, 41266 min: min, 41267 partition: partition, 41268 pluck: pluck, 41269 reject: reject, 41270 sortBy: sortBy, 41271 toArray: toArray, 41272 entries: toArray, 41273 zip: zip, 41274 size: size, 41275 inspect: inspect, 41276 find: detect
41277 }; 41278})(); 41279function $A(iterable) { 41280 if (!iterable) return []; 41281 if ('toArray' in Object(iterable)) return iterable.toArray(); 41282 var length = iterable.length || 0, results = new Array(length); 41283 while (length--) results[length] = iterable[length]; 41284 return results; 41285} 41286 41287function $w(string) { 41288 if (!Object.isString(string)) return []; 41289 string = string.strip(); 41290 return string ? string.split(/\s+/) : []; 41291} 41292 41293Array.from = $A; 41294 41295 41296(function() { 41297 var arrayProto = Array.prototype, 41298 slice = arrayProto.slice, 41299 _each = arrayProto.forEach; // use native browser JS 1.6 implementation if available 41300 41301 function each(iterator) { 41302 for (var i = 0, length = this.length; i < length; i++) 41303 iterator(this[i]); 41304 } 41305 if (!_each) _each = each; 41306 41307 function clear() { 41308 this.length = 0; 41309 return this; 41310 } 41311 41312 function first() { 41313 return this[0]; 41314 } 41315 41316 function last() { 41317 return this[this.length - 1]; 41318 } 41319 41320 function compact() { 41321 return this.select(function(value) { 41322 return value != null; 41323 }); 41324 } 41325 41326 function flatten() { 41327 return this.inject([], function(array, value) { 41328 if (Object.isArray(value)) 41329 return array.concat(value.flatten()); 41330 array.push(value); 41331 return array; 41332 }); 41333 } 41334 41335 function without() { 41336 var values = slice.call(arguments, 0); 41337 return this.select(function(value) { 41338 return !values.include(value); 41339 }); 41340 } 41341 41342 function reverse(inline) { 41343 return (inline !== false ? this : this.toArray())._reverse(); 41344 } 41345 41346 function uniq(sorted) { 41347 return this.inject([], function(array, value, index) { 41348 if (0 == index || (sorted ? array.last() != value : !array.include(value))) 41349 array.push(value); 41350 return array; 41351 }); 41352 } 41353 41354 function intersect(array) { 41355 return this.uniq().findAll(function(item) { 41356 return array.detect(function(value) { return item === value }); 41357 }); 41358 } 41359 41360 41361 function clone() { 41362 return slice.call(this, 0); 41363 } 41364 41365 function size() { 41366 return this.length; 41367 } 41368 41369 function inspect() { 41370 return '[' + this.map(Object.inspect).join(', ') + ']'; 41371 } 41372 41373 function toJSON() { 41374 var results = []; 41375 this.each(function(object) { 41376 var value = Object.toJSON(object); 41377 if (!Object.isUndefined(value)) results.push(value); 41378 }); 41379 return '[' + results.join(', ') + ']'; 41380 } 41381 41382 function indexOf(item, i) { 41383 i || (i = 0); 41384 var length = this.length; 41385 if (i < 0) i = length + i; 41386 for (; i < length; i++) 41387 if (this[i] === item) return i; 41388 return -1; 41389 } 41390 41391 function lastIndexOf(item, i) { 41392 i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1; 41393 var n = this.slice(0, i).reverse().indexOf(item); 41394 return (n < 0) ? n : i - n - 1; 41395 } 41396 41397 function concat() { 41398 var array = slice.call(this, 0), item; 41399 for (var i = 0, length = arguments.length; i < length; i++) { 41400 item = arguments[i]; 41401 if (Object.isArray(item) && !('callee' in item)) { 41402 for (var j = 0, arrayLength = item.length; j < arrayLength; j++) 41403 array.push(item[j]); 41404 } else { 41405 array.push(item); 41406 } 41407 } 41408 return array; 41409 } 41410 41411 Object.extend(arrayProto, Enumerable); 41412 41413 if (!arrayProto._reverse) 41414 arrayProto._reverse = arrayProto.reverse; 41415 41416 Object.extend(arrayProto, { 41417 _each: _each, 41418 clear: clear, 41419 first: first, 41420 last: last, 41421 compact: compact, 41422 flatten: flatten, 41423 without: without, 41424 reverse: reverse, 41425 uniq: uniq, 41426 intersect: intersect, 41427 clone: clone, 41428 toArray: clone, 41429 size: size, 41430 inspect: inspect, 41431 toJSON: toJSON 41432 }); 41433 41434 var CONCAT_ARGUMENTS_BUGGY = (function() { 41435 return [].concat(arguments)[0][0] !== 1; 41436 })(1,2) 41437 41438 if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat; 41439 41440 if (!arrayProto.indexOf) arrayProto.indexOf = indexOf; 41441 if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf; 41442})(); 41443function $H(object) { 41444 return new Hash(object); 41445}; 41446 41447var Hash = Class.create(Enumerable, (function() { 41448 function initialize(object) { 41449 this._object = Object.isHash(object) ? object.toObject() : Object.clone(object); 41450 } 41451 41452 function _each(iterator) { 41453 for (var key in this._object) { 41454 var value = this._object[key], pair = [key, value]; 41455 pair.key = key; 41456 pair.value = value; 41457 iterator(pair); 41458 } 41459 } 41460 41461 function set(key, value) { 41462 return this._object[key] = value; 41463 } 41464 41465 function get(key) { 41466 if (this._object[key] !== Object.prototype[key]) 41467 return this._object[key]; 41468 } 41469 41470 function unset(key) { 41471 var value = this._object[key]; 41472 delete this._object[key]; 41473 return value; 41474 } 41475 41476 function toObject() { 41477 return Object.clone(this._object); 41478 } 41479 41480 function keys() { 41481 return this.pluck('key'); 41482 } 41483 41484 function values() { 41485 return this.pluck('value'); 41486 } 41487 41488 function index(value) { 41489 var match = this.detect(function(pair) { 41490 return pair.value === value; 41491 }); 41492 return match && match.key; 41493 } 41494 41495 function merge(object) { 41496 return this.clone().update(object); 41497 } 41498 41499 function update(object) { 41500 return new Hash(object).inject(this, function(result, pair) { 41501 result.set(pair.key, pair.value); 41502 return result; 41503 }); 41504 } 41505 41506 function toQueryPair(key, value) { 41507 if (Object.isUndefined(value)) return key; 41508 return key + '=' + encodeURIComponent(String.interpret(value)); 41509 } 41510 41511 function toQueryString() { 41512 return this.inject([], function(results, pair) { 41513 var key = encodeURIComponent(pair.key), values = pair.value; 41514 41515 if (values && typeof values == 'object') { 41516 if (Object.isArray(values)) 41517 return results.concat(values.map(toQueryPair.curry(key))); 41518 } else results.push(toQueryPair(key, values)); 41519 return results; 41520 }).join('&'); 41521 } 41522 41523 function inspect() { 41524 return '#<Hash:{' + this.map(function(pair) { 41525 return pair.map(Object.inspect).join(': '); 41526 }).join(', ') + '}>'; 41527 } 41528 41529 function toJSON() { 41530 return Object.toJSON(this.toObject()); 41531 } 41532 41533 function clone() { 41534 return new Hash(this); 41535 } 41536 41537 return { 41538 initialize: initialize, 41539 _each: _each, 41540 set: set, 41541 get: get, 41542 unset: unset, 41543 toObject: toObject, 41544 toTemplateReplacements: toObject, 41545 keys: keys, 41546 values: values, 41547 index: index, 41548 merge: merge, 41549 update: update, 41550 toQueryString: toQueryString, 41551 inspect: inspect, 41552 toJSON: toJSON, 41553 clone: clone 41554 }; 41555})()); 41556 41557Hash.from = $H; 41558Object.extend(Number.prototype, (function() { 41559 function toColorPart() { 41560 return this.toPaddedString(2, 16); 41561 } 41562 41563 function succ() { 41564 return this + 1; 41565 } 41566 41567 function times(iterator, context) { 41568 $R(0, this, true).each(iterator, context); 41569 return this; 41570 } 41571 41572 function toPaddedString(length, radix) { 41573 var string = this.toString(radix || 10); 41574 return '0'.times(length - string.length) + string; 41575 } 41576 41577 function toJSON() { 41578 return isFinite(this) ? this.toString() : 'null';
41579 } 41580 41581 function abs() { 41582 return Math.abs(this); 41583 } 41584 41585 function round() { 41586 return Math.round(this); 41587 } 41588 41589 function ceil() { 41590 return Math.ceil(this); 41591 } 41592 41593 function floor() { 41594 return Math.floor(this); 41595 } 41596 41597 return { 41598 toColorPart: toColorPart, 41599 succ: succ, 41600 times: times, 41601 toPaddedString: toPaddedString, 41602 toJSON: toJSON, 41603 abs: abs, 41604 round: round, 41605 ceil: ceil, 41606 floor: floor 41607 }; 41608})()); 41609 41610function $R(start, end, exclusive) { 41611 return new ObjectRange(start, end, exclusive); 41612} 41613 41614var ObjectRange = Class.create(Enumerable, (function() { 41615 function initialize(start, end, exclusive) { 41616 this.start = start; 41617 this.end = end; 41618 this.exclusive = exclusive; 41619 } 41620 41621 function _each(iterator) { 41622 var value = this.start; 41623 while (this.include(value)) { 41624 iterator(value); 41625 value = value.succ(); 41626 } 41627 } 41628 41629 function include(value) { 41630 if (value < this.start) 41631 return false; 41632 if (this.exclusive) 41633 return value < this.end; 41634 return value <= this.end; 41635 } 41636 41637 return { 41638 initialize: initialize, 41639 _each: _each, 41640 include: include 41641 }; 41642})()); 41643 41644 41645 41646var Ajax = { 41647 getTransport: function() { 41648 return Try.these( 41649 function() {return new XMLHttpRequest()}, 41650 function() {return new ActiveXObject('Msxml2.XMLHTTP')}, 41651 function() {return new ActiveXObject('Microsoft.XMLHTTP')} 41652 ) || false; 41653 }, 41654 41655 activeRequestCount: 0 41656}; 41657 41658Ajax.Responders = { 41659 responders: [], 41660 41661 _each: function(iterator) { 41662 this.responders._each(iterator); 41663 }, 41664 41665 register: function(responder) { 41666 if (!this.include(responder)) 41667 this.responders.push(responder); 41668 }, 41669 41670 unregister: function(responder) { 41671 this.responders = this.responders.without(responder); 41672 }, 41673 41674 dispatch: function(callback, request, transport, json) { 41675 this.each(function(responder) { 41676 if (Object.isFunction(responder[callback])) { 41677 try { 41678 responder[callback].apply(responder, [request, transport, json]); 41679 } catch (e) { } 41680 } 41681 }); 41682 } 41683}; 41684 41685Object.extend(Ajax.Responders, Enumerable); 41686 41687Ajax.Responders.register({ 41688 onCreate: function() { Ajax.activeRequestCount++ },
41689 onComplete: function() { Ajax.activeRequestCount-- } 41690}); 41691Ajax.Base = Class.create({ 41692 initialize: function(options) { 41693 this.options = { 41694 method: 'post', 41695 asynchronous: true, 41696 contentType: 'application/x-www-form-urlencoded', 41697 encoding: 'UTF-8', 41698 parameters: '', 41699 evalJSON: true, 41700 evalJS: true 41701 }; 41702 Object.extend(this.options, options || { }); 41703 41704 this.options.method = this.options.method.toLowerCase(); 41705 41706 if (Object.isString(this.options.parameters)) 41707 this.options.parameters = this.options.parameters.toQueryParams(); 41708 else if (Object.isHash(this.options.parameters)) 41709 this.options.parameters = this.options.parameters.toObject(); 41710 } 41711}); 41712Ajax.Request = Class.create(Ajax.Base, { 41713 _complete: false, 41714 41715 initialize: function($super, url, options) { 41716 $super(options); 41717 this.transport = Ajax.getTransport(); 41718 this.request(url); 41719 }, 41720 41721 request: function(url) { 41722 this.url = url; 41723 this.method = this.options.method; 41724 var params = Object.clone(this.options.parameters); 41725 41726 if (!['get', 'post'].include(this.method)) { 41727 params['_method'] = this.method; 41728 this.method = 'post'; 41729 } 41730 41731 this.parameters = params; 41732 41733 if (params = Object.toQueryString(params)) { 41734 if (this.method == 'get') 41735 this.url += (this.url.include('?') ? '&' : '?') + params; 41736 else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) 41737 params += '&_='; 41738 } 41739 41740 try { 41741 var response = new Ajax.Response(this); 41742 if (this.options.onCreate) this.options.onCreate(response); 41743 Ajax.Responders.dispatch('onCreate', this, response); 41744 41745 this.transport.open(this.method.toUpperCase(), this.url, 41746 this.options.asynchronous); 41747 41748 if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1); 41749 41750 this.transport.onreadystatechange = this.onStateChange.bind(this); 41751 this.setRequestHeaders(); 41752 41753 this.body = this.method == 'post' ? (this.options.postBody || params) : null; 41754 this.transport.send(this.body); 41755 41756 /* Force Firefox to handle ready state 4 for synchronous requests */ 41757 if (!this.options.asynchronous && this.transport.overrideMimeType) 41758 this.onStateChange(); 41759 41760 } 41761 catch (e) { 41762 this.dispatchException(e); 41763 } 41764 }, 41765 41766 onStateChange: function() { 41767 var readyState = this.transport.readyState; 41768 if (readyState > 1 && !((readyState == 4) && this._complete)) 41769 this.respondToReadyState(this.transport.readyState); 41770 }, 41771 41772 setRequestHeaders: function() { 41773 var headers = { 41774 // 'X-Requested-With': 'XMLHttpRequest', 41775 // 'X-Prototype-Version': Prototype.Version, 41776 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*' 41777 }; 41778 41779 if (this.method == 'post') { 41780 headers['Content-type'] = this.options.contentType + 41781 (this.options.encoding ? '; charset=' + this.options.encoding : ''); 41782 41783 /* Force "Connection: close" for older Mozilla browsers to work 41784 * around a bug where XMLHttpRequest sends an incorrect 41785 * Content-length header. See Mozilla Bugzilla #246651. 41786 */ 41787 if (this.transport.overrideMimeType && 41788 (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005) 41789 headers['Connection'] = 'close'; 41790 } 41791 41792 if (typeof this.options.requestHeaders == 'object') { 41793 var extras = this.options.requestHeaders; 41794 41795 if (Object.isFunction(extras.push)) 41796 for (var i = 0, length = extras.length; i < length; i += 2) 41797 headers[extras[i]] = extras[i+1]; 41798 else 41799 $H(extras).each(function(pair) { headers[pair.key] = pair.value }); 41800 } 41801 41802 for (var name in headers) 41803 this.transport.setRequestHeader(name, headers[name]); 41804 }, 41805 41806 success: function() { 41807 var status = this.getStatus(); 41808 return !status || (status >= 200 && status < 300); 41809 }, 41810 41811 getStatus: function() { 41812 try { 41813 return this.transport.status || 0; 41814 } catch (e) { return 0 } 41815 }, 41816 41817 respondToReadyState: function(readyState) { 41818 var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this); 41819 41820 if (state == 'Complete') {
41821 try { 41822 this._complete = true; 41823 (this.options['on' + response.status] 41824 || this.options['on' + (this.success() ? 'Success' : 'Failure')] 41825 || Prototype.emptyFunction)(response, response.headerJSON); 41826 } catch (e) { 41827 this.dispatchException(e); 41828 } 41829 41830 var contentType = response.getHeader('Content-type'); 41831 if (this.options.evalJS == 'force' 41832 || (this.options.evalJS && this.isSameOrigin() && contentType 41833 && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i))) 41834 this.evalResponse(); 41835 } 41836 41837 try { 41838 (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON); 41839 Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON); 41840 } catch (e) { 41841 this.dispatchException(e); 41842 } 41843 41844 if (state == 'Complete') { 41845 this.transport.onreadystatechange = Prototype.emptyFunction; 41846 } 41847 }, 41848 41849 isSameOrigin: function() { 41850 var m = this.url.match(/^\s*https?:\/\/[^\/]*/); 41851 return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({ 41852 protocol: location.protocol, 41853 domain: document.domain, 41854 port: location.port ? ':' + location.port : '' 41855 })); 41856 }, 41857 41858 getHeader: function(name) { 41859 try { 41860 return this.transport.getResponseHeader(name) || null; 41861 } catch (e) { return null; } 41862 }, 41863 41864 evalResponse: function() { 41865 try { 41866 return eval((this.transport.responseText || '').unfilterJSON()); 41867 } catch (e) { 41868 this.dispatchException(e); 41869 } 41870 }, 41871 41872 dispatchException: function(exception) { 41873 (this.options.onException || Prototype.emptyFunction)(this, exception); 41874 Ajax.Responders.dispatch('onException', this, exception); 41875 } 41876}); 41877 41878Ajax.Request.Events = 41879 ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; 41880 41881 41882 41883 41884 41885 41886 41887 41888Ajax.Response = Class.create({ 41889 initialize: function(request){ 41890 this.request = request; 41891 var transport = this.transport = request.transport, 41892 readyState = this.readyState = transport.readyState; 41893 41894 if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) { 41895 this.status = this.getStatus(); 41896 this.statusText = this.getStatusText(); 41897 this.responseText = String.interpret(transport.responseText); 41898 this.headerJSON = this._getHeaderJSON(); 41899 } 41900 41901 if(readyState == 4) { 41902 var xml = transport.responseXML; 41903 this.responseXML = Object.isUndefined(xml) ? null : xml; 41904 this.responseJSON = this._getResponseJSON(); 41905 } 41906 }, 41907 41908 status: 0, 41909 41910 statusText: '', 41911 41912 getStatus: Ajax.Request.prototype.getStatus, 41913 41914 getStatusText: function() { 41915 try { 41916 return this.transport.statusText || ''; 41917 } catch (e) { return '' } 41918 }, 41919 41920 getHeader: Ajax.Request.prototype.getHeader, 41921 41922 getAllHeaders: function() { 41923 try { 41924 return this.getAllResponseHeaders(); 41925 } catch (e) { return null } 41926 }, 41927 41928 getResponseHeader: function(name) { 41929 return this.transport.getResponseHeader(name); 41930 }, 41931 41932 getAllResponseHeaders: function() { 41933 return this.transport.getAllResponseHeaders(); 41934 }, 41935 41936 _getHeaderJSON: function() { 41937 var json = this.getHeader('X-JSON'); 41938 if (!json) return null; 41939 json = decodeURIComponent(escape(json)); 41940 try { 41941 return json.evalJSON(this.request.options.sanitizeJSON || 41942 !this.request.isSameOrigin()); 41943 } catch (e) { 41944 this.request.dispatchException(e); 41945 } 41946 }, 41947 41948 _getResponseJSON: function() { 41949 var options = this.request.options; 41950 if (!options.evalJSON || (options.evalJSON != 'force' && 41951 !(this.getHeader('Content-type') || '').include('application/json')) || 41952 this.responseText.blank()) 41953 return null; 41954 try { 41955 return this.responseText.evalJSON(options.sanitizeJSON || 41956 !this.request.isSameOrigin()); 41957 } catch (e) { 41958 this.request.dispatchException(e); 41959 } 41960 } 41961}); 41962 41963Ajax.Updater = Class.create(Ajax.Request, { 41964 initialize: function($super, container, url, options) { 41965 this.container = { 41966 success: (container.success || container), 41967 failure: (container.failure || (container.success ? null : container)) 41968 }; 41969 41970 options = Object.clone(options); 41971 var onComplete = options.onComplete; 41972 options.onComplete = (function(response, json) { 41973 this.updateContent(response.responseText); 41974 if (Object.isFunction(onComplete)) onComplete(response, json); 41975 }).bind(this); 41976 41977 $super(url, options); 41978 }, 41979 41980 updateContent: function(responseText) { 41981 var receiver = this.container[this.success() ? 'success' : 'failure'], 41982 options = this.options; 41983 41984 if (!options.evalScripts) responseText = responseText.stripScripts(); 41985 41986 if (receiver = $(receiver)) { 41987 if (options.insertion) { 41988 if (Object.isString(options.insertion)) { 41989 var insertion = { }; insertion[options.insertion] = responseText; 41990 receiver.insert(insertion); 41991 } 41992 else options.insertion(receiver, responseText); 41993 } 41994 else receiver.update(responseText); 41995 } 41996 } 41997}); 41998
41999Ajax.PeriodicalUpdater = Class.create(Ajax.Base, { 42000 initialize: function($super, container, url, options) { 42001 $super(options); 42002 this.onComplete = this.options.onComplete; 42003 42004 this.frequency = (this.options.frequency || 2); 42005 this.decay = (this.options.decay || 1); 42006 42007 this.updater = { }; 42008 this.container = container; 42009 this.url = url; 42010 42011 this.start(); 42012 }, 42013 42014 start: function() { 42015 this.options.onComplete = this.updateComplete.bind(this); 42016 this.onTimerEvent(); 42017 }, 42018 42019 stop: function() { 42020 this.updater.options.onComplete = undefined; 42021 clearTimeout(this.timer); 42022 (this.onComplete || Prototype.emptyFunction).apply(this, arguments); 42023 }, 42024 42025 updateComplete: function(response) { 42026 if (this.options.decay) { 42027 this.decay = (response.responseText == this.lastText ? 42028 this.decay * this.options.decay : 1); 42029 42030 this.lastText = response.responseText; 42031 } 42032 this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency); 42033 }, 42034 42035 onTimerEvent: function() { 42036 this.updater = new Ajax.Updater(this.container, this.url, this.options); 42037 } 42038}); 42039 42040 42041 42042function $(element) { 42043 if (arguments.length > 1) { 42044 for (var i = 0, elements = [], length = arguments.length; i < length; i++) 42045 elements.push($(arguments[i])); 42046 return elements; 42047 } 42048 if (Object.isString(element)) 42049 element = document.getElementById(element); 42050 return Element.extend(element); 42051} 42052 42053if (Prototype.BrowserFeatures.XPath) { 42054 document._getElementsByXPath = function(expression, parentElement) { 42055 var results = []; 42056 var query = document.evaluate(expression, $(parentElement) || document, 42057 null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); 42058 for (var i = 0, length = query.snapshotLength; i < length; i++) 42059 results.push(Element.extend(query.snapshotItem(i))); 42060 return results; 42061 }; 42062} 42063 42064/*--------------------------------------------------------------------------*/ 42065 42066if (!window.Node) var Node = { }; 42067 42068if (!Node.ELEMENT_NODE) { 42069 Object.extend(Node, { 42070 ELEMENT_NODE: 1, 42071 ATTRIBUTE_NODE: 2, 42072 TEXT_NODE: 3, 42073 CDATA_SECTION_NODE: 4, 42074 ENTITY_REFERENCE_NODE: 5, 42075 ENTITY_NODE: 6, 42076 PROCESSING_INSTRUCTION_NODE: 7, 42077 COMMENT_NODE: 8, 42078 DOCUMENT_NODE: 9, 42079 DOCUMENT_TYPE_NODE: 10, 42080 DOCUMENT_FRAGMENT_NODE: 11, 42081 NOTATION_NODE: 12 42082 }); 42083} 42084 42085 42086(function(global) { 42087 42088 var SETATTRIBUTE_IGNORES_NAME = (function(){ 42089 var elForm = document.createElement("form"); 42090 var elInput = document.createElement("input"); 42091 var root = document.documentElement; 42092 elInput.setAttribute("name", "test"); 42093 elForm.appendChild(elInput); 42094 root.appendChild(elForm); 42095 var isBuggy = elForm.elements 42096 ? (typeof elForm.elements.test == "undefined") 42097 : null; 42098 root.removeChild(elForm); 42099 elForm = elInput = null; 42100 return isBuggy; 42101 })(); 42102 42103 var element = global.Element; 42104 global.Element = function(tagName, attributes) { 42105 attributes = attributes || { }; 42106 tagName = tagName.toLowerCase(); 42107 var cache = Element.cache; 42108 if (SETATTRIBUTE_IGNORES_NAME && attributes.name) { 42109 tagName = '<' + tagName + ' name="' + attributes.name + '">'; 42110 delete attributes.name; 42111 return Element.writeAttribute(document.createElement(tagName), attributes); 42112 } 42113 if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName)); 42114 return Element.writeAttribute(cache[tagName].cloneNode(false), attributes); 42115 }; 42116 Object.extend(global.Element, element || { }); 42117 if (element) global.Element.prototype = element.prototype; 42118})(this); 42119 42120Element.cache = { }; 42121Element.idCounter = 1; 42122 42123Element.Methods = { 42124 visible: function(element) { 42125 return $(element).style.display != 'none'; 42126 }, 42127 42128 toggle: function(element) { 42129 element = $(element); 42130 Element[Element.visible(element) ? 'hide' : 'show'](element); 42131 return element; 42132 }, 42133 42134 42135 hide: function(element) { 42136 element = $(element); 42137 element.style.display = 'none'; 42138 return element; 42139 }, 42140 42141 show: function(element) { 42142 element = $(element); 42143 element.style.display = ''; 42144 return element; 42145 }, 42146 42147 remove: function(element) { 42148 element = $(element); 42149 element.parentNode.removeChild(element); 42150 return element; 42151 }, 42152 42153 update: (function(){ 42154 42155 var SELECT_ELEMENT_INNERHTML_BUGGY = (function(){ 42156 var el = document.createElement("select"), 42157 isBuggy = true; 42158 el.innerHTML = "<option value=\"test\">test</option>"; 42159 if (el.options && el.options[0]) { 42160 isBuggy = el.options[0].nodeName.toUpperCase() !== "OPTION"; 42161 } 42162 el = null; 42163 return isBuggy; 42164 })(); 42165 42166 var TABLE_ELEMENT_INNERHTML_BUGGY = (function(){
42167 try { 42168 var el = document.createElement("table"); 42169 if (el && el.tBodies) { 42170 el.innerHTML = "<tbody><tr><td>test</td></tr></tbody>"; 42171 var isBuggy = typeof el.tBodies[0] == "undefined"; 42172 el = null; 42173 return isBuggy; 42174 } 42175 } catch (e) { 42176 return true; 42177 } 42178 })(); 42179 42180 var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING = (function () { 42181 var s = document.createElement("script"), 42182 isBuggy = false; 42183 try { 42184 s.appendChild(document.createTextNode("")); 42185 isBuggy = !s.firstChild || 42186 s.firstChild && s.firstChild.nodeType !== 3; 42187 } catch (e) { 42188 isBuggy = true; 42189 } 42190 s = null; 42191 return isBuggy; 42192 })(); 42193 42194 function update(element, content) { 42195 element = $(element); 42196 42197 if (content && content.toElement) 42198 content = content.toElement(); 42199 42200 if (Object.isElement(content)) 42201 return element.update().insert(content); 42202 42203 content = Object.toHTML(content); 42204 42205 var tagName = element.tagName.toUpperCase(); 42206 42207 if (tagName === 'SCRIPT' && SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING) { 42208 element.text = content; 42209 return element; 42210 } 42211 42212 if (SELECT_ELEMENT_INNERHTML_BUGGY || TABLE_ELEMENT_INNERHTML_BUGGY) { 42213 if (tagName in Element._insertionTranslations.tags) { 42214 while (element.firstChild) { 42215 element.removeChild(element.firstChild); 42216 } 42217 Element._getContentFromAnonymousElement(tagName, content.stripScripts()) 42218 .each(function(node) { 42219 element.appendChild(node) 42220 }); 42221 } 42222 else { 42223 element.innerHTML = content.stripScripts(); 42224 } 42225 } 42226 else { 42227 element.innerHTML = content.stripScripts(); 42228 } 42229 42230 content.evalScripts.bind(content).defer(); 42231 return element; 42232 } 42233 42234 return update; 42235 })(), 42236 42237 replace: function(element, content) { 42238 element = $(element); 42239 if (content && content.toElement) content = content.toElement(); 42240 else if (!Object.isElement(content)) { 42241 content = Object.toHTML(content); 42242 var range = element.ownerDocument.createRange(); 42243 range.selectNode(element); 42244 content.evalScripts.bind(content).defer(); 42245 content = range.createContextualFragment(content.stripScripts()); 42246 } 42247 element.parentNode.replaceChild(content, element); 42248 return element; 42249 }, 42250 42251 insert: function(element, insertions) { 42252 element = $(element); 42253 42254 if (Object.isString(insertions) || Object.isNumber(insertions) || 42255 Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML))) 42256 insertions = {bottom:insertions}; 42257 42258 var content, insert, tagName, childNodes; 42259 42260 for (var position in insertions) { 42261 content = insertions[position]; 42262 position = position.toLowerCase(); 42263 insert = Element._insertionTranslations[position]; 42264 42265 if (content && content.toElement) content = content.toElement(); 42266 if (Object.isElement(content)) { 42267 insert(element, content); 42268 continue; 42269 } 42270 42271 content = Object.toHTML(content); 42272 42273 tagName = ((position == 'before' || position == 'after') 42274 ? element.parentNode : element).tagName.toUpperCase(); 42275 42276 childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); 42277 42278 if (position == 'top' || position == 'after') childNodes.reverse(); 42279 childNodes.each(insert.curry(element)); 42280 42281 content.evalScripts.bind(content).defer(); 42282 } 42283 42284 return element; 42285 }, 42286 42287 wrap: function(element, wrapper, attributes) { 42288 element = $(element); 42289 if (Object.isElement(wrapper)) 42290 $(wrapper).writeAttribute(attributes || { }); 42291 else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes); 42292 else wrapper = new Element('div', wrapper); 42293 if (element.parentNode) 42294 element.parentNode.replaceChild(wrapper, element); 42295 wrapper.appendChild(element); 42296 return wrapper; 42297 }, 42298 42299 inspect: function(element) { 42300 element = $(element); 42301 var result = '<' + element.tagName.toLowerCase(); 42302 $H({'id': 'id', 'className': 'class'}).each(function(pair) { 42303 var property = pair.first(), attribute = pair.last(); 42304 var value = (element[property] || '').toString(); 42305 if (value) result += ' ' + attribute + '=' + value.inspect(true); 42306 }); 42307 return result + '>'; 42308 }, 42309 42310 recursivelyCollect: function(element, property) { 42311 element = $(element); 42312 var elements = []; 42313 while (element = element[property]) 42314 if (element.nodeType == 1) 42315 elements.push(Element.extend(element)); 42316 return elements; 42317 }, 42318 42319 ancestors: function(element) { 42320 return Element.recursivelyCollect(element, 'parentNode'); 42321 }, 42322 42323 descendants: function(element) { 42324 return Element.select(element, "*"); 42325 }, 42326 42327 firstDescendant: function(element) { 42328 element = $(element).firstChild; 42329 while (element && element.nodeType != 1) element = element.nextSibling; 42330 return $(element); 42331 }, 42332 42333 immediateDescendants: function(element) { 42334 if (!(element = $(element).firstChild)) return []; 42335 while (element && element.nodeType != 1) element = element.nextSibling; 42336 if (element) return [element].concat($(element).nextSiblings()); 42337 return []; 42338 }, 42339 42340 previousSiblings: function(element) { 42341 return Element.recursivelyCollect(element, 'previousSibling'); 42342 }, 42343 42344 nextSiblings: function(element) { 42345 return Element.recursivelyCollect(element, 'nextSibling'); 42346 }, 42347 42348 siblings: function(element) { 42349 element = $(element); 42350 return Element.previousSiblings(element).reverse() 42351 .concat(Element.nextSiblings(element)); 42352 }, 42353 42354 match: function(element, selector) { 42355 if (Object.isString(selector)) 42356 selector = new Selector(selector); 42357 return selector.match($(element)); 42358 }, 42359 42360 up: function(element, expression, index) { 42361 element = $(element); 42362 if (arguments.length == 1) return $(element.parentNode); 42363 var ancestors = Element.ancestors(element); 42364 return Object.isNumber(expression) ? ancestors[expression] : 42365 Selector.findElement(ancestors, expression, index); 42366 }, 42367 42368 down: function(element, expression, index) { 42369 element = $(element); 42370 if (arguments.length == 1) return Element.firstDescendant(element); 42371 return Object.isNumber(expression) ? Element.descendants(element)[expression] : 42372 Element.select(element, expression)[index || 0]; 42373 }, 42374 42375 previous: function(element, expression, index) { 42376 element = $(element); 42377 if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element)); 42378 var previousSiblings = Element.previousSiblings(element); 42379 return Object.isNumber(expression) ? previousSiblings[expression] : 42380 Selector.findElement(previousSiblings, expression, index); 42381 }, 42382 42383 next: function(element, expression, index) { 42384 element = $(element); 42385 if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element)); 42386 var nextSiblings = Element.nextSiblings(element); 42387 return Object.isNumber(expression) ? nextSiblings[expression] : 42388 Selector.findElement(nextSiblings, expression, index); 42389 }, 42390 42391 42392 select: function(element) { 42393 var args = Array.prototype.slice.call(arguments, 1); 42394 return Selector.findChildElements(element, args); 42395 }, 42396 42397 adjacent: function(element) { 42398 var args = Array.prototype.slice.call(arguments, 1); 42399 return Selector.findChildElements(element.parentNode, args).without(element); 42400 }, 42401 42402 identify: function(element) { 42403 element = $(element); 42404 var id = Element.readAttribute(element, 'id'); 42405 if (id) return id; 42406 do { id = 'anonymous_element_' + Element.idCounter++ } while ($(id)); 42407 Element.writeAttribute(element, 'id', id); 42408 return id; 42409 }, 42410 42411 readAttribute: function(element, name) { 42412 element = $(element); 42413 if (Prototype.Browser.IE) { 42414 var t = Element._attributeTranslations.read; 42415 if (t.values[name]) return t.values[name](element, name); 42416 if (t.names[name]) name = t.names[name]; 42417 if (name.include(':')) { 42418 return (!element.attributes || !element.attributes[name]) ? null : 42419 element.attributes[name].value; 42420 } 42421 } 42422 return element.getAttribute(name); 42423 }, 42424 42425 writeAttribute: function(element, name, value) { 42426 element = $(element); 42427 var attributes = { }, t = Element._attributeTranslations.write; 42428 42429 if (typeof name == 'object') attributes = name; 42430 else attributes[name] = Object.isUndefined(value) ? true : value; 42431 42432 for (var attr in attributes) { 42433 name = t.names[attr] || attr; 42434 value = attributes[attr]; 42435 if (t.values[attr]) name = t.values[attr](element, value); 42436 if (value === false || value === null) 42437 element.removeAttribute(name); 42438 else if (value === true) 42439 element.setAttribute(name, name); 42440 else element.setAttribute(name, value); 42441 } 42442 return element; 42443 }, 42444 42445 getHeight: function(element) { 42446 return Element.getDimensions(element).height; 42447 }, 42448 42449 getWidth: function(element) { 42450 return Element.getDimensions(element).width; 42451 }, 42452 42453 classNames: function(element) { 42454 return new Element.ClassNames(element); 42455 }, 42456 42457 hasClassName: function(element, className) { 42458 if (!(element = $(element))) return; 42459 var elementClassName = element.className; 42460 return (elementClassName.length > 0 && (elementClassName == className || 42461 new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName))); 42462 }, 42463 42464 addClassName: function(element, className) { 42465 if (!(element = $(element))) return; 42466 if (!Element.hasClassName(element, className)) 42467 element.className += (element.className ? ' ' : '') + className; 42468 return element; 42469 }, 42470 42471 removeClassName: function(element, className) { 42472 if (!(element = $(element))) return; 42473 element.className = element.className.replace( 42474 new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip(); 42475 return element; 42476 }, 42477 42478 toggleClassName: function(element, className) { 42479 if (!(element = $(element))) return; 42480 return Element[Element.hasClassName(element, className) ? 42481 'removeClassName' : 'addClassName'](element, className); 42482 }, 42483 42484 cleanWhitespace: function(element) { 42485 element = $(element); 42486 var node = element.firstChild; 42487 while (node) { 42488 var nextNode = node.nextSibling; 42489 if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) 42490 element.removeChild(node); 42491 node = nextNode; 42492 } 42493 return element; 42494 }, 42495 42496 empty: function(element) { 42497 return $(element).innerHTML.blank(); 42498 }, 42499 42500 descendantOf: function(element, ancestor) { 42501 element = $(element), ancestor = $(ancestor); 42502 42503 if (element.compareDocumentPosition) 42504 return (element.compareDocumentPosition(ancestor) & 8) === 8; 42505 42506 if (ancestor.contains) 42507 return ancestor.contains(element) && ancestor !== element; 42508 42509 while (element = element.parentNode) 42510 if (element == ancestor) return true; 42511 42512 return false;
42513 }, 42514 42515 scrollTo: function(element) { 42516 element = $(element); 42517 var pos = Element.cumulativeOffset(element); 42518 window.scrollTo(pos[0], pos[1]); 42519 return element; 42520 }, 42521 42522 getStyle: function(element, style) { 42523 element = $(element); 42524 style = style == 'float' ? 'cssFloat' : style.camelize(); 42525 var value = element.style[style]; 42526 if (!value || value == 'auto') { 42527 var css = document.defaultView.getComputedStyle(element, null); 42528 value = css ? css[style] : null; 42529 } 42530 if (style == 'opacity') return value ? parseFloat(value) : 1.0; 42531 return value == 'auto' ? null : value; 42532 }, 42533 42534 getOpacity: function(element) { 42535 return $(element).getStyle('opacity'); 42536 }, 42537 42538 setStyle: function(element, styles) { 42539 element = $(element); 42540 var elementStyle = element.style, match; 42541 if (Object.isString(styles)) { 42542 element.style.cssText += ';' + styles; 42543 return styles.include('opacity') ? 42544 element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element; 42545 } 42546 for (var property in styles) 42547 if (property == 'opacity') element.setOpacity(styles[property]); 42548 else 42549 elementStyle[(property == 'float' || property == 'cssFloat') ? 42550 (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') : 42551 property] = styles[property]; 42552 42553 return element; 42554 }, 42555 42556 setOpacity: function(element, value) { 42557 element = $(element); 42558 element.style.opacity = (value == 1 || value === '') ? '' : 42559 (value < 0.00001) ? 0 : value; 42560 return element; 42561 }, 42562 42563 getDimensions: function(element) { 42564 element = $(element); 42565 var display = Element.getStyle(element, 'display'); 42566 if (display != 'none' && display != null) // Safari bug 42567 return {width: element.offsetWidth, height: element.offsetHeight}; 42568 42569 var els = element.style; 42570 var originalVisibility = els.visibility; 42571 var originalPosition = els.position; 42572 var originalDisplay = els.display; 42573 els.visibility = 'hidden'; 42574 if (originalPosition != 'fixed') // Switching fixed to absolute causes issues in Safari 42575 els.position = 'absolute'; 42576 els.display = 'block'; 42577 var originalWidth = element.clientWidth; 42578 var originalHeight = element.clientHeight; 42579 els.display = originalDisplay; 42580 els.position = originalPosition; 42581 els.visibility = originalVisibility; 42582 return {width: originalWidth, height: originalHeight}; 42583 }, 42584 42585 makePositioned: function(element) { 42586 element = $(element); 42587 var pos = Element.getStyle(element, 'position'); 42588 if (pos == 'static' || !pos) { 42589 element._madePositioned = true; 42590 element.style.position = 'relative'; 42591 if (Prototype.Browser.Opera) { 42592 element.style.top = 0; 42593 element.style.left = 0; 42594 } 42595 } 42596 return element; 42597 }, 42598 42599 undoPositioned: function(element) { 42600 element = $(element); 42601 if (element._madePositioned) { 42602 element._madePositioned = undefined; 42603 element.style.position = 42604 element.style.top = 42605 element.style.left = 42606 element.style.bottom = 42607 element.style.right = ''; 42608 } 42609 return element; 42610 }, 42611 42612 makeClipping: function(element) { 42613 element = $(element); 42614 if (element._overflow) return element; 42615 element._overflow = Element.getStyle(element, 'overflow') || 'auto'; 42616 if (element._overflow !== 'hidden') 42617 element.style.overflow = 'hidden'; 42618 return element; 42619 }, 42620 42621 undoClipping: function(element) { 42622 element = $(element); 42623 if (!element._overflow) return element; 42624 element.style.overflow = element._overflow == 'auto' ? '' : element._overflow; 42625 element._overflow = null; 42626 return element; 42627 }, 42628 42629 cumulativeOffset: function(element) { 42630 var valueT = 0, valueL = 0; 42631 do { 42632 valueT += element.offsetTop || 0; 42633 valueL += element.offsetLeft || 0; 42634 element = element.offsetParent; 42635 } while (element); 42636 return Element._returnOffset(valueL, valueT); 42637 }, 42638 42639 positionedOffset: function(element) { 42640 var valueT = 0, valueL = 0; 42641 do { 42642 valueT += element.offsetTop || 0; 42643 valueL += element.offsetLeft || 0; 42644 element = element.offsetParent; 42645 if (element) { 42646 if (element.tagName.toUpperCase() == 'BODY') break; 42647 var p = Element.getStyle(element, 'position'); 42648 if (p !== 'static') break; 42649 } 42650 } while (element); 42651 return Element._returnOffset(valueL, valueT); 42652 }, 42653 42654 absolutize: function(element) { 42655 element = $(element); 42656 if (Element.getStyle(element, 'position') == 'absolute') return element; 42657 42658 var offsets = Element.positionedOffset(element); 42659 var top = offsets[1]; 42660 var left = offsets[0]; 42661 var width = element.clientWidth; 42662 var height = element.clientHeight; 42663 42664 element._originalLeft = left - parseFloat(element.style.left || 0); 42665 element._originalTop = top - parseFloat(element.style.top || 0); 42666 element._originalWidth = element.style.width; 42667 element._originalHeight = element.style.height; 42668 42669 element.style.position = 'absolute'; 42670 element.style.top = top + 'px'; 42671 element.style.left = left + 'px'; 42672 element.style.width = width + 'px'; 42673 element.style.height = height + 'px'; 42674 return element; 42675 }, 42676 42677 relativize: function(element) { 42678 element = $(element); 42679 if (Element.getStyle(element, 'position') == 'relative') return element; 42680 42681 element.style.position = 'relative'; 42682 var top = parseFloat(element.style.top || 0) - (element._originalTop || 0); 42683 var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0); 42684 42685 element.style.top = top + 'px'; 42686 element.style.left = left + 'px'; 42687 element.style.height = element._originalHeight; 42688 element.style.width = element._originalWidth; 42689 return element; 42690 }, 42691 42692 cumulativeScrollOffset: function(element) { 42693 var valueT = 0, valueL = 0; 42694 do { 42695 valueT += element.scrollTop || 0; 42696 valueL += element.scrollLeft || 0; 42697 element = element.parentNode; 42698 } while (element); 42699 return Element._returnOffset(valueL, valueT); 42700 }, 42701 42702 getOffsetParent: function(element) { 42703 if (element.offsetParent) return $(element.offsetParent); 42704 if (element == document.body) return $(element); 42705 42706 while ((element = element.parentNode) && element != document.body) 42707 if (Element.getStyle(element, 'position') != 'static') 42708 return $(element); 42709 42710 return $(document.body); 42711 }, 42712 42713 viewportOffset: function(forElement) { 42714 var valueT = 0, valueL = 0; 42715 42716 var element = forElement; 42717 do { 42718 valueT += element.offsetTop || 0; 42719 valueL += element.offsetLeft || 0; 42720 42721 if (element.offsetParent == document.body && 42722 Element.getStyle(element, 'position') == 'absolute') break; 42723 42724 } while (element = element.offsetParent); 42725 42726 element = forElement; 42727 do { 42728 if (!Prototype.Browser.Opera || (element.tagName && (element.tagName.toUpperCase() == 'BODY'))) { 42729 valueT -= element.scrollTop || 0; 42730 valueL -= element.scrollLeft || 0; 42731 } 42732 } while (element = element.parentNode); 42733 42734 return Element._returnOffset(valueL, valueT); 42735 }, 42736 42737 clonePosition: function(element, source) { 42738 var options = Object.extend({ 42739 setLeft: true, 42740 setTop: true, 42741 setWidth: true, 42742 setHeight: true, 42743 offsetTop: 0, 42744 offsetLeft: 0 42745 }, arguments[2] || { }); 42746 42747 source = $(source); 42748 var p = Element.viewportOffset(source); 42749 42750 element = $(element); 42751 var delta = [0, 0]; 42752 var parent = null; 42753 if (Element.getStyle(element, 'position') == 'absolute') { 42754 parent = Element.getOffsetParent(element); 42755 delta = Element.viewportOffset(parent); 42756 } 42757 42758 if (parent == document.body) { 42759 delta[0] -= document.body.offsetLeft; 42760 delta[1] -= document.body.offsetTop; 42761 } 42762 42763 if (options.setLeft) element.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px'; 42764 if (options.setTop) element.style.top = (p[1] - delta[1] + options.offsetTop) + 'px'; 42765 if (options.setWidth) element.style.width = source.offsetWidth + 'px'; 42766 if (options.setHeight) element.style.height = source.offsetHeight + 'px'; 42767 return element; 42768 } 42769}; 42770 42771Object.extend(Element.Methods, { 42772 getElementsBySelector: Element.Methods.select, 42773 42774 childElements: Element.Methods.immediateDescendants 42775}); 42776 42777Element._attributeTranslations = { 42778 write: { 42779 names: { 42780 className: 'class', 42781 htmlFor: 'for' 42782 }, 42783 values: { } 42784 } 42785}; 42786 42787if (Prototype.Browser.Opera) { 42788 Element.Methods.getStyle = Element.Methods.getStyle.wrap( 42789 function(proceed, element, style) { 42790 switch (style) { 42791 case 'left': case 'top': case 'right': case 'bottom': 42792 if (proceed(element, 'position') === 'static') return null; 42793 case 'height': case 'width': 42794 if (!Element.visible(element)) return null; 42795 42796 var dim = parseInt(proceed(element, style), 10); 42797 42798 if (dim !== element['offset' + style.capitalize()]) 42799 return dim + 'px'; 42800 42801 var properties; 42802 if (style === 'height') { 42803 properties = ['border-top-width', 'padding-top', 42804 'padding-bottom', 'border-bottom-width']; 42805 } 42806 else { 42807 properties = ['border-left-width', 'padding-left', 42808 'padding-right', 'border-right-width']; 42809 } 42810 return properties.inject(dim, function(memo, property) { 42811 var val = proceed(element, property); 42812 return val === null ? memo : memo - parseInt(val, 10); 42813 }) + 'px'; 42814 default: return proceed(element, style); 42815 } 42816 } 42817 ); 42818 42819 Element.Methods.readAttribute = Element.Methods.readAttribute.wrap( 42820 function(proceed, element, attribute) { 42821 if (attribute === 'title') return element.title; 42822 return proceed(element, attribute); 42823 } 42824 ); 42825} 42826 42827else if (Prototype.Browser.IE) { 42828 Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap( 42829 function(proceed, element) { 42830 element = $(element);
42831 try { element.offsetParent } 42832 catch(e) { return $(document.body) } 42833 var position = element.getStyle('position'); 42834 if (position !== 'static') return proceed(element); 42835 element.setStyle({ position: 'relative' }); 42836 var value = proceed(element); 42837 element.setStyle({ position: position }); 42838 return value; 42839 } 42840 ); 42841 42842 $w('positionedOffset viewportOffset').each(function(method) { 42843 Element.Methods[method] = Element.Methods[method].wrap( 42844 function(proceed, element) { 42845 element = $(element); 42846 try { element.offsetParent } 42847 catch(e) { return Element._returnOffset(0,0) } 42848 var position = element.getStyle('position'); 42849 if (position !== 'static') return proceed(element); 42850 var offsetParent = element.getOffsetParent(); 42851 if (offsetParent && offsetParent.getStyle('position') === 'fixed') 42852 offsetParent.setStyle({ zoom: 1 }); 42853 element.setStyle({ position: 'relative' }); 42854 var value = proceed(element); 42855 element.setStyle({ position: position }); 42856 return value; 42857 } 42858 ); 42859 }); 42860 42861 Element.Methods.cumulativeOffset = Element.Methods.cumulativeOffset.wrap( 42862 function(proceed, element) { 42863 try { element.offsetParent } 42864 catch(e) { return Element._returnOffset(0,0) } 42865 return proceed(element); 42866 } 42867 ); 42868 42869 Element.Methods.getStyle = function(element, style) { 42870 element = $(element); 42871 style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize(); 42872 var value = element.style[style]; 42873 if (!value && element.currentStyle) value = element.currentStyle[style]; 42874 42875 if (style == 'opacity') { 42876 if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/)) 42877 if (value[1]) return parseFloat(value[1]) / 100; 42878 return 1.0; 42879 } 42880 42881 if (value == 'auto') { 42882 if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none')) 42883 return element['offset' + style.capitalize()] + 'px'; 42884 return null; 42885 } 42886 return value; 42887 }; 42888 42889 Element.Methods.setOpacity = function(element, value) { 42890 function stripAlpha(filter){ 42891 return filter.replace(/alpha\([^\)]*\)/gi,''); 42892 } 42893 element = $(element); 42894 var currentStyle = element.currentStyle; 42895 if ((currentStyle && !currentStyle.hasLayout) || 42896 (!currentStyle && element.style.zoom == 'normal')) 42897 element.style.zoom = 1; 42898 42899 var filter = element.getStyle('filter'), style = element.style; 42900 if (value == 1 || value === '') { 42901 (filter = stripAlpha(filter)) ? 42902 style.filter = filter : style.removeAttribute('filter'); 42903 return element; 42904 } else if (value < 0.00001) value = 0; 42905 style.filter = stripAlpha(filter) + 42906 'alpha(opacity=' + (value * 100) + ')'; 42907 return element; 42908 }; 42909 42910 Element._attributeTranslations = (function(){ 42911 42912 var classProp = 'className'; 42913 var forProp = 'for'; 42914 42915 var el = document.createElement('div'); 42916 42917 el.setAttribute(classProp, 'x'); 42918 42919 if (el.className !== 'x') { 42920 el.setAttribute('class', 'x'); 42921 if (el.className === 'x') { 42922 classProp = 'class'; 42923 } 42924 } 42925 el = null; 42926 42927 el = document.createElement('label'); 42928 el.setAttribute(forProp, 'x'); 42929 if (el.htmlFor !== 'x') { 42930 el.setAttribute('htmlFor', 'x'); 42931 if (el.htmlFor === 'x') { 42932 forProp = 'htmlFor'; 42933 } 42934 } 42935 el = null; 42936 42937 return { 42938 read: { 42939 names: { 42940 'class': classProp, 42941 'className': classProp, 42942 'for': forProp, 42943 'htmlFor': forProp 42944 }, 42945 values: { 42946 _getAttr: function(element, attribute) { 42947 return element.getAttribute(attribute); 42948 }, 42949 _getAttr2: function(element, attribute) { 42950 return element.getAttribute(attribute, 2); 42951 }, 42952 _getAttrNode: function(element, attribute) { 42953 var node = element.getAttributeNode(attribute); 42954 return node ? node.value : ""; 42955 }, 42956 _getEv: (function(){ 42957 42958 var el = document.createElement('div'); 42959 el.onclick = Prototype.emptyFunction; 42960 var value = el.getAttribute('onclick'); 42961 var f; 42962 42963 if (String(value).indexOf('{') > -1) { 42964 f = function(element, attribute) { 42965 attribute = element.getAttribute(attribute); 42966 if (!attribute) return null; 42967 attribute = attribute.toString(); 42968 attribute = attribute.split('{')[1]; 42969 attribute = attribute.split('}')[0]; 42970 return attribute.strip(); 42971 }; 42972 } 42973 else if (value === '') { 42974 f = function(element, attribute) { 42975 attribute = element.getAttribute(attribute); 42976 if (!attribute) return null; 42977 return attribute.strip(); 42978 }; 42979 } 42980 el = null; 42981 return f; 42982 })(), 42983 _flag: function(element, attribute) { 42984 return $(element).hasAttribute(attribute) ? attribute : null; 42985 }, 42986 style: function(element) { 42987 return element.style.cssText.toLowerCase(); 42988 }, 42989 title: function(element) { 42990 return element.title; 42991 } 42992 } 42993 } 42994 } 42995 })(); 42996 42997 Element._attributeTranslations.write = { 42998 names: Object.extend({ 42999 cellpadding: 'cellPadding', 43000 cellspacing: 'cellSpacing' 43001 }, Element._attributeTranslations.read.names), 43002 values: { 43003 checked: function(element, value) { 43004 element.checked = !!value; 43005 }, 43006 43007 style: function(element, value) { 43008 element.style.cssText = value ? value : ''; 43009 } 43010 } 43011 }; 43012 43013 Element._attributeTranslations.has = {}; 43014 43015 $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' + 43016 'encType maxLength readOnly longDesc frameBorder').each(function(attr) { 43017 Element._attributeTranslations.write.names[attr.toLowerCase()] = attr; 43018 Element._attributeTranslations.has[attr.toLowerCase()] = attr; 43019 }); 43020 43021 (function(v) { 43022 Object.extend(v, { 43023 href: v._getAttr2, 43024 src: v._getAttr2, 43025 type: v._getAttr, 43026 action: v._getAttrNode, 43027 disabled: v._flag, 43028 checked: v._flag, 43029 readonly: v._flag, 43030 multiple: v._flag, 43031 onload: v._getEv, 43032 onunload: v._getEv, 43033 onclick: v._getEv, 43034 ondblclick: v._getEv, 43035 onmousedown: v._getEv, 43036 onmouseup: v._getEv, 43037 onmouseover: v._getEv, 43038 onmousemove: v._getEv, 43039 onmouseout: v._getEv, 43040 onfocus: v._getEv, 43041 onblur: v._getEv, 43042 onkeypress: v._getEv, 43043 onkeydown: v._getEv, 43044 onkeyup: v._getEv, 43045 onsubmit: v._getEv,
43046 onreset: v._getEv, 43047 onselect: v._getEv, 43048 onchange: v._getEv 43049 }); 43050 })(Element._attributeTranslations.read.values); 43051 43052 if (Prototype.BrowserFeatures.ElementExtensions) { 43053 (function() { 43054 function _descendants(element) { 43055 var nodes = element.getElementsByTagName('*'), results = []; 43056 for (var i = 0, node; node = nodes[i]; i++) 43057 if (node.tagName !== "!") // Filter out comment nodes. 43058 results.push(node); 43059 return results; 43060 } 43061 43062 Element.Methods.down = function(element, expression, index) { 43063 element = $(element); 43064 if (arguments.length == 1) return element.firstDescendant(); 43065 return Object.isNumber(expression) ? _descendants(element)[expression] : 43066 Element.select(element, expression)[index || 0]; 43067 } 43068 })(); 43069 } 43070 43071} 43072 43073else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) { 43074 Element.Methods.setOpacity = function(element, value) { 43075 element = $(element); 43076 element.style.opacity = (value == 1) ? 0.999999 : 43077 (value === '') ? '' : (value < 0.00001) ? 0 : value; 43078 return element; 43079 }; 43080} 43081 43082else if (Prototype.Browser.WebKit) { 43083 Element.Methods.setOpacity = function(element, value) { 43084 element = $(element); 43085 element.style.opacity = (value == 1 || value === '') ? '' : 43086 (value < 0.00001) ? 0 : value; 43087 43088 if (value == 1) 43089 if(element.tagName.toUpperCase() == 'IMG' && element.width) { 43090 element.width++; element.width--; 43091 } else try { 43092 var n = document.createTextNode(' '); 43093 element.appendChild(n); 43094 element.removeChild(n); 43095 } catch (e) { } 43096 43097 return element; 43098 }; 43099 43100 Element.Methods.cumulativeOffset = function(element) { 43101 var valueT = 0, valueL = 0; 43102 do { 43103 valueT += element.offsetTop || 0; 43104 valueL += element.offsetLeft || 0; 43105 if (element.offsetParent == document.body) 43106 if (Element.getStyle(element, 'position') == 'absolute') break; 43107 43108 element = element.offsetParent; 43109 } while (element); 43110 43111 return Element._returnOffset(valueL, valueT); 43112 }; 43113} 43114 43115if ('outerHTML' in document.documentElement) { 43116 Element.Methods.replace = function(element, content) { 43117 element = $(element); 43118 43119 if (content && content.toElement) content = content.toElement(); 43120 if (Object.isElement(content)) { 43121 element.parentNode.replaceChild(content, element); 43122 return element; 43123 } 43124 43125 content = Object.toHTML(content); 43126 var parent = element.parentNode, tagName = parent.tagName.toUpperCase(); 43127 43128 if (Element._insertionTranslations.tags[tagName]) { 43129 var nextSibling = element.next(); 43130 var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); 43131 parent.removeChild(element); 43132 if (nextSibling) 43133 fragments.each(function(node) { parent.insertBefore(node, nextSibling) }); 43134 else 43135 fragments.each(function(node) { parent.appendChild(node) }); 43136 } 43137 else element.outerHTML = content.stripScripts(); 43138 43139 content.evalScripts.bind(content).defer(); 43140 return element; 43141 }; 43142} 43143 43144Element._returnOffset = function(l, t) { 43145 var result = [l, t]; 43146 result.left = l; 43147 result.top = t; 43148 return result; 43149}; 43150 43151Element._getContentFromAnonymousElement = function(tagName, html) { 43152 var div = new Element('div'), t = Element._insertionTranslations.tags[tagName]; 43153 if (t) { 43154 div.innerHTML = t[0] + html + t[1]; 43155 t[2].times(function() { div = div.firstChild }); 43156 } else div.innerHTML = html; 43157 return $A(div.childNodes); 43158}; 43159 43160Element._insertionTranslations = { 43161 before: function(element, node) { 43162 element.parentNode.insertBefore(node, element); 43163 }, 43164 top: function(element, node) { 43165 element.insertBefore(node, element.firstChild); 43166 }, 43167 bottom: function(element, node) { 43168 element.appendChild(node); 43169 }, 43170 after: function(element, node) { 43171 element.parentNode.insertBefore(node, element.nextSibling); 43172 }, 43173 tags: { 43174 TABLE: ['<table>', '</table>', 1], 43175 TBODY: ['<table><tbody>', '</tbody></table>', 2], 43176 TR: ['<table><tbody><tr>', '</tr></tbody></table>', 3], 43177 TD: ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4], 43178 SELECT: ['<select>', '</select>', 1] 43179 } 43180}; 43181 43182(function() { 43183 var tags = Element._insertionTranslations.tags; 43184 Object.extend(tags, { 43185 THEAD: tags.TBODY, 43186 TFOOT: tags.TBODY, 43187 TH: tags.TD 43188 }); 43189})(); 43190 43191Element.Methods.Simulated = { 43192 hasAttribute: function(element, attribute) { 43193 attribute = Element._attributeTranslations.has[attribute] || attribute; 43194 var node = $(element).getAttributeNode(attribute); 43195 return !!(node && node.specified); 43196 } 43197}; 43198 43199Element.Methods.ByTag = { }; 43200 43201Object.extend(Element, Element.Methods); 43202 43203(function(div) { 43204 43205 if (!Prototype.BrowserFeatures.ElementExtensions && div['__proto__']) { 43206 window.HTMLElement = { }; 43207 window.HTMLElement.prototype = div['__proto__']; 43208 Prototype.BrowserFeatures.ElementExtensions = true; 43209 } 43210 43211 div = null; 43212 43213})(document.createElement('div')) 43214 43215Element.extend = (function() { 43216 43217 function checkDeficiency(tagName) { 43218 if (typeof window.Element != 'undefined') { 43219 var proto = window.Element.prototype; 43220 if (proto) { 43221 var id = '_' + (Math.random()+'').slice(2); 43222 var el = document.createElement(tagName); 43223 proto[id] = 'x'; 43224 var isBuggy = (el[id] !== 'x'); 43225 delete proto[id]; 43226 el = null; 43227 return isBuggy; 43228 } 43229 } 43230 return false;
43231 } 43232 43233 function extendElementWith(element, methods) { 43234 for (var property in methods) { 43235 var value = methods[property]; 43236 if (Object.isFunction(value) && !(property in element)) 43237 element[property] = value.methodize(); 43238 } 43239 } 43240 43241 var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY = checkDeficiency('object'); 43242 43243 if (Prototype.BrowserFeatures.SpecificElementExtensions) { 43244 if (HTMLOBJECTELEMENT_PROTOTYPE_BUGGY) { 43245 return function(element) { 43246 if (element && typeof element._extendedByPrototype == 'undefined') { 43247 var t = element.tagName; 43248 if (t && (/^(?:object|applet|embed)$/i.test(t))) { 43249 extendElementWith(element, Element.Methods); 43250 extendElementWith(element, Element.Methods.Simulated); 43251 extendElementWith(element, Element.Methods.ByTag[t.toUpperCase()]); 43252 } 43253 } 43254 return element; 43255 } 43256 } 43257 return Prototype.K; 43258 } 43259 43260 var Methods = { }, ByTag = Element.Methods.ByTag; 43261 43262 var extend = Object.extend(function(element) { 43263 if (!element || typeof element._extendedByPrototype != 'undefined' || 43264 element.nodeType != 1 || element == window) return element; 43265 43266 var methods = Object.clone(Methods), 43267 tagName = element.tagName.toUpperCase(); 43268 43269 if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]); 43270 43271 extendElementWith(element, methods); 43272 43273 element._extendedByPrototype = Prototype.emptyFunction; 43274 return element; 43275 43276 }, { 43277 refresh: function() { 43278 if (!Prototype.BrowserFeatures.ElementExtensions) { 43279 Object.extend(Methods, Element.Methods); 43280 Object.extend(Methods, Element.Methods.Simulated); 43281 } 43282 } 43283 }); 43284 43285 extend.refresh(); 43286 return extend; 43287})(); 43288 43289Element.hasAttribute = function(element, attribute) { 43290 if (element.hasAttribute) return element.hasAttribute(attribute); 43291 return Element.Methods.Simulated.hasAttribute(element, attribute); 43292}; 43293 43294Element.addMethods = function(methods) { 43295 var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag; 43296 43297 if (!methods) { 43298 Object.extend(Form, Form.Methods); 43299 Object.extend(Form.Element, Form.Element.Methods); 43300 Object.extend(Element.Methods.ByTag, { 43301 "FORM": Object.clone(Form.Methods), 43302 "INPUT": Object.clone(Form.Element.Methods), 43303 "SELECT": Object.clone(Form.Element.Methods), 43304 "TEXTAREA": Object.clone(Form.Element.Methods) 43305 }); 43306 } 43307 43308 if (arguments.length == 2) { 43309 var tagName = methods; 43310 methods = arguments[1]; 43311 } 43312 43313 if (!tagName) Object.extend(Element.Methods, methods || { }); 43314 else { 43315 if (Object.isArray(tagName)) tagName.each(extend); 43316 else extend(tagName); 43317 } 43318 43319 function extend(tagName) { 43320 tagName = tagName.toUpperCase(); 43321 if (!Element.Methods.ByTag[tagName]) 43322 Element.Methods.ByTag[tagName] = { }; 43323 Object.extend(Element.Methods.ByTag[tagName], methods); 43324 } 43325 43326 function copy(methods, destination, onlyIfAbsent) { 43327 onlyIfAbsent = onlyIfAbsent || false; 43328 for (var property in methods) { 43329 var value = methods[property]; 43330 if (!Object.isFunction(value)) continue; 43331 if (!onlyIfAbsent || !(property in destination)) 43332 destination[property] = value.methodize(); 43333 } 43334 } 43335 43336 function findDOMClass(tagName) { 43337 var klass; 43338 var trans = { 43339 "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph", 43340 "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList", 43341 "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading", 43342 "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote", 43343 "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION": 43344 "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD": 43345 "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR": 43346 "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET": 43347 "FrameSet", "IFRAME": "IFrame" 43348 }; 43349 if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element'; 43350 if (window[klass]) return window[klass]; 43351 klass = 'HTML' + tagName + 'Element'; 43352 if (window[klass]) return window[klass]; 43353 klass = 'HTML' + tagName.capitalize() + 'Element'; 43354 if (window[klass]) return window[klass]; 43355 43356 var element = document.createElement(tagName); 43357 var proto = element['__proto__'] || element.constructor.prototype; 43358 element = null; 43359 return proto; 43360 } 43361 43362 var elementPrototype = window.HTMLElement ? HTMLElement.prototype : 43363 Element.prototype; 43364 43365 if (F.ElementExtensions) { 43366 copy(Element.Methods, elementPrototype); 43367 copy(Element.Methods.Simulated, elementPrototype, true); 43368 } 43369 43370 if (F.SpecificElementExtensions) { 43371 for (var tag in Element.Methods.ByTag) { 43372 var klass = findDOMClass(tag); 43373 if (Object.isUndefined(klass)) continue; 43374 copy(T[tag], klass.prototype); 43375 } 43376 } 43377 43378 Object.extend(Element, Element.Methods); 43379 delete Element.ByTag; 43380 43381 if (Element.extend.refresh) Element.extend.refresh(); 43382 Element.cache = { }; 43383}; 43384 43385 43386document.viewport = { 43387 43388 getDimensions: function() { 43389 return { width: this.getWidth(), height: this.getHeight() }; 43390 }, 43391 43392 getScrollOffsets: function() { 43393 return Element._returnOffset( 43394 window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft, 43395 window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop); 43396 } 43397}; 43398 43399(function(viewport) { 43400 var B = Prototype.Browser, doc = document, element, property = {}; 43401 43402 function getRootElement() { 43403 if (B.WebKit && !doc.evaluate) 43404 return document; 43405 43406 if (B.Opera && window.parseFloat(window.opera.version()) < 9.5) 43407 return document.body; 43408 43409 return document.documentElement; 43410 } 43411 43412 function define(D) { 43413 if (!element) element = getRootElement(); 43414 43415 property[D] = 'client' + D; 43416 43417 viewport['get' + D] = function() { return element[property[D]] }; 43418 return viewport['get' + D](); 43419 } 43420 43421 viewport.getWidth = define.curry('Width'); 43422 43423 viewport.getHeight = define.curry('Height'); 43424})(document.viewport); 43425 43426 43427Element.Storage = { 43428 UID: 1 43429}; 43430 43431Element.addMethods({
43432 getStorage: function(element) { 43433 if (!(element = $(element))) return; 43434 43435 var uid; 43436 if (element === window) { 43437 uid = 0; 43438 } else { 43439 if (typeof element._prototypeUID === "undefined") 43440 element._prototypeUID = [Element.Storage.UID++]; 43441 uid = element._prototypeUID[0]; 43442 } 43443 43444 if (!Element.Storage[uid]) 43445 Element.Storage[uid] = $H(); 43446 43447 return Element.Storage[uid]; 43448 }, 43449 43450 store: function(element, key, value) { 43451 if (!(element = $(element))) return; 43452 43453 if (arguments.length === 2) { 43454 Element.getStorage(element).update(key); 43455 } else { 43456 Element.getStorage(element).set(key, value); 43457 } 43458 43459 return element; 43460 }, 43461 43462 retrieve: function(element, key, defaultValue) { 43463 if (!(element = $(element))) return; 43464 var hash = Element.getStorage(element), value = hash.get(key); 43465 43466 if (Object.isUndefined(value)) { 43467 hash.set(key, defaultValue); 43468 value = defaultValue; 43469 } 43470 43471 return value; 43472 }, 43473 43474 clone: function(element, deep) { 43475 if (!(element = $(element))) return; 43476 var clone = element.cloneNode(deep); 43477 clone._prototypeUID = void 0; 43478 if (deep) { 43479 var descendants = Element.select(clone, '*'), 43480 i = descendants.length; 43481 while (i--) { 43482 descendants[i]._prototypeUID = void 0; 43483 } 43484 } 43485 return Element.extend(clone); 43486 } 43487}); 43488/* Portions of the Selector class are derived from Jack Slocum's DomQuery, 43489 * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style 43490 * license. Please see http://www.yui-ext.com/ for more information. */ 43491 43492var Selector = Class.create({ 43493 initialize: function(expression) { 43494 this.expression = expression.strip(); 43495 43496 if (this.shouldUseSelectorsAPI()) { 43497 this.mode = 'selectorsAPI'; 43498 } else if (this.shouldUseXPath()) { 43499 this.mode = 'xpath'; 43500 this.compileXPathMatcher(); 43501 } else { 43502 this.mode = "normal"; 43503 this.compileMatcher(); 43504 } 43505 43506 }, 43507 43508 shouldUseXPath: (function() { 43509 43510 var IS_DESCENDANT_SELECTOR_BUGGY = (function(){ 43511 var isBuggy = false; 43512 if (document.evaluate && window.XPathResult) { 43513 var el = document.createElement('div'); 43514 el.innerHTML = '<ul><li></li></ul><div><ul><li></li></ul></div>'; 43515 43516 var xpath = ".//*[local-name()='ul' or local-name()='UL']" + 43517 "//*[local-name()='li' or local-name()='LI']"; 43518 43519 var result = document.evaluate(xpath, el, null, 43520 XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); 43521 43522 isBuggy = (result.snapshotLength !== 2); 43523 el = null; 43524 } 43525 return isBuggy; 43526 })(); 43527 43528 return function() { 43529 if (!Prototype.BrowserFeatures.XPath) return false; 43530 43531 var e = this.expression; 43532 43533 if (Prototype.Browser.WebKit && 43534 (e.include("-of-type") || e.include(":empty"))) 43535 return false; 43536 43537 if ((/(\[[\w-]*?:|:checked)/).test(e)) 43538 return false; 43539 43540 if (IS_DESCENDANT_SELECTOR_BUGGY) return false; 43541 43542 return true; 43543 } 43544 43545 })(), 43546 43547 shouldUseSelectorsAPI: function() { 43548 if (!Prototype.BrowserFeatures.SelectorsAPI) return false; 43549 43550 if (Selector.CASE_INSENSITIVE_CLASS_NAMES) return false; 43551 43552 if (!Selector._div) Selector._div = new Element('div'); 43553 43554 try { 43555 Selector._div.querySelector(this.expression); 43556 } catch(e) { 43557 return false; 43558 } 43559 43560 return true; 43561 }, 43562 43563 compileMatcher: function() { 43564 var e = this.expression, ps = Selector.patterns, h = Selector.handlers, 43565 c = Selector.criteria, le, p, m, len = ps.length, name; 43566 43567 if (Selector._cache[e]) { 43568 this.matcher = Selector._cache[e]; 43569 return; 43570 } 43571 43572 this.matcher = ["this.matcher = function(root) {", 43573 "var r = root, h = Selector.handlers, c = false, n;"]; 43574 43575 while (e && le != e && (/\S/).test(e)) { 43576 le = e; 43577 for (var i = 0; i<len; i++) { 43578 p = ps[i].re; 43579 name = ps[i].name; 43580 if (m = e.match(p)) { 43581 this.matcher.push(Object.isFunction(c[name]) ? c[name](m) : 43582 new Template(c[name]).evaluate(m)); 43583 e = e.replace(m[0], ''); 43584 break; 43585 } 43586 } 43587 } 43588
43589 this.matcher.push("return h.unique(n);\n}"); 43590 eval(this.matcher.join('\n')); 43591 Selector._cache[this.expression] = this.matcher; 43592 }, 43593 43594 compileXPathMatcher: function() { 43595 var e = this.expression, ps = Selector.patterns, 43596 x = Selector.xpath, le, m, len = ps.length, name; 43597 43598 if (Selector._cache[e]) { 43599 this.xpath = Selector._cache[e]; return; 43600 } 43601 43602 this.matcher = ['.//*']; 43603 while (e && le != e && (/\S/).test(e)) { 43604 le = e; 43605 for (var i = 0; i<len; i++) { 43606 name = ps[i].name; 43607 if (m = e.match(ps[i].re)) { 43608 this.matcher.push(Object.isFunction(x[name]) ? x[name](m) : 43609 new Template(x[name]).evaluate(m)); 43610 e = e.replace(m[0], ''); 43611 break; 43612 } 43613 } 43614 } 43615 43616 this.xpath = this.matcher.join(''); 43617 Selector._cache[this.expression] = this.xpath; 43618 }, 43619 43620 findElements: function(root) { 43621 root = root || document; 43622 var e = this.expression, results; 43623 43624 switch (this.mode) { 43625 case 'selectorsAPI': 43626 if (root !== document) { 43627 var oldId = root.id, id = $(root).identify(); 43628 id = id.replace(/([\.:])/g, "\\$1"); 43629 e = "#" + id + " " + e; 43630 } 43631 43632 results = $A(root.querySelectorAll(e)).map(Element.extend); 43633 root.id = oldId; 43634 43635 return results; 43636 case 'xpath': 43637 return document._getElementsByXPath(this.xpath, root); 43638 default: 43639 return this.matcher(root); 43640 } 43641 }, 43642 43643 match: function(element) { 43644 this.tokens = []; 43645 43646 var e = this.expression, ps = Selector.patterns, as = Selector.assertions; 43647 var le, p, m, len = ps.length, name; 43648 43649 while (e && le !== e && (/\S/).test(e)) { 43650 le = e; 43651 for (var i = 0; i<len; i++) { 43652 p = ps[i].re; 43653 name = ps[i].name; 43654 if (m = e.match(p)) { 43655 if (as[name]) { 43656 this.tokens.push([name, Object.clone(m)]); 43657 e = e.replace(m[0], ''); 43658 } else { 43659 return this.findElements(document).include(element); 43660 } 43661 } 43662 } 43663 } 43664 43665 var match = true, name, matches; 43666 for (var i = 0, token; token = this.tokens[i]; i++) { 43667 name = token[0], matches = token[1]; 43668 if (!Selector.assertions[name](element, matches)) { 43669 match = false; break; 43670 } 43671 } 43672 43673 return match; 43674 }, 43675 43676 toString: function() { 43677 return this.expression; 43678 }, 43679 43680 inspect: function() { 43681 return "#<Selector:" + this.expression.inspect() + ">"; 43682 } 43683}); 43684 43685if (Prototype.BrowserFeatures.SelectorsAPI && 43686 document.compatMode === 'BackCompat') { 43687 Selector.CASE_INSENSITIVE_CLASS_NAMES = (function(){ 43688 var div = document.createElement('div'), 43689 span = document.createElement('span'); 43690 43691 div.id = "prototype_test_id"; 43692 span.className = 'Test'; 43693 div.appendChild(span); 43694 var isIgnored = (div.querySelector('#prototype_test_id .test') !== null); 43695 div = span = null; 43696 return isIgnored; 43697 })(); 43698} 43699 43700Object.extend(Selector, { 43701 _cache: { }, 43702 43703 xpath: { 43704 descendant: "//*", 43705 child: "/*", 43706 adjacent: "/following-sibling::*[1]", 43707 laterSibling: '/following-sibling::*', 43708 tagName: function(m) { 43709 if (m[1] == '*') return ''; 43710 return "[local-name()='" + m[1].toLowerCase() + 43711 "' or local-name()='" + m[1].toUpperCase() + "']"; 43712 }, 43713 className: "[contains(concat(' ', @class, ' '), ' #{1} ')]", 43714 id: "[@id='#{1}']", 43715 attrPresence: function(m) { 43716 m[1] = m[1].toLowerCase(); 43717 return new Template("[@#{1}]").evaluate(m); 43718 }, 43719 attr: function(m) { 43720 m[1] = m[1].toLowerCase(); 43721 m[3] = m[5] || m[6]; 43722 return new Template(Selector.xpath.operators[m[2]]).evaluate(m); 43723 }, 43724 pseudo: function(m) { 43725 var h = Selector.xpath.pseudos[m[1]]; 43726 if (!h) return ''; 43727 if (Object.isFunction(h)) return h(m); 43728 return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m); 43729 }, 43730 operators: { 43731 '=': "[@#{1}='#{3}']", 43732 '!=': "[@#{1}!='#{3}']", 43733 '^=': "[starts-with(@#{1}, '#{3}')]", 43734 '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']", 43735 '*=': "[contains(@#{1}, '#{3}')]", 43736 '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]", 43737 '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]" 43738 }, 43739 pseudos: { 43740 'first-child': '[not(preceding-sibling::*)]', 43741 'last-child': '[not(following-sibling::*)]', 43742 'only-child': '[not(preceding-sibling::* or following-sibling::*)]', 43743 'empty': "[count(*) = 0 and (count(text()) = 0)]", 43744 'checked': "[@checked]", 43745 'disabled': "[(@disabled) and (@type!='hidden')]", 43746 'enabled': "[not(@disabled) and (@type!='hidden')]", 43747 'not': function(m) { 43748 var e = m[6], p = Selector.patterns, 43749 x = Selector.xpath, le, v, len = p.length, name; 43750 43751 var exclusion = []; 43752 while (e && le != e && (/\S/).test(e)) { 43753 le = e; 43754 for (var i = 0; i<len; i++) { 43755 name = p[i].name 43756 if (m = e.match(p[i].re)) { 43757 v = Object.isFunction(x[name]) ? x[name](m) : new Template(x[name]).evaluate(m); 43758 exclusion.push("(" + v.substring(1, v.length - 1) + ")"); 43759 e = e.replace(m[0], ''); 43760 break; 43761 } 43762 } 43763 } 43764 return "[not(" + exclusion.join(" and ") + ")]"; 43765 }, 43766 'nth-child': function(m) { 43767 return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m); 43768 }, 43769 'nth-last-child': function(m) { 43770 return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m); 43771 }, 43772 'nth-of-type': function(m) { 43773 return Selector.xpath.pseudos.nth("position() ", m); 43774 }, 43775 'nth-last-of-type': function(m) { 43776 return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m); 43777 }, 43778 'first-of-type': function(m) { 43779 m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m); 43780 }, 43781 'last-of-type': function(m) { 43782 m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m); 43783 }, 43784 'only-of-type': function(m) { 43785 var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m); 43786 }, 43787 nth: function(fragment, m) { 43788 var mm, formula = m[6], predicate; 43789 if (formula == 'even') formula = '2n+0'; 43790 if (formula == 'odd') formula = '2n+1'; 43791 if (mm = formula.match(/^(\d+)$/)) // digit only 43792 return '[' + fragment + "= " + mm[1] + ']'; 43793 if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b 43794 if (mm[1] == "-") mm[1] = -1; 43795 var a = mm[1] ? Number(mm[1]) : 1; 43796 var b = mm[2] ? Number(mm[2]) : 0; 43797 predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " + 43798 "((#{fragment} - #{b}) div #{a} >= 0)]"; 43799 return new Template(predicate).evaluate({ 43800 fragment: fragment, a: a, b: b }); 43801 } 43802 } 43803 } 43804 }, 43805 43806 criteria: { 43807 tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;', 43808 className: 'n = h.className(n, r, "#{1}", c); c = false;', 43809 id: 'n = h.id(n, r, "#{1}", c); c = false;', 43810 attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;', 43811 attr: function(m) { 43812 m[3] = (m[5] || m[6]); 43813 return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m); 43814 }, 43815 pseudo: function(m) { 43816 if (m[6]) m[6] = m[6].replace(/"/g, '\\"'); 43817 return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m); 43818 }, 43819 descendant: 'c = "descendant";', 43820 child: 'c = "child";', 43821 adjacent: 'c = "adjacent";', 43822 laterSibling: 'c = "laterSibling";' 43823 }, 43824 43825 patterns: [ 43826 { name: 'laterSibling', re: /^\s*~\s*/ }, 43827 { name: 'child', re: /^\s*>\s*/ }, 43828 { name: 'adjacent', re: /^\s*\+\s*/ }, 43829 { name: 'descendant', re: /^\s/ }, 43830 43831 { name: 'tagName', re: /^\s*(\*|[\w\-]+)(\b|$)?/ }, 43832 { name: 'id', re: /^#([\w\-\*]+)(\b|$)/ }, 43833 { name: 'className', re: /^\.([\w\-\*]+)(\b|$)/ }, 43834 { name: 'pseudo', re: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/ }, 43835 { name: 'attrPresence', re: /^\[((?:[\w-]+:)?[\w-]+)\]/ }, 43836 { name: 'attr', re: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/ } 43837 ], 43838 43839 assertions: { 43840 tagName: function(element, matches) { 43841 return matches[1].toUpperCase() == element.tagName.toUpperCase(); 43842 }, 43843 43844 className: function(element, matches) { 43845 return Element.hasClassName(element, matches[1]); 43846 }, 43847 43848 id: function(element, matches) {
43849 return element.id === matches[1]; 43850 }, 43851 43852 attrPresence: function(element, matches) { 43853 return Element.hasAttribute(element, matches[1]); 43854 }, 43855 43856 attr: function(element, matches) { 43857 var nodeValue = Element.readAttribute(element, matches[1]); 43858 return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]); 43859 } 43860 }, 43861 43862 handlers: { 43863 concat: function(a, b) { 43864 for (var i = 0, node; node = b[i]; i++) 43865 a.push(node); 43866 return a; 43867 }, 43868 43869 mark: function(nodes) { 43870 var _true = Prototype.emptyFunction; 43871 for (var i = 0, node; node = nodes[i]; i++) 43872 node._countedByPrototype = _true; 43873 return nodes; 43874 }, 43875 43876 unmark: (function(){ 43877 43878 var PROPERTIES_ATTRIBUTES_MAP = (function(){ 43879 var el = document.createElement('div'), 43880 isBuggy = false, 43881 propName = '_countedByPrototype', 43882 value = 'x' 43883 el[propName] = value; 43884 isBuggy = (el.getAttribute(propName) === value); 43885 el = null; 43886 return isBuggy; 43887 })(); 43888 43889 return PROPERTIES_ATTRIBUTES_MAP ? 43890 function(nodes) { 43891 for (var i = 0, node; node = nodes[i]; i++) 43892 node.removeAttribute('_countedByPrototype'); 43893 return nodes; 43894 } : 43895 function(nodes) { 43896 for (var i = 0, node; node = nodes[i]; i++) 43897 node._countedByPrototype = void 0; 43898 return nodes; 43899 } 43900 })(), 43901 43902 index: function(parentNode, reverse, ofType) { 43903 parentNode._countedByPrototype = Prototype.emptyFunction; 43904 if (reverse) { 43905 for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) { 43906 var node = nodes[i]; 43907 if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++; 43908 } 43909 } else { 43910 for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++) 43911 if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++; 43912 } 43913 }, 43914 43915 unique: function(nodes) { 43916 if (nodes.length == 0) return nodes; 43917 var results = [], n; 43918 for (var i = 0, l = nodes.length; i < l; i++) 43919 if (typeof (n = nodes[i])._countedByPrototype == 'undefined') { 43920 n._countedByPrototype = Prototype.emptyFunction; 43921 results.push(Element.extend(n)); 43922 } 43923 return Selector.handlers.unmark(results); 43924 }, 43925 43926 descendant: function(nodes) { 43927 var h = Selector.handlers; 43928 for (var i = 0, results = [], node; node = nodes[i]; i++) 43929 h.concat(results, node.getElementsByTagName('*')); 43930 return results; 43931 }, 43932 43933 child: function(nodes) { 43934 var h = Selector.handlers; 43935 for (var i = 0, results = [], node; node = nodes[i]; i++) { 43936 for (var j = 0, child; child = node.childNodes[j]; j++) 43937 if (child.nodeType == 1 && child.tagName != '!') results.push(child); 43938 } 43939 return results; 43940 }, 43941 43942 adjacent: function(nodes) { 43943 for (var i = 0, results = [], node; node = nodes[i]; i++) { 43944 var next = this.nextElementSibling(node); 43945 if (next) results.push(next); 43946 } 43947 return results; 43948 }, 43949 43950 laterSibling: function(nodes) { 43951 var h = Selector.handlers; 43952 for (var i = 0, results = [], node; node = nodes[i]; i++) 43953 h.concat(results, Element.nextSiblings(node)); 43954 return results; 43955 }, 43956 43957 nextElementSibling: function(node) { 43958 while (node = node.nextSibling) 43959 if (node.nodeType == 1) return node; 43960 return null; 43961 }, 43962 43963 previousElementSibling: function(node) { 43964 while (node = node.previousSibling) 43965 if (node.nodeType == 1) return node; 43966 return null; 43967 }, 43968 43969 tagName: function(nodes, root, tagName, combinator) { 43970 var uTagName = tagName.toUpperCase(); 43971 var results = [], h = Selector.handlers; 43972 if (nodes) { 43973 if (combinator) { 43974 if (combinator == "descendant") { 43975 for (var i = 0, node; node = nodes[i]; i++) 43976 h.concat(results, node.getElementsByTagName(tagName)); 43977 return results; 43978 } else nodes = this[combinator](nodes); 43979 if (tagName == "*") return nodes; 43980 } 43981 for (var i = 0, node; node = nodes[i]; i++) 43982 if (node.tagName.toUpperCase() === uTagName) results.push(node); 43983 return results; 43984 } else return root.getElementsByTagName(tagName); 43985 }, 43986 43987 id: function(nodes, root, id, combinator) { 43988 var targetNode = $(id), h = Selector.handlers; 43989 43990 if (root == document) { 43991 if (!targetNode) return [];
43992 if (!nodes) return [targetNode]; 43993 } else { 43994 if (!root.sourceIndex || root.sourceIndex < 1) { 43995 var nodes = root.getElementsByTagName('*'); 43996 for (var j = 0, node; node = nodes[j]; j++) { 43997 if (node.id === id) return [node]; 43998 } 43999 } 44000 } 44001 44002 if (nodes) { 44003 if (combinator) { 44004 if (combinator == 'child') { 44005 for (var i = 0, node; node = nodes[i]; i++) 44006 if (targetNode.parentNode == node) return [targetNode]; 44007 } else if (combinator == 'descendant') { 44008 for (var i = 0, node; node = nodes[i]; i++) 44009 if (Element.descendantOf(targetNode, node)) return [targetNode]; 44010 } else if (combinator == 'adjacent') { 44011 for (var i = 0, node; node = nodes[i]; i++) 44012 if (Selector.handlers.previousElementSibling(targetNode) == node) 44013 return [targetNode]; 44014 } else nodes = h[combinator](nodes); 44015 } 44016 for (var i = 0, node; node = nodes[i]; i++) 44017 if (node == targetNode) return [targetNode]; 44018 return []; 44019 } 44020 return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : []; 44021 }, 44022 44023 className: function(nodes, root, className, combinator) { 44024 if (nodes && combinator) nodes = this[combinator](nodes); 44025 return Selector.handlers.byClassName(nodes, root, className); 44026 }, 44027 44028 byClassName: function(nodes, root, className) { 44029 if (!nodes) nodes = Selector.handlers.descendant([root]); 44030 var needle = ' ' + className + ' '; 44031 for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) { 44032 nodeClassName = node.className; 44033 if (nodeClassName.length == 0) continue; 44034 if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle)) 44035 results.push(node); 44036 } 44037 return results; 44038 }, 44039 44040 attrPresence: function(nodes, root, attr, combinator) { 44041 if (!nodes) nodes = root.getElementsByTagName("*"); 44042 if (nodes && combinator) nodes = this[combinator](nodes); 44043 var results = []; 44044 for (var i = 0, node; node = nodes[i]; i++) 44045 if (Element.hasAttribute(node, attr)) results.push(node); 44046 return results; 44047 }, 44048 44049 attr: function(nodes, root, attr, value, operator, combinator) { 44050 if (!nodes) nodes = root.getElementsByTagName("*"); 44051 if (nodes && combinator) nodes = this[combinator](nodes); 44052 var handler = Selector.operators[operator], results = []; 44053 for (var i = 0, node; node = nodes[i]; i++) { 44054 var nodeValue = Element.readAttribute(node, attr); 44055 if (nodeValue === null) continue; 44056 if (handler(nodeValue, value)) results.push(node); 44057 } 44058 return results; 44059 }, 44060 44061 pseudo: function(nodes, name, value, root, combinator) { 44062 if (nodes && combinator) nodes = this[combinator](nodes); 44063 if (!nodes) nodes = root.getElementsByTagName("*"); 44064 return Selector.pseudos[name](nodes, value, root); 44065 } 44066 }, 44067 44068 pseudos: { 44069 'first-child': function(nodes, value, root) { 44070 for (var i = 0, results = [], node; node = nodes[i]; i++) { 44071 if (Selector.handlers.previousElementSibling(node)) continue; 44072 results.push(node); 44073 } 44074 return results; 44075 }, 44076 'last-child': function(nodes, value, root) { 44077 for (var i = 0, results = [], node; node = nodes[i]; i++) { 44078 if (Selector.handlers.nextElementSibling(node)) continue; 44079 results.push(node); 44080 } 44081 return results; 44082 }, 44083 'only-child': function(nodes, value, root) { 44084 var h = Selector.handlers; 44085 for (var i = 0, results = [], node; node = nodes[i]; i++) 44086 if (!h.previousElementSibling(node) && !h.nextElementSibling(node)) 44087 results.push(node); 44088 return results; 44089 }, 44090 'nth-child': function(nodes, formula, root) { 44091 return Selector.pseudos.nth(nodes, formula, root); 44092 }, 44093 'nth-last-child': function(nodes, formula, root) { 44094 return Selector.pseudos.nth(nodes, formula, root, true); 44095 }, 44096 'nth-of-type': function(nodes, formula, root) { 44097 return Selector.pseudos.nth(nodes, formula, root, false, true); 44098 }, 44099 'nth-last-of-type': function(nodes, formula, root) { 44100 return Selector.pseudos.nth(nodes, formula, root, true, true); 44101 }, 44102 'first-of-type': function(nodes, formula, root) { 44103 return Selector.pseudos.nth(nodes, "1", root, false, true); 44104 }, 44105 'last-of-type': function(nodes, formula, root) { 44106 return Selector.pseudos.nth(nodes, "1", root, true, true); 44107 }, 44108 'only-of-type': function(nodes, formula, root) { 44109 var p = Selector.pseudos;
44110 return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root); 44111 }, 44112 44113 getIndices: function(a, b, total) { 44114 if (a == 0) return b > 0 ? [b] : []; 44115 return $R(1, total).inject([], function(memo, i) { 44116 if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i); 44117 return memo; 44118 }); 44119 }, 44120 44121 nth: function(nodes, formula, root, reverse, ofType) { 44122 if (nodes.length == 0) return []; 44123 if (formula == 'even') formula = '2n+0'; 44124 if (formula == 'odd') formula = '2n+1'; 44125 var h = Selector.handlers, results = [], indexed = [], m; 44126 h.mark(nodes); 44127 for (var i = 0, node; node = nodes[i]; i++) { 44128 if (!node.parentNode._countedByPrototype) { 44129 h.index(node.parentNode, reverse, ofType); 44130 indexed.push(node.parentNode); 44131 } 44132 } 44133 if (formula.match(/^\d+$/)) { // just a number 44134 formula = Number(formula); 44135 for (var i = 0, node; node = nodes[i]; i++) 44136 if (node.nodeIndex == formula) results.push(node); 44137 } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b 44138 if (m[1] == "-") m[1] = -1; 44139 var a = m[1] ? Number(m[1]) : 1; 44140 var b = m[2] ? Number(m[2]) : 0; 44141 var indices = Selector.pseudos.getIndices(a, b, nodes.length); 44142 for (var i = 0, node, l = indices.length; node = nodes[i]; i++) { 44143 for (var j = 0; j < l; j++) 44144 if (node.nodeIndex == indices[j]) results.push(node); 44145 } 44146 } 44147 h.unmark(nodes); 44148 h.unmark(indexed); 44149 return results; 44150 }, 44151 44152 'empty': function(nodes, value, root) { 44153 for (var i = 0, results = [], node; node = nodes[i]; i++) { 44154 if (node.tagName == '!' || node.firstChild) continue; 44155 results.push(node); 44156 } 44157 return results; 44158 }, 44159 44160 'not': function(nodes, selector, root) { 44161 var h = Selector.handlers, selectorType, m; 44162 var exclusions = new Selector(selector).findElements(root); 44163 h.mark(exclusions); 44164 for (var i = 0, results = [], node; node = nodes[i]; i++) 44165 if (!node._countedByPrototype) results.push(node); 44166 h.unmark(exclusions); 44167 return results; 44168 }, 44169 44170 'enabled': function(nodes, value, root) { 44171 for (var i = 0, results = [], node; node = nodes[i]; i++) 44172 if (!node.disabled && (!node.type || node.type !== 'hidden')) 44173 results.push(node); 44174 return results; 44175 }, 44176 44177 'disabled': function(nodes, value, root) { 44178 for (var i = 0, results = [], node; node = nodes[i]; i++) 44179 if (node.disabled) results.push(node); 44180 return results; 44181 }, 44182 44183 'checked': function(nodes, value, root) { 44184 for (var i = 0, results = [], node; node = nodes[i]; i++) 44185 if (node.checked) results.push(node); 44186 return results; 44187 } 44188 }, 44189 44190 operators: { 44191 '=': function(nv, v) { return nv == v; }, 44192 '!=': function(nv, v) { return nv != v; }, 44193 '^=': function(nv, v) { return nv == v || nv && nv.startsWith(v); }, 44194 '$=': function(nv, v) { return nv == v || nv && nv.endsWith(v); }, 44195 '*=': function(nv, v) { return nv == v || nv && nv.include(v); }, 44196 '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); }, 44197 '|=': function(nv, v) { return ('-' + (nv || "").toUpperCase() + 44198 '-').include('-' + (v || "").toUpperCase() + '-'); } 44199 }, 44200 44201 split: function(expression) { 44202 var expressions = []; 44203 expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) { 44204 expressions.push(m[1].strip()); 44205 }); 44206 return expressions; 44207 }, 44208 44209 matchElements: function(elements, expression) { 44210 var matches = $$(expression), h = Selector.handlers; 44211 h.mark(matches); 44212 for (var i = 0, results = [], element; element = elements[i]; i++) 44213 if (element._countedByPrototype) results.push(element); 44214 h.unmark(matches); 44215 return results; 44216 }, 44217 44218 findElement: function(elements, expression, index) { 44219 if (Object.isNumber(expression)) { 44220 index = expression; expression = false; 44221 } 44222 return Selector.matchElements(elements, expression || '*')[index || 0]; 44223 }, 44224 44225 findChildElements: function(element, expressions) { 44226 expressions = Selector.split(expressions.join(',')); 44227 var results = [], h = Selector.handlers; 44228 for (var i = 0, l = expressions.length, selector; i < l; i++) { 44229 selector = new Selector(expressions[i].strip()); 44230 h.concat(results, selector.findElements(element)); 44231 } 44232 return (l > 1) ? h.unique(results) : results; 44233 } 44234}); 44235 44236if (Prototype.Browser.IE) { 44237 Object.extend(Selector.handlers, { 44238 concat: function(a, b) { 44239 for (var i = 0, node; node = b[i]; i++) 44240 if (node.tagName !== "!") a.push(node); 44241 return a; 44242 } 44243 }); 44244} 44245 44246function $$() { 44247 return Selector.findChildElements(document, $A(arguments)); 44248} 44249 44250var Form = { 44251 reset: function(form) { 44252 form = $(form); 44253 form.reset(); 44254 return form; 44255 }, 44256
44257 serializeElements: function(elements, options) { 44258 if (typeof options != 'object') options = { hash: !!options }; 44259 else if (Object.isUndefined(options.hash)) options.hash = true; 44260 var key, value, submitted = false, submit = options.submit; 44261 44262 var data = elements.inject({ }, function(result, element) { 44263 if (!element.disabled && element.name) { 44264 key = element.name; value = $(element).getValue(); 44265 if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted && 44266 submit !== false && (!submit || key == submit) && (submitted = true)))) { 44267 if (key in result) { 44268 if (!Object.isArray(result[key])) result[key] = [result[key]]; 44269 result[key].push(value); 44270 } 44271 else result[key] = value; 44272 } 44273 } 44274 return result; 44275 }); 44276 44277 return options.hash ? data : Object.toQueryString(data); 44278 } 44279}; 44280 44281Form.Methods = { 44282 serialize: function(form, options) { 44283 return Form.serializeElements(Form.getElements(form), options); 44284 }, 44285 44286 getElements: function(form) { 44287 var elements = $(form).getElementsByTagName('*'), 44288 element, 44289 arr = [ ], 44290 serializers = Form.Element.Serializers; 44291 for (var i = 0; element = elements[i]; i++) { 44292 arr.push(element); 44293 } 44294 return arr.inject([], function(elements, child) { 44295 if (serializers[child.tagName.toLowerCase()]) 44296 elements.push(Element.extend(child)); 44297 return elements; 44298 }) 44299 }, 44300 44301 getInputs: function(form, typeName, name) { 44302 form = $(form); 44303 var inputs = form.getElementsByTagName('input'); 44304 44305 if (!typeName && !name) return $A(inputs).map(Element.extend); 44306 44307 for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) { 44308 var input = inputs[i]; 44309 if ((typeName && input.type != typeName) || (name && input.name != name)) 44310 continue; 44311 matchingInputs.push(Element.extend(input)); 44312 } 44313 44314 return matchingInputs; 44315 }, 44316 44317 disable: function(form) { 44318 form = $(form); 44319 Form.getElements(form).invoke('disable'); 44320 return form; 44321 }, 44322 44323 enable: function(form) { 44324 form = $(form); 44325 Form.getElements(form).invoke('enable'); 44326 return form; 44327 }, 44328 44329 findFirstElement: function(form) { 44330 var elements = $(form).getElements().findAll(function(element) { 44331 return 'hidden' != element.type && !element.disabled; 44332 }); 44333 var firstByIndex = elements.findAll(function(element) { 44334 return element.hasAttribute('tabIndex') && element.tabIndex >= 0; 44335 }).sortBy(function(element) { return element.tabIndex }).first(); 44336 44337 return firstByIndex ? firstByIndex : elements.find(function(element) { 44338 return /^(?:input|select|textarea)$/i.test(element.tagName); 44339 }); 44340 }, 44341 44342 focusFirstElement: function(form) { 44343 form = $(form); 44344 form.findFirstElement().activate(); 44345 return form; 44346 }, 44347 44348 request: function(form, options) { 44349 form = $(form), options = Object.clone(options || { }); 44350 44351 var params = options.parameters, action = form.readAttribute('action') || ''; 44352 if (action.blank()) action = window.location.href; 44353 options.parameters = form.serialize(true); 44354 44355 if (params) { 44356 if (Object.isString(params)) params = params.toQueryParams(); 44357 Object.extend(options.parameters, params); 44358 } 44359 44360 if (form.hasAttribute('method') && !options.method) 44361 options.method = form.method; 44362 44363 return new Ajax.Request(action, options); 44364 } 44365}; 44366 44367/*--------------------------------------------------------------------------*/ 44368 44369 44370Form.Element = { 44371 focus: function(element) { 44372 $(element).focus(); 44373 return element; 44374 }, 44375 44376 select: function(element) { 44377 $(element).select(); 44378 return element; 44379 } 44380}; 44381 44382Form.Element.Methods = { 44383 44384 serialize: function(element) { 44385 element = $(element); 44386 if (!element.disabled && element.name) { 44387 var value = element.getValue(); 44388 if (value != undefined) { 44389 var pair = { }; 44390 pair[element.name] = value; 44391 return Object.toQueryString(pair); 44392 } 44393 } 44394 return ''; 44395 }, 44396 44397 getValue: function(element) { 44398 element = $(element); 44399 var method = element.tagName.toLowerCase(); 44400 return Form.Element.Serializers[method](element); 44401 }, 44402 44403 setValue: function(element, value) { 44404 element = $(element); 44405 var method = element.tagName.toLowerCase(); 44406 Form.Element.Serializers[method](element, value); 44407 return element; 44408 }, 44409 44410 clear: function(element) { 44411 $(element).value = ''; 44412 return element; 44413 }, 44414 44415 present: function(element) { 44416 return $(element).value != ''; 44417 }, 44418 44419 activate: function(element) { 44420 element = $(element); 44421 try { 44422 element.focus(); 44423 if (element.select && (element.tagName.toLowerCase() != 'input' || 44424 !(/^(?:button|reset|submit)$/i.test(element.type)))) 44425 element.select(); 44426 } catch (e) { } 44427 return element; 44428 }, 44429 44430 disable: function(element) { 44431 element = $(element); 44432 element.disabled = true; 44433 return element; 44434 }, 44435 44436 enable: function(element) { 44437 element = $(element); 44438 element.disabled = false;
44439 return element; 44440 } 44441}; 44442 44443/*--------------------------------------------------------------------------*/ 44444 44445var Field = Form.Element; 44446 44447var $F = Form.Element.Methods.getValue; 44448 44449/*--------------------------------------------------------------------------*/ 44450 44451Form.Element.Serializers = { 44452 input: function(element, value) { 44453 switch (element.type.toLowerCase()) { 44454 case 'checkbox': 44455 case 'radio': 44456 return Form.Element.Serializers.inputSelector(element, value); 44457 default: 44458 return Form.Element.Serializers.textarea(element, value); 44459 } 44460 }, 44461 44462 inputSelector: function(element, value) { 44463 if (Object.isUndefined(value)) return element.checked ? element.value : null; 44464 else element.checked = !!value; 44465 }, 44466 44467 textarea: function(element, value) { 44468 if (Object.isUndefined(value)) return element.value; 44469 else element.value = value; 44470 }, 44471 44472 select: function(element, value) { 44473 if (Object.isUndefined(value)) 44474 return this[element.type == 'select-one' ? 44475 'selectOne' : 'selectMany'](element); 44476 else { 44477 var opt, currentValue, single = !Object.isArray(value); 44478 for (var i = 0, length = element.length; i < length; i++) { 44479 opt = element.options[i]; 44480 currentValue = this.optionValue(opt); 44481 if (single) { 44482 if (currentValue == value) { 44483 opt.selected = true; 44484 return; 44485 } 44486 } 44487 else opt.selected = value.include(currentValue); 44488 } 44489 } 44490 }, 44491 44492 selectOne: function(element) { 44493 var index = element.selectedIndex; 44494 return index >= 0 ? this.optionValue(element.options[index]) : null; 44495 }, 44496 44497 selectMany: function(element) { 44498 var values, length = element.length; 44499 if (!length) return null; 44500 44501 for (var i = 0, values = []; i < length; i++) { 44502 var opt = element.options[i]; 44503 if (opt.selected) values.push(this.optionValue(opt)); 44504 } 44505 return values; 44506 }, 44507 44508 optionValue: function(opt) { 44509 return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text; 44510 } 44511}; 44512 44513/*--------------------------------------------------------------------------*/ 44514 44515 44516Abstract.TimedObserver = Class.create(PeriodicalExecuter, { 44517 initialize: function($super, element, frequency, callback) { 44518 $super(callback, frequency); 44519 this.element = $(element); 44520 this.lastValue = this.getValue(); 44521 }, 44522 44523 execute: function() { 44524 var value = this.getValue(); 44525 if (Object.isString(this.lastValue) && Object.isString(value) ? 44526 this.lastValue != value : String(this.lastValue) != String(value)) { 44527 this.callback(this.element, value); 44528 this.lastValue = value; 44529 } 44530 } 44531}); 44532 44533Form.Element.Observer = Class.create(Abstract.TimedObserver, { 44534 getValue: function() { 44535 return Form.Element.getValue(this.element); 44536 } 44537}); 44538 44539Form.Observer = Class.create(Abstract.TimedObserver, { 44540 getValue: function() { 44541 return Form.serialize(this.element); 44542 } 44543}); 44544 44545/*--------------------------------------------------------------------------*/ 44546 44547Abstract.EventObserver = Class.create({ 44548 initialize: function(element, callback) { 44549 this.element = $(element); 44550 this.callback = callback; 44551 44552 this.lastValue = this.getValue(); 44553 if (this.element.tagName.toLowerCase() == 'form') 44554 this.registerFormCallbacks(); 44555 else 44556 this.registerCallback(this.element); 44557 }, 44558 44559 onElementEvent: function() { 44560 var value = this.getValue(); 44561 if (this.lastValue != value) { 44562 this.callback(this.element, value); 44563 this.lastValue = value; 44564 } 44565 }, 44566 44567 registerFormCallbacks: function() { 44568 Form.getElements(this.element).each(this.registerCallback, this); 44569 }, 44570 44571 registerCallback: function(element) { 44572 if (element.type) { 44573 switch (element.type.toLowerCase()) { 44574 case 'checkbox': 44575 case 'radio': 44576 Event.observe(element, 'click', this.onElementEvent.bind(this)); 44577 break; 44578 default: 44579 Event.observe(element, 'change', this.onElementEvent.bind(this)); 44580 break; 44581 } 44582 } 44583 } 44584}); 44585 44586Form.Element.EventObserver = Class.create(Abstract.EventObserver, { 44587 getValue: function() { 44588 return Form.Element.getValue(this.element); 44589 } 44590}); 44591 44592Form.EventObserver = Class.create(Abstract.EventObserver, { 44593 getValue: function() { 44594 return Form.serialize(this.element); 44595 } 44596}); 44597(function() { 44598 44599 var Event = { 44600 KEY_BACKSPACE: 8, 44601 KEY_TAB: 9, 44602 KEY_RETURN: 13, 44603 KEY_ESC: 27, 44604 KEY_LEFT: 37, 44605 KEY_UP: 38, 44606 KEY_RIGHT: 39, 44607 KEY_DOWN: 40, 44608 KEY_DELETE: 46, 44609 KEY_HOME: 36, 44610 KEY_END: 35, 44611 KEY_PAGEUP: 33, 44612 KEY_PAGEDOWN: 34, 44613 KEY_INSERT: 45, 44614 44615 cache: {} 44616 }; 44617 44618 var docEl = document.documentElement; 44619 var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl 44620 && 'onmouseleave' in docEl; 44621 44622 var _isButton; 44623 if (Prototype.Browser.IE) { 44624 var buttonMap = { 0: 1, 1: 4, 2: 2 }; 44625 _isButton = function(event, code) { 44626 return event.button === buttonMap[code]; 44627 }; 44628 } else if (Prototype.Browser.WebKit) { 44629 _isButton = function(event, code) { 44630 switch (code) { 44631 case 0: return event.which == 1 && !event.metaKey; 44632 case 1: return event.which == 1 && event.metaKey; 44633 default: return false;
44634 } 44635 }; 44636 } else { 44637 _isButton = function(event, code) { 44638 return event.which ? (event.which === code + 1) : (event.button === code); 44639 }; 44640 } 44641 44642 function isLeftClick(event) { return _isButton(event, 0) } 44643 44644 function isMiddleClick(event) { return _isButton(event, 1) } 44645 44646 function isRightClick(event) { return _isButton(event, 2) } 44647 44648 function element(event) { 44649 event = Event.extend(event); 44650 44651 var node = event.target, type = event.type, 44652 currentTarget = event.currentTarget; 44653 44654 if (currentTarget && currentTarget.tagName) { 44655 if (type === 'load' || type === 'error' || 44656 (type === 'click' && currentTarget.tagName.toLowerCase() === 'input' 44657 && currentTarget.type === 'radio')) 44658 node = currentTarget; 44659 } 44660 44661 if (node.nodeType == Node.TEXT_NODE) 44662 node = node.parentNode; 44663 44664 return Element.extend(node); 44665 } 44666 44667 function findElement(event, expression) { 44668 var element = Event.element(event); 44669 if (!expression) return element; 44670 var elements = [element].concat(element.ancestors()); 44671 return Selector.findElement(elements, expression, 0); 44672 } 44673 44674 function pointer(event) { 44675 return { x: pointerX(event), y: pointerY(event) }; 44676 } 44677 44678 function pointerX(event) { 44679 var docElement = document.documentElement, 44680 body = document.body || { scrollLeft: 0 }; 44681 44682 return event.pageX || (event.clientX + 44683 (docElement.scrollLeft || body.scrollLeft) - 44684 (docElement.clientLeft || 0)); 44685 } 44686 44687 function pointerY(event) { 44688 var docElement = document.documentElement, 44689 body = document.body || { scrollTop: 0 }; 44690 44691 return event.pageY || (event.clientY + 44692 (docElement.scrollTop || body.scrollTop) - 44693 (docElement.clientTop || 0)); 44694 } 44695 44696 44697 function stop(event) { 44698 Event.extend(event); 44699 event.preventDefault(); 44700 event.stopPropagation(); 44701 44702 event.stopped = true; 44703 } 44704 44705 Event.Methods = { 44706 isLeftClick: isLeftClick, 44707 isMiddleClick: isMiddleClick, 44708 isRightClick: isRightClick, 44709 44710 element: element, 44711 findElement: findElement, 44712 44713 pointer: pointer, 44714 pointerX: pointerX, 44715 pointerY: pointerY, 44716 44717 stop: stop 44718 }; 44719 44720 44721 var methods = Object.keys(Event.Methods).inject({ }, function(m, name) { 44722 m[name] = Event.Methods[name].methodize(); 44723 return m; 44724 }); 44725 44726 if (Prototype.Browser.IE) { 44727 function _relatedTarget(event) { 44728 var element; 44729 switch (event.type) { 44730 case 'mouseover': element = event.fromElement; break; 44731 case 'mouseout': element = event.toElement; break; 44732 default: return null; 44733 } 44734 return Element.extend(element); 44735 } 44736 44737/* 44738 Object.extend(methods, { 44739 stopPropagation: function() { this.cancelBubble = true }, 44740 preventDefault: function() { this.returnValue = false }, 44741 inspect: function() { return '[object Event]' } 44742 }); 44743*/ 44744 // davidf: patch to fix IE9 drag and drop. 44745 // this is a mix of the patches found here: 44746 // http://mandagreen.com/prototype-1-6-event-stop-ie9-quick-patch/ 44747 // and here: 44748 // https://prototype.lighthouseapp.com/projects/8886/tickets/1142-ie9-event-bug 44749 var isLegacyEvent = function(event) { return false; }; 44750 44751 if (window.attachEvent) { 44752 if (window.addEventListener) { 44753 isLegacyEvent = function(event) { 44754 return !(event instanceof window.Event); 44755 }; 44756 } else { 44757 isLegacyEvent = function(event) { return true; }; 44758 } 44759 } 44760 44761 var additionalMethods = { 44762 stopPropagation: function() { this.cancelBubble = true }, 44763 preventDefault: function() { this.returnValue = false }, 44764 inspect: function() { return '[object Event]' } 44765 } 44766 44767 Event.extend = function(event, element) { 44768 if (!event) return false; 44769 if (event._extendedByPrototype) return event; 44770 44771 event._extendedByPrototype = Prototype.emptyFunction; 44772 var pointer = Event.pointer(event); 44773 44774 Object.extend(event, { 44775 target: event.srcElement || element, 44776 relatedTarget: _relatedTarget(event), 44777 pageX: pointer.x, 44778 pageY: pointer.y 44779 }); 44780 44781 if (isLegacyEvent(event)) { 44782 Object.extend(event, additionalMethods); 44783 } 44784 44785 return Object.extend(event, methods); 44786 }; 44787 } else { 44788 Event.prototype = window.Event.prototype || document.createEvent('HTMLEvents').__proto__; 44789 Object.extend(Event.prototype, methods); 44790 Event.extend = Prototype.K; 44791 } 44792 44793 function _createResponder(element, eventName, handler) { 44794 var registry = Element.retrieve(element, 'prototype_event_registry'); 44795 44796 if (Object.isUndefined(registry)) { 44797 CACHE.push(element); 44798 registry = Element.retrieve(element, 'prototype_event_registry', $H()); 44799 } 44800 44801 var respondersForEvent = registry.get(eventName); 44802 if (Object.isUndefined(respondersForEvent)) { 44803 respondersForEvent = []; 44804 registry.set(eventName, respondersForEvent); 44805 } 44806 44807 if (respondersForEvent.pluck('handler').include(handler)) return false;
44808 44809 var responder; 44810 if (eventName.include(":")) { 44811 responder = function(event) { 44812 if (Object.isUndefined(event.eventName)) 44813 return false; 44814 44815 if (event.eventName !== eventName) 44816 return false; 44817 44818 Event.extend(event, element); 44819 handler.call(element, event); 44820 }; 44821 } else { 44822 if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED && 44823 (eventName === "mouseenter" || eventName === "mouseleave")) { 44824 if (eventName === "mouseenter" || eventName === "mouseleave") { 44825 responder = function(event) { 44826 Event.extend(event, element); 44827 44828 var parent = event.relatedTarget; 44829 while (parent && parent !== element) { 44830 try { parent = parent.parentNode; } 44831 catch(e) { parent = element; } 44832 } 44833 44834 if (parent === element) return; 44835 44836 handler.call(element, event); 44837 }; 44838 } 44839 } else { 44840 responder = function(event) { 44841 Event.extend(event, element); 44842 handler.call(element, event); 44843 }; 44844 } 44845 } 44846 44847 responder.handler = handler; 44848 respondersForEvent.push(responder); 44849 return responder; 44850 } 44851 44852 function _destroyCache() { 44853 for (var i = 0, length = CACHE.length; i < length; i++) { 44854 Event.stopObserving(CACHE[i]); 44855 CACHE[i] = null; 44856 } 44857 } 44858 44859 var CACHE = []; 44860 44861 if (Prototype.Browser.IE) 44862 window.attachEvent('onunload', _destroyCache); 44863 44864 if (Prototype.Browser.WebKit) 44865 window.addEventListener('unload', Prototype.emptyFunction, false); 44866 44867 44868 var _getDOMEventName = Prototype.K; 44869 44870 if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) { 44871 _getDOMEventName = function(eventName) { 44872 var translations = { mouseenter: "mouseover", mouseleave: "mouseout" }; 44873 return eventName in translations ? translations[eventName] : eventName; 44874 }; 44875 } 44876 44877 function observe(element, eventName, handler) { 44878 element = $(element); 44879 44880 var responder = _createResponder(element, eventName, handler); 44881 44882 if (!responder) return element; 44883 44884 if (eventName.include(':')) { 44885 if (element.addEventListener) 44886 element.addEventListener("dataavailable", responder, false); 44887 else { 44888 element.attachEvent("ondataavailable", responder); 44889 element.attachEvent("onfilterchange", responder); 44890 } 44891 } else { 44892 var actualEventName = _getDOMEventName(eventName); 44893 44894 if (element.addEventListener) 44895 element.addEventListener(actualEventName, responder, false); 44896 else 44897 element.attachEvent("on" + actualEventName, responder); 44898 } 44899 44900 return element; 44901 } 44902 44903 function stopObserving(element, eventName, handler) { 44904 element = $(element); 44905 44906 var registry = Element.retrieve(element, 'prototype_event_registry'); 44907 44908 if (Object.isUndefined(registry)) return element; 44909 44910 if (eventName && !handler) { 44911 var responders = registry.get(eventName); 44912 44913 if (Object.isUndefined(responders)) return element; 44914 44915 responders.each( function(r) { 44916 Element.stopObserving(element, eventName, r.handler); 44917 }); 44918 return element; 44919 } else if (!eventName) { 44920 registry.each( function(pair) { 44921 var eventName = pair.key, responders = pair.value; 44922 44923 responders.each( function(r) { 44924 Element.stopObserving(element, eventName, r.handler); 44925 }); 44926 }); 44927 return element; 44928 } 44929 44930 var responders = registry.get(eventName); 44931 44932 if (!responders) return; 44933 44934 var responder = responders.find( function(r) { return r.handler === handler; }); 44935 if (!responder) return element; 44936 44937 var actualEventName = _getDOMEventName(eventName); 44938 44939 if (eventName.include(':')) { 44940 if (element.removeEventListener) 44941 element.removeEventListener("dataavailable", responder, false); 44942 else { 44943 element.detachEvent("ondataavailable", responder); 44944 element.detachEvent("onfilterchange", responder); 44945 } 44946 } else { 44947 if (element.removeEventListener) 44948 element.removeEventListener(actualEventName, responder, false); 44949 else 44950 element.detachEvent('on' + actualEventName, responder); 44951 } 44952 44953 registry.set(eventName, responders.without(responder)); 44954 44955 return element; 44956 } 44957 44958 function fire(element, eventName, memo, bubble) { 44959 element = $(element); 44960 44961 if (Object.isUndefined(bubble)) 44962 bubble = true; 44963 44964 if (element == document && document.createEvent && !element.dispatchEvent) 44965 element = document.documentElement; 44966 44967 var event; 44968 if (document.createEvent) { 44969 event = document.createEvent('HTMLEvents'); 44970 event.initEvent('dataavailable', true, true); 44971 } else { 44972 event = document.createEventObject(); 44973 event.eventType = bubble ? 'ondataavailable' : 'onfilterchange'; 44974 } 44975 44976 event.eventName = eventName; 44977 event.memo = memo || { }; 44978 44979 if (document.createEvent) 44980 element.dispatchEvent(event); 44981 else 44982 element.fireEvent(event.eventType, event); 44983 44984 return Event.extend(event); 44985 } 44986 44987 44988 Object.extend(Event, Event.Methods); 44989 44990 Object.extend(Event, { 44991 fire: fire, 44992 observe: observe, 44993 stopObserving: stopObserving 44994 }); 44995 44996 Element.addMethods({ 44997 fire: fire, 44998 44999 observe: observe, 45000 45001 stopObserving: stopObserving 45002 }); 45003 45004 Object.extend(document, { 45005 fire: fire.methodize(), 45006 45007 observe: observe.methodize(), 45008 45009 stopObserving: stopObserving.methodize(), 45010 45011 loaded: false 45012 }); 45013 45014 if (window.Event) Object.extend(window.Event, Event); 45015 else window.Event = Event; 45016})(); 45017 45018(function() { 45019 /* Support for the DOMContentLoaded event is based on work by Dan Webb, 45020 Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */ 45021 45022 var timer; 45023 45024 function fireContentLoadedEvent() { 45025 if (document.loaded) return; 45026 if (timer) window.clearTimeout(timer); 45027 document.loaded = true; 45028 document.fire('dom:loaded'); 45029 } 45030 45031 function checkReadyState() { 45032 if (document.readyState === 'complete') { 45033 document.stopObserving('readystatechange', checkReadyState); 45034 fireContentLoadedEvent(); 45035 } 45036 } 45037 45038 function pollDoScroll() { 45039 try { document.documentElement.doScroll('left'); } 45040 catch(e) { 45041 timer = pollDoScroll.defer(); 45042 return; 45043 } 45044 fireContentLoadedEvent(); 45045 } 45046 45047 if (document.addEventListener) { 45048 document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false); 45049 } else { 45050 document.observe('readystatechange', checkReadyState); 45051 if (window == top) 45052 timer = pollDoScroll.defer(); 45053 } 45054 45055 Event.observe(window, 'load', fireContentLoadedEvent); 45056})(); 45057 45058Element.addMethods(); 45059 45060/*------------------------------- DEPRECATED -------------------------------*/ 45061 45062Hash.toQueryString = Object.toQueryString; 45063 45064var Toggle = { display: Element.toggle }; 45065 45066Element.Methods.childOf = Element.Methods.descendantOf; 45067 45068var Insertion = { 45069 Before: function(element, content) { 45070 return Element.insert(element, {before:content}); 45071 }, 45072 45073 Top: function(element, content) { 45074 return Element.insert(element, {top:content}); 45075 }, 45076 45077 Bottom: function(element, content) { 45078 return Element.insert(element, {bottom:content}); 45079 }, 45080 45081 After: function(element, content) { 45082 return Element.insert(element, {after:content}); 45083 } 45084}; 45085 45086var $continue = new Error('"throw $continue" is deprecated, use "return" instead'); 45087 45088var Position = { 45089 includeScrollOffsets: false, 45090 45091 prepare: function() { 45092 this.deltaX = window.pageXOffset 45093 || document.documentElement.scrollLeft 45094 || document.body.scrollLeft 45095 || 0; 45096 this.deltaY = window.pageYOffset 45097 || document.documentElement.scrollTop 45098 || document.body.scrollTop 45099 || 0; 45100 }, 45101 45102 within: function(element, x, y) { 45103 if (this.includeScrollOffsets) 45104 return this.withinIncludingScrolloffsets(element, x, y); 45105 this.xcomp = x; 45106 this.ycomp = y; 45107 this.offset = Element.cumulativeOffset(element); 45108 45109 return (y >= this.offset[1] && 45110 y < this.offset[1] + element.offsetHeight && 45111 x >= this.offset[0] && 45112 x < this.offset[0] + element.offsetWidth); 45113 }, 45114 45115 withinIncludingScrolloffsets: function(element, x, y) { 45116 var offsetcache = Element.cumulativeScrollOffset(element); 45117 45118 this.xcomp = x + offsetcache[0] - this.deltaX; 45119 this.ycomp = y + offsetcache[1] - this.deltaY; 45120 this.offset = Element.cumulativeOffset(element); 45121 45122 return (this.ycomp >= this.offset[1] && 45123 this.ycomp < this.offset[1] + element.offsetHeight && 45124 this.xcomp >= this.offset[0] && 45125 this.xcomp < this.offset[0] + element.offsetWidth); 45126 }, 45127 45128 overlap: function(mode, element) { 45129 if (!mode) return 0; 45130 if (mode == 'vertical') 45131 return ((this.offset[1] + element.offsetHeight) - this.ycomp) / 45132 element.offsetHeight; 45133 if (mode == 'horizontal') 45134 return ((this.offset[0] + element.offsetWidth) - this.xcomp) / 45135 element.offsetWidth; 45136 }, 45137 45138 45139 cumulativeOffset: Element.Methods.cumulativeOffset, 45140 45141 positionedOffset: Element.Methods.positionedOffset, 45142 45143 absolutize: function(element) { 45144 Position.prepare(); 45145 return Element.absolutize(element); 45146 }, 45147 45148 relativize: function(element) { 45149 Position.prepare(); 45150 return Element.relativize(element); 45151 }, 45152 45153 realOffset: Element.Methods.cumulativeScrollOffset, 45154 45155 offsetParent: Element.Methods.getOffsetParent, 45156 45157 page: Element.Methods.viewportOffset, 45158 45159 clone: function(source, target, options) { 45160 options = options || { }; 45161 return Element.clonePosition(target, source, options); 45162 } 45163}; 45164 45165/*--------------------------------------------------------------------------*/ 45166 45167if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){ 45168 function iter(name) { 45169 return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]"; 45170 } 45171 45172 instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ? 45173 function(element, className) {
45174 className = className.toString().strip(); 45175 var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className); 45176 return cond ? document._getElementsByXPath('.//*' + cond, element) : []; 45177 } : function(element, className) { 45178 className = className.toString().strip(); 45179 var elements = [], classNames = (/\s/.test(className) ? $w(className) : null); 45180 if (!classNames && !className) return elements; 45181 45182 var nodes = $(element).getElementsByTagName('*'); 45183 className = ' ' + className + ' '; 45184 45185 for (var i = 0, child, cn; child = nodes[i]; i++) { 45186 if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) || 45187 (classNames && classNames.all(function(name) { 45188 return !name.toString().blank() && cn.include(' ' + name + ' '); 45189 })))) 45190 elements.push(Element.extend(child)); 45191 } 45192 return elements; 45193 }; 45194 45195 return function(className, parentElement) { 45196 return $(parentElement || document.body).getElementsByClassName(className); 45197 }; 45198}(Element.Methods); 45199 45200/*--------------------------------------------------------------------------*/ 45201 45202Element.ClassNames = Class.create(); 45203Element.ClassNames.prototype = { 45204 initialize: function(element) { 45205 this.element = $(element); 45206 }, 45207 45208 _each: function(iterator) { 45209 this.element.className.split(/\s+/).select(function(name) { 45210 return name.length > 0; 45211 })._each(iterator); 45212 }, 45213 45214 set: function(className) { 45215 this.element.className = className; 45216 }, 45217 45218 add: function(classNameToAdd) { 45219 if (this.include(classNameToAdd)) return; 45220 this.set($A(this).concat(classNameToAdd).join(' ')); 45221 }, 45222 45223 remove: function(classNameToRemove) { 45224 if (!this.include(classNameToRemove)) return; 45225 this.set($A(this).without(classNameToRemove).join(' ')); 45226 }, 45227 45228 toString: function() { 45229 return $A(this).join(' '); 45230 } 45231}; 45232 45233Object.extend(Element.ClassNames.prototype, Enumerable); 45234 45235/*--------------------------------------------------------------------------*/ 45236; 45237// script.aculo.us effects.js v1.8.3, Thu Oct 08 11:23:33 +0200 2009 45238 45239// Copyright (c) 2005-2009 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) 45240// Contributors: 45241// Justin Palmer (http://encytemedia.com/) 45242// Mark Pilgrim (http://diveintomark.org/) 45243// Martin Bialasinki 45244// 45245// script.aculo.us is freely distributable under the terms of an MIT-style license. 45246// For details, see the script.aculo.us web site: http://script.aculo.us/ 45247 45248// converts rgb() and #xxx to #xxxxxx format, 45249// returns self (or first argument) if not convertable 45250String.prototype.parseColor = function() { 45251 var color = '#'; 45252 if (this.slice(0,4) == 'rgb(') { 45253 var cols = this.slice(4,this.length-1).split(','); 45254 var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3); 45255 } else { 45256 if (this.slice(0,1) == '#') { 45257 if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase(); 45258 if (this.length==7) color = this.toLowerCase(); 45259 } 45260 } 45261 return (color.length==7 ? color : (arguments[0] || this)); 45262}; 45263 45264/*--------------------------------------------------------------------------*/ 45265 45266Element.collectTextNodes = function(element) { 45267 return $A($(element).childNodes).collect( function(node) { 45268 return (node.nodeType==3 ? node.nodeValue : 45269 (node.hasChildNodes() ? Element.collectTextNodes(node) : '')); 45270 }).flatten().join(''); 45271}; 45272 45273Element.collectTextNodesIgnoreClass = function(element, className) { 45274 return $A($(element).childNodes).collect( function(node) { 45275 return (node.nodeType==3 ? node.nodeValue : 45276 ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? 45277 Element.collectTextNodesIgnoreClass(node, className) : '')); 45278 }).flatten().join(''); 45279}; 45280 45281Element.setContentZoom = function(element, percent) { 45282 element = $(element); 45283 element.setStyle({fontSize: (percent/100) + 'em'}); 45284 if (Prototype.Browser.WebKit) window.scrollBy(0,0); 45285 return element; 45286}; 45287 45288Element.getInlineOpacity = function(element){ 45289 return $(element).style.opacity || ''; 45290}; 45291 45292Element.forceRerendering = function(element) { 45293 try { 45294 element = $(element); 45295 var n = document.createTextNode(' '); 45296 element.appendChild(n); 45297 element.removeChild(n); 45298 } catch(e) { } 45299}; 45300 45301/*--------------------------------------------------------------------------*/ 45302 45303var Effect = { 45304 _elementDoesNotExistError: { 45305 name: 'ElementDoesNotExistError', 45306 message: 'The specified DOM element does not exist, but is required for this effect to operate' 45307 }, 45308 Transitions: { 45309 linear: Prototype.K, 45310 sinoidal: function(pos) { 45311 return (-Math.cos(pos*Math.PI)/2) + .5; 45312 }, 45313 reverse: function(pos) { 45314 return 1-pos; 45315 }, 45316 flicker: function(pos) { 45317 var pos = ((-Math.cos(pos*Math.PI)/4) + .75) + Math.random()/4; 45318 return pos > 1 ? 1 : pos; 45319 }, 45320 wobble: function(pos) { 45321 return (-Math.cos(pos*Math.PI*(9*pos))/2) + .5; 45322 }, 45323 pulse: function(pos, pulses) { 45324 return (-Math.cos((pos*((pulses||5)-.5)*2)*Math.PI)/2) + .5; 45325 }, 45326 spring: function(pos) { 45327 return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6)); 45328 }, 45329 none: function(pos) { 45330 return 0; 45331 }, 45332 full: function(pos) { 45333 return 1; 45334 } 45335 }, 45336 DefaultOptions: { 45337 duration: 1.0, // seconds 45338 fps: 100, // 100= assume 66fps max. 45339 sync: false, // true for combining 45340 from: 0.0, 45341 to: 1.0, 45342 delay: 0.0, 45343 queue: 'parallel' 45344 }, 45345 tagifyText: function(element) { 45346 var tagifyStyle = 'position:relative'; 45347 if (Prototype.Browser.IE) tagifyStyle += ';zoom:1'; 45348 45349 element = $(element); 45350 $A(element.childNodes).each( function(child) { 45351 if (child.nodeType==3) {
45352 child.nodeValue.toArray().each( function(character) { 45353 element.insertBefore( 45354 new Element('span', {style: tagifyStyle}).update( 45355 character == ' ' ? String.fromCharCode(160) : character), 45356 child); 45357 }); 45358 Element.remove(child); 45359 } 45360 }); 45361 }, 45362 multiple: function(element, effect) { 45363 var elements; 45364 if (((typeof element == 'object') || 45365 Object.isFunction(element)) && 45366 (element.length)) 45367 elements = element; 45368 else 45369 elements = $(element).childNodes; 45370 45371 var options = Object.extend({ 45372 speed: 0.1, 45373 delay: 0.0 45374 }, arguments[2] || { }); 45375 var masterDelay = options.delay; 45376 45377 $A(elements).each( function(element, index) { 45378 new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay })); 45379 }); 45380 }, 45381 PAIRS: { 45382 'slide': ['SlideDown','SlideUp'], 45383 'blind': ['BlindDown','BlindUp'], 45384 'appear': ['Appear','Fade'] 45385 }, 45386 toggle: function(element, effect, options) { 45387 element = $(element); 45388 effect = (effect || 'appear').toLowerCase(); 45389 45390 return Effect[ Effect.PAIRS[ effect ][ element.visible() ? 1 : 0 ] ](element, Object.extend({ 45391 queue: { position:'end', scope:(element.id || 'global'), limit: 1 } 45392 }, options || {})); 45393 } 45394}; 45395 45396Effect.DefaultOptions.transition = Effect.Transitions.sinoidal; 45397 45398/* ------------- core effects ------------- */ 45399 45400Effect.ScopedQueue = Class.create(Enumerable, { 45401 initialize: function() { 45402 this.effects = []; 45403 this.interval = null; 45404 }, 45405 _each: function(iterator) { 45406 this.effects._each(iterator); 45407 }, 45408 add: function(effect) { 45409 var timestamp = new Date().getTime(); 45410 45411 var position = Object.isString(effect.options.queue) ? 45412 effect.options.queue : effect.options.queue.position; 45413 45414 switch(position) { 45415 case 'front': 45416 // move unstarted effects after this effect 45417 this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) { 45418 e.startOn += effect.finishOn; 45419 e.finishOn += effect.finishOn; 45420 }); 45421 break; 45422 case 'with-last': 45423 timestamp = this.effects.pluck('startOn').max() || timestamp; 45424 break; 45425 case 'end': 45426 // start effect after last queued effect has finished 45427 timestamp = this.effects.pluck('finishOn').max() || timestamp; 45428 break; 45429 } 45430 45431 effect.startOn += timestamp; 45432 effect.finishOn += timestamp; 45433 45434 if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit)) 45435 this.effects.push(effect); 45436 45437 if (!this.interval) 45438 this.interval = setInterval(this.loop.bind(this), 15); 45439 }, 45440 remove: function(effect) { 45441 this.effects = this.effects.reject(function(e) { return e==effect }); 45442 if (this.effects.length == 0) { 45443 clearInterval(this.interval); 45444 this.interval = null; 45445 } 45446 }, 45447 loop: function() { 45448 var timePos = new Date().getTime(); 45449 for(var i=0, len=this.effects.length;i<len;i++) 45450 this.effects[i] && this.effects[i].loop(timePos); 45451 } 45452}); 45453 45454Effect.Queues = { 45455 instances: $H(), 45456 get: function(queueName) { 45457 if (!Object.isString(queueName)) return queueName; 45458 45459 return this.instances.get(queueName) || 45460 this.instances.set(queueName, new Effect.ScopedQueue()); 45461 } 45462}; 45463Effect.Queue = Effect.Queues.get('global'); 45464 45465Effect.Base = Class.create({ 45466 position: null, 45467 start: function(options) { 45468 if (options && options.transition === false) options.transition = Effect.Transitions.linear; 45469 this.options = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { }); 45470 this.currentFrame = 0; 45471 this.state = 'idle'; 45472 this.startOn = this.options.delay*1000; 45473 this.finishOn = this.startOn+(this.options.duration*1000); 45474 this.fromToDelta = this.options.to-this.options.from; 45475 this.totalTime = this.finishOn-this.startOn; 45476 this.totalFrames = this.options.fps*this.options.duration; 45477 45478 this.render = (function() { 45479 function dispatch(effect, eventName) { 45480 if (effect.options[eventName + 'Internal']) 45481 effect.options[eventName + 'Internal'](effect); 45482 if (effect.options[eventName]) 45483 effect.options[eventName](effect); 45484 } 45485 45486 return function(pos) {
45487 if (this.state === "idle") { 45488 this.state = "running"; 45489 dispatch(this, 'beforeSetup'); 45490 if (this.setup) this.setup(); 45491 dispatch(this, 'afterSetup'); 45492 } 45493 if (this.state === "running") { 45494 pos = (this.options.transition(pos) * this.fromToDelta) + this.options.from; 45495 this.position = pos; 45496 dispatch(this, 'beforeUpdate'); 45497 if (this.update) this.update(pos); 45498 dispatch(this, 'afterUpdate'); 45499 } 45500 }; 45501 })(); 45502 45503 this.event('beforeStart'); 45504 if (!this.options.sync) 45505 Effect.Queues.get(Object.isString(this.options.queue) ? 45506 'global' : this.options.queue.scope).add(this); 45507 }, 45508 loop: function(timePos) { 45509 if (timePos >= this.startOn) { 45510 if (timePos >= this.finishOn) { 45511 this.render(1.0); 45512 this.cancel(); 45513 this.event('beforeFinish'); 45514 if (this.finish) this.finish(); 45515 this.event('afterFinish'); 45516 return; 45517 } 45518 var pos = (timePos - this.startOn) / this.totalTime, 45519 frame = (pos * this.totalFrames).round(); 45520 if (frame > this.currentFrame) { 45521 this.render(pos); 45522 this.currentFrame = frame; 45523 } 45524 } 45525 }, 45526 cancel: function() { 45527 if (!this.options.sync) 45528 Effect.Queues.get(Object.isString(this.options.queue) ? 45529 'global' : this.options.queue.scope).remove(this); 45530 this.state = 'finished'; 45531 }, 45532 event: function(eventName) { 45533 if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this); 45534 if (this.options[eventName]) this.options[eventName](this); 45535 }, 45536 inspect: function() { 45537 var data = $H(); 45538 for(property in this) 45539 if (!Object.isFunction(this[property])) data.set(property, this[property]); 45540 return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>'; 45541 } 45542}); 45543 45544Effect.Parallel = Class.create(Effect.Base, { 45545 initialize: function(effects) { 45546 this.effects = effects || []; 45547 this.start(arguments[1]); 45548 }, 45549 update: function(position) { 45550 this.effects.invoke('render', position); 45551 }, 45552 finish: function(position) { 45553 this.effects.each( function(effect) { 45554 effect.render(1.0); 45555 effect.cancel(); 45556 effect.event('beforeFinish'); 45557 if (effect.finish) effect.finish(position); 45558 effect.event('afterFinish'); 45559 }); 45560 } 45561}); 45562 45563Effect.Tween = Class.create(Effect.Base, { 45564 initialize: function(object, from, to) { 45565 object = Object.isString(object) ? $(object) : object; 45566 var args = $A(arguments), method = args.last(), 45567 options = args.length == 5 ? args[3] : null; 45568 this.method = Object.isFunction(method) ? method.bind(object) : 45569 Object.isFunction(object[method]) ? object[method].bind(object) : 45570 function(value) { object[method] = value }; 45571 this.start(Object.extend({ from: from, to: to }, options || { })); 45572 }, 45573 update: function(position) { 45574 this.method(position); 45575 } 45576}); 45577 45578Effect.Event = Class.create(Effect.Base, { 45579 initialize: function() { 45580 this.start(Object.extend({ duration: 0 }, arguments[0] || { })); 45581 }, 45582 update: Prototype.emptyFunction 45583}); 45584 45585Effect.Opacity = Class.create(Effect.Base, { 45586 initialize: function(element) { 45587 this.element = $(element); 45588 if (!this.element) throw(Effect._elementDoesNotExistError); 45589 // make this work on IE on elements without 'layout' 45590 if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout)) 45591 this.element.setStyle({zoom: 1}); 45592 var options = Object.extend({ 45593 from: this.element.getOpacity() || 0.0, 45594 to: 1.0 45595 }, arguments[1] || { }); 45596 this.start(options); 45597 }, 45598 update: function(position) { 45599 this.element.setOpacity(position); 45600 } 45601}); 45602 45603Effect.Move = Class.create(Effect.Base, { 45604 initialize: function(element) { 45605 this.element = $(element); 45606 if (!this.element) throw(Effect._elementDoesNotExistError); 45607 var options = Object.extend({ 45608 x: 0, 45609 y: 0, 45610 mode: 'relative' 45611 }, arguments[1] || { }); 45612 this.start(options); 45613 }, 45614 setup: function() { 45615 this.element.makePositioned(); 45616 this.originalLeft = parseFloat(this.element.getStyle('left') || '0'); 45617 this.originalTop = parseFloat(this.element.getStyle('top') || '0'); 45618 if (this.options.mode == 'absolute') { 45619 this.options.x = this.options.x - this.originalLeft; 45620 this.options.y = this.options.y - this.originalTop; 45621 } 45622 }, 45623 update: function(position) { 45624 this.element.setStyle({ 45625 left: (this.options.x * position + this.originalLeft).round() + 'px', 45626 top: (this.options.y * position + this.originalTop).round() + 'px' 45627 }); 45628 } 45629}); 45630 45631// for backwards compatibility 45632Effect.MoveBy = function(element, toTop, toLeft) { 45633 return new Effect.Move(element,
45634 Object.extend({ x: toLeft, y: toTop }, arguments[3] || { })); 45635}; 45636 45637Effect.Scale = Class.create(Effect.Base, { 45638 initialize: function(element, percent) { 45639 this.element = $(element); 45640 if (!this.element) throw(Effect._elementDoesNotExistError); 45641 var options = Object.extend({ 45642 scaleX: true, 45643 scaleY: true, 45644 scaleContent: true, 45645 scaleFromCenter: false, 45646 scaleMode: 'box', // 'box' or 'contents' or { } with provided values 45647 scaleFrom: 100.0, 45648 scaleTo: percent 45649 }, arguments[2] || { }); 45650 this.start(options); 45651 }, 45652 setup: function() { 45653 this.restoreAfterFinish = this.options.restoreAfterFinish || false; 45654 this.elementPositioning = this.element.getStyle('position'); 45655 45656 this.originalStyle = { }; 45657 ['top','left','width','height','fontSize'].each( function(k) { 45658 this.originalStyle[k] = this.element.style[k]; 45659 }.bind(this)); 45660 45661 this.originalTop = this.element.offsetTop; 45662 this.originalLeft = this.element.offsetLeft; 45663 45664 var fontSize = this.element.getStyle('font-size') || '100%'; 45665 ['em','px','%','pt'].each( function(fontSizeType) { 45666 if (fontSize.indexOf(fontSizeType)>0) { 45667 this.fontSize = parseFloat(fontSize); 45668 this.fontSizeType = fontSizeType; 45669 } 45670 }.bind(this)); 45671 45672 this.factor = (this.options.scaleTo - this.options.scaleFrom)/100; 45673 45674 this.dims = null; 45675 if (this.options.scaleMode=='box') 45676 this.dims = [this.element.offsetHeight, this.element.offsetWidth]; 45677 if (/^content/.test(this.options.scaleMode)) 45678 this.dims = [this.element.scrollHeight, this.element.scrollWidth]; 45679 if (!this.dims) 45680 this.dims = [this.options.scaleMode.originalHeight, 45681 this.options.scaleMode.originalWidth]; 45682 }, 45683 update: function(position) { 45684 var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position); 45685 if (this.options.scaleContent && this.fontSize) 45686 this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType }); 45687 this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale); 45688 }, 45689 finish: function(position) { 45690 if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle); 45691 }, 45692 setDimensions: function(height, width) { 45693 var d = { }; 45694 if (this.options.scaleX) d.width = width.round() + 'px'; 45695 if (this.options.scaleY) d.height = height.round() + 'px'; 45696 if (this.options.scaleFromCenter) { 45697 var topd = (height - this.dims[0])/2; 45698 var leftd = (width - this.dims[1])/2; 45699 if (this.elementPositioning == 'absolute') { 45700 if (this.options.scaleY) d.top = this.originalTop-topd + 'px'; 45701 if (this.options.scaleX) d.left = this.originalLeft-leftd + 'px'; 45702 } else { 45703 if (this.options.scaleY) d.top = -topd + 'px'; 45704 if (this.options.scaleX) d.left = -leftd + 'px'; 45705 } 45706 } 45707 this.element.setStyle(d); 45708 } 45709}); 45710 45711Effect.Highlight = Class.create(Effect.Base, { 45712 initialize: function(element) { 45713 this.element = $(element); 45714 if (!this.element) throw(Effect._elementDoesNotExistError); 45715 var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || { }); 45716 this.start(options); 45717 }, 45718 setup: function() { 45719 // Prevent executing on elements not in the layout flow 45720 if (this.element.getStyle('display')=='none') { this.cancel(); return; } 45721 // Disable background image during the effect 45722 this.oldStyle = { }; 45723 if (!this.options.keepBackgroundImage) { 45724 this.oldStyle.backgroundImage = this.element.getStyle('background-image'); 45725 this.element.setStyle({backgroundImage: 'none'}); 45726 } 45727 if (!this.options.endcolor) 45728 this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff'); 45729 if (!this.options.restorecolor) 45730 this.options.restorecolor = this.element.getStyle('background-color'); 45731 // init color calculations 45732 this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this)); 45733 this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this)); 45734 }, 45735 update: function(position) { 45736 this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){ 45737 return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) }); 45738 }, 45739 finish: function() { 45740 this.element.setStyle(Object.extend(this.oldStyle, { 45741 backgroundColor: this.options.restorecolor 45742 })); 45743 } 45744}); 45745 45746Effect.ScrollTo = function(element) { 45747 var options = arguments[1] || { }, 45748 scrollOffsets = document.viewport.getScrollOffsets(), 45749 elementOffsets = $(element).cumulativeOffset(); 45750 45751 if (options.offset) elementOffsets[1] += options.offset; 45752 45753 return new Effect.Tween(null, 45754 scrollOffsets.top, 45755 elementOffsets[1], 45756 options, 45757 function(p){ scrollTo(scrollOffsets.left, p.round()); } 45758 ); 45759}; 45760 45761/* ------------- combination effects ------------- */ 45762 45763Effect.Fade = function(element) { 45764 element = $(element); 45765 var oldOpacity = element.getInlineOpacity(); 45766 var options = Object.extend({ 45767 from: element.getOpacity() || 1.0, 45768 to: 0.0, 45769 afterFinishInternal: function(effect) { 45770 if (effect.options.to!=0) return; 45771 effect.element.hide().setStyle({opacity: oldOpacity}); 45772 } 45773 }, arguments[1] || { }); 45774 return new Effect.Opacity(element,options); 45775}; 45776 45777Effect.Appear = function(element) { 45778 element = $(element); 45779 var options = Object.extend({ 45780 from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0), 45781 to: 1.0, 45782 // force Safari to render floated elements properly 45783 afterFinishInternal: function(effect) { 45784 effect.element.forceRerendering(); 45785 }, 45786 beforeSetup: function(effect) { 45787 effect.element.setOpacity(effect.options.from).show(); 45788 }}, arguments[1] || { }); 45789 return new Effect.Opacity(element,options); 45790}; 45791 45792Effect.Puff = function(element) { 45793 element = $(element); 45794 var oldStyle = { 45795 opacity: element.getInlineOpacity(), 45796 position: element.getStyle('position'), 45797 top: element.style.top, 45798 left: element.style.left, 45799 width: element.style.width, 45800 height: element.style.height 45801 }; 45802 return new Effect.Parallel( 45803 [ new Effect.Scale(element, 200, 45804 { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), 45805 new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], 45806 Object.extend({ duration: 1.0, 45807 beforeSetupInternal: function(effect) { 45808 Position.absolutize(effect.effects[0].element); 45809 }, 45810 afterFinishInternal: function(effect) { 45811 effect.effects[0].element.hide().setStyle(oldStyle); } 45812 }, arguments[1] || { }) 45813 ); 45814}; 45815 45816Effect.BlindUp = function(element) { 45817 element = $(element); 45818 element.makeClipping(); 45819 return new Effect.Scale(element, 0, 45820 Object.extend({ scaleContent: false, 45821 scaleX: false, 45822 restoreAfterFinish: true, 45823 afterFinishInternal: function(effect) { 45824 effect.element.hide().undoClipping(); 45825 } 45826 }, arguments[1] || { }) 45827 ); 45828}; 45829 45830Effect.BlindDown = function(element) { 45831 element = $(element); 45832 var elementDimensions = element.getDimensions(); 45833 return new Effect.Scale(element, 100, Object.extend({ 45834 scaleContent: false, 45835 scaleX: false, 45836 scaleFrom: 0, 45837 scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, 45838 restoreAfterFinish: true, 45839 afterSetup: function(effect) { 45840 effect.element.makeClipping().setStyle({height: '0px'}).show(); 45841 }, 45842 afterFinishInternal: function(effect) { 45843 effect.element.undoClipping(); 45844 } 45845 }, arguments[1] || { })); 45846}; 45847 45848Effect.SwitchOff = function(element) { 45849 element = $(element); 45850 var oldOpacity = element.getInlineOpacity(); 45851 return new Effect.Appear(element, Object.extend({ 45852 duration: 0.4, 45853 from: 0, 45854 transition: Effect.Transitions.flicker, 45855 afterFinishInternal: function(effect) { 45856 new Effect.Scale(effect.element, 1, { 45857 duration: 0.3, scaleFromCenter: true, 45858 scaleX: false, scaleContent: false, restoreAfterFinish: true, 45859 beforeSetup: function(effect) { 45860 effect.element.makePositioned().makeClipping(); 45861 }, 45862 afterFinishInternal: function(effect) { 45863 effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity}); 45864 } 45865 }); 45866 } 45867 }, arguments[1] || { })); 45868}; 45869 45870Effect.DropOut = function(element) { 45871 element = $(element); 45872 var oldStyle = { 45873 top: element.getStyle('top'), 45874 left: element.getStyle('left'), 45875 opacity: element.getInlineOpacity() }; 45876 return new Effect.Parallel( 45877 [ new Effect.Move(element, {x: 0, y: 100, sync: true }), 45878 new Effect.Opacity(element, { sync: true, to: 0.0 }) ], 45879 Object.extend( 45880 { duration: 0.5, 45881 beforeSetup: function(effect) { 45882 effect.effects[0].element.makePositioned(); 45883 }, 45884 afterFinishInternal: function(effect) { 45885 effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle); 45886 } 45887 }, arguments[1] || { })); 45888}; 45889 45890Effect.Shake = function(element) { 45891 element = $(element); 45892 var options = Object.extend({ 45893 distance: 20, 45894 duration: 0.5 45895 }, arguments[1] || {}); 45896 var distance = parseFloat(options.distance); 45897 var split = parseFloat(options.duration) / 10.0; 45898 var oldStyle = { 45899 top: element.getStyle('top'), 45900 left: element.getStyle('left') }; 45901 return new Effect.Move(element, 45902 { x: distance, y: 0, duration: split, afterFinishInternal: function(effect) { 45903 new Effect.Move(effect.element, 45904 { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { 45905 new Effect.Move(effect.element, 45906 { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { 45907 new Effect.Move(effect.element, 45908 { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { 45909 new Effect.Move(effect.element, 45910 { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { 45911 new Effect.Move(effect.element, 45912 { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) { 45913 effect.element.undoPositioned().setStyle(oldStyle); 45914 }}); }}); }}); }}); }}); }}); 45915}; 45916 45917Effect.SlideDown = function(element) { 45918 element = $(element).cleanWhitespace(); 45919 // SlideDown need to have the content of the element wrapped in a container element with fixed height! 45920 var oldInnerBottom = element.down().getStyle('bottom'); 45921 var elementDimensions = element.getDimensions(); 45922 return new Effect.Scale(element, 100, Object.extend({ 45923 scaleContent: false, 45924 scaleX: false, 45925 scaleFrom: window.opera ? 0 : 1, 45926 scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, 45927 restoreAfterFinish: true, 45928 afterSetup: function(effect) { 45929 effect.element.makePositioned(); 45930 effect.element.down().makePositioned(); 45931 if (window.opera) effect.element.setStyle({top: ''}); 45932 effect.element.makeClipping().setStyle({height: '0px'}).show(); 45933 }, 45934 afterUpdateInternal: function(effect) { 45935 effect.element.down().setStyle({bottom: 45936 (effect.dims[0] - effect.element.clientHeight) + 'px' }); 45937 }, 45938 afterFinishInternal: function(effect) { 45939 effect.element.undoClipping().undoPositioned(); 45940 effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); } 45941 }, arguments[1] || { }) 45942 ); 45943}; 45944 45945Effect.SlideUp = function(element) { 45946 element = $(element).cleanWhitespace(); 45947 var oldInnerBottom = element.down().getStyle('bottom'); 45948 var elementDimensions = element.getDimensions(); 45949 return new Effect.Scale(element, window.opera ? 0 : 1, 45950 Object.extend({ scaleContent: false, 45951 scaleX: false, 45952 scaleMode: 'box', 45953 scaleFrom: 100, 45954 scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, 45955 restoreAfterFinish: true, 45956 afterSetup: function(effect) { 45957 effect.element.makePositioned(); 45958 effect.element.down().makePositioned(); 45959 if (window.opera) effect.element.setStyle({top: ''}); 45960 effect.element.makeClipping().show(); 45961 }, 45962 afterUpdateInternal: function(effect) { 45963 effect.element.down().setStyle({bottom: 45964 (effect.dims[0] - effect.element.clientHeight) + 'px' }); 45965 }, 45966 afterFinishInternal: function(effect) { 45967 effect.element.hide().undoClipping().undoPositioned(); 45968 effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); 45969 } 45970 }, arguments[1] || { }) 45971 ); 45972}; 45973 45974// Bug in opera makes the TD containing this element expand for a instance after finish 45975Effect.Squish = function(element) { 45976 return new Effect.Scale(element, window.opera ? 1 : 0, { 45977 restoreAfterFinish: true, 45978 beforeSetup: function(effect) { 45979 effect.element.makeClipping(); 45980 }, 45981 afterFinishInternal: function(effect) { 45982 effect.element.hide().undoClipping(); 45983 } 45984 }); 45985}; 45986 45987Effect.Grow = function(element) { 45988 element = $(element); 45989 var options = Object.extend({ 45990 direction: 'center', 45991 moveTransition: Effect.Transitions.sinoidal, 45992 scaleTransition: Effect.Transitions.sinoidal, 45993 opacityTransition: Effect.Transitions.full 45994 }, arguments[1] || { }); 45995 var oldStyle = { 45996 top: element.style.top, 45997 left: element.style.left, 45998 height: element.style.height, 45999 width: element.style.width, 46000 opacity: element.getInlineOpacity() }; 46001 46002 var dims = element.getDimensions(); 46003 var initialMoveX, initialMoveY; 46004 var moveX, moveY; 46005 46006 switch (options.direction) { 46007 case 'top-left': 46008 initialMoveX = initialMoveY = moveX = moveY = 0; 46009 break; 46010 case 'top-right': 46011 initialMoveX = dims.width; 46012 initialMoveY = moveY = 0; 46013 moveX = -dims.width; 46014 break; 46015 case 'bottom-left': 46016 initialMoveX = moveX = 0; 46017 initialMoveY = dims.height; 46018 moveY = -dims.height; 46019 break; 46020 case 'bottom-right': 46021 initialMoveX = dims.width; 46022 initialMoveY = dims.height; 46023 moveX = -dims.width; 46024 moveY = -dims.height; 46025 break; 46026 case 'center': 46027 initialMoveX = dims.width / 2; 46028 initialMoveY = dims.height / 2; 46029 moveX = -dims.width / 2; 46030 moveY = -dims.height / 2; 46031 break; 46032 } 46033 46034 return new Effect.Move(element, { 46035 x: initialMoveX, 46036 y: initialMoveY, 46037 duration: 0.01, 46038 beforeSetup: function(effect) { 46039 effect.element.hide().makeClipping().makePositioned(); 46040 }, 46041 afterFinishInternal: function(effect) { 46042 new Effect.Parallel( 46043 [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }), 46044 new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }), 46045 new Effect.Scale(effect.element, 100, { 46046 scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, 46047 sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true}) 46048 ], Object.extend({ 46049 beforeSetup: function(effect) { 46050 effect.effects[0].element.setStyle({height: '0px'}).show(); 46051 }, 46052 afterFinishInternal: function(effect) { 46053 effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle); 46054 } 46055 }, options) 46056 ); 46057 } 46058 }); 46059}; 46060 46061Effect.Shrink = function(element) { 46062 element = $(element); 46063 var options = Object.extend({ 46064 direction: 'center', 46065 moveTransition: Effect.Transitions.sinoidal, 46066 scaleTransition: Effect.Transitions.sinoidal, 46067 opacityTransition: Effect.Transitions.none 46068 }, arguments[1] || { }); 46069 var oldStyle = { 46070 top: element.style.top, 46071 left: element.style.left, 46072 height: element.style.height, 46073 width: element.style.width, 46074 opacity: element.getInlineOpacity() }; 46075 46076 var dims = element.getDimensions(); 46077 var moveX, moveY; 46078 46079 switch (options.direction) { 46080 case 'top-left': 46081 moveX = moveY = 0; 46082 break; 46083 case 'top-right': 46084 moveX = dims.width; 46085 moveY = 0; 46086 break; 46087 case 'bottom-left': 46088 moveX = 0; 46089 moveY = dims.height; 46090 break; 46091 case 'bottom-right': 46092 moveX = dims.width; 46093 moveY = dims.height; 46094 break; 46095 case 'center': 46096 moveX = dims.width / 2; 46097 moveY = dims.height / 2; 46098 break; 46099 } 46100 46101 return new Effect.Parallel( 46102 [ new Effect.Opacity(elemen
46102t, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }), 46103 new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}), 46104 new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }) 46105 ], Object.extend({ 46106 beforeStartInternal: function(effect) { 46107 effect.effects[0].element.makePositioned().makeClipping(); 46108 }, 46109 afterFinishInternal: function(effect) { 46110 effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); } 46111 }, options) 46112 ); 46113}; 46114 46115Effect.Pulsate = function(element) { 46116 element = $(element); 46117 var options = arguments[1] || { }, 46118 oldOpacity = element.getInlineOpacity(), 46119 transition = options.transition || Effect.Transitions.linear, 46120 reverser = function(pos){ 46121 return 1 - transition((-Math.cos((pos*(options.pulses||5)*2)*Math.PI)/2) + .5); 46122 }; 46123 46124 return new Effect.Opacity(element, 46125 Object.extend(Object.extend({ duration: 2.0, from: 0, 46126 afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); } 46127 }, options), {transition: reverser})); 46128}; 46129 46130Effect.Fold = function(element) { 46131 element = $(element); 46132 var oldStyle = { 46133 top: element.style.top, 46134 left: element.style.left, 46135 width: element.style.width, 46136 height: element.style.height }; 46137 element.makeClipping(); 46138 return new Effect.Scale(element, 5, Object.extend({ 46139 scaleContent: false, 46140 scaleX: false, 46141 afterFinishInternal: function(effect) { 46142 new Effect.Scale(element, 1, { 46143 scaleContent: false, 46144 scaleY: false, 46145 afterFinishInternal: function(effect) { 46146 effect.element.hide().undoClipping().setStyle(oldStyle); 46147 } }); 46148 }}, arguments[1] || { })); 46149}; 46150 46151Effect.Morph = Class.create(Effect.Base, { 46152 initialize: function(element) { 46153 this.element = $(element); 46154 if (!this.element) throw(Effect._elementDoesNotExistError); 46155 var options = Object.extend({ 46156 style: { } 46157 }, arguments[1] || { }); 46158 46159 if (!Object.isString(options.style)) this.style = $H(options.style); 46160 else { 46161 if (options.style.include(':')) 46162 this.style = options.style.parseStyle(); 46163 else { 46164 this.element.addClassName(options.style); 46165 this.style = $H(this.element.getStyles()); 46166 this.element.removeClassName(options.style); 46167 var css = this.element.getStyles(); 46168 this.style = this.style.reject(function(style) { 46169 return style.value == css[style.key]; 46170 }); 46171 options.afterFinishInternal = function(effect) { 46172 effect.element.addClassName(effect.options.style); 46173 effect.transforms.each(function(transform) { 46174 effect.element.style[transform.style] = ''; 46175 }); 46176 }; 46177 } 46178 } 46179 this.start(options); 46180 }, 46181 46182 setup: function(){ 46183 function parseColor(color){ 46184 if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff'; 46185 color = color.parseColor(); 46186 return $R(0,2).map(function(i){ 46187 return parseInt( color.slice(i*2+1,i*2+3), 16 ); 46188 }); 46189 } 46190 this.transforms = this.style.map(function(pair){ 46191 var property = pair[0], value = pair[1], unit = null; 46192 46193 if (value.parseColor('#zzzzzz') != '#zzzzzz') { 46194 value = value.parseColor(); 46195 unit = 'color'; 46196 } else if (property == 'opacity') { 46197 value = parseFloat(value); 46198 if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout)) 46199 this.element.setStyle({zoom: 1}); 46200 } else if (Element.CSS_LENGTH.test(value)) { 46201 var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/); 46202 value = parseFloat(components[1]); 46203 unit = (components.length == 3) ? components[2] : null; 46204 } 46205 46206 var originalValue = this.element.getStyle(property); 46207 return { 46208 style: property.camelize(), 46209 originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0), 46210 targetValue: unit=='color' ? parseColor(value) : value, 46211 unit: unit 46212 }; 46213 }.bind(this)).reject(function(transform){ 46214 return ( 46215 (transform.originalValue == transform.targetValue) || 46216 ( 46217 transform.unit != 'color' && 46218 (isNaN(transform.originalValue) || isNaN(transform.targetValue)) 46219 ) 46220 ); 46221 }); 46222 }, 46223 update: function(position) { 46224 var style = { }, transform, i = this.transforms.length; 46225 while(i--) 46226 style[(transform = this.transforms[i]).style] = 46227 transform.unit=='color' ? '#'+ 46228 (Math.round(transform.originalValue[0]+ 46229 (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() + 46230 (Math.round(transform.originalValue[1]+ 46231 (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() + 46232 (Math.round(transform.originalValue[2]+ 46233 (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() : 46234 (transform.originalValue + 46235 (transform.targetValue - transform.originalValue) * position).toFixed(3) + 46236 (transform.unit === null ? '' : transform.unit); 46237 this.element.setStyle(style, true); 46238 } 46239}); 46240 46241Effect.Transform = Class.create({ 46242 initialize: function(tracks){ 46243 this.tracks = []; 46244 this.options = arguments[1] || { }; 46245 this.addTracks(tracks); 46246 }, 46247 addTracks: function(tracks){ 46248 tracks.each(function(track){ 46249 track = $H(track); 46250 var data = track.values().first(); 46251 this.tracks.push($H({ 46252 ids: track.keys().first(), 46253 effect: Effect.Morph, 46254 options: { style: data } 46255 })); 46256 }.bind(this)); 46257 return this; 46258 }, 46259 play: function(){ 46260 return new Effect.Parallel( 46261 this.tracks.map(function(track){ 46262 var ids = track.get('ids'), effect = track.get('effect'), options = track.get('options'); 46263 var elements = [$(ids) || $$(ids)].flatten(); 46264 return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)) }); 46265 }).flatten(), 46266 this.options 46267 ); 46268 } 46269}); 46270 46271Element.CSS_PROPERTIES = $w( 46272 'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' + 46273 'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' + 46274 'borderRightColor borderRightStyle borderRightWidth borderSpacing ' + 46275 'borderTopColor borderTopStyle borderTopWidth bottom clip color ' + 46276 'fontSize fontWeight height left letterSpacing lineHeight ' + 46277 'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+ 46278 'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' + 46279 'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' + 46280 'right textIndent top width wordSpacing zIndex'); 46281 46282Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/; 46283 46284String.__parseStyleElement = document.createElement('div'); 46285String.prototype.parseStyle = function(){ 46286 var style, styleRules = $H(); 46287 if (Prototype.Browser.WebKit) 46288 style = new Element('div',{style:this}).style; 46289 else { 46290 String.__parseStyleElement.innerHTML = '<div style="' + this + '"></div>'; 46291 style = String.__parseStyleElement.childNodes[0].style; 46292 } 46293 46294 Element.CSS_PROPERTIES.each(function(property){ 46295 if (style[property]) styleRules.set(property, style[property]); 46296 }); 46297 46298 if (Prototype.Browser.IE && this.include('opacity')) 46299 styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]); 46300 46301 return styleRules; 46302}; 46303 46304if (document.defaultView && document.defaultView.getComputedStyle) { 46305 Element.getStyles = function(element) { 46306 var css = document.defaultView.getComputedStyle($(element), null); 46307 return Element.CSS_PROPERTIES.inject({ }, function(styles, property) { 46308 styles[property] = css[property]; 46309 return styles; 46310 }); 46311 }; 46312} else { 46313 Element.getStyles = function(element) { 46314 element = $(element); 46315 var css = element.currentStyle, styles; 46316 styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) { 46317 results[property] = css[property]; 46318 return results; 46319 }); 46320 if (!styles.opacity) styles.opacity = element.getOpacity(); 46321 return styles; 46322 }; 46323} 46324 46325Effect.Methods = { 46326 morph: function(element, style) { 46327 element = $(element); 46328 new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { })); 46329 return element; 46330 }, 46331 visualEffect: function(element, effect, options) { 46332 element = $(element); 46333 var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1); 46334 new Effect[klass](element, options); 46335 return element; 46336 }, 46337 highlight: function(element, options) { 46338 element = $(element); 46339 new Effect.Highlight(element, options); 46340 return element; 46341 } 46342}; 46343 46344$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+ 46345 'pulsate shake puff squish switchOff dropOut').each( 46346 function(effect) { 46347 Effect.Methods[effect] = function(element, options){ 46348 element = $(element);
46349 Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options); 46350 return element; 46351 }; 46352 } 46353); 46354 46355$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each( 46356 function(f) { Effect.Methods[f] = Element[f]; } 46357); 46358 46359Element.addMethods(Effect.Methods); 46360// script.aculo.us controls.js v1.8.3, Thu Oct 08 11:23:33 +0200 2009 46361 46362// Copyright (c) 2005-2009 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) 46363// (c) 2005-2009 Ivan Krstic (http://blogs.law.harvard.edu/ivan) 46364// (c) 2005-2009 Jon Tirsen (http://www.tirsen.com) 46365// Contributors: 46366// Richard Livsey 46367// Rahul Bhargava 46368// Rob Wills 46369// 46370// script.aculo.us is freely distributable under the terms of an MIT-style license. 46371// For details, see the script.aculo.us web site: http://script.aculo.us/ 46372 46373// Autocompleter.Base handles all the autocompletion functionality 46374// that's independent of the data source for autocompletion. This 46375// includes drawing the autocompletion menu, observing keyboard 46376// and mouse events, and similar. 46377// 46378// Specific autocompleters need to provide, at the very least, 46379// a getUpdatedChoices function that will be invoked every time 46380// the text inside the monitored textbox changes. This method 46381// should get the text for which to provide autocompletion by 46382// invoking this.getToken(), NOT by directly accessing 46383// this.element.value. This is to allow incremental tokenized 46384// autocompletion. Specific auto-completion logic (AJAX, etc) 46385// belongs in getUpdatedChoices. 46386// 46387// Tokenized incremental autocompletion is enabled automatically 46388// when an autocompleter is instantiated with the 'tokens' option 46389// in the options parameter, e.g.: 46390// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' }); 46391// will incrementally autocomplete with a comma as the token. 46392// Additionally, ',' in the above example can be replaced with 46393// a token array, e.g. { tokens: [',', '\n'] } which 46394// enables autocompletion on multiple tokens. This is most 46395// useful when one of the tokens is \n (a newline), as it 46396// allows smart autocompletion after linebreaks. 46397 46398if(typeof Effect == 'undefined') 46399 throw("controls.js requires including script.aculo.us' effects.js library"); 46400 46401var Autocompleter = { }; 46402Autocompleter.Base = Class.create({ 46403 baseInitialize: function(element, update, options) { 46404 element = $(element); 46405 this.element = element; 46406 this.update = $(update); 46407 this.hasFocus = false; 46408 this.changed = false; 46409 this.active = false; 46410 this.index = 0; 46411 this.entryCount = 0; 46412 this.oldElementValue = this.element.value; 46413 46414 if(this.setOptions) 46415 this.setOptions(options); 46416 else 46417 this.options = options || { }; 46418 46419 this.options.paramName = this.options.paramName || this.element.name; 46420 this.options.tokens = this.options.tokens || []; 46421 this.options.frequency = this.options.frequency || 0.4; 46422 this.options.minChars = this.options.minChars || 1; 46423 this.options.onShow = this.options.onShow || 46424 function(element, update){ 46425 if(!update.style.position || update.style.position=='absolute') { 46426 update.style.position = 'absolute'; 46427 Position.clone(element, update, { 46428 setHeight: false, 46429 offsetTop: element.offsetHeight 46430 }); 46431 } 46432 Effect.Appear(update,{duration:0.15}); 46433 }; 46434 this.options.onHide = this.options.onHide || 46435 function(element, update){ new Effect.Fade(update,{duration:0.15}) }; 46436 46437 if(typeof(this.options.tokens) == 'string') 46438 this.options.tokens = new Array(this.options.tokens); 46439 // Force carriage returns as token delimiters anyway 46440 if (!this.options.tokens.include('\n')) 46441 this.options.tokens.push('\n'); 46442 46443 this.observer = null; 46444 46445 this.element.setAttribute('autocomplete','off'); 46446 46447 Element.hide(this.update); 46448 46449 Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this)); 46450 Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this)); 46451 }, 46452 46453 show: function() { 46454 if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update); 46455 if(!this.iefix && 46456 (Prototype.Browser.IE) && 46457 (Element.getStyle(this.update, 'position')=='absolute')) { 46458 new Insertion.After(this.update, 46459 '<iframe id="' + this.update.id + '_iefix" '+ 46460 'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' + 46461 'src="javascript:false;" frameborder="0" scrolling="no"></iframe>'); 46462 this.iefix = $(this.update.id+'_iefix'); 46463 } 46464 if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50); 46465 }, 46466 46467 fixIEOverlapping: function() { 46468 Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)}); 46469 this.iefix.style.zIndex = 1; 46470 this.update.style.zIndex = 2; 46471 Element.show(this.iefix); 46472 }, 46473 46474 hide: function() { 46475 this.stopIndicator(); 46476 if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update); 46477 if(this.iefix) Element.hide(this.iefix); 46478 }, 46479 46480 startIndicator: function() { 46481 if(this.options.indicator) Element.show(this.options.indicator); 46482 }, 46483 46484 stopIndicator: function() { 46485 if(this.options.indicator) Element.hide(this.options.indicator); 46486 }, 46487 46488 onKeyPress: function(event) { 46489 if(this.active) 46490 switch(event.keyCode) { 46491 case Event.KEY_TAB: 46492 case Event.KEY_RETURN: 46493 this.selectEntry(); 46494 Event.stop(event); 46495 case Event.KEY_ESC: 46496 this.hide(); 46497 this.active = false;
46498 Event.stop(event); 46499 return; 46500 case Event.KEY_LEFT: 46501 case Event.KEY_RIGHT: 46502 return; 46503 case Event.KEY_UP: 46504 this.markPrevious(); 46505 this.render(); 46506 Event.stop(event); 46507 return; 46508 case Event.KEY_DOWN: 46509 this.markNext(); 46510 this.render(); 46511 Event.stop(event); 46512 return; 46513 } 46514 else 46515 if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN || 46516 (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return; 46517 46518 this.changed = true; 46519 this.hasFocus = true; 46520 46521 if(this.observer) clearTimeout(this.observer); 46522 this.observer = 46523 setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000); 46524 }, 46525 46526 activate: function() { 46527 this.changed = false; 46528 this.hasFocus = true; 46529 this.getUpdatedChoices(); 46530 }, 46531 46532 onHover: function(event) { 46533 var element = Event.findElement(event, 'LI'); 46534 if(this.index != element.autocompleteIndex) 46535 { 46536 this.index = element.autocompleteIndex; 46537 this.render(); 46538 } 46539 Event.stop(event); 46540 }, 46541 46542 onClick: function(event) { 46543 var element = Event.findElement(event, 'LI'); 46544 this.index = element.autocompleteIndex; 46545 this.selectEntry(); 46546 this.hide(); 46547 }, 46548 46549 onBlur: function(event) { 46550 // needed to make click events working 46551 setTimeout(this.hide.bind(this), 250); 46552 this.hasFocus = false; 46553 this.active = false; 46554 }, 46555 46556 render: function() { 46557 if(this.entryCount > 0) { 46558 for (var i = 0; i < this.entryCount; i++) 46559 this.index==i ? 46560 Element.addClassName(this.getEntry(i),"selected") : 46561 Element.removeClassName(this.getEntry(i),"selected"); 46562 if(this.hasFocus) { 46563 this.show(); 46564 this.active = true; 46565 } 46566 } else { 46567 this.active = false; 46568 this.hide(); 46569 } 46570 }, 46571 46572 markPrevious: function() { 46573 if(this.index > 0) this.index--; 46574 else this.index = this.entryCount-1; 46575 this.getEntry(this.index).scrollIntoView(true); 46576 }, 46577 46578 markNext: function() { 46579 if(this.index < this.entryCount-1) this.index++; 46580 else this.index = 0; 46581 this.getEntry(this.index).scrollIntoView(false); 46582 }, 46583 46584 getEntry: function(index) { 46585 return this.update.firstChild.childNodes[index]; 46586 }, 46587 46588 getCurrentEntry: function() { 46589 return this.getEntry(this.index); 46590 }, 46591 46592 selectEntry: function() { 46593 this.active = false; 46594 this.updateElement(this.getCurrentEntry()); 46595 }, 46596 46597 updateElement: function(selectedElement) { 46598 if (this.options.updateElement) { 46599 this.options.updateElement(selectedElement); 46600 return; 46601 } 46602 var value = ''; 46603 if (this.options.select) { 46604 var nodes = $(selectedElement).select('.' + this.options.select) || []; 46605 if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select); 46606 } else 46607 value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal'); 46608 46609 var bounds = this.getTokenBounds(); 46610 if (bounds[0] != -1) { 46611 var newValue = this.element.value.substr(0, bounds[0]); 46612 var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/); 46613 if (whitespace) 46614 newValue += whitespace[0]; 46615 this.element.value = newValue + value + this.element.value.substr(bounds[1]); 46616 } else { 46617 this.element.value = value; 46618 } 46619 this.oldElementValue = this.element.value; 46620 this.element.focus(); 46621 46622 if (this.options.afterUpdateElement) 46623 this.options.afterUpdateElement(this.element, selectedElement); 46624 }, 46625 46626 updateChoices: function(choices) { 46627 if(!this.changed && this.hasFocus) { 46628 this.update.innerHTML = choices; 46629 Element.cleanWhitespace(this.update); 46630 Element.cleanWhitespace(this.update.down()); 46631 46632 if(this.update.firstChild && this.update.down().childNodes) { 46633 this.entryCount = 46634 this.update.down().childNodes.length; 46635 for (var i = 0; i < this.entryCount; i++) { 46636 var entry = this.getEntry(i); 46637 entry.autocompleteIndex = i; 46638 this.addObservers(entry); 46639 } 46640 } else { 46641 this.entryCount = 0; 46642 } 46643 46644 this.stopIndicator(); 46645 this.index = 0; 46646 46647 if(this.entryCount==1 && this.options.autoSelect) { 46648 this.selectEntry(); 46649 this.hide(); 46650 } else { 46651 this.render(); 46652 } 46653 } 46654 }, 46655 46656 addObservers: function(element) { 46657 Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this)); 46658 Event.observe(element, "click", this.onClick.bindAsEventListener(this)); 46659 }, 46660 46661 onObserverEvent: function() { 46662 this.changed = false;
46663 this.tokenBounds = null; 46664 if(this.getToken().length>=this.options.minChars) { 46665 this.getUpdatedChoices(); 46666 } else { 46667 this.active = false; 46668 this.hide(); 46669 } 46670 this.oldElementValue = this.element.value; 46671 }, 46672 46673 getToken: function() { 46674 var bounds = this.getTokenBounds(); 46675 return this.element.value.substring(bounds[0], bounds[1]).strip(); 46676 }, 46677 46678 getTokenBounds: function() { 46679 if (null != this.tokenBounds) return this.tokenBounds; 46680 var value = this.element.value; 46681 if (value.strip().empty()) return [-1, 0]; 46682 var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue); 46683 var offset = (diff == this.oldElementValue.length ? 1 : 0); 46684 var prevTokenPos = -1, nextTokenPos = value.length; 46685 var tp; 46686 for (var index = 0, l = this.options.tokens.length; index < l; ++index) { 46687 tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1); 46688 if (tp > prevTokenPos) prevTokenPos = tp; 46689 tp = value.indexOf(this.options.tokens[index], diff + offset); 46690 if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp; 46691 } 46692 return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]); 46693 } 46694}); 46695 46696Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) { 46697 var boundary = Math.min(newS.length, oldS.length); 46698 for (var index = 0; index < boundary; ++index) 46699 if (newS[index] != oldS[index]) 46700 return index; 46701 return boundary; 46702}; 46703 46704Ajax.Autocompleter = Class.create(Autocompleter.Base, { 46705 initialize: function(element, update, url, options) { 46706 this.baseInitialize(element, update, options); 46707 this.options.asynchronous = true; 46708 this.options.onComplete = this.onComplete.bind(this); 46709 this.options.defaultParams = this.options.parameters || null; 46710 this.url = url; 46711 }, 46712 46713 getUpdatedChoices: function() { 46714 this.startIndicator(); 46715 46716 var entry = encodeURIComponent(this.options.paramName) + '=' + 46717 encodeURIComponent(this.getToken()); 46718 46719 this.options.parameters = this.options.callback ? 46720 this.options.callback(this.element, entry) : entry; 46721 46722 if(this.options.defaultParams) 46723 this.options.parameters += '&' + this.options.defaultParams; 46724 46725 new Ajax.Request(this.url, this.options); 46726 }, 46727 46728 onComplete: function(request) { 46729 this.updateChoices(request.responseText); 46730 } 46731}); 46732 46733// The local array autocompleter. Used when you'd prefer to 46734// inject an array of autocompletion options into the page, rather 46735// than sending out Ajax queries, which can be quite slow sometimes. 46736// 46737// The constructor takes four parameters. The first two are, as usual, 46738// the id of the monitored textbox, and id of the autocompletion menu. 46739// The third is the array you want to autocomplete from, and the fourth 46740// is the options block. 46741// 46742// Extra local autocompletion options: 46743// - choices - How many autocompletion choices to offer 46744//
46745// - partialSearch - If false, the autocompleter will match entered 46746// text only at the beginning of strings in the 46747// autocomplete array. Defaults to true, which will 46748// match text at the beginning of any *word* in the 46749// strings in the autocomplete array. If you want to 46750// search anywhere in the string, additionally set 46751// the option fullSearch to true (default: off). 46752// 46753// - fullSsearch - Search anywhere in autocomplete array strings. 46754// 46755// - partialChars - How many characters to enter before triggering 46756// a partial match (unlike minChars, which defines 46757// how many characters are required to do any match 46758// at all). Defaults to 2. 46759// 46760// - ignoreCase - Whether to ignore case when autocompleting. 46761// Defaults to true. 46762// 46763// It's possible to pass in a custom function as the 'selector' 46764// option, if you prefer to write your own autocompletion logic. 46765// In that case, the other options above will not apply unless 46766// you support them. 46767 46768Autocompleter.Local = Class.create(Autocompleter.Base, { 46769 initialize: function(element, update, array, options) { 46770 this.baseInitialize(element, update, options); 46771 this.options.array = array; 46772 }, 46773 46774 getUpdatedChoices: function() { 46775 this.updateChoices(this.options.selector(this)); 46776 }, 46777 46778 setOptions: function(options) { 46779 this.options = Object.extend({ 46780 choices: 10, 46781 partialSearch: true, 46782 partialChars: 2, 46783 ignoreCase: true, 46784 fullSearch: false, 46785 selector: function(instance) { 46786 var ret = []; // Beginning matches 46787 var partial = []; // Inside matches 46788 var entry = instance.getToken(); 46789 var count = 0; 46790 46791 for (var i = 0; i < instance.options.array.length && 46792 ret.length < instance.options.choices ; i++) { 46793 46794 var elem = instance.options.array[i]; 46795 var foundPos = instance.options.ignoreCase ? 46796 elem.toLowerCase().indexOf(entry.toLowerCase()) : 46797 elem.indexOf(entry); 46798 46799 while (foundPos != -1) { 46800 if (foundPos == 0 && elem.length != entry.length) { 46801 ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" + 46802 elem.substr(entry.length) + "</li>"); 46803 break; 46804 } else if (entry.length >= instance.options.partialChars && 46805 instance.options.partialSearch && foundPos != -1) { 46806 if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) { 46807 partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" + 46808 elem.substr(foundPos, entry.length) + "</strong>" + elem.substr( 46809 foundPos + entry.length) + "</li>"); 46810 break; 46811 } 46812 } 46813 46814 foundPos = instance.options.ignoreCase ? 46815 elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : 46816 elem.indexOf(entry, foundPos + 1); 46817 46818 } 46819 } 46820 if (partial.length) 46821 ret = ret.concat(partial.slice(0, instance.options.choices - ret.length)); 46822 return "<ul>" + ret.join('') + "</ul>"; 46823 } 46824 }, options || { }); 46825 } 46826}); 46827 46828// AJAX in-place editor and collection editor 46829// Full rewrite by Christophe Porteneuve <[email protected]> (April 2007). 46830 46831// Use this if you notice weird scrolling problems on some browsers, 46832// the DOM might be a bit confused when this gets called so do this 46833// waits 1 ms (with setTimeout) until it does the activation 46834Field.scrollFreeActivate = function(field) { 46835 setTimeout(function() { 46836 Field.activate(field); 46837 }, 1); 46838}; 46839 46840Ajax.InPlaceEditor = Class.create({ 46841 initialize: function(element, url, options) { 46842 this.url = url; 46843 this.element = element = $(element); 46844 this.prepareOptions(); 46845 this._controls = { }; 46846 arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!! 46847 Object.extend(this.options, options || { });
46848 if (!this.options.formId && this.element.id) { 46849 this.options.formId = this.element.id + '-inplaceeditor'; 46850 if ($(this.options.formId)) 46851 this.options.formId = ''; 46852 } 46853 if (this.options.externalControl) 46854 this.options.externalControl = $(this.options.externalControl); 46855 if (!this.options.externalControl) 46856 this.options.externalControlOnly = false; 46857 this._originalBackground = this.element.getStyle('background-color') || 'transparent'; 46858 this.element.title = this.options.clickToEditText; 46859 this._boundCancelHandler = this.handleFormCancellation.bind(this); 46860 this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this); 46861 this._boundFailureHandler = this.handleAJAXFailure.bind(this); 46862 this._boundSubmitHandler = this.handleFormSubmission.bind(this); 46863 this._boundWrapperHandler = this.wrapUp.bind(this); 46864 this.registerListeners(); 46865 }, 46866 checkForEscapeOrReturn: function(e) { 46867 if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return; 46868 if (Event.KEY_ESC == e.keyCode) 46869 this.handleFormCancellation(e); 46870 else if (Event.KEY_RETURN == e.keyCode) 46871 this.handleFormSubmission(e); 46872 }, 46873 createControl: function(mode, handler, extraClasses) { 46874 var control = this.options[mode + 'Control']; 46875 var text = this.options[mode + 'Text']; 46876 if ('button' == control) { 46877 var btn = document.createElement('input'); 46878 btn.type = 'submit'; 46879 btn.value = text; 46880 btn.className = 'editor_' + mode + '_button'; 46881 if ('cancel' == mode) 46882 btn.onclick = this._boundCancelHandler; 46883 this._form.appendChild(btn); 46884 this._controls[mode] = btn; 46885 } else if ('link' == control) { 46886 var link = document.createElement('a'); 46887 link.href = '#'; 46888 link.appendChild(document.createTextNode(text)); 46889 link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler; 46890 link.className = 'editor_' + mode + '_link'; 46891 if (extraClasses) 46892 link.className += ' ' + extraClasses; 46893 this._form.appendChild(link); 46894 this._controls[mode] = link; 46895 } 46896 }, 46897 createEditField: function() { 46898 var text = (this.options.loadTextURL ? this.options.loadingText : this.getText()); 46899 var fld; 46900 if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) { 46901 fld = document.createElement('input'); 46902 fld.type = 'text'; 46903 var size = this.options.size || this.options.cols || 0; 46904 if (0 < size) fld.size = size; 46905 } else { 46906 fld = document.createElement('textarea'); 46907 fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows); 46908 fld.cols = this.options.cols || 40; 46909 } 46910 fld.name = this.options.paramName; 46911 fld.value = text; // No HTML breaks conversion anymore 46912 fld.className = 'editor_field'; 46913 if (this.options.submitOnBlur) 46914 fld.onblur = this._boundSubmitHandler; 46915 this._controls.editor = fld; 46916 if (this.options.loadTextURL) 46917 this.loadExternalText(); 46918 this._form.appendChild(this._controls.editor); 46919 }, 46920 createForm: function() { 46921 var ipe = this; 46922 function addText(mode, condition) { 46923 var text = ipe.options['text' + mode + 'Controls']; 46924 if (!text || condition === false) return; 46925 ipe._form.appendChild(document.createTextNode(text)); 46926 }; 46927 this._form = $(document.createElement('form')); 46928 this._form.id = this.options.formId; 46929 this._form.addClassName(this.options.formClassName); 46930 this._form.onsubmit = this._boundSubmitHandler; 46931 this.createEditField(); 46932 if ('textarea' == this._controls.editor.tagName.toLowerCase()) 46933 this._form.appendChild(document.createElement('br')); 46934 if (this.options.onFormCustomization) 46935 this.options.onFormCustomization(this, this._form); 46936 addText('Before', this.options.okControl || this.options.cancelControl); 46937 this.createControl('ok', this._boundSubmitHandler); 46938 addText('Between', this.options.okControl && this.options.cancelControl); 46939 this.createControl('cancel', this._boundCancelHandler, 'editor_cancel'); 46940 addText('After', this.options.okControl || this.options.cancelControl); 46941 }, 46942 destroy: function() { 46943 if (this._oldInnerHTML) 46944 this.element.innerHTML = this._oldInnerHTML; 46945 this.leaveEditMode(); 46946 this.unregisterListeners(); 46947 }, 46948 enterEditMode: function(e) { 46949 if (this._saving || this._editing) return; 46950 this._editing = true; 46951 this.triggerCallback('onEnterEditMode'); 46952 if (this.options.externalControl) 46953 this.options.externalControl.hide(); 46954 this.element.hide(); 46955 this.createForm(); 46956 this.element.parentNode.insertBefore(this._form, this.element); 46957 if (!this.options.loadTextURL) 46958 this.postProcessEditField();
46959 if (e) Event.stop(e); 46960 }, 46961 enterHover: function(e) { 46962 if (this.options.hoverClassName) 46963 this.element.addClassName(this.options.hoverClassName); 46964 if (this._saving) return; 46965 this.triggerCallback('onEnterHover'); 46966 }, 46967 getText: function() { 46968 return this.element.innerHTML.unescapeHTML(); 46969 }, 46970 handleAJAXFailure: function(transport) { 46971 this.triggerCallback('onFailure', transport); 46972 if (this._oldInnerHTML) { 46973 this.element.innerHTML = this._oldInnerHTML; 46974 this._oldInnerHTML = null; 46975 } 46976 }, 46977 handleFormCancellation: function(e) { 46978 this.wrapUp(); 46979 if (e) Event.stop(e); 46980 }, 46981 handleFormSubmission: function(e) { 46982 var form = this._form; 46983 var value = $F(this._controls.editor); 46984 this.prepareSubmission(); 46985 var params = this.options.callback(form, value) || ''; 46986 if (Object.isString(params)) 46987 params = params.toQueryParams(); 46988 params.editorId = this.element.id; 46989 if (this.options.htmlResponse) { 46990 var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions); 46991 Object.extend(options, { 46992 parameters: params, 46993 onComplete: this._boundWrapperHandler, 46994 onFailure: this._boundFailureHandler 46995 }); 46996 new Ajax.Updater({ success: this.element }, this.url, options); 46997 } else { 46998 var options = Object.extend({ method: 'get' }, this.options.ajaxOptions); 46999 Object.extend(options, { 47000 parameters: params, 47001 onComplete: this._boundWrapperHandler, 47002 onFailure: this._boundFailureHandler 47003 }); 47004 new Ajax.Request(this.url, options); 47005 } 47006 if (e) Event.stop(e); 47007 }, 47008 leaveEditMode: function() { 47009 this.element.removeClassName(this.options.savingClassName); 47010 this.removeForm(); 47011 this.leaveHover(); 47012 this.element.style.backgroundColor = this._originalBackground; 47013 this.element.show(); 47014 if (this.options.externalControl) 47015 this.options.externalControl.show(); 47016 this._saving = false; 47017 this._editing = false; 47018 this._oldInnerHTML = null; 47019 this.triggerCallback('onLeaveEditMode'); 47020 }, 47021 leaveHover: function(e) { 47022 if (this.options.hoverClassName) 47023 this.element.removeClassName(this.options.hoverClassName); 47024 if (this._saving) return; 47025 this.triggerCallback('onLeaveHover'); 47026 }, 47027 loadExternalText: function() { 47028 this._form.addClassName(this.options.loadingClassName); 47029 this._controls.editor.disabled = true; 47030 var options = Object.extend({ method: 'get' }, this.options.ajaxOptions); 47031 Object.extend(options, { 47032 parameters: 'editorId=' + encodeURIComponent(this.element.id), 47033 onComplete: Prototype.emptyFunction, 47034 onSuccess: function(transport) { 47035 this._form.removeClassName(this.options.loadingClassName); 47036 var text = transport.responseText; 47037 if (this.options.stripLoadedTextTags) 47038 text = text.stripTags(); 47039 this._controls.editor.value = text; 47040 this._controls.editor.disabled = false; 47041 this.postProcessEditField(); 47042 }.bind(this), 47043 onFailure: this._boundFailureHandler 47044 }); 47045 new Ajax.Request(this.options.loadTextURL, options); 47046 }, 47047 postProcessEditField: function() { 47048 var fpc = this.options.fieldPostCreation; 47049 if (fpc) 47050 $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate'](); 47051 }, 47052 prepareOptions: function() { 47053 this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions); 47054 Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks); 47055 [this._extraDefaultOptions].flatten().compact().each(function(defs) { 47056 Object.extend(this.options, defs); 47057 }.bind(this)); 47058 }, 47059 prepareSubmission: function() { 47060 this._saving = true; 47061 this.removeForm(); 47062 this.leaveHover(); 47063 this.showSaving(); 47064 }, 47065 registerListeners: function() { 47066 this._listeners = { }; 47067 var listener; 47068 $H(Ajax.InPlaceEditor.Listeners).each(function(pair) { 47069 listener = this[pair.value].bind(this); 47070 this._listeners[pair.key] = listener; 47071 if (!this.options.externalControlOnly) 47072 this.element.observe(pair.key, listener); 47073 if (this.options.externalControl) 47074 this.options.externalControl.observe(pair.key, listener); 47075 }.bind(this)); 47076 }, 47077 removeForm: function() { 47078 if (!this._form) return; 47079 this._form.remove(); 47080 this._form = null; 47081 this._controls = { }; 47082 }, 47083 showSaving: function() { 47084 this._oldInnerHTML = this.element.innerHTML; 47085 this.element.innerHTML = this.options.savingText; 47086 this.element.addClassName(this.options.savingClassName); 47087 this.element.style.backgroundColor = this._originalBackgroun
47087d; 47088 this.element.show(); 47089 }, 47090 triggerCallback: function(cbName, arg) { 47091 if ('function' == typeof this.options[cbName]) { 47092 this.options[cbName](this, arg); 47093 } 47094 }, 47095 unregisterListeners: function() { 47096 $H(this._listeners).each(function(pair) { 47097 if (!this.options.externalControlOnly) 47098 this.element.stopObserving(pair.key, pair.value); 47099 if (this.options.externalControl) 47100 this.options.externalControl.stopObserving(pair.key, pair.value); 47101 }.bind(this)); 47102 }, 47103 wrapUp: function(transport) { 47104 this.leaveEditMode(); 47105 // Can't use triggerCallback due to backward compatibility: requires 47106 // binding + direct element 47107 this._boundComplete(transport, this.element); 47108 } 47109}); 47110 47111Object.extend(Ajax.InPlaceEditor.prototype, { 47112 dispose: Ajax.InPlaceEditor.prototype.destroy 47113}); 47114 47115Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, { 47116 initialize: function($super, element, url, options) { 47117 this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions; 47118 $super(element, url, options); 47119 }, 47120 47121 createEditField: function() { 47122 var list = document.createElement('select'); 47123 list.name = this.options.paramName; 47124 list.size = 1; 47125 this._controls.editor = list; 47126 this._collection = this.options.collection || []; 47127 if (this.options.loadCollectionURL) 47128 this.loadCollection(); 47129 else 47130 this.checkForExternalText(); 47131 this._form.appendChild(this._controls.editor); 47132 }, 47133 47134 loadCollection: function() { 47135 this._form.addClassName(this.options.loadingClassName); 47136 this.showLoadingText(this.options.loadingCollectionText); 47137 var options = Object.extend({ method: 'get' }, this.options.ajaxOptions); 47138 Object.extend(options, { 47139 parameters: 'editorId=' + encodeURIComponent(this.element.id), 47140 onComplete: Prototype.emptyFunction, 47141 onSuccess: function(transport) { 47142 var js = transport.responseText.strip(); 47143 if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check 47144 throw('Server returned an invalid collection representation.'); 47145 this._collection = eval(js); 47146 this.checkForExternalText(); 47147 }.bind(this), 47148 onFailure: this.onFailure 47149 }); 47150 new Ajax.Request(this.options.loadCollectionURL, options); 47151 }, 47152 47153 showLoadingText: function(text) { 47154 this._controls.editor.disabled = true; 47155 var tempOption = this._controls.editor.firstChild; 47156 if (!tempOption) { 47157 tempOption = document.createElement('option'); 47158 tempOption.value = ''; 47159 this._controls.editor.appendChild(tempOption); 47160 tempOption.selected = true; 47161 } 47162 tempOption.update((text || '').stripScripts().stripTags()); 47163 }, 47164 47165 checkForExternalText: function() { 47166 this._text = this.getText(); 47167 if (this.options.loadTextURL) 47168 this.loadExternalText(); 47169 else 47170 this.buildOptionList(); 47171 }, 47172 47173 loadExternalText: function() { 47174 this.showLoadingText(this.options.loadingText); 47175 var options = Object.extend({ method: 'get' }, this.options.ajaxOptions); 47176 Object.extend(options, { 47177 parameters: 'editorId=' + encodeURIComponent(this.element.id), 47178 onComplete: Prototype.emptyFunction, 47179 onSuccess: function(transport) { 47180 this._text = transport.responseText.strip(); 47181 this.buildOptionList(); 47182 }.bind(this), 47183 onFailure: this.onFailure 47184 }); 47185 new Ajax.Request(this.options.loadTextURL, options); 47186 }, 47187 47188 buildOptionList: function() { 47189 this._form.removeClassName(this.options.loadingClassName); 47190 this._collection = this._collection.map(function(entry) { 47191 return 2 === entry.length ? entry : [entry, entry].flatten(); 47192 }); 47193 var marker = ('value' in this.options) ? this.options.value : this._text; 47194 var textFound = this._collection.any(function(entry) { 47195 return entry[0] == marker; 47196 }.bind(this)); 47197 this._controls.editor.update(''); 47198 var option; 47199 this._collection.each(function(entry, index) { 47200 option = document.createElement('option'); 47201 option.value = entry[0]; 47202 option.selected = textFound ? entry[0] == marker : 0 == index; 47203 option.appendChild(document.createTextNode(entry[1])); 47204 this._controls.editor.appendChild(option); 47205 }.bind(this)); 47206 this._controls.editor.disabled = false;
47207 Field.scrollFreeActivate(this._controls.editor); 47208 } 47209}); 47210 47211//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! **** 47212//**** This only exists for a while, in order to let **** 47213//**** users adapt to the new API. Read up on the new **** 47214//**** API and convert your code to it ASAP! **** 47215 47216Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) { 47217 if (!options) return; 47218 function fallback(name, expr) { 47219 if (name in options || expr === undefined) return; 47220 options[name] = expr; 47221 }; 47222 fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' : 47223 options.cancelLink == options.cancelButton == false ? false : undefined))); 47224 fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' : 47225 options.okLink == options.okButton == false ? false : undefined))); 47226 fallback('highlightColor', options.highlightcolor); 47227 fallback('highlightEndColor', options.highlightendcolor); 47228}; 47229 47230Object.extend(Ajax.InPlaceEditor, { 47231 DefaultOptions: { 47232 ajaxOptions: { }, 47233 autoRows: 3, // Use when multi-line w/ rows == 1 47234 cancelControl: 'link', // 'link'|'button'|false 47235 cancelText: 'cancel', 47236 clickToEditText: 'Click to edit', 47237 externalControl: null, // id|elt 47238 externalControlOnly: false, 47239 fieldPostCreation: 'activate', // 'activate'|'focus'|false 47240 formClassName: 'inplaceeditor-form', 47241 formId: null, // id|elt 47242 highlightColor: '#ffff99', 47243 highlightEndColor: '#ffffff', 47244 hoverClassName: '', 47245 htmlResponse: true, 47246 loadingClassName: 'inplaceeditor-loading', 47247 loadingText: 'Loading...', 47248 okControl: 'button', // 'link'|'button'|false 47249 okText: 'ok', 47250 paramName: 'value', 47251 rows: 1, // If 1 and multi-line, uses autoRows 47252 savingClassName: 'inplaceeditor-saving', 47253 savingText: 'Saving...', 47254 size: 0, 47255 stripLoadedTextTags: false, 47256 submitOnBlur: false, 47257 textAfterControls: '', 47258 textBeforeControls: '', 47259 textBetweenControls: '' 47260 }, 47261 DefaultCallbacks: { 47262 callback: function(form) { 47263 return Form.serialize(form); 47264 }, 47265 onComplete: function(transport, element) { 47266 // For backward compatibility, this one is bound to the IPE, and passes 47267 // the element directly. It was too often customized, so we don't break it. 47268 new Effect.Highlight(element, { 47269 startcolor: this.options.highlightColor, keepBackgroundImage: true }); 47270 }, 47271 onEnterEditMode: null, 47272 onEnterHover: function(ipe) { 47273 ipe.element.style.backgroundColor = ipe.options.highlightColor; 47274 if (ipe._effect) 47275 ipe._effect.cancel(); 47276 }, 47277 onFailure: function(transport, ipe) { 47278 alert('Error communication with the server: ' + transport.responseText.stripTags()); 47279 }, 47280 onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls. 47281 onLeaveEditMode: null, 47282 onLeaveHover: function(ipe) { 47283 ipe._effect = new Effect.Highlight(ipe.element, { 47284 startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor, 47285 restorecolor: ipe._originalBackground, keepBackgroundImage: true 47286 }); 47287 } 47288 }, 47289 Listeners: { 47290 click: 'enterEditMode', 47291 keydown: 'checkForEscapeOrReturn', 47292 mouseover: 'enterHover', 47293 mouseout: 'leaveHover' 47294 } 47295}); 47296 47297Ajax.InPlaceCollectionEditor.DefaultOptions = { 47298 loadingCollectionText: 'Loading options...' 47299}; 47300 47301// Delayed observer, like Form.Element.Observer, 47302// but waits for delay after last key input 47303// Ideal for live-search fields 47304 47305Form.Element.DelayedObserver = Class.create({ 47306 initialize: function(element, delay, callback) { 47307 this.delay = delay || 0.5; 47308 this.element = $(element); 47309 this.callback = callback; 47310 this.timer = null; 47311 this.lastValue = $F(this.element); 47312 Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this)); 47313 }, 47314 delayedListener: function(event) { 47315 if(this.lastValue == $F(this.element)) return; 47316 if(this.timer) clearTimeout(this.timer); 47317 this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000); 47318 this.lastValue = $F(this.element); 47319 },
47320 onTimerEvent: function() { 47321 this.timer = null; 47322 this.callback(this.element, $F(this.element)); 47323 } 47324}); 47325// script.aculo.us scriptaculous.js v1.8.3, Thu Oct 08 11:23:33 +0200 2009 47326 47327// Copyright (c) 2005-2009 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) 47328// 47329// Permission is hereby granted, free of charge, to any person obtaining 47330// a copy of this software and associated documentation files (the 47331// "Software"), to deal in the Software without restriction, including 47332// without limitation the rights to use, copy, modify, merge, publish, 47333// distribute, sublicense, and/or sell copies of the Software, and to 47334// permit persons to whom the Software is furnished to do so, subject to 47335// the following conditions: 47336// 47337// The above copyright notice and this permission notice shall be 47338// included in all copies or substantial portions of the Software. 47339// 47340// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 47341// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 47342// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 47343// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 47344// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 47345// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 47346// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 47347// 47348// For details, see the script.aculo.us web site: http://script.aculo.us/ 47349 47350var Scriptaculous = { 47351 Version: '1.8.3', 47352 require: function(libraryName) { 47353 try{ 47354 // inserting via DOM fails in Safari 2.0, so brute force approach
47355 document.write('<script type="text/javascript" src="'+libraryName+'"><\/script>'); 47356 } catch(e) { 47357 // for xhtml+xml served content, fall back to DOM methods 47358 var script = document.createElement('script'); 47359 script.type = 'text/javascript'; 47360 script.src = libraryName; 47361 document.getElementsByTagName('head')[0].appendChild(script); 47362 } 47363 }, 47364 REQUIRED_PROTOTYPE: '1.6.0.3', 47365 load: function() { 47366 function convertVersionString(versionString) { 47367 var v = versionString.replace(/_.*|\./g, ''); 47368 v = parseInt(v + '0'.times(4-v.length)); 47369 return versionString.indexOf('_') > -1 ? v-1 : v; 47370 } 47371 47372 if((typeof Prototype=='undefined') || 47373 (typeof Element == 'undefined') || 47374 (typeof Element.Methods=='undefined') || 47375 (convertVersionString(Prototype.Version) < 47376 convertVersionString(Scriptaculous.REQUIRED_PROTOTYPE))) 47377 throw("script.aculo.us requires the Prototype JavaScript framework >= " + 47378 Scriptaculous.REQUIRED_PROTOTYPE); 47379 47380 var js = /scriptaculous\.js(\?.*)?$/; 47381 $$('head script[src]').findAll(function(s) { 47382 return s.src.match(js); 47383 }).each(function(s) { 47384 var path = s.src.replace(js, ''), 47385 includes = s.src.match(/\?.*load=([a-z,]*)/); 47386 (includes ? includes[1] : 'builder,effects,dragdrop,controls,slider,sound').split(',').each( 47387 function(include) { Scriptaculous.require(path+include+'.js') }); 47388 }); 47389 } 47390}; 47391 47392Scriptaculous.load(); 47393var __debugging__ = false; 47394 47395 47396function trace(msg) { 47397 if(!__debugging__) 47398 return; 47399 47400 var dbg = $('debug_monitor'); 47401 dbg.value += "\n" + msg + "\n"; 47402 dbg.scrollTop = dbg.scrollHeight; 47403} 47404 47405function inspect(obj) { 47406 var s = ""; 47407 for(key in obj) { 47408 s += "\nkey: " + key + ", value: " + obj[key]; 47409 } 47410 return(s); 47411} 47412 47413function setupDebug(dbg) { 47414 __debugging__= dbg; 47415 if(!__debugging__) 47416 return; 47417 47418 $('debug_window').show(); 47419 $('debug_monitor').value = ""; 47420 new Draggable('debug_window', {handle:'debug_handle'}); 47421 $('debug_window').setOpacity(0.5); 47422 $('debug_window').observe('mouseover',overDebug, false); 47423 $('debug_window').observe('mouseout',offDebug, false); 47424 $('debug_opener').observe('click',toggleDebug, false); 47425 47426 trace("Debug started"); 47427} 47428 47429function overDebug(evt) { 47430 $('debug_window').setOpacity(1); 47431} 47432 47433function offDebug(evt) { 47434 $('debug_window').setOpacity(0.5); 47435} 47436 47437function toggleDebug(evt) { 47438 evt = (evt) ? evt : event; 47439 var sender = (evt.target) ? evt.target : evt.srcElement; 47440 47441 if(sender.innerHTML == '+') { 47442 Effect.BlindDown('debug_monitor', {duration:0.2}); 47443 sender.innerHTML = '-'; 47444 } else { 47445 Effect.BlindUp('debug_monitor', {duration:0.2}); 47446 sender.innerHTML = '+'; 47447 } 47448} 47449 47450setupDebug(); 47451// script.aculo.us builder.js v1.8.3, Thu Oct 08 11:23:33 +0200 2009 47452 47453// Copyright (c) 2005-2009 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) 47454// 47455// script.aculo.us is freely distributable under the terms of an MIT-style license. 47456// For details, see the script.aculo.us web site: http://script.aculo.us/ 47457 47458var Builder = { 47459 NODEMAP: { 47460 AREA: 'map', 47461 CAPTION: 'table', 47462 COL: 'table', 47463 COLGROUP: 'table', 47464 LEGEND: 'fieldset', 47465 OPTGROUP: 'select', 47466 OPTION: 'select', 47467 PARAM: 'object', 47468 TBODY: 'table', 47469 TD: 'table', 47470 TFOOT: 'table', 47471 TH: 'table', 47472 THEAD: 'table', 47473 TR: 'table' 47474 }, 47475 // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken, 47476 // due to a Firefox bug 47477 node: function(elementName) { 47478 elementName = elementName.toUpperCase(); 47479 47480 // try innerHTML approach 47481 var parentTag = this.NODEMAP[elementName] || 'div'; 47482 var parentElement = document.createElement(parentTag); 47483 try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707 47484 parentElement.innerHTML = "<" + elementName + "></" + elementName + ">"; 47485 } catch(e) {} 47486 var element = parentElement.firstChild || null; 47487 47488 // see if browser added wrapping tags 47489 if(element && (element.tagName.toUpperCase() != elementName)) 47490 element = element.getElementsByTagName(elementName)[0]; 47491 47492 // fallback to createElement approach 47493 if(!element) element = document.createElement(elementName); 47494 47495 // abort if nothing could be created 47496 if(!element) return; 47497 47498 // attributes (or text) 47499 if(arguments[1]) 47500 if(this._isStringOrNumber(arguments[1]) || 47501 (arguments[1] instanceof Array) || 47502 arguments[1].tagName) { 47503 this._children(element, arguments[1]); 47504 } else { 47505 var attrs = this._attributes(arguments[1]); 47506 if(attrs.length) { 47507 try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707 47508 parentElement.innerHTML = "<" +elementName + " " + 47509 attrs + "></" + elementName + ">"; 47510 } catch(e) {} 47511 element = parentElement.firstChild || null; 47512 // workaround firefox 1.0.X bug 47513 if(!element) { 47514 element = document.createElement(elementName); 47515 for(attr in arguments[1]) 47516 element[attr == 'class' ? 'className' : attr] = arguments[1][attr]; 47517 } 47518 if(element.tagName.toUpperCase() != elementName) 47519 element = parentElement.getElementsByTagName(elementName)[0]; 47520 } 47521 } 47522 47523 // text, or array of children 47524 if(arguments[2]) 47525 this._children(element, arguments[2]); 47526 47527 return $(element); 47528 }, 47529 _text: function(text) { 47530 return document.createTextNode(text); 47531 }, 47532 47533 ATTR_MAP: { 47534 'className': 'class', 47535 'htmlFor': 'for' 47536 }, 47537 47538 _attributes: function(attributes) { 47539 var attrs = []; 47540 for(attribute in attributes) 47541 attrs.push((attribute in this.ATTR_MAP ? this.ATTR_MAP[attribute] : attribute) + 47542 '="' + attributes[attribute].toString().escapeHTML().gsub(/"/,'"') + '"'); 47543 return attrs.join(" "); 47544 }, 47545 _children: function(element, children) { 47546 if(children.tagName) { 47547 element.appendChild(children); 47548 return; 47549 } 47550 if(typeof children=='object') { // array can hold nodes and text 47551 children.flatten().each( function(e) { 47552 if(typeof e=='object') 47553 element.appendChild(e); 47554 else 47555 if(Builder._isStringOrNumber(e)) 47556 element.appendChild(Builder._text(e)); 47557 }); 47558 } else 47559 if(Builder._isStringOrNumber(children)) 47560 element.appendChild(Builder._text(children)); 47561 }, 47562 _isStringOrNumber: function(param) {
47563 return(typeof param=='string' || typeof param=='number'); 47564 }, 47565 build: function(html) { 47566 var element = this.node('div'); 47567 $(element).update(html.strip()); 47568 return element.down(); 47569 }, 47570 dump: function(scope) { 47571 if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope 47572 47573 var tags = ("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY " + 47574 "BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET " + 47575 "FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+ 47576 "KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+ 47577 "PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+ 47578 "TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/); 47579 47580 tags.each( function(tag){ 47581 scope[tag] = function() { 47582 return Builder.node.apply(Builder, [tag].concat($A(arguments))); 47583 }; 47584 }); 47585 } 47586}; 47587// script.aculo.us dragdrop.js v1.8.3, Thu Oct 08 11:23:33 +0200 2009 47588 47589// Copyright (c) 2005-2009 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) 47590// 47591// script.aculo.us is freely distributable under the terms of an MIT-style license. 47592// For details, see the script.aculo.us web site: http://script.aculo.us/ 47593 47594if(Object.isUndefined(Effect)) 47595 throw("dragdrop.js requires including script.aculo.us' effects.js library"); 47596 47597var Droppables = { 47598 drops: [], 47599 47600 remove: function(element) { 47601 this.drops = this.drops.reject(function(d) { return d.element==$(element) }); 47602 }, 47603 47604 add: function(element) { 47605 element = $(element); 47606 var options = Object.extend({ 47607 greedy: true, 47608 hoverclass: null, 47609 tree: false 47610 }, arguments[1] || { }); 47611
47612 // cache containers 47613 if(options.containment) { 47614 options._containers = []; 47615 var containment = options.containment; 47616 if(Object.isArray(containment)) { 47617 containment.each( function(c) { options._containers.push($(c)) }); 47618 } else { 47619 options._containers.push($(containment)); 47620 } 47621 } 47622 47623 if(options.accept) options.accept = [options.accept].flatten(); 47624 47625 Element.makePositioned(element); // fix IE 47626 options.element = element; 47627 47628 this.drops.push(options); 47629 }, 47630 47631 findDeepestChild: function(drops) { 47632 deepest = drops[0]; 47633 47634 for (i = 1; i < drops.length; ++i) 47635 if (Element.isParent(drops[i].element, deepest.element)) 47636 deepest = drops[i]; 47637 47638 return deepest; 47639 }, 47640 47641 isContained: function(element, drop) { 47642 var containmentNode; 47643 if(drop.tree) { 47644 containmentNode = element.treeNode; 47645 } else { 47646 containmentNode = element.parentNode; 47647 } 47648 return drop._containers.detect(function(c) { return containmentNode == c }); 47649 }, 47650 47651 isAffected: function(point, element, drop) { 47652 return ( 47653 (drop.element!=element) && 47654 ((!drop._containers) || 47655 this.isContained(element, drop)) && 47656 ((!drop.accept) || 47657 (Element.classNames(element).detect( 47658 function(v) { return drop.accept.include(v) } ) )) && 47659 Position.within(drop.element, point[0], point[1]) ); 47660 }, 47661 47662 deactivate: function(drop) { 47663 if(drop.hoverclass) 47664 Element.removeClassName(drop.element, drop.hoverclass); 47665 this.last_active = null; 47666 }, 47667 47668 activate: function(drop) { 47669 if(drop.hoverclass) 47670 Element.addClassName(drop.element, drop.hoverclass); 47671 this.last_active = drop; 47672 }, 47673 47674 show: function(point, element) { 47675 if(!this.drops.length) return; 47676 var drop, affected = []; 47677 47678 this.drops.each( function(drop) { 47679 if(Droppables.isAffected(point, element, drop)) 47680 affected.push(drop); 47681 }); 47682 47683 if(affected.length>0) 47684 drop = Droppables.findDeepestChild(affected); 47685 47686 if(this.last_active && this.last_active != drop) this.deactivate(this.last_active); 47687 if (drop) { 47688 Position.within(drop.element, point[0], point[1]); 47689 if(drop.onHover) 47690 drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element)); 47691 47692 if (drop != this.last_active) Droppables.activate(drop); 47693 } 47694 }, 47695 47696 fire: function(event, element) { 47697 if(!this.last_active) return; 47698 Position.prepare(); 47699 47700 if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active)) 47701 if (this.last_active.onDrop) { 47702 this.last_active.onDrop(element, this.last_active.element, event); 47703 return true; 47704 } 47705 }, 47706 47707 reset: function() { 47708 if(this.last_active) 47709 this.deactivate(this.last_active); 47710 } 47711}; 47712 47713var Draggables = { 47714 drags: [], 47715 observers: [], 47716 47717 register: function(draggable) { 47718 if(this.drags.length == 0) { 47719 this.eventMouseUp = this.endDrag.bindAsEventListener(this); 47720 this.eventMouseMove = this.updateDrag.bindAsEventListener(this); 47721 this.eventKeypress = this.keyPress.bindAsEventListener(this); 47722 47723 Event.observe(document, "mouseup", this.eventMouseUp); 47724 Event.observe(document, "mousemove", this.eventMouseMove); 47725 Event.observe(document, "keypress", this.eventKeypress); 47726 } 47727 this.drags.push(draggable); 47728 }, 47729 47730 unregister: function(draggable) { 47731 this.drags = this.drags.reject(function(d) { return d==draggable }); 47732 if(this.drags.length == 0) { 47733 Event.stopObserving(document, "mouseup", this.eventMouseUp); 47734 Event.stopObserving(document, "mousemove", this.eventMouseMove); 47735 Event.stopObserving(document, "keypress", this.eventKeypress); 47736 } 47737 }, 47738 47739 activate: function(draggable) { 47740 if(draggable.options.delay) { 47741 this._timeout = setTimeout(function() { 47742 Draggables._timeout = null; 47743 window.focus(); 47744 Draggables.activeDraggable = draggable; 47745 }.bind(this), draggable.options.delay); 47746 } else { 47747 window.focus(); // allows keypress events if window isn't currently focused, fails for Safari 47748 this.activeDraggable = draggable; 47749 } 47750 }, 47751 47752 deactivate: function() { 47753 this.activeDraggable = null; 47754 }, 47755 47756 updateDrag: function(event) { 47757 if(!this.activeDraggable) return; 47758 var pointer = [Event.pointerX(event), Event.pointerY(event)]; 47759 // Mozilla-based browsers fire successive mousemove events with 47760 // the same coordinates, prevent needless redrawing (moz bug?) 47761 if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return; 47762 this._lastPointer = pointer; 47763 47764 this.activeDraggable.updateDrag(event, pointer); 47765 }, 47766 47767 endDrag: function(event) { 47768 if(this._timeout) { 47769 clearTimeout(this._timeout); 47770 this._timeout = null; 47771 } 47772 if(!this.activeDraggable) return; 47773 this._lastPointer = null; 47774 this.activeDraggable.endDrag(event); 47775 this.activeDraggable = null; 47776 }, 47777 47778 keyPress: function(event) { 47779 if(this.activeDraggable) 47780 this.activeDraggable.keyPress(event); 47781 }, 47782 47783 addObserver: function(observer) { 47784 this.observers.push(observer); 47785 this._cacheObserverCallbacks(); 47786 }, 47787 47788 removeObserver: function(element) { // element instead of observer fixes mem leaks 47789 this.observers = this.observers.reject( function(o) { return o.element==element }); 47790 this._cacheObserverCallbacks(); 47791 }, 47792 47793 notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag' 47794 if(this[eventName+'Count'] > 0) 47795 this.observers.each( function(o) { 47796 if(o[eventName]) o[eventName](eventName, draggable, event); 47797 });
47798 if(draggable.options[eventName]) draggable.options[eventName](draggable, event); 47799 }, 47800 47801 _cacheObserverCallbacks: function() { 47802 ['onStart','onEnd','onDrag'].each( function(eventName) { 47803 Draggables[eventName+'Count'] = Draggables.observers.select( 47804 function(o) { return o[eventName]; } 47805 ).length; 47806 }); 47807 } 47808}; 47809 47810/*--------------------------------------------------------------------------*/ 47811 47812var Draggable = Class.create({ 47813 initialize: function(element) { 47814 var defaults = { 47815 handle: false, 47816 reverteffect: function(element, top_offset, left_offset) { 47817 var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02; 47818 new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur, 47819 queue: {scope:'_draggable', position:'end'} 47820 }); 47821 }, 47822 endeffect: function(element) { 47823 var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0; 47824 new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity, 47825 queue: {scope:'_draggable', position:'end'}, 47826 afterFinish: function(){ 47827 Draggable._dragging[element] = false 47828 } 47829 }); 47830 }, 47831 zindex: 1000, 47832 revert: false, 47833 quiet: false, 47834 scroll: false, 47835 scrollSensitivity: 20, 47836 scrollSpeed: 15, 47837 snap: false, // false, or xy or [x,y] or function(x,y){ return [x,y] } 47838 delay: 0 47839 }; 47840 47841 if(!arguments[1] || Object.isUndefined(arguments[1].endeffect)) 47842 Object.extend(defaults, { 47843 starteffect: function(element) { 47844 element._opacity = Element.getOpacity(element); 47845 Draggable._dragging[element] = true; 47846 new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7}); 47847 } 47848 }); 47849 47850 var options = Object.extend(defaults, arguments[1] || { }); 47851 47852 this.element = $(element); 47853 47854 if(options.handle && Object.isString(options.handle)) 47855 this.handle = this.element.down('.'+options.handle, 0); 47856 47857 if(!this.handle) this.handle = $(options.handle); 47858 if(!this.handle) this.handle = this.element; 47859 47860 if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) { 47861 options.scroll = $(options.scroll); 47862 this._isScrollChild = Element.childOf(this.element, options.scroll); 47863 } 47864 47865 Element.makePositioned(this.element); // fix IE 47866 47867 this.options = options; 47868 this.dragging = false; 47869 47870 this.eventMouseDown = this.initDrag.bindAsEventListener(this); 47871 Event.observe(this.handle, "mousedown", this.eventMouseDown); 47872 47873 Draggables.register(this); 47874 }, 47875 47876 destroy: function() { 47877 Event.stopObserving(this.handle, "mousedown", this.eventMouseDown); 47878 Draggables.unregister(this); 47879 }, 47880 47881 currentDelta: function() { 47882 return([ 47883 parseInt(Element.getStyle(this.element,'left') || '0'), 47884 parseInt(Element.getStyle(this.element,'top') || '0')]); 47885 }, 47886 47887 initDrag: function(event) { 47888 if(!Object.isUndefined(Draggable._dragging[this.element]) && 47889 Draggable._dragging[this.element]) return; 47890 if(Event.isLeftClick(event)) { 47891 // abort on form elements, fixes a Firefox issue 47892 var src = Event.element(event); 47893 if((tag_name = src.tagName.toUpperCase()) && ( 47894 tag_name=='INPUT' || 47895 tag_name=='SELECT' || 47896 tag_name=='OPTION' || 47897 tag_name=='BUTTON' || 47898 tag_name=='TEXTAREA')) return; 47899 47900 var pointer = [Event.pointerX(event), Event.pointerY(event)]; 47901 var pos = this.element.cumulativeOffset(); 47902 this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) }); 47903 47904 Draggables.activate(this); 47905 Event.stop(event); 47906 } 47907 }, 47908 47909 startDrag: function(event) { 47910 this.dragging = true; 47911 if(!this.delta) 47912 this.delta = this.currentDelta(); 47913 47914 if(this.options.zindex) { 47915 this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0); 47916 this.element.style.zIndex = this.options.zindex; 47917 } 47918 47919 if(this.options.ghosting) { 47920 this._clone = this.element.cloneNode(true); 47921 this._originallyAbsolute = (this.element.getStyle('position') == 'absolute'); 47922 if (!this._originallyAbsolute) 47923 Position.absolutize(this.element); 47924 this.element.parentNode.insertBefore(this._clone, this.element); 47925 } 47926 47927 if(this.options.scroll) { 47928 if (this.options.scroll == window) { 47929 var where = this._getWindowScroll(this.options.scroll); 47930 this.originalScrollLeft = where.left; 47931 this.originalScrollTop = where.top; 47932 } else {
47933 this.originalScrollLeft = this.options.scroll.scrollLeft; 47934 this.originalScrollTop = this.options.scroll.scrollTop; 47935 } 47936 } 47937 47938 Draggables.notify('onStart', this, event); 47939 47940 if(this.options.starteffect) this.options.starteffect(this.element); 47941 }, 47942 47943 updateDrag: function(event, pointer) { 47944 if(!this.dragging) this.startDrag(event); 47945 47946 if(!this.options.quiet){ 47947 Position.prepare(); 47948 Droppables.show(pointer, this.element); 47949 } 47950 47951 Draggables.notify('onDrag', this, event); 47952 47953 this.draw(pointer); 47954 if(this.options.change) this.options.change(this); 47955 47956 if(this.options.scroll) { 47957 this.stopScrolling(); 47958 47959 var p; 47960 if (this.options.scroll == window) { 47961 with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; } 47962 } else { 47963 p = Position.page(this.options.scroll); 47964 p[0] += this.options.scroll.scrollLeft + Position.deltaX; 47965 p[1] += this.options.scroll.scrollTop + Position.deltaY; 47966 p.push(p[0]+this.options.scroll.offsetWidth); 47967 p.push(p[1]+this.options.scroll.offsetHeight); 47968 } 47969 var speed = [0,0]; 47970 if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity); 47971 if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity); 47972 if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity); 47973 if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity); 47974 this.startScrolling(speed); 47975 } 47976 47977 // fix AppleWebKit rendering 47978 if(Prototype.Browser.WebKit) window.scrollBy(0,0); 47979 47980 Event.stop(event); 47981 }, 47982 47983 finishDrag: function(event, success) { 47984 this.dragging = false; 47985 47986 if(this.options.quiet){ 47987 Position.prepare(); 47988 var pointer = [Event.pointerX(event), Event.pointerY(event)]; 47989 Droppables.show(pointer, this.element); 47990 } 47991 47992 if(this.options.ghosting) { 47993 if (!this._originallyAbsolute) 47994 Position.relativize(this.element); 47995 delete this._originallyAbsolute; 47996 Element.remove(this._clone); 47997 this._clone = null; 47998 } 47999 48000 var dropped = false; 48001 if(success) { 48002 dropped = Droppables.fire(event, this.element); 48003 if (!dropped) dropped = false; 48004 } 48005 if(dropped && this.options.onDropped) this.options.onDropped(this.element); 48006 Draggables.notify('onEnd', this, event); 48007 48008 var revert = this.options.revert; 48009 if(revert && Object.isFunction(revert)) revert = revert(this.element); 48010 48011 var d = this.currentDelta(); 48012 if(revert && this.options.reverteffect) { 48013 if (dropped == 0 || revert != 'failure') 48014 this.options.reverteffect(this.element, 48015 d[1]-this.delta[1], d[0]-this.delta[0]); 48016 } else { 48017 this.delta = d; 48018 } 48019 48020 if(this.options.zindex) 48021 this.element.style.zIndex = this.originalZ; 48022 48023 if(this.options.endeffect) 48024 this.options.endeffect(this.element); 48025 48026 Draggables.deactivate(this); 48027 Droppables.reset(); 48028 }, 48029 48030 keyPress: function(event) { 48031 if(event.keyCode!=Event.KEY_ESC) return; 48032 this.finishDrag(event, false); 48033 Event.stop(event); 48034 }, 48035 48036 endDrag: function(event) { 48037 if(!this.dragging) return; 48038 this.stopScrolling(); 48039 this.finishDrag(event, true); 48040 Event.stop(event); 48041 }, 48042 48043 draw: function(point) { 48044 var pos = this.element.cumulativeOffset(); 48045 if(this.options.ghosting) { 48046 var r = Position.realOffset(this.element); 48047 pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY; 48048 } 48049 48050 var d = this.currentDelta(); 48051 pos[0] -= d[0]; pos[1] -= d[1]; 48052 48053 if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) { 48054 pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft; 48055 pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop; 48056 } 48057 48058 var p = [0,1].map(function(i){ 48059 return (point[i]-pos[i]-this.offset[i]) 48060 }.bind(this)); 48061 48062 if(this.options.snap) { 48063 if(Object.isFunction(this.options.snap)) { 48064 p = this.options.snap(p[0],p[1],this); 48065 } else { 48066 if(Object.isArray(this.options.snap)) { 48067 p = p.map( function(v, i) { 48068 return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this)); 48069 } else { 48070 p = p.map( function(v) { 48071 return (v/this.options.snap).round()*this.options.snap }.bind(this)); 48072 } 48073 }} 48074 48075 var style = this.element.style; 48076 if((!this.options.constraint) || (this.options.constraint=='horizontal')) 48077 style.left = p[0] + "px"; 48078 if((!this.options.constraint) || (this.options.constraint=='vertical')) 48079 style.top = p[1] + "px"; 48080 48081 if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering 48082 }, 48083 48084 stopScrolling: function() { 48085 if(this.scrollInterval) { 48086 clearInterval(this.scrollInterval); 48087 this.scrollInterval = null; 48088 Draggables._lastScrollPointer = null; 48089 } 48090 }, 48091 48092 startScrolling: function(speed) { 48093 if(!(speed[0] || speed[1])) return; 48094 this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed]; 48095 this.lastScrolled = new Date(); 48096 this.scrollInterval = setInterval(this.scroll.bind(this), 10); 48097 }, 48098 48099 scroll: function() { 48100 var current = new Date(); 48101 var delta = current - this.lastScrolled; 48102 this.lastScrolled = current; 48103 if(this.options.scroll == window) { 48104 with (this._getWindowScroll(this.options.scroll)) { 48105 if (this.scrollSpeed[0] || this.scrollSpeed[1]) { 48106 var d = delta / 1000;
48107 this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] ); 48108 } 48109 } 48110 } else { 48111 this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000; 48112 this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000; 48113 } 48114 48115 Position.prepare(); 48116 Droppables.show(Draggables._lastPointer, this.element); 48117 Draggables.notify('onDrag', this); 48118 if (this._isScrollChild) { 48119 Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer); 48120 Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000; 48121 Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000; 48122 if (Draggables._lastScrollPointer[0] < 0) 48123 Draggables._lastScrollPointer[0] = 0; 48124 if (Draggables._lastScrollPointer[1] < 0) 48125 Draggables._lastScrollPointer[1] = 0; 48126 this.draw(Draggables._lastScrollPointer); 48127 } 48128 48129 if(this.options.change) this.options.change(this); 48130 }, 48131 48132 _getWindowScroll: function(w) { 48133 var T, L, W, H; 48134 with (w.document) { 48135 if (w.document.documentElement && documentElement.scrollTop) { 48136 T = documentElement.scrollTop; 48137 L = documentElement.scrollLeft; 48138 } else if (w.document.body) { 48139 T = body.scrollTop; 48140 L = body.scrollLeft; 48141 } 48142 if (w.innerWidth) { 48143 W = w.innerWidth; 48144 H = w.innerHeight; 48145 } else if (w.document.documentElement && documentElement.clientWidth) { 48146 W = documentElement.clientWidth; 48147 H = documentElement.clientHeight; 48148 } else { 48149 W = body.offsetWidth; 48150 H = body.offsetHeight; 48151 } 48152 } 48153 return { top: T, left: L, width: W, height: H }; 48154 } 48155}); 48156 48157Draggable._dragging = { }; 48158 48159/*--------------------------------------------------------------------------*/ 48160 48161var SortableObserver = Class.create({ 48162 initialize: function(element, observer) { 48163 this.element = $(element); 48164 this.observer = observer; 48165 this.lastValue = Sortable.serialize(this.element); 48166 }, 48167 48168 onStart: function() { 48169 this.lastValue = Sortable.serialize(this.element); 48170 }, 48171 48172 onEnd: function() { 48173 Sortable.unmark(); 48174 if(this.lastValue != Sortable.serialize(this.element)) 48175 this.observer(this.element) 48176 } 48177}); 48178 48179var Sortable = { 48180 SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/, 48181 48182 sortables: { }, 48183 48184 _findRootElement: function(element) { 48185 while (element.tagName.toUpperCase() != "BODY") { 48186 if(element.id && Sortable.sortables[element.id]) return element; 48187 element = element.parentNode; 48188 } 48189 }, 48190 48191 options: function(element) { 48192 element = Sortable._findRootElement($(element)); 48193 if(!element) return; 48194 return Sortable.sortables[element.id]; 48195 }, 48196 48197 destroy: function(element){ 48198 element = $(element); 48199 var s = Sortable.sortables[element.id]; 48200 48201 if(s) { 48202 Draggables.removeObserver(s.element); 48203 s.droppables.each(function(d){ Droppables.remove(d) }); 48204 s.draggables.invoke('destroy'); 48205 48206 delete Sortable.sortables[s.element.id]; 48207 } 48208 }, 48209 48210 create: function(element) { 48211 element = $(element); 48212 var options = Object.extend({ 48213 element: element, 48214 tag: 'li', // assumes li children, override with tag: 'tagname' 48215 dropOnEmpty: false, 48216 tree: false, 48217 treeTag: 'ul', 48218 overlap: 'vertical', // one of 'vertical', 'horizontal' 48219 constraint: 'vertical', // one of 'vertical', 'horizontal', false 48220 containment: element, // also takes array of elements (or id's); or false 48221 handle: false, // or a CSS class 48222 only: false, 48223 delay: 0, 48224 hoverclass: null, 48225 ghosting: false, 48226 quiet: false, 48227 scroll: false, 48228 scrollSensitivity: 20, 48229 scrollSpeed: 15, 48230 format: this.SERIALIZE_RULE, 48231 48232 // these take arrays of elements or ids and can be 48233 // used for better initialization performance 48234 elements: false, 48235 handles: false, 48236 48237 onChange: Prototype.emptyFunction, 48238 onUpdate: Prototype.emptyFunction 48239 }, arguments[1] || { }); 48240 48241 // clear any old sortable with same element 48242 this.destroy(element); 48243 48244 // build options for the draggables 48245 var options_for_draggable = { 48246 revert: true, 48247 quiet: options.quiet, 48248 scroll: options.scroll, 48249 scrollSpeed: options.scrollSpeed,
48250 scrollSensitivity: options.scrollSensitivity, 48251 delay: options.delay, 48252 ghosting: options.ghosting, 48253 constraint: options.constraint, 48254 handle: options.handle }; 48255 48256 if(options.starteffect) 48257 options_for_draggable.starteffect = options.starteffect; 48258 48259 if(options.reverteffect) 48260 options_for_draggable.reverteffect = options.reverteffect; 48261 else 48262 if(options.ghosting) options_for_draggable.reverteffect = function(element) { 48263 element.style.top = 0; 48264 element.style.left = 0; 48265 }; 48266 48267 if(options.endeffect) 48268 options_for_draggable.endeffect = options.endeffect; 48269 48270 if(options.zindex) 48271 options_for_draggable.zindex = options.zindex; 48272 48273 // build options for the droppables 48274 var options_for_droppable = { 48275 overlap: options.overlap, 48276 containment: options.containment, 48277 tree: options.tree, 48278 hoverclass: options.hoverclass, 48279 onHover: Sortable.onHover 48280 }; 48281 48282 var options_for_tree = { 48283 onHover: Sortable.onEmptyHover, 48284 overlap: options.overlap, 48285 containment: options.containment, 48286 hoverclass: options.hoverclass 48287 }; 48288 48289 // fix for gecko engine 48290 Element.cleanWhitespace(element); 48291 48292 options.draggables = []; 48293 options.droppables = []; 48294 48295 // drop on empty handling 48296 if(options.dropOnEmpty || options.tree) { 48297 Droppables.add(element, options_for_tree); 48298 options.droppables.push(element); 48299 } 48300 48301 (options.elements || this.findElements(element, options) || []).each( function(e,i) { 48302 var handle = options.handles ? $(options.handles[i]) : 48303 (options.handle ? $(e).select('.' + options.handle)[0] : e); 48304 options.draggables.push( 48305 new Draggable(e, Object.extend(options_for_draggable, { handle: handle }))); 48306 Droppables.add(e, options_for_droppable); 48307 if(options.tree) e.treeNode = element; 48308 options.droppables.push(e); 48309 }); 48310 48311 if(options.tree) { 48312 (Sortable.findTreeElements(element, options) || []).each( function(e) { 48313 Droppables.add(e, options_for_tree); 48314 e.treeNode = element; 48315 options.droppables.push(e); 48316 }); 48317 } 48318 48319 // keep reference 48320 this.sortables[element.identify()] = options; 48321 48322 // for onupdate 48323 Draggables.addObserver(new SortableObserver(element, options.onUpdate)); 48324 48325 }, 48326 48327 // return all suitable-for-sortable elements in a guaranteed order 48328 findElements: function(element, options) { 48329 return Element.findChildren( 48330 element, options.only, options.tree ? true : false, options.tag); 48331 }, 48332 48333 findTreeElements: function(element, options) { 48334 return Element.findChildren( 48335 element, options.only, options.tree ? true : false, options.treeTag); 48336 }, 48337 48338 onHover: function(element, dropon, overlap) { 48339 if(Element.isParent(dropon, element)) return; 48340 48341 if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) { 48342 return; 48343 } else if(overlap>0.5) { 48344 Sortable.mark(dropon, 'before'); 48345 if(dropon.previousSibling != element) { 48346 var oldParentNode = element.parentNode; 48347 element.style.visibility = "hidden"; // fix gecko rendering 48348 dropon.parentNode.insertBefore(element, dropon); 48349 if(dropon.parentNode!=oldParentNode) 48350 Sortable.options(oldParentNode).onChange(element); 48351 Sortable.options(dropon.parentNode).onChange(element); 48352 } 48353 } else { 48354 Sortable.mark(dropon, 'after'); 48355 var nextElement = dropon.nextSibling || null; 48356 if(nextElement != element) { 48357 var oldParentNode = element.parentNode; 48358 element.style.visibility = "hidden"; // fix gecko rendering 48359 dropon.parentNode.insertBefore(element, nextElement); 48360 if(dropon.parentNode!=oldParentNode) 48361 Sortable.options(oldParentNode).onChange(element); 48362 Sortable.options(dropon.parentNode).onChange(element); 48363 } 48364 } 48365 }, 48366 48367 onEmptyHover: function(element, dropon, overlap) { 48368 var oldParentNode = element.parentNode; 48369 var droponOptions = Sortable.options(dropon); 48370 48371 if(!Element.isParent(dropon, element)) { 48372 var index; 48373 48374 var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only}); 48375 var child = null; 48376 48377 if(children) { 48378 var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap); 48379 48380 for (index = 0; index < children.length; index += 1) { 48381 if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) { 48382 offset -= Element.offsetSize (children[index], droponOptions.overlap); 48383 } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) { 48384 child = index + 1 < children.length ? children[index + 1] : null; 48385 break; 48386 } else { 48387 child = children[index]; 48388 break; 48389 } 48390 } 48391 } 48392 48393 dropon.insertBefore(element, child); 48394
48395 Sortable.options(oldParentNode).onChange(element); 48396 droponOptions.onChange(element); 48397 } 48398 }, 48399 48400 unmark: function() { 48401 if(Sortable._marker) Sortable._marker.hide(); 48402 }, 48403 48404 mark: function(dropon, position) { 48405 // mark on ghosting only 48406 var sortable = Sortable.options(dropon.parentNode); 48407 if(sortable && !sortable.ghosting) return; 48408 48409 if(!Sortable._marker) { 48410 Sortable._marker = 48411 ($('dropmarker') || Element.extend(document.createElement('DIV'))). 48412 hide().addClassName('dropmarker').setStyle({position:'absolute'}); 48413 document.getElementsByTagName("body").item(0).appendChild(Sortable._marker); 48414 } 48415 var offsets = dropon.cumulativeOffset(); 48416 Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'}); 48417 48418 if(position=='after') 48419 if(sortable.overlap == 'horizontal') 48420 Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'}); 48421 else 48422 Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'}); 48423 48424 Sortable._marker.show(); 48425 }, 48426 48427 _tree: function(element, options, parent) { 48428 var children = Sortable.findElements(element, options) || []; 48429 48430 for (var i = 0; i < children.length; ++i) { 48431 var match = children[i].id.match(options.format); 48432 48433 if (!match) continue; 48434 48435 var child = { 48436 id: encodeURIComponent(match ? match[1] : null), 48437 element: element, 48438 parent: parent, 48439 children: [], 48440 position: parent.children.length, 48441 container: $(children[i]).down(options.treeTag) 48442 }; 48443 48444 /* Get the element containing the children and recurse over it */ 48445 if (child.container) 48446 this._tree(child.container, options, child); 48447 48448 parent.children.push (child); 48449 } 48450 48451 return parent; 48452 }, 48453 48454 tree: function(element) { 48455 element = $(element); 48456 var sortableOptions = this.options(element); 48457 var options = Object.extend({ 48458 tag: sortableOptions.tag, 48459 treeTag: sortableOptions.treeTag, 48460 only: sortableOptions.only, 48461 name: element.id, 48462 format: sortableOptions.format 48463 }, arguments[1] || { }); 48464 48465 var root = { 48466 id: null, 48467 parent: null, 48468 children: [], 48469 container: element, 48470 position: 0 48471 }; 48472 48473 return Sortable._tree(element, options, root); 48474 }, 48475 48476 /* Construct a [i] index for a particular node */ 48477 _constructIndex: function(node) { 48478 var index = ''; 48479 do { 48480 if (node.id) index = '[' + node.position + ']' + index; 48481 } while ((node = node.parent) != null); 48482 return index; 48483 }, 48484 48485 sequence: function(element) { 48486 element = $(element); 48487 var options = Object.extend(this.options(element), arguments[1] || { }); 48488 48489 return $(this.findElements(element, options) || []).map( function(item) { 48490 return item.id.match(options.format) ? item.id.match(options.format)[1] : ''; 48491 }); 48492 }, 48493 48494 setSequence: function(element, new_sequence) { 48495 element = $(element); 48496 var options = Object.extend(this.options(element), arguments[2] || { }); 48497 48498 var nodeMap = { }; 48499 this.findElements(element, options).each( function(n) { 48500 if (n.id.match(options.format)) 48501 nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode]; 48502 n.parentNode.removeChild(n); 48503 }); 48504 48505 new_sequence.each(function(ident) { 48506 var n = nodeMap[ident]; 48507 if (n) { 48508 n[1].appendChild(n[0]); 48509 delete nodeMap[ident]; 48510 } 48511 }); 48512 }, 48513 48514 serialize: function(element) { 48515 element = $(element); 48516 var options = Object.extend(Sortable.options(element), arguments[1] || { }); 48517 var name = encodeURIComponent( 48518 (arguments[1] && arguments[1].name) ? arguments[1].name : element.id); 48519 48520 if (options.tree) { 48521 return Sortable.tree(element, arguments[1]).children.map( function (item) { 48522 return [name + Sortable._constructIndex(item) + "[id]=" + 48523 encodeURIComponent(item.id)].concat(item.children.map(arguments.callee)); 48524 }).flatten().join('&'); 48525 } else { 48526 return Sortable.sequence(element, arguments[1]).map( function(item) { 48527 return name + "[]=" + encodeURIComponent(item); 48528 }).join('&'); 48529 } 48530 } 48531}; 48532 48533// Returns true if child is contained within element 48534Element.isParent = function(child, element) { 48535 if (!child.parentNode || child == element) return false;
48536 if (child.parentNode == element) return true; 48537 return Element.isParent(child.parentNode, element); 48538}; 48539 48540Element.findChildren = function(element, only, recursive, tagName) { 48541 if(!element.hasChildNodes()) return null; 48542 tagName = tagName.toUpperCase(); 48543 if(only) only = [only].flatten(); 48544 var elements = []; 48545 $A(element.childNodes).each( function(e) { 48546 if(e.tagName && e.tagName.toUpperCase()==tagName && 48547 (!only || (Element.classNames(e).detect(function(v) { return only.include(v) })))) 48548 elements.push(e); 48549 if(recursive) { 48550 var grandchildren = Element.findChildren(e, only, recursive, tagName); 48551 if(grandchildren) elements.push(grandchildren); 48552 } 48553 }); 48554 48555 return (elements.length>0 ? elements.flatten() : []); 48556}; 48557 48558Element.offsetSize = function (element, type) { 48559 return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')]; 48560}; 48561// script.aculo.us slider.js v1.8.3, Thu Oct 08 11:23:33 +0200 2009 48562 48563// Copyright (c) 2005-2009 Marty Haught, Thomas Fuchs 48564// 48565// script.aculo.us is freely distributable under the terms of an MIT-style license. 48566// For details, see the script.aculo.us web site: http://script.aculo.us/ 48567 48568if (!Control) var Control = { }; 48569 48570// options: 48571// axis: 'vertical', or 'horizontal' (default) 48572// 48573// callbacks: 48574// onChange(value) 48575// onSlide(value) 48576Control.Slider = Class.create({ 48577 initialize: function(handle, track, options) { 48578 var slider = this; 48579 48580 if (Object.isArray(handle)) { 48581 this.handles = handle.collect( function(e) { return $(e) }); 48582 } else { 48583 this.handles = [$(handle)]; 48584 } 48585 48586 this.track = $(track); 48587 this.options = options || { }; 48588 48589 this.axis = this.options.axis || 'horizontal'; 48590 this.increment = this.options.increment || 1; 48591 this.step = parseInt(this.options.step || '1'); 48592 this.range = this.options.range || $R(0,1); 48593 48594 this.value = 0; // assure backwards compat 48595 this.values = this.handles.map( function() { return 0 }); 48596 this.spans = this.options.spans ? this.options.spans.map(function(s){ return $(s) }) : false; 48597 this.options.startSpan = $(this.options.startSpan || null); 48598 this.options.endSpan = $(this.options.endSpan || null); 48599 48600 this.restricted = this.options.restricted || false; 48601 48602 this.maximum = this.options.maximum || this.range.end; 48603 this.minimum = this.options.minimum || this.range.start; 48604 48605 // Will be used to align the handle onto the track, if necessary 48606 this.alignX = parseInt(this.options.alignX || '0'); 48607 this.alignY = parseInt(this.options.alignY || '0'); 48608 48609 this.trackLength = this.maximumOffset() - this.minimumOffset(); 48610 48611 this.handleLength = this.isVertical() ? 48612 (this.handles[0].offsetHeight != 0 ? 48613 this.handles[0].offsetHeight : this.handles[0].style.height.replace(/px$/,"")) : 48614 (this.handles[0].offsetWidth != 0 ? this.handles[0].offsetWidth : 48615 this.handles[0].style.width.replace(/px$/,"")); 48616 48617 this.active = false; 48618 this.dragging = false; 48619 this.disabled = false; 48620 48621 if (this.options.disabled) this.setDisabled(); 48622 48623 // Allowed values array 48624 this.allowedValues = this.options.values ? this.options.values.sortBy(Prototype.K) : false; 48625 if (this.allowedValues) { 48626 this.minimum = this.allowedValues.min(); 48627 this.maximum = this.allowedValues.max(); 48628 } 48629 48630 this.eventMouseDown = this.startDrag.bindAsEventListener(this); 48631 this.eventMouseUp = this.endDrag.bindAsEventListener(this); 48632 this.eventMouseMove = this.update.bindAsEventListener(this); 48633 48634 // Initialize handles in reverse (make sure first handle is active) 48635 this.handles.each( function(h,i) { 48636 i = slider.handles.length-1-i; 48637 slider.setValue(parseFloat( 48638 (Object.isArray(slider.options.sliderValue) ? 48639 slider.options.sliderValue[i] : slider.options.sliderValue) || 48640 slider.range.start), i); 48641 h.makePositioned().observe("mousedown", slider.eventMouseDown); 48642 }); 48643 48644 this.track.observe("mousedown", this.eventMouseDown); 48645 document.observe("mouseup", this.eventMouseUp); 48646 document.observe("mousemove", this.eventMouseMove); 48647 48648 this.initialized = true; 48649 }, 48650 dispose: function() { 48651 var slider = this; 48652 Event.stopObserving(this.track, "mousedown", this.eventMouseDown); 48653 Event.stopObserving(document, "mouseup", this.eventMouseUp); 48654 Event.stopObserving(document, "mousemove", this.eventMouseMove); 48655 this.handles.each( function(h) { 48656 Event.stopObserving(h, "mousedown", slider.eventMouseDown); 48657 }); 48658 }, 48659 setDisabled: function(){ 48660 this.disabled = true; 48661 }, 48662 setEnabled: function(){ 48663 this.disabled = false;
48664 }, 48665 getNearestValue: function(value){ 48666 if (this.allowedValues){ 48667 if (value >= this.allowedValues.max()) return(this.allowedValues.max()); 48668 if (value <= this.allowedValues.min()) return(this.allowedValues.min()); 48669 48670 var offset = Math.abs(this.allowedValues[0] - value); 48671 var newValue = this.allowedValues[0]; 48672 this.allowedValues.each( function(v) { 48673 var currentOffset = Math.abs(v - value); 48674 if (currentOffset <= offset){ 48675 newValue = v; 48676 offset = currentOffset; 48677 } 48678 }); 48679 return newValue; 48680 } 48681 if (value > this.range.end) return this.range.end; 48682 if (value < this.range.start) return this.range.start; 48683 return value; 48684 }, 48685 setValue: function(sliderValue, handleIdx){ 48686 if (!this.active) { 48687 this.activeHandleIdx = handleIdx || 0; 48688 this.activeHandle = this.handles[this.activeHandleIdx]; 48689 this.updateStyles(); 48690 } 48691 handleIdx = handleIdx || this.activeHandleIdx || 0; 48692 if (this.initialized && this.restricted) { 48693 if ((handleIdx>0) && (sliderValue<this.values[handleIdx-1])) 48694 sliderValue = this.values[handleIdx-1]; 48695 if ((handleIdx < (this.handles.length-1)) && (sliderValue>this.values[handleIdx+1])) 48696 sliderValue = this.values[handleIdx+1]; 48697 } 48698 sliderValue = this.getNearestValue(sliderValue); 48699 this.values[handleIdx] = sliderValue; 48700 this.value = this.values[0]; // assure backwards compat 48701 48702 this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] = 48703 this.translateToPx(sliderValue); 48704 48705 this.drawSpans(); 48706 if (!this.dragging || !this.event) this.updateFinished(); 48707 }, 48708 setValueBy: function(delta, handleIdx) { 48709 this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta, 48710 handleIdx || this.activeHandleIdx || 0); 48711 }, 48712 translateToPx: function(value) { 48713 return Math.round( 48714 ((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) * 48715 (value - this.range.start)) + "px"; 48716 }, 48717 translateToValue: function(offset) { 48718 return ((offset/(this.trackLength-this.handleLength) * 48719 (this.range.end-this.range.start)) + this.range.start); 48720 }, 48721 getRange: function(range) { 48722 var v = this.values.sortBy(Prototype.K); 48723 range = range || 0; 48724 return $R(v[range],v[range+1]); 48725 }, 48726 minimumOffset: function(){ 48727 return(this.isVertical() ? this.alignY : this.alignX); 48728 }, 48729 maximumOffset: function(){ 48730 return(this.isVertical() ? 48731 (this.track.offsetHeight != 0 ? this.track.offsetHeight : 48732 this.track.style.height.replace(/px$/,"")) - this.alignY : 48733 (this.track.offsetWidth != 0 ? this.track.offsetWidth : 48734 this.track.style.width.replace(/px$/,"")) - this.alignX); 48735 }, 48736 isVertical: function(){ 48737 return (this.axis == 'vertical'); 48738 }, 48739 drawSpans: function() { 48740 var slider = this; 48741 if (this.spans) 48742 $R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) }); 48743 if (this.options.startSpan) 48744 this.setSpan(this.options.startSpan, 48745 $R(0, this.values.length>1 ? this.getRange(0).min() : this.value )); 48746 if (this.options.endSpan) 48747 this.setSpan(this.options.endSpan, 48748 $R(this.values.length>1 ? this.getRange(this.spans.length-1).max() : this.value, this.maximum)); 48749 }, 48750 setSpan: function(span, range) { 48751 if (this.isVertical()) { 48752 span.style.top = this.translateToPx(range.start); 48753 span.style.height = this.translateToPx(range.end - range.start + this.range.start); 48754 } else { 48755 span.style.left = this.translateToPx(range.start); 48756 span.style.width = this.translateToPx(range.end - range.start + this.range.start); 48757 } 48758 }, 48759 updateStyles: function() { 48760 this.handles.each( function(h){ Element.removeClassName(h, 'selected') }); 48761 Element.addClassName(this.activeHandle, 'selected'); 48762 }, 48763 startDrag: function(event) { 48764 if (Event.isLeftClick(event)) { 48765 if (!this.disabled){ 48766 this.active = true; 48767 48768 var handle = Event.element(event); 48769 var pointer = [Event.pointerX(event), Event.pointerY(event)]; 48770 var track = handle; 48771 if (track==this.track) { 48772 var offsets = this.track.cumulativeOffset(); 48773 this.event = event; 48774 this.setValue(this.translateToValue( 48775 (this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2) 48776 )); 48777 var offsets = this.activeHandle.cumulativeOffset(); 48778 this.offsetX = (pointer[0] - offsets[0]); 48779 this.offsetY = (pointer[1] - offsets[1]); 48780 } else { 48781 // find the handle (prevents issues with Safari) 48782 while((this.handles.indexOf(handle) == -1) && handle.parentNode) 48783 handle = handle.parentNode; 48784 48785 if (this.handles.indexOf(handle)!=-1) { 48786 this.activeHandle = handle; 48787 this.activeHandleIdx = this.handles.indexOf(this.activeHandle); 48788 this.updateStyles(); 48789 48790 var offsets = this.activeHandle.cumulativeOffset(); 48791 this.offsetX = (pointer[0] - offsets[0]); 48792 this.offsetY = (pointer[1] - offsets[1]); 48793 } 48794 } 48795 } 48796 Event.stop(event); 48797 } 48798 }, 48799 update: function(event) { 48800 if (this.active) { 48801 if (!this.dragging) this.dragging = true; 48802 this.draw(event); 48803 if (Prototype.Browser.WebKit) window.scrollBy(0,0); 48804 Event.stop(event); 48805 } 48806 }, 48807 draw: function(event) { 48808 var pointer = [Event.pointerX(event), Event.pointerY(event)]; 48809 var offsets = this.track.cumulativeOffset(); 48810 pointer[0] -= this.offsetX + offsets[0]; 48811 pointer[1] -= this.offsetY + offsets[1]; 48812 this.event = event; 48813 this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] )); 48814 if (this.initialized && this.options.onSlide) 48815 this.options.onSlide(this.values.length>1 ? this.values : this.value, this); 48816 }, 48817 endDrag: function(event) { 48818 if (this.active && this.dragging) { 48819 this.finishDrag(event, true); 48820 Event.stop(event); 48821 } 48822 this.active = false;
48823 this.dragging = false; 48824 }, 48825 finishDrag: function(event, success) { 48826 this.active = false; 48827 this.dragging = false; 48828 this.updateFinished(); 48829 }, 48830 updateFinished: function() { 48831 if (this.initialized && this.options.onChange) 48832 this.options.onChange(this.values.length>1 ? this.values : this.value, this); 48833 this.event = null; 48834 } 48835}); 48836// an empty file referenced by scriptaculous.js 48837; 48838// Brain Explorer Download Prompt -- brain_explorer_prompt.js 48839// Copyright (c) 2007-2011 Allen Institute for Brain Science. All Rights Reserved. 48840 48841BrainExplorerDownloadPrompt = Class.create({ 48842/* Uses jQuery's dialog to prompt the user to download brain explorer 48843 if they don't have the cookie (or the cookie is false). 48844 48845 To use, create an empty div on the web page (will serve as the prompt) and 48846 a link element that points to the correct location (e.g. aibe://...). The 48847 prompt hijacks the onclick property of the link element to open the prompt 48848 if a supplied cookie is false or unset. If the user clicks 'yes', the 48849 cookie is set and the link is followed. If the user clicks 'no', the 48850 supplied download page is opened in a new page. 48851 48852 The prompt is initially disabled and must be enabled via 48853 prompt.enable(destination). Disable the link via prompt.disable(). */ 48854 48855 initialize: function (container, cookie, settings, download_page, anchor) 48856 { 48857 this.parent = container; 48858 this.anchor = anchor; 48859 this.settings = settings; 48860 this.cookie = cookie; 48861 this.download_page = download_page; 48862 this.enabled = false; 48863 48864 $j(this.parent) 48865 .html('<p>Brain Explorer is a desktop application for viewing brain anatomy and gene expression in 3D.</p><p>Choose "Download" to proceed to the Brain Explorer download page.</p><p>Choose "View My Sample" if you have already installed Brain Explorer.</p>') 48866 .dialog({ autoOpen: false, 48867 modal: true, 48868 title: "Is Brain Explorer installed?", 48869 width: "500px", 48870 buttons: { "Download" : this.handle_prompt_no.bind(this), 48871 "View My Sample": this.handle_prompt_yes.bind(this) } }); 48872 48873 48874 this.anchor.onclick = this.check_cookie.bind(this); 48875 }, 48876 48877 enable: function (destination) 48878 { 48879 this.anchor.href = destination; 48880 this.enabled = true; 48881 }, 48882 48883 disable: function () 48884 { 48885 this.anchor.href = ""; 48886 this.enabled = false; 48887 }, 48888 48889 check_cookie: function () 48890 { 48891 if (!this.enabled) 48892 { 48893 return false; 48894 } 48895 else if (this.settings.get(this.cookie)) 48896 { 48897 return true; 48898 } 48899 else 48900 { 48901 $j(this.parent).dialog('open'); 48902 return false; 48903 } 48904 }, 48905 48906 handle_prompt_yes: function () 48907 { 48908 $j(this.parent).dialog('close'); 48909 this.settings.set(this.cookie,true); 48910 window.location = (this.anchor.href); 48911 }, 48912 48913 handle_prompt_no: function () 48914 { 48915 $j(this.parent).dialog('close'); 48916 window.open(this.download_page); 48917 } 48918}); 48919 48920var __colormap_widget_count__ = 0; 48921 48922/* 48923 * NOTE that the sliderWidgets.js fileis required to be loaded before this file. 48924 * 48925 * The colormap_widget implements a couple of different things. 48926 * First is a window/level slider control that can operate in either 48927 * symmetric or non-symmetric mode; this is just a wrapping around 48928 * the sliderWidgets.js component, see that file for more detail. 48929 * The slider widget is combined with a colormap chooser control, 48930 * built to operate like a standard dropdown <select> element, except 48931 * with pictures of the available color maps instead of text. 48932 * 48933 */ 48934function colormap_widget(container, options, event_options) { 48935 48936// public properties 48937 this.container = container; 48938 48939 // an array of {name, url, normal, selected} objects describing the possible colormaps. 48940 // name & url are strings, normal (normalized) and selected are booleans. 48941 this.colormap_set = options ? (options.colormap_set ? options.colormap_set : []) : []; 48942 48943 // the colormap can operate over either raw or normalized data, each of which has its own limits 48944 this.normalized_limits = options ? (options.zscore_limits ? options.zscore_limits : [-6, 6]) : [-6, 6]; 48945 this.raw_limits = options ? (options.expression_limits ? options.expression_limits : [0, 16]) : [0, 16]; 48946 48947 // switching between raw and and normalized should be possible. These are their defaults. 48948 this.default_raw_expression_range = options ? (options.raw_expression_range ? options.raw_expression_range : [ 2.0, 10.0 ]) : [ 2.0, 10.0 ]; 48949 this.default_norm_expression_range = options ? (options.norm_expression_range ? options.norm_expression_range : [ -3, 3 ]) : [ -3, 3 ]; 48950 48951 // the current range depends on whehter or not we're using normalized data 48952 this.normalized = options ? (options.normalized ? options.normalized : false) : false;
48953 this.expression_range = this.normalized ? this.default_norm_expression_range : this.default_raw_expression_range; 48954 this.limits = this.normalized ? this.normalized_limits : this.raw_limits; 48955 48956// event hooks 48957 this.on_colormap_change = event_options? (event_options.on_colormap_change ? event_options.on_colormap_change : null) : null; 48958 this.on_level_change = event_options? (event_options.on_level_change ? event_options.on_level_change : null) : null; 48959 48960 48961 48962// public methods 48963 this.set_range = function(lower, upper) { 48964 the_level_widget.setRange(lower, upper); 48965 }; 48966 48967 this.set_colormap = function(name) { 48968 select_colormap(self.colormap_set,name); 48969 }; 48970 48971// private properties 48972 var self = this; 48973 var the_level_widget = null; 48974 var the_select_box = null; 48975 var normalized = false; 48976 var select_container = null; 48977 var setting = false; 48978 48979 48980// private methods 48981 function create_window_level_widget(container) { 48982 48983 var map_count = self.colormap_set.length; 48984 var extra_container = container; 48985 if(map_count > 1) { 48986 select_container = new Element('div', {'class':'colormap_select_container', title:'Click to change color map'}); 48987 container.appendChild(select_container); 48988 48989 var open_button = new Element('div', {'class':'colormap_chooser_button'}); 48990 open_button.observe('click', do_colormap_chooser); 48991 select_container.appendChild(open_button); 48992 extra_container = select_container; 48993 } 48994 48995 var ramp_box = new Element('div', {id:'control_bar_ramp_box'+__colormap_widget_count__, 'class':'colormap_ramp_box'}); 48996 extra_container.appendChild(ramp_box); 48997 48998 the_select_box = new Element('div', {id:'control_bar_select_box'+__colormap_widget_count__, 'class':'colormap_select_box'}); 48999 var colormap_list = new Element('ul', {id:'control_bar_select_list'+__colormap_widget_count__, 'class':'colormap_select_list'}); 49000 49001 if(map_count > 1) { 49002 for(var i = 0; i < self.colormap_set.length; i++) { 49003 var colormap_item = new Element('li', {id:'control_bar_select_list_item'+__colormap_widget_count__, 'class':'colormap_select_item'}); 49004 var colormap_img = new Element('img', {id:'control_bar_select_list_img'+__colormap_widget_count__, 'class':'colormap_select_img'}); 49005 49006 colormap_img.setAttribute('src', self.colormap_set[i].url); 49007 colormap_img.setAttribute('normal', self.colormap_set[i].normal); 49008 colormap_img.setAttribute('name', self.colormap_set[i].name); 49009 49010 colormap_item.setAttribute('src', self.colormap_set[i].url); 49011 colormap_item.setAttribute('normal', self.colormap_set[i].normal); 49012 colormap_item.setAttribute('name', self.colormap_set[i].name); 49013 colormap_item.update(self.colormap_set[i].normal ? 'z-score' : 'log2 intensity'); 49014 49015 colormap_item.observe('click', do_select_item_click); 49016 colormap_item.appendChild(colormap_img); 49017 colormap_list.appendChild(colormap_item); 49018 } 49019 } 49020 the_select_box.appendChild(colormap_list); 49021 container.appendChild(the_select_box); 49022 the_select_box.hide(); 49023 49024 var map = get_current_colormap(); 49025 normalized = map.normal || map.normal == 'true'; 49026 self.limits = normalized ? self.normalized_limits : self.raw_limits; 49027 self.expression_range = normalized ? self.default_norm_expression_range : self.default_raw_expression_range; 49028 49029 the_level_widget = new WindowLevelWidget( 49030 ramp_box, 49031 map.url, 49032 self.limits[0], 49033 self.limits[1], 49034 self.expression_range[0], 49035 self.expression_range[1], 49036 do_window_level_change, 49037 1); 49038 49039 the_level_widget.setSymmetric(normalized); 49040 } 49041 49042 function position_select_list() { 49043 var list_dims = the_select_box.getDimensions(); 49044 var cont_pos = select_container.cumulativeOffset(); 49045 the_select_box.setStyle({top:(cont_pos.top-(list_dims.height+1))+'px',left:cont_pos.left+'px'}); 49046 } 49047 49048 49049 function do_select_item_click(evt) { 49050 evt = (evt) ? evt : event; 49051 var sender = ((evt.target) ? evt.target : evt.srcElement); 49052 49053 the_select_box.hide(); 49054 var new_name = sender.getAttribute('name'); 49055 var new_map = select_colormap(self.colormap_set, new_name); 49056 49057 select_container.removeClassName('open_top'); 49058 49059 if(self.on_colormap_change && new_map) 49060 self.on_colormap_change(self, new_map.name, new_map.normal, self.expression_range[0], self.expression_range[1]); 49061 } 49062 49063 function select_colormap(map_set, name) { 49064 var new_map = null; 49065 for(var i = 0; i < map_set.length; i++) { 49066 if(map_set[i].name == name) { 49067 map_set[i].selected = true; 49068 new_map = map_set[i]; 49069 } else { 49070 map_set[i].selected = false;
49071 } 49072 } 49073 49074 if (new_map) { 49075 normalized = new_map.normal; 49076 var limits = new_map.limits; 49077 self.expression_range = new_map.range; 49078 var new_url = new_map.url; 49079 49080 setting = true; 49081 the_level_widget.setColormapImage(new_url); 49082 the_level_widget.setLimits(limits[0], limits[1]); 49083 the_level_widget.setSymmetric(normalized); 49084 setting = false; 49085 } 49086 49087 return new_map; 49088 } 49089 49090 function do_colormap_chooser() { 49091 position_select_list(); 49092 the_select_box.toggle(); 49093 if(the_select_box.visible()) 49094 select_container.addClassName('open_top'); 49095 else 49096 select_container.removeClassName('open_top'); 49097 } 49098 49099 function do_window_level_change(lower, upper) { 49100 49101 if(normalized) 49102 self.default_norm_expression_range = [lower, upper]; 49103 else 49104 self.default_raw_expression_range = [lower, upper]; 49105 49106 if(setting) 49107 return; 49108 49109 if(self.on_level_change) 49110 self.on_level_change(lower, upper); 49111 } 49112 49113 function get_current_colormap() { 49114 49115 for(var i = 0; i < self.colormap_set.length; i++) { 49116 var map = self.colormap_set[i]; 49117 if(map.selected) 49118 return(map); 49119 } 49120 49121 return(null); 49122 } 49123 49124 function init() { 49125 49126 create_window_level_widget(self.container) 49127 __colormap_widget_count__++; 49128 } 49129 49130 init(); 49131} 49132 49133; 49134 49135function heatmap_control_options_config(options_from_json, urls_from_env) 49136{ 49137 var _options = options_from_json; 49138 49139 // use the config info from environment.rb to override the application.json config 49140 if(urls_from_env.analysis_service) 49141 _options.env.env_mri_image_server = urls_from_env.analysis_service; 49142 49143 if(urls_from_env.related_data) 49144 _options.env.env_related_projects_service = urls_from_env.related_data; 49145 49146 if(urls_from_env.brain_explorer_host) 49147 _options.env.env_brain_explorer_host = urls_from_env.brain_explorer_host; 49148 49149 if(urls_from_env.correlation_help) 49150 _options.env.app_url_for_correlation_help_path = urls_from_env.correlation_help; 49151 49152 var _cookies = _options.cookies; 49153 49154 var _settings = new user_data(_options.user_defaults); 49155 var _app_colormap_set = configure_colormap_set(_options.app.app_name, _options.env.env_mri_image_server, _cookies, _settings); 49156 var _default_grouping = get_current_grouping(_options.query.search_type, _cookies, _settings); 49157 var _current_colormap = select_colormap(_app_colormap_set, _settings.get(_cookies.cookie_colormap)); 49158 var _current_sort = get_current_sort(_options.query.search_type, _cookies, _settings); 49159 49160 _options.app.settings = _settings; 49161 _options.app.colormap_set = _app_colormap_set; 49162 _options.query.default_grouping = _default_grouping; 49163 _options.query.current_colormap = _current_colormap; 49164 _options.query.current_sort = _current_sort; 49165 _options.select_colormap = select_colormap; // pass in the function 49166 49167 49168 49169 this.get_options = function() { 49170 return _options; 49171 }; 49172 49173 //helper functions for setting the options to heatmap_control 49174 //function configure_colormap_set(selected_map_name) { 49175 function configure_colormap_set(app_name, env_mri_image_server, cookies, settings) { 49176 49177 switch ( app_name ) 49178 { 49179 case 'ahba': 49180 case 'nhp': 49181 case 'brainspan_lcm': 49182 var colormap_set = 49183 [{ 49184 name:'redgreen', 49185 url:env_mri_image_server + 'colormap/show/redgreen', 49186 normal:true, 49187 selected:false, 49188 limits:[-6,6], 49189 range:settings.get(cookies.cookie_gene_norm_level) 49190 }, 49191 { 49192 name:'bluered', 49193 url:env_mri_image_server + 'colormap/show/bluered', 49194 normal:true, 49195 selected:false, 49196 limits:[-6,6], 49197 range:settings.get(cookies.cookie_gene_norm_level) 49198 }, 49199 { 49200 name:'ryb', 49201 url:env_mri_image_server + 'colormap/show/ryb', 49202 normal:true, 49203 selected:false, 49204 limits:[-6,6], 49205 range:settings.get(cookies.cookie_gene_norm_level) 49206 }, 49207 { 49208 name:'jet', 49209 url:env_mri_image_server + 'colormap/show/jet', 49210 normal:false, 49211 selected:false, 49212 limits:[0,16], 49213 range:settings.get(cookies.cookie_gene_raw_level) 49214 }]; 49215 break; 49216 49217 case 'brainspan_rnaseq': 49218 var colormap_set = 49219 [{ 49220 name:'jet', 49221 url:env_mri_image_server + 'colormap/show/jet', 49222 normal:false, 49223 selected:true, 49224 limits:[0,16], 49225 range:settings.get(cookies.cookie_gene_raw_level) 49226 }]; 49227 49228 break; 49229 49230 49231 } 49232 49233 return(colormap_set); 49234 } 49235 49236 49237 49238 function get_current_grouping(type, cookies, settings) { 49239 49240 var grouping = 0; 49241 switch(type) { 49242 case 'search': 49243 grouping = settings.get(cookies.cookie_gene_grouping); 49244 break; 49245 case 'differential': 49246 grouping = settings.get(cookies.cookie_diff_grouping); 49247 break; 49248 case 'correlation': 49249 grouping = settings.get(cookies.cookie_correlation_grouping); 49250 break; 49251 } 49252 49253 return(grouping); 49254 } 49255 49256 function get_current_sort(type, cookies, settings) { 49257 49258 var sort = ''; 49259 switch(type) { 49260 case 'differential': 49261 sort = settings.get(cookies.cookie_differential_sort_column); 49262 break; 49263 case 'correlation': 49264 sort = settings.get(cookies.cookie_correlation_sort_order); 49265 break; 49266 } 49267 49268 return(sort); 49269 } 49270 49271 49272 function select_colormap(map_set, name) { 49273 for(var i = 0; i < map_set.length; i++) { 49274 if(map_set[i].name == name) { 49275 map_set[i].selected = true; 49276 return(map_set[i]); 49277 } 49278 } 49279 if(map_set.length == 1) 49280 return(map_set[0]); 49281 else 49282 return(null); 49283 } 49284 49285 49286} 49287; 49288 /* 49289 * This widget consists of a single button that will place itself into 49290 * the upper right corner of the given container. The button 49291 * indicates a switch between a top & bottom item & raises a 'change' event. 49292 */ 49293 49294 function grouping_widget(container, options, event_options) { 49295 49296 // public properties 49297 this.container = container; 49298 this.default_grouping = options ? options.default_grouping ? options.default_grouping : 0 : 0; 49299 this.groupings = options ? options.groupings ? options.groupings : ['donors,structures','structures,donors'] : ['donors,structures','structures,donors']; 49300 this.tool_tip = options ? options.tool_tip ? options.tool_tip : 'Toggle sort by structure or sort by donor' : 'Toggle sort by structure or sort by donor'; 49301 49302 // event hooks 49303 this.on_grouping_change = event_options ? event_options.on_grouping_change ? event_options.on_grouping_change : null : null; 49304 49305 // public methods 49306 this.reposition = function() { 49307 position_button(); 49308 } 49309 49310 this.set_grouping = function(new_grouping) { 49311 if(new_grouping != this.groupings[current_grouping]) { 49312 toggle_grouping(); 49313 } 49314 } 49315 49316 // private properties 49317 var self = this; 49318 var the_button = null; 49319 var current_grouping = self.default_grouping; 49320 49321 // private methods 49322 function build_widget() { 49323 49324 var title = self.tool_tip; 49325 the_button = new Element('div', {'class':'grouping_switcheroo', title:title}); 49326 49327 self.container.appendChild(the_button); 49328 $j(the_button).button({text:false, icons:{secondary:'ui-icon-refresh'}}); 49329 position_button(); 49330 } 49331 49332 function position_button() { 49333 // put the button in the upper right-hand corner of the container 49334 var container_pos = self.container.cumulativeOffset(); 49335 var container_dims = self.container.getDimensions(); 49336 49337 var button_dims = the_button.getDimensions(); 49338 var pos = {top:(container_pos.top+1)+'px', 49339 left:(container_dims.width /*+ container_pos.left*/ - 10)+'px'}; 49340 // -10 probably to make up for 5px of padding in the container. 49341 the_button.setStyle(pos); 49342 the_button.observe('mousedown', do_button_down); 49343 the_button.observe('click', toggle_grouping); 49344 the_button.observe('mouseup', do_button_up); 49345 } 49346 49347 function do_button_down() { 49348 the_button.addClassName('button_down'); 49349 } 49350 49351 function do_button_up() { 49352 the_button.removeClassName('button_down'); 49353 } 49354 49355 function toggle_grouping() { 49356 current_grouping = (current_grouping < 2) ? (current_grouping ? 0 : 1) : 5 - current_grouping; // for NHP, there are two new group_by settings with value 2 and 3 here 49357 if(self.on_grouping_change) 49358 self.on_grouping_change(self.groupings[current_grouping]); 49359 } 49360
49361 function init() { 49362 build_widget(); 49363 } 49364 49365 init(); 49366 } 49367; 49368 49369// a busy indicator is added to the prototype Element to allow consistent business. 49370var busifier = { 49371 49372 busified:false, 49373 busy_panel:null, 49374 49375 clone_position:function(element, position_target) { 49376 if(!element) 49377 return(element); 49378 49379 // this is why we're using a custom clone position-- 49380 // Prototype's cumulativeScrollOffset() implementation takes a long time on 49381 // IE (on the order of 5 seconds sometimes) while jquery's offset() 49382 // returns very quickly. 49383 var offsets = $j(position_target).offset(); 49384 ////// 49385 49386 // the rest of this method is still using Prototype. 49387 element = $(element); 49388 var dims = $(position_target).getDimensions(); 49389 49390 element.setStyle({ 49391 position: 'absolute', 49392 top: offsets.top + 'px', 49393 left: offsets.left + 'px', 49394 width: dims.width + 'px', 49395 height: dims.height + 'px' 49396 }); 49397 return(element); 49398 }, 49399 49400 // adds an partialy opaque overlayed panel to the element, along with an icon. 49401 // the overlay is usually hidden, but can be made visible by calling 49402 // element.show_busy(true) and hidden by element.show_busy(false); 49403 // 49404 // options include: 49405 // color: the color of the overlay. Default = #000 49406 // opacity: the percent opacity of the overlay. Default = 0.6 49407 // icon: a path to an icon to show centered in the overlay. Default = /assets/images/ajax-loader.gif 49408 busify:function(element, options) { 49409 if(!element) 49410 return(element); 49411 49412 element = $(element); 49413 49414 // create a div and position it to cover the given element 49415 element.busy_panel = new Element('div'); 49416 element.busy_panel.hide(); 49417 var busy_image = new Element('img', {src:(options ? options.icon ? options.icon : "/assets/heatmap_ui/ajax-loader.gif" : "/assets/heatmap_ui/ajax-loader.gif")}); 49418 element.busy_panel.appendChild(busy_image); 49419 element.busy_panel.observe('click', function(evt) { Event.stop(evt); }); 49420 49421 element.appendChild(element.busy_panel); 49422 49423 element.busy_panel.setStyle({ 49424 backgroundColor:(options ? options.color ? options.color : '#aaa' : '#aaa'), 49425 position:'absolute', 49426 top:'0px', 49427 left:'0px', 49428 verticalAlign:'middle', 49429 cursor:'wait', 49430 textAlign:'center', 49431 zIndex:'99999' 49432 }); 49433 busy_image.setStyle({ 49434 marginLeft:'auto', 49435 marginRight:'auto', 49436 marginTop:'25%' 49437 }); 49438 element.busy_panel.clone_position(element); 49439 element.busy_panel.setOpacity(options ? options.opacity ? options.opacity : 0.5 : 0.5); 49440 49441 49442 element.busified = true; 49443 return(element); 49444 }, 49445 49446 // busifies the element if it isn't already, then shows/hides the busy indicator 49447 show_busy:function(element, busy) { 49448 element = $(element); 49449 49450 if(!element.busified) 49451 element = element.busify(); 49452 49453 if(busy) { 49454 $(element.busy_panel).clone_position(element); 49455 element.busy_panel.show(); 49456 } 49457 else 49458 element.busy_panel.hide(); 49459 }, 49460 49461 // if the element is resized you'll want to rebusy it 49462 rebusy:function(element) { 49463 element = $(element); 49464 49465 if(!element.busified) 49466 element = element.busify(); 49467 else 49468 element.busy_panel.clone_position(element); 49469 }, 49470 49471 // remove the busifier elements 49472 debusy:function(element) { 49473 if(!element.busified) 49474 return; 49475 49476 element.removeChild(element.busy_panel); 49477 element.busified = false; 49478 } 49479} 49480 49481Element.addMethods(busifier); 49482 49483 49484// A basket of cookies 49485function user_settings(defaults) { 49486 49487 this.defaults = defaults ? defaults : {}; 49488 49489 this.set = function(name, value) { 49490 set_cookie(name, Object.toJSON(value), 365); 49491 } 49492 49493 this.get = function(name) { 49494 var cook = get_cookie(name); 49495 if(cook && cook.length > 0 && cook != '""') { 49496 var value = cook.evalJSON(); 49497 if(value == 'false' || value == 'true') 49498 value = (value == 'true'); 49499 49500 return(value); 49501 } 49502 else 49503 cook = defaults[name]; 49504 49505 return(cook); 49506 } 49507 49508 function set_cookie(c_name, value, expiredays) { 49509 var exdate=new Date(); 49510 exdate.setDate(exdate.getDate()+expiredays); 49511 document.cookie=c_name+ "=" + escape(value) + ((expiredays==null) ? "" : ";path=/;expires="+exdate.toGMTString()); 49512 } 49513 49514 function get_cookie(c_name) { 49515 if(document.cookie.length > 0) { 49516 var c_start=document.cookie.indexOf(c_name + "=") 49517 if(c_start != -1) { 49518 49519 c_start = c_start + c_name.length+1; 49520 var c_end = document.cookie.indexOf(";", c_start); 49521 49522 if(c_end == -1) 49523 c_end = document.cookie.length; 49524 49525 return unescape(document.cookie.substring(c_start, c_end)); 49526 } 49527 } 49528 return null; 49529 } 49530} 49531 49532 49533var BrowserSupport = Class.create({ 49534
49535 // initialize with an object containing keys & values for browser names & versions as in: 49536 // {webkit:'531.0', msie:'8.0', mozilla:'1.9.2'} 49537 // 49538 // this.supported means the current browser has version >= the minumum_list, 49539 // this.not_supported means the current browser is in the minumum_list and 49540 // has vesion less than that given. 49541 // 49542 // NOTE that both supported & not_supported may legitimately be false, if the 49543 // current browser is not in the list. 49544 initialize: function(minimum_list) { 49545 49546 var userAgent = navigator.userAgent.toLowerCase(); 49547 this.minimum_list = minimum_list ? minimum_list : {}; 49548 49549 this.version = (userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [])[1]; 49550 this.extended_version = this._version_to_number(this.version); 49551 49552 this.webkit = /webkit/.test( userAgent ); 49553 this.opera = /opera/.test( userAgent ); 49554 this.msie = /msie/.test( userAgent ) && !/opera/.test( userAgent ); 49555 this.mozilla = /mozilla/.test( userAgent ) && !/(compatible|webkit)/.test( userAgent ); 49556 49557 this.name = this.webkit ? 'webkit' : this.opera ? 'opera' : this.msie ? 'msie' : this.mozilla ? 'mozilla' : 'unknown'; 49558 49559 this.supported = this._is_supported(this.minimum_list); 49560 this.not_supported = this._is_not_supported(this.minimum_list); 49561 }, 49562 49563 _is_not_supported: function(list) { 49564 49565 for(var key in list) { 49566 if(key == this.name) { 49567 var supported_version = this._version_to_number(list[key]); 49568 49569 if(supported_version.major > this.extended_version.major) 49570 return(true); 49571 49572 if((supported_version.major == this.extended_version.major) 49573 && 49574 (supported_version.minor > this.extended_version.minor)) 49575 return(true); 49576 49577 if((supported_version.major == this.extended_version.major) 49578 && 49579 (supported_version.minor == this.extended_version.minor) 49580 && 49581 (supported_version.build > this.extended_version.build)) 49582 return(true); 49583 } 49584 } 49585 return(false); 49586 }, 49587 49588 _is_supported: function(list) { 49589 for(var key in list) { 49590 if(key == this.name) { 49591 var supported_version = this._version_to_number(list[key]); 49592 49593 if((supported_version.major == this.extended_version.major) 49594 && 49595 (supported_version.minor == this.extended_version.minor) 49596 && 49597 (supported_version.build <= this.extended_version.build)) 49598 return(true); 49599 49600 if((supported_version.major == this.extended_version.major) 49601 && 49602 (supported_version.minor <= this.extended_version.minor)) 49603 return(true); 49604 49605 if(supported_version.major <= this.extended_version.major) 49606 return(true); 49607 } 49608 } 49609 return(false); 49610 }, 49611 49612 _version_to_number: function(version) { 49613 49614 var version_components = version ? version.split('.') : [0]; 49615 var ret = {}; 49616 49617 ret.major = parseInt(version_components[0]); 49618 ret.minor = version_components.length > 1 ? parseInt(version_components[1]) : 0; 49619 ret.build = version_components.length > 2 ? parseInt(version_components[2]) : 0; 49620 49621 return(ret); 49622 } 49623 49624}); 49625 49626// the value of __IE__ should be the version of the actual rendering engine 49627var __IE__ = false; 49628 49629/*@cc_on 49630 @if ( @_jscript_version >= 5.7 ) 49631 __IE__ = true; 49632 @elif ( @_jscript_version == 5.6 ) 49633 __IE__ = 6; 49634 @else 49635 __IE__ = 1; 49636 @end 49637@*/ 49638 49639; 49640function heatmap_cart_view(container, probe_list, donor_list, options, event_options) { 49641 49642 this.container = $(container); 49643 this.probe_list = probe_list; 49644 this.donor_list = donor_list; 49645 49646 this.colormap_set = options ? (options.colormap_set ? options.colormap_set : []) : []; 49647 49648 this.box_width = options.box_width ? options.box_width : 200; 49649 this.box_height = options.box_height ? options.box_height : 150; 49650 this.probes_per_page = options.probes_per_page ? options.probes_per_page : 5; 49651 this.host = options.host ? options.host : '/mri_viewer/data'; 49652 this.num_rotations = 36; 49653 49654 this.surface_set = options.surface_set ? options.surface_set : { "lh.cortical":"Left Cortex", 49655 "rh.cortical":"Right Cortex", 49656 "subcortical":"Subcortex" }; 49657 this.default_surface = options.default_surface ? options.default_surface : "lh.cortical"; 49658 49659 this.pick_callback = event_options.pick_callback ? event_options.pick_callback : null; 49660 this.on_colormap_change = event_options? (event_options.on_colormap_change ? event_options.on_colormap_change : null) : null; 49661 this.on_level_change = event_options? (event_options.on_level_change ? event_options.on_level_change : null) : null; 49662 this.on_view_click = event_options? (event_options.on_view_click ? event_options.on_view_click : null) : null; 49663 this.on_clear_click = event_options? (event_options.on_clear_click ? event_options.on_clear_click : null) : null; 49664 49665 // private properties 49666 var self = this; 49667 var the_surface_widgets = []; 49668 var the_current_page = 0; 49669 var the_current_surface = "lh.cortical"; 49670 var the_angle = 9; // default left lateral 49671 var the_table_rows = []; 49672 var the_buttons = {}; 49673 var the_slider_tracks = []; 49674 var the_slider_handles = []; 49675 var the_sliders = []; 49676 var the_colormap_boxes = []; 49677 var the_colormaps = []; 49678 var the_control_list = []; 49679 var the_colormap_range = [-3,3]; 49680 var the_cart_table = null; 49681 var the_top_button_box = null; 49682 var the_bottom_button_box = null; 49683 49684 // public methods 49685 this.set_visibility = function(visible) { 49686 if (visible) 49687 $j(self.container).show(); 49688 else 49689 $j(self.container).hide(); 49690 49691 self.resize(); 49692 }; 49693 49694 this.set_colormap_range = function(lower,upper) { 49695 for (var i=0; i<the_colormaps.length; i++) 49696 the_colormaps[i].set_range(lower,upper); 49697 49698 do_level_change(lower,upper); 49699 }; 49700 49701 this.set_colormap = function(name) { 49702 for (var i=0; i<the_colormaps.length; i++) 49703 the_colormaps[i].set_colormap(name); 49704 49705 self.update_images(); 49706 }; 49707 49708 this.set_probe_list = function(probe_list) { 49709 self.probe_list = probe_list; 49710 build_view(); 49711 }; 49712 49713 this.set_donor_list = function(donor_list) { 49714 self.donor_list = donor_list; 49715 build_view(); 49716 }; 49717 49718 this.set_page_index = function(page_index) { 49719 if (the_current_page != page_index) { 49720 the_current_page = page_index; 49721 build_view(); 49722 } 49723 }; 49724 49725 this.update_images = function() { 49726 map = get_selected_colormap(self.colormap_set); 49727 49728 for (var i=0; i<the_surface_widgets.length; i++) { 49729 var widget = the_surface_widgets[i]; 49730 widget.colormap_range = the_colormap_range; 49731 widget.colormap_name = map.name; 49732 widget.colormap_normal = map.normal; 49733 49734 widget.set_sample({ sample_probe: self.probe_list[i].id }); 49735 widget.update_image(); 49736 } 49737 }; 49738 49739 this.update_data = function(info) { 49740 // figure out which page the selected probe is on 49741 var info_page = the_current_page; 49742 for (var i=0; i<self.probe_list.length; i++) {
49743 if (self.probe_list[i].id == info.probe.id) 49744 info_page = Math.floor(i / self.probes_per_page); 49745 } 49746 49747 // if this probe is on a different page, switch to it 49748 if (info_page != the_current_page) { 49749 the_current_page = info_page; 49750 build_view(); 49751 } 49752 49753 if (info.surface) 49754 the_current_surface = info.surface; 49755 49756 update_button_styles(); 49757 49758 for (var i=0; i<the_surface_widgets.length; i++) { 49759 var widget = the_surface_widgets[i]; 49760 49761 // this is ugly 49762 var widget_probe_id = widget.image_settings[0].probe.id; 49763 if (info.probe.id == widget_probe_id) { 49764 the_table_rows[i].addClassName("highlighted"); 49765 } else { 49766 the_table_rows[i].removeClassName("highlighted"); 49767 } 49768 49769 var sample = { 49770 probe: info.probe.id, 49771 donor: info.donor.id, 49772 rna_well: info.rna_well 49773 }; 49774 49775 widget.set_current_surface(the_current_surface); 49776 widget.set_sample(sample); 49777 widget.update_image(); 49778 } 49779 }; 49780 49781 this.resize = function() { 49782 // the table resizes itself. Resize the controls to match. 49783 if (the_cart_table && the_control_list) { 49784 var table_width = the_cart_table.getWidth(); 49785 49786 for (var i=0; i<the_control_list.length; i++) 49787 the_control_list[i].style.width = table_width+'px'; 49788 49789 the_bottom_button_box.style.width = table_width+'px'; 49790 the_top_button_box.style.width = table_width+'px'; 49791 } 49792 }; 49793 49794 49795 49796 function on_slider_change(event) { 49797 the_angle = event; 49798 49799 for (var i=0; i<the_sliders.length; i++) { 49800 var slider = the_sliders[i]; 49801 if (slider.value != event) 49802 slider.setValue(event); 49803 } 49804 49805 for (var i=0; i<the_surface_widgets.length; i++) { 49806 the_surface_widgets[i].set_angle(self.num_rotations - event); 49807 the_surface_widgets[i].update_image(); 49808 } 49809 49810 }; 49811 49812 function on_image_pick(response) { 49813 // tell the other images to update themselves 49814 for (var i=0; i<the_surface_widgets.length; i++) { 49815 var widget = the_surface_widgets[i]; 49816 widget.set_sample(response); 49817 widget.update_image(); 49818 } 49819 49820 // tell the outside world about the new sample 49821 if (self.pick_callback) { 49822 // non-obvious syntax for deep-copying an object 49823 var info = jQuery.extend(true,{},response); 49824 49825 for (var i=0; i<self.probe_list.length; i++) 49826 if (self.probe_list[i].id == response.probe) info.probe = this.probe_list[i]; 49827 for (var i=0; i<self.donor_list.length; i++) 49828 if (self.donor_list[i].donor.id == response.donor) info.donor = this.donor_list[i].donor; 49829 49830 self.pick_callback(self,info); 49831 } 49832 }; 49833 49834 function on_surface_change(value) { 49835 the_current_surface = value; 49836 49837 update_button_styles(); 49838 49839 // tell the other images to update themselves 49840 for (var i=0; i<the_surface_widgets.length; i++) { 49841 var widget = the_surface_widgets[i]; 49842 widget.set_current_surface(the_current_surface); 49843 widget.update_image(); 49844 } 49845 } 49846 49847 49848 function on_page_select(page_index, jq) { 49849 alert(['hit',page_index]); 49850 } 49851 49852 function clear_view() { 49853 // clear out the parent 49854 while (self.container.hasChildNodes()) { 49855 self.container.removeChild(self.container.lastChild); 49856 } 49857 49858 the_surface_widgets.clear(); 49859 the_table_rows.clear(); 49860 the_buttons = {}; 49861 the_slider_tracks.clear(); 49862 the_slider_handles.clear(); 49863 the_sliders.clear(); 49864 the_colormap_boxes.clear(); 49865 the_colormaps.clear(); 49866 the_control_list.clear(); 49867 the_cart_table = null; 49868 the_top_button_box = null; 49869 the_bottom_button_box = null; 49870 } 49871 49872 // private methods 49873 function build_view() { 49874 clear_view(); 49875 49876 // build the table 49877 the_cart_table = new Element('table', { 'class': 'hm_cart_table' }); 49878 the_cart_table.cellSpacing = "0"; 49879 the_cart_table.cellPadding = "5"; 49880 49881 // initialize the header 49882 var cart_header = new Element('thead'); 49883 var cart_header_row = new Element('tr'); 49884 49885 cart_header_row.setStyle({ color: '#fff', 49886 backgroundColor: "#4d6d86" }); 49887 49888 for (var i=0; i<self.donor_list.length; i++) { 49889 var h = new Element('th', { 'class': 'hm_cart_table_header' }); 49890 h.innerHTML = self.donor_list[i].donor.name; 49891 cart_header_row.appendChild(h); 49892 } 49893 49894 cart_header.appendChild(cart_header_row); 49895 the_cart_table.appendChild(cart_header); 49896 49897 the_cart_table_body = new Element('tbody', { 'class': 'heatmap_cart_table_body' }); 49898 the_cart_table.appendChild(the_cart_table_body); 49899 49900 // initialize array of buttons 49901 the_buttons = {}; 49902 for (var key in self.surface_set) 49903 the_buttons[key] = []; 49904 49905 var start = self.probes_per_page * the_current_page; 49906 var end = Math.min(start + self.probes_per_page, self.probe_list.length); 49907 var no_probes = end <= start; 49908 49909 var control_list_top = make_control_list(the_buttons,'hm_cart_anchor_top'); 49910 var control_list_bottom = make_control_list(the_buttons,'hm_cart_anchor_bottom'); 49911 49912 the_top_button_box = make_button_box(); 49913 the_bottom_button_box = make_button_box(); 49914 49915 update_button_styles(); 49916 49917 self.container.appendChild(the_top_button_box); 49918 self.container.appendChild(control_list_top); 49919 self.container.appendChild(the_cart_table); 49920 self.container.appendChild(control_list_bottom); 49921 self.container.appendChild(the_bottom_button_box); 49922 49923 49924 var selected_colormap = get_selected_colormap(self.colormap_set); 49925 49926 49927 // if there are no probes, indicate that and hide the controls 49928 if (no_probes) { 49929 var row = new Element('tr'); 49930 var td = new Element('td', { 'class': 'hm_cart_table_error' }); 49931 td.colSpan = cart_header_row.children.length; 49932 td.innerHTML = "No probes selected"; 49933 49934 row.appendChild(td); 49935 the_cart_table_body.appendChild(row); 49936 49937 $j(control_list_top).hide(); 49938 $j(control_list_bottom).hide(); 49939 } else { 49940 for (var i=start; i<end; i++) { 49941 var row = new Element('tr'); 49942 49943 var widget = new Element('div', { 'class': 'hm_cart_table_widget' }); 49944 widget.style.width = self.box_width*self.donor_list.length+'px';
49945 widget.style.height = self.box_height+'px'; 49946 49947 var geneLabel = new Element('div', { 'class': 'hm_cart_table_label vtip' }); 49948 geneLabel.title = "Probe: " + self.probe_list[i].name; 49949 geneLabel.innerHTML = self.probe_list[i]["gene-symbol"]; 49950 49951 var wrapper = new Element('div', { 'class': 'hm_cart_table_row_wrapper' }); 49952 49953 wrapper.appendChild(widget); 49954 wrapper.appendChild(geneLabel); 49955 49956 var widget_container = new Element('td', { 'class': 'hm_cart_table_row' }); 49957 widget_container.colSpan = self.donor_list.length; 49958 49959 widget_container.appendChild(wrapper); 49960 49961 row.appendChild(widget_container); 49962 49963 the_cart_table_body.appendChild(row); 49964 49965 var options = { 49966 image_settings: self.donor_list.map(function(d) { return {probe: self.probe_list[i], donor: d}; }), 49967 host: self.host, 49968 colormap_name: selected_colormap.name, 49969 colormap_range: selected_colormap.range, 49970 colormap_normal: selected_colormap.normal, 49971 slider_position: i == start ? "top" : (i == (end-1) ? "bottom" : "none"), 49972 num_rotations: self.num_rotations 49973 }; 49974 49975 var event_options = { 49976 pick_callback: on_image_pick.bind(self), 49977 slider_callback: on_slider_change.bind(self) 49978 }; 49979 49980 var surface_widget = new SurfaceWidget(widget, options, event_options); 49981 49982 49983 the_surface_widgets.push(surface_widget); 49984 49985 surface_widget.set_angle(self.num_rotations-the_angle); 49986 surface_widget.set_current_surface(the_current_surface); 49987 surface_widget.update_image(); 49988 49989 the_table_rows.push(row); 49990 } 49991 } 49992 49993 the_control_list.push(control_list_top); 49994 the_control_list.push(control_list_bottom); 49995 self.resize(); 49996 49997 for (var i=0; i<the_slider_handles.length; i++) { 49998 var slider = new Control.Slider(the_slider_handles[i], the_slider_tracks[i], 49999 { 50000 onChange:on_slider_change, 50001 onSlide:on_slider_change, 50002 range:$R(0,self.num_rotations-1), 50003 sliderValue:the_angle 50004 }); 50005 the_sliders.push(slider); 50006 } 50007 50008 var colormap_options = { 50009 colormap_set:self.colormap_set, 50010 normalized:selected_colormap.normal, 50011 zscore_limits:selected_colormap.limits, 50012 raw_limits:selected_colormap.limits, 50013 raw_expression_range:selected_colormap.range, 50014 norm_expression_range:selected_colormap.range 50015 }; 50016 50017 var colormap_events = { 50018 on_level_change:do_level_change, 50019 on_colormap_change:do_colormap_change 50020 }; 50021 50022 for (var i=0; i<the_colormap_boxes.length; i++) { 50023 var widget = new colormap_widget(the_colormap_boxes[i], colormap_options, colormap_events); 50024 the_colormaps.push(widget); 50025 } 50026 50027 vtip(); 50028 } 50029 50030 function make_button_box() { 50031 var button_box = new Element('div', { 'class': 'hm_cart_button_box' }); 50032 50033 var view_button = new Element('button', { 'class': 'hm_control_button' }); 50034 view_button.style.marginBottom = "2px"; 50035 view_button.style.marginTop = "2px"; 50036 view_button.update("View Selection Heatmap"); 50037 view_button.observe('click',do_view_click); 50038 $j(view_button).button(); 50039 50040 button_box.appendChild(view_button); 50041 50042 var clear_button = new Element('button', { 'class': 'hm_control_button' }); 50043 clear_button.style.marginBottom = "2px"; 50044 clear_button.style.marginTop = "2px"; 50045 clear_button.update("Clear Selections"); 50046 clear_button.observe('click',do_clear_click); 50047 $j(clear_button).button(); 50048 50049 button_box.appendChild(clear_button); 50050 50051 return button_box; 50052 } 50053 50054 function make_control_list(button_map, anchor_id) { 50055 50056 var control_container = new Element('div', { 'class': 'hm_cart_controls' }); 50057 50058 // add the buttons 50059 for (var key in self.surface_set) { 50060 var button = new Element('button', { 'class': 'hm_cart_button' }); 50061 button.update(self.surface_set[key]); 50062 button.observe('click',function(surface) { return function() { on_surface_change(surface); }; }(key)); 50063 $j(button).button(); 50064 button_map[key].push(button); 50065 50066 var div = new Element('div', { 'class': 'hm_cart_button_container' }); 50067 div.appendChild(button); 50068 control_container.appendChild(div); 50069 } 50070 50071 // add the pagination buttons in reverse order (float right) 50072 var pagination_element = new Element('div', { 'class': 'hm_cart_pagination_box' } ); 50073 50074 $j(pagination_element).pagination(self.probe_list.length, { 50075 callback: function(page_index) { self.set_page_index(page_index); }, 50076 items_per_page: self.probes_per_page, 50077 prev_text: "<", 50078 next_text: ">", 50079 link_to: "#"+anchor_id, 50080 num_display_entries: 2,
50081 num_edge_entries: 1, 50082 current_page: the_current_page }); 50083 50084 // add the label, and a clear break 50085 var label = new Element('div', { 'class': 'hm_cart_pagination_label' }); 50086 label.update('Jump to page: '); 50087 pagination_element.appendChild(label); 50088 50089 control_container.appendChild(pagination_element); 50090 50091 // add the color map box 50092 var ramp_box = new Element('div', {'class':'windowLevelContainer'}); 50093 control_container.appendChild(ramp_box); 50094 50095 var ramp_label_box = new Element('div', {'class':'ramp_label_box'}); 50096 ramp_label_box.update('Color Map:'); 50097 ramp_box.appendChild(ramp_label_box); 50098 50099 the_colormap_boxes.push(ramp_box); 50100 50101 // add the angle slider 50102 var slider_box = new Element('div', {'class':'status_zoom_box', title:'Rotate images'}); 50103 $j(slider_box).css('float','right'); 50104 50105 var slider_label_box = new Element('div', {'class':'zoom_label_box'}); 50106 slider_label_box.update('Rotate:'); 50107 slider_box.appendChild(slider_label_box); 50108 control_container.appendChild(slider_box); 50109 50110 var slider_track = new Element('div', {'class':'status_zoom_track' }); 50111 var slider_handle = new Element('div', {'class':'status_zoom_handle' }); 50112 slider_track.appendChild(slider_handle); 50113 slider_box.appendChild(slider_track); 50114 50115 the_slider_tracks.push(slider_track); 50116 the_slider_handles.push(slider_handle); 50117 50118 control_container.appendChild(new Element('div', { style: 'clear:both' })); 50119 50120 return control_container; 50121 } 50122 50123 function update_button_styles() { 50124 // update the button styles 50125 for (var key in the_buttons) { 50126 for (var i=0; i<the_buttons[key].length; i++) { 50127 var button = the_buttons[key][i]; 50128 if (key == the_current_surface) { 50129 $j(button).addClass('ui-priority-primary'); 50130 $j(button).removeClass('ui-priority-secondary'); 50131 } else { 50132 $j(button).addClass('ui-priority-secondary'); 50133 $j(button).removeClass('ui-priority-primary'); 50134 } 50135 } 50136 } 50137 } 50138 50139 function do_level_change(lower,upper) { 50140 if (lower == the_colormap_range[0] && upper == the_colormap_range[1]) 50141 return; 50142 50143 the_colormap_range = [lower,upper]; 50144 self.update_images(); 50145 50146 if (self.on_level_change) 50147 self.on_level_change(self,lower,upper); 50148 } 50149 50150 function do_colormap_change(sender, name, normalized, lower, upper) { 50151 self.set_colormap(name); 50152 self.update_images(); 50153 50154 if(self.on_colormap_change) 50155 self.on_colormap_change(self, name, normalized, lower, upper); 50156 } 50157 50158 function get_selected_colormap(map_set) { 50159 for(var i = 0; i < map_set.length; i++) 50160 if(map_set[i].selected) 50161 return(map_set[i]); 50162 50163 return(null); 50164 } 50165 50166 function do_view_click() { 50167 if (self.on_view_click) 50168 self.on_view_click(self); 50169 } 50170 50171 function do_clear_click() { 50172 if (self.on_clear_click) 50173 self.on_clear_click(self); 50174 } 50175 50176 function init() { 50177 build_view(); 50178 } 50179 50180 init(); 50181} 50182 50183 50184 50185; 50186function heatmap_control(options) { 50187 var app_name = options.app.app_name; 50188 var debug = options.query.debug; 50189 var search_type = options.query.search_type; 50190 var donor_ids = options.query.donor_ids; 50191 var rma_dao = options.rma_dao; 50192 var hide_details_pane = options.hide_details_pane; 50193 50194 var env_mri_image_server = options.env.env_mri_image_server; 50195 var heatmap_download_service = options.heatmap_download_service; 50196 var atlas_viewer_service = options.atlas_viewer_service; 50197 if (atlas_viewer_service == undefined) { 50198 // maintain backwards compatibility with old service name 50199 atlas_viewer_service = options.legend_service; 50200 } 50201 50202 var download_service_data_set = options.download_service_data_set; 50203 var env_related_projects_service = options.env.env_related_projects_service; 50204 var env_brain_explorer_host = options.env.env_brain_explorer_host; 50205 50206 var env_project_code_hba_microarray = options.env.env_project_code_hba_microarray; 50207 var env_supported_browsers = options.env.env_supported_browsers; 50208 50209 var app_explorer_path = options.app.app_explorer_path; 50210 var app_url_for_help_path = options.app.app_url_for_help_path; 50211 var app_url_for_correlation_help_path = options.env.app_url_for_correlation_help_path ? 50212 options.env.app_url_for_correlation_help_path : options.app.app_url_for_correlation_help_path; 50213 var app_url_for_search_controller_show_action = options.app.app_url_for_search_controller_show_action; 50214 50215 //var settings = new user_settings(options.user_defaults); 50216 var settings = options.app.settings; 50217 50218 var query_result_bag = options.query; 50219 50220 //var groupings = ['donors,structures','structures,donors']; 50221 var groupings = options.app.groupings; 50222 50223 //var resolutions = {100:'Coarse', 200:'Structures', samples:'Samples'}; 50224 var resolutions = options.app.resolutions; 50225 50226 var default_grouping = options.query.default_grouping; 50227 var colormap_set = options.app.colormap_set; 50228 var current_colormap = options.query.current_colormap; 50229 var current_sort = options.query.current_sort; 50230 var user_selection_mode = options.query.user_selection_mode; 50231 var donor_list = options.donor_list; 50232 var donor_brief_info = options.donor_brief_info; 50233 var stage_list = options.stage_list; 50234 var structure_list = options.structure_list; 50235 50236 var show_display_filters = options.show_display_filters; 50237 var display_donor_filter_type = options.display_donor_filter_type; 50238 var display_structure_filter_type = options.display_structure_filter_type; 50239 var ontology_options = settings.defaults ? (settings.defaults.ontology_options ? settings.defaults.ontology_options : {}) : {}; 50240 50241 var display_donors_specified = options.display_donors_specified; 50242 var display_donors_mapping_callbacks = options.display_donors_mapping_callbacks; 50243 50244 var app_page = options.app_page; 50245 var on_correlation_search = options.on_correlation_search; 50246 var on_view_selection_click = options.on_view_selection_click; 50247 var on_view_loaded = options.on_view_loaded; 50248 var on_heatmap_click = options.on_heatmap_click; 50249 var on_get_atlas_id = options.on_get_atlas_id; 50250 50251 var show_expression_value_on_status_bar = options ? ( options.show_expression_value_on_status_bar ? options.show_expression_value_on_status_bar : false ) : false;
50252 50253 var cookies = options.cookies; 50254 var cookie_gene_grouping = cookies.cookie_gene_grouping; 50255 var cookie_diff_grouping = cookies.cookie_diff_grouping; 50256 var cookie_correlation_grouping = cookies.cookie_correlation_grouping; 50257 var cookie_gene_zoom = cookies.cookie_gene_zoom; 50258 var cookie_differential_sort_column = cookies.cookie_differential_sort_column; 50259 var cookie_correlation_sort_order = cookies.cookie_correlation_sort_order; 50260 var cookie_gene_raw_level = cookies.cookie_gene_raw_level; 50261 var cookie_gene_norm_level = cookies.cookie_gene_norm_level; 50262 var cookie_restriction = cookies.cookie_restriction; 50263 var cookie_display_donors = cookies.cookie_display_donors; 50264 var cookie_display_structures = cookies.cookie_display_structures; 50265 var cookie_row_selections = cookies.cookie_row_selections; 50266 var cookie_current_cell_selection = cookies.cookie_current_cell_selection; 50267 var cookie_correlation_domain = cookies.cookie_correlation_domain; 50268 var cookie_correlation_stage = cookies.cookie_correlation_stage; 50269 var cookie_colormap = cookies.cookie_colormap; 50270 var cookie_resolution = cookies.cookie_resolution; 50271 var cookie_correlation_donors = cookies.cookie_correlation_donors; 50272 var cookie_first_click = cookies.cookie_first_click; 50273 var cookie_brain_explorer_installed = cookies.cookie_brain_explorer_installed; 50274 var cookie_grey_browser_notified = cookies.cookie_grey_browser_notified; 50275 50276 var the_heatmap_container = $('heatmap_view_container'); 50277 var the_detail_container = $('detail_view_container'); 50278 var the_cart_container = $('cart_view_container'); 50279 50280 var the_row_data = null; 50281 var the_image_data = null; 50282 var the_heatmap_view = null; 50283 var the_detail_view = null; 50284 var the_cart_view = null; 50285 50286 var link_options = { 50287 probe: '/microarray/gene/show/', 50288 gene: '/microarray/gene/show/', 50289 planar: '/mri_viewer?', 50290 structure: '?atlas=', 50291 atlas: '?atlas=', 50292 neuroblast: '', 50293 donor: '/mri_viewers/data?', 50294 brain_explorer: env_brain_explorer_host + '/mri_viewer/data/heatmap/probe_values' 50295 }; 50296 50297 var initialized = false; 50298 50299 var _self = this; 50300 50301 var select_colormap = options.select_colormap; 50302 50303 50304 function do_busy(sender, busy) { 50305 if (the_heatmap_view) 50306 the_heatmap_view.show_busy(busy); 50307 } 50308 50309// the values generated on the server are used here to talk to the heatmap service. 50310 function do_initial_query() { 50311 if (initialized) 50312 return; 50313 50314 //if(!debug) 50315 // $('page_block').show_busy(true); 50316 50317 var search_request = build_request(search_type); 50318 if (!search_request) { 50319 data_status_callback(DataStatus.STATUS_NO_RESULTS_FOUND, ''); 50320 return; 50321 } 50322 50323 run_query(search_request); 50324 } 50325 50326 function display_initial_state(type) { 50327 50328 switch (type) { 50329 case 'search': 50330 break; 50331 case 'differential': 50332 break; 50333 case 'correlation': 50334 var data = settings.get(cookie_current_cell_selection); 50335 if (data && data.probe) 50336 if (the_detail_view) the_detail_view.update_all(data); 50337 break; 50338 } 50339 } 50340 50341 function build_request(type) { 50342 50343 var request = {}; 50344 switch (type) { 50345 case 'search': 50346 request = build_gene_search_request(); 50347 break; 50348 case 'differential': 50349 request = build_diff_search_request(); 50350 break; 50351 case 'correlation': 50352 request = build_correlation_search_request(); 50353 break; 50354 } 50355 50356 return(request); 50357 } 50358 50359 /* function get_current_sort(type) { 50360 50361 var sort = ''; 50362 switch(type) { 50363 case 'differential': 50364 sort = settings.get(cookie_differential_sort_column); 50365 break; 50366 case 'correlation': 50367 sort = settings.get(cookie_correlation_sort_order); 50368 break; 50369 } 50370 50371 return(sort); 50372 }*/ 50373 50374 /* function get_current_grouping(type) { 50375 50376 var grouping = 0; 50377 switch(type) {
50378 case 'search': 50379 grouping = settings.get(cookie_gene_grouping); 50380 break; 50381 case 'differential': 50382 grouping = settings.get(cookie_diff_grouping); 50383 break; 50384 case 'correlation': 50385 grouping = settings.get(cookie_correlation_grouping); 50386 break; 50387 } 50388 50389 return(grouping); 50390 }*/ 50391 50392 function get_cookie_for_search(search_type, cookie_type) { 50393 50394 var cookie = ''; 50395 50396 switch (search_type) { 50397 case 'search': 50398 switch (cookie_type) { 50399 case 'grouping': 50400 cookie = cookie_gene_grouping; 50401 break; 50402 } 50403 break; 50404 case 'differential': 50405 switch (cookie_type) { 50406 case 'grouping': 50407 cookie = cookie_diff_grouping; 50408 break; 50409 case 'column': 50410 cookie = cookie_differential_sort_column; 50411 break; 50412 } 50413 break; 50414 case 'correlation': 50415 switch (cookie_type) { 50416 case 'grouping': 50417 cookie = cookie_correlation_grouping; 50418 break; 50419 case 'column': 50420 cookie = cookie_correlation_sort_order; 50421 break; 50422 } 50423 break; 50424 } 50425 50426 return(cookie); 50427 } 50428 50429 function set_cookie_for_search(search_type, cookie_type, value) { 50430 50431 var cookie = ''; 50432 50433 switch (search_type) { 50434 case 'search': 50435 break; 50436 case 'differential': 50437 switch (cookie_type) { 50438 case 'column': 50439 settings.set(cookie_differential_sort_column, value); 50440 break; 50441 } 50442 break; 50443 case 'correlation': 50444 switch (cookie_type) { 50445 case 'column': 50446 settings.set(cookie_correlation_sort_order, value); 50447 break; 50448 } 50449 break; 50450 } 50451 50452 return(cookie); 50453 } 50454 50455 50456 function run_query(search_request) { 50457 var dao_options; 50458 50459 switch (app_name) { 50460 case "nhp": 50461 dao_options = {trace_func: trace, 50462 on_busy: do_busy, 50463 //donor_base_url: '/microarray/', 50464 //donor_key: 'microarray_donor'}; 50465 rma_dao: rma_dao}; 50466 break; 50467 case "brainspan_rnaseq": 50468 dao_options = {trace_func: trace, 50469 on_busy: do_busy, 50470 //donor_base_url:'/rnaseq/', 50471 //donor_key:'rnaseq_donor', 50472 rma_dao: rma_dao}; 50473 break; 50474 case "brainspan_lcm": 50475 dao_options = {trace_func: trace, 50476 on_busy: do_busy, 50477 //donor_base_url:'/lcm/', 50478 //donor_key:'lcm_donor', 50479 rma_dao: rma_dao}; 50480 break; 50481 case "ahba": 50482 dao_options = {trace_func: trace, 50483 on_busy: do_busy, 50484 rma_dao: rma_dao}; 50485 break; 50486 50487 } 50488 50489 dao_options.donor_list = donor_list; // the donor_list is passed in from the options to heatmap_control 50490 50491 dao_options.cart_view_setup_callback = set_up_cart_view; 50492 50493 the_row_data = new heatmap_dao(null, data_status_callback, env_mri_image_server, dao_options); 50494 // successful session initialization will callback to the init_heatmap(session) function, 50495 // failure will call data_status_callback(error_status). 50496 the_row_data.initialize_session(search_type, search_request, init_heatmap); 50497 initialized = true; 50498 } 50499 50500 function build_gene_search_request() { 50501 var search_request = new Object(); 50502 50503 if (query_result_bag.user_selection_mode != HM_VIEW_SELECTION_OFF) { 50504 var id_cook = settings.get(cookie_row_selections); 50505 var ids = []; 50506 for (var key in id_cook) { 50507 ids.push(new Number(key)); 50508 } 50509 search_request.row_ids = ids; 50510 } 50511 else { 50512 search_request.row_ids = query_result_bag.all_ids; 50513 } 50514 50515 switch (app_name) { 50516 case "nhp": 50517 case "brainspan_rnaseq": 50518 case "brainspan_lcm": 50519 search_request.column_set = 'samples'; 50520 break; 50521 case "ahba": 50522 50523 search_request.column_set = settings.get(cookie_resolution); 50524 break; 50525 50526 } 50527 50528 //search_request.display_columns = [{donors:(query_result_bag.donor_ids ? query_result_bag.donor_ids : [] ), structures:[]}]; 50529 var donors_array = (display_donors_specified && display_donors_specified.length > 0) ? display_donors_specified.split(',') : []; 50530 donors_array = $j.map(donors_array, function (elem) { 50531 return parseInt(elem); 50532 }); // ensure it's a numeric array 50533 50534 // if display_donors_specified, we need to turn on display restriction, but the display_structures should be cleared 50535 // if display_donors_specified is undefined, we need to turn off display restriction/filtering, in this case, display_structures 50536 // is ignored when generating search_request.display_columns 50537 search_request.display_columns = [ 50538 {donors: donors_array, structures: []} 50539 ]; 50540 search_request.group_by = groupings[settings.get(cookie_gene_grouping)]; 50541 trace("gene search request: " + inspect(search_request)); 50542 50543 if (search_request.row_ids.length == 0) 50544 return(null); 50545 50546 return(search_request); 50547 } 50548 50549 function build_diff_search_request() { 50550 var search_request = new Object(); 50551 50552 50553 search_request.group_by = groupings[settings.get(cookie_diff_grouping)]; 50554 search_request.sort_by = settings.get(cookie_differential_sort_column); 50555 50556 switch (app_name) { 50557 case "nhp": 50558 //case "brainspan_rnaseq": 50559 search_request.column_set = 'samples'; 50560 var restrict = settings.get(cookie_restriction); 50561 50562 search_request.domain2 = [ 50563 {donors: query_result_bag.target_donors, structures: query_result_bag.target_domain} 50564 ]; 50565 search_request.domain1 = [ 50566 {donors: query_result_bag.contrast_donors, structures: query_result_bag.contrast_domain} 50567 ]; 50568 search_request.display_columns = [
50569 {donors: (restrict ? query_result_bag.contrast_donors.concat(query_result_bag.target_donors) : []), 50570 structures: (restrict ? query_result_bag.contrast_domain.concat(query_result_bag.target_domain) : [])} 50571 ]; 50572 50573 break; 50574 case "brainspan_rnaseq": 50575 search_request.column_set = 'samples'; 50576 //var restrict = settings.get(cookie_restriction); 50577 50578 search_request.domain2 = [ 50579 {donors: query_result_bag.target_donors, structures: query_result_bag.target_domain} 50580 ]; 50581 search_request.domain1 = [ 50582 {donors: query_result_bag.contrast_donors, structures: query_result_bag.contrast_domain} 50583 ]; 50584// search_request.display_columns = [{donors:(restrict ? query_result_bag.contrast_donors.concat( query_result_bag.target_donors ) : []), 50585// structures:(restrict ? query_result_bag.contrast_domain.concat( query_result_bag.target_domain ) : [])}]; 50586 // heatmap service should be able to handle repeated donor IDs or structure IDs 50587 search_request.display_columns = [ 50588 { donors: query_result_bag.contrast_donors.concat(query_result_bag.target_donors), 50589 structures: query_result_bag.contrast_domain.concat(query_result_bag.target_domain) } 50590 ]; 50591 break; 50592 case "brainspan_lcm": 50593 search_request.column_set = 'samples'; 50594 var restrict = settings.get(cookie_restriction); 50595 50596 search_request.domain2 = [ 50597 {donors: query_result_bag.donor_ids, structures: query_result_bag.target_domain} 50598 ]; 50599 search_request.domain1 = [ 50600 {donors: query_result_bag.donor_ids, structures: query_result_bag.contrast_domain} 50601 ]; 50602 search_request.display_columns = [ 50603 {donors: (restrict ? query_result_bag.donor_ids : []), 50604 structures: (restrict ? query_result_bag.contrast_domain.concat(query_result_bag.target_domain) : [])} 50605 ]; 50606 50607 break; 50608 case "ahba": 50609 50610 search_request.column_set = settings.get(cookie_resolution); 50611 50612 search_request.domain2 = [ 50613 {donors: donor_ids, structures: query_result_bag.target_domain} 50614 ]; 50615 search_request.domain1 = [ 50616 {donors: donor_ids, structures: query_result_bag.contrast_domain} 50617 ]; 50618 search_request.display_columns = [ 50619 {donors: donor_ids, structures: query_result_bag.contrast_domain.concat(query_result_bag.target_domain)} 50620 ]; 50621 break; 50622 } 50623 trace("diff params: " + inspect(search_request)); 50624 50625 return(search_request); 50626 } 50627 50628 function build_correlation_search_request() { 50629 var search_request = new Object(); 50630 var restrict = settings.get(cookie_restriction); 50631 50632 search_request.group_by = groupings[settings.get(cookie_correlation_grouping)]; 50633 search_request.sort_order = current_sort; 50634 //search_request.column_set = (app_name == 'brainspan')? 'samples':settings.get(cookie_resolution); 50635 search_request.probes = query_result_bag.seed; // [<%= @seed %>]; 50636 //search_request.domain = [{donors:[<%=@correlation_donors%>], structures:[<%=@correlation_domain%>]}]; 50637 //search_request.display_columns = [{donors:(restrict ? [<%=@correlation_donors%>] : []), structures:(restrict ? [<%= @correlation_domain %>] : [])}]; 50638 50639 switch (app_name) { 50640 case 'nhp': 50641 search_request.column_set = 'samples'; 50642 search_request.domain = [ 50643 {donors: query_result_bag.donor_ids, structures: query_result_bag.correlation_domain} 50644 ]; 50645 search_request.display_columns = [
50646// {donors: (restrict ? query_result_bag.donor_ids : []), 50647// structures: (restrict ? query_result_bag.correlation_domain : [])} 50648 // always restrict the display columns when making the initial heatmap request 50649 // user can trigger calls to do_display_change() to toggle the setting, and update the 50650 // display columns 50651 {donors: query_result_bag.donor_ids , 50652 structures: query_result_bag.correlation_domain } 50653 ]; 50654 break; 50655 50656 case 'brainspan_rnaseq': 50657 search_request.column_set = 'samples'; 50658 search_request.domain = [ 50659 {donors: query_result_bag.donor_ids, 50660 structures: query_result_bag.correlation_domain} 50661 ]; 50662 search_request.display_columns = [ 50663 {donors: query_result_bag.donor_ids, 50664 structures: query_result_bag.correlation_domain} 50665 ]; 50666 50667 break; 50668 50669 case 'brainspan_lcm': 50670 search_request.column_set = 'samples'; 50671 search_request.domain = [ 50672 {donors: query_result_bag.donor_ids, 50673 structures: query_result_bag.correlation_domain} 50674 ]; 50675 search_request.display_columns = [ 50676 {donors: (restrict ? query_result_bag.donor_ids : []), 50677 structures: (restrict ? query_result_bag.correlation_domain : [])} 50678 ]; 50679 50680 break; 50681 50682 case 'ahba': 50683 search_request.column_set = settings.get(cookie_resolution); 50684 search_request.domain = [ 50685 {donors: query_result_bag.donor_ids, structures: query_result_bag.correlation_domain} 50686 ]; 50687 search_request.display_columns = [ 50688 {donors: query_result_bag.donor_ids, structures: (restrict ? query_result_bag.correlation_domain : [])} 50689 ]; 50690 50691 break; 50692 } 50693 50694 trace("correlation params: " + inspect(search_request)); 50695 50696 return(search_request); 50697 } 50698 50699 function do_restriction_change(sender, restrict) { 50700 var params = the_row_data.get_session_params(); 50701 50702 switch (app_name) { 50703 case 'nhp': 50704 if (search_type == 'differential') 50705 params.display_columns = [ 50706 {donors: (restrict ? query_result_bag.contrast_donors.concat(query_result_bag.target_donors) : []), 50707 structures: (restrict ? query_result_bag.contrast_domain.concat(query_result_bag.target_domain) : [])} 50708 ]; 50709 50710 else if (search_type == 'correlation') 50711 params.display_columns = [ 50712 {donors: (restrict ? query_result_bag.correlation_donors : []), 50713 structures: (restrict ? query_result_bag.correlation_domain : [])} 50714 ]; 50715 50716 break; 50717 case 'brainspan_rnaseq': 50718 if (search_type == 'differential') 50719 params.display_columns = [ 50720 {donors: (restrict ? query_result_bag.contrast_donors.concat(query_result_bag.target_donors) : []), 50721 structures: (restrict ? query_result_bag.contrast_domain.concat(query_result_bag.target_domain) : [])} 50722 ]; 50723 50724 else if (search_type == 'correlation') 50725 params.display_columns = [ 50726 {donors: (restrict ? query_result_bag.donor_ids : []), 50727 structures: (restrict ? query_result_bag.correlation_domain : [])} 50728 ]; 50729 50730 break; 50731 50732 case 'brainspan_lcm': 50733 if (search_type == 'differential') 50734 params.display_columns = [ 50735 {donors: (restrict ? query_result_bag.donor_ids : []),
50736 structures: (restrict ? query_result_bag.contrast_domain.concat(query_result_bag.target_domain) : [])} 50737 ]; 50738 50739 else if (search_type == 'correlation') 50740 params.display_columns = [ 50741 {donors: (restrict ? query_result_bag.donor_ids : []), 50742 structures: (restrict ? query_result_bag.correlation_domain : [])} 50743 ]; 50744 50745 break; 50746 50747 case 'ahba': 50748 50749 if (search_type == 'differential') 50750 //params.display_columns = [{donors:donor_ids, structures:(restrict ? [<@contrast_domain.join(',') unless @target_domain.nil? >,<@target_domain.join(',') unless @target_domain.nil?>] : [])}]; 50751 params.display_columns = [ 50752 {donors: donor_ids, structures: (restrict ? query_result_bag.contrast_domain.concat(query_result_bag.target_domain) : [])} 50753 ]; 50754 //params.display_columns = [{donors:donor_ids, structures:(restrict ? [query_result_bag.contrast_domain, query_result_bag.target_domain] : [])}]; 50755 else if (search_type == 'correlation') 50756 //params.display_columns = [{donors:donor_ids, structures:(restrict ? [<@correlation_domain.join(',') unless @correlation_domain.nil%>] : [])}]; 50757 params.display_columns = [ 50758 {donors: donor_ids, structures: (restrict ? query_result_bag.correlation_domain : [])} 50759 ]; 50760 50761 break; 50762 50763 } 50764 50765 settings.set(cookie_restriction, restrict); 50766 the_row_data.update_session(params); 50767 } 50768 50769 50770 function do_display_change(sender, new_display_donors, new_display_structures, restriction_is_on) { 50771 //alert(new_display_structures); 50772 var current_restriction_state = settings.get(cookie_restriction); 50773 // persist info in cookies(localStorage) 50774 settings.set(cookie_restriction, restriction_is_on); 50775 50776 var donor_array = [], new_display_donor_str = ''; 50777 // transfer the new_display_donors when it is actually "new display donor stages" 50778 if (display_donor_filter_type == 'stages' && display_donors_mapping_callbacks) { 50779 // need to apply the mappings on the passed in new_display_donors 50780 // in this case, the new_display_donors is really "new display stages" 50781 if (new_display_donors && new_display_donors.length > 0) { 50782 donor_array = display_donors_mapping_callbacks.stage_to_donor_mapping(new_dis
50782play_donors); 50783 new_display_donor_str = donor_array.join(','); // now this is a list of donors 50784 } 50785 50786 } else { 50787 new_display_donor_str = new_display_donors; 50788 } 50789 settings.set(cookie_display_donors, new_display_donor_str); 50790 50791 settings.set(cookie_display_structures, new_display_structures); 50792 50793 if (current_restriction_state == false && restriction_is_on == false) { 50794 // when user changes the display filter setting while the "display filter" is off 50795 // we only need to save the display_donors and display_structures change 50796 // there is no need to trigger any additional request to server 50797 return; 50798 } 50799 50800 // update the data session and trigger the heatmap display change 50801 var params = the_row_data.get_session_params(); 50802 var display_donors = [], display_structures = []; 50803 50804 50805 if (restriction_is_on && new_display_donor_str && new_display_donor_str.length > 0) { 50806 display_donors = new_display_donor_str.split(','); 50807 display_donors = $j.map(display_donors, function (elem) { 50808 return parseInt(elem); 50809 }); // ensure it's a numeric array 50810 } 50811 50812 50813 if (restriction_is_on && new_display_structures && new_display_structures.length > 0) { 50814 display_structures = new_display_structures.split(','); 50815 display_structures = $j.map(display_structures, function (elem) { 50816 return parseInt(elem); 50817 }); 50818 } 50819 50820 params.display_columns = [ 50821 {donors: display_donors, structures: display_structures} 50822 ]; 50823 50824 the_row_data.update_session(params); 50825 } 50826 50827 50828 function do_grouping_change(sender, new_grouping) { 50829 var params = the_row_data.get_session_params(); 50830 params.group_by = new_grouping; 50831 50832 //var new_session = the_row_data.update_session(params); 50833 50834 //trace("received new session "); 50835 //the_heatmap_view.attach_session(new_session); 50836 50837 // track the grouping as a user setting 50838 for (var i = 0; i < groupings.length; i++) { 50839 if (groupings[i] == new_grouping) { 50840 var cook = get_cookie_for_search(search_type, 'grouping'); 50841 settings.set(cook, i); 50842 break; 50843 } 50844 } 50845 the_row_data.update_session(params); 50846 } 50847 50848 function do_level_change(sender, lower, upper) { 50849 var value = [(new Number(lower)).valueOf(), (new Number(upper)).valueOf()]; 50850 50851 switch (app_name) { 50852 case 'brainspan_rnaseq': 50853 var setting = cookie_gene_raw_level; 50854 break; 50855 case 'nhp': 50856 case 'ahba': 50857 case 'brainspan_lcm': 50858 var map_name = settings.get(cookie_colormap); 50859 var map = select_colormap(colormap_set, map_name); 50860 var setting = map.normal ? cookie_gene_norm_level : cookie_gene_raw_level; 50861 50862 if (the_heatmap_view && sender != the_heatmap_view) 50863 the_heatmap_view.set_colormap_range(value[0], value[1]); 50864 50865 if (the_cart_view && sender != the_cart_view) 50866 the_cart_view.set_colormap_range(value[0], value[1]); 50867 break; 50868 } 50869 50870 50871 trace("saving this setting: " + setting + " with this value: " + value); 50872 settings.set(setting, value); 50873 if (the_detail_view) the_detail_view.update_colormap(map_name, value); 50874 } 50875 50876// This function is only found in ahba. It has not be adapted for brainspan 50877 function do_colormap_change(sender, name, normalized, upper, lower) { 50878 settings.set(cookie_colormap, name); 50879 var map = select_colormap(colormap_set, name); 50880 var setting = map.normal ? cookie_gene_norm_level : cookie_gene_raw_level; 50881 var range = settings.get(setting); 50882 50883 if (heatmap_view) 50884 the_heatmap_view.set_colormap_range(range[0], range[1]); 50885 50886 if (heatmap_view && sender != the_heatmap_view) 50887 the_heatmap_view.set_colormap(name); 50888 50889 if (app_name == 'ahba' && the_cart_view && sender != the_cart_view) { 50890 the_cart_view.set_colormap(name); 50891 the_cart_view.set_colormap_range(range[0], range[1]); 50892 } 50893 50894 50895 if (the_detail_view) the_detail_view.update_colormap(name, range); 50896 } 50897 50898 50899 function do_zoom_change(sender, new_zoom) { 50900 settings.set(cookie_gene_zoom, new_zoom); 50901 } 50902 50903 50904 function do_column_click(sender, column_label) { 50905 var column = column_label.replace(' ', '-').toLowerCase(); 50906 50907 if (column == current_sort) 50908 return; 50909 50910 var params = {}; 50911 if (search_type == 'differential') { 50912 current_sort = column; 50913 params.sort_by = column; 50914 } 50915 else if (search_type == 'correlation') { 50916 current_sort = toggle_correlation_sort(); 50917 params.sort_order = current_sort; 50918 } 50919 50920 set_cookie_for_search(search_type, 'column', current_sort); 50921 50922 //var new_session = the_row_data.sort(params); 50923 50924 //trace("received sorted session "); 50925 //the_heatmap_view.attach_session(new_session); 50926 50927 if (search_type == 'differential') { 50928 the_heatmap_view.set_column_sort_icon((current_sort == 'fold-change' ? 'Fold Change' : 'p-value'), 'desc'); 50929 the_heatmap_view.set_column_sort_icon((current_sort == 'fold-change' ? 'p-value' : 'Fold Change'), 'none'); 50930 } 50931 else if (search_type == 'correlation') { 50932 the_heatmap_view.set_column_sort_icon(column, current_sort); 50933 } 50934 50935 the_row_data.sort(params); 50936 } 50937 50938 function toggle_correlation_sort() { 50939 //var cook = settings.get('<%= COOKIE_CORRELATION_SORT_ORDER %>'); 50940 return(current_sort == 'desc' ? 'asc' : 'desc'); 50941 } 50942 50943 function init_heatmap(new_session) { 50944 50945 try { 50946 // known session initialization errors, like invalid request params 50947 // will end up in one of these first two clauses: 50948 if (new_session === null) { 50949 alert('An error occurred processing your query'); 50950 return; 50951 } 50952 if (new_session.status == undefined) { 50953 //data_status_callback(DataStatus.STATUS_SERVER_ERROR, '<%= @error %>' ); 50954 data_status_callback(DataStatus.STATUS_SERVER_ERROR, query_result_bag.error); 50955 return; 50956 } 50957 if (new_session.status.id != 0) { 50958 data_status_callback(new_session.status.id, new_session.status.message); 50959 return; 50960 } 50961 if (new_session.status.id == 0 && new_session.number_of_probes == 0) { 50962 data_status_callback(DataStatus.STATUS_NO_RESULTS_FOUND, ""); 50963 return; 50964 } 50965 50966 $('page_block').show_busy(true); 50967 50968 // the current grouping should be part of the session, but isn't returned 50969 // from the service, so add it here. 50970 new_session.group_by = groupings[default_grouping]; 50971 // the data access components have to be initialized before the heatmap 50972 // UI component can be built. 50973 configure_data_access(new_session, current_colormap); 50974// var detail_options = { 50975// env_related_projects_service:env_related_projects_service, 50976// env_project_code:env_project_code_hba_microarray, 50977// cookies:cookies, 50978// env_brain_explorer_host:env_brain_explorer_host, 50979// app_explorer_path:app_explorer_path, 50980// 50981// } 50982 50983 manage_display_filtering_from_new_request(); 50984 50985 if (!hide_details_pane) 50986 the_detail_view = configure_detail_ui(); 50987 $('page_block').rebusy(); 50988 50989 the_heatmap_view = configure_heatmap_ui(session, the_row_data, the_image_data, search_type, colormap_set); 50990 50991 trace("got a heatmap view"); 50992 50993 Event.observe(window, 'resize', do_resize); 50994 display_initial_state(search_type); 50995 $('page_block').show_busy(true); 50996 } catch (e) { 50997 $('page_block').show_busy(false); 50998 alert("Search initialization failed, please try again."); 50999 alert(e.message); 51000 trace("init failed: " + e.message); 51001 } 51002 } 51003 51004 function set_up_cart_view(){ 51005 51006 if (app_name == 'ahba' && search_type == 'search' && user_selection_mode == HM_VIEW_SELECTION_THUMB ) { 51007 the_cart_view = configure_cart_ui(); 51008 51009 // set the default visibility 51010 if (the_cart_view) { 51011 the_cart_view.set_visibility(user_selection_mode == HM_VIEW_SELECTION_THUMB); 51012 } 51013 51014 } 51015 } 51016 51017 /*** 51018 a function managing the display filteing info persisted in localStorage 51019 ***/ 51020 function manage_display_filtering_from_new_request() { 51021// if(supplied_display_filtering_logic) { 51022// supplied_display_filtering_logic(); // callback function in the controller that initialized this heatmap_control object 51023// return; 51024// } 51025 51026 // HBA should work without using the supplied_display_filtering_logic() 51027 var temp_structures, temp_donors; 51028 51029 // add a helper array function borrowed from an online discussion 51030 // in this case, the Array members will be strings like "4001", "4002", etc 51031 Array.prototype.getUnique = function () { 51032 var u = {}, a = []; 51033 for (var i = 0, l = this.length; i < l; i++) { 51034 // skip the for duplicated key values 51035 if (u.hasOwnProperty(this[i])) { 51036 continue; 51037 } 51038 a.push(this[i]); 51039 u[this[i]] = 1; 51040 } 51041 return a; 51042 }; 51043 51044 switch (search_type) { 51045 case 'search': 51046 if (display_donors_specified) { 51047 settings.set(cookie_restriction, true); 51048 settings.set(cookie_display_donors, display_donors_specified); 51049 settings.set(cookie_display_structures, ''); // need to clear out the display structure filters 51050 } 51051 else { 51052 settings.set(cookie_restriction, false); // turn off display restriction/filtering 51053 // but keep the display_donors, display_structures as is 51054 } 51055 break; 51056 51057 case 'differential': 51058 settings.set(cookie_restriction, true); 51059 51060 // update the display_donors and display_structures with the query info 51061 if (app_name == "brainspan_rnaseq" || app_name == "nhp") { 51062 temp_donors = query_result_bag.contrast_donors.concat(query_result_bag.target_donors).getUnique(); 51063 temp_donors = temp_donors.join(','); 51064 } else { 51065 // if(app_name == "ahba") { 51066 temp_donors = donor_ids.join(','); // convert the id array to a comma-separated string 51067 } 51068 51069 settings.set(cookie_display_donors, temp_donors); 51070 51071 // contrast_domain may overlap with target_domain, so convert the concatenated string to an array and filter out dup values 51072 temp_structures = query_result_bag.contrast_domain.concat(query_result_bag.target_domain).getUnique(); 51073 settings.set(cookie_display_structures, temp_structures.join(',')); 51074 break; 51075 51076 case 'correlation': 51077 settings.set(cookie_restriction, true); 51078 51079 // update the display_donors and display_structures with the query info 51080 temp_donors = donor_ids.join(','); // convert the id array to a comma-separated string 51081 settings.set(cookie_display_donors, temp_donors); 51082 temp_structures = query_result_bag.correlation_domain.join(','); // convert the id array to a comma-separated string 51083 settings.set(cookie_display_structures, temp_structures); 51084 51085 break; 51086 51087 default: 51088 //case 'user_selections', etc 51089 51090 // do not change the display filtering settings here 51091 } 51092 51093 51094 } 51095 51096 51097 function configure_detail_ui() { 51098 var related_projects_img = env_related_projects_service + ''; 51099 var related_projects_html = env_related_projects_service.replace("jpg", "html"); 51100 51101 var map_name = settings.get(cookie_colormap);
51102 var map = select_colormap(colormap_set, map_name); 51103 var setting = map.normal ? cookie_gene_norm_level : cookie_gene_raw_level; 51104 var range = settings.get(setting); 51105 51106 var options = { 51107 related_data_html_url: related_projects_html, 51108 related_data_img_url: related_projects_img, 51109 structure_list: structure_list, 51110 stage_list: stage_list, 51111 selected_structure_list: settings.get(cookie_correlation_domain), 51112 selected_stage_list: settings.get(cookie_correlation_stage), 51113 donor_list: donor_list, 51114 donor_brief_info: donor_brief_info, 51115 selected_donor_list: settings.get(cookie_correlation_donors), 51116 brain_explorer_url: 'aibe://' + env_brain_explorer_host + '/mri_viewer/data/heatmap/probe_values?atlas=351&resolution=samples', 51117 planar_url: '/mri_viewer?', 51118 explorer_install_path: app_explorer_path, 51119 explorer_installed: settings.get(cookie_brain_explorer_installed), 51120 explorer_cookie: cookie_brain_explorer_installed, 51121 colormap_name: map_name, 51122 colormap_range: range, 51123 settings: settings 51124 }; 51125 51126 var event_options = { 51127 on_link_click: do_link_click, 51128 on_correlates_click: do_correlates_click, 51129 on_correlates_help_click: do_correlates_help_click 51130 }; 51131 51132 return( new info_pane(the_detail_container, options, event_options) ); 51133 } 51134 51135 51136// this is probably the bit that will vary the most from page to page. 51137 function configure_heatmap_ui(session, dao, the_image_data, search_type, colormap_set) { 51138 51139 51140 // each dataset (or search type) will have its own schema describing the 51141 // columns to be displayed. 51142 var schema = get_table_schema_for_search(search_type); 51143 51144 // these events will be routed here 51145 var event_options = { 51146 on_cell_click: do_cell_click, 51147 on_restriction_change: do_restriction_change, 51148 on_display_change: do_display_change, 51149 on_grouping_change: do_grouping_change, 51150 on_level_change: do_level_change, 51151 on_colormap_change: do_colormap_change, 51152 on_zoom: do_zoom_change, 51153 on_column_click: do_column_click, 51154 on_view_selection_click: do_view_selection_click, 51155 on_clear_click: do_clear_click, 51156 on_select_click: do_select_click, 51157 //on_row_click:do_row_click, //This event should not be handled by ahba 51158 on_resolution_change: do_resolution_change, 51159 on_visibility_change: null//do_visibility_change // toggle button disabled 51160 }; 51161 51162 // the heatmap component can be configured to fit itself in between a 51163 // top & bottom html element 51164 var top_element = $('searchArea') ? $('searchArea') : $('pTabBar'); 51165 51166 // which controls to show in the heatmap control bar 51167 var control_mask = HM_CONTROL_ZOOM | HM_CONTROL_COLOR_MAP | HM_CONTROL_VIEW_SELECTION_HM | HM_CONTROL_CLEAR_SELECTION; 51168 control_mask |= (search_type == 'differential' || search_type == 'correlation') ? HM_CONTROL_RESTRICT_DOMAINS : 0; 51169 if (app_name == 'ahba') { 51170 control_mask |= HM_CONTROL_RESOLUTION; 51171 control_mask |= HM_VIEW_SELECTION_HM; 51172 control_mask |= HM_CONTROL_VIEW_SELECTION_THUMB; 51173 } 51174 51175 51176 // for display restriction filtering 51177 // donor selection can have individual donors as options, or have donor age stages as options 51178 var display_donors_temp = settings.get(cookie_display_donors); 51179 if (typeof display_donors_temp != 'string') { 51180 display_donors_temp = ''; 51181 } 51182 var display_donor_groups = []; //donor_brief_info; 51183 var temp_donor_object; 51184 51185 51186 if (display_donor_filter_type == 'donors') { 51187 // construct an array display_donor_groups based on info from donor_list object, while maintaining a structure similar to array donor_brief_info 51188 for (var i = 0; i < donor_brief_info.length; i++) { 51189 temp_donor_object = {id: donor_brief_info[i].donor.id, 51190 name: donor_brief_info[i].donor.name, 51191 sex: donor_list[donor_brief_info[i].donor.id].sex, 51192 age: donor_list[donor_brief_info[i].donor.id].age.replace("yrs", "y"), // special request to change "24 yrs" to "24 y" 51193 race_or_strain: donor_list[donor_brief_info[i].donor.id].race_or_strain 51194 } 51195 display_donor_groups[i] = {donor: temp_donor_object}; 51196 } 51197 } else if (display_donor_filter_type == 'stages' && display_donors_mapping_callbacks) { 51198 display_donor_groups = stage_list; 51199 display_donors_temp = display_donors_mapping_callbacks.donor_to_stage_mapping(display_donors_temp);
51199 // mapped to stage string, such as 'II,III,IV' 51200 } 51201 51202 var heatmap_options = { 51203 fit_to_page: true, 51204 fit_upper_element_id: the_detail_container, 51205 fit_lower_element_ids: ['pFooter'], 51206 min_height: 300, 51207 table_schema: schema, 51208 show_selected: true, // show the selection checkboxes to the left of each row 51209 show_row_position: true, // show 51210 colormap_set: colormap_set, // as provided 51211 control_mask: control_mask, 51212 groupings: groupings, // globally scoped groupins array 51213 default_grouping: default_grouping, // the grouping string used at initialization 51214 default_restriction: settings.get(cookie_restriction), 51215 display_donors: display_donors_temp, 51216 display_structures: settings.get(cookie_display_structures), 51217 show_display_filters: show_display_filters, 51218 display_donor_groups: display_donor_groups, // this is for constructing the donor selection UI in the status bar 51219 display_donor_filter_type: display_donor_filter_type, 51220 display_structure_groups: structure_list, // for constructing the structure selection UI in the status bar, when the ontolgoy widget not used 51221 display_structure_filter_type: display_structure_filter_type, 51222 selections: settings.get(cookie_row_selections), 51223 user_selection_mode: user_selection_mode, // for ahba cart view 51224 tool_tip: 'Toggle sort by structure or sort by donor', // the default 'Toggle sort by structure or sort by donor' will be used 51225 show_expression_value_on_status_bar: show_expression_value_on_status_bar, 51226 heatmap_download_service: heatmap_download_service, 51227 download_service_data_set: download_service_data_set, 51228 ontology_options: ontology_options 51229 }; 51230 51231 // set additional options for each app 51232 switch (app_name) { 51233 case 'ahba': 51234 heatmap_options.resolutions = resolutions; 51235 heatmap_options.default_resolution = settings.get(cookie_resolution); 51236 51237 //custom option for hiding the donor age display, as needed in ahba 51238 heatmap_options.hide_donor_age_display = true; 51239 51240 //pass in a special option to support cart view ( this is only in ahba now ) 51241 heatmap_options.enable_cart_view = true; 51242 51243 // the heatmap is invisible initially in the hba 51244 heatmap_options.visible = user_selection_mode == HM_VIEW_SELECTION_OFF || 51245 user_selection_mode == HM_VIEW_SELECTION_HM; 51246 51247 event_options.on_row_click = do_row_click; 51248 break; 51249 51250 case 'nhp': 51251 heatmap_options.tool_tip = 'Toggle sort by structure or sort by age' // nhp special case overriding 51252 event_options.on_row_click = do_row_click; // This event will be generated by the ahba heatmap, however do_row_click should not be called in the case of ahba heatmap. 51253 break; 51254 case 'brainspan_rnaseq': 51255 event_options.on_row_click = do_row_click; // This event will be generated by the ahba heatmap, however do_row_click should not be called in the case of ahba heatmap. 51256 break; 51257 case 'brainspan_lcm': 51258 event_options.on_row_click = do_row_click; // This event will be generated by the ahba heatmap, however do_row_click should not be called in the case of ahba heatmap. 51259 51260 break; 51261 51262 } 51263 51264 trace("setting these selections: " + inspect(settings.get(cookie_row_selections))); 51265 51266 var view = new heatmap_view(the_heatmap_container, 51267 session.number_of_columns, 51268 the_image_data, 51269 dao, 51270 event_options, 51271 heatmap_options); 51272 return(view); 51273 51274 } 51275 51276 51277 function configure_cart_ui() { 51278 var options = { 51279 colormap_set: colormap_set, 51280 default_structure: [ 51281 {id: 4008} 51282 ], 51283 host: env_mri_image_server 51284 }; 51285 51286 var event_options = { 51287 pick_callback: do_cell_click, 51288 on_colormap_change: do_colormap_change, 51289 on_level_change: do_level_change, 51290 on_clear_click: do_clear_click, 51291 on_view_click: do_view_selection_click }; 51292 51293 var i = 0; 51294 var donor_list_array = []; // heatmap_cart_view expects an array 51295 for (var donor_id in donor_list) { 51296 donor_list_array[i] = {donor: donor_list[donor_id] }; 51297 donor_list_array[i++].donor.id = parseInt(donor_id); 51298 } 51299 51300 return( new heatmap_cart_view(the_cart_container, 51301 get_selected_probes(), 51302 donor_list_array, 51303 options, 51304 event_options) ); 51305 } 51306 51307 51308 function do_correlates_help_click() { 51309 var helpWindow = window.open(app_url_for_correlation_help_path, 51310 "ViewerHelp", "width=860,height=500,resizable=1,scrollbars=1"); 51311 } 51312 51313 function configure_data_access(for_session, for_colormap) { 51314 session = for_session; 51315 the_row_data.attach_session(session); 51316 // these values will probably be the same for all datasets 51317 var DEFAULT_TILE_HEIGHT = 41; // HACK for IT-1779 51318 var tile_height = Math.min(DEFAULT_TILE_HEIGHT, session.number_of_probes); 51319 var tile_format_options = { 51320 colormap: for_colormap.name, 51321 normalize: for_colormap.normal, 51322 pixel_height: 4, 51323 pixel_width: 1, 51324 group_by: session.group_by, 51325 expr_window: for_colormap.range 51326 }; 51327 trace("building the the_image_data with these options: " + inspect(tile_format_options)); 51328 the_image_data = new heatmap_tiler(env_mri_image_server, session, session.number_of_columns, DEFAULT_TILE_HEIGHT, tile_height, tile_format_options); 51329 trace("built the tiler"); 51330 } 51331 51332// this function is only found in ahba 51333 /* function select_colormap(map_set, name) {
51334 for(var i = 0; i < map_set.length; i++) { 51335 if(map_set[i].name == name) { 51336 map_set[i].selected = true; 51337 return(map_set[i]); 51338 } 51339 } 51340 51341 return(null); 51342 }*/ 51343 51344 51345 function do_correlates_click(sender, info, selected_structures, selected_stages) { 51346 switch (app_name) { 51347 case 'ahba': 51348 settings.set(cookie_correlation_domain, selected_structures); 51349 settings.set(cookie_correlation_donors, selected_stages); 51350 //var path = app_url_for_search_controller_show_action; 51351 //window.location.href = path+'?type=correlation&domain='+encodeURIComponent(selected_structures)+'&seed='+info.probe.id + "&donors=" + selected_stages.join(','); 51352 //window.location.href = path+'?type=correlation&domain='+selected_structures.join(',')+'&seed='+info.probe.id; 51353 51354 // construct a state object 51355 var search_state = { 51356 domain: selected_structures, 51357 donors: selected_stages, // "donors" for hba 51358 search_type: 'correlation', 51359 search_term: '', 51360 seed: String(info.probe.id) 51361 }; 51362 if (on_correlation_search) 51363 on_correlation_search(search_state); 51364 51365 51366 break; 51367 51368 case 'brainspan_rnaseq': 51369 settings.set(cookie_correlation_domain, selected_structures); 51370 settings.set(cookie_correlation_stage, selected_stages); 51371// var query = app_url_for_search_controller_show_action + '?type=correlation'; 51372// query += '&domain='+selected_structures.join(','); 51373// query += '&stages='+selected_stages.join(','); 51374// query += '&seed='+info.probe.id; 51375// window.location.href = query; 51376 51377 // construct a state object 51378 var search_state = { 51379 domain: selected_structures, 51380 stages: selected_stages, // "donors" for hba 51381 search_type: 'correlation', 51382 search_term: '', 51383 seed: String(info.probe.id) 51384 }; 51385 if (on_correlation_search) 51386 on_correlation_search(search_state); 51387 51388 break; 51389 51390 case 'brainspan_lcm': 51391 settings.set(cookie_correlation_domain, selected_structures); 51392 //settings.set( cookie_correlation_stages, selected_stages); 51393// var query = app_url_for_search_controller_show_action + '?type=correlation'; 51394// query += '&domain='+selected_structures.join(','); 51395// //query += '&stages='+selected_stages.join(','); 51396// query += '&seed='+info.probe.id; 51397// window.location.href = query; 51398 51399 // construct a state object 51400 var search_state = { 51401 domain: selected_structures, 51402 donors: selected_stages, // "donors" for hba 51403 search_type: 'correlation', 51404 search_term: '', 51405 seed: String(info.probe.id) 51406 }; 51407 if (on_correlation_search) 51408 on_correlation_search(search_state); 51409 51410 break; 51411 51412 51413 case 'nhp': 51414 settings.set(cookie_correlation_domain, selected_structures); 51415 settings.set(cookie_correlation_stage, selected_stages); 51416 51417 // construct a state object 51418 var search_state = { 51419 domain: selected_structures, 51420 stage: selected_stages, // ages such as E40, "0 mo" 51421 search_type: 'correlation', 51422 //search_term: '', 51423 seed: String(info.probe.id) 51424 }; 51425 if (on_correlation_search) 51426 on_correlation_search(search_state); 51427 51428 break; 51429 51430 } 51431 51432 } 51433 51434 51435// this function is only used in ahba 51436 function do_resolution_change(sender, new_res) { 51437 var params = the_row_data.get_session_params(); 51438 params.column_set = new_res; 51439 the_row_data.update_session(params); 51440 settings.set(cookie_resolution, new_res); 51441 } 51442 51443 51444 function do_link_click(source, info) { 51445 var the_atlas_id; 51446 51447 switch (app_name) { 51448 case 'ahba': 51449 switch (source) { 51450 case 'gene': 51451 window.location.href = link_options[source] + info.probe['gene-id']; 51452 break; 51453 case 'probe': 51454 window.location.href = link_options[source] + info.probe['gene-id']; 51455 break; 51456 case 'donor': 51457 window.location.href = link_options[source] + 'donor=' + info.donor.id; 51458 break; 51459 case 'structure': 51460 the_atlas_id = on_get_atlas_id(info.donor); 51461 window.open(atlas_viewer_service + link_options[source] + the_atlas_id + "&structure=" + info.structure[info.structure.length - 1].id); 51462 break; 51463 default: 51464 alert('unknown link'); 51465 } 51466 break; 51467 51468 case 'nhp': 51469 switch (source) { 51470 case 'gene': 51471 window.location.href = '/microarray/genes/show?id=' + info['probe']['gene-id']; 51472 break; 51473 case 'probe': 51474 window.location.href = '/microarray/probe/show/' + info['probe']['id']; 51475 break; 51476 default: 51477 trace('unknown link'); 51478 } 51479 break; 51480 51481 case 'brainspan_rnaseq': 51482 switch (source) { 51483 case 'gene': 51484 window.location.href = '/rnaseq/gene/' + info.probe['id']; 51485 break; 51486 case 'atlas': 51487 the_atlas_id = on_get_atlas_id(info.donor); 51488 window.open(atlas_viewer_service + "?atlas=" + the_atlas_id + "&structure=" + info.structure[info.structure.length - 1].id); 51489 break; 51490 default: 51491 trace('unknown link'); 51492 } 51493 break; 51494 51495 case 'brainspan_lcm': 51496 switch (source) { 51497 case 'gene': 51498 case 'probe': 51499 window.location.href = '/lcm/gene/' + info.probe['gene-id'] 51500 break; 51501 case 'donor': 51502 window.location.href = link_options[source] + 'donor=' + info.donor.id; 51503 break; 51504 case 'atlas': 51505 the_atlas_id = on_get_atlas_id(info.donor); 51506 window.open(atlas_viewer_service + "?atlas=" + the_atlas_id + "&structure=" + info.structure[info.structure.length - 1].id); // atlas for human, 21 pcw 51507 break; 51508 default: 51509 alert('unknown link'); 51510 } 51511 break; 51512 51513 } 51514 51515 } 51516 51517 function do_clear_click(sender) { 51518 51519 if (!confirm("Clear all the selections?")) 51520 return; 51521 51522 settings.set(cookie_row_selections, {}); 51523 51524 if (sender == the_heatmap_view && the_heatmap_view) 51525 the_heatmap_view.set_selections({}); 51526 51527 if (app_name == "ahba" && the_cart_view) 51528 the_cart_view.set_probe_list([]); 51529 } 51530 51531 function do_view_selection_click(sender, mode) { 51532 switch (app_name) // we can use one app url variable to hold a valid server URL path based on app_name 51533 { 51534 case 'ahba': 51535 mode = mode || HM_VIEW_SELECTION_HM; 51536 //window.location.href = app_url_for_search_controller_show_action + 51537 // '?type=user_selections&user_selection_mode='+mode; 51538 if (on_view_selection_click) { 51539 var search_state = { 51540 search_type: 'user_selections', 51541 user_selection_mode: mode 51542 }; 51543 on_view_selection_click(search_state); // calls the search method outside of the top heatmap component 51544 } 51545 else { // on the gene page, we need to load a different page, which is the search page 51546 window.location.href = app_url_for_search_controller_show_action + 51547 '?search_type=user_selections&user_selection_mode=' + mode; 51548 51549 } 51550 break; 51551 51552 case 'brainspan_rnaseq': 51553 window.location.href = app_url_for_search_controller_show_action + '?search_type=user_selections'; 51554 break; 51555 51556 case 'brainspan_lcm': 51557 window.location.href = app_url_for_search_controller_show_action + '?search_type=user_selections'; 51558 break; 51559 51560 case 'nhp': 51561 window.location.href = app_url_for_search_controller_show_action + '?search_type=user_selections'; 51562 break; 51563 51564 } 51565 } 51566 51567 51568 function do_select_click(sender, selected_id, selected_state) { 51569 51570 var selections = settings.get(cookie_row_selections); 51571 if (selected_state) 51572 selections[selected_id] = true; 51573 else 51574 delete selections[selected_id]; 51575 51576 settings.set(cookie_row_selections, selections); 51577 trace("just set selections as: " + settings.get(cookie_row_selections)); 51578 } 51579 51580 function do_cell_click(sender, data) { 51581 do_cover_click(); 51582 51583 if (app_name == 'ahba') { 51584 51585 // populate the donor with any missing information 51586 if (!data.donor.color) { 51587 the_row_data.get_donor_column_info(data.donor); 51588 } 51589 } 51590 51591 51592 settings.set(cookie_current_cell_selection, data); 51593 settings.set(cookie_first_click, false); 51594 if (the_detail_view) the_detail_view.update_all(data); 51595 51596 51597 if (app_name == 'ahba' && the_cart_view) 51598 the_cart_view.update_data(data); 51599 51600 if (on_heatmap_click) // for the gene page's info widget
51601 on_heatmap_click(data.probe.id); // the gene info widget only needs the probe id 51602 51603 } 51604 51605 function do_row_click(sender, row, data) { 51606 do_cover_click(); 51607 settings.set(cookie_first_click, false); 51608 settings.set(cookie_current_cell_selection, data); 51609 if (the_detail_view) the_detail_view.update_all(data); 51610 51611 if (on_heatmap_click) // for the gene page's info widget 51612 on_heatmap_click(data.probe.id); // the gene info widget only needs the probe id 51613 51614 } 51615 51616 function do_visibility_change(sender) { 51617 if (app_name == 'ahba') { 51618 visibility = the_heatmap_view.get_visibility(); 51619 51620 selections = settings.get(cookie_row_selections); 51621 probes = get_selected_probes(); 51622 51623 if (sender == the_cart_view) { 51624 the_heatmap_view.set_visibility(!visibility); 51625 the_cart_view.set_visibility(visibility); 51626 } else if (sender == the_heatmap_view) { 51627 the_cart_view.set_visibility(!visibility); 51628 the_cart_view.set_probe_list(probes); 51629 } 51630 } 51631 } 51632 51633 function do_view_loaded() { 51634 var last_zoom = settings.get(cookie_gene_zoom); 51635 the_heatmap_view.set_zoom(last_zoom); 51636 $('page_block').show_busy(false); 51637 51638 var never_clicked = settings.get(cookie_first_click); 51639 if (never_clicked) 51640 show_here_and_there(); 51641 51642 if (on_view_loaded) 51643 on_view_loaded(); 51644 } 51645 51646 function data_status_callback(status, msg) { 51647 switch (status) { 51648 case DataStatus.STATUS_DATA_READY: 51649 the_heatmap_view.init(); 51650 do_view_loaded(); 51651 break; 51652 case DataStatus.STATUS_SESSION_READY: 51653 if (the_heatmap_view) 51654 the_heatmap_view.attach_session(the_row_data.session); 51655 trace("status: " + status + "; msg: " + msg); 51656 break; 51657 case DataStatus.STATUS_NO_RESULTS_FOUND: 51658 show_error('No results matched your query'); 51659 break; 51660 case DataStatus.STATUS_SESSION_EXPIRED: 51661 show_error('Your session has expired due to inactivity. Please refresh the page to continue'); 51662 break; 51663 case DataStatus.STATUS_CELL_DATA_FAIL: 51664 show_error("Error retrieving data: " + msg); 51665 break; 51666 case DataStatus.STATUS_REQUEST_INTERRUPTED: 51667 show_error('Communication with the server was interrupted. Try re-running your search, or use your browser\'s "Reload" or "Refresh" button to continue'); 51668 break; 51669 case DataStatus.STATUS_SESSION_NOT_FOUND: 51670 show_error('Communication with the server was interrupted. Try re-running your search, or use your browser\'s "Reload" or "Refresh" button to continue'); 51671 break; 51672 default: 51673 show_error(msg); 51674 } 51675 51676 $('page_block').show_busy(false); 51677 51678 if (on_view_loaded) 51679 on_view_loaded(); 51680 51681 } 51682 51683 function show_error(error) { 51684 51685 var content = "<div class='ui-widget'>"; 51686 switch (app_name) { 51687 case 'ahba': 51688 content += "<div class='ui-state-highlight ui-corner-all' style='padding: 0 .7em; width:50%; margin:auto; margin-top: 100px;border-color:#99b !important; background-color:#f7f7ff !important;'>"; 51689 content += "<p><div style='margin:auto;width:80%'><span class='ui-icon ui-icon-info' style='float: left; margin-right: .3em;'></span>"; 51690 content += error + '.'; 51691 content += "</p></div></div></div>"; 51692 break; 51693 51694 case 'nhp': 51695 case 'brainspan_rnaseq': 51696 case 'brainspan_lcm': 51697 content += "<div class='ui-state-highlight ui-corner-all' style='padding: 0 .7em; padding-bottom:20px;width:50%; margin:auto; margin-top: 100px;border-color:#99b !important; background-color:#f7f7ff !important;'>"; 51698 content += "<p><div style='margin:auto;width:80%'><span class='ui-icon ui-icon-info' style='float: left; margin-right: .3em;'></span>"; 51699 content += error + '.'; 51700 content += "</p></div></div></div>"; 51701 break; 51702 51703 } 51704 51705 the_heatmap_container.update(content); 51706 } 51707 51708 function get_table_schema_for_search(type) { 51709 51710 var table_schema = null; 51711 51712 switch (app_name) { 51713 case 'ahba': 51714 switch (type) { 51715 // this is redundant; the default schema again 51716 case 'search': 51717 table_schema = [ 51718 {id: 'gene_symbol', name: 'gene-symbol', label: 'Gene Symbol', assoc_id: 'gene-id'}, 51719 {id: 'probe_name', name: 'name', label: 'Probe Name', assoc_id: 'id'} 51720 ]; 51721 break; 51722 case 'correlation': 51723 table_schema = [ 51724 {id: 'gene_symbol', name: 'gene-symbol', label: 'Gene Symbol', assoc_id: 'gene-id'}, 51725 {id: 'r', name: 'r', label: 'r', assoc_id: null, sortable: true, sort: current_sort} 51726 ];
51727 trace("r: " + inspect({id: 'r', name: 'r', label: 'r', assoc_id: null, sortable: true, sort: current_sort})); 51728 break; 51729 case 'differential': 51730 table_schema = [ 51731 {id: 'gene_symbol', name: 'gene-symbol', label: 'Gene Symbol', assoc_id: 'id', sortable: false}, 51732 {id: 'p', name: 'p', label: 'p-value', assoc_id: null, sortable: true, sort: (current_sort == 'p-value' ? 'desc' : 'none')}, 51733 {id: 'fold_change', name: 'fold-change', label: 'Fold Change', assoc_id: null, sortable: true, sort: (current_sort == 'fold-change' ? 'desc' : 'none')} 51734 ]; 51735 break; 51736 default: 51737 table_schema = [ 51738 {id: 'gene_symbol', name: 'gene-symbol', label: 'Gene Symbol', assoc_id: 'id'}, 51739 {id: 'probe_name', name: 'name', label: 'Probe Name', assoc_id: 'id'} 51740 ]; 51741 } 51742 51743 break; 51744 51745 case 'nhp': 51746 51747 switch (type) { 51748 // this is redundant; the default schema again 51749 case 'search': 51750 case 'user_selections': 51751 table_schema = [ 51752 {id: 'gene_symbol', name: 'gene-symbol', label: 'Gene Symbol', assoc_id: 'gene-id', alternate_name: 'name'}, 51753 {id: 'probe_name', name: 'name', label: 'Probe Name', assoc_id: 'id'} 51754 ]; 51755 break; 51756 case 'correlation': 51757 table_schema = [ 51758 {id: 'gene_symbol', name: 'gene-symbol', label: 'Gene Symbol', assoc_id: 'gene-id', alternate_name: 'name'}, 51759 {id: 'r', name: 'r', label: 'r', assoc_id: null, sortable: true, sort: current_sort} 51760 ]; 51761 trace("r: " + inspect({id: 'r', name: 'r', label: 'r', assoc_id: null, sortable: true, sort: current_sort})); 51762 break; 51763 case 'differential': 51764 table_schema = [ 51765 {id: 'gene_symbol', name: 'gene-symbol', label: 'Gene Symbol', assoc_id: 'id', alternate_name: 'name', sortable: false}, 51766 {id: 'p', name: 'p', label: 'p-value', assoc_id: null, sortable: true, sort: (current_sort == 'p-value' ? 'desc' : 'none')}, 51767 {id: 'fold_change', name: 'fold-change', label: 'Fold Change', assoc_id: null, sortable: true, sort: (current_sort == 'fold-change' ? 'desc' : 'none')} 51768 ]; 51769 break; 51770 default: 51771 table_schema = [ 51772 {id: 'gene_symbol', name: 'gene-symbol', label: 'Gene Symbol', assoc_id: 'id'}, 51773 {id: 'probe_name', name: 'name', label: 'Probe Name', assoc_id: 'id'} 51774 ]; 51775 } 51776 break; 51777 51778 case 'brainspan_rnaseq': 51779 51780 switch (type) { 51781 // this is redundant; the default schema again 51782 case 'microarray': 51783 table_schema = [ 51784 {id: 'gene_symbol', name: 'gene-symbol', label: 'Gene Symbol', assoc_id: 'gene-id'}, 51785 {id: 'probe_name', name: 'name', label: 'Probe Name', assoc_id: 'id'} 51786 ]; 51787 break; 51788 case 'correlation': 51789 table_schema = [ 51790 {id: 'gene_symbol', name: 'gene-symbol', label: 'Gene Symbol', assoc_id: 'gene-id'}, 51791 {id: 'r', name: 'r', label: 'r', assoc_id: null, sortable: true, sort: current_sort} 51792 ]; 51793 trace("r: " + inspect({id: 'r', name: 'r', label: 'r', assoc_id: null, sortable: true, sort: current_sort})); 51794 break; 51795 case 'differential': 51796 table_schema = [ 51797 {id: 'gene_symbol', name: 'gene-symbol', label: 'Gene Symbol', assoc_id: 'id', sortable: false}, 51798 {id: 'p', name: 'p', label: 'p-value', assoc_id: null, sortable: true, sort: (current_sort == 'p-value' ? 'desc' : 'none')}, 51799 {id: 'fold_change', name: 'fold-change', label: 'Fold Change', assoc_id: null, sortable: true, sort: (current_sort == 'fold-change' ? 'desc' : 'none')} 51800 ]; 51801 break; 51802 default: 51803 table_schema = [ 51804 {id: 'gene_symbol', name: 'gene-symbol', label: 'Gene Symbol', assoc_id: 'id'}, 51805 {id: 'probe_name', name: 'name', label: 'Ensembl ID', assoc_id: 'id'} 51806 ]; 51807 } 51808 break; 51809 51810 case 'brainspan_lcm': 51811 switch (type) { 51812 // this is redundant; the default schema again 51813 case 'search': 51814 table_schema = [ 51815 {id: 'gene_symbol', name: 'gene-symbol', label: 'Gene Symbol', assoc_id: 'gene-id'}, 51816 {id: 'probe_name', name: 'name', label: 'Probe Name', assoc_id: 'id'} 51817 ]; 51818 break; 51819 case 'correlation': 51820 table_schema = [ 51821 {id: 'gene_symbol', name: 'gene-symbol', label: 'Gene Symbol', assoc_id: 'gene-id'}, 51822 {id: 'r', name: 'r', label: 'r', assoc_id: null, sortable: true, sort: current_sort} 51823 ];
51824 trace("r: " + inspect({id: 'r', name: 'r', label: 'r', assoc_id: null, sortable: true, sort: current_sort})); 51825 break; 51826 case 'differential': 51827 table_schema = [ 51828 {id: 'gene_symbol', name: 'gene-symbol', label: 'Gene Symbol', assoc_id: 'id', sortable: false}, 51829 {id: 'p', name: 'p', label: 'p-value', assoc_id: null, sortable: true, sort: (current_sort == 'p-value' ? 'desc' : 'none')}, 51830 {id: 'fold_change', name: 'fold-change', label: 'Fold Change', assoc_id: null, sortable: true, sort: (current_sort == 'fold-change' ? 'desc' : 'none')} 51831 ]; 51832 break; 51833 default: 51834 table_schema = [ 51835 {id: 'gene_symbol', name: 'gene-symbol', label: 'Gene Symbol', assoc_id: 'id'}, 51836 {id: 'probe_name', name: 'name', label: 'Probe Name', assoc_id: 'id'} 51837 ]; 51838 } 51839 51840 break; 51841 } 51842 51843 return(table_schema); 51844 } 51845 51846 function do_resize() { 51847 the_heatmap_view.resize(); 51848 if (the_cart_view) 51849 the_cart_view.resize(); 51850 // $('page_block').rebusy(); // refresh with the updated ahba code 51851 } 51852 51853 function show_not_supported() { 51854 show_error('Your browser is not supported in this portion of the \n\ 51855 application. <p></p>To see our list of supported browsers and platforms, please \n\ 51856 visit our <a href="/help.html/#supported" style="color:#00f">Help</a> \n\ 51857 page'); 51858 } 51859 51860 function show_unknown_support() { 51861 $j('#browser_warning').dialog({width: 410, 51862 height: 260, 51863 modal: true, 51864 buttons: {"Ok": function () { 51865 $('page_block').show_busy(true); 51866 do_initial_query(); 51867 $j(this).dialog("close"); 51868 } }}); 51869 } 51870 51871 function get_selected_probes() { 51872 ids = settings.get(cookie_row_selections); 51873 all_probes = the_row_data.get_probe_list(0, the_row_data.number_of_rows); 51874 selected_probes = []; 51875 for (var i = 0; i < all_probes.length; i++) { 51876 for (var id in ids) { 51877 if (ids[id] && parseInt(id) == all_probes[i].id) 51878 selected_probes.push(all_probes[i]); 51879 } 51880 } 51881 51882 return selected_probes; 51883 }; 51884 51885 this.get_heatmap_dao = function () { 51886 return the_row_data; 51887 } 51888 51889 this.init_page = function () { 51890 do_initial_query(); 51891 } 51892 51893 function do_cover_click() { 51894 var img = $('here_and_there_img'); 51895 img.hide(); 51896 } 51897 51898 function show_here_and_there() { 51899 var img = $('here_and_there_img'); 51900 var img_dims = img.getDimensions(); 51901 var cont = $('heatmap_view_container'); 51902 var cont_dims = cont.getDimensions(); 51903 51904 var left = (cont_dims.width / 2) - (img_dims.width / 2) - 60; 51905 img.clonePosition($('heatmap_view_container'), {setWidth: false, 51906 setHeight: false, 51907 offsetTop: -96, 51908 offsetLeft: left}); 51909 51910 img.observe('click', do_cover_click); 51911 img.show(); 51912 $j('#here_and_there_img').fadeOut(10000); 51913 } 51914} 51915; 51916 51917 51918 51919// many of the heatmap_dao methods run asynchronously and require 51920// a callback parameter. All callbacks will be invoked with an hdResponse 51921// object as the sole parameter. If the asynch method succeeded the 51922// hdResponse "success" property will be true and the payload will be 51923// the method-specific data requested. If "success" is false, the payload 51924// might be null, might be an error string. 51925function hdResponse(success, payload) 51926{ 51927 this.success = success; 51928 this.payload = payload; 51929} 51930 51931var DataStatus = { 51932 51933 STATUS_DATA_READY: 0, 51934 STATUS_SESSION_READY: 1, 51935 STATUS_DONOR_NOT_FOUND: 2, 51936 STATUS_INVALID_RESOLUTION: 3, 51937 STATUS_STRUCTURE_NOT_FOUND: 4, 51938 STATUS_TOO_MANY_COLUMNS: 5, 51939 STATUS_SERVER_ERROR: 6, 51940 STATUS_SESSION_NOT_FOUND: 7, 51941 STATUS_INVALID_COLUMN_GROUPING: 8, 51942 STATUS_INVALID_SORT_ORDER: 9, 51943 STATUS_INVALID_SORT_ATTRIBUTE: 10, 51944 STATUS_MISSING_SEED_PROBE: 11, 51945 STATUS_MISSING_SEARCH_DOMAIN: 12, 51946 STATUS_CANNOT_SORT: 13, 51947 STATUS_NOT_ENOUGH_SAMPLES: 16, 51948 STATUS_INVALID_START_ROW: 17, 51949 STATUS_NO_RESULTS_FOUND: 18, 51950 STATUS_PROBE_ID_FAIL: -2, 51951 STATUS_PROBE_META_FAIL: -3, 51952 STATUS_COLUMN_FAIL: -4, 51953 STATUS_CELL_DATA_FAIL: -5, 51954 STATUS_SESSION_EXPIRED: -6, 51955 STATUS_DATA_ERROR: -7, 51956 STATUS_SERVICE_400_ERROR_AS_HTML: -13, 51957 STATUS_REQUEST_INTERRUPTED: -20, 51958 HTTP_ERROR:400 51959} 51960 51961function heatmap_dao(session, status_callback, data_engine_base_url, options) 51962{ 51963 51964// public properties 51965
51966 // a session is an anonymous object containing these fields: 51967 // session_id, number_of_probes, number_of_columns, status. 51968 // 51969 // status is an anonymous object contining these fields: 51970 // id, message 51971 this.session = session; 51972 this.number_of_rows = 0; 51973 this.number_of_columns = 0; 51974 51975 // errors that occur in the context of an ajax call should be handled 51976 // by the individual methods. This is a catchall to notifiy the owning 51977 // process of initialization and other out-of-method events and errors. 51978 this.status_callback = status_callback; 51979 51980 // the host:port/service prefix for communicating with the data engine. 51981 // Assumes no trailing slash. 51982 this.data_engine_base_url = data_engine_base_url; 51983 51984 this.trace_function = options ? options.trace_func : null; // assign a funtion(msg) to this to handle debug output 51985 this.on_busy = options ? options.on_busy : null; 51986 this.donor_base_url = options ? options.donor_base_url ? options.donor_base_url : '' : ''; 51987 //this.donor_key = options ? options.donor_key ? options.donor_key : 'donor' : 'donor'; 51988 51989 this.rma_dao = options? options.rma_dao ? options.rma_dao : null : null; 51990 51991 var cart_view_setup_callback = options ? (options.cart_view_setup_callback ? options.cart_view_setup_callback : null ) : null; 51992 51993// public methods 51994 51995 // initialize the session with a query. 51996 // type: the type of query to perform. Currently 'search' or 'differenential' 51997 // or 'correlation' 51998 // params: the params appropriate to the given search type. 51999 // callback: a function to call back into with the response from 52000 // the initialization attempt. 52001 this.initialize_session = function(type, params, callback) { 52002 do_initalize_session(type, params, callback); 52003 } 52004 52005 this.get_session_params = function() { 52006 return(request_params); 52007 } 52008 52009 this.update_session = function(params) { 52010 do_update_session(params); 52011 this.session.group_by = params.group_by; 52012 return(this.session); 52013 } 52014 52015 52016 this.sort = function(params) { 52017 do_sort(params); 52018 52019 return(this.session); 52020 } 52021 52022 // probably some initialization here, 52023 // otherwise this goes away and we can 52024 // just use the session property. 52025 this.attach_session = function(session, keep_probes) { 52026 //trace("Attaching Session:\nStatus: " + inspect(session.status) + "\nSession: " + inspect(session) ); 52027 heartbeat(false); 52028 52029 this.session = session; 52030 if(this.session === null || this.session.status.id != 0) { 52031 return; 52032 } 52033 this.number_of_rows = this.session.number_of_probes; 52034 this.number_of_columns = this.session.number_of_columns; 52035 52036 QUERY_URL = this.data_engine_base_url + "heatmap/query/" + this.session.session_id; 52037 ROWS_URL = this.data_engine_base_url + "heatmap/probes/" + this.session.session_id + "?showDetail=1"; 52038 COLUMNS_URL = this.data_engine_base_url + "heatmap/columns/" + this.session.session_id; 52039 SORT_URL = this.data_engine_base_url + "heatmap/sort_probes/" + this.session.session_id; 52040 INFO_URL = this.data_engine_base_url + "heatmap/session_info/" + this.session.session_id; 52041 52042 // get all the probe IDs from the data engine 52043 // get the probe meta data from the web app 52044 // 52045 // a fancier scheme would have us getting & caching 52046 // the probe metadata only as it is requested. 52047 if(!keep_probes) { 52048 // prevent race condition w/Chrome browser when doing probe column sorting in Differential view 52049 fetch_probe_list(attach_session_callback, true); // ASYNC Ajax call 52050 } 52051 else 52052 attach_session_finished(); 52053 } 52054 52055 /** 52056 * Callback to fetch_probe_list() when called within attach_session() 52057 * 52058 * @param response 52059 * @param async same value as used in fetch_probe_list 2nd arg, should be 'true' 52060 */ 52061 function attach_session_callback(response, async) { 52062 receive_probe_id_list(response, async); 52063 52064 // we should have received the probe info needed by the cart view now 52065 if(cart_view_setup_callback) 52066 cart_view_setup_callback(); 52067 52068 attach_session_finished(); 52069 } 52070 52071 /** 52072 * final actions for attach_session() actions. 52073 * Placed in this function to ensure completion of fetch_probe_list() before the asynch call to 52074 * fetch_column_list(). 52075 */ 52076 function attach_session_finished() { 52077 fetch_column_list(receive_column_list); 52078 52079 session_time = new Date().getTime(); 52080 heartbeat(true); 52081 raise_busy(false); 52082 52083 if(self.status_callback) 52084 self.status_callback(DataStatus.STATUS_SESSION_READY, 'SESSION READY'); 52085 } 52086 52087 this.row_count = function() { 52088 return(this.session.number_of_probes); 52089 } 52090 52091 this.column_count = function() { 52092 return(this.session.number_of_columns); 52093 } 52094 52095 this.get_status = function() { 52096 return(this.session.status); 52097 } 52098 52099 // returns an array of probes and associated metadata 52100 // corresponding to the current data set. 52101 this.get_probe_list = function(start_row, num_rows) {
52102 return(fetch_row_set(start_row, num_rows)); 52103 } 52104 52105 // returns an array of column metadata. Each element in the array is an 52106 // anonymous object having these fields: 52107 // donor, structure_id, name, abbreviation, sample_id. 52108 // NOTE that sample_id will only be defined if the current 52109 // column set consists of samples. 52110 this.get_column_list = function() { 52111 return(column_list); 52112 } 52113 52114 this.get_donor_list = function() { 52115 return(donor_list); 52116 } 52117 52118 // this is detailed information for the column, row. The 52119 // return value will be an object containing raw & normalized expression 52120 // values and an array of structures describing the ontological position 52121 // of the given column (sample or structure). THe fields are named: 52122 // expression_level, z-score, structure, where structure is an array containing: 52123 // id, name, abbreviation. 52124 // 52125 // column & row are column_id (structure or sample, depending on 52126 // the data set) and probe_id. 52127 // 52128 // ?? 52129 // As a convenience, should we return the probe information 52130 // for the given row as well? 52131 this.get_cell_data_detail = function(column, row, callback) { 52132 fetch_cell_data(column, row, callback); 52133 } 52134 52135 // column & row are grid coordinates, that is indexes into the 52136 // set of columns and rows (probes). 52137 this.get_cell_data_brief = function(column, row) { 52138 var info = new Object(); 52139 info.column = column_list[column]; 52140 info.probe = probe_id_list[row]; 52141 return(info); 52142 } 52143 52144 this.get_donor_column_info = function(donor,column) { 52145 var more_donor = null; 52146 if (column) { 52147 more_donor = column_list[column].donor; 52148 } else { 52149 52150 for (var i=0; i<column_list.length; i++) { 52151 var col = column_list[i]; 52152 if (col.donor.id == donor.id) { 52153 more_donor = col.donor; 52154 break; 52155 } 52156 } 52157 } 52158 52159 donor.color = more_donor.color; 52160 donor.abbreviation = more_donor.abbreviation; 52161 } 52162 52163 52164// private properties 52165 var self = this; 52166 var INITIALIZE_URL = self.data_engine_base_url + "heatmap/initialize"; 52167 var DIFFERENTIAL_URL = self.data_engine_base_url + "search/differential_probes"; 52168 var CORRELATION_URL = self.data_engine_base_url + "search/correlated_probes"; 52169 var DONORS_URL = self.donor_base_url + "donors.json"; 52170 var QUERY_URL = ""; 52171 var ROWS_URL = ""; 52172 var COLUMNS_URL = ""; 52173 var SORT_URL=""; 52174 var INFO_URL=""; 52175 52176 var probe_id_list = []; 52177 var column_list = null; 52178 var donor_list = options.donor_list; 52179 52180 var probes_ready = false; 52181 var columns_ready = false; 52182 var donors_ready = false; 52183 var ready = false; 52184 var required_rows = 0; 52185 var heartbeat_id = null; 52186 52187 var request_params = {}; 52188 var session_time = null; 52189 52190 var updating = false; 52191 var sorting = false; 52192 52193 52194 52195// private methods 52196 52197 function heartbeat(start) { 52198 if(!start && heartbeat_id) { 52199 clearInterval(heartbeat_id); 52200 } 52201 else if(start) { 52202 heartbeat_id = setInterval(ping_session, 60000); 52203 } 52204 } 52205 52206 function ping_session() { 52207 var time = new Date().getTime(); 52208 trace("keep-alive ping, session life time = " + (time - session_time) + " milliseconds."); 52209 new Ajax.Request(INFO_URL + '?req=' + time, 52210 { 52211 method:'get', 52212 contentType:"application/json", 52213 onSuccess:function(response) { 52214 if(response.status != 200) { 52215 trace("info ping failed: " + inspect(response)); 52216 ping_failure(response); 52217 } 52218 else { 52219 trace("ping success: " + inspect(response.responseJSON)); 52220 } 52221 }, 52222 onFailure:function(response) { 52223 ping_failure(response); 52224 } 52225 }); 52226 } 52227 52228 function ping_failure(response) { 52229 trace("uh oh. A ping failed: " + inspect(response)); 52230 } 52231 52232 function fetch_row_set(start_row, num_rows) { 52233 // boundary check 52234 52235 start_row = start_row < 0 ? 0 : start_row; 52236 start_row = start_row >= self.number_of_rows ? self.number_of_rows - 1 : start_row; 52237 num_rows = num_rows + start_row > self.number_of_rows ? self.number_of_rows - start_row : num_rows; 52238 52239 required_rows = start_row + num_rows; 52240 while(((start_row + num_rows) > probe_id_list.length)) { 52241 trace("fetching more data, " + required_rows + " required of " + self.number_of_rows + " available, " + probe_id_list.length + " already onboard"); 52242 fetch_probe_list(receive_probe_id_list, false); 52243 raise_busy(true); 52244 } 52245 52246 var the_data = probe_id_list.slice(start_row, start_row+num_rows); 52247 //trace("the first row: " + inspect(the_data[0])); 52248 raise_busy(false); 52249 return(the_data); 52250 } 52251 52252 52253 function request_failure(response) { 52254 trace("request failure: " + inspect(response)); 52255 raise_busy(false); 52256 if(!response) { 52257 if(self.status_callback) 52258 self.status_callback(DataStatus.STATUS_DATA_ERROR, 'Unknown Data Error'); 52259 52260 return; 52261 } 52262 52263 // an HTTP response status of 0 generally means that the 52264 // response was returned without any headers. This could happen behind 52265 // a firewall, for instance. We don't really expect a firewall in this 52266 // situation, because our users have to be able to get to our web page 52267 // before the app starts making these ajax requests. 52268 // 52269 // Also possible is that the request has been interupted, as could happen 52270 // if the user leaves the page before the response is received. 52271 // 52272 // perhaps also possible if the user generates so many requests that they 52273 // interrupt each other. Which is the assumption I'm going to try making 52274 // here. 52275 // 52276 // Also possible that the client & service are running on different 52277 // machines, and the error message is filtered out as a security risk. 52278 if(response.status == 0) { 52279 var err_text = "Communication with the server failed or was interrupted"; 52280 var err_code = DataStatus.STATUS_REQUEST_INTERRUPTED; 52281 if(self.status_callback) 52282 self.status_callback(err_code, err_text); 52283 52284 return; 52285 } 52286 52287 // a response was actually received; 500 errors are returned by the 52288 // service, other HTTP status errors can be returned as well. 52289 if(self.status_callback) { 52290 try { 52291 switch(response.status) { 52292 case 500: 52293 var error = eval("(" + response.responseText + ")"); 52294 self.status_callback(error.status.id, error.status.message); 52295 break; 52296 default: 52297 self.status_callback(DataStatus.HTTP_ERROR, 52298 'There was an error communicating with the server: ' + 52299 response.status + ' ' + response.statusText); 52300 break; 52301 } 52302 } catch(e) { 52303 self.status_callback(DataStatus.STATUS_DATA_ERROR, 'Unknown Data Error: ' + e.message); 52304 } 52305 } 52306 } 52307 52308 function raise_busy(busy) { 52309 if(self.on_busy) 52310 self.on_busy(self, busy); 52311 } 52312 52313 function do_update_session(params) { 52314 if(updating) 52315 return; 52316 52317 try { 52318 updating = true; 52319 raise_busy(true); 52320 52321 // trace("the last search was this type: " + request_params.search_type); 52322 var type = request_params.search_type; 52323 request_params = params; 52324 request_params.search_type = type; 52325 52326 var request_body = new Object(); 52327 switch(request_params.search_type) { 52328 case 'search': 52329 request_body.resolution = params.column_set; 52330 request_body.display_columns = params.display_columns; 52331 request_body.group_by = params.group_by; 52332 break; 52333 case 'differential': 52334 request_body.group_by = params.group_by; 52335 request_body.sort_by = params.sort_by; 52336 request_body.resolution = params.column_set; 52337 request_body.domain1 = params.domain1; 52338 request_body.domain2 = params.domain2; 52339 request_body.display_columns = params.display_columns; 52340 if(request_body.donors) 52341 delete request_body.donors; 52342 break; 52343 case 'correlation': 52344 request_body.group_by = params.group_by; 52345 request_body.resolution = params.column_set; 52346 request_body.display_columns = params.display_columns; 52347 break; 52348 } 52349 52350 //trace("updating with this url: " + INITIALIZE_URL + "/" + self.session.session_id); 52351 //trace("and this request: " + inspect(request_body)); 52352 52353 52354 new Ajax.Request(INITIALIZE_URL + "/" + self.session.session_id, 52355 { 52356 method:'post', 52357 contentType:"application/json", 52358 postBody: $H(request_body).toJSON(), 52359 asynchronous:true, 52360 onSuccess:function(response) { 52361 if(response.status != 200) { 52362 trace("update failed: " + inspect(response)); 52363 request_failure(response); 52364 } 52365 else { 52366 //trace("update success: " + inspect(response)); 52367 self.attach_session(response.responseJSON, true); 52368 } 52369 updating = false;
52370 }, 52371 onFailure:function(response) { 52372 request_failure(response); 52373 updating = false; 52374 } 52375 }); 52376 } catch(e) { 52377 trace("exception doing an update request: " + e.message); 52378 updating = false; 52379 } 52380 } 52381 52382 52383 function do_sort(params) { 52384 if(sorting) 52385 return; 52386 52387 try { 52388 sorting = true; 52389 raise_busy(true); 52390 52391 var type = request_params.search_type; 52392 probe_id_list = []; 52393 52394 var request_body = new Object(); 52395 switch(request_params.search_type) { 52396 case 'differential': 52397 request_body.sort_by = params.sort_by; 52398 break; 52399 case 'correlation': 52400 request_body.sort_order = params.sort_order; 52401 break; 52402 } 52403 52404 trace("updating with this url: " + SORT_URL + "/" + self.session.session_id); 52405 trace("and this request: " + inspect(request_body)); 52406 52407 new Ajax.Request(SORT_URL + "/" + self.session.session_id, 52408 { 52409 method:'post', 52410 contentType:"application/json", 52411 postBody: $H(request_body).toJSON(), 52412 asynchronous:true, 52413 onSuccess:function(response) { 52414 if(response.status != 200) { 52415 trace("sort failed: " + inspect(response)); 52416 request_failure(response); 52417 } 52418 else { 52419 trace("sort success: " + inspect(response)); 52420 self.attach_session(response.responseJSON); 52421 } 52422 sorting = false; 52423 }, 52424 onFailure:function(response) { 52425 request_failure(response); 52426 sorting = false; 52427 } 52428 }); 52429 } catch(e) { 52430 trace("exception doing a sort request: " + e.message); 52431 sorting = false; 52432 } 52433 } 52434 52435 function receive_session_update(data) { 52436 self.attach_session(data); 52437 } 52438 52439 // (these data access methods are just stubbed out now to show the return types) 52440 function do_initalize_session(type, params, callback) { 52441 52442 raise_busy(true); 52443 request_params = params; 52444 request_params.search_type = type; 52445 var request_body = new Object(); 52446 var url = ""; 52447 switch(type) { 52448 case 'search': 52449 request_body.probes = params.row_ids; 52450 request_body.resolution = params.column_set; 52451 request_body.display_columns = params.display_columns; 52452 request_body.group_by = params.group_by; 52453 url = INITIALIZE_URL; 52454 break; 52455 case 'differential': 52456 request_body.group_by = params.group_by; 52457 request_body.sort_by = params.sort_by; 52458 request_body.resolution = params.column_set; 52459 request_body.domain1 = params.domain1; 52460 request_body.domain2 = params.domain2; 52461 request_body.display_columns = params.display_columns; 52462 url = DIFFERENTIAL_URL; 52463 break; 52464 case 'correlation': 52465 request_body.group_by = params.group_by; 52466 request_body.sort_order = params.sort_order; 52467 request_body.resolution = params.column_set; 52468 request_body.domain = params.domain; 52469 request_body.probes = params.probes; 52470 request_body.display_columns = params.display_columns; 52471 url = CORRELATION_URL; 52472 trace("for domain: " + inspect(request_body.domain[0])); 52473 trace("for display: " + inspect(request_body.display_columns[0])); 52474 break; 52475 } 52476 trace("initializing with this url: " + url); 52477 trace(request_params.search_type + " request: " + inspect(request_body)); 52478 //delete Array.prototype.toJSON; 52479 52480 //delete Object.prototype.toJSON; 52481 // test 52482 //var postcontent = $H(request_body); 52483 //var pjson = postcontent.toJSON(); 52484 //var pjson = JSON.stringify(request_body); 52485 52486 new Ajax.Request(url, 52487 { 52488 method:'post', 52489 contentType:"application/json", 52490 postBody: $H(request_body).toJSON(), 52491 onSuccess:function(response) { 52492 if(response.status != 200) { 52493 trace("success, not 200"); 52494 request_failure(response) 52495 } 52496 else 52497 callback(response.responseJSON); 52498 }, 52499 onFailure:function(response) { 52500 request_failure(response); 52501 } 52502 }); 52503 } 52504 52505 function fetch_probe_list(callback, asynch) { 52506
52507 trace("fetching rows with this url: " + ROWS_URL+"&startRow=" + probe_id_list.length); 52508 new Ajax.Request(ROWS_URL+"&startRow=" + probe_id_list.length, 52509 {method:'get', 52510 asynchronous:asynch, 52511 onSuccess:function(response) { 52512 if(response.status != 200) { 52513 request_failure(response) 52514 } 52515 else { 52516 callback(response, asynch); 52517 } 52518 }, 52519 onFailure:function(response) { 52520 request_failure(response); 52521 } 52522 }); 52523 } 52524 52525 function munge_stop_pos(data) { 52526 if(!data) 52527 return; 52528 52529 if(!data[0]) 52530 return; 52531 52532 if(!data[0]['end-position']) 52533 return; 52534 52535 for(var i = 0; i < data.length; i++) { 52536 data[i]['end-position'] = new Number(data[i]['end-position']) - new Number(data[i]['start-position']); 52537 } 52538 } 52539 52540 function fetch_donor_list(callback) { 52541 52542// new Ajax.Request(DONORS_URL, 52543// {method:'get', 52544// onSuccess:function(response) { 52545// if(response.status != 200) 52546// request_failure(response) 52547// else { 52548// callback(response); 52549// } 52550// }, 52551// onFailure:function(response) {request_failure(response);} 52552// }); 52553 52554 var dao_options = { 52555 success_callback: callback, 52556 error_callback: function(response) {request_failure(response);} 52557 }; 52558 52559 self.rma_dao.donor_info_by_product_ids("", dao_options); // empty product id provided, so the default product id for this app will be filled in 52560 52561 } 52562 52563 function fetch_column_list(callback) { 52564 52565 new Ajax.Request(COLUMNS_URL, 52566 {method:'get', 52567 onSuccess:function(response) { 52568 if(response.status != 200) 52569 request_failure(response) 52570 else { 52571 callback(response); 52572 } 52573 }, 52574 onFailure:function(response) {request_failure(response);} 52575 }); 52576 } 52577 52578 function fetch_cell_data(column, row, callback) { 52579 if(column >= self.number_of_columns || row >= self.number_of_rows 52580 || row < 0 || column < 0) { 52581 return; 52582 } 52583 52584 new Ajax.Request(QUERY_URL + "?column=" + parseInt(column) + "&row=" + parseInt(row), 52585 {method:'get', 52586 onSuccess:function(response) { 52587 if(response.status != 200) { 52588 request_failure(response) 52589 } 52590 else { 52591 var probe = probe_id_list[row]; 52592 if (response.responseJSON != null) { 52593 var donor = donor_list[response.responseJSON.donor]; 52594 self.get_donor_column_info(donor,column); 52595 response.responseJSON.probe = probe; 52596 response.responseJSON.donor = donor; 52597 response.responseJSON.top_level_structure = column_list[column].top_level_structure; // HACK, this is for DevHuman LCM to get the top_level_strucutre info 52598 } 52599 callback(response); 52600 } 52601 }, 52602 onFailure:function(response) {request_failure(response);} 52603 }); 52604 } 52605 52606 function receive_probe_id_list(response, finish) { 52607 var probe_set = eval("(" + response.responseText + ")"); 52608 52609 trace("there are " + probe_set.length + " probes in this response."); 52610 munge_stop_pos(probe_set); 52611 probe_id_list = probe_id_list.concat(probe_set); 52612 52613 if(finish) { 52614 probes_ready = true; 52615 check_ready(); 52616 } 52617 } 52618 52619// function receive_donor_list(response) { 52620// var ret = response.responseJSON; 52621// if(ret === null || !(ret.success == 'true')) { 52622// if(self.status_callback) 52623// self.status_callback(DataStatus.STATUS_PROBE_META_FAIL, ret ? ret.msg : ''); 52624// return; 52625// } 52626// 52627// var donors = response.responseJSON.msg; 52628// donor_list = new Object(); 52629// for(var i = 0; i < donors.length; i++) 52630// donor_list[donors[i][self.donor_key].id] = donors[i][self.donor_key]; 52631// 52632// donors_ready = true; 52633// check_ready(); 52634// } 52635 52636 //rewrite this method to process RMA response 52637 function receive_donor_list(response) { 52638 if((response === null) || !(response.response.success == true)) { 52639 if(self.status_callback) 52640 self.status_callback(DataStatus.STATUS_PROBE_META_FAIL, response ? response.response ? "response code = " + response.response.code : '' : ''); 52641 52642 return; 52643 } 52644 52645 // create the donor_list object from the response just received 52646 var donors = response; 52647 donor_list = new Object(); 52648 for(var i = 0; i < donors.length; i++) { 52649 donor_list[donors[i].id] = { 52650 id:donors[i].id, 52651 age:donors[i].age.name, 52652 handedness:donors[i].handedness, 52653 organism:donors[i].organism.name, 52654 sex:donors[i].sex, 52655 race_or_strain:donors[i].strain, 52656 name:donors[i].name 52657 } 52658 52659 } 52660 52661 donors_ready = true; 52662 check_ready(); 52663 52664 } 52665 52666 52667 function receive_column_list(response) { 52668 column_list = eval("(" + response.responseText + ")"); 52669 columns_ready = true; 52670 check_ready(); 52671 } 52672 52673 function receive_cell_detail(row, response, callback) { 52674 if(!response.success) 52675 if(self.status_callback) { 52676 self.status_callback(DataStatus.STATUS_CELL_DATA_FAIL, response ? response.msg : ''); 52677 return; 52678 } 52679 52680 var column_info = eval("(" + response.payload + ")");
52681 // do some validation here 52682 var info = new Object(); 52683 info.column = column_info; 52684 info.probe = probe_id_list[row]; 52685 52686 raise_busy(false); 52687 if(callback) 52688 callback(new hdResponse(true, info)); 52689 } 52690 52691 function check_ready() { 52692 if(ready) 52693 return; 52694 52695// if(!probes_ready || !columns_ready || !donors_ready) 52696// return; 52697 52698 if(!probes_ready || !columns_ready ) 52699 return; 52700 52701 52702 ready = true; 52703 raise_busy(false); 52704 if(self.status_callback) 52705 self.status_callback(DataStatus.STATUS_DATA_READY, 'DATA READY'); 52706 } 52707 52708 // returns the probe and column ID given the row and column indexes 52709 function ids_at_grid_position(x, y) { 52710 var column = column_list[x]; 52711 var col_id = column.sample_id ? (column.sample_id != 0 ? column.sample_id : column.structure_id) : column.structure_id; 52712 return( {probe_id:probe_id_list[y], column_id:col_id} ); 52713 } 52714 52715 52716 function init() { 52717 if(self.session === null) 52718 return; 52719 52720 self.attach_session(self.session); 52721 } 52722 52723 52724// debuggery 52725 function trace(msg) { 52726 if(self.trace_function) 52727 self.trace_function(msg); 52728 } 52729 52730 52731 52732// initialize this instance 52733 init(); 52734} 52735 52736 52737 52738 52739 52740// The heatmap dao session object may not need to be exposed at all. Depends 52741// mostly on whether or not we implement session initialization in the dao. 52742// Doing this would mean that the heatmap_dao would also need to communicate 52743// with the search service to turn the user's search criteria into a list 52744// of probes to be used with the heatmap data engine. The alternative to 52745// direct dao access to the search service is to have the page controller 52746// do the interaction with the search service, initialize a heatmap data 52747// engine session, and then render a view that contains the heatmap_dao, 52748// along with the already-initialized session object. 52749function hdSession(session_id, status, number_of_probes, number_of_columns) 52750{ 52751 this.session_id = session_id; 52752 this.status = status; 52753 this.number_of_probes = number_of_probes; 52754 this.number_of_columns = number_of_columns; 52755} 52756; 52757 52758var __header_image_count__ = 0; 52759 52760// this is the legend or header image for the main heatmap. The one widget that 52761// rules them all acts as a controller, keeping the heatmap & header 52762// scale & position in sync. 52763function header_image(container, header_url, column_count, initial_scale, initial_column_index) 52764{ 52765 52766// public properties 52767 this.container = $(container); 52768 this.header_url = header_url; 52769 this.column_count = column_count; 52770 this.trace_function = null; 52771 52772 52773// public methods 52774 this.set_position = function(new_column) { 52775 the_image.setStyle({left:(Math.floor(new_column*scale))+'px'}); 52776 } 52777 52778 this.get_position = function() { 52779 return(the_image.getStyle('left')); 52780 } 52781 52782 this.set_scale = function(new_scale) { 52783 scale = new_scale; 52784 the_image.setStyle({width:(this.column_count*scale)+'px'}); 52785 } 52786 52787 this.set_marker_position = function(new_marker_pos) { 52788 var pos = Math.floor(new_marker_pos*scale)-(7.5/scale); 52789 var pos1 = the_image.positionedOffset(); 52790 the_marker.setStyle({left:(pos+pos1.left)+'px'}); 52791 } 52792 52793 this.set_marker_client_x = function(new_x) { 52794 var left = the_image.positionedOffset().left; 52795 var pos = the_image.cumulativeOffset(); 52796 var offset = the_image.cumulativeScrollOffset(); 52797 the_marker.setStyle({left:((new_x - pos.left - 7.5 + left) + offset.left)+'px'}); 52798 } 52799 52800 this.set_click_marker_client_x = function(new_x) { 52801 var left = the_image.positionedOffset().left; 52802 var pos = the_image.cumulativeOffset(); 52803 var offset = the_image.cumulativeScrollOffset(); 52804 the_click_marker.show(); // show this marker after first user click 52805 the_click_marker.setStyle({left:((new_x - pos.left - 7.5 + left) + offset.left)+'px'}); 52806 } 52807 52808 this.refresh = function(new_url) { 52809 this.header_url = new_url; 52810 the_image.src = this.header_url; 52811 } 52812 52813// private properties 52814 var self = this; 52815 var img_width = 1; 52816 var img_column = initial_column_index; // this is the 'left' attribute of the img's position 52817 var the_image = null; 52818 var the_wrapper = null; 52819 var the_marker = null; 52820 var the_click_marker = null; 52821 var scale = 1.0; 52822 52823 52824// private methods 52825 function build_scaffold() { 52826 var wrapper_width = self.container.getWidth(); 52827 the_wrapper = new Element('div', {'class':'header_image_wrapper', id:'hiw'+__header_image_count__, width:wrapper_width+'px', title:'Columns color coded-- top: donor, bottom: structure'}); 52828 the_image = new Element('img', {'class':'header_image', id:'hi'+__header_image_count__, 52829 width:img_width, height:'100%', 52830 top:'0px', left:img_column}); 52831 52832 the_marker = new Element('img', {id:'him'+__header_image_count__,'class':'header_marker'}); 52833 the_click_marker = new Element('img', {id:'hicm'+__header_image_count__,'class':'header_marker'}); 52834 52835 //the_wrapper.clonePosition(self.container); 52836 self.container.appendChild(the_wrapper); 52837 the_wrapper.appendChild(the_image); 52838 the_wrapper.appendChild(the_marker); 52839 the_wrapper.appendChild(the_click_marker); 52840 52841 the_image.src = self.header_url; 52842 the_marker.src = '/assets/heatmap_ui/marker.png'; 52843 the_click_marker.src = '/assets/heatmap_ui/click_marker.png'; 52844 the_click_marker.hide(); 52845 } 52846 52847 function set_initial_width() { 52848 if(initial_scale == 0) // scale to width 52849 scale = self.container.getWidth() / self.column_count; 52850 else 52851 scale = initial_scale; 52852 52853 img_width = self.column_count * scale; 52854 } 52855
52856 function init() { 52857 set_initial_width(); 52858 build_scaffold(); 52859 } 52860 52861 function trace(msg) { 52862 if(self.trace_function) 52863 self.trace_function(msg); 52864 } 52865 52866 init(); 52867} 52868 52869 52870 52871; 52872 52873var HM_CONTROL_ZOOM = 1; 52874var HM_CONTROL_COLOR_MAP = 2; 52875var HM_CONTROL_RESOLUTION = 4; 52876var HM_CONTROL_RESTRICT_DOMAINS = 8; 52877var HM_CONTROL_VIEW_SELECTION_HM = 16; 52878var HM_CONTROL_VIEW_SELECTION_THUMB = 32; 52879var HM_CONTROL_CLEAR_SELECTION = 64; 52880var HM_CONTROL_TOGGLE_BUTTON = 128; 52881var HM_CONTROL_ALL = 255; 52882 52883var __control_bar_count = 0; 52884 52885function heatmap_status_bar(container, options, event_options) { 52886 52887// public properties 52888 this.container = $(container); 52889 this.ramp_url = options ? (options.ramp_url ? options.ramp_url : null) : null; 52890 this.colormap_set = options ? (options.colormap_set ? options.colormap_set : []) : []; 52891 this.control_mask = options ? (options.control_mask ? options.control_mask : []) : []; 52892 this.resolution_set = options ? (options.resolution_set ? options.resolution_set : []) : []; 52893 this.control_defaults = options ? (options.control_defaults ? options.control_defaults : {}) : {}; 52894 this.display_donors = options ? (options.display_donors ? options.display_donors : null) : null; 52895 this.display_structures = options ? (options.display_structures ? options.display_structures : null) : null; 52896 this.show_display_filters = options ? (options.show_display_filters ? options.show_display_filters : false ) : false; 52897 this.display_donor_groups = options ? (options.display_donor_groups ? options.display_donor_groups : null) : null; 52898 this.display_donor_filter_type = options ? (options.display_donor_filter_type ? options.display_donor_filter_type : null) : null; 52899 this.display_structure_groups = options ? (options.display_structure_groups ? options.display_structure_groups : null) : null; 52900 this.display_structure_filter_type = options ? (options.display_structure_filter_type ? options.display_structure_filter_type : null) : null; 52901 this.restriction_on = false; /* the state read from the radio buttons */ 52902 this.ontology_options = options ? (options.ontology_options ? options.ontology_options : {} ) : {}; 52903 52904// event hooks 52905 this.on_zoom = event_options? (event_options.on_zoom ? event_options.on_zoom : null) : null; 52906 this.on_zoom_reset = event_options? (event_options.on_zoom_reset ? event_options.on_zoom_reset : null) : null; 52907 this.on_restriction_change = event_options? (event_options.on_restriction_change ? event_options.on_restriction_change : null) : null; 52908 this.on_display_change = event_options? (event_options.on_display_change ? event_options.on_display_change : null) : null; 52909 this.on_resolution_change = event_options? (event_options.on_resolution_change ? event_options.on_resolution_change : null) : null; 52910 this.on_view_selection_click = event_options? (event_options.on_view_selection_click ? event_options.on_view_selection_click : null) : null; 52911 this.on_clear_click = event_options? (event_options.on_clear_click ? event_options.on_clear_click : null) : null; 52912 this.on_toggle_click = event_options? (event_options.on_toggle_click ? event_options.on_toggle_click : null) : null; 52913 this.on_colormap_change = event_options? (event_options.on_colormap_change ? event_options.on_colormap_change : null) : null; 52914 this.on_level_change = event_options? (event_options.on_level_change ? event_options.on_level_change : null) : null; 52915 52916 52917// public methods 52918 52919 52920 this.set_zoom = function(new_value) { 52921 the_zoom_slider.setValue(new_value); 52922 }; 52923 52924 this.set_range = function(lower, upper) { 52925 the_range_widget.set_range(lower, upper); 52926 }; 52927 52928 this.set_restriction = function(restricted) { 52929 52930 }; 52931 52932 this.set_resolution = function(selected) { 52933 52934 }; 52935 52936 this.set_colormap = function(selected_map) { 52937 the_range_widget.set_colormap(selected_map); 52938 }; 52939 52940 this.set_control_visibility = function(control_mask,visible) { 52941 controls = []; 52942 52943 if (the_zoom_box && (control_mask & HM_CONTROL_ZOOM)) 52944 controls.push(the_zoom_box); 52945 52946 if (the_ramp_box && (control_mask & HM_CONTROL_COLOR_MAP)) 52947 controls.push(the_ramp_box); 52948 52949 if (the_resolution_box && (control_mask & HM_CONTROL_RESOLUTION)) 52950 controls.push(the_resolution_box); 52951 52952 if (the_domain_box && (control_mask & HM_CONTROL_RESTRICT_DOMAINS)) 52953 controls.push(the_domain_box); 52954 52955 if (the_view_hm_button && (control_mask & HM_CONTROL_VIEW_SELECTION_HM)) 52956 controls.push(the_view_hm_button); 52957 52958 if (the_view_thumb_button && (control_mask & HM_CONTROL_VIEW_SELECTION_THUMB)) 52959 controls.push(the_view_thumb_button); 52960 52961 if (the_clear_button && (control_mask & HM_CONTROL_CLEAR_SELECTION)) 52962 controls.push(the_clear_button); 52963 52964 if (the_toggle_button && (control_mask & HM_CONTROL_TOGGLE_BUTTON)) 52965 controls.push(the_toggle_button); 52966 52967 for (var i=0; i<controls.length; i++) { 52968 if (visible) 52969 $j(controls[i]).show(); 52970 else 52971 $j(controls[i]).hide(); 52972 } 52973 }; 52974 52975// private properties 52976 var self = this; 52977 52978 var the_ramp_box = null; 52979 var the_zoom_box = null; 52980 var the_resolution_box = null; 52981 var the_domain_box = null; 52982 52983 var the_ramp = null; 52984 var the_zoom_slider = null; 52985 var the_resolution_select = null; 52986 var the_restriction_control = null; 52987 var the_view_hm_button = null; 52988 var the_view_thumb_button = null; 52989 var the_clear_button = null; 52990 var the_toggle_button = null; 52991 var the_select_box = null; 52992 var the_button_box = null; 52993 var the_range_widget = null; 52994 52995 var the_ontology_model = options.ontology_model; 52996 var the_display_structure_list = options.display_structure_list; 52997 var the_display_ontology_dropdown = null; 52998 52999 var filter_button_on_text = "Filter Heatmap: On"; 53000 var filter_button_off_text = "Filter Heatmap: Off"; 53001 var filter_toggle_button; 53002 53003 var the_display_dialog_box = null; 53004 53005 var random_id_for_display_filtering; 53006 var display_structures_with_id = "display_structures"; 53007 var display_donors_with_id = "display_donors_popup"; 53008 53009// private methods 53010 function build_scaffold() { 53011 53012 // slide for zoom control 53013 if(self.control_mask & HM_CONTROL_ZOOM) { 53014 the_zoom_box = new Element('div', {'class':'status_zoom_box', title:'Adjust heat map zoom level'}); 53015 var zoom_label_box = new Element('div', {'class':'zoom_label_box'}); 53016 zoom_label_box.update('Zoom'); 53017 the_zoom_box.appendChild(zoom_label_box); 53018 self.container.appendChild(the_zoom_box); 53019 53020 var zoom_track = new Element('div', {'class':'status_zoom_track'}); 53021 var minus_button = new Element('img',{src:'/assets/heatmap_ui/minus_small.png', 'class':'zoom_button left_button'}); 53022 the_zoom_box.appendChild(minus_button); 53023 var zoom_handle = new Element('div', {'class':'status_zoom_handle'}); 53024 zoom_track.appendChild(zoom_handle); 53025 the_zoom_box.appendChild(zoom_track); 53026 the_zoom_slider = new Control.Slider(zoom_handle, zoom_track,
53027 {onChange:do_zoom_change, 53028 range:$R(-20,20), 53029 sliderValue:0.00001}); 53030 var plus_button = new Element('img',{src:'/assets/heatmap_ui/plus_small.png', 'class':'zoom_button right_button'}); 53031 the_zoom_box.appendChild(plus_button); 53032 plus_button.observe('click', zoom_in); 53033 minus_button.observe('click', zoom_out); 53034 } 53035 53036 // the color map ramp display 53037 if(self.control_mask & HM_CONTROL_COLOR_MAP) { 53038 the_ramp_box = new Element('div', {'class':'windowLevelContainer'}); 53039 self.container.appendChild(the_ramp_box); 53040 var ramp_label_box = new Element('div', {'class':'ramp_label_box'}); 53041 ramp_label_box.update('Color Map'); 53042 the_ramp_box.appendChild(ramp_label_box); 53043 53044 var selected_map = get_selected_map(self.colormap_set); 53045 var colormap_options = { 53046 colormap_set:self.colormap_set, 53047 normalized:selected_map.normal, 53048 zscore_limits:selected_map.limits, 53049 raw_limits:selected_map.limits, 53050 raw_expression_range:selected_map.range, 53051 norm_expression_range:selected_map.range 53052 }; 53053 var colormap_events = { 53054 on_level_change:self.on_level_change, 53055 on_colormap_change:self.on_colormap_change 53056 } 53057 the_range_widget = new colormap_widget(the_ramp_box, colormap_options, colormap_events); 53058 } 53059 53060 // the resolution drop down 53061 if(self.control_mask & HM_CONTROL_RESOLUTION) { 53062 the_resolution_box = new Element('div', {id:'resolution_box_'+__control_bar_count, 'class':'resolution_box', title:'Change degree of expression rollup'}); 53063 var the_resolution_label_box = new Element('div', {id:'resolution_label_box_'+__control_bar_count, 'class':'resolution_label_box'}); 53064 the_resolution_select = new Element('select', {id:'resolution_select_'+__control_bar_count, 'class':'resolution_select'}); 53065 53066 var i = 0; 53067 for(var key in self.resolution_set) { 53068 var selected = self.control_defaults.HM_CONTROL_RESOLUTION 53069 ? self.control_defaults.HM_CONTROL_RESOLUTION == key.toString() 53070 : false; 53071 var option = new Element('option', {id:'opt_' + i++, value:key}); 53072 option.text = self.resolution_set[key]; 53073 the_resolution_select.options.add(option); 53074 if(selected) 53075 option.setAttribute('selected', true); 53076 } 53077 53078 the_resolution_label_box.update('Resolution '); 53079 the_resolution_box.appendChild(the_resolution_label_box); 53080 the_resolution_box.appendChild(the_resolution_select); 53081 self.container.appendChild(the_resolution_box); 53082 the_resolution_select.observe('change',do_resolution_changed); 53083 } 53084 53085 // the display filtering buttons 53086 if(self.show_display_filters) { 53087 53088 //var filter_toggle_button_html = '<button id="filter_button" class="display_filtering_buttons" type="button">Filter Heatmap</button>'; 53089 var filter_toggle_button_html = '<input type="checkbox" id="filter_button" name="filter_button" class="display_filtering_buttons_checkbox"/>' + 53090 '<label for="filter_button" class="display_filtering_buttons_checkbox" title="Toggle heatmap display filtering on/off">Filter Heatmap</label>'; 53091 var edit_settings_button_html = '<button id="filter_settings_button" class="display_filtering_buttons" type="button" title="Change the settings on heatmap display filtering">...</button>'; 53092 53093 // set button state 53094 var on = self.control_defaults.HM_CONTROL_RESTRICT_DOMAINS; 53095 self.restriction_on = on; // save the initial value 53096 53097 var filter_button_text = self.restriction_on ? filter_button_on_text : filter_button_off_text; 53098 53099 53100 var the_domain_box_html = "<span id='restriction_container'>" + 53101 filter_toggle_button_html + 53102 edit_settings_button_html + 53103 "</span>"; 53104 53105 the_domain_box = $j(the_domain_box_html); 53106 53107 the_domain_box.appendTo($j(self.container)); // self.container is actually an element in Prototype framework 53108 53109 53110 53111 filter_toggle_button = $j('#filter_button').button({label: filter_button_text}) 53112 .click(function(event) { 53113 toggle_domain_restriction(); 53114 }); 53115 53116 filter_toggle_button.attr("checked",self.restriction_on ? "checked" : undefined); 53117 filter_toggle_button.button("option",{label: self.restriction_on ? filter_button_on_text : filter_button_off_text }).button("refresh"); 53118 53119 var edit_settings_button = $j('#filter_settings_button').button() 53120 .click(function(event){ 53121 show_display_filtering_dialog(); 53122 }); 53123 53124 53125 // prepare the content to appear in the display filter popup dialog box 53126 // But do not create the dialog, dialog will be created right before open(), and destroyed right after close() 53127 the_display_dialog_box = create_display_filtering_dialog(); 53128 53129 } 53130 53131 the_button_box = new Element('div', {id:'the_button_box_'+__control_bar_count, 'class':'button_box'}); 53132 53133 // the view heatmap button 53134 if (self.control_mask & HM_CONTROL_VIEW_SELECTION_HM) { 53135 the_view_hm_button = new Element('button', {'class':'hm_control_button'}); 53136 the_view_hm_button.update("View Selection Heatmap"); 53137 the_button_box.appendChild(the_view_hm_button); 53138 } 53139 53140 // the view thumbnails button 53141 if (self.control_mask & HM_CONTROL_VIEW_SELECTION_THUMB) { 53142 the_view_thumb_button = new Element('button', {'class':'hm_control_button'}); 53143 the_view_thumb_button.update("View Selection Thumbnails"); 53144 the_button_box.appendChild(the_view_thumb_button); 53145 } 53146 53147 // the toggle button 53148 if (self.control_mask & HM_CONTROL_TOGGLE_BUTTON) { 53149 the_toggle_button = new Element('button', {'class':'hm_control_button'}); 53150 the_toggle_button.update("Toggle Heatmap/Thumbnails"); 53151 the_button_box.appendChild(the_toggle_button); 53152 } 53153 53154 // the clear button 53155 if (self.control_mask & HM_CONTROL_CLEAR_SELECTION) { 53156 the_clear_button = new Element('button', {'class':'hm_control_button'}); 53157 the_clear_button.update("Clear Selections"); 53158 the_button_box.appendChild(the_clear_button); 53159 } 53160 53161 self.container.appendChild(the_button_box); 53162 53163 if (the_view_hm_button) { 53164 the_view_hm_button.observe('click',function() { do_view_selection_click(HM_VIEW_SELECTION_HM); }); 53165 $j(the_view_hm_button).button(); 53166 } 53167 53168 if (the_view_thumb_button) { 53169 the_view_thumb_button.observe('click', function() { do_view_selection_click(HM_VIEW_SELECTION_THUMB); }); 53170 $j(the_view_thumb_button).button(); 53171 } 53172 53173 if (the_clear_button) { 53174 the_clear_button.observe('click',do_clear_click); 53175 $j(the_clear_button).button(); 53176 } 53177 53178 if (the_toggle_button) { 53179 the_toggle_button.observe('click',do_toggle_click); 53180 $j(the_toggle_button).button(); 53181 } 53182 53183 } 53184 53185 function create_display_filtering_dialog() { 53186 53187 // jquery ui dialog may have some remnant code under <body> even after dialog.destroy() is called 53188 $j("div#display_filtering_dialog").remove(); // remove the existing dialog's remaining component 53189 53190 random_id_for_display_filtering = Math.floor(Math.random() * 100000) + 1; 53191 display_donors_with_id = "display_donors_" + random_id_for_display_filtering; 53192 display_structures_with_id = "display_structures_" + random_id_for_display_filtering; 53193 53194 53195 var popup_title = "Filter Heatmap Settings"; 53196 var display_filtering_desc = "Configure the heatmap view by restricting columns to donors and structures of interest."; 53197 var display_donors_desc = "Select donors:"; 53198 var display_structures_desc = "Select structures:"; 53199 53200 var donor_selection_html = "<span id='display_donor_selection_popup'><select id='" + display_donors_with_id + "' multiple='multiple'>"; 53201 donor_selection_html += "<option value=0>[All Donors]</option>"; 53202 for(var i=0; i<self.display_donor_groups.length; i++) { 53203 53204 if(self.display_donor_filter_type == 'donors') { 53205 donor_selection_html += "<option value=" + self.dis
53205play_donor_groups[i].donor.id + ">" + 53206 self.display_donor_groups[i].donor.name + 53207 "(" + self.display_donor_groups[i].donor.sex + "," + self.display_donor_groups[i].donor.age + ")" + 53208 "</option>"; 53209 } 53210 else if(self.display_donor_filter_type == 'stages') { 53211 // in this case, self.display_donor_groups is actually the _stage_list passed in 53212 if(self.display_donor_groups[i].stage_name) { // used in devhuman 53213 donor_selection_html += "<option value='" + self.display_donor_groups[i].stage_name + "'>" + self.display_donor_groups[i].stage_start + 53214 '-' + self.display_donor_groups[i].stage_end + "</option>"; 53215 } else if(self.display_donor_groups[i].name) { // nhp microdissection 53216 donor_selection_html += "<option value='" + self.display_donor_groups[i].name + "'>" + self.display_donor_groups[i].name + "</option>"; 53217 } 53218 53219 53220 } 53221 } 53222 donor_selection_html += "</select></span>"; 53223 53224 var structure_input_html = "<span id='display_structure_restriction_popup'><input type='text' id='display_domain_popup' class='_sw_search_text'/></span>"; 53225 if(self.display_structure_filter_type == 'structure_selection') { 53226 // in this case the structure selection is a dropdownchecklist, which is converted from a multiple select 53227 structure_input_html = "<span id='display_structure_selection_popup'><select id='" + display_structures_with_id + "' multiple='multiple'>"; 53228 structure_input_html += "<option value=0>[All Structures]</option>"; 53229 for(var i=0; i<self.display_structure_groups.length; i++) { 53230 structure_input_html += "<option value=" + self.display_structure_groups[i].id + ">" + self.display_structure_groups[i].acronym + " - " + 53231 self.display_structure_groups[i].name + "</option>"; 53232 } 53233 structure_input_html += "</select></span>"; 53234 } 53235 var dialog_html = '<div id="display_filtering_dialog" title="' + popup_title + '">' + 53236 '<div class="display_filtering_desc">' + display_filtering_desc + '</div>' + 53237 '<div class="display_filter_ui">' + '<div class="display_filter_description">' + display_donors_desc + '</div><div>' + donor_selection_html + '</div></div>' + 53238 '<div class="display_filter_ui">' + '<div class="display_filter_description">' + display_structures_desc + '</div><div>' + structure_input_html + '</div></div>' + 53239 '</div>'; 53240 var dialog_box = $j(dialog_html); 53241 53242 dialog_box.appendTo($j(self.container)); 53243 53244 // donor selection UI 53245 $j('#' + display_donors_with_id).dropdownchecklist({icon:{placement:'right',toOpen:'ui-icon-triangle-1-s'}, 53246 forceMultiple:true, 53247 firstItemChecksAll: true, 53248 maxDropHeight:150, 53249 width:490, 53250 zIndex:9999 53251 }); 53252 53253 53254 53255 // build the display structure selection UI 53256 the_display_ontology_dropdown = $j('#display_domain_popup'); 53257 53258 if(self.display_structure_filter_type == 'ontology'){ 53259 if(!the_ontology_model) 53260 the_ontology_model = new ontology_model(null, 9, {on_ready:do_ontology_load, on_error:on_ontology_error}); 53261 else 53262 do_ontology_load(the_ontology_model); 53263 } else { 53264 // then the type is "structure_selection", we need to build a dropdownchecklist 53265 $j('#' + display_structures_with_id ).dropdownchecklist({icon:{placement:'right',toOpen:'ui-icon-triangle-1-s'}, 53266 forceMultiple:true, 53267 firstItemChecksAll: true, 53268 maxDropHeight:100, 53269 width:490, 53270 zIndex:9999 53271 }); 53272 53273 load_saved_display_domain(); // we can call this loading here, because we don't need to wait for the ontology widget in this case 53274 53275 } 53276 53277 53278 dialog_box.hide(); 53279 53280/* // as noted before, now this will be called every time before dialog.open is called 53281 dialog_box.dialog({autoOpen: false, 53282 modal: true, 53283 width: 560, 53284 height: 390, 53285 closeOnEscape:false, 53286 buttons:[ 53287 { text: "Save", click: function() { update_display_filter_settings(); dialog_box.dialog( "close" ); } }, 53288 { text: "Cancel", click: function() { dialog_box.dialog( "close" ); } } 53289 ]}); 53290*/ 53291 53292 return dialog_box; 53293 53294 } 53295 53296 function show_display_filtering_dialog() { 53297 53298 // do another load from display_donors and display_structures, in case user canceled the previous selection 53299 load_saved_display_domain(); 53300 53301 // first create the dialog box 53302 // I moved the actual dialog creation here because I have not found a reliable way to fully remove any existing 53303 // display filtering dialog before building a new one. When user repeatedly launch search from the UI, the page would not reload, and 53304 // any existing display filter dialog will stay besides a new dialog will be created. 53305 // I now have the convention that a dialog box is created and opened when user launches it, and it is closed and 53306 // destroyed when user dismisses it. 53307 the_display_dialog_box.dialog({autoOpen: false, 53308 modal: true, 53309 width: 560, 53310 height: 390, 53311 closeOnEscape:false, 53312 buttons:[ 53313 { text: "Save", click: function() { update_display_filter_settings(); the_display_dialog_box.dialog( "close" ); the_display_dialog_box.dialog("destroy"); } }, 53314 { text: "Cancel", click: function() { the_display_dialog_box.dialog( "close" ); the_display_dialog_box.dialog("destroy"); } } 53315 ]}); 53316 53317 the_display_dialog_box.dialog("open"); 53318 53319 53320 } 53321 53322 function do_ontology_load(model) { 53323 53324// // also hook up the event callbacks for closing the widget or deleting some structures entered 53325// the_display_ontology_dropdown.ontology_autocomplete(model, {on_close:do_ontology_widget_state_updated, 53326// on_remove_structure:do_ontology_widget_state_updated}); 53327 53328 the_display_ontology_dropdown.ontology_autocomplete(model, self.ontology_options); 53329 53330 load_saved_display_domain(); // we can load the ontology selections now 53331 } 53332 53333 /**
53334 * set the display filtering UI widgets based on self.display_structures and self.display_donors 53335 **/ 53336 function load_saved_display_domain() { 53337 53338 //var display_structures_array = 53339 if(self.display_structure_filter_type == 'ontology') { 53340 if(self.display_structures && self.display_structures.length > 0) { 53341 the_display_ontology_dropdown.ontology_autocomplete('set_structure_ids', self.display_structures.split(',')); 53342 } 53343 } 53344 else { 53345 // refresh the structure dropdownchecklist to reflect the passed in display_structures 53346 if(self.display_structures && self.display_structures.length > 0) { 53347 var selected_structures_array = self.display_structures.split(','); 53348 //var structure_select = $j('#display_structures option'); 53349 var structure_select = $j('#' + display_structures_with_id + ' option'); 53350 structure_select.removeAttr('selected'); // remove all the selected attr 53351 for( var i = 0; i < selected_structures_array.length; i ++ ){ 53352 //structure_select = $j('#display_structures option[value=' + selected_structures_array[i] +']'); 53353 structure_select = $j('#' + display_structures_with_id + ' option[value="' + selected_structures_array[i] +'"]'); 53354 structure_select.attr('selected', 'selected'); 53355 } 53356 } 53357 else { 53358 //var structure_select = $j('#display_structures option[value=0]'); 53359 var structure_select = $j('#' + display_structures_with_id + ' option[value="0"]'); 53360 structure_select.attr('selected', 'selected'); 53361 } 53362 $j('#' + display_structures_with_id ).dropdownchecklist("refresh"); 53363 } 53364 53365 if(self.display_donors && self.display_donors.length > 0) { 53366 var selected_donors_array = self.display_donors.split(','); 53367 //var donor_select = $j('#display_donors_popup option'); 53368 var donor_select = $j('#' + display_donors_with_id + ' option'); 53369 donor_select.removeAttr('selected'); // remove all the selected attr 53370 for( var i = 0; i < selected_donors_array.length; i ++ ){ 53371 //donor_select = $j('#display_donors_popup option[value=' + selected_donors_array[i] +']'); 53372 donor_select = $j('#' + display_donors_with_id + ' option[value="' + selected_donors_array[i] +'"]'); 53373 donor_select.attr('selected', 'selected'); 53374 } 53375 } 53376 else { 53377 //var donor_select = $j('#display_donors_popup option[value=0]'); 53378 var donor_select = $j('#' + display_donors_with_id + ' option[value="0"]'); 53379 donor_select.attr('selected', 'selected'); 53380 } 53381 //$j('#display_donors_popup').dropdownchecklist("refresh"); 53382 $j('#' + display_donors_with_id).dropdownchecklist("refresh"); 53383 } 53384 53385 function on_ontology_error() { 53386 // do nothing now 53387 } 53388 53389 /** 53390 * update self.display_donors and self.display_structures based on the state of the UI widgets, and 53391 * call self.on_display_change callback if there are changes to the display filters 53392 **/ 53393 function update_display_filter_settings() { 53394 var saved_display_donors = self.display_donors; 53395 var saved_display_structures = self.display_structures; 53396 53397 53398 // update display_structures 53399 if(self.display_structure_filter_type == 'ontology') { 53400 var structure_id_values = the_display_ontology_dropdown.ontology_autocomplete('get_tags'); // we need to process this result 53401 var structure_array = []; 53402 for(var i = 0; i < structure_id_values.length; i++) { 53403 structure_array.push(structure_id_values[i].value ); 53404 } 53405 self.display_structures = structure_array.join(','); 53406 } else { 53407// $j('#display_structures').dropdownchecklist('close'); 53408// $j('#display_structures').dropdownchecklist('refresh'); 53409// var selected_display_structures_temp = $j('#display_structures option:selected'); 53410 $j('#' + display_structures_with_id).dropdownchecklist('close'); 53411 $j('#' + display_structures_with_id).dropdownchecklist('refresh'); 53412 var selected_display_structures_temp = $j('#' + display_structures_with_id + ' option:selected'); 53413 var selected_display_structures = $j.map(selected_display_structures_temp, function(op){ if(op.value != '0') return op.value;});
53413 // get an array of strings 53414 self.display_structures = selected_display_structures.join(','); 53415 } 53416 53417 // now update display_donors 53418 // note we don't check the display_donor_filter_type here, the self.display_donors may need to be mapped when 53419 // the display_donor_filter_type is "stages" 53420// $j('#display_donors_popup').dropdownchecklist('close'); 53421// $j('#display_donors_popup').dropdownchecklist('refresh'); 53422// var selected_display_donors_temp = $j('#display_donors_popup option:selected'); 53423 $j('#' + display_donors_with_id).dropdownchecklist('close'); 53424 $j('#' + display_donors_with_id).dropdownchecklist('refresh'); 53425 var selected_display_donors_temp = $j('#' + display_donors_with_id + ' option:selected'); 53426 var selected_display_donors = $j.map(selected_display_donors_temp, function(op){ if(op.value != '0') return op.value;}); // get an array of strings 53427 self.display_donors = selected_display_donors.join(','); 53428 53429 // only need to invoke the on_display_change callback when there are changes to self.display_donors or self.display_structures 53430 if(self.on_display_change && (saved_display_donors != self.dis
53430play_donors || saved_display_structures != self.display_structures)) { 53431 self.on_display_change(self, self.display_donors, self.display_structures, self.restriction_on); 53432 } 53433 53434 } 53435 53436 53437 53438 function get_selected_map(map_set) { 53439 for(var i = 0; i < map_set.length; i++) 53440 if(map_set[i].selected) 53441 return(map_set[i]); 53442 53443 return(null); 53444 } 53445 53446 53447 function toggle_domain_restriction(){ 53448 //self.restriction_on = !self.restriction_on; 53449 self.restriction_on = filter_toggle_button.is(':checked'); 53450 53451 filter_toggle_button.button("option",{label: self.restriction_on ? filter_button_on_text : filter_button_off_text }).button("refresh"); 53452 53453 if(self.on_display_change) { 53454 self.on_display_change(self, self.display_donors, self.display_structures, self.restriction_on); 53455 } 53456 } 53457 53458 function do_resolution_changed() { 53459 if(self.on_resolution_change) { 53460 // IE8 sauce 53461 setTimeout(function() { 53462 if (the_view_hm_button) 53463 the_view_hm_button.focus(); 53464 self.on_resolution_change(self, the_resolution_select.options[the_resolution_select.selectedIndex].value); 53465 the_resolution_select.focus(); 53466 }, 100); 53467 } 53468 53469// if(self.on_resolution_change) { 53470// self.on_resolution_change( 53471// self, 53472// the_resolution_select.options[the_resolution_select.selectedIndex].value 53473// ); 53474// } 53475 } 53476 53477 function do_view_selection_click(mode) { 53478 if(self.on_view_selection_click) 53479 self.on_view_selection_click(self,mode); 53480 } 53481 53482 function do_clear_click() { 53483 if(self.on_clear_click) 53484 self.on_clear_click(self); 53485 } 53486 53487 function do_toggle_click() { 53488 if(self.on_toggle_click) 53489 self.on_toggle_click(self); 53490 } 53491 53492 function do_zoom_change(value) { 53493 if(self.on_zoom) 53494 self.on_zoom(self, value); 53495 } 53496 53497 function zoom_in() { 53498 the_zoom_slider.setValue(the_zoom_slider.value + 5); 53499 } 53500 53501 function zoom_out() { 53502 the_zoom_slider.setValue(the_zoom_slider.value - 5); 53503 } 53504 53505 function init() { 53506 build_scaffold(); 53507 __control_bar_count++; 53508 } 53509 53510 init(); 53511} 53512; 53513 53514// this oddball is necessary to provide a globally-scoped target for a 53515// timed execution. See thumbnail_navigator.init() where a local function 53516// is assigned to this place holder. 53517var global_up_scroller = function() {}; 53518var global_down_scroller = function() {}; 53519 53520// Thumbnail navigation. see the picture. 53521THUMBNAIL_NAV_COUNT = 0; 53522 53523// roi_options include: 53524// width, height, color, top, left. 53525// postion & dimension are in px, relative to the top left 53526// corner of the thumbnail image. Numeric values should be given 53527// just as numbers without any units. That is, {width:50} not {width:'50px'} 53528// 53529// NOTE that the roi_options apply to the ROI, 53530// not the widget itself. They refer to the ROI box used to navigate 53531// within the widget. The widget itself will fill its container 53532// with width & height = 100%. 53533// 53534// All are optional, defaults are: 53535// width: the same width as the image 53536// height:50px; 53537// color:#ffffff; 53538// top:0; 53539// left:0; 53540// 53541// event_options include on_load, on_move_start, on_move, on_move_end. 53542// the on_load event is raised after the thumbnail image has completely loaded 53543// and all of the internal widget layout has completed. 53544// 53545// TODO: make the container scroll as the ROI approaches the edge 53546// TODO: compensate for border width when reporting ROI dimensions 53547// TODO: recast in object notation? 53548// TODO: Add scroll_to(row, column) 53549function thumbnail_navigator(container, src, roi_initial_options, event_options) 53550{ 53551// public properties 53552 this.container = container; 53553 this.image_src = src; 53554 this.scrollbar_width = roi_initial_options ? (roi_initial_options.scrollbar_width ? roi_initial_options.scrollbar_width : 10) : 10; 53555 53556// public events 53557 this.on_load = event_options ? (event_options.on_load ? event_options.on_load : null) : null; 53558 this.on_move_start = event_options ? (event_options.on_move_start ? event_options.on_move_start : null) : null; 53559 this.on_move = event_options ? (event_options.on_move ? event_options.on_move : null) : null; 53560 this.on_move_end = event_options ? (event_options.on_move_end ? event_options.on_move_end : null) : null; 53561 53562 53563// public methods 53564 53565 // use valid css to set the color, use set_roi_color('#ff00ff'); 53566 this.set_roi_color = function(new_color) { 53567 the_roi.setStyle({color:new_color}); 53568 } 53569 53570 this.get_roi_color = function() {
53571 return(the_roi.getStyle('borderColor')); 53572 } 53573 53574 this.set_scroll_position = function(row, column) { 53575 if(scrolling || dragging) 53576 return; 53577trace("set_scroll_position " + row + "," + column); 53578 53579 if(the_vertical_scroller) { 53580 var img_height = the_image.getHeight(); 53581 var scroll_val = row/img_height; 53582 the_vertical_scroller.set_position(scroll_val); 53583 } 53584 53585 var roi_off = get_roi_offsets(); 53586trace("do scroll to " + (roi_off.y-row) + "," + column); 53587 do_scroll_to((roi_off.y-row), column); 53588 53589 } 53590 53591 53592 this.set_roi_position = function(new_top, new_left) { 53593 if(scrolling || dragging) 53594 return; 53595trace("setting position: " + new_left + ", " + new_top); 53596 roi_options.top = new_top; 53597 roi_options.left = new_left; 53598 set_roi_attributes(); 53599 recalculateLayout(); 53600 do_scroll(); 53601 } 53602 53603 // the 'navigate' method differs from the 'set_roi_position' 53604 // method only in that 'navigate' will raise the same 'on_move_start' 53605 // and 'on_move_end' events as manual user interaction, while 53606 // 'set_roi_position' will not. 53607 this.navigate_to = function(new_top, new_left) { 53608 if(scrolling || dragging) 53609 return; 53610trace("navigate_to"); 53611 53612 do_drag_start(); 53613 this.set_roi_position(new_top, new_left); 53614 do_roi_drag(); 53615 do_drag_end(); 53616 } 53617 53618 this.set_roi = function(new_top, new_left, new_width, new_height) { 53619 if(scrolling || dragging) 53620 return; 53621trace("set_roi"); 53622 roi_options.width = new_width; 53623 roi_options.top = new_top; 53624 roi_options.left = new_left; 53625 roi_options.height = new_height; 53626 set_roi_attributes(); 53627 recalculateLayout(); 53628 do_scroll(); 53629 } 53630 53631 this.get_roi = function() { 53632 return(get_roi_attributes()); 53633 } 53634 53635 this.get_image_dimensions = function() { 53636 return(the_image.getDimensions()); 53637 } 53638 53639 this.resize = function() { 53640 var roi = this.get_roi(); 53641 this.set_roi_position(roi.left, roi.top); 53642 } 53643 53644 53645// private properties 53646 var self = this; 53647 var the_roi = null; 53648 var the_image = null; 53649 var the_wrapper = null; 53650 var the_image_wrapper = null; 53651 var the_vert_scroll_wrap = null; 53652 var the_vertical_scroller = null; 53653 53654 var the_roi_dims = null; 53655 var the_image_dims = null; 53656 var the_wrapper_dims = null; 53657 53658 var roi_options = null; 53659 var last_pos = {x:0, y:0}; 53660 var dragging = false; 53661 var scrolling = false; 53662 var prev_column = 0; 53663 53664 53665// private methods 53666 53667 function get_nav_position() { 53668 var image_pos = the_image.positionedOffset(); 53669 var roi_left = new Number(the_roi.getStyle('left').replace('px','')); 53670 var nav_left = roi_left - image_pos.left; 53671 var roi_top = new Number(the_roi.getStyle('top').replace('px','')) 53672 var nav_top = roi_top - image_pos.top; 53673 53674 return({left:nav_left, top:nav_top}); 53675 } 53676 53677 function get_roi_attributes() { 53678 var pos = get_nav_position(); 53679 return({top:pos.top, left:pos.left, width:the_roi_dims.width, height:the_roi_dims.height}); 53680 } 53681 53682 53683 function get_image_relative_position(x, y) { 53684 var scroll_pos = the_image.cumulativeScrollOffset(); 53685 var image_pos = the_image.cumulativeOffset(); 53686 var thumb_relative_mouse_pos = {left:x - (image_pos.left - scroll_pos.left), 53687 top:y - (image_pos.top - scroll_pos.top)}; 53688 return(thumb_relative_mouse_pos); 53689 } 53690 53691 53692 function in_roi(x, y) { 53693 var nav_pos = get_nav_position(); 53694 var dims = the_roi.getDimensions(); 53695 53696 return( 53697 (x >= nav_pos.left) 53698 && 53699 (x <= (dims.width+nav_pos.left)) 53700 && 53701 (y >= nav_pos.top) 53702 && 53703 (y <= (nav_pos.top + dims.height)) 53704 ); 53705 } 53706 53707 // returns an anon object describing which side and by how much 53708 // an object extends outside the_wrapper. 53709 function out_of_bounds(obj) { 53710 var wrapper_pos = the_image.cumulativeOffset(); 53711 var wrapper_dims = the_image.getDimensions(); 53712 var obj_pos = obj.cumulativeOffset(); 53713 var obj_dims = obj.getDimensions(); 53714 53715 // -1, +2 to compensate for boundary widths 53716 var top = Math.abs( Math.min(0, (obj_pos.top - wrapper_pos.top) ) ); 53717 var left = Math.abs( Math.min(0, (obj_pos.left - wrapper_pos.left) ) ); 53718 var right = Math.abs(Math.min(0, (( (wrapper_pos.left + (wrapper_dims.width)) - (obj_pos.left + obj_dims.width) )))); 53719 var bottom = Math.abs(Math.min(0, (( (wrapper_pos.top + (wrapper_dims.height)) - (obj_pos.top + obj_dims.height) )))); 53720 53721 return({top:top, left:left, right:right, bottom:bottom}); 53722 } 53723 53724 function do_drag_start(evt) { 53725 evt.stop(); 53726 53727 var mouse_pos = get_image_relative_position(evt.clientX, evt.clientY);
53728 if(!in_roi(mouse_pos.left, mouse_pos.top)) { 53729 dragging = false; 53730 return; 53731 } 53732 53733 dragging = true; 53734 last_pos.x = mouse_pos.left; 53735 last_pos.y = mouse_pos.top; 53736 53737 if(self.on_move_start) 53738 self.on_move_start(this, get_nav_position()); 53739 } 53740 53741 function do_roi_drag(evt) { 53742 evt.stop(); 53743 53744 if(!dragging) 53745 return; 53746 53747 var mouse_pos = get_image_relative_position(evt.clientX, evt.clientY); 53748 53749 var delta = {left:mouse_pos.left - last_pos.x, top:mouse_pos.top - last_pos.y}; 53750 var roi_left = new Number(the_roi.getStyle('left').replace('px','')); 53751 var roi_top = new Number(the_roi.getStyle('top').replace('px','')); 53752 53753 last_pos.x = mouse_pos.left; 53754 last_pos.y = mouse_pos.top; 53755 53756 the_roi.setStyle({top:(roi_top + delta.top)+'px', left:(roi_left + delta.left)+'px'}); 53757 53758 do_scroll(); 53759 setTimeout(send_move, 10); 53760 } 53761 53762 function send_move() { 53763 if(!Prototype.Browser.IE) 53764 if(self.on_move) 53765 self.on_move(this, get_nav_position()); 53766 } 53767 53768 // This makes sure the ROI stays on the image 53769 function do_scroll() { 53770 var roi_bounds = out_of_bounds(the_roi); 53771 if(roi_bounds.bottom || roi_bounds.top || roi_bounds.left || roi_bounds.right) { 53772 var roi_top_style = the_roi.getStyle('top') ? the_roi.getStyle('top') : '0'; 53773 var roi_left_style = the_roi.getStyle('left') ? the_roi.getStyle('left') : '0'; 53774 53775 var roi_top = new Number(roi_top_style.replace('px','')); 53776 var roi_left = new Number(roi_left_style.replace('px','')); 53777 53778 if(roi_bounds.bottom) 53779 the_roi.setStyle({top:(roi_top-(roi_bounds.bottom-1))+'px'}); 53780 53781 if(roi_bounds.top) 53782 the_roi.setStyle({top:(roi_top+(roi_bounds.top-1))+'px'}); 53783 53784 if(roi_bounds.left)
53785 the_roi.setStyle({left:(roi_left+(roi_bounds.left+1))+'px'}); 53786 53787 if(roi_bounds.right) 53788 the_roi.setStyle({left:(roi_left-(roi_bounds.right-1))+'px'}); 53789 } 53790 } 53791 53792 function do_drag_end(evt) { 53793 evt.stop(); 53794 53795 if(Prototype.Browser.IE) 53796 if(self.on_move) 53797 self.on_move(this, get_nav_position()); 53798 53799 if(self.on_move_end) 53800 self.on_move_end(this, get_nav_position()); 53801 dragging = false; 53802 } 53803 53804 // calculates the sizes of the components (after the 53805 // thumbnail image has loaded) 53806 function recalculateLayout() { 53807 the_roi_dims = the_roi.getDimensions(); 53808 the_image_dims = the_image.getDimensions(); 53809 the_image_wrapper.setStyle({width:the_image_dims.width+'px'}); 53810 the_wrapper_dims = the_image_wrapper.getDimensions(); 53811 create_scrollbar(); 53812 53813 } 53814 53815 // sets the position & dimensions of the ROI box 53816 // based on the roi_initial_options given in the constructor. 53817 function set_roi_attributes() { 53818//TODO: validate these options are viable given the size of the thumbnail. 53819 var pos = the_image.positionedOffset(); 53820 var dims = the_image.getDimensions(); 53821 var new_width = roi_options.width == '100%' ? dims.width : roi_options.width; 53822 53823 the_roi.setStyle({width:new_width+'px', height:roi_options.height+'px', 53824 top:roi_options.top+pos.top+'px', left:roi_options.left+pos.left+'px', 53825 borderColor:roi_options.color}); 53826 } 53827 53828 function setup_roi_listeners() { 53829 // set up the listeners for ROI dragging 53830 the_roi.observe('mousedown',do_drag_start); 53831 the_roi.observe('mousemove',do_roi_drag); 53832 the_roi.observe('mouseup',do_drag_end); 53833 // it's easy for the mouse to get ahead of the roi, 53834 // so we need to listen for these events on the thumbnail image as well 53835 the_image.observe('mousedown',do_drag_start); 53836 the_image.observe('mousemove',do_roi_drag); 53837 the_image.observe('mouseup',do_drag_end); 53838 // If the user leaves the thumbnail area with the mouse down 53839 // we will still be in a dragging state. Listen for a mouse-up 53840 // anywhere on the page to exit dragging mode. 53841 Event.observe(window, 'mouseup', do_drag_end); 53842 53843 // this is for the benefit of a particular browser that causes a 53844 // lot of headaches and about which I will say nothing 53845 // unprofessional in the comments of my code. 53846 var filler = $('div_filler'); 53847 filler.observe('mousedown',do_drag_start); 53848 filler.observe('mousemove',do_roi_drag); 53849 filler.observe('mouseup',do_drag_end); 53850 53851 } 53852 53853 // called by the image onload event, calculates dimensions 53854 // of components after the thumbnail has fully loaded. 53855 function do_image_load(evt) { 53856 set_roi_attributes() 53857 recalculateLayout(); 53858 setup_roi_listeners(); 53859 53860 if(self.on_load) 53861 self.on_load(this); 53862 } 53863 53864 // provide default values for roi attributes not provided in the 53865 // constructor argument "roi_initial_options." 53866 function setup_roi_options() { 53867 roi_options = new Object(); 53868 roi_options.color = roi_initial_options ? (roi_initial_options.color ? roi_initial_options.color : '#f00') : '#f00'; 53869 roi_options.width = roi_initial_options ? (roi_initial_options.width ? roi_initial_options.width : '100%') : '100%'; 53870 roi_options.height = roi_initial_options ? (roi_initial_options.height ? roi_initial_options.height : '50') : '50'; 53871 roi_options.top = roi_initial_options ? (roi_initial_options.top ? roi_initial_options.top : '0') : '0'; 53872 roi_options.left = roi_initial_options ? (roi_initial_options.left ? roi_initial_options.left : '0') : '0'; 53873 } 53874 53875 // create the HTML elements necessary for the widget and add them to the doc. 53876 function create_scaffold() { 53877 // The div that wraps the nav components 53878 the_wrapper = new Element('div', {'class':'thumbnail_widget', id:'tnw_'+THUMBNAIL_NAV_COUNT}); 53879 // a container for the image 53880 the_image_wrapper = new Element('div', {'class':'image_wrapper', id:'iw_'+THUMBNAIL_NAV_COUNT}); 53881 // The thumbnail image 53882 the_image = new Element('img', {'class':'thumbnail_img', id:'ti_'+THUMBNAIL_NAV_COUNT}); 53883 // a container for the scrollbar 53884 the_vert_scroll_wrap = new Element('div', {'class':'thumb_scroll_wrapper', height:'100%', width:(self.scrollbar_width+'px'), id:'thumb_vert_scroll'+THUMBNAIL_NAV_COUNT}); 53885 53886 // add them to the container 53887 $(self.container).appendChild(the_wrapper); 53888 $(self.container).appendChild(the_vert_scroll_wrap); 53889 the_wrapper.appendChild(the_image_wrapper); 53890 the_image_wrapper.appendChild(the_image); 53891 53892 // create the ROI element 53893 the_roi = new Element('div',{'class':'roi_box', id:'roib_'+THUMBNAIL_NAV_COUNT}); 53894 the_roi.update('<img id="div_filler" style="height:100%;width:100%;cursor:move" src="data:image/gif;base64,R0lGODlhAQABAJH/AP///wAAAMDAwAAAACH5BAEAAAIALAAAAAABAAEAQAICVAEAOw=="/>'); 53895 the_image_wrapper.appendChild(the_roi); 53896 53897 } 53898 53899 function create_scrollbar() { 53900 53901 //position the wrapper to the right of the image 53902 var container_dims = the_image_wrapper.getDimensions(); 53903 var container_pos = the_image_wrapper.cumulativeOffset(); 53904 var image_pos = the_image.cumulativeOffset(); 53905 var image_dims = the_image.getDimensions(); 53906 var viewport_height = container_dims.height; 53907 53908 var pos = {top:(container_pos.top + 1)+'px', 53909 left:(image_pos.left + image_dims.width + 1)+'px', 53910 width:self.scrollbar_width+'px', 53911 height:viewport_height+'px'}; 53912 the_vert_scroll_wrap.setStyle(pos); 53913 53914 // if we don't need a scrollbar, we're done. 53915 if(image_dims.height <= viewport_height) 53916 return; 53917 53918 if(the_vertical_scroller && the_vert_scroll_wrap.removeChild) 53919 the_vert_scroll_wrap.removeChild($(the_vertical_scroller.id)); 53920 the_vertical_scroller = null; 53921 53922 var v_thumb_height = Math.max(10, (Math.min(1, viewport_height/image_dims.height)) * 100); 53923 the_vertical_scroller = new scroller(the_vert_scroll_wrap.id, 53924 'vertical', 53925 {visible:true, 53926 initial_thumb_size:v_thumb_height}, 53927 {on_move:do_vscroll_move, 53928 on_change:do_vscroll_change}); 53929 } 53930 53931 function do_vscroll_move(sender, val) { 53932 var img_height = the_image.getHeight() - the_roi.getHeight(); 53933 var new_row = -Math.ceil(img_height * val); 53934 53935 scrolling = true; 53936 do_scroll_to(new_row, prev_column); 53937 scrolling = false;
53938 } 53939 53940 function get_roi_offsets() { 53941 var roi_pos = the_roi.positionedOffset(); 53942 var wrap_pos = the_image_wrapper.positionedOffset(); 53943 var image_pos = the_image.positionedOffset(); 53944 var img_margin = new Number($(the_image_wrapper).getStyle('margin-top').replace('px','')); 53945 53946 return({x:(roi_pos.left - image_pos.left), y:(roi_pos.top + img_margin - wrap_pos.top)}); 53947 } 53948 53949 function do_scroll_to(row, column) { 53950 scrolling = true; 53951 53952 the_image.setStyle({top:row+'px'}); 53953 // if the column has changed, move the ROI rather than the image 53954 if(column != prev_column) { 53955 var roi_off = get_roi_offsets(); 53956 roi_options.top = -(row-roi_off.y); 53957 roi_options.left = column; 53958 set_roi_attributes(); 53959 recalculateLayout(); 53960 prev_column = column; 53961 } 53962 do_scroll(); 53963 53964 if(self.on_move) 53965 self.on_move(this, get_nav_position()); 53966 53967 scrolling = false; 53968 } 53969 53970 function do_vscroll_change(sender, val) { 53971/* if(navigating) 53972 return; 53973 var new_row = Math.ceil(self.tile_source.data_rows * val); 53974 navigate(current_column_index, new_row, true); 53975*/ } 53976 53977 function init() { 53978 53979 setup_roi_options(); 53980 create_scaffold(); 53981 // position the roi after the image has loaded 53982 the_image.observe('load', do_image_load); 53983 the_image.src = self.image_src; 53984 53985 // update the global count of widgets 53986 THUMBNAIL_NAV_COUNT++; 53987 } 53988 53989 init(); 53990} 53991; 53992 53993var __tiled_data_table_count__ = 0; 53994 53995function tiled_data_table(container, column_definitions, initial_scale, event_options, options) { 53996 53997// public properties 53998 this.container = $(container); 53999 this.column_defs = column_definitions; 54000 this.scale = initial_scale; 54001 this.visible_rows = 0; 54002 this.data_rows = 0; 54003 this.show_selected = (options ? (options.show_selected ? options.show_selected : false) : false); 54004 this.index = -1; 54005 this.selections = (options ? (options.selections ? options.selections : {}) : {}); 54006 54007// event hooks 54008 this.on_column_click = event_options ? event_options.on_column_click : null; 54009 this.on_select_click = event_options ? event_options.on_select_click : null; 54010 this.on_row_click = event_options ? event_options.on_row_click : null; 54011 this.on_row_over = event_options ? event_options.on_row_over : null; 54012 54013 54014// public methods 54015 this.set_scale = function(new_height) { 54016 var font_size = Math.min(14, Math.max(0, (new_height-4))); 54017 for(var i = 1; i < the_table.rows.length; i++) { 54018 $(the_table.rows[i]).setStyle({height:new_height+'px', overflow:'hidden'}); 54019 $(the_table.rows[i].cells[0]).setStyle({height:new_height+'px', fontSize:font_size+3+'px', overflow:'hidden', lineHeight: new_height+'px'}); 54020 for(var j = 1; j < the_table.rows[i].cells.length; j++) { 54021 $(the_table.rows[i].cells[j]).setStyle({height:new_height+'px', fontSize:font_size+'px'}); 54022 } 54023 } 54024 this.scale = new_height; 54025 this.visible_rows = Math.ceil(this.container.getHeight() / this.scale); 54026 } 54027
54028 this.highlight_row = function(row) { 54029 if(highlighted_row) 54030 highlighted_row.removeClassName('highlighted'); 54031 54032 if(row+1 > this.data_rows) 54033 return; 54034 54035 // +1 because row 0 is the column header 54036 highlighted_row = $(the_table.rows[(row+1)]); 54037 54038 if(highlighted_row) 54039 highlighted_row.addClassName('highlighted'); 54040 } 54041 54042 this.highlight_none = function() { 54043 if(highlighted_row) 54044 highlighted_row.removeClassName('highlighted'); 54045 } 54046 54047 this.select_row = function(row) { 54048 row -= data_row_offset; 54049 54050 if(selected_row) 54051 selected_row.removeClassName('selected_row'); 54052 54053 if(row+1 > this.data_rows) 54054 return; 54055 54056 // +1 because row 0 is the column header 54057 selected_row = $(the_table.rows[(row+1)]); 54058 54059 if(selected_row) { 54060 selected_row.addClassName('selected_row'); 54061 selected_row_data_id = selected_row.getAttribute('data_id'); 54062 } 54063 } 54064 54065 this.clear_data = function(first_column_only) { 54066 var col_count = first_column_only ? 1 : this.column_defs.length; 54067 54068 for(var i = 0; i < this.visible_rows; i++) { 54069 var row = the_table.rows[i+1]; 54070 for(var col_index = 0; col_index < col_count; col_index++) { 54071 if( col_index == 0) 54072 $(row.cells[0]).setStyle({visibility:'hidden'}); 54073 else 54074 row.cells[col_index].innerHTML = ''; 54075 } 54076 } 54077 } 54078 54079 54080 this.set_data = function(new_data, row_offset) { 54081 data_row_offset = row_offset; 54082 this.data_rows = new_data.length; 54083 54084 if(selected_row) 54085 selected_row.removeClassName('selected_row'); 54086 54087 // if we're showing selection checkboxes, there's one more column 54088 // that has no corresponding entry in this.column_defs. This means we need 54089 // two different indexes, one into the html table columns and one into column_defs. 54090 var start_index = (self.show_selected ? 1 : 0); 54091 var col_count = this.column_defs.length + start_index; 54092 54093 // for each row in the data set... 54094 for(var i = 0; i < new_data.length; i++) { 54095 if(new_data[i]) { 54096 // set the selection checkbox, if applicable 54097 if(self.show_selected) { 54098 var checker = $('r'+i+'_'+self.index); 54099 if(checker) { 54100 checker.setAttribute('data_id', new_data[i].id); 54101 checker.setStyle({visibility:'visible'}); 54102 mark_selected(checker, is_selected(new_data[i].id)); 54103 } 54104 } 54105 // set the data for each cell, according to the column definitions 54106 var row = the_table.rows[i+1]; 54107 row.setAttribute('data_id', new_data[i].id); 54108 if(selected_row_data_id == new_data[i].id) { 54109 selected_row = row; 54110 selected_row.addClassName('selected_row'); 54111 } 54112 for(var col_index = start_index; col_index < col_count; col_index++) { 54113 var index = col_index - start_index; 54114 var cell = row.cells[col_index]; 54115 54116 cell.title = ''; // clear the title display set in special case 54117 cell.setAttribute('class', 'dtc_gene_symbol'); // set class to default one 54118 54119 var data = new_data[i] && this.column_defs[index].name ? new_data[i][this.column_defs[index].name] : ''; 54120 cell.innerHTML = data; 54121 54122 if ( data == undefined || data == '') // special case - when NHP gene info is missing, display probe info instead 54123 { 54124 data = new_data[i] && this.column_defs[index].alternate_name ? new_data[i][this.column_defs[index].alternate_name] : ''; 54125 cell.innerHTML = data; 54126 cell.title = 'Gene info is unavailable. This is the Affymetrix probe set name.'; 54127 cell.setAttribute('class', 'dtc_gene_symbol_grayed'); 54128 54129 } 54130 54131 } 54132 } 54133 } 54134 54135 // for the rest of the visible rows, clear any existing data 54136 // and hide the check boxes 54137 for(var j = i; j < this.visible_rows; j++) { 54138 row = the_table.rows[j+1]; 54139 for(col_index = 0; col_index < col_count; col_index++) { 54140 if(self.show_selected && col_index == 0) { 54141 var check = $('r'+j+'_'+self.index); 54142 if(check) 54143 check.setStyle({visibility:'hidden'}); 54144 54145 } 54146 else 54147 row.cells[col_index].innerHTML = ''; 54148 } 54149 } 54150 } 54151 54152 this.get_column_width = function(column_index) { 54153 if(column_index >= the_header.cells.length) 54154 return(0); 54155 54156 return($(the_header.cells[column_index]).getWidth()); 54157 } 54158 54159 this.get_visible_column_width = function() { 54160 var a = 0; 54161 for(var i = 0; i < the_header.cells.length-1; i++) 54162 a += $(the_header.cells[i]).getWidth() 54163 54164 return(a); 54165 } 54166 54167 this.set_column_sort_icon = function(column_label, sort) { 54168 do_column_sort_icon(column_label, sort); 54169 } 54170 54171// private properties 54172 var self = this; 54173 var the_wrapper = null; 54174 var the_table = null; 54175 var the_header = null; 54176 var highlighted_row = null; 54177 var selected_row = null; 54178 var selected_row_data_id = -1; 54179 var data_row_offset = 0; 54180 54181 54182// private methods 54183 function build_scaffold() { 54184 the_wrapper = new Element('div', {id:'tdtw_1'+__tiled_data_table_count__, 'class':'data_table_wrapper'}); 54185 the_table = new Element('table', {id:'tdt_1'+__tiled_data_table_count__, 'class':'data_table'}); 54186 build_rows(the_table); 54187 self.container.appendChild(the_wrapper); 54188 the_wrapper.appendChild(the_table); 54189 } 54190 54191 var the_header_buttons = []; 54192 function build_rows(table) { 54193 // we will theoretically get as small as 1 pixel per row. 54194 var row_count = self.container.getHeight(); 54195 var col_count = self.column_defs.length; 54196 54197 the_header = table.insertRow(-1); 54198 the_header.className = 'data_header_row'; 54199 54200 // insert <th> for the checkbox column 54201 if(self.show_selected) { 54202 var th = the_header.insertCell(-1); 54203 th.className = 'data_header_column header_column_checkbox'; 54204 } 54205 54206 // insert <th> for all the rest of the columns 54207 var button_index = 0; 54208 for(var h = 0; h < col_count; h++) { 54209 var th = the_header.insertCell(-1); 54210 var class_name = 'data_header_column header_column_' + self.column_defs[h
54210].id; 54211 th.className = class_name; 54212 if(self.column_defs[h].title) 54213 th.title = self.column_defs[h].title; 54214 54215 // if the column is sortable, make a button for it 54216 if(self.column_defs[h].sortable) { 54217 var column_button = new Element('div', {id:('button_'+button_index++), 'className':'column_button'}); 54218 column_button.observe('click', do_column_click); 54219 th.appendChild(column_button); 54220 $j(column_button).button(); 54221 the_header_buttons.push(column_button); 54222 if(self.column_defs[h].title) 54223 column_button.title = self.column_defs[h].title; 54224 } 54225 } 54226 54227 // one more column to fill any empty space 54228 th = the_header.insertCell(-1); 54229 class_name = 'data_header_column header_column_filler'; 54230 th.className = class_name; 54231 54232 // make each row 54233 for(var i = 0; i < row_count; i++) { 54234 var row = $(table.insertRow(-1)); 54235 row.setAttribute('row_index', i); 54236 row.observe('mouseover', do_row_over); 54237 row.observe('click', do_row_click); 54238 54239 var cell = null; 54240 // make the checkbox cell 54241 if(self.show_selected) { 54242 cell = $(row.insertCell(-1)); 54243 cell.className = 'soft_check_cell'; 54244 var check = new Element('span', {id:'r'+i+'_'+__tiled_data_table_count__, 'class':'soft_check'}); 54245 check.update('❑'); // this is an empty box; 9746 or 9745 are checked 54246 check.observe('click', do_select_click); 54247 check.setAttribute('row_index', i); 54248 check.title = 'Click to add to or remove from selections'; 54249 cell.appendChild(check); 54250 } 54251 54252 // make the cells for each column in this row 54253 for(var j = 0; j < col_count; j++) { 54254 cell = $(row.insertCell(-1)); 54255 cell.observe('mouseover', do_row_over); 54256 cell.observe('click', do_row_click); 54257 cell.setAttribute('row_index', i); 54258 cell.className = 'dtc_' + self.column_defs[j].id; 54259 } 54260 // the filler row... 54261 row.insertCell(-1); 54262 } 54263 } 54264 54265 function label_columns() { 54266 var button_index = 0; 54267 var start_index = (self.show_selected ? 1 : 0); 54268 var col_count = self.column_defs.length + start_index; 54269 54270 for(var h = start_index; h < col_count; h++) { 54271 var col_index = h - (self.show_selected ? 1 : 0); 54272 if(self.column_defs[col_index].sortable) { 54273 var header = $j(the_header_buttons[button_index++]); 54274 header.button('option', 'label', self.column_defs[col_index].label); 54275 switch(self.column_defs[col_index].sort) { 54276 case 'asc': 54277 header.button('option', 'icons', {secondary:'ui-icon-triangle-1-n'}); 54278 break; 54279 case 'desc': 54280 header.button('option', 'icons', {secondary:'ui-icon-triangle-1-s'}); 54281 break; 54282 case 'none': 54283 break; 54284 } 54285 } 54286 else 54287 $(the_header.cells[h]).update(self.column_defs[col_index].label); 54288 } 54289 } 54290 54291 function do_row_over(evt) { 54292 evt = (evt) ? evt : event; 54293 var sender = $(((evt.target) ? evt.target : evt.srcElement)); 54294 54295 var row_index = new Number(sender.getAttribute('row_index')); 54296 self.highlight_row(row_index); 54297 54298 if(self.on_row_over) 54299 self.on_row_over(self, row_index+data_row_offset); 54300 } 54301 54302 function do_cell_over(evt) { 54303 evt = (evt) ? evt : event; 54304 var sender = $(((evt.target) ? evt.target : evt.srcElement)); 54305 54306 var row = sender.parentNode.parentNode; 54307 var row_index = new Number(sender.getAttribute('row_index')); 54308 54309 self.highlight_row(row_index+data_row_offset); 54310 } 54311 54312 function do_row_click(evt) { 54313 evt = (evt) ? evt : event; 54314 var sender = $(((evt.target) ? evt.target : evt.srcElement)); 54315 54316 var row_index = new Number(sender.getAttribute('row_index')); 54317 54318 if(row_index+1 > self.data_rows) 54319 return; 54320 54321 row_index += data_row_offset; 54322 54323 self.select_row(row_index); 54324 54325 if(self.on_row_click) 54326 self.on_row_click(self, row_index); 54327 } 54328 54329 function do_select_click(evt) { 54330 evt = (evt) ? evt : event; 54331 var sender = $(((evt.target) ? evt.target : evt.srcElement)); 54332 54333 var id = sender.getAttribute('data_id'); 54334 var new_state = !is_selected(id); 54335 mark_selected(sender, new_state); 54336 set_selected(id, new_state); 54337 54338 if(id && self.on_select_click) 54339 self.on_select_click(self, id, new_state); 54340 } 54341 54342 54343 function is_selected(id) { 54344 return(id in self.selections); 54345 } 54346 54347 function mark_selected(box, checked) { 54348 if(checked) 54349 box.update(' ☑'); 54350 else 54351 box.update(' ❑'); 54352 54353 box.setAttribute('checked', checked); 54354 } 54355 54356 function set_selected(id, selected) { 54357 // add or remove the id from the selection set 54358 if(selected) { 54359 self.selections[id] = true; 54360 } else { 54361 delete self.selections[id]; 54362 } 54363 } 54364 54365 function do_column_click(evt) { 54366 evt = (evt) ? evt : event; 54367 var sender = ((evt.target) ? evt.target : evt.srcElement); 54368 var button_index = new Number(sender.parentNode.id.replace('button_', '')); 54369 var label = $j(the_header_buttons[button_index]).button('option', 'label'); 54370 54371 if(self.on_column_click) 54372 self.on_column_click(self, label); 54373 } 54374 54375 function do_column_sort_icon(column_label, sort) { 54376 for(var i = 0; i < the_header_buttons.length; i++) { 54377 var header = $j(the_header_buttons[i]); 54378 var label = header.button('option', 'label'); 54379 if(label == column_label) { 54380 switch(sort) { 54381 case 'asc': 54382 header.button('option', 'icons', {secondary:'ui-icon-triangle-1-n'}); 54383 break; 54384 case 'desc': 54385 header.button('option', 'icons', {secondary:'ui-icon-triangle-1-s'}); 54386 break; 54387 case 'none': 54388 header.button('option', 'icons', {secondary:null}); 54389 break; 54390 default: 54391 } 54392 } 54393 } 54394 } 54395
54396 function init() { 54397 build_scaffold(); 54398 label_columns(); 54399 self.set_scale(self.scale); 54400 self.index = __tiled_data_table_count__++; 54401 } 54402 54403 init(); 54404} 54405 54406; 54407 54408var __tiled_image_window__ = 0; 54409 54410 54411// There are three different coordinate spaces to reconcile in the tiled_image_window widget: 54412// data space, which is the matrix of data we're representing as a heatmap image, 54413// tile space, which is the matrix of sub-matrices that make up the entire data set. 54414// display space, which is a projection of (or viewport on) a portion of tile space 54415// rendered in the html page as a set of images. When this widget renders the images, 54416// we may want to show more than one pixel per data point, verticaly and/or horizontaly. 54417// 54418// this.tile_source understands tile_space coordinates, which it 54419// translates into data space in order to request information from the data service. 54420// This widget needs to translate tile space into display space, among other things. 54421// 54422// this clean separation of coordinate spaces is complicated somewhat bacause the 54423// image service is capable of sending an image that uses something other than 54424// a single pixel to represent a single data point. Due to some artifacts 54425// introduced by browser image scaling, it may be desireable to have the 54426// image service deliver an image that uses (for example) 4 vertical pixels 54427// for each data point. Likewise more than one horizontal pixel may be used 54428// to represent a single data point. 54429// 54430// The upshot is that, when making the translation from the data/tile layer 54431// to the display layer, we have to take into account the pixel-per-data point 54432// attributes. 54433function tiled_image_window(container, tile_source, pixel_format, options, event_options) 54434{ 54435//public properties 54436 this.tile_source = tile_source; 54437 this.container = container; 54438 this.viewport_dims = null; 54439 this.viewport_origin = {top:0, left:0}; 54440 this.pixel_format = pixel_format; //{source_vert_pixels_per_row, display_vert_pixels_per_row, source_horiz_pixels_per_column, scale_horiz.} 54441 this.drag_content = options ? (options.drag_content ? options.drag_content : false) : false; 54442 54443 this.scrollbar_width = options ? (options.scrollbar_width ? options.scrollbar_width : 10) : 10; 54444 this.trace_function = null; // assign a function(msg) to this to handle debug output 54445 this.index = -1; 54446 54447// event hooks 54448 this.on_position_change = event_options ? (event_options.on_position_change ? event_options.on_position_change : null) : null; 54449 this.on_layout_change = event_options ? (event_options.on_layout_change ? event_options.on_layout_change : null) : null; 54450 this.on_mouse_over = event_options ? (event_options.on_mouse_over ? event_options.on_mouse_over : null) : null; 54451 this.on_mouse_out = event_options ? (event_options.on_mouse_out ? event_options.on_mouse_out : null) : null; 54452 this.on_mouse_click = event_options ? (event_options.on_mouse_click ? event_options.on_mouse_click : null) : null; 54453 this.on_load = event_options ? (event_options.on_load ? event_options.on_load : null) : null; 54454 54455 54456// public methods 54457 54458 this.get_position = function() { 54459 return({column:current_column_index, row:current_row_index}); 54460 } 54461 54462 this.set_position = function(column, row) { 54463 navigate(column, row, false); 54464 } 54465 54466 this.get_data_size = function() { 54467 return({height:rows_per_viewport, width:columns_per_viewport}); 54468 } 54469 54470 // this and the set_position method are the same, except 54471 // this navigate_to method will raise navigation events, set_position won't. 54472 this.navigate_to = function(column, row) { 54473 navigate(column, row, true); 54474 } 54475 54476 this.resize = function() { 54477 recalculate_layout(); 54478 raise_layout_change(); 54479 refresh(); 54480 } 54481 54482 this.get_scale = function() { 54483 return({width:scale_width, height:scale_height}); 54484 } 54485 54486 this.set_scale = function(width, height) { 54487 do_set_scale(width, height); 54488 } 54489 54490 this.hide = function() { 54491 the_viewport.hide(); 54492 } 54493 54494 this.show = function() { 54495 the_viewport.show(); 54496 } 54497 54498 this.refresh = function() { 54499 refresh(); 54500 } 54501 54502 this.has_dimension = function() { 54503 if(the_viewport && initialized) { 54504 var dims = the_viewport.getDimensions(); 54505 if(dims.width > 0) 54506 return(true); 54507 } 54508 return(false); 54509 } 54510 54511 // non-obvious alert! If the initial scale is given as 0 the heatmap will 54512 // rescale to fit to the container each time the layout is recalculated. 54513 // this can be cool in some situations, but it can also lead to a change 54514 // in aspect ratio, if the user manually rescales after the heat map 54515 // automatically rescales. Once the aspect ratio has changed it is practically 54516 // impossible for the user to get back to the original image that fits 54517 // the original-sized container. Locking the values for scale here 54518 // assures that once we've established an aspect ratio, it never changes. 54519 // The net effect is that the heatmap will pan rather than scale when its 54520 // container is resized. 54521 this.lock_aspect_ratio = function() { 54522 this.pixel_format.scale_height = scale_height; 54523 this.resize(); 54524 } 54525 54526// private properties 54527 54528 var self = this; 54529 var initialized = false;
54530 var the_tiles = new Object(); 54531 54532 // references to the HTML Elements of this widget 54533 var the_viewport = null; 54534 var the_canvas = null; 54535 var the_vert_scroll_wrap = null; 54536 var the_horiz_scroll_wrap = null; 54537 54538 // the scrollbar widgets 54539 var the_vertical_scroller = null; 54540 var the_horizontal_scroller = null; 54541 var navigating = false; 54542 54543 // the data set could be represented as a matrix 54544 // 1 pixel high & 1 pixel wide per data point, but we may 54545 // be using more than one pixel per data point. 54546 var tile_pixel_height = 0; 54547 var tile_pixel_width = 0; 54548 54549 // the width & height of the image you'd get if you stitched 54550 // the entire data set into a single image, one pixel per point. 54551 var image_data_width = 0; 54552 var image_data_height = 0; 54553 54554 // are we force fitting the image into the viewport? 54555 var scale_to_width = false; 54556 var scale_to_height = false; 54557 54558 // how much to stretch the tiles horizontally/vertically to fit in the viewport 54559 var scale_width = 1.0; 54560 var scale_height = 1.0; 54561 var pixel_aspect_ratio = 1.0; 54562 54563 // how many rows & columns of data can we show, 54564 // given the possible scaling and pixel formatting? 54565 var rows_per_viewport = 0; 54566 var columns_per_viewport = 0; 54567 54568 var tile_rows_per_viewport = 0; 54569 var tile_columns_per_viewport = 0; 54570 54571 // the coordinates into the data set we're currently displaying. 54572 var current_row_index = 0; 54573 var current_column_index = 0; 54574 54575 // tracking mouse up/down to determine if the event was part of a click or a drag. 54576 var click_x = 0; 54577 var click_y = 0; 54578 54579 54580// private methods 54581 54582 54583 function build_scaffold() { 54584 the_viewport = new Element('div', {'class':'tiled_image_window', id:'tiw_1'+__tiled_image_window__}); 54585 the_canvas = new Element('div', {'class':'tiled_image_canvas', id:'tc_1'+__tiled_image_window__}); 54586 the_vert_scroll_wrap = new Element('div', {height:'100%', width:(self.scrollbar_width+'px'), id:'vert_scroll', 'class':'vert_scroll'}); 54587 the_horiz_scroll_wrap = new Element('div', {width:'100%', height:(self.scrollbar_width+'px'), id:'horiz_scroll', 'class':'horiz_scroll', display:'none'}); 54588 self.container.appendChild(the_viewport); 54589 the_viewport.appendChild(the_canvas); 54590 self.container.parentNode.appendChild(the_vert_scroll_wrap); 54591 self.container.parentNode.appendChild(the_horiz_scroll_wrap); 54592 54593 the_canvas.observe('mousemove', do_canvas_over); 54594 the_canvas.observe('mousedown', prepare_canvas_click); 54595 the_canvas.observe('mouseup', do_canvas_click); 54596 the_canvas.observe('mouseout', do_canvas_out); 54597 } 54598 54599 function do_vscroll_move(sender, val) { 54600 if(navigating) 54601 return; 54602 var new_row = Math.ceil(self.tile_source.data_rows * val); 54603 navigate(current_column_index, new_row, true); 54604 } 54605 54606 function do_vscroll_change(sender, val) { 54607 if(navigating) 54608 return; 54609 var new_row = Math.ceil(self.tile_source.data_rows * val); 54610 navigate(current_column_index, new_row, true); 54611 } 54612 54613 function do_hscroll_move(sender, val) { 54614 if(navigating) 54615 return; 54616 var new_col = Math.ceil(self.tile_source.data_columns * val); 54617 navigate(new_col, current_row_index, true); 54618 } 54619 54620 function do_hscroll_change(sender, val) { 54621 if(navigating) 54622 return; 54623 var new_col = Math.ceil(self.tile_source.data_columns * val); 54624 navigate(new_col, current_row_index, true); 54625 } 54626 54627 // given the display coordinates (probably from a mouse event) 54628 // return the heatmap-relative coordinates. 54629 function display_space_to_map_space(x, y) { 54630 var off = the_viewport.cumulativeOffset(); 54631 var scrolloff = the_viewport.cumulativeScrollOffset(); 54632 54633 var column = Math.ceil(x + scrolloff.left - off.left); 54634 var row = Math.ceil(y + scrolloff.top - off.top); 54635 return({column:column, row:row}); 54636 } 54637 54638 // given heatmap-relative coordinates, return data space coordinates 54639 function map_space_to_data_space(x, y) { 54640 var column = Math.floor((x / scale_width)) + current_column_index; 54641 var row = Math.floor((y / scale_height)) + current_row_index; 54642 54643 return({column:column, row:row}); 54644 } 54645 54646 function do_canvas_over(evt) { 54647 var map_coords = display_space_to_map_space(evt.clientX, evt.clientY); 54648 var data_coords = map_space_to_data_space(map_coords.column, map_coords.row); 54649 54650 if(data_coords.row > self.tile_source.data_rows) 54651 return; 54652 54653 if(self.on_mouse_over) 54654 self.on_mouse_over(self, {column:data_coords.column, 54655 row:data_coords.row, 54656 offset_column:current_column_index, 54657 offset_row:current_row_index, 54658 clientX:evt.clientX, 54659 clientY:evt.clientY}); 54660 } 54661 54662 function do_canvas_out(evt) { 54663 var map_coords = display_space_to_map_space(evt.clientX, evt.clientY); 54664 var data_coords = map_space_to_data_space(map_coords.column, map_coords.row); 54665 54666 if(self.on_mouse_out) 54667 self.on_mouse_out(self, {column:data_coords.column, 54668 row:data_coords.row, 54669 offset_column:current_column_index, 54670 offset_row:current_row_index, 54671 clientX:evt.clientX, 54672 clientY:evt.clientY}); 54673 } 54674 54675 function do_canvas_click(evt) { 54676 54677 if(click_x != evt.clientX || click_y != evt.clientY) 54678 return; 54679 54680 var map_coords = display_space_to_map_space(evt.clientX, evt.clientY); 54681 var data_coords = map_space_to_data_space(map_coords.column, map_coords.row); 54682 54683 var position = {column:data_coords.column, 54684 row:data_coords.row, 54685 offset_column:current_column_index, 54686 offset_row:current_row_index, 54687 clientX:evt.clientX, 54688 clientY:evt.clientY}; 54689 54690 if(self.on_mouse_click) 54691 self.on_mouse_click(self, position); 54692 } 54693 54694 function prepare_canvas_click(evt) { 54695 click_x = evt.clientX; 54696 click_y = evt.clientY; 54697 } 54698 54699 function get_viewport_rect() { 54700 var dims = the_viewport.getDimensions(); 54701 var pos = the_viewport.positionedOffset(); 54702 return({top:pos.top, left:pos.left, width:dims.width, height:dims.height}); 54703 } 54704 54705 54706 // calculate the 2D range of tiles that are required given the current 54707 // data row & column indices. 54708 function calculate_required_tiles() { 54709 54710 var tile_start_row = Math.floor( current_row_index / self.tile_source.tile_height );
54711 var tile_row_offset = Math.ceil( current_row_index - (tile_start_row * self.tile_source.tile_height) ); 54712 //var tile_end_row = Math.ceil(( ((current_row_index + rows_per_viewport) * scale_height)/ (self.tile_source.tile_height / scale_height))) -1 ; 54713 var tile_end_row = tile_start_row + tile_rows_per_viewport;// / self.tile_source.tile_height); 54714 tile_end_row = Math.min(tile_end_row, self.tile_source.tiles_high-1); 54715 54716 var tile_start_column = Math.floor( current_column_index / self.tile_source.tile_width ); 54717 var tile_column_offset = Math.ceil( current_column_index - (tile_start_column * self.tile_source.tile_width) ); 54718 var tile_end_column = Math.floor( ((current_column_index + columns_per_viewport) / self.tile_source.tile_width) )-1; 54719 tile_end_column = Math.max(0, Math.min(tile_end_column, self.tile_source.tiles_wide-1)); 54720 54721 var ret = {start_row:tile_start_row, 54722 row_offset:tile_row_offset, 54723 end_row:tile_end_row, 54724 start_column:tile_start_column, 54725 column_offset:tile_column_offset, 54726 end_column:tile_end_column}; 54727 return(ret); 54728 } 54729 54730 54731 // the tile_source may choose to continue to cache some or all 54732 // of these un-needed tiles, but this widget should release them. 54733 function release_unneeded_tiles(tile_coords) { 54734 return; // do nothing and let heatmap_tiler keep all the image info 54735// for(var col in self.tile_source.tiles) { 54736// for(var row in col) { 54737// if(self.tile_source.tiles[col][row]) { 54738// if(row < tile_coords.start_row 54739// || 54740// row > tile_coords.end_row 54741// || 54742// col < tile_coords.start_column 54743// || 54744// col > tile_coords.end_column 54745// ) { 54746// self.tile_source.release_tile(col, row); 54747// } 54748// } 54749// } 54750// } 54751 } 54752 54753 54754 var new_tile_count = 0; 54755 // Set the src attribute of the img elements in the canvas to match the 54756 // given range of tiles, and shift the canvas under the viewport to show 54757 // the proper rows & columns. 54758 function set_tile_srcs(tile_coords) { 54759try { 54760 54761 // if there are any existing, get rid of them 54762 var imgs = the_canvas.select('img'); 54763 for(var i = 0; i < imgs.length; i++) 54764 imgs[i].src = "/assets/heatmap_ui/blank.gif"; 54765 54766 54767 // set the src for each of the imgs in the canvas 54768 for(var col = tile_coords.start_column; col <= tile_coords.end_column; col++) { 54769 for(var row = tile_coords.start_row; row <= tile_coords.end_row; row++) { 54770 var tile = self.tile_source.get_tile(col, row); 54771 var img; 54772 if(tile) 54773 img = the_tiles[col-tile_coords.start_column][row-tile_coords.start_row]; 54774 if(img) { 54775 img.src = tile ? tile.src : "/assets/heatmap_ui/blank.gif"; 54776 54777 // scale the last tile appropriately 54778 if (row == self.tile_source.tiles_high - 1 && self.tile_source.data_rows % self.tile_source.maximum_tile_height != 0) { 54779 scaled_tile_height = (self.tile_source.data_rows % self.tile_source.maximum_tile_height) * scale_height; 54780 img.setStyle({height: scaled_tile_height + 'px'}) 54781 } else { 54782 img.setStyle({height: tile_pixel_height + 'px'}) 54783 } 54784 } 54785 } 54786 } 54787 //adjust the position of the canvas that lies behind the viewport 54788 the_canvas.setStyle({top:(-tile_coords.row_offset * scale_height)+'px', 54789 left:(-tile_coords.column_offset * scale_width)+'px'}); 54790} catch(e) { trace("error setting tile src: " + e.message); } 54791 } 54792 54793 54794 // call this after any change that would affect how many tiles would 54795 // fit in the viewport. FOr example a page resize, a change to the 54796 // vertical or horizontal scaling/zooming, etc. 54797 // 54798 // This is a relatively expensive operation. 54799 function recalculate_layout() { 54800 54801 var view_rect = get_viewport_rect(); 54802 54803 // part of hiding/reshowing update -- might not be necessary 54804 if (view_rect.width == 0 && view_rect.height == 0) 54805 return; 54806 54807 // the source images may have other than one pixel per data point. 54808 var source_pixel_dims = self.tile_source.get_pixel_dimensions(); 54809 54810 // the size (in data coords) of the complete image if all the tiles were stitched togehter 54811 image_data_width = self.tile_source.tile_width * self.tile_source.tiles_wide; 54812 image_data_height = self.tile_source.tile_height * self.tile_source.tiles_high; 54813 54814 // are we scaling tiles to fit the container? 54815 scale_to_width = (self.pixel_format.scale_width == 0); 54816 scale_to_height = (self.pixel_format.scale_height == 0); 54817 54818 // calculate the scale factor if fitting, or use the user-supplied value. 54819 // NOTE that the images coming from the tile source may
54819not be 1 pixel 54820 // per data point, so we may need to adjust the user-supplied scale. 54821 if(scale_to_width) 54822 scale_width = view_rect.width / (self.tile_source.tile_width * self.tile_source.tiles_wide * source_pixel_dims.width); 54823 else 54824 scale_width = self.pixel_format.scale_width/source_pixel_dims.width; 54825 54826 if(scale_to_height) 54827 scale_height = view_rect.height / (self.tile_source.tile_height * self.tile_source.tiles_high * source_pixel_dims.height) 54828 else 54829 scale_height = self.pixel_format.scale_height; 54830 54831 scale_height = Math.ceil(scale_height); 54832 54833 pixel_aspect_ratio = scale_height / scale_width; 54834 54835 // how big are the tiles after scaling? 54836 tile_pixel_width = self.tile_source.tile_width * scale_width; 54837 tile_pixel_height = self.tile_source.tile_height * scale_height; 54838 54839 // rows & columns that fit in the viewport, in data coords. 54840 rows_per_viewport = Math.ceil((view_rect.height) / (scale_height)); 54841 columns_per_viewport = Math.ceil(Math.min((view_rect.width / (scale_width * source_pixel_dims.width)), image_data_width)); 54842 54843 tile_rows_per_viewport = Math.min(Math.ceil(view_rect.height / (scale_height * self.tile_source.tile_height)), self.tile_source.tiles_high); 54844 tile_columns_per_viewport = Math.min(Math.ceil(view_rect.width / (scale_width * self.tile_source.tile_width)), self.tile_source.tiles_wide); 54845 54846 if (view_rect.height > 0) { 54847 // do we need scrollbars? 54848 create_scrollbars(columns_per_viewport, rows_per_viewport, view_rect); 54849 54850 // create the img elements 54851 create_tile_elements(view_rect); 54852 } 54853 } 54854 54855 function create_scrollbars(columns_per_viewport, rows_per_viewport, view_rect) { 54856 54857 var h_scroll_needed = (columns_per_viewport > 0) && Math.ceil(columns_per_viewport) < self.tile_source.data_columns; 54858 var v_scroll_needed = (rows_per_viewport > 0) && Math.ceil(rows_per_viewport) < self.tile_source.data_rows; 54859 54860 if(v_scroll_needed) { 54861 54862 //var v_thumb_height = Math.max(10, (Math.min(1, rows_per_viewport/(self.tile_source.data_rows))) * 100); 54863 // 32 is the size of the heights of the up & down scroll buttons 54864 var v_thumb_height = Math.max(0, Math.min((view_rect.height - 48), Math.max(16, (view_rect.height - 32) * (rows_per_viewport/(self.tile_source.data_rows - rows_per_viewport))))); 54865 the_vert_scroll_wrap.setStyle({position:'absolute', 54866 top:(view_rect.top+24)+'px', 54867 left:(view_rect.left+view_rect.width)+'px', 54868 height:view_rect.height+'px', 54869 width:(self.scrollbar_width)+'px', 54870 border:'0px solid #f00'}); 54871 54872 if(the_vertical_scroller && the_vert_scroll_wrap.removeChild) { 54873 the_vert_scroll_wrap.removeChild($(the_vertical_scroller.id)); 54874 } 54875 the_vertical_scroller = null; 54876 54877 var position = current_row_index/(self.tile_source.data_rows ? self.tile_source.data_rows : current_row_index); 54878 the_vertical_scroller = new scroller(the_vert_scroll_wrap, 54879 'vertical', 54880 {visible:true, 54881 initial_thumb_size:v_thumb_height, 54882 initial_position:position}, 54883 {on_move:do_vscroll_move, 54884 on_change:do_vscroll_change, 54885 on_button1_click:do_scroll_up_click, 54886 on_button2_click:do_scroll_down_click}); 54887 } else 54888 if(the_vertical_scroller) 54889 the_vertical_scroller.show(false); 54890 54891 if(h_scroll_needed) { 54892 //var h_thumb_height = (Math.min(1, columns_per_viewport/(self.tile_source.data_columns-columns_per_viewport)) * 100); 54893 var h_thumb_height = Math.max(16, (view_rect.width - 32) * (columns_per_viewport/(self.tile_source.data_columns))); 54894 the_horiz_scroll_wrap.setStyle({position:'absolute', 54895 display:'block', 54896 top:(view_rect.top+view_rect.height+24)+'px', 54897 left:view_rect.left+'px', 54898 height:(self.scrollbar_width)+'px', 54899 width:view_rect.width+'px', 54900 border:'0px solid #f00'}); 54901 54902 if(the_horizontal_scroller && the_horiz_scroll_wrap.removeChild) { 54903 the_horiz_scroll_wrap.removeChild($(the_horizontal_scroller.id)); 54904 } 54905 54906 the_horizontal_scroller = null; 54907 var position = current_column_index/(self.tile_source.data_columns ? self.tile_source.data_columns : current_column_index); 54908 the_horizontal_scroller = new scroller(the_horiz_scroll_wrap, 54909 'horizontal', 54910 {visible:true, 54911 initial_thumb_size:h_thumb_height, 54912 initial_position:position}, 54913 {on_move:do_hscroll_move, 54914 on_change:do_hscroll_change, 54915 on_button1_click:do_scroll_left_click, 54916 on_button2_click:do_scroll_right_click}); 54917 // the_horizontal_scroller.resize(); 54918 } else 54919 if(the_horizontal_scroller) 54920 the_horizontal_scroller.show(false); 54921 54922 } 54923 54924 function create_tile_elements(view_rect) { 54925 54926 // if there are any existing, get rid of them 54927 var imgs = the_canvas.select('img'); 54928 for(var i = 0; i < imgs.length; i++) 54929 imgs[i].remove(); 54930 54931 //fill the canvas with the required number of img elements. 54932 var tile_rows = Math.ceil(view_rect.height / (tile_pixel_height)) + 1; 54933 var tile_cols = self.tile_source.tiles_wide; 54934 new_tile_count = tile_rows * tile_cols; 54935 54936 for(var col = 0; col < tile_cols; col++) { 54937 for(var row = 0; row < tile_rows; row++) { 54938 54939// var left = col * Math.floor(tile_pixel_width*scale_width); 54940// var top = row * Math.floor(tile_pixel_height*scale_height); 54941 54942 var left = col * tile_pixel_width; 54943 var top = row * tile_pixel_height; 54944 54945 var tile = self.tile_source.get_tile(col, row); 54946 var src = tile ? tile.src : "/assets/heatmap_ui/blank.gif"; 54947 54948 //var attrs = {'className':'img_tile', top:top+'px', left:left+'px'}; 54949 var attrs = {'className':'img_tile'}; 54950 var img = new Element('img', attrs); 54951 img.observe('load', do_tile_load); 54952 img.src = src; 54953 // IE requires this setting after creation, not as an attribute during creation. 54954 img.setStyle({width:(tile_pixel_width)+'px', height:(tile_pixel_height)+'px', left:left+'px', top:top+'px'}); 54955 54956 img.observe('click', do_focus_click); 54957 the_tiles[col] = the_tiles[col] ? the_tiles[col] : new Object(); 54958 the_tiles[col][row] = img; 54959 the_canvas.appendChild(img); 54960 } 54961 } 54962 } 54963 54964 function do_tile_load() { 54965 if(--new_tile_count == 0) { 54966 initialized = true; 54967 if(self.on_load) 54968 self.on_load(self); 54969 } 54970 } 54971 54972 function do_scroll_up_click() { 54973 var delta = (rows_per_viewport - 2); 54974 navigate(current_column_index, current_row_index - delta, true); 54975 } 54976 54977 function do_scroll_down_click() { 54978 var delta = (rows_per_viewport - 2); 54979 navigate(current_column_index, current_row_index + delta, true); 54980 } 54981 54982 function do_scroll_left_click() { 54983 var delta = (columns_per_viewport - 2); 54984 navigate(current_column_index - delta, current_row_index, true); 54985 } 54986 54987 function do_scroll_right_click() { 54988 var delta = (columns_per_viewport - 2); 54989 navigate(current_column_index + delta, current_row_index, true); 54990 } 54991 54992 function refresh() { 54993 var tile_coords = calculate_required_tiles(); 54994 set_tile_srcs(tile_coords); 54995 release_unneeded_tiles(tile_coords); 54996 } 54997 54998 function setup_wheel_listener() { 54999 //listen to mouse wheel events. (Credit http://www.ogonek.net/mousewheel/demo.html) 55000 Object.extend(Event, { 55001 wheel:function (event){ 55002 var delta = 0; 55003 if (!event) event = window.event; 55004 if (event.wheelDelta) { 55005 delta = event.wheelDelta/120; 55006 if (window.opera) delta = -delta; 55007 } else if (event.detail) { 55008 delta = -event.detail/3; 55009 } 55010 return Math.round(delta); 55011 } 55012 }); 55013 Event.observe(the_viewport, 'mousewheel', wheel_zoom, false); 55014 Event.observe(the_viewport, 'DOMMouseScroll', wheel_zoom, false); 55015 } 55016 55017 function setupKeyListener() { 55018 //listen for key strokes for zoom & pan 55019 if(Prototype.Browser.WebKit) { 55020 // In Safari (& all webkit-based browsers) a <div> is not focusable, 55021 // so will never report keystrokes. This adds an invisable text 55022 // element that will receive focus and process keyboard events. 55023 setupWebKitKeyListener(); 55024 } 55025 else { 55026 var imgs = the_canvas.select('img'); 55027 for(var i = 0; i < imgs.length; i++) { 55028 imgs[i].observe('keypress', doKey); 55029 //alert("assigned keypress"); 55030 } 55031 } 55032 55033 } 55034 55035 var key_catcher; 55036 function setupWebKitKeyListener() { 55037 if(Prototype.Browser.WebKit) { 55038 key_catcher = new Element('input',{id:'heatmapkey'+self.container.id, type:'text', className:'key_catcher', style:'outline:none;background-color:transparent;font-size:0pt;border:0px;width:0px;height:0px'}); 55039 self.container.appendChild(key_catcher); 55040 Event.observe(self.container, 'click', catchWebkitKeys, true);
55041 key_catcher.focus(); 55042 Event.observe(key_catcher, 'keypress', doKey, true); 55043 } 55044 } 55045 55046 function catchWebkitKeys() { 55047 key_catcher.focus(); 55048 } 55049 55050 function do_focus_click(evt) { 55051 //alert("focus evt: " + inspect(evt)); 55052// IE no likey evt.currentTarget.focus(); 55053 the_viewport.focus(); 55054 } 55055 55056 function do_scale(increment) { 55057 var new_pixel_width; 55058 var new_pixel_height; 55059 55060 //increment = (scale_width > 1) ? increment : increment/10; 55061 increment = increment/10; 55062 new_pixel_width = scale_width + increment; 55063 new_pixel_height = new_pixel_width * pixel_aspect_ratio; 55064 55065 // let's not scale any smaller than this. 55066 if( (new_pixel_width < 0.1) || (new_pixel_height < 0.1) ) 55067 return; 55068 55069 do_set_scale(new_pixel_width, new_pixel_height); 55070 } 55071 55072 function doKey(evt) { 55073 alert("key: "); 55074 // ctrl & alt in combination with any of these would mean something else. 55075 if(evt.ctrlKey || evt.altKey) 55076 return; 55077 55078 switch(evt.keyCode) { 55079 case Event.KEY_RIGHT: 55080 navigate(current_column_index+1, current_row_index, true); 55081 Event.stop(evt); 55082 break; 55083 case Event.KEY_LEFT: 55084 navigate(current_column_index-1, current_row_index, true); 55085 Event.stop(evt); 55086 break; 55087 case Event.KEY_UP: 55088 navigate(current_column_index, current_row_index-1, true); 55089 Event.stop(evt); 55090 break; 55091 case Event.KEY_DOWN: 55092 navigate(current_column_index, current_row_index+1, true); 55093 break; 55094 case 65://"a" to be consistent with SIV, zoom in 55095 do_scale(1); 55096 Event.stop(evt); 55097 break; 55098 case 90://"z" to be consistent with SIV, zoom out 55099 do_scale(-1); 55100 Event.stop(evt); 55101 break; 55102 case 107://"+" 55103 do_scale(1); 55104 Event.stop(evt); 55105 break; 55106 case 109://"-" 55107 do_scale(-1); 55108 Event.stop(evt); 55109 break; 55110 case 187://"+" on IE & Safari number row 55111 do_scale(1); 55112 Event.stop(evt); 55113 break; 55114 case 189://"-" on IE & Safari number row 55115 do_scale(-1); 55116 Event.stop(evt); 55117 break; 55118 case 61://"+" on FFv2 number row 55119 if(Prototype.Browser.Gecko) { 55120 do_scale(1); 55121 } 55122 Event.stop(evt); 55123 break; 55124 case 16:// shift 55125 break; 55126 default: 55127 break; 55128 } 55129 } 55130 55131 55132 // it would be nice to call this from within recalculate_layout(), but 55133 // it turns out that this needs to be called after refresh(); 55134 function raise_layout_change() { 55135 if(self.on_layout_change) 55136 self.on_layout_change(self, {row:Math.floor(current_row_index), 55137 column:Math.floor(current_column_index), 55138 visible_rows:rows_per_viewport, 55139 visible_columns:columns_per_viewport}); 55140 } 55141 55142 function do_set_scale(pixel_width, pixel_height) { 55143 self.pixel_format.scale_width = pixel_width < 0 ? scale_width : pixel_width; 55144 self.pixel_format.scale_height = pixel_height < 0 ? scale_height : pixel_height; 55145 recalculate_layout(); 55146 refresh(); 55147 raise_layout_change(); 55148 } 55149 55150 function navigate(x, y, raise) { 55151 navigating = true; 55152 x = Math.floor(x); 55153 y = Math.floor(y); 55154 55155 current_column_index = x < 0 ? 0 : x; 55156 current_row_index = y < 0 ? 0 : y; 55157 55158 var max_row_index = Math.max(0, (self.tile_source.data_rows - rows_per_viewport)+1); 55159 var max_col_index = Math.max(0, (self.tile_source.data_columns - columns_per_viewport)+1); 55160 current_row_index = current_row_index > max_row_index ? max_row_index : current_row_index; 55161 current_column_index = current_column_index > max_col_index ? max_col_index : current_column_index; 55162 55163 55164 if(the_horizontal_scroller && the_horizontal_scroller.visible()) { 55165 var new_h_pos = (current_column_index / self.tile_source.data_columns); 55166 the_horizontal_scroller.set_position(new_h_pos); 55167 } 55168 55169 if(the_vertical_scroller && the_vertical_scroller.visible()) { 55170 var new_v_pos = (current_row_index / self.tile_source.data_rows); 55171 the_vertical_scroller.set_position(new_v_pos); 55172 } 55173 55174 if(raise && self.on_position_change) 55175 self.on_position_change(self, {column:current_column_index, row:current_row_index}); 55176 refresh(); 55177 55178 navigating = false;
55179 } 55180 55181 function init() { 55182 build_scaffold(); 55183 recalculate_layout(); 55184 refresh(); 55185 raise_layout_change(); 55186 self.index = __tiled_image_window__++; 55187 55188 } 55189 55190 init(); 55191} 55192 55193; 55194 55195// wraps the entire heatmap image as a virtual quilt of tiles. 55196// Serves up tiles based on their x,y coordinates in the quilt. 55197// The characteristics of the quilt (tiles wide, tiles high) is 55198// determined by the number of rows and columns in the data 55199// set (defined in the given session object) and by the given 55200// tile_width & tile_height. 55201// 55202// 55203function heatmap_tiler(service_base_url, session, tile_width, maximum_tile_height, tile_height, heatmap_format_options) 55204{ 55205// public properties 55206 this.service_base_url = service_base_url; 55207 this.session = session; 55208 this.tiles = new Object(); 55209 this.tile_width = tile_width; // the number of data columns in a tile, not pixels. 55210 this.maximum_tile_height = maximum_tile_height; // height of the tiles other than the last (or only) tile 55211 this.tile_height = tile_height; // the number of data rows in a tile, not pixels. 55212 this.tiles_wide = 0; 55213 this.tiles_high = 0; 55214 this.data_columns = 0; 55215 this.data_rows = 0; 55216 this.heatmap_format = heatmap_format_options; 55217 55218 this.trace_function = null; // assign a funtion(msg) to this to handle debug output 55219 55220 55221// public methods 55222 55223 this.attach_session = function(new_session) { 55224 this.tiles_wide = new_session ? Math.ceil(new_session.number_of_columns/tile_width) : 0; 55225 this.tiles_high = new_session ? Math.ceil(new_session.number_of_probes/tile_height) : 0; 55226 this.data_columns = new_session ? new_session.number_of_columns : 0; 55227 this.data_rows = new_session ? new_session.number_of_probes : 0; 55228 this.session = new_session; 55229 55230 this.tile_width = this.data_columns; 55231 55232 HEADER_URL = this.service_base_url + "heatmap/display_header/" + this.session.session_id; 55233 HEATMAP_URL = this.service_base_url + "heatmap/display/" + this.session.session_id; 55234 55235 tile_columns = this.tiles_wide; 55236 tile_rows = this.tiles_high; 55237 } 55238 55239 55240 // returns a js Image object for the tile at position 55241 // x, y in the virtual quilt of tiles. 55242 this.get_tile = function(x, y) { 55243 var tile = fetch_tile(x, y) 55244 return(tile); 55245 } 55246 55247 // returns a js Image object of a thumbnail of the entire quilt. 55248 this.get_thumbnail = function(scale) { 55249 var url = get_thumbnail_url(format_options, scale); 55250 var thumbnail = new Image(); 55251 thumbnail.src = url; 55252 return(thumbnail); 55253 } 55254 55255 this.get_header_url = function() { 55256 var url = HEADER_URL + "?groupBy=" + this.session.group_by; 55257 return(url); 55258 } 55259 55260 this.release_tile = function(x, y) { 55261 ditch_tile(x, y); 55262 } 55263 55264 this.set_format_options = function(options) { 55265 format_options = options; 55266 } 55267 55268 this.get_format_options = function() { 55269 return(format_options); 55270 } 55271 55272 this.get_pixel_dimensions = function() { 55273 var width = format_options ? (format_options.pixel_width ? format_options.pixel_width : DEFAULT_PIXEL_WIDTH) : DEFAULT_PIXEL_WIDTH; 55274 var height = format_options ? (format_options.pixel_height ? format_options.pixel_height : DEFAULT_PIXEL_HEIGHT) : DEFAULT_PIXEL_HEIGHT; 55275 return({width:width, height:height}); 55276 } 55277 55278// private properties 55279 var self = this; 55280 var HEADER_URL = ''; 55281 var HEATMAP_URL = ''; 55282 var tile_columns = self.tiles_wide; 55283 var tile_rows = self.tiles_high; 55284 var format_options = heatmap_format_options; 55285 var DEFAULT_PIXEL_WIDTH = 1; 55286 var DEFAULT_PIXEL_HEIGHT = 1; 55287 55288 55289 55290// private methods 55291 55292 function dump_tiles() { 55293 var msg = "" 55294 55295 for(var col in self.tiles) { 55296 for(var row in col) { 55297 msg += "tile url: " + self.tiles[col][row].src 55298 } 55299 } 55300 55301 } 55302 55303 function fetch_tile(x, y) { 55304 if(x >= tile_columns || x < 0) 55305 return(null); 55306 55307 if(y >= tile_rows || y < 0) 55308 return(null); 55309 55310 var tile = create_tile(x, y);;//self.tiles[x] ? (self.tiles[x][y] ? self.tiles[x][y] : create_tile(x, y)) : create_tile(x, y); 55311 return( tile ); 55312 } 55313 55314 function ditch_tile(x, y) { 55315 // this gives notice that the tile is no longer in use. 55316 // manage tile caching here. Maybe just get rid of it immediately. 55317 delete self.tiles[x][y]; 55318 } 55319 55320 function create_tile(x, y) { 55321 var start_row = y * self.tile_height; 55322 self.tiles[x] = self.tiles[x] ? self.tiles[x] : new Object(); 55323 self.tiles[x][y] = self.tiles[x][y] ? self.tiles[x][y] : new Object(); // new Image() 55324 self.tiles[x][y].src = get_heatmap_url(start_row, self.tile_height, format_options); 55325// if( !self.tiles[x][y].src ) 55326// self.tiles[x][y].src = get_heatmap_url(start_row, self.tile_height, format_options); 55327 return(self.tiles[x][y]); 55328 } 55329 55330 55331 // returns a URL that can be used as the src of an HTML <img> tag. This 55332 // is the main heatmap image url. 55333 // 55334 // options is a hash and may may include: 55335 // thumbnail, expr_window, normalize, colormap, group_by 55336 function get_heatmap_url(start_row, num_rows, options) { 55337 var url = HEATMAP_URL + "?startRow=" + start_row + "&numRows=" + num_rows; 55338 if(options) { 55339 url += options.thumbnail ? "&thumbnail" : ""; 55340 url += options.expr_window ? "&exprWindow=" + options.expr_window : ""; 55341 url += options.normalize ? "&normalize=" + options.normalize : ""; 55342 url += options.normalize && options.colormap ? "&colormap=" + options.colormap : ""; 55343 url += self.session.group_by ? "&groupBy=" + self.session.group_by : ""; 55344 url += "&pixelWidth=" 55345 url += options.pixel_width ? options.pixel_width : DEFAULT_PIXEL_WIDTH; 55346 url += "&pixelHeight=" 55347 url += options.pixel_height ? options.pixel_height : DEFAULT_PIXEL_HEIGHT; 55348 } 55349 return(url); 55350 } 55351 55352 // returns a URL that can be used as the src of an HTML <img> tag. This 55353 // is the thumbnail/overview heatmap image url. 55354 // 55355 // options is a hash and may may include: 55356 // expr_window, normalize, colormap, group_by 55357 function get_thumbnail_url(options, scale) { 55358 var url = HEATMAP_URL + "?thumbnail"; 55359 if(options) { 55360 url += options.thumbnail ? "&thumbnail" : ""; 55361 url += options.expr_window ? "&exprWindow=" + options.expr_window : ""; 55362 url += options.normalize ? "&normalize=" + options.normalize : ""; 55363 url += options.colormap ? "&colormap=" + options.colormap : ""; 55364 url += self.session.group_by ? "&groupBy=" + self.session.group_by : ""; 55365 url += "&pixelWidth=1" 55366 url += "&pixelHeight=1" 55367 url += "&scale="+scale; 55368 } 55369 return(url); 55370 } 55371
55372 function init() { 55373 self.attach_session(self.session); 55374 } 55375 55376 init(); 55377 55378// debuggery 55379 function trace(msg) { 55380 if(self.trace_function) 55381 self.trace_function(msg); 55382 } 55383} 55384; 55385 55386 55387var __heatmap_view_count__ = 0; 55388 55389// // this will define a collection of widgets for the various components of the 55390// heatmap viewer, along with one widget to rule them all. 55391 55392var HM_VIEW_SELECTION_OFF = 0; 55393var HM_VIEW_SELECTION_HM = 1; 55394var HM_VIEW_SELECTION_THUMB = 2; 55395 55396function heatmap_view(container, number_of_columns, image_data, row_data, event_options, options) 55397{ 55398 55399// public properties 55400 this.container = $(container); 55401 this.number_of_columns = number_of_columns; 55402 this.image_data = image_data; 55403 this.the_row_data = row_data; 55404 55405// this one is just a place holder till the controls are constructed. OK. 55406 this.first_column_width = 230; 55407 55408 this.fit_to_page = options ? (options.fit_to_page ? options.fit_to_page : false) : false; 55409 this.fit_upper_element_id = options ? (options.fit_upper_element_id ? options.fit_upper_element_id : null) : null; 55410 this.fit_lower_element_ids = options ? (options.fit_lower_element_ids ? options.fit_lower_element_ids : null) : null; 55411 this.min_height = options ? (options.min_height ? options.min_height : 0) : 0; 55412 this.ramp_url = options ? (options.ramp_url ? options.ramp_url : '') : ''; 55413 this.table_schema = options ? (options.table_schema ? options.table_schema : null) : null; 55414 this.heatmap_only = options ? (options.heatmap_only ? options.heatmap_only : false) : false; 55415 this.show_thumbnail = options ? (options.show_thumbnail ? options.show_thumbnail : false) : false; 55416 this.show_selected = (options ? (options.show_selected ? options.show_selected : false) : false); 55417 this.show_row_position = (options ? (options.show_row_position ? options.show_row_position : false) : false); 55418 this.scrollbar_width = (options ? (options.scrollbar_width ? options.scrollbar_width : 16) : 16); 55419 this.colormap_set = (options ? (options.colormap_set ? options.colormap_set : []) : []); 55420 this.control_mask = (options ? (options.control_mask ? options.control_mask : HM_CONTROL_ALL) : HM_CONTROL_ALL); 55421 this.initial_zoom = (options ? (options.initial_zoom ? options.initial_zoom : 0) : 0); 55422 this.status_label = (options ? (options.status_label ? options.status_label : '') : ''); 55423 this.groupings = (options ? (options.groupings ? options.groupings : ['donors,structures','structures,donors']) : ['donors,structures','structures,donors']); 55424 this.resolutions = (options ? (options.resolutions ? options.resolutions : 55425 {100:'Coarse', 55426 200:'Averaged Samples', 55427 structures:'Structures', 55428 samples:'Samples'}) : 55429 {100:'Coarse', 55430 200:'Averaged Samples', 55431 structures:'Structures', 55432 samples:'Samples'}); 55433 this.default_resolution = (options ? (options.default_resolution ? options.default_resolution : '200') : '200'); 55434 this.default_restriction = (options ? (options.default_restriction ? options.default_restriction : false) : false); 55435 this.display_donors = (options ? (options.display_donors ? options.display_donors : null) : null); 55436 this.display_structures = (options ? (options.display_structures ? options.display_structures : null) : null); 55437 this.show_display_filters = (options ? (options.show_display_filters ? options.show_display_filters : null) : null); 55438 this.display_donor_groups = (options ? (options.display_donor_groups ? options.display_donor_groups : null) : null); 55439 this.display_donor_filter_type = (options ? (options.display_donor_filter_type ? options.display_donor_filter_type : null) : null); 55440 this.display_structure_groups = (options ? (options.display_structure_groups ? options.display_structure_groups : null) : null); 55441 this.display_structure_filter_type = (options ? (options.display_structure_filter_type ? options.display_structure_filter_type : null) : null); 55442 this.default_grouping = options ? (options.default_grouping ? options.default_grouping : 0) : 0; 55443 //this.expr_window = (options ? (options.colormap_set ? options.colormap_set : [0,10]) : [0,10]); 55444 this.selections = (options ? (options.selections ? options.selections : {}) : {}); 55445 this.user_selection_mode = (options ? (options.user_selection_mode != undefined ? options.user_selection_mode : HM_VIEW_SELECTION_OFF) : HM_VIEW_SELECTION_OFF); 55446 this.enable_cart_view = (options ? (options.enable_cart_view != undefined ? options.enable_cart_view : false ) : false ); 55447 this.default_visibility = (options ? (options.visible != undefined ? options.visible : true) : true); 55448 55449 // custom option used by ahba 55450 this.hide_donor_age_display = ( options ? (options.hide_donor_age_display ? options.hide_donor_age_display : false) : false ); 55451 this.tool_tip = ( options ? (options.tool_tip? options.tool_tip : 'Toggle sort by structure or sort by donor') : 'Toggle sort by structure or sort by donor'); 55452 55453 this.index = -1; 55454 55455 this.show_expression_value_on_status_bar = options ? ( options.show_expression_value_on_status_bar ? options.show_expression_value_on_status_bar : false ) : false;
55456 this.heatmap_download_service = ( options ? (options.heatmap_download_service ? options.heatmap_download_service : null) : null ); 55457 this.download_service_data_set = ( options ? (options.download_service_data_set ? options.download_service_data_set : null) : null ); 55458 this.ontology_options = options ? (options.ontology_options ? options.ontology_options : {} ) : {}; 55459 55460// event hooks 55461 this.on_cell_click = event_options ? event_options.on_cell_click : null; 55462 this.on_heatmap_mouseover = event_options ? event_options.on_heatmap_mouseover : null; 55463 this.on_zoom = event_options ? event_options.on_zoom : null; 55464 this.on_tiled_image_load = event_options ? event_options.on_tiled_image_load : null; 55465 this.on_initialized = event_options ? event_options.on_initialized : null; 55466 this.on_grouping_change = event_options ? event_options.on_grouping_change : null; 55467 this.on_restriction_change = event_options ? event_options.on_restriction_change : null; 55468 this.on_display_change = event_options? (event_options.on_display_change ? event_options.on_display_change : null) : null; 55469 this.on_resolution_change = event_options ? event_options.on_resolution_change : null; 55470 this.on_colormap_change = event_options ? event_options.on_colormap_change : null; 55471 this.on_level_change = event_options ? event_options.on_level_change : null; 55472 this.on_column_click = event_options ? event_options.on_column_click : null; 55473 this.on_clear_click = event_options ? event_options.on_clear_click : null; 55474 this.on_view_selection_click = event_options ? event_options.on_view_selection_click : null; 55475 this.on_select_click = event_options ? event_options.on_select_click : null; 55476 this.on_row_click = event_options ? event_options.on_row_click : null; 55477 this.on_row_over = event_options ? event_options.on_row_over : null; 55478 this.on_visibility_change = event_options ? event_options.on_visibility_change : null; 55479 55480 55481 55482// public methods 55483 this.resize = function() { 55484 55485 try { 55486 recalculate_layout(); 55487 if(the_main_heatmap) { 55488 the_main_heatmap.resize(); 55489 var scale = the_main_heatmap.get_scale(); 55490 the_image_header.set_scale(scale.width); 55491 } 55492 } catch (e) { 55493 // do nothing for possible IE errors 55494 } 55495 55496 } 55497 55498 this.init = function() { 55499 do_init(); 55500 } 55501 55502 this.set_zoom = function(new_zoom) { 55503 if(the_status_bar) 55504 the_status_bar.set_zoom(new_zoom); 55505 } 55506 55507 this.set_grouping = function(new_grouping) { 55508 if(the_grouping_widget) 55509 the_grouping_widget.set_grouping(new_grouping); 55510 } 55511 55512 this.set_status_label = function(label, border_color) { 55513 the_upper_statusbar.set_status_label(label, border_color ? border_color : 'fff'); 55514 } 55515 55516 this.attach_session = function(new_session) { 55517 var current_scale = the_main_heatmap.get_scale(); 55518 var current_width = (this.image_data.data_columns * this.image_data.heatmap_format.pixel_width) * current_scale.width; 55519 55520 this.image_data.attach_session(new_session); 55521 the_image_header.column_count = this.image_data.data_columns; 55522 the_image_header.refresh(this.image_data.get_header_url()); 55523 55524 var new_scale_width = current_width/(this.image_data.data_columns * this.image_data.heatmap_format.pixel_width); 55525 the_image_header.set_scale(new_scale_width); 55526 the_main_heatmap.set_scale(new_scale_width, current_scale.height); 55527 55528 the_main_heatmap.resize(); 55529 update_data(null, true); 55530 55531 heatmap_scale = the_main_heatmap.get_scale(); 55532 } 55533 55534 this.show_busy = function(busy) { 55535 this.container.show_busy(busy); 55536 } 55537 55538 this.set_column_sort_icon = function(column_label, sort) { 55539 the_meta_table.set_column_sort_icon(column_label, sort); 55540 } 55541 55542 this.set_selections = function(new_selections) { 55543 the_meta_table.selections = new_selections; 55544 update_data(null, true); 55545 } 55546 55547 this.set_colormap_range = function(lower, upper) { 55548 the_status_bar.set_range(lower, upper); 55549 do_level_change(lower, upper); 55550 }; 55551 55552 this.set_colormap = function(name) { 55553 the_status_bar.set_colormap(name); 55554 }; 55555 55556 this.get_visibility = function() { 55557 return heatmap_visible; 55558 }; 55559 55560 this.set_visibility = function(visible) { 55561 heatmap_visible = visible; 55562 var control_mask = HM_CONTROL_ZOOM | HM_CONTROL_RESOLUTION | HM_CONTROL_COLOR_MAP | HM_CONTROL_CLEAR_SELECTION | HM_CONTROL_TOGGLE_BUTTON; 55563 55564 if (heatmap_visible) { 55565 $j(self.container).show(); 55566 55567 // set the hm scale back to what it was before we hid it. If we don't do this, 55568 // the layout code will recalculate based on it's 0 height. 55569 if (heatmap_previous_scale) 55570 the_main_heatmap.set_scale(heatmap_previous_scale.width,heatmap_previous_scale.height); 55571 55572 final_layout(); 55573 55574 the_status_bar.set_control_visibility(control_mask,true); 55575 } else { 55576 // keep track of the scale of the heatmap before we resize it to 0 55577 heatmap_previous_scale = the_main_heatmap.get_scale(); 55578 55579 $j(self.container).hide(); 55580 55581 the_status_bar.set_control_visibility(control_mask,false); 55582 } 55583 }; 55584 55585// private properties 55586 var self = this; 55587 55588 // the html elements that are managed by this controller 55589 var the_heatmap_container = null; 55590 var the_data_table_container = null; 55591 var the_header_container = null; 55592 var the_tiled_window_container = null; 55593 var the_status_row = null; 55594 var the_upper_status_row = null; 55595 var the_download_row = null; 55596 55597 var lower_row = null; 55598 var upper_row = null; 55599 55600 // the javascript components that are managed by this controller 55601 var the_main_heatmap = null; 55602 var the_image_header = null; 55603 var the_meta_table = null; 55604 var the_status_bar = null; 55605 var the_grouping_widget = null; 55606 55607 var the_upper_statusbar = null; 55608 var the_table = null; 55609 55610 // some global state vars 55611 var current_row = -1; 55612 var expanded = true; 55613 var heatmap_aspect_ratio = 1.0; 55614 var heatmap_scale = 1; 55615 var scaffold_complete = false;
55616 var scrolling = false; 55617 var update_image_timer = 0; 55618 var images_loaded = false; 55619 var init_done = false; 55620 var level_range = [0,0]; 55621 var heatmap_previous_scale = null; //for keeping track of scale before hiding 55622 var heatmap_visible = true; 55623 55624 55625// private methods 55626 55627 function build_scaffold() { 55628 the_table = new Element('div', {'class':'scaffold_table'}); 55629 55630 upper_row = new Element('div', {'class':'upper_scaffold_row'}); 55631 the_table.appendChild(upper_row); 55632 55633 the_status_row = new Element('div', {'class':'status_row'}); 55634 the_upper_status_row = new Element('div', {'class':'upper_status_row'}); 55635 55636 //the_download_row = new Element('div', {'id':'the_download_row', 'class':'download_block'}); 55637 the_download_row = new Element('div', {'class':'download_block'}); 55638 55639 lower_row = new Element('div', {'class':'lower_scaffold_row'}); 55640 the_table.appendChild(the_upper_status_row); 55641 the_table.appendChild(lower_row); 55642 the_table.appendChild(the_status_row); 55643 the_table.appendChild(the_download_row); 55644 55645 the_data_table_container = new Element('div', {'class':'meta_table_cell'}); 55646 lower_row.appendChild(the_data_table_container); 55647 55648 the_heatmap_container = new Element('div', {'class':'heatmap_container'}); 55649 the_header_container = new Element('div', {'class':'header_image_container'}); 55650 the_tiled_window_container = new Element('div', {'class':'tile_window_container'}); 55651 55652 self.container.appendChild(the_table); 55653 lower_row.appendChild(the_heatmap_container); 55654 //self.container.appendChild(the_heatmap_container); 55655 the_heatmap_container.appendChild(the_header_container); 55656 the_heatmap_container.appendChild(the_tiled_window_container); 55657 55658 var header_container_width = the_header_container.getWidth() - self.scrollbar_width; 55659 the_header_container.setStyle({width:header_container_width}); 55660 55661 scaffold_complete = true; 55662 } 55663 55664 55665 55666 function build_widgets() { 55667//TODO: row_height = 13? Really? 13? 55668 var row_height = 13; 55669 var column_width = 0; // a column width of 0 indicates that the width 55670 // should be scaled to fit the container. 55671 55672 the_upper_statusbar = new status_bar(the_upper_status_row, {status_label:self.status_label}, null); 55673 55674 var control_bar_options = { 55675 colormap_set:self.colormap_set, 55676 ramp_url:self.ramp_url, 55677 control_mask:self.control_mask, 55678 // resolution_set:["Coarse","Averaged Samples","Samples"], 55679 // control_defaults:{HM_CONTROL_RESOLUTION:"Samples", HM_CONTROL_RESTRICT_DOMAINS:self.default_restriction} 55680 resolution_set:self.resolutions, 55681 control_defaults:{HM_CONTROL_RESOLUTION:self.default_resolution, HM_CONTROL_RESTRICT_DOMAINS:self.default_restriction}, 55682 display_donors:self.display_donors, 55683 display_structures:self.display_structures, 55684 show_display_filters:self.show_display_filters, 55685 display_donor_groups:self.display_donor_groups, 55686 display_donor_filter_type:self.display_donor_filter_type, 55687 display_structure_groups:self.display_structure_groups, 55688 display_structure_filter_type:self.display_structure_filter_type, 55689 toggle_button_visible:(self.user_selection_mode != HM_VIEW_SELECTION_OFF), 55690 ontology_options:self.ontology_options 55691 }; 55692 var control_bar_events = { 55693 on_zoom:do_zoom, 55694 on_zoom_reset:do_zoom_reset, 55695 on_level_change:do_level_change, 55696 on_colormap_change:do_colormap_change, 55697 on_restriction_change:do_restriction_change, 55698 on_resolution_change:do_resolution_change, 55699 on_display_change:do_display_change, 55700 on_view_selection_click:do_view_selection_click, 55701 on_clear_click:do_clear_click, 55702 on_toggle_click:do_toggle_click 55703 }; 55704 the_status_bar = new heatmap_status_bar(the_status_row, control_bar_options, control_bar_events); 55705 55706 var data_table_events = { 55707 on_column_click:do_column_click, 55708 on_select_click:do_select_click, 55709 on_row_click:do_row_click, 55710 on_row_over:do_row_over 55711 }; 55712 var data_table_options = { 55713 show_selected:self.show_selected, 55714 selections:self.selections 55715 }; 55716 the_meta_table = new tiled_data_table(the_data_table_container, self.table_schema, row_height, data_table_events, data_table_options); 55717 55718 55719 the_image_header = new header_image(the_header_container, 55720 self.image_data.get_header_url(null), 55721 self.the_row_data.number_of_columns, // column count 55722 column_width, // initial scale 55723 0 // initial position 55724 ); 55725 55726 var heatmap_event_options = {on_position_change:do_heatmap_position_change, 55727 on_layout_change:do_heatmap_layout_change, 55728 on_mouse_over:do_heatmap_mouseover, 55729 on_mouse_out:do_heatmap_mouseout, 55730 on_mouse_click:do_heatmap_click, 55731 on_load:do_tiled_image_load}; 55732 the_main_heatmap = new tiled_image_window(the_tiled_window_container, 55733 self.image_data, 55734 {scale_width:column_width, scale_height:row_height}, 55735 {drag_content:true, scrollbar_width:self.scrollbar_width}, 55736 heatmap_event_options); 55737 55738 the_grouping_widget = new grouping_widget(lower_row, 55739 {groupings:self.groupings, default_grouping:self.default_grouping, tool_tip:self.tool_tip}, 55740 {on_grouping_change:do_grouping_change} 55741 ); 55742 55743 new data_download(the_download_row, self.heatmap_download_service, self.the_row_data, self.image_data, {download_service_data_set:self.download_service_data_set}); 55744 55745 heatmap_scale = the_main_heatmap.get_scale(); 55746 heatmap_aspect_ratio = heatmap_scale.height/heatmap_scale.width; 55747 55748 } 55749 55750 function do_select_click(sender, selected_id, selected_state) { 55751 if(self.on_select_click) 55752 self.on_select_click(self, selected_id, selected_state); 55753 } 55754 55755 function do_column_click(sender, column_label) { 55756 if(self.on_column_click) 55757 self.on_column_click(self, column_label); 55758 } 55759 55760 function do_tiled_image_load(sender) { 55761 images_loaded = true; 55762 55763 if(self.on_tiled_image_load) 55764 self.on_tiled_image_load(self); 55765 55766 check_complete(); 55767 } 55768 55769 function do_grouping_change(new_grouping) { 55770 if(self.on_grouping_change) 55771 self.on_grouping_change(self, new_grouping); 55772 } 55773 55774 function do_restriction_change(restrict) { 55775 if(self.on_restriction_change) 55776 self.on_restriction_change(self, restrict); 55777 } 55778 55779 function do_view_selection_click(sender, mode) { 55780 if(self.on_view_selection_click) 55781 self.on_view_selection_click(sender, mode); 55782 } 55783 55784 function do_clear_click() { 55785 if(self.on_clear_click) 55786 self.on_clear_click(self); 55787 } 55788 55789 function do_toggle_click() { 55790 self.set_visibility(!heatmap_visible); 55791 55792 if (self.on_visibility_change) 55793 self.on_visibility_change(self); 55794 }; 55795 55796 function do_colormap_change(sender, name, normalized, lower, upper) { 55797 var opts = self.image_data.get_format_options(); 55798 55799 // if(normalized == 'true') { 55800 if(normalized == 'true' || normalized == true) { 55801 opts.normalize = true;
55802 opts.colormap = name; 55803 } else { 55804 delete opts.normalize; 55805 delete opts.colormap; 55806 } 55807 opts.expr_window = lower + "," + upper; 55808 55809 self.image_data.set_format_options(opts); 55810 the_main_heatmap.refresh(); 55811 55812 if(self.on_colormap_change) 55813 self.on_colormap_change(self, name, normalized, lower, upper); 55814 } 55815 55816 55817 // var level_range = [0,0]; 55818 function do_level_change(lower, upper) { 55819 level_range= [lower, upper]; 55820 update_heatmap_image(); 55821 55822 if(self.on_level_change) 55823 self.on_level_change(self, lower, upper); 55824 } 55825 55826 function update_heatmap_image() { 55827 if (update_image_timer == 0) 55828 update_image_timer = setTimeout(do_update_image, 200); 55829 } 55830 55831 function do_update_image() { 55832 if (update_image_timer != 0) 55833 clearTimeout(update_image_timer); 55834 update_image_timer = 0; 55835 55836 var opts = self.image_data.get_format_options(); 55837 opts.expr_window = level_range[0] + "," + level_range[1]; 55838 the_main_heatmap.refresh(); 55839 self.image_data.set_format_options(opts); 55840 } 55841 55842 function do_zoom(sender, new_zoom) { 55843 if(!the_main_heatmap) 55844 return; 55845 55846 var zoom_val = new_zoom; 55847 var new_pixel_width; 55848 var new_pixel_height; 55849 55850 // the slider range is -20 to 20 55851 if(new_zoom > 0) 55852 new_zoom = (new_zoom/5) + 1; 55853 else if(new_zoom < 0) 55854 new_zoom = -1/((new_zoom/5)-1); 55855 else 55856 new_zoom = 1; 55857 55858 var physical_width = (self.image_data.data_columns * self.image_data.heatmap_format.pixel_width); 55859 var viewport = the_main_heatmap.container.getDimensions(); 55860 var viewport_scaled_width = viewport.width * new_zoom; 55861 var current_scale = the_main_heatmap.get_scale(); 55862 var aspect_ratio = current_scale.height/current_scale.width; 55863 55864 var new_scale = viewport_scaled_width/physical_width; 55865 55866 var new_scale_width = new_scale; 55867 var new_scale_height = new_scale * aspect_ratio; 55868 55869 new_pixel_width = Math.max(0.0, new_scale_width); 55870 new_pixel_height = Math.max(0.0, new_scale_height); 55871 55872 the_main_heatmap.set_scale(new_pixel_width, new_pixel_height); 55873 55874 if(self.on_zoom) 55875 self.on_zoom(self, zoom_val); 55876 } 55877 55878 function do_resolution_change(sender, new_res) { 55879 if(self.on_resolution_change) 55880 self.on_resolution_change(self, new_res); 55881 } 55882 55883 function do_display_change(sender, new_display_donors, new_display_structures, restriction_is_on) { 55884 if(self.on_display_change){ 55885 self.on_display_change(self, new_display_donors, new_display_structures, restriction_is_on); 55886 55887 } 55888 } 55889 55890 function do_zoom_reset() { 55891 55892 } 55893 55894 function adjust_container() { 55895 // if requested, adjust the size of the container to fit in the screen 55896 if(!self.fit_to_page) 55897 return; 55898 55899 if(!self.fit_upper_element_id || !self.fit_lower_element_ids) 55900 return; 55901 55902 var upper_element = $(self.fit_upper_element_id); 55903 var lower_elements = []; 55904 for(var i = 0; i < self.fit_lower_element_ids.length; i++) 55905 lower_elements[i] = $(self.fit_lower_element_ids[i]); 55906 55907 // get the of the upper element 55908 var upper_dims = upper_element.getDimensions(); 55909 var upper_pos = upper_element.cumulativeOffset(); 55910 55911 var upper_bottom = (upper_pos.top + upper_dims.height); 55912 var top = upper_element.getStyle('marginTop'); 55913 var bottom = upper_element.getStyle('marginBottom'); 55914 upper_bottom += new Number((top ? top : '0').replace('px','')); 55915 upper_bottom += new Number((bottom ? bottom : '0').replace('px','')); 55916 55917 // get the size of the lower element(s) 55918 var lower_height = 0; 55919 for(var j = 0; j < lower_elements.length; j++) { 55920 lower_height += lower_elements[j].getHeight(); 55921 top = lower_elements[j].getStyle('marginTop'); 55922 bottom = lower_elements[j].getStyle('marginBottom'); 55923 lower_height += new Number((top ? top : '0').replace('px','')); 55924 lower_height += new Number((bottom ? bottom : '0').replace('px','')); 55925 } 55926 55927 // get the size of the browser screen 55928 var screen_dims = self.container.getDimensions(); 55929 //var screen_dims = $(document.body).getDimensions(); 55930 55931 55932 // figure the maximum height for the container, fitting between the bottom of the 55933 // upper element, leaving enough room for the lower element. 55934 top = self.container.getStyle('marginTop'); 55935 bottom = self.container.getStyle('marginBottom');
55936 var container_padding = new Number((top ? top : '0').replace('px','')) + new Number((bottom ? bottom : '0').replace('px','')) 55937 55938 //var max_height = Math.max(screen_dims.height - upper_bottom - lower_height - container_padding - 55, self.min_height); 55939 var max_height = (self.enable_cart_view && (self.user_selection_mode != HM_VIEW_SELECTION_OFF)) ? 55940 (screen_dims.height - upper_bottom - lower_height)/2 - container_padding - 55 : 55941 screen_dims.height - upper_bottom - lower_height - container_padding - 55; 55942 55943 max_height = Math.max(max_height, self.min_height); 55944 55945// self.container.setStyle({height:max_height+'px'}); 55946// if (heatmap_visible) 55947 self.container.setStyle({height:max_height+'px'}); 55948// else 55949//] self.container.setStyle({height:'20px'}); 55950 } 55951 55952 function recalculate_layout() { 55953 // Sometimes getDimensions() gets back 0s in IE, causing trouble setting negative width down the road 55954 // Instead of catching the exceptions, we try adjusting the width to 0 in these cases 55955 55956 if(!scaffold_complete) 55957 return; 55958 55959 // TODO: Really? 6? 55960 var view_padding = 6; 55961 var data_table_visible_width = the_meta_table ? the_meta_table.get_visible_column_width() + view_padding : self.first_column_width; 55962 55963 adjust_container(); 55964 // the view is divided into an upper and lower row. The upper 55965 // row is fixed height and contains the thumbnail and detail info pane. 55966 // The lower row is liquid and contains the main heatmap and data table. 55967 var container_pos = self.container.cumulativeOffset(); 55968 var container_dims = self.container.getDimensions(); 55969 55970 if(self.heatmap_only) { 55971 upper_row.setStyle({height:view_padding+'px'}); 55972 } 55973 var upper_row_dims = upper_row.getDimensions(); 55974 var upper_status_dims = the_upper_status_row.getDimensions(); 55975 upper_row_dims.height += upper_status_dims.height; 55976 55977 // The lower row should take all the vertical space not occupied by the 55978 // upper row. 55979 var status_row_dims = the_status_row.getDimensions(); 55980 55981 // var lower_row_height = container_dims.height - (upper_row_dims.height + status_row_dims.height + view_padding) - view_padding; 55982 var lower_row_height = Math.max(container_dims.height - (upper_row_dims.height + status_row_dims.height + view_padding) - 2*view_padding,0); 55983 55984 lower_row.setStyle({height:lower_row_height+'px'}); 55985 55986 // the heatmap container should be the same height as the lower row 55987 // of the scaffold table, and the same width as the container, minus 55988 // the width of the first column of the data table. 55989 var heatmap_height = Math.max(lower_row_height - view_padding,0); 55990 var heatmap_width = (container_dims.width - data_table_visible_width - view_padding > 0 ) ? container_dims.width - data_table_visible_width - view_padding : 0; 55991 55992 var heatmap_left = /*container_pos.left +*/ data_table_visible_width; 55993 var heatmap_top = (container_pos.top + upper_row_dims.height + view_padding); // TODO: Why 5? 55994 //var pos_style = {top:heatmap_top+'px', left:container_pos.left+'px', width:heatmap_width+'px', height:(heatmap_height)+'px'}; 55995 var pos_style = {top:heatmap_top+'px', left:heatmap_left+'px', width:heatmap_width+'px', height:(heatmap_height)+'px'}; 55996 the_heatmap_container.setStyle(pos_style); 55997 // the sub-compoennts of the heatmap are the tiled image window and 55998 // the header image. The header image height is set via CSS, the tiled image 55999 // window takes up all the remaining vertical space. 56000 56001 //var tiled_window_height = heatmap_height - the_header_container.getHeight() - self.scrollbar_width; 56002 //var tiled_window_height = Math.max(heatmap_height - the_header_container.getHeight() - self.scrollbar_width,0); 56003 var tiled_window_height = Math.max(heatmap_height - the_header_container.getHeight() - self.scrollbar_width - view_padding,0); 56004 var tiled_window_width = ( heatmap_width - self.scrollbar_width > 0 ) ? heatmap_width - self.scrollbar_width : 0; 56005 the_tiled_window_container.setStyle({height:tiled_window_height+'px', 56006 width:tiled_window_width+'px'}); 56007 56008 56009 //var header_container_width = the_header_container.getWidth() - self.scrollbar_width; 56010 the_header_container.setStyle({width:tiled_window_width+'px'}); 56011 56012 // the data table should be sized & positioned (absolutely?) 56013 //var data_table_pos = {top:heatmap_top+'px', left:'0px', width:(container_dims.width-(2*view_padding)-2)+'px', height:(lower_row_height-self.scrollbar_width)+'px'} ; 56014 var data_table_width = ( container_dims.width-(2*view_padding)-2 > 0 ) ? container_dims.width-(2*view_padding)-2 : 0; 56015 56016 var data_table_height = Math.max(lower_row_height-view_padding,0); 56017 var data_table_pos = {top:heatmap_top+'px', left:'0px', width:data_table_width+'px', height:data_table_height+'px'} ; 56018 56019 the_data_table_container.setStyle(data_table_pos); 56020 56021 if(the_grouping_widget) 56022 the_grouping_widget.reposition(); 56023 56024 self.container.rebusy(); 56025 56026 } 56027 56028 56029 56030 function do_heatmap_position_change(sender, position) { 56031 the_image_header.set_position(-(position.column)); 56032 update_data(position, false); 56033 } 56034 56035 function do_heatmap_layout_change(sender, layout) { 56036 56037 if(the_main_heatmap) { 56038 var scale = the_main_heatmap.get_scale(); 56039 the_image_header.set_scale(scale.width); 56040 the_image_header.set_position(-(layout.column)); 56041 the_meta_table.set_scale(scale.height); 56042 update_data({column:layout.column, row:layout.row}, true); 56043 } 56044 } 56045 56046 function do_thumbnail_nav_move(sender, position) { 56047 if(scrolling) 56048 return; 56049 56050 trace("thumb nav: " + inspect(position)); 56051 the_main_heatmap.navigate_to((position.left/thumb_scale), (position.top/thumb_scale)); 56052 } 56053 56054 function refresh_expression_value_on_status_bar(sender, data) 56055 { 56056 var msg = "Expression - z-score: "+data.z_score + ", log2 intensity: " + data.expression_level; 56057 var color = 'fff'; 56058 the_upper_statusbar.set_status_real_first(msg, color); 56059 the_upper_statusbar.show_click_marker_symbol(); 56060 56061 56062 } 56063 56064 function receive_cell_detail(response) { 56065 var data = response.responseJSON; 56066 if(self.on_cell_click) 56067 self.on_cell_click(self, data); 56068 if(self.show_expression_value_on_status_bar) 56069 refresh_expression_value_on_status_bar(self, data); 56070 } 56071 56072 function update_data(pos, force) { 56073 var position = pos ? pos : the_main_heatmap.get_position(); 56074 position.row = Math.ceil(position.row); 56075 position.column = Math.ceil(position.column); 56076 56077 if((position.row != current_row) || force) { 56078 current_row = position.row; 56079 var size = Math.min(self.the_row_data.number_of_rows, Math.ceil(the_main_heatmap.get_data_size().height)); 56080 56081 // self.show_busy(true); // a change made in HBA for the IE8 busy indicator and ajax async 56082 var data = self.the_row_data.get_probe_list(position.row, size); 56083 if(self.show_row_position) { 56084 var upper = Math.min((position.row + size), self.the_row_data.number_of_rows); 56085 self.set_status_label((position.row + 1) + " - " + upper + " of " + self.the_row_data.number_of_rows); 56086 } 56087 the_meta_table.set_data(data, position.row); 56088 // self.show_busy(false); // see above comment 56089 } 56090 } 56091 56092 function do_heatmap_click(sender, position) { 56093 self.the_row_data.get_cell_data_detail(position.column, position.row, receive_cell_detail); 56094 the_image_header.set_click_marker_client_x(position.clientX);
56095 the_meta_table.select_row(position.row); 56096 } 56097 56098 function do_row_click(sender, row) { 56099 var data = self.the_row_data.get_cell_data_brief(0, row); 56100 if(self.on_row_click) 56101 self.on_row_click(self, row, data); 56102 } 56103 56104 function do_row_over(sender, row) { 56105 var data = self.the_row_data.get_cell_data_brief(0, row); 56106 56107 var msg = data.column ? data.column.structure.name + ' (' + data.column.structure.abbreviation + ')' : ''; 56108 var color = data.column ? data.column.structure.color : '000'; 56109 the_upper_statusbar.set_status_wide(msg, color); 56110 56111 if ( !self.hide_donor_age_display ) 56112 { 56113 msg = data.column ? ' Age: ' + data.column.donor.age : ''; 56114 color = 'fff'; 56115 the_upper_statusbar.set_status_narrow(msg, color); 56116 } 56117 56118 msg = data.column ? 'Donor ' + data.column.donor.name : ''; 56119 color = data.column ? data.column.donor.color : '000'; 56120 the_upper_statusbar.set_status_first(msg, color); 56121 56122 if(self.on_row_over) 56123 self.on_row_over(self, row, data); 56124 } 56125 56126 function do_heatmap_mouseover(sender, position) { 56127 the_image_header.set_marker_client_x(position.clientX); 56128 the_meta_table.highlight_row(position.row-position.offset_row); 56129 56130 var data = self.the_row_data.get_cell_data_brief(position.column, position.row); 56131 56132 var msg = data.column ? data.column.structure.name + ' (' + data.column.structure.abbreviation + ')' : ''; 56133 var color = data.column ? data.column.structure.color : '000'; 56134 the_upper_statusbar.set_status_wide(msg, color); 56135 56136 // commented out in ahba 56137 //msg = data.column ? data.column.top_level_structure.abbreviation : ''; 56138 //color = data.column ? data.column.top_level_structure.color : 'black'; 56139 //the_upper_statusbar.set_status_narrow(msg, color); 56140 56141 // by default, the option is false, and this info will be displayed 56142 // when the ahba heatmap_view is configured, an extra option is passed in 56143 if ( !self.hide_donor_age_display ) 56144 { 56145 msg = data.column ? ' Age: ' + data.column.donor.age : ''; 56146 color = 'fff'; 56147 the_upper_statusbar.set_status_narrow(msg, color); 56148 } 56149 56150 msg = data.column ? 'Donor ' + data.column.donor.name : ''; 56151 color = data.column ? data.column.donor.color : '000'; 56152 the_upper_statusbar.set_status_first(msg, color); 56153 56154 if(self.on_heatmap_mouseover) 56155 self.on_heatmap_mouseover(self, position, data); 56156 } 56157 56158 function do_heatmap_mouseout(sender, position) { 56159 //the_image_header.set_marker_client_x(position.clientX); 56160 the_meta_table.highlight_none(); 56161 56162 var data = self.the_row_data.get_cell_data_brief(position.column, position.row); 56163 56164 if(self.on_heatmap_mouseout) 56165 self.on_heatmap_mouseout(self, position, data); 56166 } 56167 56168 function final_layout() { 56169 recalculate_layout(); 56170 self.resize(); 56171 the_main_heatmap.lock_aspect_ratio(); 56172 heatmap_scale = the_main_heatmap.get_scale(); 56173 heatmap_aspect_ratio = heatmap_scale.height/heatmap_scale.width; 56174 } 56175 56176 function set_initial_values() { 56177 if(self.initial_zoom) 56178 the_status_bar.set_zoom(self.initial_zoom); 56179 56180 self.set_visibility(self.default_visibility); 56181 } 56182 56183 function check_complete() { 56184 if(init_done && images_loaded) 56185 if(self.on_initialized) 56186 self.on_initialized(self); 56187 } 56188 56189 function do_init() { 56190 56191 try { 56192 build_scaffold(); 56193 56194 recalculate_layout(); 56195 build_widgets(); 56196 // The layout needs to be 56197 // mostly done in order for the widgets to construct themselves to the right size 56198 // and shape. But then we need to know here what that final size & shape is going 56199 // to be in order to place the individual widgets properly in relation to each other. 56200 // So; the first time the layout is calculated we use some reasonable defaults, then 56201 // once everybody has constructed themselves we go back and nudge everybody in to 56202 // their final positions. 56203 final_layout(); 56204 update_data(null, false); 56205 self.index = __heatmap_view_count__++; 56206 56207 set_initial_values(); 56208 56209 init_done = true; 56210 check_complete(); 56211 56212 } catch(e) { 56213 alert("error initializing the heat map view: " + e.message + "\n\n" + e.stack); 56214 } 56215 } 56216 56217 56218 56219} 56220 56221 56222 56223 56224; 56225 var current_search_term = ""; 56226 var tree = null; 56227// var tree_positioned = false;
56228 var current_search_index = 0; 56229 var structure_nodes = null; 56230 var selected_index = null; 56231 var target_ids = []; 56232 var contrast_ids = []; 56233 var current_diff_input = ''; 56234 56235 56236function inspect(obj) { 56237 var s = ""; 56238 for(key in obj) { 56239 s += "\nkey: " + key + ", value: " + obj[key]; 56240 } 56241 return(s); 56242} 56243 56244var current_input_elt = null; 56245 56246function doSearch(sender) { 56247 hideTree(); 56248 searchStrings = current_input_elt.value.split(/\s*;\s*/); 56249 searchStrings.pop(); 56250 searchStrings.push(structure_nodes[selected_index].innerHTML); 56251 current_input_elt.value = searchStrings.join(';'); 56252} 56253 56254function doTypeAhead(evt) { 56255 var sender = (evt.target) ? evt.target : evt.srcElement; 56256 current_input_elt = sender; 56257 56258 if(evt.keyCode == 13) { 56259 doSearch(sender); 56260 return; 56261 } 56262 // down arrow 56263 if(evt.keyCode == 40) { 56264 doTreeNav(evt) 56265 return; 56266 } 56267 // up arrow 56268 if(evt.keyCode == 38) { 56269 doTreeNav(evt) 56270 return; 56271 } 56272 56273 positionTree(sender); 56274 showTree(); 56275 searchStrings = sender.value.split(/\s*;\s*/); 56276 searchString = searchStrings.pop(); 56277 56278 search(searchString); 56279} 56280 56281 56282function showTree() { 56283 $('ontology_container').show(); 56284} 56285 56286function hideTree() { 56287 $('ontology_container').hide(); 56288} 56289 56290function positionTree(input) { 56291 current_diff_input = input.id; 56292 56293 var custom_offset = input.getAttribute('custom_offset_left'); 56294 var offset_left = custom_offset ? new Number(custom_offset) : -2; 56295 tree.clonePosition(input, {setHeight:false, setWidth: false, offsetTop:16, offsetLeft:offset_left}); 56296 56297 tree_positioned = true; 56298} 56299 56300function doBlur() { 56301 hideTree(); 56302} 56303 56304function search(term) { 56305 56306 if(term.length == 0) { 56307 selectNone(); 56308 return; 56309 } 56310 56311 var pat = "^"+term; 56312 var reg = new RegExp(pat, 'i'); 56313 reg.compile(pat,"i"); 56314 56315 for(var i = 0; i < structure_nodes_length; i++) { 56316 if(reg.test(structure_nodes[i].innerHTML)) { 56317 selectStructure(i); 56318 break; 56319 } 56320 } 56321} 56322 56323function moveTo(container, element){ 56324 Position.prepare(); 56325 container_y = Position.cumulativeOffset($(container))[1] 56326 element_y = Position.cumulativeOffset($(element))[1] 56327 new Effect.Scroll(container, {x:0, y:(element_y-container_y)}); 56328 return false; 56329} 56330 56331function selectStructure(index) { 56332 if(selected_index) 56333 structure_nodes[selected_index].removeClassName('selected'); 56334 56335 structure_nodes[index].addClassName('selected'); 56336 selected_index = index; 56337 56338 moveTo(tree, structure_nodes[index]); 56339} 56340 56341function selectStructureById(id) { 56342 for(var i = 0; i < structure_nodes_length; i++) { 56343 if(structure_nodes[i].id == id) { 56344 selectStructure(i); 56345 break; 56346 } 56347 } 56348} 56349 56350function selectNone() { 56351 if(selected_index) 56352 structure_nodes[selected_index].removeClassName('selected'); 56353} 56354 56355function doTreeClick(evt) { 56356 var sender = (evt.target) ? evt.target : evt.srcElement; 56357 selectStructureById(sender.id); 56358 doSearch(sender); 56359} 56360 56361function doTreeNav(evt) { 56362 if(evt.keyCode == 40) { 56363 if(selected_index < structure_nodes_length - 1) 56364 selectStructure(selected_index + 1); 56365 } else if(evt.keyCode == 38) { 56366 if(selected_index > 0) 56367 selectStructure(selected_index - 1); 56368 } 56369} 56370 56371function structure_init(search_box_id) { 56372 var search_box = $(search_box_id); 56373 search_box.observe('keyup', doTypeAhead); 56374} 56375 56376function ontology_container_init() { 56377 tree = $('ontology_container'); 56378 tree.observe('click', doTreeClick); 56379 tree.observe('keyup', doTreeNav); 56380 tree.observe('blur', doBlur); 56381 56382 structure_nodes = tree.select('.tnc'); 56383 structure_nodes_length = structure_nodes.length; 56384} 56385 56386Effect.Scroll = Class.create(); 56387Object.extend(Object.extend(Effect.Scroll.prototype, Effect.Base.prototype), { 56388 initialize: function(element) { 56389 this.element = $(element); 56390 var options = Object.extend({ 56391 x: 0, 56392 y: 0, 56393 mode: 'absolute' 56394 } , arguments[1] || {} ); 56395 this.start(options); 56396 }, 56397 setup: function() { 56398 if (this.options.continuous && !this.element._ext ) { 56399 this.element.cleanWhitespace(); 56400 this.element._ext=true; 56401 this.element.appendChild(this.element.firstChild); 56402 } 56403 56404 this.originalLeft=this.element.scrollLeft; 56405 this.originalTop=this.element.scrollTop; 56406 56407 if(this.options.mode == 'absolute') { 56408 this.options.x -= this.originalLeft; 56409 this.options.y -= this.originalTop; 56410 } else { 56411 56412 } 56413 }, 56414 update: function(position) { 56415 this.element.scrollLeft = this.options.x * position + this.originalLeft; 56416 this.element.scrollTop = this.options.y * position + this.originalTop; 56417 } 56418}); 56419// REQUIRED: 56420// prototype.js, controls.js, slider.js. 56421 56422var __scroller_count = 0; 56423function next_scroller_id() { return(__scroller_count++); } 56424 56425// creates a scrollbar element, unattached to any window content. 56426// 56427// params: 56428// container: the html ID of the parent node. Note that the scroller 56429// has no effect on the parent node as it is scrolled. The 56430// parent node is simply the DOM container for the DOM elements 56431// of the scroller. 56432// orientation: 'horizontal' or 'vertical' 56433// options: 56434// { 56435// visible: boolean 56436// initial_position: a float, 0.0 - 1.0. 56437// } 56438// event_options: 56439// { 56440// on_move: function_reference(sender, location) 56441// sender: a reference to this scroller 56442// location: a percentage position, given as a float 0.0 - 1.0. 56443// Called with each movement of the scroll thumb 56444// 56445// on_change: function_reference(sender, location) 56446// sender: a reference to this scroller 56447// location: a percentage position, given as a float 0.0 - 1.0. 56448// Called when the scroll thumb is released. 56449// } 56450function scroller(container, orientation, options, event_options) 56451{ 56452// public properties 56453 this.container = $(container); 56454 this.orientation = orientation; 56455 this.initial_position = options ? (options.initial_position ? options.initial_position : 0) : 0; 56456 this.initial_thumb_size = options ? (options.initial_thumb_size ? options.initial_thumb_size : 24) : 24; 56457 this.initial_visiblity = options ? (options.visible ? options.visible : false) : false;
56458 this.id = 'nil'; 56459 56460// event hooks 56461 this.on_move = event_options ? (event_options.on_move ? event_options.on_move : null) : null; 56462 this.on_change = event_options ? (event_options.on_change ? event_options.on_change : null) : null; 56463 this.on_button1_click = event_options ? (event_options.on_button1_click ? event_options.on_button1_click : null) : null; 56464 this.on_button2_click = event_options ? (event_options.on_button2_click ? event_options.on_button2_click : null) : null; 56465 56466 56467// public methods 56468 this.set_position = function(new_position) { 56469 do_set_position(new_position); 56470 } 56471 56472 this.get_position = function() { 56473 return(do_get_position()); 56474 } 56475 56476 this.show = function(visible) { 56477 set_visibility(visible); 56478 } 56479 56480 this.visible = function() { 56481 return(this.container.visible); 56482 } 56483 56484 // the new size should be a number representing the percentage 56485 // of scrollbar space occupied by the thumb 56486 this.set_thumb_size = function(new_size) { 56487 set_thumb_size(new_size); 56488 } 56489 56490 this.slider = function() { 56491 return(the_slider); 56492 } 56493 56494 this.resize = function() { 56495 do_resize(); 56496 } 56497 56498// private properties 56499 var self = this; 56500 var the_slider = null; 56501 var the_track = null; 56502 var the_handle = null; 56503 var the_wrapper = null; 56504 var button1 = null; 56505 var button2 = null; 56506 56507 56508// private methods 56509 function build_scaffold() { 56510 var id = next_scroller_id(); 56511 self.id = 'scroller_'+id; 56512 56513 var orient_class = '_scroll_' + self.orientation; 56514 the_wrapper = new Element('div', {id:self.id, 'className':'wrapper'+orient_class}); 56515 button1 = new Element('div',{id:'button1_'+self.id, 'className':'scroll_button_1 button'+orient_class }); 56516 button2 = new Element('div',{id:'button2_'+self.id, 'className':'scroll_button_2 button'+orient_class}); 56517 56518 var track_id = 'track_' + id; 56519 var handle_id = 'handle_' + id; 56520 the_track = new Element('div', {id:track_id, 'class':'scroller_track'}); 56521 the_handle = new Element('div', {id:handle_id, 'class':self.orientation+'_scroller_handle'}); 56522 56523 self.container.appendChild(the_wrapper); 56524 the_wrapper.appendChild(button1); 56525 the_wrapper.appendChild(the_track); 56526 the_wrapper.appendChild(button2); 56527 the_track.appendChild(the_handle); 56528 56529 var grip = (self.orientation == 'vertical') ? 'ui-icon-grip-dotted-horizontal' : 'ui-icon-grip-dotted-vertical'; 56530 $j(the_handle).button({text:false, icons:{secondary:grip}}); 56531 56532 var button1_icon = (self.orientation == 'vertical') ? 'ui-icon-triangle-1-n' : 'ui-icon-triangle-1-w'; 56533 $j(button1).button({text:false, icons:{secondary:button1_icon}}); 56534 56535 var button2_icon = (self.orientation == 'vertical') ? 'ui-icon-triangle-1-s' : 'ui-icon-triangle-1-e'; 56536 $j(button2).button({text:false, icons:{secondary:button2_icon}}); 56537 56538 //button1.observe('click', do_button1_click); 56539 repeat_button(button1, do_button1_click, 500) 56540 repeat_button(button2, do_button2_click, 500) 56541 //button2.observe('click', do_button2_click); 56542 set_thumb_size(self.initial_thumb_size); 56543 } 56544 56545 function set_thumb_size(new_size) { 56546 if(self.orientation == 'vertical') 56547 the_handle.setStyle({height:new_size+'px'}); 56548 else 56549 the_handle.setStyle({width:new_size+'px'}); 56550 } 56551 56552 function do_set_position(new_position) { 56553 the_slider.setValue(new_position); 56554 } 56555 56556 function do_get_position() { 56557 return(the_slider.value); 56558 } 56559 56560 function repeat_button(btn, action, initial_speed) { 56561 var t = null; 56562 var speed = initial_speed; 56563 var count = 0; 56564 56565 var repeat = function () { 56566 action(); 56567 if(count++ > 5) 56568 speed = initial_speed/10; 56569 56570 t = setTimeout(repeat, speed); 56571 } 56572 56573 btn.observe('mousedown', function() { 56574 repeat(); 56575 }); 56576 btn.observe('mouseup', function() { 56577 clearTimeout(t); 56578 count = 0; 56579 speed = initial_speed; 56580 }); 56581 } 56582 56583 function do_button1_click(evt) { 56584 if(self.on_button1_click) 56585 self.on_button1_click(self); 56586 } 56587 56588 function do_button2_click(evt) { 56589 if(self.on_button2_click) 56590 self.on_button2_click(self); 56591 } 56592 56593 function set_visibility(visible) { 56594 var visibility = visible ? 'block' : 'none'; 56595 self.container.setStyle({display:visibility}); 56596 } 56597 56598 function do_resize() { 56599 var container_dims = self.container.getDimensions(); 56600 var button_dims = null; 56601 56602 var extent = null; 56603 if(self.orientation == 'horizontal') { 56604 button_dims = button2.getDimensions(); 56605 extent = container_dims.width - ((2 * button_dims.width) + 3); 56606 the_track.setStyle({width:extent+'px'}); 56607 } else { 56608 button_dims = button1.getDimensions(); 56609 extent = container_dims.height - ((2 * button_dims.height) + 2); 56610 the_track.setStyle({height:extent+'px'}); 56611 } 56612//alert("sizing " + self.orientation + " scrollbar to " + extent); 56613 } 56614
56615 function init() { 56616 build_scaffold(); 56617 do_resize(); 56618 the_slider = new Control.Slider(the_handle.id, the_track.id, 56619 { 56620 axis:self.orientation, 56621 sliderValue:self.initial_position, 56622 onSlide:do_move, 56623 onChange:do_change 56624 }); 56625 set_visibility(self.initial_visiblity); 56626 56627 } 56628 56629 function do_move(val) { 56630 if(self.on_move) 56631 self.on_move(self, val); 56632 } 56633 56634 function do_change(val) { 56635 if(self.on_change) 56636 self.on_change(self, val); 56637 } 56638 56639 init(); 56640} 56641; 56642// MRI Viewer Application -- sliderWidgets.js 56643// Copyright (c) 2007-2010 Allen Institute for Brain Science. All Rights Reserved. 56644 56645WindowLevelWidget = Class.create({ 56646 56647 initialize: function(parentNode, colormapUrl, lowerLimit, upperLimit, lowerRange, upperRange, notificationFunc, precision, rangeDisplay) 56648 { 56649 /* Create the following structure 56650 <div> 56651 <div class="windowLevelBox"> 56652 <img id="colormapLowerRangeHandle" src="images/heatmap_images/windowLevelHandle.png" width="6px" height="22px" style="position:absolute; z-index:2; margin-top:-3px; margin-left:-2px; left:0px; cursor:col-resize" /> 56653 <img id="colormapUpperRangeHandle" src="images/heatmap_images/windowLevelHandle.png" width="6px" height="22px" style="position:absolute; z-index:2; margin-top:-3px; margin-left:-2px; left:0px; cursor:col-resize" /> 56654 <img id="colormapLowerRange" src="/colormap?index=0" width="128px" height="16px" style="position:absolute; left:0px" /> 56655 <img id="colormapUpperRange" src="/colormap?index=255" width="0px" height="16px" style="position:absolute; left:0px" /> 56656 <img id="colormapImage" src="/colormap" width="128px" height="16px" style="position:absolute; top:0px; left:128px; cursor:move" /> 56657 </div> 56658 <div id="rangeDisplay">[0.5, 1.0]</div> 56659 </div> 56660 */ 56661 56662 this.tracking = false; 56663 this.lastEventH = -1; 56664 this.symmetric = false; 56665 this.sourceObj = null; 56666 this.parentNode = parentNode; 56667 this.displayPrecision = precision; 56668 56669 this.controlWidth = this.parentNode.getWidth(); 56670 if (this.controlWidth == 0) 56671 this.controlWidth = 1; 56672 this.notificationFunc = notificationFunc; 56673 56674 var boxNode = new Element('div', { 'class':'windowLevelBox', title:'Adjust window/level of the color scale' }); 56675 this.colormapImage = new Element('img', { 'src':colormapUrl, 'width':'128px', 'height':'16px', 'class':'windowLevelMiddle windowLevelMoveCursor' }); 56676 56677 var sep = colormapUrl.indexOf("?") > 0 ? '&' : '?'; 56678 this.lowerRangeImage = new Element('img', { 'src':colormapUrl + sep + "index=0", 'width':'1px', 'height':'16px', 'class':'windowLevelEnds', 'style':'left:0px' }); 56679 this.upperRangeImage = new Element('img', { 'src':colormapUrl + sep + "index=255", 'width':'1px', 'height':'16px', 'class':'windowLevelEnds', 'style':'right:0px' }); 56680 56681 this.lowerHandle = new Element('div', { 'class':'windowLevelHandle' }); 56682 this.upperHandle = new Element('div', { 'class':'windowLevelHandle' }); 56683 56684 boxNode.appendChild(this.lowerRangeImage); 56685 boxNode.appendChild(this.lowerHandle); 56686 boxNode.appendChild(this.colormapImage); 56687 boxNode.appendChild(this.upperHandle); 56688 boxNode.appendChild(this.upperRangeImage); 56689 parentNode.appendChild(boxNode); 56690 56691 if (!rangeDisplay) 56692 { 56693 this.rangeDisplay = new Element('div', { 'class':'windowLevelRangeText' }); 56694 parentNode.appendChild(this.rangeDisplay); 56695 } 56696 else 56697 { 56698 this.rangeDisplay = rangeDisplay; 56699 } 56700 56701 this.setLimits(lowerLimit, upperLimit); 56702 this.setRange(lowerRange, upperRange); 56703 56704 // Register event handlers 56705 this.eventMouseDown = this.onMouseDown.bindAsEventListener(this); 56706 this.eventMouseUp = this.onMouseUp.bindAsEventListener(this); 56707 this.eventMouseMove = this.onMouseMove.bindAsEventListener(this); 56708 56709 this.setupMouseListeners(this.colormapImage); 56710 this.setupMouseListeners(this.lowerHandle); 56711 this.setupMouseListeners(this.upperHandle); 56712 56713 document.observe("mouseup", this.eventMouseUp); 56714 document.observe("mousemove", this.eventMouseMove); 56715 }, 56716 56717 56718 dispose: function() 56719 { 56720 Event.stopObserving(this.colormapImage, "mousedown", this.eventMouseDown); 56721 Event.stopObserving(this.lowerHandle, "mousedown", this.eventMouseDown); 56722 Event.stopObserving(this.upperHandle, "mousedown", this.eventMouseDown); 56723 Event.stopObserving(document, "mouseup", this.eventMouseUp); 56724 Event.stopObserving(document, "mousemove", this.eventMouseMove); 56725 }, 56726 56727 56728 setupMouseListeners: function(element) 56729 { 56730 element.observe("mousedown", this.eventMouseDown); 56731 56732 element.observe("touchstart", this.eventMouseDown); 56733 element.observe("touchmove", this.eventMouseMove); 56734 element.observe("touchup", this.eventMouseUp); 56735 }, 56736 56737 56738 setLimits: function(lowerLimit, upperLimit) 56739 { 56740 this.lowerLimit = lowerLimit; 56741 this.upperLimit = upperLimit; 56742 this.scale = this.controlWidth / (this.upperLimit - this.lowerLimit); 56743 }, 56744 56745 56746 resize: function(frameWidth) 56747 { 56748 // Get range at current control size 56749 var lower = this.getLowerRange(); 56750 var upper = this.getUpperRange(); 56751 56752 // this.parentNode.style.width = frameWidth + "px"; 56753 var containerWidth = this.parentNode.getWidth(); 56754 if (containerWidth > 0) 56755 this.controlWidth = containerWidth; 56756 this.scale = this.controlWidth / (this.upperLimit - this.lowerLimit); 56757 56758 this.lowerRange = (lower - this.lowerLimit) * this.scale; 56759 this.upperRange = (upper - this.lowerLimit) * this.scale; 56760 this.doLayout(); 56761 }, 56762 56763 56764 onMouseDown: function(event) 56765 { 56766 this.tracking = false;
56767 56768// davidf: this doesn't work because the prototype used in most apps (1.6) 56769// has a bug in IE9 such that isLeftClick always returns false. 56770// if (!event.touches && !event.isLeftClick()) 56771// return; 56772 56773 this.sourceObj = event.element(); 56774 56775 this.startRangeLower = this.lowerRange 56776 this.startRangeUpper = this.upperRange; 56777 56778 var h, v; 56779 if (event.targetTouches) 56780 { 56781 if (event.targetTouches.length > 1) 56782 return; 56783 56784 this.anchorPos = event.targetTouches[0].pageX; 56785 event.preventDefault(); 56786 } 56787 else 56788 { 56789 this.anchorPos = event.pointerX(); 56790 } 56791 56792 this.tracking = true; 56793 event.stop(); 56794 }, 56795 56796 56797 onMouseMove: function(event) 56798 { 56799 if (!this.tracking) 56800 return; 56801 56802 this.doDrag(event); 56803 event.stop(); 56804 }, 56805 56806 56807 onMouseUp: function(event) 56808 { 56809 if (this.tracking) 56810 { 56811 this.doDrag(event); 56812 event.stop(); 56813 } 56814 56815 this.tracking = false; 56816 this.sourceObj = null; 56817 }, 56818 56819 56820 doDrag: function(event) 56821 { 56822 if (event.touches && event.touches.length > 1) 56823 return; 56824 56825 var h; 56826 if (event.targetTouches) 56827 { 56828 h = event.targetTouches[0].pageX; 56829 event.preventDefault(); 56830 } 56831 else 56832 { 56833 h = event.pointerX(); 56834 } 56835 56836 var dh = h - this.anchorPos; 56837 56838 if (this.sourceObj == this.lowerHandle) 56839 { 56840 var newValue = this.startRangeLower + dh; 56841 if (newValue < 0) 56842 newValue = 0; 56843 if (newValue >= this.upperRange) 56844 newValue = this.upperRange - 1; 56845 56846 if (newValue != this.lowerRange) 56847 { 56848 this.lowerRange = newValue; 56849 if (this.symmetric) 56850 { 56851 var midPoint = this.controlWidth / 2.0; 56852 this.upperRange = midPoint + (midPoint - this.lowerRange); 56853 } 56854 this.doUpdate(); 56855 } 56856 } 56857 else if (this.sourceObj == this.upperHandle) 56858 { 56859 var newValue = this.startRangeUpper + dh; 56860 if (newValue <= this.lowerRange) 56861 newValue = this.lowerRange + 1; 56862 if (newValue >= this.controlWidth) 56863 newValue = this.controlWidth; 56864 56865 if (newValue != this.upperRange) 56866 { 56867 this.upperRange = newValue; 56868 if (this.symmetric) 56869 { 56870 var midPoint = this.controlWidth / 2.0; 56871 this.lowerRange = midPoint - (this.upperRange - midPoint); 56872 } 56873 this.doUpdate(); 56874 } 56875 } 56876 else if (this.sourceObj == this.colormapImage) 56877 { 56878 if (this.symmetric) 56879 return; 56880 56881 if (this.startRangeUpper + dh >= this.controlWidth) 56882 dh = this.controlWidth - this.startRangeUpper; 56883 else if (this.startRangeLower + dh <= 0) 56884 dh = -this.startRangeLower; 56885 56886 if (this.startRangeUpper + dh != this.upperRange) 56887 { 56888 this.upperRange = this.startRangeUpper + dh; 56889 this.lowerRange = this.startRangeLower + dh; 56890 this.doUpdate(); 56891 } 56892 } 56893 }, 56894 56895 56896 doLayout: function() 56897 { 56898 this.colormapImage.width = Math.floor(this.upperRange - this.lowerRange); 56899 this.colormapImage.style.left = Math.floor(this.lowerRange) + "px"; 56900 56901 this.lowerRangeImage.width = Math.floor(this.lowerRange); 56902 this.upperRangeImage.width = Math.floor(this.controlWidth - this.upperRange); 56903 56904 this.lowerHandle.style.left = Math.floor(this.lowerRange) + "px"; 56905 this.upperHandle.style.left = Math.floor(this.upperRange) + "px"; 56906 }, 56907 56908 56909 doUpdate: function() 56910 { 56911 this.doLayout(); 56912 this.updateText(); 56913 this.notificationFunc(this.getLowerRange(), this.getUpperRange()); 56914 }, 56915 56916 56917 updateText: function() 56918 { 56919 //this.rangeDisplay.innerHTML = "[" + round(this.getLowerRange(), this.displayPrecision) + ", " + round(this.getUpperRange(), this.displayPrecision) + "]"; 56920 56921 // BUGBUG, for testing 56922 this.rangeDisplay.innerHTML = "[" + Math.round(this.getLowerRange()) + ", " + Math.round(this.getUpperRange()) + "]"; 56923 }, 56924 56925 56926 getLowerRange: function() 56927 { 56928 return (this.lowerRange / this.scale) + this.lowerLimit; 56929 }, 56930 56931 56932 getUpperRange: function() 56933 { 56934 return (this.upperRange / this.scale) + this.lowerLimit; 56935 }, 56936 56937 56938 setRange: function(lower, upper) 56939 { 56940 if (lower < this.lowerLimit) 56941 lower = this.lowerLimit; 56942 if (upper > this.upperLimit) 56943 upper = this.upperLimit; 56944 56945 this.lowerRange = (lower - this.lowerLimit) * this.scale; 56946 this.upperRange = (upper - this.lowerLimit) * this.scale; 56947 this.doLayout(); 56948 this.updateText(); 56949 }, 56950 56951 56952 setColormapImage: function(imageUrl) 56953 { 56954 var sep = imageUrl.indexOf("?") > 0 ? '&' : '?'; 56955 this.colormapImage.src = imageUrl; 56956 this.lowerRangeImage.src = imageUrl + sep + "index=0"; 56957 this.upperRangeImage.src = imageUrl + sep + "index=255"; 56958 }, 56959 56960 56961 setSymmetric: function(s) 56962 { 56963 if (s) 56964 this.colormapImage.removeClassName('windowLevelMoveCursor'); 56965 else 56966 this.colormapImage.addClassName('windowLevelMoveCursor'); 56967 56968 this.symmetric = s; 56969 } 56970 56971}); 56972 56973 56974var __status_count = 0; 56975 56976function status_bar(container, options, event_options) { 56977 56978// public properties 56979 this.container = $(container); 56980 this.initial_label = options ? options.status_label ? options.status_label : ' ' : ' '; 56981 56982// event hooks 56983 this.on_zoom = event_options? (event_options.on_zoom ? event_options.on_zoom : null) : null; 56984 this.on_zoom_reset = event_options? (event_options.on_zoom_reset ? event_options.on_zoom_reset : null) : null; 56985 56986// public methods 56987 this.set_status_wide = function(msg, color) { 56988 the_wide_text_box.update(msg); 56989 the_wide_text_box.setStyle({'borderColor':'#'+color}); 56990 } 56991 56992 this.set_status_narrow = function(msg, color) { 56993 the_narrow_text_box.update(msg); 56994 the_narrow_text_box.setStyle({'borderColor':'#'+color}); 56995 } 56996 56997 this.set_status_first = function(msg, color) { 56998 the_first_text_box.update(msg); 56999 the_first_text_box.setStyle({'borderColor':'#'+color}); 57000 } 57001 57002 this.set_status_real_first = function(msg, color) { 57003 the_real_first_text_box.update(msg); 57004 the_real_first_text_box.setStyle({'borderColor':'#'+color}); 57005 } 57006 57007 this.show_click_marker_symbol = function() { 57008 the_click_marker_symbol.show(); 57009 } 57010 57011 this.set_status_label = function(msg, color) { 57012 the_label_box.update(msg); 57013 the_label_box.setStyle({'borderColor':'#'+color}); 57014 } 57015 57016// private properties 57017 var self = this; 57018 57019 var the_wide_text_box = null; 57020 var the_narrow_text_box = null; 57021 var the_first_text_box = null; 57022 var the_real_first_text_box = null; 57023 var the_label_box = null; 57024 var the_click_marker_symbol = null; 57025 57026 57027// private methods 57028 function build_scaffold() { 57029 // status text 57030 the_label_box= new Element('span', {id:'status_label_text_box_'+__status_count, 'class':'status_text_box status_label'}); 57031 self.container.appendChild(the_label_box); 57032 the_label_box.update(self.initial_label); 57033 the_wide_text_box = new Element('span', {id:'status_wide_text_box_'+__status_count, 'class':'status_text_box status_wide_text_box'}); 57034 self.container.appendChild(the_wide_text_box); 57035 the_wide_text_box.update(' '); 57036 the_narrow_text_box = new Element('span', {id:'status_narrow_text_box_'+__status_count, 'class':'status_text_box status_narrow_text_box'}); 57037 self.container.appendChild(the_narrow_text_box); 57038 the_narrow_text_box.update(' '); 57039 the_first_text_box = new Element('span', {id:'status_first_text_box_'+__status_count, 'class':'status_text_box status_first_text_box'}); 57040 self.container.appendChild(the_first_text_box); 57041 the_first_text_box.update(' '); 57042 the_real_first_text_box = new Element('span', {id:'status_real_first_text_box_'+__status_count, 'class':'status_real_first_text_box'}); 57043 self.container.appendChild(the_real_first_text_box); 57044 the_real_first_text_box.update(' '); 57045 the_click_marker_symbol = new Element('img', {id:'hicm_symbol'+__status_count, 'class':'header_marker_symbol'}); 57046 the_click_marker_symbol.src = '/assets/heatmap_ui/click_marker_shortened.png'; 57047 the_click_marker_symbol.hide(); 57048 self.container.appendChild(the_click_marker_symbol); 57049 57050 } 57051
57052 function init() { 57053 build_scaffold(); 57054 __status_count++; 57055 } 57056 57057 init(); 57058} 57059; 57060var __surface_widget_count__ = 0; 57061 57062function SurfaceWidget(container,options,event_options) { 57063 57064 // public properties 57065 this.container = $(container); 57066 this.frame_width = (options.frame_width != undefined) ? options.frame_width : 200; 57067 this.frame_height = (options.frame_height != undefined) ? options.frame_height : 150; 57068 this.index = (options.index != undefined) ? options.index : -1; 57069 this.colormap_name = (options.colormap_name != undefined) ? options.colormap_name : "redgreen"; 57070 this.colormap_range = (options.colormap_range != undefined) ? options.colormap_range : [-3.0,3.0]; 57071 this.colormap_normal = (options.colormap_normal != undefined) ? options.colormap_normal : true; 57072 this.host = (options.host != undefined) ? options.host : ""; 57073 this.num_rotations = (options.num_rotations != undefined) ? options.num_rotations : 36; 57074 this.image_settings = (options.image_settings != undefined) ? options.image_settings : []; 57075 this.full_resolution = (options.full_resolution != undefined) ? options.full_resolution : false; 57076 57077 this.pick_callback = event_options.pick_callback ? 57078 event_options.pick_callback : null; 57079 57080 // private properties 57081 var self = this; 57082 57083 var active = false; 57084 var the_angle = 0; 57085 var the_sample = {}; 57086 var the_images = []; 57087 var the_current_surface = ""; 57088 57089 // private methods 57090 57091 // initialize surface images and layout the page 57092 function build_scaffold() { 57093 57094 // initialize one surface image for each donor 57095 for (var i=0; i<self.image_settings.length; i++) 57096 the_images.push(make_image(self.index,i, 57097 self.frame_width, 57098 self.frame_height, 57099 self.image_settings[i].donor, 57100 self.image_settings[i].probe)); 57101 57102 // lay out the page 57103 for (var i=0; i<the_images.length; i++) 57104 self.container.appendChild(the_images[i].div); 57105 57106 } 57107 57108 // construct a surface image for a donor and probe at a given size 57109 function make_image(widget_index,image_index,width,height,donor,probe) { 57110 var o = { 57111 'div': new Element('div', { 'id' : 'image_'+widget_index+'_'+image_index }), 57112 'donor_id': donor.donor.id, 57113 'probe_id': probe.id, 57114 'param_str': "", 57115 'image' : new Image() 57116 }; 57117 o.div.setStyle({ 57118 width : width+'px', 57119 height : height+'px', 57120 position: 'absolute', 57121 left: (image_index * width) + 'px', 57122 background: "black", 57123 cursor: "crosshair" }); 57124 57125 var t = new Element('p', { 'style' : 'color: gray; text-align: center; margin-top: '+self.frame_height*.45+'px'}); 57126 $(t).update('no expression data'); 57127 57128 o.text = t; 57129 57130 o.div.observe("click",on_image_click); 57131 57132 return o; 57133 } 57134 57135 // callback for pick events 57136 function on_image_click(event) { 57137 57138 if (event.touches && event.touches.length > 1) 57139 return; 57140 57141 var h, v; 57142 if (event.targetTouches) 57143 { 57144 h = event.targetTouches[0].pageX; 57145 v = event.targetTouches[0].pageY; 57146 event.preventDefault(); 57147 } 57148 else 57149 { 57150 h = event.pointerX(); 57151 v = event.pointerY(); 57152 } 57153 57154 // Convert from page to image element coordinates 57155 var imgElement = event.target || event.srcElement; 57156 57157 if (!imgElement) 57158 return; 57159 57160 var container = imgElement.parentNode; 57161 57162 if (!container) 57163 return; 57164 57165 var nextParent = container; 57166 while (nextParent) 57167 { 57168 h = h - nextParent.offsetLeft; 57169 v = v - nextParent.offsetTop; 57170 57171 nextParent = nextParent.offsetParent; 57172 } 57173 57174 var width = container.getWidth(); 57175 var angle = the_angle; 57176 var posX = (h+1.0+width*Math.floor(angle)) / (width*self.num_rotations); 57177 var posY = (v+1.0) / container.getHeight(); 57178 57179 // figure out which donor & probe the click came from by checking the div id 57180 var donor_id = 0; 57181 var probe_id = 0; 57182 57183 for (var i=0; i<the_images.length; i++) 57184 { 57185 if (container.id == the_images[i].div.id) { 57186 donor_id = the_images[i].donor_id; 57187 probe_id = the_images[i].probe_id; 57188 } 57189 } 57190 57191 // send the surface image query 57192 var params = { 57193 "probe" : probe_id, 57194 "donor" : donor_id, 57195 "selectedPos" : posX+","+posY 57196 }; 57197 57198 if (the_current_surface) 57199 params.surface = the_current_surface; 57200
57201 new Ajax.Request(self.host + "/surface/query", 57202 { 57203 method:"get", 57204 parameters: params, 57205 onSuccess: function(result) { 57206 active = true; 57207 self.set_sample(result.responseJSON); 57208 if (self.pick_callback) self.pick_callback(result.responseJSON); 57209 active = false; 57210 } 57211 }); 57212 }; 57213 57214 // privileged methods 57215 this.set_sample = function(info) { 57216 if (info.probe) the_sample.probe = info.probe; 57217 if (info.donor) the_sample.donor = info.donor; 57218 if (info.rna_well) the_sample.rna_well = info.rna_well; 57219 if (info.surface) the_sample.surface = info.surface; 57220 } 57221 57222 this.clear_sample = function() { 57223 the_sample = {}; 57224 } 57225 57226 this.get_current_well = function() { 57227 return the_sample.rna_well; 57228 } 57229 57230 this.set_current_surface = function(surface) { 57231 the_current_surface = surface; 57232 } 57233 57234 this.is_visible = function() { 57235 return !(self.container.style.visibility == "hidden"); 57236 }; 57237 57238 this.set_angle = function(val) { 57239 the_angle = Math.max(0,Math.min(val,self.num_rotations-1)); 57240 }; 57241 57242 this.set_size = function(width, height) { 57243 this.frame_width = width; 57244 this.frame_height = height; 57245 57246 for (var i=0; i<the_images.length; i++) 57247 { 57248 the_images[i].div.setStyle({ 57249 width : width+'px', 57250 height : height+'px', 57251 left: (i * width) + 'px' 57252 }); 57253 57254 var img = jQuery(the_images[i].div.childElements()[0]); 57255 if (img) 57256 { 57257 var imgWidth = img.width(); 57258 var imgHeight = img.height(); 57259 57260 if (imgWidth <= 0 || imgHeight <= 0) 57261 continue; 57262 57263 var scale_x = this.frame_width / (imgWidth / this.num_rotations); 57264 var scale_y = this.frame_height / imgHeight; 57265 57266 img.css('width', (imgWidth * scale_x)+'px'); 57267// img.css('height', (imgHeight * scale_y)+'px'); 57268 57269 img.css('position','absolute') 57270 .css('left', -(Math.floor(the_angle) * this.frame_width)+'px'); 57271 } 57272 } 57273 } 57274 57275 this.update_image = function() { 57276 // make sure the widget is visible 57277 if (!self.is_visible()) 57278 return; 57279 57280 if (!the_current_surface) 57281 return; 57282 57283 // find out as much as we can about the sample 57284 var sample_well = the_sample.rna_well; 57285 var sample_probe_id = the_sample.probe; 57286 var sample_donor_id = the_sample.donor; 57287 var angle = the_angle; 57288 57289 // update all of the surface images. cached images (unchanged src) should 57290 // just have their cropping frame moved, no service requests necessary. 57291 for (var i=0; i<the_images.length; i++) 57292 { 57293 var image = the_images[i]; 57294 57295 // build the new img src 57296 var paramStr = "donor=" + image.donor_id + 57297 "&probe=" + image.probe_id + 57298 "&exprWindow=" + self.colormap_range; 57299 57300 if (the_current_surface) 57301 paramStr += "&surface="+the_current_surface; 57302 57303 if (self.colormap_normal) { 57304 paramStr += "&normalize"; 57305 paramStr += "&colormap=" + self.colormap_name; 57306 } 57307 57308 if (self.full_resolution) { 57309 paramStr += "&fullResolution"; 57310 } 57311 57312 // if there's a well and it belongs to this image, pass it 57313 if (sample_well && sample_well > 0 && 57314 sample_donor_id == image.donor_id && 57315 sample_probe_id == image.probe_id) 57316 paramStr += "&wellId="+sample_well; 57317 57318 // preload the Image, but only if something has changed 57319 if (image.paramStr != paramStr) { 57320 image.paramStr = paramStr; 57321 57322 // tell the surface image to update it's img source once the 57323 // Image has finished preloading 57324 image.image.onload = (function() { 57325 57326 // Empty the image's container. 57327 jQuery(this.div).empty(); 57328 57329 // Compute the scale factor between the requested frame size and 57330 // the actual image size. 57331 var imgWidth = this.image.width; 57332 var imgHeight = this.image.height; 57333 57334 var scale_x = self.frame_width / (imgWidth / self.num_rotations); 57335 var scale_y = self.frame_height / imgHeight; 57336 57337 // Create a new img element. We could just use the image's img 57338 // element, but IE7 is stupid about setting width/height on img's 57339 // that already have a src. 57340 var newImg = jQuery('<img>'); 57341 57342 jQuery(this.div).append(newImg); 57343 57344 // IE7 can only scale proportionally, at least as far as I can tell. 57345 // Set the width to the correct size, and height: 100% will take care 57346 // of the scaling.
57347 newImg.css('width', (imgWidth * scale_x)+'px'); 57348 newImg.css('height', '100%'); 57349 57350 // Set the src now, after width/height are correct. 57351 newImg.attr('src', this.image.src); 57352 57353 // Shift the img to the left, so that the container div crops out the 57354 // correct thumbnail. 57355 newImg.css('position','absolute') 57356 .css('left', -(Math.floor(angle) * self.frame_width)+'px'); 57357 }).bind(the_images[i]); 57358 57359 // if the server doesn't return an image, make the error msg visible 57360 image.image.onerror = (function(event) { 57361 jQuery(this.div).empty(); 57362 jQuery(this.div).append(this.text); 57363 }).bind(the_images[i]); 57364 57365 image.image.src = self.host + "/surface/show?" + paramStr; 57366 } 57367 57368 jQuery(image.div).css('overflow','hidden'); 57369 57370 // The img that was created after the image was loaded should be the only 57371 // child of the container div. 57372 var img = jQuery(image.div.childElements()[0]); 57373 if (img) 57374 { 57375 img.css('position','absolute') 57376 .css('left', -(Math.floor(angle) * self.frame_width )+'px'); 57377 } 57378 } 57379 }; 57380 57381 function initialize() { 57382 self.index = __surface_widget_count__++; 57383 build_scaffold(); 57384 }; 57385 57386 initialize(); 57387}; 57388/** 57389Vertigo Tip by www.vertigo-project.com 57390Requires jQuery 57391*/ 57392 57393 57394this.vtip = function() { 57395 this.xOffset = -10; // x distance from mouse 57396 this.yOffset = 10; // y distance from mouse 57397 57398 jQuery(".vtip").unbind().hover( 57399 function(e) { 57400 this.t = this.title; 57401 this.title = ''; 57402 this.top = (e.pageY + yOffset); this.left = (e.pageX + xOffset); 57403 57404 jQuery('body').append( '<p id="vtip"><img id="vtipArrow" />' + this.t + '</p>' ); 57405 57406 jQuery('p#vtip #vtipArrow').attr("src", '/assets/heatmap_ui/vtip_arrow.png'); 57407 jQuery('p#vtip').css("top", this.top+"px").css("left", this.left+"px").fadeIn("slow"); 57408 57409 }, 57410 function() { 57411 this.title = this.t; 57412 jQuery("p#vtip").fadeOut("slow").remove(); 57413 } 57414 ).mousemove( 57415 function(e) { 57416 this.top = (e.pageY + yOffset); 57417 this.left = (e.pageX + xOffset); 57418 57419 jQuery("p#vtip").css("top", this.top+"px").css("left", this.left+"px"); 57420 } 57421 ); 57422 57423}; 57424 57425jQuery(document).ready(function(jQuery){vtip();}) 57426; 57427// javascript manifest file for this gem, this is the file that is loaded by apps that use this gem. 57428// 57429 57430; 57431AIBS.array = AIBS.array || {}; // namespace 57432 57433 57434/** 57435 * lifted from Google Closure Library 57436 * 57437 * Compares its two arguments for order, using the built in < and > 57438 * operators. 57439 * @param {*} a The first object to be compared. 57440 * @param {*} b The second object to be compared. 57441 * @return {number} A negative number, zero, or a positive number as the first 57442 * argument is less than, equal to, or greater than the second. 57443 */ 57444AIBS.array.defaultCompare = function(a, b) { 57445 return a > b ? 1 : a < b ? -1 : 0; 57446}; 57447 57448 57449/** 57450 * adapted from Google Closure Library 57451 * To be extra conservative, does not use native array prototype functions. 57452 * 57453 * Calls a function for each element in an array. 57454 * 57455 * See {@link http://tinyurl.com/developer-mozilla-org-array-foreach} 57456 * 57457 * @param {goog.array.ArrayLike} arr Array or array like object over 57458 * which to iterate. 57459 * @param {?function(this: T, ...)} f The function to call for every element. 57460 * This function takes 3 arguments (the element, the index and the array). 57461 * The return value is ignored. The function is called only for indexes of 57462 * the array which have assigned values; it is not called for indexes which 57463 * have been deleted or which have never been assigned values. 57464 * @param {T=} opt_obj The object to be used as the value of 'this' 57465 * within f. 57466 * @template T 57467 */ 57468AIBS.array.forEach = function(arr, f, opt_obj) { 57469 var len = arr.length; // must be fixed during loop... see docs 57470 var arr2 = AIBS.isString(arr) ? arr.split('') : arr; 57471 for (var i = 0; i < len; i++) { 57472 if (i in arr2) { 57473 f.call(opt_obj, arr2[i], i, arr); 57474 } 57475 } 57476}; 57477 57478 57479/** 57480 * Like AIBS.array.forEach but f is called with a slice of the input array. 57481 * Each slice except the last is guaranteed to contain 'n' elements. The last 57482 * slice is guaranteed to contain at least one element. 57483 * @param arr 57484 * @param n 57485 * @param f 57486 * @param opt_obj 57487 */ 57488AIBS.array.forEachN = function(arr, n, f, opt_obj) { 57489 var len = arr.length; // must be fixed during loop... see docs 57490 var arr2 = AIBS.isString(arr) ? arr.split('') : arr; 57491 for (var i = 0; i < len; i += n) { 57492 if (i in arr2) { 57493 f.call(opt_obj, arr2.slice(i, i + n), i, arr); 57494 } 57495 } 57496}; 57497 57498 57499/** 57500 * adapted from Google Closure Library 57501 * To be extra conservative, does not use native array prototype functions. 57502 * 57503 * Calls a function for each element in an array and inserts the result into a 57504 * new array. 57505 * 57506 * See {@link http://tinyurl.com/developer-mozilla-org-array-map} 57507 * 57508 * @param {goog.array.ArrayLike} arr The array over which to iterate. 57509 * @param {Function} f The function to call for every element. This function 57510 * takes 3 arguments (the element, the index and the array) and should 57511 * return something. The result will be inserted into a new array. 57512 * @param {Object=} opt_obj The object to be used as the value of 'this' 57513 * within f. 57514 * @return {!Array} a new array with the results from f. 57515 */ 57516AIBS.array.map = function(arr, f, opt_obj) { 57517 var l = arr.length; // must be fixed during loop... see docs 57518 var res = new Array(l); 57519 var arr2 = AIBS.isString(arr) ? arr.split('') : arr; 57520 for (var i = 0; i < l; i++) { 57521 if (i in arr2) { 57522 res[i] = f.call(opt_obj, arr2[i], i, arr); 57523 } 57524 } 57525 return res; 57526}; 57527 57528 57529/** 57530 * lifted from Google Closure Library 57531 * 57532 * Passes every element of an array into a function and accumulates the result. 57533 * 57534 * See {@link http://tinyurl.com/developer-mozilla-org-array-reduce} 57535 * 57536 * For example: 57537 * var a = [1, 2, 3, 4]; 57538 * goog.array.reduce(a, function(r, v, i, arr) {return r + v;}, 0); 57539 * returns 10 57540 * 57541 * @param {goog.array.ArrayLike} arr The array over which to iterate. 57542 * @param {Function} f The function to call for every element. This function 57543 * takes 4 arguments (the function's previous result or the initial value, 57544 * the value of the current array element, the current array index, and the 57545 * array itself) 57546 * function(previousValue, currentValue, index, array). 57547 * @param {*} val The initial value to pass into the function on the first call. 57548 * @param {Object=} opt_obj The object to be used as the value of 'this' 57549 * within f. 57550 * @return {*} Result of evaluating f repeatedly across the values of the array. 57551 */ 57552AIBS.array.reduce = function(arr, f, val, opt_obj) { 57553 if (arr.reduce) { 57554 if (opt_obj) { 57555 return arr.reduce(AIBS.bind(f, opt_obj), val); 57556 } else { 57557 return arr.reduce(f, val); 57558 } 57559 } 57560 var rval = val; 57561 AIBS.array.forEach(arr, function(val, index) { 57562 rval = f.call(opt_obj, rval, val, index, arr); 57563 }); 57564 return rval; 57565}; 57566// requires pre-existing AIBS namespace 57567 57568/** 57569 * lifted from Google Closure Library 57570 * Null function used for default values of callbacks, etc. 57571 * @return {void} Nothing. 57572 */ 57573 57574AIBS.nullFunction = function() {}; 57575 57576 57577/** 57578 * lifted from Google Closure Library 57579 * This is a "fixed" version of the typeof operator. It differs from the typeof 57580 * operator in such a way that null returns 'null' and arrays return 'array'. 57581 * @param {*} value The value to get the type of. 57582 * @return {string} The name of the type. 57583 */ 57584AIBS.typeOf = function(value) { 57585 var s = typeof value; 57586 if (s == 'object') { 57587 if (value) {
vendor: 9,504 bytes, lines 57588-57877
57588 // Check these first, so we can avoid calling Object.prototype.toString if 57589 // possible. 57590 // 57591 // IE improperly marshals tyepof across execution contexts, but a 57592 // cross-context object will still return false for "instanceof Object". 57593 if (value instanceof Array) { 57594 return 'array'; 57595 } else if (value instanceof Object) { 57596 return s; 57597 } 57598 57599 // HACK: In order to use an Object prototype method on the arbitrary 57600 // value, the compiler requires the value be cast to type Object, 57601 // even though the ECMA spec explicitly allows it. 57602 var className = Object.prototype.toString.call( 57603 /** @type {Object} */ (value)); 57604 // In Firefox 3.6, attempting to access iframe window objects' length 57605 // property throws an NS_ERROR_FAILURE, so we need to special-case it 57606 // here. 57607 if (className == '[object Window]') { 57608 return 'object'; 57609 } 57610 57611 // We cannot always use constructor == Array or instanceof Array because 57612 // different frames have different Array objects. In IE6, if the iframe 57613 // where the array was created is destroyed, the array loses its 57614 // prototype. Then dereferencing val.splice here throws an exception, so 57615 // we can't use goog.isFunction. Calling typeof directly returns 'unknown' 57616 // so that will work. In this case, this function will return false and 57617 // most array functions will still work because the array is still 57618 // array-like (supports length and []) even though it has lost its 57619 // prototype. 57620 // Mark Miller noticed that Object.prototype.toString 57621 // allows access to the unforgeable [[Class]] property. 57622 // 15.2.4.2 Object.prototype.toString ( ) 57623 // When the toString method is called, the following steps are taken: 57624 // 1. Get the [[Class]] property of this object. 57625 // 2. Compute a string value by concatenating the three strings 57626 // "[object ", Result(1), and "]". 57627 // 3. Return Result(2). 57628 // and this behavior survives the destruction of the execution context. 57629 if ((className == '[object Array]' || 57630 // In IE all non value types are wrapped as objects across window 57631 // boundaries (not iframe though) so we have to do object detection 57632 // for this edge case 57633 typeof value.length == 'number' && 57634 typeof value.splice != 'undefined' && 57635 typeof value.propertyIsEnumerable != 'undefined' && 57636 !value.propertyIsEnumerable('splice') 57637 57638 )) { 57639 return 'array'; 57640 } 57641 // HACK: There is still an array case that fails. 57642 // function ArrayImpostor() {} 57643 // ArrayImpostor.prototype = []; 57644 // var impostor = new ArrayImpostor; 57645 // this can be fixed by getting rid of the fast path 57646 // (value instanceof Array) and solely relying on 57647 // (value && Object.prototype.toString.vall(value) === '[object Array]') 57648 // but that would require many more function calls and is not warranted 57649 // unless closure code is receiving objects from untrusted sources. 57650 57651 // IE in cross-window calls does not correctly marshal the function type 57652 // (it appears just as an object) so we cannot use just typeof val == 57653 // 'function'. However, if the object has a call property, it is a 57654 // function. 57655 if ((className == '[object Function]' || 57656 typeof value.call != 'undefined' && 57657 typeof value.propertyIsEnumerable != 'undefined' && 57658 !value.propertyIsEnumerable('call'))) { 57659 return 'function'; 57660 } 57661 57662 57663 } else { 57664 return 'null'; 57665 } 57666 57667 } else if (s == 'function' && typeof value.call == 'undefined') { 57668 // In Safari typeof nodeList returns 'function', and on Firefox 57669 // typeof behaves similarly for HTML{Applet,Embed,Object}Elements 57670 // and RegExps. We would like to return object for those and we can 57671 // detect an invalid function by making sure that the function 57672 // object has a call method. 57673 return 'object'; 57674 } 57675 return s; 57676}; 57677 57678 57679/** 57680 * lifted from Google Closure Library 57681 * Returns true if the specified value is not |undefined|. 57682 * WARNING: Do not use this to test if an object has a property. Use the in 57683 * operator instead. Additionally, this function assumes that the global 57684 * undefined variable has not been redefined. 57685 * @param {*} val Variable to test. 57686 * @return {boolean} Whether variable is defined. 57687 */ 57688AIBS.isDef = function(val) { 57689 return val !== undefined; 57690}; 57691 57692 57693/** 57694 * lifted from Google Closure Library 57695 * Returns true if the specified value is |null| 57696 * @param {*} val Variable to test. 57697 * @return {boolean} Whether variable is null. 57698 */ 57699AIBS.isNull = function(val) { 57700 return val === null; 57701}; 57702 57703 57704/** 57705 * lifted from Google Closure Library 57706 * Returns true if the specified value is defined and not null 57707 * @param {*} val Variable to test. 57708 * @return {boolean} Whether variable is defined and not null. 57709 */ 57710AIBS.isDefAndNotNull = function(val) { 57711 // Note that undefined == null. 57712 return val != null; 57713}; 57714 57715 57716/** 57717 * lifted from Google Closure Library 57718 * Returns true if the specified value is an array 57719 * @param {*} val Variable to test. 57720 * @return {boolean} Whether variable is an array. 57721 */ 57722AIBS.isArray = function(val) { 57723 return AIBS.typeOf(val) == 'array'; 57724}; 57725 57726 57727/** 57728 * lifted from Google Closure Library 57729 * Returns true if the specified value is an array 57730 * @param {*} val Variable to test. 57731 * @return {boolean} Whether variable is an array. 57732 */ 57733AIBS.isArrayLike = function(val) { 57734 var type = AIBS.typeOf(val); 57735 return type == 'array' || type == 'object' && typeof val.length == 'number'; 57736}; 57737 57738 57739/** 57740 * lifted from Google Closure Library 57741 * Returns true if the object looks like a Date. To qualify as Date-like 57742 * the value needs to be an object and have a getFullYear() function. 57743 * @param {*} val Variable to test. 57744 * @return {boolean} Whether variable is a like a Date. 57745 */ 57746AIBS.isDateLike = function(val) { 57747 return AIBS.isObject(val) && typeof val.getFullYear == 'function'; 57748}; 57749 57750 57751/** 57752 * lifted from Google Closure Library 57753 * Returns true if the specified value is a string 57754 * @param {*} val Variable to test. 57755 * @return {boolean} Whether variable is a string. 57756 */ 57757AIBS.isString = function(val) { 57758 return typeof val == 'string'; 57759}; 57760 57761 57762/** 57763 * lifted from Google Closure Library 57764 * Returns true if the specified value is a boolean 57765 * @param {*} val Variable to test. 57766 * @return {boolean} Whether variable is boolean. 57767 */ 57768AIBS.isBoolean = function(val) { 57769 return typeof val == 'boolean'; 57770}; 57771 57772 57773/** 57774 * lifted from Google Closure Library 57775 * Returns true if the specified value is a number 57776 * @param {*} val Variable to test. 57777 * @return {boolean} Whether variable is a number. 57778 */ 57779AIBS.isNumber = function(val) { 57780 return typeof val == 'number'; 57781}; 57782 57783 57784/** 57785 * lifted from Google Closure Library 57786 * 57787 * Returns true if the specified value is a function 57788 * @param {*} val Variable to test. 57789 * @return {boolean} Whether variable is a function. 57790 */ 57791AIBS.isFunction = function(val) { 57792 return AIBS.typeOf(val) == 'function'; 57793}; 57794 57795 57796/** 57797 * lifted from Google Closure Library 57798 * 57799 * Returns true if the specified value is an object. This includes arrays 57800 * and functions. 57801 * @param {*} val Variable to test. 57802 * @return {boolean} Whether variable is an object. 57803 */ 57804AIBS.isObject = function(val) { 57805 var type = typeof val; 57806 return type == 'object' && val != null || type == 'function'; 57807 // return Object(val) === val also works, but is slower, especially if val is 57808 // not an object. 57809}; 57810 57811 57812/** 57813 * adatpted from Google Closure Library 57814 * Does not use browser native bind even if it exists. 57815 * 57816 * A pure-JS implementation of goog.bind. 57817 * @param {Function} fn A function to partially apply. 57818 * @param {Object|undefined} selfObj Specifies the object which |this| should 57819 * point to when the function is run. 57820 * @param {...*} var_args Additional arguments that are partially 57821 * applied to the function. 57822 * @return {!Function} A partially-applied form of the function bind() was 57823 * invoked as a method of. 57824 * @private 57825 */ 57826AIBS.bind = function(fn, selfObj, var_args) { 57827 if (!fn) { 57828 throw new Error(); 57829 } 57830 57831 if (arguments.length > 2) { 57832 var boundArgs = Array.prototype.slice.call(arguments, 2); 57833 return function() { 57834 // Prepend the bound arguments to the current arguments. 57835 var newArgs = Array.prototype.slice.call(arguments); 57836 Array.prototype.unshift.apply(newArgs, boundArgs); 57837 return fn.apply(selfObj, newArgs); 57838 }; 57839 57840 } else { 57841 return function() { 57842 return fn.apply(selfObj, arguments); 57843 }; 57844 } 57845}; 57846 57847 57848/** 57849 * lifted from Google Closure Library 57850 * 57851 * Like bind(), except that a 'this object' is not required. Useful when the 57852 * target function is already bound. 57853 * 57854 * Usage: 57855 * var g = partial(f, arg1, arg2); 57856 * g(arg3, arg4); 57857 * 57858 * @param {Function} fn A function to partially apply. 57859 * @param {...*} var_args Additional arguments that are partially 57860 * applied to fn. 57861 * @return {!Function} A partially-applied form of the function bind() was 57862 * invoked as a method of. 57863 */ 57864AIBS.partial = function(fn, var_args) { 57865 var args = Array.prototype.slice.call(arguments, 1); 57866 return function() { 57867 // Prepend the bound arguments to the current arguments. 57868 var newArgs = Array.prototype.slice.call(arguments); 57869 newArgs.unshift.apply(newArgs, args); 57870 return fn.apply(this, newArgs); 57871 }; 57872}; 57873// requires pre-existing AIBS namespace 57874 57875AIBS.dbg = AIBS.dbg || {}; // namespace 57876 57877
57878/** @param {...} msg TODO. */ 57879AIBS.dbg.info = function(msg) { 57880 try { window.console.info.apply(window.console, arguments); } catch (ex) {} 57881}; 57882 57883 57884/** @param {...} msg TODO. */ 57885AIBS.dbg.warn = function(msg) { 57886 try { window.console.warn.apply(window.console, arguments); } catch (ex) {} 57887}; 57888 57889 57890/** @param {...} msg TODO. */ 57891AIBS.dbg.error = function(msg) { 57892 try { window.console.error.apply(window.console, arguments); } catch (ex) {} 57893}; 57894// adapted from slick.model.js 57895 57896 57897AIBS.events = AIBS.events || {}; // namespace 57898 57899/** 57900 * @constructor 57901 */ 57902AIBS.events.EventHelper = function() { 57903 /** @private */ 57904 this.handlers_ = []; 57905}; 57906 57907 57908AIBS.events.EventHelper.prototype.subscribe = function(fn) { 57909 this.handlers_.push(fn); 57910}; 57911 57912 57913AIBS.events.EventHelper.prototype.notify = function(args) { 57914 for (var i = 0; i < this.handlers_.length; i++) { 57915 this.handlers_[i].call(this, args); 57916 } 57917}; 57918// requires pre-existing AIBS namespace 57919 57920AIBS.object = AIBS.object || {}; // namespace 57921 57922 57923/** 57924 * lifted from Google Closure Library 57925 * Calls a function for each element in an object/map/hash. 57926 * 57927 * @param {Object} obj The object over which to iterate. 57928 * @param {Function} f The function to call for every element. This function 57929 * takes 3 arguments (the element, the index and the object) 57930 * and the return value is irrelevant. 57931 * @param {Object=} opt_obj This is used as the 'this' object within f. 57932 */ 57933AIBS.object.forEach = function(obj, f, opt_obj) { 57934 for (var key in obj) { 57935 f.call(opt_obj, obj[key], key, obj); 57936 } 57937}; 57938 57939 57940/** 57941 * lifted from Google Closure Library 57942 * Get a value from an object multiple levels deep. This is useful for 57943 * pulling values from deeply nested objects, such as JSON responses. 57944 * Example usage: getValueByKeys(jsonObj, 'foo', 'entries', 3) 57945 * 57946 * @param {!Object} obj An object to get the value from. Can be array-like. 57947 * @param {...(string|number|!Array.<number|string>)} var_args A number of keys 57948 * (as strings, or nubmers, for array-like objects). Can also be 57949 * specified as a single array of keys. 57950 * @return {*} The resulting value. If, at any point, the value for a key 57951 * is undefined, returns undefined. 57952 */ 57953AIBS.object.getValueByKeys = function(obj, var_args) { 57954 var isArrayLike = AIBS.isArrayLike(var_args); 57955 var keys = isArrayLike ? var_args : arguments; 57956 57957 // Start with the 2nd parameter for the variable parameters syntax. 57958 for (var i = isArrayLike ? 0 : 1; i < keys.length; i++) { 57959 obj = obj[keys[i]]; 57960 if (!AIBS.isDef(obj)) { 57961 break; 57962 } 57963 } 57964 57965 return obj; 57966}; 57967AIBS.string = AIBS.string || {}; // namespace 57968 57969/** 57970 * lifted from Google Closure Library
57971 * Trims white spaces to the left and right of a string. 57972 * @param {string} s The string to trim. 57973 * @return {string} A trimmed copy of {@code str}. 57974 */ 57975AIBS.string.trim = function(s) { 57976 // Since IE doesn't include non-breaking-space (0xa0) in their \s character 57977 // class (as required by section 7.2 of the ECMAScript spec), we explicitly 57978 // include it in the regexp to enforce consistent cross-browser behavior. 57979 return s.replace(/^[\s\xa0]+|[\s\xa0]+$/g, ''); 57980}; 57981 57982 57983AIBS.string.stripNonNumerals = function(s) { 57984 return s.replace(/[^0-9]/g, ''); 57985}; 57986// requires pre-existing AIBS namespace 57987 57988AIBS.util = AIBS.util || {}; // namespace 57989 57990/** 57991 * 57992 * @param {!string} prefix TODO. 57993 * @param {*} o TODO. 57994 * @return {!string} TODO. 57995 */ 57996AIBS.util.inspect = function(prefix, o) { 57997 var s = ''; 57998 AIBS.object.forEach(o, function(val, key, o) { 57999 s += '\n' + prefix + 'key: ' + key; 58000 if (AIBS.typeOf(val) == 'object' && val !== null & key != 'element') { 58001 s += AIBS.util.inspect(prefix + ' ', val); 58002 } else { 58003 s += ', value: ' + val; 58004 } 58005 }); 58006 return s; 58007}; 58008"use strict"; 58009/** 58010 * An object encapsulating portions of the Allen Institute Analysis services, 58011 * like Differential search and Correlation search. These services have a different 58012 * workflow than the API data service in that these are session based, and they 58013 * have a different user focus, as they encapsulate calculations performed over the data 58014 * rather than the raw data itself. 58015 * <p> 58016 58017 * </p> 58018 * @class 58019 * @param service_url: The base url for the analysis services 58020 * @param options: A hash of the following (work in progress): 58021 * <ul> 58022 * <li> 58023 * <b>tineout</b>: A default value after which an ajax call will fail, given in milliseconds. Default = 10000. 58024 * </li> 58025 * <li> 58026 * <b>others</b>: TBD 58027 * </li> 58028 * </ul> 58029 */ 58030AIBS.Analysis.DAO = (function($j) { 58031 58032 return ( 58033 function(service_url, options) { 58034 58035 // public properties 58036 this.service_url = service_url; 58037 this.user_options = { 58038 timeout:10000, 58039 heartbeat_interval:600000 58040 }; 58041 $j.extend(true, this.user_options, options); 58042 58043 /** 58044 * Fetches an object and the specified associations 58045 * @param domain1 {Array<integer>} AKA the Contrast structures(s) 58046 * @param domain2 {Array<integer>} AKA the Target structures(s) 58047 * @param options An object containing: 58048 * <ul> 58049 * <li> 58050 * <b>success_callback</b>: A function to be called when the server responds with HTTP status 200 58051 * </li> 58052 * <li> 58053 * <b>error_callback</b>: a function to be called when the server responds with anything other than http status 200. 58054 * Both callback function should look like this: 58055 * <pre> 58056 * fn(response_data, text_status, xhdr) 58057 * response_data: The server's response to the request, already parsed into the requested format. 58058 * text_status: a string describing the status 58059 * xhdr: a superset of the browser's native XMLHttpRequest object. For example, it contains 58060 * responseText and responseXML properties, as well as a getResponseHeader() 58061 * </pre> 58062 * </li> 58063 * <li> 58064 * <b>timeout</b>: A number in milliseconds after which the error_callback will be called. 58065 * NOTE that this value overrides the global timeout value given as part of the DAO 58066 * initialization. That value defaults to 10 seconds. 58067 * </li> 58068 * </ul> 58069 */ 58070 this.create_differential_session = function(domain1, domain2, options) { 58071 58072 var settings = { 58073 domain1:domain1, 58074 domain2:domain2, 58075 image_set:'P56', 58076 format:'json', 58077 timeout:30000 58078 } 58079 58080 if(options) 58081 $j.extend(settings, options); 58082 58083 settings.verb = 'POST';
58084 _attempted_image_set = settings.image_set; 58085 var url = this.service_url + settings.image_set + '/search/differential_rows'; 58086 return(place_request(settings, url)); 58087 } 58088 58089 58090 /** 58091 * Run the correlation query that defines the session. Returns information about the 58092 * session, which looks like this: 58093 * <pre> 58094 * { 58095 msg:{ 58096 success: "OK" for success, an error message otherwise. 58097 code: 0 for success, an error code otherwise. 58098 rows_available: If the request succeeds, this contains the count of the total rows available in the session 58099 session_id: If the request succeeds, the ID of the session connected to this result set. 58100 } 58101 } 58102 * </pre> 58103 * @param {Array<integer>} domain 58104 * @param {integer} rows 58105 * @param (object) options 58106 * Options include: 58107 * <ul> 58108 * <li> 58109 * image_set: Either 'P56', for all the sections or 'P56Coronal' for just the coronal data. Default = 'P56' 58110 * </li> 58111 * <li> 58112 * sort_order: sorts according to the correlation 'r' value. Either 'asc' or desc'. Default = 'desc'. 58113 * </li> 58114 * <li> 58115 * timeout: time (in milliseconds) without response before the request returns an error. Default = 10000.. 58116 * </li> 58117 * <li> 58118 * <b>success_callback</b>: A function to be called when the server responds with HTTP status 200 58119 * </li> 58120 * <li> 58121 * <b>error_callback</b>: a function to be called when the server responds with anything other than http status 200. 58122 * Both callback function should look like this: 58123 * <pre> 58124 * fn(response_data, text_status, xhdr) 58125 * response_data: The server's response to the request, already parsed into the requested format. 58126 * text_status: a string describing the status 58127 * xhdr: a superset of the browser's native XMLHttpRequest object. For example, it contains 58128 * responseText and responseXML properties, as well as a getResponseHeader() 58129 * </pre> 58130 * </li> 58131 * </ul> 58132 */ 58133 this.create_correlation_session = function(domain, ages, rows, sort_order, options) { 58134 58135 var settings = { 58136 domain:domain, 58137 ages:ages, 58138 rows:rows, 58139 sort_order:'desc', 58140 timeout:3000 58141 } 58142 if(options) 58143 $j.extend(true, settings, options); 58144 58145 settings.verb = 'POST'; 58146 58147 var url = this.service_url + 'search/correlated_rows'; 58148 return(place_request(settings, url)); 58149 } 58150 58151 /** 58152 Lots of documentation goes here, but there isn't any yet. 58153 */ 58154 58155 this.create_agea_session = function(seed_point, options) { 58156 var settings = { 58157 seed_point: seed_point, 58158 image_set: '', 58159 map_age: 'P56', 58160 seed_age: 'P56' 58161 } 58162 58163 if (options) 58164 $j.extend(true, settings, options); 58165 58166 settings.verb = 'POST'; 58167 58168 _attempted_image_set = settings.image_set; 58169 // var url = this.service_url + settings.image_set + '/search/differential_rows'; 58170 var url = this.service_url + '/search/differential_rows'; 58171 58172 return(place_request(settings, url)); 58173 } 58174 58175 this.sort_correlated_rows = function(session_id, sort_order, options) { 58176 58177 var settings = { 58178 sort_order:sort_order, 58179 timeout:3000 58180 } 58181 58182 if(options) 58183 $j.extend(settings, options); 58184 58185 settings.verb = 'POST'; 58186 58187 var url = this.service_url + 'search/sort_rows/' + session_id; 58188 return(place_request(settings, url)); 58189 } 58190 58191 /** 58192 * Return some rows for the given session 58193 * @param session_id 58194 * @param options 58195 */ 58196 this.get_rows = function(session_id, options) { 58197 var settings = { 58198 verb:'GET', 58199 format:'json', 58200 timeout:3000, 58201 limit:100, 58202 offset:0, 58203 detail:1 58204 } 58205 58206 if(options) 58207 $j.extend(settings, options); 58208 58209 var url = this.service_url + 'search/list_rows/' + session_id; 58210 return(place_request(settings, url)); 58211 } 58212 58213 /** 58214 * @private 58215 * @scope AIBS.DATA.API_DAO 58216 */ 58217 var _self = this; 58218 var _current_session_id = ''; 58219 var _heartbeat_timer = null; 58220 var _current_image_set = '';
58221 var _attempted_image_set = ''; 58222 58223 // private methods 58224 function place_request(options, url) { 58225 // user options are parsed into usable formats 58226 var req_data = build_request(options); 58227 var req_options = build_options(options); 58228 58229 var request = { 58230 type:req_options.verb, 58231 url:url, 58232 data:req_options.verb == 'POST' ? JSON.stringify(req_data) : req_data, 58233 success: function(response_data, text_status, xhdr) {success_handler(req_options, response_data, text_status, xhdr)}, 58234 error: function(xhdr, text_status, error) {error_handler(options, xhdr, text_status, error)}, 58235 dataType:req_options.format 58236 } 58237 58238 if(options.parse_request) 58239 return(request); 58240 58241 // the success & error callback functions are 58242 // wrapped in order to do some housekeeping. 58243 request.success = function(response_data, text_status, xhdr) {success_handler(req_options, response_data, text_status, xhdr)}; 58244 request.error = function(xhdr, text_status, error) {error_handler(options, xhdr, text_status, error)}; 58245 $j.ajax(request); 58246 } 58247 58248 58249 function start_heartbeat(session_id) { 58250 _current_session_id = session_id; 58251 if(_heartbeat_timer) 58252 clearInterval(_heartbeat_timer); 58253 _heartbeat_timer = 0; 58254 58255 _current_image_set = _attempted_image_set; 58256 _heartbeat_timer = setInterval(beat, _self.user_options.heartbeat_interval); 58257 } 58258 58259 function stop_heartbeat() { 58260 _current_session_id = ''; 58261 _current_image_set = ''; 58262 58263 if(_heartbeat_timer) 58264 clearInterval(_heartbeat_timer); 58265 58266 _heartbeat_timer = 0; 58267 } 58268 58269 function beat() { 58270 var time = new Date().getTime(); 58271 var url = _self.service_url + _current_image_set + '/search/session_info/' + _current_session_id; 58272 58273 var request = { 58274 type:'GET', 58275 url:url, 58276 data:null, 58277 success:function(response_data, text_status, xhdr) { /* NOP */}, 58278 error:function(response_data, text_status, xhdr) { /* NOP */}, 58279 dataType:'json' 58280 } 58281 $j.ajax(request); 58282 } 58283 58284 function success_handler(callbacks, response_data, text_status, xhdr) { 58285 58286 if(!response_data) { 58287 callbacks.error_callback(response_data, text_status, xhdr); 58288 return; 58289 } 58290 if(response_data.status && response_data.status.id == 0) { 58291 var response = { 58292 response:{ 58293 success:response_data.status.message, 58294 code:response_data.status.id, 58295 rows_available:response_data.number_of_rows, 58296 session_id:response_data.session_id 58297 } 58298 }; 58299 callbacks.success_callback(response, text_status, xhdr); 58300 } 58301 else { // assume success 58302 response_data.response = { 58303 success:"OK", 58304 code:0 58305 } 58306 callbacks.success_callback(response_data, text_status, xhdr); 58307 } 58308 58309 if(response_data.session_id) { 58310 _current_image_set = _attempted_image_set; 58311 start_heartbeat(response_data.session_id); 58312 } 58313 } 58314 58315 function error_handler(callbacks, xhdr, text_status, error) { 58316 if(callbacks) { 58317 callbacks.error_callback(xhdr, text_status, error); 58318 } 58319 stop_heartbeat(); 58320 } 58321 58322 /** 58323 * Decoupling from the service parameters & semantics 58324 * @param options 58325 */ 58326 function build_request(options) { 58327 58328 var request = {}; 58329 if(options.limit) 58330 request.numRows = options.limit; 58331 if(options.offset >= 0) 58332 request.startRow = options.offset; 58333 if(options.domain1) 58334 request.domain1 = options.domain1; 58335 if(options.domain2) 58336 request.domain2 = options.domain2; 58337 if(options.detail) 58338 request.showDetail = options.detail; 58339 if(options.domain) 58340 request.domain = options.domain; 58341 if(options.rows) 58342 request.rows = options.rows; 58343 if(options.ages) 58344 request.ages = options.ages; 58345 if(options.sort_order) 58346 request.sort_order = options.sort_order; 58347 if(options.target_domain_threshold) 58348 request.target_domain_threshold = options.target_domain_threshold; 58349 if(options.contrast_domain_threshold) 58350 request.contrast_domain_threshold = options.contrast_domain_threshold; 58351 if (options.seed_point) 58352 request.seed_point = options.seed_point; 58353 if (options.correlation_map_thresholds) 58354 request.correlation_map_thresholds = options.correlation_map_thresholds; 58355 if (options.correlation_seed_age_threshold) 58356 request.correlation_seed_age_threshold = options.correlation_seed_age_threshold; 58357 if (options.correlation_map_age_threshold) 58358 request.correlation_map_age_threshold = options.correlation_map_age_threshold; 58359 if(options.map_age) 58360 request.map_age = options.map_age; 58361 if(options.seed_age) 58362 request.seed_age = options.seed_age; 58363 58364 58365 return(request); 58366 } 58367 58368 function build_options(options) { 58369 58370 var request = {}; 58371 request.success_callback = options.success_callback ? options.success_callback : function(){}; 58372 request.error_callback = options.error_callback ? options.error_callback : function(){}; 58373 request.verb = options.verb ? options.verb : 'GET'; 58374 request.format = options.format ? options.format : 'json'; 58375 return(request); 58376 } 58377 58378 function initialize() { 58379 58380 58381 // HEY! this is wacky. Possibly an interaction with the Prototype library which 58382 // adds toJSON methods to most objects, including Array, which DOESN'T WORK RIGHT. 58383 // I don't know how the removal of this method would affect other components that 58384 // depend on Prototype's Array.toJSON method (that doesn't work) but since it 58385 // doesn't work anyway, I'm going to go ahead and remove it because it is preventing 58386 // my JSON-formatted requests from working properly. Specifically, the Array.toJSON 58387 // that's causing me trouble is causing arrays to be quoted. 58388 //delete Array.prototype.toJSON; // NOTE Array.prototype.toJSON added by prototype lib is needed by the current NHP microarray theme 58389
58390 // do some ajax config. Caching, etc. 58391 // This should reference the user supplied DAO options. 58392 var settings = { 58393 timeout:_self.user_options.timeout 58394 } 58395 $j.ajaxSetup(settings); 58396 58397 } 58398 58399 58400 // initialize it all up, like. 58401 initialize(); 58402 58403 } 58404 ); 58405 58406 58407})(jQuery); 58408 58409"use strict"; 58410/** 58411 * An object encapsulating portions of the Allen Institute API service 58412 * <p> 58413 58414 * </p> 58415 * @class 58416 * @param service_url: The base url for the API data service 58417 * @param options: A hash of the following (work in progress): 58418 * <ul> 58419 * <li> 58420 * <b>tineout</b>: A default value after which an ajax call will fail, given in milliseconds. Default = 3000. Note that this 58421 * </li> 58422 * <li> 58423 * <b>others</b>: TBD 58424 * </li> 58425 * </ul> 58426 */ 58427AIBS.DATA.API_DAO = (function($j) { 58428 58429 return ( 58430 function(service_url, options) { 58431 58432 // public properties 58433 this.service_url = service_url; 58434 this.user_options = options ? options : {}; 58435 58436 /** 58437 * Fetches an object and the specified associations 58438 * @param type The name of the data model being queried. 58439 * @param id The ID of the instance being fetched. 58440 * @param options An object containing: 58441 * <ul> 58442 * <li> 58443 * <b>format</b>: 'json' or 'xml' 58444 * </li> 58445 * <li> 58446 * <b>verb</b>: 'get' or 'post' 58447 * </li> 58448 * <li> 58449 * <b>success_callback</b>: A function to be called when the server responds with HTTP status 200 58450 * </li> 58451 * <li> 58452 * <b>error_callback</b>: a function to be called when the server responds with anything other than http status 200. 58453 * Both callback function should look like this: 58454 * <pre> 58455 * fn(response_data, text_status, xhdr) 58456 * response_data: The server's response to the request, already parsed into the requested format. 58457 * text_status: a string describing the status 58458 * xhdr: a superset of the browser's native XMLHttpRequest object. For example, it contains 58459 * responseText and responseXML properties, as well as a getResponseHeader() 58460 * </pre> 58461 * </li> 58462 * <li> 58463 * <b>timeout</b>: A number in milliseconds after which the error_callback will be called. 58464 * NOTE that this value overrides the global timeout value given as part of the DAO 58465 * initialization. That value defaults to 10 seconds. 58466 * </li> 58467 * <li> 58468 * <b>cache</b>: If true, the result of this request will be cached. If the same request is made again in this session 58469 * the cached result will be returned, bypassing the ajax request cycle entirely. 58470 * </li> 58471 * <li> 58472 * <b>include</b>: A description of the associations and related criteria. See the big documentation on the RMA API 58473 * </li> 58474 * <li> 58475 * <b>exclude</b>: A comma-delimited list of fields to exclude from the result set. 58476 * </li> 58477 * <li> 58478 * <b>select</b>: A comma delimited list of fields to return in the result set. 58479 * </li> 58480 * <li> 58481 * <b>order</b>: A a comma-delimited list of fields by which the result set should be ordered. 58482 * </li> 58483 * <li> 58484 * <b>limit</b>: The maximum number of rows that should be returned in a result set. 58485 * </li> 58486 * <li> 58487 * <b>offset</b>: The offset into the entire result set that should be returned. 58488 * </li> 58489 * <li> 58490 * <b>query</b>: A string that, if provided, will be used for a search of the given Type. 58491 * </li> 58492 * </ul> 58493 */ 58494 this.fetch_by_id = function(type, id, options) { 58495 var url = this.service_url + type + '/' + id + '.' + options.format; 58496 return(place_request(options, url)); 58497 } 58498 58499 58500 /** 58501 * Queries the service for the specified type and its specified associations 58502 * @param type The name of the data model being queried.
58503 * @param options An object containing: 58504 * <ul> 58505 * <li> 58506 * <b>format</b>: 'json' or 'xml' 58507 * </li> 58508 * <li> 58509 * <b>verb</b>: 'get' or 'post' 58510 * </li> 58511 * <li> 58512 * <b>success_callback</b>: A function to be called when the server responds with HTTP status 200 58513 * </li> 58514 * <li> 58515 * <b>error_callback</b>: a function to be called when the server responds with anything other than http status 200. 58516 * Both callback function should look like this: 58517 * <pre> 58518 * fn(response_data, text_status, xhdr) 58519 * response_data: The server's response to the request, already parsed into the requested format. 58520 * text_status: a string describing the status 58521 * xhdr: a superset of the browser's native XMLHttpRequest object. For example, it contains 58522 * responseText and responseXML properties, as well as a getResponseHeader() 58523 * </pre> 58524 * </li> 58525 * <li> 58526 * <b>timeout</b>: A number in milliseconds after which the error_callback will be called. 58527 * NOTE that this value overrides the global timeout value given as part of the DAO 58528 * initialization. That value defaults to 10 seconds. 58529 * </li> 58530 * <li> 58531 * <b>cache</b>: If true, the result of this request will be cached. If the same request is made again in this session 58532 * the cached result will be returned, bypassing the ajax request cycle entirely. 58533 * </li> 58534 * <li> 58535 * <b>include</b>: A description of the associations and related criteria. See the big documentation on the RMA API 58536 * </li> 58537 * <li> 58538 * <b>exclude</b>: A comma-delimited list of fields to exclude from the result set. 58539 * </li> 58540 * <li> 58541 * <b>select</b>: A comma delimited list of fields to return in the result set. 58542 * </li> 58543 * <li> 58544 * <b>order</b>: A a comma-delimited list of fields by which the result set should be ordered. 58545 * </li> 58546 * <li> 58547 * <b>limit</b>: The maximum number of rows that should be returned in a result set. 58548 * </li> 58549 * <li> 58550 * <b>offset</b>: The offset into the entire result set that should be returned. 58551 * </li> 58552 * <li> 58553 * <b>query</b>: A string that, if provided, will be used for a search of the given Type. 58554 * </li> 58555 * </ul> 58556 */ 58557 this.query = function(type, options) { 58558 var url = this.service_url + type + '/query' + '.' + options.format; 58559 return(place_request(options, url)); 58560 } 58561 58562 58563 /** 58564 * Queries the service for a description of the specified type, its fields and associations 58565 * @param type The name of the data model being queried. 58566 * @param options An object containing: 58567 * <ul> 58568 * <li> 58569 * <b>format</b>: 'json' or 'xml' 58570 * </li> 58571 * <li> 58572 * <b>verb</b>: 'get' or 'post' 58573 * </li> 58574 * <li> 58575 * <b>success_callback</b>: A function to be called when the server responds with HTTP status 200 58576 * </li> 58577 * <li> 58578 * <b>error_callback</b>: a function to be called when the server responds with anything other than http status 200. 58579 * Both callback function should look like this: 58580 * <pre> 58581 * fn(response_data, text_status, xhdr) 58582 * response_data: The server's response to the request, already parsed into the requested format. 58583 * text_status: a string describing the status 58584 * xhdr: a superset of the browser's native XMLHttpRequest object. For example, it contains 58585 * responseText and responseXML properties, as well as a getResponseHeader() 58586 * </pre> 58587 * </li> 58588 * <li> 58589 * <b>timeout</b>: A number in milliseconds after which the error_callback will be called. 58590 * NOTE that this value overrides the global timeout value given as part of the DAO 58591 * initialization. That value defaults to 10 seconds. 58592 * </li> 58593 * <li> 58594 * <b>cache</b>: If true, the result of this request will be cached. If the same request is made again in this session 58595 * the cached result will be returned, bypassing the ajax request cycle entirely. 58596 * </li> 58597 * </ul> 58598 */ 58599 this.describe = function(type, options) { 58600 var url = this.service_url + type + '/describe' + '.' + options.format; 58601 return(place_request(options, url)); 58602 } 58603 58604 58605 /** 58606 * Enumerates all available types, their fields and associations
58607 * @param options An object containing: 58608 * <ul> 58609 * <li> 58610 * <b>format</b>: 'json' or 'xml' 58611 * </li> 58612 * <li> 58613 * <b>verb</b>: 'get' or 'post' 58614 * </li> 58615 * <li> 58616 * <b>success_callback</b>: A function to be called when the server responds with HTTP status 200 58617 * </li> 58618 * <li> 58619 * <b>error_callback</b>: a function to be called when the server responds with anything other than http status 200. 58620 * Both callback function should look like this: 58621 * <pre> 58622 * fn(response_data, text_status, xhdr) 58623 * response_data: The server's response to the request, already parsed into the requested format. 58624 * text_status: a string describing the status 58625 * xhdr: a superset of the browser's native XMLHttpRequest object. For example, it contains 58626 * responseText and responseXML properties, as well as a getResponseHeader() 58627 * </pre> 58628 * </li> 58629 * <li> 58630 * <b>timeout</b>: A number in milliseconds after which the error_callback will be called. 58631 * NOTE that this value overrides the global timeout value given as part of the DAO 58632 * initialization. That value defaults to 10 seconds. 58633 * </li> 58634 * <li> 58635 * <b>cache</b>: If true, the result of this request will be cached. If the same request is made again in this session 58636 * the cached result will be returned, bypassing the ajax request cycle entirely. 58637 * </li> 58638 * </ul> 58639 */ 58640 this.enumerate = function(options) { 58641 var url = this.service_url + 'enumerate' + '.' + options.format; 58642 return(place_request(options, url)); 58643 } 58644 58645 /** 58646 * Queries the service with the supplied customized data, skipping all the request building and response parsing 58647 * @param options An object containing: 58648 * <ul> 58649 * <li> 58650 * <b>format</b>: 'json' or 'xml' 58651 * </li> 58652 * <li> 58653 * <b>verb</b>: 'get' or 'post' 58654 * </li> 58655 * <li> 58656 * <b>success_callback</b>: A function to be called when the server responds with HTTP status 200 58657 * </li> 58658 * <li> 58659 * <b>error_callback</b>: a function to be called when the server responds with anything other than http status 200. 58660 * Both callback function should look like this: 58661 * <pre> 58662 * fn(response_data, text_status, xhdr) 58663 * response_data: The server's response to the request, already parsed into the requested format. 58664 * text_status: a string describing the status 58665 * xhdr: a superset of the browser's native XMLHttpRequest object. For example, it contains 58666 * responseText and responseXML properties, as well as a getResponseHeader() 58667 * </pre> 58668 * </li> 58669 * </ul> 58670 * @param url The url of this request, usually a url on the warehouse server 58671 * @param customized_request_data The data blob for the jquery.ajax() call's request.data 58672 */ 58673 this.customized_request = function(options, url, customized_request_data ) { 58674 return(place_customized_request(options, url, customized_request_data)); 58675 } 58676 58677 /** 58678 * @private 58679 * @scope AIBS.DATA.API_DAO 58680 */ 58681 var _self = this; 58682 58683 58684 // private methods 58685 function place_request(options, url) { 58686 58687 // user options are parsed into usable formats 58688 var req_data = build_request(options); 58689 var req_options = build_options(options); 58690 58691 var request = { 58692 // cacheResponse:true, 58693 type:req_options.verb, 58694 url:url, 58695 data:req_data, 58696 // the success & error callback functions are wrapped in order to do some housekeeping. 58697 success: function(response_data, text_status, xhdr) {success_handler(req_options, response_data, text_status, xhdr)}, 58698 error: function(xhdr, text_status, error) {error_handler(options, xhdr, text_status, error)}, 58699 dataType:req_options.format 58700 }; 58701 58702 if(options.parse_request) 58703 return(request); 58704 58705 $j.ajax(request); 58706 } 58707 58708 function place_customized_request(options, url, customized_req_data) { 58709 // user options are parsed into usable formats 58710 //var req_data = build_request(options); 58711 var req_options = build_options(options); 58712 58713 var request = { 58714 // cacheResponse:true, 58715 type:req_options.verb, 58716 url:url, 58717 //data:req_data, 58718 //success:function(response_data, text_status, xhdr) {success_handler(req_options, response_data, text_status, xhdr)}, 58719 //error:function(xhdr, text_status, error) {error_handler(options, xhdr, text_status, error)}, 58720 success:function(response_data, text_status, xhdr) {options.success_callback(response_data, text_status, xhdr);}, 58721 error:function(xhdr, text_status, error) {options.error_callback(xhdr, text_status, error);}, 58722 dataType:req_options.format 58723 } 58724 58725 if(customized_req_data) 58726 request.data = customized_req_data; 58727 58728 if(options.parse_request) 58729 return(request); 58730 58731 // the success & error callback functions are 58732 // wrapped in order to do some housekeeping. 58733 //request.success = function(response_data, text_status, xhdr) {success_handler(req_options, response_data, text_status, xhdr)}; 58734 //request.error = function(xhdr, text_status, error) {error_handler(options, xhdr, text_status, error)}; 58735 $j.ajax(request); 58736 } 58737 58738 function success_handler(callbacks, response_data, text_status, xhdr) { 58739 58740 if(!response_data || response_data.success == false) { 58741 callbacks.error_callback(response_data, text_status, xhdr); 58742 return; 58743 } 58744 58745 //TODO: handle XML properly 58746 if(response_data.success == true) { 58747 if(response_data.msg) 58748 response_data.msg.response = {success:response_data.success, code:response_data.id, rows_available:response_data.total_rows}; 58749 callbacks.success_callback(response_data.msg, text_status, xhdr); 58750 } 58751 else 58752 // if there is an error that's handled by the server, which then 58753 // returns an error code & HTTP 200, we have to identify it 58754 // here and call the user's error callback. 58755 callbacks.error_callback(response_data, text_status, xhdr); 58756 } 58757 58758 function error_handler(callbacks, xhdr, text_status, error) { 58759 if(callbacks) { 58760 callbacks.error_callback(xhdr, text_status, error); 58761 } 58762 } 58763 58764 /** 58765 * Decoupling from the service parameters & semantics 58766 * @param options 58767 */ 58768 function build_request(options) { 58769 58770 var request = {}; 58771 request.wrap = options.wrap ? options.wrap : 'true'; 58772 if(options.include) 58773 request.include = options.include; 58774 if(options.exclude) 58775 request.except = options.exclude; 58776 if(options.select) 58777 request.only = options.select; 58778 if(options.order) 58779 request.order = options.order; 58780 if(options.limit) 58781 request.numRows = options.limit; 58782 if(options.offset) 58783 request.startRow = options.offset; 58784 if(options.query) 58785 request.query = options.query; 58786 if(options.criteria) 58787 request.criteria = options.criteria; 58788 if(options.tabular) 58789 request.tabular = options.tabular; 58790 if(options.filter) 58791 request.filter = options.filter; 58792 return(request); 58793 } 58794 58795 function build_options(options) { 58796 58797 var request = {}; 58798 request.success_callback = options.success_callback ? options.success_callback : function(){}; 58799 request.error_callback = options.error_callback ? options.error_callback : function(){}; 58800 request.format = options.format ? options.format : 'json'; 58801 request.verb = options.verb ? options.verb : 'GET'; 58802 58803 return(request); 58804 } 58805 58806 function initialize() {
58807 // do some ajax config. Caching, etc. 58808 // This should reference the user supplied DAO options. 58809 var settings = { 58810 timeout:_self.user_options ? _self.user_options.timeout ? _self.user_options.timeout : 30000 : 30000 58811 } 58812 $j.ajaxSetup(settings); 58813 58814 } 58815 58816 // initialize it all up, like. 58817 initialize(); 58818 58819 } 58820 ); 58821 58822 58823})(jQuery); 58824"use strict"; 58825/** 58826 * 58827 * @param dao 58828 * @param options 58829 */ 58830function atlas_factory(dao, options) { 58831 58832 this.dao = dao; 58833 this.user_options= options; 58834 this.settings = { 58835 image_type:'', // used to differentiate P56 Sagittal for Adult, DevMouse atlases 58836 success_callback:null, 58837 error_callback:null 58838 }; 58839 58840 $j.extend(true, this.settings, options); 58841 58842 58843 this.get_detail = function(id) { 58844 get_atlas_detail(id); 58845 } 58846 58847 this.get_atlas_data_set_detail = function(data_set_id) { 58848 get_atlas_data_set_detail(data_set_id); 58849 } 58850 58851 // private 58852 var _self = this; 58853 58854 function get_atlas_detail(id) { 58855 58856 var opts = { 58857 success_callback:receive_atlas_detail, 58858 error_callback:receive_atlas_detail_error 58859 }; 58860 58861 $j.extend(true, _self.settings, opts); 58862 58863 set_atlas_query_includes(id, _self.settings); 58864 dao.fetch_by_id('Atlas', id, _self.settings); 58865 } 58866 58867 function get_atlas_data_set_detail(data_set_id) { 58868 58869 var opts = { 58870 success_callback:receive_atlas_data_set_detail, 58871 error_callback:receive_atlas_detail_error 58872 }; 58873 58874 $j.extend(true, _self.settings, opts); 58875 58876 set_dataset_query_includes(data_set_id, _self.settings); 58877 dao.fetch_by_id('AtlasDataSet', data_set_id, _self.settings); 58878 } 58879 58880 /** 58881 * 58882 * @param atlas_id atlas id 58883 * @param options 58884 */ 58885 function set_atlas_query_includes(atlas_id, options) { 58886 var a_id = parseInt(atlas_id); 58887 58888 switch(a_id) { 58889 case 1: // Mouse, P56 Coronal 58890 options.include += '(alternate_images[image_type$li\'Atlas - Adult Mouse\']))'; 58891 break; 58892 case 2: // Mouse, P56 Sagittal 58893 options.include += '(alternate_images[image_type$li\'Atlas - Adult Mouse\']))'; 58894 break; 58895 case 181275741: // 2013 E11.5 58896 case 181276130: // 2013 E13.5 58897 case 181276151: // 2013 E15.5 58898 case 181276160: // 2013 E18.5 58899 case 181276162: // 2013 P4 58900 case 181276164: // 2013 P14 58901 case 181276165: // 2013 P56 58902 options.include += '(alternate_images[image_type$li\'Atlas - Developing Mouse\']))'; 58903 break; 58904 case 112953465: // 2010 E11.5 58905 case 112953466: // 2010 E13.5 58906 case 112953467: // 2010 E15.5 58907 case 112953468: // 2010 E18.5 58908 case 112953469: // 2010 P4 58909 case 112953470: // 2010 P14 58910 case 112953471: // 2010 P56 58911 // no special includes 58912 break; 58913 default: 58914 options.include += ')'; 58915 58916 } 58917 } 58918 58919 /** 58920 * 58921 * @param data_set_id dataset id 58922 * @param options 58923 */ 58924 function set_dataset_query_includes(data_set_id, options) { 58925 var ds_id = parseInt(data_set_id); 58926 58927 switch(ds_id) { 58928 case 100048576: // Mouse, P56 Coronal 58929 options.include += '(alternate_images[image_type$li\'Atlas - Adult Mouse\'])'; 58930 break; 58931 case 100042147: // ABA Mouse, P56 Sagittal *AND* 2013 DevMouse P56 Sagittal 58932 if (options.image_type && options.image_type == AIBS.DATA.p56_sagittal_adult_atlas.image_type) 58933 options.include += '(alternate_images[image_type$li\'Atlas - Adult Mouse\'])'; 58934 else 58935 options.include += '(alternate_images[image_type$li\'Atlas - Developing Mouse\'])'; 58936 break; 58937 case 100020056: // 2013 E11.5 58938 case 100035755: // 2013 E13.5 58939 case 100040861: // 2013 E15.5 58940 case 100034997: // 2013 E18.5 58941 case 100033295: // 2013 P4 58942 case 100016572: // 2013 P14 58943 options.include += '(alternate_images[image_type$li\'Atlas - Developing Mouse\'])'; 58944 break; 58945 case 100054043: // 2010 E11.5 58946 case 100054042: // 2010 E13.5 58947 case 100054041: // 2010 E15.5 58948 case 100054040: // 2010 E18.5 58949 case 100054039: // 2010 P4 58950 case 100054038: // 2010 P14 58951 case 100054036: // 2010 P56 58952 // no special includes 58953 break; 58954 default: 58955 //nothing 58956 } 58957 } 58958 58959 function receive_atlas_detail(response_data, text_status, xhdr) { 58960 // the atlas image model is somewhat inside out. The point of this factory is to 58961 // turn right-side out again 58962 58963 // some atlases are inside out, some are sideways 58964 var atlas_id = parseInt(response_data[0].id); 58965 58966 switch(atlas_id) { 58967 case 1: // Mouse, P56 Coronal 58968 swap_primary_alternate(response_data[0].atlas_data_sets, response_data[0].name); 58969 break; 58970 case 2: // Mouse, P56 Sagittal 58971 swap_primary_alternate(response_data[0].atlas_data_sets, response_data[0].name); 58972 break; 58973 case 4: // Mouse Coronal, v1 (2008) 58974 change_name(response_data[0].atlas_data_sets, response_data[0].name); 58975 break; 58976 case 5: // Mouse Sagittal, v1 (2008) 58977 change_name(response_data[0].atlas_data_sets, response_data[0].name); 58978 break; 58979 case 181275741: // 2013 E11.5 58980 case 181276130: // 2013 E13.5 58981 case 181276151: // 2013 E15.5 58982 case 181276160: // 2013 E18.5 58983 case 181276162: // 2013 P4 58984 case 181276164: // 2013 P14 58985 case 181276165: // 2013 P56 58986 swap_primary_alternate(response_data[0].atlas_data_sets, response_data[0].name); 58987 break; 58988 case 112953465: // 2010 E11.5 58989 case 112953466: // 2010 E13.5 58990 case 112953467: // 2010 E15.5 58991 case 112953468: // 2010 E18.5 58992 case 112953469: // 2010 P4 58993 case 112953470: // 2010 P14 58994 case 112953471: // 2010 P56 58995 change_name(response_data[0].atlas_data_sets, response_data[0].name); 58996 default: 58997 // just return it as is 58998 } 58999 if(_self.user_options.success_callback) 59000 _self.user_options.success_callback(response_data, text_status, xhdr); 59001 } 59002 59003 59004 function receive_atlas_data_set_detail(response_data, text_status, xhdr) { 59005 59006 // the atlas image model is somewhat inside out. The point of this factory is to 59007 // turn right-side out again 59008 59009 // some atlases are inside out, some are sideways 59010 if(!response_data || !response_data[0]) { 59011 receive_atlas_detail_error({}, 'no data', 'No results found'); 59012 return; 59013 } 59014 59015 var data_set_id = parseInt(response_data[0].id); 59016 59017 var name = response_data[0].atlases[0].name; 59018 switch(data_set_id) { 59019 case 100048576: // Mouse, P56 Coronal 59020 swap_primary_alternate(response_data, name); 59021 break; 59022 case 100042147: // Mouse, P56 Sagittal 59023 swap_primary_alternate(response_data, name); 59024 break; 59025 case 100142144: // Mouse Sagittal, v1 (2008) 59026 change_name(response_data, name); 59027 break; 59028 case 100142143: // Mouse Coronal, v1 (2008) 59029 change_name(response_data, name); 59030 break; 59031 case 100020056: // 2013 E11.5 59032 case 100035755: // 2013 E13.5 59033 case 100040861: // 2013 E15.5 59034 case 100034997: // 2013 E18.5 59035 case 100033295: // 2013 P4 59036 case 100016572: // 2013 P14 59037 case 100042147: // 2013 P56 59038 swap_primary_alternate(response_data, name); 59039 break; 59040 case 100054043: // 2010 E11.5 59041 case 100054042: // 2010 E13.5 59042 case 100054041: // 2010 E15.5 59043 case 100054040: // 2010 E18.5 59044 case 100054039: // 2010 P4 59045 case 100054038: // 2010 P14 59046 case 100054036: // 2010 P56 59047 change_name(response_data, name); 59048 break; 59049 default: 59050 // just return it as is 59051 } 59052//alert("munged data set: " + inspect('', response_data)); 59053 if(_self.user_options.success_callback) 59054 _self.user_options.success_callback(response_data, text_status, xhdr); 59055 } 59056 59057 59058 // this method alters the given data_sets object by adding a 'section_images array. 59059 // the existing atlas_images array is not altered. 59060 function swap_primary_alternate(data_sets, name) { 59061 59062 // might there be multiple data sets in this atlas? The schema allows it. 59063 var data_set_count = data_sets.length; 59064 for(var i = 0; i < data_set_count; i++) { 59065 59066 // for this data set, make an additional image array 59067 var section_images = []; 59068 var atlas_images = data_sets[i].atlas_images; 59069 var image_count = atlas_images.length; 59070 59071 // swap the paths for primary & alternate images 59072 for(var j = 0; j < image_count; j++) { 59073 if(atlas_images[j].alternate_images && atlas_images[j].alternate_images.length) { 59074 59075 // make a copy of this image record 59076 var new_image = {}; 59077 $j.extend(true, new_image, atlas_images[j]); 59078 59079 // swap the paths from alternate to primary 59080 new_image.path = atlas_images[j].alternate_images[0].path; 59081 new_image.alternate_images[0].path = atlas_images[j].path; 59082 59083 section_images.push(new_image); 59084 } 59085 } 59086 59087 data_sets[i].section_images = section_images; 59088 data_sets[i].name = name; 59089 data_sets[i].atlas = true; 59090 } 59091 } 59092 59093 function change_name(data_sets, name) { 59094 59095 // might there be multiple data sets in this atlas? The schema allows it. 59096 var data_set_count = data_sets.length; 59097 for(var i = 0; i < data_set_count; i++) { 59098 59099 // for this data set, make an additional image array 59100 var section_images = []; 59101 var atlas_images = data_sets[i].atlas_images; 59102 var image_count = atlas_images.length; 59103 59104 // copy atlas_images into section_images 59105 for(var j = 0; j < image_count; j++) { 59106 // make a copy of this image record 59107 var new_image = {}; 59108 $j.extend(true, new_image, atlas_images[j]); 59109 section_images.push(new_image); 59110 } 59111 59112 data_sets[i].section_images = section_images; 59113 data_sets[i].name = name; 59114 data_sets[i].atlas = true; 59115 } 59116 } 59117 59118 function receive_atlas_detail_error(xhdr, text_status, error) { 59119 59120 if(_self.user_options.error_callback) 59121 _self.user_options.error_callback(xhdr, text_status, error); 59122 } 59123 59124 function inspect(prefix, obj) { 59125 var s = ""; 59126 for(var key in obj) { 59127 if((typeof obj[key]) == "function") 59128 s = s; 59129 else if((typeof obj[key]) == "object" && obj[key] !== null & key != "element") 59130 s += "\n" + prefix + "key: " + key + inspect(prefix + " ", obj[key]); 59131 else 59132 s += "\n" + prefix + "key: " + key + ", value: " + obj[key]; 59133 } 59134 return(s); 59135 } 59136} 59137; 59138"use strict";
59139// ABA P56 Sagittal Atlas has SAME dataset id as DevMouse P56 Atlas, but different images. Need this data structure info 59140// to differential between the two atlases. 'extension' is appended to 'data_set_id' to yield '100042147_P56_SAGITTAL_ADULT', 59141// which is used in IVT as the unique index/key to identify each viewer. 'image_type' is used an an url arg in the thumbnails 59142// url in lieu of 'atlas' to differentiate this atlas from the DevMouse atlas. You can change the values for 'extension' and 59143// 'image_type' here, and the values will be picked up and used without further changes. 59144AIBS.DATA.p56_sagittal_adult_atlas = { 59145 'atlas_id': 2, 59146 'data_set_id': 100042147, 59147 'extension': '_P56_SAGITTAL_ADULT', 59148 'image_type': 'atlas_p56_sagittal_adult' 59149}; 59150 59151// Used in ish_dao queries to figure out if a data_set_id belongs to an atlas 59152AIBS.DATA.atlas_dataset_lookup = { 59153 '100054043':'2010', // 2010 E11.5 59154 '100054042':'2010', // 2010 E13.5 59155 '100054041':'2010', // 2010 E15.5 59156 '100054040':'2010', // 2010 E18.5 59157 '100054039':'2010', // 2010 P4 59158 '100054038':'2010', // 2010 P14 59159 '100054036':'2010', // 2010 P56 59160 59161 '100020056':'2013', // 2013 E11.5 59162 '100035755':'2013', // 2013 E13.5 59163 '100040861':'2013', // 2013 E15.5 59164 '100034997':'2013', // 2013 E18.5 59165 '100033295':'2013', // 2013 P4 59166 '100016572':'2013', // 2013 P14 59167 '100042147':'2013', // 2013 P56 Sagittal (both atlas_id = 2, atlas_id = 181276165 map to same data_set_id!) 59168 '100048576':'2013' // 2013 P56 Coronal (not really 2013, it's really older but we use it with 2013 data) 59169}; 59170 59171// categorize the atlas based on its id 59172AIBS.DATA.atlas_id_lookup = { 59173 '1': 'ABA', // ABA Mouse, P56 Coronal 59174 '2': 'ABA', // ABA Mouse, P56 Sagittal 59175 59176 '112953465': '2010', // 2010 E11.5 59177 '112953466': '2010', // 2010 E13.5 59178 '112953467': '2010', // 2010 E15.5 59179 '112953468': '2010', // 2010 E18.5 59180 '112953469': '2010', // 2010 P4 59181 '112953470': '2010', // 2010 P14 59182 '112953471': '2010', // 2010 P56 59183 59184 '181275741': '2013', // 2013 E11.5 59185 '181276130': '2013', // 2013 E13.5 59186 '181276151': '2013', // 2013 E15.5 59187 '181276160': '2013', // 2013 E18.5 59188 '181276162': '2013', // 2013 P4 59189 '181276164': '2013', // 2013 P14 59190 '181276165': '2013' // 2013 P56 59191 59192}; 59193 59194// convert dataset id to atlas id. 100042147 deliberately omitted, because it can map to either 2 (ABA P56 Sagittal atlas) 59195// or 181276165 (2013 P56 atlas) 59196AIBS.DATA.dataset_to_atlas = { 59197 100048576:1, // Mouse, P56 Coronal 59198 100020056:181275741, // 2013 E11.5 59199 100035755:181276130, // 2013 E13.5 59200 100040861:181276151, // 2013 E15.5 59201 100034997:181276160, // 2013 E18.5 59202 100033295:181276162, // 2013 P4 59203 100016572:181276164, // 2013 P14 59204 59205 100054043:112953465, // 2010 E11.5 59206 100054042:112953466, // 2010 E13.5 59207 100054041:112953467, // 2010 E15.5 59208 100054040:112953468, // 2010 E18.5 59209 100054039:112953469, // 2010 P4 59210 100054038:112953470, // 2010 P14 59211 100054036:112953471 // 2010 P56 59212}; 59213 59214// determine if a dataset belongs to a Reference Set 59215// {age, theiler, plane, treatment 59216AIBS.DATA.reference_set_id_lookup = { 59217 '100020056': {age:'E11.5', theiler:'TS19', plane:'sagittal', treatment:'HP Yellow'}, 59218 '100015815': {age:'E11.5', theiler:'TS19', plane:'coronal', treatment:'HP Yellow'}, 59219 '100071965': {age:'E12.5', theiler:'TS20', plane:'sagittal', treatment:'HP Yellow'}, 59220 '100073791': {age:'E12.5', theiler:'TS20', plane:'coronal', treatment:'HP Yellow'}, 59221 '100035755': {age:'E13.5', theiler:'TS21', plane:'sagittal', treatment:'HP Yellow'}, 59222 '100026526': {age:'E13.5', theiler:'TS21', plane:'coronal', treatment:'HP Yellow'}, 59223 '100071964': {age:'E14.5', theiler:'TS23', plane:'sagittal', treatment:'HP Yellow'}, 59224 '100074513': {age:'E14.5', theiler:'TS23', plane:'coronal', treatment:'HP Yellow'}, 59225 '100040861': {age:'E15.5', theiler:'TS24', plane:'sagittal', treatment:'HP Yellow'}, 59226 '100020054': {age:'E15.5', theiler:'TS24', plane:'coronal', treatment:'HP Yellow'}, 59227 '100072704': {age:'E16.5', theiler:'TS24.5', plane:'sagittal', treatment:'HP Yellow'},
59228 '100075063': {age:'E16.5', theiler:'TS24.5', plane:'coronal', treatment:'HP Yellow'}, 59229 '100072703': {age:'E17.5', theiler:'TS25', plane:'sagittal', treatment:'HP Yellow'}, 59230 '100075359': {age:'E17.5', theiler:'TS25', plane:'coronal', treatment:'HP Yellow'}, 59231 '100034997': {age:'E18.5', theiler:'TS26', plane:'sagittal', treatment:'NISSL'}, 59232 '100054035': {age:'E18.5', theiler:'TS26', plane:'coronal', treatment:'NISSL'}, 59233 '100073354': {age:'P1', theiler:'', plane:'sagittal', treatment:'NISSL'}, 59234 '100102322': {age:'P1', theiler:'', plane:'coronal', treatment:'NISSL'}, 59235 '100098646': {age:'P1', theiler:'', plane:'sagittal', treatment:'HP Yellow'}, 59236 '100098647': {age:'P1', theiler:'', plane:'coronal', treatment:'HP Yellow'}, 59237 '100073355': {age:'P2', theiler:'', plane:'sagittal', treatment:'NISSL'}, 59238 '100102321': {age:'P2', theiler:'', plane:'coronal', treatment:'NISSL'}, 59239 '100098653': {age:'P2', theiler:'', plane:'coronal', treatment:'HP Yellow'}, 59240 '100033295': {age:'P4', theiler:'', plane:'sagittal', treatment:'NISSL'}, 59241 '100034998': {age:'P4', theiler:'', plane:'coronal', treatment:'NISSL'}, 59242 '100073790': {age:'P7', theiler:'', plane:'sagittal', treatment:'NISSL'}, 59243 '100098039': {age:'P7', theiler:'', plane:'coronal', treatment:'NISSL'}, 59244 '100016572': {age:'P14', theiler:'', plane:'sagittal', treatment:'NISSL'}, 59245 '100014118': {age:'P14', theiler:'', plane:'coronal', treatment:'NISSL'}, 59246 '100015421': {age:'P28', theiler:'', plane:'sagittal', treatment:'NISSL'}, 59247 '100024848': {age:'P28', theiler:'', plane:'coronal', treatment:'NISSL'}, 59248 '100042147': {age:'P56', theiler:'', plane:'sagittal', treatment:'NISSL'}, 59249 '100048576': {age:'P56', theiler:'', plane:'coronal', treatment:'NISSL'} 59250}; 59251 59252"use strict"; 59253/** 59254 * The ISH DAO leverages the AIBS.DATA.api_dao to access the api service. The methods 59255 * in this object effectively create a set of data views that are common to the ISH-centric 59256 * applications. 59257 * <p> 59258 59259 * </p> 59260 * @class 59261 * @param service_url: The base url for the API data service 59262 * @param options: A hash of the following (work in progress): 59263 * <ul> 59264 * <li> 59265 * <b>tineout</b>: A default value after which an ajax call will fail, given in milliseconds. Default = 3000. Note that this 59266 * </li> 59267 * <li> 59268 * <b>others</b>: TBD 59269 * </li> 59270 * </ul> 59271 */ 59272AIBS.DATA.ISH_DAO = (function ($j) { 59273 59274 return ( 59275 function (service_url, options) { 59276 // private, placed up here for visibility. Other private stuff way below. 59277 var _ish_product_id = '18'; // NHP ISH, also includes Molecular Map (pan gene) info 59278 59279 // public properties 59280 this.service_url = service_url; 59281 59282 this.user_options = { 59283 product: '', // Note: this value is overwritten by application.json's config.default.themes.ish.application.data_sets[] 59284 search_includes: "plane_of_section,specimen(donor(age)),treatments,probes(orientation,gene(gene_aliases)),products[id$in@products@]", 59285 search_excludes: "description,term,termtype,product_id,gene_id,plane_of_section_id," + 59286 "section_thickness,specimen_id,predicted_sequence_id,forward_sequence_id," + 59287 "reverse_sequence_id,label,orientation_id,probe_type," + 59288 "organism_id,chromosome_id,homologene_group_id,created_at,updated_at,gene_tag_type_id" 59289 }; 59290 59291 if (options) 59292 $j.extend(true, this.user_options, options); 59293 59294 // public methods 59295 this.gene_typeahead = function (term, options) { 59296 59297 if (!term || term.length < 1) 59298 throw "Empty term in type-ahead request"; 59299 59300 if (!options.exact_match) 59301 term = term + '*'; 59302 59303 options.criteria = "[product_id$in" + _self.user_options.product + "][term$il'" + term + "']"; // filter with the product info saved in the dao 59304 options.select = "gene_id,term"; 59305 options.order = "term"; 59306 59307 return(_the_dao.query('GeneLookup', build_option_defaults(options))); 59308 }; 59309 59310 this.geneNamesFromClassificationId = function (id, options) { 59311 options.criteria = '[id$eq' + id + ']'; 59312 options.include = 'genes'; 59313 options.select = 'genes.acronym,genes.name'; 59314 options.format = 'json'; 59315 59316 return(_the_dao.query('GeneClassification', build_option_defaults(options))); 59317 }; 59318 59319 this.ontology_graph = function (structure_graph_id, options) { 59320 59321 options.include = "[structure_graph_id$eq1]"; 59322 options.order = "st_order"; 59323 options.exclude = "structure_id_path,acronym_path,id,structure_graph_id"; 59324 59325 return(_the_dao.query('OntologyNode', build_option_defaults(options))); 59326 }; 59327 59328 this.structures_by_set = function (structure_set_id, options) { 59329 59330 options.include = "structure_sets_structures(structure)"; 59331 options.order = "structure_sets_structures.structure_order"; 59332 59333 return(_the_dao.fetch_by_id('StructureSet', structure_set_id, build_option_defaults(options))); 59334 }; 59335 59336 this.structure_set = function (id, options) { 59337 59338 options.include = 'structure_sets_structures'; 59339 options.order = "structure_sets_structures.structure_order"; 59340 // can't order by structure_order because it is not a field of the type being searched 59341 //options.order = 'structure_order'; 59342 59343 return(_the_dao.fetch_by_id('StructureSet', id, build_option_defaults(options))); 59344 }; 59345 59346 this.gene_detail = function (id, options) { 59347 options.include = 'organism,chromosome,gene_aliases,data_sets(
59347products[id$in' + _ish_product_id + '])'; 59348 return(_the_dao.fetch_by_id('Gene', id, build_option_defaults(options))); 59349 }; 59350 59351 this.gene_detail_and_experiments_info = function (id, options) { 59352 options.include = 'organism,chromosome,gene_aliases,data_sets[failed$eqfalse](products[id$in' + _ish_product_id + '],specimen(donor(age)))'; 59353 options.order = 'embryonic$desc,days$asc'; 59354 return(_the_dao.fetch_by_id('Gene', id, build_option_defaults(options))); 59355 }; 59356 59357 this.gene_experiments = function (id, options) { 59358 59359 options.include = 'data_sets(products[id$in' + _ish_product_id + '])'; 59360 return(_the_dao.fetch_by_id('Gene', id, build_option_defaults(options))); 59361 }; 59362 59363 59364 /** 59365 * Called by ish_ivt_controller.js 59366 * 59367 * @param atlas_id 59368 * @param options 59369 * @returns {*} 59370 */ 59371 this.atlas_images = function (atlas_id, options) { 59372 var atlas_type = AIBS.DATA.atlas_id_lookup[atlas_id]; 59373 if (atlas_type == '2010') 59374 options.include = 'atlas_data_sets(atlas_images'; 59375 else 59376 options.include = 'atlas_data_sets(atlas_images[sub_images.annotated$eqtrue]'; 59377 options.order = 'section_number'; 59378 var opts = build_option_defaults(options); 59379 59380 var atlases = new atlas_factory(_the_dao, opts); 59381 var atlas = atlases.get_detail(atlas_id); 59382 return(atlas); 59383 }; 59384 59385 this.atlas_by_structure_graph_id = function (structure_graph_id, options) { 59386 59387 options.include = "atlases"; 59388 options.order = "atlases.reference_space_id"; // it appears this will have all the atlases sorted by age 59389 59390 return(_the_dao.fetch_by_id('StructureGraph', structure_graph_id, build_option_defaults(options))); 59391 }; 59392 59393 /** 59394 * Gene Classification Search RMA 59395 * Primary search model is SectionDataSet, which is then associated with Gene Classifications, Specimens and Genes. 59396 * Ages span E40, E50, E70, E80, E90, 0 mo, 3 mo, 12 mo, 48 mo 59397 * Sex is M for all timepoints, and also include F for prenatal ages 59398 * 59399 * This query is almost identical to experiment_search_by_gene(), with the addition of gene_classification table. 59400 * 59401 * @param {Number} classification_id GeneClassification id 59402 * @param options 59403 * @returns {*} 59404 */ 59405 this.experiment_search_by_gene_classification = function (classification_id, options) { 59406 // override the default search parameters if necessary 59407 if (options) 59408 $j.extend(true, this.user_options, options); 59409 59410 var rma_only; 59411 if (options.format == 'xml') 59412 rma_only = ''; // show everything in xml rendering 59413 else { 59414 rma_only = "[only$eq'data_sets.id,genes.id,genes.acronym,genes.name,genes.organism_id,gene_classifications.id,gene_classifications.name,"; 59415 rma_only += "structures.name,donors.id,donors.sex_full_name,ages.embryonic,ages.name,ages.days,specimens.id']"; 59416 } 59417 59418 /* business logic: 59419 * - never show failed experiments in search results 59420 */ 59421 // treatment id = 1 is for ISH 59422 options.criteria = "rma::criteria,[failed$eqfalse],products[id$eq" + _ish_product_id + "],treatments[id$eq1],"; 59423 options.criteria += "rma::include,genes(homologous_genes,gene_classifications[id$eq" + classification_id + "][use$eq'Non-Human Primate ISH gene classifications'])" + ",specimen(structure,donor(age)),"; 59424 options.criteria += "rma::options"; 59425 options.criteria += rma_only; 59426 options.criteria += "[order$eq'genes.acronym$asc']"; 59427 options.order = options.order ? options.order : ""; 59428 59429 var all_opts = build_option_defaults(options); 59430 59431 return(_the_dao.query('SectionDataSet', all_opts)); 59432 }; 59433 59434 /** 59435 * Gene Search 59436 * Primary search model is SectionDataSet, which is then associated with Specimens and Genes. 59437 * Ages span E40, E50, E70, E80, E90, 0 mo, 3 mo, 12 mo, 48 mo 59438 * Sex is M for all timepoints, and also include F for prenatal ages 59439 * 59440 * @param term 59441 * @param options 59442 * @returns {*} 59443 */ 59444 this.experiment_search_by_gene = function (term, options) { 59445 59446 var reg_exp_pattern = /(and|or|not|"|\*)/i; 59447 if (term.charAt(0) == '"' || term.charAt(term.length - 1) == '"') 59448 term = term + ''; 59449 else if (options.exact_match) 59450 term = '"' + term + '"'; 59451 else if (!term.match(reg_exp_pattern)) // do not append '*' when special pattern exists 59452 term = '*' + term + '*'; 59453 59454 // 'query' is used for Sphinx indexed text search. 'facets' are used by IS
59454H Advanced Search 59455 var product_name_facet = options.product_name ? (" @product_name " + options.product_name) : ""; 59456 options.query = term + product_name_facet; 59457 59458 if (options.facets) { 59459 options.query += options.facets; 59460 } 59461 59462 59463 var rma_only; // rma::options[only] config 59464 if (options.format == 'xml') 59465 rma_only = ''; // show everything in xml rendering 59466 else { 59467 rma_only = "[only$eq'data_sets.id,genes.id,genes.acronym,genes.name,genes.organism_id,structures.name,"; 59468 rma_only += "donors.id,donors.name,donors.sex_full_name,ages.embryonic,ages.name,ages.days,specimens.id']"; 59469 } 59470 59471 /* business logic: 59472 * - never show failed experiments in search results 59473 */ 59474 // treatment id = 1 is for ISH 59475 options.criteria = "rma::criteria,[failed$eqfalse],products[id$eq" + _ish_product_id + "],treatments[id$eq1],"; 59476 options.criteria += "rma::include,genes(homologous_genes),specimen(structure,donor(age)),"; 59477 options.criteria += "rma::options"; 59478 options.criteria += rma_only; 59479 options.order = options.order ? options.order : "genes.acronym$asc"; 59480 59481 var all_opts = build_option_defaults(options); 59482 all_opts.timeout = 30000; // for some reason, the default timeout is 10 sec, which is too short for wildcard searches 59483 59484 // Note: could have used SectionDataSet, which has additional associations which are not needed for this project 59485 return(_the_dao.query('SectionDataSet', all_opts)); 59486 }; 59487 59488 this.inflate_datasets = function (ids, options) { 59489 59490 options.verb = 'POST'; 59491 options.include = '[data_sets.id$in' + ids + '],' + this.user_options.search_includes.replace('@products@', _ish_product_id); 59492 options.exclude = this.user_options.search_excludes; 59493 59494 var all_opts = build_option_defaults(options); 59495 59496 return(_the_dao.query('SectionDataSet', all_opts)); 59497 }; 59498 59499 /** 59500 * Get the dataset and image info associated with each of the data_set_ids in the ids array 59501 * This function is used by the IVT. 59502 * 59503 * @param ids array of data_set_ids 59504 * @param options 59505 * @returns {*} 59506 */ 59507 this.experiment_list = function (ids, options) { 59508 59509 options.include = '[data_sets.id$in' + ids + '],section_images(associates,alternate_images),products[id$in' + _ish_product_id + '],plane_of_section,genes,treatments,specimen(structure,donor(age))'; 59510 options.order = 'sub_images.section_number$asc'; 59511 options.reverse_coronals = false; 59512 59513 var all_opts = build_option_defaults(options); 59514 59515 return(_the_dao.query('SectionDataSet', all_opts)); 59516 }; 59517 59518 this.experiment_detail = function (id, options) { 59519 59520 options.include = 'genes,plane_of_section,treatments,specimen(donor(age,organism)),probes(orientation,predicted_sequence,forward_primer_sequence,reverse_primer_sequence),products[id$eq' + _ish_product_id + ']'; 59521 return(_the_dao.fetch_by_id('SectionDataSet', id, build_option_defaults(options))); 59522 }; 59523 59524 // combines details, images & expressions 59525 this.experiment_all = function (id, options) { 59526 59527 if (!options.structure_ids) 59528 throw "Invalid query; you must specify a set of structure IDs to retireve expression data related to this experiment"; 59529 59530 options.include = 'structure_unionizes(structure[id$in' + options.structure_ids + ']),section_images(associates,alternate_images),plane_of_section,treatments,specimen(donor(age,organism)),probes(orientation,predicted_sequence,forward_primer_sequence,reverse_primer_sequence,gene(organism,chromosome)),products[id$eq' + _ish_product_id + ']'; 59531 options.order = 'sub_images.section_number$asc'; 59532 options.reverse_coronals = false;
59533 59534 return(_the_dao.fetch_by_id('SectionDataSet', id, build_option_defaults(options))); 59535 }; 59536 59537 /** 59538 * 59539 * @param id data set id 59540 * @param options 59541 * @returns {*} 59542 */ 59543 this.experiment_images = function (id, options) { 59544 options.include = "genes,plane_of_section,treatments,specimen(structure,donor(age)),section_images(associates,alternate_images)"; 59545 options.order = 'sub_images.section_number$asc'; 59546 options.criteria = '[id$eq' + id + ']'; 59547 options.limit = 'all'; 59548 59549 return(_the_dao.query('SectionDataSet', build_option_defaults(options))); 59550 }; 59551 59552 /** 59553 * Used by dual_view_model for dual view SIV 59554 * 59555 * @param id 59556 * @param options 59557 * @returns {*} 59558 */ 59559 this.experiment_images_by_image = function (id, options) { 59560 options.include = "section_data_set(specimen(structure,donor(age)),genes,plane_of_section,section_images(alternate_images,associates))"; 59561 options.order = 'sub_images.section_number$asc'; 59562 options.criteria = '[id$eq' + id + ']'; 59563 options.limit = 'all'; 59564 59565 return(_the_dao.query('SectionImage', build_option_defaults(options))); 59566 }; 59567 59568 /** 59569 * Called from thumbnails_controller.js 59570 * 59571 * @param data_set_id dataset id 59572 * @param options hash with optional image_type 59573 * @returns {*} 59574 */ 59575 this.experiment_specimen = function (data_set_id, options) { 59576 59577 options.include = 'plane_of_section,treatments,specimen(donor(organism,age)),section_images(associates,alternate_images),genes,treatments,probes(orientation)'; 59578 options.order = 'sub_images.section_number$asc'; 59579 options.reverse_coronals = false; 59580 59581 return(_the_dao.fetch_by_id('SectionDataSet', data_set_id, build_option_defaults(options))); 59582 }; 59583 59584 this.experiment_expressions = function (id, options) { 59585 59586 var include = ''; 59587 if (options.structure_ids) 59588 include = 'structure_unionizes(structure[id$in' + options.structure_ids + '])'; 59589 else 59590 //TODO: fix this 59591 // this odd construction is due to a flaw in the parser that doesn't deal with the included :structure 59592 // unless there is a where clause referencing it. 59593 include = 'structure_unionizes(structure)'; 59594 59595 options.include = include; 59596 options.select = 'id,name,expression_energy,acronym,color_hex_triplet'; 59597 59598 return(_the_dao.fetch_by_id('SectionDataSet', id, build_option_defaults(options))); 59599 }; 59600 59601 59602 this.expression_set = function (experiment_ids, structure_ids, options) { 59603 59604 var include = '[id$in' + experiment_ids + '],structure_unionizes(structure[id$in' + structure_ids + '])'; 59605 59606 options.include = include; 59607 options.select = 'id,name,expression_energy,acronym,color_hex_triplet'; 59608 59609 return(_the_dao.query('SectionDataSet', build_option_defaults(options))); 59610 }; 59611 59612 this.specimen_detail = function (id, options) { 59613 options.include = "donor(age,conditions),data_sets(products[id$in" + _ish_product_id + "],genes,treatments),specimen_images,specimen_types,well_known_files,structure"; 59614 return(_the_dao.fetch_by_id('Specimen', id, build_option_defaults(options))); 59615 }; 59616 59617 this.specimen_children = function (parent_id, options) { 59618 59619 options.criteria = "[parent_id$eq" + parent_id + "]"; 59620 options.select = "id,parent_x_coord,parent_y_coord,parent_z_coord"; 59621 59622 if (options.has_experiments) 59623 options.criteria += ",data_sets"; 59624 59625 var all_opts = build_option_defaults(options); 59626 59627 return(_the_dao.query('Specimen', all_opts)); 59628 }; 59629 59630 // get images associated with the given specimen. Optional provide a treatment type & operator
59631 // for filtering the images. For example to get only NISSL images use 59632 // options = {treatment:'NISSL'}. FOr getting all images except NISSLS, use 59633 // options = {treatment:'NISSL', treatment_op:'ne'}. 59634 this.specimen_images_by_treatment = function (id, options) { 59635 59636 //options.include = 'section_data_set(genes,plane_of_section)'; 59637 var opts = { 59638 treatment: null, 59639 treatment_op: 'eq', 59640 criteria: "section_data_set(specimen[id$eq" + id + "])", 59641 order: 'sub_images.section_number$asc', 59642 limit: 'all' 59643 }; 59644 $j.extend(true, opts, options); 59645 59646 if (opts.treatment) 59647 opts.criteria += ",treatments[name$" + opts.treatment_op + "'" + opts.treatment + "']"; 59648 59649 return(_the_dao.query('SectionImage', build_option_defaults(opts))); 59650 }; 59651 59652 59653 this.specimen_images_by_donor = function (donor_id, specimen_types, options) { 59654 59655 if (options.section_images) 59656 options.include = "specimen_types,data_sets(treatments,section_images)"; 59657 else 59658 options.include = 'specimen_images,specimen_types'; 59659 59660 //options.include = 'specimen_images,specimen_types'; 59661 options.criteria = "[donor_id$eq" + donor_id + "],specimen_types[id$in" + specimen_types + "]"; 59662 options.select = 'id,name,path,width,height,x,y,section_number,failed,specimen_id,tier_count,resolution,image_width,image_height,image_type'; 59663 options.order = 'sub_images.section_number'; 59664 59665 return(_the_dao.query('Specimen', build_option_defaults(options))); 59666 }; 59667 59668 this.section_images_by_specimen = function (specimen_id, options) { 59669 59670 options.criteria = 'data_set[specimen_id$eq' + specimen_id + '][width$gt0]'; 59671 if (options.treatment && options.treatment.length) 59672 options.criteria += ",data_set(treatments[name$li'" + options.treatment + "'])"; 59673 59674 options.include = "section_data_set(genes),treatments"; 59675 options.select = 'id,name,path,width,height,x,y,section_number,failed,specimen_id,tier_count,resolution,image_width,image_height,image_type,acronym'; 59676 options.order = 'sub_images.section_number'; 59677 59678 return(_the_dao.query('SectionImage', build_option_defaults(options))); 59679 }; 59680 59681 this.specimen_images_by_specimen = function (specimen_id, options) { 59682 59683 options.criteria = '[specimen_id$eq' + specimen_id + '][width$gt0]'; 59684 if (options.image_type && options.image_type.length) 59685 options.criteria += "[image_type$li'" + options.image_type + "']"; 59686 59687 options.select = 'id,name,path,width,height,x,y,section_number,failed,specimen_id,tier_count,resolution,image_width,image_height,image_type'; 59688 options.order = 'sub_images.section_number'; 59689 59690 return(_the_dao.query('SubImage', build_option_defaults(options))); 59691 }; 59692 59693 59694 /********************************** 59695 * 59696 * functions used by Microarray theme 59697 * 59698 ***********************************/ 59699 // get the donor info based on product ids 59700 this.donor_info_by_product_ids = function (term, options) { 59701 if (!term || term.length < 1) 59702 term = this.user_options.product; 59703 59704 options.include = "products[id$in" + term + "],age,organism"; 59705 options.order = "ages.embryonic desc,ages.days,donors.name"; 59706 59707 return(_the_dao.query('Donor', build_option_defaults(options))); 59708 59709 }; 59710 59711 /* // look up the list of related probe ids based on a search term, for the microarray app 59712 this.probe_id_by_gene_term = function (term, options) { 59713 59714 if (!term || term.length < 1) 59715 throw "Empty term in probe_id_by_gene_term request"; 59716 59717 var product = this.user_options.product; 59718 if (options.product) 59719 product = options.product; 59720 59721 var reg_exp_pattern = /(and|or|not|"|\052)/i; 59722 if (term.charAt(0) == '"' || term.charAt(term.length - 1) == '"') 59723 term = term + ''; 59724 else if (options.exact_match) 59725 term = '"' + term + '"'; 59726 else if (!term.match(reg_exp_pattern)) // do not append '*' when special pattern exists 59727 term = '*' + term + '*'; 59728 59729 //options.include = "gene(gene_lookups[term$il'" + term + "']),products[id$in" + +this.user_options.product+ "]"; 59730 //options.select = "id"; 59731 //return(_the_dao.query('Probe', build_option_defaults(options))); 59732 59733 // try to use the sphinx index search 59734 // BUGBUG: we should also support probe name search, I think. 59735 options.include = "probes[probe_type$eq'DNA'](products[id$eq" + product + "])"; 59736 options.query = term; 59737 options.order = "acronym,probes.name"; 59738 options.select = "id"; 59739 options.limit = _num_rows; // this maps to the RMA option "numRows" later. 59740 return(_the_dao.query('Gene', build_option_defaults(options))); 59741 59742 }*/ 59743 59744/* 59745 // used in NHP microdisection theme 59746 this.affymetrix_probe_id_by_gene_term = function (term, options) { 59747 59748 if (!term || term.length < 1) 59749 throw "Empty term in probe_id_by_gene_term request"; 59750 59751 var product = this.user_options.product; 59752 if (options.product) 59753 product = options.product; 59754 59755 var reg_exp_pattern = /(and|or|not|"|\052)/i; 59756 if (term.charAt(0) == '"' || term.charAt(term.length - 1) == '"') 59757 term = term + ''; 59758 else if (options.exact_match) 59759 term = '"' + term + '"'; 59760 else if (!term.match(reg_exp_pattern)) { // do not append '*' when special pattern exists 59761 term = $j.trim(term); 59762 term = '*' + term + '*'; 59763 } 59764 59765 //options.criteria = "service::text_search[query_string$eq'@(acronym,gene_alias_acronym,name,entrez_id)+" + term + "'][k$eqGene],pi
59765pe::list[xid$eq'id'],model::AffymetrixMacaqueProbeset,rma::criteria,gene_association[gene_association_source_id$eq2][gene_feature_type$eq'AffymetrixMacaqueProbeset'](gene[id$in$xid](organism[name$eq'Macaca mulatta'])),rma::options[order$eq'genes.acronym,affymetrix_macaque_probesets.displayable_id_from_affymetrix'][num_rows$eq'all'][tabular$eq'distinct genes.acronym as gene,affymetrix_macaque_probesets.displayable_id_from_affymetrix as display_id,affymetrix_macaque_probesets.id as probe_id']"; 59766 // 59767 // switch to use the sphinx index on AffymetrixMacaqueProbeset model to include human gene results, see IT-7987 and related issues 59768 options.criteria = "service::text_search[query_string$eq'" + term + "'][k$eqAffymetrixMacaqueProbeset],pipe::list[xid$eq'id'],model::AffymetrixMacaqueProbeset,rma::criteria,[id$in$xid],gene_association[gene_association_source_id$eq2][gene_feature_type$eq'AffymetrixMacaqueProbeset'](gene),rma::options[order$eq'genes.acronym,affymetrix_macaque_probesets.displayable_id_from_affymetrix'][num_rows$eq'all'][tabular$eq'distinct genes.acronym as gene,affymetrix_macaque_probesets.displayable_id_from_affymetrix as display_id,affymetrix_macaque_probesets.id as probe_id']"; 59769 59770 return(_the_dao.query('AffymetrixMacaqueProbeset', build_option_defaults(options))); 59771 59772 59773 }; 59774*/ 59775 59776 59777 // used in NHP microdisection theme 59778 this.affymetrix_probe_id_by_gene_term = function (term, options) { 59779 59780 if (!term || term.length < 1) 59781 throw "Empty term in probe_id_by_gene_term request"; 59782 59783 var product = this.user_options.product; 59784 if (options.product) 59785 product = options.product; 59786 59787 var reg_exp_pattern = /(and|or|not|"|\052)/i; 59788 if (term.charAt(0) == '"' || term.charAt(term.length - 1) == '"') 59789 term = term + ''; 59790 else if (options.exact_match) 59791 term = '"' + term + '"'; 59792 else if (!term.match(reg_exp_pattern)) { // do not append '*' when special pattern exists 59793 term = $j.trim(term); 59794 term = '*' + term + '*'; 59795 } 59796 59797 //options.criteria = "service::text_search[query_string$eq'@(acronym,gene_alias_acronym,name,entrez_id)+" + term + "'][k$eqGene],pipe::list[xid$eq'id'],model::AffymetrixMacaqueProbeset,rma::criteria,gene_association[gene_association_source_id$eq2][gene_feature_type$eq'AffymetrixMacaqueProbeset'](gene[id$in$xid](organism[name$eq'Macaca mulatta'])),rma::options[order$eq'genes.acronym,affymetrix_macaque_probesets.displayable_id_from_affymetrix'][num_rows$eq'all'][tabular$eq'distinct genes.acronym as gene,affymetrix_macaque_probesets.displayable_id_from_affymetrix as display_id,affymetrix_macaque_probesets.id as probe_id']"; 59798 // 59799 // switch to use the sphinx index on AffymetrixMacaqueProbeset model to include human gene results, see IT-7987 and related issues 59800 options.criteria = "service::text_search[query_string$eq'" + term + "'][k$eqAffymetrixMacaqueProbeset],pipe::list[xid$eq'id'],model::AffymetrixMacaqueProbeset,rma::criteria,[id$in$xid],rma::include,gene_associations(gene),rma::options[order$eq'gene_associations.gene_association_source_id$desc,genes.type$desc,genes.acronym$asc,genes.organism_id$desc,affymetrix_macaque_probesets.displayable_id_from_affymetrix$asc'][num_rows$eq1000]"; 59801 59802 return(_the_dao.query('', build_option_defaults(options))); 59803 59804 59805 }; 59806 59807 59808 // look up the list of related probe ids based on a search term of gene classification ID, for the microarray app 59809 this.probe_id_by_gene_classification = function (term, options) { 59810 59811 if (!term || term.length < 1) 59812 throw "Empty term in probe_id_by_gene_classification request"; 59813 59814 var product = this.user_options.product; 59815 if (options.product) 59816 product = options.product; 59817 59818 options.include = "gene_classifications[id$eq" + term + "],probes[probe_type$eq'DNA'](products[id$eq" + product + "])"; 59819 options.order = "acronym,probes.name"; 59820 options.select = "id"; 59821 options.limit = _num_rows; // this maps to the RMA option "numRows" later. 59822 return(_the_dao.query('Gene', build_option_defaults(options))); 59823 59824 }; 59825 59826 this.describe = function (type, options) { 59827 return(_the_dao.describe(type, build_option_defaults(options))); 59828 }; 59829 59830 this.enumerate = function (options) { 59831 return(_the_dao.enumerate(build_option_defaults(options))); 59832 }; 59833 59834 this.customized_request = function (options, url, customized_request_data) { 59835 return(_the_dao.customized_request(options, url, customized_request_data)); 59836 }; 59837 59838 /** 59839 * @private 59840 * @scope AIBS.DATA.ISH_DAO 59841 */ 59842 var _self = this; 59843 var _the_dao = null; 59844 // block repeated requests for the same method before the 59845 // response to the previous call has completed. 59846 var _request_blocks = { 59847 gene_detail: false, 59848 gene_experiments: false, 59849 experiment_detail: false, 59850 experiment_images: false, 59851 experiment_probes: false, 59852 experiment_expressions: false,
59853 structure_set: false 59854 }; 59855 59856 var _num_rows = 'all'; // for rma queries 59857 59858 // private methods 59859 59860 /** 59861 * Prepare a string of comma delimited text items for an RMA '$in' clause by surrounding each item in single quotes 59862 * 59863 * @param str input string of comma delimited items 59864 * @returns {string} each item in input string surrounded by single quotes 59865 */ 59866 function surround_with_single_quotes(str) { 59867 var quoted = ''; 59868 59869 var the_array = str.split(','); 59870 for (var i = 0; i < the_array.length; ++i) { 59871 quoted += "'" + the_array[i] + "',"; 59872 } 59873 59874 // remove trailing comma 59875 quoted = quoted.substring(0, quoted.length - 1); 59876 59877 return quoted; 59878 } 59879 59880 function build_option_defaults(options) { 59881 var opts = { 59882 include: options.include, 59883 success_callback: function (response_data, text_status, xhdr) { 59884 success_handler(options, response_data, text_status, xhdr) 59885 }, 59886 error_callback: function (xhdr, text_status, error) { 59887 error_handler(options, xhdr, text_status, error) 59888 }, 59889 format: options.format ? options.format : 'json', 59890 verb: options.verb ? options.verb : 'GET' 59891 }; 59892 59893 if (options.reverse_coronals) 59894 opts.success_callback = function (response_data, text_status, xhdr) { 59895 success_handler_with_reverse(options, response_data, text_status, xhdr) 59896 }; 59897 else 59898 opts.success_callback = function (response_data, text_status, xhdr) { 59899 success_handler(options, response_data, text_status, xhdr) 59900 }; 59901 59902 if (options.select) 59903 opts.select = options.select; 59904 59905 if (options.exclude) 59906 opts.exclude = options.exclude; 59907 59908 if (options.order) 59909 opts.order = options.order; 59910 59911 if (options.parse_request) 59912 opts.parse_request = options.parse_request; 59913 59914 if (options.limit) 59915 opts.limit = options.limit; 59916 59917 if (options.offset != undefined) { 59918 opts.offset = options.offset; 59919 } 59920 59921 if (options.query != undefined) { 59922 opts.query = options.query; 59923 } 59924 59925 if (options.criteria != undefined) { 59926 opts.criteria = options.criteria; 59927 } 59928 59929 return(opts); 59930 } 59931 59932 59933 function success_handler(callbacks, response_data, text_status, xhdr) { 59934 try { 59935 callbacks.success_callback(response_data, text_status, xhdr); 59936 } 59937 finally { 59938 // whatever else happens, after the user callback is complete we're 59939 // ready for a new request _of the same method_. NOTE that this means that the user CAN NOT 59940 // make a new request for this same method in the handler for the previous request. 59941 // This might be trouble. 59942 // _request_blocks[callbacks.block_key] = false; 59943 } 59944 } 59945 59946 59947 function success_handler_with_reverse(callbacks, response_data, text_status, xhdr) { 59948 try { 59949 var len = response_data.length; 59950 for (var i = 0; i < len; i++) { 59951 var data = response_data[i]; 59952 // if this is a section_data_set AND it is coronal (plane_of_section == 1) reverse the images 59953 if (data.section_images && data.plane_of_section_id && data.plane_of_section_id == 1) 59954 data.section_images.reverse(); 59955 else if (data.section_data_set && data.section_data_set.section_images && data.section_data_set.plane_of_section_id && data.section_data_set.plane_of_section_id == 1) 59956 data.section_data_set.section_images.reverse(); 59957 } 59958 callbacks.success_callback(response_data, text_status, xhdr); 59959 } 59960 finally { 59961 // whatever else happens, after the user callback is complete we're 59962 // ready for a new request _of the same method_. NOTE that this means that the user CAN NOT 59963 // make a new request for this same method in the handler for the previous request. 59964 // This might be trouble. 59965 // _request_blocks[callbacks.block_key] = false;
59966 } 59967 } 59968 59969 function error_handler(callbacks, xhdr, text_status, error) { 59970 try { 59971 if (callbacks.error_callback) { 59972 callbacks.error_callback(xhdr, text_status, error); 59973 } 59974 } 59975 finally { 59976 // _request_blocks[callbacks.block_key] = false; 59977 } 59978 } 59979 59980 function init() { 59981 _the_dao = new AIBS.DATA.API_DAO(_self.service_url, _self.user_options); 59982 } 59983 59984 59985 function inspect(prefix, obj) { 59986 var s = ""; 59987 for (var key in obj) { 59988 if ((typeof obj[key]) == "function") 59989 s = s; 59990 else if ((typeof obj[key]) == "object" && obj[key] !== null & key != "element") 59991 s += "\n" + prefix + "key: " + key + inspect(prefix + " ", obj[key]); 59992 else 59993 s += "\n" + prefix + "key: " + key + ", value: " + obj[key]; 59994 } 59995 return(s); 59996 } 59997 59998 // initialization 59999 init(); 60000 60001 } 60002 ); 60003 60004 60005})(jQuery); 60006"use strict"; 60007/** 60008 * The Mapping DAO wraps the spatial mapping portion of the AIBS warehouse service. 60009 * <p> 60010 60011 * </p> 60012 * @class 60013 * @param service_url: The base url for the spatial mapping data service 60014 60015 */ 60016AIBS.DATA.Mapping_DAO = (function($j) { 60017 60018 return ( 60019 function(service_url, options) { 60020 60021 // public properties 60022 this.service_url = service_url; 60023 60024 this.settings = { 60025 // ? 60026 }; 60027 60028 if(options) 60029 $j.extend(true, this.settings, options); 60030 60031 60032 // public methods 60033 this.image_to_image = function(image_id, x, y, experiment_ids, options) { 60034 60035 var query_options = { 60036 format:'json', 60037 verb:'GET', 60038 success_callback:null, 60039 error_callback:null 60040 }; 60041 60042 var request = { 60043 x:x, 60044 y:y, 60045 section_data_set_ids:experiment_ids 60046 }; 60047 60048 $j.extend(true, query_options, options); 60049 60050 var url = this.service_url + "image_to_image/" + image_id; 60051 return(place_request(url, request, query_options)); 60052 } 60053 60054 this.image_to_atlas = function(image_id, x, y, atlas_id, options) { 60055 60056 var query_options = { 60057 format:'json', 60058 verb:'GET', 60059 success_callback:null, 60060 error_callback:null 60061 }; 60062 60063 var request = { 60064 x:x, 60065 y:y, 60066 atlas_id:atlas_id 60067 }; 60068 60069 $j.extend(true, query_options, options); 60070 60071 var url = this.service_url + "image_to_atlas/" + image_id; 60072 return(place_request(url, request, query_options)); 60073 } 60074 60075 60076 this.reference_to_image = function(reference_space_id, x, y, z, experiment_ids, options) { 60077 60078 var query_options = { 60079 format:'json', 60080 verb:'GET', 60081 success_callback:null, 60082 error_callback:null 60083 }; 60084 60085 var request = { 60086 x:x, 60087 y:y, 60088 z:z, 60089 section_data_set_ids:experiment_ids.join(',') 60090 }; 60091 60092 $j.extend(true, query_options, options); 60093 60094 var url = this.service_url + "reference_to_image/" + reference_space_id; 60095 return(place_request(url, request, query_options)); 60096 } 60097 60098 /** 60099 * @private 60100 * @scope AIBS.DATA.Mapping_DAO 60101 */ 60102 var _self = this; 60103 60104 60105 // private methods 60106 function place_request(url, query, options) { 60107 60108 var request = { 60109 type:options.verb, 60110 url:url + "." + options.format, 60111 data:query, 60112 success: function(response_data, text_status, xhdr) {success_handler(options, response_data, text_status, xhdr)}, 60113 error: function(xhdr, text_status, error) {error_handler(options, xhdr, text_status, error)}, 60114 dataType:options.format 60115 } 60116 60117 if(options.parse_request) 60118 return(request); 60119 60120 // the success & error callback functions are 60121 // wrapped in order to do some housekeeping. 60122 request.success = function(response_data, text_status, xhdr) {success_handler(options, response_data, text_status, xhdr)}; 60123 request.error = function(xhdr, text_status, error) {error_handler(options, xhdr, text_status, error)}; 60124 $j.ajax(request); 60125 } 60126 60127 function success_handler(callbacks, response_data, text_status, xhdr) { 60128 try { 60129 if(response_data && response_data.success != undefined && (response_data.success == true || response_data.success == 'true')) { 60130 callbacks.success_callback(response_data.msg, text_status, xhdr); 60131 } 60132 else { 60133 callbacks.error_callback(xhdr, text_status, response_data); 60134 } 60135 } 60136 finally { 60137 60138 } 60139 } 60140 60141 function error_handler(callbacks, xhdr, text_status, error) { 60142 60143 try { 60144 if(callbacks.error_callback) { 60145 callbacks.error_callback(xhdr, text_status, error); 60146 } 60147 } 60148 finally { 60149 60150 } 60151 } 60152 60153 function inspect(prefix, obj) { 60154 var s = ""; 60155 for(var key in obj) { 60156 if((typeof obj[key]) == "function") 60157 s = s; 60158 else if((typeof obj[key]) == "object" && obj[key] !== null & key != "element") 60159 s += "\n" + prefix + "key: " + key + inspect(prefix + " ", obj[key]); 60160 else 60161 s += "\n" + prefix + "key: " + key + ", value: " + obj[key]; 60162 } 60163 return(s); 60164 }
60165 function init() { 60166 60167 } 60168 60169 // initialization 60170 init(); 60171 60172 } 60173 ); 60174 60175 60176})(jQuery); 60177"use strict"; 60178// Brain Explorer Download Prompt -- brain_explorer_prompt.js 60179// Copyright (c) 2007-2013 Allen Institute for Brain Science. All Rights Reserved. 60180 60181/** 60182 * 60183 * @param anchor 60184 * @param url 60185 */ 60186function brain_explorer_link(anchor, url) { 60187/* Uses jQuery's dialog to prompt the user to download brain explorer 60188 if they don't have the setting (or the setting is false). 60189 60190 To use, create an empty div on the web page (will serve as the prompt) and 60191 a link element that points to the correct location (e.g. aibe://...). The 60192 prompt hijacks the onclick property of the link element to open the prompt 60193 if a supplied setting is false or unset. If the user clicks 'yes', the 60194 setting is set and the link is followed. If the user clicks 'no', the 60195 supplied download page is opened in a new page. 60196 60197 The prompt is initially disabled and must be enabled via 60198 prompt.set_link(destination). Disable the link via prompt.disable(). */ 60199 60200 var dialog_name = "brain_explorer_install_dialog"; 60201 var container = $j("#" + dialog_name); 60202 if (!container || !container.size()) 60203 { 60204 container = document.createElement("div"); 60205 container.setAttribute("id", dialog_name); 60206 } 60207 60208 this.dialog_container = $j(container); 60209 this.anchor = $j(anchor); 60210 this.settings = new user_data(); 60211// TODO: These next 2 should be global settings 60212 this.setting_name = "brain_explorer_2_installed"; 60213 this.download_page = "/static/brainexplorer"; 60214 if (url) 60215 this.set_url(url); 60216 else 60217 this.disable(); 60218 60219 var _self = this; 60220 60221 this.dialog_container 60222 .html('<p>Brain Explorer is a desktop application for viewing brain anatomy and gene expression in 3D.</p><p>Choose "Download" to proceed to the Brain Explorer download page.</p><p>Choose "View Data" if you have already installed Brain Explorer.</p>') 60223 .dialog({ autoOpen: false, 60224 modal: true, 60225 title: "Is Brain Explorer installed?", 60226 width: "500px", 60227 buttons: { "Download" : function() { _self.handle_prompt_no(); }, 60228 "View Data": function() { _self.handle_prompt_yes(); } } 60229 }); 60230 60231 anchor.click(function() { return _self.check_installed(); }); 60232} 60233 60234brain_explorer_link.prototype.set_url = function (destination) 60235{ 60236 this.anchor.attr('href', destination); 60237 this.enabled = true; 60238} 60239 60240brain_explorer_link.prototype.disable = function () 60241{ 60242 this.anchor.attr('href', 'javascript:void(0);'); 60243 this.enabled = false; 60244} 60245 60246brain_explorer_link.prototype.check_installed = function () 60247{ 60248 if (!this.enabled) 60249 { 60250 return false; 60251 } 60252 else if (this.settings.get(this.setting_name)) 60253 { 60254 return true; 60255 } 60256 else 60257 { 60258 $j(this.dialog_container).dialog('open'); 60259 return false; 60260 } 60261} 60262 60263brain_explorer_link.prototype.handle_prompt_yes = function (s) 60264{ 60265 this.dialog_container.dialog('close'); 60266 this.settings.set(this.setting_name, true); 60267 window.location = this.anchor.attr('href'); 60268} 60269 60270brain_explorer_link.prototype.handle_prompt_no = function s() 60271{ 60272 this.dialog_container.dialog('close'); 60273 window.open(this.download_page); 60274} 60275; 60276 60277 60278 60279 60280function data_download(container, download_service, session_dao, image_data, options) { 60281 60282 this.container = $j(container); 60283 this.download_service = download_service; 60284 this.session_dao = session_dao; 60285 this.image_data = image_data; 60286 this.settings = { 60287 default_start_row:1, 60288 default_num_rows:200, 60289 max_rows:2000 60290 }; 60291 $j.extend(true, this.settings, options); 60292 60293 60294 this.show_dialog = function() { 60295 60296 if(_dialog) 60297 _dialog.dialog('open'); 60298 } 60299 60300 var _self = this; 60301 var _dialog = null; 60302 60303 var _start_row = _self.settings.default_start_row; 60304 var _num_rows = _self.settings.default_num_rows; 60305 var _normalize = false; 60306 var _resolution = "unknown"; 60307 var _group_by = "donors,structures"; 60308 var _search_type = ""; 60309 60310 var _start_spinner = null; 60311 var _end_spinner = null; 60312 60313 60314 function build_scaffold() { 60315 60316 if(document.getElementById('download_dialog_wrapper')) 60317 return; 60318 60319 var html = ''; 60320 html += '<div id="download_dialog_wrapper">'; 60321 html += ' <div id="download_dialog_text">Download this data</div>'; 60322 html += '</div>'; 60323 60324 _self.container.append(html); 60325 $j('#download_dialog_text').bind('click', do_download_click); 60326 } 60327 60328 function build_dialog() { 60329 60330 60331 if(!document.getElementById('download_dialog_container')) { 60332 60333 var html = ''; 60334 html += '<div id="download_dialog_container" class="download_dialog_container">'; 60335 html += '<div id="download_dialog" class="download_dialog">'; 60336 html += ' <div id="download_dialog_header" class="download_dialog_header">';
60337 html += ' <span id="download_header_text" class="download_header_text">You may download up to 2000 rows at a time.</span>'; 60338 html += ' </div>'; 60339 html += ' <div id="download_download_input_container" class="download_download_input_container">'; 60340 html += ' <span id="download_start_spinner_container" class="download_start_spinner_container">'; 60341 html += ' Start row: <input type="text" id="download_start_input" value="1" class="download_spinner_input"/>'; 60342 html += ' </span>'; 60343 html += ' <span id="download_end_spinner_container" class="download_end_spinner_container">'; 60344 html += ' Number of rows: <input type="text" id="download_count_input" value="1" class="download_spinner_input"/>'; 60345 html += ' </span>'; 60346 html += ' </div>'; 60347 html += '</div>'; 60348 html += '</div>'; 60349 60350 $j(document.body).append(html); 60351 } 60352 60353 if(!_dialog) { 60354 _dialog = $j('#download_dialog').dialog({ 60355 autoOpen:false, 60356 minWidth:380, 60357 minHeight:160, 60358 resizable:false, 60359 title:'Data Download', 60360 buttons:[ 60361 {text:"Cancel", click:function() { $j(this).dialog('close');}}, 60362 {text:"Download", click:do_ok} 60363 ] 60364 }); 60365 } 60366 } 60367 60368 function request_download() { 60369 60370 var session = _self.session_dao.session; 60371 60372 var url = _self.download_service + '?session_id=' + session.session_id; 60373 url += '&start_row=' + (_start_row-1); 60374 url += '&num_rows=' + _num_rows; 60375 url += '&normalize=' + (_normalize ? 'true' : 'false'); 60376 url += '&groupBy=' + _group_by; 60377 url += '&resolution=' + _resolution; 60378 url += "&search_type=" + _search_type; 60379 60380 url += "&url=" + encodeURIComponent(window.location.href); 60381 60382 window.location.href = url; 60383 } 60384 60385 function do_ok() { 60386 60387 var valid = false; 60388 var reason = null; 60389 60390 try { 60391 60392 _start_row = parseInt(document.getElementById('download_start_input').value); 60393 _num_rows = parseInt(document.getElementById('download_count_input').value); 60394 _normalize = _self.image_data.get_format_options().normalize; 60395 _group_by = _self.session_dao.get_session_params().group_by; 60396 _resolution = _self.session_dao.get_session_params().column_set; 60397 _search_type = _self.session_dao.get_session_params().search_type; 60398 60399 } catch(e) { 60400 60401 warn('Both the start row and end row must be valid positive integers. Please try again.'); 60402 return; 60403 } 60404 60405 reason = validate(); 60406 if(reason) { 60407 warn(reason); 60408 return; 60409 } 60410 60411 _dialog.dialog('close'); 60412 request_download(); 60413 } 60414 60415 function warn(msg) { 60416 60417 alert(msg); 60418 } 60419 60420 function validate() { 60421 60422 if(_num_rows < 0 || _start_row < 0) 60423 return('Both the start row and end row must be valid positive integers. Please try again.'); 60424 60425 if(_start_row > _self.session_dao.session.number_of_probes) 60426 return("Your start row value '" + _start_row + "' exceeds the number of rows available. Please try again."); 60427 60428 if(_start_row - 1 + _num_rows > _self.session_dao.session.number_of_probes) 60429 return("Your indicated range of rows exceeds the total available. Please try again."); 60430 60431 return null; 60432 } 60433 60434 function do_download_click() { 60435 60436 // if they haven't already been created, create the spinners 60437 build_scaffold(); 60438 build_dialog(); 60439 60440 if(!_start_spinner ) { 60441 _start_spinner = document.getElementById('download_start_input'); 60442 if(_start_spinner) 60443 $j(_start_spinner).spinner({min:1, max:999999}); 60444 } 60445 60446 if(!_end_spinner ) { 60447 _end_spinner = document.getElementById('download_count_input'); 60448 if(_end_spinner) 60449 $j(_end_spinner).spinner({min:1, max:999999}); 60450 } 60451 60452 var available_rows = _self.session_dao.session.number_of_probes; 60453 60454 // set the header text depending on the task 60455 var row_overflow = (available_rows > _self.settings.max_rows); 60456 var row_count = row_overflow ? _self.settings.max_rows : available_rows; 60457 var msg = row_overflow ? "You may download up to 2000 rows at a time." : ("There are " + row_count + " rows available for download."); 60458 document.getElementById('download_header_text').innerHTML = msg; 60459 60460 // set the rows start & row count values to something reasonable for this data set 60461 $j('#download_count_input').spinner("option", "max", row_count); 60462 $j('#download_start_input').spinner("value", 1); 60463 $j('#download_count_input').spinner("value", row_count); 60464 60465 _dialog.dialog('open'); 60466 } 60467
60468 function init() { 60469 60470 build_scaffold(); 60471 build_dialog(); 60472 } 60473 60474 init(); 60475} 60476; 60477"use strict"; 60478/** 60479 * A tiny widget to place a "Download as XML link on the page. Methods include 60480 * "set_link", "show" and "hide". 60481 * 60482 * @param container 60483 * @param options 60484 */ 60485function data_linker(container, options) { 60486 60487 this.container = $j(container); 60488 this.settings = { 60489 link_text:"This data is also available as XML", 60490 link_title:"" 60491 }; 60492 $j.extend(true, this.settings, options); 60493 60494 60495 this.set_link = function (href) { 60496 60497 $j('#'+_id).attr('href', href); 60498 }; 60499 60500 this.show = function (show) { 60501 60502 if (show) 60503 _linker.show(); 60504 else 60505 _linker.hide(); 60506 } 60507 60508 60509 var _self = this; 60510 var _id = 'data_linker' + Math.floor(Math.random() * 10001); 60511 var _linker = null; 60512 60513 60514 function build_scaffold() { 60515 60516 var html = "<div class='data_linker' style='float:right;margin-top:-18px'>"; 60517 html += "<a id='" + _id + "' href='' target='_blank' title=" + _self.settings.link_title + ">" + _self.settings.link_text + "</a>"; 60518 html += "</div>"; 60519 60520 _linker = $j(html).appendTo(_self.container); 60521 } 60522 60523 build_scaffold(); 60524} 60525; 60526"use strict"; 60527/** 60528 * Modal dialog box that can display image filter sliders -- contrast, rgb, or ranged_rgb 60529 * When sliders change value, a user's callback function is invoked with event.filterVals set up as a comma-delimited string of 60530 * filter values appropriate to the current filter. The same callback is used to handle all of the filter types; the types 60531 * are differentiated by event.type. 60532 * This dialog designed to be accessed via a toolbar button on a zap_viewer. 60533 * 60534 * Once the dialog is instantiated on the page, it is alternatively shown/hidden, but not destroyed. 60535 * The contents of the dialog are deleted and re-created each time the dialog is shown. 60536 * This means each time the dialog is shown, you can swap in any of the filter slider types, as well as 60537 * changing their ranges and values. However, you can only show one slider type at any time. 60538 * 60539 * The sliders are created between optional header and footer text. 60540 * The actual dialog HTML lives on the display page, but it is constructed dynamically by this function. 60541 * 60542 * Each slider type is represented by a separate filterWidget instance. 60543 * The dialog box itself is from jQuery UI 60544 * 60545 * Assumption: this is the only instance of a filter_dialog on a given page. 60546 * If there are multiple zap_viewers on a page (like the IVT page), they should share a common filterDialog instance. 60547 * The filterDialog is set up to be modal to avoid dealing with the user interacting with the current zap_viewer (changing viewed image 60548 * type, which may require a different image filter), and with changing focus from one viewer to the next if multiple viewers are on the page. 60549 * 60550 * Usage: once the filterDialog is created, it is instantiated but invisible on the page, and contains no sliders. 60551 * Just prior to showing the dialog, you specify which slider type to display and provide the filter slider settings values. 60552 * 60553 * Example: open the dialog 60554 * dialogInstance.createFilterSliders(viewer, settings) // 'settings' has both the type, filter values, filter ranges, and options 60555 * dialogInstance.show() 60556 * 60557 * Example: close the dialog 60558 * dialogInstance.hide() 60559 * 60560 * @param owner - owner of the dialog, such as current zap viewer or zap wrapper which launched this dialog 60561 * @param container - CSS id of div that will contain the filter_dialog HTML. Can also be jQuery element. 60562 * @param callBacks - hash of optional callbacks for 'onChange', 'onOpen', 'onClose' 60563 * You 'onChange' callBack gets an event arg which has event.type which indicate the slider widget type<br/> 60564 * and event.filterVal, which is an imageservice-ready filter string reflecting changes to be applied to an image. 60565 * @param zindex - dialog's CSS z-index value. 60566 * 60567 * @return - filterDialog instance 60568 * 60569 * 60570 * settings - settings hash, defining settings for a unique filter slider widget type.<br/> 60571 * See below for description of hashes. 60572 * You must define settings to match each of the filter slider widget types called out in the 'types' argument. 60573 * --- Settings hash for 'contrast' slider widget (has one slider): 60574 * {'contrast': { 60575 * values: number, 60576 * ranges: [min, max], 60577 * filter_to_slider: number 60578 * options: {filter_label:{contrast:label}} 60579 * } 60580 * } 60581 * 60582 * where values = initial slider value 60583 * where ranges = lower and upper bounds of the slider 60584 * where filter_to_slider = multiplier used to convert filter value to displayed slider setting 60585 * where filter_label = text label associated with contrast slider 60586 * 60587 * --- Settings hash for 'rgb' filter slider widget (has three sliders): 60588 * 60589 * {'rgb': { 60590 * values: {r: number, g: number, b: number}, 60591 * ranges: {r: [min, max], g: [min, max], b: [min, max]}, 60592 * filter_to_slider: number, 60593 * options: {channel_toggle: {selected:{r:boolean, g:boolean, b:boolean}}, 60594 * slider_labels: {r:label, g:label, b:label}} 60595 * } 60596 * } 60597 * 60598 * where values = initial slider values for each slider 60599 * where ranges = lower and upper bounds of each slider 60600 * where filter_to_slider = multiplier used to convert filter value to displayed slider setting 60601 * where options.channel_toggle.selected indicates the selection state of each of the channel toggle checkboxes for each slider 60602 * where filter_label = text labels associated with red, green, blue sliders 60603 * 60604 * --- Settings hash for 'ranged_rgb' filter slider widget (has three sliders with two range slider handles per slider): 60605 * 60606 * {'ranged_rgb': { 60607 * values: {r: [low,hi], g: [low,hi], b: [low,hi]}, 60608 * ranges: {r: [min, max], g: [min, max], b: [min, max]}, 60609 * filter_to_slider: number, 60610 * options: {channel_toggle: {selected:{r:boolean, g:boolean, b:boolean}}, 60611 * algorithm: "", 60612 * slider_labels: {r:label, g:label, b:label}} 60613 * } 60614 * } 60615 * 60616 * where values = initial slider values for each slider, there is a low and high number for each slider, since there are two handles on each slider. 60617 * where ranges = lower and upper bounds of each slider 60618 * where options.channel_toggle.selected indicates the selection state of each of the channel toggle checkboxes for each slider 60619 * where options.algorithm is a placeholder for future callback functions to determine slider calibration 60620 * where filter_to_slider = multiplier used to convert filter value to displayed slider setting 60621 * where options.channel_toggle.selected indicates the selection state of each of the channel toggle checkboxes for each slider 60622 * where filter_label = text labels associated with red, green, blue sliders 60623 */ 60624 60625//function filterDialog(viewer, container, callBack, zindex) { 60626function filterDialog(owner, container, callBacks, zindex) { 60627 var _self = this; 60628 60629 var _owner = owner; 60630 var _outerContainer = jQuery(container); 60631// var _callBack = callBack; 60632 60633 var _contrastSettings; 60634 var _rgbSettings; 60635 var _rangedRgbSettings; 60636 var _sliderInstanceId = 0; 60637 var _contrastContainerName = "contrast_container_" + _sliderInstanceId; 60638 var _rgbContainerName = "rgb_container_" + _sliderInstanceId; 60639 var _rangedRgbContainerName = "ranged_rgb_container_" + _sliderInstanceId; 60640 var _filterWidgetContainer; 60641 var _contrastWidget; 60642 var _rgbWidget; 60643 var _rangedRgbWidget; 60644 60645 var _theDialog; 60646 var _savedOpacity; 60647 var _settings; 60648 var _type; 60649 60650 var _defaultValues; 60651 60652 var _onChange = callBacks['onChange'] == undefined ? undefined : callBacks['onChange']; 60653 var _onOpen = callBacks['onOpen'] == undefined ? undefined : callBacks['onOpen']; 60654 var _onClose = callBacks['onClose'] == undefined ? undefined : callBacks['onClose']; 60655 var _onReset = callBacks['onReset'] == undefined ? undefined : callBacks['onReset']; 60656 var _zindex = zindex !== undefined ? zindex : 20000; // applied during dialog construction, can't be done via CSS 60657 60658 // public 60659 this.setTitle = function(text) { 60660 _theDialog.dialog( "option", "title", text); 60661 } 60662 60663 this.setHeader = function(text) { 60664 _outerContainer.prepend(text); 60665 } 60666 60667 this.setFooter = function(text) { 60668 _outerContainer.append(text); 60669 } 60670 60671 this.show = function() { 60672 _theDialog.dialog("open"); 60673 } 60674 60675 this.hide = function() { 60676 _theDialog.dialog("close"); 60677 } 60678 60679 /* 60680 * Creates new filter sliders inside body of the filter dialog. 60681 * This removes and replaces the existing filter, as well 60682 * anything else in the body of the dialog, like added text. 60683 * 60684 * @param owner - dialog's owner 60685 * @param filterSettings - hash with same format as filterDialog's 'settings' arg 60686 */ 60687 this.createFilterSliders = function(owner, filterSettings) { 60688 _owner = owner; 60689 _outerContainer.empty(); // clear out DOM of filter elements 60690 60691 _contrastWidget = undefined; 60692 _rgbWidget = undefined; 60693 _rangedRgbWidget = undefined; 60694 60695 _contrastSettings = undefined; 60696 _rgbSettings = undefined; 60697 _rangedRgbSettings = undefined; 60698 60699 _settings = filterSettings; 60700 60701 if (_settings["contrast"]) 60702 _type = "contrast"; 60703 else if (_settings["rgb"]) 60704 _type = "rgb" 60705 else if (_settings["ranged_rgb"]) 60706 _type = "ranged_rgb" 60707 60708 _filterWidgetContainer = buildContainer(_outerContainer, _type); 60709 parseSettings(_type, _settings); 60710 createFilterWidgets(_type); 60711 } 60712 60713 /* 60714 * Total removal of filterDialog, including all DOM elements created 60715 * Not currently used. 60716 */ 60717 this.destroy = function() { 60718 jQuery("body").css("cursor", "auto"); 60719 _theDialog.dialog("destroy"); 60720 _outerContainer.empty(); 60721 } 60722 60723 /* 60724 * Set callbacks to the owner of this dialog. There are three possible callbacks that can be set: 60725 * onChange - called when the filter values change. An event object is passed along, with these attributes: 60726 * event.type - 'contrast', 'rgb', or 'ranged_rgb' 60727 * event.filterVals - a string with filter values. contrast will be "number", rgb will be "num1,num2,num3", 60728 * ranged_rgb will be "num1a,num1b,num2a,num2b,num3a,num3b" 60729 * onOpen - called when the dialog is opened for display 60730 * onClose - called when the dialog is closed/hidden 60731 * 60732 * @param callBacks - hash of callbacks, can have keys 'onChange', 'onOpen', 'onClose'. All keys are optional. 60733 */ 60734 this.setCallBacks = function(callBacks) {
60735 if (callBacks["onChange"]) _onChange = callBacks["onChange"]; 60736 if (callBacks["onOpen"]) _onOpen = callBacks["onOpen"]; 60737 if (callBacks["onClose"]) _onClose = callBacks["onClose"]; 60738 if (callBacks["onReset"]) _onReset = callBacks["onReset"]; 60739 } 60740 60741 /* 60742 * Gets the current filter dialog settings, including slider range and values, checkbox states. 60743 * This function is usually called when the filter dialog is close, so that the current settings can 60744 * be saved and restored the next time the dialog is opened. 60745 * 60746 * @return hash of filter settings in the same format as found in application.json in theme.application.filter_settings. 60747 * slider range and values are in the context of the actual filter. 60748 */ 60749 this.getFilterSettings = function() { 60750 var widget; 60751 if (_type == 'contrast') 60752 widget = _contrastWidget; 60753 else if (_type == 'rgb') 60754 widget = _rgbWidget; 60755 else if (_type == 'ranged_rgb') 60756 widget = _rangedRgbWidget; 60757 60758 var curRanges = widget.getCurrentRanges(); 60759 var curValues = widget.getCurrentValues(); 60760 var curOptions = widget.getCurrentOptions(); 60761 var curFilterToSlider = _settings[_type].filter_to_slider; 60762 var curWidth = _theDialog.dialog("widget").width(); 60763 60764 var settings = {}; 60765 settings[_type] = { 60766 values: curValues, 60767 ranges: curRanges, 60768 filter_to_slider: curFilterToSlider, 60769 options: curOptions 60770 } 60771 60772 settings["width"] = parseInt(curWidth); 60773 settings["zindex"] = _zindex; 60774 60775 var filterSettings; 60776 if (_type == 'contrast') 60777 filterSettings = convertContrastSettings(settings, 'toFilter'); 60778 else if (_type == 'rgb') 60779 filterSettings = convertRgbSettings(settings, 'toFilter'); 60780 else if (_type == 'ranged_rgb') 60781 filterSettings = convertRangedRgbSettings(settings, 'toFilter'); 60782 60783 filterSettings.type = _type; 60784 return filterSettings; 60785 } 60786 60787 /* 60788 * Utility function that overrides the filter values in an existing filter settings hash. 60789 * 60790 * @param filterName 60791 * @param filterVals - comma delimited string of filter values 60792 * @param settings - filter settings hash 60793 * 60794 * @return copy of modified settings hash, or an empty hash if there is an error 60795 */ 60796 this.overrideFilterValues = function(filterName, filterVals, settings) { 60797 var newSettings = {}; 60798 60799 // convert filter name to a slider name 60800 var sliderName = filterName == 'range' ? 'ranged_rgb' : filterName; 60801 60802 // ensure that the settings hash has a key with the sliderName 60803 if (settings[sliderName] == undefined) { 60804 // error condition, do nothing 60805 } 60806 else { 60807 // copy the original hash 60808 jQuery.extend(true, newSettings, settings); 60809 60810 var values = filterVals.split(","); 60811 60812 // insert filterVals into newSettings 60813 if (sliderName == 'contrast') { 60814 // contrast filter value has two values, but the first value never changes (0.5) 60815 newSettings['contrast'].values = values[1]; 60816 } 60817 else if (sliderName == 'rgb') { 60818 newSettings['rgb'].values.r = values[0]; 60819 newSettings['rgb'].values.g = values[1]; 60820 newSettings['rgb'].values.b = values[2]; 60821 60822 } 60823 else if (sliderName == 'ranged_rgb') { 60824 newSettings['ranged_rgb'].values.r[0] = values[0]; 60825 newSettings['ranged_rgb'].values.r[1] = values[1]; 60826 newSettings['ranged_rgb'].values.g[0] = values[2]; 60827 newSettings['ranged_rgb'].values.g[1] = values[3]; 60828 newSettings['ranged_rgb'].values.b[0] = values[4]; 60829 newSettings['ranged_rgb'].values.b[1] = values[5]; 60830 } 60831 } 60832 60833 return newSettings; 60834 } 60835 60836 /* 60837 * Utility that returns the CSS z-index value for the dialog. 60838 * This can be used by zap_viewer and other page components that need to juggle z-index values to ensure optimal visiblity 60839 */ 60840 this.getMaxZIndex = function() { 60841 return _zindex; 60842 } 60843 60844 // private 60845 60846 /* 60847 * Creates a jQuery UI Dialog, it will be created as a modal 60848 * dialog and will be hidden after creation. 60849 * 60850 * @param containerElement - is a jQuery element object which servers as the container for the sliders and other GUI objects. 60851 * @return - reference to jQuery Dialog 60852 */ 60853 function createDialog(containerElement) { 60854 // The actual dialog comprises a <div> that encloses the containerElement div. 60855 // So you can dynamically edit the contents containerElement without affecting the dialog itself 60856 var dialogRef = containerElement.dialog({ 60857 modal: true, 60858 draggable: true, 60859 resizable: true, 60860 autoOpen: false, 60861 zIndex: _zindex, 60862 open: doDialogOpen, 60863 close: doDialogClose, 60864 width: 370 60865 }); 60866 60867 return dialogRef; 60868 } 60869 60870 /* 60871 * UI housekeeping applied to page, cursors when the dialog is opened 60872 */ 60873 function doDialogOpen(event, ui) { 60874 // save existing overlay opacity style, then set it to zero. This is so the underlying viewer image will be WYSIWYG when 60875 // affected by dynamic slider changes.
60876 var overlay = jQuery(".ui-widget-overlay"); // this is a jQuery CSS class 60877 _savedOpacity = overlay.css("opacity"); // will automagically handle the IE nonstandard 'filter' style 60878 overlay.css("opacity", "0"); 60879 60880 // immediately change cursor to 'not-allowed' 60881 jQuery("body").css("cursor", "not-allowed"); 60882 60883 // change cursors when over/out of the dialog 60884 _theDialog.hover( 60885 function () { 60886 _theDialog.css("cursor", "auto"); // over 60887 }, 60888 function () { 60889 jQuery("body").css("cursor", "not-allowed"); // out 60890 } 60891 ); 60892 60893 if (_onOpen) 60894 _onOpen(_owner, event); 60895 } 60896 60897 /* 60898 * UI housekeeping applied to page, cursors when the dialog is closed 60899 */ 60900 function doDialogClose(event, ui) { 60901 jQuery("body").css("cursor", "auto"); 60902 60903 // TODO: figure out why we cannot restore opacity setting 60904 // restore overlay opacity styles. This *not working* for some reason. 60905 jQuery(".ui-widget-overlay").css("opacity", _savedOpacity); 60906 60907 if (_onClose) 60908 _onClose(_owner, event); 60909 } 60910 60911 /* 60912 * Dynamically add filter slider container (divs) as child of the outerContainer. 60913 * 60914 * @param outerContainer 60915 * @param type filter type which needs a container 60916 * @return jQuery element of the new filter slider container 'div', or undefined if type is unknown 60917 */ 60918 function buildContainer(outerContainer, type) { 60919 var html = ''; 60920 var type; 60921 var containerName; 60922 var containerElement; 60923 60924 if (type == 'contrast') { 60925 containerName = _contrastContainerName; 60926 } 60927 else if (type == 'rgb') { 60928 containerName = _rgbContainerName; 60929 } 60930 else if (type == 'ranged_rgb') { 60931 containerName = _rangedRgbContainerName; 60932 } 60933 60934 if (containerName !== undefined) { 60935 html = '<div id="' + containerName + '" style="display:block"</div>'; 60936 jQuery(outerContainer).prepend(html); 60937 60938 containerElement = jQuery("#" + containerName); 60939 } 60940 60941 return containerElement; 60942 } 60943 60944 /* 60945 * Parse out the filter slider settings, and convert filter values to slider display values. 60946 * Will update one of the following vars: _contrastSettings, _rgbSettings, _rangedRgbSettings 60947 * 60948 * @param type slider type 60949 * @param settings filter slider settings 60950 */ 60951 function parseSettings(type, settings) { 60952 var direction = "toSlider"; 60953 60954 if (type == 'contrast') 60955 _contrastSettings = convertContrastSettings(settings, direction); 60956 else if (type == 'rgb') 60957 _rgbSettings = convertRgbSettings(settings, direction); 60958 else if (type == 'ranged_rgb') 60959 _rangedRgbSettings = convertRangedRgbSettings(settings, direction); 60960 60961 } 60962 60963 /* 60964 * Convert contrast settings from filter --> slider or slider --> filter 60965 * 60966 * contrast settings: 60967 * {"contrast":{ 60968 * values:number, 60969 * ranges:[number,number], 60970 * filter_to_slider:number, 60971 * options:{channel_toggle:{selected:boolean} } 60972 * } 60973 * } 60974 * 60975 * @param settings - settings hash, numerical values are either in filter or slider context 60976 * @param direction - conversion direction, values are "toFilter" or "toSlider" 60977 * @return hash with same keys as 'settings' but whose numeric values are converted 60978 */ 60979 function convertContrastSettings(settings, direction) { 60980 var converted = {}; 60981 60982 // apply conversions to a copy of the existing settings 60983 jQuery.extend(true, converted, settings); 60984 60985 var multiplier; 60986 if (direction == "toSlider") 60987 multiplier = settings["contrast"].filter_to_slider; 60988 else if (direction == "toFilter") 60989 multiplier = 1/(settings["contrast"].filter_to_slider); 60990 60991 converted["contrast"].values = settings["contrast"].values * multiplier; 60992 converted["contrast"].ranges[0] = settings["contrast"].ranges[0] * multiplier; 60993 converted["contrast"].ranges[1] = settings["contrast"].ranges[1] * multiplier; 60994 60995 return converted; 60996 } 60997 60998 /* 60999 * Convert rgb settings from filter --> slider or slider --> filter 61000 * 61001 * rgb settings structure: 61002 * {"rgb": { 61003 * values:{r:number, g:number, b:number}, 61004 * ranges:{r:[number,number], g:[number,number], b:[number,number]}, 61005 * filter_to_slider:number, 61006 * options:{channel_toggle:{selected:boolean} } 61007 * } 61008 * } 61009 * 61010 * @param settings - settings hash, numerical values are either in filter or slider context 61011 * @param direction - conversion direction, values are "toFilter" or "toSlider" 61012 * @return hash with same keys as 'settings' but whose numeric values are converted 61013 */
61014 function convertRgbSettings(settings, direction) { 61015 var converted = {}; 61016 jQuery.extend(true, converted, settings); 61017 61018 var multiplier; 61019 if (direction == "toSlider") 61020 multiplier = settings["rgb"].filter_to_slider; 61021 else if (direction == "toFilter") 61022 multiplier = 1/(settings["rgb"].filter_to_slider); 61023 61024 converted["rgb"].values.r = settings["rgb"].values.r * multiplier; 61025 converted["rgb"].values.g = settings["rgb"].values.g * multiplier; 61026 converted["rgb"].values.b = settings["rgb"].values.b * multiplier; 61027 61028 converted["rgb"].ranges.r[0] = settings["rgb"].ranges.r[0] * multiplier; 61029 converted["rgb"].ranges.r[1] = settings["rgb"].ranges.r[1] * multiplier; 61030 converted["rgb"].ranges.g[0] = settings["rgb"].ranges.g[0] * multiplier; 61031 converted["rgb"].ranges.g[1] = settings["rgb"].ranges.g[1] * multiplier; 61032 converted["rgb"].ranges.b[0] = settings["rgb"].ranges.b[0] * multiplier; 61033 converted["rgb"].ranges.b[1] = settings["rgb"].ranges.b[1] * multiplier; 61034 61035 return converted; 61036 } 61037 61038 /* 61039 * Convert ranged_rgb settings from filter --> slider or slider --> filter 61040 * 61041 * ranged_rgb settings structure: 61042 * {"ranged_rgb": { 61043 * values:{r:[number,number], g:[number,number], b:[number,number]}, 61044 * ranges:{r:[number,number], g:[number,number], b:[number,number]}, 61045 * filter_to_slider:number, 61046 * options:{channel_toggle:{selected:{r:boolean, g:boolean, b:boolean}}, 61047 * algorithm:{callback or empty string or undefined}} 61048 * } 61049 * } 61050 * 61051 * @param settings - settings hash, numerical values are either in filter or slider context 61052 * @param direction - conversion direction, values are "toFilter" or "toSlider" 61053 * @return hash with same keys as 'settings' but whose numeric values are converted 61054 */ 61055 function convertRangedRgbSettings(settings, direction) { 61056 var converted = {}; 61057 jQuery.extend(true, converted, settings); 61058 61059 var multiplier; 61060 if (direction == "toSlider") 61061 multiplier = settings["ranged_rgb"].filter_to_slider; 61062 else if (direction == "toFilter") 61063 multiplier = 1/(settings["ranged_rgb"].filter_to_slider); 61064 61065 converted["ranged_rgb"].values.r[0] = Math.round(settings["ranged_rgb"].values.r[0] * multiplier); 61066 converted["ranged_rgb"].values.r[1] = Math.round(settings["ranged_rgb"].values.r[1] * multiplier); 61067 converted["ranged_rgb"].values.g[0] = Math.round(settings["ranged_rgb"].values.g[0] * multiplier); 61068 converted["ranged_rgb"].values.g[1] = Math.round(settings["ranged_rgb"].values.g[1] * multiplier); 61069 converted["ranged_rgb"].values.b[0] = Math.round(settings["ranged_rgb"].values.b[0] * multiplier); 61070 converted["ranged_rgb"].values.b[1] = Math.round(settings["ranged_rgb"].values.b[1] * multiplier); 61071 61072 converted["ranged_rgb"].ranges.r[0] = Math.round(settings["ranged_rgb"].ranges.r[0] * multiplier); 61073 converted["ranged_rgb"].ranges.r[1] = Math.round(settings["ranged_rgb"].ranges.r[1] * multiplier); 61074 converted["ranged_rgb"].ranges.g[0] = Math.round(settings["ranged_rgb"].ranges.g[0] * multiplier); 61075 converted["ranged_rgb"].ranges.g[1] = Math.round(settings["ranged_rgb"].ranges.g[1] * multiplier); 61076 converted["ranged_rgb"].ranges.b[0] = Math.round(settings["ranged_rgb"].ranges.b[0] * multiplier); 61077 converted["ranged_rgb"].ranges.b[1] = Math.round(settings["ranged_rgb"].ranges.b[1] * multiplier); 61078 61079 return converted; 61080 } 61081 61082 /* 61083 * Build filter widgets, based on the existence of the corresponding container. 61084 * In other words, only build widgets if there is already a place for them to live. 61085 * Values and ranges should be in display context of the slider, not of the filter. 61086 * 61087 * @param type filter slider type (contrast, rgb, ranged_rgb) 61088 */ 61089 function createFilterWidgets(type) { 61090 var ranges; 61091 var values; 61092 var options; 61093 var imageType = ""; 61094 61095 if (type == "contrast") { 61096 ranges = []; 61097 options = {}; 61098 values = _contrastSettings["contrast"].values; 61099 ranges = _contrastSettings["contrast"].ranges; 61100 options = _contrastSettings["contrast"].options; 61101 imageType = _owner.get_current_image_type(); 61102 _contrastWidget = new filterWidget("contrast", _filterWidgetContainer, 0, sliderDialogCallBack, ranges, values, options, imageType); 61103 } 61104 if (type == "rgb") { 61105 values = {}; 61106 ranges = {}; 61107 options = {}; 61108 values = _rgbSettings["rgb"].values; 61109 ranges = _rgbSettings["rgb"].ranges; 61110 options = _rgbSettings["rgb"].options; 61111 imageType = _owner.get_current_image_type(); 61112 _rgbWidget = new filterWidget("rgb", _filterWidgetContainer, 0, sliderDialogCallBack, ranges, values, options, imageType); 61113 } 61114 if (type == "ranged_rgb") { 61115 values = {}; 61116 ranges = {}; 61117 options = {}; 61118 values = _rangedRgbSettings["ranged_rgb"].values; 61119 ranges = _rangedRgbSettings["ranged_rgb"].ranges; 61120 options = _rangedRgbSettings["ranged_rgb"].options; 61121 imageType = _owner.get_current_image_type(); 61122 if (options["algorithm"] == undefined || options["algorithm"] == "") { 61123 options["algorithm"] = pixelMapper; // algorithmic transform object, only used by ranged_rgb filter widget 61124 } 61125 61126 _rangedRgbWidget = new filterWidget("ranged_rgb", _filterWidgetContainer, 0, sliderDialogCallBack, ranges, values, options, imageType); 61127 } 61128 } 61129 61130 /* 61131 * Called whenever sliders change value. 61132 * Attaches an additional properties "owner" and "filterVals" to the event object passed to the user's onChange c
61132allback. 61133 * 'filterVals' is a comma-delimited string of filter values appropriate for the current filter 61134 */ 61135 function sliderDialogCallBack(event, ui) { 61136 var type = event.sliderType; 61137 61138 var sliderValues; 61139 var sliderWidget; 61140 var multiplier; 61141 var filterString; 61142 61143 // determine which slider widget changed values 61144 if (type == 'contrast') { 61145 sliderWidget = _contrastWidget; 61146 } 61147 else if (type == 'rgb') { 61148 sliderWidget = _rgbWidget; 61149 } 61150 else if (type == 'ranged_rgb') { 61151 sliderWidget = _rangedRgbWidget; 61152 } 61153 61154 if (sliderWidget !== undefined) { 61155 61156 // get slider values and convert them to a filter values string 61157 sliderValues = sliderWidget.getSliderValues(); 61158 if (type == 'contrast') { 61159 multiplier = 1/(_contrastSettings["contrast"].filter_to_slider); 61160 filterString = (sliderValues * multiplier).toString(); 61161 } 61162 else if (type == 'rgb') { 61163 multiplier = 1/(_rgbSettings["rgb"].filter_to_slider); 61164 filterString = sliderValues.r * multiplier + ","; 61165 filterString += sliderValues.g * multiplier + ","; 61166 filterString += sliderValues.b * multiplier; 61167 } 61168 else if (type == 'ranged_rgb') { 61169 multiplier = 1/(_rangedRgbSettings["ranged_rgb"].filter_to_slider); 61170 filterString = Math.round(sliderValues.r[0] * multiplier) + "," + Math.round(sliderValues.r[1] * multiplier) + ","; 61171 filterString += Math.round(sliderValues.g[0] * multiplier) + "," + Math.round(sliderValues.g[1] * multiplier) + ","; 61172 filterString += Math.round(sliderValues.b[0] * multiplier) + "," + Math.round(sliderValues.b[1] * multiplier); 61173 } 61174 61175 // send the owner, filter type, and filter string back to the dialog owner 61176 event.owner = _owner; 61177 event.filterVals = filterString; 61178 61179 if(event.type === "reset"){ 61180 return _onReset(event, ui); 61181 } 61182 else if (_onChange){ 61183 61184 _onChange(event, ui); 61185 } 61186 } 61187 } 61188 61189 /* Pixel value transform functions to map the intensity window slider widget (ranged slider) such that it is linear 61190 * in the 12-bit range and log2 scale from 12 to 16 bits. 61191 * 61192 * Example Usage 61193 * 61194 * Set up a slider widget for 16-bit range 61195 * 61196 * var rangeLimit = [0, 65535]; 61197 * slider.setMinimum(pixelMapper.transform(rangeLimit[0]); 61198 * slider.setMaximum(pixelMapper.transform(rangeLimit[1]); 61199 * 61200 * Retrieve values from the slider and map to the image service range parameter 61201 * 61202 * var range = [ Math.round(pixelMapper.inverseTransform(slider.getLowerValue()), 0), 61203 * Math.round(pixelMapper.inverseTransform(slider.getUpperValue()), 0) ].join(); 61204 * 61205 * In the example above, the actual slider range becomes lo,hi = 0,5686, 61206 * but the displayed slider values will be lo,hi = 0, 65535 61207 * 61208 * author: ChrisL 61209 */ 61210 var pixelMapper = 61211 { 61212 breakPoint: 4096.0, 61213 scale: 100.0, 61214 61215 // @param v - 61216 transform: function(v) { 61217 if (v < this.breakPoint) 61218 return v; 61219 else 61220 return this.breakPoint + (Math.log(v - this.breakPoint + 1.0) / Math.LN2) * this.scale; 61221 }, 61222 61223 inverseTransform: function(v) { 61224 if (v < this.breakPoint) 61225 return v; 61226 else 61227 return Math.pow(2.0, (v - this.breakPoint) / this.scale) + this.breakPoint - 1.0 61228 } 61229 }; 61230 61231 function init() { 61232 _theDialog = createDialog(_outerContainer); 61233 } 61234 61235 init(); 61236 61237 return _self; 61238} 61239; 61240"use strict"; 61241/** 61242 * filter_widget.js 61243 * 61244 * This class creates horizontal sliders used to control image filter settings. 61245 * It uses jQuery and jQuery UI to construct the javascript sliders for one of three different configurations: 61246 * 61247 * 1) Single contrast slider 61248 * 2) Three RGB sliders 61249 * 3) Three ranged RGB sliders 61250 * 61251 * The sliders are displayed horizontally, and take up 100% of the width of their container. 61252 * They are prelabeled as "Contrast:", "Red:", "Green:", "Blue" 61253 * 61254 * There are also two optional checkbox widget types that can be added to the rgb or ranged_rgb slider layouts: 61255 * Channel toggles - checkboxes placed in front of each slider, whose intent is to be able to 'turn off' the adjacent color channel slider by 61256 * returning a value of 0 rather than the normal slider value. This happens when the checkbox is unselected. Toggling these checkboxes does not 61257 * affect the actual slider handle positions, they retain their current settings. 61258 * Expand range - when selected, this checkbox causes the high/low range of the sliders to be expanded. Unselecting the checkbox restores 61259 * a 'normal' range. 61260 * 61261 * Styling should be done via CSS
61262 * The user-supplied listener is called in response to a 'change' event from the sliders. It is up to the user's listener to redraw the 61263 * image in response to slider changes. 61264 * 61265 * All optional widgets (checkboxes) direct their change events to local listener functions, not to the user-supplied listener. 61266 * 61267 * Usage 61268 * ---- 61269 * Right after creating a sliderWidget, the user should get the current slider values by calling getSliderValues(), then redrawing 61270 * their image(s) using the filter values derived from the sliders. Whenever the slider values change, whether by direct manipulation of the 61271 * slider handles or by checking/unchecking checkbox buttons, the callback (listener) function is sent a 'change' event. The listener function 61272 * should then call getSliderValues() and update the image(s). 61273 * 61274 * It is possible to have multiple instances of sliderWidget on a page, just make sure the 'ordinal' arg is unique for each instance. This is often 61275 * done when different image types wit different image filters are displayed on a page; the program logic shows/hides the different slider 61276 * containers via CSS. 61277 * 61278 * Error handling 61279 * -------------- 61280 * There is very minimal error handling. For the most part, if there is a problem, it will fail silently. There is almost no 61281 * checking for correct format/inclusion of input args. 61282 */ 61283 61284/** 61285 * filter_widget 61286 * 61287 * @param type slider type. 'contrast', 'rbg', or 'ranged_rgb' 61288 * @param container DOM element of container that will hold the slider(s) 61289 * @param ordinal unique ordinal number for the filter_widget instance. This is appended to DOM element ids to ensure uniqueness if more than one<br/> 61290 * sliderWidget is on a page. 61291 * @param listener listener function callback for slider and widget state changes<br/> 61292 * Called with jQuery's event args, 'event' and 'ui'. Do not rely on 'ui', sometimes it does not have jQuery property info. 61293 * @param ranges hash of min/max values for each slider.<br/> 61294 * contrast - [min, max]<br/> 61295 * rgb, ranged_rgb - {r:[min, max], g:[min, max], b:[min, max]} 61296 * @param values hash of initial values for each slider<br/> 61297 * contrast - val<br/> 61298 * rgb - {r:val, g:val, b:val}<br/> 61299 * ranged_rgb - {r:[lo, hi], g:[lo, hi], b:[lo, hi]} 61300 * @param options 61301 * Configure the slider label text, if not present, default labels will be used. 61302 * {slider_labels: {r:label, g:label, b:label, contrast:label} 61303 * Add slider value toggle checkbox, and initial checkbox state. Defining the option enables it for creation. 61304 * contrast - {channel_toggle: {selected: true or false}} 61305 * rgb, ranged_rgb - {channel_toggle: {selected: {r: true or false, g: {true or false}, b: {true or false}} 61306 * Add expand range checkbox and initial checkbox state. Defining the option enables it for creation. 61307 * {expand_range: {selected: true or false,<br/> 61308 * ranges: {r:[min, max], g:[min, max], b:[min, max]},<br/> 61309 * values: {r:[lo, hi], g:[lo, hi], b:[lo, hi]}<br/> 61310 * }<br/> 61311 * } 61312 * Add slider range and value processing algorithm; this is only used by ranged_rgb slider 61313 * ranged_rgb - {algorithm:{transform:fnc(rangeLimit), inverseTransform:fnc(sliderValue)} 61314 * @param imageType the type of the current image in display. 61315 */ 61316function filterWidget(type, container, ordinal, listener, ranges, values, options, imageType) { 61317 /* ------- private vars --------- */ 61318 var _type = type; 61319 var _container = jQuery(container); 61320 var _ordinal = ordinal; 61321 var _listener = listener; 61322 var _ranges = ranges; // save initial range settings 61323 var _values = values; // save initial value settings 61324 var _options = options == undefined? {} : options; // save initial options settings 61325 61326 var _self = this; 61327 61328 var _channelToggleOption = false; 61329 // TODO: _expandRangeOption is no longer used 61330 var _expandRangeOption = false; 61331 var _algorithmOption = false;
61332 var _instanceId = "_" + _ordinal; // append to CSS element ids to ensure unique instance id 61333 61334 var _sliderTypes = ["contrast", "rgb", "ranged_rgb"]; 61335 61336 var _imageType = imageType; 61337 61338 // set up default slider labels 61339 var _contrastLabel = "Contrast"; 61340 var _redLabel = "Red: "; 61341 var _greenLabel = "Green: "; 61342 var _blueLabel = "Blue: "; 61343 var _expandRangeLabel = "Expand Slider Range"; 61344 61345 var _defaultValues; 61346 61347 // color guide descriptions 61348 var _redDescription = ""; 61349 var _greenDescription = ""; 61350 var _blueDescription = ""; 61351 var _contrastDescription = ""; 61352 61353 // track state of channel toggle; there are used for their default values, even if this option is not enabled! 61354 var _red_on = true; 61355 var _green_on = true; 61356 var _blue_on = true; 61357 61358 /* ------------------------ public functions ---------------------------- */ 61359 61360 /** 61361 * Show/hide expand range checkbox 61362 * This assumes the checkbox already exists. 61363 * 61364 * @param show true = show, false= hide 61365 */ 61366 this.showExpandRangeCB = function(show) { 61367 if (_expandRangeOption == true) { 61368 var value = show == true ? "block" : "none"; 61369 jQuery("#expand_range_cb" + _instanceId).css("display", value); 61370 } 61371 } 61372 61373 /** 61374 * select/unselect expand range checkbox 61375 * This assumes the checkbox already exists. 61376 * @param select true = select, false = unselect 61377 */ 61378 this.selectExpandRangeCB = function(select) { 61379 if (_expandRangeOption == true) { 61380 var state = select == true ? true : false; 61381 jQuery("#expand_range_cb" + _instanceId).attribute("checked", state); 61382 } 61383 } 61384 61385 /** 61386 * Show/hide channel toggle checkboxes for RGB sliders 61387 * This assumes the checkboxes already exist. 61388 * 61389 * @param show true = show, false= hide 61390 */ 61391 this.showChannelToggleCB = function(show) { 61392 if (_channelToggleOption == true) { 61393 var value = show == true ? "block" : "none"; 61394 jQuery("#red_cb" + _instanceId).css("display", value); 61395 jQuery("#blue_cb" + _instanceId).css("display", value); 61396 jQuery("#green_cb" + _instanceId).css("display", value); 61397 } 61398 } 61399 61400 /** 61401 * select/unselect channel toggle checkboxes 61402 * This assumes the checkboxes already exist. 61403 * @param selectStates hash {red:state, green:state, blue:state} where state is boolean; true = select, false = unselect 61404 * You can leave out any key:value pairs that you do not wish to change. 61405 */ 61406 this.selectChannelToggleCB = function(selectStates) { 61407 var redState, greenState, blueState; 61408 61409 if (_channelToggleOption == true) { 61410 redState = selectStates["red"]; 61411 greenState = selectStates["green"]; 61412 blueState = selectStates["blue"]; 61413 61414 if (redState !== undefined) 61415 jQuery("#red_cb" + _instanceId).attribute("checked", redState); 61416 if (greenState !== undefined) 61417 jQuery("#green_cb" + _instanceId).attribute("checked", greenState); 61418 if (blueState !== undefined) 61419 jQuery("#blue_cb" + _instanceId).attribute("checked", blueState); 61420 } 61421 } 61422 61423 /** 61424 * Get current slider(s) values. 61425 * This function will return 0 for sliders that have channel toggle buttons 61426 * that are in the 'off' or unselected state. 61427 * Note: if using ranged_rgb, you may need to call 'isExpandRange' to determine if the 61428 * values are for the normal vs. expanded range. 61429 * 61430 * If 'algorithm' option is being used for ranged_rgb, convert the slider 61431 * value to filter (display) values. 61432 * 61433 * @param raw optional arg, if true then return raw slider values, disregarding current setting of channel toggle checkbox. 61434 * Default is false. 61435 * @return hash; values depend on sliderType<br/> 61436 * contrast - number<br/> 61437 * rgb - {r:number, g:number, b:number} 61438 * ranged_rgb - {r:[lo,hi], g:[lo,hi], b:[lo,hi]} 61439 */ 61440 this.getSliderValues = function(raw) { 61441 if (raw == undefined) 61442 raw = false;
61443 61444 var r, g, b; 61445 var sliderValues; 61446 61447 if (_type == 'contrast') { 61448 sliderValues = jQuery("#contrast_slider" + _instanceId).slider("option", "value"); 61449 } 61450 else if (_type == 'rgb') { 61451 sliderValues = {}; 61452 r = jQuery("#red_slider" + _instanceId).slider("option", "value"); 61453 g = jQuery("#green_slider" + _instanceId).slider("option", "value"); 61454 b = jQuery("#blue_slider" + _instanceId).slider("option", "value"); 61455 61456 // zero out slider values if channel is turned off 61457 if (raw == false) { 61458 if (_red_on == false) r = 0; 61459 if (_green_on == false) g = 0; 61460 if (_blue_on == false) b = 0; 61461 } 61462 61463 sliderValues.r = r; 61464 sliderValues.g = g; 61465 sliderValues.b = b; 61466 } 61467 else if (_type == 'ranged_rgb') { 61468 var lo, hi; 61469 var usingExpandedRange = false; 61470 61471 if (_expandRangeOption == true) { 61472 usingExpandedRange = jQuery("#expand_range_cb" + _instanceId).attr("checked") !== undefined ? true : false; 61473 } 61474 sliderValues = {}; 61475 61476 if (_algorithmOption == true) { 61477 var lo, hi; 61478 var tLo, tHi; // 't' = 'transformed' 61479 61480 lo = jQuery("#red_slider" + _instanceId).slider("values", 0); 61481 hi = jQuery("#red_slider" + _instanceId).slider("values", 1); 61482 tLo = Math.round(_options.algorithm.inverseTransform(lo)); 61483 tHi = Math.round(_options.algorithm.inverseTransform(hi)); 61484 sliderValues.r = [tLo, tHi]; 61485 61486 lo = jQuery("#green_slider" + _instanceId).slider("values", 0); 61487 hi = jQuery("#green_slider" + _instanceId).slider("values", 1); 61488 tLo = Math.round(_options.algorithm.inverseTransform(lo)); 61489 tHi = Math.round(_options.algorithm.inverseTransform(hi)); 61490 sliderValues.g = [tLo, tHi]; 61491 61492 lo = jQuery("#blue_slider" + _instanceId).slider("values", 0); 61493 hi = jQuery("#blue_slider" + _instanceId).slider("values", 1); 61494 tLo = Math.round(_options.algorithm.inverseTransform(lo)); 61495 tHi = Math.round(_options.algorithm.inverseTransform(hi)); 61496 sliderValues.b = [tLo, tHi]; 61497 61498 } 61499 else { 61500 lo = jQuery("#red_slider" + _instanceId).slider("values", 0); 61501 hi = jQuery("#red_slider" + _instanceId).slider("values", 1); 61502 sliderValues.r = [lo, hi]; 61503 61504 lo = jQuery("#green_slider" + _instanceId).slider("values", 0); 61505 hi = jQuery("#green_slider" + _instanceId).slider("values", 1); 61506 sliderValues.g = [lo, hi]; 61507 61508 lo = jQuery("#blue_slider" + _instanceId).slider("values", 0); 61509 hi = jQuery("#blue_slider" + _instanceId).slider("values", 1); 61510 sliderValues.b = [lo, hi]; 61511 } 61512 61513 // zero out any slider values if their respective channel is turned off 61514 if (raw == false) { 61515 61516 if (_red_on == false) 61517 sliderValues.r = [0, 0]; 61518 61519 if (_green_on == false) 61520 sliderValues.g = [0, 0]; 61521 61522 if (_blue_on == false) 61523 sliderValues.b = [0, 0]; 61524 } 61525 } 61526 return sliderValues; 61527 } 61528 61529 /** 61530 * Set the slider handle values and ranges. 61531 * You can specify values, ranges, or both. 61532 * 61533 * @param sliderType - 'contrast', 'rgb', or 'ranged_rgb'<br/> 61534 * @param values hash of slider handle values<br/> 61535 * 'contrast' - {values:number}<br/> 61536 * 'rgb' - {values:{r:number, g:number, b:number}} 61537 * 'ranged_rgb' - {values:{r:[lo,hi], g:[lo,hi], b:[lo,hi]}} 61538 * @param ranges hash of slider min/max ranges 61539 * 'contrast' - {ranges: [min, max]}<br/> 61540 * 'rgb' - {ranges: {r:[min, max], g:[min, max], b:[min, max]} 61541 * 'ranged_rgb' - same as 'rgb' 61542 */ 61543 this.setSliderValues = function(sliderType, values, ranges) { 61544 if (ranges !== undefined) { 61545 // set ranges 61546 if (sliderType == 'contrast') { 61547 jQuery("#contrast_slider" + _instanceId).slider("option", "min", ranges[0]); 61548 jQuery("#contrast_slider" + _instanceId).slider("option", "max", ranges[1]); 61549 } 61550 else if (sliderType == 'rgb') { 61551 jQuery("#red_slider" + _instanceId).slider("option", "min", ranges.r[0]); 61552 jQuery("#red_slider" + _instanceId).slider("option", "max", ranges.r[1]); 61553 jQuery("#green_slider" + _instanceId).slider("option", "min", ranges.r[0]); 61554 jQuery("#green_slider" + _instanceId).slider("option", "max", ranges.r[1]); 61555 jQuery("#blue_slider" + _instanceId).slider("option", "min", ranges.r[0]); 61556 jQuery("#blue_slider" + _instanceId).slider("option", "max", ranges.r[1]); 61557 } 61558 else if (sliderType == 'ranged_rgb') { 61559 jQuery("#red_slider" + _instanceId).slider("option", "min", ranges.r[0]); 61560 jQuery("#red_slider" + _instanceId).slider("option", "max", ranges.r[1]); 61561 jQuery("#green_slider" + _instanceId).slider("option", "min", ranges.r[0]); 61562 jQuery("#green_slider" + _instanceId).slider("option", "max", ranges.r[1]); 61563 jQuery("#blue_slider" + _instanceId).slider("option", "min", ranges.r[0]); 61564 jQuery("#blue_slider" + _instanceId).slider("option", "max", ranges.r[1]); 61565 } 61566 } 61567 61568 if (values !== undefined ) { 61569 // set values 61570 if (sliderType == 'contrast') { 61571 jQuery("#contrast_slider" + _instanceId).slider("option", "value", values); 61572 } 61573 else if (sliderType == 'rgb') { 61574 jQuery("#red_slider" + _instanceId).slider("option", "value", values.r); 61575 jQuery("#green_slider" + _instanceId).slider("option", "value", values.g); 61576 jQuery("#blue_slider" + _instanceId).slider("option", "value", values.b); 61577 } 61578 else if (sliderType == 'ranged_rgb') { 61579 // value in this case is a two element array, instead of a single value like it is in 'rgb' 61580 jQuery("#red_slider" + _instanceId).slider("option", "values", values.r); 61581 jQuery("#green_slider" + _instanceId).slider("option", "values", values.g); 61582 jQuery("#blue_slider" + _instanceId).slider("option", "values", values.b); 61583 } 61584 } 61585 } 61586 61587 /** 61588 * Update the slider labels that display the numeric slider values
61589 * This is usually called in response to a slider change event. 61590 * 61591 */ 61592 this.updateSliderLabels = function() { 61593 var contrast; 61594 var r,g,b; 61595 var rlo,rhi,glo,ghi,blo,bhi; 61596 61597 if (_type == 'contrast') { 61598 values = jQuery("#contrast_slider" + _instanceId).slider("option", "value"); 61599 jQuery("#contrast_label" + _instanceId).val(values); 61600 } 61601 else if (_type == 'rgb') { 61602 r = jQuery("#red_slider" + _instanceId).slider("option", "value"); 61603 g = jQuery("#green_slider" + _instanceId).slider("option", "value"); 61604 b = jQuery("#blue_slider" + _instanceId).slider("option", "value"); 61605 61606 jQuery("#red_label" + _instanceId).val(r); 61607 jQuery("#green_label" + _instanceId).val(g); 61608 jQuery("#blue_label" + _instanceId).val(b); 61609 } 61610 else if (_type == 'ranged_rgb') { 61611 rlo = jQuery("#red_slider" + _instanceId).slider("values", 0); 61612 rhi = jQuery("#red_slider" + _instanceId).slider("values", 1); 61613 glo = jQuery("#green_slider" + _instanceId).slider("values", 0); 61614 ghi = jQuery("#green_slider" + _instanceId).slider("values", 1); 61615 blo = jQuery("#blue_slider" + _instanceId).slider("values", 0); 61616 bhi = jQuery("#blue_slider" + _instanceId).slider("values", 1); 61617 61618 if (_algorithmOption == true) { 61619 // inverse transform slider values to display values (which are also the actual filter values) 61620 var lo, hi; 61621 lo = Math.round(_options.algorithm.inverseTransform(rlo)); 61622 hi = Math.round(_options.algorithm.inverseTransform(rhi)); 61623 jQuery("#red_label" + _instanceId).val(lo + " - " + hi); 61624 61625 lo = Math.round(_options.algorithm.inverseTransform(glo)); 61626 hi = Math.round(_options.algorithm.inverseTransform(ghi)); 61627 jQuery("#green_label" + _instanceId).val(lo + " - " + hi); 61628 61629 lo = Math.round(_options.algorithm.inverseTransform(blo)); 61630 hi = Math.round(_options.algorithm.inverseTransform(bhi)); 61631 jQuery("#blue_label" + _instanceId).val(lo + " - " + hi); 61632 } 61633 else { 61634 jQuery("#red_label" + _instanceId).val(rlo + " - " + rhi); 61635 jQuery("#green_label" + _instanceId).val(glo + " - " + ghi); 61636 jQuery("#blue_label" + _instanceId).val(blo + " - " + bhi); 61637 } 61638 } 61639 61640 } 61641 61642 /* ------------------------ private functions ---------------------------- */ 61643 61644 function parseOptions(options) { 61645 if (!options) { 61646 return; 61647 } 61648 61649 if (options["channel_toggle"] !== undefined) { 61650 _channelToggleOption = true; 61651 } 61652 if (options["expand_range"] !== undefined) { 61653 _expandRangeOption = true; 61654 } 61655 if (options["algorithm"] !== undefined) { 61656 _algorithmOption = true; 61657 } 61658 if (options["slider_labels"] !== undefined) { 61659 _redLabel = options["slider_labels"].r ? options["slider_labels"].r : _redLabel; 61660 _greenLabel = options["slider_labels"].g ? options["slider_labels"].g : _greenLabel; 61661 _blueLabel = options["slider_labels"].b ? options["slider_labels"].b : _blueLabel; 61662 _contrastLabel = options["slider_labels"].contrast ? options["slider_labels"].contrast : _contrastLabel; 61663 } 61664 else if (options.options){ 61665 _contrastLabel = options.options["filter_label"].contrast ? options.options["filter_label"].contrast : _contrastLabel; 61666 } 61667 } 61668 61669 function buildHtml(sliderType) { 61670 if (sliderType == "contrast") 61671 buildContrastHtml(); 61672 else if (sliderType == "rgb" || sliderType == "ranged_rgb") 61673 buildRgbHtml(sliderType) 61674 } 61675 61676 /** 61677 * Build HTML for Contrast slider and insert into DOM 61678 * uses _instanceId, _container 61679 */ 61680 function buildContrastHtml() { 61681 var html = ""; 61682 var inputClass = "slider_input_class"; 61683 setColorDescriptions(); 61684 61685 html += '<table class="slider_table_class">'; 61686 html += '<tbody>'; 61687 html += '<tr colspan="2">'; 61688 html += ' <td class="slider_cb_class"></td>'; 61689 html += ' <td><label for="contrast_label' + _instanceId + '">' + _contrastLabel + ' ' + '</label><input type="text" readonly="true" class="' + inputClass + '" id="contrast_label' + _instanceId + '"/></td>'; 61690 html += ' <td><label for="contrast_label_title' + _instanceId + '">' + _contrastDescription + '</label></td>'; 61691 html += '</tr>'; 61692 html += '<tr>'; 61693 if (_channelToggleOption) { 61694 html += ' <td><input id="contrast_cb' + _instanceId + '" class="slider_cb" type="checkbox" title="contrast on/off"/></td>'; 61695 } 61696 else { 61697 html += ' <td></td>'; 61698 } 61699 html += ' <td colspan="2"><div id="contrast_slider' + _instanceId + '"' + ' class="contrast_slider_class"' + '></div></td>'; // jQuery UI inserts slider HTML into this container 61700 html += '</tr>'; 61701 html += '</tbody>'; 61702 html += '</table>'; 61703 html += '<div style="height: 40px;"><input style="top: 20px;" id="reset_button" type="button" class="reset_button_class" value="Reset" title="Reset color filters"></div>'; 61704 61705 jQuery(_container).prepend(html); 61706 } 61707 61708 /** 61709 * Build HTML for RGB or Ranged RGB sliders and insert into DOM 61710 * This function also adds the HTML for any optional buttons/checkboxes. 61711 * If there are any button/checkbox options built, they are not set to any particular states. 61712 * uses _instanceId, _container, _channelToggleOption, _expandRangeOption (only appropriate for ranged_rgb) 61713 * 61714 * Because of cross-browser background gradient problems, RGB vs. Ranged RGB sliders must be assigned 61715 * different classes so that the appropriate CSS can be applied to the slider backgrounds. 61716 */ 61717 function buildRgbHtml(sliderType) { 61718 var redClass, greenClass, blueClass, inputClass; 61719 var html = ""; 61720 61721 inputClass = "slider_input_class"; 61722 61723 setColorDescriptions(); 61724 61725 if (sliderType == "rgb") { 61726 redClass = "red_slider_class"; 61727 greenClass = "green_slider_class"; 61728 blueClass = "blue_slider_class"; 61729 } 61730 else if (sliderType == "ranged_rgb") { 61731 redClass = "ranged_red_slider_class"; 61732 greenClass = "ranged_green_slider_class"; 61733 blueClass = "ranged_blue_slider_class"; 61734 } 61735 61736 html += '<table class="slider_table_class">'; 61737 html += '<tbody>'; 61738 html += '<tr>'; 61739 html += ' <td class="slider_cb_class"></td>'; 61740 html += ' <td><label for="red_label' + _instanceId + '">' + _redLabel + '</label><input type="text" readonly="true" class="' + inputClass + '" id="red_label' + _instanceId + '"/></td>'; 61741 html += ' <td><label for="red_label_title' + _instanceId + '">' + _redDescription + '</label></td>'; 61742 html += '</tr>'; 61743 html += '<tr>'; 61744 if (_channelToggleOption) { 61745 html += ' <td><input id="red_cb' + _instanceId + '" class="slider_cb" type="checkbox" title="red channel on/off"/></td>'; 61746 } 61747 else { 61748 html += ' <td></td>'; 61749 } 61750 html += ' <td colspan="2"><div id="red_slider' + _instanceId + '" class="' + redClass + '"></div></td>'; // jQuery UI inserts slider HTML into this container 61751 html += '</tr>'; 61752 html += '<tr>'; 61753 html += ' <td></td>'; 61754 html += ' <td><label for="green_label' + _instanceId + '">' + _greenLabel + '</label><input type="text" readonly="true" class="' + inputClass + '" id="green_label' + _instanceId + '"/></td>'; 61755 html += ' <td><label for="green_label_title' + _instanceId + '">' + _greenDescription + '</label></td>'; 61756 html += '</tr>'; 61757 html += '<tr>'; 61758 if (_channelToggleOption) { 61759 html += ' <td><input id="green_cb' + _instanceId + '" class="slider_cb" type="checkbox" title="green channel on/off"></td>'; 61760 } 61761 else { 61762 html += ' <td></td>'; 61763 } 61764 html += ' <td colspan="2"><div id="green_slider' + _instanceId + '" class="' + greenClass + '"></div></td>'; // jQuery UI inserts slider HTML into this container 61765 html += '</tr>'; 61766 html += '<tr>'; 61767 html += ' <td></td>'; 61768 html += ' <td><label for="blue_label' + _instanceId + '">' + _blueLabel + '</label><input type="text" readonly="true" class="' + inputClass + '" id="blue_label' + _instanceId + '"/></td>'; 61769 html += ' <td><label for="blue_label_title' + _instanceId + '">' + _blueDescription + '</label></td>'; 61770 html += '</tr>'; 61771 html += '<tr>'; 61772 if (_channelToggleOption) { 61773 html += ' <td><input id="blue_cb' + _instanceId + '" class="slider_cb" type="checkbox" title="blue channel on/off"/></td>'; 61774 } 61775 else { 61776 html += ' <td></td>'; 61777 } 61778 html += ' <td colspan="2"><div id="blue_slider' + _instanceId + '" class="' + blueClass + '"></div></td>'; // jQuery UI inserts slider HTML into this container 61779 html += '</tr>'; 61780 html += '<tr>'; 61781 html += '</tr>'; 61782 if (_expandRangeOption) { 61783 html += '<tr>'; 61784 html += ' <td colspan="2"><hr></td>'; 61785 html += '</tr>'; 61786 html += '<tr>'; 61787 html += ' <td></td>'; 61788 html += ' <td><input id="expand_range_cb' + _instanceId + '" class="slider_cb" type="checkbox"/>' + _expandRangeLabel + '</td>'; 61789 html += '</tr>'; 61790 } 61791 html += '</tbody>'; 61792 html += '</table>'; 61793 html += '<div style="height: 40px;"><input id="reset_button" type="button" class="reset_button_class" value="Reset" title="Reset color filters"></div>'; 61794 61795 jQuery(_container).prepend(html); 61796 } 61797 61798 function setColorDescriptions( ){ 61799 61800 if(_imageType === "NISSL"){ 61801 _contrastDescription = _options["slider_labels"].cNI; 61802 } 61803 else if(_imageType === "ACHE"){ 61804 _contrastDescription = _options["slider_labels"].cAC; 61805 } 61806 else if(_imageType === "CYOX"){ 61807 _contrastDescription = _options["slider_labels"].cCY; 61808 } 61809 else if(_imageType === 'BDA_AAV'){ 61810 _redDescription = _options["slider_labels"].rBDA_AAV; 61811 _greenDescription = _options["slider_labels"].gBDA_AAV; 61812 _blueDescription = _options["slider_labels"].bBDA_AAV; 61813 } 61814 else if(_imageType === 'SMI_32'){ 61815 _redDescription = _options["slider_labels"].rSMI_32; 61816 _greenDescription = _options["slider_labels"].gSMI_32; 61817 _blueDescription = _options["slider_labels"].bSMI_32; 61818 } 61819 else if(_imageType === 'TWO_PHOTON'){ 61820 _redDescription = _options["slider_labels"].rPH; 61821 _greenDescription = _options["slider_labels"].gPH; 61822 _blueDescription = _options["slider_labels"].bPH; 61823 } 61824 else if(_imageType === 'PVALB'){ 61825 _redDescription = _options["slider_labels"].rPV; 61826 _greenDescription = _options["slider_labels"].gPV; 61827 _blueDescription = _options["slider_labels"].bPV; 61828 } 61829 else if(_imageType === 'CALB1'){ 61830 _redDescription = _options["slider_labels"].rCA; 61831 _greenDescription = _options["slider_labels"].gCA; 61832 _blueDescription = _options["slider_labels"].bCA; 61833 } 61834 else if(_imageType === 'NEUN'){ 61835 _redDescription = _options["slider_labels"].rNE;
61836 _greenDescription = _options["slider_labels"].gNE; 61837 _blueDescription = _options["slider_labels"].bNE; 61838 } 61839 } 61840 61841 /** 61842 * Build sliders and insert into DOM 61843 * 61844 * @param sliderType 61845 * @param listener 61846 */ 61847 function buildSliders(sliderType, listener) { 61848 if (sliderType == "contrast") 61849 buildContrastSlider(sliderType, listener); 61850 else if (sliderType == "rgb") 61851 buildRgbSliders(sliderType, listener); 61852 else if (sliderType == "ranged_rgb") 61853 buildRangedRgbSliders(sliderType, listener); 61854 } 61855 61856 /** 61857 * Build the contrast slider and sets its label value 61858 * Before calling this function, you need to call buildContrastHtml 61859 * @param sliderType 61860 * @param listener 61861 */ 61862 function buildContrastSlider(sliderType, listener) { 61863 var values = getInitialValues(sliderType); 61864 var ranges = getInitialRanges(sliderType); 61865 61866 jQuery("#contrast_label" + _instanceId).val(values["values"]); 61867 jQuery("#contrast_slider" + _instanceId).slider({range: false, min: ranges[0], max: ranges[1], value: values, 61868 change: listener, slide: listener}); 61869 } 61870 61871 /** 61872 * Build the RGB sliders and sets their label values. 61873 * 61874 * Before calling this function, you need to call buildRgbHtml 61875 * @param sliderType determines whether to build regular RGB sliders, or ranged RGB sliders 61876 * @param listener callback for slider 'change' events 61877 */ 61878 function buildRgbSliders(sliderType, listener) { 61879 var rVals, gVals, bVals; 61880 var rRanges, gRanges, bRanges; 61881 61882 var values = getInitialValues(sliderType); 61883 var ranges = getInitialRanges(sliderType); 61884 61885 rVals = values.r; 61886 gVals = values.g; 61887 bVals = values.b; 61888 61889 rRanges = ranges.r; 61890 gRanges = ranges.g; 61891 bRanges = ranges.b; 61892 61893 jQuery("#red_label" + _instanceId).val(rVals); 61894 jQuery("#red_slider" + _instanceId).slider({range: false, min: rRanges[0], max: rRanges[1], 61895 value: rVals, change: listener, slide: listener}); 61896 61897 jQuery("#green_label" + _instanceId).val(gVals); 61898 jQuery("#green_slider" + _instanceId).slider({range: false, min: gRanges[0], max: gRanges[1], 61899 value: gVals, change: listener, slide: listener}); 61900 61901 jQuery("#blue_label" + _instanceId).val(bVals); 61902 jQuery("#blue_slider" + _instanceId).slider({range: false, min: bRanges[0], max: bRanges[1], 61903 value: bVals, change: listener, slide: listener}); 61904 } 61905 61906 /** 61907 * Build the Ranged RGB sliders and sets their label values. 61908 * 61909 * Before calling this function, you need to call buildRgbHtml 61910 * @param sliderType determines whether to build regular RGB sliders, or ranged RGB sliders 61911 * @param listener callback for slider 'change' events 61912 */ 61913 function buildRangedRgbSliders(sliderType, listener) { 61914 var rVals, gVals, bVals; 61915 var rRanges, gRanges, bRanges; 61916 61917 var values = getInitialValues(sliderType); 61918 var ranges = getInitialRanges(sliderType); 61919 61920 rVals = values.r; 61921 gVals = values.g; 61922 bVals = values.b; 61923 61924 rRanges = ranges.r; 61925 gRanges = ranges.g; 61926 bRanges = ranges.b; 61927 61928 jQuery("#red_label" + _instanceId).val(rVals[0] + " - " + rVals[1]); 61929 jQuery("#red_slider" + _instanceId).slider({range: true, min: rRanges[0], max: rRanges[1], 61930 values: rVals, change: listener, slide: listener}); 61931 61932 jQuery("#green_label" + _instanceId).val(gVals[0] + " - " + gVals[1]); 61933 jQuery("#green_slider" + _instanceId).slider({range: true, min: gRanges[0], max: gRanges[1], 61934 values: gVals, change: listener, slide: listener}); 61935 61936 jQuery("#blue_label" + _instanceId).val(bVals[0] + " - " + bVals[1]); 61937 jQuery("#blue_slider" + _instanceId).slider({range: true, min: bRanges[0], max: bRanges[1], 61938 values: bVals, change: listener, slide: listener}); 61939 } 61940 61941 /** 61942 * Get the initial slider value(s). 61943 * 61944 * If the 'expanded range' option is enabled and selected (on) for ranged rgb, then 61945 * the sliders will have the expanded range values and ranges, instead of the normal values. 61946 * Uses _expandedRangeOption, _options 61947 * 61948 * @param sliderType 61949 * @return numeric values.<br/> 61950 * 'contrast' - {value:number} 61951 * 'rgb' - {r:number, g:number, b:number}<br/> 61952 * 'ranged_rgb' - {r:[lo,hi], g:[lo,hi], b:[lo, hi]} 61953 */ 61954 function getInitialValues(sliderType) { 61955 var initialValues = {}; 61956 61957 if (sliderType == "contrast") { 61958 initialValues = _values; 61959 } 61960 else if (sliderType == "rgb") { 61961 initialValues = _values; 61962 } 61963 else if (sliderType == "ranged_rgb") { 61964 if (_expandRangeOption == true && _options["expand_range"].selected === true) { 61965 // expanded range value 61966 initialValues = _options["expand_range"]; 61967 } 61968 else if (_algorithmOption == true) { 61969 // transform the values 61970 var lo, hi; 61971 jQuery.extend(true, initialValues, _values); 61972 lo = initialValues.r[0]; 61973 hi = initialValues.r[1]; 61974 initialValues.r[0] = _options.algorithm.transform(lo); 61975 initialValues.r[1] = _options.algorithm.transform(hi); 61976 61977 lo = initialValues.g[0]; 61978 hi = initialValues.g[1]; 61979 initialValues.g[0] = _options.algorithm.transform(lo); 61980 initialValues.g[1] = _options.algorithm.transform(hi); 61981 61982 lo = initialValues.b[0]; 61983 hi = initialValues.b[1]; 61984 initialValues.b[0] = _options.algorithm.transform(lo); 61985 initialValues.b[1] = _options.algorithm.transform(hi); 61986 } 61987 else { 61988 // normal value 61989 initialValues = _values; 61990 } 61991 } 61992 return initialValues; 61993 } 61994 61995 /* 61996 * Get current slider values. 61997 * This function returns actual slider values, ignoring any effect due to channel toggle checkboxes. 61998 */ 61999 this.getCurrentValues = function() { 62000 var raw = true; 62001 var vals = _self.getSliderValues(raw); 62002 62003 return vals; 62004 } 62005 62006 /** 62007 * Get initial min/max range values for each slider. 62008 * 62009 * If the 'expanded range' option is enabled and selected (on) for ranged rgb, then set 62010 * up the sliders using the expanded range values and ranges, instead of the normal values. 62011 * 62012 * If the 'algorithm' option is enabled, transform the lo, hi range values. 62013 * 62014 * Uses _ranges, _expandedRangeOption, _options, _algorithmOption 62015 * 62016 * @param sliderType 62017 * @return Array of range values.<br/> 62018 * 'contrast' - [min, max]<br/> 62019 * 'rgb' - {r:[min, max], g:[min, max], b:[min, max]} 62020 * 'ranged_rgb' = same as 'rgb' 62021 */ 62022 function getInitialRanges(sliderType) { 62023 var initialRanges = {}; 62024 if (sliderType == "contrast") { 62025 initialRanges = _ranges; 62026 } 62027 else if (sliderType == "rgb") { 62028 initialRanges = _ranges; 62029 } 62030 else if (sliderType == "ranged_rgb") { 62031 if (_options["expand_range"] !== undefined && _options["expand_range"].selected === true) { 62032 // expanded range 62033 initialRanges = _options["expand_range"]["ranges"]; 62034 } 62035 else if (_algorithmOption == true) { 62036 var lo, hi; 62037 jQuery.extend(true, initialRanges, _ranges); 62038 lo = initialRanges.r[0]; 62039 hi = initialRanges.r[1]; 62040 initialRanges.r[0] = _options.algorithm.transform(lo); 62041 initialRanges.r[1] = _options.algorithm.transform(hi); 62042 62043 lo = initialRanges.g[0]; 62044 hi = initialRanges.g[1]; 62045 initialRanges.g[0] = _options.algorithm.transform(lo); 62046 initialRanges.g[1] = _options.algorithm.transform(hi); 62047 62048 lo = initialRanges.b[0]; 62049 hi = initialRanges.b[1]; 62050 initialRanges.b[0] = _options.algorithm.transform(lo); 62051 initialRanges.b[1] = _options.algorithm.transform(hi); 62052 } 62053 else { 62054 // normal range 62055 initialRanges = _ranges; 62056 } 62057 } 62058 62059 return initialRanges; 62060 } 62061 62062 /* 62063 * Get current slider ranges 62064 */ 62065 this.getCurrentRanges = function() { 62066 var curRanges; 62067 62068 //TODO: couldn't figure out how to access current ranges settings, so for now just return the original range settings. 62069 //in most cases, the range setting won't be changed after initial setting. 62070 curRanges = _ranges; 62071 62072 return curRanges; 62073 } 62074 62075 /** 62076 * Expand the range of the ranged rgb sliders and update the handles. 62077 * It is important to update the handles in order to generate a 'change' event, 62078 * even if the handles retain the same value. 62079 * Uses _options 62080 */ 62081 function expandRange() { 62082 var rVals, gVals, bVals; 62083 var rRanges, gRanges, bRanges; 62084 62085 var curValues = _self.getSliderValues(); 62086 62087 rVals = curValues.r; 62088 gVals = curValues.g; 62089 bVals = curValues.b; 62090 62091 rRanges = _options["expand_range"]["ranges"].r; 62092 gRanges = _options["expand_range"]["ranges"].g; 62093 bRanges = _options["expand_range"]["ranges"].b; 62094 62095 // increase max range value of RGB sliders. 62096 jQuery("#red_slider" + _instanceId).slider("option", "min", rRanges[0]); 62097 jQuery("#red_slider" + _instanceId).slider("option", "max", rRanges[1]); 62098 jQuery("#red_slider" + _instanceId).slider("option", "values", rVals); 62099 62100 jQuery("#green_slider" + _instanceId).slider("option", "min", gRanges[0]); 62101 jQuery("#green_slider" + _instanceId).slider("option", "max", gRanges[1]); 62102 jQuery("#green_slider" + _instanceId).slider("option", "values", gVals); 62103 62104 jQuery("#blue_slider" + _instanceId).slider("option", "min", bRanges[0]); 62105 jQuery("#blue_slider" + _instanceId).slider("option", "max", bRanges[1]); 62106 jQuery("#blue_slider" + _instanceId).slider("option", "values", bVals); 62107 } 62108 62109 /** 62110 * Reset all sliders to their initial default values and ranges. 62111 * Also resets color checkbox. 62112 * 62113 * @param sliderType 62114 */ 62115 function resetSliderDefaults(sliderType) { 62116 62117 if(sliderType === 'rgb' || sliderType === 'ranged_rgb' ){ 62118 // reset checkbox 62119 _red_on = true; 62120 _blue_on = true; 62121 _green_on = true; 62122 62123 $j("#red_cb" + _instanceId).prop("checked", _red_on); 62124 $j("#green_cb" + _instanceId).prop("checked", _green_on); 62125 $j("#blue_cb" + _instanceId).prop("checked", _blue_on); 62126 62127 var initialVals = {}; 62128 jQuery.extend(true, initialVals, _defaultValues); 62129 62130 if( sliderType === 'rgb'){ 62131 initialVals['rgb'].values.r = initialVals['rgb'].values.r * initialVals['rgb'].filter_to_slider; 62132 initialVals['rgb'].values.g = initialVals['rgb'].values.g * initialVals['rgb'].filter_to_slider; 62133 initialVals['rgb'].values.b = initialVals['rgb'].values.b *
62133initialVals['rgb'].filter_to_slider; 62134 var ranges = getInitialRanges(sliderType); 62135 _self.setSliderValues(sliderType,initialVals['rgb'].values,ranges); 62136 } 62137 62138 else if(sliderType === 'ranged_rgb'){ 62139 var ranges = getInitialRanges(sliderType); 62140 _self.setSliderValues(sliderType, initialVals, ranges); 62141 } 62142 } 62143 62144 else if(sliderType === 'contrast'){ 62145 var values = (parseFloat(_defaultValues['contrast'].values) ) * _defaultValues['contrast'].filter_to_slider; 62146 var ranges = getInitialRanges(sliderType); 62147 _self.setSliderValues(sliderType, values, ranges); 62148 } 62149 } 62150 62151 /** 62152 * Initialize the previously-created optional UI widgets (buttons, checkboxes) 62153 * with default setting values, and attach change events to the listener fnc. 62154 * Uses _channelToggleOption, _expandRangeOption 62155 * Uses _options 62156 * 62157 * @param sliderType 62158 */ 62159 function setOptionDefaults(sliderType) { 62160 62161 // options are applicable for color sliders 62162 if (sliderType == "rgb" || sliderType == "ranged_rgb") { 62163 62164 $j("#reset_button").bind("click",doReset); 62165 62166 if (_channelToggleOption == true) { 62167 // set local state vars and selection state of the channel toggle checkboxes 62168 _red_on = _options["channel_toggle"].selected.r; 62169 _green_on = _options["channel_toggle"].selected.g; 62170 _blue_on = _options["channel_toggle"].selected.b; 62171 62172 $j("#red_cb" + _instanceId).prop("checked", _red_on); 62173 $j("#green_cb" + _instanceId).prop("checked", _green_on); 62174 jQuery("#blue_cb" + _instanceId).prop("checked", _blue_on); 62175 62176 jQuery("#red_cb" + _instanceId).bind("change", doChannelToggle); 62177 jQuery("#green_cb" + _instanceId).bind("change", doChannelToggle); 62178 jQuery("#blue_cb" + _instanceId).bind("change", doChannelToggle); 62179 } 62180 if (_expandRangeOption == true) { 62181 // set selection state of the Expand Range checkbox, and if the checkbox is 62182 // initially selected, reconfigure the sliders to use the expanded range. 62183 var expand = _options["expand_range"].selected; 62184 jQuery("#expand_range_cb" + _instanceId).attr("checked", expand); 62185 jQuery("#expand_range_cb" + _instanceId).bind("change", doExpandRange); 62186 62187 if (expand == true) { 62188 // prevent sliders from generating 'change' events while we expand their range. 62189 // This is hacky, but effective 62190 62191 jQuery("#red_slider" + _instanceId).slider({change: undefined}); 62192 jQuery("#green_slider" + _instanceId).slider({change: undefined}); 62193 jQuery("#blue_slider" + _instanceId).slider({change: undefined}); 62194 62195 expandRange(); 62196 62197 jQuery("#red_slider" + _instanceId).slider({change: doSliderChange}); 62198 jQuery("#green_slider" + _instanceId).slider({change: doSliderChange}); 62199 jQuery("#blue_slider" + _instanceId).slider({change: doSliderChange}); 62200 } 62201 } 62202 } 62203 62204 // options for contrast 62205 if(sliderType == "contrast"){ 62206 $j("#reset_button").bind("click",doReset); 62207 } 62208 62209 } 62210 62211 this.getCurrentOptions = function() { 62212 var curOptions = {}; 62213 62214 if (_type == "rgb" || _type == "ranged_rgb") { 62215 jQuery.extend(curOptions, _options); 62216 62217 if (_channelToggleOption == true) { 62218 // channel toggle checkboxes may have been selected/unselected 62219 curOptions["channel_toggle"].selected.r = _red_on; 62220 curOptions["channel_toggle"].selected.g = _green_on; 62221 curOptions["channel_toggle"].selected.b = _blue_on; 62222 } 62223 62224 if (_algorithmOption == true) { 62225 // TODO: temporary placeholder logic, assumes application.json sets to "" 62226 curOptions["algorithm"] = ""; 62227 } 62228 } 62229 62230 else if(_type == "contrast"){ 62231 // need to call this in order to keep the color guide legend in the _options hash 62232 jQuery.extend(curOptions, _options); 62233 } 62234 62235 return curOptions; 62236 } 62237 62238 /* -------------------- listener/event handlers -------------------------------- */ 62239 62240 /** 62241 * Intercept slider change events so that the slider labels can be updated automatically. 62242 * After label update, the user's listener function is called to allow normal event processing. 62243 * 62244 * @param event - sets a property "sliderType" on this object to identify the slider 'flavor' 62245 * @param ui 62246 */ 62247 function doSliderChange(event, ui) { 62248 62249 _self.updateSliderLabels(); 62250 62251 event.sliderType = _type; // identifies the sliderWidget type 62252 62253 if(event.type == "slide"){ 62254 // don't do anything in this case (just the updateSliderLabel action already performed above) 62255 } 62256 else if(event.type == "reset"){ 62257 return _listener(event, ui); 62258 } 62259 else 62260 _listener(event, ui); 62261 } 62262 62263 /** 62264 * Listener function for the optional channel toggle checkboxes. This is only used for c
62264olor channel sliders (RGB) 62265 * When selected, color channel is on and operates normally. 62266 * When unselected, color channel value will be interpreted as value = 0, 62267 * althought the slider positions will not be affected. 62268 * This logic is implemented in the 'getSliderValues' function. 62269 * Will generate a 'change' event so that the listener can redraw the image. 62270 * 62271 */ 62272 function doChannelToggle() { 62273 if (_type == "rgb" || _type == "ranged_rgb") { 62274 62275 _red_on = jQuery("#red_cb" + _instanceId).attr("checked") !== undefined ? true : false; 62276 _green_on = jQuery("#green_cb" + _instanceId).attr("checked") !== undefined ? true : false; 62277 _blue_on = jQuery("#blue_cb" + _instanceId).attr("checked") !== undefined ? true : false; 62278 } 62279 62280 var e = jQuery.Event("change"); 62281 // we don't have a jQuery ui object, so just pass in an arbitrary object. 62282 doSliderChange(e, new Object()); 62283 } 62284 62285 function doReset() { 62286 var e = jQuery.Event("reset"); 62287 var defVals = doSliderChange(e, new Object()); 62288 _defaultValues = defVals; 62289 resetSliderDefaults(_type); 62290 } 62291 62292 /** 62293 * Listener function for the optional Expand Range checkbox. This is only used for ranged_rgb sliders. 62294 * When selected, the range is expanded. When unselected, the range is returned to normal 62295 * range. If the handle values exceed the upper bounds of the normal range, then are set 62296 * to the upper bounds value of the normal range. 62297 * TODO: ensure that lower bounds are set correctly; cannot assume that they are fixed at zero! 62298 * 62299 * The slider handle changes will autogenerate a 'change' event 62300 */ 62301 function doExpandRange() { 62302 var curValues; 62303 62304 // expand the range slider's range if the checkbox is selected 62305 var expandChecked = this.checked; // 'this' is the event target, the checkbox 62306 if (expandChecked == true) { 62307 expandRange(); 62308 } 62309 else { 62310 // contract the range slider's expanded range down to to the normal range if the checkbox is unselected, 62311 // and update the lo/hi slider handle values because there is a good chance that they are out-of-range. 62312 // Note lower bound of range is the same for normal, expanded range. 62313 var handle_lo, handle_hi; 62314 var lowerBound, upperBound; 62315 62316 curValues = jQuery("#red_slider" + _instanceId).slider("option", "values"); 62317 handle_lo = curValues[0]; 62318 handle_hi = curValues[1]; 62319 lowerBound = _ranges.r[0]; 62320 upperBound = _ranges.r[1]; 62321 handle_lo = handle_lo > upperBound ? upperBound : handle_lo; 62322 handle_hi = handle_hi > upperBound ? upperBound : handle_hi; 62323 jQuery("#red_slider" + _instanceId).slider("option", "min", lowerBound); 62324 jQuery("#red_slider" + _instanceId).slider("option", "max", upperBound); 62325 curValues[0] = handle_lo; 62326 curValues[1] = handle_hi; 62327 jQuery("#red_slider" + _instanceId).slider("option", "values", curValues); 62328 62329 curValues = jQuery("#green_slider" + _instanceId).slider("option", "values"); 62330 handle_lo = curValues[0]; 62331 handle_hi = curValues[1]; 62332 handle_lo = handle_lo > upperBound ? upperBound : handle_lo; 62333 handle_hi = handle_hi > upperBound ? upperBound : handle_hi; 62334 jQuery("#green_slider" + _instanceId).slider("option", "min", lowerBound); 62335 jQuery("#green_slider" + _instanceId).slider("option", "max", upperBound); 62336 curValues[0] = handle_lo; 62337 curValues[1] = handle_hi; 62338 jQuery("#green_slider" + _instanceId).slider("option", "values", curValues); 62339 62340 curValues = jQuery("#blue_slider" + _instanceId).slider("option", "values"); 62341 handle_lo = curValues[0]; 62342 handle_hi = curValues[1]; 62343 handle_lo = handle_lo > upperBound ? upperBound : handle_lo; 62344 handle_hi = handle_hi > upperBound ? upperBound : handle_hi; 62345 jQuery("#blue_slider" + _instanceId).slider("option", "min", lowerBound); 62346 jQuery("#blue_slider" + _instanceId).slider("option", "max", upperBound); 62347 curValues[0] = handle_lo; 62348 curValues[1] = handle_hi; 62349 jQuery("#blue_slider" + _instanceId).slider("option", "values", curValues); 62350 } 62351 } 62352 62353 /* ------------------------ initialize the function ---------------------- */ 62354
62355 function init() { 62356 // ensure all required input args are present and valid 62357 // TODO: this needs to be completed 62358 if (jQuery.inArray(_type, _sliderTypes) == -1) { 62359 return false; 62360 } 62361 62362 parseOptions(_options); 62363 buildHtml(_type); 62364 buildSliders(_type, doSliderChange); 62365 setOptionDefaults(_type); 62366 _self.updateSliderLabels(); 62367 } 62368 62369 init(); 62370} 62371; 62372"use strict"; 62373function busy_panel(busy_container) { 62374 62375 this.container = busy_container; 62376 62377 this.show_busy = function(is_busy) { 62378 62379 do_show_busy(is_busy, true); 62380 }; 62381 62382 this.show_disabled = function(is_disabled) { 62383 62384 do_show_busy(is_disabled, false); 62385 }; 62386 62387 var _self = this; 62388 var _events_setup = false; 62389 var _busy_indicator = {}; 62390 var BUSY = true; 62391 62392 62393 function position_busy_indicator() { 62394 62395 if(!_busy_indicator.panel) 62396 setup_busy_indicator(); 62397 62398 var pos = _self.container.offset(); 62399 var container_width = _self.container.outerWidth(); 62400 var container_height = _self.container.outerHeight(); 62401 62402 _busy_indicator.panel.css({ 62403 62404 position:'absolute', 62405 background: "none",//"url('/assets/aiclient/ajax-loader.gif') no-repeat center center", 62406 top:pos.top, 62407 left:pos.left, 62408 'background-color':'#FFF', 62409 opacity:0.7, 62410 width:container_width, 62411 height:container_height 62412 }); 62413 } 62414 62415 function setup_busy_indicator() { 62416 62417 var html = "<div id='busy_panel2'></div>"; 62418 _busy_indicator.panel = $j(html); 62419 _busy_indicator.panel.appendTo(_self.container); 62420 } 62421 62422 function do_show_busy(busy, busy_or_disabled) { 62423 62424 if(!_busy_indicator.panel) { 62425 setup_busy_indicator(); 62426 } 62427 62428 if(busy_or_disabled == BUSY) { 62429 _busy_indicator.panel.css({"background-image": "url('/assets/aiclient/ajax-loader.gif')"}); 62430 } 62431 else { 62432 _busy_indicator.panel.css({"background-image": "0 !important", "background-color":"#fff"}); 62433 } 62434 62435 if(busy) { 62436 62437 position_busy_indicator(); 62438 _busy_indicator.panel.show(); 62439 } 62440 else { 62441 62442 _busy_indicator.panel.hide(); 62443 } 62444 } 62445 62446 function setup_events() { 62447 62448 if(_events_setup) 62449 return; 62450 62451 $j(window).bind('resize', function() { position_busy_indicator()}); 62452 _events_setup = true; 62453 } 62454 62455 function init() { 62456 62457 position_busy_indicator(); 62458 setup_events(); 62459 } 62460 62461 init(); 62462} 62463; 62464"use strict"; 62465AIBS = AIBS || {}; 62466AIBS.UI = AIBS.UI || {}; 62467/** 62468 * @class 62469 * 62470 * 62471 * 62472 * 62473 * @param container <pre>The ID or element that is the container for this component_container. This component_container 62474 * will attempt to fill the given container.</pre> 62475 * 62476 * @param layout <pre>A description of how the components contained in this component_container should be arranged. 62477 * This uses the same format as AIBS.UI.info_widget, see details on the layout parameter there.</pre> 62478 * @param components <pre>A description and settings for each component, details TBD.</pre> 62479 * 62480 * @param options <pre>A hash containing optional information. Possible values are: 62481 * 62482 * on_ready: A callback function to be called when the experiment have been fetched. This 62483 * callback should accept a single argument, which is the experiment data. 62484 * 62485 * on_error: A callback function to be called if an error occurs fetching data. 62486 * 62487 * set_component_properties: A callback invoked after the HTML layout has been created but before 62488 * any components have been constructed. The intended use is for setting run-time 62489 * properties that aren't available as part of the component configuration that's 62490 * passed in as part of the options.component_config hash. 62491 * 62492 * data_map: A description of which fields in the data object should be passed to 62493 * which component, details TBD. 62494 * 62495 * table_class: A class name to be added to the table. 62496 * 62497 * row_class: A class name to be added to each row. 62498 * 62499 * cell_class: A class name to be added to each cell. 62500 * 62501 * The info_widget table is loaded with class names to facilitate styling. Adding a table class is usually all 62502 * that's necessary to target any cell in an info_widget table. 62503 * </pre> 62504 */ 62505AIBS.UI.component_container = (function () { 62506 62507 return( 62508 function (container, options) { 62509 62510 this.container = $j(container); 62511 this.settings = { 62512 on_ready:null, 62513 on_error:null, 62514 set_component_properties:null, 62515 layout_config:{ 62516 layout:null, 62517 data_map:[], 62518 decorators:null, 62519 table_class:null, 62520 row_class:null, 62521 cell_class:null 62522 }, 62523 component_config:{} 62524 }; 62525 $j.extend(true, this.settings, options); 62526 62527 62528 /** 62529 * The get_state method is called on all contained components. They are aggregated 62530 * and returned as a hash, keyed by component name. 62531 * @return {*} 62532 */ 62533 this.get_state = function () { 62534 62535 return(do_get_state()); 62536 }; 62537 62538 /** 62539 * The set_state method is called on all contained components. 62540 * @param state 62541 */ 62542 this.set_state = function (state) { 62543 62544 do_set_state(state); 62545 }; 62546 62547 /** 62548 * The reset method is called on all contained components. Generally this means 62549 * that the component is reset to its default state. 62550 */ 62551 this.clear = function () { 62552 62553 do_clear(); 62554 }; 62555 62556 /** 62557 * The enable method is called on all contained components 62558 * 62559 * @param enabled 62560 */ 62561 this.enable = function(enabled) { 62562 62563 do_enable(enabled); 62564 }; 62565 62566 /** 62567 * As in the CSS declarations for display:none & visible:hidden, 62568 * the display & visible methods on a component container (or component) 62569 * mean different things. display(false) means the component should 62570 * not be included in layout. visible(false) means that the component 62571 * still takes up space in the page layout, but is not rendered. 62572 * 62573 * @param displayed 62574 */ 62575 this.display = function(displayed) { 62576 62577 do_display(displayed); 62578 }; 62579 62580 /** 62581 * As in the CSS declarations for display:none & visible:hidden, 62582 * the display & visible methods on a component container (or component) 62583 * mean different things. display(false) means the component should 62584 * not be included in layout. visible(false) means that the component 62585 * still takes up space in the page layout, but is not rendered. 62586 * 62587 * @param vis 62588 */ 62589 this.visible = function(vis) { 62590 62591 do_visible(vis); 62592 }; 62593 62594 /** 62595 * The component container and all that it contains are removed from the DOM. 62596 * 62597 */ 62598 this.destroy = function() { 62599 62600 do_destroy(); 62601 }; 62602 62603 /** 62604 * 62605 * @private 62606 */ 62607 var _id = "_cc_" + Math.floor(Math.random() * 10001); 62608 var _self = this; 62609 var _state = null; 62610 var _the_view = null; 62611 var _layout_widget = null; 62612 62613 62614 function create_view() { 62615 62616 var settings = { 62617 widget_config:_self.settings.component_config, 62618 on_ready:_self.settings.on_ready, 62619 delegates:{ 62620 layout_delegate: _self.settings.layout ? build_layout : null 62621 } 62622 }; 62623 62624 if(_self.settings.set_component_properties) 62625 _self.settings.set_component_properties(_self.settings.component_config); 62626 62627 _the_view = new AIBS.Application.view(_self.container, settings); 62628 } 62629 62630 function build_layout(layout_container, component_config, view_id) { 62631 62632 var layout_options = { 62633 table_class:_self.settings.layout_config.table_class, 62634 row_class:_self.settings.layout_config.row_class, 62635 cell_class:_self.settings.layout_config.cell_class 62636 }; 62637 62638 _layout_widget = new AIBS.UI.info_widget( 62639 layout_container, 62640 _self.settings.layout_config.layout, 62641 _self.settings.layout_config.data_map, 62642 _self.settings.layout_config.decorators, 62643 layout_options); 62644 62645 // each component_config must be given a container_id value that matches 62646 // the ID of the table cell in which it is supposed to appear. 62647 for(var name in component_config) { 62648 62649 var component = component_config[name]; 62650 var cell_id = _id + view_id + component.column + "_" + component.row; 62651 var cell = _layout_widget.get_cell(component.row, component.column); 62652 $j(cell).attr('id', cell_id); 62653 component.container_id = cell_id; 62654 } 62655 } 62656 62657 function do_enable(enabled) { 62658 62659 invoke_widget_method('enable', enabled); 62660 } 62661 62662 function do_destroy() { 62663 62664 _the_view.destroy(); 62665 if(_layout_widget) 62666 $j('#_iw_t_'+_layout_widget.id).remove(); 62667 } 62668 62669 function do_clear() { 62670 62671 invoke_widget_method('clear'); 62672 } 62673 62674 function do_visible(vis) { 62675 62676 $j('#_iw_t_'+_layout_widget.id).css({visibility:vis ? 'visible' : 'hidden'}); 62677 } 62678 62679 function do_display(displayed) { 62680 62681 $j('#_iw_t_'+_layout_widget.id).css({display:displayed ? 'block' : 'none'}); 62682 } 62683 62684 function do_set_state(new_state) { 62685 62686 _state = new_state; 62687 invoke_widget_method('set_state', _state); 62688 } 62689 62690 function do_get_state() { 62691 62692 if(!_the_view) 62693 return({}); 62694 62695 _state = {}; 62696 var widgets = _the_view.get_widgets();
62697 for(var name in widgets) { 62698 62699 var component = widgets[name].instance; 62700 if(component && component.get_state) 62701 _state[name] = component.get_state(); 62702 } 62703 62704 return(_state); 62705 } 62706 62707 function invoke_widget_method(method_name, param) { 62708 62709 if(!_the_view) 62710 return; 62711 62712 _state = {}; 62713 var widgets = _the_view.get_widgets(); 62714 for(var name in widgets) { 62715 62716 var component = widgets[name].instance; 62717 if(component && typeof(component[method_name]) == 'function') 62718 component[method_name](param); 62719 } 62720 } 62721 62722 function init() { 62723 62724 create_view(); 62725 } 62726 62727 init(); 62728 }); 62729})(); 62730 62731 62732"use strict"; 62733function gene_neuroblast_widget(container, options) { 62734 62735 this.container = $j(container); 62736 this.settings = { 62737 on_search:null, 62738 on_error:null, 62739 on_search_ready:null, 62740 "select_width":"200px", 62741 "ages":[], 62742 "structures":[] 62743 }; 62744 $j.extend(true, this.settings, options); 62745 62746 62747 this.get_state = function () { 62748 62749 var state = { 62750 search_type:"neuroblast", 62751 search_term:get_search_state() 62752 }; 62753 62754 return(get_search_state()); 62755 }; 62756 62757 this.set_state = function (state) { 62758 62759 set_search_state(state); 62760 }; 62761 62762 this.clear = function () { 62763 62764 _gene_label.text(''); 62765 }; 62766 62767 62768 /** 62769 * 62770 * @private 62771 */ 62772 var _id = "_nb_" + Math.floor(Math.random() * 10001); 62773 var _self = this; 62774 var _state = null; 62775 var _structure_select = null; 62776 var _age_select = null; 62777 var _search_button = null; 62778 var _gene_label = null; 62779 62780 62781 function build_scaffold() { 62782 62783 var html = "<table class='neuroblast_table'>"; 62784 html += "<tr><td>Correlation Search</td><td id='neuroblast_gene_label'></td></tr>"; 62785 html += "<tr><td><select id='structures" + _id + "' class='neuroblast_structure_select' multiple></select></td>"; 62786 html += "<td rowspan='2'><div id='search" + _id + "'>Search</div></td></tr>"; 62787 html += "<tr><td><select id='ages" + _id + "' class='neuroblast_age_select' multiple></select></td></tr>"; 62788 html += "</table>"; 62789 62790 _self.container.append(html); 62791 _structure_select = $j("#structures" + _id); 62792 _age_select = $j("#ages" + _id); 62793 _search_button = $j('#search'+_id); 62794 _gene_label = $j('#neuroblast_gene_label'); 62795 } 62796 62797 function build_components() { 62798 62799 for(var i = 0; i < _self.settings.ages.length; i++) { 62800 var age = _self.settings.ages[i]; 62801 var options = _age_select[0].options; 62802 options[options.length] = new Option(age.label, age.id); 62803 if(age.selected) 62804 options[options.length-1].selected = 'selected'; 62805 } 62806 62807 for(var i = 0; i < _self.settings.structures.length; i++) { 62808 var structure = _self.settings.structures[i]; 62809 var options = _structure_select[0].options; 62810 options[options.length] = new Option(structure.label, structure.id); 62811 if(structure.selected) 62812 options[options.length-1].selected = 'selected'; 62813 } 62814 62815 _structure_select.dropdownchecklist({ 62816 firstItemChecksAll:true, 62817 icon: { 62818 placement: 'right', 62819 toOpen: 'ui-icon-triangle-1-s', 62820 toClose: 'ui-icon-triangle-1-w' 62821 }, 62822 zIndex: 200, 62823 width:_self.settings.select_width 62824 }); 62825 //_structure_select.dropdownchecklist('disable'); 62826 62827 _age_select.dropdownchecklist({ 62828 firstItemChecksAll:true, 62829 icon: { 62830 placement: 'right', 62831 toOpen: 'ui-icon-triangle-1-s', 62832 toClose: 'ui-icon-triangle-1-w' 62833 }, 62834 zIndex: 200, 62835 width:_self.settings.select_width 62836 }); 62837 //_age_select.dropdownchecklist('disable'); 62838 62839 _search_button 62840 .button() 62841 .bind('click', do_search); 62842 } 62843 62844 function get_search_state() { 62845 62846 if(!_state) 62847 return(null); 62848 62849 //_search_options.domain, _search_options.rows, _search_options.sort_order, _search_options.image_set 62850 var state = { 62851 62852 search_type:'neuroblast', 62853 search_term:{ 62854 domain:_structure_select.val().filter(function(item) { return(item != 'All'); }), // structure acronym or id 62855 ages:_age_select.val().filter(function(item) { return(item != 'All')} ), // age labels 62856 rows:[_state.gene_id], // the gene ID 62857 gene_id:_state.gene_id, 62858 gene_acronym:_state.gene_acronym 62859 } 62860 }; 62861 62862 return(state); 62863 } 62864 62865 function set_search_state(new_state) { 62866 62867 if(!new_state) 62868 return; 62869 62870 if(new_state.gene_acronym) 62871 _gene_label.text("Gene: " + new_state.gene_acronym); 62872 62873 if(new_state.domain) { 62874 62875 var structures = new_state.domain.split(','); 62876 _structure_select.val(structures); 62877 _structure_select.dropdownchecklist('refresh'); 62878 } 62879 62880 if(new_state.ages) { 62881 62882 var ages = new_state.ages.split(','); 62883 _age_select.val(ages); 62884 _age_select.dropdownchecklist('refresh'); 62885 } 62886 62887 _state = new_state; 62888 62889 if(_self.settings.on_search_ready) 62890 _self.settings.on_search_ready(); 62891 } 62892 62893 function do_search() { 62894 62895 if(_self.settings.on_search) 62896 _self.settings.on_search(get_search_state()); 62897 } 62898
62899 function init() { 62900 62901 build_scaffold(); 62902 build_components(); 62903 } 62904 62905 init(); 62906} 62907; 62908"use strict"; 62909/** 62910 * 62911 * @param container 62912 * @param options 62913 */ 62914function gene_preview_container(container, options) { 62915 62916 this.container = $j(container); 62917 this.settings = { 62918 on_search_ready:null, 62919 on_error:null, 62920 on_search:null, 62921 urls:null, 62922 set_component_properties:set_widget_properties, 62923 layout_config:{ 62924 layout:null, 62925 data_map:[], 62926 decorators:null, 62927 table_class:null, 62928 row_class:null, 62929 cell_class:null 62930 }, 62931 component_config:{} 62932 }; 62933 $j.extend(true, this.settings, options); 62934 62935 62936 // state will have a gene_id, and might also have neuroblast search state. 62937 this.set_state = function(new_state) { 62938 62939 this.clear(); 62940 62941 _state = new_state; 62942 if(_state.id || _state.gene_id) 62943 do_new_gene(_state.id || _state.gene_id); 62944 62945 }; 62946 62947 this.get_state = function() { 62948 62949 var widget_state = _widget_container.get_state(); 62950 return(widget_state['neuroblast_widget']); 62951 }; 62952 62953 this.clear = function() { 62954 62955 if(_widget_container) 62956 _widget_container.clear(); 62957 62958 _busy_panel.show_disabled(true); 62959 }; 62960 62961 var _self = this; 62962 var _urls = _self.settings.urls; 62963 var _state = null; 62964 var _the_model = null; 62965 var _busy_panel = null; 62966 var _widget_container = null; 62967 62968 62969 function set_widget_properties(widgets) { 62970 62971 for (var name in widgets) { 62972 62973 switch (name) { 62974 62975 case "related_data_widget": 62976 widgets[name].settings.related_data_url = _self.settings.urls.related_data; 62977 break; 62978 case "thumbnail_set_widget": 62979 widgets[name].settings.thumbnail_url = _self.settings.urls.threed_thumbnail; 62980 widgets[name].settings.detail_url = _self.settings.urls.experiment; 62981 widgets[name].settings.image_view_url = _self.settings.urls.ivt; 62982 break; 62983 case "neuroblast_widget": 62984 widgets[name].settings.on_search_ready = _self.settings.on_search_ready; 62985 widgets[name].settings.on_error = _self.settings.on_error; 62986 widgets[name].settings.on_search = _self.settings.on_search; 62987 break; 62988 default: 62989 ; 62990 } 62991 } 62992 } 62993 62994 62995 function build_components() { 62996 62997 _widget_container = new AIBS.UI.component_container(_self.container, _self.settings); 62998 _busy_panel = new busy_panel(_self.container); 62999 } 63000 63001 function do_new_gene(gene_id) { 63002 63003 _busy_panel.show_busy(true); 63004 _the_model = new AIBS.DATA.gene_preview_model(_urls.data_service, 63005 gene_id, 63006 { 63007 on_ready:do_data_ready, 63008 on_error:do_data_error, 63009 metadata_delegate:manage_metadata 63010 }); 63011 } 63012 63013 function manage_metadata(raw_data) { 63014 63015 if(raw_data.response) 63016 delete raw_data.response; 63017 63018 var len = raw_data.length; 63019 for(var i = 0; i < len; i++) { 63020 63021 // for some reason the 'delegate' column is not actually a boolean; it is 63022 // a char either 't' or 'f'. turn it into a bool here. 63023 raw_data[i].delegate = (raw_data[i].delegate == 't'); 63024 63025 // add a field to mark which of the experiments should be displayed as thumbnails. 63026 // if it's a delegated experiment AND its included in the thumbnail sizes AND it has a valid 63027 // storage directory, mark the 'thumbnail' field true. 63028 raw_data[i].thumbnail = (raw_data[i].delegate 63029 && 63030 (raw_data[i].storage_directory !== null) 63031 && 63032 (_self.settings.component_config.thumbnail_set_widget.settings.thumbnail_sizes[raw_data[i].label] !== undefined)) 63033 } 63034 63035 return(raw_data); 63036 } 63037 63038 function do_data_ready(data) { 63039 63040 var glom = { 63041 63042 gene_id:_state.id || _state.gene_id, 63043 gene_acronym:_state.acronym ? _state.acronym : 63044 _state['genes.acronym'] ? _state['genes.acronym'] : '', 63045 experiments:data 63046 }; 63047 $j.extend(true, glom, _state); 63048 63049 _busy_panel.show_busy(false); 63050 _widget_container.set_state(glom); 63051 } 63052 63053 function do_data_error(err) { 63054 63055 _busy_panel.show_busy(false); 63056 63057 if(_self.settings.on_error) 63058 _self.settings.on_error(err); 63059 } 63060
63061 function init() { 63062 63063 build_components(); 63064 _busy_panel.show_disabled(true); 63065 } 63066 63067 init(); 63068} 63069 63070 63071function placeholder_widget(container, settings) { 63072 63073 $j(container) 63074 .css({height:(settings.height ? settings.height : 20) +'px'}) 63075 .html(settings.name ? settings.name : ''); 63076} 63077 63078; 63079"use strict"; 63080AIBS = AIBS || {}; 63081AIBS.DATA = AIBS.DATA || {}; 63082 63083/** 63084 * @class 63085 * 63086 * The AIBS.DATA.gene_preview_model provides access to information about a single gene. It is 63087 * designed to provide all the necessary (non-image) information to work with the gene-centric 63088 * search result preview component used in the Developing Mouse project. 63089 * 63090 * This object requires the AIBS.DATA.API_DAO and will throw at instantiation if it is not present. 63091 * 63092 * 63093 * @param data_service_url <pre>A string that is the base path of the RMA API, for example http://api.brain-map.org/api/v2/data.</pre> 63094 * @param gene_id <pre>The ID of the gene (NCBI Gene) for which data is to be fetched.</pre> 63095 * @param options <pre>A hash containing optional information. Possible values are: 63096 * 63097 * on_ready: A callback function to be called when the experiment have been fetched. This 63098 * callback should accept a single argument, which is the experiment data. 63099 * 63100 * on_error: A callback function to be called if an error occurs fetching data. 63101 * 63102 * metadata_delegate: If provided, the metadata delegate will be called _after_ the default metadata 63103 * routine is run. This delegate is called with one parameter, which is the raw data 63104 * returned by the RMA call. 63105 * 63106 * Typically the delegate will add or update fields on the given gene 63107 * and return the same object it received. 63108 * 63109 * </pre> 63110 */ 63111AIBS.DATA.gene_preview_model = (function () { 63112 63113 return( 63114 function (data_service_url, gene_id, options) { 63115 63116 this.data_service = data_service_url; 63117 this.gene_id = gene_id; 63118 this.settings = { 63119 on_ready:null, 63120 on_error:null, 63121 "product_ids":"3", 63122 metadata_delegate:null 63123 }; 63124 $j.extend(true, this.settings, options); 63125 63126 63127 /** 63128 * returns the gene data previously fetched 63129 * @return {*} 63130 */ 63131 this.gene_data = function () { 63132 63133 return(_the_data); 63134 }; 63135 63136 /** 63137 * Fetch data for the given gene_id. WHen the fetch completes either 63138 * on_ready or on_error will be called. The newly fetched data can 63139 * then be accessed via the gene_data() method. 63140 * 63141 * @param gene_id 63142 */ 63143 this.fetch_gene_data = function (gene_id) { 63144 63145 this.gene_id = gene_id; 63146 fetch_data(this.gene_id); 63147 } 63148 63149 63150 /** 63151 * 63152 * @private 63153 */ 63154 var _self = this; 63155 var _the_data = null; 63156 var _the_dao = null; // the API_DAO instantiated internally 63157 63158 63159 function fetch_data(gene_id) { 63160 63161 var opts = { 63162 success_callback:receive_data, 63163 error_callback:show_data_error 63164 }; 63165 gene_data(gene_id, opts); 63166 } 63167 63168 63169 function receive_data(response_data, text_status, xhdr) { 63170 63171 _the_data = massage_data(response_data); 63172 63173 if (_self.settings.on_ready) 63174 _self.settings.on_ready(_the_data); 63175 } 63176 63177 63178 function massage_data(raw_data) { 63179 63180 // if the caller wants to do their own metadata calculation, do it here. 63181 if (_self.settings.metadata_delegate) 63182 raw_data = _self.settings.metadata_delegate(raw_data); 63183 63184 return(raw_data); 63185 } 63186 63187 63188 function show_data_error(msg) { 63189 63190 var err_msg = 'Your search could not be completed'; 63191 63192 if (msg) { 63193 if (msg.msg) 63194 err_msg += ' (' + msg.msg + ')'; 63195 else 63196 err_msg += ' (' + inspect('', msg) + ')'; 63197 } 63198 else 63199 err_msg += '.'; 63200 63201 if (_self.settings.on_error) 63202 _self.settings.on_error(err_msg); 63203 } 63204 63205 63206 /* The actual data access bits here-- */ 63207 function gene_data(id, options) { 63208 63209 options.criteria = "products[id$in'" + _self.settings.product_ids + "'][failed$eqfalse],genes[id$eq" + id + "]," 63210 if (options.ages) 63211 options.criteria += "specimen(donor(age[name$in" + options.ages + ")),"; 63212 else 63213 options.criteria += "specimen(donor(age)),"; 63214 options.criteria += "rma::options[tabular$eq'data_sets.id,data_sets.delegate,data_sets.storage_directory,ages.name as label'],rma::options[order$eq'ages.embryonic desc,ages.days']"; 63215 63216 options.format = 'json'; 63217 options.verb = 'GET'; 63218 63219 return(_the_dao.query('SectionDataSet', options)); 63220 } 63221
63222 function init() { 63223 63224 if (!AIBS.DATA.API_DAO) 63225 throw "The gene preview model requires AIBS.DATA.API_DAO, which is not present."; 63226 63227 _the_dao = new AIBS.DATA.API_DAO(_self.data_service, {}); 63228 63229 if (_self.gene_id) 63230 fetch_data(_self.gene_id); 63231 } 63232 63233 /* initialize the instance */ 63234 init(); 63235 63236 }); 63237})(); 63238 63239 63240 63241"use strict"; 63242/** 63243 * Just a wrapper around the existing AIBS.UI.related_data_widget class that makes it compliant with a 63244 * standard Component interface. 63245 * 63246 * @param container 63247 * @param settings 63248 * @param view 63249 * @return {AIBS.UI.related_data_widget} 63250 */ 63251function related_gene_data_widget(container, options, view) { 63252 63253 this.container = container; 63254 this.view = view; 63255 this.settings = { 63256 related_data_url:null 63257 }; 63258 $j.extend(true, this.settings, options); 63259 63260 /** 63261 * The new_state param is expected to contain a 'gene' hash with at least the field 'id' 63262 * which should be the aibs id of the gene for which related data is wanted. 63263 * 63264 * @param new_state 63265 */ 63266 this.set_state = function(new_state) { 63267 63268 if(!_the_widget) 63269 return; 63270 63271 var data = { 63272 id:new_state.gene ? new_state.gene.id : new_state.gene_id ? new_state.gene_id : new_state.id ? new_state.id : 0 63273 }; 63274 63275 _the_widget.set_data(data); 63276 }; 63277 63278 this.clear = function() { 63279 63280 63281 }; 63282 63283 var _self = this; 63284 var _the_widget = null; 63285 63286 function build() { 63287 63288 var gene_version = _self.settings.related_data_url.replace('related/matches', 'related/gene/matches'); 63289 _the_widget = new AIBS.UI.related_data_widget(container, gene_version + '?project=350&id='); 63290 63291 // The header for this widget is persistently a dark blue, via the ui-widget-header class. 63292 // The bit below seems to be the only way to override that. 63293 $j('#_iw_t_'+_the_widget.id()).find('td._c_0_0').removeClass('ui-widget-header').css({backgroundColor:"#efefef"}); 63294 } 63295 63296 build(); 63297} 63298; 63299"use strict"; 63300/** 63301 * 63302 * @param container 63303 * @param options 63304 */ 63305function threed_thumbnail_set(container, options) { 63306 63307 this.container = $j(container); 63308 this.settings = { 63309 "thumbnail_url":null, 63310 "detail_url":null, 63311 "image_view_url":null, 63312 "slider_height":0, 63313 "num_slider_images":36, 63314 "thumbnail_sizes":{}, 63315 "rows":0, 63316 "columns":0, 63317 "control_coordinates":{ 63318 "column":0, 63319 "row":0, 63320 "colspan":0, 63321 "rowspan":0 63322 }, 63323 "initial_state":{ 63324 "data":[], 63325 "positions":[ 63326 { 63327 "row":0, 63328 "column":0, 63329 "position":0 63330 } 63331 ] 63332 } 63333 }; 63334 $j.extend(true, this.settings, options); 63335 63336 63337 this.get_state = function () { 63338 63339 return(_state); 63340 }; 63341 63342 this.set_state = function (state) { 63343 63344 _state.data = state.experiments; 63345 create_thumbnails(_state.data); 63346 build_image_set_button(null); 63347 }; 63348 63349 this.clear = function () { 63350 63351 for(var c in _controls) { 63352 63353 _controls[c].thumbnail.clear(); 63354 } 63355 }; 63356 63357 63358 /** 63359 * the new_settings param can contain anything that's : 63360 * 63361 * 63362 * @param new_settings 63363 */ 63364 this.rebuild = function(new_settings) { 63365 63366 63367 } 63368 63369 63370 this.enable = function(enabled) { 63371 63372 63373 }; 63374 63375 this.display = function(displayed) { 63376 63377 63378 }; 63379 63380 this.visible = function(vis) { 63381 63382 63383 }; 63384 63385 this.destroy = function() { 63386 63387 63388 }; 63389 63390 /** 63391 * 63392 * @private 63393 */ 63394 var _id = "_nb_" + Math.floor(Math.random() * 10001); 63395 var _self = this; 63396 var _table = null; 63397 var _state = _self.settings.initial_state; 63398 var _controls = {}; 63399 var _the_slider = null; 63400 var _slider_index = null; 63401 var _control_cell = null; 63402 var _images_link = null; 63403 63404 63405 function build_scaffold() { 63406 63407 var html = "<table class='_threed_thumbnail_set'>"; 63408 var coords = _self.settings.control_coordinates; 63409 63410 for(var i = 0; i < _self.settings.rows; i++) { 63411 63412 html += "<tr>"; 63413 63414 for(var j = 0; j < _self.settings.columns; j++) { 63415 63416 html += "<td"; 63417 // if this is the cell where we're putting the controls, we have some work o do... 63418 if(i == coords.row && j == coords.column) { 63419 63420 html += " id='control_cell" + _id + "'"; 63421 html += " class='threed_set_controls'"; 63422 63423 if(coords.colspan && coords.colspan != 1) {
63424 html += " colspan='" + coords.colspan + "'"; 63425 j += coords.colspan - 1; 63426 } 63427 63428 if(coords.rowspan && coords.rowspan != 1) { 63429 html += " rowspan='" + coords.rowspan + "'"; 63430 i += coords.rowspan - 1; 63431 } 63432 } 63433 html += ">"; 63434 html += "</td>"; 63435 } 63436 html += "</tr>"; 63437 } 63438 html += "</table>"; 63439 63440 _table = $j(html).appendTo(_self.container); 63441 _control_cell = $j('#control_cell' + _id); 63442 } 63443 63444 function build_controls() { 63445 63446 build_slider(_control_cell); 63447 build_image_set_button(_control_cell); 63448 } 63449 63450 function create_thumbnails(data) { 63451 63452 if(!data) 63453 return; 63454 63455 var thumbnail_options = { 63456 project_cell_height:100, // height in pixels of the container 63457 slider_height:_self.settings.slider_height, // height in pixels reserved for the slider 63458 num_slider_images:_self.settings.num_slider_images, // number of images in an aba mini thumbnail 63459 thumbnail_dims:[], // dimensions of a single frame of an aba mini thumbnail filmstrip 63460 animate_duration:0, 63461 include_slider:false 63462 }; 63463 63464 // not all of the experiments given will be used for thumbnails, filter here. 63465 var thumbs = data.filter(function(exp) { return( exp.thumbnail); }); 63466 63467 for(var i = 0; i < thumbs.length; i++) { 63468 63469 var r = Math.floor(i / _self.settings.columns); 63470 var c = i % _self.settings.columns; 63471 var cell = $j(_table[0].rows[r].cells[c]); 63472 63473 // the thumbnails are all different widths 63474 thumbnail_options.thumbnail_dims = _self.settings.thumbnail_sizes[thumbs[i].label]; 63475 63476 var control_set = _controls[r+"_"+c]; 63477 if(!control_set) { 63478 63479 control_set = { 63480 thumbnail:new AIBS.UI.threed_thumbnail_widget(cell, thumbs[i].id, _self.settings.thumbnail_url, thumbnail_options), 63481 // put a label in the lower left corner of the thumbnail. This should be the age with a link to the experiment. 63482 label:new age_label(cell, thumbs[i].label, thumbs[i].id) 63483 }; 63484 _controls[r+"_"+c] = control_set; 63485 } else { 63486 63487 control_set.thumbnail.set_experiment(thumbs[i].id); 63488 control_set.label.set_label(thumbs[i].label, thumbs[i].id) 63489 } 63490 } 63491 } 63492 63493 function age_label(container, label, id) { 63494 63495 63496 this.set_label = function(new_label, new_id) { 63497 63498 _label.find('a') 63499 .attr('href', _self.settings.detail_url + "/" + new_id) 63500 .text(new_label); 63501 }; 63502 63503 var _label = null; 63504 63505 function build() { 63506 63507 var html = "<div class='age_label_container'>" 63508 html += "<a href='" + _self.settings.detail_url + "/" + id + "' target='_blank'>" + label + "</a>"; 63509 html += "</div>"; 63510 63511 _label = $j(html).appendTo(container); 63512 } 63513 63514 build(); 63515 } 63516 63517 63518 function build_slider(container) { 63519 63520 var slider_height = _self.settings.slider_height; 63521 var slider_width = container.width() - 20; 63522 63523 var slider_container = $j("<div class='slider_container'></div>").appendTo(container); 63524 _the_slider = $j("<div class='_threed_slider'>") 63525 .css({ 63526 width: slider_width+"px", 63527 height: slider_height+"px" 63528 }) 63529 .appendTo(slider_container); 63530 63531 _the_slider.slider({ 63532 min: 0, 63533 max: _self.settings.num_slider_images-1, 63534 value: _slider_index, 63535 slide: do_slide, 63536 animate:0 63537 }); 63538 63539 $j("<div class='slider_label_container'>Rotate Thumbnails</div>").appendTo(slider_container); 63540 } 63541 63542 function build_image_set_button(container) { 63543 63544 var id_list = ""; 63545 if(_state.data) { 63546 63547 var ids = _state.data.map(function(d) { return(d.id); }); 63548 if(ids) 63549 id_list = ids.join(','); 63550 } 63551 63552 if(!_images_link) { 63553 var cont = $j("<div class='image_button_container'></div>").appendTo(container);
63554 _images_link = $j("<a href='" + _self.settings.image_view_url + "?id=" + id_list + "' target='_blank'>See All Images for this Gene</a>") 63555 .appendTo(cont) 63556 //.button() 63557 //.bind('click', show_images); 63558 } else { 63559 63560 _images_link.attr('href', _self.settings.image_view_url + "?id=" + id_list); 63561 } 63562 } 63563 63564 63565 function do_slide(event, ui) { 63566 63567 _slider_index = ui.value; 63568 _table.find('img').each(function() { 63569 var img_width = $j(this).width() / _self.settings.num_slider_images; 63570 $j(this).css({ left:(-_slider_index * img_width)+"px" }); 63571 }); 63572 } 63573 63574 function rebuild() { 63575 63576 _table.remove(); 63577 _controls = {}; 63578 63579 build_scaffold(); 63580 build_controls(); 63581 } 63582 63583 function init() { 63584 63585 build_scaffold(); 63586 build_controls(); 63587 if(_state.data) 63588 create_thumbnails(_state.data); 63589 } 63590 63591 init(); 63592} 63593; 63594"use strict"; 63595AIBS.UI.ish_gene_heatmap = (function($j) { 63596 63597 return ( 63598 63599 function(container, gene_id, options) { 63600 63601 // public properties 63602 this.container = $j(container); 63603 63604 63605 // public methods 63606 63607 63608 63609 // private properties 63610 var _self = this; 63611 var _gene_id = gene_id; 63612 var _info_widget = null; 63613 var _canvas, _ctx, _img_for_canvas; 63614 var _img_div_container = null; 63615 var _canvas_container = null; 63616 var _tooltip_timer = null; 63617 var _heatmap_tooltip_container = null; 63618 var _saved_tooltip_x, _saved_tooltip_y, _img_div_offset_x, _img_div_offset_y, _img_div_offset; 63619 63620 var _age_points = options.age_points; 63621 var _display_structures = options.structures; 63622 var _urls = options.urls; 63623 63624 var _x_pixels_per_structure = 50; 63625 var _y_pixels_per_age = 30; 63626 63627 var _x_pixels_offset = 25; 63628 var _y_pixels_offset = 15; 63629 63630 var _x_pixels_expression_area = 575; // the heatmap image will be stretched to this size 63631 var _y_pixels_expression_area = 225; 63632 63633 var _mousemove_refresh_delay = 300; // in ms 63634 63635 function replace_img_with_canvas() { 63636 63637 if(_img_div_container.length == 0) // do nothing if not found 63638 return; 63639 63640 // the pre-generated heatmap images are 115 x 75 63641 // stretch the image to the canvas size defined
63642 var canvas_html = "<canvas id='heatmap_canvas' width='" + _x_pixels_expression_area + "' height='" + 63643 _y_pixels_expression_area + "'>"; 63644 _img_div_container.append(canvas_html); 63645 63646 var target_img = $j("img#heatmap_img"); 63647 target_img.hide(); 63648 63649 _canvas = document.getElementById('heatmap_canvas'); 63650 if(_canvas && _canvas.getContext) { 63651 _ctx = _canvas.getContext('2d'); 63652 _ctx.imageSmoothingEnabled = false; 63653 _ctx.webkitImageSmoothingEnabled = false; 63654 _ctx.mozImageSmoothingEnabled = false; 63655 _ctx.drawImage(target_img[0], 0, 0, _x_pixels_expression_area, _y_pixels_expression_area); 63656 } 63657 else { 63658 63659 } 63660 63661 return; 63662 63663 } 63664 63665 function hide_tooltip() { 63666 if (_tooltip_timer) { 63667 clearTimeout(_tooltip_timer); 63668 } 63669 63670 _heatmap_tooltip_container.fadeOut("fast"); 63671 63672 } 63673 63674 function show_tooltip(event) { 63675 63676 // many mousemove events may happen with the same x, y 63677 if(_saved_tooltip_x == event.pageX && _saved_tooltip_y == event.pageY ) { 63678 return; 63679 } 63680 63681 hide_tooltip(); 63682 63683 _tooltip_timer = setTimeout(function() { 63684 63685 // use jquery offset instead of the prototype cumulativeOffset 63686 var imgPos = jQuery(event.currentTarget).offset(); 63687 //var mousePos = [ event.pageX - imgPos.left, event.pageY - imgPos.top ]; 63688 var display_x = event.pageX - imgPos.left; 63689 var display_y = event.pageY - imgPos.top; 63690 63691 // do not show the tool tip if the mouse position is out of the heatmap expression area 63692 if(display_x < 0 || display_x >= _x_pixels_expression_area || display_y < 0 || display_y >= _y_pixels_expression_area) 63693 return; 63694 63695 var age_index = Math.floor((display_y - _y_pixels_offset) / _y_pixels_per_age); 63696 var structure_index = Math.floor((display_x - _x_pixels_offset) / _x_pixels_per_structure); 63697 63698 if(age_index < 0 || age_index > _age_points.length - 1 || structure_index < 0 || structure_index > _display_structures.length - 1 ) { 63699 //hide_tooltip(); 63700 return; 63701 } 63702 63703
63704 var age_string = _age_points[age_index].atlas_name; 63705 var structure_string = _display_structures[structure_index].name; 63706 63707 _heatmap_tooltip_container.css({ "left": event.pageX+"px", "top": event.pageY+"px"}); 63708 63709 //_heatmap_tooltip_container.html(display_x+","+display_y+" : " + age_string + " - " + structure_string); 63710 //_heatmap_tooltip_container.html( age_string + " - " + structure_string ); 63711 _heatmap_tooltip_container.html( "View " + structure_string + " in the " + age_string ); 63712 63713 _heatmap_tooltip_container.fadeIn("fast"); 63714 63715 63716 }, _mousemove_refresh_delay); 63717 63718 _saved_tooltip_x = event.pageX; 63719 _saved_tooltip_y = event.pageY; 63720 } 63721 63722 function show_atlas_page(event) { 63723 63724 // use jquery offset instead of the prototype cumulativeOffset 63725 var imgPos = jQuery(event.currentTarget).offset(); 63726 63727 var display_x = event.pageX - imgPos.left; 63728 var display_y = event.pageY - imgPos.top; 63729 63730 // do nothing if the mouse position is out of the heatmap expression area 63731 if(display_x < 0 || display_x >= _x_pixels_expression_area || display_y < 0 || display_y >= _y_pixels_expression_area) 63732 return; 63733 63734 var age_index = Math.floor((display_y - _y_pixels_offset) / _y_pixels_per_age); 63735 var structure_index = Math.floor((display_x - _x_pixels_offset) / _x_pixels_per_structure); 63736 63737 if(age_index < 0 || age_index > _age_points.length - 1 || structure_index < 0 || structure_index > _display_structures.length - 1 ) 63738 return; 63739 63740 var atlas_id = _age_points[age_index].atlas_id; 63741 var structure_id = _display_structures[structure_index].id; 63742 63743 var reference_atlas_url = _urls.atlas_viewer_service + "/#atlas=" + atlas_id + "&structure=" +structure_id; 63744 window.open(reference_atlas_url); 63745 63746 } 63747 63748 63749 function browser() { 63750 63751 var ua = navigator.userAgent.toString(); 63752 63753 if(/Chrome/g.exec(ua) && !/MSIE/g.exec(ua)) // in case IE10 will have "Chrome" in its user agent string 63754 return('Chrome'); 63755 63756 if(/WebKit/g.exec(ua)) 63757 return('WebKit'); 63758 63759 if(/Firefox/g.exec(ua)) 63760 return('Firefox'); 63761 63762 if(/MSIE/g.exec(ua)) 63763 return('MSIE'); 63764 63765 return('Unknown'); 63766 } 63767 63768 63769 function post_image_load() { 63770 63771 _img_div_container = $j("div#heatmap_img_div"); 63772 63773 _img_div_offset = _img_div_container.offset(); 63774 _img_div_offset_x = _img_div_offset.left; 63775 _img_div_offset_y = _img_div_offset.top; 63776 63777 _heatmap_tooltip_container = $j("div#HeatmapTooltip"); 63778 _heatmap_tooltip_container.hide(); 63779 63780 var browser_detected; 63781 browser_detected = browser(); 63782 if(browser_detected == 'Chrome' || browser_detected == 'WebKit') { 63783 //alert(browser_detected + ' browser detected'); 63784 replace_img_with_canvas(); 63785 } 63786 63787 // hook up mouse events 63788 _img_div_container.mousemove(show_tooltip); 63789 _img_div_container.mouseout(hide_tooltip); 63790 _img_div_container.click(show_atlas_page); 63791 63792 } 63793 63794 function build_table(){ 63795 var container_table_html = "<div class='gene_heatmap_widget _gene_widget'><div class='ui-widget-header'>Expression Summary</div><table id='gene_heatmap_table'>" + 63796 "<tr class='row_for_one_age'><td class='first_column' rowspan='10'><b>Age</b></td><td class='center_text' colspan='13'><span><b>Anatomic Region</b></span></td>" + 63797 "<td class='expression_legend_column' rowspan='10'><table><tr class='row_padding'><td></td></tr><tr class='row_padding'><td></td></tr><tr><td>3.5</td></tr>" + 63798 "<tr><td><img height='140' width='20' src='/expressionSummaries/colormap.png'></td></tr><tr><td>-1.5</td></tr><tr><td>log(expression)</td></tr></table></td></tr>" + 63799 "<tr><td class='table_column_age'></td><td class='column_padding'></td>" + 63800 "<td class='column_for_one_structure'>RSP</td><td class='column_for_one_structure'>Tel</td><td class='column_for_one_structure'>PedHy</td>" + 63801 "<td class='column_for_one_structure'>p3</td><td class='column_for_one_structure'>
63801p2</td><td class='column_for_one_structure'>p1</td>" + 63802 "<td class='column_for_one_structure'>M</td><td class='column_for_one_structure'>PPH</td><td class='column_for_one_structure'>PH</td>" + 63803 "<td class='column_for_one_structure'>PMH</td><td class='column_for_one_structure'>MH</td>" + 63804 "</tr>" + 63805 "<tr class='row_padding'><td class='table_column_age'><td class='img_holder' colspan='12' rowspan='8'>" + 63806 "<div id='heatmap_img_div'><img id='heatmap_img' height='225' width='575' src='"+ "/expressionSummaries/"+ _gene_id + ".png'>" + "</div></td>" + 63807 "<tr class='row_for_one_age'><td class='table_column_age'>E11.5</td></tr>" + 63808 "<tr class='row_for_one_age'><td class='table_column_age'>E13.5</td></tr>" + 63809 "<tr class='row_for_one_age'><td class='table_column_age'>E15.5</td></tr>" + 63810 "<tr class='row_for_one_age'><td class='table_column_age'>E18.5</td></tr>" + 63811 "<tr class='row_for_one_age'><td class='table_column_age'>P4</td></tr>" + 63812 "<tr class='row_for_one_age'><td class='table_column_age'>P14</td></tr>" + 63813 "<tr class='row_for_one_age'><td class='table_column_age'>P28</td></tr>" + 63814 "</table></div><div id='HeatmapTooltip'>Brain</div>"; 63815 63816 _self.container.append(container_table_html); 63817 63818 var img = document.getElementById('heatmap_img'); 63819 img.onload = post_image_load; 63820 63821 } 63822 63823 63824 function init() { 63825 build_table(); 63826 63827 } 63828 63829 init(); 63830 } 63831 ); 63832 63833})(jQuery); 63834"use strict"; 63835/** 63836 * Copyright(c) The Allen Institute for Brain Science 2011 63837 * 63838 * 63839 * Tim Dolbeare 63840 */ 63841 63842/** 63843 * A widget for displaying the fields of a data object in a table. 63844 * <p> 63845 * The info widget class encapsulates the concept of using a table to show the fields of some data object, for 63846 * example, if you need to show the data contained in a Probe object, you could use an info_widget to manage the 63847 * task of laying out, constructing, labeling & populating a table with the fields of the Probe object. 63848 * </p><p> 63849 * This is a basic component used in composing more useful widgets. The info_widget doesn't do anything 63850 * useful on its own. You have to provide it a description of the layout you want to use as well as a map 63851 * of which fields should be displayed in which table cells, and optionally a map of 'decorator' functions used to do 63852 * any formatting necessary for specific fields. 63853 * </p> 63854 * 63855 * @class 63856 * @param container The DOM element or jQuery object that contains the info_widget. <p></p> 63857 * @param layout A description of how the rows and columns of the underlying HTML table should be constructed. 63858 * For example: 63859 <pre> 63860 var layout = { 63861 columns:4, 63862 rows:5, 63863 spans:[ 63864 {row:0, column:0, column_span:4, row_span:1}, 63865 {row:4, column:0, column_span:4, row_span:1} 63866 ] 63867 }; 63868 </pre> 63869 The example layout above would result in a table like this: 63870 <style> 63871 .ex_table {border:1px solid #000; width:500px; border-collapse:collapse} 63872 .ex_table td {border:1px solid #000; height:14px;} 63873 </style> 63874 <table class="ex_table"> 63875 <tbody> 63876 <tr> 63877 <td rowspan="1" colspan="4"></td> 63878 </tr> 63879 <tr> 63880 <td></td> 63881 <td></td> 63882 <td></td> 63883 <td></td> 63884 </tr> 63885 <tr> 63886 <td class="_iw_td _co _c_0 _c_0_2"></td> 63887 <td class="_iw_td _ce _c_1 _c_1_2"></td> 63888 <td class="_iw_td _co _c_2 _c_2_2"></td> 63889 <td class="_iw_td _ce _c_3 _c_3_2"></td> 63890 </tr> 63891 <tr class="_iw_tr _re _r_3"> 63892 <td class="_iw_td _co _c_0 _c_0_3"></td> 63893 <td class="_iw_td _ce _c_1 _c_1_3"></td> 63894 <td class="_iw_td _co _c_2 _c_2_3"></td> 63895 <td class="_iw_td _ce _c_3 _c_3_3"></td> 63896 </tr> 63897 <tr class="_iw_tr _ro _r_4"> 63898 <td rowspan="1" colspan="4" class="_iw_td _co _c_0 _c_0_4"></td> 63899 </tr> 63900 </tbody> 63901</table> 63902<p></p> 63903 * 63904 * @param data_map The data_map describes which fields in the data object should be displayed in 63905 * which cells of the table. For example, mapping the data in a Probe object to the table layed out 63906 * in the example above could look like this: <p></p> 63907 * <pre> 63908 var data_map = [ 63909 {field:'name', cell:[0,0]}, 63910 {field:'gene.acronym', cell:[1,1]}, 63911 {field:'orientation.name', cell:[1,2]}, 63912 {field:'forward_primer_sequence.sequence_data', cell:[3,1]}, 63913 {field:'reverse_primer_sequence.sequence_data', cell:[3,2]}, 63914 {field:'predicted_sequence.sequence_length', cell:[3,3]} 63915 ]; 63916 </pre> 63917 Note that field names can be qualified. Given a Probe object that has an associated Gene object, you 63918 can display the gene name by referencing the field 'gene.name'. The depth of qualification is only 63919 limited by the associations of the given data object. Also note the the field names are 63920 given as strings. 63921 <p></p> 63922 * @param decorators The decorator map can be used to map functions that do any special 63923 formatting required to a specific cell. The decorator map looks like this: 63924 <pre> 63925 var decorators = { 63926 '0,0':add_title, 63927 '0,4':wrap_sequence 63928 }; 63929 </pre> 63930 Decorator functions have a prototype like so: 63931 <pre> 63932 function my_decorator(container, data) : returns String; 63933 </pre> 63934 where 'container' is the TD element referenced in the decorator map and 'data' is the field to be displayed. 63935 Note that 'data' is often a string, but can be an array or any other object represented by a field in the 63936 data object passed to the info_widget in the set_data(data) method. The decorator should return a string,
63937 which may be any text or valid HTML. Below is an example that labels the 0,0 cell of our Probe info table 63938 with the Probe.name if the name field is not null, or just the word 'Probe' if it is null: 63939 <pre> 63940 function add_title(container, data) { 63941 if(data) 63942 return("Probe " + data); 63943 63944 return("Probe"); 63945 } 63946 </pre> 63947 * @param options 63948 * <ul> 63949 * <li> 63950 * redecorate: if true, any decorator functions will be called any time the info_widget is resized. Default = false. 63951 * </li> 63952 * <li> 63953 * table_class: A class name to be added to the table. 63954 * </li> 63955 * <li> 63956 * row_class: A class name to be added to each row. 63957 * </li> 63958 * <li> 63959 * cell_class: A class name to be added to each cell. 63960 * </li> 63961 * </ul> 63962 * <p></p> 63963 * For example: 63964 * <pre> 63965 var options = { 63966 table_class:'_probe_widget', 63967 redecorate:true 63968 }; 63969 * </pre> 63970 * The info_widget table is loaded with class names to facilitate styling. Adding a table class is usually all 63971 * that's necessary to target any cell in an info_widget table. Below is the HTML that would be 63972 * produced by the example parameters and options above:<p></p> 63973<table class="_iw_t _probe_widget" id="_iw_t_0"> 63974<br> <tbody> 63975<br> <tr class="_iw_tr _ro _r_0"> 63976<br> <td rowspan="1" colspan="4" class="_iw_td _co _c_0 _c_0_0"></td> 63977<br> </tr> 63978<br> <tr class="_iw_tr _re _r_1"> 63979<br> <td class="_iw_td _co _c_0 _c_0_1"></td> 63980<br> <td class="_iw_td _ce _c_1 _c_1_1"></td> 63981<br> <td class="_iw_td _co _c_2 _c_2_1"></td> 63982<br> <td class="_iw_td _ce _c_3 _c_3_1"></td> 63983<br> </tr> 63984<br> <tr class="_iw_tr _ro _r_2"> 63985<br> <td class="_iw_td _co _c_0 _c_0_2"></td> 63986<br> <td class="_iw_td _ce _c_1 _c_1_2"></td> 63987<br> <td class="_iw_td _co _c_2 _c_2_2"></td> 63988<br> <td class="_iw_td _ce _c_3 _c_3_2"></td> 63989<br> </tr> 63990<br> <tr class="_iw_tr _re _r_3"> 63991<br> <td class="_iw_td _co _c_0 _c_0_3"></td> 63992<br> <td class="_iw_td _ce _c_1 _c_1_3"></td> 63993<br> <td class="_iw_td _co _c_2 _c_2_3"></td> 63994<br> <td class="_iw_td _ce _c_3 _c_3_3"></td> 63995<br> </tr> 63996<br> <tr class="_iw_tr _ro _r_4"> 63997<br> <td rowspan="1" colspan="4" class="_iw_td _co _c_0 _c_0_4"></td> 63998<br> </tr> 63999<br> </tbody> 64000<br></table> 64001<p></p> 64002Both rows and cells have classes defined; columns have a class; there are also unique classes for even and odd on both 64003rows and columns; each row has an indexed class, as does each cell. To style just the second cell of the third row 64004of our Probe table on a page that has multiple info_widgets, you could use this: 64005<pre> 64006 ._probe_widget _c_1_2 { ...my style... } 64007</pre> 64008<p></p> 64009To do the same for all cells in the second column, you could use this: 64010<pre> 64011 ._probe_widget _c_1 { ...my style... } 64012</pre> 64013<p></p> 64014 64015 */ 64016AIBS.UI.info_widget = (function($j) { 64017 64018 64019 AIBS.UI._INFO_WIDGET_INDEX = {widget_count:0}; 64020 64021 return ( 64022 64023 function(container, layout, data_map, decorators, options) { 64024 64025 64026 this.container = container; 64027 this.layout = layout; 64028 this.data_map = data_map; 64029 this.decorators = decorators; 64030 this.options = options; 64031 this.id = null; 64032 64033 64034 /** 64035 * Sets the content at the given column,row 64036 * @param row {integer} 64037 * @param column {integer} 64038 * @param content {string} The content to be appended to the TD at the given column,row. 64039 */
64040 this.set_content = function(column, row, content) { 64041 var cell = _the_table.context.rows[row].cells[column]; 64042 cell.innerHTML = content; 64043 } 64044 64045 /** 64046 * Sets the data used to populate the info_widget. 64047 * @param data The data object to be displayed 64048 */ 64049 this.set_data = function(data) { 64050 _the_data = data; 64051 var len = this.data_map.length; 64052 64053 for(var i = 0; i < len; i++) { 64054 var map = this.data_map[i]; 64055 var cell = _the_table.context.rows[map.cell[1]].cells[map.cell[0]]; 64056 var decorator = this.decorators[map.cell[0] + ',' + map.cell[1]] 64057 var content = "" 64058 try { 64059 content = decorator ? decorator(cell, eval('_the_data.' + map.field)) : eval('_the_data.' + map.field); 64060 } catch(e) { 64061 // swallowing this. The eval tried to find a non-existent field 64062 } 64063 if(content) 64064 cell.innerHTML = content; 64065 } 64066 } 64067 64068 /** 64069 * Gets the DOM element (a TD) at the given column, row. 64070 * @param column 64071 * @param row 64072 * @return The TD at the given column,row. 64073 */ 64074 this.get_cell = function(row, column) { 64075 return( _the_table.context.rows[row].cells[column] ); 64076 } 64077 64078 64079 // private properties 64080 var _self = this; 64081 var _table_class = '_iw_t ' + (_self.options ? _self.options.table_class ? _self.options.table_class : '' : ''); 64082 var _row_class = '_iw_tr' + (_self.options ? _self.options.row_class ? _self.options.row_class : '' : ''); 64083 var _cell_class = '_iw_td' + (_self.options ? _self.options.cell_class ? _self.options.cell_class : '' : ''); 64084 var _redecorate = (_self.options ? _self.options.redecorate ? _self.options.redecorate : false : false); 64085 var _span_map = null; 64086 var _the_table = null; 64087 var _the_data = null; 64088 64089 // private methods 64090 /** 64091 * @private 64092 * @scope AIBS.UI.info_widget 64093 */ 64094 64095 64096 /** 64097 * Builds the html table based on user-supplied layout 64098 * @param container 64099 * @param layout 64100 * @param map 64101 */ 64102 function build_scaffold(container, layout, map) { 64103 64104 var id = '_iw_t_' + _self.id; 64105 var html = "<table id='" + id + "' class='" + _table_class + "'>"; 64106 for(var row = 0; row < layout.rows; row++) { 64107 var row_classes = _row_class + (row%2 ? ' _re' : ' _ro') + (' _r_'+row); 64108 html += "<tr class='" + row_classes + "'>"; 64109 64110 for(var column = 0; column < layout.columns; column++) { 64111 // consult the map to properly set the rowspan & colspan attributes. 64112 // where spans have been defined, TDs should not be created. 64113 var cell_classes = _cell_class + (column%2 ? ' _ce' : ' _co') + (' _c_'+column) + (' _c_'+column+'_'+row); 64114 if(_self.options.jquery_theme && column+row == 0) 64115 cell_classes += ' ui-widget-header'; 64116 64117 var flag = map[column][row]; 64118 switch(flag) { 64119 case 1: 64120 html += "<td class='" + cell_classes + "'>"; 64121 break; 64122 case 0: 64123 break; 64124 default: // if the map does not contain a 0 or 1 it is expected to contain a span object. 64125 html += "<td class='" + cell_classes; 64126 html += "' colspan='" + flag.column_span; 64127 html += "' rowspan='" + flag.row_span + "'>"; 64128 } 64129 html += "</td>"; 64130 } 64131 html += "</tr>"; 64132 } 64133 html += "</table>"; 64134 var e = $j(html); 64135 e.appendTo(_self.container); 64136 _the_table = $j(e[0]); 64137 64138 } 64139 64140 /** 64141 * Returns a dense row x column array, Where the array contains 64142 * a 0 if no td is necessary, a 1 where a td is necessary, and where 64143 * rowspan & colspans are defined, an anon object like so: 64144 * {row_span:3, column_span:1} 64145 * @param layout The user-supplied table layout 64146 */ 64147 function build_span_map(layout) { 64148 64149 // create a dense 2d array... 64150 var map = {}; 64151 for(var x = 0; x < layout.columns; x++) { 64152 map[x] = {}; 64153 for( var y = 0; y < layout.rows; y++) { 64154 map[x][y] = 1; 64155 } 64156 } 64157 64158 // and populate it with flags describing the row & column spans 64159 var len= layout.spans.length; 64160 for(var i = 0; i < len; i++) { 64161 var span = layout.spans[i]; 64162 64163 var row_start = span.row; 64164 var col_start = span.column; 64165 var row_stop = span.row + span.row_span; 64166 var col_stop = span.column + span.column_span; 64167 64168 for(var row = row_start;
64168 row < row_stop; row++) { 64169 for(var col = col_start; col < col_stop; col++) { 64170 map[col][row] = 0; 64171 } 64172 } 64173 map[span.column][span.row] = {row_span:span.row_span, column_span:span.column_span}; 64174 } 64175 return(map); 64176 } 64177 64178 /** 64179 * The data is re-set and any decorator functions are called again. 64180 */ 64181 function redecorate() { 64182 _self.set_data(_the_data); 64183 } 64184 64185 /** 64186 * Builds the private data structures, the HTML table, and binds 64187 * to the window 'resize' event. 64188 */ 64189 function init() { 64190 _self.id = AIBS.UI._INFO_WIDGET_INDEX.widget_count++; 64191 _span_map = build_span_map(_self.layout); 64192 build_scaffold(_self.container, _self.layout, _span_map); 64193 if(_redecorate) 64194 _self.container.bind('resize', redecorate); 64195 } 64196 64197 init(); 64198 64199 } 64200 64201 ); 64202 64203 64204})(jQuery); 64205 64206 64207 64208"use strict"; 64209/** 64210 * The ISH Experiment widget class. 64211 * <p></p> 64212 * The ish_experiment_widget widget presents some of the information contained in the data 64213 * object returned by a AIBS.DATA.ISH_DAO.experiment_detail() object. 64214 * <p></p> 64215 * This is a wrapper around the info_widget. 64216 * @class 64217 * @param container The DOM element or jQuery object that contains the info_widget. <p></p> 64218 */ 64219AIBS.UI.ish_experiment_widget = (function($j) { 64220 64221 return ( 64222 64223 function(container) { 64224 64225 // public properties 64226 this.container = $j(container); 64227 64228 /** 64229 * Set the data to be displayed. 64230 * @param data The return value from AIBS.DATA.ISH_DAO.experiment_detail() 64231 */ 64232 this.set_data = function(data) { 64233 _info_widget.set_data(data); 64234 } 64235 64236 this.set_title = function(title) { 64237 64238 _info_widget.set_content(0, 0, title); 64239 } 64240 64241 /** 64242 * A unique ID for this widget. This is not an HTML ID, it is an integer value scoped to 64243 * AIBS.UI. 64244 */ 64245 this.id = function() { 64246 if(_info_widget) 64247 return(_info_widget.id); 64248 64249 return(null); 64250 } 64251 64252 64253 /** 64254 * @private 64255 * @scope AIBS.UI.ish_experiment_widget 64256 */ 64257 var _self = this; 64258 var _info_widget = null; 64259 64260 64261 /** 64262 * The layout, data_map & decorators are defined for this info widget. 64263 */ 64264 function build_table() { 64265 64266 // describe the table 64267 var layout = { 64268 columns:4, 64269 rows:3, 64270 spans:[ 64271 {row:0, column:0, column_span:4, row_span:1}, 64272 {row:2, column:1, column_span:3, row_span:1} 64273 ] 64274 }; 64275 // describe how data should be mapped to cells in the table 64276 var data_map = [ 64277 {field:'name', cell:[0,0]}, 64278 {field:'genes[0].acronym', cell:[1,1]}, 64279 {field:'plane_of_section.name', cell:[3,1]}, 64280 {field:'treatments', cell:[1,2]} 64281 ]; 64282 // which cells require a callback to massage the data presentation before 64283 // placing it in the table. 64284 var decorators = { 64285 '0,0':add_title, 64286 '1,2':list_treatments 64287 }; 64288 64289 var options = { 64290 table_class:'_experiment_widget ui-widget-content', 64291 jquery_theme:true 64292 }; 64293 // build the widget 64294 _info_widget = new AIBS.UI.info_widget(_self.container, layout, data_map, decorators, options); 64295 // set the static field labels 64296 set_labels(_info_widget); 64297 } 64298 64299 /** 64300 * set the static labels for the Experiment fields 64301 * @param widget 64302 */ 64303 function set_labels(widget) { 64304 _info_widget.set_content(0,0, "Experiment"); 64305 _info_widget.set_content(0,1, "Gene"); 64306 _info_widget.set_content(2,1, "Plane of Section"); 64307 _info_widget.set_content(0,2, "Treatments"); 64308 } 64309 64310 /** 64311 * A decorator function that sets a title in the top-most cell 64312 * 64313 */ 64314 function add_title(container, data) { 64315 if(data) 64316 return("Experiment " + data); 64317 64318 return("Experiment"); 64319 } 64320 64321 /** 64322 * A decorator function that creates a comma-delimited list out of an array of treatment names. 64323 * @param container 64324 * @param data 64325 */ 64326 function list_treatments(container, data) { 64327 if(!data) 64328 return(""); 64329 64330 var s = ""; 64331 for(var i = 0; i < data.length; i++) { 64332 if(i) 64333 s += ", "; 64334 s += data[i].name 64335 } 64336 return(s); 64337 } 64338
64339 function init() { 64340 build_table(); 64341 } 64342 64343 init(); 64344 } 64345 ); 64346 64347})(jQuery); 64348"use strict"; 64349/** 64350 * 64351 * Creates instance of AIBS.UI.info_widget 64352 * 64353 * @param container 64354 * @param options 64355 */ 64356function ish_experiment_specimen_widget(container, options) { 64357 64358// public properties 64359 this.container = $j(container); 64360 64361 // these settings are appropriate for ABA, probably mouse in general 64362 this.settings = { 64363 jquery_theme:true, 64364 layout:{ 64365 columns:4, 64366 rows:6, 64367 spans:[ 64368 {row:0, column:0, column_span:4, row_span:1} 64369 ] 64370 }, 64371 data_map:{ 64372 "genes[0].acronym":[1,1], 64373 "genes[0].name":[3,1], 64374 "plane_of_section.name":[1,2], 64375 "treatments":[3,2], 64376 "probes[0].probe_type":[1,3], 64377 "probes[0].orientation.name":[3,3], 64378 "specimen.donor.organism.name":[1,4], 64379 "specimen.donor.strain":[3,4], 64380 "specimen.donor.sex":[1,5], 64381 "specimen.donor.age.days":[3,5] 64382 }, 64383 labels:{ 64384 "Experiment - Specimen":[0,0], 64385 "Gene Symbol":[0,1], 64386 "Gene Name":[2,1], 64387 "Plane of Section":[0,2], 64388 "Treatments":[2,2], 64389 "Probe Type":[0,3], 64390 "Orientation":[2,3], 64391 "Organism":[0,4], 64392 "Strain":[2,4], 64393 "Sex":[0,5], 64394 "Age":[2,5] 64395 }, 64396 decorators:{ 64397 "0,0":{ 64398 "name":"add_title", 64399 "options":{} 64400 }, 64401 "3,2":{ 64402 "name":"list_treatments", 64403 "options":{} 64404 }, 64405 "3,5":{ 64406 "name":"show_age_units", 64407 "options":{} 64408 } 64409 }, 64410 info_opts:{ 64411 table_class:'_experiment_specimen_widget' 64412 } 64413 }; 64414 $j.extend(true, this.settings, options); 64415 64416// public methods 64417 64418 // data is expected to be the graph of goodies returned by the ish_dao.experiment_specimen method 64419 this.set_data = function(data) { 64420 _info_widget.set_data(data); 64421 } 64422 64423 this.id = function() { 64424 if(_info_widget) 64425 return(_info_widget.id); 64426 64427 return(null); 64428 } 64429 64430 64431// private properties 64432 var _self = this; 64433 var _info_widget = null; 64434 64435 64436 function inspect(prefix, obj) { 64437 var s = ""; 64438 for(var key in obj) { 64439 if((typeof obj[key]) == "function") 64440 s = s; 64441 else if((typeof obj[key]) == "object" && obj[key] !== null & key != "element") 64442 s += "\n" + prefix + "key: " + key + inspect(prefix + " ", obj[key]); 64443 else 64444 s += "\n" + prefix + "key: " + key + ", value: " + obj[key]; 64445 } 64446 return(s); 64447 } 64448 64449 function build_table() { 64450 64451 _self.settings.info_opts.decorator_options = {}; 64452 for(var key in _self.settings.decorators) { 64453 var dec_options = _self.settings.decorators[key].options; 64454 _self.settings.decorators[key] = eval(_self.settings.decorators[key].name); 64455 _self.settings.info_opts.decorator_options[key] = dec_options; 64456 } 64457 64458 _info_widget = new AIBS.UI.info_widget( 64459 _self.container, 64460 _self.settings.layout, 64461 build_data_map(_self.settings.data_map), 64462 _self.settings.decorators, 64463 _self.settings.info_opts 64464 ); 64465 // set the static field labels 64466 set_labels(_info_widget); 64467 } 64468 64469 function set_labels(widget) { 64470 64471 for(var key in _self.settings.labels) { 64472 var coords = _self.settings.labels[key]; 64473 _info_widget.set_content(coords[0], coords[1], key); 64474 } 64475 } 64476 64477 function build_data_map(map) { 64478 64479 var map_array = []; 64480 for(var key in map) { 64481 map_array.push({ 64482 field:key, 64483 cell:map[key] 64484 }) 64485 } 64486 return(map_array); 64487 } 64488 64489 function add_title(container, data) { 64490 if(data) 64491 return("Experiment " + data); 64492 64493 return("Experiment"); 64494 } 64495 64496 function show_age_units(container, data) { 64497 if(data) 64498 return(data + " days"); 64499 64500 return(""); 64501 } 64502 64503 function list_treatments(container, data) { 64504 if(!data) 64505 return(""); 64506 64507 var s = ""; 64508 for(var i = 0; i < data.length; i++) { 64509 if(i) 64510 s += ", "; 64511 s += data[i].name 64512 } 64513 return(s); 64514 } 64515
64516 function init() { 64517 build_table(); 64518 } 64519 64520 init(); 64521} 64522; 64523/** 64524 * This file is intended to encapsulate model data for ISH image filters, and is intended to by used by 64525 * several different page components, like IVT, thumbnails, SIV. Ideally, this might be located in a 64526 * different directory, such as assets/javascripts/models. 64527 * 64528 * See ish_image_types.js for ISH image types 64529 */ 64530 64531function ish_filter_types() { 64532 var _filters = [ 64533 { 64534 filterName:'contrast', 64535 filterVals:'0.5,0.5,0,256,4' 64536 }, 64537 { 64538 filterName:'colormap', 64539 filterVals:'0.5,1,0,256,4' 64540 } 64541 ]; 64542 64543 var _image_to_filter = { 64544 'ish':'contrast', 64545 'expression':'colormap', 64546 'nissl':'contrast', 64547 'ache':'contrast', 64548 'ihc:parvalbumin':'contrast' 64549 }; 64550 64551 this.get_all_filters = function() { 64552 return _filters; 64553 }; 64554 64555 /** 64556 * Get all the filters associated with an array of image types 64557 * 64558 * @param {array} image_types 64559 * @returns {object} hashes with key = image_type, value = {filterName:name, filterVals:string_of_values} 64560 */ 64561 this.get_image_filters = function(image_types) { 64562 var image_filters = {}; 64563 var filter_name; 64564 var filter; 64565 var type; 64566 64567 for (var i = 0, len = image_types.length; i < len; ++i) { 64568 type = image_types[i]; 64569 filter_name = _image_to_filter[type]; 64570 for (var j = 0, count = _filters.length; j < count; ++j) { 64571 if (_filters[j].filterName == filter_name) { 64572 image_filters[type] = _filters[j]; 64573 } 64574 } 64575 } 64576 64577 return image_filters; 64578 }; 64579 64580 /** 64581 * Gets the filter name and default value 64582 * 64583 * @param {string} image_type 'ish', 'expression', 'nissl', 'ache', or 'ihc:parvalbumin' 64584 * @returns {object} hash of filter_name:default_filter_value or undefined if image_type not found 64585 */ 64586 this.get_filter = function(image_type) { 64587 var filter; 64588 var filter_name = _filters[image_type]; 64589 64590 if (filter_name != undefined) { 64591 for (var i = 0, len = _filters.length; i < len; ++i) { 64592 if (_filters[i].filterName == filter_name) { 64593 filter = _filters[i]; 64594 break; 64595 } 64596 } 64597 } 64598 64599 return filter; 64600 }; 64601} 64602; 64603"use strict"; 64604/** 64605 * The ISH Gene widget class. 64606 * <p></p> 64607 * The ish_gene_info widget presents the information contained in the data 64608 * object returned by a AIBS.DATA.ISH_DAO.experiment_detail().probes[0].gene object. 64609 * <p></p> 64610 * This is a wrapper around the info_widget. 64611 * @class 64612 * @param container The DOM element or jQuery object that contains the info_widget. <p></p> 64613 */ 64614AIBS.UI.ish_gene_widget = (function($j) { 64615 64616 return ( 64617 64618 function(container) { 64619 64620 // public properties 64621 this.container = $j(container); 64622 64623 // public methods 64624 /** 64625 * Set the Gene data to be displayed 64626 * @param data AIBS.DATA.ISH_DAO.experiment_detail().probes[0].gene 64627 */ 64628 this.set_data = function(data) { 64629 _info_widget.set_data(data); 64630 } 64631 64632 /** 64633 * A unique ID for this widget. This is not an HTML ID, it is an integer value scoped to 64634 * AIBS.UI. 64635 */ 64636 this.id = function() { 64637 if(_info_widget) 64638 return(_info_widget.id); 64639 64640 return(null); 64641 } 64642 64643 64644 // private properties 64645 /** 64646 * @private 64647 * @scope AIBS.UI.ish_gene_widget 64648 */ 64649 var _self = this; 64650 var _info_widget = null; 64651 64652 /** 64653 * The layout, data_map & decorators are defined for this info widget. 64654 */ 64655 function build_table() { 64656 64657 // describe the table 64658 var layout = { 64659 columns:4, 64660 rows:4, 64661 spans:[ 64662 {row:0, column:0, column_span:4, row_span:1}, 64663 {row:1, column:1, column_span:3, row_span:1} 64664 ] 64665 }; 64666 // describe how data should be mapped to cells in the table 64667 var data_map = [ 64668 {field:'acronym', cell:[0,0]}, 64669 {field:'name', cell:[1,1]}, 64670 {field:'gene_aliases', cell:[1,2]}, 64671 {field:'entrez_id', cell:[3,2]}, 64672 {field:'organism.name', cell:[1,3]}, 64673 {field:'chromosome.name', cell:[3,3]} 64674 ]; 64675 // which cells require a callback to massage the data presentation before 64676 // placing it in the table. 64677 var decorators = { 64678 '0,0':add_title, 64679 '1,2':list_aliases 64680 }; 64681 64682 var options = { 64683 table_class:'_gene_widget', 64684 jquery_theme:true 64685 }; 64686 // build the widget 64687 _info_widget = new AIBS.UI.info_widget(_self.container, layout, data_map, decorators, options); 64688 // set the static field labels 64689 set_labels(_info_widget); 64690 } 64691 64692 /** 64693 * The static field labels are set 64694 * @param widget The underlying info_widget 64695 */ 64696 function set_labels(widget) { 64697 _info_widget.set_content(0,0, "Gene"); 64698 _info_widget.set_content(0,1, "Name"); 64699 _info_widget.set_content(0,2, "Aliases"); 64700 _info_widget.set_content(0,3, "Organism"); 64701 _info_widget.set_content(2,2, "Entrez ID"); 64702 _info_widget.set_content(2,3, "Chromosome"); 64703 } 64704 64705 /** 64706 * 64707 * @param container The table cell that holds the title 64708 * @param data The data in the container 64709 * @returns The title string. 64710 */ 64711 function add_title(container, data) { 64712 if(data) 64713 return("Gene " + data); 64714 64715 return("Gene"); 64716 } 64717 64718 64719 /** 64720 * A decorator function that creates a comma-delimited list out of an array of alias acronyms. 64721 * @param container 64722 * @param data 64723 */ 64724 function list_aliases(container, data) { 64725 if(!data) 64726 return(""); 64727 64728 var s = ""; 64729 for(var i = 0; i < data.length; i++) { 64730 if(i) 64731 s += ", "; 64732 s += data[i].acronym 64733 } 64734 var w = $j(container).width();
64735 var d = "<div class='_pw_list' style='width:" + w + "px;' title='" + s + "'>" + s + "</div>"; 64736 return(d); 64737 } 64738 64739 /** 64740 * builds the table. 64741 */ 64742 function init() { 64743 build_table(); 64744 } 64745 64746 init(); 64747 } 64748 ); 64749 64750})(jQuery); 64751/** 64752 * This file is intended to encapsulate model data for ISH image filters, and is intended to by used by 64753 * several different page components, like IVT, thumbnails, SIV. Ideally, this might be located in a 64754 * different directory, such as assets/javascripts/models. 64755 * 64756 * This object provides custom delegate functions related to ISH image determination to aiclient's image_set_model, 64757 * as well as custom functions called directly to to get image type information. 64758 */ 64759 64760function ish_image_types() { 64761 /** 64762 * Gets the primary image type of the dataset by examining the treatment data in the experiment. 64763 * 64764 * Use as the "determine_primary_image_type" delegate for aiclient's image_set_model 64765 * 64766 * @param {number} experiment experiment (dataset) object 64767 * @returns {string} 'atlas', 'ish', 'nissl', 'hp_yellow' or '' if unable to determine primary type 64768 */ 64769 this.get_primary_image_type = function(experiment) { 64770 var primary_image_type = ''; 64771 64772 var atlas = (experiment.atlas != undefined && experiment.atlas == true); 64773 var treatment = experiment.treatments ? (experiment.treatments.length > 0 ? experiment.treatments[0].name : undefined) : undefined; 64774 64775 if (atlas) 64776 primary_image_type = 'atlas'; 64777 else { 64778 switch (treatment) { 64779 case 'NISSL': 64780 primary_image_type = 'nissl'; 64781 break; 64782 case 'ISH': 64783 primary_image_type = 'ish' 64784 break 64785 case 'AchE': 64786 primary_image_type = 'ache' 64787 break; 64788 case 'IHC:Parvalbumin': 64789 primary_image_type = 'ihc:parvalbumin' 64790 break; 64791 default: 64792 primary_image_type = ''; 64793 break; 64794 } 64795 } 64796 64797 return primary_image_type; 64798 }; 64799 64800 /** 64801 * Traverse the array of image metadata, and determine what image types are present for display in menus. 64802 * The return values are either the display values (used in a menu UI) or the actual image type value. 64803 * See the 'forDisplay' argument description. 64804 * 64805 * @param {string} primary_image_type name of primary image for the dataset (lowercase value) 64806 * @param {array} images 64807 * @param [boolean} forDisplay set true to get display values of image types, set false to get actual image type. Defaults to false. 64808 * @returns {array} elements are one or more of 'Atlas', 'ISH', 'Expression', 'NISSL', 'AchE', 'Parvalbumin', either mixed or lower case. 64809 */ 64810 // the_data, image, relation ('alternate', 'associate') 64811 this.get_image_types = function(primary_image_type, images, forDisplay) { 64812 var display = forDisplay ? (forDisplay == true ? true : false) : false; 64813 64814 var image_types_array = []; 64815 var image_types = {}; 64816 64817 switch (primary_image_type) { 64818 case 'atlas': 64819 image_types.Atlas = true; 64820 break; 64821 case 'ish': 64822 image_types.ISH = true; 64823 break; 64824 case 'nissl': 64825 image_types.NISSL = true; 64826 break; 64827 case 'ache': 64828 image_types.AchE = true; 64829 break; 64830 case 'ihc:parvalbumin': 64831 image_types['IHC:Parvalbumin'] = true; 64832 break; 64833 default: 64834 image_types.ISH = true; 64835 break; 64836 } 64837 64838 // Determine if any secondary image types are present; these will be 'Expression' or 'NISSL' 64839 var expression_image; 64840 var len = images.length; 64841 for (var i = 0; i < len; ++i) { 64842 expression_image = images[i].alternate_images ? (images[i].alternate_images.length ? images[i].alternate_images[0] : {path:''}) : {path:''};
64843 if (expression_image.path != '' && primary_image_type != 'atlas') { 64844 // Edge case: eliminate 'false positive' expression images when image_type == 'Atlas - Developing Mouse' or 'Atlas - Adult Mouse' 64845 // which occurs when the dataset is from the 'Additional Reference Data' of the DevMouse Reference Atlas theme 64846 image_types.Expression = expression_image.image_type.indexOf('Atlas') == -1 ? true : false; 64847 } 64848 64849 // for ISH zap viewers we assume that the one-and-only associate is a nissl 64850 if (images[i].associates && images[i].associates.length == 1) { 64851 image_types.NISSL = true; 64852 } 64853 } 64854 64855 // copy valid image_type hash keys into an array 64856 for (var key in image_types) { 64857 if (image_types.hasOwnProperty(key)) { 64858 if (image_types[key] == true) 64859 image_types_array.push(key); 64860 } 64861 } 64862 64863 // convert display value to actual value 64864 if (display == false) { 64865 for (i = 0, len = image_types_array.length; i < len; ++i) { 64866 image_types_array[i] = image_types_array[i].toLowerCase(); 64867 } 64868 } 64869 64870 return image_types_array; 64871 }; 64872} 64873; 64874"use strict"; 64875/** 64876 * The ISH Probe widget class. 64877 * <p></p> 64878 * The ish_probe_widget widget presents some of the information contained in the data 64879 * object returned by a AIBS.DATA.ISH_DAO.experiment_detail().probes[0] object. 64880 * <p></p> 64881 * This is a wrapper around the info_widget. 64882 * @class 64883 * @param container The DOM element or jQuery object that contains the info_widget. <p></p> 64884 */ 64885AIBS.UI.ish_probe_widget = (function($j) { 64886 64887 return ( 64888 64889 function(container) { 64890 64891 // public properties 64892 this.container = $j(container); 64893 64894 /** 64895 * Set the data to be displayed. 64896 * @param data AIBS.DATA.ISH_DAO.experiment_detail().probes[0] 64897 */ 64898 this.set_data = function(data) { 64899 _info_widget.set_data(data); 64900 } 64901 64902 /** 64903 * A unique ID for this widget. This is not an HTML ID, it is an integer value scoped to 64904 * AIBS.UI. 64905 */ 64906 this.id = function() { 64907 if(_info_widget) 64908 return(_info_widget.id); 64909 64910 return(null); 64911 } 64912 64913 64914 /** 64915 * @private 64916 * @scope AIBS.UI.ish_probe_widget 64917 */ 64918 var _self = this; 64919 var _info_widget = null; 64920 var _tester_div_id = '_piw_test_size_tester'; 64921 64922 64923 function build_table() { 64924 64925 // describe the table 64926 var layout = { 64927 columns:4, 64928 rows:5, 64929 spans:[ 64930 {row:0, column:0, column_span:4, row_span:1}, 64931 {row:4, column:0, column_span:4, row_span:1} 64932 ] 64933 }; 64934 // describe how data should be mapped to cells in the table 64935 var data_map = [ 64936 {field:'name', cell:[0,0]}, 64937 64938 {field:'probe_type', cell:[1,1]}, 64939 {field:'orientation.name', cell:[3,1]}, 64940 64941 {field:'ncbi_accession_number', cell:[1,2]}, 64942 {field:'forward_primer_sequence.sequence_data', cell:[3,2]}, 64943 64944 {field:'gi', cell:[1,3]}, 64945 {field:'reverse_primer_sequence.sequence_data', cell:[3,3]}, 64946 64947 {field:'predicted_sequence.sequence_data', cell:[0,4]} 64948 ]; 64949 // which cells require a callback to massage the data 64950 // presentation before placing it in the table. 64951 var decorators = { 64952 '0,0':add_title, 64953 '0,4':wrap_sequence 64954 }; 64955 64956 // NOTE we're telling it to call the decorator 64957 // functions every time it redraws due to resize, etc. 64958 var options = { 64959 table_class:'_probe_widget', 64960 redecorate:true, 64961 jquery_theme:true 64962 }; 64963 64964 // build the widget. 64965 _info_widget = new AIBS.UI.info_widget(_self.container, layout, data_map, decorators, options); 64966 64967 // set the static field labels 64968 set_probe_labels(_info_widget); 64969 } 64970 64971 64972 function build_text_tester() { 64973 var tester_div = $j("<div id='" + _tester_div_id + "'></div>"); 64974 tester_div.css({ 64975 'position':'absolute', 64976 'visibility':'hidden', 64977 'height':'auto', 64978 'width':'auto' 64979 }); 64980 tester_div.appendTo(document.body); 64981 } 64982 64983 function set_probe_labels(widget) { 64984 _info_widget.set_content(0,0, "Probe"); 64985 64986 _info_widget.set_content(0,1, "Type"); 64987 _info_widget.set_content(2,1, "Orientation"); 64988 64989 _info_widget.set_content(0,2, "NCBI Accession"); 64990 _info_widget.set_content(2,2, "Forward Primer"); 64991 64992 _info_widget.set_content(0,3, "GI"); 64993 _info_widget.set_content(2,3, "Reverse primer"); 64994 64995 _info_widget.set_content(0,4, "Sequence"); 64996 } 64997 64998 // the sequence data is a very long unbroken string of G,T,C & A.
64999 // this decorator method breaks it up to fit into its container. 65000 function wrap_sequence(container, data) { 65001 /* 65002 // the sequence string is placed into a hidden 65003 // div so we can measure it and get the correct size, regardless 65004 // of what font or other attributes are being used to display it. 65005 65006 // set the font size on the tester div to 65007 // whatever it will be in our actual container 65008 var font_size = $j(container).css('fontSize'); 65009 var test_div = $j('#'+_tester_div_id); 65010 test_div.css({fontSize:font_size}); 65011 65012 // place the content in the the test div and measure it 65013 test_div.html(data); 65014 var text_pixel_width = $j('#'+_tester_div_id).width(); 65015 // get rid of the content in the tester div; even though it's 65016 // hidden, long content will cause scroll bars to appear 65017 // in the browser. 65018 test_div.html(""); 65019 65020 // figure out how to break the long sequence string so it will fit 65021 // in the container. 65022 var data_chars = data.length; 65023 var char_width = text_pixel_width / data_chars; 65024 65025 var container_pixel_width = $j(_self.container).width() - 20; 65026 var line_char_count = Math.ceil(container_pixel_width/char_width); 65027 var pieces = Math.ceil((data_chars >= line_char_count) ? data_chars/line_char_count : 1); 65028 65029 // Add a label and some line breaks to the sequence string. 65030 var new_string = "<b>Sequence:</b><br/>"; 65031 for(var i = 0; i < pieces-1; i++) { 65032 new_string += data.substr(i*line_char_count,line_char_count) + "<br>"; 65033 } 65034 new_string += data.substr(i*line_char_count); 65035 return(new_string); 65036 65037*/ 65038 return("<b>Sequence: </b>" + data); 65039 } 65040 65041 // decorator to add the label "Probe" to the probe name in the title bar 65042 function add_title(container, data) { 65043 if(data) 65044 return("Probe " + data); 65045 65046 return("Probe"); 65047 } 65048 65049 function init() { 65050 build_table(); 65051 build_text_tester(); 65052 } 65053 65054 init(); 65055 } 65056 ); 65057 65058 65059})(jQuery); 65060 65061"use strict"; 65062/** 65063 * The ISH Specimen widget class. 65064 * <p></p> 65065 * The ish_specimen_widget widget presents some of the information contained in the data 65066 * object returned by a AIBS.DATA.ISH_DAO.experiment_detail().specimen object. 65067 * <p></p> 65068 * This is a wrapper around the info_widget. 65069 * @class 65070 * @param container The DOM element or jQuery object that contains the info_widget. <p></p> 65071 */ 65072AIBS.UI.ish_specimen_widget = (function($j) { 65073 65074 return ( 65075 function(container) { 65076 65077 // public properties 65078 this.container = $j(container); 65079 65080 /** 65081 * Set the data to be displayed. 65082 * @param data AIBS.DATA.ISH_DAO.experiment_detail().specimen 65083 */ 65084 this.set_data = function(data) { 65085 _info_widget.set_data(data); 65086 } 65087 65088 /** 65089 * A unique ID for this widget. This is not an HTML ID, it is an integer value scoped to 65090 * AIBS.UI. 65091 */ 65092 this.id = function() { 65093 if(_info_widget) 65094 return(_info_widget.id); 65095 65096 return(null); 65097 } 65098 65099 65100 /** 65101 * @private 65102 * @scope AIBS.UI.ish_specimen_widget 65103 */ 65104 var _self = this; 65105 var _info_widget = null; 65106 65107 65108 function build_table() { 65109 65110 // describe the table 65111 var layout = { 65112 columns:4, 65113 rows:3, 65114 spans:[ 65115 {row:0, column:0, column_span:4, row_span:1} 65116 ] 65117 }; 65118 // describe how data should be mapped to cells in the table 65119 var data_map = [ 65120 {field:'name', cell:[0,0]}, 65121 {field:'donor.organism.name', cell:[1,1]}, 65122 {field:'donor.age.days', cell:[1,2]}, 65123 {field:'donor.strain', cell:[3,1]}, 65124 {field:'donor.sex', cell:[3,2]} 65125 ]; 65126 // which cells require a callback to massage the data presentation before 65127 // placing it in the table. 65128 var decorators = { 65129 '0,0':add_title 65130 }; 65131 65132 var options = { 65133 table_class:'_specimen_widget', 65134 redecorate:true, 65135 jquery_theme:true 65136 }; 65137 65138 // build the widget 65139 _info_widget = new AIBS.UI.info_widget(_self.container, layout, data_map, decorators, options); 65140 // set the static field labels 65141 set_labels(_info_widget); 65142 } 65143 65144 function set_labels(widget) { 65145 _info_widget.set_content(0,0, "Specimen"); 65146 _info_widget.set_content(0,1, "Organism"); 65147 _info_widget.set_content(2,1, "Strain"); 65148 _info_widget.set_content(2,2, "Sex"); 65149 _info_widget.set_content(0,2, "Age"); 65150 } 65151 65152 65153 function add_title(container, data) { 65154 if(data) 65155 return("Specimen " + data); 65156 65157 return("Specimen"); 65158 } 65159
65160 function init() { 65161 build_table(); 65162 } 65163 65164 init(); 65165 } 65166 ); 65167 65168 65169})(jQuery); 65170"use strict"; 65171/** 65172 * The ISH Specimen widget class. 65173 * <p></p> 65174 * The ish_specimen_widget widget presents some of the information contained in the data 65175 * object returned by a AIBS.DATA.ISH_DAO.experiment_detail().specimen object. 65176 * <p></p> 65177 * This is a wrapper around the info_widget. 65178 * @class 65179 * @param container The DOM element or jQuery object that contains the info_widget. <p></p> 65180 */ 65181AIBS.UI.ish_specimen_widget_narrow = (function($j) { 65182 65183 return ( 65184 function(container) { 65185 65186 // public properties 65187 this.container = $j(container); 65188 65189 /** 65190 * Set the data to be displayed. 65191 * @param data AIBS.DATA.ISH_DAO.experiment_detail().specimen 65192 */ 65193 this.set_data = function(data) { 65194 _info_widget.set_data(data); 65195 } 65196 65197 /** 65198 * A unique ID for this widget. This is not an HTML ID, it is an integer value scoped to 65199 * AIBS.UI. 65200 */ 65201 this.id = function() { 65202 if(_info_widget) 65203 return(_info_widget.id); 65204 65205 return(null); 65206 } 65207 65208 65209 /** 65210 * @private 65211 * @scope AIBS.UI.ish_specimen_widget 65212 */ 65213 var _self = this; 65214 var _info_widget = null; 65215 65216 65217 function build_table() { 65218 65219 // describe the table 65220 var layout = { 65221 columns:2, 65222 rows:5, 65223 spans:[ 65224 {row:0, column:0, column_span:2, row_span:1} 65225 ] 65226 }; 65227 // describe how data should be mapped to cells in the table 65228 var data_map = [ 65229 {field:'name', cell:[0,0]}, 65230 {field:'donor.organism.name', cell:[1,1]}, 65231 {field:'donor.strain', cell:[1,2]}, 65232 {field:'donor.age.days', cell:[1,3]}, 65233 {field:'donor.sex', cell:[1,4]} 65234 ]; 65235 // which cells require a callback to massage the data presentation before 65236 // placing it in the table. 65237 var decorators = { 65238 '0,0':add_title 65239 }; 65240 65241 var options = { 65242 table_class:'_specimen_widget', 65243 redecorate:true, 65244 jquery_theme:true 65245 }; 65246 65247 // build the widget 65248 _info_widget = new AIBS.UI.info_widget(_self.container, layout, data_map, decorators, options); 65249 // set the static field labels 65250 set_labels(_info_widget); 65251 } 65252 65253 function set_labels(widget) { 65254 _info_widget.set_content(0,0, "Specimen"); 65255 _info_widget.set_content(0,1, "Organism"); 65256 _info_widget.set_content(0,2, "Strain"); 65257 _info_widget.set_content(0,3, "Age"); 65258 _info_widget.set_content(0,4, "Sex"); 65259 } 65260 65261 function add_title(container, data) { 65262 if(data) 65263 return("Specimen " + data); 65264 65265 return("Specimen"); 65266 } 65267 65268 function init() { 65269 build_table(); 65270 } 65271 65272 init(); 65273 } 65274 ); 65275 65276 65277})(jQuery); 65278"use strict"; 65279/** 65280 * The ISH Specimen widget class. 65281 * <p></p> 65282 * The ish_specimen_widget_narrow_for_nhp presents a subset of the information contained in the data 65283 * object returned by a AIBS.DATA.ISH_DAO.experiment_detail().specimen object. 65284 * <p></p> 65285 * This is a wrapper around the info_widget. 65286 * @class 65287 * @param container The DOM element or jQuery object that contains the info_widget. <p></p> 65288 */ 65289AIBS.UI.ish_specimen_widget_narrow_for_nhp = (function ($j) { 65290 65291 return ( 65292 function (container, options) { 65293 65294 // public properties 65295 this.container = $j(container); 65296 65297 // these settings are appropriate for NHP 65298 this.settings = { 65299 jquery_theme: true, 65300 "layout": { 65301 "columns": 2, 65302 "rows": 7, 65303 "spans": [ 65304 {"row": 0, "column": 0, "column_span": 2, "row_span": 1} 65305 ] 65306 }, 65307 "data_map": { 65308 "name": [0, 0], 65309 "id": [1, 1], 65310 "donor.organism.name": [1, 2], 65311 "donor.age.name": [1, 3], 65312 "donor.sex": [1, 4], 65313 "structure.name": [1, 5], 65314 "hemisphere": [1, 6] 65315 }, 65316 "labels": { 65317 "Specimen": [0, 0], 65318 "ID": [0, 1], 65319 "Organism": [0, 2], 65320 "Age": [0, 3], 65321 "Sex": [0, 4], 65322 "Tissue Location": [0, 5], 65323 "Hemisphere": [0, 6] 65324 }, 65325 "decorators": { 65326 "0,0": { 65327 "name": "AIBS.UI.decorators.add_specimen_title", 65328 "options": {} 65329 }, 65330 "1,1": { 65331 "name": "AIBS.UI.decorators.specimen_link_decorator", 65332 "options": { 65333 "path": "/ish/specimen/show/" 65334 } 65335 } 65336 }, 65337 info_opts: { 65338 table_class: '_specimen_widget', 65339 redecorate: true, 65340 jquery_theme: true 65341 } 65342 }; 65343 $j.extend(true, this.settings, options); 65344 65345 // public methods 65346 /** 65347 * Set the data to be displayed. 65348 * @param data AIBS.DATA.ISH_DAO.experiment_detail().specimen 65349 */ 65350 this.set_data = function (data) { 65351 _info_widget.set_data(data); 65352 }; 65353 65354 /** 65355 * A unique ID for this widget. This is not an HTML ID, it is an integer value scoped to 65356 * AIBS.UI. 65357 */ 65358 this.id = function () { 65359 if (_info_widget) 65360 return(_info_widget.id); 65361 65362 return(null); 65363 }; 65364 65365 65366 // private properties 65367 65368 var _self = this; 65369 var _info_widget = null; 65370 65371 65372 function build_table() { 65373 65374 _self.settings.info_opts.decorator_options = {}; 65375 for (var key in _self.settings.decorators) { 65376 var dec_options = _self.settings.decorators[key].options; 65377 _self.settings.decorators[key] = eval(_self.settings.decorators[key].name); 65378 _self.settings.info_opts.decorator_options[key] = dec_options; 65379 } 65380 65381 // build the widget 65382 _info_widget = new AIBS.UI.info_widget( 65383 _self.container, 65384 _self.settings.layout, 65385 build_data_map(_self.settings.data_map), 65386 _self.settings.decorators, 65387 _self.settings.info_opts 65388 ); 65389 65390 // set the static field labels 65391 set_labels(_info_widget); 65392 } 65393 65394 function set_labels(widget) { 65395 65396 for (var key in _self.settings.labels) { 65397 var coords = _self.settings.labels[key]; 65398 _info_widget.set_content(coords[0], coords[1], key); 65399 } 65400 } 65401 65402 function build_data_map(map) { 65403 65404 var map_array = []; 65405 for (var key in map) { 65406 map_array.push({ 65407 field: key, 65408 cell: map[key] 65409 }) 65410 } 65411 return(map_array); 65412 } 65413
65414 function init() { 65415 build_table(); 65416 } 65417 65418 init(); 65419 } 65420 ); 65421 65422})(jQuery); 65423"use strict"; 65424/** 65425 * The ISH Specimen widget class. 65426 * <p></p> 65427 * The ish_specimen_widget widget presents some of the information contained in the data 65428 * object returned by a AIBS.DATA.ISH_DAO.experiment_detail() object. 65429 * <p></p> 65430 * This is a wrapper around the info_widget. 65431 * @class 65432 * @param container The DOM element or jQuery object that contains the info_widget. <p></p> 65433 */ 65434AIBS.UI.related_data_widget = (function($j) { 65435 65436 return ( 65437 function(container, related_url) { 65438 65439 // public properties 65440 this.container = $j(container); 65441 this.related_url = related_url; 65442 65443 /** 65444 * Set the data to be displayed. 65445 * @param data AIBS.DATA.ISH_DAO.experiment_detail() 65446 */ 65447 this.set_data = function(data) { 65448 _info_widget.set_data(data); 65449 } 65450 65451 /** 65452 * A unique ID for this widget. This is not an HTML ID, it is an integer value scoped to 65453 * AIBS.UI. 65454 */ 65455 this.id = function() { 65456 if(_info_widget) 65457 return(_info_widget.id); 65458 65459 return(null); 65460 } 65461 65462 65463 /** 65464 * @private 65465 * @scope AIBS.UI.ish_specimen_widget 65466 */ 65467 var _self = this; 65468 var _info_widget = null; 65469 65470 65471 function build_table() { 65472 65473 // describe the table 65474 var layout = { 65475 columns:1, 65476 rows:2, 65477 spans:[] 65478 }; 65479 // describe how data should be mapped to cells in the table 65480 var data_map = [ 65481 {field:'id', cell:[0,1]} // experiment id 65482 ]; 65483 65484 var decorators = { 65485 '0,1':make_related_link 65486 }; 65487 65488 var options = { 65489 table_class:'_related_data', 65490 jquery_theme:true 65491 }; 65492 65493 // build the widget 65494 _info_widget = new AIBS.UI.info_widget(_self.container, layout, data_map, decorators, options); 65495 // set the static field labels 65496 set_labels(_info_widget); 65497 } 65498 65499 function set_labels(widget) { 65500 _info_widget.set_content(0,0, "Related Institute Data"); 65501 } 65502 65503 function make_related_link(container, data) { 65504 if(!data) 65505 return(""); 65506 65507 var img_path = _self.related_url + data; 65508 var link_path = img_path.replace('.jpg', '.html'); 65509 65510 var img_link = "<a href='" + link_path + "' class='_related_data_img_link'><img src='" + img_path + "'/></a>"; 65511 return(img_link); 65512 } 65513 65514 function init() { 65515 build_table(); 65516 } 65517 65518 init(); 65519 } 65520 ); 65521 65522 65523})(jQuery); 65524/** 65525 * The section info widget class. 65526 * <p></p> 65527 * The section_info_narrow widget presents some of the information contained in the data 65528 * object returned by a AIBS.DATA.ISH_DAO.experiment_detail() object. 65529 * <p></p> 65530 * This is a wrapper around the info_widget. 65531 * @class 65532 * @param container The DOM element or jQuery object that contains the info_widget. <p></p> 65533 */ 65534 65535AIBS.UI.section_info_narrow = (function($j) { 65536 65537 return ( 65538 function(container, options) { 65539 65540 // public properties 65541 this.container = $j(container); 65542/* 65543 var layout = { 65544 columns:2, 65545 rows:6,
65546 spans:[ 65547 {row:0, column:0, column_span:2, row_span:1} 65548 ] 65549 }; 65550 // describe how data should be mapped to cells in the table 65551 var data_map = [ 65552 {field:'gene_acronym', cell:[1,1]}, 65553 {field:'experiment_id', cell:[1,2]}, 65554 {field:'section_number', cell:[1,3]}, 65555 {field:'treatments', cell:[1,4]}, 65556 {field:'study', cell:[1,5]} 65557 ]; 65558 65559 _info_widget.set_content(0,0, "Section Information"); 65560 _info_widget.set_content(0,1, "Gene"); 65561 _info_widget.set_content(0,2, "Experiment"); 65562 _info_widget.set_content(0,3, "Section Number"); 65563 _info_widget.set_content(0,4, "Treatments"); 65564 _info_widget.set_content(0,5, "Study"); 65565 65566 65567 // which cells require a callback to massage the data presentation before 65568 // placing it in the table. 65569 var decorators = { 65570 '1,4':list_treatments 65571 }; 65572 65573 var options = { 65574 table_class:'_section_info_widget ui-widget-content', 65575 jquery_theme:true 65576 }; 65577 */ 65578 65579 65580 // these settings are appropriate for ABA, probably mouse in general 65581 this.settings = { 65582 jquery_theme:true, 65583 "layout":{ 65584 "columns":2, 65585 "rows":6, 65586 "spans":[ 65587 {"row":0, "column":0, "column_span":2, "row_span":1} 65588 ] 65589 }, 65590 "data_map":{ 65591 "gene":[1,1], 65592 "experiment_id":[1,2], 65593 "section_number":[1,3], 65594 "treatments":[1,4], 65595 "study":[1,5] 65596 }, 65597 "labels":{ 65598 "Section Information":[0,0], 65599 "Gene":[0,1], 65600 "Experiment":[0,2], 65601 "Section Number":[0,3], 65602 "Treatments":[0,4], 65603 "Study":[0,5] 65604 }, 65605 "decorators":{ 65606 "1,4":{ 65607 "name":"AIBS.UI.decorators.list_treatments", 65608 "options":{} 65609 } 65610 }, 65611 "info_opts":{ 65612 "table_class":"_section_info_widget ui-widget-content", 65613 "jquery_theme":true 65614 } 65615 }; 65616 $j.extend(true, this.settings, options); 65617 65618 65619 // public methods 65620 // data is expected to be the graph of goodies returned by the ish_dao.experiment_specimen method 65621 this.set_data = function(data) { 65622 _info_widget.set_data(data); 65623 } 65624 65625 this.id = function() { 65626 if(_info_widget) 65627 return(_info_widget.id); 65628 65629 return(null); 65630 } 65631 65632 65633 // private properties 65634 var _self = this; 65635 var _info_widget = null; 65636 65637 65638 function build_table() { 65639 65640 _self.settings.info_opts.decorator_options = {}; 65641 for(var key in _self.settings.decorators) { 65642 var dec_options = _self.settings.decorators[key].options; 65643 _self.settings.decorators[key] = eval(_self.settings.decorators[key].name); 65644 _self.settings.info_opts.decorator_options[key] = dec_options; 65645 } 65646 65647 _info_widget = new AIBS.UI.info_widget( 65648 _self.container, 65649 _self.settings.layout, 65650 build_data_map(_self.settings.data_map), 65651 _self.settings.decorators, 65652 _self.settings.info_opts 65653 ); 65654 // set the static field labels 65655 set_labels(_info_widget); 65656 } 65657 65658 function set_labels(widget) { 65659 65660 for(var key in _self.settings.labels) { 65661 var coords = _self.settings.labels[key]; 65662 _info_widget.set_content(coords[0], coords[1], key); 65663 } 65664 } 65665 65666 function build_data_map(map) { 65667 65668 var map_array = []; 65669 for(var key in map) { 65670 map_array.push({ 65671 field:key, 65672 cell:map[key] 65673 }) 65674 } 65675 return(map_array); 65676 } 65677
65678 function init() { 65679 build_table(); 65680 } 65681 65682 init(); 65683 } 65684 ); 65685 65686})(jQuery); 65687 65688/** 65689 * The ISH Specimen donor widget class. 65690 * <p></p> 65691 * The ish_specimen_donor_widget widget presents a subset of the information contained in the data 65692 * object returned by a AIBS.DATA.ISH_DAO.specimen_detail().specimen object. 65693 * <p></p> 65694 * This is a wrapper around the info_widget. 65695 * @class 65696 * @param container The DOM element or jQuery object that contains the info_widget. <p></p> 65697 */ 65698 65699AIBS.UI.ish_specimen_donor_widget = (function($j) { 65700 65701 return ( 65702 function(container, options) { 65703 65704 // public properties 65705 this.container = $j(container); 65706 this.settings = { 65707 strain_label:'Race' 65708 }; 65709 $j.extend(true, this.settings, options); 65710 65711 /** 65712 * Set the data to be displayed. 65713 * @param data AIBS.DATA.ISH_DAO.experiment_detail().specimen 65714 */ 65715 this.set_data = function(data) { 65716 _info_widget.set_data(data); 65717 }; 65718 65719 /** 65720 * A unique ID for this widget. This is not an HTML ID, it is an integer value scoped to 65721 * AIBS.UI. 65722 */ 65723 this.id = function() { 65724 if(_info_widget) 65725 return(_info_widget.id); 65726 65727 return(null); 65728 }; 65729 65730 65731 /** 65732 * @private 65733 * @scope AIBS.UI.ish_specimen_widget 65734 */ 65735 var _self = this; 65736 var _info_widget = null; 65737 65738 65739 function build_table() { 65740 // describe the table 65741 var layout = { 65742 columns:4, 65743 rows:3, 65744 spans:[ 65745 {row:0, column:0, column_span:4, row_span:1}, 65746 ] 65747 }; 65748 // describe how data should be mapped to cells in the table 65749 var data_map = [ 65750 {field:'name', cell:[0,0]}, 65751 {field:'donor.age.name', cell:[1,1]}, 65752 {field:'donor.sex', cell:[3,1]}, 65753 {field:'structure.name', cell:[1,2]}, 65754 {field:'hemisphere', cell:[3,2]} 65755 ]; 65756 // which cells require a callback to massage the data presentation before 65757 // placing it in the table. 65758 var decorators = { 65759 '0,0':add_title 65760 }; 65761 65762 var options = { 65763 table_class:'_specimen_donor_widget', 65764 redecorate:true, 65765 jquery_theme:true 65766 }; 65767 65768 // build the widget 65769 _info_widget = new AIBS.UI.info_widget(_self.container, layout, data_map, decorators, options); 65770 // set the static field labels 65771 set_labels(_info_widget); 65772 } 65773 65774 function set_labels(widget) { 65775 _info_widget.set_content(0,0, "Specimen"); 65776 _info_widget.set_content(0,1, "Age"); 65777 _info_widget.set_content(2,1, "Sex"); 65778 _info_widget.set_content(0,2, "Tissue Location"); 65779 _info_widget.set_content(2,2, "Hemisphere"); 65780 } 65781 65782 65783 function add_title(container, data) { 65784 if(data) 65785 return("Specimen " + data); 65786 65787 return("Specimen"); 65788 } 65789 65790 function show_conditions(container, data) { 65791 65792 if(!data) 65793 return(""); 65794 65795 var s = ""; 65796 for(var i = 0; i < data.length; i++) { 65797 if(i) 65798 s += ", "; 65799 s += data[i].name 65800 } 65801 var w = $j(container).width(); 65802 var d = "<div class='_pw_list' style='width:90%;' title='" + s + "'>" + s + "</div>"; 65803 return(d); 65804 } 65805 65806 function init() { 65807 build_table(); 65808 } 65809 65810 init(); 65811 } 65812 ); 65813 65814 65815})(jQuery); 65816"use strict"; 65817/** 65818 * Note: this file is a direct parallel to HBA's app/assests/javascripts/pages/siv/all_dual_graphics.js file. 65819 * The primary difference between the two files is that this file hides function and variable scope within 65820 * the single global var, ish_zap_viewer_globals, whereas the HBA file exposes all variables and functions into 65821 * the global scope. 65822 * 65823 */ 65824var ish_zap_viewer_globals = { 65825 65826// some legacy codes 65827 65828 // vars 65829 contrastFilter:"contrast", 65830 colorMapFilter:"colormap", 65831 filterName:this.contrastFilter, 65832 sliderDivName:"track", 65833 redraw_delay : 50, // msc 65834 on_get_atlas_id : null, // intialized in setupGraphics 65835 atlas_viewer_service : null, // initialized in setupGraphics 65836 65837 // Note: the dao data is from structure_graph_id = 8, "Non-Human Primate Brain Atlas", which has ontology_id = 4 65838 // Use these warehouse SQL query to get data: 65839 // select structure_id, acronym from ontology_nodes where structure_graph_id = 8 order by structure_id; -- structure_labels 65840 // select structure_id, name from ontology_nodes where structure_graph_id = 8 order by structure_id; -- structure_names 65841 // select structure_id, '0x'||color from ontology_nodes where structure_graph_id = 8 order by structure_id; -- structure_colors 65842 dao:{ 65843 structure_labels:"3999,Br,4000,MFC,4001,BN,4002,Amy,4003,HF,4004,Ocx,9999,rCG,10000,rCG1,10001,rCG2,10002,rCG3,10003,rCG4,10004,rCG5,10005,rCG6,10007,rCGwm,10008,mOF,10009,mOF1,10010,mOF2,10011,mOF3,10012,mOF4,10013,mOF5,10014,mOF6,10016,mOFwm,10017,cOF,10018,cOF1,10019,cOF2,10020,cOF3,10021,cOF4,10022,cOF5,10023,cOF6,10025,cOFwm,10026,V1,10027,V1-1,10028,V1-2,10029,V1-3,10030,V1-4,10031,V1-4A,10032,V1-4B,10033,V14Ca,10034,V14Cb,10035,V1-5,10036,V1-6,10038,V1wm,10039,V2,10040,V2-1,10041,V2-2,10042,V2-3,10043,V2-4,10048,V2_L5,10049,V2-6,10051,V2wm,10053,ProS,10054,S,10055,PrS,10058,CA1,10059,CA1or,10060,CA1py,10061,CA1ra,10062,CA1lm,10063,CA2,10064,CA2or,10065,CA2py,10066,CA2ra,10067,CA2lm,10068,CA3,10069,CA3or,10070,CA3py,10071,CA3sl,10072,CA3ra,10073,CA3lm,10074,CA4,10075,DG,10076,DGmo,10077,DGgr,10078,DGsg,10079,DGpf,10080,STR,10081,dSTR,10082,Ca,10086,Pu,10091,vSTR,10092,NAC,10093,NACc,10094,NACs,10095,IsCj,10096,Tu,10097,GP,10098,GPi,10099,GPe,10102,ic,10103,AA,10106,B,10107,Bi,10108,Bd,10109,Bv,10110,AB,10111,ABd,10112,ABv,10116,L,10117,Ldl,10118,Ldm,10119,Lvl,10120,Lvm,10121,CE,10123,CEl,10124,CEm,10125,Me,10128,PL,10132,INA,10134,rPAC,10135,AHA,10136,AHAmc,10137,AHApc,10139,IMG,10140,APir,10141,BNM,12984,dlPF,12986,dlPF2,12987,dlPF3,12988,dlPF4,12989,dlPF5,12990,dlPF6,128011350,Tel,128011354,Cx,128011368,dlPFsg,128011372,dlPFmz,128011378,dlPFcp,128011614,rCGmz,128011624,rCGcpo,128011638,rCGcpi,128011646,rCGsp,128011650,rCGiz,128011654,rCGsz,128011658,rCGszo,128011662,rCGszi,128011666,rCGvz,128011670,rCGvzo,128011674,rCGvzi,128011776,S1mz,128011788,S1cpo,128011796,S1-2,128011800,S1-3,128011804,S1-4,128011808,S1-cpi,128011812,S1-5,128011816,S1-6,128011820,S1sp,128011824,S1iz,128011828,S1sz,128011832,S1szo,128011836,S1szi,128011840,S1vz,128011844,S1vzo,128011848,S1vzi,128011876,LEC,128011900,LEC2,128011936,MEC,128011960,MEC2,128012080,PC,128012084,MPC,128012120,MPC5,128012144,LPC,128012220,DGmz,128012226,DGgra,128012232,DGsga,128012238,DGpfa,128012244,Hip,128012250,CA1mz,128012258,CA1cp,128012264,CA1sp,128012270,CA1iz,128012274,CA1szi,128012278,CA1vz,128012284,CA2mz,128012292,CA2cp,128012298,CA2sp,128012318,CA3mz,128012326,CA3cp,128012332,CA3sp,128012354,SCx,128012368,Scp,128012372,Spy,128012400,PrSr,128012404,PrSc,128012418,V1sg,128012422,V1mz,128012428,V1cp,128012432,V1cpo,128012436,V1-2/3,128012458,V1cpi,128012466,V1sp,128012470,V1icd,128012474,V1iz,128012478,V1sz,128012482,V1szo,
65843128012486,V1szi,128012490,V1vz,128012494,V1vzo,128012498,V1vzi,128012584,RMS,128012588,RMSvz,128012596,CxN,128012616,CM,128012620,PAC,128012634,cPAC,128012638,cPACd,128012678,BLN,128012818,GE,128012822,LGE,128012834,MGE,128012846,CGE,128012920,NACsm,128012926,IsCjm,128012960,Cla,128012976,BF,128013010,Die,128013014,Thal,128013018,DTh,128013070,LGN,128013074,DLG,128013078,DLGmc,128013091,DLGpc,128013113,DLGkc,128013147,ETh,159658800,rCGofz,159658804,rCGifz,159658808,S1ofz,159658816,S1ifz,159658820,DMS,159658824,Hem,159658836,V1ofz,159658840,V1tmz,159658844,V1ifz,159658852,LGEcx,294021746,NCx,294021766,SG,294021774,MZ,294021786,L1,294021800,L2,294021818,L2/3,294021824,L3,294021842,CPo,294021852,L4,294021878,L5,294021896,L6,294021914,CP,294021920,CPi,294021932,SP,294021942,IZ,294021954,TMZ,294021960,OFZ,294021970,SZ,294021980,SZo,294021990,IFZ,294022000,SZi,294022010,VZo,294022020,VZi,294022030,VZ,294022044,WM,294022158,ECx", 65844 structure_names:{3999:"brain",4000:"medial frontal cortex (areas 24, 25 and 32)",4001:"basal nuclei (basal ganglia)",4002:"amygdaloid complex",4003:"hippocampal cortex (hippocampal formation)",4004:"occipital cortex",9999:"rostral cingulate cortex (areas 24 and 32)",10000:"layer I of rostral cingulate cortex",10001:"layer II of rostral cingulate cortex",10002:"layer III of rostral cingulate cortex",10003:"granular layer IV of rostral cingulate cortex",10004:"layer V of rostral cingulate cortex",10005:"layer VI of rostral cingulate cortex",10007:"white matter of rostral cingulate cortex",10008:"medial orbital frontal cortex (area 14)",10009:"layer I of medial orbitofrontal cortex",10010:"layer II of medial orbitofrontal cortex",10011:"layer III of medial orbitofrontal cortex",10012:"granular layer IV of medial orbitofrontal cortex",10013:"layer V of medial orbitofrontal cortex",10014:"layer VI of medial orbitofrontal cortex",10016:"white matter of medial orbitofrontal cortex",10017:"caudal orbital frontal cortex (area 13)",10018:"layer I of caudal orbitofrontal cortex",10019:"layer II of caudal orbitofrontal cortex",10020:"layer III of caudal orbitofrontal cortex",10021:"granular layer IV of caudal orbitofrontal cortex",10022:"layer V of caudal orbitofrontal cortex",10023:"layer VI of caudal orbitofrontal cortex",10025:"white matter of caudal orbitofrontal cortex",10026:"primary visual cortex (V1, striate cortex, area 17)",10027:"layer I of V1",10028:"layer II of V1",10029:"layer III of V1",10030:"granular layer IV of V1",10031:"layer IVA of V1",10032:"layer IVB of V1",10033:"layer IVCa of V1",10034:"layer IVCb of V1",10035:"layer V of V1",10036:"layer VI of V1",10038:"white matter of V1",10039:"parastriate cortex (V2, area 18)",10040:"layer I of V2",10041:"layer II of V2",10042:"layer III of V2",10043:"granular layer IV of V2",10048:"layer V of V2",10049:"layer VI of V2",10051:"white matter of V2",10053:"prosubiculum",10054:"subiculum",10055:"presubicular cortex (presubiculum)",10058:"CA1 region",10059:"stratum oriens of CA1",10060:"stratum pyramidale of CA1",10061:"stratum radiatum of CA1",10062:"stratum lacunosum-moleculare of CA1",10063:"CA2 region",10064:"stratum oriens of CA2",10065:"stratum pyramidale of CA2",10066:"stratum radiatum of CA2",10067:"stratum lacunosum-moleculare of CA2",10068:"CA3 region",10069:"stratum oriens of CA3",10070:"stratum pyramidale of CA3",10071:"hippocampal stratum lucidum of CA3",10072:"stratum radiatum of CA3",10073:"stratum lacunosum-moleculare of CA3",10074:"CA4 region",10075:"dentate gyrus (dentate cortex)",10076:"molecular layer of dentate gyrus (cortex)",10077:"granular layer of dentate gyrus (cortex)",10078:"subgranular zone of dentate gyrus (cortex)",10079:"polyform layer of dentate gyrus (cortex)",10080:"striatum",10081:"dorsal striatum",10082:"caudate nucleus",10086:"putamen",10091:"ventral striatum",10092:"nucleus accumbens",10093:"core of nucleus accumbens",10094:"shell of nucleus accumbens",10095:"islands of Calleja",10096:"olfactory tubercle",10097:"globus pallidus",10098:"internal segment of globus pallidus",10099:"external segment of globus pallidus",10102:"internal
65844capsule",10103:"anterior amygdaloid area",10106:"basal nucleus (basolateral nucleus)",10107:"intermediate division of basal nucleus",10108:"dorsal (magnocellular) division of basal nucleus",10109:"ventral (parvocellular) division of basal nucleus",10110:"accessory basal nucleus (basomedial nucleus)",10111:"dorsal (magnocellular) division of accessory basal nucleus",10112:"ventral (parvocellular) division of accessory basal nucleus",10116:"lateral nucleus",10117:"dorsal division of lateral nucleus, lateral part",10118:"dorsal division of lateral nucleus, medial part",10119:"ventral division of lateral nucleus, lateral part",10120:"ventral division of lateral nucleus, medial part",10121:"central amygdaloid nucleus",10123:"lateral division of central nucleus",10124:"medial division of central nucleus",10125:"medial nucleus",10128:"paralaminar nucleus",10132:"intercalated nucleus of amygdala",10134:"rostral periamygdaloid cortex (rPAC)",10135:"amygdalohippocampal area",10136:"amygdalohippocampal area, magnocellualr part",10137:"amygdalohippocampal area, parvocelluar part",10139:"amygdaloid intramedullary gray",10140:"amygdalopiriform transition area",10141:"basal nucleus of Meynert",12984:"dorsolateral prefrontal cortex (areas 9 and 46)",12986:"layer II of dorsolateral prefrontal cortex",12987:"layer III of dorsolateral prefrontal cortex",12988:"granular layer IV of dorsolateral prefrontal cortex",12989:"layer V of dorsolateral prefrontal cortex",12990:"layer VI of dorsolateral prefrontal cortex",128011350:"telencephalon",128011354:"cerebral cortex",128011368:"subpial granular zone of dorsolateral prefrontal cortex",128011372:"marginal zone of dorsolateral prefrontal cortex",128011378:"cortical plate of dorsolateral prefrontal cortex",128011614:"marginal zone of rostral cingulate cortex",128011624:"outer cortical plate of rostral cingulate cortex",128011638:"inner cortical plate (infragranular layer) of rostral cingulate cortex",128011646:"subplate zone of rostral cingulate cortex",128011650:"intermediate zone of rostral cingulate cortex",128011654:"subventricular zone of rostral cingulate cortex",128011658:"outer subventricular zone of rostral cingulate cortex",128011662:"inner subventricular zone of rostral cingulate cortex",128011666:"ventr
65844icular zone of rostral cingulate cortex",128011670:"outer ventricular zone of rostral cingulate cortex",128011674:"inner ventricular zone of rostral cingulate cortex",128011776:"marginal zone of S1",128011788:"outer cortical plate of S1",128011796:"layer II of S1",128011800:"layer III of S1",128011804:"granular layer IV of S1",128011808:"inner cortical plate (infragranular layer) of S1",128011812:"layer V of S1",128011816:"layer VI of S1",128011820:"subplate zone of S1",128011824:"intermediate zone of S1",128011828:"subventricular zone of S1",128011832:"outer subventricular zone of S1",128011836:"inner subventricular zone of S1",128011840:"ventricular zone of S1",128011844:"outer ventricular zone of S1",128011848:"inner ventricular zone of S1",128011876:"lateral (anterior) entorhinal cortex (LEC)",128011900:"layer II of LEC",128011936:"medial (posterior) entorhinal cortex (MEC)",128011960:"layer II of MEC",128012080:"perirhinal cortex",128012084:"medial perirhinal cortex (area 35)",128012120:"layer V of perirhinal cortex (area 35)",128012144:"lateral perirhinal cortex (area 36)",128012220:"marginal zone of dentate gyrus (cortex)",128012226:"granular layer anlage of dentate gyrus (cortex)",128012232:"subgranular layer anlage of dentate gyrus (cortex)",128012238:"polyform layer anlage of dentate gyrus (cortex)",128012244:"hippocampal proper",128012250:"marginal zone of CA1",128012258:"hippocampal plate of CA1",128012264:"hippocampal subplate of CA1",128012270:"hippocampal intermediate zone of CA1",128012274:"hippocampal subventricular zone of CA1",128012278:"hippocampal ventricular zone of CA1",128012284:"marginal zone of CA2",128012292:"hippocampal plate of CA2",128012298:"hippocampal subplate of CA2",128012318:"marginal zone of CA3",128012326:"hippocampal plate of CA3",128012332:"hippocampal subplate of CA3",128012354:"subicular cortex",128012368:"hippocampal plate of subiculum",128012372:"pyramidal layer of subiculum",128012400:"rostral (anterior) presubiculum",128012404:"caudal (posterior) presubiculum",128012418:"subpial granular zone of V1",128012422:"marginal zone of V1",128012428:"cortical plate of V1",128012432:"outer cortical plate of V1",128012436:"supragranular layer of V1",128012458:"inner cortical plate (infragranular layer) of V1",128012466:"subplate zone of V1",128012470:"intermediate cell dense zone of V1",128012474:"intermediate zone of V1",128012478:"subventricular zone of V1",128012482:"outer subventricular zone of V1",128012486:"inner subventricular zone of V1",128012490:"ventricular zone of V1",128012494:"outer ventricular zone of V1",128012498:"inner ventricular zone of V1",128012584:"rostral migratory stream (RMS)",128012588:"ventricular portion of RMS",128012596:"cerebral nuclei",128012616:"corticomedial amygdaloid nucleus",128012620:"periamygdaloid cortex (cortical amygdaloid nucleus)",128012634:"caudal periamygdaloid cortex (cPAC)",128012638:"caudal periamygdaloid cortex, dorsal part",128012678:"basolateral nuclear complex",128012818:"ganglionic eminence (VZ in basal nuclei)",128012822:"lateral ganglionic eminence",128012834:"medial ganglionic eminence",128012846:"caudal ganglionic eminence",128012920:"shell of nucleus accumbens, medial part",128012926:"main island of Calleja",128012960:"claustrum",128012976:"basal forebrain",128013010:"diencephalon",128013014:"thalamus",128013018:"dorsal thalamus",128013070:"lateral geniculate nucleus",128013074:"dorsal lateral geniculate nucleus",128013078:"dorsal lateral geniculate nucleus, magnocellular layers",128013091:"dorsal lateral geniculate nucleus, parvocellular layers",128013113:"dorsal lateral geniculate nucleus, koniocellular layers",128013147:"epithalamus",159658800:"outer fiber (plexiform) zone of rostral cingulate cortex",159658804:"inner fiber (plexiform) zone of rostral cingulate cortex",159658808:"outer fiber (plexiform) zone of S1",159658816:"inner fiber (plexiform) zone of S1",159658820:"dentate migratory stream",159658824:"cortical hem",159658836:"outer fiber (plexiform) zone of V1",159658840:"transitory migratory zone of V1",159658844:"inner fiber (plexiform) zone of V1",159658852:"lateral ganglionic eminence-cortex border",294021746:"neocortex",294021766:"subpial granular zone",294021774:"marginal zone",294021786:"layer I",294021800:"layer II",294021818:"layer II/III",294021824:"layer III",294021842:"cortical plate (outer)",294021852:"layer IV",294021878:"layer V",294021896:"layer VI",294021914:"cortical plate",294021920:"cortical plate (inner)",294021932:"subplate",294021942:"intermediate zone",294021954:"transitory migratory zone",294021960:"outer fiber zone",294021970:"subventricular zone",294021980:"subventricular zone (outer)",294021990:"inner fiber zone",294022000:"subventricular zone (inner)",294022010:"ventr
65844icular zone (outer)",294022020:"ventricular zone (inner)",294022030:"ventricular zone",294022044:"white matter",294022158:"entorhinal cortex"}, 65845 structure_colors:"3999,0xE0E0E0,4000,0xE26880,4001,0xE2EACA,4002,0xC9E2B1,4003,0xBFB5D5,4004,0xE7ACA1,9999,0xE26880,10000,0xF2E5BB,10001,0xC2A335,10002,0xB8A26D,10003,0xFFE168,10004,0xEC813B,10005,0xED8F56,10007,0xE0E0DE,10008,0xB8A26D,10009,0xF2E5BB,10010,0xC2A335,10011,0xB8A26D,10012,0xFFE168,10013,0xEC813B,10014,0xED8F56,10016,0xE0E0DE,10017,0xCAB781,10018,0xF2E5BB,10019,0xC2A335,10020,0xB8A26D,10021,0xFFE168,10022,0xEC813B,10023,0xED8F56,10025,0xE0E0DE,10026,0xC13331,10027,0xF2E5BB,10028,0xC2A335,10029,0xB8A26D,10030,0xFFE168,10031,0xFFE168,10032,0xFFE168,10033,0xFFE168,10034,0xFFE168,10035,0xEC813B,10036,0xED8F56,10038,0xE0E0DE,10039,0xD14D46,10040,0xF2E5BB,10041,0xC2A335,10042,0xB8A26D,10043,0xFFE168,10048,0xEC813B,10049,0xED8F56,10051,0xE0E0DE,10053,0xAA9DC6,10054,0x9988BB,10055,0xC1A68F,10058,0xAA9DC6,10059,0x8A7AB1,10060,0x694F8C,10061,0xE6E6EC,10062,0xD1CEE2,10063,0x9988BB,10064,0x8A7AB1,10065,0x5D457F,10066,0xE6E6EC,10067,0xD1CEE2,10068,0x9988BB,10069,0x8A7AB1,10070,0x614884,10071,0xE6E6EC,10072,0xE6E6EC,10073,0xD1CEE2,10074,0xAA9DC6,10075,0x8A7AB1,10076,0xE6E6EC,10077,0x543E74,10078,0x725398,10079,0xAA9DC6,10080,0xC1CEA7,10081,0xC1CEA7,10082,0x99A677,10086,0xA8B485,10091,0xC1CEA7,10092,0x91A06E,10093,0x91A06E,10094,0x899662,10095,0x858536,10096,0x9988BB,10097,0xD7E0BF,10098,0xD0D9B2,10099,0xD7E0BF,10102,0xF2F1F0,10103,0xC0DD9D,10106,0x89BA46,10107,0x7EA541,10108,0x779E3F,10109,0x82B146,10110,0x9CCC66,10111,0x7EA541,10112,0x89BA46,10116,0xA6D17A,10117,0x92C851,10118,0x92C851,10119,0x89BA46,10120,0x89BA46,10121,0xAFD58A,10123,0xAFD58A,10124,0xAFD58A,10125,0x9CCC66,10128,0x92C851,10132,0x72983F,10134,0x89BA46,10135,0x89BA46,10136,0x72983F,10137,0x82B146,10139,0x89BA46,10140,0x74A046,10141,0x8F8E42,12984,0xD4B235,12986,0xC2A335,12987,0xB8A26D,12988,0xFFE168,12989,0xEC813B,12990,0xED8F56,128011350,0xEBD6D0,128011354,0xE9CCC2,128011368,0x806D2C,128011372,0xEEE0A8,128011378,0xE49C33,128011614,0
65845xEEE0A8,128011624,0xEDCF37,128011638,0xE49C33,128011646,0xD38E32,128011650,0xEBBFD0,128011654,0xD670A0,128011658,0xDA80A8,128011662,0xD55C92,128011666,0xBA2E2E,128011670,0xD14D46,128011674,0xCE3D39,128011776,0xEEE0A8,128011788,0xEDCF37,128011796,0xC2A335,128011800,0xB8A26D,128011804,0xFFE168,128011808,0xE49C33,128011812,0xEC813B,128011816,0xED8F56,128011820,0xD38E32,128011824,0xEBBFD0,128011828,0xD670A0,128011832,0xDA80A8,128011836,0xD55C92,128011840,0xBA2E2E,128011844,0xD14D46,128011848,0xCE3D39,128011876,0xCBB7A8,128011900,0xCBB7A8,128011936,0xCBB7A8,128011960,0xCBB7A8,128012080,0xB67BB4,128012084,0xB67BB4,128012120,0xB67BB4,128012144,0xB67BB4,128012220,0xE6E6EC,128012226,0x543E74,128012232,0x725398,128012238,0xAA9DC6,128012244,0xBFB5D5,128012250,0xD1CEE2,128012258,0x5D457F,128012264,0xD1CEE2,128012270,0xE6E6EC,128012274,0x674881,128012278,0x5D457F,128012284,0xD1CEE2,128012292,0x5D457F,128012298,0xD1CEE2,128012318,0xD1CEE2,128012326,0x5D457F,128012332,0xD1CEE2,128012354,0x9988BB,128012368,0xF2F1F0,128012372,0x614884,128012400,0xC1A68F,128012404,0xC1A68F,128012418,0x806D2C,128012422,0xEEE0A8,128012428,0xFDD92E,128012432,0xEDCF37,128012436,0xD4B235,128012458,0xE49C33,128012466,0xD38E32,128012470,0xEBBFD0,128012474,0xEBBFD0,128012478,0xD670A0,128012482,0xDA80A8,128012486,0xD55C92,128012490,0xBA2E2E,128012494,0xD14D46,128012498,0xCE3D39,128012584,0xA78D8D,128012588,0x937B7C,128012596,0xD7E9C3,128012616,0xA6D17A,128012620,0x9CCC66,128012634,0x89BA46,128012638,0x9CCC66,128012678,0xA6D17A,128012818,0x81696B,128012822,0x937B7C,128012834,0x8C7373,128012846,0x81696B,128012920,0x899662,128012926,0x858536,128012960,0xAADBCE,128012976,0xDEDDAF,128013010,0xE4CCE3,128013014,0xE4CCE3,128013018,0xCC9FC9,128013070,0x8C4685,128013074,0x8C4685,128013078,0x773671,128013091,0x8C4685,128013113,0xE4CDE4,128013147,0xCC9FC9,159658800,0xE196B7,159658804,0xE3A8C2,159658808,0xE196B7,159658816,0xE3A8C2,159658820,0x50487F,159658824,0x937B7C,159658836,0xE196B7,159658840,0xF0D5E1,159658844,0xE3A8C2,159658852,0x937B7C,294021746,0xE9CCC2,294021766,0x806D2C,294021774,0xEEE0A8,294021786,0xF2E5BB,294021800,0xC2A335,294021818,0xD4B235,294021824,0xB8A26D,294021842,0xEDCF37,294021852,0xFFE168,294021878,0xEC813B,294021896,0xED8F56,294021914,0xFDD92E,294021920,0xE49C33,294021932,0xD38E32,294021942,0xEBBFD0,294021954,0xF0D5E1,294021960,0xE196B7,294021970,0xD670A0,294021980,0xDA80A8,294021990,0xE3A8C2,294022000,0xD55C92,294022010,0xD14D46,294022020,0xCE3D39,294022030,0xBA2E2E,294022044,0xE0E0DE,294022158,0xD2BFB2" 65846 }, 65847 structureInfo:"", 65848 self:ish_zap_viewer_globals, 65849 resizeTimer:0, 65850 65851 // state var used by doStructureClick() 65852 isPanning: false, 65853 65854 // overriding zap viewer's key handlers 65855 keyDownEvent:function (evt) { 65856 var target = evt.target; 65857 var series_id = target.id.replace(/\D*/, ''); // strip non-decimal chars from id to grab ImageSeriesId 65858 65859 var viewer_object = _zapviewers[series_id]; 65860 65861 if (viewer_object) { 65862 var images = viewer_object.getImages(); 65863 var image = viewer_object.getImageInfo(); 65864 var pos = viewer_object.getPosition(); 65865 var keyCode = evt.memo.keyCode; 65866 65867 switch (keyCode) { 65868 case 84: // "t" = advance all viewers to next image 65869 hbaMoveAll(true); 65870 break; 65871 case 87: // "w" = reverse all viewers to previous image 65872 hbaMoveAll(false); 65873 break; 65874 /* case 83: // "s" = synch 65875 doSync(viewer_object); 65876 break; */ 65877 default: // pass unprocessed key events back to Zap Viewer, such as the zoom keys and arrow keys 65878 evt.keyCode = keyCode; 65879 var viewer = evt.memo.viewer; 65880 viewer.doKey(evt); 65881 break; 65882 } 65883 } 65884 }, 65885 65886 // move to next/previous image in all zap viewers on page 65887 // isNext - next if true, previous if false 65888 hbaMoveAll:function (isNext) { 65889 for (var key in _zapviewers) { 65890 var viewer = _zapviewers[key]; 65891 var images = viewer.getImages(); 65892 var image = viewer.getImageInfo(); 65893 var pos = viewer.getPosition(); 65894 var targetImage; 65895 65896 if (isNext) { 65897 targetImage = getNextImage(image.id, images); 65898 } 65899 else { 65900 targetImage = getPreviousImage(image.id, images); 65901 } 65902 65903 if (targetImage != null) { 65904 hbaMoveToImage(viewer, targetImage.id, image.tier, pos); 65905 } 65906 } 65907 }, 65908 65909 getNextImage:function (currentImageId, images) { 65910 var isNextImage = false;
65911 var nextImage; 65912 65913 for (var key in images) { 65914 if (isNextImage) { 65915 nextImage = images[key]; 65916 break; 65917 } 65918 65919 if (images[key].id == currentImageId) { 65920 isNextImage = true; 65921 } 65922 } 65923 return nextImage; 65924 }, 65925 65926 getPreviousImage:function (currentImageId, images) { 65927 var prevImage; 65928 65929 for (var key in images) { 65930 if (images[key].id == currentImageId) { 65931 break; 65932 } 65933 else { 65934 prevImage = images[key]; 65935 } 65936 } 65937 return prevImage; 65938 }, 65939 65940 hbaMoveToImage:function (viewer, image_id, image_tier, position) { 65941 viewer.select(image_id); 65942 viewer.moveToImage(image_id); 65943 var info = viewer.getImageInfo(); 65944 var newTier; 65945 // edge case, moving from an image at a higher tier than exists in the new image 65946 if (image_tier > (info.num_tiers - 1)) { 65947 newTier = info.num_tiers - 1; 65948 } 65949 else { 65950 newTier = image_tier; 65951 } 65952 viewer.setPosition('normal', newTier, position.normal.x, position.normal.y); 65953 65954 var evt = new Object(); 65955 var memo = new Object(); 65956 memo.position = position; 65957 memo.viewer = viewer; 65958 memo.image = viewer.getImageInfo(); 65959 evt.memo = memo; 65960 viewer.scalebar.updateScaleBar(evt); 65961 65962 // synch pan, zoom of vector graphics to underlying image 65963 var zapimg = viewer.zapimg; 65964 if (zapimg.enable_graphics && zapimg.graphics !== null && zapimg.graphics.graphics_enabled) { 65965 zapimg.updateGraphicsPosition(); 65966 } 65967 }, 65968 65969 65970 setPanel:function (viewer) { 65971 var panel = null; 65972 65973 // this can get called by an onImageChange event that is issued before viewer is fully instantiated 65974 if (typeof viewer.getPanel == 'function') { 65975 panel = viewer.getPanel('info_panel'); 65976 } 65977 65978 var image = viewer.getImageInfo(); 65979 65980 if (panel != null && image != null) { 65981 var image = viewer.getImageInfo(); 65982 var panelHTML = "<table class='viewer_key' cell-spacing='0' cell-padding='0'><tr>"; 65983 65984 if (viewer.series_data.failed == 'true') { 65985 panelHTML += "<td width='90px'><span class='warning'> FAILED QC </span></td>"; 65986 } 65987 if ((viewer.series_data.age != undefined) && (viewer.imgView != "NISSL")) { 65988 panelHTML += "<td width='90px'>Age: <span class='info'>" + viewer.series_data.age + "</span></td>"; 65989 } 65990 if (image.section_number !== undefined) { 65991 if (viewer.imgView === "NISSL" && (image.nearest_nissl_sti !== undefined && image.nearest_nissl_sti !== "")) { 65992 panelHTML += "<td width='110px'>Tissue Index: <span class='info'>" + image.nearest_nissl_sti + "</span></td>"; 65993 } 65994 else { 65995 panelHTML += "<td width='110px'>Tissue Index: <span class='info'>" + image.section_number + "</span></td>"; 65996 } 65997 } 65998 if (viewer.series_data.structure_name !== undefined) { 65999 panelHTML += "<td>Location: <span class='info'>" + viewer.series_data.structure_name + "</span></td></tr>"; 66000 } 66001 // hotspot info from Nissl graphic layer, SIV Zap only 66002 if (viewer.show_structure_name == true && ish_zap_viewer_globals.structureInfo !== undefined && ish_zap_viewer_globals.structureInfo !== "") { 66003 panelHTML += "<tr><td colspan='4'>Structure Name: <span class='info'>" + ish_zap_viewer_globals.structureInfo + "</span></td>"; 66004 } 66005 panelHTML += "</tr></table>"; 66006 panel.foreground.innerHTML = panelHTML; 66007 } 66008 }, 66009 66010 66011 setupGraphics:function (viewer, options) { 66012 if (options !== undefined) { 66013 ish_zap_viewer_globals.on_get_atlas_id = options.on_get_atlas_id; 66014 ish_zap_viewer_globals.atlas_viewer_service = options.atlas_viewer_service; 66015 } 66016 66017 if (viewer.graphics_enabled == false) { 66018 // if we've tried to enable graphics, but graphics are not enabled,
66019 // let the user know why. Probably they don't have flash/up-to-date version 66020 viewer.addPanel('no_flash', 'panel_class'); 66021 var panel = viewer.getPanel('no_flash'); 66022 var flash_warning = viewer.zapimg.graphics.failure_message; 66023 panel.foreground.innerHTML = flash_warning; 66024 } 66025 66026 viewer.zapimg.graphics.addEventListener('STRUCTURE_CLICK', ish_zap_viewer_globals.doStructureClick); 66027 viewer.zapimg.graphics.addEventListener('MOUSE_CLICK', ish_zap_viewer_globals.doStructureClick); 66028 viewer.zapimg.graphics.addEventListener('PAN_DRAG', ish_zap_viewer_globals.doStructureClick); 66029 66030 viewer.zapimg.graphics.addEventListener('STRUCTURE_MOUSE_OVER', ish_zap_viewer_globals.doStructureOver); 66031 viewer.zapimg.graphics.addEventListener('STRUCTURE_MOUSE_OUT', ish_zap_viewer_globals.doStructureOut); 66032 66033 viewer.showing_colors = true; 66034 viewer.showing_shapes = true; 66035 viewer.showing_text = false; 66036 ish_zap_viewer_globals.showText(viewer, viewer.showing_text); 66037 ish_zap_viewer_globals.showOutlines(viewer, viewer.showing_shapes); 66038 ish_zap_viewer_globals.showColors(viewer, viewer.showing_colors); 66039 }, 66040 66041 /** 66042 * Construct an url to the Atlas Viewer and open a new window for display 66043 * 66044 * Because there is a bug and it isn't possible to get a STRUCTURE_MOUSE_OUT event, the best we can do is ensure 66045 * that mouse dragging won't cause the unintended launch of Atlas Viewer. 66046 * 66047 * Fixed event sequence for panning: PAN_START_DRAG --> PAN_DRAG(multiple) --> PAN_END_DRAG --> MOUSE_CLICK | STRUCTURE_CLICK 66048 * 66049 * @param id 66050 * @param val 66051 * @param json 66052 * @param evt 66053 */ 66054 doStructureClick: function(id, val, json, evt) { 66055 if (evt == 'PAN_DRAG') 66056 ish_zap_viewer_globals.isPanning = true; 66057 66058 if (evt == 'STRUCTURE_CLICK') { 66059 if (ish_zap_viewer_globals.isPanning == true) { 66060 // don't process bogus STRUCTURE_CLICK 66061 ish_zap_viewer_globals.isPanning = false; 66062 } 66063 else { 66064 // launch Atlas Viewer 66065 var value; 66066 eval("value = " + val); // string representation of a hash 66067 var structure_id = value.structure_id; 66068 var atlas_id = ish_zap_viewer_globals.on_get_atlas_id(); 66069 var atlas_viewer_service = ish_zap_viewer_globals.atlas_viewer_service; 66070 66071 var window_name = "hba_atlas_viewer"; 66072 window.open(atlas_viewer_service + "?atlas=" + atlas_id + "&structure=" + structure_id, window_name, "devhuman_atlas_window", "resizable=1"); 66073 } 66074 } 66075 66076 if (evt == 'MOUSE_CLICK') { 66077 ish_zap_viewer_globals.isPanning = false; 66078 } 66079 }, 66080 66081 doStructureOver:function (id, val, structure, evt) { 66082 ish_zap_viewer_globals.showStructureInfo(id, structure); 66083 }, 66084 66085 doStructureOut:function (id, val, json, evt) { 66086 ish_zap_viewer_globals.showStructureInfo(id, null); 66087 }, 66088 66089 showStructureInfo:function (id, structure) { 66090 ish_zap_viewer_globals.structureInfo = ""; 66091 if (structure) { 66092 ish_zap_viewer_globals.structureInfo = ish_zap_viewer_globals.dao.structure_names[structure.structure_id]; 66093 } 66094 var viewer = ish_zap_viewer_globals.findViewer(id); 66095 ish_zap_viewer_globals.setPanel(viewer); 66096 }, 66097 66098 // find zap viewer corresponding to the zap graphics object id 66099 findViewer:function (zapgraphicsId) { 66100 var numericId = zapgraphicsId.match(/\d+/); // looks like "zapimg100102590" 66101 var viewer = _zapviewers[numericId]; 66102 return(viewer); 66103 }, 66104 66105 66106 inspect:function (prefix, obj) { 66107 var s = ""; 66108 for (var key in obj) { 66109 if ((typeof obj[key]) == "function") 66110 s = s; 66111 else if ((typeof obj[key]) == "object" && obj[key] !== null & key != "element") 66112 s += "\n" + prefix + "key: " + key;// + inspect(prefix + " ", obj[key]); 66113 else 66114 s += "\n" + prefix + "key: " + key + ", value: " + obj[key]; 66115 } 66116 return(s); 66117 }, 66118 66119 // listener for onImageChange 66120 imageChangeEvent : function(viewer, image) { 66121 // update display metatdata via panel 66122 ish_zap_viewer_globals.setPanel(viewer); 66123 66124 if (viewer.graphics_enabled) { 66125 ish_zap_viewer_globals.showText(viewer, viewer.showing_text); 66126 ish_zap_viewer_globals.showOutlines(viewer, viewer.showing_shapes); 66127 ish_zap_viewer_globals.showColors(viewer, viewer.showing_colors); 66128 } 66129 }, 66130 66131 onSelectEvent:function (evt) { 66132 66133 }, 66134 66135 showOutlines:function (viewer, show) { 66136 if (!show) 66137 return; 66138
66139 if (viewer.zapimg.graphics !== null && viewer.zapimg.graphics.graphics_enabled && viewer.zapimg.graphics.canvas !== null) { 66140 viewer.zapimg.graphics.canvas.executeToolMethod("showShapes", show); 66141 viewer.zapimg.graphics.redraw(); 66142 } 66143 return(show); 66144 }, 66145 66146 showText:function (viewer, show) { 66147 if (viewer.graphics_enabled && viewer.zapimg.graphics.canvas !== null) { 66148 if (!show) { 66149 viewer.zapimg.graphics.executeToolMethod("removeStructureLabels"); 66150 viewer.zapimg.graphics.clear(); 66151 } else 66152 viewer.zapimg.graphics.executeToolMethod("applyStructureLabels", ish_zap_viewer_globals.dao.structure_labels); 66153 66154 viewer.zapimg.graphics.redraw(); 66155 } 66156 return(show); 66157 }, 66158 66159 showColors:function (viewer, show) { 66160 if (viewer.zapimg.graphics !== null && viewer.zapimg.graphics.graphics_enabled && viewer.zapimg.graphics.canvas !== null) { 66161 if (!show) { 66162 viewer.zapimg.graphics.executeToolMethod("removeStructureColors"); 66163 viewer.zapimg.graphics.clear(); 66164 } else { 66165 viewer.zapimg.graphics.executeToolMethod("applyStructureColors", ish_zap_viewer_globals.dao.structure_colors); 66166 } 66167 viewer.zapimg.graphics.redraw(); 66168 } 66169 return(show); 66170 } 66171}; 66172 66173"use strict"; 66174/** 66175 * Wrapper function enclosing zapviewer() 66176 * 66177 * NOTE: this app's variant of ish_zap_wrapper has a large chunk of code added to support the structure pulldown 66178 * menu, which is present when viewing Atlases. When a structure is selected, the viewer zooms in to the area 66179 * of interest. 66180 * 66181 * @param {string|Object} container_id - CSS id or jQuery element 66182 * @param {Object} services 66183 * @param {string} services.image_service 66184 * @param {string} services.tile_service 66185 * @param {string} services.info_service 66186 * @param {string} mode - unused argument 66187 * @param {Object} tools 66188 * @param {Object} options 66189 * @param {Object} options.atlas 66190 * @param {string} options.atlas_id 66191 * @param {number} options.length 66192 * @param {Object} options.event_options 66193 * @param {Object} options.event_options.on_load 66194 * @param {Object} options.event_options.on_before_image_type_change 66195 * @param {Object} options.event_options.on_after_image_type_change 66196 * @param {Object} options.event_options.on_before_siv 66197 * @param {Object} options.event_options.on_after_siv 66198 * @param {Object} options.event_options.on_before_contact_sheet 66199 * @param {Object} options.event_options.on_after_contact_sheet 66200 * @param {Object} options.event_options.on_before_legend 66201 * @param {Object} options.event_options.on_after_legend 66202 * @param {Object} options.event_options.on_before_contrast 66203 * @param {Object} options.event_options.on_after_contrast 66204 * @param {Object} options.event_options.on_before_info 66205 * @param {Object} options.event_options.on_after_info 66206 * @param {Object} options.event_options.on_before_info 66207 * @param {Object} options.event_options.on_before_help 66208 * @param {Object} options.event_options.on_after_help 66209 * @param {Object} options.event_options.on_before_download 66210 * @param {Object} options.event_options.on_after_download 66211 * @param {Object} options.event_options.on_before_sync 66212 * @param {Object} options.event_options.on_after_sync 66213 * @param {Object} options.event_options.on_before_sync_check 66214 * @param {Object} options.event_options.on_after_sync_check 66215 * @param {Object} options.event_options.on_before_nissl_only 66216 * @param {Object} options.event_options.on_after_nissl_only 66217 * @param {Object} options.event_options.on_before_all_stains 66218 * @param {Object} options.event_options.on_after_all_stains 66219 * @param {Object} options.event_options.on_before_show_text 66220 * @param {Object} options.event_options.on_after_show_text 66221 * @param {Object} options.event_options.on_before_show_color 66222 * @param {Object} options.event_options.on_after_show_color 66223 * @param {Object} options.event_options.on_before_show_outline 66224 * @param {Object} options.event_options.on_after_show_outline 66225 * @param {Object} options.event_options.on_before_navigation 66226 * @param {Object} options.event_options.on_after_navigation 66227 * @param {Object} options.event_options.on_before_blockface 66228 * @param {Object} options.event_options.on_after_blockface 66229 * @param {Object} options.event_options.on_before_color_filters 66230 * @param {Object} options.event_options.on_after_color_filters 66231 * @param {Object} options.event_options.on_image_set_load 66232 * @param {Object} options.event_options.on_before_close 66233 * @param {Object} options.defer_init 66234 * @param {Object} options.strip_tier 66235 * @param {Object} options.include_failed 66236 * @param {Object} options.sync_strip_image_type 66237 * @param {Object} options.sync_strip_image_filter 66238 * @param {Object} options._apply_styles 66239 * @param {Object} options.image_types 66240 * @param {Object} options.initial_image_type 66241 * @param {Object} options.daos 66242 * @param {Object} options.debugger 66243 * @param {Object} options._error_manager 66244 * @param {Object} options.title 66245 * @param {Object} options.graphics_options 66246 * @param {Object} options.zap_options 66247 * @param {Object} options.onKeyBeforeFncs 66248 * @param {Object} options.sb_create 66249 * @param {Object} options.onImageChangeFncs 66250 */
66251function ish_zap_wrapper(container_id, services, mode, tools, options) { 66252// public properties 66253 this.container_id = container_id instanceof jQuery ? container_id.attr('id') : container_id; 66254 this.container = $j('#' + container_id); 66255 this.services = services; 66256 this.mode = mode; 66257 this.tools = tools; 66258 this.options = options; 66259 this.atlas = options.atlas; 66260 this.atlas_id = options.atlas_id; 66261 66262// public methods 66263 this.set_images = function (images) { 66264 66265 do_set_images(images); 66266 scalebar_in_viewport(); 66267 }; 66268 66269 this.get_image_types = function() { 66270 var select = $j('#_zap_tool_image_select_' + _the_viewer.id)[0]; 66271 if(!select) 66272 return('atlas'); 66273 66274 var types = ""; 66275 for(var i = 0; i < select.options.length; i++) { 66276 if(i) 66277 types += ","; 66278 types += select.options[i].value; 66279 } 66280 66281 return(types); 66282 }; 66283 66284 /** 66285 * Sets all the menu options in the image types menu 66286 * @param image_types 66287 */ 66288 this.set_image_types_menu = function(image_types) { 66289 do_set_image_types_menu(image_types); 66290 }; 66291 66292 /** 66293 * Set the current selection from the image types menu. 66294 * This does not generate a change event. 66295 * 66296 * @param image_type 66297 */ 66298 this.set_image_types_selection = function(image_type) { 66299 do_set_image_types_selection(image_type); 66300 } 66301 66302 this.clear_images = function () { 66303 _the_viewer.strip.clear(); 66304 }; 66305 66306 this.get_image_info = function () { 66307 var image = _the_viewer.getImageInfo(); 66308 var pos = _the_viewer.getImagePosition(); 66309 return({image:image, position:pos} ); 66310 }; 66311 66312 // Current view info (for download) 66313 this.get_download_info = function(){ 66314 var info = _the_viewer.getViewInfo(); 66315 66316 return({ 66317 image:info 66318 }) 66319 }; 66320 66321 this.get_images = function () { 66322 return(_the_viewer.getImages()); 66323 }; 66324 66325 /** 66326 * Get centerpoint of viewport image 66327 * 66328 * @returns {Object} hash with keys x, y in full resolution image coords 66329 */ 66330 this.get_center_point = function () { 66331 return(get_center_point()); 66332 }; 66333 66334 this.get_current_image = function() { 66335 return (_the_viewer.getCurrentImage()); 66336 }; 66337 66338 this.update_scale_bar = function() { 66339 update_scale_bar(_the_viewer); 66340 }; 66341 66342 /** 66343 * Select an image from the image strip thumbnails, scrolling that image into view, and displaying that image 66344 * in the main viewer area. The scalebar is also updated to correspond with the image in the main viewer area. 66345 * 66346 * @param {number} id image id 66347 * @param {boolean} retainPanZoom if true, current x, y and zoom values (if any) from the previous display 66348 */ 66349 this.select_image = function(id, retainPanZoom) { 66350 _the_viewer.moveToImage(id); 66351 _the_viewer.select(id, retainPanZoom); 66352 update_scale_bar(_the_viewer); 66353 }; 66354 66355 /** 66356 * Zooms and pans the current image, then updates the scale bar 66357 * 66358 * @param coord_system 66359 * @param tier 66360 * @param left 66361 * @param top 66362 */ 66363 this.set_image_position = function (coord_system, tier, left, top) { 66364 _the_viewer.setPosition(coord_system, tier, left, top); 66365 update_scale_bar(_the_viewer); 66366 }; 66367 66368 /** 66369 * 66370 * @param image_id 66371 * @param center_x 66372 * @param center_y 66373 * @param {Object} options 66374 * @param {number} [options.absolute_tier] - target tier 66375 * @param {number} [options.tier_delta] - tier delta 66376 * @param {Object} options.ready - completion callback 66377 */ 66378 this.move_select_and_center = function (image_id, center_x, center_y, options) { 66379 var tier_delta = 0; // used for relative tier changes from current image tier. Not applicable. 66380 66381 select_image_by_center(image_id, center_x, center_y, tier_delta, options); 66382 }; 66383 66384 this.resize = function () { 66385 if (_the_viewer) { 66386 _the_viewer.resize(); 66387 scalebar_in_viewport(); 66388 } 66389 }; 66390 66391 this.get_viewer = function () { 66392 return(_the_viewer); 66393 }; 66394 66395 this.init = function () { 66396 init(); 66397 }; 66398 66399 this.destroy = function () { 66400 unbind_toolbar_handlers(); 66401 _the_viewer = null; 66402 }; 66403 66404 this.get_current_image_type = function() { 66405 return(_current_image_type); 66406 }; 66407 66408 this.get_filter = function () { 66409 if (!_the_viewer) 66410 return; 66411 66412 return(_the_viewer.getFilter()); 66413 }; 66414 66415 /** 66416 * Pass in the filter data for each image type of the viewer. The filter values will be the default values, 66417 * this should be overridden with the actual current value. 66418 * 66419 * @param image_filters hashes of image_type:{filterName:name, filterVals:filter_value} 66420 */ 66421 this.set_image_filters = function(image_filters) { 66422 _image_filters = image_filters; 66423 }; 66424 66425 /** 66426 * This is called after the viewer is created and after the images are loaded by
66427 * the external caller. 66428 * 66429 * Business logic: 66430 * In general, filters should only be applied to the primary image, and not the imagestrip thumbnails. 66431 * The only exception is for Expression images, which require that a filter be applied to all its images to covert 66432 * its native grayscale images to color. 66433 * 66434 * @param {string} name filter name 66435 * @param {string}values filter values, comma-delimited 66436 * @param {boolean} apply_imagestrip optional arg, if true then filter is applied to both primary viewed image and imagestrip (thumbnail) images 66437 * @return {*} 66438 */ 66439 this.set_filter = function(name, values, apply_imagestrip) { 66440 apply_imagestrip = apply_imagestrip == undefined ? false : apply_imagestrip; 66441 apply_imagestrip = _current_image_type == 'expression' ? true : apply_imagestrip; 66442 _the_viewer.setFilter(name, values, apply_imagestrip); 66443 66444 // update local filter store values 66445 var filter = _the_viewer.getFilter(); 66446 update_image_filters(filter); 66447 }; 66448 66449 this.position_contrast_slider = function() { 66450 // expression images do not use the contrast filter/slider 66451 if (_current_image_type != 'expression') { 66452 position_contrast_slider(); 66453 } 66454 }; 66455 66456 this.get_sync_check = function () { 66457 return(_sync_checkbox.is(':checked')); 66458 }; 66459 66460 this.set_sync_check = function (checked) { 66461 _sync_checkbox.prop('checked', checked); 66462 }; 66463 66464 this.add_event_listener = function (event_name, callback) { 66465 add_zap_listener(event_name, callback); 66466 }; 66467 66468 this.set_title = function (new_title) { 66469 if (!_the_viewer) 66470 return; 66471 66472 _the_viewer.setTitle(new_title); 66473 }; 66474 66475 66476 this.set_tool_state = function (tool_name, enabled) { 66477 66478 var tool = $j('#_zap_tool_' + tool_name + '_' + _the_viewer.id); 66479 if (enabled) { 66480 tool.removeClass('_tb_disabled'); 66481 tool.bind('click', _tools[tool_name]); 66482 } 66483 else { 66484 tool.addClass('_tb_disabled'); 66485 tool.unbind('click', _tools[tool_name]); 66486 } 66487 }; 66488// event hooks 66489 // lotsa hooks 66490 66491 // let's make these all before-and-after, with the before having 66492 // the option to cancel the default event handling, so the caller 66493 // can substitute their own delegate if necessary. 66494 66495 // all event handler prototypes should look like this: 66496 // handler(this (the ish_zap_wrapper instance), event_data (includes event_name & event-specific data) 66497 // 66498 // for each event type there is an on_before_xx event and an on_after_xx event. 66499 // on_before_event handlers should return true or false. If true, the default handler is also called; 66500 // if false the default handler is not called. 66501 66502 this.on_load = options.event_options ? options.event_options.on_load : null; 66503 66504 this.on_before_image_type_change = options.event_options ? options.event_options.on_before_image_type_change : null; 66505 this.on_after_image_type_change = options.event_options ? options.event_options.on_after_image_type_change : null; 66506 66507 this.on_before_structure_change = options.event_options ? options.event_options.on_before_structure_change : null; 66508 this.on_after_structure_change = options.event_options ? options.event_options.on_before_structure_change : null; 66509 66510 this.on_before_siv = options.event_options ? options.event_options.on_before_siv : null; 66511 this.on_after_siv = options.event_options ? options.event_options.on_after_siv : null; 66512 66513 this.on_before_contact_sheet = options.event_options ? options.event_options.on_before_contact_sheet : null; 66514 this.on_after_contact_sheet = options.event_options ? options.event_options.on_after_contact_sheet : null; 66515 66516 this.on_before_legend = options.event_options ? options.event_options.on_before_legend : null; 66517 this.on_after_legend = options.event_options ? options.event_options.on_after_legend : null; 66518 66519 this.on_before_contrast = options.event_options ? options.event_options.on_before_contrast : null; 66520 this.on_after_contrast = options.event_options ? options.event_options.on_after_contrast : null; 66521 66522 this.on_before_info = options.event_options ? options.event_options.on_before_info : null; 66523 this.on_after_info = options.event_options ? options.event_options.on_after_info : null; 66524 66525 this.on_before_help = options.event_options ? options.event_options.on_before_help : null; 66526 this.on_after_help = options.event_options ? options.event_options.on_after_help : null; 66527 66528 this.on_before_download = options.event_options ? options.event_options.on_before_download : null; 66529 this.on_after_download = options.event_options ? options.event_options.on_after_download : null; 66530 66531 this.on_before_sync = options.event_options ? options.event_options.on_before_sync : null; 66532 this.on_after_sync = options.event_options ? options.event_options.on_after_sync : null; 66533 66534 this.on_before_sync_check = options.event_options ? options.event_options.on_before_sync_check : null; 66535 this.on_after_sync_check = options.event_options ? options.event_options.on_after_sync_check : null; 66536 66537 this.on_before_nissl_only = options.event_options ? options.event_options.on_before_nissl_only : null; 66538 this.on_after_nissl_only = options.event_options ? options.event_options.on_after_nissl_only : null; 66539 66540 this.on_before_all_stains = options.event_options ? options.event_options.on_before_all_stains : null; 66541 this.on_after_all_stains = options.event_options ? options.event_options.on_after_all_stains : null; 66542 66543 this.on_before_show_text = options.event_options ? options.event_options.on_before_show_text : null; 66544 this.on_after_show_text = options.event_options ? options.event_options.on_after_show_text : null; 66545 66546 this.on_before_show_color = options.event_options ? options.event_options.on_before_show_color : null; 66547 this.on_after_show_color = options.event_options ? options.event_options.on_after_show_color : null; 66548 66549 this.on_before_show_outline = options.event_options ? options.event_options.on_before_show_outline : null; 66550 this.on_after_show_outline = options.event_options ? options.event_options.on_after_show_outline : null; 66551 66552 this.on_before_navigation = options.event_options ? options.event_options.on_before_navigation : null; 66553 this.on_after_navigation = options.event_options ? options.event_options.on_after_navigation : null; 66554 66555 this.on_before_blockface = options.event_options ? options.event_options.on_before_blockface : null; 66556 this.on_after_blockface = options.event_options ? options.event_options.on_after_blockface : null; 66557 66558 this.on_before_color_filters = options.event_options ? options.event_options.on_before_color_filters : null; 66559 this.on_after_color_filters = options.event_options ? options.event_options.on_after_color_filters : null; 66560 66561 this.on_image_set_load = options.event_options ? options.event_options.on_image_set_load : null; 66562 66563 // the close event is an exception because, you know. 66564 this.on_before_close = options.event_options ? options.event_options.on_before_close : null; 66565 66566 $j('#zaptitletext' + container_id).css({'width':'25px', 'display':'inline-block', 'float':'left'}); 66567// private properties 66568 66569 var _self = this; 66570 var _the_viewer = null; 66571 var _defer_init = _self.options ? _self.options.defer_init ? _self.options.defer_init : false : false;
66572 var _strip_tier = _self.options ? _self.options.strip_tier ? _self.options.strip_tier : 0 : 0; 66573 var _include_failed = _self.options ? _self.options.include_failed ? _self.options.include_failed : false : false; 66574 var _sync_strip_image_type = _self.options ? _self.options.sync_strip_image_type ? _self.options.sync_strip_image_type : false : false; 66575 var _sync_strip_image_filter = _self.options ? _self.options.sync_strip_image_filter ? _self.options.sync_strip_image_filter : false : false; 66576//TODO: well, this needs a lot of documentation. IE will not honor styles applied in any way other than programaticaly. 66577 var _default_styles = [ 66578 {selector:'#zaptitletext' + container_id, style:{'width':'25px', 'display':'inline-block', 'float':'left'}} 66579 ]; 66580 var _apply_styles = _self.options ? _self.options._apply_styles ? _self.options._apply_styles : _default_styles : _default_styles; 66581 var _image_types = _self.options ? _self.options.image_types ? _self.options.image_types : "" : ""; 66582 var _initial_image_type = _self.options ? (_self.options.initial_image_type ? _self.options.initial_image_type.toLowerCase() : 'ish') : 'ish'; 66583 var _position_queue = null; 66584 var _positioning = false; 66585 var _sync_checkbox = null; 66586 var _contrast_slider = null; 66587 var _filter_cache = {}; 66588 var _the_daos = options.daos; 66589 66590 var _current_image_type = _initial_image_type; 66591 66592 // populated by call to this.set_image_filters(), the hash will have entries like image_type:{filterName:name, filterVals:filter_value} 66593 var _image_filters = {}; 66594 66595 var _tools = { 66596 siv:do_siv, 66597 info:do_info, 66598 help:do_help, 66599 legend:do_legend, 66600 download:do_download, 66601 sync:do_sync, 66602 sync_check:do_sync_check, 66603 close:do_close, 66604 image_select:do_image_select, 66605 contrast:do_contrast, 66606 nissl_only:do_nissl_only, 66607 all_stains:do_all_stains, 66608 show_text:do_show_text, 66609 show_color:do_show_color, 66610 show_outline:do_show_outline, 66611 contact_sheet:do_contact_sheet, 66612 navigation:do_navigation, 66613 blockface:do_blockface, 66614 color_filters:do_color_filters 66615 }; 66616 66617 66618// TODO: these make sense if they are implemented throughout the app. 66619 var _debugger = options ? options._debugger ? options._debugger : null : null; 66620 var _error_manager = options ? options._error_manager ? options._error_manager : null : null; 66621 66622 66623// private methods 66624 66625 /** 66626 * Update the filter values of all _image_filters entries which have the same filter name as the input arg 66627 * 66628 * @param filter hash {filterName:name, filterVals:values} 66629 */ 66630 function update_image_filters(filter) { 66631 for (var f in _image_filters) { 66632 if (_image_filters[f].filterName == filter.filterName) { 66633 _image_filters[f].filterVals = filter.filterVals; 66634 } 66635 } 66636 } 66637 66638 function scalebar_in_viewport() { 66639 66640 if (!_the_viewer) 66641 return; 66642 66643 var id = _the_viewer.id; 66644 var img = $j("#zapimg" + id); 66645 var sb = $j("#scalebar" + id); 66646 var sbHeight = sb.height(); 66647 var imgHeight = img.height(); 66648 var newX = 0; 66649 var newY = imgHeight - sbHeight; 66650 sb.css({'top':newY + "px", 'left':newX + "px"}); 66651 } 66652 66653 function add_zap_listener(event_name, callback) { 66654 // !!!!!!!!!!!!!!! Craziness alert !!!!!!!!!!!!!!!!!! 66655 // the zap viewer is a Prototype library widget. the following statement uses a Prototype library 66656 // method to capture the viewer creation event and bring it back into the jquery world: 66657 $(_self.container_id).observe(event_name, function (event) { 66658 callback(event.memo.viewer); 66659 }); 66660 // !!!!!!!!!!! end this particular craziness !!!!!!!!!!!!!!!! 66661 } 66662 66663 function container_error(container, msg) { 66664 // it would be nice to have one central 66665 // place for handling UI error reporting... 66666 if (_error_manager) { 66667 _error_manager.ui_report(_self, container, msg); 66668 return; 66669 } 66670 66671 var err_html = "<div class='err_notice ui-state-error'>" + msg + "</div>"; 66672 container.html(err_html); 66673 } 66674 66675 function show_image_error(msg) { 66676 var cont = $j(".zapimg") ? $j(".zapimg") : this.container; 66677 var err_msg = 'Images can not be displayed'; 66678 if (msg && msg.length) 66679 err_msg += ': <BR/><BR/>(' + msg + ')'; 66680 else 66681 err_msg += '.'; 66682 66683 container_error(cont, err_msg); 66684 } 66685 66686 function build_viewer() { 66687 66688 var zap_options = { 66689 defer_init:_self.defer_init, 66690 src_key:'path',
66691 strip_tier:_self.strip_tier, 66692 precalculated_path:'true' 66693 }; 66694 66695 if (_self.options.title) 66696 zap_options.title = _self.options.title; 66697 66698 $j.extend(true, zap_options, _self.options.zap_options); 66699 66700 // update the scalebar resolution when the image changes, if the scalebar is present 66701 if (zap_options.sb_create && zap_options.sb_create == 'true') { 66702 zap_options.onImageChangeFncs = zap_options.onImageChangeFncs ? zap_options.onImageChangeFncs + ',' + 'zap_functions.update_scalebar' : 'zap_functions.update_scalebar'; 66703 $j('#' + _self.container_id).bind('resize', scalebar_in_viewport); 66704 } 66705 66706 66707 // !!!!!!!!!!!!!!! Craziness alert !!!!!!!!!!!!!!!!!! 66708 // the zap viewer is a Prototype library widget. the following statement uses a Prototype library 66709 // method to capture the viewer creation event and bring it back into the jquery world: 66710 $(container_id).observe("zapviewer:viewercreated", function (event) { 66711 viewer_created(event.memo.viewer); 66712 }); 66713 // !!!!!!!!!!! end this particular craziness !!!!!!!!!!!!!!!! 66714 66715 _the_viewer = new zapviewer( 66716 container_id, 66717 _self.services.image_service, 66718 _self.services.tile_service, 66719 _self.services.info_service, 66720 zap_options 66721 ); 66722 66723 //attach_zap_event('imagechange', do_image_change); 66724 66725 if (_self.options.jquery_theme) { 66726 $j('#zapviewer' + container_id).addClass('ui-widget-content'); 66727 // $j('#zaptitle'+container_id).addClass('ui-widget-header'); 66728 } 66729 66730 apply_styles(); 66731 } 66732 66733 function attach_zap_event(event_name, callback) { 66734 66735 // !!!!!!!!!!!!!!! Craziness alert !!!!!!!!!!!!!!!!!! 66736 // the zap viewer is a Prototype library widget. the following statement uses a Prototype library 66737 // method to capture the viewer creation event and bring it back into the jquery world: 66738 $(container_id).observe("zapviewer:" + event_name, function (event) { 66739 callback(event.memo.viewer, event_name); 66740 }); 66741 // !!!!!!!!!!! end this particular craziness !!!!!!!!!!!!!!!! 66742 } 66743 66744 /** 66745 * Callback from zapviewer after successful creation. This is asynchronous. 66746 * 66747 * @param viewer 66748 */ 66749 function viewer_created(viewer) { 66750 66751 if (_self.on_load) 66752 _self.on_load(viewer); 66753 } 66754 66755 66756 function do_image_change(viewer) { 66757 66758 66759 } 66760 66761 /** 66762 * Update the scalebar to reflect the zoom level of the current image 66763 */ 66764 function update_scale_bar(viewer) { 66765 var img_info = viewer.getImageInfo(); 66766 if (img_info != null) 66767 viewer.scalebar.autoUpdateScaleBar(viewer, img_info); 66768 } 66769 66770 function apply_styles() { 66771 if (_apply_styles) { 66772 for (var i = 0; i < _apply_styles.length; i++) { 66773 var style = _apply_styles[i]; 66774 $j(style.selector).css(style.style); 66775 } 66776 } 66777 } 66778 66779 function add_toolbar() { 66780 // set any custom event handlers 66781 set_toolbar_user_handlers(); 66782 66783 // build the html for the tool controls and hand them to the viewer 66784 // to be added to the DOM 66785 create_toolbar_controls(); 66786 66787 // find the newly created toolbar controls and attach handlers 66788 bind_toolbar_handlers(); 66789 } 66790 66791 /** 66792 * Assuming the current filter values are appropriate for the contrast filter, 66793 * set the contrast slider's thumb to correspond to the filter value. 66794 */ 66795 function position_contrast_slider() { 66796 var filter = _the_viewer.getFilter(); 66797 var valArray = filter.filterVals.split(','); 66798 var contrastVal = valArray[1]; 66799 if (isNaN(contrastVal) == false) { 66800 var sliderVal = parseInt(100 * (parseFloat(contrastVal) + .0001)); 66801 _contrast_slider.slider("value", sliderVal); 66802 } 66803 } 66804 66805 function set_toolbar_user_handlers() { 66806 var len = _self.tools.length; 66807 for (var i = 0; i < len; i++) 66808 if (_self.tools[i].callback) 66809 _tools[_self.tools[i].name] = _self.tools[i].callback; 66810 } 66811 66812 function bind_toolbar_handlers() { 66813 // bind handlers to the control events 66814 // there are default handlers for all, but the user may have over-ridden them
66815 var viewer_id = _the_viewer.id; 66816 var len = _self.tools.length; 66817 for (var i = 0; i < len; i++) { 66818 var html = ""; 66819 switch (_self.tools[i].name) { 66820 case 'siv': 66821 $j('#_zap_tool_siv_' + viewer_id).bind('click', _tools[_self.tools[i].name]); 66822 break; 66823 case 'info': 66824 $j('#_zap_tool_info_' + viewer_id).bind('click', _tools[_self.tools[i].name]); 66825 break; 66826 case 'help': 66827 $j('#_zap_tool_help_' + viewer_id).bind('click', _tools[_self.tools[i].name]); 66828 break; 66829 case 'download': 66830 $j('#_zap_tool_download_' + viewer_id).bind('click', _tools[_self.tools[i].name]); 66831 break; 66832 case 'sync': 66833 $j('#_zap_tool_sync_' + viewer_id).bind('click', _tools[_self.tools[i].name]); 66834 break; 66835 case 'legend': 66836 $j('#_zap_tool_legend_' + viewer_id).bind('click', _tools[_self.tools[i].name]); 66837 break; 66838 case 'close': 66839 $j('#_zap_tool_close_' + viewer_id).bind('click', _tools[_self.tools[i].name]); 66840 break; 66841 case 'image_select': 66842 $j('#_zap_tool_image_select_' + viewer_id).bind('change', _tools[_self.tools[i].name]); 66843 break; 66844 case 'contact_sheet': 66845 $j('#_zap_tool_contact_sheet_' + viewer_id).bind('click', _tools[_self.tools[i].name]); 66846 break; 66847 case 'nissl_only': 66848 $j('#_zap_tool_nissl_only_' + viewer_id).bind('click', _tools[_self.tools[i].name]); 66849 break; 66850 case 'all_stains': 66851 $j('#_zap_tool_all_stains_' + viewer_id).bind('click', _tools[_self.tools[i].name]); 66852 break; 66853 66854 case 'show_text': 66855 $j('#_zap_tool_show_text_' + viewer_id).bind('click', _tools[_self.tools[i].name]); 66856 break; 66857 case 'show_color': 66858 $j('#_zap_tool_show_color_' + viewer_id).bind('click', _tools[_self.tools[i].name]); 66859 break; 66860 case 'show_outline': 66861 $j('#_zap_tool_show_outline_' + viewer_id).bind('click', _tools[_self.tools[i].name]); 66862 break; 66863 66864 case 'navigation': 66865 $j('#_zap_tool_navigation_' + viewer_id).bind('click', _tools[_self.tools[i].name]); 66866 break; 66867 case 'blockface': 66868 $j('#_zap_tool_blockface_' + viewer_id).bind('click', _tools[_self.tools[i].name]); 66869 break; 66870 case 'color_filters': 66871 $j('#_zap_tool_color_filters_'+ viewer_id).bind('click', _tools[_self.tools[i].name]); 66872 break; 66873 66874 case 'contrast': 66875 _contrast_slider = $j('#_zap_tool_contrast_'+ viewer_id); 66876 _contrast_slider.slider({value:50}); 66877 _contrast_slider.bind('slidechange', _tools[_self.tools[i].name]); 66878 break; 66879 case 'sync_check': 66880 _sync_checkbox = $j('#_zap_tool_sync_check_' + viewer_id); 66881 _sync_checkbox.bind('click', _tools[_self.tools[i].name]); 66882 break; 66883 default: 66884 } 66885 } 66886 } 66887 66888 function unbind_toolbar_handlers() { 66889 // bind handlers to the control events 66890 // there are default handlers for all, but the user may have over-ridden them 66891 var viewer_id = _the_viewer.id; 66892 var len = _self.tools.length; 66893 for (var i = 0; i < len; i++) { 66894 var html = ""; 66895 switch (_self.tools[i].name) { 66896 case 'siv': 66897 $j('#_zap_tool_siv_' + viewer_id).unbind('click', _tools[_self.tools[i].name]); 66898 break; 66899 case 'info': 66900 $j('#_zap_tool_info_' + viewer_id).unbind('click', _tools[_self.tools[i].name]); 66901 break; 66902 case 'help': 66903 $j('#_zap_tool_help_' + viewer_id).unbind('click', _tools[_self.tools[i].name]); 66904 break; 66905 case 'sync': 66906 $j('#_zap_tool_sync_' + viewer_id).unbind('click', _tools[_self.tools[i].name]); 66907 break; 66908 case 'legend': 66909 $j('#_zap_tool_legend_' + viewer_id).unbind('click', _tools[_self.tools[i].name]); 66910 break; 66911 case 'close': 66912 $j('#_zap_tool_close_' + viewer_id).unbind('click', _tools[_self.tools[i].name]); 66913 break; 66914 case 'image_select': 66915 $j('#_zap_tool_image_select_' + viewer_id).unbind('change', _tools[_self.tools[i].name]); 66916 break; 66917 case 'contact_sheet': 66918 $j('#_zap_tool_contact_sheet_' + viewer_id).unbind('click', _tools[_self.tools[i].name]); 66919 break; 66920 case 'nissl_only': 66921 $j('#_zap_tool_nissl_only_' + viewer_id).unbind('click', _tools[_self.tools[i].name]); 66922 break; 66923 case 'all_stains': 66924 $j('#_zap_tool_all_stains_' + viewer_id).unbind('click', _tools[_self.tools[i].name]); 66925 break; 66926 66927 case 'show_text': 66928 $j('#_zap_tool_show_text_' + viewer_id).unbind('click', _tools[_self.tools[i].name]); 66929 break; 66930 case 'show_color': 66931 $j('#_zap_tool_show_color_' + viewer_id).unbind('click', _tools[_self.tools[i].name]); 66932 break; 66933 case 'show_outline': 66934 $j('#_zap_tool_show_outline_' + viewer_id).unbind('click', _tools[_self.tools[i].name]); 66935 break; 66936 66937 case 'navigation': 66938 $j('#_zap_tool_navigation_' + viewer_id).unbind('click', _tools[_self.tools[i].name]); 66939 break; 66940 case 'blockface': 66941 $j('#_zap_tool_blockface_' + viewer_id).unbind('click', _tools[_self.tools[i].name]); 66942 break; 66943 case 'color_filters': 66944 $j('#_zap_tool_color_filters_'+ viewer_id).unbind('click', _tools[_self.tools[i].name]); 66945 break; 66946 66947 case 'contrast': 66948 _contrast_slider = $j('#_zap_tool_contrast_'+ viewer_id); 66949 _contrast_slider.slider({value:50}); 66950 _contrast_slider.unbind('slidechange', _tools[_self.tools[i].name]); 66951 break; 66952 case 'sync_check': 66953 _sync_checkbox.unbind('click', _tools[_self.tools[i].name]); 66954 break; 66955 default: 66956 } 66957 } 66958 } 66959 66960 function create_toolbar_controls() { 66961 // create the tool buttons 66962 var viewer_id = _the_viewer.id; 66963 var len = _self.tools.length; 66964 var html = ""; 66965 for (var i = 0; i < len; i++) { 66966 switch (_self.tools[i].name) { 66967 case 'siv': 66968 html += "<img id='_zap_tool_siv_" + viewer_id + "' title='Launch a high resolution viewer in a
66968new window' class='viewControlButton' src='/assets/showInSIV.png' />"; 66969 break; 66970 case 'info': 66971 html += "<img id='_zap_tool_info_" + viewer_id + "' title='See details for this gene and image series in a new browser window' class='viewControlButton' src='/assets/info.png' />"; 66972 break; 66973 case 'help': 66974 html += "<img id='_zap_tool_help_" + viewer_id + "' title='Help - How to use this viewer' class='viewControlButton' src='/assets/ZapViewerHelp.png' />"; 66975 break; 66976 case 'download': 66977 html += "<img id='_zap_tool_download_" + viewer_id + "' title='Download current image as a jpeg file' class='viewControlButton' src='/assets/download.png' />"; 66978 break; 66979 case 'sync': 66980 html += "<img id='_zap_tool_sync_" + viewer_id + "' title='Synchronize other viewers on this page to this image' class='viewControlButton' src='/assets/syncToMe2.png' />"; 66981 break; 66982 case 'legend': 66983 html += "<img id='_zap_tool_legend_" + viewer_id + "' title='See the Interactive Allen Reference Atlas in a new browser window' class='viewControlButton' src='/assets/key.png' />"; 66984 break; 66985 case 'close': 66986 html += "<img id='_zap_tool_close_" + viewer_id + "' title='Remove this viewer from the page' class='viewControlButton' src='/assets/exit.png' />"; 66987 break; 66988 case 'contact_sheet': 66989 html += "<img id='_zap_tool_contact_sheet_" + viewer_id + "' title='See all the images in a contact sheet layout' class='viewControlButton' src='/assets/thumbSets.png' />"; 66990 break; 66991 case 'nissl_only': 66992 html += "<img id='_zap_tool_nissl_only_" + viewer_id + "' title='Show only Nissl images from this specimen' class='viewControlButton toggle_button group_button' src='/assets/nisslOnly.png' />"; 66993 break; 66994 case 'all_stains': 66995 html += "<img id='_zap_tool_all_stains_" + viewer_id + "' title='Show all images from this specimen' class='viewControlButton toggle_button group_button' src='/assets/allStains.png' />"; 66996 break; 66997 66998 case 'show_text': 66999 html += "<img id='_zap_tool_show_text_" + viewer_id + "' title='Toggle text on/off' class='viewControlButton toggle_button' src='/assets/structureLabels.png' />"; 67000 break; 67001 case 'show_color': 67002 html += "<img id='_zap_tool_show_color_" + viewer_id + "' title='Toggle colors on/off' class='viewControlButton toggle_button' src='/assets/structureColors.png' />"; 67003 break; 67004 case 'show_outline': 67005 html += "<img id='_zap_tool_show_outline_" + viewer_id + "' title='Toggle outlines on/off' class='viewControlButton toggle_button' src='/assets/outline.png' />"; 67006 break; 67007 67008 case 'navigation': 67009 html += "<img id='_zap_tool_navigation_" + viewer_id + "' title='Show navigation images' class='viewControlButton toggle_button group_button' src='/assets/navigation.png' />"; 67010 break; 67011 case 'blockface': 67012 html += "<img id='_zap_tool_blockface_" + viewer_id + "' title='Show blockface images' class='viewControlButton toggle_button group_button' src='/assets/blockface.png' />"; 67013 break; 67014 case 'color_filters': 67015 html += "<img id='_zap_tool_color_filters_" + viewer_id + "' title='Adjust image filters' class='viewControlButton toggle_button group_button' src='/assets/filterSliders.png' />"; 67016 break; 67017 67018 case 'image_select': 67019 html += build_image_select(viewer_id, _initial_image_type); 67020 67021 break; 67022 67023 case 'sync_check': 67024 html += "<div class='zap_sync_check'>" 67025 html += "<input type='checkbox' name='" + (options.sync_check.name ? options.sync_check.name : 'sync_check') + " " + (options.sync_check.checked ? 'checked' : ''); 67026 html += " class='sync_checkbox' id='_zap_tool_sync_check_" + viewer_id + "'/>"; 67027 html += "<div class='zap_sync_check_label'>" + (options.sync_check.label ? options.sync_check.label : 'Sync') + "</div></div>"; 67028 break; 67029 case 'contrast': 67030 html += "<div id='_zap_tool_contrast_" + viewer_id + "' class='zap_contrast_slider' title='Adjust image contrast'></div>"; 67031 break; 67032 default: 67033 } 67034 } 67035 // add the tools to the viewer 67036 _the_viewer.setControls(html); 67037 67038 // edge case: if initial display image is Expression, hide the contrast slider tool if it is present in the DOM 67039 if (_initial_image_type == 'expression') { 67040 hide_contrast(true); 67041 } 67042 } 67043 67044 /** 67045 * Construct the HTML for the drop down image select menu, and setting the default selection 67046 * 67047 * @param viewer_id 67048 * @param default_selection value of default selection 67049 * @return {String} 67050 */ 67051 function build_image_select(viewer_id, default_selection) { 67052 var html = ""; 67053 67054 var len = _image_types.length; 67055 if (len > 0) { 67056 html = '<select class="viewControlSelect" id="_zap_tool_image_select_' + viewer_id + '">'; 67057 var select_value; 67058 var selected = ''; 67059 for (var i = 0; i < len; i++) { 67060 select_value = _image_types[i].toLowerCase(); 67061 if (select_value == default_selection) 67062 selected = ' selected="selected"';
67063 else 67064 selected = ''; 67065 html += ' <option value="' + select_value + '"' + selected + '>' + _image_types[i] + '</option>'; 67066 } 67067 html += '</select>'; 67068 } 67069 else { 67070 html += "<select class='viewControlSelect' id='_zap_tool_image_select_" + viewer_id + "'>"; 67071 html += " <option value='ish'>ISH</option>"; 67072 html += " <option value='expression'>Expression</option>"; 67073 html += " <option value='nissl'>Nissl</option>"; 67074 html += "</select>"; 67075 } 67076 67077 return(html); 67078 } 67079 67080 /** 67081 * Set an item as selected from the image type menu. This does not trigger a selection event. 67082 * 67083 * @param {string} image_type value of the menu item, which is simply the image type in lowercase 67084 */ 67085 function do_set_image_types_selection(image_type) { 67086 var type = image_type.toLowerCase(); 67087 $j('#_zap_tool_image_select_' + _the_viewer.id).val(type); 67088 } 67089 67090 /** 67091 * Sets the contents of the image types menu. The input arg consists of the display names of the menu, 67092 * the menu value is set to be the lowercased value of the display name. 67093 * 67094 * @param {Object[]} image_types 67095 */ 67096 function do_set_image_types_menu(image_types) { 67097 67098 var select_menu = $j("#_zap_tool_image_select_" + _the_viewer.id); 67099 if(!select_menu || !select_menu[0].options) 67100 return; 67101 67102 select_menu.children().remove().end(); 67103 for (var i = 0, len = image_types.length; i < len; ++i) { 67104 select_menu.append('<option selected value="' + sanitize_image_type(image_types[i]) + '">' + image_types[i] + '</option>'); 67105 } 67106 } 67107 67108 function sanitize_image_type(image_type) { 67109 return image_type.toLowerCase().replace(/\s+/g, "_"); 67110 } 67111 67112 function do_set_images(images) { 67113 if (!images) { 67114 show_image_error(); 67115 return; 67116 } 67117 67118 var target_thumbnail_size = $j('#' + _self.container_id).height() * 0.24; 67119 var len = images.length; 67120 var first_id = null; 67121 for (var i = 0; i < len; i++) { 67122 67123 // don't show failed images unless the caller has specified that we should in the options hash 67124 if ((images[i].failed == false || images[i].failed == 'false') || _include_failed) { 67125 if (!first_id) 67126 first_id = images[i].id; 67127 67128 // NOTE num_tiers should be a field of the image object 67129//TODO: this is broken because the tier_count field is not properly populated. This estimation should work for most 67130 var num_tiers = images[i].tier_count; 67131 67132 var num_tiers = 0; 67133 if (images[i].tier_count && images[i].tier_count > 0) 67134 num_tiers = images[i].tier_count; 67135 else { 67136 num_tiers = Math.ceil(((Math.log(Math.max(images[i].image_width, images[i].image_height) / 256.0) ) / Math.log(2))) + 1; 67137 } 67138 67139 // NOTE target_tier should be a configurable options. It is the tier 67140 // at which the thumbnails should be generated. 67141 // NOTE target_tier should be a configurable options. It is the tier 67142 // at which the thumbnails should be generated. Now being generated as the 67143 // smallest tier that produces an image with largest size not more than the target_thumbnail_size. 67144 // The target size is calculated to be 24% of the height of the container for this viewer. 67145 _strip_tier = find_strip_tier(images[i], target_thumbnail_size); 67146 var target_tier = _strip_tier; 67147 var scale = Math.pow(2, (num_tiers - 1) - _strip_tier); 67148 67149 var expression_image = images[i].alternate_images ? images[i].alternate_images.length ? images[i].alternate_images[0] : {path:''} : {path:''}; 67150 67151 images[i].ish_id = images[i].id; 67152 images[i].precalculated_path = 'true'; 67153 var path = (images[i].path && images[i].path.indexOf("&path=") >= 0) ? images[i].path : _self.services.image_service + "&path=" + images[i].path + "&zoom=" + target_tier + "&top=" + images[i].y + "&left=" + images[i].x + "&width="
67153+ images[i].width / scale + "&height=" + images[i].height / scale; 67154 images[i].path = path; 67155 images[i].expression_path = _self.services.image_service + "&path=" + expression_image.path + "&zoom=" + target_tier + "&top=" + images[i].y + "&left=" + images[i].x + "&width=" + images[i].width / scale + "&height=" + images[i].height / scale; 67156 images[i].src_key = 'path'; 67157 images[i].tier = _strip_tier; 67158 images[i].subimage_path = {top:images[i].y, left:images[i].x, width:images[i].width, height:images[i].height}; 67159 images[i].subimage_expression_path = {top:images[i].y, left:images[i].x, width:images[i].width, height:images[i].height}; 67160 67161 // for ISH zap viewers we assume that the one-and-only associate is a nissl 67162 if (images[i].associates && images[i].associates.length == 1) { 67163 var nissl = images[i].associates[0]; 67164 var nissl_strip_tier = find_strip_tier(nissl, target_thumbnail_size); 67165 var nissl_scale = Math.pow(2, (nissl.tier_count - 1) - nissl_strip_tier); 67166 images[i].nissl_id = nissl.id; 67167 images[i].nissl_path = _self.services.image_service + "&path=" + nissl.path + "&zoom=" + nissl_strip_tier + "&top=" + nissl.y + "&left=" + nissl.x + "&width=" + nissl.width / nissl_scale + "&height=" + nissl.height / nissl_scale; 67168 images[i].subimage_nissl_path = {top:nissl.y, left:nissl.x, width:nissl.width, height:nissl.height}; 67169 } 67170 67171 _the_viewer.addImage(images[i]); 67172 } 67173 } 67174 _the_viewer.select(first_id); 67175 } 67176 67177 67178 function find_strip_tier(image, target_size) { 67179 67180 // trying to find the lowest tier at which the image is >= 128 pixels wide/height 67181 var max = Math.max(parseInt(image.width), parseInt(image.height)); 67182 var target = target_size / ( max / Math.pow(2, (image.tier_count - 1))); 67183 var target_tier = Math.max(0, Math.ceil(Math.log(target) / Math.log(2))); 67184 67185 return(target_tier); 67186 } 67187 67188 function do_siv(e) { 67189 var image_pos = _the_viewer.getImagePosition(); 67190 var image_info = _the_viewer.getImageInfo(); 67191 var proceed = true; 67192 if (_self.on_before_siv) 67193 proceed = _self.on_before_siv(_self, {event_name:'before_siv', image_info:image_info, image_position:image_pos}); 67194 67195 if (!proceed) 67196 return; 67197 67198 if (_self.on_after_siv) 67199 _self.on_after_siv(_self, {event_name:'after_siv', image_info:image_info, image_position:image_pos}); 67200 } 67201 67202 function do_info(e) { 67203 var proceed = true; 67204 if (_self.on_before_info) 67205 proceed = _self.on_before_info(_self, {event_name:'before_info', value:null}); 67206 67207 if (!proceed) 67208 return; 67209 67210 if (_self.on_after_info) 67211 _self.on_after_info(_self, {event_name:'after_info', value:null}); 67212 } 67213 67214 function do_sync_check(e) { 67215 var proceed = true; 67216 if (_self.on_before_sync_check) 67217 proceed = _self.on_before_sync_check(_self, {event_name:'before_sync_check', value:null}); 67218 67219 if (!proceed) 67220 return; 67221 67222 if (_self.on_after_sync_check) 67223 _self.on_after_sync_check(_self, {event_name:'after_sync_check', value:null}); 67224 } 67225 67226 function do_help(e) { 67227 var proceed = true; 67228 if (_self.on_before_help) 67229 proceed = _self.on_before_help(_self, {event_name:'before_help', value:null}); 67230 67231 if (!proceed) 67232 return; 67233 67234 if (_self.on_after_help) 67235 _self.on_after_help(_self, {event_name:'after_help', value:null}); 67236 } 67237 67238 function do_download(e) { 67239 var proceed = true; 67240 if (_self.on_before_download) 67241 proceed = _self.on_before_download(_self, {event_name:'before_download', value:null}); 67242 67243 if (!proceed) 67244 return; 67245 67246 if (_self.on_after_download) 67247 _self.on_after_download(_self, {event_name:'after_download', value:null}); 67248 } 67249 67250 function do_nissl_only(e) { 67251 var proceed = true; 67252 if (_self.on_before_nissl_only) 67253 proceed = _self.on_before_nissl_only(_self, {event_name:'before_nissl_only', value:null}); 67254 67255 if (!proceed) 67256 return; 67257 67258 if (_self.on_after_nissl_only) 67259 _self.on_after_nissl_only(_self, {event_name:'after_nissl_only', value:null}); 67260 } 67261 67262 function do_all_stains(e) { 67263 var proceed = true; 67264 if (_self.on_before_all_stains) 67265 proceed = _self.on_before_all_stains(_self, {event_name:'before_all_stains', value:null}); 67266 67267 if (!proceed) 67268 return; 67269 67270 if (_self.on_after_all_stains) 67271 _self.on_after_all_stains(_self, {event_name:'after_all_stains', value:null}); 67272 } 67273 67274 function do_show_text(e) { 67275 var proceed = true; 67276 if (_self.on_before_show_text) 67277 proceed = _self.on_before_show_text(_self, {event_name:'before_show_text', value:null}); 67278 67279 if (!proceed) 67280 return; 67281 67282 if (_self.on_after_show_text) 67283 _self.on_after_show_text(_self, {event_name:'after_show_text', value:null}); 67284 } 67285 67286 function do_show_color(e) { 67287 var proceed = true; 67288 if (_self.on_before_show_color) 67289 proceed = _self.on_before_show_color(_self, {event_name:'before_show_color', value:null}); 67290 67291 if (!proceed) 67292 return; 67293 67294 if (_self.on_after_show_color) 67295 _self.on_after_show_color(_self, {event_name:'after_show_color', value:null}); 67296 } 67297 67298 function do_show_outline(e) { 67299 var proceed = true; 67300 if (_self.on_before_show_outline) 67301 proceed = _self.on_before_show_outline(_self, {event_name:'before_show_outline', value:null}); 67302 67303 if (!proceed) 67304 return; 67305 67306 if (_self.on_after_show_outline) 67307 _self.on_after_show_outline(_self, {event_name:'after_show_outline', value:null}); 67308 } 67309 67310 function do_navigation(e) { 67311 var proceed = true; 67312 if (_self.on_before_navigation) 67313 proceed = _self.on_before_navigation(_self, {event_name:'before_navigation', value:null}); 67314 67315 if (!proceed) 67316 return; 67317 67318 if (_self.on_after_navigation) 67319 _self.on_after_navigation(_self, {event_name:'after_navigation', value:null}); 67320 } 67321 67322 function do_blockface(e) { 67323 var proceed = true; 67324 if (_self.on_before_blockface) 67325 proceed = _self.on_before_blockface(_self, {event_name:'before_blockface', value:null}); 67326 67327 if (!proceed) 67328 return; 67329 67330 if (_self.on_after_blockface) 67331 _self.on_after_blockface(_self, {event_name:'after_blockface', value:null}); 67332 } 67333 67334 function do_color_filters(e) { 67335 67336 var proceed = true; 67337 if(_self.on_before_color_filters) 67338 proceed = _self.on_before_color_filters( _self, {event_name:'before_color_filters', value:null} ); 67339 67340 if(!proceed) 67341 return; 67342 67343 do_color_filter_click(_self, null); 67344 67345 if(_self.on_after_color_filters) 67346 _self.on_after_color_filters( _self, {event_name:'after_color_filters', value:null} ); 67347 } 67348 67349 function do_legend(e) { 67350 var image_info = _the_viewer.getImagePosition(); 67351 67352 var proceed = true; 67353 if (_self.on_before_legend) 67354 proceed = _self.on_before_legend(_self, {event_name:'before_legend', value:image_info}); 67355 67356 if (!proceed) 67357 return; 67358 67359 if (_self.on_after_legend) 67360 _self.on_after_legend(_self, {event_name:'after_legend', value:image_info}); 67361 } 67362 67363 function do_sync(e) { 67364 var image_info = _the_viewer.getImagePosition(); 67365 67366 var proceed = true; 67367 if (_self.on_before_sync) 67368 proceed = _self.on_before_sync(_self, {event_name:'before_sync', value:image_info}); 67369 67370 if (!proceed) 67371 return; 67372 67373 if (_self.on_after_sync) 67374 _self.on_after_sync(_self, {event_name:'after_sync', value:image_info}); 67375 } 67376 67377 function do_close(e) { 67378 var image_info = _the_viewer.getImagePosition(); 67379 67380 var proceed = true; 67381 if (_self.on_before_close) 67382 _self.on_before_close(_self, {event_name:'close', value:image_info}); 67383 67384 if (!proceed) 67385 return; 67386 67387 // what to do here? unlink the DOM elements? 67388 } 67389 67390 function manage_graphics_image_type_change() { 67391 67392 if (!_the_viewer.graphics_enabled) 67393 return; 67394 67395 // update the id field of the zap viewers images to point to the correct thing 67396 67397 // TODO: current display default always shows initial display image type in the imagestr
67397ip. 67398 // You don't want to alter the imagestrip's array of image.id values, because keyboard nav 67399 // uses the DOM <img> tag id attribute values to retrieve ids which it then matches against 67400 // the imagestrip's image[id] to get next/previous/first/last image objects. Should change 67401 // aizap gem's keyboard nav code to get away from using DOM values. 67402// var id_field = (_current_image_type == 'nissl') ? 'nissl_id' : 'ish_id'; 67403// var images = _the_viewer.strip.getImages(); 67404// for (var key in images) images[key].id = images[key][id_field]; 67405 67406// TODO: hack alert. This shouldn't be necessary but there seems to be a bug in the flash renderer that causes the last graphics doc to persist. This hides all the layers 67407 var group_list = _the_viewer.zapimg.graphics.getGroupList('json'); 67408 if (group_list && group_list.length) { 67409 var groups = eval(group_list); 67410 for (var i = 0; i < groups.length; i++) { 67411 _the_viewer.zapimg.graphics.setGroupVisibility(groups[i].name, false); 67412 } 67413 } 67414 } 67415 67416 /** 67417 * Event handler for image selection menu (class=viewControlSelect) 67418 * @param e 67419 */ 67420 function do_image_select(e) { 67421 67422 // the value of the select option after the change 67423 var value = $j(this).val(); 67424 var proceed = true; 67425 67426 if (_self.on_before_image_type_change) 67427 proceed = _self.on_before_image_type_change(_self, {event_name:'before_image_type_change', value:value}); 67428 67429 if (!proceed) 67430 return; 67431 67432 var new_image_type = value; 67433 var new_filter = _image_filters[new_image_type]; 67434 _the_viewer.setFilter(new_filter.filterName, new_filter.filterVals, _sync_strip_image_filter); 67435 67436 switch(new_image_type) { 67437 case 'ish': 67438 hide_contrast(false); 67439 _the_viewer.setSrcKey('path'); 67440 if (_sync_strip_image_type) 67441 _the_viewer.strip.setSrcKey('path'); 67442 break; 67443 case 'expression': 67444 hide_contrast(true); 67445 _the_viewer.setSrcKey('expression_path'); 67446 if (_sync_strip_image_type) { 67447 _the_viewer.strip.setSrcKey('expression_path'); 67448 // always apply the filter to thumbnails otherwise they appear grayscale 67449 _the_viewer.strip.setFilter(new_filter.filterName, new_filter.filterVals); 67450 } 67451 break; 67452 case 'nissl': 67453 hide_contrast(false); 67454 _the_viewer.setSrcKey('nissl_path'); 67455 if (_sync_strip_image_type) 67456 _the_viewer.strip.setSrcKey('nissl_path'); 67457 break; 67458 } 67459 67460 manage_graphics_image_type_change(); 67461 67462 _current_image_type = new_image_type; 67463 67464 if (_self.on_after_image_type_change) 67465 _self.on_after_image_type_change(_self, {event_name:'after_image_type_change', value:value}); 67466 67467 } 67468 67469 function do_contact_sheet(e) { 67470 var proceed = true; 67471 if (_self.on_before_contact_sheet) 67472 proceed = _self.on_before_contact_sheet(_self, {event_name:'before_contact_sheet', value:null}); 67473 67474 if (!proceed) 67475 return; 67476 67477 if (_self.on_after_contact_sheet) 67478 _self.on_after_contact_sheet(_self, {event_name:'after_contact_sheet', value:null}); 67479 } 67480 67481 /** 67482 * 67483 * @param {Object} e - jQuery event 67484 * @param {Object} ui - jquery ui 67485 */ 67486 function do_contrast(e, ui) { 67487 var val = ui.value / 100; 67488 67489 var proceed = true; 67490 if (_self.on_before_contrast) 67491 proceed = _self.on_before_contrast(_self, {event_name:'before_contrast', value:val}); 67492 67493 if (!proceed) 67494 return; 67495 67496 _the_viewer.setFilter('contrast', '0.5,' + (val - .0001).toFixed(4) + ',0,255,4', _sync_strip_image_filter); 67497 67498 // update local copy of the appropriate filter 67499 var f = _the_viewer.getFilter(); 67500 update_image_filters(f); 67501 67502 if (_self.on_after_contrast) 67503 _self.on_after_contrast(_self, {event_name:'after_contrast', value:val}); 67504 } 67505 67506 function hide_contrast(hide) { 67507 67508 if(hide) { 67509 $j('#_zap_tool_contrast_' + _the_viewer.id).hide(); 67510 $j('#_zap_tool_color_filters_' + _the_viewer.id).hide(); 67511 } 67512 else { 67513 $j('#_zap_tool_contrast_' + _the_viewer.id).show(); 67514 $j('#_zap_tool_color_filters_' + _the_viewer.id).show(); 67515 } 67516 } 67517 67518 /** 67519 * 67520 * @param {number} image_id 67521 * @param {number} center_x 67522 * @param {number} center_y 67523 * @param {number} tier_delta tier difference from displayed image tier 67524 * @param {Object} options 67525 * @param {number} [options.absolute_tier] (optional argument) tier to use, regardless of tier_delta or current display tier 67526 * @param {Object} options.image_selected - callback function 67527 */ 67528 function select_image_by_center(image_id, center_x, center_y, tier_delta, options) { 67529 67530 if (_positioning) 67531 return; 67532 67533 _positioning = true; 67534 // it takes some time for the selected image to load, 67535 // so wait for it to be available before trying to set the position. 67536 67537 // we're taking over the onImageChange event handler, so keep a handle 67538 // to any existing handler so it can be restored and called when we're done with it. 67539 var old_changer = _the_viewer.onImageChange; 67540 _the_viewer.onImageChange = do_queued_position; 67541 var tier_value = options.absolute_tier ? options.absolute_tier : 0; // use if defined, otherwise set to 0 67542 _position_queue = { 67543 tier_delta:tier_delta, 67544 x:center_x, 67545 y:center_y, 67546 tier:tier_value, 67547 old_handler:old_changer, 67548 callback: options.ready 67549 }; 67550 67551 _self.select_image(image_id, false); 67552 } 67553 67554 /** 67555 * 67556 * @param evt Prototype library callback arg 67557 */ 67558 function do_queued_position(evt) { 67559 67560 if (!_position_queue || !_positioning) 67561 return; 67562 67563 // what the user gave us was a tier_delta, so figure out what the new tier should be 67564 var image_info = _self.get_image_info(); 67565 if (_position_queue.tier >= 0 && _position_queue.tier_delta == 0) { 67566 // use absolute tier number, which is existing value of _position_queue.tier 67567 } 67568 else { 67569 // change tier number relative to current display tier 67570 _position_queue.tier = image_info.position.image.tier + _position_queue.tier_delta; 67571 } 67572 67573 // we are setting the center point of the image, while setPosition 67574 // expects left, top values. Calculate top,left that will put 67575 // our x,y in the center of the image. 67576 var top_left = get_top_left(_position_queue); 67577 67578 _the_viewer.setPosition('image', _position_queue.tier, parseFloat(top_left.x), parseFloat(top_left.y)); 67579 67580 // restore original handler 67581 if (_position_queue.old_handler) 67582 _the_viewer.onImageChange = _position_queue.old_handler; 67583 67584 // now call the old handler to finish remaining tasks 67585 if (_position_queue.old_handler) 67586 _the_viewer.onImageChange(evt); 67587 67588 // now call the ready callback 67589 if (_position_queue.callback) { 67590 _position_queue.callback(); 67591 } 67592 67593 _position_queue = null; 67594 _positioning = false;
67595 } 67596 67597 67598 /** 67599 * Find the zoomify tier number (0 to n) that will cause a rectangle to be maximally displayed in the view, 67600 * also described as 'fit-to-view'. 67601 * In our tier notation, 'n' corresponds to the tier of the full-resolution image. 67602 * Assume that the rectangle dimensions are larger than the view. 67603 * 67604 * @param tier_count total number of zoomify tiers in the current image 67605 * @param rect rectangle dimensions scaled to full resolution image. Hash has keys 'width', 'height' 67606 * @param view view dimensions scaled to full resolution image. Hash has keys 'width', 'height' 67607 * @return tier number 67608 */ 67609 function fit_rect_to_view(tier_count, rect, view) { 67610 var fit_tier; 67611 67612 var rw = rect.width; 67613 var rh = rect.height; 67614 var vw = view.width; 67615 var vh = view.height; 67616 67617 // calculate how many 2X reductions of the rectangle dimensions are needed 67618 // to fit within the view dimensions 67619 var width_reductions = Math.ceil((Math.log(rect.width / view.width)) / Math.log(2)); 67620 width_reductions = width_reductions < 0 ? 0 : width_reductions; 67621 var height_reductions = Math.ceil((Math.log(rect.height / view.height)) / Math.log(2)); 67622 height_reductions = height_reductions < 0 ? 0 : height_reductions; 67623 67624 // use the larger of the reductions in the width, height dimensions to ensure no cropping 67625 var reductions = (width_reductions - height_reductions) > 0 ? width_reductions : height_reductions; 67626 67627 fit_tier = tier_count - 1 - reductions; 67628 fit_tier = fit_tier < 0 ? 0 : fit_tier; 67629 67630 return fit_tier; 67631 } 67632 67633 function get_top_left(position) { 67634 67635 var image_info = _self.get_image_info(); 67636 var scale = Math.pow(2, (image_info.image.tier_count - 1) - position.tier); 67637 var view = $j('#zapimg' + _self.container_id); 67638 67639 var viewer_width = view.width(); 67640 var viewer_height = view.height(); 67641 67642 var top_left = { 67643 x:(position.x) - ((viewer_width * scale) / 2), 67644 y:(position.y) - ((viewer_height * scale) / 2) 67645 }; 67646 67647 return(top_left); 67648 } 67649 67650 /** 67651 * Get centerpoint of viewport image 67652 * 67653 * @returns {Object} hash with keys x, y in full resolution image coords 67654 */ 67655 function get_center_point() { 67656 67657 var image_info = _self.get_image_info(); 67658 var scale = Math.pow(2, (image_info.image.tier_count - 1) - image_info.position.image.tier); 67659 67660 var view = $j('#' + _self.container_id).find('.zapimg'); 67661 var viewer_width = view.width(); 67662 var viewer_height = view.height(); 67663 67664 var center = { 67665 x:((viewer_width * scale) / 2) + image_info.position.image.x, 67666 y:((viewer_height * scale) / 2) + image_info.position.image.y 67667 }; 67668 return(center); 67669 } 67670 67671 /** 67672 * Zoom viewport into the Atlas structure, selecting the image whose section that is the closest to the center 67673 * of the structure. This is the intent of the function, although to enact this operation, a series of 67674 * seemingly unrelated calls need to be made. 67675 * 67676 * @param atlas_id DevHuman atlas id 67677 * @param structure_id 67678 */ 67679 function zoom_atlas_structure(atlas_id, structure_id) { 67680 // get the bounding box data so we can select the correct image/structure 67681 get_structure_bounding_boxes(structure_id, atlas_id, receive_bounding_boxes); 67682 } 67683 67684 /** 67685 * Return bounding boxes for this atlas and the given structure_id. 67686 * 67687 * @param structure_id An ID from the set of structures associated with this atlas. 67688 * @param atlas_id ID of current atlas. 67689 * @param callback Called when the data is fetched successfully. The first argument 67690 * to this callback will be an array of BoundingBox records. 67691 * NOTE that if an error occurs during the request the model's 67692 * settings.on_error callback will be used. 67693 */ 67694 function get_structure_bounding_boxes(structure_id, atlas_id, callback) {
67695 _the_daos.data.fetch_structure_bounding_boxes(structure_id, atlas_id, { 67696 success_callback:callback, 67697 error_callback:on_error 67698 }); 67699 } 67700 67701 function receive_bounding_boxes(data) { 67702 if (!data) 67703 return; 67704 67705 var viewer_images = _self.get_images(); 67706 67707 // this set is ordered by section number, so get the box at the mid-point 67708 var mid_box = data[Math.floor(data.length / 2)]; 67709 67710 // find viewer image closest to mid-box, using section_number 67711 var closest_image = find_closest_image(viewer_images, mid_box.section_number); 67712 67713 var tier_count = closest_image.tier_count; 67714 var image_view = $j('#zapimg' + _self.container_id); 67715 var view = {width:image_view.width(), height:image_view.height()}; 67716 var target_tier = fit_rect_to_view(tier_count, mid_box, view); 67717 67718 // select and display the image in the viewer, centered over the structure bounding box and zoomed in 67719 var xCenter = mid_box.x + mid_box.width / 2; 67720 var yCenter = mid_box.y + mid_box.height / 2; 67721// console.log("tier_count = " + tier_count + ", target_tier = " + target_tier); 67722 67723 var tier_delta = 0; // used for relative tier changes from current image tier. Not applicable. 67724 select_image_by_center(closest_image.id, xCenter, yCenter, tier_delta, target_tier); 67725 } 67726 67727 /** 67728 * Find image record that is closest to the input section_number. 67729 * 67730 * @param images hash of hashes with values = image records 67731 * @param section_number target section number 67732 * @return {*} 67733 */ 67734 function find_closest_image(images, section_number) { 67735 var closest_image = undefined; 67736 var snum; 67737 67738 // create sorted array of section_numbers from the images hash 67739 var snum_array = []; 67740 for (var key in images) { 67741 if (images.hasOwnProperty(key)) { 67742 snum = images[key].section_number; 67743 snum_array.push(snum); 67744 } 67745 } 67746 snum_array.sort(function (a, b) { 67747 return a - b 67748 }); // numeric sort, ascending order 67749 67750 // find image section number closest to target section number 67751 var closest_section_number = find_closest_number(snum_array, section_number); 67752 67753 // now find image object that has the closest_section_number 67754 for (key in images) { 67755 if (images.hasOwnProperty(key)) { 67756 if (closest_section_number == images[key].section_number) { 67757 closest_image = images[key]; 67758 break; 67759 } 67760 } 67761 } 67762 67763 return closest_image; 67764 } 67765 67766 /** 67767 * Find number in array that is closest to the target number. 67768 * 67769 * @param numbers array of numbers, sorted in ascending order 67770 * @param target number to find in array 67771 * @return array value that is closest to target number 67772 */ 67773 function find_closest_number(numbers, target) { 67774 var closest_number; 67775 67776 var len = numbers.length; 67777 var previous_number = -1; 67778 var current_number; 67779 for (var i = 0; i < len; ++i) { 67780 current_number = numbers[i]; 67781 if (target == current_number) { 67782 // exact match 67783 closest_number = current_number; 67784 break; 67785 } 67786 else if (target < current_number && target > previous_number) { 67787 // edge case, target is less than all values in numbers array, so pick 1st array number 67788 if (previous_number == -1) { 67789 closest_number = current_number; 67790 break; 67791 } 67792 else { 67793 // target value is between array numbers, so default to the closest array number 67794 var hi_diff = current_number - target; 67795 var lo_diff = target - previous_number; 67796 if (hi_diff == lo_diff) { 67797 closest_number = current_number; // arbitrary decision 67798 } 67799 else if (hi_diff > lo_diff) { 67800 closest_number = previous_number; 67801 } 67802 else { 67803 closest_number = current_number; 67804 } 67805 break; 67806 } 67807 } 67808 previous_number = current_number; 67809 } 67810 67811 if (closest_number == undefined) { 67812 // target larger than any of the array numbers, pick the last number in array.
67813 closest_number = numbers[len - 1]; 67814 } 67815 67816 return closest_number; 67817 } 67818 67819 function on_error(msg) { 67820 alert(msg); 67821 } 67822 67823 function init() { 67824 build_viewer(); 67825 add_toolbar(); 67826 } 67827 67828 if (!_defer_init) 67829 init(); 67830 67831} 67832 67833var zap_functions = { 67834 67835 update_scalebar:function (evt) { 67836 67837 var viewer = evt.memo.viewer; 67838 var image = viewer.getImageInfo(); 67839 67840 if (!viewer || !image || !image.resolution) 67841 return; 67842 67843 var res = image.resolution < 0 ? 1 : image.resolution; 67844 viewer.setResolution(res); 67845 } 67846}; 67847"use strict"; 67848/** 67849 * Represent manual annotation data as a two axis (x-y) graphical chart, showing age on the x axis and intensity and 67850 * density on the y axis 67851 * 67852 * @param container 67853 * @param data 67854 * @param options 67855 */ 67856function manual_annotation_chart(container, data, options) { 67857 this.data = data; 67858 this.container = jQuery(container); 67859 this.settings = { 67860 color_map: { 67861 "Cannot Annotate": "#ddd", 67862 Undetected: "#ffff00", 67863 Low: "#ff8000", 67864 Medium: "#ff0000", 67865 High: "#800000" 67866 }, 67867 hotmetal_color_map: { 67868 "Cannot Annotate": "#000", 67869 Undetected: "#a50000", 67870 Low: "#ff5000", 67871 Medium: "#fffa00", 67872 High: "#fffffb" 67873 }, 67874 redtemp_color_map: { 67875 "Cannot Annotate": "#000", 67876 Undetected: "#590000", 67877 Low: "#b60b00", 67878 Medium: "#ff8400", 67879 High: "#fffdfb" 67880 }, 67881 byr_color_map: { 67882 "Cannot Annotate": "#ddd", 67883 Undetected: "#000000", 67884 Low: "#0000ff", 67885 Medium: "#ffff00", 67886 High: "#ff0000" 67887 }, 67888 blacktored_color_map: { 67889 "Cannot Annotate": "#ddd", 67890 Undetected: "#000000", 67891 Low: "#3F0000", 67892 Medium: "#7F0000", 67893 High: "#BD0000" 67894 }, 67895 height: '100px', // or any valid css height descriptor 67896 chart_type: 'heatmap', // or 'bars' 67897 age_label_position: 'bottom', // or 'top' or 'none' 67898 measure_label_position: 'left', // or 'right' or 'none' 67899 render_delegate: null 67900 }; 67901 jQuery.extend(true, this.settings, options); 67902 67903 this.set_type = function (new_type) { 67904 this.settings.chart_type = new_type; 67905 render_chart(); 67906 }; 67907 67908 var _self = this; 67909 var _structures = []; 67910 var _ages = []; 67911 var _table = null; 67912 var _browser = browser(); 67913 67914 67915 function build_scaffold() { 67916 var rows = _self.settings.include_pattern ? 6 : 5; 67917 var columns = (_structures.length * _ages.length) + 2; 67918 var age_cell_width = _structures.length; 67919 var header_row_cell_count = _ages.length + 2; 67920 var html = "<table class='anno_chart_table'>"; 67921 67922 // the first header row is for the top age labels 67923 html += "<tr class='anno_chart_top_header'>"; 67924 for (var i = 0; i < header_row_cell_count; i++) { 67925 if (i == 0 || i == header_row_cell_count - 1) { 67926 html += i == 0 ? "<th class='anno_measure_label_left'></th>" : "<th class='anno_measure_label_right'></th>"; 67927 } else { 67928 html += "<th colspan='" + age_cell_width + "'>"; 67929 html += _ages[i - 1]; 67930 html += "</th>"; 67931 } 67932 } 67933 html += "</tr>"; 67934 67935 // the next chunk of rows is for each of the data cells + 1 cell on either end for labels 67936 for (var r = 0; r < rows - 2; r++) { 67937 html += "<tr>"; 67938 for (var c = 0; c < columns; c++) { 67939 // this is a label cell 67940 if (c == 0 || c == columns - 1) { 67941 html += "<td class='anno_row_label"; 67942 switch (r) { 67943 case 0: 67944 html += c == 0 ? " anno_measure_label_left" : " anno_measure_label_right"; 67945 html += " anno_struct_strip_cell'>"; // adding a class name to the first row of interior cells 67946 break; 67947 case 1: 67948 html += c == 0 ? " anno_measure_label_left" : " anno_measure_label_right"; 67949 html += "'>I"; // intensity 67950 break; 67951 case 2: 67952 html += c == 0 ? " anno_measure_label_left" : " anno_measure_label_right"; 67953 html += "'>D"; 67954 break; 67955 case 3: 67956 html += c == 0 ? " anno_measure_label_left" : " anno_measure_label_right"; 67957 html += "'>P"; 67958 break; 67959 default: 67960 html += "'>"; 67961 } 67962 html += "</td>"; 67963 } 67964 else { // this is an interior cell that will hold a value 67965 html += "<td class='"; 67966 switch (r) { 67967 case 0: 67968 html += " anno_struct_strip_cell'>"; // adding a class name to the first row of interior cells 67969 break; 67970 default: 67971 html += "anno_cell'>"; 67972 } 67973 html += "</td>"; 67974 } 67975 } 67976 html += "</tr>"; 67977 } 67978 67979 // the last header row is for the bottom age labels 67980 html += "<tr class='anno_chart_bottom_header'>"; 67981 for (var i = 0; i < header_row_cell_count; i++) { 67982 if (i == 0 || i == header_row_cell_count - 1) { 67983 html += i == 0 ? "<th class='anno_measure_label_left'></th>" : "<th class='anno_measure_label_right'></th>"; 67984 } else { 67985 html += "<th foo='goo' colspan='" + age_cell_width + "'>"; 67986 if (_self.settings.age_label_link) { 67987 html += "<a class='age_label_link' href='' title='" + _self.settings.age_label_link_title + "'>"; 67988 html += _ages[i - 1]; 67989 html += "</a>"; 67990 } else { 67991 html += _ages[i - 1]; 67992 } 67993 html += "<input type='checkbox' class='_experiment_select_check' experiment.id=''/>"; 67994 html += "</th>"; 67995 } 67996 } 67997 html += "</tr>"; 67998 67999 _self.container.empty(); 68000 _table = jQuery(html).appendTo(_self.container); 68001 } 68002 68003 function set_chart_options() { 68004 if (_self.settings.measure_label_position == 'left') 68005 _table.find('.anno_measure_label_right').css({display: 'none'}); 68006 else if (_self.settings.measure_label_position == 'right') 68007 _table.find('.anno_measure_label_left').css({display: 'none'}); 68008 if (_self.settings.measure_label_position == 'none') { 68009 _table.find('.anno_measure_label_left').css({display: 'none'}); 68010 _table.find('.anno_measure_label_right').css({display: 'none'}); 68011 } 68012 68013 if (_self.settings.age_label_position == 'bottom') { 68014 _table.find('.anno_chart_top_header').css({display: 'none'}); 68015 } 68016 else if (_self.settings.age_label_position == 'top') 68017 _table.find('.anno_chart_bottom_header').css({display: 'none'}); 68018 if (_self.settings.age_label_position == 'none') { 68019 _table.find('.anno_chart_top_header').css({display: 'none'}); 68020 _table.find('.anno_chart_bottom_header').css({display: 'none'}); 68021 } 68022 } 68023 68024 function render_chart() { 68025 if (_self.settings.render_delegate) { 68026 _self.settings.render_delegate(data, _table); 68027 return; 68028 } 68029 68030 if (_self.settings.chart_type == 'bars') { 68031 _table.find('.anno_struct_strip_cell').css({display: 'none'}); 68032 render_bars(); 68033 } 68034 else 68035 render_heatmap(); 68036 } 68037 68038 function render_heatmap() { 68039 // color the header with the structure colors 68040 var cells = _table[0].rows[1].cells; 68041 var struct_count = _structures.length; 68042 for (var a = 0; a < _ages.length; a++) 68043 for (var s = 0; s < struct_count; s++) 68044 jQuery(cells[(a * struct_count) + 1 + s]).css({backgroundColor: '#' + _structures[s].color_hex_triplet}); 68045 68046 68047 // color the interior cells based on the color_map 68048 // The intensity row is index 2, density = 3
68049 var intensity_cells = _table[0].rows[2].cells; 68050 var density_cells = _table[0].rows[3].cells; 68051 for (a = 0; a < _ages.length; a++) { 68052 for (s = 0; s < struct_count; s++) { 68053 var anno = _self.data.data_sets[a].manual_annotations[s]; 68054 jQuery(intensity_cells[(a * struct_count) + 1 + s]).css({backgroundColor: _self.settings.color_map[anno.intensity_call]}); 68055 jQuery(density_cells[(a * struct_count) + 1 + s]).css({backgroundColor: _self.settings.color_map[anno.density_call]}); 68056 } 68057 } 68058 68059 // add hrefs to the age labels 68060 if (_self.settings.age_label_link) { 68061 var label_row = _self.settings.include_pattern ? 5 : 4; 68062 var cells = _table[0].rows[label_row].cells; 68063 68064 for (var a = 0; a < _ages.length; a++) { 68065 68066 var anno = _self.data.data_sets[a]; 68067 jQuery(cells[a + 1]) 68068 .find('.age_label_link') 68069 .attr('href', (_self.settings.age_label_link + anno.id)); 68070 68071 jQuery(cells[a + 1]) 68072 .find('._experiment_select_check') 68073 .attr('experiment.id', anno.id); 68074 } 68075 } 68076 68077 if (_self.settings.include_pattern) 68078 render_pattern(); 68079 } 68080 68081 68082 function render_bars() { 68083 68084 // color the interior cells based on the color_map 68085 // The intensity row is index 2, density = 3 68086 var intensity_cells = _table[0].rows[2].cells; 68087 var density_cells = _table[0].rows[3].cells; 68088 var struct_count = _structures.length; 68089 for (var a = 0; a < _ages.length; a++) { 68090 for (var s = 0; s < struct_count; s++) { 68091 var anno = _self.data.data_sets[a].manual_annotations[s]; 68092 jQuery(intensity_cells[(a * struct_count) + 1 + s]).css({backgroundColor: _self.settings.color_map[anno.intensity_call]}); 68093 jQuery(density_cells[(a * struct_count) + 1 + s]).css({backgroundColor: _self.settings.color_map[anno.density_call]}); 68094 } 68095 } 68096 68097 render_pattern(true); 68098 } 68099 68100 function new_bar(anno) { 68101 // apparently this has not been implemented 68102 } 68103 68104 function render_pattern(structure_colored) { 68105 // for the patter call we need to represent one of 68106 // Undetected, Regional, Gradient or Full. 68107 // do these by 68108 // Undetected: background is the same color as density Undetected. 68109 // Regional: Undetected background with two regions at High color 68110 // Gradient: css gradient from Undetected to High 68111 // Full: High color. 68112 var pattern_cells = _table[0].rows[4].cells; 68113 var cell = jQuery(pattern_cells[1]); 68114 var height = (cell.height() / 2); 68115 var width = (cell.width()); 68116 68117 var struct_count = _structures.length; 68118 for (var a = 0; a < _ages.length; a++) { 68119 for (var s = 0; s < struct_count; s++) { 68120 var anno = _self.data.data_sets[a].manual_annotations[s]; 68121 var style = {}; 68122 68123 switch (anno.pattern_call) { 68124 case "Undetected": 68125 style.backgroundColor = _self.settings.color_map["Undetected"]; 68126 break; 68127 case "Regional": 68128 style.backgroundColor = _self.settings.color_map["Undetected"]; 68129 // build a couple of little boxes and put them in the cell 68130 cell = jQuery(pattern_cells[(a * struct_count) + 1 + s]); 68131 var html = "<div class='region_wrapper'><div class='anno_region_box' "; 68132 html += "style='position:absolute;top:0;left:0;"; 68133 html += "background-color:" + _self.settings.color_map["High"] + ";"; 68134 html += "width:" + width + "px;"; 68135 html += "height:" + height + "px;display:inline-block'></div>"; 68136 68137 html += "<div class='anno_region_box' "; 68138 html += "style='position:absolute;top:" + height + "px;left:" + width + "px;"; 68139 html += "background-color:" + _self.settings.color_map["High"] + ";";
68140 html += "width:" + width + "px;"; 68141 html += "height:" + height + "px;display:inline-block'></div></div>"; 68142 68143 cell.append(html); 68144 break; 68145 case "Gradient": 68146 var start_color = _self.settings.color_map["Undetected"]; 68147 var stop_color = _self.settings.color_map["High"]; 68148 //TODO: this is browser specific. I don't know of any other way to do this: 68149 switch (_browser) { 68150 case "WebKit": 68151 style.background = "-webkit-linear-gradient(left top, " + start_color + ", " + stop_color + ")"; 68152 break; 68153 case "Firefox": 68154 style.background = "linear-gradient(to right bottom, " + start_color + ", " + stop_color + ")"; 68155 break; 68156 case "MSIE": 68157 style.filter = "progid:DXImageTransform.Microsoft.gradient(startColorstr='" + start_color + "', endColorstr='" + stop_color + "')"; 68158 break; 68159 default: 68160 style.backgroundColor = start_color; 68161 } 68162 break; 68163 case "Full": 68164 style.backgroundColor = _self.settings.color_map["High"]; 68165 break; 68166 default: 68167 style.backgroundColor = _self.settings.color_map["Undetected"]; 68168 } 68169 jQuery(pattern_cells[(a * struct_count) + 1 + s]).css(style); 68170 } 68171 } 68172 68173 } 68174 68175 68176 function browser() { 68177 var ua = navigator.userAgent.toString(); 68178 68179 if (/WebKit/g.exec(ua)) 68180 return('WebKit'); 68181 68182 if (/Firefox/g.exec(ua)) 68183 return('Firefox'); 68184 68185 if (/MSIE/g.exec(ua)) 68186 return('MSIE'); 68187 68188 return('Unknown'); 68189 } 68190 68191 function build_chart() { 68192 build_scaffold(); 68193 set_chart_options(); 68194 render_chart(); 68195 } 68196 68197 function munge_data() { 68198 var sample = _self.data.data_sets; 68199 if (!sample) 68200 return(false); 68201 68202 var age_count = sample.length; 68203 68204 for (var i = 0; i < age_count; i++) { 68205 _ages[i] = sample[i].specimen.donor.age.name; 68206 } 68207 68208 sample = sample[0].manual_annotations; 68209 var structure_count = sample.length; 68210 for (i = 0; i < structure_count; i++) { 68211 _structures[i] = sample[i].structure; 68212 } 68213 68214 return(true); 68215 } 68216 68217 function set_color_map() { 68218 try { 68219 var url = window.location.href; 68220 var qstring = url.split('?')[1]; 68221 var pairs = qstring.split('&'); 68222 var params = {}; 68223 for (var i = 0; i < pairs.length; i++) { 68224 68225 var pair_vals = pairs[i].split('='); 68226 if (pair_vals && pair_vals.length == 2) 68227 params[pair_vals[0]] = pair_vals[1]; 68228 } 68229 68230 if (params['colormap']) { 68231 if (_self.settings[params['colormap'] + '_color_map']) { 68232 _self.settings.color_map = _self.settings[params['colormap'] + '_color_map']; 68233 } 68234 } 68235 } catch (e) { 68236 trace("failed parsing debug options: " + e); 68237 } 68238 68239 } 68240 68241 function init() { 68242 if (munge_data()) { 68243 build_chart(); 68244 } 68245 } 68246 68247 init(); 68248} 68249; 68250/** 68251 * @file multi_column_sort_widget.js 68252 * @class MultiColumnSortWidget 68253 * 68254 * This widget provides a UI which allows a user to specify the column sort order for one or more columns in a 68255 * SlickGrid table. The user defines the content of a single menu, and how many duplicate copies of the menu are to be 68256 * displayed in a horizontal row. Each menu is accompanied by a checkbox that is used to define sorting 68257 * direction -- if checked then the sort is ascending order, otherwise it is descending order. Requires use of jQuery 68258 * javascript library. 68259 * 68260 * When the widget's 'Sort' button is pressed, the column sort info is collected from the menus and checkboxes, and 68261 * delivered to a callback function defined by the user. It is the user's responsibility to perform the sorting. 68262 * 68263 * Assumptions 68264 * - menus are always defined so that the 1st item is blank (empty string) 68265 * 68266 * You can CSS style this widget based on the <div> container class, 'mcsort', <select> class 'mcsort_menu', 68267 * <button> class 'mcsort_button' 68268 * 68269 * Example of menu configuration 68270 * ----------------------------- 68271 * Usually the menu configuration is read out of the public/application.json file. Here is a short example, defining 68272 * the menu (notice the 1st item is blank), the initial configuration of each menu, and the number of menus to display. 68273 * The menu items are usually comprised of all of the sortable columns in the SlickGrid table. 68274 * As shown below, the initial configuration of the menus is set to the blank item. However, if the SlickGrid table 68275 * is initially displayed with a particular column sort order, it may be useful to preconfigure the menus to match that 68276 * sort order, giving the user some visual feedback. 68277 * You can have define more initial configurations than there are number of menus, any extra configurations will be ignored. 68278 * 68279 * "multi_column_sort_widget":{ 68280 * "menu":[ // same menu contents used for each menu that is created 68281 * {"name":"", "value":""}, 68282 * {"name":"Experiment", "value":"data_sets.id"}, 68283 * {"name":"Gene Symbol", "value":"genes.acronym"},
68284 * {"name":"Tissue Location", "value":"structures.name"}, 68285 * {"name":"Age", "value":"ages.embryonic,ages.days"} 68286 * ], 68287 * "menu_configurations":[ // the 'asc' arg defines the check/uncheck state of the sort order checkbox 68288 * {"name":"", "value":"", "asc":false}, 68289 * {"name":"", "value":"", "asc":false}, 68290 * {"name":"", "value":"", "asc":false} 68291 * ], 68292 * "num_menus":3 // number of menus to create and display 68293 * } 68294 * 68295 * Example of integration with app code, SlickGrid 68296 * ----------------------------------------------- 68297 * There is usually a javascript view associated with the search results. This is the file where you instantiate an 68298 * instance of MultiColumnSortWidget: 68299 * function build_mcsort() { 68300 * var menu = _self.settings.mcsort_config.menu; 68301 * var configs = _self.settings.mcsort_config.menu_configurations; 68302 * var num_menus = _self.settings.mcsort_config.num_menus; 68303 * var parent = _self.settings.mcsort_parent_id; 68304 * _mcsort = new MultiColumnSortWidget(parent, menu, configs, num_menus, do_mcsort); // var defined elsewhere 68305 * } 68306 * 68307 * Also found in this view are two callbacks, the first used by SlickGrid's single column sort, and the second by 68308 * MultiColumnSortWidget's 'Sort' button: 68309 * 68310 * function do_sort(sort_column, sort_asc) { 68311 * _self.model.sort(sort_column, sort_asc); 68312 * 68313 * if (_mcsort) { 68314 * // update multicolumn sort UI to display single column sort selection; UI reflects user sort action on SlickGrid 68315 * _mcsort.setSingleMenuConfiguration(sort_column.name, sort_asc); 68316 * } 68317 * } 68318 * 68319 * function do_mcsort(sort_data) { 68320 * _self.model.mcsort(sort_data); // SlickGrid model function that performs the actual multicolumnsort 68321 * } 68322 * 68323 * Finally, there is a cookie-cutter model file used by SlickGrid. You need to add a multicolumn sort function here: 68324 * 68325 * this.mcsort = function (sort_data) { 68326 * var sort_order = ""; 68327 * for (var i = 0; i < sort_data.length; ++i) { 68328 * sort_order += sort_data[i].value + (sort_data[i].asc ? '$asc' : '$desc') + ','; 68329 * } 68330 * _search_options.order = sort_order.substring(0, sort_order.length - 1); // remove trailing ',' 68331 * do_gene_search(_search_term, _search_options, 'sort'); // this model's search function. Yours will be different. 68332 * } 68333 * 68334 * @param container either a CSS id or a jQuery object 68335 * @param {array} menuItems define each item present in a menu 68336 * @param {array} menuConfiguration define state of each menu (default selection) and state of menu's asc checkbox 68337 * @param {number} numMenus number of menus to create 68338 * @param {object} sortCallback callback function that will perform the actual sort process 68339 */ 68340 68341function MultiColumnSortWidget(parentContainer, menuItems, menuConfigurations, numMenus, sortCallback) { 68342 var _mcsortIdFragment = "_mcsort" 68343 var _menuIdFragment = "_menu_"; // use to distinguish GUI element ids 68344 var _cbIdFragment = "_cb_"; // use to distinguish GUI element ids 68345 var _buttonFragment = "_button"; 68346 68347 var _mcsortClass = "mcsort"; 68348 var _menuClass = "mcsort_menu"; 68349 var _buttonClass = "mcsort_button"; 68350 68351 var _self = this; 68352 var _parentContainer = (parentContainer instanceof jQuery) ? parentContainer : jQuery('#' + parentContainer); 68353 var _mcsortId = _parentContainer.attr('id') + _mcsortIdFragment; 68354 var _buttonId = _mcsortId + _buttonFragment; 68355 var _menuItems = menuItems; 68356 var _menuConfigs = menuConfigurations; 68357 var _numMenus = numMenus; 68358 var _callback = sortCallback; 68359 var _mcsortContainer; 68360 var _sortButton; 68361 68362 // public 68363 /** 68364 * 68365 * @param items array of menu item hashes, name = display name of menu item, value = selection value for the item 68366 * @param configs array of menu configurations for each menu, specifies default selection and checkbox state. 68367 */ 68368 this.createUIComponents = function (items, configs, numMenus) { 68369 var description = 'Sort by: '; 68370 var menuDesc = '<select class="' + _menuClass + '">'; 68371 68372 // don't create more menus that there are menu items 68373 var numMenuItems = items.length - 1; // don't count the blank menu item 68374 if (numMenus > numMenuItems) { 68375 numMenus = numMenuItems; 68376 } 68377 68378 // construct HTML string that constructs a single menu 68379 for (var i = 0; i < items.length; ++i) { 68380 menuDesc += '<option value="' + items[i].value + '">' + items[i].name + '</option>'; 68381 } 68382 menuDesc += '</select>'; 68383 68384 // construct HTML string that constructs a single checkbox 68385 var cbDesc = '<input type="checkbox" value="asc" checked="true" title="Sort column in ascending order if checked, otherwise sort in descending order">'; 68386 68387 // insert text description that appears to far left of row of menus 68388 _mcsortContainer.text(description); 68389
68390 // create set of menuUnits (a menu and a checkbox), adding a unique CSS id to the <select> and <input> tags, 68391 // and inserting menuUnit into DOM in the mcsort container 68392 var menuId; 68393 var cbId; 68394 var menuElement; 68395 var cbElement; 68396 68397 // create each menu and its checkbox using a default configuration, and inserting it into the DOM 68398 for (i = 0; i < numMenus; ++i) { 68399 menuId = _mcsortId + _menuIdFragment + i; 68400 cbId = _mcsortId + _cbIdFragment + i; 68401 menuElement = jQuery(menuDesc).attr('id', menuId); 68402 cbElement = jQuery(cbDesc).attr('id', cbId); 68403 cbElement.after('asc'); 68404 _mcsortContainer.append(menuElement); 68405 _mcsortContainer.append(cbElement); 68406 cbElement.after('asc'); 68407 68408 } 68409 68410 // now configure the state of each menu + checkbox pair 68411 _self.setMenuConfigurations(configs); 68412 68413 // lastly, add a pushbutton for 'Sort' and attach it's click handler 68414 _sortButton = jQuery('<button type="button" id="' + _buttonId + '" class="' + _buttonClass + '">Sort</button>'); 68415 _mcsortContainer.append(_sortButton); 68416 _sortButton.click(sortButtonPressed); 68417 } 68418 68419 /** 68420 * Configure the current selection of each menu, and the checked/unchecked 68421 * state of its checkbox. Configuration proceeds for the menus from left to right, 68422 * and if there are insufficient configurations for each menu, the remaining menus 68423 * will use a default configuration, where the 1st menu item is selected, and the 68424 * checkbox is checked (selected). 68425 * 68426 * Assumes menus have already been created and inserted into the DOM 68427 * 68428 * @param configs Array of hashes, {name:<menu item name>, value:<menu item value>, asc:<true or false} 68429 */ 68430 this.setMenuConfigurations = function (configs) { 68431 // configure the state of each menu + checkbox pair 68432 var menuId; 68433 var cbId; 68434 var itemValue; 68435 var isChecked; 68436 68437 var numConfigs = (configs.length > _numMenus) ? _numMenus : configs.length; 68438 for (var i = 0; i < numConfigs; ++i) { 68439 itemValue = configs[i].value; 68440 isChecked = configs[i].asc; 68441 menuId = _mcsortId + _menuIdFragment + i; 68442 cbId = _mcsortId + _cbIdFragment + i; 68443 jQuery('#' + menuId).val(itemValue); // select menu item based on its value 68444 jQuery('#' + cbId).prop('checked', isChecked); 68445 } 68446 68447 // remaining menus that do not have configuration info are set to default state of 1st menu item is selected, 68448 // checkbox is checked. 68449 if (configs.length < _numMenus) { 68450 for (i = configs.length; i < _numMenus; ++i) { 68451 itemValue = _menuItems[0].value; // use value of 1st menu item, it should normally be "" 68452 isChecked = true; // always set to checked 68453 menuId = _mcsortId + _menuIdFragment + i; 68454 cbId = _mcsortId + _cbIdFragment + i; 68455 jQuery('#' + menuId).val(itemValue); 68456 jQuery('#' + cbId).prop('checked', isChecked); 68457 } 68458 } 68459 } 68460 68461 /** 68462 * When the user performs a single column sort on a SlickGrid column, this function can also be called to 68463 * have the MultiColumnSortWidget mirror the column sort choice visually, including column name and sort direction. 68464 * 68465 * Configures the state of the first menu and checkbox on the left to match the input arguments. 68466 * The remainder of the menus are set to the default state of blank menu item (first item of each menu) 68467 * and checked checkbox. 68468 * If the itemName is invalid, the first menu item in a menu is selected, and the checkbox is checked. 68469 * 68470 * @param itemName menu item that to be selected 68471 * @asc {boolean} selection state of 'asc' checkbox, true = checked, false = unchecked 68472 */ 68473 this.setSingleMenuConfiguration = function (itemName, asc) { 68474 var itemValue; 68475 for (var i = 0; i < _menuItems.length; ++i) { 68476 if (_menuItems[i].name == itemName) { 68477 itemValue = _menuItems[i].value; 68478 break; 68479 } 68480 } 68481 68482 if (itemValue == undefined) { 68483 itemValue = _menuItems[0].value; 68484 itemName = _menuItems[0].name; 68485 asc = true; 68486 } 68487 68488 var configs = [ 68489 {"name":itemName, "value":itemValue, "asc":asc} 68490 ]; 68491 _self.setMenuConfigurations(configs); 68492 68493 } 68494 68495 this.setSortCallback = function (callback) { 68496 _callback = callback; 68497 } 68498 68499 this.saveStates = function () { 68500 // TBD 68501 } 68502 68503 this.restoreStates = function () { 68504 // TBD 68505 } 68506 68507 // privileged/private 68508 68509 /** 68510 * Creates a mcsort div container. If a preexisting container is in the DOM,
68511 * it is removed and re-created, then positioned as the first child element of the parent container. 68512 * 68513 * @return jQuery container div with css id value of _mcsortId 68514 */ 68515 function createContainer() { 68516 var container; 68517 68518 container = jQuery('#' + _mcsortId); 68519 if (container.length != 0) { 68520 // remove existing container and its children from the DOM 68521 container.remove(); 68522 } 68523 68524 // create new, insert as 1st child of parent 68525 container = jQuery('<div id="' + _mcsortId + '" class="' + _mcsortClass + '"></div>'); 68526 _parentContainer.prepend(container); 68527 68528 return container; 68529 } 68530 68531 /** 68532 * Called when the Sort button is pressed. 68533 * Parses values from all the menus, updates the UI, then invokes the external callback 68534 * to pass back the final menu selections in an array of hashes. The array order corresponds the column sort 68535 * order, and the contents of the array hash. 68536 */ 68537 function sortButtonPressed() { 68538 // parse out all menu selections and checkbox states, removing duplicates and ignoring 'blank' selections 68539 var selections = parseMenus(); 68540 68541 // update visual state of all the menus based on the parsed menu selections 68542 _self.setMenuConfigurations(selections); 68543 68544 // make callback to deliver the sort data 68545 _callback(selections); 68546 } 68547 68548 /** 68549 * Menus are parsed from left to right. 68550 * Rule is: first one wins. This means that is there is a duplicate menu selection encountered, the original and 68551 * its checkbox state are used, and the duplicate is discarded. 68552 * Any menus with blank selections are ignored, along with its accompanying checkbox state. 68553 * @return array of hashes, each hash has keys name, value, asc 68554 */ 68555 function parseMenus() { 68556 var selections = []; 68557 var usedMenuVals = []; 68558 var menuId, cbId; 68559 for (var i = 0; i < _numMenus; ++i) { 68560 menuId = _mcsortId + _menuIdFragment + i; 68561 cbId = _mcsortId + _cbIdFragment + i; 68562 var menu = jQuery('#' + menuId); 68563 var cb = jQuery('#' + cbId); 68564 var value = menu.val(); 68565 // pass over any menus with values already used, or any values that are blank (including values comprised of space
68565s) 68566 if (jQuery.inArray(value, usedMenuVals) != -1 || value.replace(/ /g, '') == '') { 68567 continue; 68568 } 68569 else { 68570 usedMenuVals.push(value); 68571 var h = {"name":jQuery('#' + menuId + ' option:selected').text(), "value":value, "asc":cb.prop("checked")}; 68572 selections.push(h); 68573 } 68574 } 68575 68576 return selections; 68577 } 68578 68579 function init() { 68580 _mcsortContainer = createContainer(); 68581 _self.createUIComponents(_menuItems, _menuConfigs, _numMenus); 68582 } 68583 68584 init(); 68585} 68586; 68587/** 68588 * Accordions with top level gene categories, enclosed content consisting of gene names. 68589 * Note: Accordion API must use jquery ui v1.10 68590 * 68591 * jQuery accordion terminology: 68592 * header - the text that always visible which you click on to show/hide content below the header 68593 * panel - the content that is shown/hidden below an accordion header 68594 * 68595 * @param container 68596 * @param daos 68597 * @param urls 68598 * @param opts 68599 * @constructor 68600 */ 68601 68602AIBS.UI.StudyWidget = function(container, daos, urls, opts) { 68603 68604 /** @private */ 68605 this.dao_ = daos.data; 68606 68607 /** @private */ 68608 this.path_ = urls.search; 68609 68610 /** 68611 * opts may provide a restricted search type (one of 'autism', 'subcortex', 68612 * 'cortex' or 'schizophrenia'.) If not, use 'gene' search type. 68613 * @private 68614 */ 68615 this.searchType_ = opts['mode'] || 'gene'; 68616 68617 /** @private */ 68618 this.opts_ = { 68619 min_font: 9, // needed? 68620 max_font: 34, // needed? 68621 class_names: '', // needed? 68622 header: true // needed? 68623 }; 68624 jQuery.extend(true, this.opts_, opts); 68625 68626 // begin wrapper 68627 var html = '<div class="studywidget ' + this.opts_.class_names + '">'; 68628 68629 // add one box per category 68630 AIBS.array.forEach(opts['gene_categories'], function(cat) { 68631 // category bar 68632 html += 68633 '<div class="ui-widget-header"><div>' + cat['name'] + '</div></div>'; 68634 68635 html += '<div class="studywidget_accordion">'; // begin accordion 68636 68637 // add one accordion section per classification 68638 AIBS.array.forEach(cat['classifications'], function(cl) { 68639 html += 68640 '<h3><a href="#">' + cl['name'] + '</a></h3>' + 68641 '<div id="gc' + cl['id'] + '">' + // id contains classification id 68642 '</div>'; // "contents", gene items to be added on demand 68643 }, this); 68644 68645 html += '</div>'; // end accordion 68646 68647 }, this); 68648 68649 html += '</div>'; // end wrapper 68650 68651 container[0].innerHTML = html; 68652 68653 // apply jQuery accordion plugin when document is entered 68654 jQuery(jQuery.proxy(function() { 68655 jQuery(".studywidget_accordion").accordion({ 68656 heightStyle: "content", 68657 collapsible: true, 68658 header: "h3", 68659 active: false, // no initial expansion, needed to trigger ajax 68660 beforeActivate: jQuery.proxy(function(jevt, ui) { 68661// var newContent = ui.newContent; 68662// var collapsing = newContent.length == 0; 68663// if (!collapsing) { 68664 if (ui.newHeader.length > 0 && ui.newPanel.length > 0) { 68665 this.reqGeneLinks_(ui.newHeader); 68666 }}, this) 68667 }); 68668 }, this)); 68669}; 68670 68671 68672/** 68673 * Populate contents of accordion via RMA query. 68674 * If already populated, skip the query. 68675 * 68676 * @param {!Element} el An accordion header element (the thing you click on) 68677 */ 68678AIBS.UI.StudyWidget.prototype.reqGeneLinks_ = function(el) { 68679 var id = el.context.nextElementSibling.id; // accordion panel element, will have a child element consisting of accordion content links 68680 var classificationId = parseInt(AIBS.string.stripNonNumerals(id)); 68681 if (!jQuery.data(el[0], 'genesLoaded')) { 68682 var opts = {}; 68683 68684 opts['error_callback'] = function(o, status, xhdr) { 68685 AIBS.dbg.error('StudyWidget#reqGeneLinks_/error_callback: ' + status); 68686 }; 68687 68688 opts['success_callback'] = jQuery.proxy(function(response_data, status, xhdr) { 68689 var html = '<div><table>' + 68690 '<colgroup><col class=col1><col></colgroup>'; 68691 68692 var geneArr = response_data[0].genes; 68693 geneArr.sort(function(geneA, geneB) { // RMA sort unreliable 68694 return AIBS.array.defaultCompare(geneA['acronym'], geneB['acronym']); 68695 }); 68696 68697 AIBS.array.forEach(geneArr, function(gene) { 68698 var acronym = gene['acronym']; 68699 var name = gene['name']; 68700 html += '<tr><td><a href="' + this.path_ + 68701 '?search_term=' + acronym + 68702 '&search_type=' + this.searchType_ + 68703 '&exact_match=true' + 68704 '">' + acronym + '</a></td><td>' + name + '</td></tr>'; 68705 }, this); 68706 html += '</table></div>'; 68707 jQuery('#' + id).append(html); // append html to the child <div> below the <h3> 68708 jQuery(el[0]).data('genesLoaded', true); // attach named boolean data to the <h3> element 68709 }, this); 68710 68711 this.dao_.geneNamesFromClassificationId(classificationId, opts); 68712 } 68713}; 68714"use strict";
68715// this depends on tag_container.js 68716 68717function tag_cloud(container, daos, urls, options) { 68718 68719 this.container = container; 68720 this.data = options.gene_classifications; 68721 this.urls = urls; 68722 68723 this.settings = { 68724 min_font:9, 68725 max_font:34, 68726 class_names:'', 68727 header:true, 68728 title:'Browse by Gene Classification' 68729 }; 68730 $j.extend(true, this.settings, options); 68731 68732 this.set_title = function(new_title) { 68733 if(this.settings.header) 68734 _cloud_header.innerHTML = new_title; 68735 } 68736 68737 68738 var _self = this; 68739 var _cloud_header = null; 68740 var _cloud_container = null; 68741 68742 68743 function set_font_size(tags) { 68744 68745 var len = tags.length; 68746 68747 var min = 99999; 68748 var max = 0; 68749 for(var i = 0; i < len; i++) { 68750 min = Math.min(min, tags[i].value); 68751 max = Math.max(max, tags[i].value); 68752 } 68753 var range = max - min; 68754 var font_range = _self.settings.max_font - _self.settings.min_font; 68755 68756 var scale = font_range / Math.sqrt(range); 68757 68758 for(i = 0; i < len; i++) { 68759 //var size = (tags[i].value/max) * font_range; 68760 var size = _self.settings.min_font + (Math.sqrt((tags[i].value) - min) * scale); 68761 var limited_size = Math.ceil( Math.min( _self.settings.max_font, Math.max(_self.settings.min_font, size))); 68762 tags[i].font_size = limited_size + 'px'; 68763 } 68764 return(tags); 68765 } 68766 68767 function cloud_renderer(tagger) { 68768 var tags = tagger.get_tags(); 68769 var tag_container = tagger.container; 68770 var len = tags.length; 68771 68772 tags = set_font_size(tags); 68773 68774 var tag_html = ""; 68775 for(var i = 0; i < len; i++) { 68776 var href = _self.urls.search + '?search_type=gene_classification&search_term=' + tags[i].classification_id; 68777 tag_html += "<div class='cloud_tag'><a class='cloud_link' href='" + href + "' style='font-size:" + tags[i].font_size + "'>" + tags[i].label + "</a></div>"; 68778 } 68779 tag_container[0].innerHTML = tag_html; 68780 } 68781 68782 function init() { 68783 68784 var tag_container = new AIBS.UI.tag_container(_self.container, {tag_renderer:cloud_renderer}); 68785 tag_container.add_tags(_self.data); 68786 } 68787 68788 init(); 68789} 68790; 68791"use strict"; 68792/** 68793 * I suspect we'll end up with a number of tag containers in our UI toolbox. This 68794 * is a bare-bones implementation. 68795 * <p> 68796 * Other tag containers might look like the tag collections in Jira, FaceBook, or 68797 * like tag clouds. Supply a delegate 'tag_renderer' function as an option when instantiating 68798 * this object to do the actual tag rendering for any sort of interesting tag container you like. 68799 * It might work. 68800 * </p> 68801 * <p> 68802 * The default tag renderer assumes an <input type='text'/> element is the container. A tag_renderer delegate 68803 * has this prototype: tag_renderer(this); through 'this' your renderer will have access to 68804 * this.get_tags & this.container to do its rendering. 68805 * </p> 68806 * <p> 68807 * In all of the methods below the 'tag' object looks like this: {label:'foo', value:'bar'} and 68808 * labels must be unique. 68809 * </p> 68810 * <p> 68811 * This should probably be refactored as a jQuery plugin, so it can be instantiated with something like this: 68812 * <pre> 68813 * $('#md_div').tag_container({ 68814 * tag_renderer:my_cool_tag_cloud_renderer 68815 * }); 68816 * </pre> 68817 */ 68818 AIBS.UI.tag_container = (function($j) { 68819 68820 return ( 68821 68822 function(container, options) { 68823 68824 this.container = $j(container); 68825 this.renderer = options ? options.tag_renderer ? options.tag_renderer : render_tags : render_tags; 68826 68827 /** 68828 * add a tag to the container 68829 * @param tag {object} Like this: {label:'foo', value:'bar'} 68830 */ 68831 this.add_tag = function(tag) { 68832 _tags[tag.label] = tag; 68833 this.renderer(this); 68834 } 68835 68836 /** 68837 * add an array of tags to the container 68838 * @param tags {array} Like this: [{label:'foo', value:'bar'}] 68839 */ 68840 this.add_tags = function(tags) { 68841 68842 var len = tags.length; 68843 for(var i = 0; i < len; i++) 68844 _tags[tags[i].label] = tags[i]; 68845 68846 this.renderer(this); 68847 } 68848 68849 /** 68850 * remove a tag from the container 68851 * @param tag {object} Like this: {label:'foo', value:'bar'} 68852 */ 68853 this.remove_tag = function(tag) { 68854 delete _tags[tag.label]; 68855 this.renderer(this); 68856 } 68857 68858 /** 68859 * get an array of all the tags in the container 68860 * @returns {array} Array of tag objects 68861 */ 68862 this.get_tags = function() { 68863 var list = []; 68864 for(var key in _tags) 68865 list.push(_tags[key]); 68866 68867 return(list); 68868 } 68869 68870 /** 68871 * re-renders the tags. _Don't_ call this from your custom tag_renderer. 68872 */ 68873 this.render = function() { 68874 this.renderer(this); 68875 } 68876 68877 /** 68878 * returns the text that's displayed in the container, which in some renderings 68879 * might be different than the set of labels in the tag collection. 68880 */ 68881 this.get_displayed_text = function() { 68882 return( this.container.val()); 68883 } 68884 68885 /** 68886 * removes all tags from the collection 68887 */ 68888 this.clear = function() { 68889 _tags = {}; 68890 this.renderer(this); 68891 } 68892 68893 /** 68894 * @private 68895 * @scope AIBS.UI.tag_container 68896 */ 68897 68898 var _self = this; 68899 var _tags = {}; 68900 68901 function render_tags(me) { 68902 var list = ''; 68903 for(var key in _tags) 68904 list += _tags[key].label + '; '; 68905 68906 if(list.length > 0) 68907 list = list.slice(0, -2); 68908 68909 var control_type = me.container.get(0).tagName; 68910//alert("the container: " + inspect('', me.container.get(0))); 68911 if(control_type == 'INPUT') 68912 me.container.val(list); 68913 else 68914 me.container.text(list); 68915 68916 } 68917 68918 function inspect(prefix, obj) { 68919 var s = ""; 68920 for(var key in obj) { 68921 if((typeof obj[key]) == "function") 68922 s = s; 68923 else if((typeof obj[key]) == "object" && obj[key] !== null & key != "element") 68924 s += "\n" + prefix + "key: " + key;// + inspect(prefix + " ", obj[key]); 68925 else 68926 s += "\n" + prefix + "key: " + key + ", value: " + obj[key]; 68927 } 68928 return(s); 68929 } 68930 } 68931 68932 ); 68933 68934 68935})(jQuery); 68936"use strict"; 68937/** 68938 * 68939 * @param container 68940 * @param experiment_id 68941 * @param service_url 68942 * @param options 68943 */ 68944AIBS.UI.threed_thumbnail_widget = function threed_thumbnail(container, experiment_id, service_url, options) { 68945 68946 this.container = $j(container); 68947 this.service_url = service_url; 68948 this.experiment_id = experiment_id; 68949 this.settings = { 68950 project_cell_height:125, // height in pixels of the container 68951 slider_height:20, // height in pixels reserved for the slider 68952 num_slider_images:36, // number of images in an aba mini thumbnail 68953 thumbnail_dims:[164,99], // dimensions of a single frame of an aba mini thumbnail filmstrip 68954 animate_duration:0, 68955 include_slider:true 68956 }; 68957 $j.extend(true, this.settings, options); 68958 68959 68960 this.set_experiment = function(experiment_id) { 68961 68962 this.experiment_id = experiment_id; 68963 68964 if(!_img_element) 68965 get_image(); 68966 else 68967 _img_element.attr('src', this.service_url + this.experiment_id); 68968 68969 position_thumbnail(); 68970 } 68971 68972 this.clear = function() { 68973 68974 this.experiment_id = 0; 68975 68976 if(!_img_element) 68977 return; 68978 68979 _img_element.attr('src', '/assets/black.png'); 68980 _img_element.css({'background-image':'/assets/black.png'}); 68981 _slider_index = 0; 68982 position_thumbnail(); 68983 } 68984 68985 var _self = this; 68986 var _the_image = null; 68987 var _the_wrapper = null; 68988 var _cell_width = 0; 68989 var _cell_height = 0; 68990 var _slider_index = 0; 68991 var _img_element = null; 68992 var _the_slider = null; 68993 68994 68995 function build_scaffold() { 68996 68997 _cell_height = _self.settings.project_cell_height;
68998 _cell_width = _self.settings.thumbnail_dims[0]; 68999 69000 var html = "<div id='_threed_wrapper_" + _self.experiment_id + "' class='_threed_wrapper'></div>"; 69001 _the_wrapper = $j(html) 69002 .css({ 69003 'padding':'0px', 69004 'background-color':'#000', 69005 width: _cell_width+'px', 69006 height: _cell_height+'px', 69007 overflow: "hidden", 69008 background: "#000000 url('/assets/spinner_black.gif') no-repeat center center" 69009 }).appendTo(_self.container); 69010 } 69011 69012 69013 function build_slider() { 69014 69015 var slider_height = _self.settings.slider_height; 69016 var slider_width = _self.settings.thumbnail_dims[0]; 69017 69018 _the_slider = $j("<div class='_threed_slider'>") 69019 .css({ 69020 width: slider_width+"px", 69021 height: slider_height+"px" 69022 }) 69023 .appendTo(_the_wrapper); 69024 69025 _the_slider.slider({ 69026 min: 0, 69027 max: _self.settings.num_slider_images-1, 69028 value: _slider_index, 69029 slide: do_slide, 69030 animate:_self.settings.animate_duration 69031 }); 69032 } 69033 69034 function one_revolution() { 69035 var handle = _the_slider.find('a'); 69036 $j(handle).animate({ 69037 'left':'100%' 69038 }, 69039 { 69040 'step':do_animate_step 69041 } 69042 ); 69043 } 69044 69045 function do_animate_step(now, fx) { 69046 if(!now) 69047 return; 69048 69049 _slider_index = Math.round( ( (_self.settings.num_slider_images-1)/100 ) * now) ; 69050 position_thumbnail(); 69051 } 69052 69053 function do_slide(event, ui) { 69054 _slider_index = ui.value; 69055 position_thumbnail(); 69056 } 69057 69058 function get_image() { 69059 _the_image = new Image(); 69060 _the_image.onload = on_image_ready; 69061 _the_image.onerror = function() {_the_wrapper.css({background: "#000" }) }; 69062 69063 _the_image.src = _self.service_url + _self.experiment_id; 69064 } 69065 69066 function on_image_ready() { 69067 var scaled_width = Math.round(_the_image.width * _cell_width / _self.settings.thumbnail_dims[0]); 69068 var scaled_height = Math.round(_the_image.height * _cell_height / _self.settings.thumbnail_dims[1]); 69069 var div = $j("<div class='_threed_img_wrapper'>").css({ 69070 width: _self.settings.thumbnail_dims[0]+"px", 69071 height: _self.settings.thumbnail_dims[1]+"px", 69072 'position':'relative', 69073 'top':0, 69074 'overflow':'hidden'}); 69075 69076 _img_element = $j("<img id='_threed_img_" + _self.experiment_id + "'>") 69077 .attr('src',_the_image.src) 69078 .css({ 69079 'width': (_self.settings.num_slider_images * _self.settings.thumbnail_dims[0])+"px", 69080 'height': _self.settings.thumbnail_dims[1]+"px", 69081 'position':'absolute', 69082 'left':'0px', 69083 'top':'0px'}); 69084 69085 69086 div.append(_img_element); 69087 _the_wrapper.append(div); 69088 _img_element = $j('#_threed_img_'+_self.experiment_id); 69089 69090 if(_self.settings.include_slider) 69091 build_slider(); 69092 69093 position_thumbnail(); 69094 if(_self.settings.animate_duration) 69095 setTimeout(one_revolution, 250); 69096 } 69097 69098 function position_thumbnail() { 69099 var img_width = _img_element.width() / _self.settings.num_slider_images; 69100 _img_element.css({ left:(-_slider_index * img_width)+"px" }); 69101 } 69102 69103 function init() { 69104 69105 build_scaffold(); 69106 69107 if(_self.experiment_id) 69108 get_image(); 69109 } 69110 69111 init(); 69112} 69113; 69114"use strict"; 69115var zap_keyboard = { 69116 69117 69118 keyDownEvent : function(evt) { 69119 69120 var viewer_object = evt.memo.viewer; 69121 69122 if (viewer_object) { 69123 var images = viewer_object.getImages(); 69124 var image = viewer_object.getImageInfo(); 69125 var pos = viewer_object.getPosition(); 69126 var keyCode = evt.memo.keyCode; 69127 var key; 69128 69129 switch (keyCode) { 69130 case 70: // "f" = next image 69131 var nextImage = zap_keyboard.getNextImage(image.id, images); 69132 69133 if (nextImage != null) { 69134 zap_keyboard.moveToImage(viewer_object, nextImage.id, image.tier, pos); 69135 } 69136 break; 69137 case 68: // "d" = go back to previous image 69138 var prevImage = zap_keyboard.getPreviousImage(image.id, images); 69139 69140 if (prevImage != null) { 69141 zap_keyboard.moveToImage(viewer_object, prevImage.id, image.tier, pos); 69142 } 69143 break; 69144 case 69: // "e" = go to beginning of image list 69145 var begImage; 69146 69147 for (key in images) { 69148 begImage = images[key]; 69149 break; 69150 } 69151 zap_keyboard.moveToImage(viewer_object, begImage.id, image.tier, pos); 69152 break; 69153 case 82: // "r" = go to end of the image list 69154 var endImage; 69155 69156 for (key in images) { 69157 endImage = images[key]; 69158 } 69159 zap_keyboard.moveToImage(viewer_object, endImage.id, image.tier, pos); 69160 break; 69161 /* case 84: // "t" = advance all viewers to next image 69162 //hbaMoveAll(true); 69163 trace("move all to next"); 69164 break; 69165 case 87: // "w" = reverse all viewers to previous image 69166 //hbaMoveAll(false); 69167 trace("move to previous"); 69168 break; 69169 case 83: // "s" = synch 69170 doSync(viewer_object); 69171 break; 69172 */ 69173 case 83: // "s" = sync 69174 // trigger an artificial click event on the "Sync" button 69175 var e = jQuery.Event("click"); 69176 jQuery('#_zap_tool_sync_' + viewer_object.id).trigger(e); 69177 break; 69178 default: // pass unprocessed key events back to Zap Viewer, such as the zoom keys and arrow keys 69179 evt.keyCode = keyCode; 69180 var viewer = evt.memo.viewer; 69181 viewer.doKey(evt); 69182 } 69183 } 69184 }, 69185 69186 69187 getNextImage : function(currentImageId, images) { 69188 var isNextImage = false;
69189 var nextImage; 69190 69191 for (var key in images) { 69192 if (isNextImage) { 69193 nextImage = images[key]; 69194 break; 69195 } 69196 69197 if (images[key].id == currentImageId) { 69198 isNextImage = true; 69199 } 69200 } 69201 return nextImage; 69202 }, 69203 69204 getPreviousImage : function(currentImageId, images) { 69205 var prevImage; 69206 69207 for (var key in images) { 69208 if (images[key].id == currentImageId) { 69209 break; 69210 } 69211 else { 69212 prevImage = images[key]; 69213 } 69214 } 69215 return prevImage; 69216 }, 69217 69218 moveToImage : function(viewer, image_id, image_tier, position) { 69219 69220 var moveInfo = {}; 69221 moveInfo.saveOnImageChange = viewer.onImageChange; 69222 moveInfo.tier = image_tier; 69223 moveInfo.x = position.normal.x; 69224 moveInfo.y = position.normal.y; 69225 viewer.moveInfo = moveInfo; // attach hash of movement info to the viewer as a property 69226 69227 // temporarily replace the existing onImageChange handler 69228 viewer.onImageChange = zap_keyboard.finishMoveToImage; 69229 69230 viewer.select(image_id); 69231 viewer.moveToImage(image_id); 69232 }, 69233 69234 /* applies former image's pan and zoom, refreshes scale bar settings */ 69235 finishMoveToImage : function(viewer, image) { 69236 var newTier; 69237 var moveInfo = viewer.moveInfo; // hash of movement info attached to viewer as a property 69238 69239 // restore original handler 69240 viewer.onImageChange = moveInfo.saveOnImageChange; 69241 69242 // edge case, moving from an image at a higher tier than exists in the new image 69243 var info = viewer.getImageInfo(); 69244 if (moveInfo.tier > (info.num_tiers - 1)) { 69245 newTier = info.num_tiers - 1; 69246 } 69247 else { 69248 newTier = moveInfo.tier; 69249 } 69250 69251 viewer.setPosition('normal', newTier, moveInfo.x, moveInfo.y); 69252 69253 if(viewer.scalebar) { 69254 var evt = new Object(); 69255 var memo = new Object(); 69256 memo.viewer = viewer; 69257 memo.image = viewer.getImageInfo(); 69258 evt.memo = memo; 69259 viewer.setResolution(memo.image.resolution); // viewer may not have set its global resolution to the image.resolution yet 69260 viewer.scalebar.updateScaleBar(evt); 69261 } 69262 } 69263} 69264; 69265/** 69266 * This function contains common functions used by the ISH search models for gene, 69267 * advanced (faceted), and gene classification. It contains boilerplate functions that support SlickGrid use, as well 69268 * as functions used to support multicolumn sorting. 69269 * When you instantiate one of the specific models, you need to instantiate this function and assign as a property 69270 * of the model instance.. 69271 * 69272 * Example: 69273 * 69274 * model = new ish_gene_search_model(daos, model_options); 69275 * model.IshCommon = new IshCommon; 69276 * 69277 * Accessing the common functions within the specific model: 69278 * 69279 * _self.IshCommon.single_column_sort_order(args); 69280 * 69281 * @constructor 69282 */ 69283 69284function IshCommon() { 69285// public methods 69286 69287 /** 69288 * Get row metadata 69289 * 69290 * @param row_index 69291 * @param data 69292 * @param column_config 69293 * @return {Object} hash containing row metadata, or empty hash for invalid row 69294 */ 69295 this.do_get_item = function(row_index, data, column_config) { 69296 var item = {}; 69297 if (data && row_index < data.length) { 69298 var d = data[row_index]; 69299 var len = column_config.length; 69300 for (var i = 0; i < len; i++) { 69301 var column = column_config[i]; 69302 if (column.field && column.field.length > 0) 69303 item[column.field] = eval('d.' + column.data); 69304 } 69305 item.experiment_detail = d; 69306 item.failed = d.failed; 69307 } 69308 69309 return(item); 69310 }; 69311 69312 /** 69313 * Logic calculating new paging info 69314 * 69315 * @param args hash with key-values for: pageSize, pageNum 69316 * @param page_num 69317 * @param page_size 69318 * @param total_rows 69319 * @return {Object} hash with key-values for: no_change, no_page, page_num, page_size 69320 */ 69321 this.calc_paging_info = function(args, page_num, page_size, total_rows) { 69322 var reset_page_zero = false;
69323 var info = {}; 69324 69325 info.no_change = false; 69326 info.no_page = false; 69327 info.page_num = page_num; 69328 info.page_size = page_size; 69329 69330 if (args.pageSize != undefined) { 69331 // if the page size hasn't changed, or is smaller than it was, and that's the only field being set, we're done. 69332 if ((args.pageSize == page_size) && (args.pageNum == undefined)) { 69333 info.no_change = true; 69334 return info; 69335 } 69336 69337 if (args.pageSize == 0) { 69338 info.page_size = total_rows + (page_size - 1); 69339 info.no_page = true; 69340 } 69341 else { 69342 if (args.pageSize !== page_size) { 69343 // if the user changes the rows per page via the UI, then reset the current page number to 0 (see IT-6512) 69344 reset_page_zero = true; 69345 } 69346 info.page_size = args.pageSize; 69347 } 69348 } 69349 69350 if (args.pageNum != undefined) { 69351 info.page_num = Math.min(args.pageNum, Math.ceil(total_rows / info.page_size)); 69352 } 69353 else if (reset_page_zero) { 69354 info.page_num = 0; 69355 } 69356 69357 return info; 69358 }; 69359 69360 /** 69361 * Default single column sort, called when user clicks on SlickGrid column title. 69362 * 69363 * @param sort_column column name 69364 * @param sort_asc true = sort ascending, false = sort descending 69365 * @param sort_suffix 69366 * @return {String} 69367 */ 69368 this.single_column_sort_order = function(sort_column, sort_asc, sort_suffix) { 69369 // edge case for Age column sorting, can't sort on display value ('field' property) 69370 // assumes definition of metadata fields, 'metadata_days', 'metadata_embryonic' 69371 var sort_order = ""; 69372 if (sort_column.id.toUpperCase() == 'AGE') { 69373 sort_order = sortable_age_order(sort_column.metadata_days, sort_column.metadata_embryonic, sort_asc); 69374 } 69375 else { 69376 sort_order = sort_column.field + (sort_asc ? '$asc' : '$desc'); 69377 } 69378 sort_order += sort_suffix ? (',' + sort_suffix) : ''; 69379 69380 return sort_order; 69381 }; 69382 69383 /** 69384 * Multicolumn sort order 69385 * 69386 * Creates a warehouse-friendly string containing the column sorting information.
69387 * Each hash in the sort_data array consists of column keys and values for: name, value, asc 69388 * 69389 * @param sort_data array of hashes with info specifying the column sort order 69390 * @return {String} 69391 */ 69392 this.mcsort_order = function(sort_data) { 69393 var sort_order = ""; 69394 for (var i = 0; i < sort_data.length; ++i) { 69395 // edge case processing for Age column 69396 if (sort_data[i].name.toUpperCase() == 'AGE') { 69397 sort_order += mcsort_sortable_age_order(sort_data[i]) + ','; 69398 } 69399 else { 69400 sort_order += sort_data[i].value + (sort_data[i].asc ? '$asc' : '$desc') + ','; 69401 } 69402 } 69403 69404 return sort_order.substring(0, sort_order.length - 1); // remove trailing ',' 69405 }; 69406 69407 /** 69408 * Debug support function, pretty prints key-value pair found in obj 69409 * 69410 * @param prefix description of obj 69411 * @param obj hash object that is pretty printed 69412 * @return {String} formatted string of obj contents 69413 */ 69414 this.inspect = function(prefix, obj) { 69415 var s = ""; 69416 69417 for (var key in obj) { 69418 if ((typeof obj[key]) == "function") 69419 s = s; 69420 else if ((typeof obj[key]) == "object" && obj[key] !== null && key != "element") 69421 s += "\n" + prefix + "key: " + key + inspect(prefix + " ", obj[key]); 69422 else 69423 s += "\n" + prefix + "key: " + key + ", value: " + obj[key]; 69424 } 69425 return(s); 69426 }; 69427 69428 // private 69429 69430 /** 69431 * Create a sort order for SlickGrid single column sort for Age, based on both age in days and the embryonic boolean value. 69432 * Require configuration definition of properties 'metadata_days' and 'metadata_embryonic' 69433 * in the age_column object, this should be done in application.json file. 69434 * The age in days for postnatal ranges is a positive integer value from 0 to n, measured from time of birth. 69435 * The age in days for embryonic donors is also a positive integer value, from 0 to n, but measured from time of 69436 * conception. In order to correctly sort a mix of embryonic and postnatal donors, we have to incorporate the 69437 * boolean value for 'embryonic', which is true for prenatal and false for postnatal donors. 69438 * 69439 * sort the Age column correctly, from prenatal/postconception to postnatal to adult. 69440 * This is done by replacing the "field" property, which contains the display age value (14 yrs) 69441 * with a numeric value, the age in days. For prenatal/postconception, the value is multiplied by -1 69442 * so that we get proper numeric ordering; postnatal and adult ages are all > 0. 69443 * We use nondisplay meta-values that are present in the Age configuration, the properties 'metadata_days' 69444 * and "metadata_embryonic" to configure the "field" property. 69445 * 69446 * @param {number} age_days either days after conception, or days after birth. See param 'is_embryonic'. 69447 * @param {boolean} is_embryonic true if prebirth, false if postbirth. 69448 * @param {boolean} sort_asc boolean sort direction, true = ascending, false = descending 69449 * @return sort order string in AllenInstitute API syntax 69450 */ 69451 function sortable_age_order(age_days, is_embryonic, sort_asc) { 69452 var sort_order; 69453 69454 if (!age_days || !is_embryonic) { 69455 // conventional sort order 69456 sort_order = age_column.field + (sort_asc ? '$asc' : '$desc'); 69457 } 69458 else { 69459 if (sort_asc) 69460 sort_order = is_embryonic + '$desc,' + age_days + '$asc'; 69461 else 69462 sort_order = is_embryonic + '$asc,' + age_days + '$desc'; 69463 } 69464 69465 return sort_order; 69466 } 69467 69468 /** 69469 * Reformats the age params from multicolumn sort into a sortable format suitable for a warehouse query. 69470 * Assumes that the age value is in the form 'ages.embryonic,ages.days' 69471 * 69472 * @param age_hash keys name, value, asc 69473 * @return sort_order string that looks like 'ages.embryonic$desc,ages.days$asc' for ascending age or 69474 * 'ages.embryonic$asc,ages.days$desc' for descending age 69475 */ 69476 function mcsort_sortable_age_order(age_hash) { 69477 var sort_order = ""; 69478 69479 var value = age_hash.value; 69480 var asc = age_hash.asc; 69481 var after_embryonic = value.indexOf(','); 69482 var after_days = value.length; 69483 var start_days = value.indexOf('ages.days'); 69484 69485 if (asc) { 69486 sort_order = value.substring(0, after_embryonic) + '$desc,' + value.substring(start_days, after_days) + '$asc'; 69487 } 69488 else { 69489 sort_order = value.substring(0, after_embryonic) + '$asc,' + value.substring(start_days, after_days) + '$desc'; 69490 } 69491 69492 return sort_order; 69493 } 69494} 69495 69496 69497 69498; 69499"use strict"; 69500/** 69501 * Displays the 'About' text panel and contents, which are placed in a <div> with id = "about_block" 69502 * This plugin assumes that there are up to three blocks of HTML text that can be displayed in the panel, 69503 * and are called "first", "second", "third". These are configured in application.json
69503in a section that can look like this: 69504 * "gene": { 69505 * "class_name": "about_widget", 69506 * "title": "NIH Blueprint NHP Atlas", 69507 * "options": { 69508 * "file_prefix": <optional, if present the file_prefix should be prepended to any file path in first/second/third hash values> 69509 * "url_replacement_delimiter": <optional, if present the delimiter should surround a string which will later be replaced by an url> 69510 * "first": <html for 1st block>, 69511 * "second": <html for 2nd block>, 69512 * "third": <html for 3rd block> 69513 * } 69514 * 69515 * Note: this widget requires jQuery 69516 * 69517 * @param container 69518 * @param daos 69519 * @param urls 69520 * @param options 69521 */ 69522function about_widget(container, daos, urls, options) { 69523 69524 this.container = container; 69525 this.settings = { 69526 "file_prefix":null, 69527 "class_prefix":"_aw", 69528 "url_replacement_delimiter":null, 69529 "first":"first", 69530 "second":"second", 69531 "third":"third" 69532 }; 69533 69534 $j.extend(true, this.settings, options); 69535 69536 69537 this.set_content = function(content) { 69538 69539 update_content(content); 69540 } 69541 69542 69543 var _self = this; 69544 var _urls = urls; 69545 var _first_block = null; 69546 var _second_block = null; 69547 var _third_block = null; 69548 69549 69550 function build_scaffold() { 69551 69552 var classes = _self.settings.class_prefix; 69553 var id = _self.container.attr('id') + "_aw"; 69554 69555 var html = "<div id='_" + id + "_wrapper' class='" + classes + "'>"; 69556 html += " <div id='_" + id + "_first' class='" + classes + "_first'></div>"; 69557 html += " <div id='_" + id + "_second' class='" + classes + "_second'></div>"; 69558 html += " <div id='_" + id + "_third' class='" + classes + "_third'></div>"; 69559 html += "</div>"; 69560 69561 _self.container.append(html); 69562 69563 _first_block = $j('#_' + id + "_first")[0]; 69564 _second_block = $j('#_' + id + "_second")[0]; 69565 _third_block = $j('#_' + id + "_third")[0]; 69566 } 69567 69568 function update_content(content) { 69569 69570 if(content.first && content.first.length) 69571 load_html_block( _first_block, content.first); 69572 69573 if(content.second && content.second.length) 69574 load_html_block( _second_block, content.second); 69575 69576 if(content.third && content.third.length) 69577 load_html_block( _third_block, content.third); 69578 } 69579 69580 /** 69581 * Loads HTML content from either an application.json string or from an HTML file on the server, placing inside of 69582 * the specified <div> 69583 * If the HTML is read from a file, url string replacement will occur for specially delimited text (url_replacement_delimiter) 69584 * 69585 * @param {Object} block one of three display <div> elements 69586 * @param {String} content either the HTML for display in the block, or else a relative file path to an HTML file in public dir 69587 */ 69588 function load_html_block(block, content) { 69589 // determine the nature of 'content' by seeing if the string begins with a predefined file prefix substring 69590 var file_prefix = _self.settings.file_prefix; 69591 var index = content.indexOf(file_prefix); 69592 var text; 69593 69594 if (index == 0) { 69595 // 'content' is a path to an HTML file 69596 var file_path = content.slice(file_prefix.length); 69597 var file_url = location.protocol + "//" + location.host + "/" + file_path; 69598 var file_contents = jQuery.get(file_url, function(data) { 69599 var text = replace_strings_with_url(data); 69600 block.innerHTML = html_decode(text); 69601 }); 69602 } 69603 else { 69604 // 'content' is directly displayable HTML 69605 text = replace_strings_with_url(content); 69606 block.innerHTML = html_decode(text); 69607 } 69608 } 69609 69610 /** 69611 * Replace delimited strings with urls. The urls are defined in _application_load.html.erb 69612 * 69613 * @param {String} content HTML for display 69614 * @returns {String} Original HTML, with delimited strings replaced with urls 69615 */ 69616 function replace_strings_with_url(content) { 69617 if (!_self.settings.url_replacement_delimiter) { 69618 return content; 69619 } 69620 else { 69621 var delimiter = _self.settings.url_replacement_delimiter; 69622 var parts = content.split(delimiter); 69623 var whole = ""; 69624 var match; 69625 for (var i = 0, len = parts.length; i < len; ++i) { 69626 match = _urls[parts[i]]; 69627 whole += (match == undefined) ? parts[i] : match; 69628 } 69629 69630 return whole; 69631 } 69632 } 69633 69634 function html_decode(value){ 69635 return(value); 69636 } 69637
69638 function init() { 69639 69640 build_scaffold(); 69641 update_content(_self.settings); 69642 } 69643 69644 init(); 69645} 69646; 69647 69648function browse_widget(container, daos, urls, options) { 69649 69650 this.container = $j(container); 69651 this.urls = urls; 69652 this.data = options.searches; 69653 this.settings = { 69654 column_count:3, 69655 cell_decorator:null, 69656 table_class:null, 69657 row_class:null, 69658 cell_class:null 69659 }; 69660 $j.extend(true, this.settings, options); 69661 69662 69663 this.set_content = function(mode) { 69664 // there are no additional modes for this widget 69665 } 69666 69667 69668 var _self = this; 69669 var _row_count = 0; 69670 var _table_id = _self.container.id + '_browse_table'; 69671 var _table = null; 69672 var _data_length = 0; 69673 69674 69675 69676 function build_table() { 69677 _data_length = _self.data.length; 69678 _row_count = Math.ceil(_data_length / _self.settings.column_count); 69679 69680 var table_class = _self.settings.table_class ? '_browse_table ' + _self.settings.table_class : '_browse_table'; 69681 var row_class = _self.settings.table_class ? '_browse_row ' + _self.settings.table_class : '_browse_row'; 69682 var cell_class = _self.settings.table_class ? '_browse_cell ' + _self.settings.table_class : '_browse_cell'; 69683 69684 var html = "<table id='" + _table_id + "' class='" + table_class + "'>"; 69685 for(var r = 0; r < _row_count; r++) { 69686 html += "<tr class='" + row_class + "'>"; 69687 for(var c = 0; c < _self.settings.column_count; c++) 69688 html += "<td class='" + cell_class + "'></td>"; 69689 69690 html += "</tr>"; 69691 } 69692 69693 _table = $j(html).appendTo(_self.container); 69694 } 69695 69696 function populate_table() { 69697 69698 var table = _table[0]; 69699 var rows = table.rows; 69700 var data = _self.data; 69701 var index = 0; 69702 69703 69704 69705 for(var c = 0; c < _self.settings.column_count; c++) { 69706 for(var r = 0; r < _row_count; r++) { 69707 var cells = rows[r].cells; 69708 if(index == _data_length) 69709 break; 69710 69711 var val = data[index++]; 69712 69713 if(_self.settings.cell_decorator) 69714 cells[c].innerHTML = _self.settings.cell_decorator(cells[c], val); 69715 else 69716 //cells[c].innerHTML = make_link(cells[c], val); 69717 cells[c].innerHTML = generate_cell(cells[c], val); 69718 } 69719 } 69720 } 69721 69722 69723 function make_link(container, data) { 69724 69725 var url = _self.urls.search + '?search_type=differential&domain2=' + data.target; 69726 url += '&domain1=' + data.contrast + '&domain1_threshold=' + data.threshold.join(','); 69727 url += '&image_set=' + (data.set == 'all' ? 'P56' : 'P56coronal'); 69728 69729 var html = "<a class='_browse_diff_search_link' href='" + url + "'>" + data.name + "</a>"; 69730 69731 return(html); 69732 } 69733 69734 function generate_cell(container, data) { 69735 var html, url; 69736 if(data.title_line) { 69737 html = "<span class = '_browse_category_title'>" + data.name + "</span>"; 69738 //$j(container).addClass("_browse_category_title"); 69739 return (html); 69740 69741 } 69742 url = _self.urls.search + '?search_type=differential&domain2=' + data.target + '&domain1=' + data.contrast; 69743 html = "<a class='_browse_diff_search_link' href='" + url + "'>" + data.name + "</a>"; 69744 return(html); 69745 69746 69747 69748 } 69749 69750 function init() { 69751 build_table(); 69752 populate_table(); 69753 } 69754 69755 init(); 69756} 69757 69758; 69759 69760function browse_widget_with_column_layout(container, daos, urls, options) { 69761 69762 this.container = $j(container); 69763 this.urls = urls; 69764 this.data = options.search_groups; 69765 this.settings = { 69766 column_count:3, 69767 cell_decorator:null, 69768 table_class:null, 69769 row_class:null, 69770 cell_class:null 69771 }; 69772 $j.extend(true, this.settings, options); 69773 69774 69775 this.set_content = function(mode) { 69776 // there are no additional modes for this widget 69777 } 69778 69779 69780 var _self = this; 69781 var _row_count = []; 69782 var _table_id = _self.container.id + '_browse_table'; 69783 var _table = null; 69784 var _data_length = 0; 69785 var _column_count = 1; 69786 69787 69788 function build_table() { 69789 69790 _column_count = _self.data.length; 69791 var row_count; 69792 69793 var outer_table_class = _self.settings.table_class ? '_browse_table ' + _self.settings.table_class : '_browse_table'; 69794 var outer_td_class = _self.settings.outer_td_class ? _self.settings.outer_td_class : '_outer_td'; 69795 var odd_or_even = 'td_odd'; 69796 var inner_table_class = _self.settings.inner_table_class ? _self.settings.inner_table_class : '_inner_browse_table'; 69797 var inner_tr_class = _self.settings.inner_tr_class ? '_browse_row ' + _self.settings.inner_tr_class : '_browse_row'; 69798 var inner_td_class = _self.settings.inner_td_class ? '_browse_cell ' + _self.settings.inner_td_class : '_browse_cell'; 69799 var html = "<table id='" + _table_id + "' class='" + outer_table_class + "'><tr>"; 69800 for(var c = 0; c < _column_count; c++) { 69801 odd_or_even = c%2 == 1 ? 'td_odd' : 'td_even'; 69802 var td_id = "outer_td_" + c; 69803 html += "<td id='" + td_id + "' class='" + outer_td_class + " " + odd_or_even + "'>"; 69804 row_count = _self.data[c].length; 69805 html += "<table class='" + inner_table_class + "'>"; 69806 for(var r = 0; r < row_count; r++) { 69807 var inner_tr_id = "inner_tr_" + r; 69808 var inner_td_id = "inner_td_" + r; 69809 html += "<tr id='" + inner_tr_id + "' class='" + inner_tr_class + "'>"; 69810 html += "<td id='" + inner_td_id + "' class='" + inner_td_class + "'>"; 69811 html += generate_cell(null, _self.data[c][r]); 69812 html += "</td>"; 69813 html += "</tr>"; 69814 } 69815 html += "</table>"; 69816 html += "</td>"; // outer table's td 69817 69818 69819 } 69820 html += "</tr></table>"; 69821 _table = $j(html).appendTo(_self.container); 69822 } 69823 69824 function populate_table() { 69825 69826 69827 } 69828 69829 69830 69831 function generate_cell(container, data) { 69832 var html, url; 69833 if(data.title_line) { 69834 html = "<span class = '_browse_category_title'>" + data.name + "</span>"; 69835 //$j(container).addClass("_browse_category_title"); 69836 return (html); 69837 69838 } 69839 url = _self.urls.search + '?search_type=differential&domain2=' + data.domain2 + '&domain1=' + data.domain1; 69840 url += '&stage2='+ data.stage2 + '&stage1=' + data.stage1; 69841 html = "<span><a class='_browse_diff_search_link' href='" + url + "'>" + data.name + "</a></span>"; 69842 return(html); 69843 69844 69845 69846 } 69847
69848 function init() { 69849 build_table(); 69850 //populate_table(); 69851 } 69852 69853 init(); 69854} 69855 69856; 69857// For a faceted search there are multiple dimensions, each with 69858// their own sub-dimensions, that can be used in building a query. 69859// The facet model is intended to abstract access to the 69860// facet information from the actual encoding of that info. 69861// 69862// Regardless of how the facet information is actually stored/retrieved, 69863// the model should make the facet information available as an array 69864// of ordered_list objects. See ordered_lists.js for details on 69865// that structure. 69866// 69867// Currently the facet information is included in the application config file. 69868// This incarnation of the facet model expects the input data to look like this: 69869 69870/* [ 69871 { 69872 "name":"Medical Conditions", 69873 "id":"medical_conditions", 69874 "order":30, 69875 "subgroups":[ 69876 { 69877 "name":"Disease", 69878 "order":10, 69879 "items":[ 69880 {"id":"control","name":"control","order":10,"type":"boolean", "value":true}, 69881 {"id":"schizophrenia","name":"schizophrenia","order":20,"type":"boolean", "value":true} 69882 ] 69883 }, 69884 { 69885 "name":"Smoker", 69886 "order":20, 69887 "items":[ 69888 {"id":"smoker","name":"smoker","order":10,"type":"boolean", "value":true}, 69889 {"id":"nonsmoker","name":"non-smoker","order":20,"type":"boolean", "value":true}, 69890 {"id":"unknown","name":"unknown","order":30,"type":"boolean", "value":true} 69891 ] 69892 } 69893 ] 69894 }, 69895 { 69896 "name":"Donor", 69897 "id":"donors", 69898 "order":10, 69899 "subgroups":[ 69900 { 69901 "name":"Sex", 69902 "order":10, 69903 "items":[ 69904 {"id":"female","name":"female","order":10,"type":"boolean", "value":true}, 69905 {"id":"male","name":"male","order":20,"type":"boolean", "value":true} 69906 ] 69907 }, 69908 { 69909 "name":"Handedness", 69910 "order":20, 69911 "items":[ 69912 {"id":"left","name":"left","order":10,"type":"boolean", "value":true}, 69913 {"id":"right","name":"right","order":20,"type":"boolean", "value":true}, 69914 {"id":"unknown","name":"unknown","order":30,"type":"boolean", "value":true} 69915 ] 69916 }, 69917 { 69918 "name":"Age", 69919 "order":30, 69920 "items":[ 69921 {"id":"20_29","name":"20 - 29","order":10,"type":"boolean", "value":true}, 69922 {"id":"30_39","name":"30 - 39","order":20,"type":"boolean", "value":true}, 69923 {"id":"40_49","name":"40 - 49","order":30,"type":"boolean", "value":true}, 69924 {"id":"50_59","name":"50 - 59","order":40,"type":"boolean", "value":true} 69925 ] 69926 } 69927 ] 69928 } 69929 ] 69930*/ 69931 69932 69933function facet_model(facet_data) { 69934 69935 this.raw_data = facet_data ? facet_data : {groups:[]}; 69936 this.ordered_data = []; 69937 69938 // returns the top-level groups of facets as an 69939 // ordered_list structure. This is a named & ordered list of lists. 69940 this.get_groups = function() { 69941 return(this.ordered_data.sub_lists); 69942 } 69943 69944 // returns the next-to-top level lists of facets as an 69945 // array of ordered_list structures. 69946 this.get_lists = function() { 69947 69948 var lists = []; 69949 var len = this.ordered_data.sub_lists.length; 69950 for(var i = 0; i < len; i++) 69951 lists.push(this.ordered_data.sub_lists[i].sub_lists); 69952 69953 return(lists); 69954 } 69955 69956 69957 var _self = this; 69958 69959
69960 function init() { 69961 _self.ordered_data = new ordered_list('top', 'top_name', 0, false, null, load_groups(_self.raw_data.groups) ); 69962 } 69963 69964 function load_groups(data) { 69965 69966 if(!data) 69967 return(null); 69968 69969 var list = []; 69970 var list_len = data.length; 69971 for(var i = 0; i < list_len; i++) { 69972 list.push(new ordered_list(data[i].id, data[i].name, data[i].order, data[i].ignore_when_all_selected, 69973 load_items(data[i].items), load_groups(data[i].subgroups) )); 69974 } 69975 69976 return(list) 69977 } 69978 69979 function load_items(items) { 69980 69981 if(!items) 69982 return(null); 69983 69984 var list = []; 69985 for(var i = 0; i < items.length; i++) { 69986 var item = items[i]; 69987 list.push(new list_item(item.id, item.name, item.order, item.type, item.value, item.qtype)) 69988 } 69989 69990 return(list); 69991 } 69992 69993 function inspect(prefix, obj) { 69994 var s = ""; 69995 for(key in obj) { 69996 if((typeof obj[key]) == "function") 69997 s = s; 69998 else if((typeof obj[key]) == "object" && obj[key] !== null & key != "element") 69999 s += "\n" + prefix + "key: " + key + inspect(prefix + " ", obj[key]); 70000 else 70001 s += "\n" + prefix + "key: " + key + ", value: " + obj[key]; 70002 } 70003 return(s); 70004 } 70005 init(); 70006} 70007; 70008function faceted_search_ui(dao, definition, info_widget, options) { 70009 70010 this.the_dao = dao; 70011 this.info_widget = info_widget; 70012 this.definition = definition; 70013 70014 this.settings = { 70015 search_on_update:false, 70016 exact_match:false, 70017 on_search:null, 70018 on_error:null, 70019 on_ready:null 70020 }; 70021 $j.extend(this.settings, true, options); 70022 70023 70024 this.get_state = function () { 70025 return(get_search_state()); 70026 } 70027 70028 this.set_state = function (state) { 70029 set_search_state(state); 70030 } 70031 70032 /* private */ 70033 var _self = this; 70034 var _info_widget = _self.info_widget; 70035 var _closed = true; 70036 var _selected_item = false; 70037 var _search_text_box = null; 70038 var _facets = null; 70039 var _facet_table = null; 70040 var _search_on_update = null; 70041 70042 70043 function get_search_state() { 70044 var exact = get_exact_search_state(); 70045 var term = _search_text_box.val(); 70046 if (_selected_item) 70047 term = '"' + term + '"'; 70048 70049 var state = { 70050 exact_match:exact, 70051 search_term:term, 70052 search_type:_self.definition.id 70053 } 70054 70055 state = get_facet_values(state); 70056 return(state); 70057 } 70058 70059 70060 function set_search_state(state) { 70061 var exact = document.getElementById('_exact_match'); 70062 if(exact) 70063 exact.checked = state.exact_match; 70064 70065 if (state.search_term) 70066 _search_text_box.val(state.search_term); 70067 70068 set_facet_state(state); 70069 70070 if (_self.settings.on_ready) 70071 _self.settings.on_ready(); 70072 } 70073 70074 function load_facets() { 70075 _facets = new facet_model(_self.definition.options); 70076 } 70077 70078 function set_facet_state(state) { 70079 70080 // parse the range queries 70081 for (var key in state) { 70082 if (key.indexOf('facet_range_') == 0) { 70083 var new_facet = parse_range_facet(state[key]); 70084 delete state[key]; 70085 $j.extend(state, new_facet); 70086 } 70087 } 70088 70089 var control_lists = $j('.facet_list_container'); 70090 for (var i = 0; i < control_lists.length; i++) { 70091 var facet_state = state['facet_' + control_lists[i].id]; 70092 var checkboxes = $j('#' + control_lists[i].id + ' input[type="checkbox"]'); 70093 70094 if (facet_state) { 70095 var facet_items = facet_state.split(','); 70096 for (var k = 0; k < checkboxes.length; k++) 70097 checkboxes[k].checked = ($j.inArray(checkboxes[k].name, facet_items) >= 0); 70098 } else { 70099 var ignore_when_all_selected = $j('#' + control_lists[i].id).hasClass('ignore_when_all_selected'); 70100 70101 for (var k = 0; k < checkboxes.length; k++) { 70102 if (ignore_when_all_selected) 70103 checkboxes[k].checked = true; 70104 else 70105 checkboxes[k].checked = false;
70106 70107 } 70108 } 70109 } 70110 } 70111 70112 // expecting a string that looks like this: 70113 // ( (facet_age_days *gte 7300 AND facet_age_days *lte 10585) OR (facet_age_days *gte 10950 AND facet_age_days *lte 14235) ) 70114 // each range is enclosed in parens, and is joined to other ranges by ' OR '. 70115 // Each range is then composed of two terms joined by an 'AND '. These 70116 // need to be separated and each range's components collected. The components 70117 // are a field, operator and value. Ultimately we want to end up 70118 // with the two value components from each range joined in a string like so [value]_[value], 70119 // in the proper order. The [value]_[value] string is what is used as the ID for 70120 // the checkbox that will need to have its state set. 70121 function parse_range_facet(range_state) { 70122 70123 // this is a job for a parser. 70124 var facet_options = []; 70125 70126 // get rid of the parens and separate all of the ranges 70127 var ranges = $j.trim(range_state.replace(/\(/gi, '').replace(/\)/gi, '')).split(' OR '); 70128 70129 70130 for (var i = 0; i < ranges.length; i++) { 70131 var range = ranges[i]; 70132 var terms = range.split(' AND '); 70133 70134 // each range should have two terms; usually a 'greater-than' and a 'less than' 70135 if (terms.length != 2) 70136 break; 70137 70138 var pair = [] 70139 for (var j = 0; j < terms.length; j++) { 70140 70141 // each term has three components, a name, an operator and a value. 70142 // we don't really care about the operator. 70143 var term = terms[j]; 70144 var components = term.split(' '); 70145 70146 if (components.length != 3) 70147 break; 70148 70149 pair.push({ 70150 name:components[0], 70151 value:components[2] 70152 }); 70153 } 70154 70155 if (pair.length != 2) 70156 break; 70157 70158 // we need to put the values in the proper order. This assumes that the lower value 70159 // always comes first. 70160 var option_id = ''; 70161 try { 70162 if (parseFloat(pair[0].value) <= parseFloat(pair[1].value)) 70163 option_id = pair[0].value + '_' + pair[1].value; 70164 else 70165 option_id = pair[1].value + '_' + pair[0].value; 70166 70167 facet_options.push({ 70168 name:pair[0].name, 70169 id:option_id 70170 }); 70171 } catch (e) { 70172 // not numeric values? 70173 } 70174 } 70175 70176 if (facet_options.length == 0) 70177 return; 70178 70179 // now paste together the bits into the same format that the non-range 70180 // facet objects have. 70181 var new_facet = {}; 70182 var id_list = ''; 70183 for (var k = 0; k < facet_options.length; k++) { 70184 if (k) 70185 id_list += ','; 70186 70187 id_list += facet_options[k].id; 70188 } 70189 70190 new_facet[facet_options[0].name] = id_list; 70191 return(new_facet); 70192 } 70193 70194 function build_search_controls() { 70195 var search_box_html = "<div class='facet_term_controls'>"; 70196 search_box_html += "<div class='faceted_text_and_button'><div class='_search_text_label'>" + _self.definition.input_title + "</div>"; 70197 search_box_html += "<textarea id='_search_text' class='_search_widget_bulk_area _sw_search_text'></textarea>"; 70198 search_box_html += "<div id='_search_button' class='_search_widget_bulk_button'>Search</div></div>"; 70199 search_box_html += "<div class='facet_exact_match'><input type='checkbox' " + (_self.definition.options.exact_match ? 'checked' : '') + " id='_exact_match'/>Show exact matches only</div>"; 70200 search_box_html += "<div class='_search_on_update'><input type='checkbox' " + (_self.definition.options.search_on_update ? 'checked' : '') + " id='_search_on_update'/>Update results when criteria change</div></div>"; 70201 70202 var facet_table_html = build_facet_controls(); 70203 var whole_shebang = "<div class='faceted_search_container'>" + facet_table_html + search_box_html + "</div>"; 70204 70205 _info_widget.set_content(1, 0, ''); 70206 _info_widget.set_content(0, 1, whole_shebang); 70207 _info_widget.set_content(0, 2, ""); 70208 70209 _search_text_box = $j('#_search_text'); 70210 _search_on_update = $j('#_search_on_update'); 70211 var search_button = $j('#_search_button'); 70212 $j('.facet_item_check').bind('click', do_facet_click); 70213 70214 // control-ify 70215 search_button.button(); 70216 search_button.bind('click', do_search); 70217 } 70218 70219 function get_facet_values(state) { 70220 70221 var facets = {}; 70222 var facet_add = false;
70223 70224 var lists = $j('.facet_list_container'); 70225 for (var j = 0; j < lists.length; j++) { 70226 var val_add = false; 70227 var range_add = false; 70228 70229 //var selected_opts = $j('#' + lists[j].id + ' input[checked=true]'); 70230 var selected_opts = $j('#' + lists[j].id + ' input:checked'); 70231 70232 var opts = $j('#' + lists[j].id + ' .facet_item_check'); 70233 70234 var ignore_when_all_selected = $j('#' + lists[j].id).hasClass('ignore_when_all_selected'); 70235 if (ignore_when_all_selected && selected_opts.length == opts.length) 70236 continue; 70237 70238 var vals = ''; 70239 for (var k = 0; k < opts.length; k++) { 70240 if (opts[k].checked) { 70241 70242 var qtype = opts[k].getAttribute('qtype'); 70243 70244 switch (qtype) { 70245 case 'range': 70246 if (vals != '') 70247 vals += ' OR' 70248 else 70249 vals += ' ('; 70250 70251 vals += ' ('; 70252 var extents = opts[k].name.split('_'); 70253 vals += 'facet_' + lists[j].id + ' *gte ' + extents[0]; 70254 vals += ' AND '; 70255 vals += 'facet_' + lists[j].id + ' *lte ' + extents[1]; 70256 vals += ')'; 70257 70258 facet_add = true; 70259 range_add = true; 70260 break; 70261 default: 70262 if (vals != '') 70263 vals += ',' 70264 70265 vals += opts[k].name; 70266 val_add = true; 70267 facet_add = true; 70268 } 70269 70270 } 70271 } 70272 70273 if (val_add) 70274 facets['facet_' + lists[j].id] = vals; 70275 70276 if (range_add) { 70277 vals += ')'; 70278 facets['facet_range_' + lists[j].id] = vals; 70279 } 70280 } 70281 70282 if (facet_add) { 70283 state.facets = facets; 70284 } 70285 70286 return(state); 70287 } 70288 70289 function build_facet_controls() { 70290 70291 var lists = _facets.get_lists(); 70292 if (!lists || lists.length == 0) 70293 return; 70294 70295 // build a table to hold all of the facet lists 70296 var table_html = "<table class='facet_table'><tr>"; 70297 for (var i = 0; i < lists.length; i++) { 70298 70299 // for each facet type... 70300 for (var k = 0; k < lists[i].length; k++) { 70301 70302 // for each facet list create a container 70303 var facet = lists[i][k]; 70304 table_html += "<td>"; 70305 70306 if (facet.ignore_when_all_selected) 70307 table_html += "<div class='facet_list_container ignore_when_all_selected' id='" + facet.id + "'>"; 70308 else 70309 table_html += "<div class='facet_list_container' id='" + facet.id + "'>"; 70310 70311 table_html += "<div class='facet_label'>" + facet.name + "</div>"; 70312 70313 // for each item in the list create a labeled control 70314 var items = facet.items; 70315 for (var j = 0; j < items.length; j++) { 70316 var item = items[j]; 70317 table_html += "<div class='facet_item'>"; 70318 switch (item.type) { 70319 case 'boolean': 70320 table_html += "<input class='facet_item_check' qtype='" + (item.qtype ? item.qtype : 'default') + "' name='" + item.id + "' type='checkbox'" + (item.value == true ? ' checked' : '') + "/>"; 70321 table_html += "<div class='facet_item_label'>" + item.name + "</div>"; 70322 break; 70323 default : 70324 table_html += "<div class='facet_item_label'>" + item.name + "</div>"; 70325 } 70326 table_html += "</div>"; // close facet_item 70327 } 70328 table_html += "</div></td>"; // close facet_list_container and the table cell 70329 } 70330 } 70331 table_html += "</tr></table>"; // close the single row and the facet_table 70332 70333 return(table_html); 70334 } 70335 70336 function do_start_search(event, ui) { 70337 if (_closed) 70338 _search_text_box.autocomplete('close'); 70339 } 70340 70341 function do_open(event, ui) { 70342 if (_closed) 70343 _search_text_box.autocomplete('close'); 70344 } 70345 70346 function do_facet_click(event, ui) { 70347 if (_search_on_update && _search_on_update.is(':checked')) 70348 do_search(event, ui); 70349 } 70350 70351 function get_exact_search_state() { 70352 var exact = $j('#_exact_match'); 70353 70354 return( exact ? exact[0] ? exact[0].checked : false : false ); 70355 } 70356 70357 function do_search(event, ui) { 70358 var term = _search_text_box.val(); 70359 70360 if (_self.settings.on_search) 70361 _self.settings.on_search(_self, get_search_state()); 70362 } 70363
70364 function init() { 70365 load_facets(); 70366 var lists = _facets.get_lists(); 70367 build_search_controls(); 70368 } 70369 70370 function inspect(prefix, obj) { 70371 var s = ""; 70372 for (key in obj) { 70373 if ((typeof obj[key]) == "function") 70374 s = s; 70375 else if ((typeof obj[key]) == "object" && obj[key] !== null & key != "element") 70376 s += "\n" + prefix + "key: " + key + inspect(prefix + " ", obj[key]); 70377 else 70378 s += "\n" + prefix + "key: " + key + ", value: " + obj[key]; 70379 } 70380 return(s); 70381 } 70382 70383 init(); 70384} 70385; 70386 70387 70388 70389function ordered_list(id, name, order, ignore_when_all_selected, items, sub_lists) { 70390 70391 this.id = id; 70392 this.name = name; 70393 this.order = order; 70394 this.ignore_when_all_selected = ignore_when_all_selected; 70395 this.items = items; 70396 this.sub_lists = sub_lists ? sub_lists : []; 70397 70398 this.add_items = function(items) { 70399 this.items = this.items.concat(items); 70400 sort_items(); 70401 } 70402 70403 this.add_sub_lists = function(sub_lists) { 70404 this.sub_lists = this.sub_lists.concat(sub_lists); 70405 sort_lists(); 70406 } 70407 70408 70409 var _self = this; 70410 70411 function init() { 70412 sort_items(); 70413 sort_lists(); 70414 } 70415 70416 function sort_items() { 70417 if(_self.items) 70418 _self.items = _self.items.sort(function(a, b) { return(a.order - b.order);}); 70419 } 70420 70421 function sort_lists() { 70422 if(_self.sub_lists) 70423 _self.sub_lists = _self.sub_lists.sort(function(a, b) { return(a.order - b.order);}); 70424 } 70425 70426 init(); 70427} 70428 70429function list_item(id, name, order, type, value, qtype) { 70430 70431 this.id = id; 70432 this.name = name; 70433 this.order = order; 70434 this.type = type; 70435 this.value = value; 70436 this.qtype = qtype; 70437} 70438; 70439"use strict"; 70440/* 70441 * File: gene_classification_search_plugin.js 70442 * Class: gene_classification_search_plugin 70443 * 70444 * Used to style the UI components for the 'Gene Classification' search modality. 70445 * UI configuration data comes from application.json 70446 */ 70447 70448/** 70449 * 70450 * @param dao 70451 * @param definition has search_type, configuration data 70452 * @param info_widget 70453 * @param options 70454 */ 70455function gene_classification_search_plugin(dao, definition, info_widget, options) { 70456 70457 this.the_dao = dao; 70458 this.info_widget = info_widget; 70459 this.definition = definition; 70460 70461 this.on_search = options ? options.on_search ? options.on_search : null : null; 70462 this.on_error = options ? options.on_error ? options.on_error : null : null; 70463 this.on_ready = options ? options.on_ready ? options.on_ready : null : null; 70464 70465 this.classification_options = options ? (options.gene_classifications? options.gene_classifications : undefined) : undefined; 70466 70467 /** 70468 * Get state object containing properties describing UI element states 70469 * 70470 * @returns {{exact_match: *, search_term: *, search_type: (*|Function|Function|id|Selector.xpath.id|Selector.criteria.id|Function|Function|id|Selector.xpath.id|Selector.criteria.id|Function|Function|id|Selector.xpath.id|Selector.criteria.id|Function|Function|id|id|id|._newInst.id|id|._newInst.id|._newInst.id|._newInst.id|id|ordered_list.id|list_item.id|id|Function|Function|Function|Function|Function|Function|Function|Function|Function|Function|Function|h.id|Function|Function|Function|Function|Function|*|*|Function|Function|id|Y.id|id|Y.id|id|Y.id|Function|Function|Function|Function|AIBS.UI.info_widget.id|id|AIBS.UI.info_widget.id|id|Function|zapGraphics.id|Ajax.InPlaceEditor._form.id|Ajax.InPlaceEditor._form.id|Ajax.InPlaceEditor._form.id|simpleStrip.id|sliderStrip.id|scaleStrip.id|string|string|string|newState.id|id|id|zapviewer.id|id|string|.callee.id|Selector.root.id|string|Selector.root.id|string|Selector.root.id|string|core.id|.attr.id|id|id|uniqueId.id|id|id|uniqueId.id|id)}} 70471 */ 70472 this.get_state = function() { 70473 return(get_search_state()); 70474 } 70475 70476 /** 70477 * Set state of all current UI elements 70478 * 70479 * @param state object with properties describing UI element states 70480 */ 70481 this.set_state = function(state) { 70482 set_search_state(state); 70483 } 70484 70485 /** 70486 * Remove UI elements from the DOM. 70487 * This function will 'undo' the actions of build_gene_search_controls() 70488 * Parent search widget calls this function just before it deletes the plugin object. 70489 */ 70490 this.destroy = function() { 70491 // remove HTML elements (text input, button, checkbox)
70492 // jQuery 'remove' automatically detaches any bound events from DOM elements 70493 $j('#_gene_classification_menu').remove(); 70494 $j('#_gene_classification_search_button').remove(); 70495 70496 // remove any text 70497 _info_widget.set_content(1,0, ""); 70498 _info_widget.set_content(0,1, ""); 70499 } 70500 70501 70502 var _self = this; 70503 var _info_widget = _self.info_widget; 70504 var _classification_menu; 70505 var _search_button; 70506 var _state = { 70507 search_term: 0, // updated via menu choices or by set_search_state() 70508 search_type: this.definition.id 70509 } 70510 70511 /** 70512 * Get search state object, which is comprised of: 70513 * - selected structure id, from Structure dropdown menu 70514 * - search type 70515 * 70516 * @returns {search_term: number, search_type: string} 70517 */ 70518 function get_search_state() { 70519 var state = _state; 70520 70521 return(state); 70522 } 70523 70524 /** 70525 * Set search state of Gene Classification Search UI elements (one menu) 70526 * 70527 * @param state 70528 */ 70529 function set_search_state(state) { 70530 if (state.search_term) { 70531 $j('#_gene_classification_menu').val(state.search_term); 70532 _state.search_term = state.search_term; 70533 } 70534 70535 if(_self.on_ready) 70536 _self.on_ready(); 70537 } 70538 70539 function build_search_controls() { 70540 var title_html = "Find genes corresponding to the gene classifications"; 70541 var classification_menu = build_classification_menu(_self.classification_options); 70542 70543 var menu_and_button_html = '<table><tr>'; 70544 menu_and_button_html += '<td>Gene Classifications<br>' + classification_menu + '</td>'; 70545 menu_and_button_html += '<td> <br><div id="_gene_classification_search_button">Search</div></td>'; 70546 menu_and_button_html += '</tr></table>'; 70547 70548 _info_widget.set_content(1,0, title_html); 70549 _info_widget.set_content(0,1, menu_and_button_html); 70550 70551 _classification_menu = $j('#_gene_classification_menu'); 70552 _search_button = $j('#_gene_classification_search_button'); 70553 70554 // control-ify 70555 _search_button.button(); 70556 70557 // bind events 70558 _classification_menu.bind('change', do_classification_menu_change); 70559 _search_button.bind('click', do_classification_search); 70560 } 70561 70562 70563 /** 70564 * 70565 * @param classifications array of hashes with 'label', 'classification_id', 'value' 70566 * @returns {string} 70567 */ 70568 function build_classification_menu(classifications) { 70569 var classification_id; 70570 var len = classifications.length; 70571 70572 var html = "<select id='_gene_classification_menu' class='_sw_search_text'>"; 70573 html += "<option value='0'>Select to Search</option>"; 70574 for(var i = 0; i < len; i++) { 70575 classification_id = classifications[i].classification_id; 70576 html += "<option value='" + classification_id + "'>" + classifications[i].label + "</option>"; 70577 } 70578 html += "</select>"; 70579 70580 return html; 70581 } 70582 70583 function do_error(xhdr, text_status, error) { 70584 var err = xhdr.msg == undefined ? error : xhdr.msg; 70585 70586 if(_self.on_error) 70587 return(_self.on_error(_self, err)); 70588 70589 throw "Error fetching gene classification set: " + err; 70590 } 70591 70592 function do_classification_menu_change(event, ui) { 70593 var new_term = _classification_menu.val(); 70594 _state.search_term = new_term; 70595 } 70596 70597 /** 70598 * Event handler for 'Search' button 70599 * 70600 * @param event 70601 * @param ui 70602 */ 70603 function do_classification_search(event, ui) { 70604 var classification_id = _state.search_term; 70605 70606 if(!classification_id || classification_id == '0') 70607 return; 70608 70609 if(_self.on_search) 70610 _self.on_search(_self, get_search_state()); 70611 } 70612 70613 function init() { 70614 build_search_controls(); 70615 } 70616 70617 init(); 70618} 70619; 70620"use strict"; 70621/** 70622 * 70623 * @param dao 70624 * @param definition 70625 * @param info_widget 70626 * @param options 70627 */ 70628function gene_search_plugin(dao, definition, info_widget, options) { 70629 70630 this.the_dao = dao; 70631 this.info_widget = info_widget; 70632 this.definition = definition; 70633 70634 this.get_state = function() { 70635 return(get_gene_search_state()); 70636 } 70637 70638 this.set_state = function(state) { 70639 set_gene_search_state(state); 70640 } 70641 70642 /** 70643 * Remove UI elements from the DOM. 70644 * This function will 'undo' the actions of build_gene_search_controls() 70645 * Parent search widget calls this function just before it deletes the plugin object. 70646 */ 70647 this.destroy = function() { 70648 // remove HTML elements (text input, button, checkbox)
70649 // jQuery 'remove' automatically detaches any bound events from DOM elements 70650 $j('#_search_text').remove(); 70651 $j('#_search_button').remove(); 70652 $j('#_search_exact').remove(); 70653 70654 // remove text 70655 _info_widget.set_content(1,0, ""); 70656 _info_widget.set_content(0,2, ""); 70657 } 70658 70659 this.on_search = options ? options.on_search ? options.on_search : null : null; 70660 this.on_error = options ? options.on_error ? options.on_error : null : null; 70661 this.on_ready = options ? options.on_ready ? options.on_ready : null : null; 70662 70663 70664 var _self = this; 70665 var _info_widget = _self.info_widget; 70666 var _type_ahead_callback = null; 70667 var _closed = true; 70668 var _selected_item = false; 70669 var _search_text_box = null; 70670 70671 70672 function get_gene_search_state() { 70673 var exact = get_exact_search_state(); 70674 var type = $j('input[name=search_type]:checked').val(); 70675 var term = _search_text_box.val(); 70676 if(_selected_item) 70677 exact = true; 70678 70679 var state = { 70680 exact_match:exact, 70681 search_term:term, 70682 search_type:_self.definition.id 70683 } 70684 return(state); 70685 } 70686 70687 function set_gene_search_state(state) { 70688 var exact = document.getElementById('_search_exact'); 70689 if(exact) 70690 exact.checked = state.exact_match; 70691 70692 if(state.search_term) 70693 _search_text_box.val( state.search_term ); 70694 70695 if(_self.on_ready) 70696 _self.on_ready(); 70697 } 70698 70699 function build_gene_search_controls() { 70700 70701 var search_box_html = "<input type='text' id='_search_text' class='_sw_search_text'/>"; 70702 search_box_html += "<div id='_search_button'>Search</div>"; 70703 var exact_html = "<input type='checkbox' id='_search_exact'/>Show exact matches only"; 70704 70705 _info_widget.set_content(1,0, "Enter Gene Name, Gene Symbol, Homolog, Entrez Gene ID or NCBI Accession number:"); 70706 _info_widget.set_content(0,1, search_box_html); 70707 _info_widget.set_content(0,2, exact_html); 70708 70709 _search_text_box = $j('#_search_text'); 70710 var search_button = $j('#_search_button'); 70711 70712 // control-ify 70713 search_button.button(); 70714 search_button.bind('click', do_gene_search); 70715 70716 _search_text_box.autocomplete( 70717 { 70718 source:do_gene_auto_complete, 70719 select:do_gene_search, 70720 search:do_start_search, 70721 open:do_open, 70722 appendTo:_self.info_widget.container, 70723 delay:100 70724 }); 70725 70726 _search_text_box.bind('keyup', do_gene_enter_check); 70727 } 70728 70729 function do_start_search(event, ui) { 70730 if(_closed) 70731 _search_text_box.autocomplete('close'); 70732 } 70733 70734 function do_open(event, ui) { 70735 if(_closed) 70736 _search_text_box.autocomplete('close'); 70737 } 70738 70739 function do_gene_auto_complete(request, response) { 70740 _type_ahead_callback = response; 70741 var opts = { 70742 success_callback:gene_autocomplete_responder, 70743 error_callback:dummy, 70744 limit:10, 70745 exact_match:get_exact_search_state() 70746 }; 70747 70748 _self.the_dao.gene_typeahead(request.term, opts); 70749 } 70750 70751 function gene_autocomplete_responder(response_data, text_status, xhdr) { 70752 70753 if(!response_data) 70754 return; 70755 70756 var ret = []; 70757 var len = response_data.length; 70758//TODO: get rid of the explicit reference to a field name 70759 for(var i = 0; i < len; i++) 70760 ret.push(response_data[i].term); 70761 70762 _type_ahead_callback(ret); 70763 } 70764 70765 function dummy(xhdr, text_status, error) { 70766 // swallow this type ahead error 70767 } 70768 70769 function get_exact_search_state() { 70770 var exact = $j('#_search_exact'); 70771 70772 return( exact ? exact[0] ? exact[0].checked : false : false ); 70773 } 70774 70775 function do_gene_search(event, ui) { 70776 var term = ''; 70777 if(ui && ui.item) { 70778 //term = '"' + ui.item.value + '"'; 70779 _search_text_box.val(ui.item.value); 70780 70781 _selected_item = true; 70782 } 70783 else { 70784 term = _search_text_box.val(); 70785 _selected_item = false;
70786 70787 if(!term || term.length < 1) 70788 return(false); 70789 } 70790 70791 if(_self.on_search) 70792 _self.on_search(_self, get_gene_search_state()); 70793 70794 return(false); 70795 } 70796 70797 function do_gene_enter_check(event, ui) { 70798 if(event.keyCode == 13) { 70799 _search_text_box.autocomplete('close'); 70800 _closed = true; 70801 do_gene_search(null, null); 70802 } else { 70803 _closed = false; 70804 } 70805 } 70806 70807 function init() { 70808 build_gene_search_controls(); 70809 } 70810 70811 init(); 70812} 70813; 70814"use strict"; 70815/** 70816 * 70817 * @param container 70818 * @param dao 70819 * @param plugins array of plugin definitions, comes from application.json 70820 * @param options 70821 */ 70822function ish_search_widget(container, dao, plugins, options) { 70823 70824// public properties 70825 this.container = $j(container); 70826 this.the_dao = dao; 70827 this.options = options; 70828 this.plugins = plugins; 70829 this.stage_list = options ? ( options.stage_list ? options.stage_list : null ) : null; 70830 this.structure_list = options ? ( options.structure_list ? options.structure_list : null ) : null; 70831 70832// public methods 70833 this.id = function () { 70834 if (_info_widget) 70835 return(_info_widget.id); 70836 70837 return(null); 70838 }; 70839 70840 this.get_state = function () { 70841 return(do_get_state()); 70842 }; 70843 70844 this.set_state = function (new_state) { 70845 do_set_state(new_state); 70846 }; 70847 70848 this.get_definition = function() { 70849 if(_current_plugin) 70850 return(_current_plugin.definition); 70851 70852 return({}); 70853 }; 70854 70855// event hooks 70856 this.on_search = options ? options.on_search ? options.on_search : null : null; 70857 this.on_search_type_change = options ? options.on_search_type_change ? options.on_search_type_change : null : null; 70858 this.on_ontology_error = options ? options.on_ontology_error ? options.on_ontology_error : do_ontology_error : do_ontology_error; 70859 this.on_search_ready = options ? options.on_search_ready ? options.on_search_ready : null : null; 70860 this.on_search_error = options ? options.on_search_error ? options.on_search_error : null : null; 70861 this.on_help = options ? options.on_help ? options.on_help : null : null; 70862 70863 70864// private properties 70865 var _self = this; 70866 var _info_widget = null; 70867 var _installed_plugins = {}; 70868 var _current_plugin = null; 70869 70870 70871 function by_plugin_order_field(a, b) { 70872 return(a.order - b.order); 70873 } 70874 70875 /** 70876 * Gets plugin definition hash 70877 * 70878 * @param id hash key, value uniquely identifies a plugin 70879 * @returns {*} 70880 */ 70881 function plugin_definition_by_id(id) { 70882 for (var i = 0; i < _self.plugins.length; i++) 70883 if (_self.plugins[i].id == id) 70884 return(_self.plugins[i]); 70885 70886 return(null); 70887 } 70888 70889 /** 70890 * Called whenever the user switches search type (radio button change) 70891 * 70892 * @param plugin 70893 * @returns {Object} 70894 */ 70895 function install_plugin(plugin) { 70896 var plugin_options = {}; 70897 $j.extend(true, plugin_options, plugin.options); 70898 70899 // see if there are any external options (not from application.json) defined specifically for this plugin. 70900 // Assume that the caller to the ish_search_widget will set a hash key with the plugin's 'id' as an identifier. 70901 if (_self.options[plugin.id]) 70902 plugin_options[plugin.id] = _self.options[plugin.id]; 70903 70904 // add options from application.json 70905 if (plugin.options) 70906 $j.extend(true, plugin_options, plugin.options); 70907 70908 plugin_options.on_search = do_search; 70909 plugin_options.on_ready = do_ready; 70910 plugin_options.on_error = do_error; 70911 plugin_options.on_help = do_help; 70912 plugin_options.stage_list = _self.stage_list; 70913 plugin_options.structure_list = _self.structure_list; 70914 70915 70916 var instance_str = 'new ' + plugin.name + '(_self.the_dao, plugin, _info_widget, plugin_options);'; 70917 var p = eval(instance_str); 70918 _installed_plugins[plugin.id] = p; 70919 return(p); 70920 } 70921 70922 function do_search(sender, state) { 70923 state.page_num = 0; 70924 70925 // hidden plugins should go back to being hidden when a search is initiated on 70926 // any other search. 70927 for (var i = 0; i < _self.plugins.length; i++) { 70928 var plugin = _self.plugins[i]; 70929
70930 var input = $j('input[name=search_type]').filter('[value=' + plugin.id + ']'); 70931 70932 if (plugin.hidden == true && plugin.id != state.search_type) { 70933 input.parent().hide(); 70934 } 70935 } 70936 70937 if (_self.on_search) 70938 _self.on_search(state); 70939 } 70940 70941 function do_ready() { 70942 if (_self.on_search_ready) 70943 _self.on_search_ready(); 70944 } 70945 70946 70947 function do_error(msg) { 70948 if(_self.on_search_error) 70949 _self.on_search_error(msg); 70950 } 70951 70952 function do_help(url) { 70953 if (_self.on_help) 70954 _self.on_help(url); 70955 } 70956 70957 function remove_plugin(plugin_id) { 70958 // do we need to do some manual removal of DOM elements? probably. 70959 var plugin = _installed_plugins[plugin_id]; 70960 if (plugin.destroy) 70961 plugin.destroy(); 70962 delete _installed_plugins[plugin_id]; 70963 } 70964 70965 function install_default_plugin() { 70966 for (var i = 0; i < _self.plugins.length; i++) { 70967 if (_self.plugins[i].default_search) { 70968 _current_plugin = install_plugin(_self.plugins[i]); 70969 return; 70970 } 70971 } 70972 } 70973 70974 function do_get_state() { 70975 var state = _current_plugin.get_state(); 70976 if (state) 70977 state.search_type = _current_plugin.definition.id; 70978 return(state); 70979 } 70980 70981 function do_set_state(state) { 70982 if (!_current_plugin) 70983 return; 70984 70985 var type = state.search_type; 70986 70987 var input = $j('input[name=search_type]').filter('[value=' + type + ']').attr('checked', 'checked'); 70988 70989 // if the containing nobr was hidden, unhide it now that it's been requested. 70990 input.parent().show(); 70991 70992 do_search_type_change(); 70993 _current_plugin.set_state(state); 70994 } 70995 70996 function build_table() { 70997 70998 // describe the table 70999 var layout = { 71000 columns: 3, 71001 rows: 3, 71002 spans: [ 71003 {row: 0, column: 0, column_span: 1, row_span: 3}, 71004 {row: 0, column: 1, column_span: 2, row_span: 1}, 71005 {row: 1, column: 1, column_span: 2, row_span: 1}, 71006 {row: 2, column: 1, column_span: 2, row_span: 1}, 71007 {row: 0, column: 2, column_span: 1, row_span: 1} 71008 ] 71009 }; 71010 // describe how data should be mapped to cells in the table 71011 var data_map = []; 71012 // which cells require a callback to massage the data 71013 // presentation before placing it in the table. 71014 var decorators = {}; 71015 71016 var options = { 71017 table_class: '_search_widget' 71018 }; 71019 71020 // build the widget. 71021 _info_widget = new AIBS.UI.info_widget(_self.container, layout, data_map, decorators, options); 71022 } 71023 71024 function build_search_type_controls() { 71025 71026 plugins.sort(by_plugin_order_field); 71027 71028 var control_html = "<div id='_search_type_wrapper'>"; 71029 71030 for (var i = 0; i < _self.plugins.length; i++) { 71031 var p = _self.plugins[i]; 71032 // The option for hiding a control if desired was for agea search; you can only 71033 // do an agea search via the URL string (entry from the agea page), so hide it 71034 // until the controller explicitly requests a hidden search type. 71035 control_html += "<div" + (p.hidden == true ? " style=\"display:none\"" : "") + ">"; 71036 control_html += "<input type='radio' id='" + p.id + "' name='search_type' "; 71037 control_html += p.default_search == true ? 'checked ' : ' '; 71038 control_html += "value='" + p.id + "'/> "; 71039 control_html += "<label for='" + p.id + "'>" + p.label + "</label>"; 71040 control_html += "</div>"; 71041 } 71042 control_html += "</div>"; 71043 71044 _info_widget.set_content(0, 0, control_html); 71045 $j('#_search_type_wrapper input[name=search_type]:radio').bind('click', do_search_type_change); 71046 } 71047 71048 /** 71049 * Event callback in response to click on on a Search radio button or a call to do_set_state() 71050 */ 71051 function do_search_type_change() { 71052 var new_type = $j('input[name=search_type]:checked').get(0).value; 71053 var new_definition = plugin_definition_by_id(new_type); 71054 71055 // only do something if the requested type differs from the current type 71056 if (_current_plugin.definition.id != new_definition.id) { 71057 71058 // Make sure this is empty before plugins start laying things out. 71059 for (var col = 1; col < 3; col++) 71060 for (var row = 0; row < 3; row++) 71061 if (_info_widget.get_cell(row, col)) _info_widget.set_content(col, row, ''); 71062 71063 remove_plugin(_current_plugin.definition.id); 71064 _current_plugin = install_plugin(new_definition); 71065 71066 if (_self.on_search_type_change) 71067 _self.on_search_type_change(_self, new_type); 71068 } 71069 } 71070
71071 function init() { 71072 build_table(); 71073 build_search_type_controls(); 71074 install_default_plugin(); 71075 } 71076 71077 init(); 71078 71079} 71080; 71081"use strict"; 71082/** 71083 * 71084 * @param container 71085 * @param state 71086 * @param options 71087 */ 71088function manual_annotation_query_builder(container, state, options) { 71089 71090 this.container = $j(container); 71091 this.state = state; 71092 this.settings = { 71093 "max_rows": 4, 71094 on_ready: null, 71095 on_error: null, 71096 on_search: null, 71097 on_remove_row: null, 71098 on_add_row: null, 71099 on_help: null 71100 }; 71101 71102 $j.extend(true, this.settings, options); 71103 71104 71105 this.set_state = function (new_state) { 71106 71107 if (!new_state) 71108 return; 71109 71110 for (var i = 0; i < new_state.length; i++) { 71111 71112 var state = new_state[i]; 71113 71114 if (i) 71115 add_query_row(state, false); 71116 else { 71117 // this is the first row, and we've already built that 71118 // as part of the initialization, so fetch the first row 71119 // from the collection and set its state. 71120 if (!_query_rows) 71121 return; 71122 71123 for (var key in _query_rows) { 71124 71125 if (!_query_rows[key].settings.header_only) 71126 _query_rows[key].set_state(state); 71127 } 71128 } 71129 71130 } 71131 71132 // do_all_ready(); 71133 }; 71134 71135 this.get_state = function () { 71136 71137 var state = { 71138 query_set: [] 71139 }; 71140 71141 for (var key in _query_rows) { 71142 71143 if (!_query_rows[key].settings.header_only) { 71144 71145 var row_state = _query_rows[key].get_state(); 71146 state.query_set.push(row_state); 71147 } 71148 } 71149 71150 return(state); 71151 }; 71152 71153 /** 71154 * Mark the controls in the query builder that are not in a valid state to perform a query. 71155 */ 71156 this.validate_state = function () { 71157 71158 var all_valid = true; 71159 71160 for (var key in _query_rows) { 71161 71162 if (!_query_rows[key].settings.header_only) { 71163 71164 var row_state = _query_rows[key].get_state(); 71165 var control_index = 0; 71166 for (var control in row_state) { 71167 71168// trace("control: " + control + " has value " + row_state[control] + " and length " + row_state[control].length); 71169 //Almost any state is valid, as long as every row has a value for each control 71170 var valid = (row_state[control] && row_state[control].length > 0); 71171 _query_rows[key].set_control_class(control_index++, 'invalid', !valid); 71172 71173 all_valid = all_valid && valid; 71174 } 71175 } 71176 } 71177 71178 return(all_valid); 71179 }; 71180 71181 71182 var _self = this; 71183 var _id = 'manual_annotation_query_builder_' + Math.floor(Math.random() * 10001); 71184 var _table = null; 71185 var _query_rows = {}; 71186 var _row_count = 0; 71187 var _ready_count = 0; 71188 71189 71190 function build_scaffold() { 71191 71192 var html = "<table class='manual_anno_qbuilder_table' id='" + _id + "'></table>"; 71193 _table = $j(html).appendTo(_self.container); 71194 } 71195 71196 71197 function add_query_row(row_state, header_only) { 71198 71199 var row = _table[0].insertRow(-1); 71200 var cell = row.insertCell(-1); 71201 var index = _table[0].rows.length - 1; 71202 var qrow = new query_row( 71203 cell, 71204 index, 71205 row_state, 71206 { 71207 layout: _self.settings.row_layout, 71208 on_ready: do_row_ready, 71209 on_add_row: add_query_row, 71210 on_delete_row: remove_query_row, 71211 on_search: _self.settings.on_search, 71212 on_help: _self.settings.on_help, 71213 header_only: !!header_only 71214 }); 71215 71216 var qrow_id = qrow.get_id(); 71217 $j(row).attr('q_id', qrow_id); 71218 _query_rows[qrow_id] = qrow; 71219 71220 if (!header_only) 71221 _row_count++; 71222 71223 update_row_manage_states(); 71224 71225 if (_self.settings.on_add_row) 71226 _self.settings.on_add_row(); 71227 } 71228 71229 function remove_query_row(row) { 71230 71231 var row_key = row.get_id(); 71232 _query_rows[row_key].destroy(); 71233 71234 for (var i = 0; i < _table[0].rows.length; i++) { 71235 71236 var r = _table[0].rows[i]; 71237 if ($j(r).attr('q_id') == row_key) { 71238 _table[0].deleteRow(i); 71239 break; 71240 } 71241 } 71242 71243 delete _query_rows[row_key]; 71244 71245 _row_count--; 71246 update_row_manage_states(); 71247 if (_self.settings.on_remove_row) 71248 _self.settings.on_remove_row(); 71249 } 71250 71251 function update_row_manage_states() { 71252 71253 var i = 0; 71254 for (var row in _query_rows) { 71255 71256 if (!_query_rows[row].settings.header_only) { 71257 71258 var first = !i; 71259 var last = (++i == _row_count); 71260 var terminal = (i == _self.settings.max_rows); 71261 _query_rows[row].set_row_manage_state(first, last, terminal); 71262 } 71263 } 71264 71265 } 71266 71267 function do_row_ready(query_row) { 71268 71269 if (++_ready_count == _row_count) 71270 do_all_ready(); 71271 } 71272 71273 function do_all_ready() { 71274 71275 if (_self.settings.on_ready) 71276 _self.settings.on_ready(_self); 71277 } 71278
71279 function init() { 71280 71281 build_scaffold(); 71282 add_query_row(null, true); 71283 add_query_row(null, false); 71284 } 71285 71286 71287 init(); 71288 71289} 71290 71291 71292AIBS = AIBS || {}; 71293AIBS.Components = AIBS.Components || {}; 71294AIBS.Components.control_builders = { 71295 71296 build_ontology_select: function (container, scope, config) { 71297 71298 // the 'control' object: 71299 var control = { 71300 71301 html_control: null, 71302 widget: null, 71303 config: config, 71304 71305 get_state: function () { 71306 71307 var state = []; 71308 var structure_tags = this.widget.get_state(); 71309 for (var i = 0; i < structure_tags.length; i++) { 71310 71311 state.push(structure_tags[i].value); 71312 } 71313 return(state); 71314 }, 71315 71316 set_state: function (state) { 71317 71318 this.widget.set_state(state) 71319 }, 71320 71321 name: "ontology_autocomplete", 71322 71323 set_class: function (class_name, add) { 71324 71325 if (add) 71326 $j(this.html_control[0].parentNode).find('.tagsinput').addClass(class_name); 71327 else 71328 $j(this.html_control[0].parentNode).find('.tagsinput').removeClass(class_name); 71329 } 71330 }; 71331 71332 // first build a regular input element 71333 var html = "<input type='text' class='" + config.class_name + "_ontology_autocomplete' style='width:135px'/>"; 71334 control.html_control = $j(html).appendTo(container); 71335 71336 // then ontology_autocompletify it. 71337 control.widget = new AIBS.Components.ontology_component( 71338 control.html_control, 71339 config.ontology_url, 71340 { 71341 on_ready: config.on_ready, 71342 ontology_options: config.options.ontology_options 71343 }); 71344 71345 return(control); 71346 }, 71347 71348 build_dropdown_checklist: function (container, scope, config) { 71349 71350 71351 // the 'control' object: 71352 var control = { 71353 71354 html_control: null, 71355 config: config, 71356 71357 get_state: function () { 71358 71359 var selected = []; 71360 var options = this.html_control[0].options; 71361 for (var i = 0; i < options.length; i++) { 71362 71363 if (options[i].selected && (options[i].value != "Any")) 71364 selected.push(options[i].value); 71365 } 71366 return(selected); 71367 }, 71368 71369 set_state: function (state) { 71370 71371 if (!state) 71372 return; 71373 71374 var options = this.html_control[0].options; 71375 71376 71377 for (var j = 0; j < options.length; j++) 71378 options[j].selected = ($j.inArray(options[j].value, state) >= 0); 71379 71380 this.html_control.dropdownchecklist('refresh'); 71381 }, 71382 71383 name: "dropdown_checklist", 71384 71385 set_class: function (class_name, add) { 71386 71387 if (add) 71388 $j(this.html_control[0].parentNode).find('.ui-dropdownchecklist').addClass(class_name); 71389 else 71390 $j(this.html_control[0].parentNode).find('.ui-dropdownchecklist').removeClass(class_name); 71391 } 71392 }; 71393 71394 // first build a regular select element 71395 var item_name, item_value; 71396 var html = "<select class='" + config.class_name + "_dropdowncheck' multiple='multiple'>"; 71397 for (var i = 0, len = config.options.values.length; i < len; ++i) { 71398 var item = config.options.values[i]; 71399 // values can either be an array of strings or an array of hashes {name:<item name>, value:<item value} 71400 if (typeof item == "string") { 71401 item_name = item_value = item; 71402 } 71403 else { 71404 item_name = item.name; 71405 item_value = item.value; 71406 } 71407 var selected = (config.options.selected && config.options.selected[item_name]) ? 'selected' : ''; 71408 html += "<option value='" + item_value + "' " + selected + ">" + item_name + "</option>"; 71409 } 71410 html += "</select>"; 71411
71412 control.html_control = $j(html).appendTo(container); 71413 71414 // then dropdownchecklistify it 71415 control.html_control.dropdownchecklist({firstItemChecksAll: true, width: config.width}); 71416 71417 return(control); 71418 }, 71419 71420 build_deleter_adder: function (container, scope, config) { 71421 71422 var _id = scope.get_id(); 71423 71424 // the 'control' object: 71425 var control = { 71426 71427 html_control: null, 71428 config: config, 71429 71430 get_state: function () { 71431 71432 return(null); 71433 }, 71434 71435 set_state: function (state) { 71436 71437 }, 71438 71439 name: "deleter_adder" 71440 }; 71441 71442 // make a couple of buttons for deleting the row or adding a new row 71443 var html = "<div id='" + _id + "deleter_adder_wrapper' class='deleter_adder_wrapper'>"; 71444 html += "<div id='" + _id + "adder_button' class='adder_deleter_button' title='Add another criteria row'></div>"; 71445 html += "<div id='" + _id + "deleter_button' class='adder_deleter_button' title='Remove criteria row'></div>"; 71446 html += "<span id='" + _id + "search_button' class='adder_deleter_search_button' title='Search'>Search</span>"; 71447 html += "</div>"; 71448 71449 control.html_control = $j(html).appendTo(container); 71450 71451 // add handlers for the button click events 71452 $j("#" + _id + "deleter_button").button({ 71453 icons: { 71454 primary: "ui-icon-closethick" 71455 }, 71456 text: false 71457 }).bind('click', function () { 71458// trace("delete row"); 71459 if (scope.settings.on_delete_row) 71460 scope.settings.on_delete_row(scope); 71461 }); 71462 71463 $j("#" + _id + "adder_button").button({ 71464 icons: { 71465 primary: "ui-icon-plusthick" 71466 }, 71467 text: false 71468 }).bind('click', function () { 71469// trace("add row"); 71470 if (scope.settings.on_add_row) 71471 scope.settings.on_add_row(null); 71472 }); 71473 71474 $j("#" + _id + "search_button").button({icons: {}}).bind('click', function () { 71475// trace("do search"); 71476 if (scope.settings.on_search) 71477 scope.settings.on_search(null); 71478 }); 71479 } 71480 71481 71482}; 71483"use strict"; 71484/** 71485 * 71486 * @param container 71487 * @param index 71488 * @param state 71489 * @param options 71490 */ 71491function query_row(container, index, state, options) { 71492 71493 this.container = $j(container); 71494 this.index = index; 71495 this.state = state; 71496 this.settings = { 71497 "header_only":false, 71498 "on_ready":null, 71499 "on_delete_row":null, 71500 "on_add_row":null, 71501 "on_search":null, 71502 "on_help":null 71503 }; 71504 $j.extend(true, this.settings, options); 71505 71506 this.set_state = function(new_state) { 71507 71508 if(!new_state || !_controls) 71509 return; 71510 71511 for(var key in new_state) { 71512 71513 var field = _controls[key]; 71514 if(field && field.control) 71515 field.control.set_state(new_state[key]); 71516 } 71517 }; 71518 71519 this.get_state = function() { 71520 71521 var state = {}; 71522 for(var key in _controls) { 71523 71524 if(_controls[key].control) 71525 state[key] = _controls[key].control.get_state(); 71526 } 71527 71528 return(state); 71529 }; 71530 71531 this.destroy = function() { 71532 71533 if(_controls["structure"]) 71534 _controls["structure"].control.widget.destroy(); 71535 71536 $j('#'+_id).remove(); 71537 }; 71538 71539 this.get_id = function() { 71540 71541 return(_id); 71542 }; 71543 71544 this.set_row_manage_state = function(first, last, terminal) { 71545 71546 set_manage_button_state(first, last, terminal); 71547 }; 71548 71549 this.set_control_class = function(control_index, class_name, add) { 71550 71551 var index = 0; 71552 for(var key in _controls) { 71553 71554 if(index++ == control_index) { 71555 71556 var control = _controls[key].control; 71557//trace("matched control index " + control_index + ", control = " + control + ", class = " + control.name); 71558 71559 if(control.set_class) { 71560 71561// trace("set class method available, add class = " + add + ", class_name = " + class_name); 71562 control.set_class(class_name, add); 71563 return; 71564 } 71565 } 71566 } 71567 } 71568 71569 var _self = this; 71570 var _id = 'qbuilder_row' + Math.floor(Math.random() * 10001); 71571 var _table = null; 71572 var _controls = {}; 71573 var _control_wait_count = 0; 71574 71575 71576 function set_manage_button_state(first, last, terminal) { 71577 71578 if(terminal) { 71579 71580 $j('#' + _id + 'adder_button').hide(); 71581 $j('#' + _id + 'deleter_button').show(); 71582 $j('#' + _id + 'search_button').show(); 71583 return; 71584 } 71585 71586 if(first && last) { 71587 71588 $j('#' + _id + 'adder_button').show(); 71589 $j('#' + _id + 'deleter_button').hide(); 71590 $j('#' + _id + 'search_button').show(); 71591 return; 71592 } 71593 71594 if(first && !last) { 71595 71596 $j('#' + _id + 'adder_button').hide(); 71597 $j('#' + _id + 'deleter_button').show(); 71598 $j('#' + _id + 'search_button').hide(); 71599 return; 71600 } 71601 71602 if(!first && last) { 71603 71604 $j('#' + _id + 'adder_button').show(); 71605 $j('#' + _id + 'deleter_button').show(); 71606 $j('#' + _id + 'search_button').show(); 71607 return; 71608 } 71609 71610 if(!first && !last) { 71611 71612 $j('#' + _id + 'adder_button').hide(); 71613 $j('#' + _id + 'deleter_button').show(); 71614 $j('#' + _id + 'search_button').hide(); 71615 return; 71616 } 71617 71618 } 71619 71620 function build_scaffold() { 71621 71622 // the container here is expected to be a <td> but could be anything... 71623 var html = "<table class='builder_row_table' id='" + _id + "'>"; 71624 71625 if(_self.settings.header_only) { 71626 71627 html += "<tr>"; 71628 for(var item in _self.settings.layout) { 71629 var control = _self.settings.layout[item]; 71630 71631 if(control.label == '') { 71632 71633 html += "<td class='" + control.html_class + "_header final'>"; 71634 html += control.label; 71635 html += "<div class='man_anno_search_help' id='" + _id + "_help_button'></div>"; 71636 html += "</td>"; 71637 } 71638 else { 71639 71640 html += "<td class='" + control.html_class + "_header'>" + control.label + "</td>"; 71641 } 71642 } 71643 html += "</tr></table>"; 71644 } 71645 else { 71646 71647 // a row for the controls 71648 html += "<tr>"; 71649 for(item in _self.settings.layout) { 71650 control = _self.settings.layout[item]; 71651 html += "<td id='" + _id + item + "' class='" + control.html_class + "'></td>"; 71652 } 71653 html += "</tr></table>"; 71654 } 71655 71656 _table = $j(html).appendTo(_self.container); 71657 var help = $j('#' + _id + '_help_button'); 71658 if(help && help.length) 71659 help.button({icons:{ 71660 primary:"ui-icon-help" 71661 }, 71662 text:false 71663 }).bind('click', function() { 71664// trace("manual search help"); 71665 do_help(); 71666 if(_self.settings.on_help) 71667 _self.settings.on_help(); 71668 }); 71669 71670 } 71671 71672 function do_help() { 71673 71674 if(_self.settings.help_url) 71675 window.open(_self.settings.help_url, '_help', 'status=0,toolbar=0,menubar=0,width=800,height=600,scrollbars=1'); 71676 } 71677 71678 function build_controls() { 71679 var build_delegate_fnc; 71680 71681 if(_self.settings.header_only) 71682 return; 71683 71684 for(var item in _self.settings.layout) { 71685 71686 var container = $j('#' + _id + item); 71687 var config = _self.settings.layout[item]; 71688 71689 if(config.control.wait_for_ready) { 71690 config.control.on_ready = control_ready; 71691 _control_wait_count++; 71692 } 71693 71694 if(config.control.build_delegate) { 71695 build_delegate_fnc = eval(config.control.build_delegate); // convert JSON string to function reference 71696 _controls[item] = { 71697 id:item, 71698 control:build_delegate_fnc(container, _self, config.control) 71699 }; 71700 } 71701 else 71702 container.text("Build a control around here."); 71703 } 71704 } 71705 71706 function control_ready() { 71707 71708 if(--_control_wait_count == 0) { 71709 71710 if(_self.settings.on_ready) 71711 _self.settings.on_ready(_self); 71712 } 71713 } 71714
71715 function init() { 71716 71717 build_scaffold(); 71718 build_controls(); 71719 71720 if(_self.state) 71721 _self.set_state(_self.state); 71722 } 71723 71724 init(); 71725} 71726; 71727"use strict"; 71728/** 71729 * It would be really nice to be able to simply JSON.stringify 71730 * and JSON.parse the query_set. Unfortunately the History component 71731 * used in this app URLdecodes anything pushed onto the state stack and 71732 * puts it on the URL. This breaks a page reload. Someday soon that 71733 * History component should be replaced with a better-behaving version, 71734 * at which point these ugly methods can be replaced with JSON.parse & 71735 * JSON.stringify. 71736 * 71737 * The query_set is an Array of query_rows, each of which contains these fields: 71738 * row[n] = { 71739 * structure:[], // values are structure ids 71740 * age:[], 71741 * level:[], 71742 * density:[] 71743 * }; 71744 * 71745 * @param init_term This object can be initialized with either an encoded string or a query_set object. 71746 */ 71747function manual_annotation_query_set(init_term) { 71748 71749 this.query_set = []; 71750 71751 this.stringify = function () { 71752 var encoded_term = "qset"; 71753 for (var i = 0; i < this.query_set.length; i++) { 71754 var qrow = this.query_set[i]; 71755 encoded_term += "+s" + i + "*" + qrow.structure.join(','); 71756 encoded_term += "+a" + i + "*" + qrow.age.join(','); 71757 encoded_term += "+i" + i + "*" + qrow.intensity.join(','); 71758 encoded_term += "+d" + i + "*" + qrow.density.join(','); 71759 } 71760 71761 encoded_term = encoded_term.replace('qset+', ''); 71762 return(encoded_term); 71763 }; 71764 71765 /** 71766 * 71767 * @param str encoded string 71768 * @returns {Array} query set 71769 */ 71770 this.parse = function (str) { 71771 this.query_set = []; 71772 71773 // convert the string into a hash of term/value pairs 71774 var terms = decodeURIComponent(str).replace('qset+', '').split('+'); 71775 var term_hash = {}; 71776 for (var i = 0; i < terms.length; i++) { 71777 var pair = terms[i].split('*'); 71778 if (pair && pair.length && pair.length == 2) 71779 term_hash[pair[0]] = pair[1]; 71780 } 71781 71782 // There is a variable number of query rows. 71783 // Every row should have at least one structure. 71784 // All other fields are optional. 71785 var count = 0; 71786 var done = false; 71787 while (!done) { 71788 var structure = term_hash[("s" + count)]; 71789 if (!structure) 71790 done = true; 71791 else { 71792 var row = { 71793 structure: [], 71794 age: [], 71795 intensity: [], 71796 density: [] 71797 }; 71798 71799 row.structure = structure.split(','); 71800 71801 if (term_hash[("a" + count)]) 71802 row.age = term_hash[("a" + count)].split(','); 71803 71804 if (term_hash[("i" + count)]) 71805 row.intensity = term_hash[("i" + count)].split(','); 71806 71807 if (term_hash[("d" + count)]) 71808 row.density = term_hash[("d" + count)].split(','); 71809 71810 this.query_set.push(row); 71811 } 71812 71813 count++; 71814 } 71815 71816 return(this.query_set); 71817 }; 71818 71819 // This object can be initialized with either an encoded string or a query_set object. 71820 if (init_term) { 71821 if (typeof init_term === 'string') 71822 this.parse(init_term); 71823 else if (Object.prototype.toString.call(init_term) === '[object Array]') 71824 this.query_set = init_term; 71825 else 71826 throw "Unknown initializer for manual_annotation_query_set."; 71827 } 71828 71829} 71830; 71831"use strict"; 71832/** 71833 * 71834 * @param dao 71835 * @param definition 71836 * @param info_widget 71837 * @param options 71838 */ 71839function manual_annotation_search_plugin(dao, definition, info_widget, options) { 71840 71841 this.the_dao = dao; 71842 this.info_widget = info_widget; 71843 this.definition = definition; 71844 this.help_url = options ? options[definition.id] ? options[definition.id].help_url : '/' : '/'; 71845 this.on_search = null; 71846 this.on_error = null; 71847 this.on_ready = null; 71848 this.settings = { 71849 71850 }; 71851 $j.extend(true, this.settings, options); 71852 71853 this.get_state = function(encode) { 71854 71855 return(get_search_state(encode)); 71856 }; 71857 71858 this.set_state = function(state) { 71859 71860 if(_state_set) 71861 return; 71862 71863 load_state(state); 71864 }; 71865 71866 this.on_search = options ? options.on_search ? options.on_search : null : null; 71867 this.on_error = options ? options.on_error ? options.on_error : null : null; 71868 this.on_ready = options ? options.on_ready ? options.on_ready : null : null; 71869 this.on_help = options ? options.on_help ? options.on_help : null : null; 71870 71871 71872 71873 var _self = this; 71874 var _info_widget = _self.info_widget; 71875 var _query_builder = null; 71876 var _all_ready = false;
71877 var _state_set = false; 71878 var _queued_state = null; 71879 71880 71881 function get_color_map() { 71882 71883 try { 71884 var url = window.location.href; 71885 var qstring = url.split('?')[1]; 71886 var pairs = qstring.split('&'); 71887 var params = {}; 71888 for(var i = 0; i < pairs.length; i++) { 71889 71890 var pair_vals = pairs[i].split('='); 71891 if(pair_vals && pair_vals.length == 2) 71892 params[pair_vals[0]] = pair_vals[1]; 71893 } 71894 71895 return(params['colormap']); 71896 71897 } catch(e) { 71898 71899 trace("failed parsing debug options: " + e); 71900 } 71901 71902 return(null); 71903 } 71904 71905 function get_search_state(encode) { 71906 71907 //trace("returning empty state from the manual annotation search plugin"); 71908 var state = { 71909 search_type:"manual_annotation", 71910 search_term:"" 71911 }; 71912 71913 if(!_query_builder || !_all_ready) 71914 return(state); 71915 71916 var result_set = _query_builder.get_state(); 71917 if(result_set) 71918 state.search_term = encode_search_term(result_set.query_set); 71919 71920 var colormap = get_color_map(); 71921 if(colormap) 71922 state.colormap = colormap; 71923 71924 return(state); 71925 } 71926 71927 function set_search_state(state) { 71928 71929 if(!state || !state.search_term) 71930 return; 71931 71932 if(_query_builder && _all_ready) 71933 _query_builder.set_state(decode_search_term(state.search_term)); 71934 else 71935 _queued_state = state; 71936 } 71937 71938 function encode_search_term(query_set) { 71939 71940 var q = new manual_annotation_query_set(query_set); 71941 return(q.stringify()); 71942 } 71943 71944 function decode_search_term(term) { 71945 71946 var q = new manual_annotation_query_set(term); 71947 return(q.query_set); 71948 } 71949 71950 function load_state(state) { 71951 71952 set_search_state(state); 71953 _state_set = true; 71954 } 71955 71956 function build_search_controls() { 71957 71958 var qbuilder_id = 'manual_anno_qbuilder_wrapper' + Math.floor(Math.random() * 10001); 71959 _info_widget.set_content(1,0, 'Search expert manual annotation of gene expression intensity and density by structure and postnatal age'); 71960 _info_widget.set_content(0,1, '<div class="manual_anno_qbuilder_wrapper" id="' + qbuilder_id + '"></div>'); 71961 _info_widget.set_content(0,2, ''); 71962 71963 var options = {}; 71964 $j.extend(true, options, _self.settings[_self.definition.id]); 71965 71966 options.on_search = do_search, 71967 options.on_help = do_help, 71968 options.on_ready = do_ready, 71969// options.on_remove_row = do_search; // don't want to automatically do a search when a row is removed! 71970 71971 _query_builder = new manual_annotation_query_builder('#'+qbuilder_id, {/* initial state */}, options); 71972 } 71973 71974 71975 function do_ready() { 71976 71977// trace("the query builder says it's ready"); 71978 _all_ready = true; 71979 71980 if(_queued_state) 71981 load_state(_queued_state); 71982 71983 _queued_state = null; 71984 71985 if(_self.on_ready) 71986 _self.on_ready(); 71987 } 71988 71989 function do_search() { 71990 if(!_query_builder.validate_state()) 71991 return; 71992 71993 if(_self.on_search) 71994 _self.on_search(_self, get_search_state()); 71995 } 71996 71997 function do_help() { 71998 if (_self.on_help) 71999 _self.on_help(_self.help_url); 72000 } 72001 72002 72003 function init() { 72004 build_search_controls(); 72005 } 72006 72007 72008 init(); 72009} 72010 72011; 72012 72013 72014function microarray_gene_search_plugin(dao, definition, info_widget, options) { 72015 72016 this.the_dao = dao; 72017 this.info_widget = info_widget; 72018 this.definition = definition; 72019 72020 this.get_state = function() { 72021 return(get_gene_search_state()); 72022 } 72023 72024 this.set_state = function(state) { 72025 set_gene_search_state(state); 72026 } 72027 72028 this.on_search = options ? options.on_search ? options.on_search : null : null; 72029 this.on_error = options ? options.on_error ? options.on_error : null : null; 72030 this.on_ready = options ? options.on_ready ? options.on_ready : null : null; 72031 this.on_search_term_lookup = options ? options.on_search_term_lookup ? options.on_search_term_lookup : null : null; 72032 72033 72034 var _self = this; 72035 var _info_widget = _self.info_widget; 72036 var _type_ahead_callback = null; 72037 var _closed = true; 72038 var _selected_item = false;
72039 var _search_text_box = null; 72040 //var _tag_container = null; 72041 var _display_donors; 72042 var _product_ids = _self.definition.product_ids; 72043 72044 function get_gene_search_state() { 72045 var exact = get_exact_search_state(); 72046 var type = $j('input[name="search_type"]:checked').val(); 72047 var term = _search_text_box.val(); 72048/* 72049 72050 var terms = _tag_container.get_tags(); 72051 //alert("get_state terms:"+ terms); 72052 72053 var term, terms_array, new_terms_array; 72054 //terms_array = terms.split(','); 72055 new_terms_array = terms.map(filter_term); 72056 72057 term = new_terms_array.join(','); 72058 //alert("get_state new terms:"+term); 72059*/ 72060 72061 72062 if(_selected_item) 72063 term = '"' + term + '"'; 72064 72065 //alert("the definition: " + inspect('', _self.definition)); 72066 var state = { 72067 exact_match:exact, 72068 search_term:term, 72069 search_type:_self.definition.id 72070 } 72071 if(_display_donors) { 72072 state.donors = _display_donors; 72073 } 72074 72075 return(state); 72076 } 72077 72078 function inspect(prefix, obj) { 72079 var s = ""; 72080 for(key in obj) { 72081 if((typeof obj[key]) == "function") 72082 s = s; 72083 else if((typeof obj[key]) == "object" && obj[key] !== null & key != "element") 72084 s += "\n" + prefix + "key: " + key + inspect(prefix + " ", obj[key]); 72085 else 72086 s += "\n" + prefix + "key: " + key + ", value: " + obj[key]; 72087 } 72088 return(s); 72089 } 72090 72091 function set_gene_search_state(state) { 72092 var exact = document.getElementById('_search_exact'); 72093 if(exact) 72094 exact.checked = state.exact_match; 72095 72096 if(state.search_term) 72097 _search_text_box.val( state.search_term ); 72098 72099 if(state.donors) 72100 _display_donors = state.donors; 72101 72102 if(_self.on_ready) 72103 _self.on_ready(); 72104 } 72105 72106 function build_gene_search_controls() { 72107 72108 var search_box_html = "<input type='text' id='_search_text' class='_sw_search_text'/>"; 72109 search_box_html += "<div id='_search_button'>Search</div>"; 72110 var exact_html = "<input type='checkbox' id='_search_exact'/>Show exact matches only"; 72111 72112 _info_widget.set_content(1,0, _self.definition.input_title); 72113 _info_widget.set_content(0,1, search_box_html); 72114 _info_widget.set_content(0,2, exact_html); 72115 72116 _search_text_box = $j('#_search_text'); 72117 var search_button = $j('#_search_button'); 72118 72119 // control-ify 72120 search_button.button(); 72121 search_button.bind('click', do_gene_search); 72122 72123 _search_text_box.autocomplete( 72124 { 72125 source:do_gene_auto_complete, 72126 select:do_gene_search, 72127 search:do_start_search, 72128 open:do_open, 72129 appendTo:_self.info_widget.container, 72130 delay:100 72131 }); 72132 72133// _tag_container = new AIBS.UI.ontology_tag_container(_search_text_box, { 72134// on_autocomplete:gene_lookup_search, 72135// on_tag_added:do_add_tag, 72136// on_tag_removed:do_remove_tag 72137// //default_text: settings.default_text 72138// }); 72139 72140 _search_text_box.bind('keyup', do_gene_enter_check); 72141 } 72142 72143/* 72144 function do_add_tag(tag) { 72145 //alert("added: "+tag); 72146 } 72147 72148 function do_remove_tag(tag) { 72149 // alert("removed: "+tag); 72150 } 72151*/ 72152 72153 function gene_lookup_search(term, callback) { 72154 var match = []; 72155 if(!_self.on_search_term_lookup) { 72156 callback(match); // return an empty array of matched entries 72157 } 72158 else { 72159 match = _self.on_search_term_lookup(term.term, _product_ids); 72160 callback(match); 72161 } 72162 72163 } 72164 72165 72166 function do_start_search(event, ui) { 72167 if(_closed) 72168 _search_text_box.autocomplete('close'); 72169 } 72170 72171 function do_open(event, ui) { 72172 if(_closed) 72173 _search_text_box.autocomplete('close'); 72174 } 72175 72176 function do_gene_auto_complete(request, response) { 72177 _type_ahead_callback = response; 72178 var opts = { 72179 success_callback:gene_autocomplete_responder, 72180 error_callback:dummy, 72181 limit:10, 72182 exact_match:get_exact_search_state() 72183 }; 72184
72185 _self.the_dao.gene_typeahead(request.term, opts); 72186 } 72187 72188 function gene_autocomplete_responder(response_data, text_status, xhdr) { 72189 72190 if(!response_data) 72191 return; 72192 72193 var ret = []; 72194 var len = response_data.length; 72195//TODO: get rid of the explicit reference to a field name 72196 // remove duplicate terms - when a gene is involved to multiple products, multiple GeneLookup records will be found for this gene 72197 var term_cache = {}; 72198 72199 for(var i = 0; i < len; i++) 72200 if(!term_cache[response_data[i].term]) { 72201 ret.push(response_data[i].term); 72202 term_cache[response_data[i].term] = true; 72203 } 72204 72205 72206 _type_ahead_callback(ret); 72207 } 72208 72209 function dummy(xhdr, text_status, error) { 72210 // swallow this type ahead error 72211 } 72212 72213 function get_exact_search_state() { 72214 var exact = $j('#_search_exact'); 72215 72216 return( exact ? exact[0] ? exact[0].checked : false : false ); 72217 } 72218 72219 function do_gene_search(event, ui) { 72220 var term = ''; 72221 if(ui && ui.item) { 72222 //term = '"' + ui.item.value + '"'; 72223 _search_text_box.val(ui.item.value); 72224 72225 _selected_item = true; 72226 } 72227 else { 72228 term = _search_text_box.val(); 72229/* term = _tag_container.get_tags(); 72230 //alert("orig term:"+term); 72231 term = filter_term(term); 72232 //alert("modified term:"+term);*/ 72233 72234 _selected_item = false; 72235 72236 if(!term || term.length < 1) { 72237 if(_self.on_error) 72238 _self.on_error("Empty search term"); 72239 } 72240 } 72241 72242 if(_self.on_search) { 72243 var temp_state = get_gene_search_state(); 72244 if(temp_state.donors) { 72245 delete temp_state.donors; // need to remove the "donors" state if it was set by a previous search 72246 } 72247 _self.on_search(_self, temp_state); 72248 } 72249 72250 return(false); 72251 } 72252 72253 function do_gene_enter_check(event, ui) { 72254 if(event.keyCode == 13) { 72255 _search_text_box.autocomplete('close'); 72256 _closed = true; 72257 do_gene_search(null, null); 72258 } else { 72259 _closed = false; 72260 } 72261 } 72262 72263/* function filter_term(term) { 72264 var from_tag_input = false, result_array, new_term; 72265 var tag_input_patt = /(symbol|alias|name): (.+) \(id:(\d+)\)/; 72266 result_array = tag_input_patt.exec(term); 72267 if(!result_array) { 72268 return term; // no match -- term not from a generated tag 72269 } 72270 else { 72271 new_term = result_array[3]; 72272 //alert("term:" + new_term); 72273 return new_term; 72274 72275 } 72276 72277 72278 72279 }*/ 72280 72281 function init() { 72282 build_gene_search_controls(); 72283 } 72284 72285 init(); 72286} 72287; 72288 72289 72290function nhp_macrodissection_differential_search_ui(dao, definition, info_widget, options) { 72291 72292 this.the_dao = dao; 72293 this.info_widget = info_widget; 72294 this.definition = definition; 72295 this.help_url = options ? options[definition.id] ? options[definition.id].help_url : '/' : '/'; 72296 this.threshold = options ? options.threshold ? options.threshold : '1' : '1'; 72297 this.settings = { 72298 default_contrast_domains:[8], 72299 default_target_domains:null 72300 } 72301 $j.extend(true, this.settings, options); 72302 72303 this.get_state = function() { 72304 return(get_differential_search_state()); 72305 } 72306 72307 this.set_state = function(state) { 72308 set_differential_search_state(state); 72309 } 72310 72311 this.destroy = function() { 72312 72313 if(this.search_domain_1 && this.search_domain_1.ontology_autocomplete) 72314 this.search_domain_1.ontology_autocomplete('destroy'); 72315 72316 if(this.search_domain_2 && this.search_domain_2.ontology_autocomplete) 72317 this.search_domain_2.ontology_autocomplete('destroy'); 72318 } 72319 72320 this.on_search = options ? options.on_search ? options.on_search : null : null; 72321 this.on_error = options ? options.on_error ? options.on_error : null : null; 72322 this.on_ready = options ? options.on_ready ? options.on_ready : null : null; 72323 72324 var _self = this; 72325 var _info_widget = _self.info_widget; 72326 var _set_state = null; 72327 var _ontology_model = null; 72328 var _initializing = false;
72329 var _donor_list = null; 72330 //var _id = Math.floor(Math.random() * 10201); 72331 72332 var _stage_list; 72333 //var _stage_lis = options ? options.stage_list ? options.stage_list : null : null; 72334 var _structure_list; 72335 72336 function set_selections(options, selections) { 72337 for(var i = 0; i < options.length; i++) 72338 options[i].selected = selections.include(options[i].id); 72339 } 72340 72341 72342 function get_differential_search_state() { 72343 72344 $j('.diff_structure').dropdownchecklist('close'); // close both select 72345 $j('.diff_structure').dropdownchecklist('refresh'); 72346 72347 var selected_structures_1 = $j('#_search_domain_1 option:selected'); 72348 var selected_structures_2 = $j('#_search_domain_2 option:selected'); 72349 72350 var selected_structures1 = $j.map(selected_structures_1, function(op){ if(op.id != '0') return op.id;}); // get an array of strings 72351 var selected_structures1_str = selected_structures1.join(','); 72352 72353 var selected_structures2 = $j.map(selected_structures_2, function(op){ if(op.id != '0') return op.id;}); // get an array of strings 72354 var selected_structures2_str = selected_structures2.join(','); 72355 72356 $j('.diff_stage').dropdownchecklist('close'); 72357 $j('.diff_stage').dropdownchecklist('refresh'); 72358 var selected_stage_options_temp = $j('#_search_stage_1 option:selected'); 72359 var selected_stage_1 = $j.map(selected_stage_options_temp, function(op){ if(op.id != '0') return op.id;}); // get an array of strings 72360 72361 var selected_stage_1_str = selected_stage_1.join(','); 72362 72363 selected_stage_options_temp = $j('#_search_stage_2 option:selected'); 72364 var selected_stage_2 = $j.map(selected_stage_options_temp, function(op){ if(op.id != '0') return op.id;}); // get an array of strings 72365 72366 var selected_stage_2_str = selected_stage_2.join(','); 72367 72368 var state = { 72369 domain1:selected_structures1_str, 72370 domain2:selected_structures2_str, 72371 stage1:selected_stage_1_str, 72372 stage2:selected_stage_2_str, 72373 search_type:_self.definition.id 72374 } 72375 return(state); 72376 } 72377 72378 function set_differential_search_state(state) { 72379 72380 _set_state = {}; 72381 _set_state.domain1 = state.domain1; 72382 _set_state.domain2 = state.domain2; 72383 72384 _set_state.search_type = _self.definition.id; 72385 //_set_state.selected_donors = state.selected_donors; 72386 _set_state.stage1 = state.stage1; 72387 _set_state.stage2 = state.stage2; 72388 72389 // need to call load_differential_state after _set_state is populated 72390 load_differential_state(); 72391 } 72392 72393 function load_differential_state() { 72394 _initializing = true; 72395 72396 if(_set_state && _set_state.domain1 && _set_state.domain1.length > 0) { 72397 set_selections($j('#_search_domain_1 option'), _set_state.domain1.split(',')); 72398 } 72399 else { 72400 var domain1_select = $j('#_search_domain_1 option[id=0]'); 72401 domain1_select.attr('selected', 'selected'); 72402 } 72403 72404 if(_set_state && _set_state.domain2 && _set_state.domain2.length > 0) { 72405 set_selections($j('#_search_domain_2 option'), _set_state.domain2.split(',')); 72406 } 72407 else { 72408 var domain2_select = $j('#_search_domain_2 option[id=0]'); 72409 domain2_select.attr('selected', 'selected'); 72410 } 72411 72412 72413 if(_set_state && _set_state.stage1 && _set_state.stage1.length > 0) { 72414 var selected_stage1_array = _set_state.stage1.split(','); 72415 var stage1_select = $j('#_search_stage_1 option'); 72416 stage1_select.removeAttr('selected'); // remove all the selected attr 72417 for( var i = 0; i < selected_stage1_array.length; i ++ ){ 72418 stage1_select = $j('#_search_stage_1 option[id="' + selected_stage1_array[i] +'"]'); 72419 stage1_select.attr('selected', 'selected'); 72420 } 72421 } 72422 else { 72423 var stage1_select = $j('#_search_stage_2 option[id=0]'); 72424 stage1_select.attr('selected', 'selected'); 72425 } 72426 72427 if(_set_state && _set_state.stage2 && _set_state.stage2.length > 0) { 72428 var selected_stage2_array = _set_state.stage2.split(','); 72429 var stage2_select = $j('#_search_stage_2 option'); 72430 stage2_select.removeAttr('selected'); // remove all the selected attr 72431 for( var i = 0; i < selected_stage2_array.length; i ++ ){ 72432 stage2_select = $j('#_search_stage_2 option[id="' + selected_stage2_array[i] +'"]'); 72433 stage2_select.attr('selected', 'selected'); 72434 } 72435 } 72436 else { 72437 var stage2_select = $j('#_search_stage_2 option[id=0]'); 72438 stage2_select.attr('selected', 'selected'); 72439 } 72440 72441 $j('.diff_stage').dropdownchecklist("refresh"); 72442 72443 72444// // if there is no state data, set a default contrast structure 72445// if(!_set_state) { 72446// if(_self.settings.default_contrast_domains) 72447// _self.search_domain_1.ontology_autocomplete('set_structure_ids', _self.settings.default_contrast_domains); 72448// if(_self.settings.default_target_domains) 72449// _self.search_domain_2.ontology_autocomplete('set_structure_ids', _self.settings.default_target_domains); 72450// } 72451 72452 _initializing = false;
72453 _set_state = null; 72454 72455 if(_self.on_ready) 72456 _self.on_ready(); 72457 } 72458 72459 function build_differential_search_controls() { 72460 72461 var search_box1_html = "<table id='differential_controls'><tr><td class='td_label'><div class='_diff_search_input_label'>Target Structure(s), Age(s)</div></td>"; 72462 //search_box1_html += "<td class='td_structure'><input type='text' id='_search_domain_2_" + _id + "' class='_sw_search_text'/></td>"; 72463 search_box1_html += "<td class='td_structure'>"; 72464 search_box1_html += "<select id='_search_domain_2' class='_sw_search_text diff_structure'/>"; 72465 search_box1_html += "<option id=0 >[All Structures]</option>"; 72466 for(var i = 0; i < _structure_list.length; i++ ) { 72467 search_box1_html += "<option id=" + _structure_list[i].id + ">" + _structure_list[i].acronym + ' - ' +_structure_list[i].name + "</option>"; 72468 } 72469 search_box1_html += "</td>"; // for <td class='td_structure'> 72470 search_box1_html += "<td class='td_stage'><select id='_search_stage_2' class='_sw_search_text diff_stage'/>"; 72471 search_box1_html += "<option id=0 >[All Stages]</option>"; 72472 for(var i=0; i<_stage_list.length; i++) { 72473 search_box1_html += "<option id='" + _stage_list[i].name + "'>" + _stage_list[i].name + "</option>"; 72474 } 72475 search_box1_html += "</select></td>"; 72476 search_box1_html += "<td class='td_controls'><div id='_swap_button' class='_sw_icon_button'></div>"; 72477 search_box1_html += "<div id='_diff_help_button' class='_sw_icon_button'></div></td></tr>"; 72478 72479 var search_box2_html = "<tr><td class='td_label'><div class='_diff_search_input_label'>Contrast Structure(s), Age(s)</div></td>"; 72480 //search_box2_html += "<td class='td_structure'><input type='text' id='_search_domain_1_" + _id + "' class='_sw_search_text'/></td>"; 72481 search_box2_html += "<td class='td_structure'>"; 72482 search_box2_html += "<select id='_search_domain_1' class='_sw_search_text diff_structure'/>"; 72483 search_box2_html += "<option id=0 >[All Structures]</option>"; 72484 for(var i = 0; i < _structure_list.length; i++ ) { 72485 search_box2_html += "<option id=" + _structure_list[i].id + ">" + _structure_list[i].acronym + ' - ' +_structure_list[i].name + "</option>"; 72486 } 72487 search_box2_html += "</td>"; // for <td class='td_structure'> 72488 72489 search_box2_html += "<td class='td_stage'><select id='_search_stage_1' class='_sw_search_text diff_stage'/>"; 72490 search_box2_html += "<option id=0 >[All Stages]</option>"; 72491 for(i=0; i<_stage_list.length; i++) { 72492 search_box2_html += "<option id='" + _stage_list[i].name + "'>" + _stage_list[i].name + "</option>"; 72493 } 72494 search_box2_html += "</select></td>"; 72495 search_box2_html += "<td class='td_controls'><div id='_search_button'>Search</div></td></tr></table>"; 72496 72497 _info_widget.set_content(1,0, search_box1_html+search_box2_html); 72498 _info_widget.set_content(0,1, ''); 72499 _info_widget.set_content(0,2, ''); 72500 72501 72502 _self.diff_search_button = $j('#_search_button'); 72503 _self.diff_help_button = $j('#_diff_help_button'); 72504 _self.swap_button = $j('#_swap_button'); 72505 72506 // control-ify 72507 _self.diff_search_button.button(); 72508 _self.diff_search_button.bind('click', do_diff_search); 72509 72510 _self.diff_help_button.button({icons:{secondary:'ui-icon-help'}}); 72511 _self.diff_help_button.bind('click', do_diff_help); 72512 72513 _self.swap_button.button({icons:{secondary:'ui-icon-refresh'}}); 72514 _self.swap_button.bind('click', do_domain_swap); 72515 72516 72517 $j('.diff_structure').dropdownchecklist({icon:{placement:'right',toOpen:'ui-icon-triangle-1-s'}, 72518 forceMultiple:true, 72519 firstItemChecksAll: true, 72520 maxDropHeight:600, 72521 width:250, 72522 zIndex:9999}); 72523 72524 $j('.diff_stage').dropdownchecklist({icon:{placement:'right',toOpen:'ui-icon-triangle-1-s'}, 72525 forceMultiple:true, 72526 firstItemChecksAll: true, 72527 maxDropHeight:300, 72528 width:250, 72529 zIndex:9999}); 72530 72531 } 72532 72533 72534 72535 function do_diff_search(event, ui) { 72536 if(_initializing) 72537 return; 72538 72539 if(_self.on_search) 72540 _self.on_search(_self, get_differential_search_state()); 72541 } 72542 72543 function do_diff_help(event, ui) { 72544 window.open(_self.help_url, '_help', 'status=0,toolbar=0,menubar=0,width=800,height=600,scrollbars=1,resizable=1'); 72545 } 72546 72547 function do_domain_swap(event, ui) { 72548 72549 $j('.diff_structure').dropdownchecklist('close');
72549 // close both select 72550 $j('.diff_structure').dropdownchecklist('refresh'); 72551 72552 var selected_structures_1 = $j('#_search_domain_1 option:selected'); 72553 var selected_structures_2 = $j('#_search_domain_2 option:selected'); 72554 72555 var selected_structures1 = $j.map(selected_structures_1, function(op){ if(op.id != '0') return op.id;}); // get an array of strings 72556 var selected_structures2 = $j.map(selected_structures_2, function(op){ if(op.id != '0') return op.id;}); // get an array of strings 72557 72558 set_selections($j('#_search_domain_1 option'), selected_structures2); 72559 set_selections($j('#_search_domain_2 option'), selected_structures1); 72560 72561 $j('.diff_stage').dropdownchecklist('close'); // close both select 72562 $j('.diff_stage').dropdownchecklist('refresh'); 72563 72564 var selected_stages_1 = $j('#_search_stage_1 option:selected'); 72565 var selected_stages_2 = $j('#_search_stage_2 option:selected'); 72566 72567 var selected_stages1 = $j.map(selected_stages_1, function(op){ if(op.id != '0') return op.id;}); // get an array of strings 72568 var selected_stages2 = $j.map(selected_stages_2, function(op){ if(op.id != '0') return op.id;}); // get an array of strings 72569 72570 set_selections($j('#_search_stage_1 option'), selected_stages2); 72571 set_selections($j('#_search_stage_2 option'), selected_stages1); 72572 72573 72574// if(check_domains()) 72575// do_diff_search(null, null); 72576 72577 do_diff_search(null, null); 72578 } 72579 72580 function tags_to_string(tags) { 72581 var s = ""; 72582 for(var i = 0; i < tags.length; i++) 72583 s += tags[i].label + '; '; 72584 72585 return(s.slice(0,-2)); 72586 } 72587 72588 72589 function fetch_donor_list(product_ids) { 72590 var dao_options = { 72591 success_callback: received_donor_list, 72592 error_callback: received_no_donor_list 72593 }; 72594 72595 _self.the_dao.donor_info_by_product_ids( product_ids, dao_options); 72596 } 72597 72598 function received_donor_list(response_data, text_status, xhdr) { 72599 72600 var donor_array = new Array(); 72601 var donor_index = 0; 72602 72603 for ( var i = 0; i < response_data.length; i++ ) { 72604 var donor = response_data[i]; 72605 donor_array[donor_index++] = {"donor":{"id":donor.id,"name":donor.name}} 72606 } 72607 _donor_list = donor_array; 72608 72609 build_differential_search_controls(); 72610 } 72611 72612 function received_no_donor_list() 72613 { 72614 alert("Failed to fetch a list of donors."); 72615 } 72616 72617 72618 72619 function init() { 72620 if(_self.settings.stage_list && _self.settings.structure_list ) { 72621 _stage_list = _self.settings.stage_list; 72622 _structure_list = _self.settings.structure_list; 72623 build_differential_search_controls(); 72624 } 72625 else 72626 fetch_donor_list(''); // use the default product id provided in the theme 72627 72628 72629 } 72630 72631 init(); 72632} 72633; 72634 72635 72636function nhp_microdissection_differential_search_ui(dao, definition, info_widget, options) { 72637 72638 this.the_dao = dao; 72639 this.info_widget = info_widget; 72640 this.definition = definition; 72641 this.help_url = options ? options[definition.id] ? options[definition.id].help_url : '/' : '/'; 72642 this.threshold = options ? options.threshold ? options.threshold : '1' : '1'; 72643 this.settings = { 72644 default_contrast_domains:[8], 72645 default_target_domains:null 72646 } 72647 $j.extend(true, this.settings, options); 72648 72649 this.get_state = function() { 72650 return(get_differential_search_state()); 72651 } 72652 72653 this.set_state = function(state) { 72654 set_differential_search_state(state); 72655 } 72656 72657 this.destroy = function() { 72658 72659 if(this.search_domain_1 && this.search_domain_1.ontology_autocomplete) 72660 this.search_domain_1.ontology_autocomplete('destroy'); 72661 72662 if(this.search_domain_2 && this.search_domain_2.ontology_autocomplete) 72663 this.search_domain_2.ontology_autocomplete('destroy'); 72664 } 72665 72666 this.on_search = options ? options.on_search ? options.on_search : null : null; 72667 this.on_error = options ? options.on_error ? options.on_error : null : null; 72668 this.on_ready = options ? options.on_ready ? options.on_ready : null : null; 72669 72670 var _self = this; 72671 var _info_widget = _self.info_widget; 72672 var _set_state = null; 72673 var _ontology_model = null; 72674 var _initializing = false;
72675 var _donor_list = null; 72676 var _id = Math.floor(Math.random() * 10201); 72677 72678 var _stage_list; 72679 //var _stage_list = options ? options.stage_list ? options.stage_list : null : null; 72680 72681 function check_domains() { 72682 var d1 = _self.search_domain_1.ontology_autocomplete('get_tags'); 72683 var d2 = _self.search_domain_2.ontology_autocomplete('get_tags'); 72684 72685 return(d1.length && d2.length); 72686 } 72687 72688 function set_selections(options, selections) { 72689 for(var i = 0; i < options.length; i++) 72690 options[i].selected = selections.include(options[i].id); 72691 } 72692 72693 72694 function get_differential_search_state() { 72695 72696 if(!check_domains()) { 72697 alert("Both 'Target' and 'Contrast' domains must be set in order to do a differential search."); 72698 return(null); 72699 } 72700 72701 var d1 = _self.search_domain_1.ontology_autocomplete('get_tags'); 72702 var d2 = _self.search_domain_2.ontology_autocomplete('get_tags'); 72703 72704 var d1_ids = []; 72705 var d2_ids = []; 72706 for(var i = 0; i < d1.length; i++) d1_ids.push(d1[i].value ); 72707 for(var i = 0; i < d2.length; i++) d2_ids.push(d2[i].value ); 72708 var d1_ids_str = d1_ids.join(','); 72709 var d2_ids_str = d2_ids.join(','); 72710 72711 $j('.diff_stage').dropdownchecklist('close'); 72712 $j('.diff_stage').dropdownchecklist('refresh'); 72713 var selected_stage_options_temp = $j('#_search_stage_1 option:selected'); 72714 var selected_stage_1 = $j.map(selected_stage_options_temp, function(op){ if(op.id != '0') return op.id;}); // get an array of strings 72715 72716 var selected_stage_1_str = selected_stage_1.join(','); 72717 72718 selected_stage_options_temp = $j('#_search_stage_2 option:selected'); 72719 var selected_stage_2 = $j.map(selected_stage_options_temp, function(op){ if(op.id != '0') return op.id;}); // get an array of strings 72720 72721 var selected_stage_2_str = selected_stage_2.join(','); 72722 72723 var state = { 72724 domain1:d1_ids_str, 72725 domain2:d2_ids_str, 72726// domain1_threshold:[0,50], 72727// domain2_threshold:[t_val,50], 72728// image_set: (_self.image_set_check.get(0).checked == true) ? 'P56coronal' : 'P56', 72729 //selected_donors:selected_donors_str, 72730 stage1:selected_stage_1_str, 72731 stage2:selected_stage_2_str, 72732 search_type:_self.definition.id 72733 } 72734 return(state); 72735 } 72736 72737 function set_differential_search_state(state) { 72738 // the call to do_search_type_change provokes a load of 72739 // the ontology. We have to wait until that completes 72740 // before setting the control states. That on-load' handler 72741 // will call load_differential_state. So there's not much to 72742 // do here except record the state that should be set when 72743 // everything's ready. 72744// var t_val = _self.threshold; 72745// var t_val_list = state.domain2_threshold; 72746// if(t_val_list) { 72747// var vals = t_val_list.split(','); 72748// if(vals && vals.length) 72749// t_val = vals[0]; 72750// } 72751 72752 _set_state = {}; 72753 _set_state.domain1 = state.domain1; 72754 _set_state.domain2 = state.domain2; 72755 //_set_state.image_set = state.image_set; 72756 _set_state.search_type = _self.definition.id; 72757 //_set_state.selected_donors = state.selected_donors; 72758 _set_state.stage1 = state.stage1; 72759 _set_state.stage2 = state.stage2; 72760 //_set_state.t_val = t_val; 72761 //_self.threshold_slider.slider('value', t_val); 72762 } 72763 72764 function load_differential_state() { 72765 _initializing = true; 72766 72767 if(_set_state && _set_state.domain1 && _set_state.domain1.length > 0) 72768 _self.search_domain_1.ontology_autocomplete('set_structure_ids', _set_state.domain1.split(',')); 72769 72770 if(_set_state && _set_state.domain2 && _set_state.domain2.length > 0) 72771 _self.search_domain_2.ontology_autocomplete('set_structure_ids', _set_state.domain2.split(',')); 72772 72773 if(_set_state && _set_state.stage1 && _set_state.stage1.length > 0) { 72774 var selected_stage1_array = _set_state.stage1.split(','); 72775 var stage1_select = $j('#_search_stage_1 option'); 72776 stage1_select.removeAttr('selected'); // remove all the selected attr 72777 for( var i = 0; i < selected_stage1_array.length; i ++ ){ 72778 stage1_select = $j('#_search_stage_1 option[id="' + selected_stage1_array[i] +'"]'); 72779 stage1_select.attr('selected', 'selected'); 72780 } 72781 } 72782 else { 72783 var stage1_select = $j('#_search_stage_2 option[id=0]'); 72784 stage1_select.attr('selected', 'selected'); 72785 } 72786 72787 if(_set_state && _set_state.stage2 && _set_state.stage2.length > 0) { 72788 var selected_stage2_array = _set_state.stage2.split(','); 72789 var stage2_select = $j('#_search_stage_2 option'); 72790 stage2_select.removeAttr('selected'); // remove all the selected attr 72791 for( var i = 0; i < selected_stage2_array.length; i ++ ){ 72792 stage2_select = $j('#_search_stage_2 option[id="' + selected_stage2_array[i] +'"]'); 72793 stage2_select.attr('selected', 'selected'); 72794 } 72795 } 72796 else { 72797 var stage2_select = $j('#_search_stage_2 option[id=0]'); 72798 stage2_select.attr('selected', 'selected'); 72799 } 72800 72801 $j('.diff_stage').dropdownchecklist("refresh"); 72802 72803 72804// // if there is no state data, set a default contrast structure 72805// if(!_set_state) { 72806// if(_self.settings.default_contrast_domains) 72807// _self.search_domain_1.ontology_autocomplete('set_structure_ids', _self.settings.default_contrast_domains); 72808// if(_self.settings.default_target_domains) 72809// _self.search_domain_2.ontology_autocomplete('set_structure_ids', _self.settings.default_target_domains); 72810// } 72811 72812 _initializing = false;
72813 _set_state = null; 72814 72815 if(_self.on_ready) 72816 _self.on_ready(); 72817 } 72818 72819 function build_differential_search_controls() { 72820 72821 var search_box1_html = "<table id='differential_controls'><tr><td class='td_label'><div class='_diff_search_input_label'>Target Structure(s), Age(s)</div></td>"; 72822 search_box1_html += "<td class='td_structure'><input type='text' id='_search_domain_2_" + _id + "' class='_sw_search_text'/></td>"; 72823 search_box1_html += "<td class='td_stage'><select id='_search_stage_2' class='_sw_search_text diff_stage'/>"; 72824 search_box1_html += "<option id=0 >[All Stages]</option>"; 72825 for(var i=0; i<_stage_list.length; i++) { 72826 search_box1_html += "<option id='" + _stage_list[i].name + "'>" + _stage_list[i].name + "</option>"; 72827 } 72828 search_box1_html += "</select></td>"; 72829 search_box1_html += "<td class='td_controls'><div id='_swap_button' class='_sw_icon_button'></div>"; 72830 search_box1_html += "<div id='_diff_help_button' class='_sw_icon_button'></div></td></tr>"; 72831 72832 var search_box2_html = "<tr><td class='td_label'><div class='_diff_search_input_label'>Contrast Structure(s), Age(s)</div></td>"; 72833 search_box2_html += "<td class='td_structure'><input type='text' id='_search_domain_1_" + _id + "' class='_sw_search_text'/></td>"; 72834 search_box2_html += "<td class='td_stage'><select id='_search_stage_1' class='_sw_search_text diff_stage'/>"; 72835 search_box2_html += "<option id=0 >[All Stages]</option>"; 72836 for(i=0; i<_stage_list.length; i++) { 72837 search_box2_html += "<option id='" + _stage_list[i].name + "'>" + _stage_list[i].name + "</option>"; 72838 } 72839 search_box2_html += "</select></td>"; 72840 search_box2_html += "<td class='td_controls'><div id='_search_button'>Search</div></td></tr></table>"; 72841 72842/* 72843 // differential donor selection 72844 var search_box3_html = "<div class='_diff_search_input_label'>Selected Donor(s)</div>"; 72845 search_box3_html += "<span id='_diff_donor_selection'><select id='diff_donors' multiple='multiple'>"; 72846 72847 //var donor_list_index = 0; 72848 search_box3_html += "<option id=0 >[All Donors]</option>"; 72849 for(var i=0; i<_donor_list.length; i++) { 72850 search_box3_html += "<option id=" + _donor_list[i].donor.id + ">" + _donor_list[i].donor.name + "</option>"; 72851 } 72852 72853 search_box3_html += "</select></span>"; 72854 72855*/ 72856 72857 72858 _info_widget.set_content(1,0, search_box1_html+search_box2_html); 72859 _info_widget.set_content(0,1, ''); 72860 _info_widget.set_content(0,2, ''); 72861 72862// _info_widget.set_content(0,1, search_box2_html); 72863// _info_widget.set_content(0,2, search_box3_html); 72864 72865 72866 72867// they want to reverse the position of the target & contrast domains. 72868// changing the name here seems like the lowest-impact solution. 72869 _self.search_domain_1 = $j('#_search_domain_1_' + _id); 72870 _self.search_domain_2 = $j('#_search_domain_2_' + _id); 72871 72872 _self.diff_search_button = $j('#_search_button'); 72873 _self.diff_help_button = $j('#_diff_help_button'); 72874 _self.swap_button = $j('#_swap_button'); 72875 72876 // control-ify 72877 _self.diff_search_button.button(); 72878 _self.diff_search_button.bind('click', do_diff_search); 72879 72880 _self.diff_help_button.button({icons:{secondary:'ui-icon-help'}}); 72881 _self.diff_help_button.bind('click', do_diff_help); 72882 72883 _self.swap_button.button({icons:{secondary:'ui-icon-refresh'}}); 72884 _self.swap_button.bind('click', do_domain_swap); 72885 72886 if(!_ontology_model) 72887 _ontology_model = new ontology_model(_self.the_dao, 9, {on_ready:do_ontology_load, on_error:_self.on_ontology_error}); 72888 else 72889 do_ontology_load(_ontology_model); 72890 72891 $j('.diff_stage').dropdownchecklist({icon:{placement:'right',toOpen:'ui-icon-triangle-1-s'}, 72892 forceMultiple:true, 72893 firstItemChecksAll: true, 72894 maxDropHeight:300, 72895 width:100, 72896 zIndex:9999}); 72897 72898 } 72899 72900 72901 function do_ontology_load(model) { 72902 72903 // this selector should apply the ontology_autocomplete behavior to 72904 // both of the domain inputs 72905 //$j('._sw_search_text').ontology_autocomplete(model, {/* empty options for now */}); 72906 72907 // make sure each time only one text box is selected by jquery when initializing ontology_autocomplete 72908 _self.search_domain_1.ontology_autocomplete(model, {default_open_nodes:_self.settings.open_structures}); 72909 _self.search_domain_2.ontology_autocomplete(model, {default_open_nodes:_self.settings.open_structures}); 72910 load_differential_state(); 72911 } 72912 72913 function do_ontology_error(model, error) { 72914 var msg = "Due to an error in communicating with the data service, differential search is not available at this time. "; 72915 msg += "Please try again in a few minutes."; 72916 msg += "Error: " + error; 72917 alert(msg); 72918 } 72919 72920 function do_diff_search(event, ui) { 72921 if(_initializing) 72922 return; 72923 var search_state; 72924 if(_self.on_search) { 72925 search_state = get_differential_search_state(); // when one or more ontology widgets with value unset, search_state is null 72926 if(search_state) 72927 _self.on_search(_self, search_state); 72928 } 72929 } 72930 72931 function do_diff_help(event, ui) { 72932 window.open(_self.help_url, '_help', 'status=0,toolbar=0,menubar=0,width=800,height=600,scrollbars=1,resizable=1'); 72933 } 72934 72935 function do_domain_swap(event, ui) { 72936 var d1 = _self.search_domain_1.ontology_autocomplete('get_structure_string'); 72937 var d2 = _self.search_domain_2.ontology_autocomplete('get_structure_string'); 72938 72939 _self.search_domain_1.ontology_autocomplete('set_structure_string', d2); 72940 _self.search_domain_2.ontology_autocomplete('set_structure_string', d1); 72941 72942 $j('.diff_stage').dropdownchecklist('close');
72942 // close both select 72943 $j('.diff_stage').dropdownchecklist('refresh'); 72944 72945 var selected_stages_1 = $j('#_search_stage_1 option:selected'); 72946 var selected_stages_2 = $j('#_search_stage_2 option:selected'); 72947 72948 var selected_stages1 = $j.map(selected_stages_1, function(op){ if(op.id != '0') return op.id;}); // get an array of strings 72949 var selected_stages2 = $j.map(selected_stages_2, function(op){ if(op.id != '0') return op.id;}); // get an array of strings 72950 72951 set_selections($j('#_search_stage_1 option'), selected_stages2); 72952 set_selections($j('#_search_stage_2 option'), selected_stages1); 72953 72954 72955 if(check_domains()) 72956 do_diff_search(null, null); 72957 } 72958 72959 function tags_to_string(tags) { 72960 var s = ""; 72961 for(var i = 0; i < tags.length; i++) 72962 s += tags[i].label + '; '; 72963 72964 return(s.slice(0,-2)); 72965 } 72966 72967 72968 function fetch_donor_list(product_ids) { 72969 var dao_options = { 72970 success_callback: received_donor_list, 72971 error_callback: received_no_donor_list 72972 }; 72973 72974 _self.the_dao.donor_info_by_product_ids( product_ids, dao_options); 72975 } 72976 72977 function received_donor_list(response_data, text_status, xhdr) { 72978 72979 var donor_array = new Array(); 72980 var donor_index = 0; 72981 72982 for ( var i = 0; i < response_data.length; i++ ) { 72983 var donor = response_data[i]; 72984 donor_array[donor_index++] = {"donor":{"id":donor.id,"name":donor.name}} 72985 } 72986 _donor_list = donor_array; 72987 72988 build_differential_search_controls(); 72989 } 72990 72991 function received_no_donor_list() 72992 { 72993 alert("Failed to fetch a list of donors."); 72994 } 72995 72996 function init() { 72997 if(_self.settings.stage_list) { 72998 _stage_list = _self.settings.stage_list; 72999 build_differential_search_controls(); 73000 } 73001 else 73002 fetch_donor_list(''); // use the default product id provided in the theme 73003 73004 73005 } 73006 73007 init(); 73008} 73009; 73010"use strict"; 73011/** 73012 * 73013 * @param browse_container 73014 * @param search_container 73015 * @param page 73016 * @param options 73017 */ 73018function ish_browse_controller(browse_container, search_container, page, options) { 73019 73020 this.container = $j(browse_container); 73021 this.search_container = $j(search_container); 73022 this.page = page; 73023 this.settings = {}; 73024 $j.extend(true, this.settings, options); 73025 73026 73027 var _self = this; 73028 var _the_app = _self.page.application; 73029 var _the_page = _self.page; 73030 var _the_daos = _the_app.DAOs(); 73031 var _page_config = _the_page.get_config(); 73032 var _component_configs = _page_config.components; 73033 var _urls = _the_app.settings.urls; 73034 73035 var _search_widget = null; 73036 var _landing_widget = null; 73037 73038 function build_search_widget() { 73039 var search_options = { 73040 // plugin-specific options, hash key is plugin 'id' from application.json 73041 manual_annotation: { 73042 foobar: "floobydust" // !!!! just a placeholder to see if the options are being passed along 73043 }, 73044 on_search:load_search_page, 73045 on_search_type_change:do_search_type_change, 73046 on_help:do_help, 73047 on_ontology_error:show_ontology_error 73048 }; 73049 73050 _search_widget = new ish_search_widget(_self.search_container, _the_daos.data, _component_configs.search_widget.search_plugins, search_options); 73051 } 73052 73053 function do_search_type_change(sender, new_type) { 73054 73055 if(_landing_widget) 73056 _landing_widget.set_landing_mode(new_type); 73057 } 73058 73059 function do_help(url) { 73060 // if the url came from an application.json setting, then it is a key to the hash in _urls, and will not have 73061 // prefix 'http://' 73062 console.log("--> url before = " + url); 73063 73064 if (url.indexOf('http') != 0) { 73065 console.log("--> url before = " + url); 73066 url = _urls[url]; 73067 console.log("--> url after = " + url); 73068 } 73069 window.open(url, '_help', 'status=0,toolbar=0,menubar=0,width=800,height=600,scrollbars=1'); 73070 } 73071 73072 function encode_each_term(term, delimiter) { 73073 var terms = term.split(delimiter); 73074 73075 if(terms.length == 1) 73076 return(encodeURIComponent(trim(term))); 73077 73078 var term_string = ''; 73079 for(var i = 0; i < terms.length; i++) 73080 term_string += trim(terms[i]) + '$'; 73081 73082 var new_term = encodeURIComponent(term_string); 73083 73084 return(new_term); 73085 } 73086 73087 function encode_term(term) { 73088 73089 return(encode_each_term(encode_each_term(term, '\n'), '\t')); 73090 } 73091 73092 73093 function build_state_string(state) { 73094 73095 var state_string = "?"; 73096 var count = 0; 73097 var val = ""; 73098 73099 for(var key in state) { 73100 if(count++) 73101 state_string += "&"; 73102 73103 if( (typeof state[key]) == "object" && ( state[key] instanceof Array ) && state[key] !== null ){ 73104 state_string += encode_array(state[key], key); 73105 } 73106 else if((typeof state[key]) == "object" && state[key] !== null) { 73107 state_string += encode_object(state[key]); 73108 } else { 73109 val = encode_term(new String(state[key])); 73110 state_string += key + "=" + val; 73111 } 73112 73113 } 73114 73115 return(state_string); 73116 } 73117 73118 /** 73119 * 73120 * @param {Object} obj hash of key-value pairs, transformed into key1=value1&key2=value2&key3=value3... 73121 * @returns {string} 73122 */ 73123 function encode_object(obj) { 73124 var val = '' 73125 for(var key in obj) { 73126 if(val.length) 73127 val += '&'; 73128 val += key + '=' + encodeURIComponent(new String(obj[key])); 73129 } 73130 return(val); 73131 } 73132 73133 function encode_array(arr,attr_key) { 73134 var target_str = ''; 73135 for(var i=0; i < arr.length; i++) { 73136 if(i>0) 73137 target_str += ','; 73138 else 73139 target_str += attr_key + '='; 73140 target_str += arr[i]; 73141 } 73142 return target_str; 73143 } 73144 73145 73146 function trim(s) 73147 { 73148 var l = 0; 73149 var r = s.length - 1; 73150
73151 while(l < s.length && s[l] == ' ') l++; 73152 while(r > l && s[r] == ' ') r -= 1; 73153 73154 return s.substring(l, r+1); 73155 } 73156 73157 /** 73158 * Callback function chained all the way from from a Search plugin. 73159 * 73160 * @param state - hash of key/value pairs that constitute the url args used for the search url. 73161 */ 73162 function load_search_page(state) { 73163 73164 state.page_num = 0; 73165 state.page_size = 22; 73166 state.no_paging = false; 73167 73168 var url = _urls.search + build_state_string(state); 73169 window.location.href = url; 73170 } 73171 73172 function show_ontology_error(err) { 73173 trace("ontology error: " + err); 73174 } 73175 73176 function build_landing_widget() { 73177 73178 _landing_widget = new landing_widget(_self.container, _the_daos, _urls, _component_configs.landing_widget ); 73179 } 73180 73181 function init() { 73182 73183 build_search_widget(); 73184 build_landing_widget(); 73185 } 73186 73187 73188 init(); 73189} 73190; 73191"use strict"; 73192/** 73193 * 73194 * @param container 73195 * @param store 73196 * @param options 73197 */ 73198function ish_gene_search_selection_control_widget(container, store, options) { 73199 73200// public properties 73201 this.container = $j(container); 73202 this.store = store; 73203 this.options = options; 73204 73205 73206 73207// public methods 73208 this.set_select_text = function(new_text) { 73209 set_view_text(new_text); 73210 } 73211 73212 73213 73214// event hooks 73215 this.on_view_selections = options ? options.on_view_selections ? options.on_view_selections : null : null; 73216 this.on_clear_selections = options ? options.on_clear_selections ? options.on_clear_selections : null : null; 73217 73218 73219// private properties 73220 var _self = this; 73221 var _view_button = null; 73222 var _clear_button = null; 73223 var _menu_button = null; 73224 var _list_name = 'View Selections'; 73225 var _count = 0; 73226 73227 73228// private methods 73229 function build_scaffold() { 73230 var html = "<div id='_select_widget_wrapper'>"; 73231 html += "<div class='_select_widget_button _select_widget_view' title='View the selected experiments in a new window'>View Selections</div>"; 73232 html += "<div class='_select_widget_menu_button _select_widget_button' title='Manage Selections'></div>"; 73233 html += "<div class='_select_widget_button _select_widget_clear' title='Remove all the selections from the current list'>Clear Selections</div>"; 73234 html += "</div>"; 73235 73236 $j(html).appendTo(_self.container); 73237 73238 _view_button = $j('#_select_widget_wrapper ._select_widget_view'); 73239 _menu_button = $j('#_select_widget_wrapper ._select_widget_menu_button'); 73240 _clear_button = $j('#_select_widget_wrapper ._select_widget_clear'); 73241 73242 _view_button.button(); //({icons:{secondary:'ui-icon-help'}}); 73243 _menu_button.button({icons:{secondary:'ui-icon-triangle-1-n'}}); //({icons:{secondary:'ui-icon-help'}}); 73244 _clear_button.button(); 73245 73246 _view_button.bind('click', do_view_click); 73247 _clear_button.bind('click', do_clear_click); 73248 _menu_button.bind('click', do_menu_click); 73249 } 73250 73251 function setup_store() { 73252 _self.store.on_add = do_count_change; 73253 _self.store.on_remove = do_count_change; 73254 _self.store.on_clear = do_count_change; 73255 _count = _self.store.count(); 73256 _list_name = _self.store.list_name; 73257 73258 update_view_label(); 73259 } 73260 73261 function do_count_change() { 73262 _count = _self.store.count(); 73263 update_view_label(); 73264 } 73265 73266 function do_menu_click() { 73267 alert("Create a new list? Change the active list? Not yet."); 73268 } 73269 73270 function update_view_label() { 73271 var label = ""; 73272 73273 if(_list_name == 'default') 73274 label = "View Selections (" + _count + ")"; 73275 else 73276 label = "View '" + _list_name + "' List (" + _count + ")"; 73277 73278 set_view_text(label); 73279 } 73280 73281 function set_view_text(text) { 73282 _view_button.find('.ui-button-text').text(text); 73283 } 73284 73285 function do_view_click(event, ui) { 73286 if(_self.on_view_selections) { 73287 var list = _self.store.get_list(); 73288 var a = []; 73289 for(var key in list) a.push(key); 73290 73291 if(a.length) 73292 _self.on_view_selections(a); 73293 else 73294 alert("There are no selections in your list."); 73295 } 73296 } 73297 73298 function do_clear_click(event, ui) { 73299 _self.store.clear(); 73300 if(_self.on_clear_selections) 73301 _self.on_clear_selections(); 73302 } 73303
73304 function init() { 73305 build_scaffold(); 73306 setup_store(); 73307 } 73308 73309 73310// initialization 73311 init(); 73312 73313 73314} 73315; 73316"use strict"; 73317/** 73318 * 73319 * @param container The primary container within which all of the search UI will live 73320 * 73321 * @param page The page object contains the configuration data for the page, which references the 73322 * application object, which manages things like the service URLs as well as application- 73323 * wide user data. The page also manages page state, which is encoded on the page URL, 73324 * and a busy indicator which is initially associated with the container given in the 73325 * controller instantiation. 73326 * 73327 * @param options optional settings 73328 */ 73329function ish_search_controller(container, page, options) { 73330 73331 this.container = $j(container); 73332 this.page = page; 73333 this.settings = { 73334 format: 'html', 73335 show_preview: false, 73336 xml_link: true, 73337 xml_root: 'experiment_search' 73338 }; 73339 73340 $j.extend(true, this.settings, options); 73341 73342 this.get_data = function () { 73343 if (_search_model) 73344 return(_search_model.get_data()); 73345 73346 return([]); 73347 }; 73348 73349 var _self = this; 73350 var _the_app = _self.page.application; 73351 var _the_page = _self.page; 73352 var _the_daos = _the_app.DAOs(); 73353 var _page_config = _the_page.get_config(); 73354 var _component_configs = _page_config.components; 73355 var _urls = _the_app.settings.urls; 73356 var _selection_store = _the_app.experiment_id_store(); 73357 var _structure_ids = _the_app.app_data().coarse_structure_ids; 73358 var _graph_structures = _the_app.app_data().coarse_structure_set; 73359 73360 var _search_widget = null; 73361 var _preview_widget = null; 73362 var _selection_widget = null; 73363 var _mcsort_widget = null; 73364 73365 var _search_model = null; 73366 var _search_view = null; 73367 var _paging_state = { 73368 page_num: 0, 73369 page_size: 20, 73370 no_paging: false 73371 }; 73372 var _resize_timer = 0; 73373 73374 var _initializing = false; 73375 var _resizing = false; 73376 var _previous_search = ""; 73377 var _first_search = true; // one time display of #selection_control_container, #data_link 73378 var _searching = false; // prevents adjust_size() from executing during a search. That's all this does. 73379 var _queued_state = null; 73380 var _last_search_attempted = {search_type: ''}; 73381 73382 73383 function init() { 73384 73385 if (_self.settings.format != 'html') { 73386 73387 // don't render html, just do the search 73388 var state = _the_page.get_state(); 73389 do_search(state); 73390 73391 } else { 73392 73393 // render the HTML view 73394 _initializing = true; 73395 73396 // create containers 73397 build_scaffold(); 73398 73399 // resize containers to fit between an upper and lower element 73400 resize_containers('#search_container', '#pFooter'); 73401 73402 // listen for back/forward button events 73403 _the_page.on_page_change = do_page_change; 73404 73405 // build components 73406 build_components(); 73407 73408 // setup event handlers 73409 $j('#search_container').bind('resize', do_resize); 73410 73411 // set search widget state and wait for ready event 73412 var state = _the_page.get_state(); 73413 73414 _initializing = false; 73415 73416 load_state(state); 73417 } 73418 } 73419 73420 function do_page_change(state) { 73421 load_state(state); 73422 } 73423 73424 function load_state(state) { 73425 if (!$j.isEmptyObject(state)) { 73426 _queued_state = state; 73427 73428 var widget = get_search_widget(_queued_state.search_type); 73429 widget.set_state(_queued_state); 73430 } 73431 } 73432 73433 function get_search_widget(search_type) { 73434 // no longer do anything fancy 73435 return(_search_widget); 73436 } 73437 73438 function build_components() { 73439 73440 // search widget 73441 var search_options = { 73442 on_search: do_search, 73443 on_ontology_error: show_ontology_error, 73444 on_search_ready: do_search_ready, 73445 on_search_error: show_search_error, 73446 on_help: do_help 73447 }; 73448 var search_plugins = _component_configs.search_widget.search_plugins; 73449 _search_widget = new ish_search_widget($j('#search_container'), _the_daos.data, search_plugins, search_options); 73450 73451 // selection widget (view/clear selections) 73452 var selection_options = { 73453 on_clear_selections: do_clear_selections, 73454 on_view_selections: do_view_selections 73455 }; 73456 _selection_widget = new ish_gene_search_selection_control_widget($j('#selection_control_container'), _selection_store, selection_options); 73457 73458 // the selection controls are hidden until there are search results.
73459 $j('#selection_control_container').hide(); 73460 } 73461 73462 function build_scaffold() { 73463 73464 var html = ""; 73465 html += "<div id='search_container'></div>"; 73466 html += "<div id='search_result_common_container'>"; // add a container shared by ISH and meta search results 73467 html += " <table id='search_block'>"; 73468 html += " <tr>"; 73469 html += " <td id='result_cell'>"; 73470 html += " <div id='result_block'>"; 73471 html += " <div id='pager_container'></div>"; 73472 html += " <div id='search_results_container'></div>"; 73473 html += " <div id='selection_control_container'></div>"; 73474 html += " </div>"; 73475 html += " </td>"; 73476 html += " </tr>"; 73477 73478 if (_self.settings.xml_link) { 73479 html += " <tr>"; 73480 html += " <td>"; 73481 html += " <div id='data_link' class='info_blockx data_link_block'>"; 73482 html += " This data is also available as <a id='xml_link' href='javascript:export_xml(\""; 73483 html += _self.settings.xml_root + "\", true, \"experiments\");' >XML</a>"; 73484 html += " </div>"; 73485 html += " <form id='xml_form' action='/export/ish/data.xml' method='POST' target='_blank'>"; 73486 html += " <input type='hidden' name='type' value='application/xml'/>"; 73487 html += " <input id='xml_form_data' type='hidden' name='data' value=''/>"; 73488 html += " </form>"; 73489 html += " </td>" 73490 html += " </tr>"; 73491 } 73492 73493 html += " </table>"; 73494 html += "</div>"; 73495 73496 $j(html).appendTo(_self.container); 73497 } 73498 73499 /** 73500 * Callback function chained all the way from from a Search plugin. 73501 * 73502 * @param state - hash of key/value pairs that constitute the url args used for the search url. 73503 */ 73504 function do_search_ready() { 73505 73506 if (_initializing) 73507 return; 73508 73509 if (_queued_state) { 73510 var widget = get_search_widget(_queued_state.search_type); 73511 var state = widget.get_state(); 73512 73513 do_search(state); 73514 _queued_state = null; 73515 } 73516 } 73517 73518 /** 73519 * 73520 * @param search_state - hash of key/value pairs that constitute the url args used for the search url, and paging info 73521 */ 73522 function do_search(search_state) { 73523 73524 try { 73525 73526 if (!search_state) 73527 return; 73528 73529 _searching = true; 73530 $j('#busy_container').css('display', 'none'); 73531 73532 // merge in default paging info, if it's not present 73533 var state = {}; 73534 73535 _paging_state.page_num = 0; 73536 if (_search_model) 73537 _search_model.reset_page_num(); 73538 73539 $j.extend(true, state, search_state, _paging_state); 73540 73541 if (state.search_type != _previous_search) 73542 _search_model = create_search_model(state); 73543 73544 _last_search_attempted = state; 73545 _search_model.search(state.search_term, state); 73546 73547 } catch (e) { 73548 search_failure(_search_model, e); 73549 } 73550 } 73551 73552 function create_search_model(search_state) { 73553 73554 var widget = get_search_widget(search_state.search_type); 73555 var model_options = { 73556 on_search_failure: search_failure, 73557 on_search_begin: do_search_begin, 73558 on_search_complete: do_search_complete, // ideally these three methods would be handled by the view 73559 on_sort_complete: do_sort_complete, // 73560 on_page_complete: do_page_complete, // 73561 page_size: search_state.page_size, 73562 page_num: search_state.page_num, 73563 no_page: search_state.no_paging, 73564 product_names: widget.get_definition().products 73565 }; 73566 var model = null; 73567 var daos = { 73568 rma_dao: _the_daos.data, 73569 analysis_dao: _the_daos.analysis 73570 }; 73571 73572 var base_config = _component_configs.result_table.column_config.base_config; 73573 var custom_config = []; 73574 var combined_config; 73575 var config_name = "column_config"; 73576 73577 switch (search_state.search_type) { 73578 case 'gene': 73579 custom_config = _component_configs.result_table.column_config.gene_column_config; 73580 combined_config = combine_column_configs(base_config, custom_config, config_name); 73581 $j.extend(true, model_options, combined_config); 73582 model = new ish_gene_search_model(daos, model_options); 73583 model.IshCommon = new IshCommon; 73584 break; 73585 case 'advanced_gene': 73586 custom_config = _component_configs.result_table.column_config.gene_column_config; 73587 combined_config = combine_column_configs(base_config, custom_config, config_name); 73588 $j.extend(true, model_options, combined_config); 73589 model = new faceted_search_model(daos, model_options); 73590 model.IshCommon = new IshCommon; 73591 break; 73592 case 'gene_classification': 73593 custom_config = _component_configs.result_table.column_config.gene_classification_column_config; 73594 combined_config = combine_column_configs(base_config, custom_config, config_name); 73595 $j.extend(true, model_options, combined_config); 73596 model = new gene_classification_search_model(daos, model_options); 73597 model.IshCommon = new IshCommon; 73598 break; 73599 case 'manual_annotation': 73600 custom_config = _component_configs.result_table.column_config.manual_annotation_column_config; 73601 combined_config = combine_column_configs(base_config, custom_config, config_name); 73602 $j.extend(true, model_options, combined_config); 73603 model = new manual_annotation_search_model(daos, model_options); 73604 model.IshCommon = new IshCommon; 73605 break; 73606 default: 73607 throw 'Unknown search type requested.'; 73608 } 73609 73610 model.onPagingInfoChanged.subscribe(on_page_info_change); 73611 73612 return(model); 73613 } 73614 73615 function create_search_view(search_state, model) { 73616 73617 var view_options = { 73618 urls: _urls, 73619 structure_ids: _structure_ids, 73620 graph_structures: (_graph_structures ? _graph_structures.length ? _graph_structures[0].structure_sets_structures : [] : []), 73621 mcsort_parent_id: "result_block", 73622 mcsort_config: {}, 73623 on_row_over: do_row_over, 73624 on_row_out: do_row_out, 73625 on_row_click: do_row_click 73626 }; 73627 73628 var custom_config = []; 73629 var mcsort_config = {}; 73630 73631 switch (search_state) { 73632 case 'gene': 73633 custom_config = _component_configs.result_table.column_config.gene_column_config; 73634 mcsort_config = _component_configs.result_table.multicolumn_sort_config.gene_mcsort_config; 73635 break; 73636 case 'advanced_gene': 73637 custom_config = _component_configs.result_table.column_config.advanced_gene_column_config; 73638 mcsort_config = _component_configs.result_table.multicolumn_sort_config.gene_mcsort_config; 73639 break; 73640 case 'gene_classification': 73641 custom_config = _component_configs.result_table.column_config.gene_classification_column_config; 73642 mcsort_config = _component_configs.result_table.multicolumn_sort_config.gene_mcsort_config; 73643 break; 73644 case 'manual_annotation': 73645 custom_config = _component_configs.result_table.column_config.manual_annotation_column_config; 73646 mcsort_config = _component_configs.result_table.multicolumn_sort_config.manual_annotation_mcsort_config; 73647 break; 73648 default: 73649 throw 'Unknown search view requested.'; 73650 } 73651 73652 // add base_config + custom columns array elements to the view_options' column_config array 73653 // add the required columns to the view_options hash 73654 var base_config = _component_configs.result_table.column_config.base_config; 73655 var config_name = "column_config"; 73656 var combined_config = combine_column_configs(base_config, custom_config, config_name); 73657 $j.extend(true, view_options, combined_config); 73658 $j.extend(true, view_options, {"mcsort_config": mcsort_config}); 73659 73660 // TODO: remove magic number (row height) from ish_search_view call 73661 var view = new ish_search_view($j('#search_results_container'), model, _selection_store, 21, view_options); 73662 73663 return (view); 73664 } 73665 73666 /** 73667 * Combine (join) contents of two column configs 73668 * Column config consists of array of hashes: [{column_name:column_value}] 73669 * 73670 * @param config1 73671 * @param config2 73672 * @param {String} name of new hash 73673 * @returns {Object} hash containing array of combined column config 73674 */ 73675 function combine_column_configs(config1, config2, name) { 73676 var new_config = {}; 73677 var copy_1 = []; 73678 var copy_2 = []; 73679 73680 $j.extend(true, copy_1, config1); 73681 $j.extend(true, copy_2, config2); 73682 73683 new_config[name] = []; 73684 73685 var i, len; 73686 for (i = 0, len = copy_1.length; i < len; ++i) { 73687 new_config[name].push(copy_1[i]); 73688 } 73689 for (i = 0, len = copy_2.length; i < len; ++i) { 73690 new_config[name].push(copy_2[i]); 73691 } 73692 73693 return new_config; 73694 } 73695 73696 73697 function do_search_complete(model) { 73698 73699 if (_self.settings.format != 'html') { 73700 73701 if (_self.settings.on_ready) 73702 _self.settings.on_ready(model); 73703 73704 } else { 73705 73706 var state = {}; 73707 $j.extend(true, state, _last_search_attempted); 73708 73709 if (state.search_type != _previous_search) {
73710 if (_search_view) { 73711 _search_view.destroy(); 73712 _search_view = null; 73713 } 73714 } 73715 73716 if (_search_view) 73717 _search_view.refresh(); 73718 else { 73719 _search_view = create_search_view(state.search_type, model); 73720 _previous_search = state.search_type; 73721 } 73722 73723 //update_download_link(); 73724 transition_results_in(); 73725 _searching = false; 73726 } 73727 } 73728 73729 73730 function do_search_begin(model) { 73731 _searching = true; 73732 $j('#busy_container').css('display', 'block'); 73733 $j('#search_results_container').css({'visibility': 'hidden'}); 73734 } 73735 73736 function do_page_complete(model) { 73737 transition_results_in(); 73738 _search_view.refresh(); 73739 _searching = false; 73740 } 73741 73742 function do_sort_complete(model) { 73743 transition_results_in(); 73744 73745 _search_view.refresh(); 73746 _searching = false; 73747 } 73748 73749 function do_row_over(item) { 73750 73751 return(true); 73752 } 73753 73754 function do_row_out(item) { 73755 73756 return(true); 73757 } 73758 73759 function do_row_click(item) { 73760 // do nothing for now 73761 return(true); 73762 } 73763 73764 function on_page_info_change(paging_info) { 73765 if (_self.settings.format != 'html') 73766 return; 73767 73768 _paging_state.page_size = paging_info.pageSize; 73769 _paging_state.page_num = paging_info.pageNum; 73770 _paging_state.no_paging = paging_info.showAll; 73771 73772 var widget = get_search_widget(_last_search_attempted.search_type); 73773 var search_state = widget.get_state(true); 73774 var state = {}; 73775 $j.extend(true, state, _paging_state, search_state); 73776 73777 _the_page.set_state(state); 73778 } 73779 73780 /** END the searching handlers **/ 73781 73782 function transition_results_in() { 73783 $j('#busy_container').css('display', 'none'); 73784 $j('#search_results_container').css({'visibility': 'visible'}); 73785 73786//TODO: handle this some other way? 73787 if (!$j.browser.msie) { 73788 $j('#search_results_container').hide(); 73789 $j('#search_results_container').fadeIn(400); 73790 } 73791 73792 if (_first_search) { 73793 $j('#selection_control_container').fadeIn(400); 73794 $j('#data_link').fadeIn(400); 73795 _first_search = false; 73796 } 73797 } 73798 73799 function do_clear_selections() { 73800 if (_search_view.clear_selections) 73801 _search_view.clear_selections(); 73802 } 73803 73804 function do_view_selections(list) { 73805 var ids = list.join(','); 73806 var url = _urls.ivt + "?id=" + ids + "&popup=true"; 73807 window.open(url, '_ivt', 'status=0,toolbar=0,menubar=0,width=1000,height=760,scrollbars=1,resizable=1'); 73808 } 73809 73810 73811 function do_resize(event, ui) { 73812 if (_resizing) 73813 return; 73814// Comment out this code to prevent re-rendering of search results table each time you click on a different search type radio button 73815// if (_resize_timer == 0) 73816// _resize_timer = setTimeout(function () { 73817// adjust_size(); 73818// }, 250); 73819 } 73820 73821 function do_help(url) { 73822 // if the url came from an application.json setting, then it is a key to the hash in _urls, and will not have 73823 // prefix 'http://' 73824 console.log("--> url before = " + url); 73825 73826 if (url.indexOf('http') != 0) { 73827 console.log("--> url before = " + url); 73828 url = _urls[url]; 73829 console.log("--> url after = " + url); 73830 } 73831 window.open(url, '_help', 'status=0,toolbar=0,menubar=0,width=800,height=600,scrollbars=1'); 73832 } 73833 73834 function adjust_size() { 73835 if (_resize_timer != 0) 73836 clearTimeout(_resize_timer); 73837 _resize_timer = 0; 73838 73839 if (_searching) 73840 return; 73841 73842 var prev_page_size = _paging_state.page_size; 73843 resize_containers('#search_container', '#pFooter'); 73844 73845 if (_search_model/* && (prev_page_size != _paging_state.page_size)*/) 73846 _search_model.setPagingOptions({pageSize: _paging_state.page_size}); 73847 73848 if (_search_view) 73849 _search_view.refresh(); 73850 } 73851 73852 function resize_containers(element_above, element_below) { 73853 _resizing = true; 73854 73855 // TODO: replace height_hack with dynamic height calculation;
73855 hack needed when multicolumn sort UI added 73856 var height_hack = 24; 73857 73858 // measure the height of the window, the position of the elements 73859 // above and below that we have to fit between, and apply minimums 73860 //var $window = $j('#search_block'); 73861 var win = { 73862 height: $j(window).height(), 73863 width: $j(window).width() 73864 } 73865 73866 var page_config = _component_configs.result_table.layout; 73867 73868 var $above = $j(element_above); 73869 var top = $above.offset().top + $above.outerHeight(); 73870 var height = win.height - top - $j(element_below).outerHeight(); 73871 var view_height = Math.max(height, page_config.min_view_height); 73872 var total_width = win.width; 73873 73874 var result_width = Math.max(page_config.min_result_width, ((total_width - page_config.min_preview_width) * 0.9)); 73875 var preview_width = page_config.min_preview_width ? Math.max(page_config.min_preview_width, ((total_width - result_width) - 60)) : 0; 73876 page_config.number_of_rows = Math.floor((view_height - 80 /*magic number should be replaced with component measurements*/) / page_config.row_height); 73877 _paging_state.page_size = page_config.number_of_rows; 73878 73879 // set heights 73880 $j('#result_cell').css({width: result_width + 'px', 'text-align': 'center'}); 73881 $j('#result_block').css({height: (view_height + height_hack) + 'px', width: (result_width - 20) + 'px', 'text-align': 'left'}); 73882 $j('#selection_control_container').css({'text-align': 'left', width: parseInt(result_width - 20) + 'px'}); 73883 $j('#search_results_container').css({height: (view_height - 58), width: parseInt(result_width - 20) + 'px'}); 73884 73885 // set width 73886 if (page_config.min_preview_width) 73887 $j('#preview_cell').css({minWidth: preview_width}); 73888 73889 _resizing = false; 73890 } 73891 73892 73893 function search_failure(model, error) { 73894 // we're destroying the _view object by showing the error, so 73895 // rebuild it. 73896 transition_results_in(); 73897 show_search_error(error); 73898 } 73899 73900 function show_ontology_error(msg) { 73901 var cont = $j('#search_results_container'); 73902 var err_msg = 'Due to an error communicating with the data service, differential search is not available at this time. Please try again in a few minutes'; 73903 73904 if (msg && msg.length) 73905 err_msg += ' (' + msg + ')'; 73906 else 73907 err_msg += '.'; 73908 73909 container_error(cont, err_msg); 73910 } 73911 73912 function show_search_error(msg) { 73913 var cont = $j('#search_results_container'); 73914 var err_msg = 'Your search could not be completed'; 73915 73916 if (msg) { 73917 if (msg.msg) 73918 err_msg += ' (' + msg.msg + ')'; 73919 else 73920 err_msg += ' (' + msg + ')'; 73921 } 73922 else 73923 err_msg += '.'; 73924 73925 err_msg += '<br/><br/>Please try again.'; 73926 73927 container_error(cont, err_msg); 73928 } 73929 73930 function container_error(container, msg) { 73931 // it would be nice to have one central 73932 // place for handling UI error reporting... 73933 // if(_error_manager) { 73934 // _error_manager.ui_report(_self, container, msg); 73935 // return; 73936 // } 73937 73938 var err_html = "<div class='err_notice ui-state-error'>" + msg + "</div>"; 73939 container.html(err_html); 73940 } 73941 73942 function on_widgets_ready() { 73943 73944 // if there's search state available, run the search 73945 } 73946 73947 init(); 73948} 73949; 73950"use strict"; 73951/* 73952 * This is the panel that appears to the right of the search results table, and contains additional metadata that is 73953 * updated for each row that the user clicks on in the search results table. 73954 * 73955 * Notes: CSS ids with 'pw' in name stand for 'preview widget' 73956 */ 73957AIBS.UI.ish_search_preview_info = (function ($j) { 73958 73959 return ( 73960 73961 function (container, options) { 73962 73963 // public properties 73964 this.container = $j(container); 73965 this.settings = { 73966 jquery_style: false, 73967 on_ready: null, 73968 on_neuroblast_search: null, 73969 brain_explorer_url: '', 73970 related_data_url: '', 73971 threed_thumbnail_url: '', 73972 data: null, 73973 graph_structures: [], 73974 default_structure_id: 8 73975 }; 73976 73977 $j.extend(true, this.settings, options); 73978 73979 73980 // public methods 73981 /** 73982 * Set the Gene data to be displayed 73983 * @param data AIBS.DATA.ISH_DAO.experiment_detail().probes[0].gene 73984 * @param persist 73985 */ 73986 this.set_data = function (data, persist) { 73987 73988 _info_widget.set_data(data); 73989 if (persist) { 73990 _persistent_data = data; 73991 set_selection(); 73992 } 73993 } 73994 73995 this.clear = function (persistent) { 73996 73997 do_clear(); 73998 _persistent_data = null; 73999 clear_selection(); 74000 74001 if (_persistent_data) 74002 _info_widget.set_data(_persistent_data); 74003 } 74004 74005 this.get_state = function () { 74006 return(do_get_state()); 74007 } 74008 74009 this.set_state = function (state) { 74010 do_set_state(state); 74011 } 74012 74013 /** 74014 * A unique ID for this widget. This is not an HTML ID, it is an integer value scoped to 74015 * AIBS.UI. 74016 */ 74017 this.id = function () { 74018 if (_info_widget) 74019 return(_info_widget.id); 74020 74021 return(null); 74022 } 74023 74024 74025 // private properties 74026 /** 74027 * @private 74028 * @scope AIBS.UI.ish_gene_widget 74029 */ 74030 var _self = this; 74031 var _info_widget = null; 74032 var _ontology_model = false;
74033 var _seed = 0; 74034 var _structure_selector = null; 74035 var _received_state = null; 74036 var _ontology_loaded = false; 74037 var _persistent_data = null; 74038 var _graph_container_id = 'null'; 74039 var _3D_thumb = null; 74040 var _3D_file_link = null; 74041 74042 74043 /** 74044 * The layout, data_map & decorators are defined for this info widget. 74045 */ 74046 function build_table() { 74047 74048 // describe the table 74049 var layout = { 74050 columns: 2, 74051 rows: 9, 74052 spans: [ 74053 // header row 74054 {row: 0, column: 0, column_span: 2, row_span: 1}, 74055 74056 // neuroblast controls 74057 {row: 2, column: 0, column_span: 2, row_span: 1}, 74058 74059 // spacer 74060 {row: 3, column: 0, column_span: 2, row_span: 1}, 74061 74062 // row 4 has the brain explorer controls (now deleted from code, might need to reformat?) 74063 74064 // 3d thumbnail 74065 {row: 5, column: 0, column_span: 2, row_span: 1}, 74066 74067 // spacer 74068 {row: 6, column: 0, column_span: 2, row_span: 1}, 74069 74070 // related data controls 74071 {row: 7, column: 0, column_span: 2, row_span: 1}, 74072 {row: 8, column: 0, column_span: 2, row_span: 1} 74073 74074 ] 74075 }; 74076 // describe how data should be mapped to cells in the table 74077 var data_map = [ 74078 // a couple fields that just need the experiment ID 74079 {field: 'id', cell: [0, 0]}, // used to set the seed ID for neuroblast 74080 {field: 'probes[0].gene.acronym', cell: [1, 1]}, 74081 {field: 'id', cell: [0, 5]}, 74082 {field: 'id', cell: [0, 8]} // the experiment ID is used to create the related data link 74083 ]; 74084 74085 // which cells require a callback to massage the data presentation before 74086 // placing it in the table. 74087 var decorators = { 74088 '0,0': set_seed, 74089 '1,1': show_gene, 74090 '0,5': make_be_thumbnail, 74091 '0,8': make_related_link 74092 }; 74093 74094 var options = { 74095 table_class: '_preview_widget', 74096 jquery_theme: false 74097 }; 74098 74099 // build the widget 74100 _info_widget = new AIBS.UI.info_widget(_self.container, layout, data_map, decorators, options); 74101 74102 // set the static field labels 74103 set_labels(_info_widget); 74104 } 74105 74106 function set_selection() { 74107 if (_persistent_data && _persistent_data.plane_of_section.name != 'coronal') { 74108 $j('#_pw_image_set_check').attr('checked', false); 74109 $j('#_pw_image_set_check').attr('disabled', true); 74110 } 74111 else 74112 $j('#_pw_image_set_check').removeAttr('disabled'); 74113 74114 74115 $j('#_pw_nb_search').button({disabled: false}); 74116 //$j('#_pw_neuroblast').attr('disabled',false); 74117 $j('#_pw_neuroblast').dropdownchecklist('enable'); 74118 } 74119 74120 function clear_selection() { 74121 74122 $j('#_pw_image_set_check').attr('checked', false); 74123 $j('#_pw_image_set_check').attr('disabled', true); 74124 74125 $j('#_pw_nb_search').button({disabled: true}); 74126 74127 $j('#_pw_neuroblast').dropdownchecklist('disable'); 74128 $j('#_pw_neuroblast').val([_self.settings.default_structure_id]); 74129 $j('#_pw_neuroblast').dropdownchecklist('refresh'); 74130 } 74131 74132 function do_get_state() { 74133 74134 if (!_seed) { 74135 alert("A correlation search must be done in relation to a specific experiment. Please select an experiment from the main table by clicking on it, then try again."); 74136 return(null); 74137 } 74138 74139 var selections = $j('#_pw_neuroblast').val(); 74140 if (!selections || !selections.length) { 74141 alert("Please select at least one structure."); 74142 return(null); 74143 } 74144 74145 var state = { 74146 domain: selections, 74147 rows: [_seed], 74148 image_set: ($j('#_pw_image_set_check')[0].checked == true) ? 'P56coronal' : 'P56', 74149 search_type: 'neuroblast' 74150 } 74151 return(state); 74152 } 74153 74154 function do_set_state(state) { 74155 if (!state) 74156 return; 74157 74158 _received_state = state; 74159 _seed = parseInt(_received_state.rows); 74160 74161 var domain = _received_state.domain ? _received_state.domain.split(',') : []; 74162 var select = $j('#_pw_neuroblast'); 74163 select.val(domain); 74164 select.dropdownchecklist('refresh'); 74165 74166 _received_state = null; 74167 74168 if (_self.settings.on_ready) 74169 _self.settings.on_ready(); 74170 } 74171 74172 74173 function load_neuroblast_state() { 74174 // _structure_selector.ontology_autocomplete('set_structure_ids', _received_state.domain.split(',')); 74175 _seed = parseInt(_received_state.rows); 74176 _received_state = null; 74177 74178 if (_self.settings.on_ready) 74179 _self.settings.on_ready(); 74180 } 74181 74182 74183 function do_clear() { 74184 // what's this? 74185 _info_widget.set_content(0, 8, ""); 74186 _info_widget.set_content(1, 1, ""); 74187 if (_3D_thumb) 74188 _3D_thumb.clear(); 74189 74190 if (_3D_file_link) 74191 _3D_file_link.set_url(null); 74192 } 74193 74194 /** 74195 * The static field labels are set 74196 * @param widget The underlying info_widget 74197 */ 74198 function set_labels(widget) { 74199 74200 74201 // var open_close_button = "<span id='_pw_open_close' class='ui-state-default ui-corner-all ui-icon-container'>"; 74202 // open_close_button += "<span class='ui-icon ui-icon-pin-s' title='Click to hide this preview pane'></span></span>"; 74203 // _info_widget.set_content(0,0, open_close_button); 74204 74205 var neuroblast_input = "<select id='_pw_neuroblast' multiple='multiple'/>"; 74206 var selected = ''; 74207 for (var i = 0; i < _self.settings.neuroblast_structures[0].structure_sets_structures.length; i++) { 74208 var opt = _self.settings.neuroblast_structures[0].structure_sets_structures[i]; 74209 if (parseInt(opt.structure_id) == _self.settings.default_structure_id) 74210 selected = 'selected'; 74211 else 74212 selected = ''; 74213 neuroblast_input += "<option value='" + opt.structure_id + "' " + selected + ">" + opt.name + " (" + opt.acronym + ")</option>"; 74214 } 74215 neuroblast_input += "</select>"; 74216 neuroblast_input += "<div id='_pw_image_set_wrapper'><input type='checkbox' id='_pw_image_set_check'/>Coronal data only</div><div id='_pw_nb_search'>Search</div>"; 74217 _info_widget.set_content(0, 1, "Correlation"); 74218 _info_widget.set_content(0, 2, neuroblast_input); 74219 74220 _info_widget.set_content(0, 4, "BrainExplorer"); 74221 74222 // 5 spacer 74223 _info_widget.set_content(0, 7, "Related Institute Data"); 74224 74225 $j('#_pw_open_close').hover(function () { 74226 $j(this).toggleClass("ui-state-hover"); 74227 }).bind('click', do_open_close); 74228 74229 var header_class = _self.settings.jquery_style ? 'ui-widget-header' : '_pw_header'; 74230 $j('._preview_widget td[colspan="2"]').addClass(header_class); 74231 $j('._preview_widget td[colspan!="2"]').addClass('_pw_label'); 74232 74233 $j('#_pw_neuroblast').dropdownchecklist({ 74234 emptyText: "Select Structures...", 74235 explicitClose: "Close...", 74236 icon: { 74237 placement: 'right', 74238 toOpen: 'ui-icon-triangle-1-s', 74239 toClose: 'ui-icon-triangle-1-w' 74240 }, 74241 zIndex: 200 74242 }); 74243 $j('#_pw_neuroblast').dropdownchecklist('disable'); 74244 74245 74246 $j('#_pw_nb_search').button().bind('click', do_neuroblast_search); 74247 clear_selection(); 74248 74249 if (!_ontology_model) { 74250 _ontology_model = new ontology_model(null, 9, {on_ready: do_ontology_load, on_error: _self.on_ontology_error}); 74251 } 74252 else { 74253 do_ontology_load(_ontology_model); 74254 } 74255 74256 } 74257 74258 function do_neuroblast_search(event, ui) { 74259 74260 var state = do_get_state(); 74261 if (!state) 74262 return; 74263 74264 if (_self.settings.on_neuroblast_search) 74265 _self.settings.on_neuroblast_search(_self, state); 74266 } 74267 74268 function do_ontology_load(model) { 74269 /* 74270 // the model has finished loading and the UI has been drawn. 74271 _structure_selector = $j('#_pw_neuroblast'); 74272 _structure_selector.ontology_autocomplete(model, {horizontal_align:'right'}); 74273 74274 // now that the UI is fully available, load existing state 74275 // if there's some just waiting around for us-- 74276 */ 74277 _ontology_loaded = true; 74278 if (_received_state) 74279 load_neuroblast_state(); 74280 } 74281 74282 74283 function do_ontology_error(model, error) { 74284 var msg = "Due to an error in communicating with the data service, Correlation search is not available at this time. "; 74285 msg += "Please try again in a few minutes."; 74286 msg += "Error: " + error; 74287 alert(msg); 74288 } 74289 74290 function do_open_close(event, ui) { 74291 74292 var $cont = $j(this); 74293 var $this = $cont.find('.ui-icon'); 74294 if ($this.hasClass('ui-icon-pin-s')) { 74295 $this.removeClass('ui-icon-pin-s'); 74296 $this.addClass('ui-icon-pin-w'); 74297 $this.attr('title', 'Click to show the preview pane'); 74298 } else { 74299 $this.removeClass('ui-icon-pin-w'); 74300 $this.addClass('ui-icon-pin-s'); 74301 $this.attr('title', 'Click to hide this preview pane'); 74302 } 74303 } 74304 74305 function make_related_link(container, data) { 74306 if (!data) 74307 return("") 74308 74309 var jpg_url = _self.settings.related_data_url + '?section_data_set=' + data; 74310 var html_link = jpg_url.replace('.jpg', '.html'); 74311 var link = "<a class='_pw_related_link' href='" + html_link + "' target='_related_data'><img id='_pw_related_data_img' src='" + jpg_url + "'/></a>"; 74312 return(link); 74313 } 74314 74315 function make_be_thumbnail(container, data) { 74316 if (!data) 74317 return("") 74318 74319 if (!_3D_thumb) { 74320 //var link = "<a class='_pw_3dlink' href='" + _self.settings.brain_explorer_url + data + "'></a>"; 74321 var link = "<div id='_pw_3d_thumbnail_placeholder'></div>"; 74322 $j(link).appendTo(container); 74323 _3D_thumb = new AIBS.UI.threed_thumbnail_widget('#_pw_3d_thumbnail_placeholder', data, _self.settings.threed_thumbnail_url, {}); 74324 } 74325 else { 74326 _3D_thumb.set_experiment(data); 74327 } 74328 return(null); 74329 } 74330 74331 function set_seed(container, data) { 74332 if (data) 74333 _seed = data; 74334 74335 return(null); 74336 } 74337 74338 74339 function show_gene(container, data) { 74340 if (!data) 74341 return(""); 74342 74343 return(data); 74344 } 74345 74346 74347 function show_expressions(response_data, text_status, xhdr) { 74348 var data = response_data ? response_data[0] : null; 74349 if (!data) 74350 return; 74351 new expression_graph_widget($j('#' + _graph_container_id), data.structure_unionizes, _self.settings.graph_structures, {graph_mode: 'small', tooltip: true}); 74352 } 74353 74354 74355 function graph_failure(model, error) { 74356 // swallowing this error 74357 } 74358 74359 /** 74360 * A decorator function that creates a comma-delimited list out of an array of tag names. 74361 * @param container 74362 * @param data 74363 */ 74364 function list_tags(container, data) { 74365 if (!data) 74366 return(""); 74367 74368 var s = ""; 74369 for (var i = 0; i < data.length; i++) { 74370 if (i) 74371 s += ", "; 74372 s += data[i].name 74373 } 74374 return(s); 74375 } 74376 74377 /** 74378 * A decorator function that creates a comma-delimited list out of an array of alias acronyms. 74379 * @param container 74380 * @param data 74381 */ 74382 function list_aliases(container, data) { 74383 if (!data) 74384 return(""); 74385 74386 var s = ""; 74387 for (var i = 0; i < data.length; i++) { 74388 if (i) 74389 s += ", "; 74390 s += data[i].acronym 74391 } 74392 var w = $j(container).width();
74393 var d = "<div class='_pw_list' style='width:" + w + "px;' title='" + s + "'>" + s + "</div>"; 74394 return(d); 74395 } 74396 74397 74398 function inspect(prefix, obj) { 74399 var s = ""; 74400 for (var key in obj) { 74401 if ((typeof obj[key]) == "function") 74402 s = s; 74403 else if ((typeof obj[key]) == "object" && obj[key] !== null & key != "element") 74404 s += "\n" + prefix + "key: " + key + inspect(prefix + " ", obj[key]); 74405 else 74406 s += "\n" + prefix + "key: " + key + ", value: " + obj[key]; 74407 } 74408 return(s); 74409 } 74410 74411 /** 74412 * builds the table. 74413 */ 74414 function init() { 74415 build_table(); 74416 } 74417 74418 init(); 74419 } 74420 74421 74422 ); 74423 74424})(jQuery); 74425"use strict"; 74426/** 74427 * 74428 * @param container 74429 * @param model 74430 * @param experiment_selections 74431 * @param table_row_height 74432 * @param options 74433 */ 74434function ish_search_view(container, model, experiment_selections, table_row_height, options) { 74435 74436 this.container = $j(container); 74437 this.model = model; 74438 74439 this.on_row_over = options ? options.on_row_over ? options.on_row_over : null : null; 74440 this.on_row_out = options ? options.on_row_out ? options.on_row_out : null : null; 74441 this.on_row_click = options ? options.on_row_click ? options.on_row_click : null : null; 74442 74443 this.settings = { 74444 urls: null, 74445 structure_ids: [], 74446 pager_container: null, 74447 row_over_class: '_row_over', 74448 selected_row_class: '_row_select', 74449 column_config: [], 74450 mcsort_config: {}, 74451 table_options: { 74452 rowCssClasses: row_formatter, 74453 rowHeight: table_row_height, 74454 syncColumnCellResize: true, 74455 forceFitColumns: true, 74456 enableAsyncPostRender: true, 74457 asyncPostRenderDelay: 5, 74458 enableAddRow: false, 74459 leaveSpaceForNewRows: false, 74460 autoHeight: false, 74461 showSecondaryHeaderRow: false 74462 } 74463 }; 74464 74465 74466 if (options) 74467 $j.extend(true, this.settings, options); 74468 74469 74470 this.refresh = function () { 74471 _result_table.invalidate(); 74472 } 74473 74474 this.clear_selections = function () { 74475 do_clear_selections(); 74476 } 74477 74478 this.destroy = function () { 74479 74480 unbind_events(); 74481 74482 for (var key in _plots) { 74483 _plots[key].shutdown(); 74484 delete _plots[key]; 74485 } 74486 74487 if (_result_table) 74488 _result_table.destroy(); 74489 } 74490 74491 var _self = this; 74492 var _mcsort = null; // multicolumn sort widget instance 74493 var _result_table = null; // Slick.Grid instance 74494 var _the_pager = null; // Slick.Controls.Pager instance 74495 var _table_ready = false; 74496 var _expression_node_index = {}; 74497 var _experiment_id_lists = experiment_selections; 74498 var _previous_selection = null; 74499 var _plots = {}; 74500 74501 74502 function init() { 74503 eval_columns(); 74504 build_mcsort(); 74505 build_table(); 74506 bind_events(); 74507 } 74508 74509 function eval_columns() { 74510 var len = _self.settings.column_config.length; 74511 for (var i = 0; i < len; i++) { 74512 if (_self.settings.column_config[i].formatter) 74513 _self.settings.column_config[i].formatter = eval(_self.settings.column_config[i].formatter); 74514 74515 if (_self.settings.column_config[i].asyncPostRender) 74516 _self.settings.column_config[i].asyncPostRender = eval(_self.settings.column_config[i].asyncPostRender); 74517 } 74518 74519 _self.settings.column_config.sort(column_sorter); 74520 } 74521 74522 function column_sorter(a, b) { 74523 return(a.order - b.order); 74524 } 74525 74526 function bind_events() { 74527 $j('._experiment_master_check').bind('click', do_master_check); 74528 $j('._aibs_row').live('mouseover', do_mouse_over); 74529 $j('._aibs_row').live('mouseout', do_mouse_out); 74530 $j('._aibs_row').live('click', do_row_click); 74531 } 74532 74533 function unbind_events() { 74534 $j('._experiment_master_check').unbind('click'); 74535 $j('._aibs_row').die('mouseover'); 74536 $j('._aibs_row').die('mouseout'); 74537 $j('._aibs_row').die('click'); 74538 } 74539 74540 function do_mouse_over(event, ui) { 74541 var item = _self.model.getItem(parseInt($j(this).attr('row'))); 74542
74543 // the caller can cancel the default behavior by returning false 74544 if (_self.on_row_over) 74545 if (!_self.on_row_over(item)) 74546 return; 74547 74548 // default behavior adds the '_row_over' class 74549 $j(this).addClass(_self.settings.row_over_class); 74550 $j(this).find('.slick-cell').addClass(_self.settings.row_over_class); 74551 } 74552 74553 function do_mouse_out(event, ui) { 74554 var item = _self.model.getItem(parseInt($j(this).attr('row'))); 74555 74556 // the caller can cancel the default behavior by returning false 74557 if (_self.on_row_out) 74558 if (!_self.on_row_out(item)) 74559 return; 74560 74561 $j(this).removeClass(_self.settings.row_over_class); 74562 $j(this).find('.slick-cell').removeClass(_self.settings.row_over_class); 74563 } 74564 74565 function do_row_click(event, ui) { 74566 var row = $j(this); 74567 var item = _self.model.getItem(parseInt(row.attr('row'))); 74568 74569 // the caller can cancel the default behavior by returning false 74570 if (_self.on_row_click) 74571 if (!_self.on_row_click(item)) 74572 return; 74573 74574 if (_previous_selection) { 74575 _previous_selection.removeClass(_self.settings.selected_row_class); 74576 // _previous_selection.find('.slick-cell').removeClass(_self.settings.selected_row_class); 74577 } 74578 74579 row.addClass(_self.settings.selected_row_class); 74580 // row.find('.slick-cell').addClass(_self.settings.selected_row_class); 74581 74582 _previous_selection = row; 74583 } 74584 74585 /** 74586 * Build multicolumn sort widget, put instance into _mcsort 74587 */ 74588 function build_mcsort() { 74589 var menu = _self.settings.mcsort_config.menu; 74590 var configs = _self.settings.mcsort_config.menu_configurations; 74591 var num_menus = _self.settings.mcsort_config.num_menus; 74592 var parent = _self.settings.mcsort_parent_id; 74593 _mcsort = new MultiColumnSortWidget(parent, menu, configs, num_menus, do_mcsort); 74594 } 74595 74596 function build_table() { 74597 _result_table = new Slick.Grid(_self.container, _self.model, _self.settings.column_config, _self.settings.table_options); 74598 _result_table.onSort = do_sort; 74599 _the_pager = new Slick.Controls.Pager(_self.model, _result_table, $j('#pager_container')); 74600 _table_ready = true; 74601 } 74602 74603 function inspect(prefix, obj) { 74604 var s = ""; 74605 for (var key in obj) { 74606 if ((typeof obj[key]) == "function") 74607 s = s; 74608 else if ((typeof obj[key]) == "object" && obj[key] !== null & key != "element") 74609 s += "\n" + prefix + "key: " + key + inspect(prefix + " ", obj[key]); 74610 else 74611 s += "\n" + prefix + "key: " + key + ", value: " + obj[key]; 74612 } 74613 return(s); 74614 } 74615 74616 /* 74617 * Column sort callback invoked by SlickGrid when user clicks on sortable column header 74618 * 74619 * @param sort_column {Object} SlickGrid object containing properties used for column sorting, including name:<column heading name>, 74620 * field:<sort field specification>. Example name:'Gene Symbol', field:'genes.acronym' 74621 * @param sort_asc {Boolean} sort direction, true if ascending, false if descending 74622 */ 74623 function do_sort(sort_column, sort_asc) { 74624 _self.model.sort(sort_column, sort_asc); 74625 74626 if (_mcsort) { 74627 // update multicolumn sort UI to display single column sort selection; UI reflects user sort action on SlickGrid 74628 _mcsort.setSingleMenuConfiguration(sort_column.name, sort_asc); 74629 } 74630 } 74631 74632 /** 74633 * Multicolumn sort callback, resulting from pressing "Sort" button 74634 * 74635 * @param sort_data array of menu selection info, consisting of hashes {name:<menu selection>, value:<menu value>, 74636 * asc:<true=ascending, false=descending>} 74637 */ 74638 function do_mcsort(sort_data) { 74639 _self.model.mcsort(sort_data); 74640 } 74641 74642 function row_formatter(row_data) { 74643 var classes = "_aibs_row"; 74644 if (row_data.failed) 74645 classes += " _failed "; 74646 74647 return(classes); 74648 } 74649 74650 function expression_cell_formatter(cell_node, row, dataContext, colDef) { 74651 74652 var exp_opts = { 74653 success_callback: show_expressions, 74654 error_callback: graph_failure, 74655 structure_ids: _self.settings.structure_ids 74656 }; 74657 74658 var id = dataContext['data_sets.id']; 74659 if (!id) 74660 return; 74661 74662 var failed = dataContext['failed']; 74663 74664 if (!failed) { 74665 cell_node.innerHTML = 'loading...'; 74666 _expression_node_index[id] = cell_node; 74667 _self.model.get_expression(id, exp_opts);//.rma_dao.experiment_expressions(id, exp_opts); 74668 } 74669 } 74670 74671 /** 74672 * the manual annotation expression data is shown as some sort of x-y axis visual graph 74673 * 74674 * @param cell_node 74675 * @param row 74676 * @param dataContext 74677 * @param colDef 74678 */ 74679 function manual_expression_cell_formatter(cell_node, row, dataContext, colDef) { 74680 74681 new manual_annotation_chart( 74682 cell_node, 74683 dataContext, 74684 { 74685 age_label_link: colDef.formatter_options.path, 74686 age_label_link_title: colDef.formatter_options.tool_tip 74687 }); 74688 74689 // check checkboxes for experiments that are already in the user's cart 74690 $j(cell_node).find('._experiment_select_check').each(function (check) { 74691 74692 var id = this.getAttribute('experiment.id');
74693 if (_experiment_id_lists.exists(id)) 74694 this.checked = true; 74695 }); 74696 74697 // add checkbox binding for the chart checkboxes. 74698 $j(cell_node).find('._experiment_select_check').bind('click', do_select_check); 74699 } 74700 74701 74702 function man_anno_download_cell_formatter(cell_node, row, dataContext, colDef) { 74703 74704 if (!dataContext || !dataContext.data_sets || !dataContext.data_sets.length) 74705 return; 74706 74707 var data_set_ids = dataContext.data_sets.map(function (item) { 74708 return(item.id) 74709 }).join(','); 74710 74711 var query = _self.settings.urls.data_service + "ManualAnnotation/query.csv?criteria=[section_data_set_id$in" + data_set_ids + "],structure,section_data_set(genes,plane_of_section,specimen(donor(age)))" + 74712 ",rma::options[tabular$eq'manual_annotations.section_data_set_id as section_data_set,genes.acronym as gene,ages.name as age,plane_of_sections.name as plane_of_section,structures.acronym as structure,manual_annotations.intensity_call,manual_annotations.density_call,manual_annotations.pattern_call,manual_annotations.mixed_population_call,manual_annotations.comment']" + 74713 ",rma::options[num_rows$eq'all']" + 74714 ",rma::options[order$eq'ages.days,structures.graph_order']"; 74715 74716 $j(cell_node).html("<a href=\"" + query + "\" style='position:relative;top:80%;left:30%;' target='_new_query'>CSV</a>"); 74717 } 74718 74719 74720 function attach_check_select(cell_node, row, dataContext, colDef) { 74721 var check = $j(cell_node).find('._experiment_select_check')[0]; 74722 if (check) 74723 $j(check).bind('click', do_select_check) 74724 } 74725 74726 74727 function show_expressions(response_data, text_status, xhdr) { 74728 var data = response_data ? response_data[0] : null; 74729 if (!data) 74730 return; 74731 74732 var exp_id = data.id; 74733 var container = $(_expression_node_index[exp_id]); 74734 if (_plots[exp_id]) { 74735 //alert("plot already exists: " + inspect('', _plots[exp_id])); 74736 _plots[exp_id].shutdown(); 74737 delete _plots[exp_id]; 74738 } 74739 74740 try { 74741 //_plots[exp_id] = new expression_graph_widget(container, data.structure_unionizes, _self.settings.graph_structures, {graph_mode:'small', tooltip:false}); 74742 build_graphlette(container, data.structure_unionizes, _self.settings.graph_structures); 74743 } catch (e) { 74744 // this throws an exception if the container has no dimensions, which happens frequently as the table is being resized/scrolled/rebuilt. 74745 } 74746 74747 } 74748 74749 function build_graphlette(container, expression_data, structure_data) { 74750 74751 var separation = 2; 74752 var max_bar_height = 5; 74753 var len = structure_data.length; 74754 var total_width = $j(container).innerWidth(); 74755 var total_height = $j(container).innerHeight(); 74756 var bar_width = (total_width / len) - separation; 74757 var expr_index = index_expressions(expression_data); 74758 74759 74760 var html = "<div class='graphlette_container'><div class='inner_graphlette_container'>"; 74761 74762 for (var i = 0; i < len; i++) { 74763 var e = expr_index[structure_data[i].structure_id]; 74764 if (e) { 74765 var energy = Math.max(0, (Math.log(e.expression_energy) / Math.log(2))); 74766 var height = (energy / max_bar_height) * total_height; 74767 var color = '#' + e.structure.color_hex_triplet; 74768 74769 html += "<div class='graphlette' style='width:" + bar_width + "px;height:" + height + "px;background-color:" + color + "'></div>"; 74770 } else { 74771 html += "<div class='graphlette' style='width:" + bar_width + "px;height:0px;background-color:#000'></div>"; 74772 } 74773 } 74774 html += "</div></div>"; 74775 74776 container.innerHTML = html; 74777 } 74778 74779 function index_expressions(expression_set) { 74780 if (!expression_set) 74781 return({}); 74782 74783 var index = {}; 74784 var len = expression_set.length; 74785 74786 for (var i = 0; i < len; i++) { 74787 index[expression_set[i].structure.id] = expression_set[i]; 74788 } 74789 74790 return(index); 74791 } 74792 74793 74794 /** 74795 * Generic cell formatter that turns the cell text into a link, accompanied by the human homolog symbol (if any) 74796 * 74797 * @param row 74798 * @param cell 74799 * @param value 74800 * @param columnDef 74801 * @param dataContext 74802 * @returns {*} 74803 */ 74804 function link_cell_formatter(row, cell, value, columnDef, dataContext) { 74805 if (value == undefined) 74806 return ''; 74807 74808 try { 74809 var options = columnDef.formatter_options; 74810 var homologous_genes = eval('dataContext.' + options.homologous_genes); 74811 74812 // only retrieve human homolog (no mice!) 74813 var human_homolog = ''; 74814 if (homologous_genes != undefined) { 74815 var len = homologous_genes.length; 74816 for (var i = 0, len = homologous_genes.length; i < len; ++i ) { 74817 if (homologous_genes[i].organism_id == 1) { 74818 human_homolog = '<span title="human homolog"> (' + homologous_genes[i].acronym + ')</span>'; 74819 break; 74820 } 74821 } 74822 } 74823 74824 var val = eval('dataContext.' + options.field); 74825 return("<a href='" + options.path + val + "'>" + value + "</a>" + human_homolog); 74826 } catch (e) { 74827 return(value); 74828 } 74829 74830 } 74831 74832 function manual_dual_expression_cell_formatter(row, cell, value, columnDef, dataContext) { 74833 74834 74835 var html = "<div class='man_anno_horiz_bar bar_intensity' title='Expression intensity: " + dataContext.intensity_call + "' style='width:"
74835+ (+dataContext.intensity_call * 10) + "%'></div>"; 74836 html += "<div class='man_anno_horiz_bar bar_density' title='Expression density: " + dataContext.density_call + "' style='width:" + (+dataContext.density_call * 10) + "%'></div>"; 74837 return(value === undefined ? '' : html); 74838 } 74839 74840 74841 /** 74842 * Formats an image with a link, or just the image itself if you leave 'path' and 'path_field' unconfigured 74843 * Uses these configuration values: 74844 * src_field - image value 74845 * path_field - link value 74846 * src - image path 74847 * path - link path 74848 * file_extension - file extension of image 74849 * img_class - CSS class for img tag 74850 * hover_text - image mouseover text 74851 * 74852 * @param row 74853 * @param cell 74854 * @param value 74855 * @param columnDef contains formatter_options from application.json 74856 * @param dataContext 74857 * @returns {string} 74858 */ 74859 function image_link_cell_formatter(row, cell, value, columnDef, dataContext) { 74860 74861 try { 74862 var comps = columnDef.formatter_options; 74863 var src_val = eval('dataContext.' + comps.src_field); 74864 var path_val = eval('dataContext.' + comps.path_field); 74865 var img_class = (comps.img_class ? comps.img_class : 'related_img'); 74866 74867 var href_open_tag = ""; 74868 var href_close_tag = ""; 74869 var hover_text = ""; 74870 74871 if (comps.path != undefined && comps.path_field != undefined) { 74872 href_open_tag = "<a href='" + comps.path + path_val + "'>"; 74873 href_close_tag = "</a>"; 74874 } 74875 74876 if (comps.hover_text != undefined) 74877 hover_text = " title='" + comps.hover_text + "' "; 74878 74879 var img_tag = "<img class='" + img_class + "' src='" + comps.src + src_val + comps.file_extension + "'" + hover_text + "/>"; 74880 return(href_open_tag + img_tag + href_close_tag); 74881 74882 } catch (e) { 74883 return(''); 74884 } 74885 } 74886 74887 function related_image_link_cell_formatter(row, cell, value, columnDef, dataContext) { 74888 74889 try { 74890 var comps = columnDef.formatter_options; 74891 74892 var src_field = eval('dataContext.' + comps.src_field); 74893 var path_field = eval('dataContext.' + comps.path_field); 74894 if (isNaN(src_field) || isNaN(path_field)) 74895 return(''); 74896 74897 var src_val = '?section_data_set=' + src_field; 74898 var path_val = '?section_data_set=' + path_field; 74899 var img_class = (comps.img_class ? comps.img_class : 'related_img'); 74900 74901 return("<a href='" + _self.settings.urls.related_data.replace('.jpg', '.html') + path_val + "'><img class='" + img_class + "' src='" + _self.settings.urls.related_data + src_val + "'/></a>"); 74902 74903 } catch (e) { 74904 return(''); 74905 } 74906 } 74907 74908 /** 74909 * adds the expression summary image to the cell. Also builds some checkboxes (for now...) 74910 * 74911 * @param row 74912 * @param cell 74913 * @param value 74914 * @param columnDef 74915 * @param dataContext 74916 * @returns {string} 74917 */ 74918 function expression_summary_image_link_cell_formatter(row, cell, value, columnDef, dataContext) { 74919 // edge condition: empty SlickGrid row 74920 if (dataContext.result_type == undefined) 74921 return ""; 74922 74923 var ages = ['E11.5', 'E13.5', 'E15.5', 'E18.5', 'P4', 'P14', 'P28']; 74924 74925 try { 74926 var comps = columnDef.formatter_options; 74927 var src_val = eval('dataContext.' + comps.src_field); 74928 var row_data = expression_summary_row_data(dataContext); 74929 var img_class = (comps.img_class ? comps.img_class : ''); 74930 var highlight_age = comps.highlight_age ? comps.highlight_age : undefined; 74931 74932 var html = "<div class='exp_summ_img_wrapper'>"; 74933 html += "<table class='exp_summ_check_wrapper'>"; 74934 74935 var the_age, open_tag, close_tag; 74936 for (var i = 0; i < ages.length; ++i) { 74937 the_age = ages[i]; 74938 open_tag = close_tag = ""; 74939 if (row_data[the_age].is_search_age && highlight_age) { 74940 open_tag = highlight_age.open_tag ? highlight_age.open_tag : ""; 74941 close_tag = highlight_age.close_tag ? highlight_age.close_tag : ""; 74942 } 74943 html += "<tr><td>" + open_tag + the_age + close_tag + "</td><td>"; 74944 html += "<input class='_experiment_select_check' experiment.id='" + row_data[the_age].id + "' " + row_data[the_age].disabled + " type='checkbox'/></td></tr>"; 74945 } 74946 html += "</table>"; // close the checkbox wrapper 74947 74948 html += "<img class='" + img_class + "' src='" + comps.src + src_val + comps.file_extension + "'/>"; 74949 html += "</div>"; // close the image wrapper 74950 74951 return(html); 74952 74953 } catch (e) { 74954 return("error formatting expression summary: " + e); 74955 } 74956 } 74957 74958 74959 /** 74960 * this is intended to make it possible for different data formats 74961 * to respond in the same way to these questions: 74962 * 74963 * Should the checkbox for age X be enabled? 74964 * What is the experiment id for age X? 74965 * 74966 * returns an anonymous object that looks like this: 74967 * { 74968 * 'E11.5':{ 74969 * disabled: 'disabled' | '' 74970 * id: the id of the data set corresponding to this age. 74971 * }, 74972 * 'E13.5':{ 74973 * disabled: 'disabled' | '' 74974 * id: the id of the data set corresponding to this age. 74975 * }, 74976 * etc. for all 7 ages. 74977 * } 74978 * 74979 * @param dataContext 74980 * @returns {Object} hash with keys corresponding to the age names, values a hash with keys 'disabled', 'id' 74981 */ 74982 function expression_summary_row_data(dataContext) { 74983 74984 var ages = ['E11.5', 'E13.5', 'E15.5', 'E18.5', 'P4', 'P14', 'P28']; 74985 var answers = {}; 74986 74987 // some sniffing of the dataContext here will be necessary to determine how to get the answers... 74988 // for a neuroblast search it works like this. I added the result_type field 74989 // in the neuroblast_search_model's get_item method to make sniffing easier. 74990 if (dataContext.result_type) { 74991 switch (dataContext.result_type) { 74992 case 'gene_search': 74993 case 'gene_classification_search': 74994 var data_sets = dataContext.experiment_detail.data_sets; 74995 for (var i = 0; i < ages.length; i++) { 74996 answers[ages[i]] = { 74997 disabled: (data_sets[ages[i]] ? '' : 'disabled'), 74998 id: (data_sets[ages[i]] ? data_sets[ages[i]].data_set_id : 0) 74999 } 75000 } 75001 break; 75002 default: 75003 break; 75004 } 75005 } 75006 75007 return(answers); 75008 } 75009 75010 /** 75011 * this is called post-render, that is after the expression_summary_image_link_cell_formatter 75012 * has built the heatmap table for each row. 75013 * 75014 * @param cell_node 75015 * @param row 75016 * @param dataContext 75017 * @param colDef 75018 */ 75019 function expression_summary_check_select(cell_node, row, dataContext, colDef) { 75020 75021 // check checkboxes for experiments that are already in the user's cart 75022 $j(cell_node).find('input').each(function (check) { 75023 75024 var id = this.getAttribute('experiment.id');
75025 if (_experiment_id_lists.exists(id)) 75026 this.checked = true; 75027 }); 75028 75029 // add checkbox binding for the chart checkboxes. 75030 $j(cell_node).find('input') 75031 .bind('click', do_select_check); 75032 } 75033 75034 function asynch_color_structure_cell(cell_node, row, dataContext, colDef) { 75035 75036 if (dataContext && dataContext.color_hex_triplet) 75037 $j(cell_node).css({"background-color": '#' + dataContext.color_hex_triplet}); 75038 } 75039 75040 function asynch_color_age_cell(cell_node, row, dataContext, colDef) { 75041 75042 if (!dataContext || !dataContext.age_name) 75043 return; 75044 75045 var color = ""; 75046 var back = ""; 75047 switch (dataContext.age_name) { 75048 75049 case "0 mo": 75050 back = "#606060"; 75051 color = "#fff"; 75052 break; 75053 case "3 mo": 75054 back = "#808080"; 75055 color = "#fff"; 75056 break; 75057 case "12 mo": 75058 back = "#C0C0C0"; 75059 color = "#000"; 75060 break; 75061 case "48 mo": 75062 back = "#DDDDDD"; 75063 color = "#000"; 75064 break; 75065 default: 75066 back = "#000000"; 75067 color = "#fff"; 75068 } 75069 75070 var style = {"background-color": back, color: color}; 75071 75072 $j(cell_node).css(style); 75073 } 75074 75075 function experiment_cell_formatter(row, cell, value, columnDef, dataContext) { 75076 if (value) { 75077 return("<a href='/experiment/show/" + value + "'>" + value + "</a>"); 75078 } else { 75079 return ""; 75080 } 75081 } 75082 75083 function gene_cell_formatter(row, cell, value, columnDef, dataContext) { 75084 75085 return("<a href='/gene/show/" + dataContext["genes.id"] + "'>" + value + "</a>"); 75086 } 75087 75088 /** 75089 * Structure cell displays structure acronym, add hover text showing structure name 75090 * The field name for structure name is defined in application.json as {formatter_options:{field:<data access field>}} 75091 * 75092 * @param row 75093 * @param cell 75094 * @param value 75095 * @param columnDef 75096 * @param dataContext 75097 * @returns {string} 75098 */ 75099 function structure_cell_formatter(row, cell, value, columnDef, dataContext) { 75100 if (value == undefined) 75101 return ''; 75102 75103 var struct_name_field = columnDef.formatter_options.field; 75104 75105 return("<div title='" + dataContext[struct_name_field] + "'>" + value + "</div>"); 75106 } 75107 75108 function text_replace_cell_formatter(row, cell, value, columnDef, dataContext) { 75109 try { 75110 var reps = columnDef.formatter_options.replacements; 75111 var new_str = value; 75112 for (var i = 0; i < reps.length; i++) 75113 new_str = new_str.replace(new RegExp(reps[i].from), reps[i].to); 75114 75115 return(new_str); 75116 } catch (e) { 75117 return(value); 75118 } 75119 } 75120 75121 function selected_cell_formatter(row, cell, value, columnDef, dataContext) { 75122 var html = "<input type='checkbox' class='_experiment_select_check' title='Add/Remove this item to your selection list.' "; 75123 75124 var id = dataContext['data_sets.id'] ? dataContext['data_sets.id'] : dataContext.data_set_id ? dataContext.data_set_id : 0; 75125 html += " experiment.id='" + id + "'"; 75126 75127 if (dataContext.failed || id == 0) 75128 html += " disabled"; 75129 75130 html += _experiment_id_lists.exists(id) ? ' checked ' : ''; 75131 html += "/>"; 75132 return(html); 75133 } 75134 75135 function do_select_check(event, ui) { 75136 if (this.disabled) 75137 return; 75138 75139 var id = this.getAttribute('experiment.id'); 75140 75141 if (this.checked) 75142 _experiment_id_lists.add(id); 75143 else 75144 _experiment_id_lists.remove(id); 75145 75146 // we don't want checking the checkbox to select the row. 75147 event.stopPropagation(); 75148 } 75149 75150 75151 function do_master_check(event, ui) { 75152 var checked = this.checked; 75153 $j('._experiment_select_check').each( 75154 function (index, el) { 75155 if (el.disabled) 75156 return; 75157 75158 var id = el.getAttribute('experiment.id') 75159 if (id == '0') 75160 return; 75161 75162 el.checked = checked; 75163 75164 if (el.checked) 75165 _experiment_id_lists.add(id); 75166 else 75167 _experiment_id_lists.remove(id); 75168 } 75169 ); 75170 } 75171 75172 function do_clear_selections() { 75173 $j('._experiment_select_check').each( 75174 function (index, el) { 75175 if (el.disabled) 75176 return; 75177 75178 var id = el.getAttribute('experiment.id') 75179 if (id == '0') 75180 return; 75181 75182 el.checked = false;
75183 } 75184 ); 75185 } 75186 75187 75188 function graph_failure(model, error) { 75189 // swallowing this error 75190 } 75191 75192 function do_sort_complete(model) { 75193 _result_table.invalidate(); 75194 } 75195 75196 init(); 75197 75198} 75199; 75200"use strict"; 75201/** 75202 * 75203 * @param container 75204 * @param daos 75205 * @param urls 75206 * @param options 75207 */ 75208function landing_widget(container, daos, urls, options) { 75209 75210 this.container = $j(container); 75211 this.daos = daos; 75212 this.urls = urls; 75213 this.settings = { 75214 "wrapper_class":"_lw" 75215 }; 75216 $j.extend(true, this.settings, options); 75217 75218 75219 this.set_landing_mode = function(new_mode) { 75220 build_widgets(new_mode); 75221 } 75222 75223 75224 75225 var _self = this; 75226 var _widgets = {}; 75227 var _widget_containers = {}; 75228 var _widget_headers = {}; 75229 75230 75231 function build_widgets(mode) { 75232 75233 var widgets = _self.settings.components; 75234 for(var widget in widgets) { 75235 75236 var widget_config = _self.settings.components[widget]; 75237 var mode_config = mode ? widget_config.modes[mode] : widget_config.modes[widget_config.initial_mode]; 75238 75239 if(mode_config) { 75240 var widget_constructor = "new " + mode_config.class_name + "(_widget_containers[widget], _self.daos, _self.urls, mode_config.options)"; 75241 75242 if(_widgets[widget]) 75243 delete _widgets[widget]; 75244 75245 _widget_headers[widget][0].innerHTML = mode_config.title; 75246 _widgets[widget] = eval(widget_constructor); 75247 } 75248 } 75249 } 75250 75251 function build_scaffold() { 75252 75253 var html = '<div id="landing_block">'; 75254 75255 var widgets = _self.settings.components; 75256 for(var widget in widgets) { 75257 75258 html += ' <div id="' + widget + '_block" class="' + _self.settings.wrapper_class + ' _' + widget + '_class">'; 75259 html += ' <div id="' + widget + '_inner_block" class="' + _self.settings.wrapper_class + '_inner _' + widget + '_inner_class">'; 75260 html += ' <div id="' + widget + '_header" class="' + _self.settings.wrapper_class + '_header _' + widget + '_header_class"></div>'; 75261 html += ' <div id="' + widget + '_container" class="' + _self.settings.wrapper_class + '_container _' + widget + '_container_class"></div>'; 75262 html += ' </div>'; 75263 html += ' </div>'; 75264 } 75265 75266 html += '</div>'; 75267 _self.container.append(html); 75268 75269 for(var widget in widgets) { 75270 _widget_containers[widget] = $j('#' + widget + '_container'); 75271 _widget_headers[widget] = $j('#' + widget + '_header'); 75272 } 75273 } 75274 75275 function init() { 75276 75277 build_scaffold(); 75278 build_widgets(); 75279 } 75280 75281 75282 init(); 75283} 75284; 75285"use strict"; 75286/** 75287 * This Faceted Search model uses numerous common functions defined in IshCommon.js 75288 * It follows model interface defined by SlickGrid 75289 * 75290 * @return {*} 75291 * @constructor 75292 */ 75293// lifted from slick.model.js 75294function EventHelper() { 75295 this.handlers = []; 75296 75297 this.subscribe = function (fn) { 75298 this.handlers.push(fn); 75299 }; 75300 75301 this.notify = function (args) { 75302 for (var i = 0; i < this.handlers.length; i++) { 75303 this.handlers[i].call(this, args); 75304 } 75305 }; 75306 75307 return this; 75308} 75309 75310/** 75311 * 75312 * @param daos 75313 * @param options 75314 */ 75315function faceted_search_model(daos, options) { 75316 75317//public properties 75318 this.rma_dao = daos.rma_dao; 75319 this.options = options; 75320 75321// public methods 75322 75323 this.reset_page_num = function() { 75324 _page_num = 0; 75325 this.onPagingInfoChanged.notify(this.getPagingInfo()); 75326 }; 75327 75328// these two methods are used by the search_controllers 75329 /** 75330 * 75331 * @param term url arg, 'search_term' 75332 * @param options all the url args 75333 * @returns {*} 75334 */ 75335 this.search = function (term, options) { 75336 return(do_gene_search(term, options, 'search')); 75337 }; 75338 75339 this.sort = function (sort_column, sort_asc) { 75340 _search_options.order = _self.IshCommon.single_column_sort_order(sort_column, sort_asc, options.sort_suffix); 75341 if(_current_facets) 75342 _search_options.facets = _current_facets; 75343 75344 do_gene_search(_search_term, _search_options, 'sort'); 75345 }; 75346 75347 /**
75348 * Multicolumn sort 75349 * @param sort_data array of hashes containing keys name, value, asc. 75350 */ 75351 this.mcsort = function (sort_data) { 75352 _search_options.order = _self.IshCommon.mcsort_order(sort_data); 75353 do_gene_search(_search_term, _search_options, 'sort'); 75354 }; 75355 75356 this.get_data = function () { 75357 return({experiments:_the_data}); 75358 }; 75359 75360// end those two methods that are used by the search_controllers. 75361 75362 75363// these two support the implied interface for the slick.grid 75364 this.getItem = function (row_index) { 75365 return(_self.IshCommon.do_get_item(row_index, _the_data, _self.options.column_config)); 75366 }; 75367 75368 this.getLength = function () { 75369 return(_page_size); 75370 }; 75371// end slick.grid functions 75372 75373 75374// these functions & events support the implied interface required by slick.paging 75375 this.onPagingInfoChanged = new EventHelper(); 75376 75377 this.setPagingOptions = function (args) { 75378 var info = _self.IshCommon.calc_paging_info(args, _page_num, _page_size, _total_rows); 75379 _no_page = info.no_page; 75380 if (info.no_change) { 75381 return; 75382 } 75383 else { 75384 _page_num = info.page_num; 75385 _page_size = info.page_size; 75386 } 75387 75388 this.onPagingInfoChanged.notify(this.getPagingInfo()); 75389 75390 _search_options.limit = _page_size; 75391 _search_options.offset = _page_num * _page_size; 75392 if(_current_facets) 75393 _search_options.facets = _current_facets; 75394 75395 do_gene_search(_search_term, _search_options, 'page'); 75396 }; 75397 75398 this.getPagingInfo = function () { 75399 return {pageSize:_page_size, 75400 pageNum:_page_num, 75401 totalRows:_total_rows, 75402 showAll:_no_page}; 75403 }; 75404// end slick.paging support 75405 75406 75407// event hooks 75408 this.on_search_begin = this.options ? this.options.on_search_begin ? this.options.on_search_begin : null : null; 75409 this.on_search_complete = this.options ? this.options.on_search_complete ? this.options.on_search_complete : null : null; 75410 this.on_sort_complete = this.options ? this.options.on_sort_complete ? this.options.on_sort_complete : null : null; 75411 this.on_page_complete = this.options ? this.options.on_page_complete ? this.options.on_page_complete : null : null; 75412 this.on_search_failure = this.options ? this.options.on_search_failure ? this.options.on_search_failure : null : null; 75413 75414 75415// private properties 75416 var _self = this; 75417 var _search_term = ''; 75418 var _search_options = {}; 75419 var _the_data = null; 75420 var _page_size = _self.options ? _self.options.page_size ? _self.options.page_size : 25 : 25; 75421 var _page_num = _self.options ? _self.options.page_num ? _self.options.page_num : 0 : 0; 75422 var _no_page = _self.options ? _self.options.no_page ? _self.options.no_page : false : false; 75423 var _total_rows = 0; 75424 var _callback = null; 75425 var _default_order = 'genes.acronym$asc' + (this.options.sort_suffix ? ',' + this.options.sort_suffix : ',data_sets.id'); 75426 var _current_facets = null; 75427 var _last_query = {}; 75428 75429 75430 function do_gene_search(term, options, source) { 75431 75432 term = $j.trim(term); 75433 75434 if (_self.on_search_begin) 75435 _self.on_search_begin(_self); 75436 _search_term = term; 75437 75438 if (options) { 75439 if (options.limit) { 75440 _page_size = options.limit ? options.limit : _page_size; 75441 _page_num = options.offset ? options.offset / _page_size : _page_num; 75442 } else if (options.page_num) { 75443 _page_num = options.page_num; 75444 } 75445 } 75446 75447 var opts = { 75448 order:_default_order, 75449 product_name:_self.options.product_names 75450 }; 75451 $j.extend(opts, options); 75452 75453 opts.limit = opts.limit ? opts.limit : _page_size; 75454 opts.offset = opts.offset ? opts.offset : _page_num * _page_size; 75455 75456 _search_options = opts; 75457 75458 _callback = null; 75459 switch (source) { 75460 case 'search': 75461 _callback = _self.on_search_complete; 75462 break; 75463 case 'sort': 75464 _callback = _self.on_sort_complete; 75465 break; 75466 case 'page': 75467 _callback = _self.on_page_complete; 75468 break; 75469 default: 75470 throw 'Unknown search source'; 75471 } 75472 75473 // Note: _callback is invoked from inside show_search_results 75474 opts.success_callback = show_search_results; 75475 opts.error_callback = erroror; 75476 75477 if (options.facets) { 75478 // Note: if facets/ranges already parsed, skip capture_facets. 75479 // If it's not parsed, it will have a hash value, if parsed, it will be a string 75480 if (typeof options.facets === 'object') { 75481 var q = capture_facets(options.facets); 75482 opts.facets = q.facets; 75483 opts.filter = q.ranges; 75484 } 75485 _current_facets = options.facets; 75486 } 75487 else { 75488 _current_facets = null; 75489 } 75490 75491 75492 return(_self.rma_dao.experiment_search_by_gene(_search_term, opts)); 75493 } 75494 75495 75496 /** 75497 * Parse facet url args and reformats to Sphinx-friendly format. 75498 * Example: 75499 * facet_donor_age="3 mo","12 mo","48 mo" --> ' @donor_age="3 mo"|"12 mo"|"48 mo" 75500 * 75501 * @param options 75502 * @returns {{}} 75503 */ 75504 function capture_facets(options) { 75505 75506 var queries = {}; 75507 var facet_queries = ''; 75508 var ranges = ""; 75509 75510 for (var key in options) { 75511 if (key.indexOf('facet_range_') == 0)
75512 ranges += options[key].replace(/facet_/g, '').replace(/\*gte/g, '>=').replace(/\*lte/g, '<='); 75513 else if (key.indexOf('facet_') == 0) 75514 facet_queries += ' @' + key.substr(6) + ' ' + options[key].replace(/,/g, '|'); 75515 } 75516 75517 queries.facets = facet_queries; 75518 queries.ranges = ranges; 75519 return(queries); 75520 } 75521 75522 function capture_range(option) { 75523 75524 } 75525 75526 function show_search_results(response_data, text_status, xhdr) { 75527 75528 _the_data = response_data; 75529 _total_rows = parseInt(_the_data.response.rows_available); 75530 75531 if (_callback) 75532 _callback(_self); 75533 75534 // let the pager know about it 75535 _self.onPagingInfoChanged.notify(_self.getPagingInfo()); 75536 } 75537 75538 function erroror(xhdr, text_status, error) { 75539 var err = xhdr.msg == undefined ? error : xhdr; 75540 if (_self.on_search_failure) 75541 _self.on_search_failure(_self, err); 75542 } 75543 75544} 75545 75546 75547 75548; 75549"use strict"; 75550/** 75551 * This Gene Classification Search model uses numerous common functions defined in IshCommon.js 75552 * It follows model interface defined by SlickGrid 75553 * 75554 */ 75555 75556// lifted from slick.model.js 75557function EventHelper() { 75558 this.handlers = []; 75559 75560 this.subscribe = function (fn) { 75561 this.handlers.push(fn); 75562 }; 75563 75564 this.notify = function (args) { 75565 for (var i = 0; i < this.handlers.length; i++) { 75566 this.handlers[i].call(this, args); 75567 } 75568 }; 75569 75570 return this; 75571} 75572 75573 75574/** 75575 * 75576 * @param daos 75577 * @param options 75578 */ 75579function gene_classification_search_model(daos, options) { 75580 75581//public properties 75582 this.rma_dao = daos.rma_dao; 75583 this.options = options; 75584 75585// public methods 75586 75587 this.reset_page_num = function () { 75588 _page_num = 0; 75589 this.onPagingInfoChanged.notify(this.getPagingInfo()); 75590 }; 75591 75592// these two methods are used by the search_controllers 75593 /** 75594 * 75595 * @param term url arg, 'search_term' 75596 * @param options all the url args 75597 * @returns {*} 75598 */ 75599 this.search = function (term, options) { 75600 return(do_gene_classification_search(term, options, 'search')); 75601 }; 75602 75603 this.sort = function (sort_column, sort_asc) { 75604 _search_options.order = _self.IshCommon.single_column_sort_order(sort_column, sort_asc, options.sort_suffix); 75605 do_gene_classification_search(_search_term, _search_options, 'sort'); 75606 }; 75607 75608 /** 75609 * Multicolumn sort 75610 * @param sort_data array of hashes containing key-value pairs for name, value, asc. 75611 */ 75612 this.mcsort = function (sort_data) { 75613 _search_options.order = _self.IshCommon.mcsort_order(sort_data); 75614 do_gene_classification_search(_search_term, _search_options, 'sort'); 75615 }; 75616 75617 /** 75618 * Used to get search results data to turn into XML 75619 * 75620 * @returns {Object} hash of {experiments:[search results]} 75621 */ 75622 this.get_data = function () { 75623 return({experiments: _the_data}); 75624 }; 75625 75626 this.get_expression = function (id, exp_opts) { 75627 75628 this.rma_dao.experiment_expressions(id, exp_opts); 75629 75630 } 75631// end those two methods that are used by the search_controllers. 75632 75633 75634// these two support the implied interface for the slick.grid 75635 this.getItem = function (row_index) { 75636 return(_self.IshCommon.do_get_item(row_index, _the_data, _self.options.column_config)); 75637 }; 75638 75639 this.getLength = function () { 75640 return(_page_size); 75641 }; 75642// end slick.grid functions 75643 75644 75645// these functions & events support the implied interface required by slick.paging 75646 this.onPagingInfoChanged = new EventHelper(); 75647 75648 this.setPagingOptions = function (args) { 75649 var info = _self.IshCommon.calc_paging_info(args, _page_num, _page_size, _total_rows); 75650 _no_page = info.no_page; 75651 if (info.no_change) { 75652 return; 75653 } 75654 else { 75655 _page_num = info.page_num; 75656 _page_size = info.page_size; 75657 } 75658 75659 this.onPagingInfoChanged.notify(this.getPagingInfo()); 75660 75661 _search_options.limit = _page_size; 75662 _search_options.offset = _page_num * _page_size; 75663 75664 do_gene_classification_search(_search_term, _search_options, 'page'); 75665 }; 75666 75667 this.getPagingInfo = function () { 75668 return {pageSize: _page_size, 75669 pageNum: _page_num, 75670 totalRows: _total_rows, 75671 showAll: _no_page}; 75672 }; 75673// end slick.paging support 75674 75675 75676// event hooks 75677 this.on_search_begin = this.options ? this.options.on_search_begin ? this.options.on_search_begin : null : null; 75678 this.on_search_complete = this.options ? this.options.on_search_complete ? this.options.on_search_complete : null : null; 75679 this.on_sort_complete = this.options ? this.options.on_sort_complete ? this.options.on_sort_complete : null : null; 75680 this.on_page_complete = this.options ? this.options.on_page_complete ? this.options.on_page_complete : null : null; 75681 this.on_search_failure = this.options ? this.options.on_search_failure ? this.options.on_search_failure : null : null; 75682 75683 75684// private properties 75685 var _self = this; 75686 var _search_term = ''; 75687 var _search_options = {}; 75688 var _the_data = null; 75689 var _page_size = _self.options ? _self.options.page_size ? _self.options.page_size : 25 : 25; 75690 var _page_num = _self.options ? _self.options.page_num ? _self.options.page_num : 0 : 0; 75691 var _no_page = _self.options ? _self.options.no_page ? _self.options.no_page : false : false;
75692 var _total_rows = 0; 75693 var _callback = null; 75694 var _default_order = 'genes.acronym$asc' + (this.options.sort_suffix ? ',' + this.options.sort_suffix : ',data_sets.id'); 75695 var _last_query = {}; 75696 75697 75698 function inspect(prefix, obj) { 75699 var s = ""; 75700 for (var key in obj) { 75701 if ((typeof obj[key]) == "function") 75702 s = s; 75703 else if ((typeof obj[key]) == "object" && obj[key] !== null & key != "element") 75704 s += "\n" + prefix + "key: " + key + inspect(prefix + " ", obj[key]); 75705 else 75706 s += "\n" + prefix + "key: " + key + ", value: " + obj[key]; 75707 } 75708 return(s); 75709 } 75710 75711 75712 function do_gene_classification_search(term, options, source) { 75713 75714 term = $j.trim(term); 75715 if (!term || term.length == 0) 75716 throw "Invalid search term; must include at least one character."; 75717 75718 if (_self.on_search_begin) 75719 _self.on_search_begin(_self); 75720 75721 _search_term = term; 75722 if (options) { 75723 if (options.limit) { 75724 _page_size = options.limit ? options.limit : _page_size; 75725 _page_num = options.offset ? options.offset / _page_size : _page_num; 75726 } else if (options.page_num) { 75727 _page_num = options.page_num; 75728 } 75729 } 75730 75731 var opts = { 75732 order: _default_order, 75733 product_name: _self.options.product_names 75734 }; 75735 $j.extend(opts, options); 75736 75737 opts.limit = opts.limit ? opts.limit : _page_size; 75738 opts.offset = opts.offset ? opts.offset : _page_num * _page_size; 75739 75740 _search_options = opts; 75741 75742 _callback = null; 75743 switch (source) { 75744 case 'search': 75745 _callback = _self.on_search_complete; 75746 break; 75747 case 'sort': 75748 _callback = _self.on_sort_complete; 75749 break; 75750 case 'page': 75751 _callback = _self.on_page_complete; 75752 break; 75753 default: 75754 throw 'Unknown search source'; 75755 } 75756 75757 // Note: _callback is invoked from inside show_search_results 75758 opts.success_callback = show_search_results; 75759 opts.error_callback = erroror; 75760 75761 return(_self.rma_dao.experiment_search_by_gene_classification(_search_term, opts)); 75762 } 75763 75764 function show_search_results(response_data, text_status, xhdr) { 75765 75766 // !!!! You may need to do this later 75767// massage_data(response_data); 75768 _the_data = response_data; 75769 _total_rows = parseInt(_the_data.response.rows_available); 75770 75771 if (_callback) 75772 _callback(_self); 75773 75774 // let the pager know about it 75775 _self.onPagingInfoChanged.notify(_self.getPagingInfo()); 75776 } 75777 75778 /** 75779 * Parse out the age info from data_sets.specimen.donor.age, and glue it to the data_sets object using the age name as 75780 * the hash key. Example: data_sets['E11.5']. Also put the data_set_id into the age hash. 75781 * This function directly modifies the original response_data. 75782 * 75783 * @param response_data 75784 */ 75785// function massage_data(response_data) { 75786// var len = response_data.length; 75787// var ds_array, age; 75788// for (var i = 0; i < len; ++i) { 75789// ds_array = response_data[i].data_sets; 75790// for (var j = 0; j < ds_array.length; ++j) { 75791// age = ds_array[j].specimen.donor.age; 75792// ds_array[age.name] = { 75793// id: age.id, 75794// name: age.name, 75795// data_set_id: ds_array[j].id 75796// }; 75797// } 75798// } 75799// } 75800 75801 function erroror(xhdr, text_status, error) { 75802 var err = xhdr.msg == undefined ? error : xhdr; 75803 if (_self.on_search_failure) 75804 _self.on_search_failure(_self, err); 75805 } 75806 75807} 75808 75809 75810 75811; 75812"use strict"; 75813/** 75814 * This ISH Gene Search model uses numerous common functions defined in IshCommon.js 75815 * It follows model interface defined by SlickGrid 75816 * 75817 * Instantiation example: 75818 * 75819 * model = new ish_gene_search_model(daos, model_options); 75820 * model.IshCommon = new IshCommon; 75821 * 75822 * @return {*} 75823 * @constructor 75824 */ 75825// lifted from slick.model.js 75826function EventHelper() { 75827 this.handlers = []; 75828 75829 this.subscribe = function(fn) { 75830 this.handlers.push(fn); 75831 }; 75832 75833 this.notify = function(args) { 75834 for (var i = 0; i < this.handlers.length; i++) { 75835 this.handlers[i].call(this, args); 75836 } 75837 }; 75838 75839 return this; 75840} 75841 75842 75843/** 75844 * 75845 * @param daos 75846 * @param options 75847 */ 75848function ish_gene_search_model(daos, options) { 75849 75850//public properties 75851 this.rma_dao = daos.rma_dao; 75852 this.options = options; 75853 75854// public methods 75855 75856 this.reset_page_num = function() { 75857 _page_num = 0; 75858 this.onPagingInfoChanged.notify(this.getPagingInfo()); 75859 }; 75860 75861// these two methods are used by the search_controllers 75862 /** 75863 * 75864 * @param term url arg, 'search_term' 75865 * @param options all the url args 75866 * @returns {*} 75867 */ 75868 this.search = function(term, options) { 75869 return(do_gene_search(term, options, 'search')); 75870 }; 75871 75872 this.sort = function(sort_column, sort_asc) { 75873 _search_options.order = _self.IshCommon.single_column_sort_order(sort_column, sort_asc, options.sort_suffix
75873); 75874 do_gene_search(_search_term, _search_options, 'sort'); 75875 }; 75876 75877 /** 75878 * Multicolumn sort 75879 * @param sort_data array of hashes containing keys name, value, asc. 75880 */ 75881 this.mcsort = function (sort_data) { 75882 _search_options.order = _self.IshCommon.mcsort_order(sort_data); 75883 do_gene_search(_search_term, _search_options, 'sort'); 75884 }; 75885 75886 /** 75887 * Used to get search results data to turn into XML 75888 * 75889 * @returns {Object} hash of {experiments:[search results]} 75890 */ 75891 this.get_data = function () { 75892 return({experiments:_the_data}); 75893 }; 75894 75895// end those two methods that are used by the search_controllers. 75896 75897 75898// these two support the implied interface for the slick.grid 75899 this.getItem = function(row_index) { 75900 return(_self.IshCommon.do_get_item(row_index, _the_data, _self.options.column_config)); 75901 }; 75902 75903 this.getLength = function() { 75904 return(_page_size); 75905 }; 75906// end slick.grid functions 75907 75908 75909// these functions & events support the implied interface required by slick.paging 75910 this.onPagingInfoChanged = new EventHelper(); 75911 75912 this.setPagingOptions = function(args) { 75913 var info = _self.IshCommon.calc_paging_info(args, _page_num, _page_size, _total_rows); 75914 _no_page = info.no_page; 75915 if (info.no_change) { 75916 return; 75917 } 75918 else { 75919 _page_num = info.page_num; 75920 _page_size = info.page_size; 75921 } 75922 75923 this.onPagingInfoChanged.notify(this.getPagingInfo()); 75924 75925 _search_options.limit = _page_size; 75926 _search_options.offset = _page_num * _page_size; 75927 75928 do_gene_search(_search_term, _search_options, 'page'); 75929 }; 75930 75931 this.getPagingInfo = function() { 75932 return {pageSize:_page_size, 75933 pageNum:_page_num, 75934 totalRows:_total_rows, 75935 showAll:_no_page}; 75936 }; 75937// end slick.paging support 75938 75939 75940// event hooks 75941 this.on_search_begin = this.options ? this.options.on_search_begin ? this.options.on_search_begin : null : null; 75942 this.on_search_complete = this.options ? this.options.on_search_complete ? this.options.on_search_complete : null : null; 75943 this.on_sort_complete = this.options ? this.options.on_sort_complete ? this.options.on_sort_complete : null : null; 75944 this.on_page_complete = this.options ? this.options.on_page_complete ? this.options.on_page_complete : null : null; 75945 this.on_search_failure = this.options ? this.options.on_search_failure ? this.options.on_search_failure : null : null; 75946 75947 75948// private properties 75949 var _self = this; 75950 var _search_term = ''; 75951 var _search_options = {}; 75952 var _the_data = null; 75953 var _page_size = _self.options ? _self.options.page_size ? _self.options.page_size : 25 : 25; 75954 var _page_num = _self.options ? _self.options.page_num ? _self.options.page_num : 0 : 0; 75955 var _no_page = _self.options ? _self.options.no_page ? _self.options.no_page : false : false; 75956 var _total_rows = 0; 75957 var _callback = null; 75958 var _default_order = 'genes.acronym$asc' + (this.options.sort_suffix ? ',' + this.options.sort_suffix : ',data_sets.id'); 75959 var _last_query = {}; 75960 75961 75962 function inspect(prefix, obj) { 75963 var s = ""; 75964 s = _self.IshCommon.inspect(prefix, obj); 75965 75966 return(s); 75967 } 75968 75969 function do_gene_search(term, options, source) { 75970 75971 term = $j.trim(term); 75972 if(!term || term.length == 0) 75973 throw "Invalid search term; must include at least one character."; 75974 75975 if(_self.on_search_begin) 75976 _self.on_search_begin(_self); 75977 _search_term = term; 75978 if(options) { 75979 if(options.limit) { 75980 _page_size = options.limit ? options.limit : _page_size; 75981 _page_num = options.offset ? options.offset / _page_size : _page_num; 75982 } else if(options.page_num) { 75983 _page_num = options.page_num; 75984 } 75985 } 75986 75987 var opts = { 75988 order:_default_order, 75989 product_name:_self.options.product_names 75990 }; 75991 $j.extend(opts, options); 75992 75993 opts.limit = opts.limit ? opts.limit : _page_size; 75994 opts.offset = opts.offset ? opts.offset : _page_num*_page_size; 75995 75996 _search_options = opts; 75997 75998 _callback = null; 75999 switch(source) { 76000 case 'search': 76001 _callback = _self.on_search_complete; 76002 break;
76003 case 'sort': 76004 _callback = _self.on_sort_complete; 76005 break; 76006 case 'page': 76007 _callback = _self.on_page_complete; 76008 break; 76009 default: 76010 throw 'Unknown search source'; 76011 } 76012 76013 // Note: _callback is invoked from inside show_search_results 76014 opts.success_callback = show_search_results; 76015 opts.error_callback = erroror; 76016 76017 return(_self.rma_dao.experiment_search_by_gene(_search_term, opts)); 76018 } 76019 76020 function show_search_results(response_data, text_status, xhdr) { 76021 76022// massage_data(response_data); 76023 _the_data = response_data; 76024 _total_rows = parseInt(_the_data.response.rows_available); 76025 76026 if(_callback) 76027 _callback(_self); 76028 76029 // let the pager know about it 76030 _self.onPagingInfoChanged.notify(_self.getPagingInfo()); 76031 } 76032 76033 /** 76034 * Parse out the age info from data_sets.specimen.donor.age, and glue it to the data_sets object using the age name as 76035 * the hash key. Example: data_sets['E11.5']. Also put the data_set_id into the age hash. 76036 * This function directly modifies the original response_data. 76037 * 76038 * @param response_data 76039 */ 76040 function massage_data(response_data) { 76041 var len = response_data.length; 76042 var ds_array, age; 76043 for (var i = 0; i < len; ++i) { 76044 ds_array = response_data[i].data_sets; 76045 for (var j = 0; j < ds_array.length; ++j) { 76046 age = ds_array[j].specimen.donor.age; 76047 ds_array[age.name] = { 76048 id: age.id, 76049 embryonic: age.embryonic, 76050 name: age.name, 76051 data_set_id: ds_array[j].id 76052 }; 76053 } 76054 } 76055 } 76056 76057 function erroror(xhdr, text_status, error) { 76058 var err = xhdr.msg == undefined ? error : xhdr; 76059 if(_self.on_search_failure) 76060 _self.on_search_failure(_self, err); 76061 } 76062 76063} 76064 76065 76066 76067; 76068"use strict"; 76069/** 76070 * This Manual Annotation Search model uses numerous common functions defined in IshCommon.js 76071 * It follows model interface defined by SlickGrid 76072 * 76073 * It is different from the other ISH search modalities in that it requires two RMA database retrieval. It makes this 76074 * sequence of calls: 76075 * do_full_search 76076 * get_ids // first determine which unique genes are in common between all tuples of structure+age+intensity+density 76077 * // that the user specified 76078 * dao.query('ManualAnnotation') // returns gene id, acronymn, name 76079 * receive_ids 76080 * fetch_metadata // re-run original query, but this time restricting search criteria to the common gene ids from original query. 76081 * dao.query('Gene') // returns experiment, gene, structure, age, donor, intensity, density, related data 76082 * 76083 * It also differs in that it retains a local copy of the entire search result set. Column sorting is done locally 76084 * on this search result set, rather that running a new RMA query with additional sort order parameters. 76085 * 76086 */ 76087// lifted from slick.model.js 76088function EventHelper() { 76089 this.handlers = []; 76090 76091 this.subscribe = function (fn) { 76092 this.handlers.push(fn); 76093 }; 76094 76095 this.notify = function (args) { 76096 for (var i = 0; i < this.handlers.length; i++) { 76097 this.handlers[i].call(this, args); 76098 } 76099 }; 76100 76101 return this; 76102} 76103 76104 76105/** 76106 * 76107 * @param daos 76108 * @param options 76109 */ 76110function manual_annotation_search_model(daos, options) { 76111//public properties 76112 this.rma_dao = daos.rma_dao; 76113 this.options = options; 76114 76115// public methods 76116 76117 this.reset_page_num = function () { 76118 _page_num = 0; 76119 this.onPagingInfoChanged.notify(this.getPagingInfo()); 76120 }; 76121 76122// these two methods are used by the search controller or search view 76123 /** 76124 * Called by controller 76125 * 76126 * @param term url arg, 'search_term' 76127 * @param options all the url args 76128 * @returns {*} 76129 */ 76130 this.search = function (term, options) { 76131 return(do_search(term, options, 'search')); 76132 }; 76133 76134 /** 76135 * Called by view via SlickGrid when user clicks on column header of a sortable column 76136 * 76137 * @param sort_column 76138 * @param {Boolean} sort_asc ascending sort if true, else descending sort 76139 */ 76140 this.sort = function (sort_column, sort_asc) {
76141 var field = sort_column.field; 76142 var compare_type = compare_strings; 76143 76144 // edge case for Age sorting, we don't want to sort on descriptive 'age_name', but on value for 'age_days' 76145 if (field == 'age_name' && sort_column.metadata_days) { 76146 field = sort_column.metadata_days; 76147 compare_type = compare_ints; 76148 } 76149 76150 do_sort_one(field, sort_asc, compare_type); 76151 76152 // !!! is this needed??? 76153 // let the pager know about it 76154// _self.onPagingInfoChanged.notify(_self.getPagingInfo()); 76155 76156 if(_self.on_page_complete) 76157 _self.on_page_complete(_self); 76158 }; 76159 76160 /** 76161 * Multicolumn sort 76162 * Note the edge case logic for handling the 'Age' column 76163 * 76164 * Called when user presses "Sort" button 76165 * Called by search view to be consistent with location of sort function 76166 * 76167 * @param {Array} sort_data array of hashes containing key-value pairs for name, value, asc. Corresponds to pulldown menu choices. 76168 */ 76169 this.mcsort = function (sort_data) { 76170 // sort_data[] is ordered 0 to n, reflecting left-to-right menu order in UI. Sort order precedence is left-to-right. 76171 76172 // these values are metadata column value names, like 'gene_symbol', 'structure_name' 76173 var sort1_colname = sort_data[0] ? sort_data[0].value : undefined; 76174 var sort2_colname = sort_data[1] ? sort_data[1].value : undefined; 76175 var sort3_colname = sort_data[2] ? sort_data[2].value : undefined; 76176 76177 var sort1, 76178 sort2, 76179 sort3, 76180 sort_state, 76181 compare_type1, 76182 compare_type2, 76183 compare_type3; 76184 76185 var len = sort_data.length; 76186 var sort_asc = sort_data[0].asc; 76187 if (len == 1) { 76188 compare_type1 = sort1_colname == 'age_days' ? compare_ints : compare_strings; 76189 _the_data.metadata.rows.sort(function (a, b) { 76190 if (sort_asc) 76191 return( compare_type1(a[sort1_colname], b[sort1_colname]) ); 76192 else 76193 return( compare_type1(b[sort1_colname], a[sort1_colname]) ); 76194 }); 76195 } 76196 else if (len == 2) {
76197 // 'asc' is either boolean true or false. The 'sort_state' is decimal representation of the base 2 'asc' 76198 // value of the menu items, where we assume true = 1 and false = 0 76199 // Example: menu Experiment (asc), menu Gene Name (desc), equals 10 (base 2), or equals 2 (base 10) 76200 sort1 = sort_data[0].asc * 2; 76201 sort2 = sort_data[1].asc * 1; 76202 sort_state = sort1 + sort2; 76203 76204 compare_type1 = sort1_colname == 'age_days' ? compare_ints : compare_strings; 76205 compare_type2 = sort2_colname == 'age_days' ? compare_ints : compare_strings; 76206 76207 _the_data.metadata.rows.sort(function (a, b) { 76208 switch(sort_state) { 76209 case 0: // 0, 0 (desc, desc) 76210 return( compare_type1(b[sort1_colname], a[sort1_colname]) || 76211 compare_type2(b[sort2_colname], a[sort2_colname]) ); 76212 break; 76213 case 1: // 0, 1 (desc, asc) 76214 return( compare_type1(b[sort1_colname], a[sort1_colname]) || 76215 compare_type2(a[sort2_colname], b[sort2_colname]) ); 76216 break; 76217 case 2: // 1, 0 (asc, desc) 76218 return( compare_type1(a[sort1_colname], b[sort1_colname]) || 76219 compare_type2(b[sort2_colname], a[sort2_colname]) ); 76220 break; 76221 case 3: // 1, 1 (asc, asc) 76222 return( compare_type1(a[sort1_colname], b[sort1_colname]) || 76223 compare_type2(a[sort2_colname], b[sort2_colname]) ); 76224 break; 76225 default: 76226 break; 76227 } 76228 }); 76229 } 76230 else if (len == 3) {
76231 // 'asc' is either boolean true or false. The 'sort_state' is decimal representation of the base 2 'asc' 76232 // value of the menu items, where we assume true = 1 and false = 0 76233 // Example: menu Experiment (asc), menu Gene Name (desc), equals 10 (base 2), or equals 2 (base 10) 76234 sort1 = sort_data[0].asc * 4; 76235 sort2 = sort_data[1].asc * 2; 76236 sort3 = sort_data[2].asc * 1; 76237 sort_state = sort1 + sort2 + sort3; 76238 76239 compare_type1 = sort1_colname == 'age_days' ? compare_ints : compare_strings; 76240 compare_type2 = sort2_colname == 'age_days' ? compare_ints : compare_strings; 76241 compare_type3 = sort3_colname == 'age_days' ? compare_ints : compare_strings; 76242 76243 _the_data.metadata.rows.sort(function (a, b) { 76244 switch(sort_state) { 76245 case 0: // 0, 0, 0 (desc, desc, desc) 76246 return( compare_type1(b[sort1_colname], a[sort1_colname]) || 76247 compare_type2(b[sort2_colname], a[sort2_colname]) || 76248 compare_type3(b[sort3_colname], a[sort3_colname]) ); 76249 break; 76250 case 1: // 0, 0, 1 (desc, desc, asc) 76251 return( compare_type1(b[sort1_colname], a[sort1_colname]) || 76252 compare_type2(b[sort2_colname], a[sort2_colname]) || 76253 compare_type3(a[sort3_colname], b[sort3_colname]) ); 76254 break; 76255 case 2: // 0, 1, 0 (desc, asc, desc) 76256 return( compare_type1(b[sort1_colname], a[sort1_colname]) || 76257 compare_type2(a[sort2_colname], b[sort2_colname]) || 76258 compare_type3(b[sort3_colname], a[sort3_colname]) ); 76259 break; 76260 case 3: // 0, 1, 1 (desc, asc, asc) 76261 return( compare_type1(b[sort1_colname], a[sort1_colname]) || 76262 compare_type2(a[sort2_colname], b[sort2_colname]) || 76263 compare_type3(a[sort3_colname], b[sort3_colname]) ); 76264 break; 76265 case 4: // 1, 0, 0 (asc, desc, desc) 76266 return( compare_type1(a[sort1_colname], b[sort1_colname]) || 76267 compare_type2(b[sort2_colname], a[sort2_colname]) || 76268 compare_type3(b[sort3_colname], a[sort3_colname]) ); 76269 break; 76270 case 5: // 1, 0, 1 (asc, desc, asc) 76271 return( compare_type1(a[sort1_colname], b[sort1_colname]) || 76272 compare_type2(b[sort2_colname], a[sort2_colname]) || 76273 compare_type3(a[sort3_colname], b[sort3_colname]) ); 76274 break; 76275 case 6: // 1, 1, 0 (asc, asc, desc) 76276 return( compare_type1(a[sort1_colname], b[sort1_colname]) || 76277 compare_type2(a[sort2_colname], b[sort2_colname]) || 76278 compare_type3(b[sort3_colname], a[sort3_colname]) ); 76279 break; 76280 case 7: // 1, 1, 1 (asc, asc, asc) 76281 return( compare_type1(a[sort1_colname], b[sort1_colname]) || 76282 compare_type2(a[sort2_colname], b[sort2_colname]) || 76283 compare_type3(a[sort3_colname], b[sort3_colname]) ); 76284 break; 76285 default: 76286 break; 76287 } 76288 }); 76289 } 76290 76291 if(_self.on_page_complete) 76292 _self.on_page_complete(_self); 76293 }; 76294 76295 /** 76296 * Used to get search results data to turn into XML 76297 * 76298 * @returns {Object} hash of {experiments:[search results]} 76299 */ 76300 this.get_data = function () { 76301 return({experiments:_row_search_metadata.results}); 76302 }; 76303 76304// end those two methods that are used by the search controller, view. 76305 76306 76307// these two support the implied interface for the slick.grid 76308 this.getItem = function (row_index) { 76309 return(do_get_item(row_index)); 76310 }; 76311 76312 this.getLength = function () { 76313 return(_page_size); 76314 }; 76315// end slick.grid functions 76316 76317 76318// these functions & events support the implied interface required by slick.paging 76319 this.onPagingInfoChanged = new EventHelper(); 76320 76321 this.setPagingOptions = function (args) { 76322 var info = _self.IshCommon.calc_paging_info(args, _page_num, _page_size, _total_rows); 76323 _no_page = info.no_page; 76324 if (info.no_change) { 76325 return; 76326 } 76327 else { 76328 _page_num = info.page_num; 76329 _page_size = info.page_size; 76330 } 76331 76332 this.onPagingInfoChanged.notify(this.getPagingInfo()); 76333 76334 _search_options.limit = _page_size; 76335 _search_options.offset = _page_num * _page_size; 76336 76337 do_search(_search_term, _search_options, 'page'); 76338 }; 76339 76340 this.getPagingInfo = function () { 76341 return {pageSize: _page_size, 76342 pageNum: _page_num, 76343 totalRows:_total_rows, 76344 showAll: _no_page}; 76345 }; 76346// end slick.paging support 76347 76348 76349// event hooks 76350 this.on_search_begin = this.options ? this.options.on_search_begin ? this.options.on_search_begin : null : null; 76351 this.on_search_complete = this.options ? this.options.on_search_complete ? this.options.on_search_complete : null : null; 76352 this.on_sort_complete = this.options ? this.options.on_sort_complete ? this.options.on_sort_complete : null : null; 76353 this.on_page_complete = this.options ? this.options.on_page_complete ? this.options.on_page_complete : null : null; 76354 this.on_search_failure = this.options ? this.options.on_search_failure ? this.options.on_search_failure : null : null; 76355 76356 76357// private properties 76358 var _self = this; 76359 var _query_timeout = 120000; // msc, same as 2 minutes. Note: each row query takes about 40 seconds 76360 var _search_term = ''; 76361 var _search_options = {}; 76362 var _default_order = 'acronym$asc' + (this.options.sort_suffix ? ',' + this.options.sort_suffix : ''); 76363 var _query_set = null; 76364 var _page_size = _self.options ? _self.options.page_size ? _self.options.page_size : 25 : 25; 76365 var _page_num = _self.options ? _self.options.page_num ? _self.options.page_num : 0 : 0; 76366 var _no_page = _self.options ? _self.options.no_page ? _self.options.no_page : false : false;
76367 var _total_rows = 0; 76368 var _callback = null; 76369 var _the_dao = null; 76370 var _the_data = new_data_set(); 76371 var _last_query = ""; // used for xml link 76372 var _row_search_metadata = create_row_search_metadata_container(); 76373 76374 /** 76375 * Metadata container used when conducting an RMA search for a given row tuple (structure, age, intensity, density) 76376 * results - accumulated query results for each row search 76377 * query_set - search params for each row. After a given row search is completed, the used element is deleted. 76378 * gene_list - list of common gene ids present for each row 76379 * final_callback - function called after the last row query has completed 76380 * 76381 * @returns {Object} empty row_search_metadata object 76382 */ 76383 function create_row_search_metadata_container() { 76384 return({ 76385 results: [], 76386 query_set: [], 76387 gene_list: [], 76388 final_callback: null 76389 }); 76390 } 76391 76392 /** 76393 * Simulated structure of RMA response data 76394 * 76395 * @returns {Object} empty response data structure 76396 */ 76397 function new_data_set() { 76398 return({ 76399 id_list: [], 76400 id_count: 0, 76401 metadata: { 76402 start_row: 0, 76403 num_rows: 0, 76404 rows: {} 76405 } 76406 }); 76407 } 76408 76409 /** 76410 * Get row metadata 76411 * 76412 * @param row_index 76413 * @returns {Object} hash of row metadata, or empty hash for invalid row 76414 */ 76415 function do_get_item(row_index) { 76416 var item = {}; 76417 76418 var data_index = (_page_num * _page_size) + row_index; 76419 if (data_index < _total_rows) { 76420 // prefill column data to empty string. Is this necessary? 76421 var len = _self.options.column_config.length; 76422 for (var i = 0; i < len; i++) { 76423 var column = _self.options.column_config[i]; 76424 if (column.field && column.field.length) 76425 item[column.field] = ''; 76426 } 76427 76428 item = _the_data.metadata.rows[data_index]; 76429 } 76430 76431 return(item); 76432 } 76433 76434 /** 76435 * Entry point for initial search, paging, and column sorting 76436 * 76437 * @param term 76438 * @param options 76439 * @param source 76440 */ 76441 function do_search(term, options, source) { 76442 if (_self.on_search_begin) 76443 _self.on_search_begin(_self); 76444 76445 switch (source) { 76446 case 'search': 76447 do_full_search(term, options); 76448 break; 76449 case 'sort': 76450 do_sort(term, options); 76451 break; 76452 case 'page': 76453 do_page(term, options); 76454 break; 76455 default: 76456 throw 'Unknown search source'; 76457 } 76458 } 76459 76460 /** 76461 * Two phase search 76462 * 1) get common gene ids 76463 * 2) get gene data 76464 * @param term 76465 * @param options 76466 */ 76467 function do_full_search(term, options) { 76468 _search_term = term; 76469 if (options) { 76470 if (options.limit) { 76471 _page_size = options.limit ? options.limit : _page_size; 76472 _page_num = options.offset ? options.offset / _page_size : _page_num; 76473 } else if (options.page_num) { 76474 _page_num = options.page_num; 76475 } 76476 } 76477 76478 var opts = { 76479 order: _default_order 76480 }; 76481 $j.extend(opts, options); 76482 _search_options = opts; 76483 76484 opts.success_callback = receive_ids; 76485 opts.error_callback = erroror; 76486 76487 _callback = _self.on_search_complete; 76488 _query_set = decompose_query(_search_term); 76489 76490 get_ids(_query_set, opts); 76491 } 76492 76493 76494 /** 76495 * Sort one column 76496 * @param {String} sort_column column name 76497 * @param {Boolean} sort_asc ascending sort if true, else descending sort 76498 * @param {Object}compare_type function reference to sorting algorithm. Can be (default) compare_strings or compare_ints. 76499 */ 76500 function do_sort_one(sort_column, sort_asc, compare_type) { 76501 if (!compare_type) 76502 compare_type = compare_strings; 76503 76504 var field_name = sort_column;//order_field_for_field_name(sort_column); 76505 _the_data.metadata.rows = _the_data.metadata.rows.sort(function(a, b) { 76506 if(sort_asc) 76507 return( compare_type(a[field_name], b[field_name]) ); 76508 else 76509 return( compare_type(b[field_name], a[field_name]) ); 76510 }); 76511 } 76512 76513 function compare_strings(a, b) { 76514 return( a > b ? 1 : b > a ? -1 : 0); 76515 } 76516 76517 function compare_ints(a, b) { 76518 var ai = parseInt(a); 76519 var bi = parseInt(b); 76520 return( ai > bi ? 1 : bi > ai ? -1 : 0); 76521 } 76522 76523 function do_page(term, options) { 76524 // all of the data is present in this model, so return immediately. 76525 if(_self.on_page_complete) 76526 _self.on_page_complete(_self); 76527 } 76528 76529 function receive_ids(response_data) { 76530 // the response data has a key 'response' which is meaningless in this context. 76531 delete response_data['response']; 76532 76533 // empty skeleton to be populated 76534 _the_data = new_data_set(); 76535 76536 _the_data.id_list = response_data; 76537 _the_data.id_count = _the_data.id_list.length; 76538 76539 // now get a page of metadata 76540 fetch_metadata(_query_set, receive_metadata); 76541 } 76542 76543 function receive_metadata(response_data) { 76544 _the_data.metadata.start_row = _page_num * _page_size; 76545 _the_data.metadata.rows = response_data; 76546 _the_data.metadata.num_rows = response_data.length; 76547 _total_rows = response_data.length; 76548 76549 if (_callback) 76550 _callback(_self); 76551 76552 // let the pager know about it 76553 _self.onPagingInfoChanged.notify(_self.getPagingInfo()); 76554 } 76555 76556 76557 76558 function erroror(xhdr, text_status, error) { 76559 var err = xhdr.msg == undefined ? error : inspect('', xhdr); 76560 76561 _the_data = new_data_set(); 76562 _the_data.id_list = []; 76563 _the_data.id_count = 0; 76564 _total_rows = _the_data.id_count; 76565 76566 if (_self.on_search_failure) 76567 _self.on_search_failure(_self, err); 76568 } 76569 76570 /** 76571 * Decompose encoded search terms from url args. 76572 * Typical term looks like: 76573 * s0*10038+a0*12 mo,48 mo+i0*0,1,2,3,4,5+d0*0,1,2,3,4,5 76574 * where structure for query row 0 has id = 10038, 76575 * where age for query row 0 has ages = 12 mo and 48 mo 76576 * where intensity for query row 0 has magnitudes = 0, 1, 2, 3, 4, 5 76577 * where density for query row 0 has magnitudes = 0, 1, 2, 3, 4, 5 76578 * 76579 * @param term 76580 * @returns {Array} each array element corresponds to a single 'query row'. The array element is an object: 76581 * {structure[], age[], intensity[], density[]} 76582 */ 76583 function decompose_query(term) { 76584 var q = new manual_annotation_query_set(term); 76585 return(q.query_set); 76586 } 76587 76588 76589 /** 76590 * Construct RMA query to find genes common amongst each user-defined row tuple (struct/age/intensity/density) 76591 * 76592 * At each stage of the query (that is, for each row in the query_set) an RMA query is made for 76593 * matching Gene IDs. The return set is then piped to the next stage in the query, until the last 76594 * stage, when the gene IDs are returned as the query result. 76595 * 76596 * @param {Array} query_set each element is a hash of search params for each search row tuple 76597 * @return {String} RMA request 76598 */ 76599 function construct_id_request(query_set) { 76600 var row_count = query_set.length; 76601 var query_string = ""; 76602 76603 // for each query row in the query_set 76604 for (var i = 0; i < row_count; i++) { 76605 var row = query_set[i]; 76606 76607 // for the first row we don't need a Model declaration. 76608 if (i > 0) 76609 query_string += ",model::ManualAnnotation,rma::criteria,"; 76610 76611 query_string += "structure[id$in" + row.structure.join(',') + "]"; 76612 76613 var intensity_string = row.intensity.map(function (e) { 76614 return("'" + e + "'"); 76615 }); 76616 query_string += ",[intensity_call$in" + intensity_string.join(',') + "]"; 76617 76618 var density_string = row.density.map(function (e) { 76619 return("'" + e + "'"); 76620 }); 76621 query_string += ",[density_call$in" + density_string.join(',') + "]"; 76622
76623 var age_string = row.age.map(function (e) { 76624 return("'" + e + "'"); 76625 }); 76626 var gene_restriction = (i > 0) ? "[id$in$var" + (i - 1) + "]" : ""; 76627 query_string += ",section_data_set(products[id$eq18],genes" + gene_restriction + ",specimen(donor(age[name$in" + age_string.join(',') + "])))"; 76628 76629 query_string += ",rma::options[num_rows$eq'all']"; 76630 query_string += ",rma::options[count$eqfalse]"; 76631 76632 // unless this is the last query row, we're piping the gene IDs to the next stage in the query. 76633 if (i < row_count - 1) { 76634 query_string += ",rma::options[tabular$eq'distinct genes.id']"; 76635 query_string += ",pipe::list[var" + i + "$eq'*/id']"; 76636 } 76637 else { 76638 query_string += ",rma::options[tabular$eq'distinct genes.id,genes.acronym,genes.name']"; 76639 } 76640 } 76641 76642 return(query_string); 76643 } 76644 76645 /** 76646 * Create RMA query for a single row tuple, restricted to the genes common to all of the row tuples 76647 * 76648 * @param {Object} single_query_set hash of search params for each search row tuple 76649 * @param {Array} gene_id_list gene ids common to each search row tuple 76650 * @returns {string} RMA query 76651 */ 76652 function construct_row_tuple_metadata_request(single_query_set, gene_id_list) { 76653 var query_string = ""; 76654 76655 var row = single_query_set; 76656 76657 query_string += "model::ManualAnnotation,rma::criteria,"; 76658 query_string += "structure[id$in" + row.structure.join(',') + "]"; 76659 76660 var intensity_string = row.intensity.map(function (e) { 76661 return("'" + e + "'"); 76662 }); 76663 query_string += ",[intensity_call$in" + intensity_string.join(',') + "]"; 76664 76665 var density_string = row.density.map(function (e) { 76666 return("'" + e + "'"); 76667 }); 76668 query_string += ",[density_call$in" + density_string.join(',') + "]"; 76669
76670 var age_string = row.age.map(function (e) { 76671 return("'" + e + "'"); 76672 }); 76673 query_string += ",section_data_set(products[id$eq18],genes[id$in" + (gene_id_list.join(',')) + "]"; 76674 query_string += ",specimen(donor(age[name$in" + age_string.join(',') + "])))"; 76675 76676 query_string += ",rma::options[num_rows$eq'all']"; 76677 query_string += ",rma::options[count$eqfalse]"; 76678 76679 // final tabular output 76680 query_string += ",rma::options[tabular$eq' distinct genes.id as gene_id,genes.acronym as gene_symbol,genes.name as gene_name,"; 76681 query_string += "data_sets.id as data_set_id,intensity_call,density_call,structures.id as structure_id,structures.graph_order,"; 76682 query_string += "structures.acronym as structure_acronym,structures.name as structure_name,structures.color_hex_triplet,"; 76683 query_string += "ages.name as age_name,ages.days as age_days,ages.embryonic,donors.id as donor_id']"; 76684 76685 return(query_string); 76686 } 76687 76688 /** 76689 * Conducts a query for common gene ids 76690 * 76691 * @param query_set 76692 * @param options 76693 * @returns {*} 76694 */ 76695 function get_ids(query_set, options) { 76696 var new_q = construct_id_request(query_set); 76697 76698 options.criteria = new_q; 76699 options.format = 'json'; 76700 options.verb = 'POST'; 76701 76702 // we want to fetch all the IDs, ordered as we specified. The ordering 76703 // of the IDs will be the order in which the data is displayed. 76704 options.limit = 'all'; 76705 76706 return(_the_dao.query('ManualAnnotation', options)); 76707 } 76708 76709 function fetch_metadata(query_set, callback) { 76710 var gene_set = _the_data.id_list;//.slice(row_start, (row_start + _page_size)); 76711 var gene_list = gene_set.map(function (gene) { 76712 return(gene.id); 76713 }); 76714 76715 if (!gene_list || !gene_list.length) { 76716 erroror({}, null, "there are no genes matching this criteria"); 76717 return; 76718 } 76719 76720 76721 var query_set_copy = []; 76722 $j.extend(true, query_set_copy, query_set); 76723 76724 _row_search_metadata = create_row_search_metadata_container(); 76725 _row_search_metadata.query_set = query_set_copy; 76726 _row_search_metadata.final_callback = callback; 76727 _row_search_metadata.gene_list = gene_list; 76728 76729 row_tuple_search(null); 76730 76731 // !!!! old code, only one call, logic not what we want 76732// var new_q = construct_metadata_request(query_set, gene_list); 76733// 76734// var options = {}; 76735// options.criteria = new_q; 76736// options.format = 'json'; 76737// options.verb = 'GET'; 76738// options.success_callback = callback; 76739// options.error_callback = erroror; 76740// 76741// // Because we're limiting the metadata by restricting to just the 76742// // exact set of IDs we're passing in, we are working around the RMA 76743// // peculiarity of summing inner and outer rows for its row_count. 76744// options.limit = 'all'; 76745// 76746// _last_query = {}; 76747// $j.extend(true, _last_query, options); 76748// 76749// return(_the_dao.query('Gene', options)); 76750 } 76751 76752 /** 76753 * Gene search on a single tuple row. 76754 * 76755 * This function will be called once for each user-defined tuple row (structure(s), age(s), intensity(s), density(s)), 76756 * accumulating the intermediate search within _row_search_metadata.results. 76757 * 76758 * _row_search_metadata is altered by this function: 76759 * results - concat new row tuple search results to this array 76760 * query_set - after row tuple search, delete array element representing the old query params 76761 * 76762 * @param {Object} response_data search results from previous call to row_tuple_search. Initial call to this 76763 * function sets response_data to null. 76764 */ 76765 function row_tuple_search(response_data) { 76766 var options = {}; 76767 76768 if (response_data != null) { 76769 // accumulate results from previous query, remove already-used query_set 76770 $j.merge(_row_search_metadata.results, response_data); 76771 _row_search_metadata.query_set.splice(0, 1); 76772 } 76773 76774 if (_row_search_metadata.query_set.length == 0) { 76775 // finished 76776 _row_search_metadata.final_callback(_row_search_metadata.results); 76777 } 76778 else { 76779 var query = construct_row_tuple_metadata_request(_row_search_metadata.query_set[0], _row_search_metadata.gene_list); 76780 options.criteria = query; 76781 options.format = 'json'; 76782 options.verb = 'GET'; 76783 options.success_callback = row_tuple_search; // recursive 76784 options.error_callback = erroror; 76785 76786 // Because we're limiting the metadata by restricting to just the
76787 // exact set of IDs we're passing in, we are working around the RMA 76788 // peculiarity of summing inner and outer rows for its row_count. 76789 options.limit = 'all'; 76790 76791 var options_copy = {}; 76792 $j.extend(true, options_copy, options); 76793 76794 _the_dao.query('ManualAnnotation', options_copy); 76795 } 76796 } 76797 76798 function init() { 76799 if (!AIBS.DATA.API_DAO) 76800 throw "The image model requires AIBS.DATA.API_DAO, which is not present."; 76801 76802 _the_dao = new AIBS.DATA.API_DAO(_self.rma_dao.service_url, {timeout:_query_timeout}); 76803 } 76804 76805 init(); 76806 76807} 76808; 76809 76810function microarray_gene_classification_heatmap_model(daos, options) 76811{ 76812 this._rma_dao = daos.data; 76813 this.options = options; 76814 76815 76816 this.fetch_probe_ids_by_gene_classification = function(gene_search_term, options) 76817 { 76818 var term = $j.trim(gene_search_term); 76819 if(!term || term.length == 0) 76820 throw "Invalid search term; must include at least one character."; 76821 76822 var dao_options = { 76823 success_callback: this.options.success_callback, 76824 error_callback: this.options.error_callback 76825 }; 76826 76827 this._rma_dao.probe_id_by_gene_classification( term, dao_options); 76828 } 76829 76830 76831 this.search = function( term, options ) { 76832 return this.fetch_probe_ids_by_gene_classification(term, options); 76833 } 76834 76835 var _self = this; 76836}; 76837 76838function microarray_heatmap_model(daos, options) 76839{ 76840 this._rma_dao = daos.data; 76841 this.options = options; 76842 76843 76844 this.fetch_probe_ids = function(gene_search_term, options) 76845 { 76846 var term = $j.trim(gene_search_term); 76847 if(!term || term.length == 0) 76848 throw "Invalid search term; must include at least one character."; 76849 76850 var dao_options = { 76851 success_callback: this.options.success_callback, 76852 error_callback: this.options.error_callback 76853 }; 76854 76855 $j.extend( dao_options, options); 76856 this._rma_dao.affymetrix_probe_id_by_gene_term( term, dao_options); 76857 } 76858 76859 this.search = function( term, options ) { 76860 return this.fetch_probe_ids( term, options); 76861 } 76862 76863 var _self = this; 76864}; 76865// 76866// This model only invokes the callback method to create a heatmap control object 76867// This is used by the correlation query as well as the differential query 76868// 76869function microarray_heatmap_pass_through_model(daos, options) 76870{ 76871 this._rma_dao = daos.data; 76872 this.options = options; 76873 76874 this.search = function( term, options ) { 76875 // The parameter "term" should be null, and should be ignored 76876 return this.options.prepare_heatmap_method( term, options); 76877 } 76878 76879 var _self = this; 76880}; 76881 76882function microarray_browse_controller(browse_container, search_container, page, options) { 76883 76884 this.container = $j(browse_container); 76885 this.search_container = $j(search_container); 76886 this.page = page; 76887 this.settings = {}; 76888 $j.extend(true, this.settings, options); 76889 76890 76891 var _self = this; 76892 var _the_app = _self.page.application; 76893 var _the_page = _self.page; 76894 var _the_daos = _the_app.DAOs(); 76895 var _page_config = _the_page.get_config(); 76896 var _component_configs = _page_config.components; 76897 var _urls = _the_app.settings.urls; 76898 76899 var _search_widget = null; 76900 var _landing_widget = null; 76901 76902 var _donor_list = null; 76903 var _stage_list = null; 76904 76905 function build_search_widget() { 76906 76907 var search_options = { 76908 on_search:load_search_page, 76909 on_search_type_change:do_search_type_change, 76910 on_ontology_error:show_ontology_error, 76911 differential:{ 76912 help_url:_urls.micro_differential_help 76913 }, 76914 stage_list:_stage_list 76915 }; 76916 _search_widget = new ish_search_widget(_self.search_container, _the_daos.data, _component_configs.search_widget.search_plugins, search_options); 76917 } 76918 76919 function do_search_type_change(sender, new_type) { 76920 76921 if(_landing_widget) 76922 _landing_widget.set_landing_mode(new_type); 76923 } 76924 76925 function encode_each_term(term, delimiter) { 76926 var terms = term.split(delimiter); 76927 76928 if(terms.length == 1) 76929 return(trim(term)); 76930 76931 var term_string = ''; 76932 for(var i = 0; i < terms.length; i++) 76933 term_string += trim(terms[i]) + '$'; 76934 76935 return(term_string); 76936 } 76937 76938 function encode_term(term) { 76939 return(encode_each_term(encode_each_term(term, '\n'), '\t')); 76940 } 76941 76942 function encode_object(obj) { 76943 var val = '' 76944 for(var key in obj) { 76945 if(val.length) 76946 val += '&'; 76947 val += key + '=' + new String(obj[key]); 76948 } 76949 return(val); 76950 } 76951 76952 function trim(s) 76953 { 76954 var l = 0; 76955 var r = s.length - 1; 76956
76957 while(l < s.length && s[l] == ' ') l++; 76958 while(r > l && s[r] == ' ') r -= 1; 76959 76960 return s.substring(l, r+1); 76961 } 76962 76963// function load_search_page(state) { 76964// 76965// state.page_num = 0; 76966// state.page_size = 22; 76967// state.no_paging = false; 76968// 76969// var state_string = "?"; 76970// var count = 0; 76971// var val = ""; 76972// 76973// for(var key in state) { 76974// if(count++) 76975// state_string += "&"; 76976// 76977// val = encode_term(new String(state[key])); 76978// state_string += key + "=" + val; 76979// } 76980// 76981// //var url = _urls.search + state_string; 76982// var url = ""; 76983// switch(state.search_type) { 76984// case 'search': 76985// url = _urls.search; 76986// break; 76987// case 'sleep_study': 76988// url = _urls.sleep_search; 76989// break; 76990// case 'mouse_diversity_study': 76991// url = _urls.diversity_search; 76992// break; 76993// default: 76994// url = _urls.search; 76995// } 76996// url += state_string; 76997// window.location.href = url; 76998// } 76999 77000 function load_search_page(state) { 77001 77002 state.page_num = 0; 77003 state.page_size = 22; 77004 state.no_paging = false; 77005 77006 var state_string = "?"; 77007 var count = 0; 77008 var val = ""; 77009 77010 for(var key in state) { 77011 77012 if(count++) 77013 state_string += "&"; 77014 77015 if( (typeof state[key]) == "object" && ( state[key] instanceof Array ) && state[key] !== null ){ 77016 state_string += encode_array(state[key], key); 77017 } 77018 else if((typeof state[key]) == "object" && state[key] !== null) { 77019 state_string += encode_object(state[key]); 77020 } else { 77021 val = encode_term(new String(state[key])); 77022 state_string += key + "=" + val; 77023 } 77024 } 77025 77026 var url = ""; 77027 switch(state.search_type) { 77028 case 'search': 77029 url = _urls.search; 77030 break; 77031 case 'sleep_study': 77032 url = _urls.sleep_search; 77033 break; 77034 case 'mouse_diversity_study': 77035 url = _urls.diversity_search; 77036 break; 77037 default: 77038 url = _urls.search; 77039 } 77040 url += state_string; 77041 window.location.href = url; 77042 } 77043 77044 function encode_array(arr,attr_key) { 77045 77046 var target_str = ''; 77047 for(var i=0; i < arr.length; i++) { 77048 if(i>0) 77049 target_str += ','; 77050 else 77051 target_str += attr_key + '='; 77052 target_str += arr[i]; 77053 } 77054 return target_str; 77055 } 77056 77057 function show_ontology_error(err) { 77058 77059 } 77060 77061 function build_landing_widget() { 77062 77063 _landing_widget = new landing_widget(_self.container, _the_daos, _urls, _component_configs.landing_widget ); 77064 } 77065 77066 function init() { 77067 77068 build_search_widget(); 77069 build_landing_widget(); 77070 } 77071 77072 77073 function prepare_donor_data_before_initialization() { 77074 var dao_options = { 77075 success_callback: received_donor_list, 77076 error_callback: failure_receiving_donor_info 77077 }; 77078 77079 _the_daos.data.donor_info_by_product_ids( '', dao_options); // the app page's default product id will be used 77080 } 77081 77082 function received_donor_list(response_data, text_status, xhdr) { 77083 77084 var donor_array = new Array(); 77085 var donor_index = 0; 77086 _stage_list = new Array(); 77087 77088 for ( var i = 0; i < response_data.length; i++ ) { 77089 var donor = response_data[i]; 77090 donor_array[donor_index++] = {"donor":{"id":donor.id,"name":donor.name}} 77091 if(i==0 || response_data[i].age.id != response_data[i-1].age.id) { 77092 _stage_list.push(donor.age); 77093 } 77094 } 77095 _donor_list = donor_array; 77096 77097 init(); 77098 } 77099 77100 function received_no_donor_list() 77101 { 77102 alert("Failed to fetch a list of donors."); 77103 } 77104 77105 function failure_receiving_donor_info(response) { 77106 //var cont = $j('#search_page_content'); 77107 var cont = $j('#landing_container'); 77108 var err_msg = 'Your search could not be completed'; 77109 77110 if(response) { 77111 if(response.msg) 77112 err_msg += ' ('+ response.msg +')'; 77113 else { 77114 if (typeof response === 'string') { 77115 err_msg += ' ('+ response.msg +')'; 77116 } 77117 else { 77118 err_msg += ' (the data service may be unresponsive)'; 77119 } 77120 } 77121 } 77122 else { 77123 err_msg += '.'; 77124 } 77125 77126 err_msg += '<br/><br/>Please try later.'; 77127 77128 container_error(cont, err_msg); 77129 } 77130 77131 function container_error(container, msg) { 77132 // it would be nice to have one central 77133 // place for handling UI error reporting... 77134 // if(_error_manager) { 77135 // _error_manager.ui_report(_self, container, msg); 77136 // return; 77137 // } 77138 77139 var err_html = "<div class='err_notice ui-state-error'>" + msg + "</div>"; 77140 container.html(err_html); 77141 } 77142 77143 prepare_donor_data_before_initialization(); 77144} 77145; 77146// This is a manifest file that'll be compiled into including all the files listed below. 77147// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically 77148// be included in the compiled file accessible from http://example.com/assets/application.js 77149// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 77150// the compiled file. 77151// 77152 77153 77154 77155// -- jquery-migrate needed by ui-spinner.js v1.20 77156 77157 77158 77159 77160// -- SlickGrid requires presence of these 3rd party jquery drag/drop libraries, must load before SlickGrid 77161 77162 77163 77164 77165 77166 77167 77168 77169 77170 77171 77172 77173 77174 77175 77176 77177 77178 77179 77180 77181 77182 77183 77184 77185 77186 77187 77188 77189 77190 77191 77192 77193 77194 77195 77196 77197 77198 77199 77200 77201;
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.