/lib/infusion/MyInfusion.js

https://bitbucket.org/cindyli/decapod-0.7-ui-iteration3 · JavaScript · 25522 lines · 18988 code · 3115 blank · 3419 comment · 3893 complexity · a7d596364914612aa7a0c2b292b87087 MD5 · raw file

  1. /*!
  2. * jQuery JavaScript Library v1.7.2
  3. * http://jquery.com/
  4. *
  5. * Copyright 2011, John Resig
  6. * Dual licensed under the MIT or GPL Version 2 licenses.
  7. * http://jquery.org/license
  8. *
  9. * Includes Sizzle.js
  10. * http://sizzlejs.com/
  11. * Copyright 2011, The Dojo Foundation
  12. * Released under the MIT, BSD, and GPL Licenses.
  13. *
  14. * Date: Wed Mar 21 12:46:34 2012 -0700
  15. */
  16. (function( window, undefined ) {
  17. // Use the correct document accordingly with window argument (sandbox)
  18. var document = window.document,
  19. navigator = window.navigator,
  20. location = window.location;
  21. var jQuery = (function() {
  22. // Define a local copy of jQuery
  23. var jQuery = function( selector, context ) {
  24. // The jQuery object is actually just the init constructor 'enhanced'
  25. return new jQuery.fn.init( selector, context, rootjQuery );
  26. },
  27. // Map over jQuery in case of overwrite
  28. _jQuery = window.jQuery,
  29. // Map over the $ in case of overwrite
  30. _$ = window.$,
  31. // A central reference to the root jQuery(document)
  32. rootjQuery,
  33. // A simple way to check for HTML strings or ID strings
  34. // Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
  35. quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,
  36. // Check if a string has a non-whitespace character in it
  37. rnotwhite = /\S/,
  38. // Used for trimming whitespace
  39. trimLeft = /^\s+/,
  40. trimRight = /\s+$/,
  41. // Match a standalone tag
  42. rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/,
  43. // JSON RegExp
  44. rvalidchars = /^[\],:{}\s]*$/,
  45. rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
  46. rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
  47. rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
  48. // Useragent RegExp
  49. rwebkit = /(webkit)[ \/]([\w.]+)/,
  50. ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/,
  51. rmsie = /(msie) ([\w.]+)/,
  52. rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/,
  53. // Matches dashed string for camelizing
  54. rdashAlpha = /-([a-z]|[0-9])/ig,
  55. rmsPrefix = /^-ms-/,
  56. // Used by jQuery.camelCase as callback to replace()
  57. fcamelCase = function( all, letter ) {
  58. return ( letter + "" ).toUpperCase();
  59. },
  60. // Keep a UserAgent string for use with jQuery.browser
  61. userAgent = navigator.userAgent,
  62. // For matching the engine and version of the browser
  63. browserMatch,
  64. // The deferred used on DOM ready
  65. readyList,
  66. // The ready event handler
  67. DOMContentLoaded,
  68. // Save a reference to some core methods
  69. toString = Object.prototype.toString,
  70. hasOwn = Object.prototype.hasOwnProperty,
  71. push = Array.prototype.push,
  72. slice = Array.prototype.slice,
  73. trim = String.prototype.trim,
  74. indexOf = Array.prototype.indexOf,
  75. // [[Class]] -> type pairs
  76. class2type = {};
  77. jQuery.fn = jQuery.prototype = {
  78. constructor: jQuery,
  79. init: function( selector, context, rootjQuery ) {
  80. var match, elem, ret, doc;
  81. // Handle $(""), $(null), or $(undefined)
  82. if ( !selector ) {
  83. return this;
  84. }
  85. // Handle $(DOMElement)
  86. if ( selector.nodeType ) {
  87. this.context = this[0] = selector;
  88. this.length = 1;
  89. return this;
  90. }
  91. // The body element only exists once, optimize finding it
  92. if ( selector === "body" && !context && document.body ) {
  93. this.context = document;
  94. this[0] = document.body;
  95. this.selector = selector;
  96. this.length = 1;
  97. return this;
  98. }
  99. // Handle HTML strings
  100. if ( typeof selector === "string" ) {
  101. // Are we dealing with HTML string or an ID?
  102. if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
  103. // Assume that strings that start and end with <> are HTML and skip the regex check
  104. match = [ null, selector, null ];
  105. } else {
  106. match = quickExpr.exec( selector );
  107. }
  108. // Verify a match, and that no context was specified for #id
  109. if ( match && (match[1] || !context) ) {
  110. // HANDLE: $(html) -> $(array)
  111. if ( match[1] ) {
  112. context = context instanceof jQuery ? context[0] : context;
  113. doc = ( context ? context.ownerDocument || context : document );
  114. // If a single string is passed in and it's a single tag
  115. // just do a createElement and skip the rest
  116. ret = rsingleTag.exec( selector );
  117. if ( ret ) {
  118. if ( jQuery.isPlainObject( context ) ) {
  119. selector = [ document.createElement( ret[1] ) ];
  120. jQuery.fn.attr.call( selector, context, true );
  121. } else {
  122. selector = [ doc.createElement( ret[1] ) ];
  123. }
  124. } else {
  125. ret = jQuery.buildFragment( [ match[1] ], [ doc ] );
  126. selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes;
  127. }
  128. return jQuery.merge( this, selector );
  129. // HANDLE: $("#id")
  130. } else {
  131. elem = document.getElementById( match[2] );
  132. // Check parentNode to catch when Blackberry 4.6 returns
  133. // nodes that are no longer in the document #6963
  134. if ( elem && elem.parentNode ) {
  135. // Handle the case where IE and Opera return items
  136. // by name instead of ID
  137. if ( elem.id !== match[2] ) {
  138. return rootjQuery.find( selector );
  139. }
  140. // Otherwise, we inject the element directly into the jQuery object
  141. this.length = 1;
  142. this[0] = elem;
  143. }
  144. this.context = document;
  145. this.selector = selector;
  146. return this;
  147. }
  148. // HANDLE: $(expr, $(...))
  149. } else if ( !context || context.jquery ) {
  150. return ( context || rootjQuery ).find( selector );
  151. // HANDLE: $(expr, context)
  152. // (which is just equivalent to: $(context).find(expr)
  153. } else {
  154. return this.constructor( context ).find( selector );
  155. }
  156. // HANDLE: $(function)
  157. // Shortcut for document ready
  158. } else if ( jQuery.isFunction( selector ) ) {
  159. return rootjQuery.ready( selector );
  160. }
  161. if ( selector.selector !== undefined ) {
  162. this.selector = selector.selector;
  163. this.context = selector.context;
  164. }
  165. return jQuery.makeArray( selector, this );
  166. },
  167. // Start with an empty selector
  168. selector: "",
  169. // The current version of jQuery being used
  170. jquery: "1.7.2",
  171. // The default length of a jQuery object is 0
  172. length: 0,
  173. // The number of elements contained in the matched element set
  174. size: function() {
  175. return this.length;
  176. },
  177. toArray: function() {
  178. return slice.call( this, 0 );
  179. },
  180. // Get the Nth element in the matched element set OR
  181. // Get the whole matched element set as a clean array
  182. get: function( num ) {
  183. return num == null ?
  184. // Return a 'clean' array
  185. this.toArray() :
  186. // Return just the object
  187. ( num < 0 ? this[ this.length + num ] : this[ num ] );
  188. },
  189. // Take an array of elements and push it onto the stack
  190. // (returning the new matched element set)
  191. pushStack: function( elems, name, selector ) {
  192. // Build a new jQuery matched element set
  193. var ret = this.constructor();
  194. if ( jQuery.isArray( elems ) ) {
  195. push.apply( ret, elems );
  196. } else {
  197. jQuery.merge( ret, elems );
  198. }
  199. // Add the old object onto the stack (as a reference)
  200. ret.prevObject = this;
  201. ret.context = this.context;
  202. if ( name === "find" ) {
  203. ret.selector = this.selector + ( this.selector ? " " : "" ) + selector;
  204. } else if ( name ) {
  205. ret.selector = this.selector + "." + name + "(" + selector + ")";
  206. }
  207. // Return the newly-formed element set
  208. return ret;
  209. },
  210. // Execute a callback for every element in the matched set.
  211. // (You can seed the arguments with an array of args, but this is
  212. // only used internally.)
  213. each: function( callback, args ) {
  214. return jQuery.each( this, callback, args );
  215. },
  216. ready: function( fn ) {
  217. // Attach the listeners
  218. jQuery.bindReady();
  219. // Add the callback
  220. readyList.add( fn );
  221. return this;
  222. },
  223. eq: function( i ) {
  224. i = +i;
  225. return i === -1 ?
  226. this.slice( i ) :
  227. this.slice( i, i + 1 );
  228. },
  229. first: function() {
  230. return this.eq( 0 );
  231. },
  232. last: function() {
  233. return this.eq( -1 );
  234. },
  235. slice: function() {
  236. return this.pushStack( slice.apply( this, arguments ),
  237. "slice", slice.call(arguments).join(",") );
  238. },
  239. map: function( callback ) {
  240. return this.pushStack( jQuery.map(this, function( elem, i ) {
  241. return callback.call( elem, i, elem );
  242. }));
  243. },
  244. end: function() {
  245. return this.prevObject || this.constructor(null);
  246. },
  247. // For internal use only.
  248. // Behaves like an Array's method, not like a jQuery method.
  249. push: push,
  250. sort: [].sort,
  251. splice: [].splice
  252. };
  253. // Give the init function the jQuery prototype for later instantiation
  254. jQuery.fn.init.prototype = jQuery.fn;
  255. jQuery.extend = jQuery.fn.extend = function() {
  256. var options, name, src, copy, copyIsArray, clone,
  257. target = arguments[0] || {},
  258. i = 1,
  259. length = arguments.length,
  260. deep = false;
  261. // Handle a deep copy situation
  262. if ( typeof target === "boolean" ) {
  263. deep = target;
  264. target = arguments[1] || {};
  265. // skip the boolean and the target
  266. i = 2;
  267. }
  268. // Handle case when target is a string or something (possible in deep copy)
  269. if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
  270. target = {};
  271. }
  272. // extend jQuery itself if only one argument is passed
  273. if ( length === i ) {
  274. target = this;
  275. --i;
  276. }
  277. for ( ; i < length; i++ ) {
  278. // Only deal with non-null/undefined values
  279. if ( (options = arguments[ i ]) != null ) {
  280. // Extend the base object
  281. for ( name in options ) {
  282. src = target[ name ];
  283. copy = options[ name ];
  284. // Prevent never-ending loop
  285. if ( target === copy ) {
  286. continue;
  287. }
  288. // Recurse if we're merging plain objects or arrays
  289. if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
  290. if ( copyIsArray ) {
  291. copyIsArray = false;
  292. clone = src && jQuery.isArray(src) ? src : [];
  293. } else {
  294. clone = src && jQuery.isPlainObject(src) ? src : {};
  295. }
  296. // Never move original objects, clone them
  297. target[ name ] = jQuery.extend( deep, clone, copy );
  298. // Don't bring in undefined values
  299. } else if ( copy !== undefined ) {
  300. target[ name ] = copy;
  301. }
  302. }
  303. }
  304. }
  305. // Return the modified object
  306. return target;
  307. };
  308. jQuery.extend({
  309. noConflict: function( deep ) {
  310. if ( window.$ === jQuery ) {
  311. window.$ = _$;
  312. }
  313. if ( deep && window.jQuery === jQuery ) {
  314. window.jQuery = _jQuery;
  315. }
  316. return jQuery;
  317. },
  318. // Is the DOM ready to be used? Set to true once it occurs.
  319. isReady: false,
  320. // A counter to track how many items to wait for before
  321. // the ready event fires. See #6781
  322. readyWait: 1,
  323. // Hold (or release) the ready event
  324. holdReady: function( hold ) {
  325. if ( hold ) {
  326. jQuery.readyWait++;
  327. } else {
  328. jQuery.ready( true );
  329. }
  330. },
  331. // Handle when the DOM is ready
  332. ready: function( wait ) {
  333. // Either a released hold or an DOMready/load event and not yet ready
  334. if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) {
  335. // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
  336. if ( !document.body ) {
  337. return setTimeout( jQuery.ready, 1 );
  338. }
  339. // Remember that the DOM is ready
  340. jQuery.isReady = true;
  341. // If a normal DOM Ready event fired, decrement, and wait if need be
  342. if ( wait !== true && --jQuery.readyWait > 0 ) {
  343. return;
  344. }
  345. // If there are functions bound, to execute
  346. readyList.fireWith( document, [ jQuery ] );
  347. // Trigger any bound ready events
  348. if ( jQuery.fn.trigger ) {
  349. jQuery( document ).trigger( "ready" ).off( "ready" );
  350. }
  351. }
  352. },
  353. bindReady: function() {
  354. if ( readyList ) {
  355. return;
  356. }
  357. readyList = jQuery.Callbacks( "once memory" );
  358. // Catch cases where $(document).ready() is called after the
  359. // browser event has already occurred.
  360. if ( document.readyState === "complete" ) {
  361. // Handle it asynchronously to allow scripts the opportunity to delay ready
  362. return setTimeout( jQuery.ready, 1 );
  363. }
  364. // Mozilla, Opera and webkit nightlies currently support this event
  365. if ( document.addEventListener ) {
  366. // Use the handy event callback
  367. document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
  368. // A fallback to window.onload, that will always work
  369. window.addEventListener( "load", jQuery.ready, false );
  370. // If IE event model is used
  371. } else if ( document.attachEvent ) {
  372. // ensure firing before onload,
  373. // maybe late but safe also for iframes
  374. document.attachEvent( "onreadystatechange", DOMContentLoaded );
  375. // A fallback to window.onload, that will always work
  376. window.attachEvent( "onload", jQuery.ready );
  377. // If IE and not a frame
  378. // continually check to see if the document is ready
  379. var toplevel = false;
  380. try {
  381. toplevel = window.frameElement == null;
  382. } catch(e) {}
  383. if ( document.documentElement.doScroll && toplevel ) {
  384. doScrollCheck();
  385. }
  386. }
  387. },
  388. // See test/unit/core.js for details concerning isFunction.
  389. // Since version 1.3, DOM methods and functions like alert
  390. // aren't supported. They return false on IE (#2968).
  391. isFunction: function( obj ) {
  392. return jQuery.type(obj) === "function";
  393. },
  394. isArray: Array.isArray || function( obj ) {
  395. return jQuery.type(obj) === "array";
  396. },
  397. isWindow: function( obj ) {
  398. return obj != null && obj == obj.window;
  399. },
  400. isNumeric: function( obj ) {
  401. return !isNaN( parseFloat(obj) ) && isFinite( obj );
  402. },
  403. type: function( obj ) {
  404. return obj == null ?
  405. String( obj ) :
  406. class2type[ toString.call(obj) ] || "object";
  407. },
  408. isPlainObject: function( obj ) {
  409. // Must be an Object.
  410. // Because of IE, we also have to check the presence of the constructor property.
  411. // Make sure that DOM nodes and window objects don't pass through, as well
  412. if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
  413. return false;
  414. }
  415. try {
  416. // Not own constructor property must be Object
  417. if ( obj.constructor &&
  418. !hasOwn.call(obj, "constructor") &&
  419. !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
  420. return false;
  421. }
  422. } catch ( e ) {
  423. // IE8,9 Will throw exceptions on certain host objects #9897
  424. return false;
  425. }
  426. // Own properties are enumerated firstly, so to speed up,
  427. // if last one is own, then all properties are own.
  428. var key;
  429. for ( key in obj ) {}
  430. return key === undefined || hasOwn.call( obj, key );
  431. },
  432. isEmptyObject: function( obj ) {
  433. for ( var name in obj ) {
  434. return false;
  435. }
  436. return true;
  437. },
  438. error: function( msg ) {
  439. throw new Error( msg );
  440. },
  441. parseJSON: function( data ) {
  442. if ( typeof data !== "string" || !data ) {
  443. return null;
  444. }
  445. // Make sure leading/trailing whitespace is removed (IE can't handle it)
  446. data = jQuery.trim( data );
  447. // Attempt to parse using the native JSON parser first
  448. if ( window.JSON && window.JSON.parse ) {
  449. return window.JSON.parse( data );
  450. }
  451. // Make sure the incoming data is actual JSON
  452. // Logic borrowed from http://json.org/json2.js
  453. if ( rvalidchars.test( data.replace( rvalidescape, "@" )
  454. .replace( rvalidtokens, "]" )
  455. .replace( rvalidbraces, "")) ) {
  456. return ( new Function( "return " + data ) )();
  457. }
  458. jQuery.error( "Invalid JSON: " + data );
  459. },
  460. // Cross-browser xml parsing
  461. parseXML: function( data ) {
  462. if ( typeof data !== "string" || !data ) {
  463. return null;
  464. }
  465. var xml, tmp;
  466. try {
  467. if ( window.DOMParser ) { // Standard
  468. tmp = new DOMParser();
  469. xml = tmp.parseFromString( data , "text/xml" );
  470. } else { // IE
  471. xml = new ActiveXObject( "Microsoft.XMLDOM" );
  472. xml.async = "false";
  473. xml.loadXML( data );
  474. }
  475. } catch( e ) {
  476. xml = undefined;
  477. }
  478. if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) {
  479. jQuery.error( "Invalid XML: " + data );
  480. }
  481. return xml;
  482. },
  483. noop: function() {},
  484. // Evaluates a script in a global context
  485. // Workarounds based on findings by Jim Driscoll
  486. // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
  487. globalEval: function( data ) {
  488. if ( data && rnotwhite.test( data ) ) {
  489. // We use execScript on Internet Explorer
  490. // We use an anonymous function so that context is window
  491. // rather than jQuery in Firefox
  492. ( window.execScript || function( data ) {
  493. window[ "eval" ].call( window, data );
  494. } )( data );
  495. }
  496. },
  497. // Convert dashed to camelCase; used by the css and data modules
  498. // Microsoft forgot to hump their vendor prefix (#9572)
  499. camelCase: function( string ) {
  500. return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
  501. },
  502. nodeName: function( elem, name ) {
  503. return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
  504. },
  505. // args is for internal usage only
  506. each: function( object, callback, args ) {
  507. var name, i = 0,
  508. length = object.length,
  509. isObj = length === undefined || jQuery.isFunction( object );
  510. if ( args ) {
  511. if ( isObj ) {
  512. for ( name in object ) {
  513. if ( callback.apply( object[ name ], args ) === false ) {
  514. break;
  515. }
  516. }
  517. } else {
  518. for ( ; i < length; ) {
  519. if ( callback.apply( object[ i++ ], args ) === false ) {
  520. break;
  521. }
  522. }
  523. }
  524. // A special, fast, case for the most common use of each
  525. } else {
  526. if ( isObj ) {
  527. for ( name in object ) {
  528. if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
  529. break;
  530. }
  531. }
  532. } else {
  533. for ( ; i < length; ) {
  534. if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) {
  535. break;
  536. }
  537. }
  538. }
  539. }
  540. return object;
  541. },
  542. // Use native String.trim function wherever possible
  543. trim: trim ?
  544. function( text ) {
  545. return text == null ?
  546. "" :
  547. trim.call( text );
  548. } :
  549. // Otherwise use our own trimming functionality
  550. function( text ) {
  551. return text == null ?
  552. "" :
  553. text.toString().replace( trimLeft, "" ).replace( trimRight, "" );
  554. },
  555. // results is for internal usage only
  556. makeArray: function( array, results ) {
  557. var ret = results || [];
  558. if ( array != null ) {
  559. // The window, strings (and functions) also have 'length'
  560. // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930
  561. var type = jQuery.type( array );
  562. if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) {
  563. push.call( ret, array );
  564. } else {
  565. jQuery.merge( ret, array );
  566. }
  567. }
  568. return ret;
  569. },
  570. inArray: function( elem, array, i ) {
  571. var len;
  572. if ( array ) {
  573. if ( indexOf ) {
  574. return indexOf.call( array, elem, i );
  575. }
  576. len = array.length;
  577. i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
  578. for ( ; i < len; i++ ) {
  579. // Skip accessing in sparse arrays
  580. if ( i in array && array[ i ] === elem ) {
  581. return i;
  582. }
  583. }
  584. }
  585. return -1;
  586. },
  587. merge: function( first, second ) {
  588. var i = first.length,
  589. j = 0;
  590. if ( typeof second.length === "number" ) {
  591. for ( var l = second.length; j < l; j++ ) {
  592. first[ i++ ] = second[ j ];
  593. }
  594. } else {
  595. while ( second[j] !== undefined ) {
  596. first[ i++ ] = second[ j++ ];
  597. }
  598. }
  599. first.length = i;
  600. return first;
  601. },
  602. grep: function( elems, callback, inv ) {
  603. var ret = [], retVal;
  604. inv = !!inv;
  605. // Go through the array, only saving the items
  606. // that pass the validator function
  607. for ( var i = 0, length = elems.length; i < length; i++ ) {
  608. retVal = !!callback( elems[ i ], i );
  609. if ( inv !== retVal ) {
  610. ret.push( elems[ i ] );
  611. }
  612. }
  613. return ret;
  614. },
  615. // arg is for internal usage only
  616. map: function( elems, callback, arg ) {
  617. var value, key, ret = [],
  618. i = 0,
  619. length = elems.length,
  620. // jquery objects are treated as arrays
  621. isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ;
  622. // Go through the array, translating each of the items to their
  623. if ( isArray ) {
  624. for ( ; i < length; i++ ) {
  625. value = callback( elems[ i ], i, arg );
  626. if ( value != null ) {
  627. ret[ ret.length ] = value;
  628. }
  629. }
  630. // Go through every key on the object,
  631. } else {
  632. for ( key in elems ) {
  633. value = callback( elems[ key ], key, arg );
  634. if ( value != null ) {
  635. ret[ ret.length ] = value;
  636. }
  637. }
  638. }
  639. // Flatten any nested arrays
  640. return ret.concat.apply( [], ret );
  641. },
  642. // A global GUID counter for objects
  643. guid: 1,
  644. // Bind a function to a context, optionally partially applying any
  645. // arguments.
  646. proxy: function( fn, context ) {
  647. if ( typeof context === "string" ) {
  648. var tmp = fn[ context ];
  649. context = fn;
  650. fn = tmp;
  651. }
  652. // Quick check to determine if target is callable, in the spec
  653. // this throws a TypeError, but we will just return undefined.
  654. if ( !jQuery.isFunction( fn ) ) {
  655. return undefined;
  656. }
  657. // Simulated bind
  658. var args = slice.call( arguments, 2 ),
  659. proxy = function() {
  660. return fn.apply( context, args.concat( slice.call( arguments ) ) );
  661. };
  662. // Set the guid of unique handler to the same of original handler, so it can be removed
  663. proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
  664. return proxy;
  665. },
  666. // Mutifunctional method to get and set values to a collection
  667. // The value/s can optionally be executed if it's a function
  668. access: function( elems, fn, key, value, chainable, emptyGet, pass ) {
  669. var exec,
  670. bulk = key == null,
  671. i = 0,
  672. length = elems.length;
  673. // Sets many values
  674. if ( key && typeof key === "object" ) {
  675. for ( i in key ) {
  676. jQuery.access( elems, fn, i, key[i], 1, emptyGet, value );
  677. }
  678. chainable = 1;
  679. // Sets one value
  680. } else if ( value !== undefined ) {
  681. // Optionally, function values get executed if exec is true
  682. exec = pass === undefined && jQuery.isFunction( value );
  683. if ( bulk ) {
  684. // Bulk operations only iterate when executing function values
  685. if ( exec ) {
  686. exec = fn;
  687. fn = function( elem, key, value ) {
  688. return exec.call( jQuery( elem ), value );
  689. };
  690. // Otherwise they run against the entire set
  691. } else {
  692. fn.call( elems, value );
  693. fn = null;
  694. }
  695. }
  696. if ( fn ) {
  697. for (; i < length; i++ ) {
  698. fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
  699. }
  700. }
  701. chainable = 1;
  702. }
  703. return chainable ?
  704. elems :
  705. // Gets
  706. bulk ?
  707. fn.call( elems ) :
  708. length ? fn( elems[0], key ) : emptyGet;
  709. },
  710. now: function() {
  711. return ( new Date() ).getTime();
  712. },
  713. // Use of jQuery.browser is frowned upon.
  714. // More details: http://docs.jquery.com/Utilities/jQuery.browser
  715. uaMatch: function( ua ) {
  716. ua = ua.toLowerCase();
  717. var match = rwebkit.exec( ua ) ||
  718. ropera.exec( ua ) ||
  719. rmsie.exec( ua ) ||
  720. ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) ||
  721. [];
  722. return { browser: match[1] || "", version: match[2] || "0" };
  723. },
  724. sub: function() {
  725. function jQuerySub( selector, context ) {
  726. return new jQuerySub.fn.init( selector, context );
  727. }
  728. jQuery.extend( true, jQuerySub, this );
  729. jQuerySub.superclass = this;
  730. jQuerySub.fn = jQuerySub.prototype = this();
  731. jQuerySub.fn.constructor = jQuerySub;
  732. jQuerySub.sub = this.sub;
  733. jQuerySub.fn.init = function init( selector, context ) {
  734. if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) {
  735. context = jQuerySub( context );
  736. }
  737. return jQuery.fn.init.call( this, selector, context, rootjQuerySub );
  738. };
  739. jQuerySub.fn.init.prototype = jQuerySub.fn;
  740. var rootjQuerySub = jQuerySub(document);
  741. return jQuerySub;
  742. },
  743. browser: {}
  744. });
  745. // Populate the class2type map
  746. jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
  747. class2type[ "[object " + name + "]" ] = name.toLowerCase();
  748. });
  749. browserMatch = jQuery.uaMatch( userAgent );
  750. if ( browserMatch.browser ) {
  751. jQuery.browser[ browserMatch.browser ] = true;
  752. jQuery.browser.version = browserMatch.version;
  753. }
  754. // Deprecated, use jQuery.browser.webkit instead
  755. if ( jQuery.browser.webkit ) {
  756. jQuery.browser.safari = true;
  757. }
  758. // IE doesn't match non-breaking spaces with \s
  759. if ( rnotwhite.test( "\xA0" ) ) {
  760. trimLeft = /^[\s\xA0]+/;
  761. trimRight = /[\s\xA0]+$/;
  762. }
  763. // All jQuery objects should point back to these
  764. rootjQuery = jQuery(document);
  765. // Cleanup functions for the document ready method
  766. if ( document.addEventListener ) {
  767. DOMContentLoaded = function() {
  768. document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
  769. jQuery.ready();
  770. };
  771. } else if ( document.attachEvent ) {
  772. DOMContentLoaded = function() {
  773. // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
  774. if ( document.readyState === "complete" ) {
  775. document.detachEvent( "onreadystatechange", DOMContentLoaded );
  776. jQuery.ready();
  777. }
  778. };
  779. }
  780. // The DOM ready check for Internet Explorer
  781. function doScrollCheck() {
  782. if ( jQuery.isReady ) {
  783. return;
  784. }
  785. try {
  786. // If IE is used, use the trick by Diego Perini
  787. // http://javascript.nwbox.com/IEContentLoaded/
  788. document.documentElement.doScroll("left");
  789. } catch(e) {
  790. setTimeout( doScrollCheck, 1 );
  791. return;
  792. }
  793. // and execute any waiting functions
  794. jQuery.ready();
  795. }
  796. return jQuery;
  797. })();
  798. // String to Object flags format cache
  799. var flagsCache = {};
  800. // Convert String-formatted flags into Object-formatted ones and store in cache
  801. function createFlags( flags ) {
  802. var object = flagsCache[ flags ] = {},
  803. i, length;
  804. flags = flags.split( /\s+/ );
  805. for ( i = 0, length = flags.length; i < length; i++ ) {
  806. object[ flags[i] ] = true;
  807. }
  808. return object;
  809. }
  810. /*
  811. * Create a callback list using the following parameters:
  812. *
  813. * flags: an optional list of space-separated flags that will change how
  814. * the callback list behaves
  815. *
  816. * By default a callback list will act like an event callback list and can be
  817. * "fired" multiple times.
  818. *
  819. * Possible flags:
  820. *
  821. * once: will ensure the callback list can only be fired once (like a Deferred)
  822. *
  823. * memory: will keep track of previous values and will call any callback added
  824. * after the list has been fired right away with the latest "memorized"
  825. * values (like a Deferred)
  826. *
  827. * unique: will ensure a callback can only be added once (no duplicate in the list)
  828. *
  829. * stopOnFalse: interrupt callings when a callback returns false
  830. *
  831. */
  832. jQuery.Callbacks = function( flags ) {
  833. // Convert flags from String-formatted to Object-formatted
  834. // (we check in cache first)
  835. flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {};
  836. var // Actual callback list
  837. list = [],
  838. // Stack of fire calls for repeatable lists
  839. stack = [],
  840. // Last fire value (for non-forgettable lists)
  841. memory,
  842. // Flag to know if list was already fired
  843. fired,
  844. // Flag to know if list is currently firing
  845. firing,
  846. // First callback to fire (used internally by add and fireWith)
  847. firingStart,
  848. // End of the loop when firing
  849. firingLength,
  850. // Index of currently firing callback (modified by remove if needed)
  851. firingIndex,
  852. // Add one or several callbacks to the list
  853. add = function( args ) {
  854. var i,
  855. length,
  856. elem,
  857. type,
  858. actual;
  859. for ( i = 0, length = args.length; i < length; i++ ) {
  860. elem = args[ i ];
  861. type = jQuery.type( elem );
  862. if ( type === "array" ) {
  863. // Inspect recursively
  864. add( elem );
  865. } else if ( type === "function" ) {
  866. // Add if not in unique mode and callback is not in
  867. if ( !flags.unique || !self.has( elem ) ) {
  868. list.push( elem );
  869. }
  870. }
  871. }
  872. },
  873. // Fire callbacks
  874. fire = function( context, args ) {
  875. args = args || [];
  876. memory = !flags.memory || [ context, args ];
  877. fired = true;
  878. firing = true;
  879. firingIndex = firingStart || 0;
  880. firingStart = 0;
  881. firingLength = list.length;
  882. for ( ; list && firingIndex < firingLength; firingIndex++ ) {
  883. if ( list[ firingIndex ].apply( context, args ) === false && flags.stopOnFalse ) {
  884. memory = true; // Mark as halted
  885. break;
  886. }
  887. }
  888. firing = false;
  889. if ( list ) {
  890. if ( !flags.once ) {
  891. if ( stack && stack.length ) {
  892. memory = stack.shift();
  893. self.fireWith( memory[ 0 ], memory[ 1 ] );
  894. }
  895. } else if ( memory === true ) {
  896. self.disable();
  897. } else {
  898. list = [];
  899. }
  900. }
  901. },
  902. // Actual Callbacks object
  903. self = {
  904. // Add a callback or a collection of callbacks to the list
  905. add: function() {
  906. if ( list ) {
  907. var length = list.length;
  908. add( arguments );
  909. // Do we need to add the callbacks to the
  910. // current firing batch?
  911. if ( firing ) {
  912. firingLength = list.length;
  913. // With memory, if we're not firing then
  914. // we should call right away, unless previous
  915. // firing was halted (stopOnFalse)
  916. } else if ( memory && memory !== true ) {
  917. firingStart = length;
  918. fire( memory[ 0 ], memory[ 1 ] );
  919. }
  920. }
  921. return this;
  922. },
  923. // Remove a callback from the list
  924. remove: function() {
  925. if ( list ) {
  926. var args = arguments,
  927. argIndex = 0,
  928. argLength = args.length;
  929. for ( ; argIndex < argLength ; argIndex++ ) {
  930. for ( var i = 0; i < list.length; i++ ) {
  931. if ( args[ argIndex ] === list[ i ] ) {
  932. // Handle firingIndex and firingLength
  933. if ( firing ) {
  934. if ( i <= firingLength ) {
  935. firingLength--;
  936. if ( i <= firingIndex ) {
  937. firingIndex--;
  938. }
  939. }
  940. }
  941. // Remove the element
  942. list.splice( i--, 1 );
  943. // If we have some unicity property then
  944. // we only need to do this once
  945. if ( flags.unique ) {
  946. break;
  947. }
  948. }
  949. }
  950. }
  951. }
  952. return this;
  953. },
  954. // Control if a given callback is in the list
  955. has: function( fn ) {
  956. if ( list ) {
  957. var i = 0,
  958. length = list.length;
  959. for ( ; i < length; i++ ) {
  960. if ( fn === list[ i ] ) {
  961. return true;
  962. }
  963. }
  964. }
  965. return false;
  966. },
  967. // Remove all callbacks from the list
  968. empty: function() {
  969. list = [];
  970. return this;
  971. },
  972. // Have the list do nothing anymore
  973. disable: function() {
  974. list = stack = memory = undefined;
  975. return this;
  976. },
  977. // Is it disabled?
  978. disabled: function() {
  979. return !list;
  980. },
  981. // Lock the list in its current state
  982. lock: function() {
  983. stack = undefined;
  984. if ( !memory || memory === true ) {
  985. self.disable();
  986. }
  987. return this;
  988. },
  989. // Is it locked?
  990. locked: function() {
  991. return !stack;
  992. },
  993. // Call all callbacks with the given context and arguments
  994. fireWith: function( context, args ) {
  995. if ( stack ) {
  996. if ( firing ) {
  997. if ( !flags.once ) {
  998. stack.push( [ context, args ] );
  999. }
  1000. } else if ( !( flags.once && memory ) ) {
  1001. fire( context, args );
  1002. }
  1003. }
  1004. return this;
  1005. },
  1006. // Call all the callbacks with the given arguments
  1007. fire: function() {
  1008. self.fireWith( this, arguments );
  1009. return this;
  1010. },
  1011. // To know if the callbacks have already been called at least once
  1012. fired: function() {
  1013. return !!fired;
  1014. }
  1015. };
  1016. return self;
  1017. };
  1018. var // Static reference to slice
  1019. sliceDeferred = [].slice;
  1020. jQuery.extend({
  1021. Deferred: function( func ) {
  1022. var doneList = jQuery.Callbacks( "once memory" ),
  1023. failList = jQuery.Callbacks( "once memory" ),
  1024. progressList = jQuery.Callbacks( "memory" ),
  1025. state = "pending",
  1026. lists = {
  1027. resolve: doneList,
  1028. reject: failList,
  1029. notify: progressList
  1030. },
  1031. promise = {
  1032. done: doneList.add,
  1033. fail: failList.add,
  1034. progress: progressList.add,
  1035. state: function() {
  1036. return state;
  1037. },
  1038. // Deprecated
  1039. isResolved: doneList.fired,
  1040. isRejected: failList.fired,
  1041. then: function( doneCallbacks, failCallbacks, progressCallbacks ) {
  1042. deferred.done( doneCallbacks ).fail( failCallbacks ).progress( progressCallbacks );
  1043. return this;
  1044. },
  1045. always: function() {
  1046. deferred.done.apply( deferred, arguments ).fail.apply( deferred, arguments );
  1047. return this;
  1048. },
  1049. pipe: function( fnDone, fnFail, fnProgress ) {
  1050. return jQuery.Deferred(function( newDefer ) {
  1051. jQuery.each( {
  1052. done: [ fnDone, "resolve" ],
  1053. fail: [ fnFail, "reject" ],
  1054. progress: [ fnProgress, "notify" ]
  1055. }, function( handler, data ) {
  1056. var fn = data[ 0 ],
  1057. action = data[ 1 ],
  1058. returned;
  1059. if ( jQuery.isFunction( fn ) ) {
  1060. deferred[ handler ](function() {
  1061. returned = fn.apply( this, arguments );
  1062. if ( returned && jQuery.isFunction( returned.promise ) ) {
  1063. returned.promise().then( newDefer.resolve, newDefer.reject, newDefer.notify );
  1064. } else {
  1065. newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] );
  1066. }
  1067. });
  1068. } else {
  1069. deferred[ handler ]( newDefer[ action ] );
  1070. }
  1071. });
  1072. }).promise();
  1073. },
  1074. // Get a promise for this deferred
  1075. // If obj is provided, the promise aspect is added to the object
  1076. promise: function( obj ) {
  1077. if ( obj == null ) {
  1078. obj = promise;
  1079. } else {
  1080. for ( var key in promise ) {
  1081. obj[ key ] = promise[ key ];
  1082. }
  1083. }
  1084. return obj;
  1085. }
  1086. },
  1087. deferred = promise.promise({}),
  1088. key;
  1089. for ( key in lists ) {
  1090. deferred[ key ] = lists[ key ].fire;
  1091. deferred[ key + "With" ] = lists[ key ].fireWith;
  1092. }
  1093. // Handle state
  1094. deferred.done( function() {
  1095. state = "resolved";
  1096. }, failList.disable, progressList.lock ).fail( function() {
  1097. state = "rejected";
  1098. }, doneList.disable, progressList.lock );
  1099. // Call given func if any
  1100. if ( func ) {
  1101. func.call( deferred, deferred );
  1102. }
  1103. // All done!
  1104. return deferred;
  1105. },
  1106. // Deferred helper
  1107. when: function( firstParam ) {
  1108. var args = sliceDeferred.call( arguments, 0 ),
  1109. i = 0,
  1110. length = args.length,
  1111. pValues = new Array( length ),
  1112. count = length,
  1113. pCount = length,
  1114. deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ?
  1115. firstParam :
  1116. jQuery.Deferred(),
  1117. promise = deferred.promise();
  1118. function resolveFunc( i ) {
  1119. return function( value ) {
  1120. args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;
  1121. if ( !( --count ) ) {
  1122. deferred.resolveWith( deferred, args );
  1123. }
  1124. };
  1125. }
  1126. function progressFunc( i ) {
  1127. return function( value ) {
  1128. pValues[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;
  1129. deferred.notifyWith( promise, pValues );
  1130. };
  1131. }
  1132. if ( length > 1 ) {
  1133. for ( ; i < length; i++ ) {
  1134. if ( args[ i ] && args[ i ].promise && jQuery.isFunction( args[ i ].promise ) ) {
  1135. args[ i ].promise().then( resolveFunc(i), deferred.reject, progressFunc(i) );
  1136. } else {
  1137. --count;
  1138. }
  1139. }
  1140. if ( !count ) {
  1141. deferred.resolveWith( deferred, args );
  1142. }
  1143. } else if ( deferred !== firstParam ) {
  1144. deferred.resolveWith( deferred, length ? [ firstParam ] : [] );
  1145. }
  1146. return promise;
  1147. }
  1148. });
  1149. jQuery.support = (function() {
  1150. var support,
  1151. all,
  1152. a,
  1153. select,
  1154. opt,
  1155. input,
  1156. fragment,
  1157. tds,
  1158. events,
  1159. eventName,
  1160. i,
  1161. isSupported,
  1162. div = document.createElement( "div" ),
  1163. documentElement = document.documentElement;
  1164. // Preliminary tests
  1165. div.setAttribute("className", "t");
  1166. div.innerHTML = " <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
  1167. all = div.getElementsByTagName( "*" );
  1168. a = div.getElementsByTagName( "a" )[ 0 ];
  1169. // Can't get basic test support
  1170. if ( !all || !all.length || !a ) {
  1171. return {};
  1172. }
  1173. // First batch of supports tests
  1174. select = document.createElement( "select" );
  1175. opt = select.appendChild( document.createElement("option") );
  1176. input = div.getElementsByTagName( "input" )[ 0 ];
  1177. support = {
  1178. // IE strips leading whitespace when .innerHTML is used
  1179. leadingWhitespace: ( div.firstChild.nodeType === 3 ),
  1180. // Make sure that tbody elements aren't automatically inserted
  1181. // IE will insert them into empty tables
  1182. tbody: !div.getElementsByTagName("tbody").length,
  1183. // Make sure that link elements get serialized correctly by innerHTML
  1184. // This requires a wrapper element in IE
  1185. htmlSerialize: !!div.getElementsByTagName("link").length,
  1186. // Get the style information from getAttribute
  1187. // (IE uses .cssText instead)
  1188. style: /top/.test( a.getAttribute("style") ),
  1189. // Make sure that URLs aren't manipulated
  1190. // (IE normalizes it by default)
  1191. hrefNormalized: ( a.getAttribute("href") === "/a" ),
  1192. // Make sure that element opacity exists
  1193. // (IE uses filter instead)
  1194. // Use a regex to work around a WebKit issue. See #5145
  1195. opacity: /^0.55/.test( a.style.opacity ),
  1196. // Verify style float existence
  1197. // (IE uses styleFloat instead of cssFloat)
  1198. cssFloat: !!a.style.cssFloat,
  1199. // Make sure that if no value is specified for a checkbox
  1200. // that it defaults to "on".
  1201. // (WebKit defaults to "" instead)
  1202. checkOn: ( input.value === "on" ),
  1203. // Make sure that a selected-by-default option has a working selected property.
  1204. // (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
  1205. optSelected: opt.selected,
  1206. // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)
  1207. getSetAttribute: div.className !== "t",
  1208. // Tests for enctype support on a form(#6743)
  1209. enctype: !!document.createElement("form").enctype,
  1210. // Makes sure cloning an html5 element does not cause problems
  1211. // Where outerHTML is undefined, this still works
  1212. html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav></:nav>",
  1213. // Will be defined later
  1214. submitBubbles: true,
  1215. changeBubbles: true,
  1216. focusinBubbles: false,
  1217. deleteExpando: true,
  1218. noCloneEvent: true,
  1219. inlineBlockNeedsLayout: false,
  1220. shrinkWrapBlocks: false,
  1221. reliableMarginRight: true,
  1222. pixelMargin: true
  1223. };
  1224. // jQuery.boxModel DEPRECATED in 1.3, use jQuery.support.boxModel instead
  1225. jQuery.boxModel = support.boxModel = (document.compatMode === "CSS1Compat");
  1226. // Make sure checked status is properly cloned
  1227. input.checked = true;
  1228. support.noCloneChecked = input.cloneNode( true ).checked;
  1229. // Make sure that the options inside disabled selects aren't marked as disabled
  1230. // (WebKit marks them as disabled)
  1231. select.disabled = true;
  1232. support.optDisabled = !opt.disabled;
  1233. // Test to see if it's possible to delete an expando from an element
  1234. // Fails in Internet Explorer
  1235. try {
  1236. delete div.test;
  1237. } catch( e ) {
  1238. support.deleteExpando = false;
  1239. }
  1240. if ( !div.addEventListener && div.attachEvent && div.fireEvent ) {
  1241. div.attachEvent( "onclick", function() {
  1242. // Cloning a node shouldn't copy over any
  1243. // bound event handlers (IE does this)
  1244. support.noCloneEvent = false;
  1245. });
  1246. div.cloneNode( true ).fireEvent( "onclick" );
  1247. }
  1248. // Check if a radio maintains its value
  1249. // after being appended to the DOM
  1250. input = document.createElement("input");
  1251. input.value = "t";
  1252. input.setAttribute("type", "radio");
  1253. support.radioValue = input.value === "t";
  1254. input.setAttribute("checked", "checked");
  1255. // #11217 - WebKit loses check when the name is after the checked attribute
  1256. input.setAttribute( "name", "t" );
  1257. div.appendChild( input );
  1258. fragment = document.createDocumentFragment();
  1259. fragment.appendChild( div.lastChild );
  1260. // WebKit doesn't clone checked state correctly in fragments
  1261. support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
  1262. // Check if a disconnected checkbox will retain its checked
  1263. // value of true after appended to the DOM (IE6/7)
  1264. support.appendChecked = input.checked;
  1265. fragment.removeChild( input );
  1266. fragment.appendChild( div );
  1267. // Technique from Juriy Zaytsev
  1268. // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/
  1269. // We only care about the case where non-standard event systems
  1270. // are used, namely in IE. Short-circuiting here helps us to
  1271. // avoid an eval call (in setAttribute) which can cause CSP
  1272. // to go haywire. See: https://developer.mozilla.org/en/Security/CSP
  1273. if ( div.attachEvent ) {
  1274. for ( i in {
  1275. submit: 1,
  1276. change: 1,
  1277. focusin: 1
  1278. }) {
  1279. eventName = "on" + i;
  1280. isSupported = ( eventName in div );
  1281. if ( !isSupported ) {
  1282. div.setAttribute( eventName, "return;" );
  1283. isSupported = ( typeof div[ eventName ] === "function" );
  1284. }
  1285. support[ i + "Bubbles" ] = isSupported;
  1286. }
  1287. }
  1288. fragment.removeChild( div );
  1289. // Null elements to avoid leaks in IE
  1290. fragment = select = opt = div = input = null;
  1291. // Run tests that need a body at doc ready
  1292. jQuery(function() {
  1293. var container, outer, inner, table, td, offsetSupport,
  1294. marginDiv, conMarginTop, style, html, positionTopLeftWidthHeight,
  1295. paddingMarginBorderVisibility, paddingMarginBorder,
  1296. body = document.getElementsByTagName("body")[0];
  1297. if ( !body ) {
  1298. // Return for frameset docs that don't have a body
  1299. return;
  1300. }
  1301. conMarginTop = 1;
  1302. paddingMarginBorder = "padding:0;margin:0;border:";
  1303. positionTopLeftWidthHeight = "position:absolute;top:0;left:0;width:1px;height:1px;";
  1304. paddingMarginBorderVisibility = paddingMarginBorder + "0;visibility:hidden;";
  1305. style = "style='" + positionTopLeftWidthHeight + paddingMarginBorder + "5px solid #000;";
  1306. html = "<div " + style + "display:block;'><div style='" + paddingMarginBorder + "0;display:block;overflow:hidden;'></div></div>" +
  1307. "<table " + style + "' cellpadding='0' cellspacing='0'>" +
  1308. "<tr><td></td></tr></table>";
  1309. container = document.createElement("div");
  1310. container.style.cssText = paddingMarginBorderVisibility + "width:0;height:0;position:static;top:0;margin-top:" + conMarginTop + "px";
  1311. body.insertBefore( container, body.firstChild );
  1312. // Construct the test element
  1313. div = document.createElement("div");
  1314. container.appendChild( div );
  1315. // Check if table cells still have offsetWidth/Height when they are set
  1316. // to display:none and there are still other visible table cells in a
  1317. // table row; if so, offsetWidth/Height are not reliable for use when
  1318. // determining if an element has been hidden directly using
  1319. // display:none (it is still safe to use offsets if a parent element is
  1320. // hidden; don safety goggles and see bug #4512 for more information).
  1321. // (only IE 8 fails this test)
  1322. div.innerHTML = "<table><tr><td style='" + paddingMarginBorder + "0;display:none'></td><td>t</td></tr></table>";
  1323. tds = div.getElementsByTagName( "td" );
  1324. isSupported = ( tds[ 0 ].offsetHeight === 0 );
  1325. tds[ 0 ].style.display = "";
  1326. tds[ 1 ].style.display = "none";
  1327. // Check if empty table cells still have offsetWidth/Height
  1328. // (IE <= 8 fail this test)
  1329. support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
  1330. // Check if div with explicit width and no margin-right incorrectly
  1331. // gets computed margin-right based on width of container. For more
  1332. // info see bug #3333
  1333. // Fails in WebKit before Feb 2011 nightlies
  1334. // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
  1335. if ( window.getComputedStyle ) {
  1336. div.innerHTML = "";
  1337. marginDiv = document.createElement( "div" );
  1338. marginDiv.style.width = "0";
  1339. marginDiv.style.marginRight = "0";
  1340. div.style.width = "2px";
  1341. div.appendChild( marginDiv );
  1342. support.reliableMarginRight =
  1343. ( parseInt( ( window.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0;
  1344. }
  1345. if ( typeof div.style.zoom !== "undefined" ) {
  1346. // Check if natively block-level elements act like inline-block
  1347. // elements when setting their display to 'inline' and giving
  1348. // them layout
  1349. // (IE < 8 does this)
  1350. div.innerHTML = "";
  1351. div.style.width = div.style.padding = "1px";
  1352. div.style.border = 0;
  1353. div.style.overflow = "hidden";
  1354. div.style.display = "inline";
  1355. div.style.zoom = 1;
  1356. support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 );
  1357. // Check if elements with layout shrink-wrap their children
  1358. // (IE 6 does this)
  1359. div.style.display = "block";
  1360. div.style.overflow = "visible";
  1361. div.innerHTML = "<div style='width:5px;'></div>";
  1362. support.shrinkWrapBlocks = ( div.offsetWidth !== 3 );
  1363. }
  1364. div.style.cssText = positionTopLeftWidthHeight + paddingMarginBorderVisibility;
  1365. div.innerHTML = html;
  1366. outer = div.firstChild;
  1367. inner = outer.firstChild;
  1368. td = outer.nextSibling.firstChild.firstChild;
  1369. offsetSupport = {
  1370. doesNotAddBorder: ( inner.offsetTop !== 5 ),
  1371. doesAddBorderForTableAndCells: ( td.offsetTop === 5 )
  1372. };
  1373. inner.style.position = "fixed";
  1374. inner.style.top = "20px";
  1375. // safari subtracts parent border width here which is 5px
  1376. offsetSupport.fixedPosition = ( inner.offsetTop === 20 || inner.offsetTop === 15 );
  1377. inner.style.position = inner.style.top = "";
  1378. outer.style.overflow = "hidden";
  1379. outer.style.position = "relative";
  1380. offsetSupport.subtractsBorderForOverflowNotVisible = ( inner.offsetTop === -5 );
  1381. offsetSupport.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== conMarginTop );
  1382. if ( window.getComputedStyle ) {
  1383. div.style.marginTop = "1%";
  1384. support.pixelMargin = ( window.getComputedStyle( div, null ) || { marginTop: 0 } ).marginTop !== "1%";
  1385. }
  1386. if ( typeof container.style.zoom !== "undefined" ) {
  1387. container.style.zoom = 1;
  1388. }
  1389. body.removeChild( container );
  1390. marginDiv = div = container = null;
  1391. jQuery.extend( support, offsetSupport );
  1392. });
  1393. return support;
  1394. })();
  1395. var rbrace = /^(?:\{.*\}|\[.*\])$/,
  1396. rmultiDash = /([A-Z])/g;
  1397. jQuery.extend({
  1398. cache: {},
  1399. // Please use with caution
  1400. uuid: 0,
  1401. // Unique for each copy of jQuery on the page
  1402. // Non-digits removed to match rinlinejQuery
  1403. expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ),
  1404. // The following elements throw uncatchable exceptions if you
  1405. // attempt to add expando properties to them.
  1406. noData: {
  1407. "embed": true,
  1408. // Ban all objects except for Flash (which handle expandos)
  1409. "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
  1410. "applet": true
  1411. },
  1412. hasData: function( elem ) {
  1413. elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
  1414. return !!elem && !isEmptyDataObject( elem );
  1415. },
  1416. data: function( elem, name, data, pvt /* Internal Use Only */ ) {
  1417. if ( !jQuery.acceptData( elem ) ) {
  1418. return;
  1419. }
  1420. var privateCache, thisCache, ret,
  1421. internalKey = jQuery.expando,
  1422. getByName = typeof name === "string",
  1423. // We have to handle DOM nodes and JS objects differently because IE6-7
  1424. // can't GC object references properly across the DOM-JS boundary
  1425. isNode = elem.nodeType,
  1426. // Only DOM nodes need the global jQuery cache; JS object data is
  1427. // attached directly to the object so GC can occur automatically
  1428. cache = isNode ? jQuery.cache : elem,
  1429. // Only defining an ID for JS objects if its cache already exists allows
  1430. // the code to shortcut on the same path as a DOM node with no cache
  1431. id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey,
  1432. isEvents = name === "events";
  1433. // Avoid doing any more work than we need to when trying to get data on an
  1434. // object that has no data at all
  1435. if ( (!id || !cache[id] || (!isEvents && !pvt && !cache[id].data)) && getByName && data === undefined ) {
  1436. return;
  1437. }
  1438. if ( !id ) {
  1439. // Only DOM nodes need a new unique ID for each element since their data
  1440. // ends up in the global cache
  1441. if ( isNode ) {
  1442. elem[ internalKey ] = id = ++jQuery.uuid;
  1443. } else {
  1444. id = internalKey;
  1445. }
  1446. }
  1447. if ( !cache[ id ] ) {
  1448. cache[ id ] = {};
  1449. // Avoids exposing jQuery metadata on plain JS objects when the object
  1450. // is serialized using JSON.stringify
  1451. if ( !isNode ) {
  1452. cache[ id ].toJSON = jQuery.noop;
  1453. }
  1454. }
  1455. // An object can be passed to jQuery.data instead of a key/value pair; this gets
  1456. // shallow copied over onto the existing cache
  1457. if ( typeof name === "object" || typeof name === "function" ) {
  1458. if ( pvt ) {
  1459. cache[ id ] = jQuery.extend( cache[ id ], name );
  1460. } else {
  1461. cache[ id ].data = jQuery.extend( cache[ id ].data, name );
  1462. }
  1463. }
  1464. privateCache = thisCache = cache[ id ];
  1465. // jQuery data() is stored in a separate object inside the object's internal data
  1466. // cache in order to avoid key collisions between internal data and user-defined
  1467. // data.
  1468. if ( !pvt ) {
  1469. if ( !thisCache.data ) {
  1470. thisCache.data = {};
  1471. }
  1472. thisCache = thisCache.data;
  1473. }
  1474. if ( data !== undefined ) {
  1475. thisCache[ jQuery.camelCase( name ) ] = data;
  1476. }
  1477. // Users should not attempt to inspect the internal events object using jQuery.data,
  1478. // it is undocumented and subject to change. But does anyone listen? No.
  1479. if ( isEvents && !thisCache[ name ] ) {
  1480. return privateCache.events;
  1481. }
  1482. // Check for both converted-to-camel and non-converted data property names
  1483. // If a data property was specified
  1484. if ( getByName ) {
  1485. // First Try to find as-is property data
  1486. ret = thisCache[ name ];
  1487. // Test for null|undefined property data
  1488. if ( ret == null ) {
  1489. // Try to find the camelCased property
  1490. ret = thisCache[ jQuery.camelCase( name ) ];
  1491. }
  1492. } else {
  1493. ret = thisCache;
  1494. }
  1495. return ret;
  1496. },
  1497. removeData: function( elem, name, pvt /* Internal Use Only */ ) {
  1498. if ( !jQuery.acceptData( elem ) ) {
  1499. return;
  1500. }
  1501. var thisCache, i, l,
  1502. // Reference to internal data cache key
  1503. internalKey = jQuery.expando,
  1504. isNode = elem.nodeType,
  1505. // See jQuery.data for more information
  1506. cache = isNode ? jQuery.cache : elem,
  1507. // See jQuery.data for more information
  1508. id = isNode ? elem[ internalKey ] : internalKey;
  1509. // If there is already no cache entry for this object, there is no
  1510. // purpose in continuing
  1511. if ( !cache[ id ] ) {
  1512. return;
  1513. }
  1514. if ( name ) {
  1515. thisCache = pvt ? cache[ id ] : cache[ id ].data;
  1516. if ( thisCache ) {
  1517. // Support array or space separated string names for data keys
  1518. if ( !jQuery.isArray( name ) ) {
  1519. // try the string as a key before any manipulation
  1520. if ( name in thisCache ) {
  1521. name = [ name ];
  1522. } else {
  1523. // split the camel cased version by spaces unless a key with the spaces exists
  1524. name = jQuery.camelCase( name );
  1525. if ( name in thisCache ) {
  1526. name = [ name ];
  1527. } else {
  1528. name = name.split( " " );
  1529. }
  1530. }
  1531. }
  1532. for ( i = 0, l = name.length; i < l; i++ ) {
  1533. delete thisCache[ name[i] ];
  1534. }
  1535. // If there is no data left in the cache, we want to continue
  1536. // and let the cache object itself get destroyed
  1537. if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) {
  1538. return;
  1539. }
  1540. }
  1541. }
  1542. // See jQuery.data for more information
  1543. if ( !pvt ) {
  1544. delete cache[ id ].data;
  1545. // Don't destroy the parent cache unless the internal data object
  1546. // had been the only thing left in it
  1547. if ( !isEmptyDataObject(cache[ id ]) ) {
  1548. return;
  1549. }
  1550. }
  1551. // Browsers that fail expando deletion also refuse to delete expandos on
  1552. // the window, but it will allow it on all other JS objects; other browsers
  1553. // don't care
  1554. // Ensure that `cache` is not a window object #10080
  1555. if ( jQuery.support.deleteExpando || !cache.setInterval ) {
  1556. delete cache[ id ];
  1557. } else {
  1558. cache[ id ] = null;
  1559. }
  1560. // We destroyed the cache and need to eliminate the expando on the node to avoid
  1561. // false lookups in the cache for entries that no longer exist
  1562. if ( isNode ) {
  1563. // IE does not allow us to delete expando properties from nodes,
  1564. // nor does it have a removeAttribute function on Document nodes;
  1565. // we must handle all of these cases
  1566. if ( jQuery.support.deleteExpando ) {
  1567. delete elem[ internalKey ];
  1568. } else if ( elem.removeAttribute ) {
  1569. elem.removeAttribute( internalKey );
  1570. } else {
  1571. elem[ internalKey ] = null;
  1572. }
  1573. }
  1574. },
  1575. // For internal use only.
  1576. _data: function( elem, name, data ) {
  1577. return jQuery.data( elem, name, data, true );
  1578. },
  1579. // A method for determining if a DOM node can handle the data expando
  1580. acceptData: function( elem ) {
  1581. if ( elem.nodeName ) {
  1582. var match = jQuery.noData[ elem.nodeName.toLowerCase() ];
  1583. if ( match ) {
  1584. return !(match === true || elem.getAttribute("classid") !== match);
  1585. }
  1586. }
  1587. return true;
  1588. }
  1589. });
  1590. jQuery.fn.extend({
  1591. data: function( key, value ) {
  1592. var parts, part, attr, name, l,
  1593. elem = this[0],
  1594. i = 0,
  1595. data = null;
  1596. // Gets all values
  1597. if ( key === undefined ) {
  1598. if ( this.length ) {
  1599. data = jQuery.data( elem );
  1600. if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) {
  1601. attr = elem.attributes;
  1602. for ( l = attr.length; i < l; i++ ) {
  1603. name = attr[i].name;
  1604. if ( name.indexOf( "data-" ) === 0 ) {
  1605. name = jQuery.camelCase( name.substring(5) );
  1606. dataAttr( elem, name, data[ name ] );
  1607. }
  1608. }
  1609. jQuery._data( elem, "parsedAttrs", true );
  1610. }
  1611. }
  1612. return data;
  1613. }
  1614. // Sets multiple values
  1615. if ( typeof key === "object" ) {
  1616. return this.each(function() {
  1617. jQuery.data( this, key );
  1618. });
  1619. }
  1620. parts = key.split( ".", 2 );
  1621. parts[1] = parts[1] ? "." + parts[1] : "";
  1622. part = parts[1] + "!";
  1623. return jQuery.access( this, function( value ) {
  1624. if ( value === undefined ) {
  1625. data = this.triggerHandler( "getData" + part, [ parts[0] ] );
  1626. // Try to fetch any internally stored data first
  1627. if ( data === undefined && elem ) {
  1628. data = jQuery.data( elem, key );
  1629. data = dataAttr( elem, key, data );
  1630. }
  1631. return data === undefined && parts[1] ?
  1632. this.data( parts[0] ) :
  1633. data;
  1634. }
  1635. parts[1] = value;
  1636. this.each(function() {
  1637. var self = jQuery( this );
  1638. self.triggerHandler( "setData" + part, parts );
  1639. jQuery.data( this, key, value );
  1640. self.triggerHandler( "changeData" + part, parts );
  1641. });
  1642. }, null, value, arguments.length > 1, null, false );
  1643. },
  1644. removeData: function( key ) {
  1645. return this.each(function() {
  1646. jQuery.removeData( this, key );
  1647. });
  1648. }
  1649. });
  1650. function dataAttr( elem, key, data ) {
  1651. // If nothing was found internally, try to fetch any
  1652. // data from the HTML5 data-* attribute
  1653. if ( data === undefined && elem.nodeType === 1 ) {
  1654. var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
  1655. data = elem.getAttribute( name );
  1656. if ( typeof data === "string" ) {
  1657. try {
  1658. data = data === "true" ? true :
  1659. data === "false" ? false :
  1660. data === "null" ? null :
  1661. jQuery.isNumeric( data ) ? +data :
  1662. rbrace.test( data ) ? jQuery.parseJSON( data ) :
  1663. data;
  1664. } catch( e ) {}
  1665. // Make sure we set the data so it isn't changed later
  1666. jQuery.data( elem, key, data );
  1667. } else {
  1668. data = undefined;
  1669. }
  1670. }
  1671. return data;
  1672. }
  1673. // checks a cache object for emptiness
  1674. function isEmptyDataObject( obj ) {
  1675. for ( var name in obj ) {
  1676. // if the public data object is empty, the private is still empty
  1677. if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
  1678. continue;
  1679. }
  1680. if ( name !== "toJSON" ) {
  1681. return false;
  1682. }
  1683. }
  1684. return true;
  1685. }
  1686. function handleQueueMarkDefer( elem, type, src ) {
  1687. var deferDataKey = type + "defer",
  1688. queueDataKey = type + "queue",
  1689. markDataKey = type + "mark",
  1690. defer = jQuery._data( elem, deferDataKey );
  1691. if ( defer &&
  1692. ( src === "queue" || !jQuery._data(elem, queueDataKey) ) &&
  1693. ( src === "mark" || !jQuery._data(elem, markDataKey) ) ) {
  1694. // Give room for hard-coded callbacks to fire first
  1695. // and eventually mark/queue something else on the element
  1696. setTimeout( function() {
  1697. if ( !jQuery._data( elem, queueDataKey ) &&
  1698. !jQuery._data( elem, markDataKey ) ) {
  1699. jQuery.removeData( elem, deferDataKey, true );
  1700. defer.fire();
  1701. }
  1702. }, 0 );
  1703. }
  1704. }
  1705. jQuery.extend({
  1706. _mark: function( elem, type ) {
  1707. if ( elem ) {
  1708. type = ( type || "fx" ) + "mark";
  1709. jQuery._data( elem, type, (jQuery._data( elem, type ) || 0) + 1 );
  1710. }
  1711. },
  1712. _unmark: function( force, elem, type ) {
  1713. if ( force !== true ) {
  1714. type = elem;
  1715. elem = force;
  1716. force = false;
  1717. }
  1718. if ( elem ) {
  1719. type = type || "fx";
  1720. var key = type + "mark",
  1721. count = force ? 0 : ( (jQuery._data( elem, key ) || 1) - 1 );
  1722. if ( count ) {
  1723. jQuery._data( elem, key, count );
  1724. } else {
  1725. jQuery.removeData( elem, key, true );
  1726. handleQueueMarkDefer( elem, type, "mark" );
  1727. }
  1728. }
  1729. },
  1730. queue: function( elem, type, data ) {
  1731. var q;
  1732. if ( elem ) {
  1733. type = ( type || "fx" ) + "queue";
  1734. q = jQuery._data( elem, type );
  1735. // Speed up dequeue by getting out quickly if this is just a lookup
  1736. if ( data ) {
  1737. if ( !q || jQuery.isArray(data) ) {
  1738. q = jQuery._data( elem, type, jQuery.makeArray(data) );
  1739. } else {
  1740. q.push( data );
  1741. }
  1742. }
  1743. return q || [];
  1744. }
  1745. },
  1746. dequeue: function( elem, type ) {
  1747. type = type || "fx";
  1748. var queue = jQuery.queue( elem, type ),
  1749. fn = queue.shift(),
  1750. hooks = {};
  1751. // If the fx queue is dequeued, always remove the progress sentinel
  1752. if ( fn === "inprogress" ) {
  1753. fn = queue.shift();
  1754. }
  1755. if ( fn ) {
  1756. // Add a progress sentinel to prevent the fx queue from being
  1757. // automatically dequeued
  1758. if ( type === "fx" ) {
  1759. queue.unshift( "inprogress" );
  1760. }
  1761. jQuery._data( elem, type + ".run", hooks );
  1762. fn.call( elem, function() {
  1763. jQuery.dequeue( elem, type );
  1764. }, hooks );
  1765. }
  1766. if ( !queue.length ) {
  1767. jQuery.removeData( elem, type + "queue " + type + ".run", true );
  1768. handleQueueMarkDefer( elem, type, "queue" );
  1769. }
  1770. }
  1771. });
  1772. jQuery.fn.extend({
  1773. queue: function( type, data ) {
  1774. var setter = 2;
  1775. if ( typeof type !== "string" ) {
  1776. data = type;
  1777. type = "fx";
  1778. setter--;
  1779. }
  1780. if ( arguments.length < setter ) {
  1781. return jQuery.queue( this[0], type );
  1782. }
  1783. return data === undefined ?
  1784. this :
  1785. this.each(function() {
  1786. var queue = jQuery.queue( this, type, data );
  1787. if ( type === "fx" && queue[0] !== "inprogress" ) {
  1788. jQuery.dequeue( this, type );
  1789. }
  1790. });
  1791. },
  1792. dequeue: function( type ) {
  1793. return this.each(function() {
  1794. jQuery.dequeue( this, type );
  1795. });
  1796. },
  1797. // Based off of the plugin by Clint Helfers, with permission.
  1798. // http://blindsignals.com/index.php/2009/07/jquery-delay/
  1799. delay: function( time, type ) {
  1800. time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
  1801. type = type || "fx";
  1802. return this.queue( type, function( next, hooks ) {
  1803. var timeout = setTimeout( next, time );
  1804. hooks.stop = function() {
  1805. clearTimeout( timeout );
  1806. };
  1807. });
  1808. },
  1809. clearQueue: function( type ) {
  1810. return this.queue( type || "fx", [] );
  1811. },
  1812. // Get a promise resolved when queues of a certain type
  1813. // are emptied (fx is the type by default)
  1814. promise: function( type, object ) {
  1815. if ( typeof type !== "string" ) {
  1816. object = type;
  1817. type = undefined;
  1818. }
  1819. type = type || "fx";
  1820. var defer = jQuery.Deferred(),
  1821. elements = this,
  1822. i = elements.length,
  1823. count = 1,
  1824. deferDataKey = type + "defer",
  1825. queueDataKey = type + "queue",
  1826. markDataKey = type + "mark",
  1827. tmp;
  1828. function resolve() {
  1829. if ( !( --count ) ) {
  1830. defer.resolveWith( elements, [ elements ] );
  1831. }
  1832. }
  1833. while( i-- ) {
  1834. if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) ||
  1835. ( jQuery.data( elements[ i ], queueDataKey, undefined, true ) ||
  1836. jQuery.data( elements[ i ], markDataKey, undefined, true ) ) &&
  1837. jQuery.data( elements[ i ], deferDataKey, jQuery.Callbacks( "once memory" ), true ) )) {
  1838. count++;
  1839. tmp.add( resolve );
  1840. }
  1841. }
  1842. resolve();
  1843. return defer.promise( object );
  1844. }
  1845. });
  1846. var rclass = /[\n\t\r]/g,
  1847. rspace = /\s+/,
  1848. rreturn = /\r/g,
  1849. rtype = /^(?:button|input)$/i,
  1850. rfocusable = /^(?:button|input|object|select|textarea)$/i,
  1851. rclickable = /^a(?:rea)?$/i,
  1852. rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
  1853. getSetAttribute = jQuery.support.getSetAttribute,
  1854. nodeHook, boolHook, fixSpecified;
  1855. jQuery.fn.extend({
  1856. attr: function( name, value ) {
  1857. return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 );
  1858. },
  1859. removeAttr: function( name ) {
  1860. return this.each(function() {
  1861. jQuery.removeAttr( this, name );
  1862. });
  1863. },
  1864. prop: function( name, value ) {
  1865. return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 );
  1866. },
  1867. removeProp: function( name ) {
  1868. name = jQuery.propFix[ name ] || name;
  1869. return this.each(function() {
  1870. // try/catch handles cases where IE balks (such as removing a property on window)
  1871. try {
  1872. this[ name ] = undefined;
  1873. delete this[ name ];
  1874. } catch( e ) {}
  1875. });
  1876. },
  1877. addClass: function( value ) {
  1878. var classNames, i, l, elem,
  1879. setClass, c, cl;
  1880. if ( jQuery.isFunction( value ) ) {
  1881. return this.each(function( j ) {
  1882. jQuery( this ).addClass( value.call(this, j, this.className) );
  1883. });
  1884. }
  1885. if ( value && typeof value === "string" ) {
  1886. classNames = value.split( rspace );
  1887. for ( i = 0, l = this.length; i < l; i++ ) {
  1888. elem = this[ i ];
  1889. if ( elem.nodeType === 1 ) {
  1890. if ( !elem.className && classNames.length === 1 ) {
  1891. elem.className = value;
  1892. } else {
  1893. setClass = " " + elem.className + " ";
  1894. for ( c = 0, cl = classNames.length; c < cl; c++ ) {
  1895. if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) {
  1896. setClass += classNames[ c ] + " ";
  1897. }
  1898. }
  1899. elem.className = jQuery.trim( setClass );
  1900. }
  1901. }
  1902. }
  1903. }
  1904. return this;
  1905. },
  1906. removeClass: function( value ) {
  1907. var classNames, i, l, elem, className, c, cl;
  1908. if ( jQuery.isFunction( value ) ) {
  1909. return this.each(function( j ) {
  1910. jQuery( this ).removeClass( value.call(this, j, this.className) );
  1911. });
  1912. }
  1913. if ( (value && typeof value === "string") || value === undefined ) {
  1914. classNames = ( value || "" ).split( rspace );
  1915. for ( i = 0, l = this.length; i < l; i++ ) {
  1916. elem = this[ i ];
  1917. if ( elem.nodeType === 1 && elem.className ) {
  1918. if ( value ) {
  1919. className = (" " + elem.className + " ").replace( rclass, " " );
  1920. for ( c = 0, cl = classNames.length; c < cl; c++ ) {
  1921. className = className.replace(" " + classNames[ c ] + " ", " ");
  1922. }
  1923. elem.className = jQuery.trim( className );
  1924. } else {
  1925. elem.className = "";
  1926. }
  1927. }
  1928. }
  1929. }
  1930. return this;
  1931. },
  1932. toggleClass: function( value, stateVal ) {
  1933. var type = typeof value,
  1934. isBool = typeof stateVal === "boolean";
  1935. if ( jQuery.isFunction( value ) ) {
  1936. return this.each(function( i ) {
  1937. jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
  1938. });
  1939. }
  1940. return this.each(function() {
  1941. if ( type === "string" ) {
  1942. // toggle individual class names
  1943. var className,
  1944. i = 0,
  1945. self = jQuery( this ),
  1946. state = stateVal,
  1947. classNames = value.split( rspace );
  1948. while ( (className = classNames[ i++ ]) ) {
  1949. // check each className given, space seperated list
  1950. state = isBool ? state : !self.hasClass( className );
  1951. self[ state ? "addClass" : "removeClass" ]( className );
  1952. }
  1953. } else if ( type === "undefined" || type === "boolean" ) {
  1954. if ( this.className ) {
  1955. // store className if set
  1956. jQuery._data( this, "__className__", this.className );
  1957. }
  1958. // toggle whole className
  1959. this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
  1960. }
  1961. });
  1962. },
  1963. hasClass: function( selector ) {
  1964. var className = " " + selector + " ",
  1965. i = 0,
  1966. l = this.length;
  1967. for ( ; i < l; i++ ) {
  1968. if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {
  1969. return true;
  1970. }
  1971. }
  1972. return false;
  1973. },
  1974. val: function( value ) {
  1975. var hooks, ret, isFunction,
  1976. elem = this[0];
  1977. if ( !arguments.length ) {
  1978. if ( elem ) {
  1979. hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
  1980. if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
  1981. return ret;
  1982. }
  1983. ret = elem.value;
  1984. return typeof ret === "string" ?
  1985. // handle most common string cases
  1986. ret.replace(rreturn, "") :
  1987. // handle cases where value is null/undef or number
  1988. ret == null ? "" : ret;
  1989. }
  1990. return;
  1991. }
  1992. isFunction = jQuery.isFunction( value );
  1993. return this.each(function( i ) {
  1994. var self = jQuery(this), val;
  1995. if ( this.nodeType !== 1 ) {
  1996. return;
  1997. }
  1998. if ( isFunction ) {
  1999. val = value.call( this, i, self.val() );
  2000. } else {
  2001. val = value;
  2002. }
  2003. // Treat null/undefined as ""; convert numbers to string
  2004. if ( val == null ) {
  2005. val = "";
  2006. } else if ( typeof val === "number" ) {
  2007. val += "";
  2008. } else if ( jQuery.isArray( val ) ) {
  2009. val = jQuery.map(val, function ( value ) {
  2010. return value == null ? "" : value + "";
  2011. });
  2012. }
  2013. hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
  2014. // If set returns undefined, fall back to normal setting
  2015. if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
  2016. this.value = val;
  2017. }
  2018. });
  2019. }
  2020. });
  2021. jQuery.extend({
  2022. valHooks: {
  2023. option: {
  2024. get: function( elem ) {
  2025. // attributes.value is undefined in Blackberry 4.7 but
  2026. // uses .value. See #6932
  2027. var val = elem.attributes.value;
  2028. return !val || val.specified ? elem.value : elem.text;
  2029. }
  2030. },
  2031. select: {
  2032. get: function( elem ) {
  2033. var value, i, max, option,
  2034. index = elem.selectedIndex,
  2035. values = [],
  2036. options = elem.options,
  2037. one = elem.type === "select-one";
  2038. // Nothing was selected
  2039. if ( index < 0 ) {
  2040. return null;
  2041. }
  2042. // Loop through all the selected options
  2043. i = one ? index : 0;
  2044. max = one ? index + 1 : options.length;
  2045. for ( ; i < max; i++ ) {
  2046. option = options[ i ];
  2047. // Don't return options that are disabled or in a disabled optgroup
  2048. if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) &&
  2049. (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) {
  2050. // Get the specific value for the option
  2051. value = jQuery( option ).val();
  2052. // We don't need an array for one selects
  2053. if ( one ) {
  2054. return value;
  2055. }
  2056. // Multi-Selects return an array
  2057. values.push( value );
  2058. }
  2059. }
  2060. // Fixes Bug #2551 -- select.val() broken in IE after form.reset()
  2061. if ( one && !values.length && options.length ) {
  2062. return jQuery( options[ index ] ).val();
  2063. }
  2064. return values;
  2065. },
  2066. set: function( elem, value ) {
  2067. var values = jQuery.makeArray( value );
  2068. jQuery(elem).find("option").each(function() {
  2069. this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
  2070. });
  2071. if ( !values.length ) {
  2072. elem.selectedIndex = -1;
  2073. }
  2074. return values;
  2075. }
  2076. }
  2077. },
  2078. attrFn: {
  2079. val: true,
  2080. css: true,
  2081. html: true,
  2082. text: true,
  2083. data: true,
  2084. width: true,
  2085. height: true,
  2086. offset: true
  2087. },
  2088. attr: function( elem, name, value, pass ) {
  2089. var ret, hooks, notxml,
  2090. nType = elem.nodeType;
  2091. // don't get/set attributes on text, comment and attribute nodes
  2092. if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
  2093. return;
  2094. }
  2095. if ( pass && name in jQuery.attrFn ) {
  2096. return jQuery( elem )[ name ]( value );
  2097. }
  2098. // Fallback to prop when attributes are not supported
  2099. if ( typeof elem.getAttribute === "undefined" ) {
  2100. return jQuery.prop( elem, name, value );
  2101. }
  2102. notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
  2103. // All attributes are lowercase
  2104. // Grab necessary hook if one is defined
  2105. if ( notxml ) {
  2106. name = name.toLowerCase();
  2107. hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook );
  2108. }
  2109. if ( value !== undefined ) {
  2110. if ( value === null ) {
  2111. jQuery.removeAttr( elem, name );
  2112. return;
  2113. } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) {
  2114. return ret;
  2115. } else {
  2116. elem.setAttribute( name, "" + value );
  2117. return value;
  2118. }
  2119. } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) {
  2120. return ret;
  2121. } else {
  2122. ret = elem.getAttribute( name );
  2123. // Non-existent attributes return null, we normalize to undefined
  2124. return ret === null ?
  2125. undefined :
  2126. ret;
  2127. }
  2128. },
  2129. removeAttr: function( elem, value ) {
  2130. var propName, attrNames, name, l, isBool,
  2131. i = 0;
  2132. if ( value && elem.nodeType === 1 ) {
  2133. attrNames = value.toLowerCase().split( rspace );
  2134. l = attrNames.length;
  2135. for ( ; i < l; i++ ) {
  2136. name = attrNames[ i ];
  2137. if ( name ) {
  2138. propName = jQuery.propFix[ name ] || name;
  2139. isBool = rboolean.test( name );
  2140. // See #9699 for explanation of this approach (setting first, then removal)
  2141. // Do not do this for boolean attributes (see #10870)
  2142. if ( !isBool ) {
  2143. jQuery.attr( elem, name, "" );
  2144. }
  2145. elem.removeAttribute( getSetAttribute ? name : propName );
  2146. // Set corresponding property to false for boolean attributes
  2147. if ( isBool && propName in elem ) {
  2148. elem[ propName ] = false;
  2149. }
  2150. }
  2151. }
  2152. }
  2153. },
  2154. attrHooks: {
  2155. type: {
  2156. set: function( elem, value ) {
  2157. // We can't allow the type property to be changed (since it causes problems in IE)
  2158. if ( rtype.test( elem.nodeName ) && elem.parentNode ) {
  2159. jQuery.error( "type property can't be changed" );
  2160. } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
  2161. // Setting the type on a radio button after the value resets the value in IE6-9
  2162. // Reset value to it's default in case type is set after value
  2163. // This is for element creation
  2164. var val = elem.value;
  2165. elem.setAttribute( "type", value );
  2166. if ( val ) {
  2167. elem.value = val;
  2168. }
  2169. return value;
  2170. }
  2171. }
  2172. },
  2173. // Use the value property for back compat
  2174. // Use the nodeHook for button elements in IE6/7 (#1954)
  2175. value: {
  2176. get: function( elem, name ) {
  2177. if ( nodeHook && jQuery.nodeName( elem, "button" ) ) {
  2178. return nodeHook.get( elem, name );
  2179. }
  2180. return name in elem ?
  2181. elem.value :
  2182. null;
  2183. },
  2184. set: function( elem, value, name ) {
  2185. if ( nodeHook && jQuery.nodeName( elem, "button" ) ) {
  2186. return nodeHook.set( elem, value, name );
  2187. }
  2188. // Does not return so that setAttribute is also used
  2189. elem.value = value;
  2190. }
  2191. }
  2192. },
  2193. propFix: {
  2194. tabindex: "tabIndex",
  2195. readonly: "readOnly",
  2196. "for": "htmlFor",
  2197. "class": "className",
  2198. maxlength: "maxLength",
  2199. cellspacing: "cellSpacing",
  2200. cellpadding: "cellPadding",
  2201. rowspan: "rowSpan",
  2202. colspan: "colSpan",
  2203. usemap: "useMap",
  2204. frameborder: "frameBorder",
  2205. contenteditable: "contentEditable"
  2206. },
  2207. prop: function( elem, name, value ) {
  2208. var ret, hooks, notxml,
  2209. nType = elem.nodeType;
  2210. // don't get/set properties on text, comment and attribute nodes
  2211. if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
  2212. return;
  2213. }
  2214. notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
  2215. if ( notxml ) {
  2216. // Fix name and attach hooks
  2217. name = jQuery.propFix[ name ] || name;
  2218. hooks = jQuery.propHooks[ name ];
  2219. }
  2220. if ( value !== undefined ) {
  2221. if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
  2222. return ret;
  2223. } else {
  2224. return ( elem[ name ] = value );
  2225. }
  2226. } else {
  2227. if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
  2228. return ret;
  2229. } else {
  2230. return elem[ name ];
  2231. }
  2232. }
  2233. },
  2234. propHooks: {
  2235. tabIndex: {
  2236. get: function( elem ) {
  2237. // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
  2238. // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
  2239. var attributeNode = elem.getAttributeNode("tabindex");
  2240. return attributeNode && attributeNode.specified ?
  2241. parseInt( attributeNode.value, 10 ) :
  2242. rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
  2243. 0 :
  2244. undefined;
  2245. }
  2246. }
  2247. }
  2248. });
  2249. // Add the tabIndex propHook to attrHooks for back-compat (different case is intentional)
  2250. jQuery.attrHooks.tabindex = jQuery.propHooks.tabIndex;
  2251. // Hook for boolean attributes
  2252. boolHook = {
  2253. get: function( elem, name ) {
  2254. // Align boolean attributes with corresponding properties
  2255. // Fall back to attribute presence where some booleans are not supported
  2256. var attrNode,
  2257. property = jQuery.prop( elem, name );
  2258. return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ?
  2259. name.toLowerCase() :
  2260. undefined;
  2261. },
  2262. set: function( elem, value, name ) {
  2263. var propName;
  2264. if ( value === false ) {
  2265. // Remove boolean attributes when set to false
  2266. jQuery.removeAttr( elem, name );
  2267. } else {
  2268. // value is true since we know at this point it's type boolean and not false
  2269. // Set boolean attributes to the same name and set the DOM property
  2270. propName = jQuery.propFix[ name ] || name;
  2271. if ( propName in elem ) {
  2272. // Only set the IDL specifically if it already exists on the element
  2273. elem[ propName ] = true;
  2274. }
  2275. elem.setAttribute( name, name.toLowerCase() );
  2276. }
  2277. return name;
  2278. }
  2279. };
  2280. // IE6/7 do not support getting/setting some attributes with get/setAttribute
  2281. if ( !getSetAttribute ) {
  2282. fixSpecified = {
  2283. name: true,
  2284. id: true,
  2285. coords: true
  2286. };
  2287. // Use this for any attribute in IE6/7
  2288. // This fixes almost every IE6/7 issue
  2289. nodeHook = jQuery.valHooks.button = {
  2290. get: function( elem, name ) {
  2291. var ret;
  2292. ret = elem.getAttributeNode( name );
  2293. return ret && ( fixSpecified[ name ] ? ret.nodeValue !== "" : ret.specified ) ?
  2294. ret.nodeValue :
  2295. undefined;
  2296. },
  2297. set: function( elem, value, name ) {
  2298. // Set the existing or create a new attribute node
  2299. var ret = elem.getAttributeNode( name );
  2300. if ( !ret ) {
  2301. ret = document.createAttribute( name );
  2302. elem.setAttributeNode( ret );
  2303. }
  2304. return ( ret.nodeValue = value + "" );
  2305. }
  2306. };
  2307. // Apply the nodeHook to tabindex
  2308. jQuery.attrHooks.tabindex.set = nodeHook.set;
  2309. // Set width and height to auto instead of 0 on empty string( Bug #8150 )
  2310. // This is for removals
  2311. jQuery.each([ "width", "height" ], function( i, name ) {
  2312. jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
  2313. set: function( elem, value ) {
  2314. if ( value === "" ) {
  2315. elem.setAttribute( name, "auto" );
  2316. return value;
  2317. }
  2318. }
  2319. });
  2320. });
  2321. // Set contenteditable to false on removals(#10429)
  2322. // Setting to empty string throws an error as an invalid value
  2323. jQuery.attrHooks.contenteditable = {
  2324. get: nodeHook.get,
  2325. set: function( elem, value, name ) {
  2326. if ( value === "" ) {
  2327. value = "false";
  2328. }
  2329. nodeHook.set( elem, value, name );
  2330. }
  2331. };
  2332. }
  2333. // Some attributes require a special call on IE
  2334. if ( !jQuery.support.hrefNormalized ) {
  2335. jQuery.each([ "href", "src", "width", "height" ], function( i, name ) {
  2336. jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
  2337. get: function( elem ) {
  2338. var ret = elem.getAttribute( name, 2 );
  2339. return ret === null ? undefined : ret;
  2340. }
  2341. });
  2342. });
  2343. }
  2344. if ( !jQuery.support.style ) {
  2345. jQuery.attrHooks.style = {
  2346. get: function( elem ) {
  2347. // Return undefined in the case of empty string
  2348. // Normalize to lowercase since IE uppercases css property names
  2349. return elem.style.cssText.toLowerCase() || undefined;
  2350. },
  2351. set: function( elem, value ) {
  2352. return ( elem.style.cssText = "" + value );
  2353. }
  2354. };
  2355. }
  2356. // Safari mis-reports the default selected property of an option
  2357. // Accessing the parent's selectedIndex property fixes it
  2358. if ( !jQuery.support.optSelected ) {
  2359. jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, {
  2360. get: function( elem ) {
  2361. var parent = elem.parentNode;
  2362. if ( parent ) {
  2363. parent.selectedIndex;
  2364. // Make sure that it also works with optgroups, see #5701
  2365. if ( parent.parentNode ) {
  2366. parent.parentNode.selectedIndex;
  2367. }
  2368. }
  2369. return null;
  2370. }
  2371. });
  2372. }
  2373. // IE6/7 call enctype encoding
  2374. if ( !jQuery.support.enctype ) {
  2375. jQuery.propFix.enctype = "encoding";
  2376. }
  2377. // Radios and checkboxes getter/setter
  2378. if ( !jQuery.support.checkOn ) {
  2379. jQuery.each([ "radio", "checkbox" ], function() {
  2380. jQuery.valHooks[ this ] = {
  2381. get: function( elem ) {
  2382. // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
  2383. return elem.getAttribute("value") === null ? "on" : elem.value;
  2384. }
  2385. };
  2386. });
  2387. }
  2388. jQuery.each([ "radio", "checkbox" ], function() {
  2389. jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], {
  2390. set: function( elem, value ) {
  2391. if ( jQuery.isArray( value ) ) {
  2392. return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
  2393. }
  2394. }
  2395. });
  2396. });
  2397. var rformElems = /^(?:textarea|input|select)$/i,
  2398. rtypenamespace = /^([^\.]*)?(?:\.(.+))?$/,
  2399. rhoverHack = /(?:^|\s)hover(\.\S+)?\b/,
  2400. rkeyEvent = /^key/,
  2401. rmouseEvent = /^(?:mouse|contextmenu)|click/,
  2402. rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
  2403. rquickIs = /^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,
  2404. quickParse = function( selector ) {
  2405. var quick = rquickIs.exec( selector );
  2406. if ( quick ) {
  2407. // 0 1 2 3
  2408. // [ _, tag, id, class ]
  2409. quick[1] = ( quick[1] || "" ).toLowerCase();
  2410. quick[3] = quick[3] && new RegExp( "(?:^|\\s)" + quick[3] + "(?:\\s|$)" );
  2411. }
  2412. return quick;
  2413. },
  2414. quickIs = function( elem, m ) {
  2415. var attrs = elem.attributes || {};
  2416. return (
  2417. (!m[1] || elem.nodeName.toLowerCase() === m[1]) &&
  2418. (!m[2] || (attrs.id || {}).value === m[2]) &&
  2419. (!m[3] || m[3].test( (attrs[ "class" ] || {}).value ))
  2420. );
  2421. },
  2422. hoverHack = function( events ) {
  2423. return jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" );
  2424. };
  2425. /*
  2426. * Helper functions for managing events -- not part of the public interface.
  2427. * Props to Dean Edwards' addEvent library for many of the ideas.
  2428. */
  2429. jQuery.event = {
  2430. add: function( elem, types, handler, data, selector ) {
  2431. var elemData, eventHandle, events,
  2432. t, tns, type, namespaces, handleObj,
  2433. handleObjIn, quick, handlers, special;
  2434. // Don't attach events to noData or text/comment nodes (allow plain objects tho)
  2435. if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) {
  2436. return;
  2437. }
  2438. // Caller can pass in an object of custom data in lieu of the handler
  2439. if ( handler.handler ) {
  2440. handleObjIn = handler;
  2441. handler = handleObjIn.handler;
  2442. selector = handleObjIn.selector;
  2443. }
  2444. // Make sure that the handler has a unique ID, used to find/remove it later
  2445. if ( !handler.guid ) {
  2446. handler.guid = jQuery.guid++;
  2447. }
  2448. // Init the element's event structure and main handler, if this is the first
  2449. events = elemData.events;
  2450. if ( !events ) {
  2451. elemData.events = events = {};
  2452. }
  2453. eventHandle = elemData.handle;
  2454. if ( !eventHandle ) {
  2455. elemData.handle = eventHandle = function( e ) {
  2456. // Discard the second event of a jQuery.event.trigger() and
  2457. // when an event is called after a page has unloaded
  2458. return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ?
  2459. jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
  2460. undefined;
  2461. };
  2462. // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
  2463. eventHandle.elem = elem;
  2464. }
  2465. // Handle multiple events separated by a space
  2466. // jQuery(...).bind("mouseover mouseout", fn);
  2467. types = jQuery.trim( hoverHack(types) ).split( " " );
  2468. for ( t = 0; t < types.length; t++ ) {
  2469. tns = rtypenamespace.exec( types[t] ) || [];
  2470. type = tns[1];
  2471. namespaces = ( tns[2] || "" ).split( "." ).sort();
  2472. // If event changes its type, use the special event handlers for the changed type
  2473. special = jQuery.event.special[ type ] || {};
  2474. // If selector defined, determine special event api type, otherwise given type
  2475. type = ( selector ? special.delegateType : special.bindType ) || type;
  2476. // Update special based on newly reset type
  2477. special = jQuery.event.special[ type ] || {};
  2478. // handleObj is passed to all event handlers
  2479. handleObj = jQuery.extend({
  2480. type: type,
  2481. origType: tns[1],
  2482. data: data,
  2483. handler: handler,
  2484. guid: handler.guid,
  2485. selector: selector,
  2486. quick: selector && quickParse( selector ),
  2487. namespace: namespaces.join(".")
  2488. }, handleObjIn );
  2489. // Init the event handler queue if we're the first
  2490. handlers = events[ type ];
  2491. if ( !handlers ) {
  2492. handlers = events[ type ] = [];
  2493. handlers.delegateCount = 0;
  2494. // Only use addEventListener/attachEvent if the special events handler returns false
  2495. if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
  2496. // Bind the global event handler to the element
  2497. if ( elem.addEventListener ) {
  2498. elem.addEventListener( type, eventHandle, false );
  2499. } else if ( elem.attachEvent ) {
  2500. elem.attachEvent( "on" + type, eventHandle );
  2501. }
  2502. }
  2503. }
  2504. if ( special.add ) {
  2505. special.add.call( elem, handleObj );
  2506. if ( !handleObj.handler.guid ) {
  2507. handleObj.handler.guid = handler.guid;
  2508. }
  2509. }
  2510. // Add to the element's handler list, delegates in front
  2511. if ( selector ) {
  2512. handlers.splice( handlers.delegateCount++, 0, handleObj );
  2513. } else {
  2514. handlers.push( handleObj );
  2515. }
  2516. // Keep track of which events have ever been used, for event optimization
  2517. jQuery.event.global[ type ] = true;
  2518. }
  2519. // Nullify elem to prevent memory leaks in IE
  2520. elem = null;
  2521. },
  2522. global: {},
  2523. // Detach an event or set of events from an element
  2524. remove: function( elem, types, handler, selector, mappedTypes ) {
  2525. var elemData = jQuery.hasData( elem ) && jQuery._data( elem ),
  2526. t, tns, type, origType, namespaces, origCount,
  2527. j, events, special, handle, eventType, handleObj;
  2528. if ( !elemData || !(events = elemData.events) ) {
  2529. return;
  2530. }
  2531. // Once for each type.namespace in types; type may be omitted
  2532. types = jQuery.trim( hoverHack( types || "" ) ).split(" ");
  2533. for ( t = 0; t < types.length; t++ ) {
  2534. tns = rtypenamespace.exec( types[t] ) || [];
  2535. type = origType = tns[1];
  2536. namespaces = tns[2];
  2537. // Unbind all events (on this namespace, if provided) for the element
  2538. if ( !type ) {
  2539. for ( type in events ) {
  2540. jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
  2541. }
  2542. continue;
  2543. }
  2544. special = jQuery.event.special[ type ] || {};
  2545. type = ( selector? special.delegateType : special.bindType ) || type;
  2546. eventType = events[ type ] || [];
  2547. origCount = eventType.length;
  2548. namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.)?") + "(\\.|$)") : null;
  2549. // Remove matching events
  2550. for ( j = 0; j < eventType.length; j++ ) {
  2551. handleObj = eventType[ j ];
  2552. if ( ( mappedTypes || origType === handleObj.origType ) &&
  2553. ( !handler || handler.guid === handleObj.guid ) &&
  2554. ( !namespaces || namespaces.test( handleObj.namespace ) ) &&
  2555. ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
  2556. eventType.splice( j--, 1 );
  2557. if ( handleObj.selector ) {
  2558. eventType.delegateCount--;
  2559. }
  2560. if ( special.remove ) {
  2561. special.remove.call( elem, handleObj );
  2562. }
  2563. }
  2564. }
  2565. // Remove generic event handler if we removed something and no more handlers exist
  2566. // (avoids potential for endless recursion during removal of special event handlers)
  2567. if ( eventType.length === 0 && origCount !== eventType.length ) {
  2568. if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
  2569. jQuery.removeEvent( elem, type, elemData.handle );
  2570. }
  2571. delete events[ type ];
  2572. }
  2573. }
  2574. // Remove the expando if it's no longer used
  2575. if ( jQuery.isEmptyObject( events ) ) {
  2576. handle = elemData.handle;
  2577. if ( handle ) {
  2578. handle.elem = null;
  2579. }
  2580. // removeData also checks for emptiness and clears the expando if empty
  2581. // so use it instead of delete
  2582. jQuery.removeData( elem, [ "events", "handle" ], true );
  2583. }
  2584. },
  2585. // Events that are safe to short-circuit if no handlers are attached.
  2586. // Native DOM events should not be added, they may have inline handlers.
  2587. customEvent: {
  2588. "getData": true,
  2589. "setData": true,
  2590. "changeData": true
  2591. },
  2592. trigger: function( event, data, elem, onlyHandlers ) {
  2593. // Don't do events on text and comment nodes
  2594. if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) {
  2595. return;
  2596. }
  2597. // Event object or event type
  2598. var type = event.type || event,
  2599. namespaces = [],
  2600. cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType;
  2601. // focus/blur morphs to focusin/out; ensure we're not firing them right now
  2602. if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
  2603. return;
  2604. }
  2605. if ( type.indexOf( "!" ) >= 0 ) {
  2606. // Exclusive events trigger only for the exact event (no namespaces)
  2607. type = type.slice(0, -1);
  2608. exclusive = true;
  2609. }
  2610. if ( type.indexOf( "." ) >= 0 ) {
  2611. // Namespaced trigger; create a regexp to match event type in handle()
  2612. namespaces = type.split(".");
  2613. type = namespaces.shift();
  2614. namespaces.sort();
  2615. }
  2616. if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) {
  2617. // No jQuery handlers for this event type, and it can't have inline handlers
  2618. return;
  2619. }
  2620. // Caller can pass in an Event, Object, or just an event type string
  2621. event = typeof event === "object" ?
  2622. // jQuery.Event object
  2623. event[ jQuery.expando ] ? event :
  2624. // Object literal
  2625. new jQuery.Event( type, event ) :
  2626. // Just the event type (string)
  2627. new jQuery.Event( type );
  2628. event.type = type;
  2629. event.isTrigger = true;
  2630. event.exclusive = exclusive;
  2631. event.namespace = namespaces.join( "." );
  2632. event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)") : null;
  2633. ontype = type.indexOf( ":" ) < 0 ? "on" + type : "";
  2634. // Handle a global trigger
  2635. if ( !elem ) {
  2636. // TODO: Stop taunting the data cache; remove global events and always attach to document
  2637. cache = jQuery.cache;
  2638. for ( i in cache ) {
  2639. if ( cache[ i ].events && cache[ i ].events[ type ] ) {
  2640. jQuery.event.trigger( event, data, cache[ i ].handle.elem, true );
  2641. }
  2642. }
  2643. return;
  2644. }
  2645. // Clean up the event in case it is being reused
  2646. event.result = undefined;
  2647. if ( !event.target ) {
  2648. event.target = elem;
  2649. }
  2650. // Clone any incoming data and prepend the event, creating the handler arg list
  2651. data = data != null ? jQuery.makeArray( data ) : [];
  2652. data.unshift( event );
  2653. // Allow special events to draw outside the lines
  2654. special = jQuery.event.special[ type ] || {};
  2655. if ( special.trigger && special.trigger.apply( elem, data ) === false ) {
  2656. return;
  2657. }
  2658. // Determine event propagation path in advance, per W3C events spec (#9951)
  2659. // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
  2660. eventPath = [[ elem, special.bindType || type ]];
  2661. if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
  2662. bubbleType = special.delegateType || type;
  2663. cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode;
  2664. old = null;
  2665. for ( ; cur; cur = cur.parentNode ) {
  2666. eventPath.push([ cur, bubbleType ]);
  2667. old = cur;
  2668. }
  2669. // Only add window if we got to document (e.g., not plain obj or detached DOM)
  2670. if ( old && old === elem.ownerDocument ) {
  2671. eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]);
  2672. }
  2673. }
  2674. // Fire handlers on the event path
  2675. for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) {
  2676. cur = eventPath[i][0];
  2677. event.type = eventPath[i][1];
  2678. handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
  2679. if ( handle ) {
  2680. handle.apply( cur, data );
  2681. }
  2682. // Note that this is a bare JS function and not a jQuery handler
  2683. handle = ontype && cur[ ontype ];
  2684. if ( handle && jQuery.acceptData( cur ) && handle.apply( cur, data ) === false ) {
  2685. event.preventDefault();
  2686. }
  2687. }
  2688. event.type = type;
  2689. // If nobody prevented the default action, do it now
  2690. if ( !onlyHandlers && !event.isDefaultPrevented() ) {
  2691. if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) &&
  2692. !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) {
  2693. // Call a native DOM method on the target with the same name name as the event.
  2694. // Can't use an .isFunction() check here because IE6/7 fails that test.
  2695. // Don't do default actions on window, that's where global variables be (#6170)
  2696. // IE<9 dies on focus/blur to hidden element (#1486)
  2697. if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) {
  2698. // Don't re-trigger an onFOO event when we call its FOO() method
  2699. old = elem[ ontype ];
  2700. if ( old ) {
  2701. elem[ ontype ] = null;
  2702. }
  2703. // Prevent re-triggering of the same event, since we already bubbled it above
  2704. jQuery.event.triggered = type;
  2705. elem[ type ]();
  2706. jQuery.event.triggered = undefined;
  2707. if ( old ) {
  2708. elem[ ontype ] = old;
  2709. }
  2710. }
  2711. }
  2712. }
  2713. return event.result;
  2714. },
  2715. dispatch: function( event ) {
  2716. // Make a writable jQuery.Event from the native event object
  2717. event = jQuery.event.fix( event || window.event );
  2718. var handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []),
  2719. delegateCount = handlers.delegateCount,
  2720. args = [].slice.call( arguments, 0 ),
  2721. run_all = !event.exclusive && !event.namespace,
  2722. special = jQuery.event.special[ event.type ] || {},
  2723. handlerQueue = [],
  2724. i, j, cur, jqcur, ret, selMatch, matched, matches, handleObj, sel, related;
  2725. // Use the fix-ed jQuery.Event rather than the (read-only) native event
  2726. args[0] = event;
  2727. event.delegateTarget = this;
  2728. // Call the preDispatch hook for the mapped type, and let it bail if desired
  2729. if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
  2730. return;
  2731. }
  2732. // Determine handlers that should run if there are delegated events
  2733. // Avoid non-left-click bubbling in Firefox (#3861)
  2734. if ( delegateCount && !(event.button && event.type === "click") ) {
  2735. // Pregenerate a single jQuery object for reuse with .is()
  2736. jqcur = jQuery(this);
  2737. jqcur.context = this.ownerDocument || this;
  2738. for ( cur = event.target; cur != this; cur = cur.parentNode || this ) {
  2739. // Don't process events on disabled elements (#6911, #8165)
  2740. if ( cur.disabled !== true ) {
  2741. selMatch = {};
  2742. matches = [];
  2743. jqcur[0] = cur;
  2744. for ( i = 0; i < delegateCount; i++ ) {
  2745. handleObj = handlers[ i ];
  2746. sel = handleObj.selector;
  2747. if ( selMatch[ sel ] === undefined ) {
  2748. selMatch[ sel ] = (
  2749. handleObj.quick ? quickIs( cur, handleObj.quick ) : jqcur.is( sel )
  2750. );
  2751. }
  2752. if ( selMatch[ sel ] ) {
  2753. matches.push( handleObj );
  2754. }
  2755. }
  2756. if ( matches.length ) {
  2757. handlerQueue.push({ elem: cur, matches: matches });
  2758. }
  2759. }
  2760. }
  2761. }
  2762. // Add the remaining (directly-bound) handlers
  2763. if ( handlers.length > delegateCount ) {
  2764. handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) });
  2765. }
  2766. // Run delegates first; they may want to stop propagation beneath us
  2767. for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) {
  2768. matched = handlerQueue[ i ];
  2769. event.currentTarget = matched.elem;
  2770. for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) {
  2771. handleObj = matched.matches[ j ];
  2772. // Triggered event must either 1) be non-exclusive and have no namespace, or
  2773. // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
  2774. if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) {
  2775. event.data = handleObj.data;
  2776. event.handleObj = handleObj;
  2777. ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
  2778. .apply( matched.elem, args );
  2779. if ( ret !== undefined ) {
  2780. event.result = ret;
  2781. if ( ret === false ) {
  2782. event.preventDefault();
  2783. event.stopPropagation();
  2784. }
  2785. }
  2786. }
  2787. }
  2788. }
  2789. // Call the postDispatch hook for the mapped type
  2790. if ( special.postDispatch ) {
  2791. special.postDispatch.call( this, event );
  2792. }
  2793. return event.result;
  2794. },
  2795. // Includes some event props shared by KeyEvent and MouseEvent
  2796. // *** attrChange attrName relatedNode srcElement are not normalized, non-W3C, deprecated, will be removed in 1.8 ***
  2797. props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
  2798. fixHooks: {},
  2799. keyHooks: {
  2800. props: "char charCode key keyCode".split(" "),
  2801. filter: function( event, original ) {
  2802. // Add which for key events
  2803. if ( event.which == null ) {
  2804. event.which = original.charCode != null ? original.charCode : original.keyCode;
  2805. }
  2806. return event;
  2807. }
  2808. },
  2809. mouseHooks: {
  2810. props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
  2811. filter: function( event, original ) {
  2812. var eventDoc, doc, body,
  2813. button = original.button,
  2814. fromElement = original.fromElement;
  2815. // Calculate pageX/Y if missing and clientX/Y available
  2816. if ( event.pageX == null && original.clientX != null ) {
  2817. eventDoc = event.target.ownerDocument || document;
  2818. doc = eventDoc.documentElement;
  2819. body = eventDoc.body;
  2820. event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
  2821. event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 );
  2822. }
  2823. // Add relatedTarget, if necessary
  2824. if ( !event.relatedTarget && fromElement ) {
  2825. event.relatedTarget = fromElement === event.target ? original.toElement : fromElement;
  2826. }
  2827. // Add which for click: 1 === left; 2 === middle; 3 === right
  2828. // Note: button is not normalized, so don't use it
  2829. if ( !event.which && button !== undefined ) {
  2830. event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
  2831. }
  2832. return event;
  2833. }
  2834. },
  2835. fix: function( event ) {
  2836. if ( event[ jQuery.expando ] ) {
  2837. return event;
  2838. }
  2839. // Create a writable copy of the event object and normalize some properties
  2840. var i, prop,
  2841. originalEvent = event,
  2842. fixHook = jQuery.event.fixHooks[ event.type ] || {},
  2843. copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
  2844. event = jQuery.Event( originalEvent );
  2845. for ( i = copy.length; i; ) {
  2846. prop = copy[ --i ];
  2847. event[ prop ] = originalEvent[ prop ];
  2848. }
  2849. // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2)
  2850. if ( !event.target ) {
  2851. event.target = originalEvent.srcElement || document;
  2852. }
  2853. // Target should not be a text node (#504, Safari)
  2854. if ( event.target.nodeType === 3 ) {
  2855. event.target = event.target.parentNode;
  2856. }
  2857. // For mouse/key events; add metaKey if it's not there (#3368, IE6/7/8)
  2858. if ( event.metaKey === undefined ) {
  2859. event.metaKey = event.ctrlKey;
  2860. }
  2861. return fixHook.filter? fixHook.filter( event, originalEvent ) : event;
  2862. },
  2863. special: {
  2864. ready: {
  2865. // Make sure the ready event is setup
  2866. setup: jQuery.bindReady
  2867. },
  2868. load: {
  2869. // Prevent triggered image.load events from bubbling to window.load
  2870. noBubble: true
  2871. },
  2872. focus: {
  2873. delegateType: "focusin"
  2874. },
  2875. blur: {
  2876. delegateType: "focusout"
  2877. },
  2878. beforeunload: {
  2879. setup: function( data, namespaces, eventHandle ) {
  2880. // We only want to do this special case on windows
  2881. if ( jQuery.isWindow( this ) ) {
  2882. this.onbeforeunload = eventHandle;
  2883. }
  2884. },
  2885. teardown: function( namespaces, eventHandle ) {
  2886. if ( this.onbeforeunload === eventHandle ) {
  2887. this.onbeforeunload = null;
  2888. }
  2889. }
  2890. }
  2891. },
  2892. simulate: function( type, elem, event, bubble ) {
  2893. // Piggyback on a donor event to simulate a different one.
  2894. // Fake originalEvent to avoid donor's stopPropagation, but if the
  2895. // simulated event prevents default then we do the same on the donor.
  2896. var e = jQuery.extend(
  2897. new jQuery.Event(),
  2898. event,
  2899. { type: type,
  2900. isSimulated: true,
  2901. originalEvent: {}
  2902. }
  2903. );
  2904. if ( bubble ) {
  2905. jQuery.event.trigger( e, null, elem );
  2906. } else {
  2907. jQuery.event.dispatch.call( elem, e );
  2908. }
  2909. if ( e.isDefaultPrevented() ) {
  2910. event.preventDefault();
  2911. }
  2912. }
  2913. };
  2914. // Some plugins are using, but it's undocumented/deprecated and will be removed.
  2915. // The 1.7 special event interface should provide all the hooks needed now.
  2916. jQuery.event.handle = jQuery.event.dispatch;
  2917. jQuery.removeEvent = document.removeEventListener ?
  2918. function( elem, type, handle ) {
  2919. if ( elem.removeEventListener ) {
  2920. elem.removeEventListener( type, handle, false );
  2921. }
  2922. } :
  2923. function( elem, type, handle ) {
  2924. if ( elem.detachEvent ) {
  2925. elem.detachEvent( "on" + type, handle );
  2926. }
  2927. };
  2928. jQuery.Event = function( src, props ) {
  2929. // Allow instantiation without the 'new' keyword
  2930. if ( !(this instanceof jQuery.Event) ) {
  2931. return new jQuery.Event( src, props );
  2932. }
  2933. // Event object
  2934. if ( src && src.type ) {
  2935. this.originalEvent = src;
  2936. this.type = src.type;
  2937. // Events bubbling up the document may have been marked as prevented
  2938. // by a handler lower down the tree; reflect the correct value.
  2939. this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false ||
  2940. src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse;
  2941. // Event type
  2942. } else {
  2943. this.type = src;
  2944. }
  2945. // Put explicitly provided properties onto the event object
  2946. if ( props ) {
  2947. jQuery.extend( this, props );
  2948. }
  2949. // Create a timestamp if incoming event doesn't have one
  2950. this.timeStamp = src && src.timeStamp || jQuery.now();
  2951. // Mark it as fixed
  2952. this[ jQuery.expando ] = true;
  2953. };
  2954. function returnFalse() {
  2955. return false;
  2956. }
  2957. function returnTrue() {
  2958. return true;
  2959. }
  2960. // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
  2961. // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
  2962. jQuery.Event.prototype = {
  2963. preventDefault: function() {
  2964. this.isDefaultPrevented = returnTrue;
  2965. var e = this.originalEvent;
  2966. if ( !e ) {
  2967. return;
  2968. }
  2969. // if preventDefault exists run it on the original event
  2970. if ( e.preventDefault ) {
  2971. e.preventDefault();
  2972. // otherwise set the returnValue property of the original event to false (IE)
  2973. } else {
  2974. e.returnValue = false;
  2975. }
  2976. },
  2977. stopPropagation: function() {
  2978. this.isPropagationStopped = returnTrue;
  2979. var e = this.originalEvent;
  2980. if ( !e ) {
  2981. return;
  2982. }
  2983. // if stopPropagation exists run it on the original event
  2984. if ( e.stopPropagation ) {
  2985. e.stopPropagation();
  2986. }
  2987. // otherwise set the cancelBubble property of the original event to true (IE)
  2988. e.cancelBubble = true;
  2989. },
  2990. stopImmediatePropagation: function() {
  2991. this.isImmediatePropagationStopped = returnTrue;
  2992. this.stopPropagation();
  2993. },
  2994. isDefaultPrevented: returnFalse,
  2995. isPropagationStopped: returnFalse,
  2996. isImmediatePropagationStopped: returnFalse
  2997. };
  2998. // Create mouseenter/leave events using mouseover/out and event-time checks
  2999. jQuery.each({
  3000. mouseenter: "mouseover",
  3001. mouseleave: "mouseout"
  3002. }, function( orig, fix ) {
  3003. jQuery.event.special[ orig ] = {
  3004. delegateType: fix,
  3005. bindType: fix,
  3006. handle: function( event ) {
  3007. var target = this,
  3008. related = event.relatedTarget,
  3009. handleObj = event.handleObj,
  3010. selector = handleObj.selector,
  3011. ret;
  3012. // For mousenter/leave call the handler if related is outside the target.
  3013. // NB: No relatedTarget if the mouse left/entered the browser window
  3014. if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
  3015. event.type = handleObj.origType;
  3016. ret = handleObj.handler.apply( this, arguments );
  3017. event.type = fix;
  3018. }
  3019. return ret;
  3020. }
  3021. };
  3022. });
  3023. // IE submit delegation
  3024. if ( !jQuery.support.submitBubbles ) {
  3025. jQuery.event.special.submit = {
  3026. setup: function() {
  3027. // Only need this for delegated form submit events
  3028. if ( jQuery.nodeName( this, "form" ) ) {
  3029. return false;
  3030. }
  3031. // Lazy-add a submit handler when a descendant form may potentially be submitted
  3032. jQuery.event.add( this, "click._submit keypress._submit", function( e ) {
  3033. // Node name check avoids a VML-related crash in IE (#9807)
  3034. var elem = e.target,
  3035. form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined;
  3036. if ( form && !form._submit_attached ) {
  3037. jQuery.event.add( form, "submit._submit", function( event ) {
  3038. event._submit_bubble = true;
  3039. });
  3040. form._submit_attached = true;
  3041. }
  3042. });
  3043. // return undefined since we don't need an event listener
  3044. },
  3045. postDispatch: function( event ) {
  3046. // If form was submitted by the user, bubble the event up the tree
  3047. if ( event._submit_bubble ) {
  3048. delete event._submit_bubble;
  3049. if ( this.parentNode && !event.isTrigger ) {
  3050. jQuery.event.simulate( "submit", this.parentNode, event, true );
  3051. }
  3052. }
  3053. },
  3054. teardown: function() {
  3055. // Only need this for delegated form submit events
  3056. if ( jQuery.nodeName( this, "form" ) ) {
  3057. return false;
  3058. }
  3059. // Remove delegated handlers; cleanData eventually reaps submit handlers attached above
  3060. jQuery.event.remove( this, "._submit" );
  3061. }
  3062. };
  3063. }
  3064. // IE change delegation and checkbox/radio fix
  3065. if ( !jQuery.support.changeBubbles ) {
  3066. jQuery.event.special.change = {
  3067. setup: function() {
  3068. if ( rformElems.test( this.nodeName ) ) {
  3069. // IE doesn't fire change on a check/radio until blur; trigger it on click
  3070. // after a propertychange. Eat the blur-change in special.change.handle.
  3071. // This still fires onchange a second time for check/radio after blur.
  3072. if ( this.type === "checkbox" || this.type === "radio" ) {
  3073. jQuery.event.add( this, "propertychange._change", function( event ) {
  3074. if ( event.originalEvent.propertyName === "checked" ) {
  3075. this._just_changed = true;
  3076. }
  3077. });
  3078. jQuery.event.add( this, "click._change", function( event ) {
  3079. if ( this._just_changed && !event.isTrigger ) {
  3080. this._just_changed = false;
  3081. jQuery.event.simulate( "change", this, event, true );
  3082. }
  3083. });
  3084. }
  3085. return false;
  3086. }
  3087. // Delegated event; lazy-add a change handler on descendant inputs
  3088. jQuery.event.add( this, "beforeactivate._change", function( e ) {
  3089. var elem = e.target;
  3090. if ( rformElems.test( elem.nodeName ) && !elem._change_attached ) {
  3091. jQuery.event.add( elem, "change._change", function( event ) {
  3092. if ( this.parentNode && !event.isSimulated && !event.isTrigger ) {
  3093. jQuery.event.simulate( "change", this.parentNode, event, true );
  3094. }
  3095. });
  3096. elem._change_attached = true;
  3097. }
  3098. });
  3099. },
  3100. handle: function( event ) {
  3101. var elem = event.target;
  3102. // Swallow native change events from checkbox/radio, we already triggered them above
  3103. if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) {
  3104. return event.handleObj.handler.apply( this, arguments );
  3105. }
  3106. },
  3107. teardown: function() {
  3108. jQuery.event.remove( this, "._change" );
  3109. return rformElems.test( this.nodeName );
  3110. }
  3111. };
  3112. }
  3113. // Create "bubbling" focus and blur events
  3114. if ( !jQuery.support.focusinBubbles ) {
  3115. jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
  3116. // Attach a single capturing handler while someone wants focusin/focusout
  3117. var attaches = 0,
  3118. handler = function( event ) {
  3119. jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
  3120. };
  3121. jQuery.event.special[ fix ] = {
  3122. setup: function() {
  3123. if ( attaches++ === 0 ) {
  3124. document.addEventListener( orig, handler, true );
  3125. }
  3126. },
  3127. teardown: function() {
  3128. if ( --attaches === 0 ) {
  3129. document.removeEventListener( orig, handler, true );
  3130. }
  3131. }
  3132. };
  3133. });
  3134. }
  3135. jQuery.fn.extend({
  3136. on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
  3137. var origFn, type;
  3138. // Types can be a map of types/handlers
  3139. if ( typeof types === "object" ) {
  3140. // ( types-Object, selector, data )
  3141. if ( typeof selector !== "string" ) { // && selector != null
  3142. // ( types-Object, data )
  3143. data = data || selector;
  3144. selector = undefined;
  3145. }
  3146. for ( type in types ) {
  3147. this.on( type, selector, data, types[ type ], one );
  3148. }
  3149. return this;
  3150. }
  3151. if ( data == null && fn == null ) {
  3152. // ( types, fn )
  3153. fn = selector;
  3154. data = selector = undefined;
  3155. } else if ( fn == null ) {
  3156. if ( typeof selector === "string" ) {
  3157. // ( types, selector, fn )
  3158. fn = data;
  3159. data = undefined;
  3160. } else {
  3161. // ( types, data, fn )
  3162. fn = data;
  3163. data = selector;
  3164. selector = undefined;
  3165. }
  3166. }
  3167. if ( fn === false ) {
  3168. fn = returnFalse;
  3169. } else if ( !fn ) {
  3170. return this;
  3171. }
  3172. if ( one === 1 ) {
  3173. origFn = fn;
  3174. fn = function( event ) {
  3175. // Can use an empty set, since event contains the info
  3176. jQuery().off( event );
  3177. return origFn.apply( this, arguments );
  3178. };
  3179. // Use same guid so caller can remove using origFn
  3180. fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
  3181. }
  3182. return this.each( function() {
  3183. jQuery.event.add( this, types, fn, data, selector );
  3184. });
  3185. },
  3186. one: function( types, selector, data, fn ) {
  3187. return this.on( types, selector, data, fn, 1 );
  3188. },
  3189. off: function( types, selector, fn ) {
  3190. if ( types && types.preventDefault && types.handleObj ) {
  3191. // ( event ) dispatched jQuery.Event
  3192. var handleObj = types.handleObj;
  3193. jQuery( types.delegateTarget ).off(
  3194. handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
  3195. handleObj.selector,
  3196. handleObj.handler
  3197. );
  3198. return this;
  3199. }
  3200. if ( typeof types === "object" ) {
  3201. // ( types-object [, selector] )
  3202. for ( var type in types ) {
  3203. this.off( type, selector, types[ type ] );
  3204. }
  3205. return this;
  3206. }
  3207. if ( selector === false || typeof selector === "function" ) {
  3208. // ( types [, fn] )
  3209. fn = selector;
  3210. selector = undefined;
  3211. }
  3212. if ( fn === false ) {
  3213. fn = returnFalse;
  3214. }
  3215. return this.each(function() {
  3216. jQuery.event.remove( this, types, fn, selector );
  3217. });
  3218. },
  3219. bind: function( types, data, fn ) {
  3220. return this.on( types, null, data, fn );
  3221. },
  3222. unbind: function( types, fn ) {
  3223. return this.off( types, null, fn );
  3224. },
  3225. live: function( types, data, fn ) {
  3226. jQuery( this.context ).on( types, this.selector, data, fn );
  3227. return this;
  3228. },
  3229. die: function( types, fn ) {
  3230. jQuery( this.context ).off( types, this.selector || "**", fn );
  3231. return this;
  3232. },
  3233. delegate: function( selector, types, data, fn ) {
  3234. return this.on( types, selector, data, fn );
  3235. },
  3236. undelegate: function( selector, types, fn ) {
  3237. // ( namespace ) or ( selector, types [, fn] )
  3238. return arguments.length == 1? this.off( selector, "**" ) : this.off( types, selector, fn );
  3239. },
  3240. trigger: function( type, data ) {
  3241. return this.each(function() {
  3242. jQuery.event.trigger( type, data, this );
  3243. });
  3244. },
  3245. triggerHandler: function( type, data ) {
  3246. if ( this[0] ) {
  3247. return jQuery.event.trigger( type, data, this[0], true );
  3248. }
  3249. },
  3250. toggle: function( fn ) {
  3251. // Save reference to arguments for access in closure
  3252. var args = arguments,
  3253. guid = fn.guid || jQuery.guid++,
  3254. i = 0,
  3255. toggler = function( event ) {
  3256. // Figure out which function to execute
  3257. var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i;
  3258. jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 );
  3259. // Make sure that clicks stop
  3260. event.preventDefault();
  3261. // and execute the function
  3262. return args[ lastToggle ].apply( this, arguments ) || false;
  3263. };
  3264. // link all the functions, so any of them can unbind this click handler
  3265. toggler.guid = guid;
  3266. while ( i < args.length ) {
  3267. args[ i++ ].guid = guid;
  3268. }
  3269. return this.click( toggler );
  3270. },
  3271. hover: function( fnOver, fnOut ) {
  3272. return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
  3273. }
  3274. });
  3275. jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
  3276. "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
  3277. "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {
  3278. // Handle event binding
  3279. jQuery.fn[ name ] = function( data, fn ) {
  3280. if ( fn == null ) {
  3281. fn = data;
  3282. data = null;
  3283. }
  3284. return arguments.length > 0 ?
  3285. this.on( name, null, data, fn ) :
  3286. this.trigger( name );
  3287. };
  3288. if ( jQuery.attrFn ) {
  3289. jQuery.attrFn[ name ] = true;
  3290. }
  3291. if ( rkeyEvent.test( name ) ) {
  3292. jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks;
  3293. }
  3294. if ( rmouseEvent.test( name ) ) {
  3295. jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks;
  3296. }
  3297. });
  3298. /*!
  3299. * Sizzle CSS Selector Engine
  3300. * Copyright 2011, The Dojo Foundation
  3301. * Released under the MIT, BSD, and GPL Licenses.
  3302. * More information: http://sizzlejs.com/
  3303. */
  3304. (function(){
  3305. var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
  3306. expando = "sizcache" + (Math.random() + '').replace('.', ''),
  3307. done = 0,
  3308. toString = Object.prototype.toString,
  3309. hasDuplicate = false,
  3310. baseHasDuplicate = true,
  3311. rBackslash = /\\/g,
  3312. rReturn = /\r\n/g,
  3313. rNonWord = /\W/;
  3314. // Here we check if the JavaScript engine is using some sort of
  3315. // optimization where it does not always call our comparision
  3316. // function. If that is the case, discard the hasDuplicate value.
  3317. // Thus far that includes Google Chrome.
  3318. [0, 0].sort(function() {
  3319. baseHasDuplicate = false;
  3320. return 0;
  3321. });
  3322. var Sizzle = function( selector, context, results, seed ) {
  3323. results = results || [];
  3324. context = context || document;
  3325. var origContext = context;
  3326. if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
  3327. return [];
  3328. }
  3329. if ( !selector || typeof selector !== "string" ) {
  3330. return results;
  3331. }
  3332. var m, set, checkSet, extra, ret, cur, pop, i,
  3333. prune = true,
  3334. contextXML = Sizzle.isXML( context ),
  3335. parts = [],
  3336. soFar = selector;
  3337. // Reset the position of the chunker regexp (start from head)
  3338. do {
  3339. chunker.exec( "" );
  3340. m = chunker.exec( soFar );
  3341. if ( m ) {
  3342. soFar = m[3];
  3343. parts.push( m[1] );
  3344. if ( m[2] ) {
  3345. extra = m[3];
  3346. break;
  3347. }
  3348. }
  3349. } while ( m );
  3350. if ( parts.length > 1 && origPOS.exec( selector ) ) {
  3351. if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
  3352. set = posProcess( parts[0] + parts[1], context, seed );
  3353. } else {
  3354. set = Expr.relative[ parts[0] ] ?
  3355. [ context ] :
  3356. Sizzle( parts.shift(), context );
  3357. while ( parts.length ) {
  3358. selector = parts.shift();
  3359. if ( Expr.relative[ selector ] ) {
  3360. selector += parts.shift();
  3361. }
  3362. set = posProcess( selector, set, seed );
  3363. }
  3364. }
  3365. } else {
  3366. // Take a shortcut and set the context if the root selector is an ID
  3367. // (but not if it'll be faster if the inner selector is an ID)
  3368. if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
  3369. Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
  3370. ret = Sizzle.find( parts.shift(), context, contextXML );
  3371. context = ret.expr ?
  3372. Sizzle.filter( ret.expr, ret.set )[0] :
  3373. ret.set[0];
  3374. }
  3375. if ( context ) {
  3376. ret = seed ?
  3377. { expr: parts.pop(), set: makeArray(seed) } :
  3378. Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
  3379. set = ret.expr ?
  3380. Sizzle.filter( ret.expr, ret.set ) :
  3381. ret.set;
  3382. if ( parts.length > 0 ) {
  3383. checkSet = makeArray( set );
  3384. } else {
  3385. prune = false;
  3386. }
  3387. while ( parts.length ) {
  3388. cur = parts.pop();
  3389. pop = cur;
  3390. if ( !Expr.relative[ cur ] ) {
  3391. cur = "";
  3392. } else {
  3393. pop = parts.pop();
  3394. }
  3395. if ( pop == null ) {
  3396. pop = context;
  3397. }
  3398. Expr.relative[ cur ]( checkSet, pop, contextXML );
  3399. }
  3400. } else {
  3401. checkSet = parts = [];
  3402. }
  3403. }
  3404. if ( !checkSet ) {
  3405. checkSet = set;
  3406. }
  3407. if ( !checkSet ) {
  3408. Sizzle.error( cur || selector );
  3409. }
  3410. if ( toString.call(checkSet) === "[object Array]" ) {
  3411. if ( !prune ) {
  3412. results.push.apply( results, checkSet );
  3413. } else if ( context && context.nodeType === 1 ) {
  3414. for ( i = 0; checkSet[i] != null; i++ ) {
  3415. if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {
  3416. results.push( set[i] );
  3417. }
  3418. }
  3419. } else {
  3420. for ( i = 0; checkSet[i] != null; i++ ) {
  3421. if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
  3422. results.push( set[i] );
  3423. }
  3424. }
  3425. }
  3426. } else {
  3427. makeArray( checkSet, results );
  3428. }
  3429. if ( extra ) {
  3430. Sizzle( extra, origContext, results, seed );
  3431. Sizzle.uniqueSort( results );
  3432. }
  3433. return results;
  3434. };
  3435. Sizzle.uniqueSort = function( results ) {
  3436. if ( sortOrder ) {
  3437. hasDuplicate = baseHasDuplicate;
  3438. results.sort( sortOrder );
  3439. if ( hasDuplicate ) {
  3440. for ( var i = 1; i < results.length; i++ ) {
  3441. if ( results[i] === results[ i - 1 ] ) {
  3442. results.splice( i--, 1 );
  3443. }
  3444. }
  3445. }
  3446. }
  3447. return results;
  3448. };
  3449. Sizzle.matches = function( expr, set ) {
  3450. return Sizzle( expr, null, null, set );
  3451. };
  3452. Sizzle.matchesSelector = function( node, expr ) {
  3453. return Sizzle( expr, null, null, [node] ).length > 0;
  3454. };
  3455. Sizzle.find = function( expr, context, isXML ) {
  3456. var set, i, len, match, type, left;
  3457. if ( !expr ) {
  3458. return [];
  3459. }
  3460. for ( i = 0, len = Expr.order.length; i < len; i++ ) {
  3461. type = Expr.order[i];
  3462. if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
  3463. left = match[1];
  3464. match.splice( 1, 1 );
  3465. if ( left.substr( left.length - 1 ) !== "\\" ) {
  3466. match[1] = (match[1] || "").replace( rBackslash, "" );
  3467. set = Expr.find[ type ]( match, context, isXML );
  3468. if ( set != null ) {
  3469. expr = expr.replace( Expr.match[ type ], "" );
  3470. break;
  3471. }
  3472. }
  3473. }
  3474. }
  3475. if ( !set ) {
  3476. set = typeof context.getElementsByTagName !== "undefined" ?
  3477. context.getElementsByTagName( "*" ) :
  3478. [];
  3479. }
  3480. return { set: set, expr: expr };
  3481. };
  3482. Sizzle.filter = function( expr, set, inplace, not ) {
  3483. var match, anyFound,
  3484. type, found, item, filter, left,
  3485. i, pass,
  3486. old = expr,
  3487. result = [],
  3488. curLoop = set,
  3489. isXMLFilter = set && set[0] && Sizzle.isXML( set[0] );
  3490. while ( expr && set.length ) {
  3491. for ( type in Expr.filter ) {
  3492. if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
  3493. filter = Expr.filter[ type ];
  3494. left = match[1];
  3495. anyFound = false;
  3496. match.splice(1,1);
  3497. if ( left.substr( left.length - 1 ) === "\\" ) {
  3498. continue;
  3499. }
  3500. if ( curLoop === result ) {
  3501. result = [];
  3502. }
  3503. if ( Expr.preFilter[ type ] ) {
  3504. match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
  3505. if ( !match ) {
  3506. anyFound = found = true;
  3507. } else if ( match === true ) {
  3508. continue;
  3509. }
  3510. }
  3511. if ( match ) {
  3512. for ( i = 0; (item = curLoop[i]) != null; i++ ) {
  3513. if ( item ) {
  3514. found = filter( item, match, i, curLoop );
  3515. pass = not ^ found;
  3516. if ( inplace && found != null ) {
  3517. if ( pass ) {
  3518. anyFound = true;
  3519. } else {
  3520. curLoop[i] = false;
  3521. }
  3522. } else if ( pass ) {
  3523. result.push( item );
  3524. anyFound = true;
  3525. }
  3526. }
  3527. }
  3528. }
  3529. if ( found !== undefined ) {
  3530. if ( !inplace ) {
  3531. curLoop = result;
  3532. }
  3533. expr = expr.replace( Expr.match[ type ], "" );
  3534. if ( !anyFound ) {
  3535. return [];
  3536. }
  3537. break;
  3538. }
  3539. }
  3540. }
  3541. // Improper expression
  3542. if ( expr === old ) {
  3543. if ( anyFound == null ) {
  3544. Sizzle.error( expr );
  3545. } else {
  3546. break;
  3547. }
  3548. }
  3549. old = expr;
  3550. }
  3551. return curLoop;
  3552. };
  3553. Sizzle.error = function( msg ) {
  3554. throw new Error( "Syntax error, unrecognized expression: " + msg );
  3555. };
  3556. /**
  3557. * Utility function for retreiving the text value of an array of DOM nodes
  3558. * @param {Array|Element} elem
  3559. */
  3560. var getText = Sizzle.getText = function( elem ) {
  3561. var i, node,
  3562. nodeType = elem.nodeType,
  3563. ret = "";
  3564. if ( nodeType ) {
  3565. if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
  3566. // Use textContent || innerText for elements
  3567. if ( typeof elem.textContent === 'string' ) {
  3568. return elem.textContent;
  3569. } else if ( typeof elem.innerText === 'string' ) {
  3570. // Replace IE's carriage returns
  3571. return elem.innerText.replace( rReturn, '' );
  3572. } else {
  3573. // Traverse it's children
  3574. for ( elem = elem.firstChild; elem; elem = elem.nextSibling) {
  3575. ret += getText( elem );
  3576. }
  3577. }
  3578. } else if ( nodeType === 3 || nodeType === 4 ) {
  3579. return elem.nodeValue;
  3580. }
  3581. } else {
  3582. // If no nodeType, this is expected to be an array
  3583. for ( i = 0; (node = elem[i]); i++ ) {
  3584. // Do not traverse comment nodes
  3585. if ( node.nodeType !== 8 ) {
  3586. ret += getText( node );
  3587. }
  3588. }
  3589. }
  3590. return ret;
  3591. };
  3592. var Expr = Sizzle.selectors = {
  3593. order: [ "ID", "NAME", "TAG" ],
  3594. match: {
  3595. ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
  3596. CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
  3597. NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,
  3598. ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,
  3599. TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,
  3600. CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,
  3601. POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,
  3602. PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
  3603. },
  3604. leftMatch: {},
  3605. attrMap: {
  3606. "class": "className",
  3607. "for": "htmlFor"
  3608. },
  3609. attrHandle: {
  3610. href: function( elem ) {
  3611. return elem.getAttribute( "href" );
  3612. },
  3613. type: function( elem ) {
  3614. return elem.getAttribute( "type" );
  3615. }
  3616. },
  3617. relative: {
  3618. "+": function(checkSet, part){
  3619. var isPartStr = typeof part === "string",
  3620. isTag = isPartStr && !rNonWord.test( part ),
  3621. isPartStrNotTag = isPartStr && !isTag;
  3622. if ( isTag ) {
  3623. part = part.toLowerCase();
  3624. }
  3625. for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
  3626. if ( (elem = checkSet[i]) ) {
  3627. while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
  3628. checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?
  3629. elem || false :
  3630. elem === part;
  3631. }
  3632. }
  3633. if ( isPartStrNotTag ) {
  3634. Sizzle.filter( part, checkSet, true );
  3635. }
  3636. },
  3637. ">": function( checkSet, part ) {
  3638. var elem,
  3639. isPartStr = typeof part === "string",
  3640. i = 0,
  3641. l = checkSet.length;
  3642. if ( isPartStr && !rNonWord.test( part ) ) {
  3643. part = part.toLowerCase();
  3644. for ( ; i < l; i++ ) {
  3645. elem = checkSet[i];
  3646. if ( elem ) {
  3647. var parent = elem.parentNode;
  3648. checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
  3649. }
  3650. }
  3651. } else {
  3652. for ( ; i < l; i++ ) {
  3653. elem = checkSet[i];
  3654. if ( elem ) {
  3655. checkSet[i] = isPartStr ?
  3656. elem.parentNode :
  3657. elem.parentNode === part;
  3658. }
  3659. }
  3660. if ( isPartStr ) {
  3661. Sizzle.filter( part, checkSet, true );
  3662. }
  3663. }
  3664. },
  3665. "": function(checkSet, part, isXML){
  3666. var nodeCheck,
  3667. doneName = done++,
  3668. checkFn = dirCheck;
  3669. if ( typeof part === "string" && !rNonWord.test( part ) ) {
  3670. part = part.toLowerCase();
  3671. nodeCheck = part;
  3672. checkFn = dirNodeCheck;
  3673. }
  3674. checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML );
  3675. },
  3676. "~": function( checkSet, part, isXML ) {
  3677. var nodeCheck,
  3678. doneName = done++,
  3679. checkFn = dirCheck;
  3680. if ( typeof part === "string" && !rNonWord.test( part ) ) {
  3681. part = part.toLowerCase();
  3682. nodeCheck = part;
  3683. checkFn = dirNodeCheck;
  3684. }
  3685. checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML );
  3686. }
  3687. },
  3688. find: {
  3689. ID: function( match, context, isXML ) {
  3690. if ( typeof context.getElementById !== "undefined" && !isXML ) {
  3691. var m = context.getElementById(match[1]);
  3692. // Check parentNode to catch when Blackberry 4.6 returns
  3693. // nodes that are no longer in the document #6963
  3694. return m && m.parentNode ? [m] : [];
  3695. }
  3696. },
  3697. NAME: function( match, context ) {
  3698. if ( typeof context.getElementsByName !== "undefined" ) {
  3699. var ret = [],
  3700. results = context.getElementsByName( match[1] );
  3701. for ( var i = 0, l = results.length; i < l; i++ ) {
  3702. if ( results[i].getAttribute("name") === match[1] ) {
  3703. ret.push( results[i] );
  3704. }
  3705. }
  3706. return ret.length === 0 ? null : ret;
  3707. }
  3708. },
  3709. TAG: function( match, context ) {
  3710. if ( typeof context.getElementsByTagName !== "undefined" ) {
  3711. return context.getElementsByTagName( match[1] );
  3712. }
  3713. }
  3714. },
  3715. preFilter: {
  3716. CLASS: function( match, curLoop, inplace, result, not, isXML ) {
  3717. match = " " + match[1].replace( rBackslash, "" ) + " ";
  3718. if ( isXML ) {
  3719. return match;
  3720. }
  3721. for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
  3722. if ( elem ) {
  3723. if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) {
  3724. if ( !inplace ) {
  3725. result.push( elem );
  3726. }
  3727. } else if ( inplace ) {
  3728. curLoop[i] = false;
  3729. }
  3730. }
  3731. }
  3732. return false;
  3733. },
  3734. ID: function( match ) {
  3735. return match[1].replace( rBackslash, "" );
  3736. },
  3737. TAG: function( match, curLoop ) {
  3738. return match[1].replace( rBackslash, "" ).toLowerCase();
  3739. },
  3740. CHILD: function( match ) {
  3741. if ( match[1] === "nth" ) {
  3742. if ( !match[2] ) {
  3743. Sizzle.error( match[0] );
  3744. }
  3745. match[2] = match[2].replace(/^\+|\s*/g, '');
  3746. // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
  3747. var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec(
  3748. match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||
  3749. !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
  3750. // calculate the numbers (first)n+(last) including if they are negative
  3751. match[2] = (test[1] + (test[2] || 1)) - 0;
  3752. match[3] = test[3] - 0;
  3753. }
  3754. else if ( match[2] ) {
  3755. Sizzle.error( match[0] );
  3756. }
  3757. // TODO: Move to normal caching system
  3758. match[0] = done++;
  3759. return match;
  3760. },
  3761. ATTR: function( match, curLoop, inplace, result, not, isXML ) {
  3762. var name = match[1] = match[1].replace( rBackslash, "" );
  3763. if ( !isXML && Expr.attrMap[name] ) {
  3764. match[1] = Expr.attrMap[name];
  3765. }
  3766. // Handle if an un-quoted value was used
  3767. match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" );
  3768. if ( match[2] === "~=" ) {
  3769. match[4] = " " + match[4] + " ";
  3770. }
  3771. return match;
  3772. },
  3773. PSEUDO: function( match, curLoop, inplace, result, not ) {
  3774. if ( match[1] === "not" ) {
  3775. // If we're dealing with a complex expression, or a simple one
  3776. if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
  3777. match[3] = Sizzle(match[3], null, null, curLoop);
  3778. } else {
  3779. var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
  3780. if ( !inplace ) {
  3781. result.push.apply( result, ret );
  3782. }
  3783. return false;
  3784. }
  3785. } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
  3786. return true;
  3787. }
  3788. return match;
  3789. },
  3790. POS: function( match ) {
  3791. match.unshift( true );
  3792. return match;
  3793. }
  3794. },
  3795. filters: {
  3796. enabled: function( elem ) {
  3797. return elem.disabled === false && elem.type !== "hidden";
  3798. },
  3799. disabled: function( elem ) {
  3800. return elem.disabled === true;
  3801. },
  3802. checked: function( elem ) {
  3803. return elem.checked === true;
  3804. },
  3805. selected: function( elem ) {
  3806. // Accessing this property makes selected-by-default
  3807. // options in Safari work properly
  3808. if ( elem.parentNode ) {
  3809. elem.parentNode.selectedIndex;
  3810. }
  3811. return elem.selected === true;
  3812. },
  3813. parent: function( elem ) {
  3814. return !!elem.firstChild;
  3815. },
  3816. empty: function( elem ) {
  3817. return !elem.firstChild;
  3818. },
  3819. has: function( elem, i, match ) {
  3820. return !!Sizzle( match[3], elem ).length;
  3821. },
  3822. header: function( elem ) {
  3823. return (/h\d/i).test( elem.nodeName );
  3824. },
  3825. text: function( elem ) {
  3826. var attr = elem.getAttribute( "type" ), type = elem.type;
  3827. // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
  3828. // use getAttribute instead to test this case
  3829. return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null );
  3830. },
  3831. radio: function( elem ) {
  3832. return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type;
  3833. },
  3834. checkbox: function( elem ) {
  3835. return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type;
  3836. },
  3837. file: function( elem ) {
  3838. return elem.nodeName.toLowerCase() === "input" && "file" === elem.type;
  3839. },
  3840. password: function( elem ) {
  3841. return elem.nodeName.toLowerCase() === "input" && "password" === elem.type;
  3842. },
  3843. submit: function( elem ) {
  3844. var name = elem.nodeName.toLowerCase();
  3845. return (name === "input" || name === "button") && "submit" === elem.type;
  3846. },
  3847. image: function( elem ) {
  3848. return elem.nodeName.toLowerCase() === "input" && "image" === elem.type;
  3849. },
  3850. reset: function( elem ) {
  3851. var name = elem.nodeName.toLowerCase();
  3852. return (name === "input" || name === "button") && "reset" === elem.type;
  3853. },
  3854. button: function( elem ) {
  3855. var name = elem.nodeName.toLowerCase();
  3856. return name === "input" && "button" === elem.type || name === "button";
  3857. },
  3858. input: function( elem ) {
  3859. return (/input|select|textarea|button/i).test( elem.nodeName );
  3860. },
  3861. focus: function( elem ) {
  3862. return elem === elem.ownerDocument.activeElement;
  3863. }
  3864. },
  3865. setFilters: {
  3866. first: function( elem, i ) {
  3867. return i === 0;
  3868. },
  3869. last: function( elem, i, match, array ) {
  3870. return i === array.length - 1;
  3871. },
  3872. even: function( elem, i ) {
  3873. return i % 2 === 0;
  3874. },
  3875. odd: function( elem, i ) {
  3876. return i % 2 === 1;
  3877. },
  3878. lt: function( elem, i, match ) {
  3879. return i < match[3] - 0;
  3880. },
  3881. gt: function( elem, i, match ) {
  3882. return i > match[3] - 0;
  3883. },
  3884. nth: function( elem, i, match ) {
  3885. return match[3] - 0 === i;
  3886. },
  3887. eq: function( elem, i, match ) {
  3888. return match[3] - 0 === i;
  3889. }
  3890. },
  3891. filter: {
  3892. PSEUDO: function( elem, match, i, array ) {
  3893. var name = match[1],
  3894. filter = Expr.filters[ name ];
  3895. if ( filter ) {
  3896. return filter( elem, i, match, array );
  3897. } else if ( name === "contains" ) {
  3898. return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0;
  3899. } else if ( name === "not" ) {
  3900. var not = match[3];
  3901. for ( var j = 0, l = not.length; j < l; j++ ) {
  3902. if ( not[j] === elem ) {
  3903. return false;
  3904. }
  3905. }
  3906. return true;
  3907. } else {
  3908. Sizzle.error( name );
  3909. }
  3910. },
  3911. CHILD: function( elem, match ) {
  3912. var first, last,
  3913. doneName, parent, cache,
  3914. count, diff,
  3915. type = match[1],
  3916. node = elem;
  3917. switch ( type ) {
  3918. case "only":
  3919. case "first":
  3920. while ( (node = node.previousSibling) ) {
  3921. if ( node.nodeType === 1 ) {
  3922. return false;
  3923. }
  3924. }
  3925. if ( type === "first" ) {
  3926. return true;
  3927. }
  3928. node = elem;
  3929. /* falls through */
  3930. case "last":
  3931. while ( (node = node.nextSibling) ) {
  3932. if ( node.nodeType === 1 ) {
  3933. return false;
  3934. }
  3935. }
  3936. return true;
  3937. case "nth":
  3938. first = match[2];
  3939. last = match[3];
  3940. if ( first === 1 && last === 0 ) {
  3941. return true;
  3942. }
  3943. doneName = match[0];
  3944. parent = elem.parentNode;
  3945. if ( parent && (parent[ expando ] !== doneName || !elem.nodeIndex) ) {
  3946. count = 0;
  3947. for ( node = parent.firstChild; node; node = node.nextSibling ) {
  3948. if ( node.nodeType === 1 ) {
  3949. node.nodeIndex = ++count;
  3950. }
  3951. }
  3952. parent[ expando ] = doneName;
  3953. }
  3954. diff = elem.nodeIndex - last;
  3955. if ( first === 0 ) {
  3956. return diff === 0;
  3957. } else {
  3958. return ( diff % first === 0 && diff / first >= 0 );
  3959. }
  3960. }
  3961. },
  3962. ID: function( elem, match ) {
  3963. return elem.nodeType === 1 && elem.getAttribute("id") === match;
  3964. },
  3965. TAG: function( elem, match ) {
  3966. return (match === "*" && elem.nodeType === 1) || !!elem.nodeName && elem.nodeName.toLowerCase() === match;
  3967. },
  3968. CLASS: function( elem, match ) {
  3969. return (" " + (elem.className || elem.getAttribute("class")) + " ")
  3970. .indexOf( match ) > -1;
  3971. },
  3972. ATTR: function( elem, match ) {
  3973. var name = match[1],
  3974. result = Sizzle.attr ?
  3975. Sizzle.attr( elem, name ) :
  3976. Expr.attrHandle[ name ] ?
  3977. Expr.attrHandle[ name ]( elem ) :
  3978. elem[ name ] != null ?
  3979. elem[ name ] :
  3980. elem.getAttribute( name ),
  3981. value = result + "",
  3982. type = match[2],
  3983. check = match[4];
  3984. return result == null ?
  3985. type === "!=" :
  3986. !type && Sizzle.attr ?
  3987. result != null :
  3988. type === "=" ?
  3989. value === check :
  3990. type === "*=" ?
  3991. value.indexOf(check) >= 0 :
  3992. type === "~=" ?
  3993. (" " + value + " ").indexOf(check) >= 0 :
  3994. !check ?
  3995. value && result !== false :
  3996. type === "!=" ?
  3997. value !== check :
  3998. type === "^=" ?
  3999. value.indexOf(check) === 0 :
  4000. type === "$=" ?
  4001. value.substr(value.length - check.length) === check :
  4002. type === "|=" ?
  4003. value === check || value.substr(0, check.length + 1) === check + "-" :
  4004. false;
  4005. },
  4006. POS: function( elem, match, i, array ) {
  4007. var name = match[2],
  4008. filter = Expr.setFilters[ name ];
  4009. if ( filter ) {
  4010. return filter( elem, i, match, array );
  4011. }
  4012. }
  4013. }
  4014. };
  4015. var origPOS = Expr.match.POS,
  4016. fescape = function(all, num){
  4017. return "\\" + (num - 0 + 1);
  4018. };
  4019. for ( var type in Expr.match ) {
  4020. Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) );
  4021. Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) );
  4022. }
  4023. // Expose origPOS
  4024. // "global" as in regardless of relation to brackets/parens
  4025. Expr.match.globalPOS = origPOS;
  4026. var makeArray = function( array, results ) {
  4027. array = Array.prototype.slice.call( array, 0 );
  4028. if ( results ) {
  4029. results.push.apply( results, array );
  4030. return results;
  4031. }
  4032. return array;
  4033. };
  4034. // Perform a simple check to determine if the browser is capable of
  4035. // converting a NodeList to an array using builtin methods.
  4036. // Also verifies that the returned array holds DOM nodes
  4037. // (which is not the case in the Blackberry browser)
  4038. try {
  4039. Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;
  4040. // Provide a fallback method if it does not work
  4041. } catch( e ) {
  4042. makeArray = function( array, results ) {
  4043. var i = 0,
  4044. ret = results || [];
  4045. if ( toString.call(array) === "[object Array]" ) {
  4046. Array.prototype.push.apply( ret, array );
  4047. } else {
  4048. if ( typeof array.length === "number" ) {
  4049. for ( var l = array.length; i < l; i++ ) {
  4050. ret.push( array[i] );
  4051. }
  4052. } else {
  4053. for ( ; array[i]; i++ ) {
  4054. ret.push( array[i] );
  4055. }
  4056. }
  4057. }
  4058. return ret;
  4059. };
  4060. }
  4061. var sortOrder, siblingCheck;
  4062. if ( document.documentElement.compareDocumentPosition ) {
  4063. sortOrder = function( a, b ) {
  4064. if ( a === b ) {
  4065. hasDuplicate = true;
  4066. return 0;
  4067. }
  4068. if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
  4069. return a.compareDocumentPosition ? -1 : 1;
  4070. }
  4071. return a.compareDocumentPosition(b) & 4 ? -1 : 1;
  4072. };
  4073. } else {
  4074. sortOrder = function( a, b ) {
  4075. // The nodes are identical, we can exit early
  4076. if ( a === b ) {
  4077. hasDuplicate = true;
  4078. return 0;
  4079. // Fallback to using sourceIndex (in IE) if it's available on both nodes
  4080. } else if ( a.sourceIndex && b.sourceIndex ) {
  4081. return a.sourceIndex - b.sourceIndex;
  4082. }
  4083. var al, bl,
  4084. ap = [],
  4085. bp = [],
  4086. aup = a.parentNode,
  4087. bup = b.parentNode,
  4088. cur = aup;
  4089. // If the nodes are siblings (or identical) we can do a quick check
  4090. if ( aup === bup ) {
  4091. return siblingCheck( a, b );
  4092. // If no parents were found then the nodes are disconnected
  4093. } else if ( !aup ) {
  4094. return -1;
  4095. } else if ( !bup ) {
  4096. return 1;
  4097. }
  4098. // Otherwise they're somewhere else in the tree so we need
  4099. // to build up a full list of the parentNodes for comparison
  4100. while ( cur ) {
  4101. ap.unshift( cur );
  4102. cur = cur.parentNode;
  4103. }
  4104. cur = bup;
  4105. while ( cur ) {
  4106. bp.unshift( cur );
  4107. cur = cur.parentNode;
  4108. }
  4109. al = ap.length;
  4110. bl = bp.length;
  4111. // Start walking down the tree looking for a discrepancy
  4112. for ( var i = 0; i < al && i < bl; i++ ) {
  4113. if ( ap[i] !== bp[i] ) {
  4114. return siblingCheck( ap[i], bp[i] );
  4115. }
  4116. }
  4117. // We ended someplace up the tree so do a sibling check
  4118. return i === al ?
  4119. siblingCheck( a, bp[i], -1 ) :
  4120. siblingCheck( ap[i], b, 1 );
  4121. };
  4122. siblingCheck = function( a, b, ret ) {
  4123. if ( a === b ) {
  4124. return ret;
  4125. }
  4126. var cur = a.nextSibling;
  4127. while ( cur ) {
  4128. if ( cur === b ) {
  4129. return -1;
  4130. }
  4131. cur = cur.nextSibling;
  4132. }
  4133. return 1;
  4134. };
  4135. }
  4136. // Check to see if the browser returns elements by name when
  4137. // querying by getElementById (and provide a workaround)
  4138. (function(){
  4139. // We're going to inject a fake input element with a specified name
  4140. var form = document.createElement("div"),
  4141. id = "script" + (new Date()).getTime(),
  4142. root = document.documentElement;
  4143. form.innerHTML = "<a name='" + id + "'/>";
  4144. // Inject it into the root element, check its status, and remove it quickly
  4145. root.insertBefore( form, root.firstChild );
  4146. // The workaround has to do additional checks after a getElementById
  4147. // Which slows things down for other browsers (hence the branching)
  4148. if ( document.getElementById( id ) ) {
  4149. Expr.find.ID = function( match, context, isXML ) {
  4150. if ( typeof context.getElementById !== "undefined" && !isXML ) {
  4151. var m = context.getElementById(match[1]);
  4152. return m ?
  4153. m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ?
  4154. [m] :
  4155. undefined :
  4156. [];
  4157. }
  4158. };
  4159. Expr.filter.ID = function( elem, match ) {
  4160. var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
  4161. return elem.nodeType === 1 && node && node.nodeValue === match;
  4162. };
  4163. }
  4164. root.removeChild( form );
  4165. // release memory in IE
  4166. root = form = null;
  4167. })();
  4168. (function(){
  4169. // Check to see if the browser returns only elements
  4170. // when doing getElementsByTagName("*")
  4171. // Create a fake element
  4172. var div = document.createElement("div");
  4173. div.appendChild( document.createComment("") );
  4174. // Make sure no comments are found
  4175. if ( div.getElementsByTagName("*").length > 0 ) {
  4176. Expr.find.TAG = function( match, context ) {
  4177. var results = context.getElementsByTagName( match[1] );
  4178. // Filter out possible comments
  4179. if ( match[1] === "*" ) {
  4180. var tmp = [];
  4181. for ( var i = 0; results[i]; i++ ) {
  4182. if ( results[i].nodeType === 1 ) {
  4183. tmp.push( results[i] );
  4184. }
  4185. }
  4186. results = tmp;
  4187. }
  4188. return results;
  4189. };
  4190. }
  4191. // Check to see if an attribute returns normalized href attributes
  4192. div.innerHTML = "<a href='#'></a>";
  4193. if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
  4194. div.firstChild.getAttribute("href") !== "#" ) {
  4195. Expr.attrHandle.href = function( elem ) {
  4196. return elem.getAttribute( "href", 2 );
  4197. };
  4198. }
  4199. // release memory in IE
  4200. div = null;
  4201. })();
  4202. if ( document.querySelectorAll ) {
  4203. (function(){
  4204. var oldSizzle = Sizzle,
  4205. div = document.createElement("div"),
  4206. id = "__sizzle__";
  4207. div.innerHTML = "<p class='TEST'></p>";
  4208. // Safari can't handle uppercase or unicode characters when
  4209. // in quirks mode.
  4210. if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
  4211. return;
  4212. }
  4213. Sizzle = function( query, context, extra, seed ) {
  4214. context = context || document;
  4215. // Only use querySelectorAll on non-XML documents
  4216. // (ID selectors don't work in non-HTML documents)
  4217. if ( !seed && !Sizzle.isXML(context) ) {
  4218. // See if we find a selector to speed up
  4219. var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query );
  4220. if ( match && (context.nodeType === 1 || context.nodeType === 9) ) {
  4221. // Speed-up: Sizzle("TAG")
  4222. if ( match[1] ) {
  4223. return makeArray( context.getElementsByTagName( query ), extra );
  4224. // Speed-up: Sizzle(".CLASS")
  4225. } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) {
  4226. return makeArray( context.getElementsByClassName( match[2] ), extra );
  4227. }
  4228. }
  4229. if ( context.nodeType === 9 ) {
  4230. // Speed-up: Sizzle("body")
  4231. // The body element only exists once, optimize finding it
  4232. if ( query === "body" && context.body ) {
  4233. return makeArray( [ context.body ], extra );
  4234. // Speed-up: Sizzle("#ID")
  4235. } else if ( match && match[3] ) {
  4236. var elem = context.getElementById( match[3] );
  4237. // Check parentNode to catch when Blackberry 4.6 returns
  4238. // nodes that are no longer in the document #6963
  4239. if ( elem && elem.parentNode ) {
  4240. // Handle the case where IE and Opera return items
  4241. // by name instead of ID
  4242. if ( elem.id === match[3] ) {
  4243. return makeArray( [ elem ], extra );
  4244. }
  4245. } else {
  4246. return makeArray( [], extra );
  4247. }
  4248. }
  4249. try {
  4250. return makeArray( context.querySelectorAll(query), extra );
  4251. } catch(qsaError) {}
  4252. // qSA works strangely on Element-rooted queries
  4253. // We can work around this by specifying an extra ID on the root
  4254. // and working up from there (Thanks to Andrew Dupont for the technique)
  4255. // IE 8 doesn't work on object elements
  4256. } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
  4257. var oldContext = context,
  4258. old = context.getAttribute( "id" ),
  4259. nid = old || id,
  4260. hasParent = context.parentNode,
  4261. relativeHierarchySelector = /^\s*[+~]/.test( query );
  4262. if ( !old ) {
  4263. context.setAttribute( "id", nid );
  4264. } else {
  4265. nid = nid.replace( /'/g, "\\$&" );
  4266. }
  4267. if ( relativeHierarchySelector && hasParent ) {
  4268. context = context.parentNode;
  4269. }
  4270. try {
  4271. if ( !relativeHierarchySelector || hasParent ) {
  4272. return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra );
  4273. }
  4274. } catch(pseudoError) {
  4275. } finally {
  4276. if ( !old ) {
  4277. oldContext.removeAttribute( "id" );
  4278. }
  4279. }
  4280. }
  4281. }
  4282. return oldSizzle(query, context, extra, seed);
  4283. };
  4284. for ( var prop in oldSizzle ) {
  4285. Sizzle[ prop ] = oldSizzle[ prop ];
  4286. }
  4287. // release memory in IE
  4288. div = null;
  4289. })();
  4290. }
  4291. (function(){
  4292. var html = document.documentElement,
  4293. matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector;
  4294. if ( matches ) {
  4295. // Check to see if it's possible to do matchesSelector
  4296. // on a disconnected node (IE 9 fails this)
  4297. var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ),
  4298. pseudoWorks = false;
  4299. try {
  4300. // This should fail with an exception
  4301. // Gecko does not error, returns false instead
  4302. matches.call( document.documentElement, "[test!='']:sizzle" );
  4303. } catch( pseudoError ) {
  4304. pseudoWorks = true;
  4305. }
  4306. Sizzle.matchesSelector = function( node, expr ) {
  4307. // Make sure that attribute selectors are quoted
  4308. expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']");
  4309. if ( !Sizzle.isXML( node ) ) {
  4310. try {
  4311. if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) {
  4312. var ret = matches.call( node, expr );
  4313. // IE 9's matchesSelector returns false on disconnected nodes
  4314. if ( ret || !disconnectedMatch ||
  4315. // As well, disconnected nodes are said to be in a document
  4316. // fragment in IE 9, so check for that
  4317. node.document && node.document.nodeType !== 11 ) {
  4318. return ret;
  4319. }
  4320. }
  4321. } catch(e) {}
  4322. }
  4323. return Sizzle(expr, null, null, [node]).length > 0;
  4324. };
  4325. }
  4326. })();
  4327. (function(){
  4328. var div = document.createElement("div");
  4329. div.innerHTML = "<div class='test e'></div><div class='test'></div>";
  4330. // Opera can't find a second classname (in 9.6)
  4331. // Also, make sure that getElementsByClassName actually exists
  4332. if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {
  4333. return;
  4334. }
  4335. // Safari caches class attributes, doesn't catch changes (in 3.2)
  4336. div.lastChild.className = "e";
  4337. if ( div.getElementsByClassName("e").length === 1 ) {
  4338. return;
  4339. }
  4340. Expr.order.splice(1, 0, "CLASS");
  4341. Expr.find.CLASS = function( match, context, isXML ) {
  4342. if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
  4343. return context.getElementsByClassName(match[1]);
  4344. }
  4345. };
  4346. // release memory in IE
  4347. div = null;
  4348. })();
  4349. function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
  4350. for ( var i = 0, l = checkSet.length; i < l; i++ ) {
  4351. var elem = checkSet[i];
  4352. if ( elem ) {
  4353. var match = false;
  4354. elem = elem[dir];
  4355. while ( elem ) {
  4356. if ( elem[ expando ] === doneName ) {
  4357. match = checkSet[elem.sizset];
  4358. break;
  4359. }
  4360. if ( elem.nodeType === 1 && !isXML ){
  4361. elem[ expando ] = doneName;
  4362. elem.sizset = i;
  4363. }
  4364. if ( elem.nodeName.toLowerCase() === cur ) {
  4365. match = elem;
  4366. break;
  4367. }
  4368. elem = elem[dir];
  4369. }
  4370. checkSet[i] = match;
  4371. }
  4372. }
  4373. }
  4374. function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
  4375. for ( var i = 0, l = checkSet.length; i < l; i++ ) {
  4376. var elem = checkSet[i];
  4377. if ( elem ) {
  4378. var match = false;
  4379. elem = elem[dir];
  4380. while ( elem ) {
  4381. if ( elem[ expando ] === doneName ) {
  4382. match = checkSet[elem.sizset];
  4383. break;
  4384. }
  4385. if ( elem.nodeType === 1 ) {
  4386. if ( !isXML ) {
  4387. elem[ expando ] = doneName;
  4388. elem.sizset = i;
  4389. }
  4390. if ( typeof cur !== "string" ) {
  4391. if ( elem === cur ) {
  4392. match = true;
  4393. break;
  4394. }
  4395. } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
  4396. match = elem;
  4397. break;
  4398. }
  4399. }
  4400. elem = elem[dir];
  4401. }
  4402. checkSet[i] = match;
  4403. }
  4404. }
  4405. }
  4406. if ( document.documentElement.contains ) {
  4407. Sizzle.contains = function( a, b ) {
  4408. return a !== b && (a.contains ? a.contains(b) : true);
  4409. };
  4410. } else if ( document.documentElement.compareDocumentPosition ) {
  4411. Sizzle.contains = function( a, b ) {
  4412. return !!(a.compareDocumentPosition(b) & 16);
  4413. };
  4414. } else {
  4415. Sizzle.contains = function() {
  4416. return false;
  4417. };
  4418. }
  4419. Sizzle.isXML = function( elem ) {
  4420. // documentElement is verified for cases where it doesn't yet exist
  4421. // (such as loading iframes in IE - #4833)
  4422. var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;
  4423. return documentElement ? documentElement.nodeName !== "HTML" : false;
  4424. };
  4425. var posProcess = function( selector, context, seed ) {
  4426. var match,
  4427. tmpSet = [],
  4428. later = "",
  4429. root = context.nodeType ? [context] : context;
  4430. // Position selectors must be done after the filter
  4431. // And so must :not(positional) so we move all PSEUDOs to the end
  4432. while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
  4433. later += match[0];
  4434. selector = selector.replace( Expr.match.PSEUDO, "" );
  4435. }
  4436. selector = Expr.relative[selector] ? selector + "*" : selector;
  4437. for ( var i = 0, l = root.length; i < l; i++ ) {
  4438. Sizzle( selector, root[i], tmpSet, seed );
  4439. }
  4440. return Sizzle.filter( later, tmpSet );
  4441. };
  4442. // EXPOSE
  4443. // Override sizzle attribute retrieval
  4444. Sizzle.attr = jQuery.attr;
  4445. Sizzle.selectors.attrMap = {};
  4446. jQuery.find = Sizzle;
  4447. jQuery.expr = Sizzle.selectors;
  4448. jQuery.expr[":"] = jQuery.expr.filters;
  4449. jQuery.unique = Sizzle.uniqueSort;
  4450. jQuery.text = Sizzle.getText;
  4451. jQuery.isXMLDoc = Sizzle.isXML;
  4452. jQuery.contains = Sizzle.contains;
  4453. })();
  4454. var runtil = /Until$/,
  4455. rparentsprev = /^(?:parents|prevUntil|prevAll)/,
  4456. // Note: This RegExp should be improved, or likely pulled from Sizzle
  4457. rmultiselector = /,/,
  4458. isSimple = /^.[^:#\[\.,]*$/,
  4459. slice = Array.prototype.slice,
  4460. POS = jQuery.expr.match.globalPOS,
  4461. // methods guaranteed to produce a unique set when starting from a unique set
  4462. guaranteedUnique = {
  4463. children: true,
  4464. contents: true,
  4465. next: true,
  4466. prev: true
  4467. };
  4468. jQuery.fn.extend({
  4469. find: function( selector ) {
  4470. var self = this,
  4471. i, l;
  4472. if ( typeof selector !== "string" ) {
  4473. return jQuery( selector ).filter(function() {
  4474. for ( i = 0, l = self.length; i < l; i++ ) {
  4475. if ( jQuery.contains( self[ i ], this ) ) {
  4476. return true;
  4477. }
  4478. }
  4479. });
  4480. }
  4481. var ret = this.pushStack( "", "find", selector ),
  4482. length, n, r;
  4483. for ( i = 0, l = this.length; i < l; i++ ) {
  4484. length = ret.length;
  4485. jQuery.find( selector, this[i], ret );
  4486. if ( i > 0 ) {
  4487. // Make sure that the results are unique
  4488. for ( n = length; n < ret.length; n++ ) {
  4489. for ( r = 0; r < length; r++ ) {
  4490. if ( ret[r] === ret[n] ) {
  4491. ret.splice(n--, 1);
  4492. break;
  4493. }
  4494. }
  4495. }
  4496. }
  4497. }
  4498. return ret;
  4499. },
  4500. has: function( target ) {
  4501. var targets = jQuery( target );
  4502. return this.filter(function() {
  4503. for ( var i = 0, l = targets.length; i < l; i++ ) {
  4504. if ( jQuery.contains( this, targets[i] ) ) {
  4505. return true;
  4506. }
  4507. }
  4508. });
  4509. },
  4510. not: function( selector ) {
  4511. return this.pushStack( winnow(this, selector, false), "not", selector);
  4512. },
  4513. filter: function( selector ) {
  4514. return this.pushStack( winnow(this, selector, true), "filter", selector );
  4515. },
  4516. is: function( selector ) {
  4517. return !!selector && (
  4518. typeof selector === "string" ?
  4519. // If this is a positional selector, check membership in the returned set
  4520. // so $("p:first").is("p:last") won't return true for a doc with two "p".
  4521. POS.test( selector ) ?
  4522. jQuery( selector, this.context ).index( this[0] ) >= 0 :
  4523. jQuery.filter( selector, this ).length > 0 :
  4524. this.filter( selector ).length > 0 );
  4525. },
  4526. closest: function( selectors, context ) {
  4527. var ret = [], i, l, cur = this[0];
  4528. // Array (deprecated as of jQuery 1.7)
  4529. if ( jQuery.isArray( selectors ) ) {
  4530. var level = 1;
  4531. while ( cur && cur.ownerDocument && cur !== context ) {
  4532. for ( i = 0; i < selectors.length; i++ ) {
  4533. if ( jQuery( cur ).is( selectors[ i ] ) ) {
  4534. ret.push({ selector: selectors[ i ], elem: cur, level: level });
  4535. }
  4536. }
  4537. cur = cur.parentNode;
  4538. level++;
  4539. }
  4540. return ret;
  4541. }
  4542. // String
  4543. var pos = POS.test( selectors ) || typeof selectors !== "string" ?
  4544. jQuery( selectors, context || this.context ) :
  4545. 0;
  4546. for ( i = 0, l = this.length; i < l; i++ ) {
  4547. cur = this[i];
  4548. while ( cur ) {
  4549. if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
  4550. ret.push( cur );
  4551. break;
  4552. } else {
  4553. cur = cur.parentNode;
  4554. if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) {
  4555. break;
  4556. }
  4557. }
  4558. }
  4559. }
  4560. ret = ret.length > 1 ? jQuery.unique( ret ) : ret;
  4561. return this.pushStack( ret, "closest", selectors );
  4562. },
  4563. // Determine the position of an element within
  4564. // the matched set of elements
  4565. index: function( elem ) {
  4566. // No argument, return index in parent
  4567. if ( !elem ) {
  4568. return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1;
  4569. }
  4570. // index in selector
  4571. if ( typeof elem === "string" ) {
  4572. return jQuery.inArray( this[0], jQuery( elem ) );
  4573. }
  4574. // Locate the position of the desired element
  4575. return jQuery.inArray(
  4576. // If it receives a jQuery object, the first element is used
  4577. elem.jquery ? elem[0] : elem, this );
  4578. },
  4579. add: function( selector, context ) {
  4580. var set = typeof selector === "string" ?
  4581. jQuery( selector, context ) :
  4582. jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ),
  4583. all = jQuery.merge( this.get(), set );
  4584. return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?
  4585. all :
  4586. jQuery.unique( all ) );
  4587. },
  4588. andSelf: function() {
  4589. return this.add( this.prevObject );
  4590. }
  4591. });
  4592. // A painfully simple check to see if an element is disconnected
  4593. // from a document (should be improved, where feasible).
  4594. function isDisconnected( node ) {
  4595. return !node || !node.parentNode || node.parentNode.nodeType === 11;
  4596. }
  4597. jQuery.each({
  4598. parent: function( elem ) {
  4599. var parent = elem.parentNode;
  4600. return parent && parent.nodeType !== 11 ? parent : null;
  4601. },
  4602. parents: function( elem ) {
  4603. return jQuery.dir( elem, "parentNode" );
  4604. },
  4605. parentsUntil: function( elem, i, until ) {
  4606. return jQuery.dir( elem, "parentNode", until );
  4607. },
  4608. next: function( elem ) {
  4609. return jQuery.nth( elem, 2, "nextSibling" );
  4610. },
  4611. prev: function( elem ) {
  4612. return jQuery.nth( elem, 2, "previousSibling" );
  4613. },
  4614. nextAll: function( elem ) {
  4615. return jQuery.dir( elem, "nextSibling" );
  4616. },
  4617. prevAll: function( elem ) {
  4618. return jQuery.dir( elem, "previousSibling" );
  4619. },
  4620. nextUntil: function( elem, i, until ) {
  4621. return jQuery.dir( elem, "nextSibling", until );
  4622. },
  4623. prevUntil: function( elem, i, until ) {
  4624. return jQuery.dir( elem, "previousSibling", until );
  4625. },
  4626. siblings: function( elem ) {
  4627. return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem );
  4628. },
  4629. children: function( elem ) {
  4630. return jQuery.sibling( elem.firstChild );
  4631. },
  4632. contents: function( elem ) {
  4633. return jQuery.nodeName( elem, "iframe" ) ?
  4634. elem.contentDocument || elem.contentWindow.document :
  4635. jQuery.makeArray( elem.childNodes );
  4636. }
  4637. }, function( name, fn ) {
  4638. jQuery.fn[ name ] = function( until, selector ) {
  4639. var ret = jQuery.map( this, fn, until );
  4640. if ( !runtil.test( name ) ) {
  4641. selector = until;
  4642. }
  4643. if ( selector && typeof selector === "string" ) {
  4644. ret = jQuery.filter( selector, ret );
  4645. }
  4646. ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret;
  4647. if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) {
  4648. ret = ret.reverse();
  4649. }
  4650. return this.pushStack( ret, name, slice.call( arguments ).join(",") );
  4651. };
  4652. });
  4653. jQuery.extend({
  4654. filter: function( expr, elems, not ) {
  4655. if ( not ) {
  4656. expr = ":not(" + expr + ")";
  4657. }
  4658. return elems.length === 1 ?
  4659. jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] :
  4660. jQuery.find.matches(expr, elems);
  4661. },
  4662. dir: function( elem, dir, until ) {
  4663. var matched = [],
  4664. cur = elem[ dir ];
  4665. while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
  4666. if ( cur.nodeType === 1 ) {
  4667. matched.push( cur );
  4668. }
  4669. cur = cur[dir];
  4670. }
  4671. return matched;
  4672. },
  4673. nth: function( cur, result, dir, elem ) {
  4674. result = result || 1;
  4675. var num = 0;
  4676. for ( ; cur; cur = cur[dir] ) {
  4677. if ( cur.nodeType === 1 && ++num === result ) {
  4678. break;
  4679. }
  4680. }
  4681. return cur;
  4682. },
  4683. sibling: function( n, elem ) {
  4684. var r = [];
  4685. for ( ; n; n = n.nextSibling ) {
  4686. if ( n.nodeType === 1 && n !== elem ) {
  4687. r.push( n );
  4688. }
  4689. }
  4690. return r;
  4691. }
  4692. });
  4693. // Implement the identical functionality for filter and not
  4694. function winnow( elements, qualifier, keep ) {
  4695. // Can't pass null or undefined to indexOf in Firefox 4
  4696. // Set to 0 to skip string check
  4697. qualifier = qualifier || 0;
  4698. if ( jQuery.isFunction( qualifier ) ) {
  4699. return jQuery.grep(elements, function( elem, i ) {
  4700. var retVal = !!qualifier.call( elem, i, elem );
  4701. return retVal === keep;
  4702. });
  4703. } else if ( qualifier.nodeType ) {
  4704. return jQuery.grep(elements, function( elem, i ) {
  4705. return ( elem === qualifier ) === keep;
  4706. });
  4707. } else if ( typeof qualifier === "string" ) {
  4708. var filtered = jQuery.grep(elements, function( elem ) {
  4709. return elem.nodeType === 1;
  4710. });
  4711. if ( isSimple.test( qualifier ) ) {
  4712. return jQuery.filter(qualifier, filtered, !keep);
  4713. } else {
  4714. qualifier = jQuery.filter( qualifier, filtered );
  4715. }
  4716. }
  4717. return jQuery.grep(elements, function( elem, i ) {
  4718. return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep;
  4719. });
  4720. }
  4721. function createSafeFragment( document ) {
  4722. var list = nodeNames.split( "|" ),
  4723. safeFrag = document.createDocumentFragment();
  4724. if ( safeFrag.createElement ) {
  4725. while ( list.length ) {
  4726. safeFrag.createElement(
  4727. list.pop()
  4728. );
  4729. }
  4730. }
  4731. return safeFrag;
  4732. }
  4733. var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" +
  4734. "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",
  4735. rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,
  4736. rleadingWhitespace = /^\s+/,
  4737. rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,
  4738. rtagName = /<([\w:]+)/,
  4739. rtbody = /<tbody/i,
  4740. rhtml = /<|&#?\w+;/,
  4741. rnoInnerhtml = /<(?:script|style)/i,
  4742. rnocache = /<(?:script|object|embed|option|style)/i,
  4743. rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"),
  4744. // checked="checked" or checked
  4745. rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
  4746. rscriptType = /\/(java|ecma)script/i,
  4747. rcleanScript = /^\s*<!(?:\[CDATA\[|\-\-)/,
  4748. wrapMap = {
  4749. option: [ 1, "<select multiple='multiple'>", "</select>" ],
  4750. legend: [ 1, "<fieldset>", "</fieldset>" ],
  4751. thead: [ 1, "<table>", "</table>" ],
  4752. tr: [ 2, "<table><tbody>", "</tbody></table>" ],
  4753. td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
  4754. col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
  4755. area: [ 1, "<map>", "</map>" ],
  4756. _default: [ 0, "", "" ]
  4757. },
  4758. safeFragment = createSafeFragment( document );
  4759. wrapMap.optgroup = wrapMap.option;
  4760. wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
  4761. wrapMap.th = wrapMap.td;
  4762. // IE can't serialize <link> and <script> tags normally
  4763. if ( !jQuery.support.htmlSerialize ) {
  4764. wrapMap._default = [ 1, "div<div>", "</div>" ];
  4765. }
  4766. jQuery.fn.extend({
  4767. text: function( value ) {
  4768. return jQuery.access( this, function( value ) {
  4769. return value === undefined ?
  4770. jQuery.text( this ) :
  4771. this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) );
  4772. }, null, value, arguments.length );
  4773. },
  4774. wrapAll: function( html ) {
  4775. if ( jQuery.isFunction( html ) ) {
  4776. return this.each(function(i) {
  4777. jQuery(this).wrapAll( html.call(this, i) );
  4778. });
  4779. }
  4780. if ( this[0] ) {
  4781. // The elements to wrap the target around
  4782. var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
  4783. if ( this[0].parentNode ) {
  4784. wrap.insertBefore( this[0] );
  4785. }
  4786. wrap.map(function() {
  4787. var elem = this;
  4788. while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
  4789. elem = elem.firstChild;
  4790. }
  4791. return elem;
  4792. }).append( this );
  4793. }
  4794. return this;
  4795. },
  4796. wrapInner: function( html ) {
  4797. if ( jQuery.isFunction( html ) ) {
  4798. return this.each(function(i) {
  4799. jQuery(this).wrapInner( html.call(this, i) );
  4800. });
  4801. }
  4802. return this.each(function() {
  4803. var self = jQuery( this ),
  4804. contents = self.contents();
  4805. if ( contents.length ) {
  4806. contents.wrapAll( html );
  4807. } else {
  4808. self.append( html );
  4809. }
  4810. });
  4811. },
  4812. wrap: function( html ) {
  4813. var isFunction = jQuery.isFunction( html );
  4814. return this.each(function(i) {
  4815. jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );
  4816. });
  4817. },
  4818. unwrap: function() {
  4819. return this.parent().each(function() {
  4820. if ( !jQuery.nodeName( this, "body" ) ) {
  4821. jQuery( this ).replaceWith( this.childNodes );
  4822. }
  4823. }).end();
  4824. },
  4825. append: function() {
  4826. return this.domManip(arguments, true, function( elem ) {
  4827. if ( this.nodeType === 1 ) {
  4828. this.appendChild( elem );
  4829. }
  4830. });
  4831. },
  4832. prepend: function() {
  4833. return this.domManip(arguments, true, function( elem ) {
  4834. if ( this.nodeType === 1 ) {
  4835. this.insertBefore( elem, this.firstChild );
  4836. }
  4837. });
  4838. },
  4839. before: function() {
  4840. if ( this[0] && this[0].parentNode ) {
  4841. return this.domManip(arguments, false, function( elem ) {
  4842. this.parentNode.insertBefore( elem, this );
  4843. });
  4844. } else if ( arguments.length ) {
  4845. var set = jQuery.clean( arguments );
  4846. set.push.apply( set, this.toArray() );
  4847. return this.pushStack( set, "before", arguments );
  4848. }
  4849. },
  4850. after: function() {
  4851. if ( this[0] && this[0].parentNode ) {
  4852. return this.domManip(arguments, false, function( elem ) {
  4853. this.parentNode.insertBefore( elem, this.nextSibling );
  4854. });
  4855. } else if ( arguments.length ) {
  4856. var set = this.pushStack( this, "after", arguments );
  4857. set.push.apply( set, jQuery.clean(arguments) );
  4858. return set;
  4859. }
  4860. },
  4861. // keepData is for internal use only--do not document
  4862. remove: function( selector, keepData ) {
  4863. for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
  4864. if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {
  4865. if ( !keepData && elem.nodeType === 1 ) {
  4866. jQuery.cleanData( elem.getElementsByTagName("*") );
  4867. jQuery.cleanData( [ elem ] );
  4868. }
  4869. if ( elem.parentNode ) {
  4870. elem.parentNode.removeChild( elem );
  4871. }
  4872. }
  4873. }
  4874. return this;
  4875. },
  4876. empty: function() {
  4877. for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
  4878. // Remove element nodes and prevent memory leaks
  4879. if ( elem.nodeType === 1 ) {
  4880. jQuery.cleanData( elem.getElementsByTagName("*") );
  4881. }
  4882. // Remove any remaining nodes
  4883. while ( elem.firstChild ) {
  4884. elem.removeChild( elem.firstChild );
  4885. }
  4886. }
  4887. return this;
  4888. },
  4889. clone: function( dataAndEvents, deepDataAndEvents ) {
  4890. dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
  4891. deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
  4892. return this.map( function () {
  4893. return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
  4894. });
  4895. },
  4896. html: function( value ) {
  4897. return jQuery.access( this, function( value ) {
  4898. var elem = this[0] || {},
  4899. i = 0,
  4900. l = this.length;
  4901. if ( value === undefined ) {
  4902. return elem.nodeType === 1 ?
  4903. elem.innerHTML.replace( rinlinejQuery, "" ) :
  4904. null;
  4905. }
  4906. if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
  4907. ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) &&
  4908. !wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) {
  4909. value = value.replace( rxhtmlTag, "<$1></$2>" );
  4910. try {
  4911. for (; i < l; i++ ) {
  4912. // Remove element nodes and prevent memory leaks
  4913. elem = this[i] || {};
  4914. if ( elem.nodeType === 1 ) {
  4915. jQuery.cleanData( elem.getElementsByTagName( "*" ) );
  4916. elem.innerHTML = value;
  4917. }
  4918. }
  4919. elem = 0;
  4920. // If using innerHTML throws an exception, use the fallback method
  4921. } catch(e) {}
  4922. }
  4923. if ( elem ) {
  4924. this.empty().append( value );
  4925. }
  4926. }, null, value, arguments.length );
  4927. },
  4928. replaceWith: function( value ) {
  4929. if ( this[0] && this[0].parentNode ) {
  4930. // Make sure that the elements are removed from the DOM before they are inserted
  4931. // this can help fix replacing a parent with child elements
  4932. if ( jQuery.isFunction( value ) ) {
  4933. return this.each(function(i) {
  4934. var self = jQuery(this), old = self.html();
  4935. self.replaceWith( value.call( this, i, old ) );
  4936. });
  4937. }
  4938. if ( typeof value !== "string" ) {
  4939. value = jQuery( value ).detach();
  4940. }
  4941. return this.each(function() {
  4942. var next = this.nextSibling,
  4943. parent = this.parentNode;
  4944. jQuery( this ).remove();
  4945. if ( next ) {
  4946. jQuery(next).before( value );
  4947. } else {
  4948. jQuery(parent).append( value );
  4949. }
  4950. });
  4951. } else {
  4952. return this.length ?
  4953. this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value ) :
  4954. this;
  4955. }
  4956. },
  4957. detach: function( selector ) {
  4958. return this.remove( selector, true );
  4959. },
  4960. domManip: function( args, table, callback ) {
  4961. var results, first, fragment, parent,
  4962. value = args[0],
  4963. scripts = [];
  4964. // We can't cloneNode fragments that contain checked, in WebKit
  4965. if ( !jQuery.support.checkClone && arguments.length === 3 && typeof value === "string" && rchecked.test( value ) ) {
  4966. return this.each(function() {
  4967. jQuery(this).domManip( args, table, callback, true );
  4968. });
  4969. }
  4970. if ( jQuery.isFunction(value) ) {
  4971. return this.each(function(i) {
  4972. var self = jQuery(this);
  4973. args[0] = value.call(this, i, table ? self.html() : undefined);
  4974. self.domManip( args, table, callback );
  4975. });
  4976. }
  4977. if ( this[0] ) {
  4978. parent = value && value.parentNode;
  4979. // If we're in a fragment, just use that instead of building a new one
  4980. if ( jQuery.support.parentNode && parent && parent.nodeType === 11 && parent.childNodes.length === this.length ) {
  4981. results = { fragment: parent };
  4982. } else {
  4983. results = jQuery.buildFragment( args, this, scripts );
  4984. }
  4985. fragment = results.fragment;
  4986. if ( fragment.childNodes.length === 1 ) {
  4987. first = fragment = fragment.firstChild;
  4988. } else {
  4989. first = fragment.firstChild;
  4990. }
  4991. if ( first ) {
  4992. table = table && jQuery.nodeName( first, "tr" );
  4993. for ( var i = 0, l = this.length, lastIndex = l - 1; i < l; i++ ) {
  4994. callback.call(
  4995. table ?
  4996. root(this[i], first) :
  4997. this[i],
  4998. // Make sure that we do not leak memory by inadvertently discarding
  4999. // the original fragment (which might have attached data) instead of
  5000. // using it; in addition, use the original fragment object for the last
  5001. // item instead of first because it can end up being emptied incorrectly
  5002. // in certain situations (Bug #8070).
  5003. // Fragments from the fragment cache must always be cloned and never used
  5004. // in place.
  5005. results.cacheable || ( l > 1 && i < lastIndex ) ?
  5006. jQuery.clone( fragment, true, true ) :
  5007. fragment
  5008. );
  5009. }
  5010. }
  5011. if ( scripts.length ) {
  5012. jQuery.each( scripts, function( i, elem ) {
  5013. if ( elem.src ) {
  5014. jQuery.ajax({
  5015. type: "GET",
  5016. global: false,
  5017. url: elem.src,
  5018. async: false,
  5019. dataType: "script"
  5020. });
  5021. } else {
  5022. jQuery.globalEval( ( elem.text || elem.textContent || elem.innerHTML || "" ).replace( rcleanScript, "/*$0*/" ) );
  5023. }
  5024. if ( elem.parentNode ) {
  5025. elem.parentNode.removeChild( elem );
  5026. }
  5027. });
  5028. }
  5029. }
  5030. return this;
  5031. }
  5032. });
  5033. function root( elem, cur ) {
  5034. return jQuery.nodeName(elem, "table") ?
  5035. (elem.getElementsByTagName("tbody")[0] ||
  5036. elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
  5037. elem;
  5038. }
  5039. function cloneCopyEvent( src, dest ) {
  5040. if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {
  5041. return;
  5042. }
  5043. var type, i, l,
  5044. oldData = jQuery._data( src ),
  5045. curData = jQuery._data( dest, oldData ),
  5046. events = oldData.events;
  5047. if ( events ) {
  5048. delete curData.handle;
  5049. curData.events = {};
  5050. for ( type in events ) {
  5051. for ( i = 0, l = events[ type ].length; i < l; i++ ) {
  5052. jQuery.event.add( dest, type, events[ type ][ i ] );
  5053. }
  5054. }
  5055. }
  5056. // make the cloned public data object a copy from the original
  5057. if ( curData.data ) {
  5058. curData.data = jQuery.extend( {}, curData.data );
  5059. }
  5060. }
  5061. function cloneFixAttributes( src, dest ) {
  5062. var nodeName;
  5063. // We do not need to do anything for non-Elements
  5064. if ( dest.nodeType !== 1 ) {
  5065. return;
  5066. }
  5067. // clearAttributes removes the attributes, which we don't want,
  5068. // but also removes the attachEvent events, which we *do* want
  5069. if ( dest.clearAttributes ) {
  5070. dest.clearAttributes();
  5071. }
  5072. // mergeAttributes, in contrast, only merges back on the
  5073. // original attributes, not the events
  5074. if ( dest.mergeAttributes ) {
  5075. dest.mergeAttributes( src );
  5076. }
  5077. nodeName = dest.nodeName.toLowerCase();
  5078. // IE6-8 fail to clone children inside object elements that use
  5079. // the proprietary classid attribute value (rather than the type
  5080. // attribute) to identify the type of content to display
  5081. if ( nodeName === "object" ) {
  5082. dest.outerHTML = src.outerHTML;
  5083. } else if ( nodeName === "input" && (src.type === "checkbox" || src.type === "radio") ) {
  5084. // IE6-8 fails to persist the checked state of a cloned checkbox
  5085. // or radio button. Worse, IE6-7 fail to give the cloned element
  5086. // a checked appearance if the defaultChecked value isn't also set
  5087. if ( src.checked ) {
  5088. dest.defaultChecked = dest.checked = src.checked;
  5089. }
  5090. // IE6-7 get confused and end up setting the value of a cloned
  5091. // checkbox/radio button to an empty string instead of "on"
  5092. if ( dest.value !== src.value ) {
  5093. dest.value = src.value;
  5094. }
  5095. // IE6-8 fails to return the selected option to the default selected
  5096. // state when cloning options
  5097. } else if ( nodeName === "option" ) {
  5098. dest.selected = src.defaultSelected;
  5099. // IE6-8 fails to set the defaultValue to the correct value when
  5100. // cloning other types of input fields
  5101. } else if ( nodeName === "input" || nodeName === "textarea" ) {
  5102. dest.defaultValue = src.defaultValue;
  5103. // IE blanks contents when cloning scripts
  5104. } else if ( nodeName === "script" && dest.text !== src.text ) {
  5105. dest.text = src.text;
  5106. }
  5107. // Event data gets referenced instead of copied if the expando
  5108. // gets copied too
  5109. dest.removeAttribute( jQuery.expando );
  5110. // Clear flags for bubbling special change/submit events, they must
  5111. // be reattached when the newly cloned events are first activated
  5112. dest.removeAttribute( "_submit_attached" );
  5113. dest.removeAttribute( "_change_attached" );
  5114. }
  5115. jQuery.buildFragment = function( args, nodes, scripts ) {
  5116. var fragment, cacheable, cacheresults, doc,
  5117. first = args[ 0 ];
  5118. // nodes may contain either an explicit document object,
  5119. // a jQuery collection or context object.
  5120. // If nodes[0] contains a valid object to assign to doc
  5121. if ( nodes && nodes[0] ) {
  5122. doc = nodes[0].ownerDocument || nodes[0];
  5123. }
  5124. // Ensure that an attr object doesn't incorrectly stand in as a document object
  5125. // Chrome and Firefox seem to allow this to occur and will throw exception
  5126. // Fixes #8950
  5127. if ( !doc.createDocumentFragment ) {
  5128. doc = document;
  5129. }
  5130. // Only cache "small" (1/2 KB) HTML strings that are associated with the main document
  5131. // Cloning options loses the selected state, so don't cache them
  5132. // IE 6 doesn't like it when you put <object> or <embed> elements in a fragment
  5133. // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
  5134. // Lastly, IE6,7,8 will not correctly reuse cached fragments that were created from unknown elems #10501
  5135. if ( args.length === 1 && typeof first === "string" && first.length < 512 && doc === document &&
  5136. first.charAt(0) === "<" && !rnocache.test( first ) &&
  5137. (jQuery.support.checkClone || !rchecked.test( first )) &&
  5138. (jQuery.support.html5Clone || !rnoshimcache.test( first )) ) {
  5139. cacheable = true;
  5140. cacheresults = jQuery.fragments[ first ];
  5141. if ( cacheresults && cacheresults !== 1 ) {
  5142. fragment = cacheresults;
  5143. }
  5144. }
  5145. if ( !fragment ) {
  5146. fragment = doc.createDocumentFragment();
  5147. jQuery.clean( args, doc, fragment, scripts );
  5148. }
  5149. if ( cacheable ) {
  5150. jQuery.fragments[ first ] = cacheresults ? fragment : 1;
  5151. }
  5152. return { fragment: fragment, cacheable: cacheable };
  5153. };
  5154. jQuery.fragments = {};
  5155. jQuery.each({
  5156. appendTo: "append",
  5157. prependTo: "prepend",
  5158. insertBefore: "before",
  5159. insertAfter: "after",
  5160. replaceAll: "replaceWith"
  5161. }, function( name, original ) {
  5162. jQuery.fn[ name ] = function( selector ) {
  5163. var ret = [],
  5164. insert = jQuery( selector ),
  5165. parent = this.length === 1 && this[0].parentNode;
  5166. if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) {
  5167. insert[ original ]( this[0] );
  5168. return this;
  5169. } else {
  5170. for ( var i = 0, l = insert.length; i < l; i++ ) {
  5171. var elems = ( i > 0 ? this.clone(true) : this ).get();
  5172. jQuery( insert[i] )[ original ]( elems );
  5173. ret = ret.concat( elems );
  5174. }
  5175. return this.pushStack( ret, name, insert.selector );
  5176. }
  5177. };
  5178. });
  5179. function getAll( elem ) {
  5180. if ( typeof elem.getElementsByTagName !== "undefined" ) {
  5181. return elem.getElementsByTagName( "*" );
  5182. } else if ( typeof elem.querySelectorAll !== "undefined" ) {
  5183. return elem.querySelectorAll( "*" );
  5184. } else {
  5185. return [];
  5186. }
  5187. }
  5188. // Used in clean, fixes the defaultChecked property
  5189. function fixDefaultChecked( elem ) {
  5190. if ( elem.type === "checkbox" || elem.type === "radio" ) {
  5191. elem.defaultChecked = elem.checked;
  5192. }
  5193. }
  5194. // Finds all inputs and passes them to fixDefaultChecked
  5195. function findInputs( elem ) {
  5196. var nodeName = ( elem.nodeName || "" ).toLowerCase();
  5197. if ( nodeName === "input" ) {
  5198. fixDefaultChecked( elem );
  5199. // Skip scripts, get other children
  5200. } else if ( nodeName !== "script" && typeof elem.getElementsByTagName !== "undefined" ) {
  5201. jQuery.grep( elem.getElementsByTagName("input"), fixDefaultChecked );
  5202. }
  5203. }
  5204. // Derived From: http://www.iecss.com/shimprove/javascript/shimprove.1-0-1.js
  5205. function shimCloneNode( elem ) {
  5206. var div = document.createElement( "div" );
  5207. safeFragment.appendChild( div );
  5208. div.innerHTML = elem.outerHTML;
  5209. return div.firstChild;
  5210. }
  5211. jQuery.extend({
  5212. clone: function( elem, dataAndEvents, deepDataAndEvents ) {
  5213. var srcElements,
  5214. destElements,
  5215. i,
  5216. // IE<=8 does not properly clone detached, unknown element nodes
  5217. clone = jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ?
  5218. elem.cloneNode( true ) :
  5219. shimCloneNode( elem );
  5220. if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) &&
  5221. (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) {
  5222. // IE copies events bound via attachEvent when using cloneNode.
  5223. // Calling detachEvent on the clone will also remove the events
  5224. // from the original. In order to get around this, we use some
  5225. // proprietary methods to clear the events. Thanks to MooTools
  5226. // guys for this hotness.
  5227. cloneFixAttributes( elem, clone );
  5228. // Using Sizzle here is crazy slow, so we use getElementsByTagName instead
  5229. srcElements = getAll( elem );
  5230. destElements = getAll( clone );
  5231. // Weird iteration because IE will replace the length property
  5232. // with an element if you are cloning the body and one of the
  5233. // elements on the page has a name or id of "length"
  5234. for ( i = 0; srcElements[i]; ++i ) {
  5235. // Ensure that the destination node is not null; Fixes #9587
  5236. if ( destElements[i] ) {
  5237. cloneFixAttributes( srcElements[i], destElements[i] );
  5238. }
  5239. }
  5240. }
  5241. // Copy the events from the original to the clone
  5242. if ( dataAndEvents ) {
  5243. cloneCopyEvent( elem, clone );
  5244. if ( deepDataAndEvents ) {
  5245. srcElements = getAll( elem );
  5246. destElements = getAll( clone );
  5247. for ( i = 0; srcElements[i]; ++i ) {
  5248. cloneCopyEvent( srcElements[i], destElements[i] );
  5249. }
  5250. }
  5251. }
  5252. srcElements = destElements = null;
  5253. // Return the cloned set
  5254. return clone;
  5255. },
  5256. clean: function( elems, context, fragment, scripts ) {
  5257. var checkScriptType, script, j,
  5258. ret = [];
  5259. context = context || document;
  5260. // !context.createElement fails in IE with an error but returns typeof 'object'
  5261. if ( typeof context.createElement === "undefined" ) {
  5262. context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
  5263. }
  5264. for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
  5265. if ( typeof elem === "number" ) {
  5266. elem += "";
  5267. }
  5268. if ( !elem ) {
  5269. continue;
  5270. }
  5271. // Convert html string into DOM nodes
  5272. if ( typeof elem === "string" ) {
  5273. if ( !rhtml.test( elem ) ) {
  5274. elem = context.createTextNode( elem );
  5275. } else {
  5276. // Fix "XHTML"-style tags in all browsers
  5277. elem = elem.replace(rxhtmlTag, "<$1></$2>");
  5278. // Trim whitespace, otherwise indexOf won't work as expected
  5279. var tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase(),
  5280. wrap = wrapMap[ tag ] || wrapMap._default,
  5281. depth = wrap[0],
  5282. div = context.createElement("div"),
  5283. safeChildNodes = safeFragment.childNodes,
  5284. remove;
  5285. // Append wrapper element to unknown element safe doc fragment
  5286. if ( context === document ) {
  5287. // Use the fragment we've already created for this document
  5288. safeFragment.appendChild( div );
  5289. } else {
  5290. // Use a fragment created with the owner document
  5291. createSafeFragment( context ).appendChild( div );
  5292. }
  5293. // Go to html and back, then peel off extra wrappers
  5294. div.innerHTML = wrap[1] + elem + wrap[2];
  5295. // Move to the right depth
  5296. while ( depth-- ) {
  5297. div = div.lastChild;
  5298. }
  5299. // Remove IE's autoinserted <tbody> from table fragments
  5300. if ( !jQuery.support.tbody ) {
  5301. // String was a <table>, *may* have spurious <tbody>
  5302. var hasBody = rtbody.test(elem),
  5303. tbody = tag === "table" && !hasBody ?
  5304. div.firstChild && div.firstChild.childNodes :
  5305. // String was a bare <thead> or <tfoot>
  5306. wrap[1] === "<table>" && !hasBody ?
  5307. div.childNodes :
  5308. [];
  5309. for ( j = tbody.length - 1; j >= 0 ; --j ) {
  5310. if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {
  5311. tbody[ j ].parentNode.removeChild( tbody[ j ] );
  5312. }
  5313. }
  5314. }
  5315. // IE completely kills leading whitespace when innerHTML is used
  5316. if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
  5317. div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
  5318. }
  5319. elem = div.childNodes;
  5320. // Clear elements from DocumentFragment (safeFragment or otherwise)
  5321. // to avoid hoarding elements. Fixes #11356
  5322. if ( div ) {
  5323. div.parentNode.removeChild( div );
  5324. // Guard against -1 index exceptions in FF3.6
  5325. if ( safeChildNodes.length > 0 ) {
  5326. remove = safeChildNodes[ safeChildNodes.length - 1 ];
  5327. if ( remove && remove.parentNode ) {
  5328. remove.parentNode.removeChild( remove );
  5329. }
  5330. }
  5331. }
  5332. }
  5333. }
  5334. // Resets defaultChecked for any radios and checkboxes
  5335. // about to be appended to the DOM in IE 6/7 (#8060)
  5336. var len;
  5337. if ( !jQuery.support.appendChecked ) {
  5338. if ( elem[0] && typeof (len = elem.length) === "number" ) {
  5339. for ( j = 0; j < len; j++ ) {
  5340. findInputs( elem[j] );
  5341. }
  5342. } else {
  5343. findInputs( elem );
  5344. }
  5345. }
  5346. if ( elem.nodeType ) {
  5347. ret.push( elem );
  5348. } else {
  5349. ret = jQuery.merge( ret, elem );
  5350. }
  5351. }
  5352. if ( fragment ) {
  5353. checkScriptType = function( elem ) {
  5354. return !elem.type || rscriptType.test( elem.type );
  5355. };
  5356. for ( i = 0; ret[i]; i++ ) {
  5357. script = ret[i];
  5358. if ( scripts && jQuery.nodeName( script, "script" ) && (!script.type || rscriptType.test( script.type )) ) {
  5359. scripts.push( script.parentNode ? script.parentNode.removeChild( script ) : script );
  5360. } else {
  5361. if ( script.nodeType === 1 ) {
  5362. var jsTags = jQuery.grep( script.getElementsByTagName( "script" ), checkScriptType );
  5363. ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) );
  5364. }
  5365. fragment.appendChild( script );
  5366. }
  5367. }
  5368. }
  5369. return ret;
  5370. },
  5371. cleanData: function( elems ) {
  5372. var data, id,
  5373. cache = jQuery.cache,
  5374. special = jQuery.event.special,
  5375. deleteExpando = jQuery.support.deleteExpando;
  5376. for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
  5377. if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
  5378. continue;
  5379. }
  5380. id = elem[ jQuery.expando ];
  5381. if ( id ) {
  5382. data = cache[ id ];
  5383. if ( data && data.events ) {
  5384. for ( var type in data.events ) {
  5385. if ( special[ type ] ) {
  5386. jQuery.event.remove( elem, type );
  5387. // This is a shortcut to avoid jQuery.event.remove's overhead
  5388. } else {
  5389. jQuery.removeEvent( elem, type, data.handle );
  5390. }
  5391. }
  5392. // Null the DOM reference to avoid IE6/7/8 leak (#7054)
  5393. if ( data.handle ) {
  5394. data.handle.elem = null;
  5395. }
  5396. }
  5397. if ( deleteExpando ) {
  5398. delete elem[ jQuery.expando ];
  5399. } else if ( elem.removeAttribute ) {
  5400. elem.removeAttribute( jQuery.expando );
  5401. }
  5402. delete cache[ id ];
  5403. }
  5404. }
  5405. }
  5406. });
  5407. var ralpha = /alpha\([^)]*\)/i,
  5408. ropacity = /opacity=([^)]*)/,
  5409. // fixed for IE9, see #8346
  5410. rupper = /([A-Z]|^ms)/g,
  5411. rnum = /^[\-+]?(?:\d*\.)?\d+$/i,
  5412. rnumnonpx = /^-?(?:\d*\.)?\d+(?!px)[^\d\s]+$/i,
  5413. rrelNum = /^([\-+])=([\-+.\de]+)/,
  5414. rmargin = /^margin/,
  5415. cssShow = { position: "absolute", visibility: "hidden", display: "block" },
  5416. // order is important!
  5417. cssExpand = [ "Top", "Right", "Bottom", "Left" ],
  5418. curCSS,
  5419. getComputedStyle,
  5420. currentStyle;
  5421. jQuery.fn.css = function( name, value ) {
  5422. return jQuery.access( this, function( elem, name, value ) {
  5423. return value !== undefined ?
  5424. jQuery.style( elem, name, value ) :
  5425. jQuery.css( elem, name );
  5426. }, name, value, arguments.length > 1 );
  5427. };
  5428. jQuery.extend({
  5429. // Add in style property hooks for overriding the default
  5430. // behavior of getting and setting a style property
  5431. cssHooks: {
  5432. opacity: {
  5433. get: function( elem, computed ) {
  5434. if ( computed ) {
  5435. // We should always get a number back from opacity
  5436. var ret = curCSS( elem, "opacity" );
  5437. return ret === "" ? "1" : ret;
  5438. } else {
  5439. return elem.style.opacity;
  5440. }
  5441. }
  5442. }
  5443. },
  5444. // Exclude the following css properties to add px
  5445. cssNumber: {
  5446. "fillOpacity": true,
  5447. "fontWeight": true,
  5448. "lineHeight": true,
  5449. "opacity": true,
  5450. "orphans": true,
  5451. "widows": true,
  5452. "zIndex": true,
  5453. "zoom": true
  5454. },
  5455. // Add in properties whose names you wish to fix before
  5456. // setting or getting the value
  5457. cssProps: {
  5458. // normalize float css property
  5459. "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat"
  5460. },
  5461. // Get and set the style property on a DOM Node
  5462. style: function( elem, name, value, extra ) {
  5463. // Don't set styles on text and comment nodes
  5464. if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
  5465. return;
  5466. }
  5467. // Make sure that we're working with the right name
  5468. var ret, type, origName = jQuery.camelCase( name ),
  5469. style = elem.style, hooks = jQuery.cssHooks[ origName ];
  5470. name = jQuery.cssProps[ origName ] || origName;
  5471. // Check if we're setting a value
  5472. if ( value !== undefined ) {
  5473. type = typeof value;
  5474. // convert relative number strings (+= or -=) to relative numbers. #7345
  5475. if ( type === "string" && (ret = rrelNum.exec( value )) ) {
  5476. value = ( +( ret[1] + 1) * +ret[2] ) + parseFloat( jQuery.css( elem, name ) );
  5477. // Fixes bug #9237
  5478. type = "number";
  5479. }
  5480. // Make sure that NaN and null values aren't set. See: #7116
  5481. if ( value == null || type === "number" && isNaN( value ) ) {
  5482. return;
  5483. }
  5484. // If a number was passed in, add 'px' to the (except for certain CSS properties)
  5485. if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
  5486. value += "px";
  5487. }
  5488. // If a hook was provided, use that value, otherwise just set the specified value
  5489. if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value )) !== undefined ) {
  5490. // Wrapped to prevent IE from throwing errors when 'invalid' values are provided
  5491. // Fixes bug #5509
  5492. try {
  5493. style[ name ] = value;
  5494. } catch(e) {}
  5495. }
  5496. } else {
  5497. // If a hook was provided get the non-computed value from there
  5498. if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
  5499. return ret;
  5500. }
  5501. // Otherwise just get the value from the style object
  5502. return style[ name ];
  5503. }
  5504. },
  5505. css: function( elem, name, extra ) {
  5506. var ret, hooks;
  5507. // Make sure that we're working with the right name
  5508. name = jQuery.camelCase( name );
  5509. hooks = jQuery.cssHooks[ name ];
  5510. name = jQuery.cssProps[ name ] || name;
  5511. // cssFloat needs a special treatment
  5512. if ( name === "cssFloat" ) {
  5513. name = "float";
  5514. }
  5515. // If a hook was provided get the computed value from there
  5516. if ( hooks && "get" in hooks && (ret = hooks.get( elem, true, extra )) !== undefined ) {
  5517. return ret;
  5518. // Otherwise, if a way to get the computed value exists, use that
  5519. } else if ( curCSS ) {
  5520. return curCSS( elem, name );
  5521. }
  5522. },
  5523. // A method for quickly swapping in/out CSS properties to get correct calculations
  5524. swap: function( elem, options, callback ) {
  5525. var old = {},
  5526. ret, name;
  5527. // Remember the old values, and insert the new ones
  5528. for ( name in options ) {
  5529. old[ name ] = elem.style[ name ];
  5530. elem.style[ name ] = options[ name ];
  5531. }
  5532. ret = callback.call( elem );
  5533. // Revert the old values
  5534. for ( name in options ) {
  5535. elem.style[ name ] = old[ name ];
  5536. }
  5537. return ret;
  5538. }
  5539. });
  5540. // DEPRECATED in 1.3, Use jQuery.css() instead
  5541. jQuery.curCSS = jQuery.css;
  5542. if ( document.defaultView && document.defaultView.getComputedStyle ) {
  5543. getComputedStyle = function( elem, name ) {
  5544. var ret, defaultView, computedStyle, width,
  5545. style = elem.style;
  5546. name = name.replace( rupper, "-$1" ).toLowerCase();
  5547. if ( (defaultView = elem.ownerDocument.defaultView) &&
  5548. (computedStyle = defaultView.getComputedStyle( elem, null )) ) {
  5549. ret = computedStyle.getPropertyValue( name );
  5550. if ( ret === "" && !jQuery.contains( elem.ownerDocument.documentElement, elem ) ) {
  5551. ret = jQuery.style( elem, name );
  5552. }
  5553. }
  5554. // A tribute to the "awesome hack by Dean Edwards"
  5555. // WebKit uses "computed value (percentage if specified)" instead of "used value" for margins
  5556. // which is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values
  5557. if ( !jQuery.support.pixelMargin && computedStyle && rmargin.test( name ) && rnumnonpx.test( ret ) ) {
  5558. width = style.width;
  5559. style.width = ret;
  5560. ret = computedStyle.width;
  5561. style.width = width;
  5562. }
  5563. return ret;
  5564. };
  5565. }
  5566. if ( document.documentElement.currentStyle ) {
  5567. currentStyle = function( elem, name ) {
  5568. var left, rsLeft, uncomputed,
  5569. ret = elem.currentStyle && elem.currentStyle[ name ],
  5570. style = elem.style;
  5571. // Avoid setting ret to empty string here
  5572. // so we don't default to auto
  5573. if ( ret == null && style && (uncomputed = style[ name ]) ) {
  5574. ret = uncomputed;
  5575. }
  5576. // From the awesome hack by Dean Edwards
  5577. // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
  5578. // If we're not dealing with a regular pixel number
  5579. // but a number that has a weird ending, we need to convert it to pixels
  5580. if ( rnumnonpx.test( ret ) ) {
  5581. // Remember the original values
  5582. left = style.left;
  5583. rsLeft = elem.runtimeStyle && elem.runtimeStyle.left;
  5584. // Put in the new values to get a computed value out
  5585. if ( rsLeft ) {
  5586. elem.runtimeStyle.left = elem.currentStyle.left;
  5587. }
  5588. style.left = name === "fontSize" ? "1em" : ret;
  5589. ret = style.pixelLeft + "px";
  5590. // Revert the changed values
  5591. style.left = left;
  5592. if ( rsLeft ) {
  5593. elem.runtimeStyle.left = rsLeft;
  5594. }
  5595. }
  5596. return ret === "" ? "auto" : ret;
  5597. };
  5598. }
  5599. curCSS = getComputedStyle || currentStyle;
  5600. function getWidthOrHeight( elem, name, extra ) {
  5601. // Start with offset property
  5602. var val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
  5603. i = name === "width" ? 1 : 0,
  5604. len = 4;
  5605. if ( val > 0 ) {
  5606. if ( extra !== "border" ) {
  5607. for ( ; i < len; i += 2 ) {
  5608. if ( !extra ) {
  5609. val -= parseFloat( jQuery.css( elem, "padding" + cssExpand[ i ] ) ) || 0;
  5610. }
  5611. if ( extra === "margin" ) {
  5612. val += parseFloat( jQuery.css( elem, extra + cssExpand[ i ] ) ) || 0;
  5613. } else {
  5614. val -= parseFloat( jQuery.css( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0;
  5615. }
  5616. }
  5617. }
  5618. return val + "px";
  5619. }
  5620. // Fall back to computed then uncomputed css if necessary
  5621. val = curCSS( elem, name );
  5622. if ( val < 0 || val == null ) {
  5623. val = elem.style[ name ];
  5624. }
  5625. // Computed unit is not pixels. Stop here and return.
  5626. if ( rnumnonpx.test(val) ) {
  5627. return val;
  5628. }
  5629. // Normalize "", auto, and prepare for extra
  5630. val = parseFloat( val ) || 0;
  5631. // Add padding, border, margin
  5632. if ( extra ) {
  5633. for ( ; i < len; i += 2 ) {
  5634. val += parseFloat( jQuery.css( elem, "padding" + cssExpand[ i ] ) ) || 0;
  5635. if ( extra !== "padding" ) {
  5636. val += parseFloat( jQuery.css( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0;
  5637. }
  5638. if ( extra === "margin" ) {
  5639. val += parseFloat( jQuery.css( elem, extra + cssExpand[ i ]) ) || 0;
  5640. }
  5641. }
  5642. }
  5643. return val + "px";
  5644. }
  5645. jQuery.each([ "height", "width" ], function( i, name ) {
  5646. jQuery.cssHooks[ name ] = {
  5647. get: function( elem, computed, extra ) {
  5648. if ( computed ) {
  5649. if ( elem.offsetWidth !== 0 ) {
  5650. return getWidthOrHeight( elem, name, extra );
  5651. } else {
  5652. return jQuery.swap( elem, cssShow, function() {
  5653. return getWidthOrHeight( elem, name, extra );
  5654. });
  5655. }
  5656. }
  5657. },
  5658. set: function( elem, value ) {
  5659. return rnum.test( value ) ?
  5660. value + "px" :
  5661. value;
  5662. }
  5663. };
  5664. });
  5665. if ( !jQuery.support.opacity ) {
  5666. jQuery.cssHooks.opacity = {
  5667. get: function( elem, computed ) {
  5668. // IE uses filters for opacity
  5669. return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ?
  5670. ( parseFloat( RegExp.$1 ) / 100 ) + "" :
  5671. computed ? "1" : "";
  5672. },
  5673. set: function( elem, value ) {
  5674. var style = elem.style,
  5675. currentStyle = elem.currentStyle,
  5676. opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "",
  5677. filter = currentStyle && currentStyle.filter || style.filter || "";
  5678. // IE has trouble with opacity if it does not have layout
  5679. // Force it by setting the zoom level
  5680. style.zoom = 1;
  5681. // if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652
  5682. if ( value >= 1 && jQuery.trim( filter.replace( ralpha, "" ) ) === "" ) {
  5683. // Setting style.filter to null, "" & " " still leave "filter:" in the cssText
  5684. // if "filter:" is present at all, clearType is disabled, we want to avoid this
  5685. // style.removeAttribute is IE Only, but so apparently is this code path...
  5686. style.removeAttribute( "filter" );
  5687. // if there there is no filter style applied in a css rule, we are done
  5688. if ( currentStyle && !currentStyle.filter ) {
  5689. return;
  5690. }
  5691. }
  5692. // otherwise, set new filter values
  5693. style.filter = ralpha.test( filter ) ?
  5694. filter.replace( ralpha, opacity ) :
  5695. filter + " " + opacity;
  5696. }
  5697. };
  5698. }
  5699. jQuery(function() {
  5700. // This hook cannot be added until DOM ready because the support test
  5701. // for it is not run until after DOM ready
  5702. if ( !jQuery.support.reliableMarginRight ) {
  5703. jQuery.cssHooks.marginRight = {
  5704. get: function( elem, computed ) {
  5705. // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
  5706. // Work around by temporarily setting element display to inline-block
  5707. return jQuery.swap( elem, { "display": "inline-block" }, function() {
  5708. if ( computed ) {
  5709. return curCSS( elem, "margin-right" );
  5710. } else {
  5711. return elem.style.marginRight;
  5712. }
  5713. });
  5714. }
  5715. };
  5716. }
  5717. });
  5718. if ( jQuery.expr && jQuery.expr.filters ) {
  5719. jQuery.expr.filters.hidden = function( elem ) {
  5720. var width = elem.offsetWidth,
  5721. height = elem.offsetHeight;
  5722. return ( width === 0 && height === 0 ) || (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || jQuery.css( elem, "display" )) === "none");
  5723. };
  5724. jQuery.expr.filters.visible = function( elem ) {
  5725. return !jQuery.expr.filters.hidden( elem );
  5726. };
  5727. }
  5728. // These hooks are used by animate to expand properties
  5729. jQuery.each({
  5730. margin: "",
  5731. padding: "",
  5732. border: "Width"
  5733. }, function( prefix, suffix ) {
  5734. jQuery.cssHooks[ prefix + suffix ] = {
  5735. expand: function( value ) {
  5736. var i,
  5737. // assumes a single number if not a string
  5738. parts = typeof value === "string" ? value.split(" ") : [ value ],
  5739. expanded = {};
  5740. for ( i = 0; i < 4; i++ ) {
  5741. expanded[ prefix + cssExpand[ i ] + suffix ] =
  5742. parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
  5743. }
  5744. return expanded;
  5745. }
  5746. };
  5747. });
  5748. var r20 = /%20/g,
  5749. rbracket = /\[\]$/,
  5750. rCRLF = /\r?\n/g,
  5751. rhash = /#.*$/,
  5752. rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL
  5753. rinput = /^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,
  5754. // #7653, #8125, #8152: local protocol detection
  5755. rlocalProtocol = /^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,
  5756. rnoContent = /^(?:GET|HEAD)$/,
  5757. rprotocol = /^\/\//,
  5758. rquery = /\?/,
  5759. rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
  5760. rselectTextarea = /^(?:select|textarea)/i,
  5761. rspacesAjax = /\s+/,
  5762. rts = /([?&])_=[^&]*/,
  5763. rurl = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,
  5764. // Keep a copy of the old load method
  5765. _load = jQuery.fn.load,
  5766. /* Prefilters
  5767. * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
  5768. * 2) These are called:
  5769. * - BEFORE asking for a transport
  5770. * - AFTER param serialization (s.data is a string if s.processData is true)
  5771. * 3) key is the dataType
  5772. * 4) the catchall symbol "*" can be used
  5773. * 5) execution will start with transport dataType and THEN continue down to "*" if needed
  5774. */
  5775. prefilters = {},
  5776. /* Transports bindings
  5777. * 1) key is the dataType
  5778. * 2) the catchall symbol "*" can be used
  5779. * 3) selection will start with transport dataType and THEN go to "*" if needed
  5780. */
  5781. transports = {},
  5782. // Document location
  5783. ajaxLocation,
  5784. // Document location segments
  5785. ajaxLocParts,
  5786. // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
  5787. allTypes = ["*/"] + ["*"];
  5788. // #8138, IE may throw an exception when accessing
  5789. // a field from window.location if document.domain has been set
  5790. try {
  5791. ajaxLocation = location.href;
  5792. } catch( e ) {
  5793. // Use the href attribute of an A element
  5794. // since IE will modify it given document.location
  5795. ajaxLocation = document.createElement( "a" );
  5796. ajaxLocation.href = "";
  5797. ajaxLocation = ajaxLocation.href;
  5798. }
  5799. // Segment location into parts
  5800. ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
  5801. // Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
  5802. function addToPrefiltersOrTransports( structure ) {
  5803. // dataTypeExpression is optional and defaults to "*"
  5804. return function( dataTypeExpression, func ) {
  5805. if ( typeof dataTypeExpression !== "string" ) {
  5806. func = dataTypeExpression;
  5807. dataTypeExpression = "*";
  5808. }
  5809. if ( jQuery.isFunction( func ) ) {
  5810. var dataTypes = dataTypeExpression.toLowerCase().split( rspacesAjax ),
  5811. i = 0,
  5812. length = dataTypes.length,
  5813. dataType,
  5814. list,
  5815. placeBefore;
  5816. // For each dataType in the dataTypeExpression
  5817. for ( ; i < length; i++ ) {
  5818. dataType = dataTypes[ i ];
  5819. // We control if we're asked to add before
  5820. // any existing element
  5821. placeBefore = /^\+/.test( dataType );
  5822. if ( placeBefore ) {
  5823. dataType = dataType.substr( 1 ) || "*";
  5824. }
  5825. list = structure[ dataType ] = structure[ dataType ] || [];
  5826. // then we add to the structure accordingly
  5827. list[ placeBefore ? "unshift" : "push" ]( func );
  5828. }
  5829. }
  5830. };
  5831. }
  5832. // Base inspection function for prefilters and transports
  5833. function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR,
  5834. dataType /* internal */, inspected /* internal */ ) {
  5835. dataType = dataType || options.dataTypes[ 0 ];
  5836. inspected = inspected || {};
  5837. inspected[ dataType ] = true;
  5838. var list = structure[ dataType ],
  5839. i = 0,
  5840. length = list ? list.length : 0,
  5841. executeOnly = ( structure === prefilters ),
  5842. selection;
  5843. for ( ; i < length && ( executeOnly || !selection ); i++ ) {
  5844. selection = list[ i ]( options, originalOptions, jqXHR );
  5845. // If we got redirected to another dataType
  5846. // we try there if executing only and not done already
  5847. if ( typeof selection === "string" ) {
  5848. if ( !executeOnly || inspected[ selection ] ) {
  5849. selection = undefined;
  5850. } else {
  5851. options.dataTypes.unshift( selection );
  5852. selection = inspectPrefiltersOrTransports(
  5853. structure, options, originalOptions, jqXHR, selection, inspected );
  5854. }
  5855. }
  5856. }
  5857. // If we're only executing or nothing was selected
  5858. // we try the catchall dataType if not done already
  5859. if ( ( executeOnly || !selection ) && !inspected[ "*" ] ) {
  5860. selection = inspectPrefiltersOrTransports(
  5861. structure, options, originalOptions, jqXHR, "*", inspected );
  5862. }
  5863. // unnecessary when only executing (prefilters)
  5864. // but it'll be ignored by the caller in that case
  5865. return selection;
  5866. }
  5867. // A special extend for ajax options
  5868. // that takes "flat" options (not to be deep extended)
  5869. // Fixes #9887
  5870. function ajaxExtend( target, src ) {
  5871. var key, deep,
  5872. flatOptions = jQuery.ajaxSettings.flatOptions || {};
  5873. for ( key in src ) {
  5874. if ( src[ key ] !== undefined ) {
  5875. ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ];
  5876. }
  5877. }
  5878. if ( deep ) {
  5879. jQuery.extend( true, target, deep );
  5880. }
  5881. }
  5882. jQuery.fn.extend({
  5883. load: function( url, params, callback ) {
  5884. if ( typeof url !== "string" && _load ) {
  5885. return _load.apply( this, arguments );
  5886. // Don't do a request if no elements are being requested
  5887. } else if ( !this.length ) {
  5888. return this;
  5889. }
  5890. var off = url.indexOf( " " );
  5891. if ( off >= 0 ) {
  5892. var selector = url.slice( off, url.length );
  5893. url = url.slice( 0, off );
  5894. }
  5895. // Default to a GET request
  5896. var type = "GET";
  5897. // If the second parameter was provided
  5898. if ( params ) {
  5899. // If it's a function
  5900. if ( jQuery.isFunction( params ) ) {
  5901. // We assume that it's the callback
  5902. callback = params;
  5903. params = undefined;
  5904. // Otherwise, build a param string
  5905. } else if ( typeof params === "object" ) {
  5906. params = jQuery.param( params, jQuery.ajaxSettings.traditional );
  5907. type = "POST";
  5908. }
  5909. }
  5910. var self = this;
  5911. // Request the remote document
  5912. jQuery.ajax({
  5913. url: url,
  5914. type: type,
  5915. dataType: "html",
  5916. data: params,
  5917. // Complete callback (responseText is used internally)
  5918. complete: function( jqXHR, status, responseText ) {
  5919. // Store the response as specified by the jqXHR object
  5920. responseText = jqXHR.responseText;
  5921. // If successful, inject the HTML into all the matched elements
  5922. if ( jqXHR.isResolved() ) {
  5923. // #4825: Get the actual response in case
  5924. // a dataFilter is present in ajaxSettings
  5925. jqXHR.done(function( r ) {
  5926. responseText = r;
  5927. });
  5928. // See if a selector was specified
  5929. self.html( selector ?
  5930. // Create a dummy div to hold the results
  5931. jQuery("<div>")
  5932. // inject the contents of the document in, removing the scripts
  5933. // to avoid any 'Permission Denied' errors in IE
  5934. .append(responseText.replace(rscript, ""))
  5935. // Locate the specified elements
  5936. .find(selector) :
  5937. // If not, just inject the full result
  5938. responseText );
  5939. }
  5940. if ( callback ) {
  5941. self.each( callback, [ responseText, status, jqXHR ] );
  5942. }
  5943. }
  5944. });
  5945. return this;
  5946. },
  5947. serialize: function() {
  5948. return jQuery.param( this.serializeArray() );
  5949. },
  5950. serializeArray: function() {
  5951. return this.map(function(){
  5952. return this.elements ? jQuery.makeArray( this.elements ) : this;
  5953. })
  5954. .filter(function(){
  5955. return this.name && !this.disabled &&
  5956. ( this.checked || rselectTextarea.test( this.nodeName ) ||
  5957. rinput.test( this.type ) );
  5958. })
  5959. .map(function( i, elem ){
  5960. var val = jQuery( this ).val();
  5961. return val == null ?
  5962. null :
  5963. jQuery.isArray( val ) ?
  5964. jQuery.map( val, function( val, i ){
  5965. return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
  5966. }) :
  5967. { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
  5968. }).get();
  5969. }
  5970. });
  5971. // Attach a bunch of functions for handling common AJAX events
  5972. jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split( " " ), function( i, o ){
  5973. jQuery.fn[ o ] = function( f ){
  5974. return this.on( o, f );
  5975. };
  5976. });
  5977. jQuery.each( [ "get", "post" ], function( i, method ) {
  5978. jQuery[ method ] = function( url, data, callback, type ) {
  5979. // shift arguments if data argument was omitted
  5980. if ( jQuery.isFunction( data ) ) {
  5981. type = type || callback;
  5982. callback = data;
  5983. data = undefined;
  5984. }
  5985. return jQuery.ajax({
  5986. type: method,
  5987. url: url,
  5988. data: data,
  5989. success: callback,
  5990. dataType: type
  5991. });
  5992. };
  5993. });
  5994. jQuery.extend({
  5995. getScript: function( url, callback ) {
  5996. return jQuery.get( url, undefined, callback, "script" );
  5997. },
  5998. getJSON: function( url, data, callback ) {
  5999. return jQuery.get( url, data, callback, "json" );
  6000. },
  6001. // Creates a full fledged settings object into target
  6002. // with both ajaxSettings and settings fields.
  6003. // If target is omitted, writes into ajaxSettings.
  6004. ajaxSetup: function( target, settings ) {
  6005. if ( settings ) {
  6006. // Building a settings object
  6007. ajaxExtend( target, jQuery.ajaxSettings );
  6008. } else {
  6009. // Extending ajaxSettings
  6010. settings = target;
  6011. target = jQuery.ajaxSettings;
  6012. }
  6013. ajaxExtend( target, settings );
  6014. return target;
  6015. },
  6016. ajaxSettings: {
  6017. url: ajaxLocation,
  6018. isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
  6019. global: true,
  6020. type: "GET",
  6021. contentType: "application/x-www-form-urlencoded; charset=UTF-8",
  6022. processData: true,
  6023. async: true,
  6024. /*
  6025. timeout: 0,
  6026. data: null,
  6027. dataType: null,
  6028. username: null,
  6029. password: null,
  6030. cache: null,
  6031. traditional: false,
  6032. headers: {},
  6033. */
  6034. accepts: {
  6035. xml: "application/xml, text/xml",
  6036. html: "text/html",
  6037. text: "text/plain",
  6038. json: "application/json, text/javascript",
  6039. "*": allTypes
  6040. },
  6041. contents: {
  6042. xml: /xml/,
  6043. html: /html/,
  6044. json: /json/
  6045. },
  6046. responseFields: {
  6047. xml: "responseXML",
  6048. text: "responseText"
  6049. },
  6050. // List of data converters
  6051. // 1) key format is "source_type destination_type" (a single space in-between)
  6052. // 2) the catchall symbol "*" can be used for source_type
  6053. converters: {
  6054. // Convert anything to text
  6055. "* text": window.String,
  6056. // Text to html (true = no transformation)
  6057. "text html": true,
  6058. // Evaluate text as a json expression
  6059. "text json": jQuery.parseJSON,
  6060. // Parse text as xml
  6061. "text xml": jQuery.parseXML
  6062. },
  6063. // For options that shouldn't be deep extended:
  6064. // you can add your own custom options here if
  6065. // and when you create one that shouldn't be
  6066. // deep extended (see ajaxExtend)
  6067. flatOptions: {
  6068. context: true,
  6069. url: true
  6070. }
  6071. },
  6072. ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
  6073. ajaxTransport: addToPrefiltersOrTransports( transports ),
  6074. // Main method
  6075. ajax: function( url, options ) {
  6076. // If url is an object, simulate pre-1.5 signature
  6077. if ( typeof url === "object" ) {
  6078. options = url;
  6079. url = undefined;
  6080. }
  6081. // Force options to be an object
  6082. options = options || {};
  6083. var // Create the final options object
  6084. s = jQuery.ajaxSetup( {}, options ),
  6085. // Callbacks context
  6086. callbackContext = s.context || s,
  6087. // Context for global events
  6088. // It's the callbackContext if one was provided in the options
  6089. // and if it's a DOM node or a jQuery collection
  6090. globalEventContext = callbackContext !== s &&
  6091. ( callbackContext.nodeType || callbackContext instanceof jQuery ) ?
  6092. jQuery( callbackContext ) : jQuery.event,
  6093. // Deferreds
  6094. deferred = jQuery.Deferred(),
  6095. completeDeferred = jQuery.Callbacks( "once memory" ),
  6096. // Status-dependent callbacks
  6097. statusCode = s.statusCode || {},
  6098. // ifModified key
  6099. ifModifiedKey,
  6100. // Headers (they are sent all at once)
  6101. requestHeaders = {},
  6102. requestHeadersNames = {},
  6103. // Response headers
  6104. responseHeadersString,
  6105. responseHeaders,
  6106. // transport
  6107. transport,
  6108. // timeout handle
  6109. timeoutTimer,
  6110. // Cross-domain detection vars
  6111. parts,
  6112. // The jqXHR state
  6113. state = 0,
  6114. // To know if global events are to be dispatched
  6115. fireGlobals,
  6116. // Loop variable
  6117. i,
  6118. // Fake xhr
  6119. jqXHR = {
  6120. readyState: 0,
  6121. // Caches the header
  6122. setRequestHeader: function( name, value ) {
  6123. if ( !state ) {
  6124. var lname = name.toLowerCase();
  6125. name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
  6126. requestHeaders[ name ] = value;
  6127. }
  6128. return this;
  6129. },
  6130. // Raw string
  6131. getAllResponseHeaders: function() {
  6132. return state === 2 ? responseHeadersString : null;
  6133. },
  6134. // Builds headers hashtable if needed
  6135. getResponseHeader: function( key ) {
  6136. var match;
  6137. if ( state === 2 ) {
  6138. if ( !responseHeaders ) {
  6139. responseHeaders = {};
  6140. while( ( match = rheaders.exec( responseHeadersString ) ) ) {
  6141. responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
  6142. }
  6143. }
  6144. match = responseHeaders[ key.toLowerCase() ];
  6145. }
  6146. return match === undefined ? null : match;
  6147. },
  6148. // Overrides response content-type header
  6149. overrideMimeType: function( type ) {
  6150. if ( !state ) {
  6151. s.mimeType = type;
  6152. }
  6153. return this;
  6154. },
  6155. // Cancel the request
  6156. abort: function( statusText ) {
  6157. statusText = statusText || "abort";
  6158. if ( transport ) {
  6159. transport.abort( statusText );
  6160. }
  6161. done( 0, statusText );
  6162. return this;
  6163. }
  6164. };
  6165. // Callback for when everything is done
  6166. // It is defined here because jslint complains if it is declared
  6167. // at the end of the function (which would be more logical and readable)
  6168. function done( status, nativeStatusText, responses, headers ) {
  6169. // Called once
  6170. if ( state === 2 ) {
  6171. return;
  6172. }
  6173. // State is "done" now
  6174. state = 2;
  6175. // Clear timeout if it exists
  6176. if ( timeoutTimer ) {
  6177. clearTimeout( timeoutTimer );
  6178. }
  6179. // Dereference transport for early garbage collection
  6180. // (no matter how long the jqXHR object will be used)
  6181. transport = undefined;
  6182. // Cache response headers
  6183. responseHeadersString = headers || "";
  6184. // Set readyState
  6185. jqXHR.readyState = status > 0 ? 4 : 0;
  6186. var isSuccess,
  6187. success,
  6188. error,
  6189. statusText = nativeStatusText,
  6190. response = responses ? ajaxHandleResponses( s, jqXHR, responses ) : undefined,
  6191. lastModified,
  6192. etag;
  6193. // If successful, handle type chaining
  6194. if ( status >= 200 && status < 300 || status === 304 ) {
  6195. // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
  6196. if ( s.ifModified ) {
  6197. if ( ( lastModified = jqXHR.getResponseHeader( "Last-Modified" ) ) ) {
  6198. jQuery.lastModified[ ifModifiedKey ] = lastModified;
  6199. }
  6200. if ( ( etag = jqXHR.getResponseHeader( "Etag" ) ) ) {
  6201. jQuery.etag[ ifModifiedKey ] = etag;
  6202. }
  6203. }
  6204. // If not modified
  6205. if ( status === 304 ) {
  6206. statusText = "notmodified";
  6207. isSuccess = true;
  6208. // If we have data
  6209. } else {
  6210. try {
  6211. success = ajaxConvert( s, response );
  6212. statusText = "success";
  6213. isSuccess = true;
  6214. } catch(e) {
  6215. // We have a parsererror
  6216. statusText = "parsererror";
  6217. error = e;
  6218. }
  6219. }
  6220. } else {
  6221. // We extract error from statusText
  6222. // then normalize statusText and status for non-aborts
  6223. error = statusText;
  6224. if ( !statusText || status ) {
  6225. statusText = "error";
  6226. if ( status < 0 ) {
  6227. status = 0;
  6228. }
  6229. }
  6230. }
  6231. // Set data for the fake xhr object
  6232. jqXHR.status = status;
  6233. jqXHR.statusText = "" + ( nativeStatusText || statusText );
  6234. // Success/Error
  6235. if ( isSuccess ) {
  6236. deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
  6237. } else {
  6238. deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
  6239. }
  6240. // Status-dependent callbacks
  6241. jqXHR.statusCode( statusCode );
  6242. statusCode = undefined;
  6243. if ( fireGlobals ) {
  6244. globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ),
  6245. [ jqXHR, s, isSuccess ? success : error ] );
  6246. }
  6247. // Complete
  6248. completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
  6249. if ( fireGlobals ) {
  6250. globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
  6251. // Handle the global AJAX counter
  6252. if ( !( --jQuery.active ) ) {
  6253. jQuery.event.trigger( "ajaxStop" );
  6254. }
  6255. }
  6256. }
  6257. // Attach deferreds
  6258. deferred.promise( jqXHR );
  6259. jqXHR.success = jqXHR.done;
  6260. jqXHR.error = jqXHR.fail;
  6261. jqXHR.complete = completeDeferred.add;
  6262. // Status-dependent callbacks
  6263. jqXHR.statusCode = function( map ) {
  6264. if ( map ) {
  6265. var tmp;
  6266. if ( state < 2 ) {
  6267. for ( tmp in map ) {
  6268. statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ];
  6269. }
  6270. } else {
  6271. tmp = map[ jqXHR.status ];
  6272. jqXHR.then( tmp, tmp );
  6273. }
  6274. }
  6275. return this;
  6276. };
  6277. // Remove hash character (#7531: and string promotion)
  6278. // Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
  6279. // We also use the url parameter if available
  6280. s.url = ( ( url || s.url ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
  6281. // Extract dataTypes list
  6282. s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( rspacesAjax );
  6283. // Determine if a cross-domain request is in order
  6284. if ( s.crossDomain == null ) {
  6285. parts = rurl.exec( s.url.toLowerCase() );
  6286. s.crossDomain = !!( parts &&
  6287. ( parts[ 1 ] != ajaxLocParts[ 1 ] || parts[ 2 ] != ajaxLocParts[ 2 ] ||
  6288. ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) !=
  6289. ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) )
  6290. );
  6291. }
  6292. // Convert data if not already a string
  6293. if ( s.data && s.processData && typeof s.data !== "string" ) {
  6294. s.data = jQuery.param( s.data, s.traditional );
  6295. }
  6296. // Apply prefilters
  6297. inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
  6298. // If request was aborted inside a prefilter, stop there
  6299. if ( state === 2 ) {
  6300. return false;
  6301. }
  6302. // We can fire global events as of now if asked to
  6303. fireGlobals = s.global;
  6304. // Uppercase the type
  6305. s.type = s.type.toUpperCase();
  6306. // Determine if request has content
  6307. s.hasContent = !rnoContent.test( s.type );
  6308. // Watch for a new set of requests
  6309. if ( fireGlobals && jQuery.active++ === 0 ) {
  6310. jQuery.event.trigger( "ajaxStart" );
  6311. }
  6312. // More options handling for requests with no content
  6313. if ( !s.hasContent ) {
  6314. // If data is available, append data to url
  6315. if ( s.data ) {
  6316. s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data;
  6317. // #9682: remove data so that it's not used in an eventual retry
  6318. delete s.data;
  6319. }
  6320. // Get ifModifiedKey before adding the anti-cache parameter
  6321. ifModifiedKey = s.url;
  6322. // Add anti-cache in url if needed
  6323. if ( s.cache === false ) {
  6324. var ts = jQuery.now(),
  6325. // try replacing _= if it is there
  6326. ret = s.url.replace( rts, "$1_=" + ts );
  6327. // if nothing was replaced, add timestamp to the end
  6328. s.url = ret + ( ( ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" );
  6329. }
  6330. }
  6331. // Set the correct header, if data is being sent
  6332. if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
  6333. jqXHR.setRequestHeader( "Content-Type", s.contentType );
  6334. }
  6335. // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
  6336. if ( s.ifModified ) {
  6337. ifModifiedKey = ifModifiedKey || s.url;
  6338. if ( jQuery.lastModified[ ifModifiedKey ] ) {
  6339. jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ ifModifiedKey ] );
  6340. }
  6341. if ( jQuery.etag[ ifModifiedKey ] ) {
  6342. jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ ifModifiedKey ] );
  6343. }
  6344. }
  6345. // Set the Accepts header for the server, depending on the dataType
  6346. jqXHR.setRequestHeader(
  6347. "Accept",
  6348. s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
  6349. s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
  6350. s.accepts[ "*" ]
  6351. );
  6352. // Check for headers option
  6353. for ( i in s.headers ) {
  6354. jqXHR.setRequestHeader( i, s.headers[ i ] );
  6355. }
  6356. // Allow custom headers/mimetypes and early abort
  6357. if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
  6358. // Abort if not done already
  6359. jqXHR.abort();
  6360. return false;
  6361. }
  6362. // Install callbacks on deferreds
  6363. for ( i in { success: 1, error: 1, complete: 1 } ) {
  6364. jqXHR[ i ]( s[ i ] );
  6365. }
  6366. // Get transport
  6367. transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
  6368. // If no transport, we auto-abort
  6369. if ( !transport ) {
  6370. done( -1, "No Transport" );
  6371. } else {
  6372. jqXHR.readyState = 1;
  6373. // Send global event
  6374. if ( fireGlobals ) {
  6375. globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
  6376. }
  6377. // Timeout
  6378. if ( s.async && s.timeout > 0 ) {
  6379. timeoutTimer = setTimeout( function(){
  6380. jqXHR.abort( "timeout" );
  6381. }, s.timeout );
  6382. }
  6383. try {
  6384. state = 1;
  6385. transport.send( requestHeaders, done );
  6386. } catch (e) {
  6387. // Propagate exception as error if not done
  6388. if ( state < 2 ) {
  6389. done( -1, e );
  6390. // Simply rethrow otherwise
  6391. } else {
  6392. throw e;
  6393. }
  6394. }
  6395. }
  6396. return jqXHR;
  6397. },
  6398. // Serialize an array of form elements or a set of
  6399. // key/values into a query string
  6400. param: function( a, traditional ) {
  6401. var s = [],
  6402. add = function( key, value ) {
  6403. // If value is a function, invoke it and return its value
  6404. value = jQuery.isFunction( value ) ? value() : value;
  6405. s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
  6406. };
  6407. // Set traditional to true for jQuery <= 1.3.2 behavior.
  6408. if ( traditional === undefined ) {
  6409. traditional = jQuery.ajaxSettings.traditional;
  6410. }
  6411. // If an array was passed in, assume that it is an array of form elements.
  6412. if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
  6413. // Serialize the form elements
  6414. jQuery.each( a, function() {
  6415. add( this.name, this.value );
  6416. });
  6417. } else {
  6418. // If traditional, encode the "old" way (the way 1.3.2 or older
  6419. // did it), otherwise encode params recursively.
  6420. for ( var prefix in a ) {
  6421. buildParams( prefix, a[ prefix ], traditional, add );
  6422. }
  6423. }
  6424. // Return the resulting serialization
  6425. return s.join( "&" ).replace( r20, "+" );
  6426. }
  6427. });
  6428. function buildParams( prefix, obj, traditional, add ) {
  6429. if ( jQuery.isArray( obj ) ) {
  6430. // Serialize array item.
  6431. jQuery.each( obj, function( i, v ) {
  6432. if ( traditional || rbracket.test( prefix ) ) {
  6433. // Treat each array item as a scalar.
  6434. add( prefix, v );
  6435. } else {
  6436. // If array item is non-scalar (array or object), encode its
  6437. // numeric index to resolve deserialization ambiguity issues.
  6438. // Note that rack (as of 1.0.0) can't currently deserialize
  6439. // nested arrays properly, and attempting to do so may cause
  6440. // a server error. Possible fixes are to modify rack's
  6441. // deserialization algorithm or to provide an option or flag
  6442. // to force array serialization to be shallow.
  6443. buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add );
  6444. }
  6445. });
  6446. } else if ( !traditional && jQuery.type( obj ) === "object" ) {
  6447. // Serialize object item.
  6448. for ( var name in obj ) {
  6449. buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
  6450. }
  6451. } else {
  6452. // Serialize scalar item.
  6453. add( prefix, obj );
  6454. }
  6455. }
  6456. // This is still on the jQuery object... for now
  6457. // Want to move this to jQuery.ajax some day
  6458. jQuery.extend({
  6459. // Counter for holding the number of active queries
  6460. active: 0,
  6461. // Last-Modified header cache for next request
  6462. lastModified: {},
  6463. etag: {}
  6464. });
  6465. /* Handles responses to an ajax request:
  6466. * - sets all responseXXX fields accordingly
  6467. * - finds the right dataType (mediates between content-type and expected dataType)
  6468. * - returns the corresponding response
  6469. */
  6470. function ajaxHandleResponses( s, jqXHR, responses ) {
  6471. var contents = s.contents,
  6472. dataTypes = s.dataTypes,
  6473. responseFields = s.responseFields,
  6474. ct,
  6475. type,
  6476. finalDataType,
  6477. firstDataType;
  6478. // Fill responseXXX fields
  6479. for ( type in responseFields ) {
  6480. if ( type in responses ) {
  6481. jqXHR[ responseFields[type] ] = responses[ type ];
  6482. }
  6483. }
  6484. // Remove auto dataType and get content-type in the process
  6485. while( dataTypes[ 0 ] === "*" ) {
  6486. dataTypes.shift();
  6487. if ( ct === undefined ) {
  6488. ct = s.mimeType || jqXHR.getResponseHeader( "content-type" );
  6489. }
  6490. }
  6491. // Check if we're dealing with a known content-type
  6492. if ( ct ) {
  6493. for ( type in contents ) {
  6494. if ( contents[ type ] && contents[ type ].test( ct ) ) {
  6495. dataTypes.unshift( type );
  6496. break;
  6497. }
  6498. }
  6499. }
  6500. // Check to see if we have a response for the expected dataType
  6501. if ( dataTypes[ 0 ] in responses ) {
  6502. finalDataType = dataTypes[ 0 ];
  6503. } else {
  6504. // Try convertible dataTypes
  6505. for ( type in responses ) {
  6506. if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
  6507. finalDataType = type;
  6508. break;
  6509. }
  6510. if ( !firstDataType ) {
  6511. firstDataType = type;
  6512. }
  6513. }
  6514. // Or just use first one
  6515. finalDataType = finalDataType || firstDataType;
  6516. }
  6517. // If we found a dataType
  6518. // We add the dataType to the list if needed
  6519. // and return the corresponding response
  6520. if ( finalDataType ) {
  6521. if ( finalDataType !== dataTypes[ 0 ] ) {
  6522. dataTypes.unshift( finalDataType );
  6523. }
  6524. return responses[ finalDataType ];
  6525. }
  6526. }
  6527. // Chain conversions given the request and the original response
  6528. function ajaxConvert( s, response ) {
  6529. // Apply the dataFilter if provided
  6530. if ( s.dataFilter ) {
  6531. response = s.dataFilter( response, s.dataType );
  6532. }
  6533. var dataTypes = s.dataTypes,
  6534. converters = {},
  6535. i,
  6536. key,
  6537. length = dataTypes.length,
  6538. tmp,
  6539. // Current and previous dataTypes
  6540. current = dataTypes[ 0 ],
  6541. prev,
  6542. // Conversion expression
  6543. conversion,
  6544. // Conversion function
  6545. conv,
  6546. // Conversion functions (transitive conversion)
  6547. conv1,
  6548. conv2;
  6549. // For each dataType in the chain
  6550. for ( i = 1; i < length; i++ ) {
  6551. // Create converters map
  6552. // with lowercased keys
  6553. if ( i === 1 ) {
  6554. for ( key in s.converters ) {
  6555. if ( typeof key === "string" ) {
  6556. converters[ key.toLowerCase() ] = s.converters[ key ];
  6557. }
  6558. }
  6559. }
  6560. // Get the dataTypes
  6561. prev = current;
  6562. current = dataTypes[ i ];
  6563. // If current is auto dataType, update it to prev
  6564. if ( current === "*" ) {
  6565. current = prev;
  6566. // If no auto and dataTypes are actually different
  6567. } else if ( prev !== "*" && prev !== current ) {
  6568. // Get the converter
  6569. conversion = prev + " " + current;
  6570. conv = converters[ conversion ] || converters[ "* " + current ];
  6571. // If there is no direct converter, search transitively
  6572. if ( !conv ) {
  6573. conv2 = undefined;
  6574. for ( conv1 in converters ) {
  6575. tmp = conv1.split( " " );
  6576. if ( tmp[ 0 ] === prev || tmp[ 0 ] === "*" ) {
  6577. conv2 = converters[ tmp[1] + " " + current ];
  6578. if ( conv2 ) {
  6579. conv1 = converters[ conv1 ];
  6580. if ( conv1 === true ) {
  6581. conv = conv2;
  6582. } else if ( conv2 === true ) {
  6583. conv = conv1;
  6584. }
  6585. break;
  6586. }
  6587. }
  6588. }
  6589. }
  6590. // If we found no converter, dispatch an error
  6591. if ( !( conv || conv2 ) ) {
  6592. jQuery.error( "No conversion from " + conversion.replace(" "," to ") );
  6593. }
  6594. // If found converter is not an equivalence
  6595. if ( conv !== true ) {
  6596. // Convert with 1 or 2 converters accordingly
  6597. response = conv ? conv( response ) : conv2( conv1(response) );
  6598. }
  6599. }
  6600. }
  6601. return response;
  6602. }
  6603. var jsc = jQuery.now(),
  6604. jsre = /(\=)\?(&|$)|\?\?/i;
  6605. // Default jsonp settings
  6606. jQuery.ajaxSetup({
  6607. jsonp: "callback",
  6608. jsonpCallback: function() {
  6609. return jQuery.expando + "_" + ( jsc++ );
  6610. }
  6611. });
  6612. // Detect, normalize options and install callbacks for jsonp requests
  6613. jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
  6614. var inspectData = ( typeof s.data === "string" ) && /^application\/x\-www\-form\-urlencoded/.test( s.contentType );
  6615. if ( s.dataTypes[ 0 ] === "jsonp" ||
  6616. s.jsonp !== false && ( jsre.test( s.url ) ||
  6617. inspectData && jsre.test( s.data ) ) ) {
  6618. var responseContainer,
  6619. jsonpCallback = s.jsonpCallback =
  6620. jQuery.isFunction( s.jsonpCallback ) ? s.jsonpCallback() : s.jsonpCallback,
  6621. previous = window[ jsonpCallback ],
  6622. url = s.url,
  6623. data = s.data,
  6624. replace = "$1" + jsonpCallback + "$2";
  6625. if ( s.jsonp !== false ) {
  6626. url = url.replace( jsre, replace );
  6627. if ( s.url === url ) {
  6628. if ( inspectData ) {
  6629. data = data.replace( jsre, replace );
  6630. }
  6631. if ( s.data === data ) {
  6632. // Add callback manually
  6633. url += (/\?/.test( url ) ? "&" : "?") + s.jsonp + "=" + jsonpCallback;
  6634. }
  6635. }
  6636. }
  6637. s.url = url;
  6638. s.data = data;
  6639. // Install callback
  6640. window[ jsonpCallback ] = function( response ) {
  6641. responseContainer = [ response ];
  6642. };
  6643. // Clean-up function
  6644. jqXHR.always(function() {
  6645. // Set callback back to previous value
  6646. window[ jsonpCallback ] = previous;
  6647. // Call if it was a function and we have a response
  6648. if ( responseContainer && jQuery.isFunction( previous ) ) {
  6649. window[ jsonpCallback ]( responseContainer[ 0 ] );
  6650. }
  6651. });
  6652. // Use data converter to retrieve json after script execution
  6653. s.converters["script json"] = function() {
  6654. if ( !responseContainer ) {
  6655. jQuery.error( jsonpCallback + " was not called" );
  6656. }
  6657. return responseContainer[ 0 ];
  6658. };
  6659. // force json dataType
  6660. s.dataTypes[ 0 ] = "json";
  6661. // Delegate to script
  6662. return "script";
  6663. }
  6664. });
  6665. // Install script dataType
  6666. jQuery.ajaxSetup({
  6667. accepts: {
  6668. script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
  6669. },
  6670. contents: {
  6671. script: /javascript|ecmascript/
  6672. },
  6673. converters: {
  6674. "text script": function( text ) {
  6675. jQuery.globalEval( text );
  6676. return text;
  6677. }
  6678. }
  6679. });
  6680. // Handle cache's special case and global
  6681. jQuery.ajaxPrefilter( "script", function( s ) {
  6682. if ( s.cache === undefined ) {
  6683. s.cache = false;
  6684. }
  6685. if ( s.crossDomain ) {
  6686. s.type = "GET";
  6687. s.global = false;
  6688. }
  6689. });
  6690. // Bind script tag hack transport
  6691. jQuery.ajaxTransport( "script", function(s) {
  6692. // This transport only deals with cross domain requests
  6693. if ( s.crossDomain ) {
  6694. var script,
  6695. head = document.head || document.getElementsByTagName( "head" )[0] || document.documentElement;
  6696. return {
  6697. send: function( _, callback ) {
  6698. script = document.createElement( "script" );
  6699. script.async = "async";
  6700. if ( s.scriptCharset ) {
  6701. script.charset = s.scriptCharset;
  6702. }
  6703. script.src = s.url;
  6704. // Attach handlers for all browsers
  6705. script.onload = script.onreadystatechange = function( _, isAbort ) {
  6706. if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {
  6707. // Handle memory leak in IE
  6708. script.onload = script.onreadystatechange = null;
  6709. // Remove the script
  6710. if ( head && script.parentNode ) {
  6711. head.removeChild( script );
  6712. }
  6713. // Dereference the script
  6714. script = undefined;
  6715. // Callback if not abort
  6716. if ( !isAbort ) {
  6717. callback( 200, "success" );
  6718. }
  6719. }
  6720. };
  6721. // Use insertBefore instead of appendChild to circumvent an IE6 bug.
  6722. // This arises when a base node is used (#2709 and #4378).
  6723. head.insertBefore( script, head.firstChild );
  6724. },
  6725. abort: function() {
  6726. if ( script ) {
  6727. script.onload( 0, 1 );
  6728. }
  6729. }
  6730. };
  6731. }
  6732. });
  6733. var // #5280: Internet Explorer will keep connections alive if we don't abort on unload
  6734. xhrOnUnloadAbort = window.ActiveXObject ? function() {
  6735. // Abort all pending requests
  6736. for ( var key in xhrCallbacks ) {
  6737. xhrCallbacks[ key ]( 0, 1 );
  6738. }
  6739. } : false,
  6740. xhrId = 0,
  6741. xhrCallbacks;
  6742. // Functions to create xhrs
  6743. function createStandardXHR() {
  6744. try {
  6745. return new window.XMLHttpRequest();
  6746. } catch( e ) {}
  6747. }
  6748. function createActiveXHR() {
  6749. try {
  6750. return new window.ActiveXObject( "Microsoft.XMLHTTP" );
  6751. } catch( e ) {}
  6752. }
  6753. // Create the request object
  6754. // (This is still attached to ajaxSettings for backward compatibility)
  6755. jQuery.ajaxSettings.xhr = window.ActiveXObject ?
  6756. /* Microsoft failed to properly
  6757. * implement the XMLHttpRequest in IE7 (can't request local files),
  6758. * so we use the ActiveXObject when it is available
  6759. * Additionally XMLHttpRequest can be disabled in IE7/IE8 so
  6760. * we need a fallback.
  6761. */
  6762. function() {
  6763. return !this.isLocal && createStandardXHR() || createActiveXHR();
  6764. } :
  6765. // For all other browsers, use the standard XMLHttpRequest object
  6766. createStandardXHR;
  6767. // Determine support properties
  6768. (function( xhr ) {
  6769. jQuery.extend( jQuery.support, {
  6770. ajax: !!xhr,
  6771. cors: !!xhr && ( "withCredentials" in xhr )
  6772. });
  6773. })( jQuery.ajaxSettings.xhr() );
  6774. // Create transport if the browser can provide an xhr
  6775. if ( jQuery.support.ajax ) {
  6776. jQuery.ajaxTransport(function( s ) {
  6777. // Cross domain only allowed if supported through XMLHttpRequest
  6778. if ( !s.crossDomain || jQuery.support.cors ) {
  6779. var callback;
  6780. return {
  6781. send: function( headers, complete ) {
  6782. // Get a new xhr
  6783. var xhr = s.xhr(),
  6784. handle,
  6785. i;
  6786. // Open the socket
  6787. // Passing null username, generates a login popup on Opera (#2865)
  6788. if ( s.username ) {
  6789. xhr.open( s.type, s.url, s.async, s.username, s.password );
  6790. } else {
  6791. xhr.open( s.type, s.url, s.async );
  6792. }
  6793. // Apply custom fields if provided
  6794. if ( s.xhrFields ) {
  6795. for ( i in s.xhrFields ) {
  6796. xhr[ i ] = s.xhrFields[ i ];
  6797. }
  6798. }
  6799. // Override mime type if needed
  6800. if ( s.mimeType && xhr.overrideMimeType ) {
  6801. xhr.overrideMimeType( s.mimeType );
  6802. }
  6803. // X-Requested-With header
  6804. // For cross-domain requests, seeing as conditions for a preflight are
  6805. // akin to a jigsaw puzzle, we simply never set it to be sure.
  6806. // (it can always be set on a per-request basis or even using ajaxSetup)
  6807. // For same-domain requests, won't change header if already provided.
  6808. if ( !s.crossDomain && !headers["X-Requested-With"] ) {
  6809. headers[ "X-Requested-With" ] = "XMLHttpRequest";
  6810. }
  6811. // Need an extra try/catch for cross domain requests in Firefox 3
  6812. try {
  6813. for ( i in headers ) {
  6814. xhr.setRequestHeader( i, headers[ i ] );
  6815. }
  6816. } catch( _ ) {}
  6817. // Do send the request
  6818. // This may raise an exception which is actually
  6819. // handled in jQuery.ajax (so no try/catch here)
  6820. xhr.send( ( s.hasContent && s.data ) || null );
  6821. // Listener
  6822. callback = function( _, isAbort ) {
  6823. var status,
  6824. statusText,
  6825. responseHeaders,
  6826. responses,
  6827. xml;
  6828. // Firefox throws exceptions when accessing properties
  6829. // of an xhr when a network error occured
  6830. // http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE)
  6831. try {
  6832. // Was never called and is aborted or complete
  6833. if ( callback && ( isAbort || xhr.readyState === 4 ) ) {
  6834. // Only called once
  6835. callback = undefined;
  6836. // Do not keep as active anymore
  6837. if ( handle ) {
  6838. xhr.onreadystatechange = jQuery.noop;
  6839. if ( xhrOnUnloadAbort ) {
  6840. delete xhrCallbacks[ handle ];
  6841. }
  6842. }
  6843. // If it's an abort
  6844. if ( isAbort ) {
  6845. // Abort it manually if needed
  6846. if ( xhr.readyState !== 4 ) {
  6847. xhr.abort();
  6848. }
  6849. } else {
  6850. status = xhr.status;
  6851. responseHeaders = xhr.getAllResponseHeaders();
  6852. responses = {};
  6853. xml = xhr.responseXML;
  6854. // Construct response list
  6855. if ( xml && xml.documentElement /* #4958 */ ) {
  6856. responses.xml = xml;
  6857. }
  6858. // When requesting binary data, IE6-9 will throw an exception
  6859. // on any attempt to access responseText (#11426)
  6860. try {
  6861. responses.text = xhr.responseText;
  6862. } catch( _ ) {
  6863. }
  6864. // Firefox throws an exception when accessing
  6865. // statusText for faulty cross-domain requests
  6866. try {
  6867. statusText = xhr.statusText;
  6868. } catch( e ) {
  6869. // We normalize with Webkit giving an empty statusText
  6870. statusText = "";
  6871. }
  6872. // Filter status for non standard behaviors
  6873. // If the request is local and we have data: assume a success
  6874. // (success with no data won't get notified, that's the best we
  6875. // can do given current implementations)
  6876. if ( !status && s.isLocal && !s.crossDomain ) {
  6877. status = responses.text ? 200 : 404;
  6878. // IE - #1450: sometimes returns 1223 when it should be 204
  6879. } else if ( status === 1223 ) {
  6880. status = 204;
  6881. }
  6882. }
  6883. }
  6884. } catch( firefoxAccessException ) {
  6885. if ( !isAbort ) {
  6886. complete( -1, firefoxAccessException );
  6887. }
  6888. }
  6889. // Call complete if needed
  6890. if ( responses ) {
  6891. complete( status, statusText, responses, responseHeaders );
  6892. }
  6893. };
  6894. // if we're in sync mode or it's in cache
  6895. // and has been retrieved directly (IE6 & IE7)
  6896. // we need to manually fire the callback
  6897. if ( !s.async || xhr.readyState === 4 ) {
  6898. callback();
  6899. } else {
  6900. handle = ++xhrId;
  6901. if ( xhrOnUnloadAbort ) {
  6902. // Create the active xhrs callbacks list if needed
  6903. // and attach the unload handler
  6904. if ( !xhrCallbacks ) {
  6905. xhrCallbacks = {};
  6906. jQuery( window ).unload( xhrOnUnloadAbort );
  6907. }
  6908. // Add to list of active xhrs callbacks
  6909. xhrCallbacks[ handle ] = callback;
  6910. }
  6911. xhr.onreadystatechange = callback;
  6912. }
  6913. },
  6914. abort: function() {
  6915. if ( callback ) {
  6916. callback(0,1);
  6917. }
  6918. }
  6919. };
  6920. }
  6921. });
  6922. }
  6923. var elemdisplay = {},
  6924. iframe, iframeDoc,
  6925. rfxtypes = /^(?:toggle|show|hide)$/,
  6926. rfxnum = /^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,
  6927. timerId,
  6928. fxAttrs = [
  6929. // height animations
  6930. [ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
  6931. // width animations
  6932. [ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
  6933. // opacity animations
  6934. [ "opacity" ]
  6935. ],
  6936. fxNow;
  6937. jQuery.fn.extend({
  6938. show: function( speed, easing, callback ) {
  6939. var elem, display;
  6940. if ( speed || speed === 0 ) {
  6941. return this.animate( genFx("show", 3), speed, easing, callback );
  6942. } else {
  6943. for ( var i = 0, j = this.length; i < j; i++ ) {
  6944. elem = this[ i ];
  6945. if ( elem.style ) {
  6946. display = elem.style.display;
  6947. // Reset the inline display of this element to learn if it is
  6948. // being hidden by cascaded rules or not
  6949. if ( !jQuery._data(elem, "olddisplay") && display === "none" ) {
  6950. display = elem.style.display = "";
  6951. }
  6952. // Set elements which have been overridden with display: none
  6953. // in a stylesheet to whatever the default browser style is
  6954. // for such an element
  6955. if ( (display === "" && jQuery.css(elem, "display") === "none") ||
  6956. !jQuery.contains( elem.ownerDocument.documentElement, elem ) ) {
  6957. jQuery._data( elem, "olddisplay", defaultDisplay(elem.nodeName) );
  6958. }
  6959. }
  6960. }
  6961. // Set the display of most of the elements in a second loop
  6962. // to avoid the constant reflow
  6963. for ( i = 0; i < j; i++ ) {
  6964. elem = this[ i ];
  6965. if ( elem.style ) {
  6966. display = elem.style.display;
  6967. if ( display === "" || display === "none" ) {
  6968. elem.style.display = jQuery._data( elem, "olddisplay" ) || "";
  6969. }
  6970. }
  6971. }
  6972. return this;
  6973. }
  6974. },
  6975. hide: function( speed, easing, callback ) {
  6976. if ( speed || speed === 0 ) {
  6977. return this.animate( genFx("hide", 3), speed, easing, callback);
  6978. } else {
  6979. var elem, display,
  6980. i = 0,
  6981. j = this.length;
  6982. for ( ; i < j; i++ ) {
  6983. elem = this[i];
  6984. if ( elem.style ) {
  6985. display = jQuery.css( elem, "display" );
  6986. if ( display !== "none" && !jQuery._data( elem, "olddisplay" ) ) {
  6987. jQuery._data( elem, "olddisplay", display );
  6988. }
  6989. }
  6990. }
  6991. // Set the display of the elements in a second loop
  6992. // to avoid the constant reflow
  6993. for ( i = 0; i < j; i++ ) {
  6994. if ( this[i].style ) {
  6995. this[i].style.display = "none";
  6996. }
  6997. }
  6998. return this;
  6999. }
  7000. },
  7001. // Save the old toggle function
  7002. _toggle: jQuery.fn.toggle,
  7003. toggle: function( fn, fn2, callback ) {
  7004. var bool = typeof fn === "boolean";
  7005. if ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) {
  7006. this._toggle.apply( this, arguments );
  7007. } else if ( fn == null || bool ) {
  7008. this.each(function() {
  7009. var state = bool ? fn : jQuery(this).is(":hidden");
  7010. jQuery(this)[ state ? "show" : "hide" ]();
  7011. });
  7012. } else {
  7013. this.animate(genFx("toggle", 3), fn, fn2, callback);
  7014. }
  7015. return this;
  7016. },
  7017. fadeTo: function( speed, to, easing, callback ) {
  7018. return this.filter(":hidden").css("opacity", 0).show().end()
  7019. .animate({opacity: to}, speed, easing, callback);
  7020. },
  7021. animate: function( prop, speed, easing, callback ) {
  7022. var optall = jQuery.speed( speed, easing, callback );
  7023. if ( jQuery.isEmptyObject( prop ) ) {
  7024. return this.each( optall.complete, [ false ] );
  7025. }
  7026. // Do not change referenced properties as per-property easing will be lost
  7027. prop = jQuery.extend( {}, prop );
  7028. function doAnimation() {
  7029. // XXX 'this' does not always have a nodeName when running the
  7030. // test suite
  7031. if ( optall.queue === false ) {
  7032. jQuery._mark( this );
  7033. }
  7034. var opt = jQuery.extend( {}, optall ),
  7035. isElement = this.nodeType === 1,
  7036. hidden = isElement && jQuery(this).is(":hidden"),
  7037. name, val, p, e, hooks, replace,
  7038. parts, start, end, unit,
  7039. method;
  7040. // will store per property easing and be used to determine when an animation is complete
  7041. opt.animatedProperties = {};
  7042. // first pass over propertys to expand / normalize
  7043. for ( p in prop ) {
  7044. name = jQuery.camelCase( p );
  7045. if ( p !== name ) {
  7046. prop[ name ] = prop[ p ];
  7047. delete prop[ p ];
  7048. }
  7049. if ( ( hooks = jQuery.cssHooks[ name ] ) && "expand" in hooks ) {
  7050. replace = hooks.expand( prop[ name ] );
  7051. delete prop[ name ];
  7052. // not quite $.extend, this wont overwrite keys already present.
  7053. // also - reusing 'p' from above because we have the correct "name"
  7054. for ( p in replace ) {
  7055. if ( ! ( p in prop ) ) {
  7056. prop[ p ] = replace[ p ];
  7057. }
  7058. }
  7059. }
  7060. }
  7061. for ( name in prop ) {
  7062. val = prop[ name ];
  7063. // easing resolution: per property > opt.specialEasing > opt.easing > 'swing' (default)
  7064. if ( jQuery.isArray( val ) ) {
  7065. opt.animatedProperties[ name ] = val[ 1 ];
  7066. val = prop[ name ] = val[ 0 ];
  7067. } else {
  7068. opt.animatedProperties[ name ] = opt.specialEasing && opt.specialEasing[ name ] || opt.easing || 'swing';
  7069. }
  7070. if ( val === "hide" && hidden || val === "show" && !hidden ) {
  7071. return opt.complete.call( this );
  7072. }
  7073. if ( isElement && ( name === "height" || name === "width" ) ) {
  7074. // Make sure that nothing sneaks out
  7075. // Record all 3 overflow attributes because IE does not
  7076. // change the overflow attribute when overflowX and
  7077. // overflowY are set to the same value
  7078. opt.overflow = [ this.style.overflow, this.style.overflowX, this.style.overflowY ];
  7079. // Set display property to inline-block for height/width
  7080. // animations on inline elements that are having width/height animated
  7081. if ( jQuery.css( this, "display" ) === "inline" &&
  7082. jQuery.css( this, "float" ) === "none" ) {
  7083. // inline-level elements accept inline-block;
  7084. // block-level elements need to be inline with layout
  7085. if ( !jQuery.support.inlineBlockNeedsLayout || defaultDisplay( this.nodeName ) === "inline" ) {
  7086. this.style.display = "inline-block";
  7087. } else {
  7088. this.style.zoom = 1;
  7089. }
  7090. }
  7091. }
  7092. }
  7093. if ( opt.overflow != null ) {
  7094. this.style.overflow = "hidden";
  7095. }
  7096. for ( p in prop ) {
  7097. e = new jQuery.fx( this, opt, p );
  7098. val = prop[ p ];
  7099. if ( rfxtypes.test( val ) ) {
  7100. // Tracks whether to show or hide based on private
  7101. // data attached to the element
  7102. method = jQuery._data( this, "toggle" + p ) || ( val === "toggle" ? hidden ? "show" : "hide" : 0 );
  7103. if ( method ) {
  7104. jQuery._data( this, "toggle" + p, method === "show" ? "hide" : "show" );
  7105. e[ method ]();
  7106. } else {
  7107. e[ val ]();
  7108. }
  7109. } else {
  7110. parts = rfxnum.exec( val );
  7111. start = e.cur();
  7112. if ( parts ) {
  7113. end = parseFloat( parts[2] );
  7114. unit = parts[3] || ( jQuery.cssNumber[ p ] ? "" : "px" );
  7115. // We need to compute starting value
  7116. if ( unit !== "px" ) {
  7117. jQuery.style( this, p, (end || 1) + unit);
  7118. start = ( (end || 1) / e.cur() ) * start;
  7119. jQuery.style( this, p, start + unit);
  7120. }
  7121. // If a +=/-= token was provided, we're doing a relative animation
  7122. if ( parts[1] ) {
  7123. end = ( (parts[ 1 ] === "-=" ? -1 : 1) * end ) + start;
  7124. }
  7125. e.custom( start, end, unit );
  7126. } else {
  7127. e.custom( start, val, "" );
  7128. }
  7129. }
  7130. }
  7131. // For JS strict compliance
  7132. return true;
  7133. }
  7134. return optall.queue === false ?
  7135. this.each( doAnimation ) :
  7136. this.queue( optall.queue, doAnimation );
  7137. },
  7138. stop: function( type, clearQueue, gotoEnd ) {
  7139. if ( typeof type !== "string" ) {
  7140. gotoEnd = clearQueue;
  7141. clearQueue = type;
  7142. type = undefined;
  7143. }
  7144. if ( clearQueue && type !== false ) {
  7145. this.queue( type || "fx", [] );
  7146. }
  7147. return this.each(function() {
  7148. var index,
  7149. hadTimers = false,
  7150. timers = jQuery.timers,
  7151. data = jQuery._data( this );
  7152. // clear marker counters if we know they won't be
  7153. if ( !gotoEnd ) {
  7154. jQuery._unmark( true, this );
  7155. }
  7156. function stopQueue( elem, data, index ) {
  7157. var hooks = data[ index ];
  7158. jQuery.removeData( elem, index, true );
  7159. hooks.stop( gotoEnd );
  7160. }
  7161. if ( type == null ) {
  7162. for ( index in data ) {
  7163. if ( data[ index ] && data[ index ].stop && index.indexOf(".run") === index.length - 4 ) {
  7164. stopQueue( this, data, index );
  7165. }
  7166. }
  7167. } else if ( data[ index = type + ".run" ] && data[ index ].stop ){
  7168. stopQueue( this, data, index );
  7169. }
  7170. for ( index = timers.length; index--; ) {
  7171. if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
  7172. if ( gotoEnd ) {
  7173. // force the next step to be the last
  7174. timers[ index ]( true );
  7175. } else {
  7176. timers[ index ].saveState();
  7177. }
  7178. hadTimers = true;
  7179. timers.splice( index, 1 );
  7180. }
  7181. }
  7182. // start the next in the queue if the last step wasn't forced
  7183. // timers currently will call their complete callbacks, which will dequeue
  7184. // but only if they were gotoEnd
  7185. if ( !( gotoEnd && hadTimers ) ) {
  7186. jQuery.dequeue( this, type );
  7187. }
  7188. });
  7189. }
  7190. });
  7191. // Animations created synchronously will run synchronously
  7192. function createFxNow() {
  7193. setTimeout( clearFxNow, 0 );
  7194. return ( fxNow = jQuery.now() );
  7195. }
  7196. function clearFxNow() {
  7197. fxNow = undefined;
  7198. }
  7199. // Generate parameters to create a standard animation
  7200. function genFx( type, num ) {
  7201. var obj = {};
  7202. jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice( 0, num )), function() {
  7203. obj[ this ] = type;
  7204. });
  7205. return obj;
  7206. }
  7207. // Generate shortcuts for custom animations
  7208. jQuery.each({
  7209. slideDown: genFx( "show", 1 ),
  7210. slideUp: genFx( "hide", 1 ),
  7211. slideToggle: genFx( "toggle", 1 ),
  7212. fadeIn: { opacity: "show" },
  7213. fadeOut: { opacity: "hide" },
  7214. fadeToggle: { opacity: "toggle" }
  7215. }, function( name, props ) {
  7216. jQuery.fn[ name ] = function( speed, easing, callback ) {
  7217. return this.animate( props, speed, easing, callback );
  7218. };
  7219. });
  7220. jQuery.extend({
  7221. speed: function( speed, easing, fn ) {
  7222. var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
  7223. complete: fn || !fn && easing ||
  7224. jQuery.isFunction( speed ) && speed,
  7225. duration: speed,
  7226. easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
  7227. };
  7228. opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
  7229. opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
  7230. // normalize opt.queue - true/undefined/null -> "fx"
  7231. if ( opt.queue == null || opt.queue === true ) {
  7232. opt.queue = "fx";
  7233. }
  7234. // Queueing
  7235. opt.old = opt.complete;
  7236. opt.complete = function( noUnmark ) {
  7237. if ( jQuery.isFunction( opt.old ) ) {
  7238. opt.old.call( this );
  7239. }
  7240. if ( opt.queue ) {
  7241. jQuery.dequeue( this, opt.queue );
  7242. } else if ( noUnmark !== false ) {
  7243. jQuery._unmark( this );
  7244. }
  7245. };
  7246. return opt;
  7247. },
  7248. easing: {
  7249. linear: function( p ) {
  7250. return p;
  7251. },
  7252. swing: function( p ) {
  7253. return ( -Math.cos( p*Math.PI ) / 2 ) + 0.5;
  7254. }
  7255. },
  7256. timers: [],
  7257. fx: function( elem, options, prop ) {
  7258. this.options = options;
  7259. this.elem = elem;
  7260. this.prop = prop;
  7261. options.orig = options.orig || {};
  7262. }
  7263. });
  7264. jQuery.fx.prototype = {
  7265. // Simple function for setting a style value
  7266. update: function() {
  7267. if ( this.options.step ) {
  7268. this.options.step.call( this.elem, this.now, this );
  7269. }
  7270. ( jQuery.fx.step[ this.prop ] || jQuery.fx.step._default )( this );
  7271. },
  7272. // Get the current size
  7273. cur: function() {
  7274. if ( this.elem[ this.prop ] != null && (!this.elem.style || this.elem.style[ this.prop ] == null) ) {
  7275. return this.elem[ this.prop ];
  7276. }
  7277. var parsed,
  7278. r = jQuery.css( this.elem, this.prop );
  7279. // Empty strings, null, undefined and "auto" are converted to 0,
  7280. // complex values such as "rotate(1rad)" are returned as is,
  7281. // simple values such as "10px" are parsed to Float.
  7282. return isNaN( parsed = parseFloat( r ) ) ? !r || r === "auto" ? 0 : r : parsed;
  7283. },
  7284. // Start an animation from one number to another
  7285. custom: function( from, to, unit ) {
  7286. var self = this,
  7287. fx = jQuery.fx;
  7288. this.startTime = fxNow || createFxNow();
  7289. this.end = to;
  7290. this.now = this.start = from;
  7291. this.pos = this.state = 0;
  7292. this.unit = unit || this.unit || ( jQuery.cssNumber[ this.prop ] ? "" : "px" );
  7293. function t( gotoEnd ) {
  7294. return self.step( gotoEnd );
  7295. }
  7296. t.queue = this.options.queue;
  7297. t.elem = this.elem;
  7298. t.saveState = function() {
  7299. if ( jQuery._data( self.elem, "fxshow" + self.prop ) === undefined ) {
  7300. if ( self.options.hide ) {
  7301. jQuery._data( self.elem, "fxshow" + self.prop, self.start );
  7302. } else if ( self.options.show ) {
  7303. jQuery._data( self.elem, "fxshow" + self.prop, self.end );
  7304. }
  7305. }
  7306. };
  7307. if ( t() && jQuery.timers.push(t) && !timerId ) {
  7308. timerId = setInterval( fx.tick, fx.interval );
  7309. }
  7310. },
  7311. // Simple 'show' function
  7312. show: function() {
  7313. var dataShow = jQuery._data( this.elem, "fxshow" + this.prop );
  7314. // Remember where we started, so that we can go back to it later
  7315. this.options.orig[ this.prop ] = dataShow || jQuery.style( this.elem, this.prop );
  7316. this.options.show = true;
  7317. // Begin the animation
  7318. // Make sure that we start at a small width/height to avoid any flash of content
  7319. if ( dataShow !== undefined ) {
  7320. // This show is picking up where a previous hide or show left off
  7321. this.custom( this.cur(), dataShow );
  7322. } else {
  7323. this.custom( this.prop === "width" || this.prop === "height" ? 1 : 0, this.cur() );
  7324. }
  7325. // Start by showing the element
  7326. jQuery( this.elem ).show();
  7327. },
  7328. // Simple 'hide' function
  7329. hide: function() {
  7330. // Remember where we started, so that we can go back to it later
  7331. this.options.orig[ this.prop ] = jQuery._data( this.elem, "fxshow" + this.prop ) || jQuery.style( this.elem, this.prop );
  7332. this.options.hide = true;
  7333. // Begin the animation
  7334. this.custom( this.cur(), 0 );
  7335. },
  7336. // Each step of an animation
  7337. step: function( gotoEnd ) {
  7338. var p, n, complete,
  7339. t = fxNow || createFxNow(),
  7340. done = true,
  7341. elem = this.elem,
  7342. options = this.options;
  7343. if ( gotoEnd || t >= options.duration + this.startTime ) {
  7344. this.now = this.end;
  7345. this.pos = this.state = 1;
  7346. this.update();
  7347. options.animatedProperties[ this.prop ] = true;
  7348. for ( p in options.animatedProperties ) {
  7349. if ( options.animatedProperties[ p ] !== true ) {
  7350. done = false;
  7351. }
  7352. }
  7353. if ( done ) {
  7354. // Reset the overflow
  7355. if ( options.overflow != null && !jQuery.support.shrinkWrapBlocks ) {
  7356. jQuery.each( [ "", "X", "Y" ], function( index, value ) {
  7357. elem.style[ "overflow" + value ] = options.overflow[ index ];
  7358. });
  7359. }
  7360. // Hide the element if the "hide" operation was done
  7361. if ( options.hide ) {
  7362. jQuery( elem ).hide();
  7363. }
  7364. // Reset the properties, if the item has been hidden or shown
  7365. if ( options.hide || options.show ) {
  7366. for ( p in options.animatedProperties ) {
  7367. jQuery.style( elem, p, options.orig[ p ] );
  7368. jQuery.removeData( elem, "fxshow" + p, true );
  7369. // Toggle data is no longer needed
  7370. jQuery.removeData( elem, "toggle" + p, true );
  7371. }
  7372. }
  7373. // Execute the complete function
  7374. // in the event that the complete function throws an exception
  7375. // we must ensure it won't be called twice. #5684
  7376. complete = options.complete;
  7377. if ( complete ) {
  7378. options.complete = false;
  7379. complete.call( elem );
  7380. }
  7381. }
  7382. return false;
  7383. } else {
  7384. // classical easing cannot be used with an Infinity duration
  7385. if ( options.duration == Infinity ) {
  7386. this.now = t;
  7387. } else {
  7388. n = t - this.startTime;
  7389. this.state = n / options.duration;
  7390. // Perform the easing function, defaults to swing
  7391. this.pos = jQuery.easing[ options.animatedProperties[this.prop] ]( this.state, n, 0, 1, options.duration );
  7392. this.now = this.start + ( (this.end - this.start) * this.pos );
  7393. }
  7394. // Perform the next step of the animation
  7395. this.update();
  7396. }
  7397. return true;
  7398. }
  7399. };
  7400. jQuery.extend( jQuery.fx, {
  7401. tick: function() {
  7402. var timer,
  7403. timers = jQuery.timers,
  7404. i = 0;
  7405. for ( ; i < timers.length; i++ ) {
  7406. timer = timers[ i ];
  7407. // Checks the timer has not already been removed
  7408. if ( !timer() && timers[ i ] === timer ) {
  7409. timers.splice( i--, 1 );
  7410. }
  7411. }
  7412. if ( !timers.length ) {
  7413. jQuery.fx.stop();
  7414. }
  7415. },
  7416. interval: 13,
  7417. stop: function() {
  7418. clearInterval( timerId );
  7419. timerId = null;
  7420. },
  7421. speeds: {
  7422. slow: 600,
  7423. fast: 200,
  7424. // Default speed
  7425. _default: 400
  7426. },
  7427. step: {
  7428. opacity: function( fx ) {
  7429. jQuery.style( fx.elem, "opacity", fx.now );
  7430. },
  7431. _default: function( fx ) {
  7432. if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) {
  7433. fx.elem.style[ fx.prop ] = fx.now + fx.unit;
  7434. } else {
  7435. fx.elem[ fx.prop ] = fx.now;
  7436. }
  7437. }
  7438. }
  7439. });
  7440. // Ensure props that can't be negative don't go there on undershoot easing
  7441. jQuery.each( fxAttrs.concat.apply( [], fxAttrs ), function( i, prop ) {
  7442. // exclude marginTop, marginLeft, marginBottom and marginRight from this list
  7443. if ( prop.indexOf( "margin" ) ) {
  7444. jQuery.fx.step[ prop ] = function( fx ) {
  7445. jQuery.style( fx.elem, prop, Math.max(0, fx.now) + fx.unit );
  7446. };
  7447. }
  7448. });
  7449. if ( jQuery.expr && jQuery.expr.filters ) {
  7450. jQuery.expr.filters.animated = function( elem ) {
  7451. return jQuery.grep(jQuery.timers, function( fn ) {
  7452. return elem === fn.elem;
  7453. }).length;
  7454. };
  7455. }
  7456. // Try to restore the default display value of an element
  7457. function defaultDisplay( nodeName ) {
  7458. if ( !elemdisplay[ nodeName ] ) {
  7459. var body = document.body,
  7460. elem = jQuery( "<" + nodeName + ">" ).appendTo( body ),
  7461. display = elem.css( "display" );
  7462. elem.remove();
  7463. // If the simple way fails,
  7464. // get element's real default display by attaching it to a temp iframe
  7465. if ( display === "none" || display === "" ) {
  7466. // No iframe to use yet, so create it
  7467. if ( !iframe ) {
  7468. iframe = document.createElement( "iframe" );
  7469. iframe.frameBorder = iframe.width = iframe.height = 0;
  7470. }
  7471. body.appendChild( iframe );
  7472. // Create a cacheable copy of the iframe document on first call.
  7473. // IE and Opera will allow us to reuse the iframeDoc without re-writing the fake HTML
  7474. // document to it; WebKit & Firefox won't allow reusing the iframe document.
  7475. if ( !iframeDoc || !iframe.createElement ) {
  7476. iframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document;
  7477. iframeDoc.write( ( jQuery.support.boxModel ? "<!doctype html>" : "" ) + "<html><body>" );
  7478. iframeDoc.close();
  7479. }
  7480. elem = iframeDoc.createElement( nodeName );
  7481. iframeDoc.body.appendChild( elem );
  7482. display = jQuery.css( elem, "display" );
  7483. body.removeChild( iframe );
  7484. }
  7485. // Store the correct default display
  7486. elemdisplay[ nodeName ] = display;
  7487. }
  7488. return elemdisplay[ nodeName ];
  7489. }
  7490. var getOffset,
  7491. rtable = /^t(?:able|d|h)$/i,
  7492. rroot = /^(?:body|html)$/i;
  7493. if ( "getBoundingClientRect" in document.documentElement ) {
  7494. getOffset = function( elem, doc, docElem, box ) {
  7495. try {
  7496. box = elem.getBoundingClientRect();
  7497. } catch(e) {}
  7498. // Make sure we're not dealing with a disconnected DOM node
  7499. if ( !box || !jQuery.contains( docElem, elem ) ) {
  7500. return box ? { top: box.top, left: box.left } : { top: 0, left: 0 };
  7501. }
  7502. var body = doc.body,
  7503. win = getWindow( doc ),
  7504. clientTop = docElem.clientTop || body.clientTop || 0,
  7505. clientLeft = docElem.clientLeft || body.clientLeft || 0,
  7506. scrollTop = win.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop,
  7507. scrollLeft = win.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft,
  7508. top = box.top + scrollTop - clientTop,
  7509. left = box.left + scrollLeft - clientLeft;
  7510. return { top: top, left: left };
  7511. };
  7512. } else {
  7513. getOffset = function( elem, doc, docElem ) {
  7514. var computedStyle,
  7515. offsetParent = elem.offsetParent,
  7516. prevOffsetParent = elem,
  7517. body = doc.body,
  7518. defaultView = doc.defaultView,
  7519. prevComputedStyle = defaultView ? defaultView.getComputedStyle( elem, null ) : elem.currentStyle,
  7520. top = elem.offsetTop,
  7521. left = elem.offsetLeft;
  7522. while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
  7523. if ( jQuery.support.fixedPosition && prevComputedStyle.position === "fixed" ) {
  7524. break;
  7525. }
  7526. computedStyle = defaultView ? defaultView.getComputedStyle(elem, null) : elem.currentStyle;
  7527. top -= elem.scrollTop;
  7528. left -= elem.scrollLeft;
  7529. if ( elem === offsetParent ) {
  7530. top += elem.offsetTop;
  7531. left += elem.offsetLeft;
  7532. if ( jQuery.support.doesNotAddBorder && !(jQuery.support.doesAddBorderForTableAndCells && rtable.test(elem.nodeName)) ) {
  7533. top += parseFloat( computedStyle.borderTopWidth ) || 0;
  7534. left += parseFloat( computedStyle.borderLeftWidth ) || 0;
  7535. }
  7536. prevOffsetParent = offsetParent;
  7537. offsetParent = elem.offsetParent;
  7538. }
  7539. if ( jQuery.support.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) {
  7540. top += parseFloat( computedStyle.borderTopWidth ) || 0;
  7541. left += parseFloat( computedStyle.borderLeftWidth ) || 0;
  7542. }
  7543. prevComputedStyle = computedStyle;
  7544. }
  7545. if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" ) {
  7546. top += body.offsetTop;
  7547. left += body.offsetLeft;
  7548. }
  7549. if ( jQuery.support.fixedPosition && prevComputedStyle.position === "fixed" ) {
  7550. top += Math.max( docElem.scrollTop, body.scrollTop );
  7551. left += Math.max( docElem.scrollLeft, body.scrollLeft );
  7552. }
  7553. return { top: top, left: left };
  7554. };
  7555. }
  7556. jQuery.fn.offset = function( options ) {
  7557. if ( arguments.length ) {
  7558. return options === undefined ?
  7559. this :
  7560. this.each(function( i ) {
  7561. jQuery.offset.setOffset( this, options, i );
  7562. });
  7563. }
  7564. var elem = this[0],
  7565. doc = elem && elem.ownerDocument;
  7566. if ( !doc ) {
  7567. return null;
  7568. }
  7569. if ( elem === doc.body ) {
  7570. return jQuery.offset.bodyOffset( elem );
  7571. }
  7572. return getOffset( elem, doc, doc.documentElement );
  7573. };
  7574. jQuery.offset = {
  7575. bodyOffset: function( body ) {
  7576. var top = body.offsetTop,
  7577. left = body.offsetLeft;
  7578. if ( jQuery.support.doesNotIncludeMarginInBodyOffset ) {
  7579. top += parseFloat( jQuery.css(body, "marginTop") ) || 0;
  7580. left += parseFloat( jQuery.css(body, "marginLeft") ) || 0;
  7581. }
  7582. return { top: top, left: left };
  7583. },
  7584. setOffset: function( elem, options, i ) {
  7585. var position = jQuery.css( elem, "position" );
  7586. // set position first, in-case top/left are set even on static elem
  7587. if ( position === "static" ) {
  7588. elem.style.position = "relative";
  7589. }
  7590. var curElem = jQuery( elem ),
  7591. curOffset = curElem.offset(),
  7592. curCSSTop = jQuery.css( elem, "top" ),
  7593. curCSSLeft = jQuery.css( elem, "left" ),
  7594. calculatePosition = ( position === "absolute" || position === "fixed" ) && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1,
  7595. props = {}, curPosition = {}, curTop, curLeft;
  7596. // need to be able to calculate position if either top or left is auto and position is either absolute or fixed
  7597. if ( calculatePosition ) {
  7598. curPosition = curElem.position();
  7599. curTop = curPosition.top;
  7600. curLeft = curPosition.left;
  7601. } else {
  7602. curTop = parseFloat( curCSSTop ) || 0;
  7603. curLeft = parseFloat( curCSSLeft ) || 0;
  7604. }
  7605. if ( jQuery.isFunction( options ) ) {
  7606. options = options.call( elem, i, curOffset );
  7607. }
  7608. if ( options.top != null ) {
  7609. props.top = ( options.top - curOffset.top ) + curTop;
  7610. }
  7611. if ( options.left != null ) {
  7612. props.left = ( options.left - curOffset.left ) + curLeft;
  7613. }
  7614. if ( "using" in options ) {
  7615. options.using.call( elem, props );
  7616. } else {
  7617. curElem.css( props );
  7618. }
  7619. }
  7620. };
  7621. jQuery.fn.extend({
  7622. position: function() {
  7623. if ( !this[0] ) {
  7624. return null;
  7625. }
  7626. var elem = this[0],
  7627. // Get *real* offsetParent
  7628. offsetParent = this.offsetParent(),
  7629. // Get correct offsets
  7630. offset = this.offset(),
  7631. parentOffset = rroot.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset();
  7632. // Subtract element margins
  7633. // note: when an element has margin: auto the offsetLeft and marginLeft
  7634. // are the same in Safari causing offset.left to incorrectly be 0
  7635. offset.top -= parseFloat( jQuery.css(elem, "marginTop") ) || 0;
  7636. offset.left -= parseFloat( jQuery.css(elem, "marginLeft") ) || 0;
  7637. // Add offsetParent borders
  7638. parentOffset.top += parseFloat( jQuery.css(offsetParent[0], "borderTopWidth") ) || 0;
  7639. parentOffset.left += parseFloat( jQuery.css(offsetParent[0], "borderLeftWidth") ) || 0;
  7640. // Subtract the two offsets
  7641. return {
  7642. top: offset.top - parentOffset.top,
  7643. left: offset.left - parentOffset.left
  7644. };
  7645. },
  7646. offsetParent: function() {
  7647. return this.map(function() {
  7648. var offsetParent = this.offsetParent || document.body;
  7649. while ( offsetParent && (!rroot.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) {
  7650. offsetParent = offsetParent.offsetParent;
  7651. }
  7652. return offsetParent;
  7653. });
  7654. }
  7655. });
  7656. // Create scrollLeft and scrollTop methods
  7657. jQuery.each( {scrollLeft: "pageXOffset", scrollTop: "pageYOffset"}, function( method, prop ) {
  7658. var top = /Y/.test( prop );
  7659. jQuery.fn[ method ] = function( val ) {
  7660. return jQuery.access( this, function( elem, method, val ) {
  7661. var win = getWindow( elem );
  7662. if ( val === undefined ) {
  7663. return win ? (prop in win) ? win[ prop ] :
  7664. jQuery.support.boxModel && win.document.documentElement[ method ] ||
  7665. win.document.body[ method ] :
  7666. elem[ method ];
  7667. }
  7668. if ( win ) {
  7669. win.scrollTo(
  7670. !top ? val : jQuery( win ).scrollLeft(),
  7671. top ? val : jQuery( win ).scrollTop()
  7672. );
  7673. } else {
  7674. elem[ method ] = val;
  7675. }
  7676. }, method, val, arguments.length, null );
  7677. };
  7678. });
  7679. function getWindow( elem ) {
  7680. return jQuery.isWindow( elem ) ?
  7681. elem :
  7682. elem.nodeType === 9 ?
  7683. elem.defaultView || elem.parentWindow :
  7684. false;
  7685. }
  7686. // Create width, height, innerHeight, innerWidth, outerHeight and outerWidth methods
  7687. jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
  7688. var clientProp = "client" + name,
  7689. scrollProp = "scroll" + name,
  7690. offsetProp = "offset" + name;
  7691. // innerHeight and innerWidth
  7692. jQuery.fn[ "inner" + name ] = function() {
  7693. var elem = this[0];
  7694. return elem ?
  7695. elem.style ?
  7696. parseFloat( jQuery.css( elem, type, "padding" ) ) :
  7697. this[ type ]() :
  7698. null;
  7699. };
  7700. // outerHeight and outerWidth
  7701. jQuery.fn[ "outer" + name ] = function( margin ) {
  7702. var elem = this[0];
  7703. return elem ?
  7704. elem.style ?
  7705. parseFloat( jQuery.css( elem, type, margin ? "margin" : "border" ) ) :
  7706. this[ type ]() :
  7707. null;
  7708. };
  7709. jQuery.fn[ type ] = function( value ) {
  7710. return jQuery.access( this, function( elem, type, value ) {
  7711. var doc, docElemProp, orig, ret;
  7712. if ( jQuery.isWindow( elem ) ) {
  7713. // 3rd condition allows Nokia support, as it supports the docElem prop but not CSS1Compat
  7714. doc = elem.document;
  7715. docElemProp = doc.documentElement[ clientProp ];
  7716. return jQuery.support.boxModel && docElemProp ||
  7717. doc.body && doc.body[ clientProp ] || docElemProp;
  7718. }
  7719. // Get document width or height
  7720. if ( elem.nodeType === 9 ) {
  7721. // Either scroll[Width/Height] or offset[Width/Height], whichever is greater
  7722. doc = elem.documentElement;
  7723. // when a window > document, IE6 reports a offset[Width/Height] > client[Width/Height]
  7724. // so we can't use max, as it'll choose the incorrect offset[Width/Height]
  7725. // instead we use the correct client[Width/Height]
  7726. // support:IE6
  7727. if ( doc[ clientProp ] >= doc[ scrollProp ] ) {
  7728. return doc[ clientProp ];
  7729. }
  7730. return Math.max(
  7731. elem.body[ scrollProp ], doc[ scrollProp ],
  7732. elem.body[ offsetProp ], doc[ offsetProp ]
  7733. );
  7734. }
  7735. // Get width or height on the element
  7736. if ( value === undefined ) {
  7737. orig = jQuery.css( elem, type );
  7738. ret = parseFloat( orig );
  7739. return jQuery.isNumeric( ret ) ? ret : orig;
  7740. }
  7741. // Set the width or height on the element
  7742. jQuery( elem ).css( type, value );
  7743. }, type, value, arguments.length, null );
  7744. };
  7745. });
  7746. // Expose jQuery to the global object
  7747. window.jQuery = window.$ = jQuery;
  7748. // Expose jQuery as an AMD module, but only for AMD loaders that
  7749. // understand the issues with loading multiple versions of jQuery
  7750. // in a page that all might call define(). The loader will indicate
  7751. // they have special allowances for multiple jQuery versions by
  7752. // specifying define.amd.jQuery = true. Register as a named module,
  7753. // since jQuery can be concatenated with other files that may use define,
  7754. // but not use a proper concatenation script that understands anonymous
  7755. // AMD modules. A named AMD is safest and most robust way to register.
  7756. // Lowercase jquery is used because AMD module names are derived from
  7757. // file names, and jQuery is normally delivered in a lowercase file name.
  7758. // Do this after creating the global so that if an AMD module wants to call
  7759. // noConflict to hide this version of jQuery, it will work.
  7760. if ( typeof define === "function" && define.amd && define.amd.jQuery ) {
  7761. define( "jquery", [], function () { return jQuery; } );
  7762. }
  7763. })( window );
  7764. /*!
  7765. * jQuery UI @VERSION
  7766. *
  7767. * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
  7768. * Dual licensed under the MIT or GPL Version 2 licenses.
  7769. * http://jquery.org/license
  7770. *
  7771. * http://docs.jquery.com/UI
  7772. */
  7773. (function( $, undefined ) {
  7774. // prevent duplicate loading
  7775. // this is only a problem because we proxy existing functions
  7776. // and we don't want to double proxy them
  7777. $.ui = $.ui || {};
  7778. if ( $.ui.version ) {
  7779. return;
  7780. }
  7781. $.extend( $.ui, {
  7782. version: "@VERSION",
  7783. keyCode: {
  7784. ALT: 18,
  7785. BACKSPACE: 8,
  7786. CAPS_LOCK: 20,
  7787. COMMA: 188,
  7788. COMMAND: 91,
  7789. COMMAND_LEFT: 91, // COMMAND
  7790. COMMAND_RIGHT: 93,
  7791. CONTROL: 17,
  7792. DELETE: 46,
  7793. DOWN: 40,
  7794. END: 35,
  7795. ENTER: 13,
  7796. ESCAPE: 27,
  7797. HOME: 36,
  7798. INSERT: 45,
  7799. LEFT: 37,
  7800. MENU: 93, // COMMAND_RIGHT
  7801. NUMPAD_ADD: 107,
  7802. NUMPAD_DECIMAL: 110,
  7803. NUMPAD_DIVIDE: 111,
  7804. NUMPAD_ENTER: 108,
  7805. NUMPAD_MULTIPLY: 106,
  7806. NUMPAD_SUBTRACT: 109,
  7807. PAGE_DOWN: 34,
  7808. PAGE_UP: 33,
  7809. PERIOD: 190,
  7810. RIGHT: 39,
  7811. SHIFT: 16,
  7812. SPACE: 32,
  7813. TAB: 9,
  7814. UP: 38,
  7815. WINDOWS: 91 // COMMAND
  7816. }
  7817. });
  7818. // plugins
  7819. $.fn.extend({
  7820. propAttr: $.fn.prop || $.fn.attr,
  7821. _focus: $.fn.focus,
  7822. focus: function( delay, fn ) {
  7823. return typeof delay === "number" ?
  7824. this.each(function() {
  7825. var elem = this;
  7826. setTimeout(function() {
  7827. $( elem ).focus();
  7828. if ( fn ) {
  7829. fn.call( elem );
  7830. }
  7831. }, delay );
  7832. }) :
  7833. this._focus.apply( this, arguments );
  7834. },
  7835. scrollParent: function() {
  7836. var scrollParent;
  7837. if (($.browser.msie && (/(static|relative)/).test(this.css('position'))) || (/absolute/).test(this.css('position'))) {
  7838. scrollParent = this.parents().filter(function() {
  7839. return (/(relative|absolute|fixed)/).test($.curCSS(this,'position',1)) && (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1));
  7840. }).eq(0);
  7841. } else {
  7842. scrollParent = this.parents().filter(function() {
  7843. return (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1));
  7844. }).eq(0);
  7845. }
  7846. return (/fixed/).test(this.css('position')) || !scrollParent.length ? $(document) : scrollParent;
  7847. },
  7848. zIndex: function( zIndex ) {
  7849. if ( zIndex !== undefined ) {
  7850. return this.css( "zIndex", zIndex );
  7851. }
  7852. if ( this.length ) {
  7853. var elem = $( this[ 0 ] ), position, value;
  7854. while ( elem.length && elem[ 0 ] !== document ) {
  7855. // Ignore z-index if position is set to a value where z-index is ignored by the browser
  7856. // This makes behavior of this function consistent across browsers
  7857. // WebKit always returns auto if the element is positioned
  7858. position = elem.css( "position" );
  7859. if ( position === "absolute" || position === "relative" || position === "fixed" ) {
  7860. // IE returns 0 when zIndex is not specified
  7861. // other browsers return a string
  7862. // we ignore the case of nested elements with an explicit value of 0
  7863. // <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
  7864. value = parseInt( elem.css( "zIndex" ), 10 );
  7865. if ( !isNaN( value ) && value !== 0 ) {
  7866. return value;
  7867. }
  7868. }
  7869. elem = elem.parent();
  7870. }
  7871. }
  7872. return 0;
  7873. },
  7874. disableSelection: function() {
  7875. return this.bind( ( $.support.selectstart ? "selectstart" : "mousedown" ) +
  7876. ".ui-disableSelection", function( event ) {
  7877. event.preventDefault();
  7878. });
  7879. },
  7880. enableSelection: function() {
  7881. return this.unbind( ".ui-disableSelection" );
  7882. }
  7883. });
  7884. $.each( [ "Width", "Height" ], function( i, name ) {
  7885. var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ],
  7886. type = name.toLowerCase(),
  7887. orig = {
  7888. innerWidth: $.fn.innerWidth,
  7889. innerHeight: $.fn.innerHeight,
  7890. outerWidth: $.fn.outerWidth,
  7891. outerHeight: $.fn.outerHeight
  7892. };
  7893. function reduce( elem, size, border, margin ) {
  7894. $.each( side, function() {
  7895. size -= parseFloat( $.curCSS( elem, "padding" + this, true) ) || 0;
  7896. if ( border ) {
  7897. size -= parseFloat( $.curCSS( elem, "border" + this + "Width", true) ) || 0;
  7898. }
  7899. if ( margin ) {
  7900. size -= parseFloat( $.curCSS( elem, "margin" + this, true) ) || 0;
  7901. }
  7902. });
  7903. return size;
  7904. }
  7905. $.fn[ "inner" + name ] = function( size ) {
  7906. if ( size === undefined ) {
  7907. return orig[ "inner" + name ].call( this );
  7908. }
  7909. return this.each(function() {
  7910. $( this ).css( type, reduce( this, size ) + "px" );
  7911. });
  7912. };
  7913. $.fn[ "outer" + name] = function( size, margin ) {
  7914. if ( typeof size !== "number" ) {
  7915. return orig[ "outer" + name ].call( this, size );
  7916. }
  7917. return this.each(function() {
  7918. $( this).css( type, reduce( this, size, true, margin ) + "px" );
  7919. });
  7920. };
  7921. });
  7922. // selectors
  7923. function focusable( element, isTabIndexNotNaN ) {
  7924. var nodeName = element.nodeName.toLowerCase();
  7925. if ( "area" === nodeName ) {
  7926. var map = element.parentNode,
  7927. mapName = map.name,
  7928. img;
  7929. if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
  7930. return false;
  7931. }
  7932. img = $( "img[usemap=#" + mapName + "]" )[0];
  7933. return !!img && visible( img );
  7934. }
  7935. return ( /input|select|textarea|button|object/.test( nodeName )
  7936. ? !element.disabled
  7937. : "a" == nodeName
  7938. ? element.href || isTabIndexNotNaN
  7939. : isTabIndexNotNaN)
  7940. // the element and all of its ancestors must be visible
  7941. && visible( element );
  7942. }
  7943. function visible( element ) {
  7944. return !$( element ).parents().andSelf().filter(function() {
  7945. return $.curCSS( this, "visibility" ) === "hidden" ||
  7946. $.expr.filters.hidden( this );
  7947. }).length;
  7948. }
  7949. $.extend( $.expr[ ":" ], {
  7950. data: function( elem, i, match ) {
  7951. return !!$.data( elem, match[ 3 ] );
  7952. },
  7953. focusable: function( element ) {
  7954. return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) );
  7955. },
  7956. tabbable: function( element ) {
  7957. var tabIndex = $.attr( element, "tabindex" ),
  7958. isTabIndexNaN = isNaN( tabIndex );
  7959. return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN );
  7960. }
  7961. });
  7962. // support
  7963. $(function() {
  7964. var body = document.body,
  7965. div = body.appendChild( div = document.createElement( "div" ) );
  7966. // access offsetHeight before setting the style to prevent a layout bug
  7967. // in IE 9 which causes the elemnt to continue to take up space even
  7968. // after it is removed from the DOM (#8026)
  7969. div.offsetHeight;
  7970. $.extend( div.style, {
  7971. minHeight: "100px",
  7972. height: "auto",
  7973. padding: 0,
  7974. borderWidth: 0
  7975. });
  7976. $.support.minHeight = div.offsetHeight === 100;
  7977. $.support.selectstart = "onselectstart" in div;
  7978. // set display to none to avoid a layout bug in IE
  7979. // http://dev.jquery.com/ticket/4014
  7980. body.removeChild( div ).style.display = "none";
  7981. });
  7982. // deprecated
  7983. $.extend( $.ui, {
  7984. // $.ui.plugin is deprecated. Use the proxy pattern instead.
  7985. plugin: {
  7986. add: function( module, option, set ) {
  7987. var proto = $.ui[ module ].prototype;
  7988. for ( var i in set ) {
  7989. proto.plugins[ i ] = proto.plugins[ i ] || [];
  7990. proto.plugins[ i ].push( [ option, set[ i ] ] );
  7991. }
  7992. },
  7993. call: function( instance, name, args ) {
  7994. var set = instance.plugins[ name ];
  7995. if ( !set || !instance.element[ 0 ].parentNode ) {
  7996. return;
  7997. }
  7998. for ( var i = 0; i < set.length; i++ ) {
  7999. if ( instance.options[ set[ i ][ 0 ] ] ) {
  8000. set[ i ][ 1 ].apply( instance.element, args );
  8001. }
  8002. }
  8003. }
  8004. },
  8005. // will be deprecated when we switch to jQuery 1.4 - use jQuery.contains()
  8006. contains: function( a, b ) {
  8007. return document.compareDocumentPosition ?
  8008. a.compareDocumentPosition( b ) & 16 :
  8009. a !== b && a.contains( b );
  8010. },
  8011. // only used by resizable
  8012. hasScroll: function( el, a ) {
  8013. //If overflow is hidden, the element might have extra content, but the user wants to hide it
  8014. if ( $( el ).css( "overflow" ) === "hidden") {
  8015. return false;
  8016. }
  8017. var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop",
  8018. has = false;
  8019. if ( el[ scroll ] > 0 ) {
  8020. return true;
  8021. }
  8022. // TODO: determine which cases actually cause this to happen
  8023. // if the element doesn't have the scroll set, see if it's possible to
  8024. // set the scroll
  8025. el[ scroll ] = 1;
  8026. has = ( el[ scroll ] > 0 );
  8027. el[ scroll ] = 0;
  8028. return has;
  8029. },
  8030. // these are odd functions, fix the API or move into individual plugins
  8031. isOverAxis: function( x, reference, size ) {
  8032. //Determines when x coordinate is over "b" element axis
  8033. return ( x > reference ) && ( x < ( reference + size ) );
  8034. },
  8035. isOver: function( y, x, top, left, height, width ) {
  8036. //Determines when x, y coordinates is over "b" element
  8037. return $.ui.isOverAxis( y, top, height ) && $.ui.isOverAxis( x, left, width );
  8038. }
  8039. });
  8040. })( jQuery );
  8041. /*!
  8042. * jQuery UI Widget @VERSION
  8043. *
  8044. * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
  8045. * Dual licensed under the MIT or GPL Version 2 licenses.
  8046. * http://jquery.org/license
  8047. *
  8048. * http://docs.jquery.com/UI/Widget
  8049. */
  8050. (function( $, undefined ) {
  8051. // jQuery 1.4+
  8052. if ( $.cleanData ) {
  8053. var _cleanData = $.cleanData;
  8054. $.cleanData = function( elems ) {
  8055. for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
  8056. try {
  8057. $( elem ).triggerHandler( "remove" );
  8058. // http://bugs.jquery.com/ticket/8235
  8059. } catch( e ) {}
  8060. }
  8061. _cleanData( elems );
  8062. };
  8063. } else {
  8064. var _remove = $.fn.remove;
  8065. $.fn.remove = function( selector, keepData ) {
  8066. return this.each(function() {
  8067. if ( !keepData ) {
  8068. if ( !selector || $.filter( selector, [ this ] ).length ) {
  8069. $( "*", this ).add( [ this ] ).each(function() {
  8070. try {
  8071. $( this ).triggerHandler( "remove" );
  8072. // http://bugs.jquery.com/ticket/8235
  8073. } catch( e ) {}
  8074. });
  8075. }
  8076. }
  8077. return _remove.call( $(this), selector, keepData );
  8078. });
  8079. };
  8080. }
  8081. $.widget = function( name, base, prototype ) {
  8082. var namespace = name.split( "." )[ 0 ],
  8083. fullName;
  8084. name = name.split( "." )[ 1 ];
  8085. fullName = namespace + "-" + name;
  8086. if ( !prototype ) {
  8087. prototype = base;
  8088. base = $.Widget;
  8089. }
  8090. // create selector for plugin
  8091. $.expr[ ":" ][ fullName ] = function( elem ) {
  8092. return !!$.data( elem, name );
  8093. };
  8094. $[ namespace ] = $[ namespace ] || {};
  8095. $[ namespace ][ name ] = function( options, element ) {
  8096. // allow instantiation without initializing for simple inheritance
  8097. if ( arguments.length ) {
  8098. this._createWidget( options, element );
  8099. }
  8100. };
  8101. var basePrototype = new base();
  8102. // we need to make the options hash a property directly on the new instance
  8103. // otherwise we'll modify the options hash on the prototype that we're
  8104. // inheriting from
  8105. // $.each( basePrototype, function( key, val ) {
  8106. // if ( $.isPlainObject(val) ) {
  8107. // basePrototype[ key ] = $.extend( {}, val );
  8108. // }
  8109. // });
  8110. basePrototype.options = $.extend( true, {}, basePrototype.options );
  8111. $[ namespace ][ name ].prototype = $.extend( true, basePrototype, {
  8112. namespace: namespace,
  8113. widgetName: name,
  8114. widgetEventPrefix: $[ namespace ][ name ].prototype.widgetEventPrefix || name,
  8115. widgetBaseClass: fullName
  8116. }, prototype );
  8117. $.widget.bridge( name, $[ namespace ][ name ] );
  8118. };
  8119. $.widget.bridge = function( name, object ) {
  8120. $.fn[ name ] = function( options ) {
  8121. var isMethodCall = typeof options === "string",
  8122. args = Array.prototype.slice.call( arguments, 1 ),
  8123. returnValue = this;
  8124. // allow multiple hashes to be passed on init
  8125. options = !isMethodCall && args.length ?
  8126. $.extend.apply( null, [ true, options ].concat(args) ) :
  8127. options;
  8128. // prevent calls to internal methods
  8129. if ( isMethodCall && options.charAt( 0 ) === "_" ) {
  8130. return returnValue;
  8131. }
  8132. if ( isMethodCall ) {
  8133. this.each(function() {
  8134. var instance = $.data( this, name ),
  8135. methodValue = instance && $.isFunction( instance[options] ) ?
  8136. instance[ options ].apply( instance, args ) :
  8137. instance;
  8138. // TODO: add this back in 1.9 and use $.error() (see #5972)
  8139. // if ( !instance ) {
  8140. // throw "cannot call methods on " + name + " prior to initialization; " +
  8141. // "attempted to call method '" + options + "'";
  8142. // }
  8143. // if ( !$.isFunction( instance[options] ) ) {
  8144. // throw "no such method '" + options + "' for " + name + " widget instance";
  8145. // }
  8146. // var methodValue = instance[ options ].apply( instance, args );
  8147. if ( methodValue !== instance && methodValue !== undefined ) {
  8148. returnValue = methodValue;
  8149. return false;
  8150. }
  8151. });
  8152. } else {
  8153. this.each(function() {
  8154. var instance = $.data( this, name );
  8155. if ( instance ) {
  8156. instance.option( options || {} )._init();
  8157. } else {
  8158. $.data( this, name, new object( options, this ) );
  8159. }
  8160. });
  8161. }
  8162. return returnValue;
  8163. };
  8164. };
  8165. $.Widget = function( options, element ) {
  8166. // allow instantiation without initializing for simple inheritance
  8167. if ( arguments.length ) {
  8168. this._createWidget( options, element );
  8169. }
  8170. };
  8171. $.Widget.prototype = {
  8172. widgetName: "widget",
  8173. widgetEventPrefix: "",
  8174. options: {
  8175. disabled: false
  8176. },
  8177. _createWidget: function( options, element ) {
  8178. // $.widget.bridge stores the plugin instance, but we do it anyway
  8179. // so that it's stored even before the _create function runs
  8180. $.data( element, this.widgetName, this );
  8181. this.element = $( element );
  8182. this.options = $.extend( true, {},
  8183. this.options,
  8184. this._getCreateOptions(),
  8185. options );
  8186. var self = this;
  8187. this.element.bind( "remove." + this.widgetName, function() {
  8188. self.destroy();
  8189. });
  8190. this._create();
  8191. this._trigger( "create" );
  8192. this._init();
  8193. },
  8194. _getCreateOptions: function() {
  8195. return $.metadata && $.metadata.get( this.element[0] )[ this.widgetName ];
  8196. },
  8197. _create: function() {},
  8198. _init: function() {},
  8199. destroy: function() {
  8200. this.element
  8201. .unbind( "." + this.widgetName )
  8202. .removeData( this.widgetName );
  8203. this.widget()
  8204. .unbind( "." + this.widgetName )
  8205. .removeAttr( "aria-disabled" )
  8206. .removeClass(
  8207. this.widgetBaseClass + "-disabled " +
  8208. "ui-state-disabled" );
  8209. },
  8210. widget: function() {
  8211. return this.element;
  8212. },
  8213. option: function( key, value ) {
  8214. var options = key;
  8215. if ( arguments.length === 0 ) {
  8216. // don't return a reference to the internal hash
  8217. return $.extend( {}, this.options );
  8218. }
  8219. if (typeof key === "string" ) {
  8220. if ( value === undefined ) {
  8221. return this.options[ key ];
  8222. }
  8223. options = {};
  8224. options[ key ] = value;
  8225. }
  8226. this._setOptions( options );
  8227. return this;
  8228. },
  8229. _setOptions: function( options ) {
  8230. var self = this;
  8231. $.each( options, function( key, value ) {
  8232. self._setOption( key, value );
  8233. });
  8234. return this;
  8235. },
  8236. _setOption: function( key, value ) {
  8237. this.options[ key ] = value;
  8238. if ( key === "disabled" ) {
  8239. this.widget()
  8240. [ value ? "addClass" : "removeClass"](
  8241. this.widgetBaseClass + "-disabled" + " " +
  8242. "ui-state-disabled" )
  8243. .attr( "aria-disabled", value );
  8244. }
  8245. return this;
  8246. },
  8247. enable: function() {
  8248. return this._setOption( "disabled", false );
  8249. },
  8250. disable: function() {
  8251. return this._setOption( "disabled", true );
  8252. },
  8253. _trigger: function( type, event, data ) {
  8254. var prop, orig,
  8255. callback = this.options[ type ];
  8256. data = data || {};
  8257. event = $.Event( event );
  8258. event.type = ( type === this.widgetEventPrefix ?
  8259. type :
  8260. this.widgetEventPrefix + type ).toLowerCase();
  8261. // the original event may come from any element
  8262. // so we need to reset the target on the new event
  8263. event.target = this.element[ 0 ];
  8264. // copy original event properties over to the new event
  8265. orig = event.originalEvent;
  8266. if ( orig ) {
  8267. for ( prop in orig ) {
  8268. if ( !( prop in event ) ) {
  8269. event[ prop ] = orig[ prop ];
  8270. }
  8271. }
  8272. }
  8273. this.element.trigger( event, data );
  8274. return !( $.isFunction(callback) &&
  8275. callback.call( this.element[0], event, data ) === false ||
  8276. event.isDefaultPrevented() );
  8277. }
  8278. };
  8279. })( jQuery );
  8280. /*!
  8281. * jQuery UI Mouse @VERSION
  8282. *
  8283. * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
  8284. * Dual licensed under the MIT or GPL Version 2 licenses.
  8285. * http://jquery.org/license
  8286. *
  8287. * http://docs.jquery.com/UI/Mouse
  8288. *
  8289. * Depends:
  8290. * jquery.ui.widget.js
  8291. */
  8292. (function( $, undefined ) {
  8293. var mouseHandled = false;
  8294. $( document ).mouseup( function( e ) {
  8295. mouseHandled = false;
  8296. });
  8297. $.widget("ui.mouse", {
  8298. options: {
  8299. cancel: ':input,option',
  8300. distance: 1,
  8301. delay: 0
  8302. },
  8303. _mouseInit: function() {
  8304. var self = this;
  8305. this.element
  8306. .bind('mousedown.'+this.widgetName, function(event) {
  8307. return self._mouseDown(event);
  8308. })
  8309. .bind('click.'+this.widgetName, function(event) {
  8310. if (true === $.data(event.target, self.widgetName + '.preventClickEvent')) {
  8311. $.removeData(event.target, self.widgetName + '.preventClickEvent');
  8312. event.stopImmediatePropagation();
  8313. return false;
  8314. }
  8315. });
  8316. this.started = false;
  8317. },
  8318. // TODO: make sure destroying one instance of mouse doesn't mess with
  8319. // other instances of mouse
  8320. _mouseDestroy: function() {
  8321. this.element.unbind('.'+this.widgetName);
  8322. },
  8323. _mouseDown: function(event) {
  8324. // don't let more than one widget handle mouseStart
  8325. if( mouseHandled ) { return };
  8326. // we may have missed mouseup (out of window)
  8327. (this._mouseStarted && this._mouseUp(event));
  8328. this._mouseDownEvent = event;
  8329. var self = this,
  8330. btnIsLeft = (event.which == 1),
  8331. // event.target.nodeName works around a bug in IE 8 with
  8332. // disabled inputs (#7620)
  8333. elIsCancel = (typeof this.options.cancel == "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false);
  8334. if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
  8335. return true;
  8336. }
  8337. this.mouseDelayMet = !this.options.delay;
  8338. if (!this.mouseDelayMet) {
  8339. this._mouseDelayTimer = setTimeout(function() {
  8340. self.mouseDelayMet = true;
  8341. }, this.options.delay);
  8342. }
  8343. if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
  8344. this._mouseStarted = (this._mouseStart(event) !== false);
  8345. if (!this._mouseStarted) {
  8346. event.preventDefault();
  8347. return true;
  8348. }
  8349. }
  8350. // Click event may never have fired (Gecko & Opera)
  8351. if (true === $.data(event.target, this.widgetName + '.preventClickEvent')) {
  8352. $.removeData(event.target, this.widgetName + '.preventClickEvent');
  8353. }
  8354. // these delegates are required to keep context
  8355. this._mouseMoveDelegate = function(event) {
  8356. return self._mouseMove(event);
  8357. };
  8358. this._mouseUpDelegate = function(event) {
  8359. return self._mouseUp(event);
  8360. };
  8361. $(document)
  8362. .bind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
  8363. .bind('mouseup.'+this.widgetName, this._mouseUpDelegate);
  8364. event.preventDefault();
  8365. mouseHandled = true;
  8366. return true;
  8367. },
  8368. _mouseMove: function(event) {
  8369. // IE mouseup check - mouseup happened when mouse was out of window
  8370. if ($.browser.msie && !(document.documentMode >= 9) && !event.button) {
  8371. return this._mouseUp(event);
  8372. }
  8373. if (this._mouseStarted) {
  8374. this._mouseDrag(event);
  8375. return event.preventDefault();
  8376. }
  8377. if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
  8378. this._mouseStarted =
  8379. (this._mouseStart(this._mouseDownEvent, event) !== false);
  8380. (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
  8381. }
  8382. return !this._mouseStarted;
  8383. },
  8384. _mouseUp: function(event) {
  8385. $(document)
  8386. .unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
  8387. .unbind('mouseup.'+this.widgetName, this._mouseUpDelegate);
  8388. if (this._mouseStarted) {
  8389. this._mouseStarted = false;
  8390. if (event.target == this._mouseDownEvent.target) {
  8391. $.data(event.target, this.widgetName + '.preventClickEvent', true);
  8392. }
  8393. this._mouseStop(event);
  8394. }
  8395. return false;
  8396. },
  8397. _mouseDistanceMet: function(event) {
  8398. return (Math.max(
  8399. Math.abs(this._mouseDownEvent.pageX - event.pageX),
  8400. Math.abs(this._mouseDownEvent.pageY - event.pageY)
  8401. ) >= this.options.distance
  8402. );
  8403. },
  8404. _mouseDelayMet: function(event) {
  8405. return this.mouseDelayMet;
  8406. },
  8407. // These are placeholder methods, to be overriden by extending plugin
  8408. _mouseStart: function(event) {},
  8409. _mouseDrag: function(event) {},
  8410. _mouseStop: function(event) {},
  8411. _mouseCapture: function(event) { return true; }
  8412. });
  8413. })(jQuery);
  8414. /*
  8415. * jQuery UI Position @VERSION
  8416. *
  8417. * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
  8418. * Dual licensed under the MIT or GPL Version 2 licenses.
  8419. * http://jquery.org/license
  8420. *
  8421. * http://docs.jquery.com/UI/Position
  8422. */
  8423. (function( $, undefined ) {
  8424. $.ui = $.ui || {};
  8425. var horizontalPositions = /left|center|right/,
  8426. verticalPositions = /top|center|bottom/,
  8427. center = "center",
  8428. support = {},
  8429. _position = $.fn.position,
  8430. _offset = $.fn.offset;
  8431. $.fn.position = function( options ) {
  8432. if ( !options || !options.of ) {
  8433. return _position.apply( this, arguments );
  8434. }
  8435. // make a copy, we don't want to modify arguments
  8436. options = $.extend( {}, options );
  8437. var target = $( options.of ),
  8438. targetElem = target[0],
  8439. collision = ( options.collision || "flip" ).split( " " ),
  8440. offset = options.offset ? options.offset.split( " " ) : [ 0, 0 ],
  8441. targetWidth,
  8442. targetHeight,
  8443. basePosition;
  8444. if ( targetElem.nodeType === 9 ) {
  8445. targetWidth = target.width();
  8446. targetHeight = target.height();
  8447. basePosition = { top: 0, left: 0 };
  8448. // TODO: use $.isWindow() in 1.9
  8449. } else if ( targetElem.setTimeout ) {
  8450. targetWidth = target.width();
  8451. targetHeight = target.height();
  8452. basePosition = { top: target.scrollTop(), left: target.scrollLeft() };
  8453. } else if ( targetElem.preventDefault ) {
  8454. // force left top to allow flipping
  8455. options.at = "left top";
  8456. targetWidth = targetHeight = 0;
  8457. basePosition = { top: options.of.pageY, left: options.of.pageX };
  8458. } else {
  8459. targetWidth = target.outerWidth();
  8460. targetHeight = target.outerHeight();
  8461. basePosition = target.offset();
  8462. }
  8463. // force my and at to have valid horizontal and veritcal positions
  8464. // if a value is missing or invalid, it will be converted to center
  8465. $.each( [ "my", "at" ], function() {
  8466. var pos = ( options[this] || "" ).split( " " );
  8467. if ( pos.length === 1) {
  8468. pos = horizontalPositions.test( pos[0] ) ?
  8469. pos.concat( [center] ) :
  8470. verticalPositions.test( pos[0] ) ?
  8471. [ center ].concat( pos ) :
  8472. [ center, center ];
  8473. }
  8474. pos[ 0 ] = horizontalPositions.test( pos[0] ) ? pos[ 0 ] : center;
  8475. pos[ 1 ] = verticalPositions.test( pos[1] ) ? pos[ 1 ] : center;
  8476. options[ this ] = pos;
  8477. });
  8478. // normalize collision option
  8479. if ( collision.length === 1 ) {
  8480. collision[ 1 ] = collision[ 0 ];
  8481. }
  8482. // normalize offset option
  8483. offset[ 0 ] = parseInt( offset[0], 10 ) || 0;
  8484. if ( offset.length === 1 ) {
  8485. offset[ 1 ] = offset[ 0 ];
  8486. }
  8487. offset[ 1 ] = parseInt( offset[1], 10 ) || 0;
  8488. if ( options.at[0] === "right" ) {
  8489. basePosition.left += targetWidth;
  8490. } else if ( options.at[0] === center ) {
  8491. basePosition.left += targetWidth / 2;
  8492. }
  8493. if ( options.at[1] === "bottom" ) {
  8494. basePosition.top += targetHeight;
  8495. } else if ( options.at[1] === center ) {
  8496. basePosition.top += targetHeight / 2;
  8497. }
  8498. basePosition.left += offset[ 0 ];
  8499. basePosition.top += offset[ 1 ];
  8500. return this.each(function() {
  8501. var elem = $( this ),
  8502. elemWidth = elem.outerWidth(),
  8503. elemHeight = elem.outerHeight(),
  8504. marginLeft = parseInt( $.curCSS( this, "marginLeft", true ) ) || 0,
  8505. marginTop = parseInt( $.curCSS( this, "marginTop", true ) ) || 0,
  8506. collisionWidth = elemWidth + marginLeft +
  8507. ( parseInt( $.curCSS( this, "marginRight", true ) ) || 0 ),
  8508. collisionHeight = elemHeight + marginTop +
  8509. ( parseInt( $.curCSS( this, "marginBottom", true ) ) || 0 ),
  8510. position = $.extend( {}, basePosition ),
  8511. collisionPosition;
  8512. if ( options.my[0] === "right" ) {
  8513. position.left -= elemWidth;
  8514. } else if ( options.my[0] === center ) {
  8515. position.left -= elemWidth / 2;
  8516. }
  8517. if ( options.my[1] === "bottom" ) {
  8518. position.top -= elemHeight;
  8519. } else if ( options.my[1] === center ) {
  8520. position.top -= elemHeight / 2;
  8521. }
  8522. // prevent fractions if jQuery version doesn't support them (see #5280)
  8523. if ( !support.fractions ) {
  8524. position.left = Math.round( position.left );
  8525. position.top = Math.round( position.top );
  8526. }
  8527. collisionPosition = {
  8528. left: position.left - marginLeft,
  8529. top: position.top - marginTop
  8530. };
  8531. $.each( [ "left", "top" ], function( i, dir ) {
  8532. if ( $.ui.position[ collision[i] ] ) {
  8533. $.ui.position[ collision[i] ][ dir ]( position, {
  8534. targetWidth: targetWidth,
  8535. targetHeight: targetHeight,
  8536. elemWidth: elemWidth,
  8537. elemHeight: elemHeight,
  8538. collisionPosition: collisionPosition,
  8539. collisionWidth: collisionWidth,
  8540. collisionHeight: collisionHeight,
  8541. offset: offset,
  8542. my: options.my,
  8543. at: options.at
  8544. });
  8545. }
  8546. });
  8547. if ( $.fn.bgiframe ) {
  8548. elem.bgiframe();
  8549. }
  8550. elem.offset( $.extend( position, { using: options.using } ) );
  8551. });
  8552. };
  8553. $.ui.position = {
  8554. fit: {
  8555. left: function( position, data ) {
  8556. var win = $( window ),
  8557. over = data.collisionPosition.left + data.collisionWidth - win.width() - win.scrollLeft();
  8558. position.left = over > 0 ? position.left - over : Math.max( position.left - data.collisionPosition.left, position.left );
  8559. },
  8560. top: function( position, data ) {
  8561. var win = $( window ),
  8562. over = data.collisionPosition.top + data.collisionHeight - win.height() - win.scrollTop();
  8563. position.top = over > 0 ? position.top - over : Math.max( position.top - data.collisionPosition.top, position.top );
  8564. }
  8565. },
  8566. flip: {
  8567. left: function( position, data ) {
  8568. if ( data.at[0] === center ) {
  8569. return;
  8570. }
  8571. var win = $( window ),
  8572. over = data.collisionPosition.left + data.collisionWidth - win.width() - win.scrollLeft(),
  8573. myOffset = data.my[ 0 ] === "left" ?
  8574. -data.elemWidth :
  8575. data.my[ 0 ] === "right" ?
  8576. data.elemWidth :
  8577. 0,
  8578. atOffset = data.at[ 0 ] === "left" ?
  8579. data.targetWidth :
  8580. -data.targetWidth,
  8581. offset = -2 * data.offset[ 0 ];
  8582. position.left += data.collisionPosition.left < 0 ?
  8583. myOffset + atOffset + offset :
  8584. over > 0 ?
  8585. myOffset + atOffset + offset :
  8586. 0;
  8587. },
  8588. top: function( position, data ) {
  8589. if ( data.at[1] === center ) {
  8590. return;
  8591. }
  8592. var win = $( window ),
  8593. over = data.collisionPosition.top + data.collisionHeight - win.height() - win.scrollTop(),
  8594. myOffset = data.my[ 1 ] === "top" ?
  8595. -data.elemHeight :
  8596. data.my[ 1 ] === "bottom" ?
  8597. data.elemHeight :
  8598. 0,
  8599. atOffset = data.at[ 1 ] === "top" ?
  8600. data.targetHeight :
  8601. -data.targetHeight,
  8602. offset = -2 * data.offset[ 1 ];
  8603. position.top += data.collisionPosition.top < 0 ?
  8604. myOffset + atOffset + offset :
  8605. over > 0 ?
  8606. myOffset + atOffset + offset :
  8607. 0;
  8608. }
  8609. }
  8610. };
  8611. // offset setter from jQuery 1.4
  8612. if ( !$.offset.setOffset ) {
  8613. $.offset.setOffset = function( elem, options ) {
  8614. // set position first, in-case top/left are set even on static elem
  8615. if ( /static/.test( $.curCSS( elem, "position" ) ) ) {
  8616. elem.style.position = "relative";
  8617. }
  8618. var curElem = $( elem ),
  8619. curOffset = curElem.offset(),
  8620. curTop = parseInt( $.curCSS( elem, "top", true ), 10 ) || 0,
  8621. curLeft = parseInt( $.curCSS( elem, "left", true ), 10) || 0,
  8622. props = {
  8623. top: (options.top - curOffset.top) + curTop,
  8624. left: (options.left - curOffset.left) + curLeft
  8625. };
  8626. if ( 'using' in options ) {
  8627. options.using.call( elem, props );
  8628. } else {
  8629. curElem.css( props );
  8630. }
  8631. };
  8632. $.fn.offset = function( options ) {
  8633. var elem = this[ 0 ];
  8634. if ( !elem || !elem.ownerDocument ) { return null; }
  8635. if ( options ) {
  8636. return this.each(function() {
  8637. $.offset.setOffset( this, options );
  8638. });
  8639. }
  8640. return _offset.call( this );
  8641. };
  8642. }
  8643. // fraction support test (older versions of jQuery don't support fractions)
  8644. (function () {
  8645. var body = document.getElementsByTagName( "body" )[ 0 ],
  8646. div = document.createElement( "div" ),
  8647. testElement, testElementParent, testElementStyle, offset, offsetTotal;
  8648. //Create a "fake body" for testing based on method used in jQuery.support
  8649. testElement = document.createElement( body ? "div" : "body" );
  8650. testElementStyle = {
  8651. visibility: "hidden",
  8652. width: 0,
  8653. height: 0,
  8654. border: 0,
  8655. margin: 0,
  8656. background: "none"
  8657. };
  8658. if ( body ) {
  8659. $.extend( testElementStyle, {
  8660. position: "absolute",
  8661. left: "-1000px",
  8662. top: "-1000px"
  8663. });
  8664. }
  8665. for ( var i in testElementStyle ) {
  8666. testElement.style[ i ] = testElementStyle[ i ];
  8667. }
  8668. testElement.appendChild( div );
  8669. testElementParent = body || document.documentElement;
  8670. testElementParent.insertBefore( testElement, testElementParent.firstChild );
  8671. div.style.cssText = "position: absolute; left: 10.7432222px; top: 10.432325px; height: 30px; width: 201px;";
  8672. offset = $( div ).offset( function( _, offset ) {
  8673. return offset;
  8674. }).offset();
  8675. testElement.innerHTML = "";
  8676. testElementParent.removeChild( testElement );
  8677. offsetTotal = offset.top + offset.left + ( body ? 2000 : 0 );
  8678. support.fractions = offsetTotal > 21 && offsetTotal < 22;
  8679. })();
  8680. }( jQuery ));
  8681. /*!
  8682. * Fluid Infusion v1.5
  8683. *
  8684. * Infusion is distributed under the Educational Community License 2.0 and new BSD licenses:
  8685. * http://wiki.fluidproject.org/display/fluid/Fluid+Licensing
  8686. *
  8687. * For information on copyright, see the individual Infusion source code files:
  8688. * https://github.com/fluid-project/infusion/
  8689. */
  8690. /*
  8691. Copyright 2007-2010 University of Cambridge
  8692. Copyright 2007-2009 University of Toronto
  8693. Copyright 2007-2009 University of California, Berkeley
  8694. Copyright 2010-2011 Lucendo Development Ltd.
  8695. Copyright 2010 OCAD University
  8696. Copyright 2011 Charly Molter
  8697. Licensed under the Educational Community License (ECL), Version 2.0 or the New
  8698. BSD license. You may not use this file except in compliance with one these
  8699. Licenses.
  8700. You may obtain a copy of the ECL 2.0 License and BSD License at
  8701. https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt
  8702. */
  8703. // Declare dependencies
  8704. /*global console, window, fluid:true, fluid_1_5:true, jQuery, opera, YAHOO*/
  8705. // JSLint options
  8706. /*jslint white: true, trailing: true, funcinvoke: true, continue: true, jslintok: true, undef: true, newcap: true, nomen: true, regexp: true, bitwise: true, browser: true, forin: true, maxerr: 100, indent: 4 */
  8707. var fluid_1_5 = fluid_1_5 || {};
  8708. var fluid = fluid || fluid_1_5;
  8709. (function ($, fluid) {
  8710. fluid.version = "Infusion 1.5";
  8711. // Export this for use in environments like node.js, where it is useful for
  8712. // configuring stack trace behaviour
  8713. fluid.Error = Error;
  8714. fluid.environment = {
  8715. fluid: fluid
  8716. };
  8717. var globalObject = window || {};
  8718. fluid.singleThreadLocal = function (initFunc) {
  8719. var value = initFunc();
  8720. return function () {
  8721. return value;
  8722. };
  8723. };
  8724. // Return to the old strategy of monkey-patching this, since this is a most frequently used function within IoC
  8725. fluid.threadLocal = fluid.singleThreadLocal;
  8726. var softFailure = [false];
  8727. // This function will be patched from FluidIoC.js in order to describe complex activities
  8728. fluid.describeActivity = function () {
  8729. return [];
  8730. };
  8731. /**
  8732. * Causes an error message to be logged to the console and a real runtime error to be thrown.
  8733. *
  8734. * @param {String|Error} message the error message to log
  8735. * @param ... Additional arguments
  8736. */
  8737. fluid.fail = function (message /*, ... */) { // jslint:ok - whitespace in arg list
  8738. fluid.setLogging(true);
  8739. fluid.log.apply(null, ["ASSERTION FAILED: "].concat(fluid.makeArray(arguments)).concat(fluid.describeActivity()));
  8740. if (softFailure[0]) {
  8741. throw new Error(message);
  8742. } else {
  8743. message.fail(); // Intentionally cause a browser error by invoking a nonexistent function.
  8744. }
  8745. };
  8746. fluid.pushSoftFailure = function (condition) {
  8747. if (typeof (condition) === "boolean") {
  8748. softFailure.unshift(condition);
  8749. } else if (condition === -1) {
  8750. softFailure.shift();
  8751. }
  8752. };
  8753. fluid.notrycatch = false;
  8754. // A wrapper for the try/catch/finally language feature, to aid debugging on environments
  8755. // such as IE, where any try will destroy stack information for errors
  8756. fluid.tryCatch = function (tryfun, catchfun, finallyfun) {
  8757. finallyfun = finallyfun || fluid.identity;
  8758. if (fluid.notrycatch) {
  8759. var togo = tryfun();
  8760. finallyfun();
  8761. return togo;
  8762. } else {
  8763. try {
  8764. return tryfun();
  8765. } catch (e) {
  8766. if (catchfun) {
  8767. catchfun(e);
  8768. } else {
  8769. throw (e);
  8770. }
  8771. } finally {
  8772. finallyfun();
  8773. }
  8774. }
  8775. };
  8776. // TODO: rescued from kettleCouchDB.js - clean up in time
  8777. fluid.expect = function (name, members, target) {
  8778. fluid.transform(fluid.makeArray(members), function (key) {
  8779. if (typeof target[key] === "undefined") {
  8780. fluid.fail(name + " missing required parameter " + key);
  8781. }
  8782. });
  8783. };
  8784. // Logging
  8785. var logging;
  8786. /** Returns whether logging is enabled **/
  8787. fluid.isLogging = function () {
  8788. return logging;
  8789. };
  8790. /** method to allow user to enable logging (off by default) */
  8791. fluid.setLogging = function (enabled) {
  8792. if (typeof enabled === "boolean") {
  8793. logging = enabled;
  8794. } else {
  8795. logging = false;
  8796. }
  8797. };
  8798. // On some dodgy environments (notably IE9 and recent alphas of Firebug 1.8),
  8799. // console.log/debug are incomplete function objects and need to be operated via
  8800. // this trick: http://stackoverflow.com/questions/5472938/does-ie9-support-console-log-and-is-it-a-real-function
  8801. fluid.applyHostFunction = function (obj, func, args) {
  8802. if (func.apply) {
  8803. func.apply(obj, args);
  8804. } else {
  8805. var applier = Function.prototype.bind.call(func, obj);
  8806. applier.apply(obj, args);
  8807. }
  8808. };
  8809. /** Log a message to a suitable environmental console. If the standard "console"
  8810. * stream is available, the message will be sent there - otherwise either the
  8811. * YAHOO logger or the Opera "postError" stream will be used. Logging must first
  8812. * be enabled with a call to the fluid.setLogging(true) function.
  8813. */
  8814. fluid.log = function (message /*, ... */) { // jslint:ok - whitespace in arg list
  8815. if (logging) {
  8816. var arg0 = fluid.renderTimestamp(new Date()) + ": ";
  8817. var args = [arg0].concat(fluid.makeArray(arguments));
  8818. var str = args.join("");
  8819. if (typeof (console) !== "undefined") {
  8820. if (console.debug) {
  8821. fluid.applyHostFunction(console, console.debug, args);
  8822. } else if (typeof (console.log) === "function") {
  8823. fluid.applyHostFunction(console, console.log, args);
  8824. } else {
  8825. console.log(str); // this branch executes on old IE, fully synthetic console.log
  8826. }
  8827. } else if (typeof (YAHOO) !== "undefined") {
  8828. YAHOO.log(str);
  8829. } else if (typeof (opera) !== "undefined") {
  8830. opera.postError(str);
  8831. }
  8832. }
  8833. };
  8834. // Functional programming utilities.
  8835. /** A basic utility that returns its argument unchanged */
  8836. fluid.identity = function (arg) {
  8837. return arg;
  8838. };
  8839. // Framework and instantiation functions.
  8840. /** Returns true if the argument is a value other than null or undefined **/
  8841. fluid.isValue = function (value) {
  8842. return value !== undefined && value !== null;
  8843. };
  8844. /** Returns true if the argument is a primitive type **/
  8845. fluid.isPrimitive = function (value) {
  8846. var valueType = typeof (value);
  8847. return !value || valueType === "string" || valueType === "boolean" || valueType === "number" || valueType === "function";
  8848. };
  8849. /** Determines whether the supplied object is an array. The strategy used is an optimised
  8850. * approach taken from an earlier version of jQuery - detecting whether the toString() version
  8851. * of the object agrees with the textual form [object Array], or else whether the object is a
  8852. * jQuery object (the most common source of "fake arrays").
  8853. */
  8854. fluid.isArrayable = function (totest) {
  8855. return totest && (totest.jquery || Object.prototype.toString.call(totest) === "[object Array]");
  8856. };
  8857. fluid.isDOMNode = function (obj) {
  8858. // This could be more sound, but messy:
  8859. // http://stackoverflow.com/questions/384286/javascript-isdom-how-do-you-check-if-a-javascript-object-is-a-dom-object
  8860. return obj && typeof (obj.nodeType) === "number";
  8861. };
  8862. /** Return an empty container as the same type as the argument (either an
  8863. * array or hash */
  8864. fluid.freshContainer = function (tocopy) {
  8865. return fluid.isArrayable(tocopy) ? [] : {};
  8866. };
  8867. /** Performs a deep copy (clone) of its argument **/
  8868. fluid.copy = function (tocopy) {
  8869. if (fluid.isPrimitive(tocopy)) {
  8870. return tocopy;
  8871. }
  8872. return $.extend(true, fluid.freshContainer(tocopy), tocopy);
  8873. };
  8874. /** Corrected version of jQuery makeArray that returns an empty array on undefined rather than crashing.
  8875. * We don't deal with as many pathological cases as jQuery **/
  8876. fluid.makeArray = function (arg) {
  8877. var togo = [];
  8878. if (arg !== null && arg !== undefined) {
  8879. if (fluid.isPrimitive(arg) || typeof(arg.length) !== "number") {
  8880. togo.push(arg);
  8881. }
  8882. else {
  8883. for (var i = 0; i < arg.length; ++ i) {
  8884. togo[i] = arg[i];
  8885. }
  8886. }
  8887. }
  8888. return togo;
  8889. };
  8890. function transformInternal(source, togo, key, args) {
  8891. var transit = source[key];
  8892. for (var j = 0; j < args.length - 1; ++j) {
  8893. transit = args[j + 1](transit, key);
  8894. }
  8895. togo[key] = transit;
  8896. }
  8897. /** Return a list or hash of objects, transformed by one or more functions. Similar to
  8898. * jQuery.map, only will accept an arbitrary list of transformation functions and also
  8899. * works on non-arrays.
  8900. * @param source {Array or Object} The initial container of objects to be transformed.
  8901. * @param fn1, fn2, etc. {Function} An arbitrary number of optional further arguments,
  8902. * all of type Function, accepting the signature (object, index), where object is the
  8903. * list member to be transformed, and index is its list index. Each function will be
  8904. * applied in turn to each list member, which will be replaced by the return value
  8905. * from the function.
  8906. * @return The finally transformed list, where each member has been replaced by the
  8907. * original member acted on by the function or functions.
  8908. */
  8909. fluid.transform = function (source) {
  8910. var togo = fluid.freshContainer(source);
  8911. if (fluid.isArrayable(source)) {
  8912. for (var i = 0; i < source.length; ++i) {
  8913. transformInternal(source, togo, i, arguments);
  8914. }
  8915. } else {
  8916. for (var key in source) {
  8917. transformInternal(source, togo, key, arguments);
  8918. }
  8919. }
  8920. return togo;
  8921. };
  8922. /** Better jQuery.each which works on hashes as well as having the arguments
  8923. * the right way round.
  8924. * @param source {Arrayable or Object} The container to be iterated over
  8925. * @param func {Function} A function accepting (value, key) for each iterated
  8926. * object. This function may return a value to terminate the iteration
  8927. */
  8928. fluid.each = function (source, func) {
  8929. if (fluid.isArrayable(source)) {
  8930. for (var i = 0; i < source.length; ++i) {
  8931. func(source[i], i);
  8932. }
  8933. } else {
  8934. for (var key in source) {
  8935. func(source[key], key);
  8936. }
  8937. }
  8938. };
  8939. /** Scan through a list or hash of objects, terminating on the first member which
  8940. * matches a predicate function.
  8941. * @param source {Arrayable or Object} The list or hash of objects to be searched.
  8942. * @param func {Function} A predicate function, acting on a member. A predicate which
  8943. * returns any value which is not <code>undefined</code> will terminate
  8944. * the search. The function accepts (object, index).
  8945. * @param deflt {Object} A value to be returned in the case no predicate function matches
  8946. * a list member. The default will be the natural value of <code>undefined</code>
  8947. * @return The first return value from the predicate function which is not <code>undefined</code>
  8948. */
  8949. fluid.find = function (source, func, deflt) {
  8950. var disp;
  8951. if (fluid.isArrayable(source)) {
  8952. for (var i = 0; i < source.length; ++i) {
  8953. disp = func(source[i], i);
  8954. if (disp !== undefined) {
  8955. return disp;
  8956. }
  8957. }
  8958. } else {
  8959. for (var key in source) {
  8960. disp = func(source[key], key);
  8961. if (disp !== undefined) {
  8962. return disp;
  8963. }
  8964. }
  8965. }
  8966. return deflt;
  8967. };
  8968. /** Scan through a list of objects, "accumulating" a value over them
  8969. * (may be a straightforward "sum" or some other chained computation).
  8970. * @param list {Array} The list of objects to be accumulated over.
  8971. * @param fn {Function} An "accumulation function" accepting the signature (object, total, index) where
  8972. * object is the list member, total is the "running total" object (which is the return value from the previous function),
  8973. * and index is the index number.
  8974. * @param arg {Object} The initial value for the "running total" object.
  8975. * @return {Object} the final running total object as returned from the final invocation of the function on the last list member.
  8976. */
  8977. fluid.accumulate = function (list, fn, arg) {
  8978. for (var i = 0; i < list.length; ++i) {
  8979. arg = fn(list[i], arg, i);
  8980. }
  8981. return arg;
  8982. };
  8983. /** Can through a list of objects, removing those which match a predicate. Similar to
  8984. * jQuery.grep, only acts on the list in-place by removal, rather than by creating
  8985. * a new list by inclusion.
  8986. * @param source {Array|Object} The list of objects to be scanned over.
  8987. * @param fn {Function} A predicate function determining whether an element should be
  8988. * removed. This accepts the standard signature (object, index) and returns a "truthy"
  8989. * result in order to determine that the supplied object should be removed from the list.
  8990. * @return The list, transformed by the operation of removing the matched elements. The
  8991. * supplied list is modified by this operation.
  8992. */
  8993. fluid.remove_if = function (source, fn) {
  8994. if (fluid.isArrayable(source)) {
  8995. for (var i = 0; i < source.length; ++i) {
  8996. if (fn(source[i], i)) {
  8997. source.splice(i, 1);
  8998. --i;
  8999. }
  9000. }
  9001. } else {
  9002. for (var key in source) {
  9003. if (fn(source[key], key)) {
  9004. delete source[key];
  9005. }
  9006. }
  9007. }
  9008. return source;
  9009. };
  9010. /** Accepts an object to be filtered, and a list of keys. Either all keys not present in
  9011. * the list are removed, or only keys present in the list are returned.
  9012. * @param toFilter {Array|Object} The object to be filtered - this will be modified by the operation
  9013. * @param keys {Array of String} The list of keys to operate with
  9014. * @param exclude {boolean} If <code>true</code>, the keys listed are removed rather than included
  9015. * @return the filtered object (the same object that was supplied as <code>toFilter</code>
  9016. */
  9017. fluid.filterKeys = function (toFilter, keys, exclude) {
  9018. return fluid.remove_if($.extend({}, toFilter), function (value, key) {
  9019. return exclude ^ ($.inArray(key, keys) === -1);
  9020. });
  9021. };
  9022. /** A convenience wrapper for <code>fluid.filterKeys</code> with the parameter <code>exclude</code> set to <code>true</code>
  9023. * Returns the supplied object with listed keys removed */
  9024. fluid.censorKeys = function (toCensor, keys) {
  9025. return fluid.filterKeys(toCensor, keys, true);
  9026. };
  9027. fluid.makeFlatten = function (index) {
  9028. return function (obj) {
  9029. var togo = [];
  9030. fluid.each(obj, function (value, key) {
  9031. togo.push(arguments[index]);
  9032. });
  9033. return togo;
  9034. };
  9035. };
  9036. /** Return the keys in the supplied object as an array **/
  9037. fluid.keys = fluid.makeFlatten(1);
  9038. /** Return the values in the supplied object as an array **/
  9039. fluid.values = fluid.makeFlatten(0);
  9040. /**
  9041. * Searches through the supplied object, and returns <code>true</code> if the supplied value
  9042. * can be found
  9043. */
  9044. fluid.contains = function (obj, value) {
  9045. return obj ? fluid.find(obj, function (thisValue, key) {
  9046. if (value === thisValue) {
  9047. return true;
  9048. }
  9049. }) : undefined;
  9050. };
  9051. /**
  9052. * Searches through the supplied object for the first value which matches the one supplied.
  9053. * @param obj {Object} the Object to be searched through
  9054. * @param value {Object} the value to be found. This will be compared against the object's
  9055. * member using === equality.
  9056. * @return {String} The first key whose value matches the one supplied, or <code>null</code> if no
  9057. * such key is found.
  9058. */
  9059. fluid.keyForValue = function (obj, value) {
  9060. return fluid.find(obj, function (thisValue, key) {
  9061. if (value === thisValue) {
  9062. return key;
  9063. }
  9064. });
  9065. };
  9066. /**
  9067. * This method is now deprecated and will be removed in a future release of Infusion.
  9068. * See fluid.keyForValue instead.
  9069. */
  9070. fluid.findKeyInObject = fluid.keyForValue;
  9071. /** Converts an array into an object whose keys are the elements of the array, each with the value "true"
  9072. */
  9073. fluid.arrayToHash = function (array) {
  9074. var togo = {};
  9075. fluid.each(array, function (el) {
  9076. togo[el] = true;
  9077. });
  9078. return togo;
  9079. };
  9080. /**
  9081. * Clears an object or array of its contents. For objects, each property is deleted.
  9082. *
  9083. * @param {Object|Array} target the target to be cleared
  9084. */
  9085. fluid.clear = function (target) {
  9086. if (fluid.isArrayable(target)) {
  9087. target.length = 0;
  9088. } else {
  9089. for (var i in target) {
  9090. delete target[i];
  9091. }
  9092. }
  9093. };
  9094. /**
  9095. * @param boolean ascending <code>true</code> if a comparator is to be returned which
  9096. * sorts strings in descending order of length
  9097. */
  9098. fluid.compareStringLength = function (ascending) {
  9099. return ascending ? function (a, b) {
  9100. return a.length - b.length;
  9101. } : function (a, b) {
  9102. return b.length - a.length;
  9103. };
  9104. };
  9105. // Model functions
  9106. fluid.model = {}; // cannot call registerNamespace yet since it depends on fluid.model
  9107. /** Another special "marker object" representing that a distinguished
  9108. * (probably context-dependent) value should be substituted.
  9109. */
  9110. fluid.VALUE = {type: "fluid.marker", value: "VALUE"};
  9111. /** Another special "marker object" representing that no value is present (where
  9112. * signalling using the value "undefined" is not possible) */
  9113. fluid.NO_VALUE = {type: "fluid.marker", value: "NO_VALUE"};
  9114. /** A marker indicating that a value requires to be expanded after component construction begins **/
  9115. fluid.EXPAND = {type: "fluid.marker", value: "EXPAND"};
  9116. /** A marker indicating that a value requires to be expanded immediately**/
  9117. fluid.EXPAND_NOW = {type: "fluid.marker", value: "EXPAND_NOW"};
  9118. /** Determine whether an object is any marker, or a particular marker - omit the
  9119. * 2nd argument to detect any marker
  9120. */
  9121. fluid.isMarker = function (totest, type) {
  9122. if (!totest || typeof (totest) !== 'object' || totest.type !== "fluid.marker") {
  9123. return false;
  9124. }
  9125. if (!type) {
  9126. return true;
  9127. }
  9128. return totest === type;
  9129. };
  9130. /** Copy a source "model" onto a target **/
  9131. fluid.model.copyModel = function (target, source) {
  9132. fluid.clear(target);
  9133. $.extend(true, target, source);
  9134. };
  9135. /** Parse an EL expression separated by periods (.) into its component segments.
  9136. * @param {String} EL The EL expression to be split
  9137. * @return {Array of String} the component path expressions.
  9138. * TODO: This needs to be upgraded to handle (the same) escaping rules (as RSF), so that
  9139. * path segments containing periods and backslashes etc. can be processed, and be harmonised
  9140. * with the more complex implementations in fluid.pathUtil(data binding).
  9141. */
  9142. fluid.model.parseEL = function (EL) {
  9143. return EL === "" ? [] : String(EL).split('.');
  9144. };
  9145. /** Compose an EL expression from two separate EL expressions. The returned
  9146. * expression will be the one that will navigate the first expression, and then
  9147. * the second, from the value reached by the first. Either prefix or suffix may be
  9148. * the empty string **/
  9149. fluid.model.composePath = function (prefix, suffix) {
  9150. return prefix === "" ? suffix : (suffix === "" ? prefix : prefix + "." + suffix);
  9151. };
  9152. /** Compose any number of path segments, none of which may be empty **/
  9153. fluid.model.composeSegments = function () {
  9154. return fluid.makeArray(arguments).join(".");
  9155. };
  9156. /** Helpful alias for old-style API **/
  9157. fluid.path = fluid.model.composeSegments;
  9158. fluid.composePath = fluid.model.composePath;
  9159. // unsupported, NON-API function
  9160. fluid.requireDataBinding = function () {
  9161. fluid.fail("Please include DataBinding.js in order to operate complex model accessor configuration");
  9162. };
  9163. fluid.model.trundle = fluid.model.getPenultimate = fluid.requireDataBinding;
  9164. // unsupported, NON-API function
  9165. fluid.model.resolvePathSegment = function (root, segment, create, origEnv) {
  9166. if (!origEnv && root.resolvePathSegment) {
  9167. return root.resolvePathSegment(segment);
  9168. }
  9169. if (create && root[segment] === undefined) {
  9170. // This optimisation in this heavily used function has a fair effect
  9171. return root[segment] = {};
  9172. }
  9173. return root[segment];
  9174. };
  9175. // unsupported, NON-API function
  9176. fluid.model.getPenultimateSimple = function (root, EL, environment, create) {
  9177. var origEnv = environment;
  9178. var segs = fluid.model.parseEL(EL);
  9179. for (var i = 0; i < segs.length - 1; ++i) {
  9180. if (!root) {
  9181. return {root: root };
  9182. }
  9183. var segment = segs[i];
  9184. if (environment && environment[segment]) {
  9185. root = environment[segment];
  9186. environment = null;
  9187. } else {
  9188. root = fluid.model.resolvePathSegment(root, segment, create, origEnv);
  9189. }
  9190. }
  9191. return {root: root, last: segs[segs.length - 1]};
  9192. };
  9193. fluid.model.setSimple = function (root, EL, newValue, environment) {
  9194. var pen = fluid.model.getPenultimateSimple(root, EL, environment, true);
  9195. pen.root[pen.last] = newValue;
  9196. };
  9197. /** Evaluates an EL expression by fetching a dot-separated list of members
  9198. * recursively from a provided root.
  9199. * @param root The root data structure in which the EL expression is to be evaluated
  9200. * @param {string} EL The EL expression to be evaluated
  9201. * @param environment An optional "environment" which, if it contains any members
  9202. * at top level, will take priority over the root data structure.
  9203. * @return The fetched data value.
  9204. */
  9205. fluid.model.getSimple = function (root, EL, environment) {
  9206. if (EL === "" || EL === null || EL === undefined) {
  9207. return root;
  9208. }
  9209. var pen = fluid.model.getPenultimateSimple(root, EL, environment);
  9210. return pen.root ? pen.root[pen.last] : pen.root;
  9211. };
  9212. // unsupported, NON-API function
  9213. // Returns undefined to signal complex configuration which needs to be farmed out to DataBinding.js
  9214. // any other return represents an environment value AND a simple configuration we can handle here
  9215. fluid.decodeAccessorArg = function (arg3) {
  9216. return (!arg3 || arg3 === fluid.model.defaultGetConfig || arg3 === fluid.model.defaultSetConfig) ?
  9217. null : (arg3.type === "environment" ? arg3.value : undefined);
  9218. };
  9219. fluid.set = function (root, EL, newValue, config) {
  9220. var env = fluid.decodeAccessorArg(config);
  9221. if (env === undefined) {
  9222. var trundler = fluid.model.getPenultimate(root, EL, config);
  9223. trundler.root[trundler.last] = newValue;
  9224. } else {
  9225. fluid.model.setSimple(root, EL, newValue, env);
  9226. }
  9227. };
  9228. /** Evaluates an EL expression by fetching a dot-separated list of members
  9229. * recursively from a provided root.
  9230. * @param root The root data structure in which the EL expression is to be evaluated
  9231. * @param {string} EL The EL expression to be evaluated
  9232. * @param environment An optional "environment" which, if it contains any members
  9233. * at top level, will take priority over the root data structure.
  9234. * @return The fetched data value.
  9235. */
  9236. fluid.get = function (root, EL, config) {
  9237. var env = fluid.decodeAccessorArg(config);
  9238. return env === undefined ?
  9239. fluid.model.trundle(root, EL, config).root
  9240. : fluid.model.getSimple(root, EL, env);
  9241. };
  9242. // This backward compatibility will be maintained for a number of releases, probably until Fluid 2.0
  9243. fluid.model.setBeanValue = fluid.set;
  9244. fluid.model.getBeanValue = fluid.get;
  9245. fluid.getGlobalValue = function (path, env) {
  9246. if (path) {
  9247. env = env || fluid.environment;
  9248. return fluid.get(globalObject, path, {type: "environment", value: env});
  9249. }
  9250. };
  9251. /**
  9252. * Allows for the calling of a function from an EL expression "functionPath", with the arguments "args", scoped to an framework version "environment".
  9253. * @param {Object} functionPath - An EL expression
  9254. * @param {Object} args - An array of arguments to be applied to the function, specified in functionPath
  9255. * @param {Object} environment - (optional) The object to scope the functionPath to (typically the framework root for version control)
  9256. */
  9257. fluid.invokeGlobalFunction = function (functionPath, args, environment) {
  9258. var func = fluid.getGlobalValue(functionPath, environment);
  9259. if (!func) {
  9260. fluid.fail("Error invoking global function: " + functionPath + " could not be located");
  9261. } else {
  9262. return func.apply(null, args);
  9263. }
  9264. };
  9265. /** Registers a new global function at a given path (currently assumes that
  9266. * it lies within the fluid namespace)
  9267. */
  9268. fluid.registerGlobalFunction = function (functionPath, func, env) {
  9269. env = env || fluid.environment;
  9270. fluid.set(globalObject, functionPath, func, {type: "environment", value: env});
  9271. };
  9272. fluid.setGlobalValue = fluid.registerGlobalFunction;
  9273. /** Ensures that an entry in the global namespace exists **/
  9274. fluid.registerNamespace = function (naimspace, env) {
  9275. env = env || fluid.environment;
  9276. var existing = fluid.getGlobalValue(naimspace, env);
  9277. if (!existing) {
  9278. existing = {};
  9279. fluid.setGlobalValue(naimspace, existing, env);
  9280. }
  9281. return existing;
  9282. };
  9283. // stubs for two functions in FluidDebugging.js
  9284. fluid.dumpEl = fluid.identity;
  9285. fluid.renderTimestamp = fluid.identity;
  9286. /*** The Model Events system. ***/
  9287. fluid.registerNamespace("fluid.event");
  9288. fluid.generateUniquePrefix = function () {
  9289. return (Math.floor(Math.random() * 1e12)).toString(36) + "-";
  9290. };
  9291. var fluid_prefix = fluid.generateUniquePrefix();
  9292. var fluid_guid = 1;
  9293. /** Allocate an string value that will be very likely unique within this Fluid scope (frame or process) **/
  9294. fluid.allocateGuid = function () {
  9295. return fluid_prefix + (fluid_guid++);
  9296. };
  9297. fluid.event.identifyListener = function (listener) {
  9298. if (!listener.$$fluid_guid) {
  9299. listener.$$fluid_guid = fluid.allocateGuid();
  9300. }
  9301. return listener.$$fluid_guid;
  9302. };
  9303. // unsupported, NON-API function
  9304. fluid.event.mapPriority = function (priority, count) {
  9305. return (priority === null || priority === undefined ? -count :
  9306. (priority === "last" ? -Number.MAX_VALUE :
  9307. (priority === "first" ? Number.MAX_VALUE : priority)));
  9308. };
  9309. // unsupported, NON-API function
  9310. fluid.event.listenerComparator = function (recA, recB) {
  9311. return recB.priority - recA.priority;
  9312. };
  9313. // unsupported, NON-API function
  9314. fluid.event.sortListeners = function (listeners) {
  9315. var togo = [];
  9316. fluid.each(listeners, function (listener) {
  9317. togo.push(listener);
  9318. });
  9319. return togo.sort(fluid.event.listenerComparator);
  9320. };
  9321. // unsupported, NON-API function
  9322. fluid.event.resolveListener = function (listener) {
  9323. if (typeof (listener) === "string") {
  9324. var listenerFunc = fluid.getGlobalValue(listener);
  9325. if (!listenerFunc) {
  9326. fluid.fail("Unable to look up name " + listener + " as a global function");
  9327. } else {
  9328. listener = listenerFunc;
  9329. }
  9330. }
  9331. return listener;
  9332. };
  9333. fluid.event.nameEvent = function (that, eventName) {
  9334. return eventName + " of " + fluid.nameComponent(that);
  9335. };
  9336. /** Construct an "event firer" object which can be used to register and deregister
  9337. * listeners, to which "events" can be fired. These events consist of an arbitrary
  9338. * function signature. General documentation on the Fluid events system is at
  9339. * http://wiki.fluidproject.org/display/fluid/The+Fluid+Event+System .
  9340. * @param {Boolean} unicast If <code>true</code>, this is a "unicast" event which may only accept
  9341. * a single listener.
  9342. * @param {Boolean} preventable If <code>true</code> the return value of each handler will
  9343. * be checked for <code>false</code> in which case further listeners will be shortcircuited, and this
  9344. * will be the return value of fire()
  9345. */
  9346. // This name will be deprecated in Fluid 2.0 for fluid.makeEventFirer (or fluid.eventFirer)
  9347. fluid.event.getEventFirer = function (unicast, preventable, name) {
  9348. var listeners; // = {}
  9349. var byId; // = {}
  9350. var sortedListeners; // = []
  9351. function fireToListeners(listeners, args, wrapper) {
  9352. if (!listeners) { return; }
  9353. fluid.log("Firing event " + name + " to list of " + listeners.length + " listeners");
  9354. for (var i = 0; i < listeners.length; ++i) {
  9355. var lisrec = listeners[i];
  9356. lisrec.listener = fluid.event.resolveListener(lisrec.listener);
  9357. var listener = lisrec.listener;
  9358. if (lisrec.predicate && !lisrec.predicate(listener, args)) {
  9359. continue;
  9360. }
  9361. var value = fluid.tryCatch(function () {
  9362. var ret = (wrapper ? wrapper(listener) : listener).apply(null, args);
  9363. if (preventable && ret === false) {
  9364. return false;
  9365. }
  9366. if (unicast) {
  9367. return ret;
  9368. }
  9369. }, function (e) { // jslint:ok - function within a loop, only invoked synchronously
  9370. fluid.log("FireEvent received exception " + e.message + " e " + e + " firing to listener " + i);
  9371. throw (e);
  9372. }); // jslint:ok - function within loop
  9373. if (value !== undefined) {
  9374. return value;
  9375. }
  9376. }
  9377. }
  9378. var that;
  9379. var lazyInit = function () { // Lazy init function to economise on object references
  9380. listeners = {};
  9381. byId = {};
  9382. sortedListeners = [];
  9383. that.addListener = function (listener, namespace, predicate, priority) {
  9384. if (!listener) {
  9385. return;
  9386. }
  9387. if (unicast) {
  9388. namespace = "unicast";
  9389. }
  9390. var id = identify(listener);
  9391. namespace = namespace || id;
  9392. var record = {listener: listener, predicate: predicate,
  9393. namespace: namespace,
  9394. priority: fluid.event.mapPriority(priority, sortedListeners.length)};
  9395. listeners[namespace] = byId[id] = record;
  9396. sortedListeners = fluid.event.sortListeners(listeners);
  9397. };
  9398. that.addListener.apply(null, arguments);
  9399. };
  9400. var identify = fluid.event.identifyListener;
  9401. that = {
  9402. name: name,
  9403. typeName: "fluid.event.firer",
  9404. addListener: function () {
  9405. lazyInit.apply(null, arguments);
  9406. },
  9407. removeListener: function (listener) {
  9408. if (!listeners) { return; }
  9409. var namespace;
  9410. if (typeof (listener) === "string") {
  9411. namespace = listener;
  9412. var record = listeners[listener];
  9413. listener = record.listener;
  9414. }
  9415. var id = identify(listener);
  9416. if (!id) {
  9417. fluid.fail("Cannot remove unregistered listener function ", listener, " from event " + that.name);
  9418. }
  9419. namespace = namespace || byId[id].namespace || id;
  9420. delete byId[id];
  9421. delete listeners[namespace];
  9422. sortedListeners = fluid.event.sortListeners(listeners);
  9423. },
  9424. // NB - this method exists currently solely for the convenience of the new,
  9425. // transactional changeApplier. As it exists it is hard to imagine the function
  9426. // being helpful to any other client. We need to get more experience on the kinds
  9427. // of listeners that are useful, and ultimately factor this method away.
  9428. fireToListeners: function (listeners, args, wrapper) {
  9429. return fireToListeners(listeners, args, wrapper);
  9430. },
  9431. fire: function () {
  9432. return fireToListeners(sortedListeners, arguments);
  9433. }
  9434. };
  9435. return that;
  9436. };
  9437. fluid.makeEventFirer = fluid.event.getEventFirer;
  9438. /** Fire the specified event with supplied arguments. This call is an optimisation utility
  9439. * which handles the case where the firer has not been instantiated (presumably as a result
  9440. * of having no listeners registered
  9441. */
  9442. fluid.fireEvent = function (component, path, args) {
  9443. var firer = fluid.get(component, path);
  9444. if (firer) {
  9445. firer.fire.apply(null, fluid.makeArray(args));
  9446. }
  9447. };
  9448. // unsupported, NON-API function
  9449. fluid.event.addListenerToFirer = function (firer, value, namespace, wrapper) {
  9450. wrapper = wrapper || fluid.identity;
  9451. if (fluid.isArrayable(value)) {
  9452. for (var i = 0; i < value.length; ++i) {
  9453. fluid.event.addListenerToFirer(firer, value[i], namespace, wrapper);
  9454. }
  9455. } else if (typeof (value) === "function" || typeof (value) === "string") {
  9456. wrapper(firer).addListener(value, namespace);
  9457. } else if (value && typeof (value) === "object") {
  9458. wrapper(firer).addListener(value.listener, namespace || value.namespace, value.predicate, value.priority);
  9459. }
  9460. };
  9461. // unsupported, NON-API function - non-IOC passthrough
  9462. fluid.event.resolveListenerRecord = function (records) {
  9463. return { records: records };
  9464. };
  9465. // unsupported, NON-API function
  9466. fluid.mergeListeners = function (that, events, listeners) {
  9467. fluid.each(listeners, function (value, key) {
  9468. var firer, namespace;
  9469. if (key.charAt(0) === "{") {
  9470. if (!fluid.expandOptions) {
  9471. fluid.fail("fluid.expandOptions could not be loaded - please include FluidIoC.js in order to operate IoC-driven event with descriptor " +
  9472. key);
  9473. }
  9474. firer = fluid.expandOptions(key, that);
  9475. } else {
  9476. var keydot = key.indexOf(".");
  9477. if (keydot !== -1) {
  9478. namespace = key.substring(keydot + 1);
  9479. key = key.substring(0, keydot);
  9480. }
  9481. if (!events[key]) {
  9482. fluid.fail("Listener registered for event " + key + " which is not defined for this component");
  9483. events[key] = fluid.makeEventFirer(null, null, fluid.event.nameEvent(that, key));
  9484. }
  9485. firer = events[key];
  9486. }
  9487. record = fluid.event.resolveListenerRecord(value, that, key);
  9488. fluid.event.addListenerToFirer(firer, record.records, namespace, record.adderWrapper);
  9489. });
  9490. };
  9491. function initEvents(that, events, pass) {
  9492. fluid.each(events, function (eventSpec, eventKey) {
  9493. var isIoCEvent = eventSpec && (typeof (eventSpec) !== "string" || eventSpec.charAt(0) === "{");
  9494. var event;
  9495. if (isIoCEvent && pass === "IoC") {
  9496. if (!fluid.event.resolveEvent) {
  9497. fluid.fail("fluid.event.resolveEvent could not be loaded - please include FluidIoC.js in order to operate IoC-driven event with descriptor ",
  9498. eventSpec);
  9499. } else {
  9500. event = fluid.event.resolveEvent(that, eventKey, eventSpec);
  9501. }
  9502. } else if (pass === "flat") {
  9503. event = fluid.makeEventFirer(eventSpec === "unicast", eventSpec === "preventable", fluid.event.nameEvent(that, eventKey));
  9504. }
  9505. if (event) {
  9506. that.events[eventKey] = event;
  9507. }
  9508. });
  9509. }
  9510. // unsupported, NON-API function
  9511. fluid.instantiateFirers = function (that, options) {
  9512. that.events = {};
  9513. // TODO: manual 2-phase instantiation since we have no GINGER WORLD
  9514. initEvents(that, options.events, "flat");
  9515. initEvents(that, options.events, "IoC");
  9516. // TODO: manually expand these late so that members attached to ourselves with preInitFunction can be detected
  9517. //var listeners = fluid.expandOptions ? fluid.expandOptions(options.listeners, that) : options.listeners;
  9518. fluid.mergeListeners(that, that.events, options.listeners);
  9519. };
  9520. fluid.mergeListenerPolicy = function (target, source, key) {
  9521. // cf. triage in mergeListeners
  9522. var hasNamespace = key.charAt(0) !== "{" && key.indexOf(".") !== -1;
  9523. return hasNamespace ? (source ? source : target)
  9524. : fluid.makeArray(target).concat(fluid.makeArray(source));
  9525. };
  9526. fluid.mergeListenersPolicy = function (target, source) {
  9527. target = target || {};
  9528. fluid.each(source, function (listeners, key) {
  9529. target[key] = fluid.mergeListenerPolicy(target[key], listeners, key);
  9530. });
  9531. return target;
  9532. };
  9533. /*** DEFAULTS AND OPTIONS MERGING SYSTEM ***/
  9534. var defaultsStore = {};
  9535. var resolveGradesImpl = function (gs, gradeNames) {
  9536. gradeNames = fluid.makeArray(gradeNames);
  9537. fluid.each(gradeNames, function (gradeName) {
  9538. var options = fluid.rawDefaults(gradeName) || {};
  9539. gs.gradeHash[gradeName] = true;
  9540. gs.gradeChain.push(gradeName);
  9541. gs.optionsChain.push(options);
  9542. var oGradeNames = fluid.makeArray(options.gradeNames);
  9543. fluid.each(oGradeNames, function (parent) {
  9544. if (!gs.gradeHash[parent]) {
  9545. resolveGradesImpl(gs, parent);
  9546. }
  9547. });
  9548. });
  9549. return gs;
  9550. };
  9551. // unsupported, NON-API function
  9552. fluid.resolveGradeStructure = function (gradeNames) {
  9553. var gradeStruct = {
  9554. gradeChain: [],
  9555. gradeHash: {},
  9556. optionsChain: []
  9557. };
  9558. return resolveGradesImpl(gradeStruct, gradeNames);
  9559. };
  9560. var mergedDefaultsCache = {};
  9561. fluid.gradeNamesToKey = function (gradeNames, defaultName) {
  9562. return defaultName + "|" + fluid.makeArray(gradeNames).sort().join("|");
  9563. };
  9564. // unsupported, NON-API function
  9565. fluid.resolveGrade = function (defaults, defaultName, gradeNames) {
  9566. var mergeArgs = [defaults];
  9567. if (gradeNames) {
  9568. var gradeStruct = fluid.resolveGradeStructure(gradeNames);
  9569. mergeArgs = gradeStruct.optionsChain.reverse().concat(mergeArgs).concat({gradeNames: gradeStruct.gradeChain});
  9570. }
  9571. var mergePolicy = {};
  9572. for (var i = 0; i < mergeArgs.length; ++ i) {
  9573. mergePolicy = $.extend(true, mergePolicy, mergeArgs[i].mergePolicy);
  9574. }
  9575. mergeArgs = [mergePolicy, {}].concat(mergeArgs);
  9576. var mergedDefaults = fluid.merge.apply(null, mergeArgs);
  9577. return mergedDefaults;
  9578. };
  9579. fluid.getGradedDefaults = function (defaults, defaultName, gradeNames) {
  9580. var key = fluid.gradeNamesToKey(gradeNames, defaultName);
  9581. var mergedDefaults = mergedDefaultsCache[key];
  9582. if (!mergedDefaults) {
  9583. mergedDefaults = mergedDefaultsCache[key] = fluid.resolveGrade(defaults, defaultName, gradeNames);
  9584. }
  9585. return mergedDefaults;
  9586. };
  9587. // unsupported, NON-API function
  9588. fluid.resolveGradedOptions = function (componentName) {
  9589. var defaults = fluid.rawDefaults(componentName);
  9590. if (!defaults) {
  9591. return defaults;
  9592. } else {
  9593. return fluid.getGradedDefaults(defaults, componentName, defaults.gradeNames);
  9594. }
  9595. };
  9596. // unsupported, NON-API function
  9597. fluid.rawDefaults = function (componentName, options) {
  9598. if (options === undefined) {
  9599. return defaultsStore[componentName];
  9600. } else {
  9601. defaultsStore[componentName] = options;
  9602. }
  9603. };
  9604. fluid.hasGrade = function (options, gradeName) {
  9605. return !options || !options.gradeNames ? false : fluid.contains(options.gradeNames, gradeName);
  9606. };
  9607. /**
  9608. * Retrieves and stores a component's default settings centrally.
  9609. * @param {boolean} (options) if true, manipulate a global option (for the head
  9610. * component) rather than instance options. NB - the use of "global options"
  9611. * is deprecated and will be removed from the framework in release 1.6
  9612. * @param {String} componentName the name of the component
  9613. * @param {Object} (optional) an container of key/value pairs to set
  9614. */
  9615. fluid.defaults = function () {
  9616. var offset = 0;
  9617. if (typeof arguments[0] === "boolean") {
  9618. offset = 1;
  9619. }
  9620. var componentName = (offset === 0 ? "" : "*.global-") + arguments[offset];
  9621. var options = arguments[offset + 1];
  9622. if (options === undefined) {
  9623. return fluid.resolveGradedOptions(componentName);
  9624. } else {
  9625. if (options && options.options) {
  9626. fluid.fail("Probable error in options structure for " + componentName +
  9627. " with option named \"options\" - perhaps you meant to write these options at top level in fluid.defaults? - ", options);
  9628. }
  9629. fluid.rawDefaults(componentName, options);
  9630. if (fluid.hasGrade(options, "autoInit")) {
  9631. fluid.makeComponent(componentName, fluid.resolveGradedOptions(componentName));
  9632. }
  9633. }
  9634. };
  9635. fluid.makeComponent = function (componentName, options) {
  9636. if (!options.initFunction || !options.gradeNames) {
  9637. fluid.fail("Cannot autoInit component " + componentName + " which does not have an initFunction and gradeName defined");
  9638. }
  9639. var creator = function () {
  9640. return fluid.initComponent(componentName, arguments);
  9641. };
  9642. var existing = fluid.getGlobalValue(componentName);
  9643. if (existing) {
  9644. $.extend(creator, existing);
  9645. }
  9646. fluid.setGlobalValue(componentName, creator);
  9647. };
  9648. fluid.makeComponents = function (components, env) {
  9649. fluid.each(components, function (value, key) {
  9650. var options = {
  9651. gradeNames: fluid.makeArray(value).concat(["autoInit"])
  9652. };
  9653. fluid.defaults(key, options);
  9654. });
  9655. };
  9656. // The base system grade definitions
  9657. fluid.defaults("fluid.function", {});
  9658. fluid.lifecycleFunctions = {
  9659. preInitFunction: true,
  9660. postInitFunction: true,
  9661. finalInitFunction: true
  9662. };
  9663. fluid.rootMergePolicy = fluid.transform(fluid.lifecycleFunctions, function () {
  9664. return fluid.mergeListenerPolicy;
  9665. });
  9666. fluid.defaults("fluid.littleComponent", {
  9667. initFunction: "fluid.initLittleComponent",
  9668. mergePolicy: fluid.rootMergePolicy,
  9669. argumentMap: {
  9670. options: 0
  9671. }
  9672. });
  9673. fluid.defaults("fluid.eventedComponent", {
  9674. gradeNames: ["fluid.littleComponent"],
  9675. events: { // Four standard lifecycle points common to all components
  9676. onCreate: null,
  9677. onAttach: null, // events other than onCreate are only fired for IoC-configured components
  9678. onClear: null,
  9679. onDestroy: null
  9680. },
  9681. mergePolicy: {
  9682. listeners: fluid.mergeListenersPolicy
  9683. }
  9684. });
  9685. fluid.preInitModelComponent = function (that) {
  9686. that.model = that.options.model || {};
  9687. that.applier = that.options.applier || (fluid.makeChangeApplier ? fluid.makeChangeApplier(that.model, that.options.changeApplierOptions) : null);
  9688. };
  9689. fluid.defaults("fluid.modelComponent", {
  9690. gradeNames: ["fluid.littleComponent"],
  9691. preInitFunction: {
  9692. namespace: "preInitModelComponent",
  9693. listener: "fluid.preInitModelComponent"
  9694. },
  9695. mergePolicy: {
  9696. model: "preserve",
  9697. applier: "nomerge"
  9698. }
  9699. });
  9700. /** Generate a name for a component for debugging purposes */
  9701. fluid.nameComponent = function (that) {
  9702. return that ? "component with typename " + that.typeName + " and id " + that.id : "[unknown component]";
  9703. };
  9704. // unsupported, NON-API function
  9705. fluid.guardCircularity = function (seenIds, source, message1, message2) {
  9706. if (source && source.id) {
  9707. if (!seenIds[source.id]) {
  9708. seenIds[source.id] = source;
  9709. } else if (seenIds[source.id] === source) {
  9710. fluid.fail("Circularity in options " + message1 + " - " + fluid.nameComponent(source)
  9711. + " has already been seen" + message2);
  9712. }
  9713. }
  9714. };
  9715. // TODO: so far, profiling does not suggest that this implementation is a performance risk, but we really should start
  9716. // "precompiling" these.
  9717. // unsupported, NON-API function
  9718. fluid.mergePolicyIs = function (policy, test) {
  9719. return typeof (policy) === "string" && $.inArray(test, policy.split(/\s*,\s*/)) !== -1;
  9720. };
  9721. // Cheapskate implementation which avoids dependency on DataBinding.js
  9722. fluid.model.mergeModel = function (target, source, applier) {
  9723. if (!fluid.isPrimitive(target)) {
  9724. var copySource = fluid.copy(source);
  9725. $.extend(true, source, target);
  9726. $.extend(true, source, copySource);
  9727. }
  9728. return source;
  9729. };
  9730. function mergeImpl(policy, basePath, target, source, thisPolicy, rec) {
  9731. if (fluid.isTracing) {
  9732. fluid.tracing.pathCount.push(basePath);
  9733. }
  9734. if (fluid.mergePolicyIs(thisPolicy, "replace")) {
  9735. fluid.clear(target);
  9736. }
  9737. fluid.guardCircularity(rec.seenIds, source, "merging", " when evaluating path " + basePath + " - please protect components from merging using the \"nomerge\" merge policy");
  9738. for (var name in source) {
  9739. var path = (basePath ? basePath + "." : "") + name;
  9740. var newPolicy = policy && typeof (policy) !== "string" ? policy[path] : policy;
  9741. var funcPolicy = typeof (newPolicy) === "function";
  9742. var thisTarget = target[name];
  9743. var thisSource = source[name];
  9744. var primitiveTarget = fluid.isPrimitive(thisTarget);
  9745. if (thisSource !== undefined) {
  9746. if (!funcPolicy && thisSource !== null && typeof (thisSource) === "object" &&
  9747. !fluid.isDOMNode(thisSource) && !thisSource.jquery && thisSource !== fluid.VALUE &&
  9748. !fluid.mergePolicyIs(newPolicy, "preserve") && !fluid.mergePolicyIs(newPolicy, "nomerge")) {
  9749. if (primitiveTarget) {
  9750. target[name] = thisTarget = fluid.freshContainer(thisSource);
  9751. }
  9752. mergeImpl(policy, path, thisTarget, thisSource, newPolicy, rec);
  9753. } else {
  9754. if (funcPolicy) {
  9755. target[name] = newPolicy.call(null, thisTarget, thisSource, name);
  9756. } else if (!fluid.isValue(thisTarget) || !fluid.mergePolicyIs(newPolicy, "reverse")) {
  9757. // TODO: When "grades" are implemented, grandfather in any paired applier to perform these operations
  9758. // NB: mergePolicy of "preserve" now creates dependency on DataBinding.js
  9759. target[name] = fluid.isValue(thisTarget) && fluid.mergePolicyIs(newPolicy, "preserve") ? fluid.model.mergeModel(thisTarget, thisSource) : thisSource;
  9760. }
  9761. }
  9762. }
  9763. }
  9764. return target;
  9765. }
  9766. // TODO: deprecate this method of detecting default value merge policies before 1.6 in favour of
  9767. // explicit typed records a la ModelTransformations
  9768. // unsupported, NON-API function
  9769. fluid.isDefaultValueMergePolicy = function (policy) {
  9770. return typeof(policy) === "string"
  9771. && (policy.indexOf(",") === -1 && !/replace|preserve|nomerge|noexpand|reverse/.test(policy));
  9772. };
  9773. // unsupported, NON-API function
  9774. fluid.applyDefaultValueMergePolicy = function (defaults, merged) {
  9775. var policy = merged.mergePolicy;
  9776. if (policy && typeof (policy) !== "string") {
  9777. for (var key in policy) {
  9778. var elrh = policy[key];
  9779. if (fluid.isDefaultValueMergePolicy(elrh)) {
  9780. var defaultTarget = fluid.get(defaults, key);
  9781. var mergedTarget = fluid.get(merged, key);
  9782. // TODO: this implementation is faulty since it will trigger if a user modifies a source value to its default value
  9783. // - and also will still copy over a target if user modifies it to its default value
  9784. // probably needs FLUID-4392 for a proper fix since the algorithm will need a fundamental change to progress from
  9785. // R2L rather than L2R (is that even possible!)
  9786. if (defaultTarget === mergedTarget) {
  9787. var defaultSource = fluid.get(defaults, elrh);
  9788. var mergedSource = fluid.get(merged, elrh);
  9789. // NB: This line represents a change in policy - will not apply default value policy to defaults themselves
  9790. if (defaultSource !== mergedSource) {
  9791. fluid.set(merged, key, mergedSource);
  9792. }
  9793. }
  9794. }
  9795. }
  9796. }
  9797. return merged;
  9798. };
  9799. /** Merge a collection of options structures onto a target, following an optional policy.
  9800. * This function is typically called automatically, as a result of an invocation of
  9801. * <code>fluid.initLittleComponent</code>. The behaviour of this function is explained more fully on
  9802. * the page http://wiki.fluidproject.org/display/fluid/Options+Merging+for+Fluid+Components .
  9803. * @param policy {Object/String} A "policy object" specifiying the type of merge to be performed.
  9804. * If policy is of type {String} it should take on the value "reverse" or "replace" representing
  9805. * a static policy. If it is an
  9806. * Object, it should contain a mapping of EL paths onto these String values, representing a
  9807. * fine-grained policy. If it is an Object, the values may also themselves be EL paths
  9808. * representing that a default value is to be taken from that path.
  9809. * @param target {Object} The options structure which is to be modified by receiving the merge results.
  9810. * @param options1, options2, .... {Object} an arbitrary list of options structure which are to
  9811. * be merged "on top of" the <code>target</code>. These will not be modified.
  9812. */
  9813. fluid.merge = function (policy, target) {
  9814. var path = "";
  9815. for (var i = 2; i < arguments.length; ++i) {
  9816. var source = arguments[i];
  9817. if (source !== null && source !== undefined) {
  9818. mergeImpl(policy, path, target, source, policy ? policy[""] : null, {seenIds: {}});
  9819. }
  9820. }
  9821. return target;
  9822. };
  9823. // unsupported, NON-API function
  9824. fluid.transformOptions = function (mergeArgs, transRec) {
  9825. fluid.expect("Options transformation record", ["transformer", "config"], transRec);
  9826. var transFunc = fluid.getGlobalValue(transRec.transformer);
  9827. var togo = fluid.transform(mergeArgs, function (value, key) {
  9828. return key === 0 ? value : transFunc.call(null, value, transRec.config);
  9829. });
  9830. return togo;
  9831. };
  9832. // unsupporter, NON-API function
  9833. fluid.lastTransformationRecord = function (extraArgs) {
  9834. for (var i = extraArgs.length - 1; i >= 0; --i) {
  9835. if (extraArgs[i] && extraArgs[i].transformOptions) {
  9836. return extraArgs[i].transformOptions;
  9837. }
  9838. }
  9839. };
  9840. /**
  9841. * Merges the component's declared defaults, as obtained from fluid.defaults(),
  9842. * with the user's specified overrides.
  9843. *
  9844. * @param {Object} that the instance to attach the options to
  9845. * @param {String} componentName the unique "name" of the component, which will be used
  9846. * to fetch the default options from store. By recommendation, this should be the global
  9847. * name of the component's creator function.
  9848. * @param {Object} userOptions the user-specified configuration options for this component
  9849. */
  9850. // unsupported, NON-API function
  9851. fluid.mergeComponentOptions = function (that, componentName, userOptions, localOptions) {
  9852. var defaults = fluid.defaults(componentName) || {};
  9853. var mergePolicy = $.extend({}, fluid.rootMergePolicy, defaults.mergePolicy);
  9854. var defaultGrades = defaults.gradeNames;
  9855. localOptions = defaultGrades ? {} : fluid.copy(fluid.getGradedDefaults({}, "", localOptions.gradeNames));
  9856. var mergeArgs = [mergePolicy, localOptions];
  9857. var extraArgs;
  9858. if (fluid.expandComponentOptions) {
  9859. extraArgs = fluid.expandComponentOptions(defaults, userOptions, that);
  9860. } else {
  9861. extraArgs = [defaults, userOptions];
  9862. }
  9863. var transRec = fluid.lastTransformationRecord(extraArgs);
  9864. if (transRec) {
  9865. extraArgs = fluid.transformOptions(extraArgs, transRec);
  9866. }
  9867. mergeArgs = mergeArgs.concat(extraArgs);
  9868. var merged = fluid.merge.apply(null, mergeArgs);
  9869. merged = fluid.applyDefaultValueMergePolicy(defaults, merged);
  9870. that.options = merged;
  9871. };
  9872. // The Fluid Component System proper
  9873. /** A special "marker object" which is recognised as one of the arguments to
  9874. * fluid.initSubcomponents. This object is recognised by reference equality -
  9875. * where it is found, it is replaced in the actual argument position supplied
  9876. * to the specific subcomponent instance, with the particular options block
  9877. * for that instance attached to the overall "that" object.
  9878. * NOTE: The use of this marker has been deprecated as of the Fluid 1.4 release in
  9879. * favour of the contextual EL path "{options}" - it will be removed in a future
  9880. * release of the framework.
  9881. */
  9882. fluid.COMPONENT_OPTIONS = {type: "fluid.marker", value: "COMPONENT_OPTIONS"};
  9883. /** Construct a dummy or "placeholder" subcomponent, that optionally provides empty
  9884. * implementations for a set of methods.
  9885. */
  9886. fluid.emptySubcomponent = function (options) {
  9887. var that = {};
  9888. options = $.makeArray(options);
  9889. for (var i = 0; i < options.length; ++i) {
  9890. that[options[i]] = fluid.identity;
  9891. }
  9892. return that;
  9893. };
  9894. /** Compute a "nickname" given a fully qualified typename, by returning the last path
  9895. * segment.
  9896. */
  9897. fluid.computeNickName = function (typeName) {
  9898. var segs = fluid.model.parseEL(typeName);
  9899. return segs[segs.length - 1];
  9900. };
  9901. /** Create a "type tag" component with no state but simply a type name and id. The most
  9902. * minimal form of Fluid component */
  9903. fluid.typeTag = function (name) {
  9904. return name ? {
  9905. typeName: name,
  9906. id: fluid.allocateGuid()
  9907. } : null;
  9908. };
  9909. /** A combined "component and grade name" which allows type tags to be declaratively constructed
  9910. * from options material */
  9911. fluid.typeFount = function (options) {
  9912. var that = fluid.initLittleComponent("fluid.typeFount", options);
  9913. return fluid.typeTag(that.options.targetTypeName);
  9914. };
  9915. /**
  9916. * Creates a new "little component": a that-ist object with options merged into it by the framework.
  9917. * This method is a convenience for creating small objects that have options but don't require full
  9918. * View-like features such as the DOM Binder or events
  9919. *
  9920. * @param {Object} name the name of the little component to create
  9921. * @param {Object} options user-supplied options to merge with the defaults
  9922. */
  9923. // NOTE: the 3rd argument localOptions is NOT to be advertised as part of the stable API, it is present
  9924. // just to allow backward compatibility whilst grade specifications are not mandatory
  9925. fluid.initLittleComponent = function (name, options, localOptions) {
  9926. var that = fluid.typeTag(name);
  9927. // TODO: nickName must be available earlier than other merged options so that component may resolve to itself
  9928. that.nickName = options && options.nickName ? options.nickName : fluid.computeNickName(that.typeName);
  9929. localOptions = localOptions || {gradeNames: "fluid.littleComponent"};
  9930. fluid.mergeComponentOptions(that, name, options, localOptions);
  9931. fluid.initLifecycleFunctions(that);
  9932. fluid.fireEvent(that.options, "preInitFunction", that);
  9933. if (fluid.hasGrade(that.options, "fluid.eventedComponent")) {
  9934. fluid.instantiateFirers(that, that.options);
  9935. }
  9936. if (!fluid.hasGrade(that.options, "autoInit")) {
  9937. fluid.clearLifecycleFunctions(that.options);
  9938. }
  9939. return that;
  9940. };
  9941. // unsupported, NON-API function
  9942. fluid.initLifecycleFunctions = function (that) {
  9943. fluid.each(fluid.lifecycleFunctions, function (func, key) {
  9944. var value = that.options[key];
  9945. if (value) {
  9946. that.options[key] = fluid.makeEventFirer(null, null, key);
  9947. fluid.event.addListenerToFirer(that.options[key], value);
  9948. }
  9949. });
  9950. };
  9951. // unsupported, NON-API function
  9952. fluid.clearLifecycleFunctions = function (options) {
  9953. fluid.each(fluid.lifecycleFunctions, function (value, key) {
  9954. delete options[key];
  9955. });
  9956. delete options.initFunction;
  9957. };
  9958. fluid.diagnoseFailedView = fluid.identity;
  9959. fluid.makeRootDestroy = function (that) {
  9960. return function () {
  9961. fluid.fireEvent(that, "events.onClear", [that, "", null]);
  9962. fluid.fireEvent(that, "events.onDestroy", [that, "", null]);
  9963. };
  9964. };
  9965. fluid.initComponent = function (componentName, initArgs) {
  9966. var options = fluid.defaults(componentName);
  9967. if (!options.gradeNames) {
  9968. fluid.fail("Cannot initialise component " + componentName + " which has no gradeName registered");
  9969. }
  9970. var args = [componentName].concat(fluid.makeArray(initArgs)); // TODO: support different initFunction variants
  9971. var that = fluid.invokeGlobalFunction(options.initFunction, args);
  9972. fluid.diagnoseFailedView(componentName, that, options, args);
  9973. fluid.fireEvent(that.options, "postInitFunction", that);
  9974. if (fluid.initDependents) {
  9975. fluid.initDependents(that);
  9976. }
  9977. fluid.fireEvent(that.options, "finalInitFunction", that);
  9978. fluid.clearLifecycleFunctions(that.options);
  9979. that.destroy = fluid.makeRootDestroy(that); // overwritten by FluidIoC for constructed subcomponents
  9980. fluid.fireEvent(that, "events.onCreate", that);
  9981. return that.options.returnedPath ? fluid.get(that, that.options.returnedPath) : that;
  9982. };
  9983. // unsupported, NON-API function
  9984. fluid.initSubcomponentImpl = function (that, entry, args) {
  9985. var togo;
  9986. if (typeof (entry) !== "function") {
  9987. var entryType = typeof (entry) === "string" ? entry : entry.type;
  9988. var globDef = fluid.defaults(true, entryType);
  9989. fluid.merge("reverse", that.options, globDef);
  9990. togo = entryType === "fluid.emptySubcomponent" ?
  9991. fluid.emptySubcomponent(entry.options) :
  9992. fluid.invokeGlobalFunction(entryType, args);
  9993. } else {
  9994. togo = entry.apply(null, args);
  9995. }
  9996. // TODO: deprecate "returnedOptions" and incorporate into regular ginger world system
  9997. var returnedOptions = togo ? togo.returnedOptions : null;
  9998. if (returnedOptions) {
  9999. fluid.merge(that.options.mergePolicy, that.options, returnedOptions);
  10000. if (returnedOptions.listeners) {
  10001. fluid.mergeListeners(that, that.events, returnedOptions.listeners);
  10002. }
  10003. }
  10004. return togo;
  10005. };
  10006. /** Initialise all the "subcomponents" which are configured to be attached to
  10007. * the supplied top-level component, which share a particular "class name".
  10008. * @param {Component} that The top-level component for which sub-components are
  10009. * to be instantiated. It contains specifications for these subcomponents in its
  10010. * <code>options</code> structure.
  10011. * @param {String} className The "class name" or "category" for the subcomponents to
  10012. * be instantiated. A class name specifies an overall "function" for a class of
  10013. * subcomponents and represents a category which accept the same signature of
  10014. * instantiation arguments.
  10015. * @param {Array of Object} args The instantiation arguments to be passed to each
  10016. * constructed subcomponent. These will typically be members derived from the
  10017. * top-level <code>that</code> or perhaps globally discovered from elsewhere. One
  10018. * of these arguments may be <code>fluid.COMPONENT_OPTIONS</code> in which case this
  10019. * placeholder argument will be replaced by instance-specific options configured
  10020. * into the member of the top-level <code>options</code> structure named for the
  10021. * <code>className</code>
  10022. * @return {Array of Object} The instantiated subcomponents, one for each member
  10023. * of <code>that.options[className]</code>.
  10024. */
  10025. fluid.initSubcomponents = function (that, className, args) {
  10026. var entry = that.options[className];
  10027. if (!entry) {
  10028. return;
  10029. }
  10030. var entries = $.makeArray(entry);
  10031. var optindex = -1;
  10032. var togo = [];
  10033. args = $.makeArray(args);
  10034. for (var i = 0; i < args.length; ++i) {
  10035. if (args[i] === fluid.COMPONENT_OPTIONS) {
  10036. optindex = i;
  10037. }
  10038. }
  10039. for (i = 0; i < entries.length; ++i) {
  10040. entry = entries[i];
  10041. if (optindex !== -1) {
  10042. args[optindex] = entry.options;
  10043. }
  10044. togo[i] = fluid.initSubcomponentImpl(that, entry, args);
  10045. }
  10046. return togo;
  10047. };
  10048. fluid.initSubcomponent = function (that, className, args) {
  10049. return fluid.initSubcomponents(that, className, args)[0];
  10050. };
  10051. // Message resolution and templating
  10052. /**
  10053. * Converts a string to a regexp with the specified flags given in parameters
  10054. * @param {String} a string that has to be turned into a regular expression
  10055. * @param {String} the flags to provide to the reg exp
  10056. */
  10057. fluid.stringToRegExp = function (str, flags) {
  10058. return new RegExp(str.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&"), flags);
  10059. };
  10060. /**
  10061. * Simple string template system.
  10062. * Takes a template string containing tokens in the form of "%value".
  10063. * Returns a new string with the tokens replaced by the specified values.
  10064. * Keys and values can be of any data type that can be coerced into a string. Arrays will work here as well.
  10065. *
  10066. * @param {String} template a string (can be HTML) that contains tokens embedded into it
  10067. * @param {object} values a collection of token keys and values
  10068. */
  10069. fluid.stringTemplate = function (template, values) {
  10070. var keys = fluid.keys(values);
  10071. keys = keys.sort(fluid.compareStringLength());
  10072. for (var i = 0; i < keys.length; ++i) {
  10073. var key = keys[i];
  10074. var re = fluid.stringToRegExp("%" + key, "g");
  10075. template = template.replace(re, values[key]);
  10076. }
  10077. return template;
  10078. };
  10079. fluid.messageResolver = function (options) {
  10080. var that = fluid.initLittleComponent("fluid.messageResolver", options);
  10081. that.messageBase = that.options.parseFunc(that.options.messageBase);
  10082. that.lookup = function (messagecodes) {
  10083. var resolved = fluid.messageResolver.resolveOne(that.messageBase, messagecodes);
  10084. if (resolved === undefined) {
  10085. return fluid.find(that.options.parents, function (parent) {
  10086. return parent.lookup(messagecodes);
  10087. });
  10088. } else {
  10089. return {template: resolved, resolveFunc: that.options.resolveFunc};
  10090. }
  10091. };
  10092. that.resolve = function (messagecodes, args) {
  10093. if (!messagecodes) {
  10094. return "[No messagecodes provided]";
  10095. }
  10096. messagecodes = fluid.makeArray(messagecodes);
  10097. var looked = that.lookup(messagecodes);
  10098. return looked ? looked.resolveFunc(looked.template, args) :
  10099. "[Message string for key " + messagecodes[0] + " not found]";
  10100. };
  10101. return that;
  10102. };
  10103. fluid.defaults("fluid.messageResolver", {
  10104. mergePolicy: {
  10105. messageBase: "preserve",
  10106. parents: "nomerge"
  10107. },
  10108. resolveFunc: fluid.stringTemplate,
  10109. parseFunc: fluid.identity,
  10110. messageBase: {},
  10111. parents: []
  10112. });
  10113. fluid.messageResolver.resolveOne = function (messageBase, messagecodes) {
  10114. for (var i = 0; i < messagecodes.length; ++i) {
  10115. var code = messagecodes[i];
  10116. var message = messageBase[code];
  10117. if (message !== undefined) {
  10118. return message;
  10119. }
  10120. }
  10121. };
  10122. /** Converts a data structure consisting of a mapping of keys to message strings,
  10123. * into a "messageLocator" function which maps an array of message codes, to be
  10124. * tried in sequence until a key is found, and an array of substitution arguments,
  10125. * into a substituted message string.
  10126. */
  10127. fluid.messageLocator = function (messageBase, resolveFunc) {
  10128. var resolver = fluid.messageResolver({messageBase: messageBase, resolveFunc: resolveFunc});
  10129. return function (messagecodes, args) {
  10130. return resolver.resolve(messagecodes, args);
  10131. };
  10132. };
  10133. })(jQuery, fluid_1_5);
  10134. /*
  10135. Copyright 2007-2010 University of Cambridge
  10136. Copyright 2007-2009 University of Toronto
  10137. Copyright 2010-2011 Lucendo Development Ltd.
  10138. Copyright 2010 OCAD University
  10139. Licensed under the Educational Community License (ECL), Version 2.0 or the New
  10140. BSD license. You may not use this file except in compliance with one these
  10141. Licenses.
  10142. You may obtain a copy of the ECL 2.0 License and BSD License at
  10143. https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt
  10144. */
  10145. /** This file contains functions which depend on the presence of a DOM document
  10146. * but which do not depend on the contents of Fluid.js **/
  10147. // Declare dependencies
  10148. /*global fluid_1_5:true, jQuery*/
  10149. // JSLint options
  10150. /*jslint white: true, funcinvoke: true, undef: true, newcap: true, nomen: true, regexp: true, bitwise: true, browser: true, forin: true, maxerr: 100, indent: 4 */
  10151. var fluid_1_5 = fluid_1_5 || {};
  10152. (function ($, fluid) {
  10153. // Private constants.
  10154. var NAMESPACE_KEY = "fluid-scoped-data";
  10155. /**
  10156. * Gets stored state from the jQuery instance's data map.
  10157. * This function is unsupported: It is not really intended for use by implementors.
  10158. */
  10159. fluid.getScopedData = function(target, key) {
  10160. var data = $(target).data(NAMESPACE_KEY);
  10161. return data ? data[key] : undefined;
  10162. };
  10163. /**
  10164. * Stores state in the jQuery instance's data map. Unlike jQuery's version,
  10165. * accepts multiple-element jQueries.
  10166. * This function is unsupported: It is not really intended for use by implementors.
  10167. */
  10168. fluid.setScopedData = function(target, key, value) {
  10169. $(target).each(function() {
  10170. var data = $.data(this, NAMESPACE_KEY) || {};
  10171. data[key] = value;
  10172. $.data(this, NAMESPACE_KEY, data);
  10173. });
  10174. };
  10175. /** Global focus manager - makes use of "focusin" event supported in jquery 1.4.2 or later.
  10176. */
  10177. var lastFocusedElement = null;
  10178. $(document).bind("focusin", function(event){
  10179. lastFocusedElement = event.target;
  10180. });
  10181. fluid.getLastFocusedElement = function() {
  10182. return lastFocusedElement;
  10183. }
  10184. var ENABLEMENT_KEY = "enablement";
  10185. /** Queries or sets the enabled status of a control. An activatable node
  10186. * may be "disabled" in which case its keyboard bindings will be inoperable
  10187. * (but still stored) until it is reenabled again.
  10188. * This function is unsupported: It is not really intended for use by implementors.
  10189. */
  10190. fluid.enabled = function(target, state) {
  10191. target = $(target);
  10192. if (state === undefined) {
  10193. return fluid.getScopedData(target, ENABLEMENT_KEY) !== false;
  10194. }
  10195. else {
  10196. $("*", target).add(target).each(function() {
  10197. if (fluid.getScopedData(this, ENABLEMENT_KEY) !== undefined) {
  10198. fluid.setScopedData(this, ENABLEMENT_KEY, state);
  10199. }
  10200. else if (/select|textarea|input/i.test(this.nodeName)) {
  10201. $(this).prop("disabled", !state);
  10202. }
  10203. });
  10204. fluid.setScopedData(target, ENABLEMENT_KEY, state);
  10205. }
  10206. };
  10207. fluid.initEnablement = function(target) {
  10208. fluid.setScopedData(target, ENABLEMENT_KEY, true);
  10209. };
  10210. // This function is necessary since simulation of focus events by jQuery under IE
  10211. // is not sufficiently good to intercept the "focusin" binding. Any code which triggers
  10212. // focus or blur synthetically throughout the framework and client code must use this function,
  10213. // especially if correct cross-platform interaction is required with the "deadMansBlur" function.
  10214. function applyOp(node, func) {
  10215. node = $(node);
  10216. node.trigger("fluid-"+func);
  10217. node[func]();
  10218. }
  10219. $.each(["focus", "blur"], function(i, name) {
  10220. fluid[name] = function(elem) {
  10221. applyOp(elem, name);
  10222. }
  10223. });
  10224. })(jQuery, fluid_1_5);
  10225. /*
  10226. Copyright 2008-2010 University of Cambridge
  10227. Copyright 2008-2009 University of Toronto
  10228. Licensed under the Educational Community License (ECL), Version 2.0 or the New
  10229. BSD license. You may not use this file except in compliance with one these
  10230. Licenses.
  10231. You may obtain a copy of the ECL 2.0 License and BSD License at
  10232. https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt
  10233. */
  10234. // Declare dependencies
  10235. /*global fluid_1_5:true, jQuery */
  10236. // JSLint options
  10237. /*jslint white: true, funcinvoke: true, undef: true, newcap: true, nomen: true, regexp: true, bitwise: true, browser: true, forin: true, maxerr: 100, indent: 4 */
  10238. var fluid_1_5 = fluid_1_5 || {};
  10239. (function ($, fluid) {
  10240. fluid.dom = fluid.dom || {};
  10241. // Node walker function for iterateDom.
  10242. var getNextNode = function (iterator) {
  10243. if (iterator.node.firstChild) {
  10244. iterator.node = iterator.node.firstChild;
  10245. iterator.depth += 1;
  10246. return iterator;
  10247. }
  10248. while (iterator.node) {
  10249. if (iterator.node.nextSibling) {
  10250. iterator.node = iterator.node.nextSibling;
  10251. return iterator;
  10252. }
  10253. iterator.node = iterator.node.parentNode;
  10254. iterator.depth -= 1;
  10255. }
  10256. return iterator;
  10257. };
  10258. /**
  10259. * Walks the DOM, applying the specified acceptor function to each element.
  10260. * There is a special case for the acceptor, allowing for quick deletion of elements and their children.
  10261. * Return "delete" from your acceptor function if you want to delete the element in question.
  10262. * Return "stop" to terminate iteration.
  10263. * Implementation note - this utility exists mainly for performance reasons. It was last tested
  10264. * carefully some time ago (around jQuery 1.2) but at that time was around 3-4x faster at raw DOM
  10265. * filtration tasks than the jQuery equivalents, which was an important source of performance loss in the
  10266. * Reorderer component. General clients of the framework should use this method with caution if at all, and
  10267. * the performance issues should be reassessed when we have time.
  10268. *
  10269. * @param {Element} node the node to start walking from
  10270. * @param {Function} acceptor the function to invoke with each DOM element
  10271. * @param {Boolean} allnodes Use <code>true</code> to call acceptor on all nodes,
  10272. * rather than just element nodes (type 1)
  10273. */
  10274. fluid.dom.iterateDom = function (node, acceptor, allNodes) {
  10275. var currentNode = {node: node, depth: 0};
  10276. var prevNode = node;
  10277. var condition;
  10278. while (currentNode.node !== null && currentNode.depth >= 0 && currentNode.depth < fluid.dom.iterateDom.DOM_BAIL_DEPTH) {
  10279. condition = null;
  10280. if (currentNode.node.nodeType === 1 || allNodes) {
  10281. condition = acceptor(currentNode.node, currentNode.depth);
  10282. }
  10283. if (condition) {
  10284. if (condition === "delete") {
  10285. currentNode.node.parentNode.removeChild(currentNode.node);
  10286. currentNode.node = prevNode;
  10287. }
  10288. else if (condition === "stop") {
  10289. return currentNode.node;
  10290. }
  10291. }
  10292. prevNode = currentNode.node;
  10293. currentNode = getNextNode(currentNode);
  10294. }
  10295. };
  10296. // Work around IE circular DOM issue. This is the default max DOM depth on IE.
  10297. // http://msdn2.microsoft.com/en-us/library/ms761392(VS.85).aspx
  10298. fluid.dom.iterateDom.DOM_BAIL_DEPTH = 256;
  10299. /**
  10300. * Checks if the specified container is actually the parent of containee.
  10301. *
  10302. * @param {Element} container the potential parent
  10303. * @param {Element} containee the child in question
  10304. */
  10305. fluid.dom.isContainer = function (container, containee) {
  10306. for (; containee; containee = containee.parentNode) {
  10307. if (container === containee) {
  10308. return true;
  10309. }
  10310. }
  10311. return false;
  10312. };
  10313. /** Return the element text from the supplied DOM node as a single String.
  10314. * Implementation note - this is a special-purpose utility used in the framework in just one
  10315. * position in the Reorderer. It only performs a "shallow" traversal of the text and was intended
  10316. * as a quick and dirty means of extracting element labels where the user had not explicitly provided one.
  10317. * It should not be used by general users of the framework and its presence here needs to be
  10318. * reassessed.
  10319. */
  10320. fluid.dom.getElementText = function (element) {
  10321. var nodes = element.childNodes;
  10322. var text = "";
  10323. for (var i = 0; i < nodes.length; ++i) {
  10324. var child = nodes[i];
  10325. if (child.nodeType === 3) {
  10326. text = text + child.nodeValue;
  10327. }
  10328. }
  10329. return text;
  10330. };
  10331. })(jQuery, fluid_1_5);
  10332. /*
  10333. Copyright 2008-2010 University of Cambridge
  10334. Copyright 2008-2009 University of Toronto
  10335. Copyright 2010 Lucendo Development Ltd.
  10336. Licensed under the Educational Community License (ECL), Version 2.0 or the New
  10337. BSD license. You may not use this file except in compliance with one these
  10338. Licenses.
  10339. You may obtain a copy of the ECL 2.0 License and BSD License at
  10340. https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt
  10341. */
  10342. // Declare dependencies
  10343. /*global fluid_1_5:true, jQuery*/
  10344. // JSLint options
  10345. /*jslint white: true, funcinvoke: true, undef: true, newcap: true, nomen: true, regexp: true, bitwise: true, browser: true, forin: true, maxerr: 100, indent: 4 */
  10346. fluid_1_5 = fluid_1_5 || {};
  10347. (function ($, fluid) {
  10348. var unUnicode = /(\\u[\dabcdef]{4}|\\x[\dabcdef]{2})/g;
  10349. fluid.unescapeProperties = function (string) {
  10350. string = string.replace(unUnicode, function(match) {
  10351. var code = match.substring(2);
  10352. var parsed = parseInt(code, 16);
  10353. return String.fromCharCode(parsed);
  10354. }
  10355. );
  10356. var pos = 0;
  10357. while (true) {
  10358. var backpos = string.indexOf("\\", pos);
  10359. if (backpos === -1) {
  10360. break;
  10361. }
  10362. if (backpos === string.length - 1) {
  10363. return [string.substring(0, string.length - 1), true];
  10364. }
  10365. var replace = string.charAt(backpos + 1);
  10366. if (replace === "n") replace = "\n";
  10367. if (replace === "r") replace = "\r";
  10368. if (replace === "t") replace = "\t";
  10369. string = string.substring(0, backpos) + replace + string.substring(backpos + 2);
  10370. pos = backpos + 1;
  10371. }
  10372. return [string, false];
  10373. };
  10374. var breakPos = /[^\\][\s:=]/;
  10375. fluid.parseJavaProperties = function(text) {
  10376. // File format described at http://java.sun.com/javase/6/docs/api/java/util/Properties.html#load(java.io.Reader)
  10377. var togo = {};
  10378. text = text.replace(/\r\n/g, "\n");
  10379. text = text.replace(/\r/g, "\n");
  10380. lines = text.split("\n");
  10381. var contin, key, valueComp, valueRaw, valueEsc;
  10382. for (var i = 0; i < lines.length; ++ i) {
  10383. var line = $.trim(lines[i]);
  10384. if (!line || line.charAt(0) === "#" || line.charAt(0) === '!') {
  10385. continue;
  10386. }
  10387. if (!contin) {
  10388. valueComp = "";
  10389. var breakpos = line.search(breakPos);
  10390. if (breakpos === -1) {
  10391. key = line;
  10392. valueRaw = "";
  10393. }
  10394. else {
  10395. key = $.trim(line.substring(0, breakpos + 1)); // +1 since first char is escape exclusion
  10396. valueRaw = $.trim(line.substring(breakpos + 2));
  10397. if (valueRaw.charAt(0) === ":" || valueRaw.charAt(0) === "=") {
  10398. valueRaw = $.trim(valueRaw.substring(1));
  10399. }
  10400. }
  10401. key = fluid.unescapeProperties(key)[0];
  10402. valueEsc = fluid.unescapeProperties(valueRaw);
  10403. }
  10404. else {
  10405. valueEsc = fluid.unescapeProperties(line);
  10406. }
  10407. contin = valueEsc[1];
  10408. if (!valueEsc[1]) { // this line was not a continuation line - store the value
  10409. togo[key] = valueComp + valueEsc[0];
  10410. }
  10411. else {
  10412. valueComp += valueEsc[0];
  10413. }
  10414. }
  10415. return togo;
  10416. };
  10417. /**
  10418. * Expand a message string with respect to a set of arguments, following a basic
  10419. * subset of the Java MessageFormat rules.
  10420. * http://java.sun.com/j2se/1.4.2/docs/api/java/text/MessageFormat.html
  10421. *
  10422. * The message string is expected to contain replacement specifications such
  10423. * as {0}, {1}, {2}, etc.
  10424. * @param messageString {String} The message key to be expanded
  10425. * @param args {String/Array of String} An array of arguments to be substituted into the message.
  10426. * @return The expanded message string.
  10427. */
  10428. fluid.formatMessage = function (messageString, args) {
  10429. if (!args) {
  10430. return messageString;
  10431. }
  10432. if (typeof(args) === "string") {
  10433. args = [args];
  10434. }
  10435. for (var i = 0; i < args.length; ++ i) {
  10436. messageString = messageString.replace("{" + i + "}", args[i]);
  10437. }
  10438. return messageString;
  10439. };
  10440. })(jQuery, fluid_1_5);
  10441. /*
  10442. Copyright 2007-2010 University of Cambridge
  10443. Copyright 2007-2009 University of Toronto
  10444. Copyright 2007-2009 University of California, Berkeley
  10445. Copyright 2010 OCAD University
  10446. Copyright 2010-2011 Lucendo Development Ltd.
  10447. Licensed under the Educational Community License (ECL), Version 2.0 or the New
  10448. BSD license. You may not use this file except in compliance with one these
  10449. Licenses.
  10450. You may obtain a copy of the ECL 2.0 License and BSD License at
  10451. https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt
  10452. */
  10453. // Declare dependencies
  10454. /*global fluid:true, fluid_1_5:true, jQuery*/
  10455. // JSLint options
  10456. /*jslint white: true, funcinvoke: true, undef: true, newcap: true, nomen: true, regexp: true, bitwise: true, browser: true, forin: true, maxerr: 100, indent: 4 */
  10457. var fluid_1_5 = fluid_1_5 || {};
  10458. var fluid = fluid || fluid_1_5;
  10459. (function ($, fluid) {
  10460. fluid.renderTimestamp = function (date) {
  10461. var zeropad = function (num, width) {
  10462. if (!width) width = 2;
  10463. var numstr = (num == undefined? "" : num.toString());
  10464. return "00000".substring(5 - width + numstr.length) + numstr;
  10465. }
  10466. return zeropad(date.getHours()) + ":" + zeropad(date.getMinutes()) + ":" + zeropad(date.getSeconds()) + "." + zeropad(date.getMilliseconds(), 3);
  10467. };
  10468. fluid.isTracing = true;
  10469. fluid.registerNamespace("fluid.tracing");
  10470. fluid.tracing.pathCount = [];
  10471. fluid.tracing.summarisePathCount = function (pathCount) {
  10472. pathCount = pathCount || fluid.tracing.pathCount;
  10473. var togo = {};
  10474. for (var i = 0; i < pathCount.length; ++ i) {
  10475. var path = pathCount[i];
  10476. if (!togo[path]) {
  10477. togo[path] = 1;
  10478. }
  10479. else {
  10480. ++togo[path];
  10481. }
  10482. }
  10483. var toReallyGo = [];
  10484. fluid.each(togo, function(el, path) {
  10485. toReallyGo.push({path: path, count: el});
  10486. });
  10487. toReallyGo.sort(function(a, b) {return b.count - a.count});
  10488. return toReallyGo;
  10489. };
  10490. fluid.tracing.condensePathCount = function (prefixes, pathCount) {
  10491. prefixes = fluid.makeArray(prefixes);
  10492. var prefixCount = {};
  10493. fluid.each(prefixes, function(prefix) {
  10494. prefixCount[prefix] = 0;
  10495. });
  10496. var togo = [];
  10497. fluid.each(pathCount, function(el) {
  10498. var path = el.path;
  10499. if (!fluid.find(prefixes, function(prefix) {
  10500. if (path.indexOf(prefix) === 0) {
  10501. prefixCount[prefix] += el.count;
  10502. return true;
  10503. }
  10504. })) {
  10505. togo.push(el);
  10506. }
  10507. });
  10508. fluid.each(prefixCount, function(count, path) {
  10509. togo.unshift({path: path, count: count});
  10510. });
  10511. return togo;
  10512. };
  10513. // Exception stripping code taken from https://github.com/emwendelin/javascript-stacktrace/blob/master/stacktrace.js
  10514. // BSD licence, see header
  10515. fluid.detectStackStyle = function (e) {
  10516. var style = "other";
  10517. var stackStyle = {
  10518. offset: 0
  10519. };
  10520. if (e["arguments"]) {
  10521. style = "chrome";
  10522. } else if (typeof window !== "undefined" && window.opera && e.stacktrace) {
  10523. style = "opera10";
  10524. } else if (e.stack) {
  10525. style = "firefox";
  10526. // Detect FireFox 4-style stacks which are 1 level less deep
  10527. stackStyle.offset = e.stack.indexOf("Trace exception") === -1? 1 : 0;
  10528. } else if (typeof window !== 'undefined' && window.opera && !('stacktrace' in e)) { //Opera 9-
  10529. style = "opera";
  10530. }
  10531. stackStyle.style = style;
  10532. return stackStyle;
  10533. };
  10534. fluid.obtainException = function() {
  10535. try {
  10536. throw new Error("Trace exception");
  10537. }
  10538. catch (e) {
  10539. return e;
  10540. }
  10541. };
  10542. var stackStyle = fluid.detectStackStyle(fluid.obtainException());
  10543. fluid.registerNamespace("fluid.exceptionDecoders");
  10544. fluid.decodeStack = function() {
  10545. if (stackStyle.style !== "firefox") {
  10546. return null;
  10547. }
  10548. var e = fluid.obtainException();
  10549. return fluid.exceptionDecoders[stackStyle.style](e);
  10550. };
  10551. fluid.exceptionDecoders.firefox = function(e) {
  10552. var lines = e.stack.replace(/(?:\n@:0)?\s+$/m, '').replace(/^\(/gm, '{anonymous}(').split('\n');
  10553. return fluid.transform(lines, function(line) {
  10554. var atind = line.indexOf("@");
  10555. return atind === -1? [line] : [line.substring(atind + 1), line.substring(0, atind)];
  10556. });
  10557. };
  10558. fluid.getCallerInfo = function(atDepth) {
  10559. atDepth = (atDepth || 3) - stackStyle.offset;
  10560. var stack = fluid.decodeStack();
  10561. return stack? stack[atDepth][0] : null;
  10562. };
  10563. function generate(c, count) {
  10564. var togo = "";
  10565. for (var i = 0; i < count; ++ i) {
  10566. togo += c;
  10567. }
  10568. return togo;
  10569. }
  10570. function printImpl(obj, small, options) {
  10571. var big = small + options.indentChars;
  10572. if (obj === null) {
  10573. return "null";
  10574. }
  10575. else if (fluid.isPrimitive(obj)) {
  10576. return JSON.stringify(obj);
  10577. }
  10578. else {
  10579. var j = [];
  10580. if (fluid.isArrayable(obj)) {
  10581. if (obj.length === 0) {
  10582. return "[]";
  10583. }
  10584. for (var i = 0; i < obj.length; ++ i) {
  10585. j[i] = printImpl(obj[i], big, options);
  10586. }
  10587. return "[\n" + big + j.join(",\n" + big) + "\n" + small + "]";
  10588. }
  10589. else {
  10590. var i = 0;
  10591. fluid.each(obj, function(value, key) {
  10592. j[i++] = JSON.stringify(key) + ": " + printImpl(value, big, options);
  10593. });
  10594. return "{\n" + big + j.join(",\n" + big) + "\n" + small + "}";
  10595. }
  10596. }
  10597. }
  10598. fluid.prettyPrintJSON = function(obj, options) {
  10599. options = $.extend({indent: 4}, options);
  10600. options.indentChars = generate(" ", options.indent);
  10601. return printImpl(obj, "", options);
  10602. }
  10603. /**
  10604. * Dumps a DOM element into a readily recognisable form for debugging - produces a
  10605. * "semi-selector" summarising its tag name, class and id, whichever are set.
  10606. *
  10607. * @param {jQueryable} element The element to be dumped
  10608. * @return A string representing the element.
  10609. */
  10610. fluid.dumpEl = function (element) {
  10611. var togo;
  10612. if (!element) {
  10613. return "null";
  10614. }
  10615. if (element.nodeType === 3 || element.nodeType === 8) {
  10616. return "[data: " + element.data + "]";
  10617. }
  10618. if (element.nodeType === 9) {
  10619. return "[document: location " + element.location + "]";
  10620. }
  10621. if (!element.nodeType && fluid.isArrayable(element)) {
  10622. togo = "[";
  10623. for (var i = 0; i < element.length; ++ i) {
  10624. togo += fluid.dumpEl(element[i]);
  10625. if (i < element.length - 1) {
  10626. togo += ", ";
  10627. }
  10628. }
  10629. return togo + "]";
  10630. }
  10631. element = $(element);
  10632. togo = element.get(0).tagName;
  10633. if (element.id) {
  10634. togo += "#" + element.id;
  10635. }
  10636. if (element.attr("class")) {
  10637. togo += "." + element.attr("class");
  10638. }
  10639. return togo;
  10640. };
  10641. })(jQuery, fluid_1_5);
  10642. /*
  10643. Copyright 2008-2010 University of Cambridge
  10644. Copyright 2008-2009 University of Toronto
  10645. Copyright 2010-2011 Lucendo Development Ltd.
  10646. Copyright 2010 OCAD University
  10647. Licensed under the Educational Community License (ECL), Version 2.0 or the New
  10648. BSD license. You may not use this file except in compliance with one these
  10649. Licenses.
  10650. You may obtain a copy of the ECL 2.0 License and BSD License at
  10651. https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt
  10652. */
  10653. // Declare dependencies
  10654. /*global fluid_1_5:true, jQuery*/
  10655. // JSLint options
  10656. /*jslint white: true, funcinvoke: true, continue: true, elsecatch: true, operator: true, jslintok:true, undef: true, newcap: true, regexp: true, bitwise: true, browser: true, forin: true, maxerr: 100, indent: 4 */
  10657. var fluid_1_5 = fluid_1_5 || {};
  10658. (function ($, fluid) {
  10659. fluid.BINDING_ROOT_KEY = "fluid-binding-root";
  10660. /** Recursively find any data stored under a given name from a node upwards
  10661. * in its DOM hierarchy **/
  10662. fluid.findData = function (elem, name) {
  10663. while (elem) {
  10664. var data = $.data(elem, name);
  10665. if (data) {
  10666. return data;
  10667. }
  10668. elem = elem.parentNode;
  10669. }
  10670. };
  10671. fluid.bindFossils = function (node, data, fossils) {
  10672. $.data(node, fluid.BINDING_ROOT_KEY, {data: data, fossils: fossils});
  10673. };
  10674. fluid.boundPathForNode = function (node, fossils) {
  10675. node = fluid.unwrap(node);
  10676. var key = node.name || node.id;
  10677. var record = fossils[key];
  10678. return record ? record.EL : null;
  10679. };
  10680. /** "Automatically" apply to whatever part of the data model is
  10681. * relevant, the changed value received at the given DOM node*/
  10682. fluid.applyBoundChange = function (node, newValue, applier) {
  10683. node = fluid.unwrap(node);
  10684. if (newValue === undefined) {
  10685. newValue = fluid.value(node);
  10686. }
  10687. if (node.nodeType === undefined && node.length > 0) {
  10688. node = node[0];
  10689. } // assume here that they share name and parent
  10690. var root = fluid.findData(node, fluid.BINDING_ROOT_KEY);
  10691. if (!root) {
  10692. fluid.fail("Bound data could not be discovered in any node above " + fluid.dumpEl(node));
  10693. }
  10694. var name = node.name;
  10695. var fossil = root.fossils[name];
  10696. if (!fossil) {
  10697. fluid.fail("No fossil discovered for name " + name + " in fossil record above " + fluid.dumpEl(node));
  10698. }
  10699. if (typeof(fossil.oldvalue) === "boolean") { // deal with the case of an "isolated checkbox"
  10700. newValue = newValue[0] ? true : false;
  10701. }
  10702. var EL = root.fossils[name].EL;
  10703. if (applier) {
  10704. applier.fireChangeRequest({path: EL, value: newValue, source: node.id});
  10705. } else {
  10706. fluid.set(root.data, EL, newValue);
  10707. }
  10708. };
  10709. /** MODEL ACCESSOR ENGINE (trundler) **/
  10710. /** Standard strategies for resolving path segments **/
  10711. fluid.model.environmentStrategy = function (initEnvironment) {
  10712. return {
  10713. init: function () {
  10714. var environment = initEnvironment;
  10715. return function (root, segment, index) {
  10716. var togo;
  10717. if (environment && environment[segment]) {
  10718. togo = environment[segment];
  10719. }
  10720. environment = null;
  10721. return togo;
  10722. };
  10723. }
  10724. };
  10725. };
  10726. fluid.model.defaultCreatorStrategy = function (root, segment) {
  10727. if (root[segment] === undefined) {
  10728. root[segment] = {};
  10729. return root[segment];
  10730. }
  10731. };
  10732. fluid.model.defaultFetchStrategy = function (root, segment) {
  10733. return segment === "" ? root : root[segment];
  10734. };
  10735. fluid.model.funcResolverStrategy = function (root, segment) {
  10736. if (root.resolvePathSegment) {
  10737. return root.resolvePathSegment(segment);
  10738. }
  10739. };
  10740. fluid.model.defaultGetConfig = {
  10741. strategies: [fluid.model.funcResolverStrategy, fluid.model.defaultFetchStrategy]
  10742. };
  10743. fluid.model.defaultSetConfig = {
  10744. strategies: [fluid.model.funcResolverStrategy, fluid.model.defaultFetchStrategy, fluid.model.defaultCreatorStrategy]
  10745. };
  10746. // unsupported, NON-API function
  10747. fluid.model.applyStrategy = function (strategy, root, segment, path) {
  10748. if (typeof (strategy) === "function") {
  10749. return strategy(root, segment, path);
  10750. } else if (strategy && strategy.next) {
  10751. return strategy.next(root, segment, path);
  10752. }
  10753. };
  10754. // unsupported, NON-API function
  10755. fluid.model.initStrategy = function (baseStrategy, index, oldStrategies) {
  10756. return baseStrategy.init ? baseStrategy.init(oldStrategies ? oldStrategies[index] : undefined) : baseStrategy;
  10757. };
  10758. // unsupported, NON-API function
  10759. fluid.model.makeTrundler = function (root, config, oldStrategies) {
  10760. var that = {
  10761. root: root,
  10762. strategies: fluid.isArrayable(config) ? config :
  10763. fluid.transform(config.strategies, function (strategy, index) {
  10764. return fluid.model.initStrategy(strategy, index, oldStrategies);
  10765. })
  10766. };
  10767. that.trundle = function (EL, uncess) {
  10768. uncess = uncess || 0;
  10769. var newThat = fluid.model.makeTrundler(that.root, config, that.strategies);
  10770. newThat.segs = config.parser? config.parser.parse(EL) : fluid.model.parseEL(EL);
  10771. newThat.index = 0;
  10772. newThat.path = "";
  10773. newThat.step(newThat.segs.length - uncess);
  10774. return newThat;
  10775. };
  10776. that.next = function () {
  10777. if (!that.root) {
  10778. return;
  10779. }
  10780. var accepted;
  10781. // TODO: Temporary adjustment before trundlers are destroyed by FLUID-4705
  10782. // In the final system "new strategies" should be able to declare whether any of them
  10783. // require this path computed or not
  10784. that.path = (config.parser? config.parser.compose : fluid.model.composePath)(that.path, that.segs[that.index]);
  10785. for (var i = 0; i < that.strategies.length; ++i) {
  10786. var value = fluid.model.applyStrategy(that.strategies[i], that.root, that.segs[that.index], that.path);
  10787. if (accepted === undefined) {
  10788. accepted = value;
  10789. }
  10790. }
  10791. if (accepted === fluid.NO_VALUE) {
  10792. accepted = undefined;
  10793. }
  10794. that.root = accepted;
  10795. ++that.index;
  10796. };
  10797. that.step = function (limit) {
  10798. for (var i = 0; i < limit; ++i) {
  10799. that.next();
  10800. }
  10801. that.last = that.segs[that.index];
  10802. };
  10803. return that;
  10804. };
  10805. // unsupported, NON-API function
  10806. // core trundling recursion point
  10807. fluid.model.trundleImpl = function (trundler, EL, config, uncess) {
  10808. if (typeof (EL) === "string") {
  10809. trundler = trundler.trundle(EL, uncess);
  10810. } else {
  10811. var key = EL.type || "default";
  10812. var resolver = config.resolvers[key];
  10813. if (!resolver) {
  10814. fluid.fail("Unable to find resolver of type " + key);
  10815. }
  10816. trundler = resolver(EL, trundler) || {};
  10817. if (EL.path && trundler.trundle && trundler.root !== undefined) {
  10818. trundler = fluid.model.trundleImpl(trundler, EL.path, config, uncess);
  10819. }
  10820. }
  10821. return trundler;
  10822. };
  10823. // unsupported, NON-API function
  10824. // entry point for initially unbased trundling
  10825. fluid.model.trundle = function (root, EL, config, uncess) {
  10826. EL = EL || "";
  10827. config = config || fluid.model.defaultGetConfig;
  10828. var trundler = fluid.model.makeTrundler(root, config);
  10829. return fluid.model.trundleImpl(trundler, EL, config, uncess);
  10830. };
  10831. fluid.model.getPenultimate = function (root, EL, config) {
  10832. return fluid.model.trundle(root, EL, config, 1);
  10833. };
  10834. // Implementation notes: The EL path manipulation utilities here are somewhat more thorough
  10835. // and expensive versions of those provided in Fluid.js - there is some duplication of
  10836. // functionality. This is a tradeoff between stability and performance - the versions in
  10837. // Fluid.js are the most frequently used and do not implement escaping of characters .
  10838. // as \. and \ as \\ as the versions here. The implementations here are not
  10839. // performant and are left here partially as an implementation note. Problems will
  10840. // arise if clients manipulate JSON structures containing "." characters in keys as if they
  10841. // are models. The basic utilities fluid.path(), fluid.parseEL and fluid.composePath are
  10842. // the ones recommended for general users and the following implementations will
  10843. // be upgraded to use regexes in future to make them better alternatives
  10844. fluid.pathUtil = {};
  10845. var getPathSegmentImpl = function (accept, path, i) {
  10846. var segment = null; // TODO: rewrite this with regexes and replaces
  10847. if (accept) {
  10848. segment = "";
  10849. }
  10850. var escaped = false;
  10851. var limit = path.length;
  10852. for (; i < limit; ++i) {
  10853. var c = path.charAt(i);
  10854. if (!escaped) {
  10855. if (c === '.') {
  10856. break;
  10857. }
  10858. else if (c === '\\') {
  10859. escaped = true;
  10860. }
  10861. else if (segment !== null) {
  10862. segment += c;
  10863. }
  10864. }
  10865. else {
  10866. escaped = false;
  10867. if (segment !== null) {
  10868. segment += c;
  10869. }
  10870. }
  10871. }
  10872. if (segment !== null) {
  10873. accept[0] = segment;
  10874. }
  10875. return i;
  10876. };
  10877. var globalAccept = []; // TODO: serious reentrancy risk here, why is this impl like this?
  10878. /** Parses a path segment, following escaping rules, starting from character index i in the supplied path */
  10879. fluid.pathUtil.getPathSegment = function (path, i) {
  10880. getPathSegmentImpl(globalAccept, path, i);
  10881. return globalAccept[0];
  10882. };
  10883. /** Returns just the head segment of an EL path */
  10884. fluid.pathUtil.getHeadPath = function (path) {
  10885. return fluid.pathUtil.getPathSegment(path, 0);
  10886. };
  10887. /** Returns all of an EL path minus its first segment - if the path consists of just one segment, returns "" */
  10888. fluid.pathUtil.getFromHeadPath = function (path) {
  10889. var firstdot = getPathSegmentImpl(null, path, 0);
  10890. return firstdot === path.length ? "" : path.substring(firstdot + 1);
  10891. };
  10892. function lastDotIndex(path) {
  10893. // TODO: proper escaping rules
  10894. return path.lastIndexOf(".");
  10895. }
  10896. /** Returns all of an EL path minus its final segment - if the path consists of just one segment, returns "" -
  10897. * WARNING - this method does not follow escaping rules */
  10898. fluid.pathUtil.getToTailPath = function (path) {
  10899. var lastdot = lastDotIndex(path);
  10900. return lastdot === -1 ? "" : path.substring(0, lastdot);
  10901. };
  10902. /** Returns the very last path component of an EL path
  10903. * WARNING - this method does not follow escaping rules */
  10904. fluid.pathUtil.getTailPath = function (path) {
  10905. var lastdot = lastDotIndex(path);
  10906. return fluid.pathUtil.getPathSegment(path, lastdot + 1);
  10907. };
  10908. /** A version of fluid.model.parseEL that apples escaping rules - this allows path segments
  10909. * to contain period characters . - characters "\" and "}" will also be escaped. WARNING -
  10910. * this current implementation is EXTREMELY slow compared to fluid.model.parseEL and should
  10911. * not be used in performance-sensitive applications */
  10912. fluid.pathUtil.parseEL = function (path) {
  10913. var togo = [];
  10914. var index = 0;
  10915. var limit = path.length;
  10916. while (index < limit) {
  10917. var firstdot = getPathSegmentImpl(globalAccept, path, index);
  10918. togo.push(globalAccept[0]);
  10919. index = firstdot + 1;
  10920. }
  10921. return togo;
  10922. };
  10923. var composeSegment = function (prefix, toappend) {
  10924. for (var i = 0; i < toappend.length; ++i) {
  10925. var c = toappend.charAt(i);
  10926. if (c === '.' || c === '\\' || c === '}') {
  10927. prefix += '\\';
  10928. }
  10929. prefix += c;
  10930. }
  10931. return prefix;
  10932. };
  10933. /** Escapes a single path segment by replacing any character ".", "\" or "}" with
  10934. * itself prepended by \
  10935. */
  10936. fluid.pathUtil.escapeSegment = function (segment) {
  10937. return composeSegment("", segment);
  10938. };
  10939. /**
  10940. * Compose a prefix and suffix EL path, where the prefix is already escaped.
  10941. * Prefix may be empty, but not null. The suffix will become escaped.
  10942. */
  10943. fluid.pathUtil.composePath = function (prefix, suffix) {
  10944. if (prefix.length !== 0) {
  10945. prefix += '.';
  10946. }
  10947. return composeSegment(prefix, suffix);
  10948. };
  10949. /** Determine the path by which a given path is nested within another **/
  10950. fluid.pathUtil.getExcessPath = function (base, longer) {
  10951. var index = longer.indexOf(base);
  10952. if (index !== 0) {
  10953. fluid.fail("Path " + base + " is not a prefix of path " + longer);
  10954. }
  10955. if (base.length === longer.length) {
  10956. return "";
  10957. }
  10958. if (longer[base.length] !== ".") {
  10959. fluid.fail("Path " + base + " is not properly nested in path " + longer);
  10960. }
  10961. return longer.substring(base.length + 1);
  10962. };
  10963. /** Determines whether a particular EL path matches a given path specification.
  10964. * The specification consists of a path with optional wildcard segments represented by "*".
  10965. * @param spec (string) The specification to be matched
  10966. * @param path (string) The path to be tested
  10967. * @param exact (boolean) Whether the path must exactly match the length of the specification in
  10968. * terms of path segments in order to count as match. If exact is falsy, short specifications will
  10969. * match all longer paths as if they were padded out with "*" segments
  10970. * @return (string) The path which matched the specification, or <code>null</code> if there was no match
  10971. */
  10972. fluid.pathUtil.matchPath = function (spec, path, exact) {
  10973. var togo = "";
  10974. while (true) {
  10975. if (((path === "") ^ (spec === "")) && exact) {
  10976. return null;
  10977. }
  10978. // FLUID-4625 - symmetry on spec and path is actually undesirable, but this
  10979. // quickly avoids at least missed notifications - improved (but slower)
  10980. // implementation should explode composite changes
  10981. if (!spec || !path) {
  10982. break;
  10983. }
  10984. var spechead = fluid.pathUtil.getHeadPath(spec);
  10985. var pathhead = fluid.pathUtil.getHeadPath(path);
  10986. // if we fail to match on a specific component, fail.
  10987. if (spechead !== "*" && spechead !== pathhead) {
  10988. return null;
  10989. }
  10990. togo = fluid.pathUtil.composePath(togo, pathhead);
  10991. spec = fluid.pathUtil.getFromHeadPath(spec);
  10992. path = fluid.pathUtil.getFromHeadPath(path);
  10993. }
  10994. return togo;
  10995. };
  10996. /** CHANGE APPLIER **/
  10997. fluid.model.isNullChange = function (model, request, resolverGetConfig) {
  10998. if (request.type === "ADD") {
  10999. var existing = fluid.get(model, request.path, resolverGetConfig);
  11000. if (existing === request.value) {
  11001. return true;
  11002. }
  11003. }
  11004. };
  11005. /** Applies the supplied ChangeRequest object directly to the supplied model.
  11006. */
  11007. fluid.model.applyChangeRequest = function (model, request, resolverSetConfig) {
  11008. var pen = fluid.model.getPenultimate(model, request.path, resolverSetConfig || fluid.model.defaultSetConfig);
  11009. if (request.type === "ADD" || request.type === "MERGE") {
  11010. if (request.path === "" || request.type === "MERGE") {
  11011. if (request.type === "ADD") {
  11012. fluid.clear(pen.root);
  11013. }
  11014. $.extend(true, request.path === "" ? pen.root : pen.root[pen.last], request.value);
  11015. }
  11016. else {
  11017. pen.root[pen.last] = request.value;
  11018. }
  11019. }
  11020. else if (request.type === "DELETE") {
  11021. if (request.path === "") {
  11022. fluid.clear(pen.root);
  11023. }
  11024. else {
  11025. delete pen.root[pen.last];
  11026. }
  11027. }
  11028. };
  11029. /** Add a listener to a ChangeApplier event that only acts in the case the event
  11030. * has not come from the specified source (typically ourself)
  11031. * @param modelEvent An model event held by a changeApplier (typically applier.modelChanged)
  11032. * @param path The path specification to listen to
  11033. * @param source The source value to exclude (direct equality used)
  11034. * @param func The listener to be notified of a change
  11035. * @param [eventName] - optional - the event name to be listened to - defaults to "modelChanged"
  11036. */
  11037. fluid.addSourceGuardedListener = function(applier, path, source, func, eventName) {
  11038. eventName = eventName || "modelChanged";
  11039. applier[eventName].addListener(path,
  11040. function() {
  11041. if (!applier.hasChangeSource(source)) {
  11042. func.apply(null, arguments);
  11043. }
  11044. });
  11045. };
  11046. /** Convenience method to fire a change event to a specified applier, including
  11047. * a supplied "source" identified (perhaps for use with addSourceGuardedListener)
  11048. */
  11049. fluid.fireSourcedChange = function (applier, path, value, source) {
  11050. applier.fireChangeRequest({
  11051. path: path,
  11052. value: value,
  11053. source: source
  11054. });
  11055. };
  11056. /** Dispatches a list of changes to the supplied applier */
  11057. fluid.requestChanges = function (applier, changes) {
  11058. for (var i = 0; i < changes.length; ++i) {
  11059. applier.fireChangeRequest(changes[i]);
  11060. }
  11061. };
  11062. // Utility shared between changeApplier and superApplier
  11063. function bindRequestChange(that) {
  11064. that.requestChange = function (path, value, type) {
  11065. var changeRequest = {
  11066. path: path,
  11067. value: value,
  11068. type: type
  11069. };
  11070. that.fireChangeRequest(changeRequest);
  11071. };
  11072. }
  11073. // Utility used for source tracking in changeApplier
  11074. function sourceWrapModelChanged(modelChanged, threadLocal) {
  11075. return function(changeRequest) {
  11076. var sources = threadLocal().sources;
  11077. var args = arguments;
  11078. var source = changeRequest.source || "";
  11079. fluid.tryCatch(function() {
  11080. if (sources[source] === undefined) {
  11081. sources[source] = 0;
  11082. }
  11083. ++sources[source];
  11084. modelChanged.apply(null, args);
  11085. }, null, function() {
  11086. --sources[source];
  11087. });
  11088. };
  11089. }
  11090. /** The core creator function constructing ChangeAppliers. See API documentation
  11091. * at http://wiki.fluidproject.org/display/fluid/ChangeApplier+API for the various
  11092. * options supported in the options structure */
  11093. fluid.makeChangeApplier = function (model, options) {
  11094. options = options || {};
  11095. var baseEvents = {
  11096. guards: fluid.event.getEventFirer(false, true, "guard event"),
  11097. postGuards: fluid.event.getEventFirer(false, true, "postGuard event"),
  11098. modelChanged: fluid.event.getEventFirer(false, false, "modelChanged event")
  11099. };
  11100. var threadLocal = fluid.threadLocal(function() { return {sources: {}};});
  11101. var that = {
  11102. // For now, we don't use "id" to avoid confusing component detection which uses
  11103. // a simple algorithm looking for that field
  11104. changeid: fluid.allocateGuid(),
  11105. model: model
  11106. };
  11107. function makeGuardWrapper(cullUnchanged) {
  11108. if (!cullUnchanged) {
  11109. return null;
  11110. }
  11111. var togo = function (guard) {
  11112. return function (model, changeRequest, internalApplier) {
  11113. var oldRet = guard(model, changeRequest, internalApplier);
  11114. if (oldRet === false) {
  11115. return false;
  11116. }
  11117. else {
  11118. if (fluid.model.isNullChange(model, changeRequest)) {
  11119. togo.culled = true;
  11120. return false;
  11121. }
  11122. }
  11123. };
  11124. };
  11125. return togo;
  11126. }
  11127. function wrapListener(listener, spec) {
  11128. var pathSpec = spec;
  11129. var transactional = false;
  11130. var priority = Number.MAX_VALUE;
  11131. if (typeof (spec) !== "string") {
  11132. pathSpec = spec.path;
  11133. transactional = spec.transactional;
  11134. if (spec.priority !== undefined) {
  11135. priority = spec.priority;
  11136. }
  11137. }
  11138. else {
  11139. if (pathSpec.charAt(0) === "!") {
  11140. transactional = true;
  11141. pathSpec = pathSpec.substring(1);
  11142. }
  11143. }
  11144. return function (changePath, fireSpec, accum) {
  11145. var guid = fluid.event.identifyListener(listener);
  11146. var exist = fireSpec.guids[guid];
  11147. if (!exist) {
  11148. var match = fluid.pathUtil.matchPath(pathSpec, changePath);
  11149. if (match !== null) {
  11150. var record = {
  11151. changePath: changePath,
  11152. pathSpec: pathSpec,
  11153. listener: listener,
  11154. priority: priority,
  11155. transactional: transactional
  11156. };
  11157. if (accum) {
  11158. record.accumulate = [accum];
  11159. }
  11160. fireSpec.guids[guid] = record;
  11161. var collection = transactional ? "transListeners" : "listeners";
  11162. fireSpec[collection].push(record);
  11163. fireSpec.all.push(record);
  11164. }
  11165. }
  11166. else if (accum) {
  11167. if (!exist.accumulate) {
  11168. exist.accumulate = [];
  11169. }
  11170. exist.accumulate.push(accum);
  11171. }
  11172. };
  11173. }
  11174. function fireFromSpec(name, fireSpec, args, category, wrapper) {
  11175. return baseEvents[name].fireToListeners(fireSpec[category], args, wrapper);
  11176. }
  11177. function fireComparator(recA, recB) {
  11178. return recA.priority - recB.priority;
  11179. }
  11180. function prepareFireEvent(name, changePath, fireSpec, accum) {
  11181. baseEvents[name].fire(changePath, fireSpec, accum);
  11182. fireSpec.all.sort(fireComparator);
  11183. fireSpec.listeners.sort(fireComparator);
  11184. fireSpec.transListeners.sort(fireComparator);
  11185. }
  11186. function makeFireSpec() {
  11187. return {guids: {}, all: [], listeners: [], transListeners: []};
  11188. }
  11189. function getFireSpec(name, changePath) {
  11190. var fireSpec = makeFireSpec();
  11191. prepareFireEvent(name, changePath, fireSpec);
  11192. return fireSpec;
  11193. }
  11194. function fireEvent(name, changePath, args, wrapper) {
  11195. var fireSpec = getFireSpec(name, changePath);
  11196. return fireFromSpec(name, fireSpec, args, "all", wrapper);
  11197. }
  11198. function adaptListener(that, name) {
  11199. that[name] = {
  11200. addListener: function (spec, listener, namespace) {
  11201. baseEvents[name].addListener(wrapListener(listener, spec), namespace);
  11202. },
  11203. removeListener: function (listener) {
  11204. baseEvents[name].removeListener(listener);
  11205. }
  11206. };
  11207. }
  11208. adaptListener(that, "guards");
  11209. adaptListener(that, "postGuards");
  11210. adaptListener(that, "modelChanged");
  11211. function preFireChangeRequest(changeRequest) {
  11212. if (!changeRequest.type) {
  11213. changeRequest.type = "ADD";
  11214. }
  11215. }
  11216. var bareApplier = {
  11217. fireChangeRequest: function (changeRequest) {
  11218. that.fireChangeRequest(changeRequest, true);
  11219. }
  11220. };
  11221. bindRequestChange(bareApplier);
  11222. that.fireChangeRequest = function (changeRequest, defeatGuards) {
  11223. preFireChangeRequest(changeRequest);
  11224. var guardFireSpec = defeatGuards ? null : getFireSpec("guards", changeRequest.path);
  11225. if (guardFireSpec && guardFireSpec.transListeners.length > 0) {
  11226. var ation = that.initiate();
  11227. ation.fireChangeRequest(changeRequest, guardFireSpec);
  11228. ation.commit();
  11229. }
  11230. else {
  11231. if (!defeatGuards) {
  11232. // TODO: this use of "listeners" seems pointless since we have just verified that there are no transactional listeners
  11233. var prevent = fireFromSpec("guards", guardFireSpec, [model, changeRequest, bareApplier], "listeners");
  11234. if (prevent === false) {
  11235. return false;
  11236. }
  11237. }
  11238. var oldModel = model;
  11239. if (!options.thin) {
  11240. oldModel = {};
  11241. fluid.model.copyModel(oldModel, model);
  11242. }
  11243. fluid.model.applyChangeRequest(model, changeRequest, options.resolverSetConfig);
  11244. fireEvent("modelChanged", changeRequest.path, [model, oldModel, [changeRequest]]);
  11245. }
  11246. };
  11247. that.fireChangeRequest = sourceWrapModelChanged(that.fireChangeRequest, threadLocal);
  11248. bindRequestChange(that);
  11249. function fireAgglomerated(eventName, formName, changes, args, accpos) {
  11250. var fireSpec = makeFireSpec();
  11251. for (var i = 0; i < changes.length; ++i) {
  11252. prepareFireEvent(eventName, changes[i].path, fireSpec, changes[i]);
  11253. }
  11254. for (var j = 0; j < fireSpec[formName].length; ++j) {
  11255. var spec = fireSpec[formName][j];
  11256. if (accpos) {
  11257. args[accpos] = spec.accumulate;
  11258. }
  11259. var ret = spec.listener.apply(null, args);
  11260. if (ret === false) {
  11261. return false;
  11262. }
  11263. }
  11264. }
  11265. that.initiate = function (newModel) {
  11266. var cancelled = false;
  11267. var changes = [];
  11268. if (options.thin) {
  11269. newModel = model;
  11270. }
  11271. else {
  11272. newModel = newModel || {};
  11273. fluid.model.copyModel(newModel, model);
  11274. }
  11275. // the guard in the inner world is given a private applier to "fast track"
  11276. // and glob collateral changes it requires
  11277. var internalApplier = {
  11278. fireChangeRequest: function (changeRequest) {
  11279. preFireChangeRequest(changeRequest);
  11280. fluid.model.applyChangeRequest(newModel, changeRequest, options.resolverSetConfig);
  11281. changes.push(changeRequest);
  11282. }
  11283. };
  11284. bindRequestChange(internalApplier);
  11285. var ation = {
  11286. commit: function () {
  11287. var oldModel;
  11288. if (cancelled) {
  11289. return false;
  11290. }
  11291. var ret = fireAgglomerated("postGuards", "transListeners", changes, [newModel, null, internalApplier], 1);
  11292. if (ret === false) {
  11293. return false;
  11294. }
  11295. if (options.thin) {
  11296. oldModel = model;
  11297. }
  11298. else {
  11299. oldModel = {};
  11300. fluid.model.copyModel(oldModel, model);
  11301. fluid.clear(model);
  11302. fluid.model.copyModel(model, newModel);
  11303. }
  11304. fireAgglomerated("modelChanged", "all", changes, [model, oldModel, null], 2);
  11305. },
  11306. fireChangeRequest: function (changeRequest) {
  11307. preFireChangeRequest(changeRequest);
  11308. if (options.cullUnchanged && fluid.model.isNullChange(model, changeRequest, options.resolverGetConfig)) {
  11309. return;
  11310. }
  11311. var wrapper = makeGuardWrapper(options.cullUnchanged);
  11312. var prevent = fireEvent("guards", changeRequest.path, [newModel, changeRequest, internalApplier], wrapper);
  11313. if (prevent === false && !(wrapper && wrapper.culled)) {
  11314. cancelled = true;
  11315. }
  11316. if (!cancelled) {
  11317. if (!(wrapper && wrapper.culled)) {
  11318. fluid.model.applyChangeRequest(newModel, changeRequest, options.resolverSetConfig);
  11319. changes.push(changeRequest);
  11320. }
  11321. }
  11322. }
  11323. };
  11324. ation.fireChangeRequest = sourceWrapModelChanged(ation.fireChangeRequest, threadLocal);
  11325. bindRequestChange(ation);
  11326. return ation;
  11327. };
  11328. that.hasChangeSource = function (source) {
  11329. return threadLocal().sources[source] > 0;
  11330. };
  11331. return that;
  11332. };
  11333. fluid.makeSuperApplier = function () {
  11334. var subAppliers = [];
  11335. var that = {};
  11336. that.addSubApplier = function (path, subApplier) {
  11337. subAppliers.push({path: path, subApplier: subApplier});
  11338. };
  11339. that.fireChangeRequest = function (request) {
  11340. for (var i = 0; i < subAppliers.length; ++i) {
  11341. var path = subAppliers[i].path;
  11342. if (request.path.indexOf(path) === 0) {
  11343. var subpath = request.path.substring(path.length + 1);
  11344. var subRequest = fluid.copy(request);
  11345. subRequest.path = subpath;
  11346. // TODO: Deal with the as yet unsupported case of an EL rvalue DAR
  11347. subAppliers[i].subApplier.fireChangeRequest(subRequest);
  11348. }
  11349. }
  11350. };
  11351. bindRequestChange(that);
  11352. return that;
  11353. };
  11354. fluid.attachModel = function (baseModel, path, model) {
  11355. var segs = fluid.model.parseEL(path);
  11356. for (var i = 0; i < segs.length - 1; ++i) {
  11357. var seg = segs[i];
  11358. var subModel = baseModel[seg];
  11359. if (!subModel) {
  11360. baseModel[seg] = subModel = {};
  11361. }
  11362. baseModel = subModel;
  11363. }
  11364. baseModel[segs[segs.length - 1]] = model;
  11365. };
  11366. fluid.assembleModel = function (modelSpec) {
  11367. var model = {};
  11368. var superApplier = fluid.makeSuperApplier();
  11369. var togo = {model: model, applier: superApplier};
  11370. for (var path in modelSpec) {
  11371. var rec = modelSpec[path];
  11372. fluid.attachModel(model, path, rec.model);
  11373. if (rec.applier) {
  11374. superApplier.addSubApplier(path, rec.applier);
  11375. }
  11376. }
  11377. return togo;
  11378. };
  11379. })(jQuery, fluid_1_5);
  11380. /*
  11381. Copyright 2010 University of Toronto
  11382. Copyright 2010-2011 OCAD University
  11383. Licensed under the Educational Community License (ECL), Version 2.0 or the New
  11384. BSD license. You may not use this file except in compliance with one these
  11385. Licenses.
  11386. You may obtain a copy of the ECL 2.0 License and BSD License at
  11387. https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt
  11388. */
  11389. // Declare dependencies
  11390. /*global fluid:true, fluid_1_5:true, jQuery*/
  11391. // JSLint options
  11392. /*jslint white: true, elsecatch: true, jslintok: true, funcinvoke: true, undef: true, newcap: true, nomen: true, regexp: true, bitwise: true, browser: true, forin: true, maxerr: 100, indent: 4 */
  11393. var fluid_1_5 = fluid_1_5 || {};
  11394. var fluid = fluid || fluid_1_5;
  11395. (function ($) {
  11396. fluid.registerNamespace("fluid.model.transform");
  11397. /** Grade definitions for standard transformation function hierarchy **/
  11398. fluid.defaults("fluid.transformFunction", {
  11399. gradeNames: "fluid.function"
  11400. });
  11401. // uses standard layout and workflow involving inputPath
  11402. fluid.defaults("fluid.standardInputTransformFunction", {
  11403. gradeNames: "fluid.transformFunction"
  11404. });
  11405. fluid.defaults("fluid.standardOutputTransformFunction", {
  11406. gradeNames: "fluid.transformFunction"
  11407. });
  11408. // uses the standard layout and workflow involving inputPath and outputPath
  11409. fluid.defaults("fluid.standardTransformFunction", {
  11410. gradeNames: ["fluid.standardInputTransformFunction", "fluid.standardOutputTransformFunction"]
  11411. });
  11412. fluid.defaults("fluid.lens", {
  11413. gradeNames: "fluid.transformFunction",
  11414. invertConfiguration: null
  11415. // this function method returns "inverted configuration" rather than actually performing inversion
  11416. // TODO: harmonise with strategy used in VideoPlayer_framework.js
  11417. });
  11418. /***********************************
  11419. * Base utilities for transformers *
  11420. ***********************************/
  11421. // unsupported, NON-API function
  11422. fluid.model.transform.pathToRule = function (inputPath) {
  11423. return {
  11424. expander: {
  11425. type: "fluid.model.transform.value",
  11426. inputPath: inputPath
  11427. }
  11428. };
  11429. };
  11430. // unsupported, NON-API function
  11431. fluid.model.transform.valueToRule = function (value) {
  11432. return {
  11433. expander: {
  11434. type: "fluid.model.transform.literalValue",
  11435. value: value
  11436. }
  11437. };
  11438. };
  11439. /** Accepts two fully escaped paths, either of which may be empty or null **/
  11440. fluid.model.composePaths = function (prefix, suffix) {
  11441. prefix = prefix || "";
  11442. suffix = suffix || "";
  11443. return !prefix ? suffix : (!suffix ? prefix : prefix + "." + suffix);
  11444. };
  11445. fluid.model.transform.accumulateInputPath = function (inputPath, expander, paths) {
  11446. if (inputPath !== undefined) {
  11447. paths.push(fluid.model.composePaths(expander.inputPrefix, inputPath));
  11448. }
  11449. };
  11450. fluid.model.transform.getValue = function (inputPath, value, expander) {
  11451. var togo;
  11452. if (inputPath !== undefined) { // NB: We may one day want to reverse the crazy jQuery-like convention that "no path means root path"
  11453. togo = fluid.get(expander.source, fluid.model.composePaths(expander.inputPrefix, inputPath), expander.resolverGetConfig);
  11454. }
  11455. if (togo === undefined) {
  11456. togo = fluid.isPrimitive(value) ? value : expander.expand(value);
  11457. }
  11458. return togo;
  11459. };
  11460. // distinguished value which indicates that a transformation rule supplied a
  11461. // non-default output path, and so the user should be prevented from making use of it
  11462. // in a compound expander definition
  11463. fluid.model.transform.NONDEFAULT_OUTPUT_PATH_RETURN = {};
  11464. fluid.model.transform.setValue = function (userOutputPath, value, expander) {
  11465. // avoid crosslinking to input object - this might be controlled by a "nocopy" option in future
  11466. var toset = fluid.copy(value);
  11467. var outputPath = fluid.model.composePaths(expander.outputPrefix, userOutputPath);
  11468. // TODO: custom resolver config here to create non-hash output model structure
  11469. if (toset !== undefined) {
  11470. expander.applier.requestChange(outputPath, toset);
  11471. }
  11472. return userOutputPath ? fluid.model.transform.NONDEFAULT_OUTPUT_PATH_RETURN : toset;
  11473. };
  11474. /**********************************
  11475. * Standard transformer functions *
  11476. **********************************/
  11477. fluid.model.transform.value = fluid.identity;
  11478. fluid.defaults("fluid.model.transform.value", {
  11479. gradeNames: "fluid.standardTransformFunction",
  11480. invertConfiguration: "fluid.model.transform.invertValue"
  11481. });
  11482. fluid.model.transform.invertValue = function (expandSpec, expander) {
  11483. var togo = fluid.copy(expandSpec);
  11484. // TODO: this will not behave correctly in the face of compound "value" which contains
  11485. // further expanders
  11486. togo.inputPath = fluid.model.composePaths(expander.outputPrefix, expandSpec.outputPath);
  11487. togo.outputPath = fluid.model.composePaths(expander.inputPrefix, expandSpec.inputPath);
  11488. return togo;
  11489. };
  11490. fluid.model.transform.literalValue = function (expanderSpec) {
  11491. return expanderSpec.value;
  11492. };
  11493. fluid.defaults("fluid.model.transform.literalValue", {
  11494. gradeNames: "fluid.standardOutputTransformFunction"
  11495. });
  11496. fluid.model.transform.arrayValue = fluid.makeArray;
  11497. fluid.defaults("fluid.model.transform.arrayValue", {
  11498. gradeNames: "fluid.standardTransformFunction"
  11499. });
  11500. fluid.model.transform.count = function (value) {
  11501. return fluid.makeArray(value).length;
  11502. };
  11503. fluid.defaults("fluid.model.transform.count", {
  11504. gradeNames: "fluid.standardTransformFunction"
  11505. });
  11506. fluid.model.transform.firstValue = function (expandSpec, expander) {
  11507. if (!expandSpec.values || !expandSpec.values.length) {
  11508. fluid.fail("firstValue transformer requires an array of values at path named \"values\", supplied", expandSpec);
  11509. }
  11510. for (var i = 0; i < expandSpec.values.length; i++) {
  11511. var value = expandSpec.values[i];
  11512. var expanded = expander.expand(value);
  11513. if (expanded !== undefined) {
  11514. return expanded;
  11515. }
  11516. }
  11517. };
  11518. fluid.defaults("fluid.model.transform.firstValue", {
  11519. gradeNames: "fluid.transformFunction"
  11520. });
  11521. // TODO: Incomplete implementation which only checks expected paths
  11522. fluid.deepEquals = function (expected, actual, stats) {
  11523. if (fluid.isPrimitive(expected)) {
  11524. if (expected === actual) {
  11525. ++stats.matchCount;
  11526. } else {
  11527. ++stats.mismatchCount;
  11528. stats.messages.push("Value mismatch at path " + stats.path + ": expected " + expected + " actual " + actual);
  11529. }
  11530. }
  11531. else {
  11532. if (typeof(expected) !== typeof(actual)) {
  11533. ++stats.mismatchCount;
  11534. stats.messages.push("Type mismatch at path " + stats.path + ": expected " + typeof(expected) + " actual " + typeof(actual));
  11535. } else {
  11536. fluid.each(expected, function (value, key) {
  11537. stats.pathOps.push(key);
  11538. fluid.deepEquals(expected[key], actual[key], stats);
  11539. stats.pathOps.pop(key);
  11540. });
  11541. }
  11542. }
  11543. };
  11544. fluid.model.transform.matchValue = function (expected, actual) {
  11545. if (fluid.isPrimitive(expected)) {
  11546. return expected === actual ? 1 : 0;
  11547. } else {
  11548. var stats = {
  11549. matchCount: 0,
  11550. mismatchCount: 0,
  11551. messages: []
  11552. };
  11553. fluid.model.makePathStack(stats, "path");
  11554. fluid.deepEquals(expected, actual, stats);
  11555. return stats.matchCount;
  11556. }
  11557. };
  11558. // unsupported, NON-API function
  11559. fluid.model.transform.compareMatches = function (speca, specb) {
  11560. return specb.matchCount - speca.matchCount;
  11561. };
  11562. fluid.firstDefined = function (a, b) {
  11563. return a === undefined ? b : a;
  11564. };
  11565. // unsupported, NON-API function
  11566. fluid.model.transform.matchValueMapperFull = function (outerValue, expander, expandSpec) {
  11567. var o = expandSpec.options;
  11568. if (o.length === 0) {
  11569. fluid.fail("valueMapper supplied empty list of options: ", expandSpec);
  11570. }
  11571. if (o.length === 1) {
  11572. return 0;
  11573. }
  11574. var matchPower = [];
  11575. for (var i = 0; i < o.length; ++i) {
  11576. var option = o[i];
  11577. var value = fluid.firstDefined(fluid.model.transform.getValue(option.inputPath, undefined, expander),
  11578. outerValue);
  11579. var matchCount = fluid.model.transform.matchValue(option.undefinedInputValue ? undefined : option.inputValue, value);
  11580. matchPower[i] = {index: i, matchCount: matchCount};
  11581. }
  11582. matchPower.sort(fluid.model.transform.compareMatches);
  11583. return matchPower[0].matchCount === matchPower[1].matchCount ? -1 : matchPower[0].index;
  11584. };
  11585. fluid.model.transform.valueMapper = function (expandSpec, expander) {
  11586. if (!expandSpec.options) {
  11587. fluid.fail("demultiplexValue requires a list or hash of options at path named \"options\", supplied ", expandSpec);
  11588. }
  11589. var value = fluid.model.transform.getValue(expandSpec.inputPath, undefined, expander);
  11590. var deref = fluid.isArrayable(expandSpec.options) ? // long form with list of records
  11591. function (testVal) {
  11592. var index = fluid.model.transform.matchValueMapperFull(testVal, expander, expandSpec);
  11593. return index === -1 ? null : expandSpec.options[index];
  11594. } :
  11595. function (testVal) {
  11596. return expandSpec.options[testVal];
  11597. };
  11598. var indexed = deref(value);
  11599. if (!indexed) {
  11600. // if no branch matches, try again using this value - WARNING, this seriously
  11601. // threatens invertibility
  11602. indexed = deref(expandSpec.defaultInputValue);
  11603. }
  11604. if (!indexed) {
  11605. return;
  11606. }
  11607. var outputValue = fluid.isPrimitive(indexed) ? indexed :
  11608. (indexed.undefinedOutputValue ? undefined :
  11609. (indexed.outputValue === undefined ? expandSpec.defaultOutputValue : indexed.outputValue));
  11610. var outputPath = indexed.outputPath === undefined ? expandSpec.outputPath : indexed.outputPath;
  11611. return fluid.model.transform.setValue(outputPath, outputValue, expander);
  11612. };
  11613. fluid.model.transform.valueMapper.invert = function (expandSpec, expander) {
  11614. var options = [];
  11615. var togo = {
  11616. type: "fluid.model.transform.valueMapper",
  11617. options: options
  11618. };
  11619. var isArray = fluid.isArrayable(expandSpec.options);
  11620. var findCustom = function (name) {
  11621. return fluid.find(expandSpec.options, function (option) {
  11622. if (option[name]) {
  11623. return true;
  11624. }
  11625. });
  11626. };
  11627. var anyCustomOutput = findCustom("outputPath");
  11628. var anyCustomInput = findCustom("inputPath");
  11629. if (!anyCustomOutput) {
  11630. togo.inputPath = fluid.model.composePaths(expander.outputPrefix, expandSpec.outputPath);
  11631. }
  11632. if (!anyCustomInput) {
  11633. togo.outputPath = fluid.model.composePaths(expander.inputPrefix, expandSpec.inputPath);
  11634. }
  11635. var def = fluid.firstDefined;
  11636. fluid.each(expandSpec.options, function (option, key) {
  11637. var outOption = {};
  11638. var origInputValue = def(isArray ? option.inputValue : key, expandSpec.defaultInputValue);
  11639. if (origInputValue === undefined) {
  11640. fluid.fail("Failure inverting configuration for valueMapper - inputValue could not be resolved for record " + key + ": ", expandSpec);
  11641. }
  11642. outOption.outputValue = origInputValue;
  11643. var origOutputValue = def(option.outputValue, expandSpec.defaultOutputValue);
  11644. outOption.inputValue = origOutputValue;
  11645. if (anyCustomOutput) {
  11646. outOption.inputPath = fluid.model.composePaths(expander.outputPrefix, def(option.outputPath, expandSpec.outputPath));
  11647. }
  11648. if (anyCustomInput) {
  11649. outOption.outputPath = fluid.model.composePaths(expander.inputPrefix, def(option.inputPath, expandSpec.inputPath));
  11650. }
  11651. options.push(outOption);
  11652. });
  11653. return togo;
  11654. };
  11655. fluid.model.transform.valueMapper.collect = function (expandSpec, expander) {
  11656. var togo = [];
  11657. fluid.model.transform.accumulateInputPath(expandSpec.inputPath, expander, togo);
  11658. fluid.each(expandSpec.options, function (option) {
  11659. fluid.model.transform.accumulateInputPath(option.inputPath, expander, togo);
  11660. });
  11661. return togo;
  11662. };
  11663. fluid.defaults("fluid.model.transform.valueMapper", {
  11664. gradeNames: ["fluid.transformFunction", "fluid.lens"],
  11665. invertConfiguration: "fluid.model.transform.valueMapper.invert",
  11666. collectInputPaths: "fluid.model.transform.valueMapper.collect"
  11667. });
  11668. // TODO: prefixApplier is an expander which is currently unused and untested
  11669. fluid.model.transform.prefixApplier = function (expandSpec, expander) {
  11670. if (expandSpec.inputPrefix) {
  11671. expander.inputPrefixOp.push(expandSpec.inputPrefix);
  11672. }
  11673. if (expandSpec.outputPrefix) {
  11674. expander.outputPrefixOp.push(expandSpec.outputPrefix);
  11675. }
  11676. expander.expand(expandSpec.value);
  11677. if (expandSpec.inputPrefix) {
  11678. expander.inputPrefixOp.pop();
  11679. }
  11680. if (expandSpec.outputPrefix) {
  11681. expander.outputPrefixOp.pop();
  11682. }
  11683. };
  11684. fluid.defaults("fluid.model.transform.prefixApplier", {
  11685. gradeNames: ["fluid.transformFunction"]
  11686. });
  11687. // unsupported, NON-API function
  11688. fluid.model.makePathStack = function (expander, prefixName) {
  11689. var stack = expander[prefixName + "Stack"] = [];
  11690. expander[prefixName] = "";
  11691. return {
  11692. push: function (prefix) {
  11693. var newPath = fluid.model.composePaths(expander[prefixName], prefix);
  11694. stack.push(expander[prefixName]);
  11695. expander[prefixName] = newPath;
  11696. },
  11697. pop: function () {
  11698. expander[prefixName] = stack.pop();
  11699. }
  11700. };
  11701. };
  11702. // unsupported, NON-API function
  11703. fluid.model.transform.expandExpander = function (expandSpec, expander) {
  11704. var typeName = expandSpec.type;
  11705. if (!typeName) {
  11706. fluid.fail("Transformation record is missing a type name: ", expandSpec);
  11707. }
  11708. if (typeName.indexOf(".") === -1) {
  11709. typeName = "fluid.model.transform." + typeName;
  11710. }
  11711. var expanderFn = fluid.getGlobalValue(typeName);
  11712. var expdef = fluid.defaults(typeName);
  11713. if (typeof(expanderFn) !== "function") {
  11714. fluid.fail("Transformation record specifies transformation function with name " +
  11715. expandSpec.type + " which is not a function - ", expanderFn);
  11716. }
  11717. if (!fluid.hasGrade(expdef, "fluid.transformFunction")) {
  11718. // If no suitable grade is set up, assume that it is intended to be used as a standardTransformFunction
  11719. expdef = fluid.defaults("fluid.standardTransformFunction");
  11720. }
  11721. var expanderArgs = [expandSpec, expander];
  11722. if (fluid.hasGrade(expdef, "fluid.standardInputTransformFunction")) {
  11723. var expanded = fluid.model.transform.getValue(expandSpec.inputPath, expandSpec.value, expander);
  11724. expanderArgs[0] = expanded;
  11725. expanderArgs[2] = expandSpec;
  11726. }
  11727. var transformed = expanderFn.apply(null, expanderArgs);
  11728. if (fluid.hasGrade(expdef, "fluid.standardOutputTransformFunction")) {
  11729. transformed = fluid.model.transform.setValue(expandSpec.outputPath, transformed, expander);
  11730. }
  11731. return transformed;
  11732. };
  11733. // unsupported, NON-API function
  11734. fluid.model.transform.expandWildcards = function (expander, source) {
  11735. fluid.each(source, function (value, key) {
  11736. var q = expander.queued;
  11737. expander.pathOp.push(fluid.pathUtil.escapeSegment(key.toString()));
  11738. for (var i = 0; i < q.length; ++i) {
  11739. if (fluid.pathUtil.matchPath(q[i].matchPath, expander.path, true)) {
  11740. var esCopy = fluid.copy(q[i].expandSpec);
  11741. if (esCopy.inputPath === undefined || fluid.model.transform.hasWildcard(esCopy.inputPath)) {
  11742. esCopy.inputPath = "";
  11743. }
  11744. // TODO: allow some kind of interpolation for output path
  11745. expander.inputPrefixOp.push(expander.path);
  11746. expander.outputPrefixOp.push(expander.path);
  11747. fluid.model.transform.expandExpander(esCopy, expander);
  11748. expander.outputPrefixOp.pop();
  11749. expander.inputPrefixOp.pop();
  11750. }
  11751. }
  11752. if (!fluid.isPrimitive(value)) {
  11753. fluid.model.transform.expandWildcards(expander, value);
  11754. }
  11755. expander.pathOp.pop();
  11756. });
  11757. };
  11758. // unsupported, NON-API function
  11759. fluid.model.transform.hasWildcard = function (path) {
  11760. return typeof(path) === "string" && path.indexOf("*") !== -1;
  11761. };
  11762. // unsupported, NON-API function
  11763. fluid.model.transform.maybePushWildcard = function (expander, expandSpec) {
  11764. var hw = fluid.model.transform.hasWildcard;
  11765. var matchPath;
  11766. if (hw(expandSpec.inputPath)) {
  11767. matchPath = fluid.model.composePaths(expander.inputPrefix, expandSpec.inputPath);
  11768. }
  11769. else if (hw(expander.outputPrefix) || hw(expandSpec.outputPath)) {
  11770. matchPath = fluid.model.composePaths(expander.outputPrefix, expandSpec.outputPath);
  11771. }
  11772. if (matchPath) {
  11773. expander.queued.push({expandSpec: expandSpec, outputPrefix: expander.outputPrefix, inputPrefix: expander.inputPrefix, matchPath: matchPath});
  11774. return true;
  11775. }
  11776. return false;
  11777. };
  11778. // From UIOptions utility fluid.uiOptions.sortByKeyLength!
  11779. fluid.model.sortByKeyLength = function (inObject) {
  11780. var keys = fluid.keys(inObject);
  11781. return keys.sort(fluid.compareStringLength(true));
  11782. };
  11783. // Three handler functions operating the (currently) three different processing modes
  11784. // unsupported, NON-API function
  11785. fluid.model.transform.handleExpandExpander = function (expander, expandSpec) {
  11786. if (fluid.model.transform.maybePushWildcard(expander, expandSpec)) {
  11787. return;
  11788. }
  11789. else {
  11790. return fluid.model.transform.expandExpander(expandSpec, expander);
  11791. }
  11792. };
  11793. // unsupported, NON-API function
  11794. fluid.model.transform.handleInvertExpander = function (expander, expandSpec, expdef) {
  11795. var invertor = expdef.invertConfiguration;
  11796. if (invertor) {
  11797. var inverted = fluid.invokeGlobalFunction(invertor, [expandSpec, expander]);
  11798. expander.inverted.push(inverted);
  11799. }
  11800. };
  11801. // unsupported, NON-API function
  11802. fluid.model.transform.handlerCollectExpander = function (expander, expandSpec, expdef) {
  11803. var standardInput = fluid.hasGrade(expdef, "fluid.standardInputTransformFunction");
  11804. if (standardInput) {
  11805. fluid.model.transform.accumulateInputPath(expandSpec.inputPath, expander, expander.inputPaths);
  11806. }
  11807. else {
  11808. var collector = expdef.collectInputPaths;
  11809. if (collector) {
  11810. var collected = fluid.makeArray(fluid.invokeGlobalFunction(collector, [expandSpec, expander]));
  11811. expander.inputPaths = expander.inputPaths.concat(collected);
  11812. }
  11813. }
  11814. };
  11815. // unsupported, NON-API function
  11816. fluid.model.transform.expandValue = function (rule, expander) {
  11817. if (typeof(rule) === "string") {
  11818. rule = fluid.model.transform.pathToRule(rule);
  11819. }
  11820. // special dispensation to allow "value" at top level
  11821. // TODO: Proper escaping rules
  11822. else if (rule.value && expander.outputPrefix !== "") {
  11823. rule = fluid.model.transform.valueToRule(rule.value);
  11824. }
  11825. var togo;
  11826. if (rule.expander) {
  11827. var expanders = fluid.makeArray(rule.expander);
  11828. for (var i = 0; i < expanders.length; ++i) {
  11829. var expandSpec = expanders[i];
  11830. var expdef = fluid.defaults(expandSpec.type);
  11831. var returned = expander.expanderHandler(expander, expandSpec, expdef);
  11832. if (returned !== undefined) {
  11833. togo = returned;
  11834. }
  11835. }
  11836. }
  11837. // always apply rules with shortest keys first
  11838. var keys = fluid.model.sortByKeyLength(rule);
  11839. for (var i = 0; i < keys.length; ++i) { // jslint:ok - already defined
  11840. var key = keys[i];
  11841. if (key !== "expander") {
  11842. var value = rule[key];
  11843. expander.outputPrefixOp.push(key);
  11844. expander.expand(value, expander);
  11845. expander.outputPrefixOp.pop();
  11846. }
  11847. }
  11848. togo = fluid.get(expander.target, expander.outputPrefix);
  11849. return togo;
  11850. };
  11851. // unsupported, NON-API function
  11852. fluid.model.transform.makeExpander = function (expander, handleFn, expandFn) {
  11853. expandFn = expandFn || fluid.model.transform.expandValue;
  11854. expander.expand = function (rules) {
  11855. return expandFn(rules, expander);
  11856. };
  11857. expander.outputPrefixOp = fluid.model.makePathStack(expander, "outputPrefix");
  11858. expander.inputPrefixOp = fluid.model.makePathStack(expander, "inputPrefix");
  11859. expander.expanderHandler = handleFn;
  11860. };
  11861. fluid.model.transform.invertConfiguration = function (rules) {
  11862. var expander = {
  11863. inverted: []
  11864. };
  11865. fluid.model.transform.makeExpander(expander, fluid.model.transform.handleInvertExpander);
  11866. expander.expand(rules);
  11867. return {
  11868. expander: expander.inverted
  11869. };
  11870. };
  11871. fluid.model.transform.collectInputPaths = function (rules) {
  11872. var expander = {
  11873. inputPaths: []
  11874. };
  11875. fluid.model.transform.makeExpander(expander, fluid.model.transform.handlerCollectExpander);
  11876. expander.expand(rules);
  11877. return expander.inputPaths;
  11878. };
  11879. // unsupported, NON-API function
  11880. fluid.model.transform.flatSchemaStrategy = function (flatSchema) {
  11881. var keys = fluid.model.sortByKeyLength(flatSchema);
  11882. return function (root, segment, path) {
  11883. // TODO: clearly this implementation could be much more efficient
  11884. for (var i = 0; i < keys.length; ++i) {
  11885. var key = keys[i];
  11886. if (fluid.pathUtil.matchPath(key, path, true) !== null) {
  11887. return flatSchema[key];
  11888. }
  11889. }
  11890. };
  11891. };
  11892. // unsupported, NON-API function
  11893. fluid.model.transform.defaultSchemaValue = function (schemaValue) {
  11894. var type = fluid.isPrimitive(schemaValue) ? schemaValue : schemaValue.type;
  11895. return type === "array" ? [] : {};
  11896. };
  11897. // unsupported, NON-API function
  11898. fluid.model.transform.isomorphicSchemaStrategy = function (source, getConfig) {
  11899. return function (root, segment, path) {
  11900. var existing = fluid.get(source, path, getConfig);
  11901. return fluid.isArrayable(existing) ? "array" : "object";
  11902. };
  11903. };
  11904. // unsupported, NON-API function
  11905. fluid.model.transform.decodeStrategy = function (source, options, getConfig) {
  11906. if (options.isomorphic) {
  11907. return fluid.model.transform.isomorphicSchemaStrategy(source, getConfig);
  11908. }
  11909. else if (options.flatSchema) {
  11910. return fluid.model.transform.flatSchemaStrategy(options.flatSchema, getConfig);
  11911. }
  11912. };
  11913. // unsupported, NON-API function
  11914. fluid.model.transform.schemaToCreatorStrategy = function (strategy) {
  11915. return function (root, segment, path) {
  11916. if (root[segment] === undefined) {
  11917. var schemaValue = strategy(root, segment, path);
  11918. return root[segment] = fluid.model.transform.defaultSchemaValue(schemaValue);
  11919. }
  11920. };
  11921. };
  11922. /** Transforms a model by a sequence of rules. Parameters as for fluid.model.transform,
  11923. * only with an array accepted for "rules"
  11924. */
  11925. fluid.model.transform.sequence = function (source, rules, options) {
  11926. for (var i = 0; i < rules.length; ++i) {
  11927. source = fluid.model.transform(source, rules[i], options);
  11928. }
  11929. return source;
  11930. };
  11931. /**
  11932. * Transforms a model based on a specified expansion rules objects.
  11933. * Rules objects take the form of:
  11934. * {
  11935. * "target.path": "value.el.path" || {
  11936. * expander: {
  11937. * type: "expander.function.path",
  11938. * ...
  11939. * }
  11940. * }
  11941. * }
  11942. *
  11943. * @param {Object} source the model to transform
  11944. * @param {Object} rules a rules object containing instructions on how to transform the model
  11945. * @param {Object} options a set of rules governing the transformations. At present this may contain
  11946. * the values <code>isomorphic: true</code> indicating that the output model is to be governed by the
  11947. * same schema found in the input model, or <code>flatSchema</code> holding a flat schema object which
  11948. * consists of a hash of EL path specifications with wildcards, to the values "array"/"object" defining
  11949. * the schema to be used to construct missing trunk values.
  11950. */
  11951. fluid.model.transformWithRules = function (source, rules, options) {
  11952. options = options || {};
  11953. var parser = {
  11954. parse: fluid.pathUtil.parseEL,
  11955. compose: fluid.pathUtil.composePath
  11956. };
  11957. var getConfig = {
  11958. parser: parser,
  11959. strategies: [fluid.model.defaultFetchStrategy]
  11960. };
  11961. var schemaStrategy = fluid.model.transform.decodeStrategy(source, options, getConfig);
  11962. var setConfig = {
  11963. parser: parser,
  11964. strategies: [fluid.model.defaultFetchStrategy, schemaStrategy ? fluid.model.transform.schemaToCreatorStrategy(schemaStrategy)
  11965. : fluid.model.defaultCreatorStrategy]
  11966. };
  11967. var expander = {
  11968. source: source,
  11969. target: schemaStrategy ? fluid.model.transform.defaultSchemaValue(schemaStrategy(null, "", "")) : {},
  11970. resolverGetConfig: getConfig,
  11971. queued: []
  11972. };
  11973. fluid.model.transform.makeExpander(expander, fluid.model.transform.handleExpandExpander);
  11974. expander.applier = fluid.makeChangeApplier(expander.target, {resolverSetConfig: setConfig});
  11975. expander.expand(rules);
  11976. if (expander.queued.length > 0) {
  11977. expander.typeStack = [];
  11978. expander.pathOp = fluid.model.makePathStack(expander, "path");
  11979. fluid.model.transform.expandWildcards(expander, source);
  11980. }
  11981. return expander.target;
  11982. };
  11983. $.extend(fluid.model.transformWithRules, fluid.model.transform);
  11984. fluid.model.transform = fluid.model.transformWithRules;
  11985. })(jQuery, fluid_1_5);
  11986. /*
  11987. Copyright 2008-2010 University of Cambridge
  11988. Copyright 2008-2010 University of Toronto
  11989. Copyright 2010-2011 Lucendo Development Ltd.
  11990. Copyright 2010-2011 OCAD University
  11991. Licensed under the Educational Community License (ECL), Version 2.0 or the New
  11992. BSD license. You may not use this file except in compliance with one these
  11993. Licenses.
  11994. You may obtain a copy of the ECL 2.0 License and BSD License at
  11995. https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt
  11996. */
  11997. // Declare dependencies
  11998. /*global fluid:true, fluid_1_5:true, jQuery*/
  11999. // JSLint options
  12000. /*jslint white: true, funcinvoke: true, elsecatch: true, operator: true, undef: true, newcap: true, nomen: true, regexp: true, bitwise: true, browser: true, forin: true, maxerr: 100, indent: 4 */
  12001. var fluid_1_5 = fluid_1_5 || {};
  12002. var fluid = fluid || fluid_1_5;
  12003. (function ($, fluid) {
  12004. // $().fluid("selectable", args)
  12005. // $().fluid("selectable".that()
  12006. // $().fluid("pager.pagerBar", args)
  12007. // $().fluid("reorderer", options)
  12008. /** Create a "bridge" from code written in the Fluid standard "that-ist" style,
  12009. * to the standard JQuery UI plugin architecture specified at http://docs.jquery.com/UI/Guidelines .
  12010. * Every Fluid component corresponding to the top-level standard signature (JQueryable, options)
  12011. * will automatically convert idiomatically to the JQuery UI standard via this adapter.
  12012. * Any return value which is a primitive or array type will become the return value
  12013. * of the "bridged" function - however, where this function returns a general hash
  12014. * (object) this is interpreted as forming part of the Fluid "return that" pattern,
  12015. * and the function will instead be bridged to "return this" as per JQuery standard,
  12016. * permitting chaining to occur. However, as a courtesy, the particular "this" returned
  12017. * will be augmented with a function that() which will allow the original return
  12018. * value to be retrieved if desired.
  12019. * @param {String} name The name under which the "plugin space" is to be injected into
  12020. * JQuery
  12021. * @param {Object} peer The root of the namespace corresponding to the peer object.
  12022. */
  12023. fluid.thatistBridge = function (name, peer) {
  12024. var togo = function (funcname) {
  12025. var segs = funcname.split(".");
  12026. var move = peer;
  12027. for (var i = 0; i < segs.length; ++i) {
  12028. move = move[segs[i]];
  12029. }
  12030. var args = [this];
  12031. if (arguments.length === 2) {
  12032. args = args.concat($.makeArray(arguments[1]));
  12033. }
  12034. var ret = move.apply(null, args);
  12035. this.that = function () {
  12036. return ret;
  12037. };
  12038. var type = typeof(ret);
  12039. return !ret || type === "string" || type === "number" || type === "boolean"
  12040. || (ret && ret.length !== undefined) ? ret : this;
  12041. };
  12042. $.fn[name] = togo;
  12043. return togo;
  12044. };
  12045. fluid.thatistBridge("fluid", fluid);
  12046. fluid.thatistBridge("fluid_1_5", fluid_1_5);
  12047. /*************************************************************************
  12048. * Tabindex normalization - compensate for browser differences in naming
  12049. * and function of "tabindex" attribute and tabbing order.
  12050. */
  12051. // -- Private functions --
  12052. var normalizeTabindexName = function () {
  12053. return $.browser.msie ? "tabIndex" : "tabindex";
  12054. };
  12055. var canHaveDefaultTabindex = function (elements) {
  12056. if (elements.length <= 0) {
  12057. return false;
  12058. }
  12059. return $(elements[0]).is("a, input, button, select, area, textarea, object");
  12060. };
  12061. var getValue = function (elements) {
  12062. if (elements.length <= 0) {
  12063. return undefined;
  12064. }
  12065. if (!fluid.tabindex.hasAttr(elements)) {
  12066. return canHaveDefaultTabindex(elements) ? Number(0) : undefined;
  12067. }
  12068. // Get the attribute and return it as a number value.
  12069. var value = elements.attr(normalizeTabindexName());
  12070. return Number(value);
  12071. };
  12072. var setValue = function (elements, toIndex) {
  12073. return elements.each(function (i, item) {
  12074. $(item).attr(normalizeTabindexName(), toIndex);
  12075. });
  12076. };
  12077. // -- Public API --
  12078. /**
  12079. * Gets the value of the tabindex attribute for the first item, or sets the tabindex value of all elements
  12080. * if toIndex is specified.
  12081. *
  12082. * @param {String|Number} toIndex
  12083. */
  12084. fluid.tabindex = function (target, toIndex) {
  12085. target = $(target);
  12086. if (toIndex !== null && toIndex !== undefined) {
  12087. return setValue(target, toIndex);
  12088. } else {
  12089. return getValue(target);
  12090. }
  12091. };
  12092. /**
  12093. * Removes the tabindex attribute altogether from each element.
  12094. */
  12095. fluid.tabindex.remove = function (target) {
  12096. target = $(target);
  12097. return target.each(function (i, item) {
  12098. $(item).removeAttr(normalizeTabindexName());
  12099. });
  12100. };
  12101. /**
  12102. * Determines if an element actually has a tabindex attribute present.
  12103. */
  12104. fluid.tabindex.hasAttr = function (target) {
  12105. target = $(target);
  12106. if (target.length <= 0) {
  12107. return false;
  12108. }
  12109. var togo = target.map(
  12110. function () {
  12111. var attributeNode = this.getAttributeNode(normalizeTabindexName());
  12112. return attributeNode ? attributeNode.specified : false;
  12113. }
  12114. );
  12115. return togo.length === 1 ? togo[0] : togo;
  12116. };
  12117. /**
  12118. * Determines if an element either has a tabindex attribute or is naturally tab-focussable.
  12119. */
  12120. fluid.tabindex.has = function (target) {
  12121. target = $(target);
  12122. return fluid.tabindex.hasAttr(target) || canHaveDefaultTabindex(target);
  12123. };
  12124. // Keyboard navigation
  12125. // Public, static constants needed by the rest of the library.
  12126. fluid.a11y = $.a11y || {};
  12127. fluid.a11y.orientation = {
  12128. HORIZONTAL: 0,
  12129. VERTICAL: 1,
  12130. BOTH: 2
  12131. };
  12132. var UP_DOWN_KEYMAP = {
  12133. next: $.ui.keyCode.DOWN,
  12134. previous: $.ui.keyCode.UP
  12135. };
  12136. var LEFT_RIGHT_KEYMAP = {
  12137. next: $.ui.keyCode.RIGHT,
  12138. previous: $.ui.keyCode.LEFT
  12139. };
  12140. // Private functions.
  12141. var unwrap = function (element) {
  12142. return element.jquery ? element[0] : element; // Unwrap the element if it's a jQuery.
  12143. };
  12144. var makeElementsTabFocussable = function (elements) {
  12145. // If each element doesn't have a tabindex, or has one set to a negative value, set it to 0.
  12146. elements.each(function (idx, item) {
  12147. item = $(item);
  12148. if (!item.fluid("tabindex.has") || item.fluid("tabindex") < 0) {
  12149. item.fluid("tabindex", 0);
  12150. }
  12151. });
  12152. };
  12153. // Public API.
  12154. /**
  12155. * Makes all matched elements available in the tab order by setting their tabindices to "0".
  12156. */
  12157. fluid.tabbable = function (target) {
  12158. target = $(target);
  12159. makeElementsTabFocussable(target);
  12160. };
  12161. /***********************************************************************
  12162. * Selectable functionality - geometrising a set of nodes such that they
  12163. * can be navigated (by setting focus) using a set of directional keys
  12164. */
  12165. var CONTEXT_KEY = "selectionContext";
  12166. var NO_SELECTION = -32768;
  12167. var cleanUpWhenLeavingContainer = function (selectionContext) {
  12168. if (selectionContext.activeItemIndex !== NO_SELECTION) {
  12169. if (selectionContext.options.onLeaveContainer) {
  12170. selectionContext.options.onLeaveContainer(
  12171. selectionContext.selectables[selectionContext.activeItemIndex]
  12172. );
  12173. } else if (selectionContext.options.onUnselect) {
  12174. selectionContext.options.onUnselect(
  12175. selectionContext.selectables[selectionContext.activeItemIndex]
  12176. );
  12177. }
  12178. }
  12179. if (!selectionContext.options.rememberSelectionState) {
  12180. selectionContext.activeItemIndex = NO_SELECTION;
  12181. }
  12182. };
  12183. /**
  12184. * Does the work of selecting an element and delegating to the client handler.
  12185. */
  12186. var drawSelection = function (elementToSelect, handler) {
  12187. if (handler) {
  12188. handler(elementToSelect);
  12189. }
  12190. };
  12191. /**
  12192. * Does does the work of unselecting an element and delegating to the client handler.
  12193. */
  12194. var eraseSelection = function (selectedElement, handler) {
  12195. if (handler && selectedElement) {
  12196. handler(selectedElement);
  12197. }
  12198. };
  12199. var unselectElement = function (selectedElement, selectionContext) {
  12200. eraseSelection(selectedElement, selectionContext.options.onUnselect);
  12201. };
  12202. var selectElement = function (elementToSelect, selectionContext) {
  12203. // It's possible that we're being called programmatically, in which case we should clear any previous selection.
  12204. unselectElement(selectionContext.selectedElement(), selectionContext);
  12205. elementToSelect = unwrap(elementToSelect);
  12206. var newIndex = selectionContext.selectables.index(elementToSelect);
  12207. // Next check if the element is a known selectable. If not, do nothing.
  12208. if (newIndex === -1) {
  12209. return;
  12210. }
  12211. // Select the new element.
  12212. selectionContext.activeItemIndex = newIndex;
  12213. drawSelection(elementToSelect, selectionContext.options.onSelect);
  12214. };
  12215. var selectableFocusHandler = function (selectionContext) {
  12216. return function (evt) {
  12217. // FLUID-3590: newer browsers (FF 3.6, Webkit 4) have a form of "bug" in that they will go bananas
  12218. // on attempting to move focus off an element which has tabindex dynamically set to -1.
  12219. $(evt.target).fluid("tabindex", 0);
  12220. selectElement(evt.target, selectionContext);
  12221. // Force focus not to bubble on some browsers.
  12222. return evt.stopPropagation();
  12223. };
  12224. };
  12225. var selectableBlurHandler = function (selectionContext) {
  12226. return function (evt) {
  12227. $(evt.target).fluid("tabindex", selectionContext.options.selectablesTabindex);
  12228. unselectElement(evt.target, selectionContext);
  12229. // Force blur not to bubble on some browsers.
  12230. return evt.stopPropagation();
  12231. };
  12232. };
  12233. var reifyIndex = function (sc_that) {
  12234. var elements = sc_that.selectables;
  12235. if (sc_that.activeItemIndex >= elements.length) {
  12236. sc_that.activeItemIndex = (sc_that.options.noWrap ? elements.length - 1 : 0);
  12237. }
  12238. if (sc_that.activeItemIndex < 0 && sc_that.activeItemIndex !== NO_SELECTION) {
  12239. sc_that.activeItemIndex = (sc_that.options.noWrap ? 0 : elements.length - 1);
  12240. }
  12241. if (sc_that.activeItemIndex >= 0) {
  12242. fluid.focus(elements[sc_that.activeItemIndex]);
  12243. }
  12244. };
  12245. var prepareShift = function (selectionContext) {
  12246. // FLUID-3590: FF 3.6 and Safari 4.x won't fire blur() when programmatically moving focus.
  12247. var selElm = selectionContext.selectedElement();
  12248. if (selElm) {
  12249. fluid.blur(selElm);
  12250. }
  12251. unselectElement(selectionContext.selectedElement(), selectionContext);
  12252. if (selectionContext.activeItemIndex === NO_SELECTION) {
  12253. selectionContext.activeItemIndex = -1;
  12254. }
  12255. };
  12256. var focusNextElement = function (selectionContext) {
  12257. prepareShift(selectionContext);
  12258. ++selectionContext.activeItemIndex;
  12259. reifyIndex(selectionContext);
  12260. };
  12261. var focusPreviousElement = function (selectionContext) {
  12262. prepareShift(selectionContext);
  12263. --selectionContext.activeItemIndex;
  12264. reifyIndex(selectionContext);
  12265. };
  12266. var arrowKeyHandler = function (selectionContext, keyMap, userHandlers) {
  12267. return function (evt) {
  12268. if (evt.which === keyMap.next) {
  12269. focusNextElement(selectionContext);
  12270. evt.preventDefault();
  12271. } else if (evt.which === keyMap.previous) {
  12272. focusPreviousElement(selectionContext);
  12273. evt.preventDefault();
  12274. }
  12275. };
  12276. };
  12277. var getKeyMapForDirection = function (direction) {
  12278. // Determine the appropriate mapping for next and previous based on the specified direction.
  12279. var keyMap;
  12280. if (direction === fluid.a11y.orientation.HORIZONTAL) {
  12281. keyMap = LEFT_RIGHT_KEYMAP;
  12282. }
  12283. else if (direction === fluid.a11y.orientation.VERTICAL) {
  12284. // Assume vertical in any other case.
  12285. keyMap = UP_DOWN_KEYMAP;
  12286. }
  12287. return keyMap;
  12288. };
  12289. var tabKeyHandler = function (selectionContext) {
  12290. return function (evt) {
  12291. if (evt.which !== $.ui.keyCode.TAB) {
  12292. return;
  12293. }
  12294. cleanUpWhenLeavingContainer(selectionContext);
  12295. // Catch Shift-Tab and note that focus is on its way out of the container.
  12296. if (evt.shiftKey) {
  12297. selectionContext.focusIsLeavingContainer = true;
  12298. }
  12299. };
  12300. };
  12301. var containerFocusHandler = function (selectionContext) {
  12302. return function (evt) {
  12303. var shouldOrig = selectionContext.options.autoSelectFirstItem;
  12304. var shouldSelect = typeof(shouldOrig) === "function" ? shouldOrig() : shouldOrig;
  12305. // Override the autoselection if we're on the way out of the container.
  12306. if (selectionContext.focusIsLeavingContainer) {
  12307. shouldSelect = false;
  12308. }
  12309. // This target check works around the fact that sometimes focus bubbles, even though it shouldn't.
  12310. if (shouldSelect && evt.target === selectionContext.container.get(0)) {
  12311. if (selectionContext.activeItemIndex === NO_SELECTION) {
  12312. selectionContext.activeItemIndex = 0;
  12313. }
  12314. fluid.focus(selectionContext.selectables[selectionContext.activeItemIndex]);
  12315. }
  12316. // Force focus not to bubble on some browsers.
  12317. return evt.stopPropagation();
  12318. };
  12319. };
  12320. var containerBlurHandler = function (selectionContext) {
  12321. return function (evt) {
  12322. selectionContext.focusIsLeavingContainer = false;
  12323. // Force blur not to bubble on some browsers.
  12324. return evt.stopPropagation();
  12325. };
  12326. };
  12327. var makeElementsSelectable = function (container, defaults, userOptions) {
  12328. var options = $.extend(true, {}, defaults, userOptions);
  12329. var keyMap = getKeyMapForDirection(options.direction);
  12330. var selectableElements = options.selectableElements ? options.selectableElements :
  12331. container.find(options.selectableSelector);
  12332. // Context stores the currently active item(undefined to start) and list of selectables.
  12333. var that = {
  12334. container: container,
  12335. activeItemIndex: NO_SELECTION,
  12336. selectables: selectableElements,
  12337. focusIsLeavingContainer: false,
  12338. options: options
  12339. };
  12340. that.selectablesUpdated = function (focusedItem) {
  12341. // Remove selectables from the tab order and add focus/blur handlers
  12342. if (typeof(that.options.selectablesTabindex) === "number") {
  12343. that.selectables.fluid("tabindex", that.options.selectablesTabindex);
  12344. }
  12345. that.selectables.unbind("focus." + CONTEXT_KEY);
  12346. that.selectables.unbind("blur." + CONTEXT_KEY);
  12347. that.selectables.bind("focus." + CONTEXT_KEY, selectableFocusHandler(that));
  12348. that.selectables.bind("blur." + CONTEXT_KEY, selectableBlurHandler(that));
  12349. if (keyMap && that.options.noBubbleListeners) {
  12350. that.selectables.unbind("keydown." + CONTEXT_KEY);
  12351. that.selectables.bind("keydown." + CONTEXT_KEY, arrowKeyHandler(that, keyMap));
  12352. }
  12353. if (focusedItem) {
  12354. selectElement(focusedItem, that);
  12355. }
  12356. else {
  12357. reifyIndex(that);
  12358. }
  12359. };
  12360. that.refresh = function () {
  12361. if (!that.options.selectableSelector) {
  12362. fluid.fail("Cannot refresh selectable context which was not initialised by a selector");
  12363. }
  12364. that.selectables = container.find(options.selectableSelector);
  12365. that.selectablesUpdated();
  12366. };
  12367. that.selectedElement = function () {
  12368. return that.activeItemIndex < 0 ? null : that.selectables[that.activeItemIndex];
  12369. };
  12370. // Add various handlers to the container.
  12371. if (keyMap && !that.options.noBubbleListeners) {
  12372. container.keydown(arrowKeyHandler(that, keyMap));
  12373. }
  12374. container.keydown(tabKeyHandler(that));
  12375. container.focus(containerFocusHandler(that));
  12376. container.blur(containerBlurHandler(that));
  12377. that.selectablesUpdated();
  12378. return that;
  12379. };
  12380. /**
  12381. * Makes all matched elements selectable with the arrow keys.
  12382. * Supply your own handlers object with onSelect: and onUnselect: properties for custom behaviour.
  12383. * Options provide configurability, including direction: and autoSelectFirstItem:
  12384. * Currently supported directions are jQuery.a11y.directions.HORIZONTAL and VERTICAL.
  12385. */
  12386. fluid.selectable = function (target, options) {
  12387. target = $(target);
  12388. var that = makeElementsSelectable(target, fluid.selectable.defaults, options);
  12389. fluid.setScopedData(target, CONTEXT_KEY, that);
  12390. return that;
  12391. };
  12392. /**
  12393. * Selects the specified element.
  12394. */
  12395. fluid.selectable.select = function (target, toSelect) {
  12396. fluid.focus(toSelect);
  12397. };
  12398. /**
  12399. * Selects the next matched element.
  12400. */
  12401. fluid.selectable.selectNext = function (target) {
  12402. target = $(target);
  12403. focusNextElement(fluid.getScopedData(target, CONTEXT_KEY));
  12404. };
  12405. /**
  12406. * Selects the previous matched element.
  12407. */
  12408. fluid.selectable.selectPrevious = function (target) {
  12409. target = $(target);
  12410. focusPreviousElement(fluid.getScopedData(target, CONTEXT_KEY));
  12411. };
  12412. /**
  12413. * Returns the currently selected item wrapped as a jQuery object.
  12414. */
  12415. fluid.selectable.currentSelection = function (target) {
  12416. target = $(target);
  12417. var that = fluid.getScopedData(target, CONTEXT_KEY);
  12418. return $(that.selectedElement());
  12419. };
  12420. fluid.selectable.defaults = {
  12421. direction: fluid.a11y.orientation.VERTICAL,
  12422. selectablesTabindex: -1,
  12423. autoSelectFirstItem: true,
  12424. rememberSelectionState: true,
  12425. selectableSelector: ".selectable",
  12426. selectableElements: null,
  12427. onSelect: null,
  12428. onUnselect: null,
  12429. onLeaveContainer: null,
  12430. noWrap: false
  12431. };
  12432. /********************************************************************
  12433. * Activation functionality - declaratively associating actions with
  12434. * a set of keyboard bindings.
  12435. */
  12436. var checkForModifier = function (binding, evt) {
  12437. // If no modifier was specified, just return true.
  12438. if (!binding.modifier) {
  12439. return true;
  12440. }
  12441. var modifierKey = binding.modifier;
  12442. var isCtrlKeyPresent = modifierKey && evt.ctrlKey;
  12443. var isAltKeyPresent = modifierKey && evt.altKey;
  12444. var isShiftKeyPresent = modifierKey && evt.shiftKey;
  12445. return isCtrlKeyPresent || isAltKeyPresent || isShiftKeyPresent;
  12446. };
  12447. /** Constructs a raw "keydown"-facing handler, given a binding entry. This
  12448. * checks whether the key event genuinely triggers the event and forwards it
  12449. * to any "activateHandler" registered in the binding.
  12450. */
  12451. var makeActivationHandler = function (binding) {
  12452. return function (evt) {
  12453. var target = evt.target;
  12454. if (!fluid.enabled(target)) {
  12455. return;
  12456. }
  12457. // The following 'if' clause works in the real world, but there's a bug in the jQuery simulation
  12458. // that causes keyboard simulation to fail in Safari, causing our tests to fail:
  12459. // http://ui.jquery.com/bugs/ticket/3229
  12460. // The replacement 'if' clause works around this bug.
  12461. // When this issue is resolved, we should revert to the original clause.
  12462. // if (evt.which === binding.key && binding.activateHandler && checkForModifier(binding, evt)) {
  12463. var code = evt.which ? evt.which : evt.keyCode;
  12464. if (code === binding.key && binding.activateHandler && checkForModifier(binding, evt)) {
  12465. var event = $.Event("fluid-activate");
  12466. $(target).trigger(event, [binding.activateHandler]);
  12467. if (event.isDefaultPrevented()) {
  12468. evt.preventDefault();
  12469. }
  12470. }
  12471. };
  12472. };
  12473. var makeElementsActivatable = function (elements, onActivateHandler, defaultKeys, options) {
  12474. // Create bindings for each default key.
  12475. var bindings = [];
  12476. $(defaultKeys).each(function (index, key) {
  12477. bindings.push({
  12478. modifier: null,
  12479. key: key,
  12480. activateHandler: onActivateHandler
  12481. });
  12482. });
  12483. // Merge with any additional key bindings.
  12484. if (options && options.additionalBindings) {
  12485. bindings = bindings.concat(options.additionalBindings);
  12486. }
  12487. fluid.initEnablement(elements);
  12488. // Add listeners for each key binding.
  12489. for (var i = 0; i < bindings.length; ++i) {
  12490. var binding = bindings[i];
  12491. elements.keydown(makeActivationHandler(binding));
  12492. }
  12493. elements.bind("fluid-activate", function (evt, handler) {
  12494. handler = handler || onActivateHandler;
  12495. return handler ? handler(evt) : null;
  12496. });
  12497. };
  12498. /**
  12499. * Makes all matched elements activatable with the Space and Enter keys.
  12500. * Provide your own handler function for custom behaviour.
  12501. * Options allow you to provide a list of additionalActivationKeys.
  12502. */
  12503. fluid.activatable = function (target, fn, options) {
  12504. target = $(target);
  12505. makeElementsActivatable(target, fn, fluid.activatable.defaults.keys, options);
  12506. };
  12507. /**
  12508. * Activates the specified element.
  12509. */
  12510. fluid.activate = function (target) {
  12511. $(target).trigger("fluid-activate");
  12512. };
  12513. // Public Defaults.
  12514. fluid.activatable.defaults = {
  12515. keys: [$.ui.keyCode.ENTER, $.ui.keyCode.SPACE]
  12516. };
  12517. })(jQuery, fluid_1_5);
  12518. /*
  12519. Copyright 2010-2011 Lucendo Development Ltd.
  12520. Copyright 2010-2011 OCAD University
  12521. Licensed under the Educational Community License (ECL), Version 2.0 or the New
  12522. BSD license. You may not use this file except in compliance with one these
  12523. Licenses.
  12524. You may obtain a copy of the ECL 2.0 License and BSD License at
  12525. https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt
  12526. */
  12527. /** This file contains functions which depend on the presence of a DOM document
  12528. * and which depend on the contents of Fluid.js **/
  12529. // Declare dependencies
  12530. /*global fluid_1_5:true, jQuery*/
  12531. // JSLint options
  12532. /*jslint white: true, funcinvoke: true, undef: true, newcap: true, nomen: true, regexp: true, bitwise: true, browser: true, forin: true, maxerr: 100, indent: 4 */
  12533. var fluid_1_5 = fluid_1_5 || {};
  12534. (function ($, fluid) {
  12535. fluid.defaults("fluid.viewComponent", {
  12536. gradeNames: ["fluid.littleComponent", "fluid.modelComponent", "fluid.eventedComponent"],
  12537. initFunction: "fluid.initView",
  12538. argumentMap: {
  12539. container: 0,
  12540. options: 1
  12541. }
  12542. });
  12543. // unsupported, NON-API function
  12544. // NOTE: this function represents a temporary strategy until we have more integrated IoC debugging.
  12545. // It preserves the current framework behaviour for the 1.4 release, but provides a more informative
  12546. // diagnostic - in fact, it is perfectly acceptable for a component's creator to return no value and
  12547. // the failure is really in assumptions in fluid.initComponent. Revisit this issue for 1.5
  12548. fluid.diagnoseFailedView = function (componentName, that, options, args) {
  12549. if (!that && fluid.hasGrade(options, "fluid.viewComponent")) {
  12550. var container = fluid.wrap(args[1]);
  12551. var message1 = "Instantiation of autoInit component with type " + componentName + " failed, since ";
  12552. if (container.length === 0) {
  12553. fluid.fail(message1 + "selector \"", args[1], "\" did not match any markup in the document");
  12554. } else {
  12555. fluid.fail(message1 + " component creator function did not return a value");
  12556. }
  12557. }
  12558. };
  12559. fluid.checkTryCatchParameter = function () {
  12560. var location = window.location || { search: "", protocol: "file:" };
  12561. var GETParams = location.search.slice(1).split('&');
  12562. return fluid.contains(GETParams, "notrycatch");
  12563. };
  12564. fluid.notrycatch = fluid.checkTryCatchParameter();
  12565. /**
  12566. * Wraps an object in a jQuery if it isn't already one. This function is useful since
  12567. * it ensures to wrap a null or otherwise falsy argument to itself, rather than the
  12568. * often unhelpful jQuery default of returning the overall document node.
  12569. *
  12570. * @param {Object} obj the object to wrap in a jQuery
  12571. * @param {jQuery} userJQuery the jQuery object to use for the wrapping, optional - use the current jQuery if absent
  12572. */
  12573. fluid.wrap = function (obj, userJQuery) {
  12574. userJQuery = userJQuery || $;
  12575. return ((!obj || obj.jquery) ? obj : userJQuery(obj));
  12576. };
  12577. /**
  12578. * If obj is a jQuery, this function will return the first DOM element within it.
  12579. *
  12580. * @param {jQuery} obj the jQuery instance to unwrap into a pure DOM element
  12581. */
  12582. fluid.unwrap = function (obj) {
  12583. return obj && obj.jquery && obj.length === 1 ? obj[0] : obj; // Unwrap the element if it's a jQuery.
  12584. };
  12585. /**
  12586. * Fetches a single container element and returns it as a jQuery.
  12587. *
  12588. * @param {String||jQuery||element} containerSpec an id string, a single-element jQuery, or a DOM element specifying a unique container
  12589. * @param {Boolean} fallible <code>true</code> if an empty container is to be reported as a valid condition
  12590. * @return a single-element jQuery of container
  12591. */
  12592. fluid.container = function (containerSpec, fallible, userJQuery) {
  12593. if (userJQuery) {
  12594. containerSpec = fluid.unwrap(containerSpec);
  12595. }
  12596. var container = fluid.wrap(containerSpec, userJQuery);
  12597. if (fallible && (!container || container.length === 0)) {
  12598. return null;
  12599. }
  12600. // Throw an exception if we've got more or less than one element.
  12601. if (!container || !container.jquery || container.length !== 1) {
  12602. if (typeof (containerSpec) !== "string") {
  12603. containerSpec = container.selector;
  12604. }
  12605. var count = container.length !== undefined ? container.length : 0;
  12606. fluid.fail((count > 1 ? "More than one (" + count + ") container elements were"
  12607. : "No container element was") + " found for selector " + containerSpec);
  12608. }
  12609. if (!fluid.isDOMNode(container[0])) {
  12610. fluid.fail("fluid.container was supplied a non-jQueryable element");
  12611. }
  12612. return container;
  12613. };
  12614. /**
  12615. * Creates a new DOM Binder instance, used to locate elements in the DOM by name.
  12616. *
  12617. * @param {Object} container the root element in which to locate named elements
  12618. * @param {Object} selectors a collection of named jQuery selectors
  12619. */
  12620. fluid.createDomBinder = function (container, selectors) {
  12621. var cache = {}, that = {};
  12622. var userJQuery = container.constructor;
  12623. function cacheKey(name, thisContainer) {
  12624. return fluid.allocateSimpleId(thisContainer) + "-" + name;
  12625. }
  12626. function record(name, thisContainer, result) {
  12627. cache[cacheKey(name, thisContainer)] = result;
  12628. }
  12629. that.locate = function (name, localContainer) {
  12630. var selector, thisContainer, togo;
  12631. selector = selectors[name];
  12632. thisContainer = localContainer ? localContainer : container;
  12633. if (!thisContainer) {
  12634. fluid.fail("DOM binder invoked for selector " + name + " without container");
  12635. }
  12636. if (!selector) {
  12637. return thisContainer;
  12638. }
  12639. if (typeof (selector) === "function") {
  12640. togo = userJQuery(selector.call(null, fluid.unwrap(thisContainer)));
  12641. } else {
  12642. togo = userJQuery(selector, thisContainer);
  12643. }
  12644. if (togo.get(0) === document) {
  12645. togo = [];
  12646. }
  12647. if (!togo.selector) {
  12648. togo.selector = selector;
  12649. togo.context = thisContainer;
  12650. }
  12651. togo.selectorName = name;
  12652. record(name, thisContainer, togo);
  12653. return togo;
  12654. };
  12655. that.fastLocate = function (name, localContainer) {
  12656. var thisContainer = localContainer ? localContainer : container;
  12657. var key = cacheKey(name, thisContainer);
  12658. var togo = cache[key];
  12659. return togo ? togo : that.locate(name, localContainer);
  12660. };
  12661. that.clear = function () {
  12662. cache = {};
  12663. };
  12664. that.refresh = function (names, localContainer) {
  12665. var thisContainer = localContainer ? localContainer : container;
  12666. if (typeof names === "string") {
  12667. names = [names];
  12668. }
  12669. if (thisContainer.length === undefined) {
  12670. thisContainer = [thisContainer];
  12671. }
  12672. for (var i = 0; i < names.length; ++i) {
  12673. for (var j = 0; j < thisContainer.length; ++j) {
  12674. that.locate(names[i], thisContainer[j]);
  12675. }
  12676. }
  12677. };
  12678. that.resolvePathSegment = that.locate;
  12679. return that;
  12680. };
  12681. /** Expect that jQuery selector query has resulted in a non-empty set of
  12682. * results. If none are found, this function will fail with a diagnostic message,
  12683. * with the supplied message prepended.
  12684. */
  12685. fluid.expectFilledSelector = function (result, message) {
  12686. if (result && result.length === 0 && result.jquery) {
  12687. fluid.fail(message + ": selector \"" + result.selector + "\" with name " + result.selectorName +
  12688. " returned no results in context " + fluid.dumpEl(result.context));
  12689. }
  12690. };
  12691. /**
  12692. * The central initialiation method called as the first act of every Fluid
  12693. * component. This function automatically merges user options with defaults,
  12694. * attaches a DOM Binder to the instance, and configures events.
  12695. *
  12696. * @param {String} componentName The unique "name" of the component, which will be used
  12697. * to fetch the default options from store. By recommendation, this should be the global
  12698. * name of the component's creator function.
  12699. * @param {jQueryable} container A specifier for the single root "container node" in the
  12700. * DOM which will house all the markup for this component.
  12701. * @param {Object} userOptions The configuration options for this component.
  12702. */
  12703. // 4th argument is NOT SUPPORTED, see comments for initLittleComponent
  12704. fluid.initView = function (componentName, containerSpec, userOptions, localOptions) {
  12705. var container = fluid.container(containerSpec, true);
  12706. fluid.expectFilledSelector(container, "Error instantiating component with name \"" + componentName);
  12707. if (!container) {
  12708. return null;
  12709. }
  12710. var that = fluid.initLittleComponent(componentName, userOptions, localOptions || {gradeNames: ["fluid.viewComponent"]});
  12711. var userJQuery = that.options.jQuery; // Do it a second time to correct for jQuery injection
  12712. if (userJQuery) {
  12713. container = fluid.container(containerSpec, true, userJQuery);
  12714. }
  12715. fluid.log("Constructing view component " + componentName + " with container " + container.constructor.expando +
  12716. (userJQuery ? " user jQuery " + userJQuery.expando : "") + " env: " + $.expando);
  12717. that.container = container;
  12718. fluid.initDomBinder(that);
  12719. return that;
  12720. };
  12721. /**
  12722. * Creates a new DOM Binder instance for the specified component and mixes it in.
  12723. *
  12724. * @param {Object} that the component instance to attach the new DOM Binder to
  12725. */
  12726. fluid.initDomBinder = function (that) {
  12727. that.dom = fluid.createDomBinder(that.container, that.options.selectors);
  12728. that.locate = that.dom.locate;
  12729. };
  12730. // DOM Utilities.
  12731. /**
  12732. * Finds the nearest ancestor of the element that passes the test
  12733. * @param {Element} element DOM element
  12734. * @param {Function} test A function which takes an element as a parameter and return true or false for some test
  12735. */
  12736. fluid.findAncestor = function (element, test) {
  12737. element = fluid.unwrap(element);
  12738. while (element) {
  12739. if (test(element)) {
  12740. return element;
  12741. }
  12742. element = element.parentNode;
  12743. }
  12744. };
  12745. fluid.findForm = function (node) {
  12746. return fluid.findAncestor(node, function (element) {
  12747. return element.nodeName.toLowerCase() === "form";
  12748. });
  12749. };
  12750. /** A utility with the same signature as jQuery.text and jQuery.html, but without the API irregularity
  12751. * that treats a single argument of undefined as different to no arguments */
  12752. // in jQuery 1.7.1, jQuery pulled the same dumb trick with $.text() that they did with $.val() previously,
  12753. // see comment in fluid.value below
  12754. fluid.each(["text", "html"], function (method) {
  12755. fluid[method] = function (node, newValue) {
  12756. node = $(node);
  12757. return newValue === undefined ? node[method]() : node[method](newValue);
  12758. };
  12759. });
  12760. /** A generalisation of jQuery.val to correctly handle the case of acquiring and
  12761. * setting the value of clustered radio button/checkbox sets, potentially, given
  12762. * a node corresponding to just one element.
  12763. */
  12764. fluid.value = function (nodeIn, newValue) {
  12765. var node = fluid.unwrap(nodeIn);
  12766. var multiple = false;
  12767. if (node.nodeType === undefined && node.length > 1) {
  12768. node = node[0];
  12769. multiple = true;
  12770. }
  12771. if ("input" !== node.nodeName.toLowerCase() || !/radio|checkbox/.test(node.type)) {
  12772. // resist changes to contract of jQuery.val() in jQuery 1.5.1 (see FLUID-4113)
  12773. return newValue === undefined ? $(node).val() : $(node).val(newValue);
  12774. }
  12775. var name = node.name;
  12776. if (name === undefined) {
  12777. fluid.fail("Cannot acquire value from node " + fluid.dumpEl(node) + " which does not have name attribute set");
  12778. }
  12779. var elements;
  12780. if (multiple) {
  12781. elements = nodeIn;
  12782. } else {
  12783. elements = node.ownerDocument.getElementsByName(name);
  12784. var scope = fluid.findForm(node);
  12785. elements = $.grep(elements, function (element) {
  12786. if (element.name !== name) {
  12787. return false;
  12788. }
  12789. return !scope || fluid.dom.isContainer(scope, element);
  12790. });
  12791. }
  12792. if (newValue !== undefined) {
  12793. if (typeof(newValue) === "boolean") {
  12794. newValue = (newValue ? "true" : "false");
  12795. }
  12796. // jQuery gets this partially right, but when dealing with radio button array will
  12797. // set all of their values to "newValue" rather than setting the checked property
  12798. // of the corresponding control.
  12799. $.each(elements, function () {
  12800. this.checked = (newValue instanceof Array ?
  12801. $.inArray(this.value, newValue) !== -1 : newValue === this.value);
  12802. });
  12803. } else { // this part jQuery will not do - extracting value from <input> array
  12804. var checked = $.map(elements, function (element) {
  12805. return element.checked ? element.value : null;
  12806. });
  12807. return node.type === "radio" ? checked[0] : checked;
  12808. }
  12809. };
  12810. /**
  12811. * Returns a jQuery object given the id of a DOM node. In the case the element
  12812. * is not found, will return an empty list.
  12813. */
  12814. fluid.jById = function (id, dokkument) {
  12815. dokkument = dokkument && dokkument.nodeType === 9 ? dokkument : document;
  12816. var element = fluid.byId(id, dokkument);
  12817. var togo = element ? $(element) : [];
  12818. togo.selector = "#" + id;
  12819. togo.context = dokkument;
  12820. return togo;
  12821. };
  12822. /**
  12823. * Returns an DOM element quickly, given an id
  12824. *
  12825. * @param {Object} id the id of the DOM node to find
  12826. * @param {Document} dokkument the document in which it is to be found (if left empty, use the current document)
  12827. * @return The DOM element with this id, or null, if none exists in the document.
  12828. */
  12829. fluid.byId = function (id, dokkument) {
  12830. dokkument = dokkument && dokkument.nodeType === 9 ? dokkument : document;
  12831. var el = dokkument.getElementById(id);
  12832. if (el) {
  12833. // Use element id property here rather than attribute, to work around FLUID-3953
  12834. if (el.id !== id) {
  12835. fluid.fail("Problem in document structure - picked up element " +
  12836. fluid.dumpEl(el) + " for id " + id +
  12837. " without this id - most likely the element has a name which conflicts with this id");
  12838. }
  12839. return el;
  12840. } else {
  12841. return null;
  12842. }
  12843. };
  12844. /**
  12845. * Returns the id attribute from a jQuery or pure DOM element.
  12846. *
  12847. * @param {jQuery||Element} element the element to return the id attribute for
  12848. */
  12849. fluid.getId = function (element) {
  12850. return fluid.unwrap(element).id;
  12851. };
  12852. /**
  12853. * Allocate an id to the supplied element if it has none already, by a simple
  12854. * scheme resulting in ids "fluid-id-nnnn" where nnnn is an increasing integer.
  12855. */
  12856. fluid.allocateSimpleId = function (element) {
  12857. var simpleId = "fluid-id-" + fluid.allocateGuid();
  12858. if (!element) {
  12859. return simpleId;
  12860. }
  12861. element = fluid.unwrap(element);
  12862. if (!element.id) {
  12863. element.id = simpleId;
  12864. }
  12865. return element.id;
  12866. };
  12867. fluid.defaults("fluid.ariaLabeller", {
  12868. labelAttribute: "aria-label",
  12869. liveRegionMarkup: "<div class=\"liveRegion fl-offScreen-hidden\" aria-live=\"polite\"></div>",
  12870. liveRegionId: "fluid-ariaLabeller-liveRegion",
  12871. events: {
  12872. generateLiveElement: "unicast"
  12873. },
  12874. listeners: {
  12875. generateLiveElement: "fluid.ariaLabeller.generateLiveElement"
  12876. }
  12877. });
  12878. fluid.ariaLabeller = function (element, options) {
  12879. var that = fluid.initView("fluid.ariaLabeller", element, options);
  12880. that.update = function (newOptions) {
  12881. newOptions = newOptions || that.options;
  12882. that.container.attr(that.options.labelAttribute, newOptions.text);
  12883. if (newOptions.dynamicLabel) {
  12884. var live = fluid.jById(that.options.liveRegionId);
  12885. if (live.length === 0) {
  12886. live = that.events.generateLiveElement.fire(that);
  12887. }
  12888. live.text(newOptions.text);
  12889. }
  12890. };
  12891. that.update();
  12892. return that;
  12893. };
  12894. fluid.ariaLabeller.generateLiveElement = function (that) {
  12895. var liveEl = $(that.options.liveRegionMarkup);
  12896. liveEl.prop("id", that.options.liveRegionId);
  12897. $("body").append(liveEl);
  12898. return liveEl;
  12899. };
  12900. var LABEL_KEY = "aria-labelling";
  12901. fluid.getAriaLabeller = function (element) {
  12902. element = $(element);
  12903. var that = fluid.getScopedData(element, LABEL_KEY);
  12904. return that;
  12905. };
  12906. /** Manages an ARIA-mediated label attached to a given DOM element. An
  12907. * aria-labelledby attribute and target node is fabricated in the document
  12908. * if they do not exist already, and a "little component" is returned exposing a method
  12909. * "update" that allows the text to be updated. */
  12910. fluid.updateAriaLabel = function (element, text, options) {
  12911. options = $.extend({}, options || {}, {text: text});
  12912. var that = fluid.getAriaLabeller(element);
  12913. if (!that) {
  12914. that = fluid.ariaLabeller(element, options);
  12915. fluid.setScopedData(element, LABEL_KEY, that);
  12916. } else {
  12917. that.update(options);
  12918. }
  12919. return that;
  12920. };
  12921. /** "Global Dismissal Handler" for the entire page. Attaches a click handler to the
  12922. * document root that will cause dismissal of any elements (typically dialogs) which
  12923. * have registered themselves. Dismissal through this route will automatically clean up
  12924. * the record - however, the dismisser themselves must take care to deregister in the case
  12925. * dismissal is triggered through the dialog interface itself. This component can also be
  12926. * automatically configured by fluid.deadMansBlur by means of the "cancelByDefault" option */
  12927. var dismissList = {};
  12928. $(document).click(function (event) {
  12929. var target = event.target;
  12930. while (target) {
  12931. if (dismissList[target.id]) {
  12932. return;
  12933. }
  12934. target = target.parentNode;
  12935. }
  12936. fluid.each(dismissList, function (dismissFunc, key) {
  12937. dismissFunc(event);
  12938. delete dismissList[key];
  12939. });
  12940. });
  12941. // TODO: extend a configurable equivalent of the above dealing with "focusin" events
  12942. /** Accepts a free hash of nodes and an optional "dismissal function".
  12943. * If dismissFunc is set, this "arms" the dismissal system, such that when a click
  12944. * is received OUTSIDE any of the hierarchy covered by "nodes", the dismissal function
  12945. * will be executed.
  12946. */
  12947. fluid.globalDismissal = function (nodes, dismissFunc) {
  12948. fluid.each(nodes, function (node) {
  12949. // Don't bother to use the real id if it is from a foreign document - we will never receive events
  12950. // from it directly in any case - and foreign documents may be under the control of malign fiends
  12951. // such as tinyMCE who allocate the same id to everything
  12952. var id = fluid.unwrap(node).ownerDocument === document? fluid.allocateSimpleId(node) : fluid.allocateGuid();
  12953. if (dismissFunc) {
  12954. dismissList[id] = dismissFunc;
  12955. }
  12956. else {
  12957. delete dismissList[id];
  12958. }
  12959. });
  12960. };
  12961. /** Sets an interation on a target control, which morally manages a "blur" for
  12962. * a possibly composite region.
  12963. * A timed blur listener is set on the control, which waits for a short period of
  12964. * time (options.delay, defaults to 150ms) to discover whether the reason for the
  12965. * blur interaction is that either a focus or click is being serviced on a nominated
  12966. * set of "exclusions" (options.exclusions, a free hash of elements or jQueries).
  12967. * If no such event is received within the window, options.handler will be called
  12968. * with the argument "control", to service whatever interaction is required of the
  12969. * blur.
  12970. */
  12971. fluid.deadMansBlur = function (control, options) {
  12972. var that = fluid.initLittleComponent("fluid.deadMansBlur", options);
  12973. that.blurPending = false;
  12974. that.lastCancel = 0;
  12975. that.canceller = function (event) {
  12976. fluid.log("Cancellation through " + event.type + " on " + fluid.dumpEl(event.target));
  12977. that.lastCancel = Date.now();
  12978. that.blurPending = false;
  12979. };
  12980. that.noteProceeded = function () {
  12981. fluid.globalDismissal(that.options.exclusions);
  12982. };
  12983. that.reArm = function () {
  12984. fluid.globalDismissal(that.options.exclusions, that.proceed);
  12985. };
  12986. that.addExclusion = function (exclusions) {
  12987. fluid.globalDismissal(exclusions, that.proceed);
  12988. };
  12989. that.proceed = function (event) {
  12990. fluid.log("Direct proceed through " + event.type + " on " + fluid.dumpEl(event.target));
  12991. that.blurPending = false;
  12992. that.options.handler(control);
  12993. };
  12994. fluid.each(that.options.exclusions, function (exclusion) {
  12995. exclusion = $(exclusion);
  12996. fluid.each(exclusion, function (excludeEl) {
  12997. $(excludeEl).bind("focusin", that.canceller).
  12998. bind("fluid-focus", that.canceller).
  12999. click(that.canceller).mousedown(that.canceller);
  13000. // Mousedown is added for FLUID-4212, as a result of Chrome bug 6759, 14204
  13001. });
  13002. });
  13003. if (!that.options.cancelByDefault) {
  13004. $(control).bind("focusout", function (event) {
  13005. fluid.log("Starting blur timer for element " + fluid.dumpEl(event.target));
  13006. var now = Date.now();
  13007. fluid.log("back delay: " + (now - that.lastCancel));
  13008. if (now - that.lastCancel > that.options.backDelay) {
  13009. that.blurPending = true;
  13010. }
  13011. setTimeout(function () {
  13012. if (that.blurPending) {
  13013. that.options.handler(control);
  13014. }
  13015. }, that.options.delay);
  13016. });
  13017. }
  13018. else {
  13019. that.reArm();
  13020. }
  13021. return that;
  13022. };
  13023. fluid.defaults("fluid.deadMansBlur", {
  13024. delay: 150,
  13025. backDelay: 100
  13026. });
  13027. })(jQuery, fluid_1_5);
  13028. /*
  13029. Copyright 2011 OCAD University
  13030. Copyright 2010-2011 Lucendo Development Ltd.
  13031. Licensed under the Educational Community License (ECL), Version 2.0 or the New
  13032. BSD license. You may not use this file except in compliance with one these
  13033. Licenses.
  13034. You may obtain a copy of the ECL 2.0 License and BSD License at
  13035. https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt
  13036. */
  13037. // Declare dependencies
  13038. /*global fluid_1_5:true, jQuery*/
  13039. // JSLint options
  13040. /*jslint white: true, funcinvoke: true, continue: true, elsecatch: true, operator: true, jslintok:true, undef: true, newcap: true, regexp: true, bitwise: true, browser: true, forin: true, maxerr: 100, indent: 4 */
  13041. var fluid_1_5 = fluid_1_5 || {};
  13042. (function ($, fluid) {
  13043. /** The Fluid "IoC System proper" - resolution of references and
  13044. * completely automated instantiation of declaratively defined
  13045. * component trees */
  13046. var inCreationMarker = "__CURRENTLY_IN_CREATION__";
  13047. // unsupported, non-API function
  13048. fluid.isFireBreak = function(component) {
  13049. return component.options && component.options["fluid.visitComponents.fireBreak"];
  13050. };
  13051. // unsupported, non-API function
  13052. // Currently still uses manual traversal - once we ban manually instantiated components,
  13053. // it will use the instantiator's records instead. In that day, "fireBreak" will bite the dust too
  13054. fluid.visitComponentChildren = function(that, visitor, options, path) {
  13055. for (var name in that) {
  13056. var newPath = options.instantiator.composePath(path, name);
  13057. var component = that[name];
  13058. //Every component *should* have an id, but some clients may not yet be compliant
  13059. //if (component && component.typeName && !component.id) {
  13060. // fluid.fail("No id");
  13061. //}
  13062. if (!component || !component.typeName || (component.id && options.visited && options.visited[component.id])) {continue; }
  13063. if (options.visited) {
  13064. options.visited[component.id] = true;
  13065. }
  13066. if (visitor(component, name, newPath, path)) {
  13067. return true;
  13068. }
  13069. if (!fluid.isFireBreak(component) && !options.flat) {
  13070. fluid.visitComponentChildren(component, visitor, options, newPath);
  13071. }
  13072. }
  13073. };
  13074. // thatStack contains an increasing list of MORE SPECIFIC thats.
  13075. // this visits all components starting from the current location (end of stack)
  13076. // in visibility order up the tree.
  13077. var visitComponents = function(instantiator, thatStack, visitor, options) {
  13078. options = options || {
  13079. visited: {},
  13080. flat: true,
  13081. instantiator: instantiator
  13082. };
  13083. for (var i = thatStack.length - 1; i >= 0; --i) {
  13084. var that = thatStack[i];
  13085. if (fluid.isFireBreak(that)) {
  13086. return;
  13087. }
  13088. var path = instantiator.idToPath[that.id] || ""; // This resolves FLUID-4338
  13089. if (that.typeName) {
  13090. options.visited[that.id] = true;
  13091. var memberName = fluid.pathUtil.getTailPath(path);
  13092. if (visitor(that, memberName, path)) {
  13093. return;
  13094. }
  13095. }
  13096. if (fluid.visitComponentChildren(that, visitor, options, path)) {
  13097. return;
  13098. }
  13099. }
  13100. };
  13101. // An EL segment resolver strategy that will attempt to trigger creation of
  13102. // components that it discovers along the EL path, if they have been defined but not yet
  13103. // constructed. Spring, eat your heart out! Wot no SPR-2048?
  13104. function makeGingerStrategy(instantiator, that, thatStack) {
  13105. return function(component, thisSeg) {
  13106. var atval = component[thisSeg];
  13107. if (atval === undefined) {
  13108. var parentPath = instantiator.idToPath[component.id];
  13109. var childPath = fluid.composePath(parentPath, thisSeg);
  13110. atval = instantiator.pathToComponent[childPath];
  13111. // if it was not attached to the component, but it is in the instantiator, it MUST be in creation - prepare to fail
  13112. if (atval) {
  13113. atval[inCreationMarker] = true;
  13114. }
  13115. }
  13116. if (atval !== undefined) {
  13117. if (atval[inCreationMarker]) {
  13118. fluid.fail("Component " + fluid.dumpThat(atval) + " at path \"" + thisSeg
  13119. + "\" of parent " + fluid.dumpThat(component) + " cannot be used for lookup"
  13120. + " since it is still in creation. Please reorganise your dependencies so that they no longer contain circular references");
  13121. }
  13122. }
  13123. else {
  13124. if (fluid.get(component, fluid.path("options", "components", thisSeg, "type"))) {
  13125. fluid.initDependent(component, thisSeg);
  13126. atval = component[thisSeg];
  13127. }
  13128. }
  13129. return atval;
  13130. };
  13131. }
  13132. // unsupported, non-API function
  13133. fluid.dumpThat = function(that) {
  13134. return "{ typeName: \"" + that.typeName + "\" id: " + that.id + "}";
  13135. };
  13136. // unsupported, non-API function
  13137. fluid.dumpThatStack = function(thatStack, instantiator) {
  13138. var togo = fluid.transform(thatStack, function(that) {
  13139. var path = instantiator.idToPath[that.id];
  13140. return fluid.dumpThat(that) + (path? (" - path: " + path) : "");
  13141. });
  13142. return togo.join("\n");
  13143. };
  13144. // Return an array of objects describing the current activity
  13145. // unsupported, non-API function
  13146. fluid.describeActivity = function() {
  13147. return fluid.globalThreadLocal().activityStack || [];
  13148. };
  13149. // Execute the supplied function with the specified activity description pushed onto the stack
  13150. // unsupported, non-API function
  13151. fluid.pushActivity = function(func, message) {
  13152. if (!message || fluid.notrycatch) {
  13153. return func();
  13154. }
  13155. var root = fluid.globalThreadLocal();
  13156. if (!root.activityStack) {
  13157. root.activityStack = [];
  13158. }
  13159. var frames = fluid.makeArray(message);
  13160. frames.push("\n");
  13161. frames.unshift("\n");
  13162. root.activityStack = frames.concat(root.activityStack);
  13163. return fluid.tryCatch(func, null, function() {
  13164. root.activityStack = root.activityStack.slice(frames.length);
  13165. });
  13166. };
  13167. // Return a function wrapped by the activity of describing its activity
  13168. // unsupported, non-API function
  13169. fluid.wrapActivity = fluid.notrycatch? fluid.identity : function(func, messageSpec) {
  13170. return function() {
  13171. var args = fluid.makeArray(arguments);
  13172. var message = fluid.transform(fluid.makeArray(messageSpec), function(specEl) {
  13173. if (typeof(specEl) === "string" && specEl.indexOf("arguments.") === 0) {
  13174. var el = specEl.substring("arguments.".length);
  13175. return fluid.get(args, el);
  13176. }
  13177. else {
  13178. return specEl;
  13179. }
  13180. });
  13181. return fluid.pushActivity(function() {
  13182. return func.apply(null, args);
  13183. }, message);
  13184. };
  13185. };
  13186. var localRecordExpected = /arguments|options|container/;
  13187. function makeStackFetcher(instantiator, parentThat, localRecord, expandOptions) {
  13188. expandOptions = expandOptions || {};
  13189. var thatStack = instantiator.getFullStack(parentThat);
  13190. var fetchStrategies = [fluid.model.funcResolverStrategy, makeGingerStrategy(instantiator, parentThat, thatStack)];
  13191. var fetcher = function(parsed) {
  13192. var context = parsed.context;
  13193. var foundComponent;
  13194. if (context === "instantiator") {
  13195. // special treatment for the current instantiator which used to be discovered as unique in threadLocal
  13196. return instantiator;
  13197. }
  13198. else if (context === "that") {
  13199. foundComponent = parentThat;
  13200. }
  13201. else if (localRecord && localRecordExpected.test(context)) {
  13202. var fetched = fluid.get(localRecord[context], parsed.path);
  13203. return (context === "arguments" || expandOptions.direct)? fetched : {
  13204. marker: context === "options"? fluid.EXPAND : fluid.EXPAND_NOW,
  13205. value: fetched
  13206. };
  13207. }
  13208. if (!foundComponent) {
  13209. visitComponents(instantiator, thatStack, function(component, name) {
  13210. if (context === name || context === component.typeName || context === component.nickName) {
  13211. foundComponent = component;
  13212. return true; // YOUR VISIT IS AT AN END!!
  13213. }
  13214. if (fluid.get(component, fluid.path("options", "components", context, "type")) && !component[context]) {
  13215. foundComponent = fluid.get(component, context, {strategies: fetchStrategies});
  13216. return true;
  13217. }
  13218. });
  13219. }
  13220. if (!foundComponent && parsed.path !== "") {
  13221. var ref = fluid.renderContextReference(parsed);
  13222. fluid.log("Failed to resolve reference " + ref + ": thatStack contains\n" + fluid.dumpThatStack(thatStack, instantiator));
  13223. fluid.fail("Failed to resolve reference " + ref + " - could not match context with name "
  13224. + context + " from component leaf of type " + parentThat.typeName, "\ninstantiator contents: ", instantiator);
  13225. }
  13226. return fluid.get(foundComponent, parsed.path, fetchStrategies);
  13227. };
  13228. return fetcher;
  13229. }
  13230. function makeStackResolverOptions(instantiator, parentThat, localRecord, expandOptions) {
  13231. return $.extend(true, {}, fluid.defaults("fluid.resolveEnvironment"), {
  13232. fetcher: makeStackFetcher(instantiator, parentThat, localRecord, expandOptions)
  13233. });
  13234. }
  13235. // unsupported, non-API function
  13236. fluid.clearListeners = function (idToListeners, key) {
  13237. fluid.each(idToListeners[key], function (rec) {
  13238. rec.event.removeListener(rec.listener);
  13239. });
  13240. delete idToListeners[key];
  13241. }
  13242. // unsupported, non-API function - however, this structure is of considerable interest to those debugging
  13243. // into IoC issues. The structures idToPath and pathToComponent contain a complete map of the component tree
  13244. // forming the surrounding scope
  13245. fluid.instantiator = function(freeInstantiator) {
  13246. // NB: We may not use the options merging framework itself here, since "withInstantiator" below
  13247. // will blow up, as it tries to resolve the instantiator which we are instantiating *NOW*
  13248. var preThat = {
  13249. options: {
  13250. "fluid.visitComponents.fireBreak": true
  13251. },
  13252. idToPath: {},
  13253. pathToComponent: {},
  13254. idToListeners: {},
  13255. nickName: "instantiator",
  13256. composePath: fluid.composePath // For speed, we declare that no component's name may contain a period
  13257. };
  13258. var that = fluid.typeTag("fluid.instantiator");
  13259. that = $.extend(that, preThat);
  13260. that.recordListener = function (event, listener, source) {
  13261. var listeners = that.idToListeners[source.id];
  13262. if (!listeners) {
  13263. listeners = that.idToListeners[source.id] = [];
  13264. }
  13265. listeners.push({event: event, listener: listener});
  13266. };
  13267. that.getThatStack = function(component) {
  13268. var path = that.idToPath[component.id] || "";
  13269. var parsed = fluid.model.parseEL(path);
  13270. var togo = fluid.transform(parsed, function(value, i) {
  13271. var parentPath = fluid.model.composeSegments.apply(null, parsed.slice(0, i + 1));
  13272. return that.pathToComponent[parentPath];
  13273. });
  13274. var root = that.pathToComponent[""];
  13275. if (root) {
  13276. togo.unshift(root);
  13277. }
  13278. return togo;
  13279. };
  13280. that.getEnvironmentalStack = function() {
  13281. var togo = [fluid.staticEnvironment];
  13282. if (!freeInstantiator) {
  13283. togo.push(fluid.globalThreadLocal());
  13284. }
  13285. return togo;
  13286. };
  13287. that.getFullStack = function(component) {
  13288. var thatStack = component? that.getThatStack(component) : [];
  13289. return that.getEnvironmentalStack().concat(thatStack);
  13290. };
  13291. function recordComponent(component, path, created) {
  13292. if (created) {
  13293. that.idToPath[component.id] = path;
  13294. }
  13295. if (that.pathToComponent[path]) {
  13296. fluid.fail("Error during instantiation - path " + path + " which has just created component " + fluid.dumpThat(component)
  13297. + " has already been used for component " + fluid.dumpThat(that.pathToComponent[path]) + " - this is a circular instantiation or other oversight."
  13298. + " Please clear the component using instantiator.clearComponent() before reusing the path.");
  13299. }
  13300. that.pathToComponent[path] = component;
  13301. }
  13302. that.recordRoot = function(component) {
  13303. if (component && component.id && !that.pathToComponent[""]) {
  13304. recordComponent(component, "", true);
  13305. }
  13306. };
  13307. that.pushUpcomingInstantiation = function(parent, name) {
  13308. that.expectedParent = parent;
  13309. that.expectedName = name;
  13310. };
  13311. that.recordComponent = function(component) {
  13312. if (that.expectedName) {
  13313. that.recordKnownComponent(that.expectedParent, component, that.expectedName, true);
  13314. delete that.expectedName;
  13315. delete that.expectedParent;
  13316. }
  13317. else {
  13318. that.recordRoot(component);
  13319. }
  13320. };
  13321. that.clearComponent = function(component, name, child, options, noModTree, path) {
  13322. var record = that.idToPath[component.id];
  13323. // use flat recursion since we want to use our own recursion rather than rely on "visited" records
  13324. options = options || {flat: true, instantiator: that};
  13325. child = child || component[name];
  13326. path = path || record;
  13327. if (path === undefined) {
  13328. fluid.fail("Cannot clear component " + name + " from component ", component,
  13329. " which was not created by this instantiator");
  13330. }
  13331. fluid.fireEvent(child, "events.onClear", [child, name, component]);
  13332. var childPath = that.composePath(path, name);
  13333. var childRecord = that.idToPath[child.id];
  13334. // only recurse on components which were created in place - if the id record disagrees with the
  13335. // recurse path, it must have been injected
  13336. if (childRecord === childPath) {
  13337. fluid.fireEvent(child, "events.onDestroy", [child, name, component]);
  13338. fluid.clearListeners(that.idToListeners, child.id);
  13339. fluid.visitComponentChildren(child, function(gchild, gchildname, newPath, parentPath) {
  13340. that.clearComponent(child, gchildname, null, options, true, parentPath);
  13341. }, options, childPath);
  13342. }
  13343. delete that.idToPath[child.id]; // there may be no entry - if injected
  13344. delete that.pathToComponent[childPath]; // there may be no entry - if created informally
  13345. if (!noModTree) {
  13346. delete component[name]; // there may be no entry - if creation is not concluded
  13347. }
  13348. };
  13349. that.recordKnownComponent = function(parent, component, name, created) {
  13350. var parentPath = that.idToPath[parent.id] || "";
  13351. var path = that.composePath(parentPath, name);
  13352. recordComponent(component, path, created);
  13353. };
  13354. return that;
  13355. };
  13356. fluid.freeInstantiator = fluid.instantiator(true);
  13357. // unsupported, non-API function
  13358. fluid.argMapToDemands = function(argMap) {
  13359. var togo = [];
  13360. fluid.each(argMap, function(value, key) {
  13361. togo[value] = "{" + key + "}";
  13362. });
  13363. return togo;
  13364. };
  13365. // unsupported, non-API function
  13366. fluid.makePassArgsSpec = function(initArgs) {
  13367. return fluid.transform(initArgs, function(arg, index) {
  13368. return "{arguments}." + index;
  13369. });
  13370. };
  13371. function mergeToMergeAll(options) {
  13372. if (options && options.mergeOptions) {
  13373. options.mergeAllOptions = ["{options}"].concat(fluid.makeArray(options.mergeOptions));
  13374. }
  13375. }
  13376. function upgradeMergeOptions(demandspec) {
  13377. mergeToMergeAll(demandspec);
  13378. if (demandspec.mergeAllOptions) {
  13379. if (demandspec.options) {
  13380. fluid.fail("demandspec ", demandspec,
  13381. " is invalid - cannot specify literal options together with mergeOptions or mergeAllOptions");
  13382. }
  13383. demandspec.options = {
  13384. mergeAllOptions: demandspec.mergeAllOptions
  13385. };
  13386. }
  13387. if (demandspec.options) {
  13388. delete demandspec.options.mergeOptions;
  13389. }
  13390. }
  13391. /** Given a concrete argument list and/or options, determine the final concrete
  13392. * "invocation specification" which is coded by the supplied demandspec in the
  13393. * environment "thatStack" - the return is a package of concrete global function name
  13394. * and argument list which is suitable to be executed directly by fluid.invokeGlobalFunction.
  13395. */
  13396. // unsupported, non-API function
  13397. fluid.embodyDemands = function(instantiator, parentThat, demandspec, initArgs, options) {
  13398. options = options || {};
  13399. upgradeMergeOptions(demandspec);
  13400. var oldOptions = fluid.get(options, "componentRecord.options");
  13401. options.componentRecord = $.extend(true, {}, options.componentRecord,
  13402. fluid.censorKeys(demandspec, ["args", "funcName", "registeredFrom"]));
  13403. var mergeAllZero = fluid.get(options, "componentRecord.options.mergeAllOptions.0");
  13404. if (mergeAllZero === "{options}") {
  13405. fluid.set(options, "componentRecord.options.mergeAllOptions.0", oldOptions);
  13406. }
  13407. var demands = fluid.makeArray(demandspec.args);
  13408. var upDefaults = fluid.defaults(demandspec.funcName); // I can SEE into TIME!!
  13409. var argMap = upDefaults? upDefaults.argumentMap : null;
  13410. var inferMap = false;
  13411. if (!argMap && (upDefaults || (options && options.componentRecord)) && !options.passArgs) {
  13412. inferMap = true;
  13413. // infer that it must be a little component if we have any reason to believe it is a component
  13414. if (demands.length < 2) {
  13415. argMap = fluid.rawDefaults("fluid.littleComponent").argumentMap;
  13416. }
  13417. else {
  13418. argMap = {options: demands.length - 1}; // wild guess in the old style
  13419. }
  13420. }
  13421. options = options || {};
  13422. if (demands.length === 0) {
  13423. if (options.componentRecord && argMap) {
  13424. demands = fluid.argMapToDemands(argMap);
  13425. }
  13426. else if (options.passArgs) {
  13427. demands = fluid.makePassArgsSpec(initArgs);
  13428. }
  13429. }
  13430. var localRecord = $.extend({"arguments": initArgs}, fluid.censorKeys(options.componentRecord, ["type"]));
  13431. fluid.each(argMap, function(index, name) {
  13432. if (initArgs.length > 0) {
  13433. localRecord[name] = localRecord["arguments"][index];
  13434. }
  13435. if (demandspec[name] !== undefined && localRecord[name] === undefined) {
  13436. localRecord[name] = demandspec[name];
  13437. }
  13438. });
  13439. mergeToMergeAll(localRecord.options);
  13440. mergeToMergeAll(argMap && demands[argMap.options]);
  13441. var upstreamLocalRecord = $.extend({}, localRecord);
  13442. if (options.componentRecord.options !== undefined) {
  13443. upstreamLocalRecord.options = options.componentRecord.options;
  13444. }
  13445. var expandOptions = makeStackResolverOptions(instantiator, parentThat, localRecord);
  13446. var args = [];
  13447. if (demands) {
  13448. for (var i = 0; i < demands.length; ++i) {
  13449. var arg = demands[i];
  13450. // Weak detection since we cannot guarantee this material has not been copied
  13451. if (fluid.isMarker(arg) && arg.value === fluid.COMPONENT_OPTIONS.value) {
  13452. arg = "{options}";
  13453. // Backwards compatibility for non-users of GRADES - last-ditch chance to correct the inference
  13454. if (inferMap) {
  13455. argMap = {options: i};
  13456. }
  13457. }
  13458. if (typeof(arg) === "string") {
  13459. if (arg.charAt(0) === "@") {
  13460. var argpos = arg.substring(1);
  13461. arg = "{arguments}." + argpos;
  13462. }
  13463. }
  13464. if (!argMap || argMap.options !== i) {
  13465. // defer expansion required if it is non-pseudoarguments demands and this argument *is* the options
  13466. args[i] = fluid.expander.expandLight(arg, expandOptions);
  13467. }
  13468. else { // It is the component options
  13469. if (arg && typeof(arg) === "object" && !arg.targetTypeName) {
  13470. arg.targetTypeName = demandspec.funcName;
  13471. }
  13472. // ensure to copy the arg since it is an alias of the demand block material (FLUID-4223)
  13473. // and will be destructively expanded
  13474. args[i] = {marker: fluid.EXPAND, value: fluid.copy(arg), localRecord: upstreamLocalRecord};
  13475. }
  13476. if (args[i] && fluid.isMarker(args[i].marker, fluid.EXPAND_NOW)) {
  13477. args[i] = fluid.expander.expandLight(args[i].value, expandOptions);
  13478. }
  13479. }
  13480. }
  13481. else {
  13482. args = initArgs? initArgs : [];
  13483. }
  13484. var togo = {
  13485. args: args,
  13486. funcName: demandspec.funcName
  13487. };
  13488. return togo;
  13489. };
  13490. var aliasTable = {};
  13491. fluid.alias = function(demandingName, aliasName) {
  13492. if (aliasName) {
  13493. aliasTable[demandingName] = aliasName;
  13494. }
  13495. else {
  13496. return aliasTable[demandingName];
  13497. }
  13498. };
  13499. var dependentStore = {};
  13500. function searchDemands(demandingName, contextNames) {
  13501. var exist = dependentStore[demandingName] || [];
  13502. outer: for (var i = 0; i < exist.length; ++i) {
  13503. var rec = exist[i];
  13504. for (var j = 0; j < contextNames.length; ++j) {
  13505. if (rec.contexts[j] !== contextNames[j]) {
  13506. continue outer;
  13507. }
  13508. }
  13509. return rec.spec; // jslint:ok
  13510. }
  13511. }
  13512. var isDemandLogging = false;
  13513. fluid.setDemandLogging = function(set) {
  13514. isDemandLogging = set;
  13515. };
  13516. // unsupported, non-API function
  13517. fluid.isDemandLogging = function(demandingNames) {
  13518. return isDemandLogging && fluid.isLogging();
  13519. };
  13520. fluid.demands = function(demandingName, contextName, spec) {
  13521. var contextNames = fluid.makeArray(contextName).sort();
  13522. if (!spec) {
  13523. return searchDemands(demandingName, contextNames);
  13524. }
  13525. else if (spec.length) {
  13526. spec = {args: spec};
  13527. }
  13528. if (fluid.getCallerInfo && fluid.isDemandLogging()) {
  13529. var callerInfo = fluid.getCallerInfo(5);
  13530. if (callerInfo) {
  13531. spec.registeredFrom = callerInfo;
  13532. }
  13533. }
  13534. var exist = dependentStore[demandingName];
  13535. if (!exist) {
  13536. exist = [];
  13537. dependentStore[demandingName] = exist;
  13538. }
  13539. exist.push({contexts: contextNames, spec: spec});
  13540. };
  13541. // unsupported, non-API function
  13542. fluid.compareDemands = function(speca, specb) {
  13543. var p1 = speca.uncess - specb.uncess;
  13544. return p1 === 0? specb.intersect - speca.intersect : p1;
  13545. };
  13546. // unsupported, non-API function
  13547. fluid.locateAllDemands = function(instantiator, parentThat, demandingNames) {
  13548. var demandLogging = fluid.isDemandLogging(demandingNames);
  13549. if (demandLogging) {
  13550. fluid.log("Resolving demands for function names ", demandingNames, " in context of " +
  13551. (parentThat? "component " + parentThat.typeName : "no component"));
  13552. }
  13553. var contextNames = {};
  13554. var visited = [];
  13555. var thatStack = instantiator.getFullStack(parentThat);
  13556. visitComponents(instantiator, thatStack, function(component, xname, path) {
  13557. contextNames[component.typeName] = true;
  13558. visited.push(component);
  13559. });
  13560. if (demandLogging) {
  13561. fluid.log("Components in scope for resolution:\n" + fluid.dumpThatStack(visited, instantiator));
  13562. }
  13563. var matches = [];
  13564. for (var i = 0; i < demandingNames.length; ++i) {
  13565. var rec = dependentStore[demandingNames[i]] || [];
  13566. for (var j = 0; j < rec.length; ++j) {
  13567. var spec = rec[j];
  13568. var record = {spec: spec, intersect: 0, uncess: 0};
  13569. for (var k = 0; k < spec.contexts.length; ++k) {
  13570. record[contextNames[spec.contexts[k]]? "intersect" : "uncess"] += 2;
  13571. }
  13572. if (spec.contexts.length === 0) { // allow weak priority for contextless matches
  13573. record.intersect++;
  13574. }
  13575. // TODO: Potentially more subtle algorithm here - also ambiguity reports
  13576. matches.push(record);
  13577. }
  13578. }
  13579. matches.sort(fluid.compareDemands);
  13580. return matches;
  13581. };
  13582. // unsupported, non-API function
  13583. fluid.locateDemands = function(instantiator, parentThat, demandingNames) {
  13584. var matches = fluid.locateAllDemands(instantiator, parentThat, demandingNames);
  13585. var demandspec = matches.length === 0 || matches[0].intersect === 0? null : matches[0].spec.spec;
  13586. if (fluid.isDemandLogging(demandingNames)) {
  13587. if (demandspec) {
  13588. fluid.log("Located " + matches.length + " potential match" + (matches.length === 1? "" : "es") + ", selected best match with " + matches[0].intersect
  13589. + " matched context names: ", demandspec);
  13590. }
  13591. else {
  13592. fluid.log("No matches found for demands, using direct implementation");
  13593. }
  13594. }
  13595. return demandspec;
  13596. };
  13597. /** Determine the appropriate demand specification held in the fluid.demands environment
  13598. * relative to "thatStack" for the function name(s) funcNames.
  13599. */
  13600. // unsupported, non-API function
  13601. fluid.determineDemands = function (instantiator, parentThat, funcNames) {
  13602. funcNames = fluid.makeArray(funcNames);
  13603. var newFuncName = funcNames[0];
  13604. var demandspec = fluid.locateDemands(instantiator, parentThat, funcNames) || {};
  13605. if (demandspec.funcName) {
  13606. newFuncName = demandspec.funcName;
  13607. }
  13608. var aliasTo = fluid.alias(newFuncName);
  13609. if (aliasTo) {
  13610. newFuncName = aliasTo;
  13611. fluid.log("Following redirect from function name " + newFuncName + " to " + aliasTo);
  13612. var demandspec2 = fluid.locateDemands(instantiator, parentThat, [aliasTo]);
  13613. if (demandspec2) {
  13614. fluid.each(demandspec2, function(value, key) {
  13615. if (localRecordExpected.test(key)) {
  13616. fluid.fail("Error in demands block ", demandspec2, " - content with key \"" + key
  13617. + "\" is not supported since this demands block was resolved via an alias from \"" + newFuncName + "\"");
  13618. }
  13619. });
  13620. if (demandspec2.funcName) {
  13621. newFuncName = demandspec2.funcName;
  13622. fluid.log("Followed final inner demands to function name \"" + newFuncName + "\"");
  13623. }
  13624. }
  13625. }
  13626. return fluid.merge(null, {funcName: newFuncName, args: fluid.makeArray(demandspec.args)}, fluid.censorKeys(demandspec, ["funcName", "args"]));
  13627. };
  13628. // unsupported, non-API function
  13629. fluid.resolveDemands = function(instantiator, parentThat, funcNames, initArgs, options) {
  13630. var demandspec = fluid.determineDemands(instantiator, parentThat, funcNames);
  13631. return fluid.embodyDemands(instantiator, parentThat, demandspec, initArgs, options);
  13632. };
  13633. // TODO: make a *slightly* more performant version of fluid.invoke that perhaps caches the demands
  13634. // after the first successful invocation
  13635. fluid.invoke = function(functionName, args, that, environment) {
  13636. args = fluid.makeArray(args);
  13637. return fluid.withInstantiator(that, function(instantiator) {
  13638. var invokeSpec = fluid.resolveDemands(instantiator, that, functionName, args, {passArgs: true});
  13639. return fluid.invokeGlobalFunction(invokeSpec.funcName, invokeSpec.args, environment);
  13640. });
  13641. };
  13642. fluid.invoke = fluid.wrapActivity(fluid.invoke, [" while invoking function with name \"", "arguments.0", "\" from component", "arguments.2"]);
  13643. /** Make a function which performs only "static redispatch" of the supplied function name -
  13644. * that is, taking only account of the contents of the "static environment". Since the static
  13645. * environment is assumed to be constant, the dispatch of the call will be evaluated at the
  13646. * time this call is made, as an optimisation.
  13647. */
  13648. fluid.makeFreeInvoker = function(functionName, environment) {
  13649. var demandSpec = fluid.determineDemands(fluid.freeInstantiator, null, functionName);
  13650. return function() {
  13651. var invokeSpec = fluid.embodyDemands(fluid.freeInstantiator, null, demandSpec, fluid.makeArray(arguments), {passArgs: true});
  13652. return fluid.invokeGlobalFunction(invokeSpec.funcName, invokeSpec.args, environment);
  13653. };
  13654. };
  13655. fluid.makeInvoker = function(userInstantiator, that, demandspec, functionName, environment) {
  13656. demandspec = demandspec || fluid.determineDemands(userInstantiator, that, functionName);
  13657. return function() {
  13658. var args = fluid.makeArray(arguments);
  13659. // FLUID-4712: properly contextualise invoker so that any new constructions are not corrupted
  13660. return fluid.withInstantiator(that, function(instantiator) {
  13661. var invokeSpec = fluid.embodyDemands(instantiator, that, demandspec, args, {passArgs: true});
  13662. return fluid.invokeGlobalFunction(invokeSpec.funcName, invokeSpec.args, environment);
  13663. }, [" while invoking invoker with name " + functionName + " on component", that], userInstantiator);
  13664. };
  13665. };
  13666. // unsupported, non-API function
  13667. // weird higher-order function so that we can staightforwardly dispatch original args back onto listener
  13668. fluid.event.makeTrackedListenerAdder = function (instantiator, source) {
  13669. return function (event) {
  13670. return {addListener: function(listener) {
  13671. instantiator.recordListener(event, listener, source);
  13672. event.addListener.apply(null, arguments);
  13673. }
  13674. }
  13675. }
  13676. };
  13677. // unsupported, non-API function
  13678. fluid.event.listenerEngine = function(eventSpec, callback, adder) {
  13679. var argstruc = {};
  13680. function checkFire() {
  13681. var notall = fluid.find(eventSpec, function(value, key) {
  13682. if (argstruc[key] === undefined) {
  13683. return true;
  13684. }
  13685. });
  13686. if (!notall) {
  13687. callback(argstruc);
  13688. fluid.clear(argstruc);
  13689. }
  13690. }
  13691. fluid.each(eventSpec, function(event, eventName) {
  13692. adder(event).addListener(function() {
  13693. argstruc[eventName] = fluid.makeArray(arguments);
  13694. checkFire();
  13695. });
  13696. });
  13697. };
  13698. // unsupported, non-API function
  13699. fluid.event.dispatchListener = function(instantiator, that, listener, eventName, eventSpec, indirectArgs) {
  13700. return fluid.wrapActivity(function() {
  13701. listener = fluid.event.resolveListener(listener); // just resolves globals
  13702. var args = indirectArgs? arguments[0] : fluid.makeArray(arguments);
  13703. var demandspec = fluid.determineDemands(instantiator, that, eventName);
  13704. if (demandspec.args.length === 0 && eventSpec.args) {
  13705. demandspec.args = eventSpec.args;
  13706. }
  13707. var resolved = fluid.embodyDemands(instantiator, that, demandspec, args, {passArgs: true, componentOptions: eventSpec});
  13708. return listener.apply(null, resolved.args);
  13709. }, [" firing to listener to event named " + eventName + " of component ", that]);
  13710. };
  13711. // unsupported, non-API function
  13712. fluid.event.resolveListenerRecord = function(lisrec, that, eventName) {
  13713. return fluid.withInstantiator(that, function(instantiator) {
  13714. var records = fluid.makeArray(lisrec);
  13715. var transRecs = fluid.transform(records, function(record) {
  13716. if (fluid.isPrimitive(record)) {
  13717. record = {listener: record};
  13718. }
  13719. var listener = fluid.expandOptions(record.listener, that);
  13720. if (!listener) {
  13721. fluid.fail("Error in listener record - could not resolve reference " + record.listener + " to a listener or firer. "
  13722. + "Did you miss out \"events.\" when referring to an event firer?");
  13723. }
  13724. if (listener.typeName === "fluid.event.firer") {
  13725. listener = listener.fire;
  13726. }
  13727. record.listener = fluid.event.dispatchListener(instantiator, that, listener, eventName, record);
  13728. return record;
  13729. });
  13730. return {
  13731. records: transRecs,
  13732. adderWrapper: fluid.event.makeTrackedListenerAdder(instantiator, that)
  13733. };
  13734. }, [ " while resolving listener record for event named " + eventName + " for component ", that]);
  13735. };
  13736. // unsupported, non-API function
  13737. fluid.event.expandOneEvent = function(event, that) {
  13738. var origin;
  13739. if (typeof(event) === "string" && event.charAt(0) !== "{") {
  13740. // Special dispensation so we can resolve onto our own events without GINGER WORLD
  13741. origin = that.events[event];
  13742. }
  13743. else {
  13744. origin = fluid.expandOptions(event, that);
  13745. }
  13746. if (!origin || origin.typeName !== "fluid.event.firer") {
  13747. fluid.fail("Error in event specification - could not resolve base event reference ", event, " to an event firer: got ", origin);
  13748. }
  13749. return origin;
  13750. };
  13751. // unsupported, non-API function
  13752. fluid.event.expandEvents = function(event, that) {
  13753. return typeof(event) === "string"?
  13754. fluid.event.expandOneEvent(event, that) :
  13755. fluid.transform(event, function(oneEvent) {
  13756. return fluid.event.expandOneEvent(oneEvent, that);
  13757. });
  13758. };
  13759. // unsupported, non-API function
  13760. fluid.event.resolveEvent = function(that, eventName, eventSpec) {
  13761. return fluid.withInstantiator(that, function(instantiator) {
  13762. var adder = fluid.event.makeTrackedListenerAdder(instantiator, that);
  13763. if (typeof(eventSpec) === "string") {
  13764. eventSpec = {event: eventSpec};
  13765. }
  13766. var event = eventSpec.event || eventSpec.events;
  13767. if (!event) {
  13768. fluid.fail("Event specification for event with name " + eventName + " does not include a base event specification: ", eventSpec);
  13769. }
  13770. var origin = fluid.event.expandEvents(event, that);
  13771. var isMultiple = origin.typeName !== "fluid.event.firer";
  13772. var isComposite = eventSpec.args || isMultiple;
  13773. // If "event" is not composite, we want to share the listener list and FIRE method with the original
  13774. // If "event" is composite, we need to create a new firer. "composite" includes case where any boiling
  13775. // occurred - this was implemented wrongly in 1.4.
  13776. var firer;
  13777. if (isComposite) {
  13778. firer = fluid.event.getEventFirer(null, null, " [composite] " + fluid.event.nameEvent(that, eventName));
  13779. var dispatcher = fluid.event.dispatchListener(instantiator, that, firer.fire, eventName, eventSpec, isMultiple);
  13780. if (isMultiple) {
  13781. fluid.event.listenerEngine(origin, dispatcher, adder);
  13782. }
  13783. else {
  13784. adder(origin).addListener(dispatcher);
  13785. }
  13786. }
  13787. else {
  13788. firer = {typeName: "fluid.event.firer"}; // jslint:ok - already defined
  13789. firer.fire = function () {
  13790. var outerArgs = fluid.makeArray(arguments);
  13791. // TODO: this resolution should really be supplied for ALL events!
  13792. return fluid.withInstantiator(that, function () {
  13793. return origin.fire.apply(null, outerArgs);
  13794. }, " firing synthetic event " + eventName, instantiator);
  13795. };
  13796. firer.addListener = function (listener, namespace, predicate, priority) {
  13797. var dispatcher = fluid.event.dispatchListener(instantiator, that, listener, eventName, eventSpec);
  13798. adder(origin).addListener(dispatcher, namespace, predicate, priority);
  13799. };
  13800. firer.removeListener = function (listener) {
  13801. origin.removeListener(listener);
  13802. };
  13803. }
  13804. return firer;
  13805. }, [" while resolving event with name " + eventName + " attached to component ", that]);
  13806. };
  13807. fluid.registerNamespace("fluid.expander");
  13808. /** rescue that part of a component's options which should not be subject to
  13809. * options expansion via IoC - this initially consists of "components" and "mergePolicy"
  13810. * but will be expanded by the set of paths specified as "noexpand" within "mergePolicy"
  13811. */
  13812. // unsupported, non-API function
  13813. fluid.expander.preserveFromExpansion = function(options) {
  13814. var preserve = {};
  13815. var preserveList = fluid.arrayToHash(["mergePolicy", "mergeAllOptions", "components", "invokers", "events", "listeners", "transformOptions"]);
  13816. fluid.each(options.mergePolicy, function(value, key) {
  13817. if (fluid.mergePolicyIs(value, "noexpand")) {
  13818. preserveList[key] = true;
  13819. }
  13820. });
  13821. fluid.each(preserveList, function(xvalue, path) {
  13822. var pen = fluid.model.getPenultimate(options, path);
  13823. var value = pen.root[pen.last];
  13824. delete pen.root[pen.last];
  13825. fluid.set(preserve, path, value);
  13826. });
  13827. return {
  13828. restore: function(target) {
  13829. fluid.each(preserveList, function(xvalue, path) {
  13830. var preserved = fluid.get(preserve, path);
  13831. if (preserved !== undefined) {
  13832. fluid.set(target, path, preserved);
  13833. }
  13834. });
  13835. }
  13836. };
  13837. };
  13838. /** Expand a set of component options with respect to a set of "expanders" (essentially only
  13839. * deferredCall) - This substitution is destructive since it is assumed that the options are already "live" as the
  13840. * result of environmental substitutions. Note that options contained inside "components" will not be expanded
  13841. * by this call directly to avoid linearly increasing expansion depth if this call is occuring as a result of
  13842. * "initDependents" */
  13843. // TODO: This needs to be integrated with "embodyDemands" above which makes a call to "resolveEnvironment" directly
  13844. // but with very similarly derived options (makeStackResolverOptions)
  13845. fluid.expandOptions = function(args, that, localRecord, outerExpandOptions) {
  13846. if (!args) {
  13847. return args;
  13848. }
  13849. return fluid.withInstantiator(that, function(instantiator) {
  13850. //fluid.log("expandOptions for " + that.typeName + " executing with instantiator " + instantiator.id);
  13851. var expandOptions = makeStackResolverOptions(instantiator, that, localRecord, outerExpandOptions);
  13852. expandOptions.noCopy = true; // It is still possible a model may be fetched even though it is preserved
  13853. var pres;
  13854. if (!fluid.isArrayable(args) && !fluid.isPrimitive(args)) {
  13855. pres = fluid.expander.preserveFromExpansion(args);
  13856. }
  13857. var expanded = fluid.expander.expandLight(args, expandOptions);
  13858. if (pres) {
  13859. pres.restore(expanded);
  13860. }
  13861. return expanded;
  13862. }, [" while expanding options for component of type " + (that? that.typeName : "null") + ": ", that]);
  13863. };
  13864. // unsupported, non-API function
  13865. fluid.locateTransformationRecord = function(that) {
  13866. return fluid.withInstantiator(that, function(instantiator) {
  13867. var matches = fluid.locateAllDemands(instantiator, that, ["fluid.transformOptions"]);
  13868. return fluid.find(matches, function(match) {
  13869. return match.uncess === 0 && fluid.contains(match.spec.contexts, that.typeName)? match.spec.spec : undefined;
  13870. });
  13871. });
  13872. };
  13873. //
  13874. fluid.hashToArray = function(hash) {
  13875. var togo = [];
  13876. fluid.each(hash, function(value, key) {
  13877. togo.push(key);
  13878. });
  13879. return togo;
  13880. };
  13881. // unsupported, non-API function
  13882. fluid.localRecordExpected = ["type", "options", "arguments", "mergeOptions",
  13883. "mergeAllOptions", "createOnEvent", "priority"];
  13884. // unsupported, non-API function
  13885. fluid.checkComponentRecord = function(defaults, localRecord) {
  13886. var expected = fluid.arrayToHash(fluid.localRecordExpected);
  13887. fluid.each(defaults.argumentMap, function(value, key) {
  13888. expected[key] = true;
  13889. });
  13890. fluid.each(localRecord, function(value, key) {
  13891. if (!expected[key]) {
  13892. fluid.fail("Probable error in subcomponent record - key \"" + key +
  13893. "\" found, where the only legal options are " +
  13894. fluid.hashToArray(expected).join(", "));
  13895. }
  13896. });
  13897. };
  13898. // unsupported, non-API function
  13899. fluid.expandComponentOptions = function(defaults, userOptions, that) {
  13900. if (userOptions && userOptions.localRecord) {
  13901. fluid.checkComponentRecord(defaults, userOptions.localRecord);
  13902. }
  13903. defaults = fluid.expandOptions(fluid.copy(defaults), that);
  13904. var localRecord = {};
  13905. if (userOptions && userOptions.marker === fluid.EXPAND) {
  13906. // TODO: Somewhat perplexing... the local record itself, by any route we could get here, consists of unexpanded
  13907. // material taken from "componentOptions"
  13908. var localOptions = fluid.get(userOptions, "localRecord.options");
  13909. if (localOptions) {
  13910. if (defaults && defaults.mergePolicy) {
  13911. localOptions.mergePolicy = defaults.mergePolicy;
  13912. }
  13913. localRecord.options = fluid.expandOptions(localOptions, that);
  13914. }
  13915. localRecord["arguments"] = fluid.get(userOptions, "localRecord.arguments");
  13916. var toExpand = userOptions.value;
  13917. userOptions = fluid.expandOptions(toExpand, that, localRecord, {direct: true});
  13918. }
  13919. localRecord.directOptions = userOptions;
  13920. if (!localRecord.options) {
  13921. // Catch the case where there is no demands block and everything is in the subcomponent record -
  13922. // in this case, embodyDemands will not construct a localRecord and what the user refers to by "options"
  13923. // is really what we properly call "directOptions".
  13924. localRecord.options = userOptions;
  13925. }
  13926. var mergeOptions = (userOptions && userOptions.mergeAllOptions) || ["{directOptions}"];
  13927. var togo = fluid.transform(mergeOptions, function(path) {
  13928. // Avoid use of expandOptions in simple case to avoid infinite recursion when constructing instantiator
  13929. return path === "{directOptions}"? localRecord.directOptions : fluid.expandOptions(path, that, localRecord, {direct: true});
  13930. });
  13931. var transRec = fluid.locateTransformationRecord(that);
  13932. if (transRec) {
  13933. togo[0].transformOptions = transRec.options;
  13934. }
  13935. return [defaults].concat(togo);
  13936. };
  13937. fluid.expandComponentOptions = fluid.wrapActivity(fluid.expandComponentOptions,
  13938. [" while expanding component options ", "arguments.1.value", " with record ", "arguments.1", " for component ", "arguments.2"]);
  13939. // NON-API function
  13940. fluid.getInstantiators = function() {
  13941. var root = fluid.globalThreadLocal();
  13942. var ins = root["fluid.instantiator"];
  13943. if (!ins) {
  13944. ins = root["fluid.instantiator"] = [];
  13945. }
  13946. return ins;
  13947. };
  13948. // These two are the only functions which touch the instantiator stack
  13949. // NON-API function
  13950. // This function is stateful and MUST NOT be called by client code
  13951. fluid.withInstantiator = function(that, func, message, userInstantiator) {
  13952. var ins = fluid.getInstantiators();
  13953. var oldLength;
  13954. var instantiator = userInstantiator;
  13955. return fluid.pushActivity(function() {
  13956. return fluid.tryCatch(function() {
  13957. if (!instantiator) {
  13958. if (ins.length === 0) {
  13959. instantiator = fluid.instantiator();
  13960. fluid.log("Created new instantiator with id " + instantiator.id + " in order to operate on component " + (that? that.typeName : "[none]"));
  13961. }
  13962. else {
  13963. instantiator = ins[ins.length - 1];
  13964. }
  13965. }
  13966. ins.push(instantiator);
  13967. oldLength = ins.length;
  13968. if (that) {
  13969. instantiator.recordComponent(that);
  13970. }
  13971. //fluid.log("Instantiator stack +1 to " + instantiator.stackCount + " for " + typeName);
  13972. return func(instantiator);
  13973. }, null, function() {
  13974. if (ins.length !== oldLength) {
  13975. fluid.fail("Instantiator stack corrupted - old length " + oldLength + " new length " + ins.length);
  13976. }
  13977. if (ins[ins.length - 1] != instantiator) {
  13978. fluid.fail("Instantiator stack corrupted at stack top - old id " + instantiator.id + " new id " + ins[ins.length-1].id);
  13979. }
  13980. ins.length--;
  13981. //fluid.log("Instantiator stack -1 to " + instantiator.stackCount + " for " + typeName);
  13982. if (ins.length === 0) {
  13983. fluid.log("Cleared instantiators (last id " + instantiator.id + ") from threadLocal for end of " + (that? that.typeName : "[none]"));
  13984. }
  13985. });
  13986. }, message);
  13987. };
  13988. // NON-API function
  13989. fluid.fabricateDestroyMethod = function (that, name, instantiator, child) {
  13990. return function () {
  13991. instantiator.clearComponent(that, name, child);
  13992. };
  13993. };
  13994. // The case without the instantiator is from the ginger strategy - this logic is still a little ragged
  13995. fluid.initDependent = function(that, name, userInstantiator, directArgs) {
  13996. if (!that || that[name]) { return; }
  13997. fluid.log("Beginning instantiation of component with name \"" + name + "\" as child of " + fluid.dumpThat(that));
  13998. directArgs = directArgs || [];
  13999. var component = that.options.components[name];
  14000. var instance; // escape to here for debugging purposes
  14001. fluid.withInstantiator(that, function(instantiator) {
  14002. if (typeof(component) === "string") {
  14003. var instance = fluid.expandOptions([component], that)[0]; // TODO: expose more sensible semantic for expandOptions
  14004. instantiator.recordKnownComponent(that, instance, name, false);
  14005. that[name] = instance;
  14006. }
  14007. else if (component.type) {
  14008. var invokeSpec = fluid.resolveDemands(instantiator, that, [component.type, name], directArgs, {componentRecord: component});
  14009. instantiator.pushUpcomingInstantiation(that, name);
  14010. fluid.tryCatch(function() {
  14011. that[inCreationMarker] = true;
  14012. instance = fluid.initSubcomponentImpl(that, {type: invokeSpec.funcName}, invokeSpec.args);
  14013. // The existing instantiator record will be provisional, adjust it to take account of the true return
  14014. // TODO: Instantiator contents are generally extremely incomplete
  14015. var path = instantiator.composePath(instantiator.idToPath[that.id] || "", name);
  14016. var existing = instantiator.pathToComponent[path];
  14017. // This branch deals with the case where the component creator registered a component into "pathToComponent"
  14018. // that does not agree with the component which was the return value. We need to clear out "pathToComponent" but
  14019. // not shred the component since most of it is probably still valid
  14020. if (existing && existing !== instance) {
  14021. instantiator.clearComponent(that, name, existing);
  14022. }
  14023. if (instance && instance.typeName && instance.id && instance !== existing) {
  14024. instantiator.recordKnownComponent(that, instance, name, true);
  14025. }
  14026. instance.destroy = fluid.fabricateDestroyMethod(that, name, instantiator, instance);
  14027. that[name] = instance;
  14028. }, null, function() {
  14029. delete that[inCreationMarker];
  14030. instantiator.pushUpcomingInstantiation();
  14031. });
  14032. }
  14033. else {
  14034. fluid.fail("Unrecognised material in place of subcomponent " + name + " - no \"type\" field found");
  14035. }
  14036. fluid.fireEvent(instance, "events.onAttach", [instance, name, that]);
  14037. }, [" while instantiating dependent component with name \"" + name + "\" with record ", component, " as child of ", that],
  14038. userInstantiator);
  14039. if (instance) {
  14040. fluid.log("Finished instantiation of component with name \"" + name + "\" and id " + instance.id + " as child of " + fluid.dumpThat(that));
  14041. }
  14042. };
  14043. // unsupported, non-API function
  14044. fluid.bindDeferredComponent = function(that, componentName, component, instantiator) {
  14045. var events = fluid.makeArray(component.createOnEvent);
  14046. fluid.each(events, function(eventName) {
  14047. that.events[eventName].addListener(function() {
  14048. fluid.log("Beginning instantiation of deferred component " + componentName + " due to event " + eventName);
  14049. if (that[componentName]) {
  14050. instantiator.clearComponent(that, componentName);
  14051. }
  14052. fluid.initDependent(that, componentName, instantiator);
  14053. }, null, null, component.priority);
  14054. });
  14055. };
  14056. // unsupported, non-API function
  14057. fluid.priorityForComponent = function(component) {
  14058. return component.priority? component.priority :
  14059. (component.type === "fluid.typeFount" || fluid.hasGrade(fluid.defaults(component.type), "fluid.typeFount"))?
  14060. "first" : undefined;
  14061. };
  14062. fluid.initDependents = function(that) {
  14063. var options = that.options;
  14064. var components = options.components || {};
  14065. var componentSort = {};
  14066. fluid.withInstantiator(that, function(instantiator) {
  14067. fluid.each(components, function(component, name) {
  14068. if (!component.createOnEvent) {
  14069. var priority = fluid.priorityForComponent(component);
  14070. componentSort[name] = {key: name, priority: fluid.event.mapPriority(priority, 0)};
  14071. }
  14072. else {
  14073. fluid.bindDeferredComponent(that, name, component, instantiator);
  14074. }
  14075. });
  14076. var componentList = fluid.event.sortListeners(componentSort);
  14077. fluid.each(componentList, function(entry) {
  14078. fluid.initDependent(that, entry.key);
  14079. });
  14080. var invokers = options.invokers || {};
  14081. for (var name in invokers) {
  14082. var invokerec = invokers[name];
  14083. var funcName = typeof(invokerec) === "string"? invokerec : null;
  14084. that[name] = fluid.withInstantiator(that, function(instantiator) {
  14085. fluid.log("Beginning instantiation of invoker with name \"" + name + "\" as child of " + fluid.dumpThat(that));
  14086. return fluid.makeInvoker(instantiator, that, funcName? null : invokerec, funcName);
  14087. }, [" while instantiating invoker with name \"" + name + "\" with record ", invokerec, " as child of ", that]); // jslint:ok
  14088. fluid.log("Finished instantiation of invoker with name \"" + name + "\" as child of " + fluid.dumpThat(that));
  14089. }
  14090. }, [" while instantiating dependent components for component " + that.typeName]);
  14091. };
  14092. fluid.staticEnvironment = fluid.typeTag("fluid.staticEnvironment");
  14093. fluid.staticEnvironment.environmentClass = fluid.typeTag("fluid.browser");
  14094. fluid.globalThreadLocal = fluid.threadLocal(function() {
  14095. return fluid.typeTag("fluid.dynamicEnvironment");
  14096. });
  14097. // Although the following two functions are unsupported and not part of the IoC
  14098. // implementation proper, they are still used in the renderer
  14099. // expander as well as in some old-style tests and various places in CSpace.
  14100. // unsupported, non-API function
  14101. fluid.withEnvironment = function(envAdd, func, root) {
  14102. root = root || fluid.globalThreadLocal();
  14103. return fluid.tryCatch(function() {
  14104. for (var key in envAdd) {
  14105. root[key] = envAdd[key];
  14106. }
  14107. $.extend(root, envAdd);
  14108. return func();
  14109. }, null, function() {
  14110. for (var key in envAdd) { // jslint:ok duplicate "value"
  14111. delete root[key]; // TODO: users may want a recursive "scoping" model
  14112. }
  14113. });
  14114. };
  14115. // unsupported, non-API function
  14116. fluid.makeEnvironmentFetcher = function(directModel, elResolver, envGetter) {
  14117. envGetter = envGetter || fluid.globalThreadLocal;
  14118. return function(parsed) {
  14119. var env = envGetter();
  14120. return fluid.fetchContextReference(parsed, directModel, env, elResolver);
  14121. };
  14122. };
  14123. // unsupported, non-API function
  14124. fluid.extractEL = function(string, options) {
  14125. if (options.ELstyle === "ALL") {
  14126. return string;
  14127. }
  14128. else if (options.ELstyle.length === 1) {
  14129. if (string.charAt(0) === options.ELstyle) {
  14130. return string.substring(1);
  14131. }
  14132. }
  14133. else if (options.ELstyle === "${}") {
  14134. var i1 = string.indexOf("${");
  14135. var i2 = string.lastIndexOf("}");
  14136. if (i1 === 0 && i2 !== -1) {
  14137. return string.substring(2, i2);
  14138. }
  14139. }
  14140. };
  14141. // unsupported, non-API function
  14142. fluid.extractELWithContext = function(string, options) {
  14143. var EL = fluid.extractEL(string, options);
  14144. if (EL && EL.charAt(0) === "{") {
  14145. return fluid.parseContextReference(EL, 0);
  14146. }
  14147. return EL? {path: EL} : EL;
  14148. };
  14149. fluid.parseContextReference = function(reference, index, delimiter) {
  14150. var endcpos = reference.indexOf("}", index + 1);
  14151. if (endcpos === -1) {
  14152. fluid.fail("Cannot parse context reference \"" + reference + "\": Malformed context reference without }");
  14153. }
  14154. var context = reference.substring(index + 1, endcpos);
  14155. var endpos = delimiter? reference.indexOf(delimiter, endcpos + 1) : reference.length;
  14156. var path = reference.substring(endcpos + 1, endpos);
  14157. if (path.charAt(0) === ".") {
  14158. path = path.substring(1);
  14159. }
  14160. return {context: context, path: path, endpos: endpos};
  14161. };
  14162. fluid.renderContextReference = function(parsed) {
  14163. return "{" + parsed.context + "}." + parsed.path;
  14164. };
  14165. fluid.fetchContextReference = function(parsed, directModel, env, elResolver) {
  14166. if (elResolver) {
  14167. parsed = elResolver(parsed, env);
  14168. }
  14169. var base = parsed.context? env[parsed.context] : directModel;
  14170. if (!base) {
  14171. return base;
  14172. }
  14173. return parsed.noDereference? parsed.path : fluid.get(base, parsed.path);
  14174. };
  14175. // unsupported, non-API function
  14176. fluid.resolveContextValue = function(string, options) {
  14177. if (options.bareContextRefs && string.charAt(0) === "{") {
  14178. var parsed = fluid.parseContextReference(string, 0);
  14179. return options.fetcher(parsed);
  14180. }
  14181. else if (options.ELstyle && options.ELstyle !== "${}") {
  14182. var parsed = fluid.extractELWithContext(string, options); // jslint:ok
  14183. if (parsed) {
  14184. return options.fetcher(parsed);
  14185. }
  14186. }
  14187. while (typeof(string) === "string") {
  14188. var i1 = string.indexOf("${");
  14189. var i2 = string.indexOf("}", i1 + 2);
  14190. if (i1 !== -1 && i2 !== -1) {
  14191. var parsed; // jslint:ok
  14192. if (string.charAt(i1 + 2) === "{") {
  14193. parsed = fluid.parseContextReference(string, i1 + 2, "}");
  14194. i2 = parsed.endpos;
  14195. }
  14196. else {
  14197. parsed = {path: string.substring(i1 + 2, i2)};
  14198. }
  14199. var subs = options.fetcher(parsed);
  14200. var all = (i1 === 0 && i2 === string.length - 1);
  14201. // TODO: test case for all undefined substitution
  14202. if (subs === undefined || subs === null) {
  14203. return subs;
  14204. }
  14205. string = all? subs : string.substring(0, i1) + subs + string.substring(i2 + 1);
  14206. }
  14207. else {
  14208. break;
  14209. }
  14210. }
  14211. return string;
  14212. };
  14213. fluid.resolveContextValue = fluid.wrapActivity(fluid.resolveContextValue,
  14214. [" while resolving context value ", "arguments.0"]);
  14215. function resolveEnvironmentImpl(obj, options) {
  14216. fluid.guardCircularity(options.seenIds, obj, "expansion",
  14217. " - please ensure options are not circularly connected, or protect from expansion using the \"noexpand\" policy or expander");
  14218. function recurse(arg) {
  14219. return resolveEnvironmentImpl(arg, options);
  14220. }
  14221. if (typeof(obj) === "string" && !options.noValue) {
  14222. return fluid.resolveContextValue(obj, options);
  14223. }
  14224. else if (fluid.isPrimitive(obj) || obj.nodeType !== undefined || obj.jquery) {
  14225. return obj;
  14226. }
  14227. else if (options.filter) {
  14228. return options.filter(obj, recurse, options);
  14229. }
  14230. else {
  14231. return (options.noCopy? fluid.each : fluid.transform)(obj, function(value, key) {
  14232. return resolveEnvironmentImpl(value, options);
  14233. });
  14234. }
  14235. }
  14236. fluid.defaults("fluid.resolveEnvironment", {
  14237. ELstyle: "${}",
  14238. seenIds: {},
  14239. bareContextRefs: true
  14240. });
  14241. fluid.resolveEnvironment = function(obj, options) {
  14242. // Don't create a component here since this function is itself used in the
  14243. // component expansion pathway - avoid all expansion in any case to head off FLUID-4301
  14244. options = $.extend({}, fluid.rawDefaults("fluid.resolveEnvironment"), options);
  14245. options.seenIds = {};
  14246. return resolveEnvironmentImpl(obj, options);
  14247. };
  14248. /** "light" expanders, starting with support functions for the "deferredFetcher" expander **/
  14249. fluid.expander.deferredCall = function(target, source, recurse) {
  14250. var expander = source.expander;
  14251. var args = (!expander.args || fluid.isArrayable(expander.args))? expander.args : fluid.makeArray(expander.args);
  14252. args = recurse(args);
  14253. return fluid.invokeGlobalFunction(expander.func, args);
  14254. };
  14255. fluid.deferredCall = fluid.expander.deferredCall; // put in top namespace for convenience
  14256. fluid.deferredInvokeCall = function(target, source, recurse) {
  14257. var expander = source.expander;
  14258. var args = (!expander.args || fluid.isArrayable(expander.args))? expander.args : fluid.makeArray(expander.args);
  14259. args = recurse(args);
  14260. return fluid.invoke(expander.func, args);
  14261. };
  14262. // The "noexpand" expander which simply unwraps one level of expansion and ceases.
  14263. fluid.expander.noexpand = function(target, source) {
  14264. return $.extend(target, source.expander.tree);
  14265. };
  14266. fluid.noexpand = fluid.expander.noexpand; // TODO: check naming and namespacing
  14267. // unsupported, non-API function
  14268. fluid.expander.lightFilter = function (obj, recurse, options) {
  14269. var togo;
  14270. if (fluid.isArrayable(obj)) {
  14271. togo = options.noCopy? obj : [];
  14272. fluid.each(obj, function(value, key) {togo[key] = recurse(value);});
  14273. }
  14274. else {
  14275. togo = options.noCopy? obj : {};
  14276. for (var key in obj) {
  14277. var value = obj[key];
  14278. var expander;
  14279. if (key === "expander" && !(options.expandOnly && options.expandOnly[value.type])) {
  14280. expander = fluid.getGlobalValue(value.type);
  14281. if (expander) {
  14282. return expander.call(null, togo, obj, recurse, options);
  14283. }
  14284. }
  14285. if (key !== "expander" || !expander) {
  14286. togo[key] = recurse(value);
  14287. }
  14288. }
  14289. }
  14290. return options.noCopy? obj : togo;
  14291. };
  14292. // unsupported, non-API function
  14293. fluid.expander.expandLight = function (source, expandOptions) {
  14294. var options = $.extend({}, expandOptions);
  14295. options.filter = fluid.expander.lightFilter;
  14296. return fluid.resolveEnvironment(source, options);
  14297. };
  14298. })(jQuery, fluid_1_5);
  14299. /*
  14300. Copyright 2010-2011 OCAD University
  14301. Copyright 2010-2011 Lucendo Development Ltd.
  14302. Licensed under the Educational Community License (ECL), Version 2.0 or the New
  14303. BSD license. You may not use this file except in compliance with one these
  14304. Licenses.
  14305. You may obtain a copy of the ECL 2.0 License and BSD License at
  14306. https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt
  14307. */
  14308. // Declare dependencies
  14309. /*global fluid_1_5:true, jQuery*/
  14310. // JSLint options
  14311. /*jslint white: true, funcinvoke: true, undef: true, newcap: true, nomen: true, regexp: true, bitwise: true, browser: true, forin: true, maxerr: 100, indent: 4 */
  14312. var fluid_1_5 = fluid_1_5 || {};
  14313. (function ($, fluid) {
  14314. /** Framework-global caching state for fluid.fetchResources **/
  14315. var resourceCache = {};
  14316. var pendingClass = {};
  14317. /** Accepts a hash of structures with free keys, where each entry has either
  14318. * href/url or nodeId set - on completion, callback will be called with the populated
  14319. * structure with fetched resource text in the field "resourceText" for each
  14320. * entry. Each structure may contain "options" holding raw options to be forwarded
  14321. * to jQuery.ajax().
  14322. */
  14323. fluid.fetchResources = function(resourceSpecs, callback, options) {
  14324. var that = fluid.initLittleComponent("fluid.fetchResources", options);
  14325. that.resourceSpecs = resourceSpecs;
  14326. that.callback = callback;
  14327. that.operate = function() {
  14328. fluid.fetchResources.fetchResourcesImpl(that);
  14329. };
  14330. fluid.each(resourceSpecs, function(resourceSpec, key) {
  14331. resourceSpec.recurseFirer = fluid.event.getEventFirer(null, null, "I/O completion for resource \"" + key + "\"");
  14332. resourceSpec.recurseFirer.addListener(that.operate);
  14333. if (resourceSpec.url && !resourceSpec.href) {
  14334. resourceSpec.href = resourceSpec.url;
  14335. }
  14336. });
  14337. if (that.options.amalgamateClasses) {
  14338. fluid.fetchResources.amalgamateClasses(resourceSpecs, that.options.amalgamateClasses, that.operate);
  14339. }
  14340. that.operate();
  14341. return that;
  14342. };
  14343. /*
  14344. * This function is unsupported: It is not really intended for use by implementors.
  14345. */
  14346. // Add "synthetic" elements of *this* resourceSpec list corresponding to any
  14347. // still pending elements matching the PROLEPTICK CLASS SPECIFICATION supplied
  14348. fluid.fetchResources.amalgamateClasses = function(specs, classes, operator) {
  14349. fluid.each(classes, function(clazz) {
  14350. var pending = pendingClass[clazz];
  14351. fluid.each(pending, function(pendingrec, canon) {
  14352. specs[clazz+"!"+canon] = pendingrec;
  14353. pendingrec.recurseFirer.addListener(operator);
  14354. });
  14355. });
  14356. };
  14357. /*
  14358. * This function is unsupported: It is not really intended for use by implementors.
  14359. */
  14360. fluid.fetchResources.timeSuccessCallback = function(resourceSpec) {
  14361. if (resourceSpec.timeSuccess && resourceSpec.options && resourceSpec.options.success) {
  14362. var success = resourceSpec.options.success;
  14363. resourceSpec.options.success = function() {
  14364. var startTime = new Date();
  14365. var ret = success.apply(null, arguments);
  14366. fluid.log("External callback for URL " + resourceSpec.href + " completed - callback time: " +
  14367. (new Date().getTime() - startTime.getTime()) + "ms");
  14368. return ret;
  14369. };
  14370. }
  14371. };
  14372. // TODO: Integrate punch-through from old Engage implementation
  14373. function canonUrl(url) {
  14374. return url;
  14375. }
  14376. fluid.fetchResources.clearResourceCache = function(url) {
  14377. if (url) {
  14378. delete resourceCache[canonUrl(url)];
  14379. }
  14380. else {
  14381. fluid.clear(resourceCache);
  14382. }
  14383. };
  14384. /*
  14385. * This function is unsupported: It is not really intended for use by implementors.
  14386. */
  14387. fluid.fetchResources.handleCachedRequest = function(resourceSpec, response) {
  14388. var canon = canonUrl(resourceSpec.href);
  14389. var cached = resourceCache[canon];
  14390. if (cached.$$firer$$) {
  14391. fluid.log("Handling request for " + canon + " from cache");
  14392. var fetchClass = resourceSpec.fetchClass;
  14393. if (fetchClass && pendingClass[fetchClass]) {
  14394. fluid.log("Clearing pendingClass entry for class " + fetchClass);
  14395. delete pendingClass[fetchClass][canon];
  14396. }
  14397. resourceCache[canon] = response;
  14398. cached.fire(response);
  14399. }
  14400. };
  14401. /*
  14402. * This function is unsupported: It is not really intended for use by implementors.
  14403. */
  14404. fluid.fetchResources.completeRequest = function(thisSpec, recurseCall) {
  14405. thisSpec.queued = false;
  14406. thisSpec.completeTime = new Date();
  14407. fluid.log("Request to URL " + thisSpec.href + " completed - total elapsed time: " +
  14408. (thisSpec.completeTime.getTime() - thisSpec.initTime.getTime()) + "ms");
  14409. thisSpec.recurseFirer.fire();
  14410. };
  14411. /*
  14412. * This function is unsupported: It is not really intended for use by implementors.
  14413. */
  14414. fluid.fetchResources.makeResourceCallback = function(thisSpec) {
  14415. return {
  14416. success: function(response) {
  14417. thisSpec.resourceText = response;
  14418. thisSpec.resourceKey = thisSpec.href;
  14419. if (thisSpec.forceCache) {
  14420. fluid.fetchResources.handleCachedRequest(thisSpec, response);
  14421. }
  14422. fluid.fetchResources.completeRequest(thisSpec);
  14423. },
  14424. error: function(response, textStatus, errorThrown) {
  14425. thisSpec.fetchError = {
  14426. status: response.status,
  14427. textStatus: response.textStatus,
  14428. errorThrown: errorThrown
  14429. };
  14430. fluid.fetchResources.completeRequest(thisSpec);
  14431. }
  14432. };
  14433. };
  14434. /*
  14435. * This function is unsupported: It is not really intended for use by implementors.
  14436. */
  14437. fluid.fetchResources.issueCachedRequest = function(resourceSpec, options) {
  14438. var canon = canonUrl(resourceSpec.href);
  14439. var cached = resourceCache[canon];
  14440. if (!cached) {
  14441. fluid.log("First request for cached resource with url " + canon);
  14442. cached = fluid.event.getEventFirer(null, null, "cache notifier for resource URL " + canon);
  14443. cached.$$firer$$ = true;
  14444. resourceCache[canon] = cached;
  14445. var fetchClass = resourceSpec.fetchClass;
  14446. if (fetchClass) {
  14447. if (!pendingClass[fetchClass]) {
  14448. pendingClass[fetchClass] = {};
  14449. }
  14450. pendingClass[fetchClass][canon] = resourceSpec;
  14451. }
  14452. options.cache = false; // TODO: Getting weird "not modified" issues on Firefox
  14453. $.ajax(options);
  14454. }
  14455. else {
  14456. if (!cached.$$firer$$) {
  14457. options.success(cached);
  14458. }
  14459. else {
  14460. fluid.log("Request for cached resource which is in flight: url " + canon);
  14461. cached.addListener(function(response) {
  14462. options.success(response);
  14463. });
  14464. }
  14465. }
  14466. };
  14467. /*
  14468. * This function is unsupported: It is not really intended for use by implementors.
  14469. */
  14470. // Compose callbacks in such a way that the 2nd, marked "external" will be applied
  14471. // first if it exists, but in all cases, the first, marked internal, will be
  14472. // CALLED WITHOUT FAIL
  14473. fluid.fetchResources.composeCallbacks = function(internal, external) {
  14474. return external? function() {
  14475. try {
  14476. external.apply(null, arguments);
  14477. }
  14478. catch (e) {
  14479. fluid.log("Exception applying external fetchResources callback: " + e);
  14480. }
  14481. internal.apply(null, arguments); // call the internal callback without fail
  14482. } : internal;
  14483. };
  14484. /*
  14485. * This function is unsupported: It is not really intended for use by implementors.
  14486. */
  14487. fluid.fetchResources.composePolicy = function(target, source, key) {
  14488. return fluid.fetchResources.composeCallbacks(target, source);
  14489. };
  14490. fluid.defaults("fluid.fetchResources.issueRequest", {
  14491. mergePolicy: {
  14492. success: fluid.fetchResources.composePolicy,
  14493. error: fluid.fetchResources.composePolicy,
  14494. url: "reverse"
  14495. }
  14496. });
  14497. /*
  14498. * This function is unsupported: It is not really intended for use by implementors.
  14499. */
  14500. fluid.fetchResources.issueRequest = function(resourceSpec, key) {
  14501. var thisCallback = fluid.fetchResources.makeResourceCallback(resourceSpec);
  14502. var options = {
  14503. url: resourceSpec.href,
  14504. success: thisCallback.success,
  14505. error: thisCallback.error,
  14506. dataType: "text"};
  14507. fluid.fetchResources.timeSuccessCallback(resourceSpec);
  14508. fluid.merge(fluid.defaults("fluid.fetchResources.issueRequest").mergePolicy,
  14509. options, resourceSpec.options);
  14510. resourceSpec.queued = true;
  14511. resourceSpec.initTime = new Date();
  14512. fluid.log("Request with key " + key + " queued for " + resourceSpec.href);
  14513. if (resourceSpec.forceCache) {
  14514. fluid.fetchResources.issueCachedRequest(resourceSpec, options);
  14515. }
  14516. else {
  14517. $.ajax(options);
  14518. }
  14519. };
  14520. fluid.fetchResources.fetchResourcesImpl = function(that) {
  14521. var complete = true;
  14522. var allSync = true;
  14523. var resourceSpecs = that.resourceSpecs;
  14524. for (var key in resourceSpecs) {
  14525. var resourceSpec = resourceSpecs[key];
  14526. if (!resourceSpec.options || resourceSpec.options.async) {
  14527. allSync = false;
  14528. }
  14529. if (resourceSpec.href && !resourceSpec.completeTime) {
  14530. if (!resourceSpec.queued) {
  14531. fluid.fetchResources.issueRequest(resourceSpec, key);
  14532. }
  14533. if (resourceSpec.queued) {
  14534. complete = false;
  14535. }
  14536. }
  14537. else if (resourceSpec.nodeId && !resourceSpec.resourceText) {
  14538. var node = document.getElementById(resourceSpec.nodeId);
  14539. // upgrade this to somehow detect whether node is "armoured" somehow
  14540. // with comment or CDATA wrapping
  14541. resourceSpec.resourceText = fluid.dom.getElementText(node);
  14542. resourceSpec.resourceKey = resourceSpec.nodeId;
  14543. }
  14544. }
  14545. if (complete && that.callback && !that.callbackCalled) {
  14546. that.callbackCalled = true;
  14547. if ($.browser.mozilla && !allSync) {
  14548. // Defer this callback to avoid debugging problems on Firefox
  14549. setTimeout(function() {
  14550. that.callback(resourceSpecs);
  14551. }, 1);
  14552. }
  14553. else {
  14554. that.callback(resourceSpecs);
  14555. }
  14556. }
  14557. };
  14558. fluid.fetchResources.primeCacheFromResources = function(componentName) {
  14559. var resources = fluid.defaults(componentName).resources;
  14560. var that = {typeName: "fluid.fetchResources.primeCacheFromResources"};
  14561. var expanded = (fluid.expandOptions ? fluid.expandOptions : fluid.identity)(fluid.copy(resources), that);
  14562. fluid.fetchResources(expanded);
  14563. };
  14564. /** Utilities invoking requests for expansion **/
  14565. fluid.registerNamespace("fluid.expander");
  14566. /*
  14567. * This function is unsupported: It is not really intended for use by implementors.
  14568. */
  14569. fluid.expander.makeDefaultFetchOptions = function (successdisposer, failid, options) {
  14570. return $.extend(true, {dataType: "text"}, options, {
  14571. success: function(response, environmentdisposer) {
  14572. var json = JSON.parse(response);
  14573. environmentdisposer(successdisposer(json));
  14574. },
  14575. error: function(response, textStatus) {
  14576. fluid.log("Error fetching " + failid + ": " + textStatus);
  14577. }
  14578. });
  14579. };
  14580. /*
  14581. * This function is unsupported: It is not really intended for use by implementors.
  14582. */
  14583. fluid.expander.makeFetchExpander = function (options) {
  14584. return { expander: {
  14585. type: "fluid.expander.deferredFetcher",
  14586. href: options.url,
  14587. options: fluid.expander.makeDefaultFetchOptions(options.disposer, options.url, options.options),
  14588. resourceSpecCollector: "{resourceSpecCollector}",
  14589. fetchKey: options.fetchKey
  14590. }};
  14591. };
  14592. fluid.expander.deferredFetcher = function(target, source, recurse, expandOptions) {
  14593. var expander = source.expander;
  14594. var spec = fluid.copy(expander);
  14595. // fetch the "global" collector specified in the external environment to receive
  14596. // this resourceSpec
  14597. var collector = fluid.resolveEnvironment(expander.resourceSpecCollector, expandOptions);
  14598. delete spec.type;
  14599. delete spec.resourceSpecCollector;
  14600. delete spec.fetchKey;
  14601. var environmentdisposer = function(disposed) {
  14602. $.extend(target, disposed);
  14603. };
  14604. // replace the callback which is there (taking 2 arguments) with one which
  14605. // directly responds to the request, passing in the result and OUR "disposer" -
  14606. // which once the user has processed the response (say, parsing JSON and repackaging)
  14607. // finally deposits it in the place of the expander in the tree to which this reference
  14608. // has been stored at the point this expander was evaluated.
  14609. spec.options.success = function(response) {
  14610. expander.options.success(response, environmentdisposer);
  14611. };
  14612. var key = expander.fetchKey || fluid.allocateGuid();
  14613. collector[key] = spec;
  14614. return target;
  14615. };
  14616. })(jQuery, fluid_1_5);
  14617. // =========================================================================
  14618. //
  14619. // tinyxmlsax.js - an XML SAX parser in JavaScript compressed for downloading
  14620. //
  14621. // version 3.1
  14622. //
  14623. // =========================================================================
  14624. //
  14625. // Copyright (C) 2000 - 2002, 2003 Michael Houghton (mike@idle.org), Raymond Irving and David Joham (djoham@yahoo.com)
  14626. //
  14627. // This library is free software; you can redistribute it and/or
  14628. // modify it under the terms of the GNU Lesser General Public
  14629. // License as published by the Free Software Foundation; either
  14630. // version 2.1 of the License, or (at your option) any later version.
  14631. // This library is distributed in the hope that it will be useful,
  14632. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  14633. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14634. // Lesser General Public License for more details.
  14635. // You should have received a copy of the GNU Lesser General Public
  14636. // License along with this library; if not, write to the Free Software
  14637. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  14638. //
  14639. // Visit the XML for <SCRIPT> home page at http://xmljs.sourceforge.net
  14640. //
  14641. /*
  14642. The zlib/libpng License
  14643. Copyright (c) 2000 - 2002, 2003 Michael Houghton (mike@idle.org), Raymond Irving and David Joham (djoham@yahoo.com)
  14644. This software is provided 'as-is', without any express or implied
  14645. warranty. In no event will the authors be held liable for any damages
  14646. arising from the use of this software.
  14647. Permission is granted to anyone to use this software for any purpose,
  14648. including commercial applications, and to alter it and redistribute it
  14649. freely, subject to the following restrictions:
  14650. 1. The origin of this software must not be misrepresented; you must not
  14651. claim that you wrote the original software. If you use this software
  14652. in a product, an acknowledgment in the product documentation would be
  14653. appreciated but is not required.
  14654. 2. Altered source versions must be plainly marked as such, and must not be
  14655. misrepresented as being the original software.
  14656. 3. This notice may not be removed or altered from any source
  14657. distribution.
  14658. */
  14659. // Declare dependencies
  14660. /*global fluid_1_5:true, jQuery*/
  14661. // JSLint options
  14662. /*jslint white: true, funcinvoke: true, undef: true, newcap: true, nomen: true, regexp: true, bitwise: true, browser: true, forin: true, maxerr: 100, indent: 4 */
  14663. var fluid_1_5 = fluid_1_5 || {};
  14664. (function ($, fluid) {
  14665. fluid.XMLP = function(strXML) {
  14666. return fluid.XMLP.XMLPImpl(strXML);
  14667. };
  14668. // List of closed HTML tags, taken from JQuery 1.2.3
  14669. fluid.XMLP.closedTags = {
  14670. abbr: true, br: true, col: true, img: true, input: true,
  14671. link: true, meta: true, param: true, hr: true, area: true, embed:true
  14672. };
  14673. fluid.XMLP._NONE = 0;
  14674. fluid.XMLP._ELM_B = 1;
  14675. fluid.XMLP._ELM_E = 2;
  14676. fluid.XMLP._ELM_EMP = 3;
  14677. fluid.XMLP._ATT = 4;
  14678. fluid.XMLP._TEXT = 5;
  14679. fluid.XMLP._ENTITY = 6;
  14680. fluid.XMLP._PI = 7;
  14681. fluid.XMLP._CDATA = 8;
  14682. fluid.XMLP._COMMENT = 9;
  14683. fluid.XMLP._DTD = 10;
  14684. fluid.XMLP._ERROR = 11;
  14685. fluid.XMLP._CONT_XML = 0;
  14686. fluid.XMLP._CONT_ALT = 1;
  14687. fluid.XMLP._ATT_NAME = 0;
  14688. fluid.XMLP._ATT_VAL = 1;
  14689. fluid.XMLP._STATE_PROLOG = 1;
  14690. fluid.XMLP._STATE_DOCUMENT = 2;
  14691. fluid.XMLP._STATE_MISC = 3;
  14692. fluid.XMLP._errs = [];
  14693. fluid.XMLP._errs[fluid.XMLP.ERR_CLOSE_PI = 0 ] = "PI: missing closing sequence";
  14694. fluid.XMLP._errs[fluid.XMLP.ERR_CLOSE_DTD = 1 ] = "DTD: missing closing sequence";
  14695. fluid.XMLP._errs[fluid.XMLP.ERR_CLOSE_COMMENT = 2 ] = "Comment: missing closing sequence";
  14696. fluid.XMLP._errs[fluid.XMLP.ERR_CLOSE_CDATA = 3 ] = "CDATA: missing closing sequence";
  14697. fluid.XMLP._errs[fluid.XMLP.ERR_CLOSE_ELM = 4 ] = "Element: missing closing sequence";
  14698. fluid.XMLP._errs[fluid.XMLP.ERR_CLOSE_ENTITY = 5 ] = "Entity: missing closing sequence";
  14699. fluid.XMLP._errs[fluid.XMLP.ERR_PI_TARGET = 6 ] = "PI: target is required";
  14700. fluid.XMLP._errs[fluid.XMLP.ERR_ELM_EMPTY = 7 ] = "Element: cannot be both empty and closing";
  14701. fluid.XMLP._errs[fluid.XMLP.ERR_ELM_NAME = 8 ] = "Element: name must immediatly follow \"<\"";
  14702. fluid.XMLP._errs[fluid.XMLP.ERR_ELM_LT_NAME = 9 ] = "Element: \"<\" not allowed in element names";
  14703. fluid.XMLP._errs[fluid.XMLP.ERR_ATT_VALUES = 10] = "Attribute: values are required and must be in quotes";
  14704. fluid.XMLP._errs[fluid.XMLP.ERR_ATT_LT_NAME = 11] = "Element: \"<\" not allowed in attribute names";
  14705. fluid.XMLP._errs[fluid.XMLP.ERR_ATT_LT_VALUE = 12] = "Attribute: \"<\" not allowed in attribute values";
  14706. fluid.XMLP._errs[fluid.XMLP.ERR_ATT_DUP = 13] = "Attribute: duplicate attributes not allowed";
  14707. fluid.XMLP._errs[fluid.XMLP.ERR_ENTITY_UNKNOWN = 14] = "Entity: unknown entity";
  14708. fluid.XMLP._errs[fluid.XMLP.ERR_INFINITELOOP = 15] = "Infinite loop";
  14709. fluid.XMLP._errs[fluid.XMLP.ERR_DOC_STRUCTURE = 16] = "Document: only comments, processing instructions, or whitespace allowed outside of document element";
  14710. fluid.XMLP._errs[fluid.XMLP.ERR_ELM_NESTING = 17] = "Element: must be nested correctly";
  14711. fluid.XMLP._checkStructure = function(that, iEvent) {
  14712. var stack = that.m_stack;
  14713. if (fluid.XMLP._STATE_PROLOG == that.m_iState) {
  14714. // disabled original check for text node in prologue
  14715. that.m_iState = fluid.XMLP._STATE_DOCUMENT;
  14716. }
  14717. if (fluid.XMLP._STATE_DOCUMENT === that.m_iState) {
  14718. if ((fluid.XMLP._ELM_B == iEvent) || (fluid.XMLP._ELM_EMP == iEvent)) {
  14719. that.m_stack[stack.length] = that.getName();
  14720. }
  14721. if ((fluid.XMLP._ELM_E == iEvent) || (fluid.XMLP._ELM_EMP == iEvent)) {
  14722. if (stack.length === 0) {
  14723. //return this._setErr(XMLP.ERR_DOC_STRUCTURE);
  14724. return fluid.XMLP._NONE;
  14725. }
  14726. var strTop = stack[stack.length - 1];
  14727. that.m_stack.length--;
  14728. if (strTop === null || strTop !== that.getName()) {
  14729. return that._setErr(that, fluid.XMLP.ERR_ELM_NESTING);
  14730. }
  14731. }
  14732. // disabled original check for text node in epilogue - "MISC" state is disused
  14733. }
  14734. return iEvent;
  14735. };
  14736. fluid.XMLP._parseCDATA = function(that, iB) {
  14737. var iE = that.m_xml.indexOf("]]>", iB);
  14738. if (iE == -1) { return fluid.XMLP._setErr(that, fluid.XMLP.ERR_CLOSE_CDATA);}
  14739. fluid.XMLP._setContent(that, fluid.XMLP._CONT_XML, iB, iE);
  14740. that.m_iP = iE + 3;
  14741. return fluid.XMLP._CDATA;
  14742. };
  14743. fluid.XMLP._parseComment = function(that, iB) {
  14744. var iE = that.m_xml.indexOf("-" + "->", iB);
  14745. if (iE == -1) {
  14746. return fluid.XMLP._setErr(that, fluid.XMLP.ERR_CLOSE_COMMENT);
  14747. }
  14748. fluid.XMLP._setContent(that, fluid.XMLP._CONT_XML, iB - 4, iE + 3);
  14749. that.m_iP = iE + 3;
  14750. return fluid.XMLP._COMMENT;
  14751. };
  14752. fluid.XMLP._parseDTD = function(that, iB) {
  14753. var iE, strClose, iInt, iLast;
  14754. iE = that.m_xml.indexOf(">", iB);
  14755. if (iE == -1) {
  14756. return fluid.XMLP._setErr(that, fluid.XMLP.ERR_CLOSE_DTD);
  14757. }
  14758. iInt = that.m_xml.indexOf("[", iB);
  14759. strClose = ((iInt != -1) && (iInt < iE)) ? "]>" : ">";
  14760. while (true) {
  14761. if (iE == iLast) {
  14762. return fluid.XMLP._setErr(that, fluid.XMLP.ERR_INFINITELOOP);
  14763. }
  14764. iLast = iE;
  14765. iE = that.m_xml.indexOf(strClose, iB);
  14766. if(iE == -1) {
  14767. return fluid.XMLP._setErr(that, fluid.XMLP.ERR_CLOSE_DTD);
  14768. }
  14769. if (that.m_xml.substring(iE - 1, iE + 2) != "]]>") { break;}
  14770. }
  14771. that.m_iP = iE + strClose.length;
  14772. return fluid.XMLP._DTD;
  14773. };
  14774. fluid.XMLP._parsePI = function(that, iB) {
  14775. var iE, iTB, iTE, iCB, iCE;
  14776. iE = that.m_xml.indexOf("?>", iB);
  14777. if (iE == -1) { return fluid.XMLP._setErr(that, fluid.XMLP.ERR_CLOSE_PI);}
  14778. iTB = fluid.SAXStrings.indexOfNonWhitespace(that.m_xml, iB, iE);
  14779. if (iTB == -1) { return fluid.XMLP._setErr(that, fluid.XMLP.ERR_PI_TARGET);}
  14780. iTE = fluid.SAXStrings.indexOfWhitespace(that.m_xml, iTB, iE);
  14781. if (iTE == -1) { iTE = iE;}
  14782. iCB = fluid.SAXStrings.indexOfNonWhitespace(that.m_xml, iTE, iE);
  14783. if (iCB == -1) { iCB = iE;}
  14784. iCE = fluid.SAXStrings.lastIndexOfNonWhitespace(that.m_xml, iCB, iE);
  14785. if (iCE == -1) { iCE = iE - 1;}
  14786. that.m_name = that.m_xml.substring(iTB, iTE);
  14787. fluid.XMLP._setContent(that, fluid.XMLP._CONT_XML, iCB, iCE + 1);
  14788. that.m_iP = iE + 2;
  14789. return fluid.XMLP._PI;
  14790. };
  14791. fluid.XMLP._parseText = function(that, iB) {
  14792. var iE = that.m_xml.indexOf("<", iB);
  14793. if (iE == -1) { iE = that.m_xml.length;}
  14794. fluid.XMLP._setContent(that, fluid.XMLP._CONT_XML, iB, iE);
  14795. that.m_iP = iE;
  14796. return fluid.XMLP._TEXT;
  14797. };
  14798. fluid.XMLP._setContent = function(that, iSrc) {
  14799. var args = arguments;
  14800. if (fluid.XMLP._CONT_XML == iSrc) {
  14801. that.m_cAlt = null;
  14802. that.m_cB = args[2];
  14803. that.m_cE = args[3];
  14804. }
  14805. else {
  14806. that.m_cAlt = args[2];
  14807. that.m_cB = 0;
  14808. that.m_cE = args[2].length;
  14809. }
  14810. that.m_cSrc = iSrc;
  14811. };
  14812. fluid.XMLP._setErr = function(that, iErr) {
  14813. var strErr = fluid.XMLP._errs[iErr];
  14814. that.m_cAlt = strErr;
  14815. that.m_cB = 0;
  14816. that.m_cE = strErr.length;
  14817. that.m_cSrc = fluid.XMLP._CONT_ALT;
  14818. return fluid.XMLP._ERROR;
  14819. };
  14820. fluid.XMLP._parseElement = function(that, iB) {
  14821. var iE, iDE, iRet;
  14822. var iType, strN, iLast;
  14823. iDE = iE = that.m_xml.indexOf(">", iB);
  14824. if (iE == -1) {
  14825. return that._setErr(that, fluid.XMLP.ERR_CLOSE_ELM);
  14826. }
  14827. if (that.m_xml.charAt(iB) == "/") {
  14828. iType = fluid.XMLP._ELM_E;
  14829. iB++;
  14830. }
  14831. else {
  14832. iType = fluid.XMLP._ELM_B;
  14833. }
  14834. if (that.m_xml.charAt(iE - 1) == "/") {
  14835. if (iType == fluid.XMLP._ELM_E) {
  14836. return fluid.XMLP._setErr(that, fluid.XMLP.ERR_ELM_EMPTY);
  14837. }
  14838. iType = fluid.XMLP._ELM_EMP; iDE--;
  14839. }
  14840. that.nameRegex.lastIndex = iB;
  14841. var nameMatch = that.nameRegex.exec(that.m_xml);
  14842. if (!nameMatch) {
  14843. return fluid.XMLP._setErr(that, fluid.XMLP.ERR_ELM_NAME);
  14844. }
  14845. strN = nameMatch[1].toLowerCase();
  14846. // This branch is specially necessary for broken markup in IE. If we see an li
  14847. // tag apparently directly nested in another, first emit a synthetic close tag
  14848. // for the earlier one without advancing the pointer, and set a flag to ensure
  14849. // doing this just once.
  14850. if ("li" === strN && iType !== fluid.XMLP._ELM_E && that.m_stack.length > 0 &&
  14851. that.m_stack[that.m_stack.length - 1] === "li" && !that.m_emitSynthetic) {
  14852. that.m_name = "li";
  14853. that.m_emitSynthetic = true;
  14854. return fluid.XMLP._ELM_E;
  14855. }
  14856. // We have acquired the tag name, now set about parsing any attribute list
  14857. that.m_attributes = {};
  14858. that.m_cAlt = "";
  14859. if (that.nameRegex.lastIndex < iDE) {
  14860. that.m_iP = that.nameRegex.lastIndex;
  14861. while (that.m_iP < iDE) {
  14862. that.attrStartRegex.lastIndex = that.m_iP;
  14863. var attrMatch = that.attrStartRegex.exec(that.m_xml);
  14864. if (!attrMatch) {
  14865. return fluid.XMLP._setErr(that, fluid.XMLP.ERR_ATT_VALUES);
  14866. }
  14867. var attrname = attrMatch[1].toLowerCase();
  14868. var attrval;
  14869. if (that.m_xml.charCodeAt(that.attrStartRegex.lastIndex) === 61) { // =
  14870. var valRegex = that.m_xml.charCodeAt(that.attrStartRegex.lastIndex + 1) === 34? that.attrValRegex : that.attrValIERegex; // "
  14871. valRegex.lastIndex = that.attrStartRegex.lastIndex + 1;
  14872. attrMatch = valRegex.exec(that.m_xml);
  14873. if (!attrMatch) {
  14874. return fluid.XMLP._setErr(that, fluid.XMLP.ERR_ATT_VALUES);
  14875. }
  14876. attrval = attrMatch[1];
  14877. }
  14878. else { // accommodate insanity on unvalued IE attributes
  14879. attrval = attrname;
  14880. valRegex = that.attrStartRegex;
  14881. }
  14882. if (!that.m_attributes[attrname]) {
  14883. that.m_attributes[attrname] = attrval;
  14884. }
  14885. else {
  14886. return fluid.XMLP._setErr(that, fluid.XMLP.ERR_ATT_DUP);
  14887. }
  14888. that.m_iP = valRegex.lastIndex;
  14889. }
  14890. }
  14891. if (strN.indexOf("<") != -1) {
  14892. return fluid.XMLP._setErr(that, fluid.XMLP.ERR_ELM_LT_NAME);
  14893. }
  14894. that.m_name = strN;
  14895. that.m_iP = iE + 1;
  14896. // Check for corrupted "closed tags" from innerHTML
  14897. if (fluid.XMLP.closedTags[strN]) {
  14898. that.closeRegex.lastIndex = iE + 1;
  14899. var closeMatch = that.closeRegex.exec;
  14900. if (closeMatch) {
  14901. var matchclose = that.m_xml.indexOf(strN, closeMatch.lastIndex);
  14902. if (matchclose === closeMatch.lastIndex) {
  14903. return iType; // bail out, a valid close tag is separated only by whitespace
  14904. }
  14905. else {
  14906. return fluid.XMLP._ELM_EMP;
  14907. }
  14908. }
  14909. }
  14910. that.m_emitSynthetic = false;
  14911. return iType;
  14912. };
  14913. fluid.XMLP._parse = function(that) {
  14914. var iP = that.m_iP;
  14915. var xml = that.m_xml;
  14916. if (iP === xml.length) { return fluid.XMLP._NONE;}
  14917. var c = xml.charAt(iP);
  14918. if (c === '<') {
  14919. var c2 = xml.charAt(iP + 1);
  14920. if (c2 === '?') {
  14921. return fluid.XMLP._parsePI(that, iP + 2);
  14922. }
  14923. else if (c2 === '!') {
  14924. if (iP === xml.indexOf("<!DOCTYPE", iP)) {
  14925. return fluid.XMLP._parseDTD(that, iP + 9);
  14926. }
  14927. else if (iP === xml.indexOf("<!--", iP)) {
  14928. return fluid.XMLP._parseComment(that, iP + 4);
  14929. }
  14930. else if (iP === xml.indexOf("<![CDATA[", iP)) {
  14931. return fluid.XMLP._parseCDATA(that, iP + 9);
  14932. }
  14933. }
  14934. else {
  14935. return fluid.XMLP._parseElement(that, iP + 1);
  14936. }
  14937. }
  14938. else {
  14939. return fluid.XMLP._parseText(that, iP);
  14940. }
  14941. };
  14942. fluid.XMLP.XMLPImpl = function(strXML) {
  14943. var that = {};
  14944. that.m_xml = strXML;
  14945. that.m_iP = 0;
  14946. that.m_iState = fluid.XMLP._STATE_PROLOG;
  14947. that.m_stack = [];
  14948. that.m_attributes = {};
  14949. that.m_emitSynthetic = false; // state used for emitting synthetic tags used to correct broken markup (IE)
  14950. that.getColumnNumber = function() {
  14951. return fluid.SAXStrings.getColumnNumber(that.m_xml, that.m_iP);
  14952. };
  14953. that.getContent = function() {
  14954. return (that.m_cSrc == fluid.XMLP._CONT_XML) ? that.m_xml : that.m_cAlt;
  14955. };
  14956. that.getContentBegin = function() { return that.m_cB;};
  14957. that.getContentEnd = function() { return that.m_cE;};
  14958. that.getLineNumber = function() {
  14959. return fluid.SAXStrings.getLineNumber(that.m_xml, that.m_iP);
  14960. };
  14961. that.getName = function() {
  14962. return that.m_name;
  14963. };
  14964. that.next = function() {
  14965. return fluid.XMLP._checkStructure(that, fluid.XMLP._parse(that));
  14966. };
  14967. that.nameRegex = /([^\s\/>]+)/g;
  14968. that.attrStartRegex = /\s*([\w:_][\w:_\-\.]*)/gm;
  14969. that.attrValRegex = /\"([^\"]*)\"\s*/gm; // "normal" XHTML attribute values
  14970. that.attrValIERegex = /([^\>\s]+)\s*/gm; // "stupid" unquoted IE attribute values (sometimes)
  14971. that.closeRegex = /\s*<\//g;
  14972. return that;
  14973. };
  14974. fluid.SAXStrings = {};
  14975. fluid.SAXStrings.WHITESPACE = " \t\n\r";
  14976. fluid.SAXStrings.QUOTES = "\"'";
  14977. fluid.SAXStrings.getColumnNumber = function (strD, iP) {
  14978. if (!strD) { return -1;}
  14979. iP = iP || strD.length;
  14980. var arrD = strD.substring(0, iP).split("\n");
  14981. arrD.length--;
  14982. var iLinePos = arrD.join("\n").length;
  14983. return iP - iLinePos;
  14984. };
  14985. fluid.SAXStrings.getLineNumber = function (strD, iP) {
  14986. if (!strD) { return -1;}
  14987. iP = iP || strD.length;
  14988. return strD.substring(0, iP).split("\n").length;
  14989. };
  14990. fluid.SAXStrings.indexOfNonWhitespace = function (strD, iB, iE) {
  14991. if (!strD) return -1;
  14992. iB = iB || 0;
  14993. iE = iE || strD.length;
  14994. for (var i = iB; i < iE; ++ i) {
  14995. var c = strD.charAt(i);
  14996. if (c !== ' ' && c !== '\t' && c !== '\n' && c !== '\r') return i;
  14997. }
  14998. return -1;
  14999. };
  15000. fluid.SAXStrings.indexOfWhitespace = function (strD, iB, iE) {
  15001. if (!strD) { return -1;}
  15002. iB = iB || 0;
  15003. iE = iE || strD.length;
  15004. for (var i = iB; i < iE; i++) {
  15005. if (fluid.SAXStrings.WHITESPACE.indexOf(strD.charAt(i)) != -1) { return i;}
  15006. }
  15007. return -1;
  15008. };
  15009. fluid.SAXStrings.lastIndexOfNonWhitespace = function (strD, iB, iE) {
  15010. if (!strD) { return -1;}
  15011. iB = iB || 0; iE = iE || strD.length;
  15012. for (var i = iE - 1; i >= iB; i--) {
  15013. if (fluid.SAXStrings.WHITESPACE.indexOf(strD.charAt(i)) == -1) {
  15014. return i;
  15015. }
  15016. }
  15017. return -1;
  15018. };
  15019. fluid.SAXStrings.replace = function(strD, iB, iE, strF, strR) {
  15020. if (!strD) { return "";}
  15021. iB = iB || 0;
  15022. iE = iE || strD.length;
  15023. return strD.substring(iB, iE).split(strF).join(strR);
  15024. };
  15025. })(jQuery, fluid_1_5);
  15026. /*
  15027. Copyright 2008-2010 University of Cambridge
  15028. Copyright 2008-2009 University of Toronto
  15029. Copyright 2010-2011 Lucendo Development Ltd.
  15030. Licensed under the Educational Community License (ECL), Version 2.0 or the New
  15031. BSD license. You may not use this file except in compliance with one these
  15032. Licenses.
  15033. You may obtain a copy of the ECL 2.0 License and BSD License at
  15034. https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt
  15035. */
  15036. // Declare dependencies
  15037. /*global fluid_1_5:true, jQuery*/
  15038. // JSLint options
  15039. /*jslint white: true, funcinvoke: true, continue: true, elsecatch: true, operator: true, jslintok:true, undef: true, newcap: true, regexp: true, bitwise: true, browser: true, forin: true, maxerr: 100, indent: 4 */
  15040. fluid_1_5 = fluid_1_5 || {};
  15041. (function ($, fluid) {
  15042. fluid.parseTemplate = function (template, baseURL, scanStart, cutpoints_in, opts) {
  15043. opts = opts || {};
  15044. if (!template) {
  15045. fluid.fail("empty template supplied to fluid.parseTemplate");
  15046. }
  15047. var t;
  15048. var parser;
  15049. var tagstack;
  15050. var lumpindex = 0;
  15051. var nestingdepth = 0;
  15052. var justended = false;
  15053. var defstart = -1;
  15054. var defend = -1;
  15055. var debugMode = false;
  15056. var cutpoints = []; // list of selector, tree, id
  15057. var simpleClassCutpoints = {};
  15058. var cutstatus = [];
  15059. var XMLLump = function (lumpindex, nestingdepth) {
  15060. return {
  15061. //rsfID: "",
  15062. //text: "",
  15063. //downmap: {},
  15064. //attributemap: {},
  15065. //finallump: {},
  15066. nestingdepth: nestingdepth,
  15067. lumpindex: lumpindex,
  15068. parent: t
  15069. };
  15070. };
  15071. function isSimpleClassCutpoint(tree) {
  15072. return tree.length === 1 && tree[0].predList.length === 1 && tree[0].predList[0].clazz;
  15073. }
  15074. function init(baseURLin, debugModeIn, cutpointsIn) {
  15075. t.rootlump = XMLLump(0, -1); // jslint:ok - capital letter
  15076. tagstack = [t.rootlump];
  15077. lumpindex = 0;
  15078. nestingdepth = 0;
  15079. justended = false;
  15080. defstart = -1;
  15081. defend = -1;
  15082. baseURL = baseURLin;
  15083. debugMode = debugModeIn;
  15084. if (cutpointsIn) {
  15085. for (var i = 0; i < cutpointsIn.length; ++i) {
  15086. var tree = fluid.parseSelector(cutpointsIn[i].selector);
  15087. var clazz = isSimpleClassCutpoint(tree);
  15088. if (clazz) {
  15089. simpleClassCutpoints[clazz] = cutpointsIn[i].id;
  15090. }
  15091. else {
  15092. cutstatus.push([]);
  15093. cutpoints.push($.extend({}, cutpointsIn[i], {tree: tree}));
  15094. }
  15095. }
  15096. }
  15097. }
  15098. function findTopContainer() {
  15099. for (var i = tagstack.length - 1; i >= 0; --i) {
  15100. var lump = tagstack[i];
  15101. if (lump.rsfID !== undefined) {
  15102. return lump;
  15103. }
  15104. }
  15105. return t.rootlump;
  15106. }
  15107. function newLump() {
  15108. var togo = XMLLump(lumpindex, nestingdepth); // jslint:ok - capital letter
  15109. if (debugMode) {
  15110. togo.line = parser.getLineNumber();
  15111. togo.column = parser.getColumnNumber();
  15112. }
  15113. //togo.parent = t;
  15114. t.lumps[lumpindex] = togo;
  15115. ++lumpindex;
  15116. return togo;
  15117. }
  15118. function addLump(mmap, ID, lump) {
  15119. var list = mmap[ID];
  15120. if (!list) {
  15121. list = [];
  15122. mmap[ID] = list;
  15123. }
  15124. list[list.length] = lump;
  15125. }
  15126. function checkContribute(ID, lump) {
  15127. if (ID.indexOf("scr=contribute-") !== -1) {
  15128. var scr = ID.substring("scr=contribute-".length);
  15129. addLump(t.collectmap, scr, lump);
  15130. }
  15131. }
  15132. function debugLump(lump) {
  15133. // TODO expand this to agree with the Firebug "self-selector" idiom
  15134. return "<" + lump.tagname + ">";
  15135. }
  15136. function hasCssClass(clazz, totest) {
  15137. if (!totest) {
  15138. return false;
  15139. }
  15140. // algorithm from JQuery
  15141. return (" " + totest + " ").indexOf(" " + clazz + " ") !== -1;
  15142. }
  15143. function matchNode(term, headlump, headclazz) {
  15144. if (term.predList) {
  15145. for (var i = 0; i < term.predList.length; ++i) {
  15146. var pred = term.predList[i];
  15147. if (pred.id && headlump.attributemap.id !== pred.id) {return false;}
  15148. if (pred.clazz && !hasCssClass(pred.clazz, headclazz)) {return false;}
  15149. if (pred.tag && headlump.tagname !== pred.tag) {return false;}
  15150. }
  15151. return true;
  15152. }
  15153. }
  15154. function tagStartCut(headlump) {
  15155. var togo;
  15156. var headclazz = headlump.attributemap["class"];
  15157. if (headclazz) {
  15158. var split = headclazz.split(" ");
  15159. for (var i = 0; i < split.length; ++i) {
  15160. var simpleCut = simpleClassCutpoints[$.trim(split[i])];
  15161. if (simpleCut) {
  15162. return simpleCut;
  15163. }
  15164. }
  15165. }
  15166. for (var i = 0; i < cutpoints.length; ++i) { // jslint:ok - scoping
  15167. var cut = cutpoints[i];
  15168. var cutstat = cutstatus[i];
  15169. var nextterm = cutstat.length; // the next term for this node
  15170. if (nextterm < cut.tree.length) {
  15171. var term = cut.tree[nextterm];
  15172. if (nextterm > 0) {
  15173. if (cut.tree[nextterm - 1].child &&
  15174. cutstat[nextterm - 1] !== headlump.nestingdepth - 1) {
  15175. continue; // it is a failure to match if not at correct nesting depth
  15176. }
  15177. }
  15178. var isMatch = matchNode(term, headlump, headclazz);
  15179. if (isMatch) {
  15180. cutstat[cutstat.length] = headlump.nestingdepth;
  15181. if (cutstat.length === cut.tree.length) {
  15182. if (togo !== undefined) {
  15183. fluid.fail("Cutpoint specification error - node " +
  15184. debugLump(headlump) +
  15185. " has already matched with rsf:id of " + togo);
  15186. }
  15187. if (cut.id === undefined || cut.id === null) {
  15188. fluid.fail("Error in cutpoints list - entry at position " + i + " does not have an id set");
  15189. }
  15190. togo = cut.id;
  15191. }
  15192. }
  15193. }
  15194. }
  15195. return togo;
  15196. }
  15197. function tagEndCut() {
  15198. if (cutpoints) {
  15199. for (var i = 0; i < cutpoints.length; ++i) {
  15200. var cutstat = cutstatus[i];
  15201. if (cutstat.length > 0 && cutstat[cutstat.length - 1] === nestingdepth) {
  15202. cutstat.length--;
  15203. }
  15204. }
  15205. }
  15206. }
  15207. function processTagEnd() {
  15208. tagEndCut();
  15209. var endlump = newLump();
  15210. --nestingdepth;
  15211. endlump.text = "</" + parser.getName() + ">";
  15212. var oldtop = tagstack[tagstack.length - 1];
  15213. oldtop.close_tag = t.lumps[lumpindex - 1];
  15214. tagstack.length--;
  15215. justended = true;
  15216. }
  15217. function processTagStart(isempty, text) {
  15218. ++nestingdepth;
  15219. if (justended) {
  15220. justended = false;
  15221. var backlump = newLump();
  15222. backlump.nestingdepth--;
  15223. }
  15224. if (t.firstdocumentindex === -1) {
  15225. t.firstdocumentindex = lumpindex;
  15226. }
  15227. var headlump = newLump();
  15228. var stacktop = tagstack[tagstack.length - 1];
  15229. headlump.uplump = stacktop;
  15230. var tagname = parser.getName();
  15231. headlump.tagname = tagname;
  15232. // NB - attribute names and values are now NOT DECODED!!
  15233. var attrs = headlump.attributemap = parser.m_attributes;
  15234. var ID = attrs[fluid.ID_ATTRIBUTE];
  15235. if (ID === undefined) {
  15236. ID = tagStartCut(headlump);
  15237. }
  15238. for (var attrname in attrs) {
  15239. if (ID === undefined) {
  15240. if (/href|src|codebase|action/.test(attrname)) {
  15241. ID = "scr=rewrite-url";
  15242. }
  15243. // port of TPI effect of IDRelationRewriter
  15244. else if (ID === undefined && /for|headers/.test(attrname)) {
  15245. ID = "scr=null";
  15246. }
  15247. }
  15248. }
  15249. if (ID) {
  15250. // TODO: ensure this logic is correct on RSF Server
  15251. if (ID.charCodeAt(0) === 126) { // "~"
  15252. ID = ID.substring(1);
  15253. headlump.elide = true;
  15254. }
  15255. checkContribute(ID, headlump);
  15256. headlump.rsfID = ID;
  15257. var downreg = findTopContainer();
  15258. if (!downreg.downmap) {
  15259. downreg.downmap = {};
  15260. }
  15261. while (downreg) { // TODO: unusual fix for locating branches in parent contexts (applies to repetitive leaves)
  15262. if (downreg.downmap) {
  15263. addLump(downreg.downmap, ID, headlump);
  15264. }
  15265. downreg = downreg.uplump;
  15266. }
  15267. addLump(t.globalmap, ID, headlump);
  15268. var colpos = ID.indexOf(":");
  15269. if (colpos !== -1) {
  15270. var prefix = ID.substring(0, colpos);
  15271. if (!stacktop.finallump) {
  15272. stacktop.finallump = {};
  15273. }
  15274. stacktop.finallump[prefix] = headlump;
  15275. }
  15276. }
  15277. // TODO: accelerate this by grabbing original template text (requires parser
  15278. // adjustment) as well as dealing with empty tags
  15279. headlump.text = "<" + tagname + fluid.dumpAttributes(attrs) + (isempty && !ID? "/>" : ">");
  15280. tagstack[tagstack.length] = headlump;
  15281. if (isempty) {
  15282. if (ID) {
  15283. processTagEnd();
  15284. }
  15285. else {
  15286. --nestingdepth;
  15287. tagstack.length--;
  15288. }
  15289. }
  15290. }
  15291. function processDefaultTag() {
  15292. if (defstart !== -1) {
  15293. if (t.firstdocumentindex === -1) {
  15294. t.firstdocumentindex = lumpindex;
  15295. }
  15296. var text = parser.getContent().substr(defstart, defend - defstart);
  15297. justended = false;
  15298. var newlump = newLump();
  15299. newlump.text = text;
  15300. defstart = -1;
  15301. }
  15302. }
  15303. /** ACTUAL BODY of fluid.parseTemplate begins here **/
  15304. t = fluid.XMLViewTemplate();
  15305. init(baseURL, opts.debugMode, cutpoints_in);
  15306. var idpos = template.indexOf(fluid.ID_ATTRIBUTE);
  15307. if (scanStart) {
  15308. var brackpos = template.indexOf('>', idpos);
  15309. parser = fluid.XMLP(template.substring(brackpos + 1));
  15310. }
  15311. else {
  15312. parser = fluid.XMLP(template);
  15313. }
  15314. parseloop: while (true) {
  15315. var iEvent = parser.next();
  15316. switch (iEvent) {
  15317. case fluid.XMLP._ELM_B:
  15318. processDefaultTag();
  15319. //var text = parser.getContent().substr(parser.getContentBegin(), parser.getContentEnd() - parser.getContentBegin());
  15320. processTagStart(false, "");
  15321. break;
  15322. case fluid.XMLP._ELM_E:
  15323. processDefaultTag();
  15324. processTagEnd();
  15325. break;
  15326. case fluid.XMLP._ELM_EMP:
  15327. processDefaultTag();
  15328. //var text = parser.getContent().substr(parser.getContentBegin(), parser.getContentEnd() - parser.getContentBegin());
  15329. processTagStart(true, "");
  15330. break;
  15331. case fluid.XMLP._PI:
  15332. case fluid.XMLP._DTD:
  15333. defstart = -1;
  15334. continue; // not interested in reproducing these
  15335. case fluid.XMLP._TEXT:
  15336. case fluid.XMLP._ENTITY:
  15337. case fluid.XMLP._CDATA:
  15338. case fluid.XMLP._COMMENT:
  15339. if (defstart === -1) {
  15340. defstart = parser.m_cB;
  15341. }
  15342. defend = parser.m_cE;
  15343. break;
  15344. case fluid.XMLP._ERROR:
  15345. fluid.setLogging(true);
  15346. var message = "Error parsing template: " + parser.m_cAlt + " at line " + parser.getLineNumber();
  15347. fluid.log(message);
  15348. fluid.log("Just read: " + parser.m_xml.substring(parser.m_iP - 30, parser.m_iP));
  15349. fluid.log("Still to read: " + parser.m_xml.substring(parser.m_iP, parser.m_iP + 30));
  15350. fluid.fail(message);
  15351. break parseloop;
  15352. case fluid.XMLP._NONE:
  15353. break parseloop;
  15354. }
  15355. }
  15356. processDefaultTag();
  15357. var excess = tagstack.length - 1;
  15358. if (excess) {
  15359. fluid.fail("Error parsing template - unclosed tag(s) of depth " + (excess) +
  15360. ": " + fluid.transform(tagstack.splice(1, excess), function (lump) {return debugLump(lump);}).join(", "));
  15361. }
  15362. return t;
  15363. };
  15364. fluid.debugLump = function (lump) {
  15365. var togo = lump.text;
  15366. togo += " at ";
  15367. togo += "lump line " + lump.line + " column " + lump.column + " index " + lump.lumpindex;
  15368. togo += lump.parent.href === null? "" : " in file " + lump.parent.href;
  15369. return togo;
  15370. };
  15371. // Public definitions begin here
  15372. fluid.ID_ATTRIBUTE = "rsf:id";
  15373. fluid.getPrefix = function (id) {
  15374. var colpos = id.indexOf(':');
  15375. return colpos === -1? id : id.substring(0, colpos);
  15376. };
  15377. fluid.SplitID = function (id) {
  15378. var that = {};
  15379. var colpos = id.indexOf(':');
  15380. if (colpos === -1) {
  15381. that.prefix = id;
  15382. }
  15383. else {
  15384. that.prefix = id.substring(0, colpos);
  15385. that.suffix = id.substring(colpos + 1);
  15386. }
  15387. return that;
  15388. };
  15389. fluid.XMLViewTemplate = function () {
  15390. return {
  15391. globalmap: {},
  15392. collectmap: {},
  15393. lumps: [],
  15394. firstdocumentindex: -1
  15395. };
  15396. };
  15397. // TODO: find faster encoder
  15398. fluid.XMLEncode = function (text) {
  15399. return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\"/g, "&quot;");
  15400. };
  15401. fluid.dumpAttributes = function (attrcopy) {
  15402. var togo = "";
  15403. for (var attrname in attrcopy) {
  15404. var attrvalue = attrcopy[attrname];
  15405. if (attrvalue !== null && attrvalue !== undefined) {
  15406. togo += " " + attrname + "=\"" + attrvalue + "\"";
  15407. }
  15408. }
  15409. return togo;
  15410. };
  15411. fluid.aggregateMMap = function (target, source) {
  15412. for (var key in source) {
  15413. var targhas = target[key];
  15414. if (!targhas) {
  15415. target[key] = [];
  15416. }
  15417. target[key] = target[key].concat(source[key]);
  15418. }
  15419. };
  15420. /** Returns a "template structure", with globalmap in the root, and a list
  15421. * of entries {href, template, cutpoints} for each parsed template.
  15422. */
  15423. fluid.parseTemplates = function (resourceSpec, templateList, opts) {
  15424. var togo = [];
  15425. opts = opts || {};
  15426. togo.globalmap = {};
  15427. for (var i = 0; i < templateList.length; ++i) {
  15428. var resource = resourceSpec[templateList[i]];
  15429. var lastslash = resource.href.lastIndexOf("/");
  15430. var baseURL = lastslash === -1? "" : resource.href.substring(0, lastslash + 1);
  15431. var template = fluid.parseTemplate(resource.resourceText, baseURL,
  15432. opts.scanStart && i === 0, resource.cutpoints, opts);
  15433. if (i === 0) {
  15434. fluid.aggregateMMap(togo.globalmap, template.globalmap);
  15435. }
  15436. template.href = resource.href;
  15437. template.baseURL = baseURL;
  15438. template.resourceKey = resource.resourceKey;
  15439. togo[i] = template;
  15440. fluid.aggregateMMap(togo.globalmap, template.rootlump.downmap);
  15441. }
  15442. return togo;
  15443. };
  15444. // ******* SELECTOR ENGINE *********
  15445. // selector regexps copied from JQuery
  15446. var chars = "(?:[\\w\u0128-\uFFFF*_-]|\\\\.)";
  15447. // var quickChild = new RegExp("^>\\s*(" + chars + "+)");
  15448. // var quickID = new RegExp("^(" + chars + "+)(#)(" + chars + "+)");
  15449. // var selSeg = new RegExp("^\\s*([#.]?)(" + chars + "*)");
  15450. var quickClass = new RegExp("([#.]?)(" + chars + "+)", "g");
  15451. var childSeg = new RegExp("\\s*(>)?\\s*", "g");
  15452. // var whiteSpace = new RegExp("^\\w*$");
  15453. fluid.parseSelector = function (selstring) {
  15454. var togo = [];
  15455. selstring = $.trim(selstring);
  15456. //ws-(ss*)[ws/>]
  15457. quickClass.lastIndex = 0;
  15458. var lastIndex = 0;
  15459. while (true) {
  15460. var atNode = []; // a list of predicates at a particular node
  15461. while (true) {
  15462. var segMatch = quickClass.exec(selstring);
  15463. if (!segMatch || segMatch.index !== lastIndex) {
  15464. break;
  15465. }
  15466. var thisNode = {};
  15467. var text = segMatch[2];
  15468. if (segMatch[1] === "") {
  15469. thisNode.tag = text;
  15470. }
  15471. else if (segMatch[1] === "#") {
  15472. thisNode.id = text;
  15473. }
  15474. else if (segMatch[1] === ".") {
  15475. thisNode.clazz = text;
  15476. }
  15477. atNode[atNode.length] = thisNode;
  15478. lastIndex = quickClass.lastIndex;
  15479. }
  15480. childSeg.lastIndex = lastIndex;
  15481. var fullAtNode = {predList: atNode};
  15482. var childMatch = childSeg.exec(selstring);
  15483. if (!childMatch || childMatch.index !== lastIndex) {
  15484. var remainder = selstring.substring(lastIndex);
  15485. fluid.fail("Error in selector string - can not match child selector expression at " + remainder);
  15486. }
  15487. if (childMatch[1] === ">") {
  15488. fullAtNode.child = true;
  15489. }
  15490. togo[togo.length] = fullAtNode;
  15491. // >= test here to compensate for IE bug http://blog.stevenlevithan.com/archives/exec-bugs
  15492. if (childSeg.lastIndex >= selstring.length) {
  15493. break;
  15494. }
  15495. lastIndex = childSeg.lastIndex;
  15496. quickClass.lastIndex = childSeg.lastIndex;
  15497. }
  15498. return togo;
  15499. };
  15500. })(jQuery, fluid_1_5);
  15501. /*
  15502. Copyright 2008-2010 University of Cambridge
  15503. Copyright 2008-2009 University of Toronto
  15504. Copyright 2010-2011 Lucendo Development Ltd.
  15505. Licensed under the Educational Community License (ECL), Version 2.0 or the New
  15506. BSD license. You may not use this file except in compliance with one these
  15507. Licenses.
  15508. You may obtain a copy of the ECL 2.0 License and BSD License at
  15509. https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt
  15510. */
  15511. // Declare dependencies
  15512. /*global fluid_1_5:true, jQuery*/
  15513. // JSLint options
  15514. /*jslint white: true, funcinvoke: true, continue: true, elsecatch: true, operator: true, jslintok:true, undef: true, newcap: true, regexp: true, bitwise: true, browser: true, forin: true, maxerr: 100, indent: 4 */
  15515. fluid_1_5 = fluid_1_5 || {};
  15516. (function ($, fluid) {
  15517. function debugPosition(component) {
  15518. return "as child of " + (component.parent.fullID ? "component with full ID " + component.parent.fullID : "root");
  15519. }
  15520. function computeFullID(component) {
  15521. var togo = "";
  15522. var move = component;
  15523. if (component.children === undefined) { // not a container
  15524. // unusual case on the client-side, since a repetitive leaf may have localID blasted onto it.
  15525. togo = component.ID + (component.localID !== undefined ? component.localID : "");
  15526. move = component.parent;
  15527. }
  15528. while (move.parent) {
  15529. var parent = move.parent;
  15530. if (move.fullID !== undefined) {
  15531. togo = move.fullID + togo;
  15532. return togo;
  15533. }
  15534. if (move.noID === undefined) {
  15535. var ID = move.ID;
  15536. if (ID === undefined) {
  15537. fluid.fail("Error in component tree - component found with no ID " +
  15538. debugPosition(parent) + ": please check structure");
  15539. }
  15540. var colpos = ID.indexOf(":");
  15541. var prefix = colpos === -1 ? ID : ID.substring(0, colpos);
  15542. togo = prefix + ":" + (move.localID === undefined ? "" : move.localID) + ":" + togo;
  15543. }
  15544. move = parent;
  15545. }
  15546. return togo;
  15547. }
  15548. var renderer = {};
  15549. renderer.isBoundPrimitive = function (value) {
  15550. return fluid.isPrimitive(value) || value instanceof Array
  15551. && (value.length === 0 || typeof (value[0]) === "string"); // jslint:ok
  15552. };
  15553. var unzipComponent;
  15554. function processChild(value, key) {
  15555. if (renderer.isBoundPrimitive(value)) {
  15556. return {componentType: "UIBound", value: value, ID: key};
  15557. }
  15558. else {
  15559. var unzip = unzipComponent(value);
  15560. if (unzip.ID) {
  15561. return {ID: key, componentType: "UIContainer", children: [unzip]};
  15562. } else {
  15563. unzip.ID = key;
  15564. return unzip;
  15565. }
  15566. }
  15567. }
  15568. function fixChildren(children) {
  15569. if (!(children instanceof Array)) {
  15570. var togo = [];
  15571. for (var key in children) {
  15572. var value = children[key];
  15573. if (value instanceof Array) {
  15574. for (var i = 0; i < value.length; ++i) {
  15575. var processed = processChild(value[i], key);
  15576. // if (processed.componentType === "UIContainer" &&
  15577. // processed.localID === undefined) {
  15578. // processed.localID = i;
  15579. // }
  15580. togo[togo.length] = processed;
  15581. }
  15582. } else {
  15583. togo[togo.length] = processChild(value, key);
  15584. }
  15585. }
  15586. return togo;
  15587. } else {return children; }
  15588. }
  15589. function fixupValue(uibound, model, resolverGetConfig) {
  15590. if (uibound.value === undefined && uibound.valuebinding !== undefined) {
  15591. if (!model) {
  15592. fluid.fail("Cannot perform value fixup for valuebinding "
  15593. + uibound.valuebinding + " since no model was supplied to rendering");
  15594. }
  15595. uibound.value = fluid.get(model, uibound.valuebinding, resolverGetConfig);
  15596. }
  15597. }
  15598. function upgradeBound(holder, property, model, resolverGetConfig) {
  15599. if (holder[property] !== undefined) {
  15600. if (renderer.isBoundPrimitive(holder[property])) {
  15601. holder[property] = {value: holder[property]};
  15602. }
  15603. else if (holder[property].messagekey) {
  15604. holder[property].componentType = "UIMessage";
  15605. }
  15606. }
  15607. else {
  15608. holder[property] = {value: null};
  15609. }
  15610. fixupValue(holder[property], model, resolverGetConfig);
  15611. }
  15612. renderer.duckMap = {children: "UIContainer",
  15613. value: "UIBound", valuebinding: "UIBound", messagekey: "UIMessage",
  15614. markup: "UIVerbatim", selection: "UISelect", target: "UILink",
  15615. choiceindex: "UISelectChoice", functionname: "UIInitBlock"};
  15616. var boundMap = {
  15617. UISelect: ["selection", "optionlist", "optionnames"],
  15618. UILink: ["target", "linktext"],
  15619. UIVerbatim: ["markup"],
  15620. UIMessage: ["messagekey"]
  15621. };
  15622. renderer.boundMap = fluid.transform(boundMap, fluid.arrayToHash);
  15623. renderer.inferComponentType = function (component) {
  15624. for (var key in renderer.duckMap) {
  15625. if (component[key] !== undefined) {
  15626. return renderer.duckMap[key];
  15627. }
  15628. }
  15629. };
  15630. renderer.applyComponentType = function (component) {
  15631. component.componentType = renderer.inferComponentType(component);
  15632. if (component.componentType === undefined && component.ID !== undefined) {
  15633. component.componentType = "UIBound";
  15634. }
  15635. };
  15636. unzipComponent = function (component, model, resolverGetConfig) {
  15637. if (component) {
  15638. renderer.applyComponentType(component);
  15639. }
  15640. if (!component || component.componentType === undefined) {
  15641. var decorators = component.decorators;
  15642. if (decorators) {delete component.decorators;}
  15643. component = {componentType: "UIContainer", children: component};
  15644. component.decorators = decorators;
  15645. }
  15646. var cType = component.componentType;
  15647. if (cType === "UIContainer") {
  15648. component.children = fixChildren(component.children);
  15649. }
  15650. else {
  15651. var map = renderer.boundMap[cType];
  15652. if (map) {
  15653. fluid.each(map, function (value, key) {
  15654. upgradeBound(component, key, model, resolverGetConfig);
  15655. });
  15656. }
  15657. }
  15658. return component;
  15659. };
  15660. function fixupTree(tree, model, resolverGetConfig) {
  15661. if (tree.componentType === undefined) {
  15662. tree = unzipComponent(tree, model, resolverGetConfig);
  15663. }
  15664. if (tree.componentType !== "UIContainer" && !tree.parent) {
  15665. tree = {children: [tree]};
  15666. }
  15667. if (tree.children) {
  15668. tree.childmap = {};
  15669. for (var i = 0; i < tree.children.length; ++i) {
  15670. var child = tree.children[i];
  15671. if (child.componentType === undefined) {
  15672. child = unzipComponent(child, model, resolverGetConfig);
  15673. tree.children[i] = child;
  15674. }
  15675. child.parent = tree;
  15676. if (child.ID === undefined) {
  15677. fluid.fail("Error in component tree: component found with no ID " + debugPosition(child));
  15678. }
  15679. tree.childmap[child.ID] = child;
  15680. var colpos = child.ID.indexOf(":");
  15681. if (colpos === -1) {
  15682. // tree.childmap[child.ID] = child; // moved out of branch to allow
  15683. // "relative id expressions" to be easily parsed
  15684. }
  15685. else { // jslint:ok - TODO: review the above
  15686. var prefix = child.ID.substring(0, colpos);
  15687. var childlist = tree.childmap[prefix];
  15688. if (!childlist) {
  15689. childlist = [];
  15690. tree.childmap[prefix] = childlist;
  15691. }
  15692. if (child.localID === undefined && childlist.length !== 0) {
  15693. child.localID = childlist.length;
  15694. }
  15695. childlist[childlist.length] = child;
  15696. }
  15697. child.fullID = computeFullID(child);
  15698. var componentType = child.componentType;
  15699. if (componentType === "UISelect") {
  15700. child.selection.fullID = child.fullID + "-selection";
  15701. }
  15702. else if (componentType === "UIInitBlock") {
  15703. var call = child.functionname + '(';
  15704. for (var j = 0; j < child.arguments.length; ++j) { // jslint:ok
  15705. if (child.arguments[j] instanceof fluid.ComponentReference) { // jslint:ok
  15706. // TODO: support more forms of id reference
  15707. child.arguments[j] = child.parent.fullID + child.arguments[j].reference; // jslint:ok
  15708. }
  15709. call += JSON.stringify(child.arguments[j]); // jslint:ok
  15710. if (j < child.arguments.length - 1) { // jslint:ok
  15711. call += ", ";
  15712. }
  15713. }
  15714. child.markup = {value: call + ")\n"};
  15715. child.componentType = "UIVerbatim";
  15716. }
  15717. else if (componentType === "UIBound") {
  15718. fixupValue(child, model, resolverGetConfig);
  15719. }
  15720. fixupTree(child, model, resolverGetConfig);
  15721. }
  15722. }
  15723. return tree;
  15724. }
  15725. fluid.NULL_STRING = "\u25a9null\u25a9";
  15726. var LINK_ATTRIBUTES = {
  15727. a: "href", link: "href", img: "src", frame: "src", script: "src", style: "src", input: "src", embed: "src", // jslint:ok
  15728. form: "action",
  15729. applet: "codebase", object: "codebase" //jslint:ok
  15730. };
  15731. renderer.decoratorComponentPrefix = "**-renderer-";
  15732. renderer.IDtoComponentName = function(ID, num) {
  15733. return renderer.decoratorComponentPrefix + ID.replace(/\./g, "") + "-" + num;
  15734. };
  15735. renderer.invokeFluidDecorator = function(func, args, ID, num, options) {
  15736. var that;
  15737. if (options.instantiator && options.parentComponent) {
  15738. var parent = options.parentComponent;
  15739. var name = renderer.IDtoComponentName(ID, num);
  15740. // TODO: The best we can do here without GRADES is to wildly guess
  15741. // that it is a view component with options in the 2nd place and container in first place
  15742. fluid.set(parent, fluid.path("options", "components", name), {type: func});
  15743. // This MIGHT really be a variant of fluid.invoke... only we often probably DO want the component
  15744. // itself to be inserted into the that stack. This *ALSO* requires GRADES to resolve. A
  15745. // "function" is that which has no grade. The gradeless grade.
  15746. that = fluid.initDependent(options.parentComponent, name, options.instantiator, args);
  15747. }
  15748. else {
  15749. that = fluid.invokeGlobalFunction(func, args);
  15750. }
  15751. return that;
  15752. };
  15753. fluid.renderer = function (templates, tree, options, fossilsIn) {
  15754. options = options || {};
  15755. tree = tree || {};
  15756. var debugMode = options.debugMode;
  15757. if (!options.messageLocator && options.messageSource) {
  15758. options.messageLocator = fluid.resolveMessageSource(options.messageSource);
  15759. }
  15760. options.document = options.document || document;
  15761. options.jQuery = options.jQuery || $;
  15762. options.fossils = options.fossils || fossilsIn || {}; // map of submittingname to {EL, submittingname, oldvalue}
  15763. var globalmap = {};
  15764. var branchmap = {};
  15765. var rewritemap = {}; // map of rewritekey (for original id in template) to full ID
  15766. var seenset = {};
  15767. var collected = {};
  15768. var out = "";
  15769. var renderOptions = options;
  15770. var decoratorQueue = [];
  15771. var renderedbindings = {}; // map of fullID to true for UISelects which have already had bindings written
  15772. var usedIDs = {};
  15773. var that = {options: options};
  15774. function getRewriteKey(template, parent, id) {
  15775. return template.resourceKey + parent.fullID + id;
  15776. }
  15777. // returns: lump
  15778. function resolveInScope(searchID, defprefix, scope, child) {
  15779. var deflump;
  15780. var scopelook = scope? scope[searchID] : null;
  15781. if (scopelook) {
  15782. for (var i = 0; i < scopelook.length; ++i) {
  15783. var scopelump = scopelook[i];
  15784. if (!deflump && scopelump.rsfID === defprefix) {
  15785. deflump = scopelump;
  15786. }
  15787. if (scopelump.rsfID === searchID) {
  15788. return scopelump;
  15789. }
  15790. }
  15791. }
  15792. return deflump;
  15793. }
  15794. // returns: lump
  15795. function resolveCall(sourcescope, child) {
  15796. var searchID = child.jointID? child.jointID : child.ID;
  15797. var split = fluid.SplitID(searchID);
  15798. var defprefix = split.prefix + ':';
  15799. var match = resolveInScope(searchID, defprefix, sourcescope.downmap, child);
  15800. if (match) {return match;}
  15801. if (child.children) {
  15802. match = resolveInScope(searchID, defprefix, globalmap, child);
  15803. if (match) {return match;}
  15804. }
  15805. return null;
  15806. }
  15807. function noteCollected(template) {
  15808. if (!seenset[template.href]) {
  15809. fluid.aggregateMMap(collected, template.collectmap);
  15810. seenset[template.href] = true;
  15811. }
  15812. }
  15813. var fetchComponent;
  15814. function resolveRecurse(basecontainer, parentlump) {
  15815. for (var i = 0; i < basecontainer.children.length; ++i) {
  15816. var branch = basecontainer.children[i];
  15817. if (branch.children) { // it is a branch
  15818. var resolved = resolveCall(parentlump, branch);
  15819. if (resolved) {
  15820. branchmap[branch.fullID] = resolved;
  15821. var id = resolved.attributemap.id;
  15822. if (id !== undefined) {
  15823. rewritemap[getRewriteKey(parentlump.parent, basecontainer, id)] = branch.fullID;
  15824. }
  15825. // on server-side this is done separately
  15826. noteCollected(resolved.parent);
  15827. resolveRecurse(branch, resolved);
  15828. }
  15829. }
  15830. }
  15831. // collect any rewritten ids for the purpose of later rewriting
  15832. if (parentlump.downmap) {
  15833. for (var id in parentlump.downmap) { // jslint:ok - scoping
  15834. //if (id.indexOf(":") === -1) {
  15835. var lumps = parentlump.downmap[id];
  15836. for (var i = 0; i < lumps.length; ++i) { // jslint:ok - scoping
  15837. var lump = lumps[i];
  15838. var lumpid = lump.attributemap.id;
  15839. if (lumpid !== undefined && lump.rsfID !== undefined) {
  15840. var resolved = fetchComponent(basecontainer, lump.rsfID); //jslint:ok - scoping
  15841. if (resolved !== null) {
  15842. var resolveID = resolved.fullID;
  15843. if (resolved.componentType === "UISelect") {
  15844. resolveID = resolveID + "-selection";
  15845. }
  15846. rewritemap[getRewriteKey(parentlump.parent, basecontainer,
  15847. lumpid)] = resolveID;
  15848. }
  15849. }
  15850. }
  15851. // }
  15852. }
  15853. }
  15854. }
  15855. function resolveBranches(globalmapp, basecontainer, parentlump) {
  15856. branchmap = {};
  15857. rewritemap = {};
  15858. seenset = {};
  15859. collected = {};
  15860. globalmap = globalmapp;
  15861. branchmap[basecontainer.fullID] = parentlump;
  15862. resolveRecurse(basecontainer, parentlump);
  15863. }
  15864. function dumpTillLump(lumps, start, limit) {
  15865. for (; start < limit; ++start) {
  15866. var text = lumps[start].text;
  15867. if (text) { // guard against "undefined" lumps from "justended"
  15868. out += lumps[start].text;
  15869. }
  15870. }
  15871. }
  15872. function dumpScan(lumps, renderindex, basedepth, closeparent, insideleaf) {
  15873. var start = renderindex;
  15874. while (true) {
  15875. if (renderindex === lumps.length) {
  15876. break;
  15877. }
  15878. var lump = lumps[renderindex];
  15879. if (lump.nestingdepth < basedepth) {
  15880. break;
  15881. }
  15882. if (lump.rsfID !== undefined) {
  15883. if (!insideleaf) {break;}
  15884. if (insideleaf && lump.nestingdepth > basedepth + (closeparent? 0 : 1)) {
  15885. fluid.log("Error in component tree - leaf component found to contain further components - at " +
  15886. lump.toString());
  15887. }
  15888. else {break;}
  15889. }
  15890. // target.print(lump.text);
  15891. ++renderindex;
  15892. }
  15893. // ASSUMPTIONS: close tags are ONE LUMP
  15894. if (!closeparent && (renderindex === lumps.length || !lumps[renderindex].rsfID)) {
  15895. --renderindex;
  15896. }
  15897. dumpTillLump(lumps, start, renderindex);
  15898. //target.write(buffer, start, limit - start);
  15899. return renderindex;
  15900. }
  15901. function isPlaceholder(value) {
  15902. // TODO: equivalent of server-side "placeholder" system
  15903. return false;
  15904. }
  15905. function isValue(value) {
  15906. return value !== null && value !== undefined && !isPlaceholder(value);
  15907. }
  15908. // In RSF Client, this is a "flyweight" "global" object that is reused for every tag,
  15909. // to avoid generating garbage. In RSF Server, it is an argument to the following rendering
  15910. // methods of type "TagRenderContext".
  15911. var trc = {};
  15912. /*** TRC METHODS ***/
  15913. function openTag() {
  15914. if (!trc.iselide) {
  15915. out += "<" + trc.uselump.tagname;
  15916. }
  15917. }
  15918. function closeTag() {
  15919. if (!trc.iselide) {
  15920. out += "</" + trc.uselump.tagname + ">";
  15921. }
  15922. }
  15923. function renderUnchanged() {
  15924. // TODO needs work since we don't keep attributes in text
  15925. dumpTillLump(trc.uselump.parent.lumps, trc.uselump.lumpindex + 1,
  15926. trc.close.lumpindex + (trc.iselide ? 0 : 1));
  15927. }
  15928. function isSelfClose() {
  15929. return trc.endopen.lumpindex === trc.close.lumpindex && fluid.XMLP.closedTags[trc.uselump.tagname];
  15930. }
  15931. function dumpTemplateBody() {
  15932. if (isSelfClose()) {
  15933. if (!trc.iselide) {
  15934. out += "/>";
  15935. }
  15936. }
  15937. else {
  15938. if (!trc.iselide) {
  15939. out += ">";
  15940. }
  15941. dumpTillLump(trc.uselump.parent.lumps, trc.endopen.lumpindex,
  15942. trc.close.lumpindex + (trc.iselide ? 0 : 1));
  15943. }
  15944. }
  15945. function replaceAttributes() {
  15946. if (!trc.iselide) {
  15947. out += fluid.dumpAttributes(trc.attrcopy);
  15948. }
  15949. dumpTemplateBody();
  15950. }
  15951. function replaceAttributesOpen() {
  15952. if (trc.iselide) {
  15953. replaceAttributes();
  15954. }
  15955. else {
  15956. out += fluid.dumpAttributes(trc.attrcopy);
  15957. var selfClose = isSelfClose();
  15958. // TODO: the parser does not ever produce empty tags
  15959. out += selfClose ? "/>" : ">";
  15960. trc.nextpos = selfClose? trc.close.lumpindex + 1 : trc.endopen.lumpindex;
  15961. }
  15962. }
  15963. function replaceBody(value) {
  15964. out += fluid.dumpAttributes(trc.attrcopy);
  15965. if (!trc.iselide) {
  15966. out += ">";
  15967. }
  15968. out += fluid.XMLEncode(value.toString());
  15969. closeTag();
  15970. }
  15971. function rewriteLeaf(value) {
  15972. if (isValue(value)) {
  15973. replaceBody(value);
  15974. }
  15975. else {
  15976. replaceAttributes();
  15977. }
  15978. }
  15979. function rewriteLeafOpen(value) {
  15980. if (trc.iselide) {
  15981. rewriteLeaf(trc.value);
  15982. }
  15983. else {
  15984. if (isValue(value)) {
  15985. replaceBody(value);
  15986. }
  15987. else {
  15988. replaceAttributesOpen();
  15989. }
  15990. }
  15991. }
  15992. /*** END TRC METHODS**/
  15993. function rewriteUrl(template, url) {
  15994. if (renderOptions.urlRewriter) {
  15995. var rewritten = renderOptions.urlRewriter(url);
  15996. if (rewritten) {
  15997. return rewritten;
  15998. }
  15999. }
  16000. if (!renderOptions.rebaseURLs) {
  16001. return url;
  16002. }
  16003. var protpos = url.indexOf(":/");
  16004. if (url.charAt(0) === '/' || protpos !== -1 && protpos < 7) { // jslint:ok
  16005. return url;
  16006. }
  16007. else {
  16008. return renderOptions.baseURL + url;
  16009. }
  16010. }
  16011. function dumpHiddenField(/** UIParameter **/ todump) { // jslint:ok
  16012. out += "<input type=\"hidden\" ";
  16013. var isvirtual = todump.virtual;
  16014. var outattrs = {};
  16015. outattrs[isvirtual? "id" : "name"] = todump.name;
  16016. outattrs.value = todump.value;
  16017. out += fluid.dumpAttributes(outattrs);
  16018. out += " />\n";
  16019. }
  16020. var outDecoratorsImpl;
  16021. function applyAutoBind(torender, finalID) {
  16022. if (!finalID) {
  16023. // if no id is assigned so far, this is a signal that this is a "virtual" component such as
  16024. // a non-HTML UISelect which will not have physical markup.
  16025. return;
  16026. }
  16027. var tagname = trc.uselump.tagname;
  16028. var applier = renderOptions.applier;
  16029. function applyFunc() {
  16030. fluid.applyBoundChange(fluid.byId(finalID, renderOptions.document), undefined, applier);
  16031. }
  16032. if (renderOptions.autoBind && /input|select|textarea/.test(tagname)
  16033. && !renderedbindings[finalID]) {
  16034. var decorators = [{jQuery: ["change", applyFunc]}];
  16035. // Work around bug 193: http://webbugtrack.blogspot.com/2007/11/bug-193-onchange-does-not-fire-properly.html
  16036. if ($.browser.msie && tagname === "input"
  16037. && /radio|checkbox/.test(trc.attrcopy.type)) {
  16038. decorators.push({jQuery: ["click", applyFunc]});
  16039. }
  16040. if ($.browser.safari && tagname === "input" && trc.attrcopy.type === "radio") {
  16041. decorators.push({jQuery: ["keyup", applyFunc]});
  16042. }
  16043. outDecoratorsImpl(torender, decorators, trc.attrcopy, finalID); // jslint:ok - forward reference
  16044. }
  16045. }
  16046. function dumpBoundFields(/** UIBound**/ torender, parent) { // jslint:ok - whitespace
  16047. if (torender) {
  16048. var holder = parent? parent : torender;
  16049. if (renderOptions.fossils && holder.valuebinding) {
  16050. var fossilKey = holder.submittingname || torender.finalID;
  16051. // TODO: this will store multiple times for each member of a UISelect
  16052. renderOptions.fossils[fossilKey] = {
  16053. name: fossilKey,
  16054. EL: holder.valuebinding,
  16055. oldvalue: holder.value
  16056. };
  16057. // But this has to happen multiple times
  16058. applyAutoBind(torender, torender.finalID);
  16059. }
  16060. if (torender.fossilizedbinding) {
  16061. dumpHiddenField(torender.fossilizedbinding);
  16062. }
  16063. if (torender.fossilizedshaper) {
  16064. dumpHiddenField(torender.fossilizedshaper);
  16065. }
  16066. }
  16067. }
  16068. function dumpSelectionBindings(uiselect) {
  16069. if (!renderedbindings[uiselect.selection.fullID]) {
  16070. renderedbindings[uiselect.selection.fullID] = true; // set this true early so that selection does not autobind twice
  16071. dumpBoundFields(uiselect.selection);
  16072. dumpBoundFields(uiselect.optionlist);
  16073. dumpBoundFields(uiselect.optionnames);
  16074. }
  16075. }
  16076. function isSelectedValue(torender, value) {
  16077. var selection = torender.selection;
  16078. return selection.value && typeof(selection.value) !== "string" && typeof(selection.value.length) === "number" ?
  16079. $.inArray(value, selection.value, value) !== -1 :
  16080. selection.value === value;
  16081. }
  16082. function getRelativeComponent(component, relativeID) {
  16083. component = component.parent;
  16084. while (relativeID.indexOf("..::") === 0) {
  16085. relativeID = relativeID.substring(4);
  16086. component = component.parent;
  16087. }
  16088. return component.childmap[relativeID];
  16089. }
  16090. function adjustForID(attrcopy, component, late, forceID) {
  16091. if (!late) {
  16092. delete attrcopy["rsf:id"];
  16093. }
  16094. if (component.finalID !== undefined) {
  16095. attrcopy.id = component.finalID;
  16096. }
  16097. else if (forceID !== undefined) {
  16098. attrcopy.id = forceID;
  16099. }
  16100. else {
  16101. if (attrcopy.id || late) {
  16102. attrcopy.id = component.fullID;
  16103. }
  16104. }
  16105. var count = 1;
  16106. var baseid = attrcopy.id;
  16107. while (renderOptions.document.getElementById(attrcopy.id) || usedIDs[attrcopy.id]) {
  16108. attrcopy.id = baseid + "-" + (count++);
  16109. }
  16110. component.finalID = attrcopy.id;
  16111. return attrcopy.id;
  16112. }
  16113. function assignSubmittingName(attrcopy, component, parent) {
  16114. var submitting = parent || component;
  16115. // if a submittingName is required, we must already go out to the document to
  16116. // uniquify the id that it will be derived from
  16117. adjustForID(attrcopy, component, true, component.fullID);
  16118. if (submitting.submittingname === undefined && submitting.willinput !== false) {
  16119. submitting.submittingname = submitting.finalID || submitting.fullID;
  16120. }
  16121. return submitting.submittingname;
  16122. }
  16123. function explodeDecorators(decorators) {
  16124. var togo = [];
  16125. if (decorators.type) {
  16126. togo[0] = decorators;
  16127. }
  16128. else {
  16129. for (var key in decorators) {
  16130. if (key === "$") {key = "jQuery";}
  16131. var value = decorators[key];
  16132. var decorator = {
  16133. type: key
  16134. };
  16135. if (key === "jQuery") {
  16136. decorator.func = value[0];
  16137. decorator.args = value.slice(1);
  16138. }
  16139. else if (key === "addClass" || key === "removeClass") {
  16140. decorator.classes = value;
  16141. }
  16142. else if (key === "attrs") {
  16143. decorator.attributes = value;
  16144. }
  16145. else if (key === "identify") {
  16146. decorator.key = value;
  16147. }
  16148. togo[togo.length] = decorator;
  16149. }
  16150. }
  16151. return togo;
  16152. }
  16153. outDecoratorsImpl = function(torender, decorators, attrcopy, finalID) {
  16154. renderOptions.idMap = renderOptions.idMap || {};
  16155. for (var i = 0; i < decorators.length; ++i) {
  16156. var decorator = decorators[i];
  16157. var type = decorator.type;
  16158. if (!type) {
  16159. var explodedDecorators = explodeDecorators(decorator);
  16160. outDecoratorsImpl(torender, explodedDecorators, attrcopy, finalID);
  16161. continue;
  16162. }
  16163. if (type === "$") {type = decorator.type = "jQuery";}
  16164. if (type === "jQuery" || type === "event" || type === "fluid") {
  16165. var id = adjustForID(attrcopy, torender, true, finalID);
  16166. if (decorator.ids === undefined) {
  16167. decorator.ids = [];
  16168. decoratorQueue[decoratorQueue.length] = decorator;
  16169. }
  16170. decorator.ids.push(id);
  16171. }
  16172. // honour these remaining types immediately
  16173. else if (type === "attrs") {
  16174. fluid.each(decorator.attributes, function(value, key) {
  16175. if (value === null || value === undefined) {
  16176. delete attrcopy[key];
  16177. }
  16178. else {
  16179. attrcopy[key] = fluid.XMLEncode(value);
  16180. }
  16181. }); // jslint:ok - function within loop
  16182. }
  16183. else if (type === "addClass" || type === "removeClass") {
  16184. var fakeNode = {
  16185. nodeType: 1,
  16186. className: attrcopy["class"] || ""
  16187. };
  16188. renderOptions.jQuery(fakeNode)[type](decorator.classes);
  16189. attrcopy["class"] = fakeNode.className;
  16190. }
  16191. else if (type === "identify") {
  16192. var id = adjustForID(attrcopy, torender, true, finalID); // jslint:ok - scoping
  16193. renderOptions.idMap[decorator.key] = id;
  16194. }
  16195. else if (type !== "null") {
  16196. fluid.log("Unrecognised decorator of type " + type + " found at component of ID " + finalID);
  16197. }
  16198. }
  16199. };
  16200. function outDecorators(torender, attrcopy) {
  16201. if (!torender.decorators) {return;}
  16202. if (torender.decorators.length === undefined) {
  16203. torender.decorators = explodeDecorators(torender.decorators);
  16204. }
  16205. outDecoratorsImpl(torender, torender.decorators, attrcopy);
  16206. }
  16207. function dumpBranchHead(branch, targetlump) {
  16208. if (targetlump.elide) {
  16209. return;
  16210. }
  16211. var attrcopy = {};
  16212. $.extend(true, attrcopy, targetlump.attributemap);
  16213. adjustForID(attrcopy, branch); // jslint:ok - forward reference
  16214. outDecorators(branch, attrcopy);
  16215. out += "<" + targetlump.tagname + " ";
  16216. out += fluid.dumpAttributes(attrcopy);
  16217. out += ">";
  16218. }
  16219. function resolveArgs(args) {
  16220. if (!args) {return args;}
  16221. args = fluid.copy(args); // FLUID-4737: Avoid corrupting material which may have been fetched from the model
  16222. return fluid.transform(args, function (arg, index) {
  16223. upgradeBound(args, index, renderOptions.model, renderOptions.resolverGetConfig);
  16224. return args[index].value;
  16225. });
  16226. }
  16227. function degradeMessage(torender) {
  16228. if (torender.componentType === "UIMessage") {
  16229. // degrade UIMessage to UIBound by resolving the message
  16230. torender.componentType = "UIBound";
  16231. if (!renderOptions.messageLocator) {
  16232. torender.value = "[No messageLocator is configured in options - please consult documentation on options.messageSource]";
  16233. }
  16234. else {
  16235. upgradeBound(torender, "messagekey", renderOptions.model, renderOptions.resolverGetConfig);
  16236. var resArgs = resolveArgs(torender.args);
  16237. torender.value = renderOptions.messageLocator(torender.messagekey.value, resArgs);
  16238. }
  16239. }
  16240. }
  16241. function renderComponent(torender) {
  16242. var attrcopy = trc.attrcopy;
  16243. degradeMessage(torender);
  16244. var componentType = torender.componentType;
  16245. var tagname = trc.uselump.tagname;
  16246. outDecorators(torender, attrcopy);
  16247. function makeFail(torender, end) {
  16248. fluid.fail("Error in component tree - UISelectChoice with id " + torender.fullID + end);
  16249. }
  16250. if (componentType === "UIBound" || componentType === "UISelectChoice") {
  16251. var parent;
  16252. if (torender.choiceindex !== undefined) {
  16253. if (torender.parentRelativeID !== undefined) {
  16254. parent = getRelativeComponent(torender, torender.parentRelativeID);
  16255. if (!parent) {
  16256. makeFail(torender, " has parentRelativeID of " + torender.parentRelativeID + " which cannot be resolved");
  16257. }
  16258. }
  16259. else {
  16260. makeFail(torender, " does not have parentRelativeID set");
  16261. }
  16262. assignSubmittingName(attrcopy, torender, parent.selection);
  16263. dumpSelectionBindings(parent);
  16264. }
  16265. var submittingname = parent? parent.selection.submittingname : torender.submittingname;
  16266. if (!parent && torender.valuebinding) {
  16267. // Do this for all bound fields even if non submitting so that finalID is set in order to track fossils (FLUID-3387)
  16268. submittingname = assignSubmittingName(attrcopy, torender);
  16269. }
  16270. if (tagname === "input" || tagname === "textarea") {
  16271. if (submittingname !== undefined) {
  16272. attrcopy.name = submittingname;
  16273. }
  16274. }
  16275. // this needs to happen early on the client, since it may cause the allocation of the
  16276. // id in the case of a "deferred decorator". However, for server-side bindings, this
  16277. // will be an inappropriate time, unless we shift the timing of emitting the opening tag.
  16278. dumpBoundFields(torender, parent? parent.selection : null);
  16279. if (typeof(torender.value) === 'boolean' || attrcopy.type === "radio"
  16280. || attrcopy.type === "checkbox") {
  16281. var underlyingValue;
  16282. var directValue = torender.value;
  16283. if (torender.choiceindex !== undefined) {
  16284. if (!parent.optionlist.value) {
  16285. fluid.fail("Error in component tree - selection control with full ID " + parent.fullID + " has no values");
  16286. }
  16287. underlyingValue = parent.optionlist.value[torender.choiceindex];
  16288. directValue = isSelectedValue(parent, underlyingValue);
  16289. }
  16290. if (isValue(directValue)) {
  16291. if (directValue) {
  16292. attrcopy.checked = "checked";
  16293. }
  16294. else {
  16295. delete attrcopy.checked;
  16296. }
  16297. }
  16298. attrcopy.value = fluid.XMLEncode(underlyingValue? underlyingValue : "true");
  16299. rewriteLeaf(null);
  16300. }
  16301. else if (torender.value instanceof Array) {
  16302. // Cannot be rendered directly, must be fake
  16303. renderUnchanged();
  16304. }
  16305. else { // String value
  16306. var value = parent?
  16307. parent[tagname === "textarea" || tagname === "input" ? "optionlist" : "optionnames"].value[torender.choiceindex] :
  16308. torender.value; // jslint:ok - whitespace
  16309. if (tagname === "textarea") {
  16310. if (isPlaceholder(value) && torender.willinput) {
  16311. // FORCE a blank value for input components if nothing from
  16312. // model, if input was intended.
  16313. value = "";
  16314. }
  16315. rewriteLeaf(value);
  16316. }
  16317. else if (tagname === "input") {
  16318. if (torender.willinput || isValue(value)) {
  16319. attrcopy.value = fluid.XMLEncode(String(value));
  16320. }
  16321. rewriteLeaf(null);
  16322. }
  16323. else {
  16324. delete attrcopy.name;
  16325. rewriteLeafOpen(value);
  16326. }
  16327. }
  16328. }
  16329. else if (componentType === "UISelect") {
  16330. var ishtmlselect = tagname === "select";
  16331. var ismultiple = false;
  16332. if (torender.selection.value instanceof Array) {
  16333. ismultiple = true;
  16334. if (ishtmlselect) {
  16335. attrcopy.multiple = "multiple";
  16336. }
  16337. }
  16338. // assignSubmittingName is now the definitive trigger point for uniquifying output IDs
  16339. // However, if id is already assigned it is probably through attempt to decorate root select.
  16340. // in this case restore it.
  16341. var oldid = attrcopy.id;
  16342. assignSubmittingName(attrcopy, torender.selection);
  16343. if (oldid !== undefined) {
  16344. attrcopy.id = oldid;
  16345. }
  16346. if (ishtmlselect) {
  16347. // The HTML submitted value from a <select> actually corresponds
  16348. // with the selection member, not the top-level component.
  16349. if (torender.selection.willinput !== false) {
  16350. attrcopy.name = torender.selection.submittingname;
  16351. }
  16352. applyAutoBind(torender, attrcopy.id);
  16353. }
  16354. out += fluid.dumpAttributes(attrcopy);
  16355. if (ishtmlselect) {
  16356. out += ">";
  16357. var values = torender.optionlist.value;
  16358. var names = torender.optionnames === null || torender.optionnames === undefined || !torender.optionnames.value? values : torender.optionnames.value;
  16359. if (!names || !names.length) {
  16360. fluid.fail("Error in component tree - UISelect component with fullID "
  16361. + torender.fullID + " does not have optionnames set");
  16362. }
  16363. for (var i = 0; i < names.length; ++i) {
  16364. out += "<option value=\"";
  16365. var value = values[i]; //jslint:ok - scoping
  16366. if (value === null) {
  16367. value = fluid.NULL_STRING;
  16368. }
  16369. out += fluid.XMLEncode(value);
  16370. if (isSelectedValue(torender, value)) {
  16371. out += "\" selected=\"selected";
  16372. }
  16373. out += "\">";
  16374. out += fluid.XMLEncode(names[i]);
  16375. out += "</option>\n";
  16376. }
  16377. closeTag();
  16378. }
  16379. else {
  16380. dumpTemplateBody();
  16381. }
  16382. dumpSelectionBindings(torender);
  16383. }
  16384. else if (componentType === "UILink") {
  16385. var attrname = LINK_ATTRIBUTES[tagname];
  16386. if (attrname) {
  16387. degradeMessage(torender.target);
  16388. var target = torender.target.value;
  16389. if (!isValue(target)) {
  16390. target = attrcopy[attrname];
  16391. }
  16392. target = rewriteUrl(trc.uselump.parent, target);
  16393. // Note that all real browsers succeed in recovering the URL here even if it is presented in violation of XML
  16394. // seemingly due to the purest accident, the text &amp; cannot occur in a properly encoded URL :P
  16395. attrcopy[attrname] = fluid.XMLEncode(target);
  16396. }
  16397. var value; // jslint:ok
  16398. if (torender.linktext) {
  16399. degradeMessage(torender.linktext);
  16400. value = torender.linktext.value; // jslint:ok - scoping
  16401. }
  16402. if (!isValue(value)) {
  16403. replaceAttributesOpen();
  16404. }
  16405. else {
  16406. rewriteLeaf(value);
  16407. }
  16408. }
  16409. else if (torender.markup !== undefined) { // detect UIVerbatim
  16410. degradeMessage(torender.markup);
  16411. var rendered = torender.markup.value;
  16412. if (rendered === null) {
  16413. // TODO, doesn't quite work due to attr folding cf Java code
  16414. out += fluid.dumpAttributes(attrcopy);
  16415. out += ">";
  16416. renderUnchanged();
  16417. }
  16418. else {
  16419. if (!trc.iselide) {
  16420. out += fluid.dumpAttributes(attrcopy);
  16421. out += ">";
  16422. }
  16423. out += rendered;
  16424. closeTag();
  16425. }
  16426. }
  16427. if (attrcopy.id !== undefined) {
  16428. usedIDs[attrcopy.id] = true;
  16429. }
  16430. }
  16431. function rewriteIDRelation(context) {
  16432. var attrname;
  16433. var attrval = trc.attrcopy["for"];
  16434. if (attrval !== undefined) {
  16435. attrname = "for";
  16436. }
  16437. else {
  16438. attrval = trc.attrcopy.headers;
  16439. if (attrval !== undefined) {
  16440. attrname = "headers";
  16441. }
  16442. }
  16443. if (!attrname) {return;}
  16444. var tagname = trc.uselump.tagname;
  16445. if (attrname === "for" && tagname !== "label") {return;}
  16446. if (attrname === "headers" && tagname !== "td" && tagname !== "th") {return;}
  16447. var rewritten = rewritemap[getRewriteKey(trc.uselump.parent, context, attrval)];
  16448. if (rewritten !== undefined) {
  16449. trc.attrcopy[attrname] = rewritten;
  16450. }
  16451. }
  16452. function renderComment(message) {
  16453. out += ("<!-- " + fluid.XMLEncode(message) + "-->");
  16454. }
  16455. function renderDebugMessage(message) {
  16456. out += "<span style=\"background-color:#FF466B;color:white;padding:1px;\">";
  16457. out += message;
  16458. out += "</span><br/>";
  16459. }
  16460. function reportPath(/*UIComponent*/ branch) { // jslint:ok - whitespace
  16461. var path = branch.fullID;
  16462. return !path ? "component tree root" : "full path " + path;
  16463. }
  16464. function renderComponentSystem(context, torendero, lump) {
  16465. var lumpindex = lump.lumpindex;
  16466. var lumps = lump.parent.lumps;
  16467. var nextpos = -1;
  16468. var outerendopen = lumps[lumpindex + 1];
  16469. var outerclose = lump.close_tag;
  16470. nextpos = outerclose.lumpindex + 1;
  16471. var payloadlist = lump.downmap? lump.downmap["payload-component"] : null;
  16472. var payload = payloadlist? payloadlist[0] : null;
  16473. var iselide = lump.rsfID.charCodeAt(0) === 126; // "~"
  16474. var endopen = outerendopen;
  16475. var close = outerclose;
  16476. var uselump = lump;
  16477. var attrcopy = {};
  16478. $.extend(true, attrcopy, (payload === null? lump : payload).attributemap);
  16479. trc.attrcopy = attrcopy;
  16480. trc.uselump = uselump;
  16481. trc.endopen = endopen;
  16482. trc.close = close;
  16483. trc.nextpos = nextpos;
  16484. trc.iselide = iselide;
  16485. rewriteIDRelation(context);
  16486. if (torendero === null) {
  16487. if (lump.rsfID.indexOf("scr=") === (iselide? 1 : 0)) {
  16488. var scrname = lump.rsfID.substring(4 + (iselide? 1 : 0));
  16489. if (scrname === "ignore") {
  16490. nextpos = trc.close.lumpindex + 1;
  16491. }
  16492. else if (scrname === "rewrite-url") {
  16493. torendero = {componentType: "UILink", target: {}};
  16494. }
  16495. else {
  16496. openTag();
  16497. replaceAttributesOpen();
  16498. nextpos = trc.endopen.lumpindex;
  16499. }
  16500. }
  16501. }
  16502. if (torendero !== null) {
  16503. // else there IS a component and we are going to render it. First make
  16504. // sure we render any preamble.
  16505. if (payload) {
  16506. trc.endopen = lumps[payload.lumpindex + 1];
  16507. trc.close = payload.close_tag;
  16508. trc.uselump = payload;
  16509. dumpTillLump(lumps, lumpindex, payload.lumpindex);
  16510. lumpindex = payload.lumpindex;
  16511. }
  16512. adjustForID(attrcopy, torendero);
  16513. //decoratormanager.decorate(torendero.decorators, uselump.getTag(), attrcopy);
  16514. // ALWAYS dump the tag name, this can never be rewritten. (probably?!)
  16515. openTag();
  16516. renderComponent(torendero);
  16517. // if there is a payload, dump the postamble.
  16518. if (payload !== null) {
  16519. // the default case is initialised to tag close
  16520. if (trc.nextpos === nextpos) {
  16521. dumpTillLump(lumps, trc.close.lumpindex + 1, outerclose.lumpindex + 1);
  16522. }
  16523. }
  16524. nextpos = trc.nextpos;
  16525. }
  16526. return nextpos;
  16527. }
  16528. var renderRecurse;
  16529. function renderContainer(child, targetlump) {
  16530. var t2 = targetlump.parent;
  16531. var firstchild = t2.lumps[targetlump.lumpindex + 1];
  16532. if (child.children !== undefined) {
  16533. dumpBranchHead(child, targetlump);
  16534. }
  16535. else {
  16536. renderComponentSystem(child.parent, child, targetlump);
  16537. }
  16538. renderRecurse(child, targetlump, firstchild);
  16539. }
  16540. fetchComponent = function(basecontainer, id, lump) {
  16541. if (id.indexOf("msg=") === 0) {
  16542. var key = id.substring(4);
  16543. return {componentType: "UIMessage", messagekey: key};
  16544. }
  16545. while (basecontainer) {
  16546. var togo = basecontainer.childmap[id];
  16547. if (togo) {
  16548. return togo;
  16549. }
  16550. basecontainer = basecontainer.parent;
  16551. }
  16552. return null;
  16553. };
  16554. function fetchComponents(basecontainer, id) {
  16555. var togo;
  16556. while (basecontainer) {
  16557. togo = basecontainer.childmap[id];
  16558. if (togo) {
  16559. break;
  16560. }
  16561. basecontainer = basecontainer.parent;
  16562. }
  16563. return togo;
  16564. }
  16565. function findChild(sourcescope, child) {
  16566. var split = fluid.SplitID(child.ID);
  16567. var headlumps = sourcescope.downmap[child.ID];
  16568. if (!headlumps) {
  16569. headlumps = sourcescope.downmap[split.prefix + ":"];
  16570. }
  16571. return headlumps? headlumps[0] : null;
  16572. }
  16573. renderRecurse = function(basecontainer, parentlump, baselump) {
  16574. var renderindex = baselump.lumpindex;
  16575. var basedepth = parentlump.nestingdepth;
  16576. var t1 = parentlump.parent;
  16577. var rendered;
  16578. if (debugMode) {
  16579. rendered = {};
  16580. }
  16581. while (true) {
  16582. renderindex = dumpScan(t1.lumps, renderindex, basedepth, !parentlump.elide, false);
  16583. if (renderindex === t1.lumps.length) {
  16584. break;
  16585. }
  16586. var lump = t1.lumps[renderindex];
  16587. var id = lump.rsfID;
  16588. // new stopping rule - we may have been inside an elided tag
  16589. if (lump.nestingdepth < basedepth || id === undefined) {
  16590. break;
  16591. }
  16592. if (id.charCodeAt(0) === 126) { // "~"
  16593. id = id.substring(1);
  16594. }
  16595. //var ismessagefor = id.indexOf("message-for:") === 0;
  16596. if (id.indexOf(':') !== -1) {
  16597. var prefix = fluid.getPrefix(id);
  16598. var children = fetchComponents(basecontainer, prefix);
  16599. var finallump = lump.uplump.finallump[prefix];
  16600. var closefinal = finallump.close_tag;
  16601. if (children) {
  16602. for (var i = 0; i < children.length; ++i) {
  16603. var child = children[i];
  16604. if (child.children) { // it is a branch
  16605. if (debugMode) {
  16606. rendered[child.fullID] = true;
  16607. }
  16608. var targetlump = branchmap[child.fullID];
  16609. if (targetlump) {
  16610. if (debugMode) {
  16611. renderComment("Branching for " + child.fullID + " from "
  16612. + fluid.debugLump(lump) + " to " + fluid.debugLump(targetlump));
  16613. }
  16614. renderContainer(child, targetlump);
  16615. if (debugMode) {
  16616. renderComment("Branch returned for " + child.fullID
  16617. + fluid.debugLump(lump) + " to " + fluid.debugLump(targetlump));
  16618. }
  16619. }
  16620. else if (debugMode) {
  16621. renderDebugMessage(
  16622. "No matching template branch found for branch container with full ID "
  16623. + child.fullID
  16624. + " rendering from parent template branch "
  16625. + fluid.debugLump(baselump)); // jslint:ok - line breaking
  16626. }
  16627. }
  16628. else { // repetitive leaf
  16629. var targetlump = findChild(parentlump, child); // jslint:ok - scoping
  16630. if (!targetlump) {
  16631. if (debugMode) {
  16632. renderDebugMessage("Repetitive leaf with full ID " + child.fullID
  16633. + " could not be rendered from parent template branch "
  16634. + fluid.debugLump(baselump)); // jslint:ok - line breaking
  16635. }
  16636. continue;
  16637. }
  16638. var renderend = renderComponentSystem(basecontainer, child, targetlump);
  16639. var wasopentag = renderend < t1.lumps.lengtn && t1.lumps[renderend].nestingdepth >= targetlump.nestingdepth;
  16640. var newbase = child.children? child : basecontainer;
  16641. if (wasopentag) {
  16642. renderRecurse(newbase, targetlump, t1.lumps[renderend]);
  16643. renderend = targetlump.close_tag.lumpindex + 1;
  16644. }
  16645. if (i !== children.length - 1) {
  16646. // TODO - fix this bug in RSF Server!
  16647. if (renderend < closefinal.lumpindex) {
  16648. dumpScan(t1.lumps, renderend, targetlump.nestingdepth - 1, false, false);
  16649. }
  16650. }
  16651. else {
  16652. dumpScan(t1.lumps, renderend, targetlump.nestingdepth, true, false);
  16653. }
  16654. }
  16655. } // end for each repetitive child
  16656. }
  16657. else {
  16658. if (debugMode) {
  16659. renderDebugMessage("No branch container with prefix "
  16660. + prefix + ": found in container "
  16661. + reportPath(basecontainer)
  16662. + " rendering at template position " + fluid.debugLump(baselump)
  16663. + ", skipping");
  16664. }
  16665. }
  16666. renderindex = closefinal.lumpindex + 1;
  16667. if (debugMode) {
  16668. renderComment("Stack returned from branch for ID " + id + " to "
  16669. + fluid.debugLump(baselump) + ": skipping from " + fluid.debugLump(lump)
  16670. + " to " + fluid.debugLump(closefinal));
  16671. }
  16672. }
  16673. else {
  16674. var component;
  16675. if (id) {
  16676. component = fetchComponent(basecontainer, id, lump);
  16677. if (debugMode && component) {
  16678. rendered[component.fullID] = true;
  16679. }
  16680. }
  16681. if (component && component.children !== undefined) {
  16682. renderContainer(component);
  16683. renderindex = lump.close_tag.lumpindex + 1;
  16684. }
  16685. else {
  16686. renderindex = renderComponentSystem(basecontainer, component, lump);
  16687. }
  16688. }
  16689. if (renderindex === t1.lumps.length) {
  16690. break;
  16691. }
  16692. }
  16693. if (debugMode) {
  16694. var children = basecontainer.children; // jslint:ok - scoping
  16695. for (var key = 0; key < children.length; ++key) {
  16696. var child = children[key]; // jslint:ok - scoping
  16697. if (!rendered[child.fullID]) {
  16698. renderDebugMessage("Component "
  16699. + child.componentType + " with full ID "
  16700. + child.fullID + " could not be found within template "
  16701. + fluid.debugLump(baselump));
  16702. }
  16703. }
  16704. }
  16705. };
  16706. function renderCollect(collump) {
  16707. dumpTillLump(collump.parent.lumps, collump.lumpindex, collump.close_tag.lumpindex + 1);
  16708. }
  16709. // Let us pray
  16710. function renderCollects() {
  16711. for (var key in collected) {
  16712. var collist = collected[key];
  16713. for (var i = 0; i < collist.length; ++i) {
  16714. renderCollect(collist[i]);
  16715. }
  16716. }
  16717. }
  16718. function processDecoratorQueue() {
  16719. for (var i = 0; i < decoratorQueue.length; ++i) {
  16720. var decorator = decoratorQueue[i];
  16721. for (var j = 0; j < decorator.ids.length; ++j) {
  16722. var id = decorator.ids[j];
  16723. var node = fluid.byId(id, renderOptions.document);
  16724. if (!node) {
  16725. fluid.fail("Error during rendering - component with id " + id
  16726. + " which has a queued decorator was not found in the output markup");
  16727. }
  16728. if (decorator.type === "jQuery") {
  16729. var jnode = renderOptions.jQuery(node);
  16730. jnode[decorator.func].apply(jnode, fluid.makeArray(decorator.args));
  16731. }
  16732. else if (decorator.type === "fluid") {
  16733. var args = decorator.args;
  16734. if (!args) {
  16735. var thisContainer = renderOptions.jQuery(node);
  16736. if (!decorator.container) {
  16737. decorator.container = thisContainer;
  16738. }
  16739. else {
  16740. decorator.container.push(node);
  16741. }
  16742. args = [thisContainer, decorator.options];
  16743. }
  16744. var that = renderer.invokeFluidDecorator(decorator.func, args, id, i, options);
  16745. decorator.that = that;
  16746. }
  16747. else if (decorator.type === "event") {
  16748. node[decorator.event] = decorator.handler;
  16749. }
  16750. }
  16751. }
  16752. }
  16753. that.renderTemplates = function () {
  16754. tree = fixupTree(tree, options.model, options.resolverGetConfig);
  16755. var template = templates[0];
  16756. resolveBranches(templates.globalmap, tree, template.rootlump);
  16757. renderedbindings = {};
  16758. renderCollects();
  16759. renderRecurse(tree, template.rootlump, template.lumps[template.firstdocumentindex]);
  16760. return out;
  16761. };
  16762. that.processDecoratorQueue = function () {
  16763. processDecoratorQueue();
  16764. };
  16765. return that;
  16766. };
  16767. jQuery.extend(true, fluid.renderer, renderer);
  16768. /*
  16769. * This function is unsupported: It is not really intended for use by implementors.
  16770. */
  16771. fluid.ComponentReference = function (reference) {
  16772. this.reference = reference;
  16773. };
  16774. // Explodes a raw "hash" into a list of UIOutput/UIBound entries
  16775. fluid.explode = function (hash, basepath) {
  16776. var togo = [];
  16777. for (var key in hash) {
  16778. var binding = basepath === undefined ? key : basepath + "." + key;
  16779. togo[togo.length] = {ID: key, value: hash[key], valuebinding: binding};
  16780. }
  16781. return togo;
  16782. };
  16783. /**
  16784. * A common utility function to make a simple view of rows, where each row has a selection control and a label
  16785. * @param {Object} optionlist An array of the values of the options in the select
  16786. * @param {Object} opts An object with this structure: {
  16787. selectID: "",
  16788. rowID: "",
  16789. inputID: "",
  16790. labelID: ""
  16791. }
  16792. */
  16793. fluid.explodeSelectionToInputs = function (optionlist, opts) {
  16794. return fluid.transform(optionlist, function (option, index) {
  16795. return {
  16796. ID: opts.rowID,
  16797. children: [
  16798. {ID: opts.inputID, parentRelativeID: "..::" + opts.selectID, choiceindex: index},
  16799. {ID: opts.labelID, parentRelativeID: "..::" + opts.selectID, choiceindex: index}]
  16800. };
  16801. });
  16802. };
  16803. fluid.resolveMessageSource = function (messageSource) {
  16804. if (messageSource.type === "data") {
  16805. if (messageSource.url === undefined) {
  16806. return fluid.messageLocator(messageSource.messages, messageSource.resolveFunc);
  16807. }
  16808. else {
  16809. // TODO: fetch via AJAX, and convert format if necessary
  16810. }
  16811. } // jslint:ok - empty block
  16812. else if (messageSource.type === "resolver") {
  16813. return messageSource.resolver.resolve;
  16814. }
  16815. };
  16816. fluid.renderTemplates = function (templates, tree, options, fossilsIn) {
  16817. var renderer = fluid.renderer(templates, tree, options, fossilsIn);
  16818. var rendered = renderer.renderTemplates();
  16819. return rendered;
  16820. };
  16821. /** A driver to render and bind an already parsed set of templates onto
  16822. * a node. See documentation for fluid.selfRender.
  16823. * @param templates A parsed template set, as returned from fluid.selfRender or
  16824. * fluid.parseTemplates.
  16825. */
  16826. fluid.reRender = function (templates, node, tree, options) {
  16827. options = options || {};
  16828. var renderer = fluid.renderer(templates, tree, options, options.fossils);
  16829. options = renderer.options;
  16830. // Empty the node first, to head off any potential id collisions when rendering
  16831. node = fluid.unwrap(node);
  16832. var lastFocusedElement = fluid.getLastFocusedElement ? fluid.getLastFocusedElement() : null;
  16833. var lastId;
  16834. if (lastFocusedElement && fluid.dom.isContainer(node, lastFocusedElement)) {
  16835. lastId = lastFocusedElement.id;
  16836. }
  16837. if ($.browser.msie) {
  16838. options.jQuery(node).empty(); //- this operation is very slow.
  16839. }
  16840. else {
  16841. node.innerHTML = "";
  16842. }
  16843. var rendered = renderer.renderTemplates();
  16844. if (options.renderRaw) {
  16845. rendered = fluid.XMLEncode(rendered);
  16846. rendered = rendered.replace(/\n/g, "<br/>");
  16847. }
  16848. if (options.model) {
  16849. fluid.bindFossils(node, options.model, options.fossils);
  16850. }
  16851. if ($.browser.msie) {
  16852. options.jQuery(node).html(rendered);
  16853. }
  16854. else {
  16855. node.innerHTML = rendered;
  16856. }
  16857. renderer.processDecoratorQueue();
  16858. if (lastId) {
  16859. var element = fluid.byId(lastId, options.document);
  16860. if (element) {
  16861. options.jQuery(element).focus();
  16862. }
  16863. }
  16864. return templates;
  16865. };
  16866. function findNodeValue(rootNode) {
  16867. var node = fluid.dom.iterateDom(rootNode, function (node) {
  16868. // NB, in Firefox at least, comment and cdata nodes cannot be distinguished!
  16869. return node.nodeType === 8 || node.nodeType === 4 ? "stop" : null;
  16870. }, true); // jslint:ok
  16871. var value = node.nodeValue;
  16872. if (value.indexOf("[CDATA[") === 0) {
  16873. return value.substring(6, value.length - 2);
  16874. }
  16875. else {
  16876. return value;
  16877. }
  16878. }
  16879. fluid.extractTemplate = function (node, armouring) {
  16880. if (!armouring) {
  16881. return node.innerHTML;
  16882. }
  16883. else {
  16884. return findNodeValue(node);
  16885. }
  16886. };
  16887. /** A slightly generalised version of fluid.selfRender that does not assume that the
  16888. * markup used to source the template is within the target node.
  16889. * @param source Either a structure {node: node, armouring: armourstyle} or a string
  16890. * holding a literal template
  16891. * @param target The node to receive the rendered markup
  16892. * @param tree, options, return as for fluid.selfRender
  16893. */
  16894. fluid.render = function (source, target, tree, options) {
  16895. options = options || {};
  16896. var template = source;
  16897. if (typeof(source) === "object") {
  16898. template = fluid.extractTemplate(fluid.unwrap(source.node), source.armouring);
  16899. }
  16900. target = fluid.unwrap(target);
  16901. var resourceSpec = {base: {resourceText: template,
  16902. href: ".", resourceKey: ".", cutpoints: options.cutpoints}
  16903. };
  16904. var templates = fluid.parseTemplates(resourceSpec, ["base"], options);
  16905. return fluid.reRender(templates, target, tree, options);
  16906. };
  16907. /** A simple driver for single node self-templating. Treats the markup for a
  16908. * node as a template, parses it into a template structure, renders it using
  16909. * the supplied component tree and options, then replaces the markup in the
  16910. * node with the rendered markup, and finally performs any required data
  16911. * binding. The parsed template is returned for use with a further call to
  16912. * reRender.
  16913. * @param node The node both holding the template, and whose markup is to be
  16914. * replaced with the rendered result.
  16915. * @param tree The component tree to be rendered.
  16916. * @param options An options structure to configure the rendering and binding process.
  16917. * @return A templates structure, suitable for a further call to fluid.reRender or
  16918. * fluid.renderTemplates.
  16919. */
  16920. fluid.selfRender = function (node, tree, options) {
  16921. options = options || {};
  16922. return fluid.render({node: node, armouring: options.armouring}, node, tree, options);
  16923. };
  16924. })(jQuery, fluid_1_5);
  16925. /*
  16926. Copyright 2008-2010 University of Cambridge
  16927. Copyright 2008-2009 University of Toronto
  16928. Copyright 2010-2011 Lucendo Development Ltd.
  16929. Licensed under the Educational Community License (ECL), Version 2.0 or the New
  16930. BSD license. You may not use this file except in compliance with one these
  16931. Licenses.
  16932. You may obtain a copy of the ECL 2.0 License and BSD License at
  16933. https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt
  16934. */
  16935. // Declare dependencies
  16936. /*global fluid_1_5:true, jQuery*/
  16937. // JSLint options
  16938. /*jslint white: true, funcinvoke: true, continue: true, elsecatch: true, operator: true, jslintok:true, undef: true, newcap: true, regexp: true, bitwise: true, browser: true, forin: true, maxerr: 100, indent: 4 */
  16939. fluid_1_5 = fluid_1_5 || {};
  16940. (function ($, fluid) {
  16941. if (!fluid.renderer) {
  16942. fluid.fail("fluidRenderer.js is a necessary dependency of RendererUtilities");
  16943. }
  16944. /** Returns an array of size count, filled with increasing integers,
  16945. * starting at 0 or at the index specified by first.
  16946. */
  16947. fluid.iota = function (count, first) {
  16948. first = first || 0;
  16949. var togo = [];
  16950. for (var i = 0; i < count; ++i) {
  16951. togo[togo.length] = first++;
  16952. }
  16953. return togo;
  16954. };
  16955. // TODO: API status of these 3 functions is uncertain. So far, they have never
  16956. // appeared in documentation. API change required for Infusion 1.5 to include instantiator.
  16957. // This is inconvenient, but in the future, all component discovery will require this.
  16958. fluid.renderer.visitDecorators = function(that, visitor, instantiator) {
  16959. fluid.visitComponentChildren(that, function(component, name) {
  16960. if (name.indexOf(fluid.renderer.decoratorComponentPrefix) === 0) {
  16961. visitor(component, name);
  16962. }
  16963. }, {flat: true, instantiator: instantiator});
  16964. };
  16965. fluid.renderer.clearDecorators = function(instantiator, that) {
  16966. fluid.renderer.visitDecorators(that, function(component, name) {
  16967. instantiator.clearComponent(that, name);
  16968. }, instantiator);
  16969. };
  16970. fluid.renderer.getDecoratorComponents = function(that, instantiator) {
  16971. var togo = {};
  16972. fluid.renderer.visitDecorators(that, function(component, name) {
  16973. togo[name] = component;
  16974. }, instantiator);
  16975. return togo;
  16976. };
  16977. // Utilities for coordinating options in renderer components - this code is all pretty
  16978. // dreadful and needs to be organised as a suitable set of defaults and policies
  16979. fluid.renderer.modeliseOptions = function (options, defaults, baseOptions) {
  16980. return $.extend({}, defaults, options, fluid.filterKeys(baseOptions, ["model", "applier"]));
  16981. };
  16982. fluid.renderer.reverseMerge = function (target, source, names) {
  16983. names = fluid.makeArray(names);
  16984. fluid.each(names, function (name) {
  16985. if (target[name] === undefined && source[name] !== undefined) {
  16986. target[name] = source[name];
  16987. }
  16988. });
  16989. };
  16990. /** "Renderer component" infrastructure **/
  16991. // TODO: fix this up with IoC and improved handling of templateSource as well as better
  16992. // options layout (model appears in both rOpts and eOpts)
  16993. fluid.renderer.createRendererSubcomponent = function (container, selectors, options, baseObject, fossils) {
  16994. options = options || {};
  16995. var source = options.templateSource ? options.templateSource : {node: $(container)};
  16996. var rendererOptions = fluid.renderer.modeliseOptions(options.rendererOptions, null, baseObject);
  16997. rendererOptions.fossils = fossils || {};
  16998. if (container.jquery) {
  16999. var cascadeOptions = {
  17000. document: container[0].ownerDocument,
  17001. jQuery: container.constructor
  17002. };
  17003. fluid.renderer.reverseMerge(rendererOptions, cascadeOptions, fluid.keys(cascadeOptions));
  17004. }
  17005. var expanderOptions = fluid.renderer.modeliseOptions(options.expanderOptions, {ELstyle: "${}"}, baseObject);
  17006. fluid.renderer.reverseMerge(expanderOptions, options, ["resolverGetConfig", "resolverSetConfig"]);
  17007. var that = {};
  17008. if (!options.noexpand) {
  17009. that.expander = fluid.renderer.makeProtoExpander(expanderOptions);
  17010. }
  17011. var templates = null;
  17012. that.render = function (tree) {
  17013. var cutpointFn = options.cutpointGenerator || "fluid.renderer.selectorsToCutpoints";
  17014. rendererOptions.cutpoints = rendererOptions.cutpoints || fluid.invokeGlobalFunction(cutpointFn, [selectors, options]);
  17015. container = typeof(container) === "function" ? container() : $(container);
  17016. if (templates) {
  17017. fluid.clear(rendererOptions.fossils);
  17018. fluid.reRender(templates, container, tree, rendererOptions);
  17019. }
  17020. else {
  17021. if (typeof(source) === "function") { // TODO: make a better attempt than this at asynchrony
  17022. source = source();
  17023. }
  17024. templates = fluid.render(source, container, tree, rendererOptions);
  17025. }
  17026. };
  17027. return that;
  17028. };
  17029. fluid.defaults("fluid.rendererComponent", {
  17030. gradeNames: ["fluid.viewComponent"],
  17031. initFunction: "fluid.initRendererComponent",
  17032. mergePolicy: {
  17033. protoTree: "noexpand, replace",
  17034. parentBundle: "nomerge"
  17035. },
  17036. rendererOptions: {
  17037. autoBind: true
  17038. },
  17039. events: {
  17040. prepareModelForRender: null,
  17041. onRenderTree: null,
  17042. afterRender: null,
  17043. produceTree: "unicast"
  17044. }
  17045. });
  17046. fluid.initRendererComponent = function (componentName, container, options) {
  17047. var that = fluid.initView(componentName, container, options, {gradeNames: ["fluid.rendererComponent"]});
  17048. fluid.fetchResources(that.options.resources); // TODO: deal with asynchrony
  17049. var rendererOptions = fluid.renderer.modeliseOptions(that.options.rendererOptions, null, that);
  17050. if (!that.options.noUpgradeDecorators) {
  17051. fluid.withInstantiator(that, function(currentInst) {
  17052. rendererOptions.instantiator = currentInst;
  17053. rendererOptions.parentComponent = that;
  17054. });
  17055. }
  17056. var messageResolver;
  17057. if (!rendererOptions.messageSource && that.options.strings) {
  17058. messageResolver = fluid.messageResolver({
  17059. messageBase: that.options.strings,
  17060. resolveFunc: that.options.messageResolverFunction,
  17061. parents: fluid.makeArray(that.options.parentBundle)
  17062. });
  17063. rendererOptions.messageSource = {type: "resolver", resolver: messageResolver};
  17064. }
  17065. fluid.renderer.reverseMerge(rendererOptions, that.options, ["resolverGetConfig", "resolverSetConfig"]);
  17066. var rendererFnOptions = $.extend({}, that.options.rendererFnOptions, {
  17067. rendererOptions: rendererOptions,
  17068. repeatingSelectors: that.options.repeatingSelectors,
  17069. selectorsToIgnore: that.options.selectorsToIgnore,
  17070. expanderOptions: {
  17071. envAdd: {styles: that.options.styles}
  17072. }
  17073. });
  17074. if (that.options.resources && that.options.resources.template) {
  17075. rendererFnOptions.templateSource = function () { // TODO: don't obliterate, multitemplates, etc.
  17076. return that.options.resources.template.resourceText;
  17077. };
  17078. }
  17079. var produceTree = that.events.produceTree;
  17080. produceTree.addListener(function() {
  17081. return that.options.protoTree;
  17082. });
  17083. if (that.options.produceTree) {
  17084. produceTree.addListener(that.options.produceTree);
  17085. }
  17086. fluid.renderer.reverseMerge(rendererFnOptions, that.options, ["resolverGetConfig", "resolverSetConfig"]);
  17087. if (rendererFnOptions.rendererTargetSelector) {
  17088. container = function () {return that.dom.locate(rendererFnOptions.rendererTargetSelector); };
  17089. }
  17090. var renderer = {
  17091. fossils: {},
  17092. boundPathForNode: function (node) {
  17093. return fluid.boundPathForNode(node, renderer.fossils);
  17094. }
  17095. };
  17096. var rendererSub = fluid.renderer.createRendererSubcomponent(container, that.options.selectors, rendererFnOptions, that, renderer.fossils);
  17097. that.renderer = $.extend(renderer, rendererSub);
  17098. if (messageResolver) {
  17099. that.messageResolver = messageResolver;
  17100. }
  17101. that.refreshView = renderer.refreshView = function () {
  17102. if (rendererOptions.instantiator && rendererOptions.parentComponent) {
  17103. fluid.renderer.clearDecorators(rendererOptions.instantiator, rendererOptions.parentComponent);
  17104. }
  17105. that.events.prepareModelForRender.fire(that.model, that.applier, that);
  17106. var tree = produceTree.fire(that);
  17107. if (that.renderer.expander) {
  17108. tree = that.renderer.expander(tree);
  17109. }
  17110. that.events.onRenderTree.fire(that, tree);
  17111. that.renderer.render(tree);
  17112. that.events.afterRender.fire(that);
  17113. };
  17114. if (that.options.renderOnInit) {
  17115. that.refreshView();
  17116. }
  17117. return that;
  17118. };
  17119. var removeSelectors = function (selectors, selectorsToIgnore) {
  17120. fluid.each(fluid.makeArray(selectorsToIgnore), function (selectorToIgnore) {
  17121. delete selectors[selectorToIgnore];
  17122. });
  17123. return selectors;
  17124. };
  17125. var markRepeated = function (selectorKey, repeatingSelectors) {
  17126. if (repeatingSelectors) {
  17127. fluid.each(repeatingSelectors, function (repeatingSelector) {
  17128. if (selectorKey === repeatingSelector) {
  17129. selectorKey = selectorKey + ":";
  17130. }
  17131. });
  17132. }
  17133. return selectorKey;
  17134. };
  17135. fluid.renderer.selectorsToCutpoints = function (selectors, options) {
  17136. var togo = [];
  17137. options = options || {};
  17138. selectors = fluid.copy(selectors); // Make a copy before potentially destructively changing someone's selectors.
  17139. if (options.selectorsToIgnore) {
  17140. selectors = removeSelectors(selectors, options.selectorsToIgnore);
  17141. }
  17142. for (var selectorKey in selectors) {
  17143. togo.push({
  17144. id: markRepeated(selectorKey, options.repeatingSelectors),
  17145. selector: selectors[selectorKey]
  17146. });
  17147. }
  17148. return togo;
  17149. };
  17150. /** END of "Renderer Components" infrastructure **/
  17151. fluid.renderer.NO_COMPONENT = {};
  17152. /** A special "shallow copy" operation suitable for nondestructively
  17153. * merging trees of components. jQuery.extend in shallow mode will
  17154. * neglect null valued properties.
  17155. * This function is unsupported: It is not really intended for use by implementors.
  17156. */
  17157. fluid.renderer.mergeComponents = function (target, source) {
  17158. for (var key in source) {
  17159. target[key] = source[key];
  17160. }
  17161. return target;
  17162. };
  17163. fluid.registerNamespace("fluid.renderer.selection");
  17164. /** Definition of expanders - firstly, "heavy" expanders **/
  17165. fluid.renderer.selection.inputs = function (options, container, key, config) {
  17166. fluid.expect("Selection to inputs expander", ["selectID", "inputID", "labelID", "rowID"], options);
  17167. var selection = config.expander(options.tree);
  17168. var rows = fluid.transform(selection.optionlist.value, function (option, index) {
  17169. var togo = {};
  17170. var element = {parentRelativeID: "..::" + options.selectID, choiceindex: index};
  17171. togo[options.inputID] = element;
  17172. togo[options.labelID] = fluid.copy(element);
  17173. return togo;
  17174. });
  17175. var togo = {}; // TODO: JICO needs to support "quoted literal key initialisers" :P
  17176. togo[options.selectID] = selection;
  17177. togo[options.rowID] = {children: rows};
  17178. togo = config.expander(togo);
  17179. return togo;
  17180. };
  17181. fluid.renderer.repeat = function (options, container, key, config) {
  17182. fluid.expect("Repetition expander", ["controlledBy", "tree"], options);
  17183. var env = config.threadLocal();
  17184. var path = fluid.extractContextualPath(options.controlledBy, {ELstyle: "ALL"}, env);
  17185. var list = fluid.get(config.model, path, config.resolverGetConfig);
  17186. var togo = {};
  17187. if (!list || list.length === 0) {
  17188. return options.ifEmpty ? config.expander(options.ifEmpty) : togo;
  17189. }
  17190. var expanded = [];
  17191. fluid.each(list, function (element, i) {
  17192. var EL = fluid.model.composePath(path, i);
  17193. var envAdd = {};
  17194. if (options.pathAs) {
  17195. envAdd[options.pathAs] = "${" + EL + "}";
  17196. }
  17197. if (options.valueAs) {
  17198. envAdd[options.valueAs] = fluid.get(config.model, EL, config.resolverGetConfig);
  17199. }
  17200. var expandrow = fluid.withEnvironment(envAdd, function() {
  17201. return config.expander(options.tree);
  17202. }, env);
  17203. if (fluid.isArrayable(expandrow)) {
  17204. if (expandrow.length > 0) {
  17205. expanded.push({children: expandrow});
  17206. }
  17207. }
  17208. else if (expandrow !== fluid.renderer.NO_COMPONENT) {
  17209. expanded.push(expandrow);
  17210. }
  17211. });
  17212. var repeatID = options.repeatID;
  17213. if (repeatID.indexOf(":") === -1) {
  17214. repeatID = repeatID + ":";
  17215. }
  17216. fluid.each(expanded, function (entry) {entry.ID = repeatID; });
  17217. return expanded;
  17218. };
  17219. fluid.renderer.condition = function (options, container, key, config) {
  17220. fluid.expect("Selection to condition expander", ["condition"], options);
  17221. var condition;
  17222. if (options.condition.funcName) {
  17223. var args = config.expandLight(options.condition.args);
  17224. condition = fluid.invoke(options.condition.funcName, args);
  17225. } else if (options.condition.expander) {
  17226. condition = config.expander(options.condition);
  17227. } else {
  17228. condition = config.expandLight(options.condition);
  17229. }
  17230. var tree = (condition ? options.trueTree : options.falseTree);
  17231. if (!tree) {
  17232. tree = fluid.renderer.NO_COMPONENT;
  17233. }
  17234. return config.expander(tree);
  17235. };
  17236. /* An EL extraction utility suitable for context expressions which occur in
  17237. * expanding component trees. It assumes that any context expressions refer
  17238. * to EL paths that are to be referred to the "true (direct) model" - since
  17239. * the context during expansion may not agree with the context during rendering.
  17240. * It satisfies the same contract as fluid.extractEL, in that it will either return
  17241. * an EL path, or undefined if the string value supplied cannot be interpreted
  17242. * as an EL path with respect to the supplied options.
  17243. */
  17244. // unsupported, non-API function
  17245. fluid.extractContextualPath = function (string, options, env) {
  17246. var parsed = fluid.extractELWithContext(string, options);
  17247. if (parsed) {
  17248. if (parsed.context) {
  17249. return fluid.transformContextPath(parsed, env).path;
  17250. }
  17251. else {
  17252. return parsed.path;
  17253. }
  17254. }
  17255. };
  17256. fluid.transformContextPath = function (parsed, env) {
  17257. if (parsed.context) {
  17258. var fetched = env[parsed.context];
  17259. var EL;
  17260. if (typeof(fetched) === "string") {
  17261. EL = fluid.extractEL(fetched, {ELstyle: "${}"});
  17262. }
  17263. if (EL) {
  17264. return {
  17265. noDereference: parsed.path === "",
  17266. path: fluid.model.composePath(EL, parsed.path)
  17267. };
  17268. }
  17269. }
  17270. return parsed;
  17271. };
  17272. /** Create a "protoComponent expander" with the supplied set of options.
  17273. * The returned value will be a function which accepts a "protoComponent tree"
  17274. * as argument, and returns a "fully expanded" tree suitable for supplying
  17275. * directly to the renderer.
  17276. * A "protoComponent tree" is similar to the "dehydrated form" accepted by
  17277. * the historical renderer - only
  17278. * i) The input format is unambiguous - this expander will NOT accept hydrated
  17279. * components in the {ID: "myId, myfield: "myvalue"} form - but ONLY in
  17280. * the dehydrated {myID: {myfield: myvalue}} form.
  17281. * ii) This expander has considerably greater power to expand condensed trees.
  17282. * In particular, an "EL style" option can be supplied which will expand bare
  17283. * strings found as values in the tree into UIBound components by a configurable
  17284. * strategy. Supported values for "ELstyle" are a) "ALL" - every string will be
  17285. * interpreted as an EL reference and assigned to the "valuebinding" member of
  17286. * the UIBound, or b) any single character, which if it appears as the first
  17287. * character of the string, will mark it out as an EL reference - otherwise it
  17288. * will be considered a literal value, or c) the value "${}" which will be
  17289. * recognised bracketing any other EL expression.
  17290. */
  17291. fluid.renderer.makeProtoExpander = function (expandOptions) {
  17292. // shallow copy of options - cheaply avoid destroying model, and all others are primitive
  17293. var options = $.extend({
  17294. ELstyle: "${}"
  17295. }, expandOptions); // shallow copy of options
  17296. var threadLocal; // rebound on every expansion at entry point
  17297. function fetchEL(string) {
  17298. var env = threadLocal();
  17299. return fluid.extractContextualPath(string, options, env);
  17300. }
  17301. var IDescape = options.IDescape || "\\";
  17302. var expandLight = function (source) {
  17303. return fluid.resolveEnvironment(source, options);
  17304. };
  17305. var expandBound = function (value, concrete) {
  17306. if (value.messagekey !== undefined) {
  17307. return {
  17308. componentType: "UIMessage",
  17309. messagekey: expandBound(value.messagekey),
  17310. args: expandLight(value.args)
  17311. };
  17312. }
  17313. var proto;
  17314. if (!fluid.isPrimitive(value) && !fluid.isArrayable(value)) {
  17315. proto = $.extend({}, value);
  17316. if (proto.decorators) {
  17317. proto.decorators = expandLight(proto.decorators);
  17318. }
  17319. value = proto.value;
  17320. delete proto.value;
  17321. } else {
  17322. proto = {};
  17323. }
  17324. var EL = typeof (value) === "string" ? fetchEL(value) : null;
  17325. if (EL) {
  17326. proto.valuebinding = EL;
  17327. } else if (value !== undefined) {
  17328. proto.value = value;
  17329. }
  17330. if (options.model && proto.valuebinding && proto.value === undefined) {
  17331. proto.value = fluid.get(options.model, proto.valuebinding, options.resolverGetConfig);
  17332. }
  17333. if (concrete) {
  17334. proto.componentType = "UIBound";
  17335. }
  17336. return proto;
  17337. };
  17338. options.filter = fluid.expander.lightFilter;
  17339. var expandCond;
  17340. var expandLeafOrCond;
  17341. var expandEntry = function (entry) {
  17342. var comp = [];
  17343. expandCond(entry, comp);
  17344. return {children: comp};
  17345. };
  17346. var expandExternal = function (entry) {
  17347. if (entry === fluid.renderer.NO_COMPONENT) {
  17348. return entry;
  17349. }
  17350. var singleTarget;
  17351. var target = [];
  17352. var pusher = function (comp) {
  17353. singleTarget = comp;
  17354. };
  17355. expandLeafOrCond(entry, target, pusher);
  17356. return singleTarget || target;
  17357. };
  17358. var expandConfig = {
  17359. model: options.model,
  17360. resolverGetConfig: options.resolverGetConfig,
  17361. resolverSetConfig: options.resolverSetConfig,
  17362. expander: expandExternal,
  17363. expandLight: expandLight
  17364. //threadLocal: threadLocal
  17365. };
  17366. var expandLeaf = function (leaf, componentType) {
  17367. var togo = {componentType: componentType};
  17368. var map = fluid.renderer.boundMap[componentType] || {};
  17369. for (var key in leaf) {
  17370. if (/decorators|args/.test(key)) {
  17371. togo[key] = expandLight(leaf[key]);
  17372. continue;
  17373. } else if (map[key]) {
  17374. togo[key] = expandBound(leaf[key]);
  17375. } else {
  17376. togo[key] = leaf[key];
  17377. }
  17378. }
  17379. return togo;
  17380. };
  17381. // A child entry may be a cond, a leaf, or another "thing with children".
  17382. // Unlike the case with a cond's contents, these must be homogeneous - at least
  17383. // they may either be ALL leaves, or else ALL cond/childed etc.
  17384. // In all of these cases, the key will be THE PARENT'S KEY
  17385. var expandChildren = function (entry, pusher) {
  17386. var children = entry.children;
  17387. for (var i = 0; i < children.length; ++i) {
  17388. // each child in this list will lead to a WHOLE FORKED set of children.
  17389. var target = [];
  17390. var comp = { children: target};
  17391. var child = children[i];
  17392. var childPusher = function (comp) {
  17393. target[target.length] = comp;
  17394. }; // jslint:ok - function in loop
  17395. expandLeafOrCond(child, target, childPusher);
  17396. // Rescue the case of an expanded leaf into single component - TODO: check what sense this makes of the grammar
  17397. if (comp.children.length === 1 && !comp.children[0].ID) {
  17398. comp = comp.children[0];
  17399. }
  17400. pusher(comp);
  17401. }
  17402. };
  17403. function detectBareBound(entry) {
  17404. return fluid.find(entry, function (value, key) {
  17405. return key === "decorators";
  17406. }) !== false;
  17407. }
  17408. // We have reached something which is either a leaf or Cond - either inside
  17409. // a Cond or as an entry in children.
  17410. var expandLeafOrCond = function (entry, target, pusher) { // jslint:ok - forward declaration
  17411. var componentType = fluid.renderer.inferComponentType(entry);
  17412. if (!componentType && (fluid.isPrimitive(entry) || detectBareBound(entry))) {
  17413. componentType = "UIBound";
  17414. }
  17415. if (componentType) {
  17416. pusher(componentType === "UIBound" ? expandBound(entry, true) : expandLeaf(entry, componentType));
  17417. } else {
  17418. // we couldn't recognise it as a leaf, so it must be a cond
  17419. // this may be illegal if we are already in a cond.
  17420. if (!target) {
  17421. fluid.fail("Illegal cond->cond transition");
  17422. }
  17423. expandCond(entry, target);
  17424. }
  17425. };
  17426. // cond entry may be a leaf, "thing with children" or a "direct bound".
  17427. // a Cond can ONLY occur as a direct member of "children". Each "cond" entry may
  17428. // give rise to one or many elements with the SAME key - if "expandSingle" discovers
  17429. // "thing with children" they will all share the same key found in proto.
  17430. expandCond = function (proto, target) {
  17431. for (var key in proto) {
  17432. var entry = proto[key];
  17433. if (key.charAt(0) === IDescape) {
  17434. key = key.substring(1);
  17435. }
  17436. if (key === "expander") {
  17437. var expanders = fluid.makeArray(entry);
  17438. fluid.each(expanders, function (expander) {
  17439. var expanded = fluid.invokeGlobalFunction(expander.type, [expander, proto, key, expandConfig]);
  17440. if (expanded !== fluid.renderer.NO_COMPONENT) {
  17441. fluid.each(expanded, function (el) {target[target.length] = el; });
  17442. }
  17443. }); // jslint:ok - function in loop
  17444. } else if (entry) {
  17445. var condPusher = function (comp) {
  17446. comp.ID = key;
  17447. target[target.length] = comp;
  17448. }; // jslint:ok - function in loop
  17449. if (entry.children) {
  17450. if (key.indexOf(":") === -1) {
  17451. key = key + ":";
  17452. }
  17453. expandChildren(entry, condPusher);
  17454. } else if (fluid.renderer.isBoundPrimitive(entry)) {
  17455. condPusher(expandBound(entry, true));
  17456. } else {
  17457. expandLeafOrCond(entry, null, condPusher);
  17458. }
  17459. }
  17460. }
  17461. };
  17462. return function(entry) {
  17463. threadLocal = fluid.threadLocal(function() {
  17464. return $.extend({}, options.envAdd);
  17465. });
  17466. options.fetcher = fluid.makeEnvironmentFetcher(options.model, fluid.transformContextPath, threadLocal);
  17467. expandConfig.threadLocal = threadLocal;
  17468. return expandEntry(entry);
  17469. };
  17470. };
  17471. })(jQuery, fluid_1_5);
  17472. /*
  17473. Copyright 2008-2009 University of Toronto
  17474. Copyright 2010-2011 OCAD University
  17475. Copyright 2011 Lucendo Development Ltd.
  17476. Licensed under the Educational Community License (ECL), Version 2.0 or the New
  17477. BSD license. You may not use this file except in compliance with one these
  17478. Licenses.
  17479. You may obtain a copy of the ECL 2.0 License and BSD License at
  17480. https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt
  17481. */
  17482. // Declare dependencies
  17483. /*global window, fluid_1_5:true, jQuery, swfobject*/
  17484. // JSLint options
  17485. /*jslint white: true, funcinvoke: true, undef: true, newcap: true, nomen: true, regexp: true, bitwise: true, browser: true, forin: true, maxerr: 100, indent: 4 */
  17486. var fluid_1_5 = fluid_1_5 || {};
  17487. (function ($, fluid) {
  17488. fluid.registerNamespace("fluid.browser");
  17489. fluid.browser.binaryXHR = function () {
  17490. var canSendBinary = window.FormData ||
  17491. (window.XMLHttpRequest &&
  17492. window.XMLHttpRequest.prototype &&
  17493. window.XMLHttpRequest.prototype.sendAsBinary);
  17494. return canSendBinary ? fluid.typeTag("fluid.browser.supportsBinaryXHR") : undefined;
  17495. };
  17496. fluid.browser.formData = function () {
  17497. return window.FormData ? fluid.typeTag("fluid.browser.supportsFormData") : undefined;
  17498. };
  17499. fluid.browser.flash = function () {
  17500. var hasModernFlash = (typeof(swfobject) !== "undefined") && (swfobject.getFlashPlayerVersion().major > 8);
  17501. return hasModernFlash ? fluid.typeTag("fluid.browser.supportsFlash") : undefined;
  17502. };
  17503. fluid.progressiveChecker = function (options) {
  17504. var that = fluid.initLittleComponent("fluid.progressiveChecker", options);
  17505. return fluid.typeTag(fluid.find(that.options.checks, function(check) {
  17506. if (check.feature) {
  17507. return check.contextName;
  17508. }}, that.options.defaultContextName
  17509. ));
  17510. };
  17511. fluid.defaults("fluid.progressiveChecker", {
  17512. gradeNames: "fluid.typeFount",
  17513. checks: [], // [{"feature": "{IoC Expression}", "contextName": "context.name"}]
  17514. defaultContextName: undefined
  17515. });
  17516. fluid.progressiveCheckerForComponent = function (options) {
  17517. var that = fluid.initLittleComponent("fluid.progressiveCheckerForComponent", options);
  17518. var defaults = fluid.defaults(that.options.componentName);
  17519. return fluid.progressiveChecker(fluid.expandOptions(fluid.copy(defaults.progressiveCheckerOptions), that));
  17520. };
  17521. fluid.defaults("fluid.progressiveCheckerForComponent", {
  17522. gradeNames: "fluid.typeFount"
  17523. });
  17524. /**********************************************************
  17525. * This code runs immediately upon inclusion of this file *
  17526. **********************************************************/
  17527. // Use JavaScript to hide any markup that is specifically in place for cases when JavaScript is off.
  17528. // Note: the use of fl-ProgEnhance-basic is deprecated, and replaced by fl-progEnhance-basic.
  17529. // It is included here for backward compatibility only.
  17530. $("head").append("<style type='text/css'>.fl-progEnhance-basic, .fl-ProgEnhance-basic { display: none; } .fl-progEnhance-enhanced, .fl-ProgEnhance-enhanced { display: block; }</style>");
  17531. // Browser feature detection--adds corresponding type tags to the static environment,
  17532. // which can be used to define appropriate demands blocks for components using the IoC system.
  17533. var features = {
  17534. supportsBinaryXHR: fluid.browser.binaryXHR(),
  17535. supportsFormData: fluid.browser.formData(),
  17536. supportsFlash: fluid.browser.flash()
  17537. };
  17538. fluid.merge(null, fluid.staticEnvironment, features);
  17539. })(jQuery, fluid_1_5);
  17540. /*
  17541. Copyright 2008-2009 University of Toronto
  17542. Copyright 2008-2009 University of California, Berkeley
  17543. Copyright 2010-2011 OCAD University
  17544. Licensed under the Educational Community License (ECL), Version 2.0 or the New
  17545. BSD license. You may not use this file except in compliance with one these
  17546. Licenses.
  17547. You may obtain a copy of the ECL 2.0 License and BSD License at
  17548. https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt
  17549. */
  17550. // Declare dependencies
  17551. /*global fluid_1_5:true, jQuery*/
  17552. // JSLint options
  17553. /*jslint white: true, funcinvoke: true, undef: true, newcap: true, nomen: true, regexp: true, bitwise: true, browser: true, forin: true, maxerr: 100, indent: 4 */
  17554. var fluid_1_5 = fluid_1_5 || {};
  17555. (function ($, fluid) {
  17556. var animateDisplay = function (elm, animation, defaultAnimation) {
  17557. animation = (animation) ? animation : defaultAnimation;
  17558. elm.animate(animation.params, animation.duration, animation.callback);
  17559. };
  17560. var animateProgress = function (elm, width, speed) {
  17561. // de-queue any left over animations
  17562. elm.queue("fx", []);
  17563. elm.animate({
  17564. width: width,
  17565. queue: false
  17566. },
  17567. speed);
  17568. };
  17569. var showProgress = function (that, animation) {
  17570. if (animation === false) {
  17571. that.displayElement.show();
  17572. } else {
  17573. animateDisplay(that.displayElement, animation, that.options.showAnimation);
  17574. }
  17575. };
  17576. var hideProgress = function (that, delay, animation) {
  17577. delay = (delay === null || isNaN(delay)) ? that.options.delay : delay;
  17578. if (delay) {
  17579. // use a setTimeout to delay the hide for n millies, note use of recursion
  17580. var timeOut = setTimeout(function () {
  17581. hideProgress(that, 0, animation);
  17582. }, delay);
  17583. } else {
  17584. if (animation === false) {
  17585. that.displayElement.hide();
  17586. } else {
  17587. animateDisplay(that.displayElement, animation, that.options.hideAnimation);
  17588. }
  17589. }
  17590. };
  17591. var updateWidth = function (that, newWidth, dontAnimate) {
  17592. dontAnimate = dontAnimate || false;
  17593. var currWidth = that.indicator.width();
  17594. var direction = that.options.animate;
  17595. if ((newWidth > currWidth) && (direction === "both" || direction === "forward") && !dontAnimate) {
  17596. animateProgress(that.indicator, newWidth, that.options.speed);
  17597. } else if ((newWidth < currWidth) && (direction === "both" || direction === "backward") && !dontAnimate) {
  17598. animateProgress(that.indicator, newWidth, that.options.speed);
  17599. } else {
  17600. that.indicator.width(newWidth);
  17601. }
  17602. };
  17603. var percentToPixels = function (that, percent) {
  17604. // progress does not support percents over 100, also all numbers are rounded to integers
  17605. return Math.round((Math.min(percent, 100) * that.progressBar.innerWidth()) / 100);
  17606. };
  17607. var refreshRelativeWidth = function (that) {
  17608. var pixels = Math.max(percentToPixels(that, parseFloat(that.storedPercent)), that.options.minWidth);
  17609. updateWidth(that, pixels, true);
  17610. };
  17611. var initARIA = function (ariaElement, ariaBusyText) {
  17612. ariaElement.attr("role", "progressbar");
  17613. ariaElement.attr("aria-valuemin", "0");
  17614. ariaElement.attr("aria-valuemax", "100");
  17615. ariaElement.attr("aria-valuenow", "0");
  17616. //Empty value for ariaBusyText will default to aria-valuenow.
  17617. if (ariaBusyText) {
  17618. ariaElement.attr("aria-valuetext", "");
  17619. }
  17620. ariaElement.attr("aria-busy", "false");
  17621. };
  17622. var updateARIA = function (that, percent) {
  17623. var str = that.options.strings;
  17624. var busy = percent < 100 && percent > 0;
  17625. that.ariaElement.attr("aria-busy", busy);
  17626. that.ariaElement.attr("aria-valuenow", percent);
  17627. //Empty value for ariaBusyText will default to aria-valuenow.
  17628. if (str.ariaBusyText) {
  17629. if (busy) {
  17630. var busyString = fluid.stringTemplate(str.ariaBusyText, {percentComplete : percent});
  17631. that.ariaElement.attr("aria-valuetext", busyString);
  17632. } else if (percent === 100) {
  17633. // FLUID-2936: JAWS doesn't currently read the "Progress is complete" message to the user, even though we set it here.
  17634. that.ariaElement.attr("aria-valuetext", str.ariaDoneText);
  17635. }
  17636. }
  17637. };
  17638. var updateText = function (label, value) {
  17639. label.html(value);
  17640. };
  17641. var repositionIndicator = function (that) {
  17642. that.indicator.css("top", that.progressBar.position().top)
  17643. .css("left", 0)
  17644. .height(that.progressBar.height());
  17645. refreshRelativeWidth(that);
  17646. };
  17647. var updateProgress = function (that, percent, labelText, animationForShow) {
  17648. // show progress before updating, jQuery will handle the case if the object is already displayed
  17649. showProgress(that, animationForShow);
  17650. // do not update if the value of percent is falsey
  17651. if (percent !== null) {
  17652. that.storedPercent = percent;
  17653. var pixels = Math.max(percentToPixels(that, parseFloat(percent)), that.options.minWidth);
  17654. updateWidth(that, pixels);
  17655. }
  17656. if (labelText !== null) {
  17657. updateText(that.label, labelText);
  17658. }
  17659. // update ARIA
  17660. if (that.ariaElement) {
  17661. updateARIA(that, percent);
  17662. }
  17663. };
  17664. var setupProgress = function (that) {
  17665. that.displayElement = that.locate("displayElement");
  17666. // hide file progress in case it is showing
  17667. if (that.options.initiallyHidden) {
  17668. that.displayElement.hide();
  17669. }
  17670. that.progressBar = that.locate("progressBar");
  17671. that.label = that.locate("label");
  17672. that.indicator = that.locate("indicator");
  17673. that.ariaElement = that.locate("ariaElement");
  17674. that.indicator.width(that.options.minWidth);
  17675. that.storedPercent = 0;
  17676. // initialize ARIA
  17677. if (that.ariaElement) {
  17678. initARIA(that.ariaElement, that.options.strings.ariaBusyText);
  17679. }
  17680. // afterProgressHidden:
  17681. // Registering listener with the callback provided by the user and reinitializing
  17682. // the event trigger function.
  17683. // Note: callback deprecated as of 1.5, use afterProgressHidden event
  17684. if (that.options.hideAnimation.callback) {
  17685. that.events.afterProgressHidden.addListener(that.options.hideAnimation.callback);
  17686. }
  17687. // triggers the afterProgressHidden event
  17688. // Note: callback deprecated as of 1.5, use afterProgressHidden event
  17689. that.options.hideAnimation.callback = that.events.afterProgressHidden.fire;
  17690. // onProgressBegin:
  17691. // Registering listener with the callback provided by the user and reinitializing
  17692. // the event trigger function.
  17693. // Note: callback deprecated as of 1.5, use onProgressBegin event
  17694. if (that.options.showAnimation.callback) {
  17695. that.events.onProgressBegin.addListener(that.options.showAnimation.callback);
  17696. }
  17697. // triggers the onProgressBegin event
  17698. // Note: callback deprecated as of 1.5, use onProgressBegin event
  17699. that.options.showAnimation.callback = that.events.onProgressBegin.fire;
  17700. };
  17701. /**
  17702. * Instantiates a new Progress component.
  17703. *
  17704. * @param {jQuery|Selector|Element} container the DOM element in which the Uploader lives
  17705. * @param {Object} options configuration options for the component.
  17706. */
  17707. fluid.progress = function (container, options) {
  17708. var that = fluid.initView("fluid.progress", container, options);
  17709. setupProgress(that);
  17710. /**
  17711. * Shows the progress bar if is currently hidden.
  17712. *
  17713. * @param {Object} animation a custom animation used when showing the progress bar
  17714. */
  17715. that.show = function (animation) {
  17716. showProgress(that, animation);
  17717. };
  17718. /**
  17719. * Hides the progress bar if it is visible.
  17720. *
  17721. * @param {Number} delay the amount of time to wait before hiding
  17722. * @param {Object} animation a custom animation used when hiding the progress bar
  17723. */
  17724. that.hide = function (delay, animation) {
  17725. hideProgress(that, delay, animation);
  17726. };
  17727. /**
  17728. * Updates the state of the progress bar.
  17729. * This will automatically show the progress bar if it is currently hidden.
  17730. * Percentage is specified as a decimal value, but will be automatically converted if needed.
  17731. *
  17732. *
  17733. * @param {Number|String} percentage the current percentage, specified as a "float-ish" value
  17734. * @param {String} labelValue the value to set for the label; this can be an HTML string
  17735. * @param {Object} animationForShow the animation to use when showing the progress bar if it is hidden
  17736. */
  17737. that.update = function (percentage, labelValue, animationForShow) {
  17738. updateProgress(that, percentage, labelValue, animationForShow);
  17739. };
  17740. that.refreshView = function () {
  17741. repositionIndicator(that);
  17742. };
  17743. return that;
  17744. };
  17745. fluid.defaults("fluid.progress", {
  17746. gradeNames: "fluid.viewComponent",
  17747. selectors: {
  17748. displayElement: ".flc-progress", // required, the element that gets displayed when progress is displayed, could be the indicator or bar or some larger outer wrapper as in an overlay effect
  17749. progressBar: ".flc-progress-bar", //required
  17750. indicator: ".flc-progress-indicator", //required
  17751. label: ".flc-progress-label", //optional
  17752. ariaElement: ".flc-progress-bar" // usually required, except in cases where there are more than one progressor for the same data such as a total and a sub-total
  17753. },
  17754. strings: {
  17755. //Empty value for ariaBusyText will default to aria-valuenow.
  17756. ariaBusyText: "Progress is %percentComplete percent complete",
  17757. ariaDoneText: "Progress is complete."
  17758. },
  17759. // progress display and hide animations, use the jQuery animation primatives, set to false to use no animation
  17760. // animations must be symetrical (if you hide with width, you'd better show with width) or you get odd effects
  17761. // see jQuery docs about animations to customize
  17762. showAnimation: {
  17763. params: {
  17764. opacity: "show"
  17765. },
  17766. duration: "slow",
  17767. //callback has been deprecated and will be removed as of 1.5, instead use onProgressBegin event
  17768. callback: null
  17769. }, // equivalent of $().fadeIn("slow")
  17770. hideAnimation: {
  17771. params: {
  17772. opacity: "hide"
  17773. },
  17774. duration: "slow",
  17775. //callback has been deprecated and will be removed as of 1.5, instead use afterProgressHidden event
  17776. callback: null
  17777. }, // equivalent of $().fadeOut("slow")
  17778. events: {
  17779. onProgressBegin: null,
  17780. afterProgressHidden: null
  17781. },
  17782. minWidth: 5, // 0 length indicators can look broken if there is a long pause between updates
  17783. delay: 0, // the amount to delay the fade out of the progress
  17784. speed: 200, // default speed for animations, pretty fast
  17785. animate: "forward", // suppport "forward", "backward", and "both", any other value is no animation either way
  17786. initiallyHidden: true, // supports progress indicators which may always be present
  17787. updatePosition: false
  17788. });
  17789. })(jQuery, fluid_1_5);
  17790. /**
  17791. * jQuery.ScrollTo
  17792. * Copyright (c) 2007-2009 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
  17793. * Dual licensed under MIT and GPL.
  17794. * Date: 5/25/2009
  17795. *
  17796. * @projectDescription Easy element scrolling using jQuery.
  17797. * http://flesler.blogspot.com/2007/10/jqueryscrollto.html
  17798. * Works with jQuery +1.2.6. Tested on FF 2/3, IE 6/7/8, Opera 9.5/6, Safari 3, Chrome 1 on WinXP.
  17799. *
  17800. * @author Ariel Flesler
  17801. * @version 1.4.2
  17802. *
  17803. * @id jQuery.scrollTo
  17804. * @id jQuery.fn.scrollTo
  17805. * @param {String, Number, DOMElement, jQuery, Object} target Where to scroll the matched elements.
  17806. * The different options for target are:
  17807. * - A number position (will be applied to all axes).
  17808. * - A string position ('44', '100px', '+=90', etc ) will be applied to all axes
  17809. * - A jQuery/DOM element ( logically, child of the element to scroll )
  17810. * - A string selector, that will be relative to the element to scroll ( 'li:eq(2)', etc )
  17811. * - A hash { top:x, left:y }, x and y can be any kind of number/string like above.
  17812. * - A percentage of the container's dimension/s, for example: 50% to go to the middle.
  17813. * - The string 'max' for go-to-end.
  17814. * @param {Number} duration The OVERALL length of the animation, this argument can be the settings object instead.
  17815. * @param {Object,Function} settings Optional set of settings or the onAfter callback.
  17816. * @option {String} axis Which axis must be scrolled, use 'x', 'y', 'xy' or 'yx'.
  17817. * @option {Number} duration The OVERALL length of the animation.
  17818. * @option {String} easing The easing method for the animation.
  17819. * @option {Boolean} margin If true, the margin of the target element will be deducted from the final position.
  17820. * @option {Object, Number} offset Add/deduct from the end position. One number for both axes or { top:x, left:y }.
  17821. * @option {Object, Number} over Add/deduct the height/width multiplied by 'over', can be { top:x, left:y } when using both axes.
  17822. * @option {Boolean} queue If true, and both axis are given, the 2nd axis will only be animated after the first one ends.
  17823. * @option {Function} onAfter Function to be called after the scrolling ends.
  17824. * @option {Function} onAfterFirst If queuing is activated, this function will be called after the first scrolling ends.
  17825. * @return {jQuery} Returns the same jQuery object, for chaining.
  17826. *
  17827. * @desc Scroll to a fixed position
  17828. * @example $('div').scrollTo( 340 );
  17829. *
  17830. * @desc Scroll relatively to the actual position
  17831. * @example $('div').scrollTo( '+=340px', { axis:'y' } );
  17832. *
  17833. * @dec Scroll using a selector (relative to the scrolled element)
  17834. * @example $('div').scrollTo( 'p.paragraph:eq(2)', 500, { easing:'swing', queue:true, axis:'xy' } );
  17835. *
  17836. * @ Scroll to a DOM element (same for jQuery object)
  17837. * @example var second_child = document.getElementById('container').firstChild.nextSibling;
  17838. * $('#container').scrollTo( second_child, { duration:500, axis:'x', onAfter:function(){
  17839. * alert('scrolled!!');
  17840. * }});
  17841. *
  17842. * @desc Scroll on both axes, to different values
  17843. * @example $('div').scrollTo( { top: 300, left:'+=200' }, { axis:'xy', offset:-20 } );
  17844. */
  17845. ;(function( $ ){
  17846. var $scrollTo = $.scrollTo = function( target, duration, settings ){
  17847. $(window).scrollTo( target, duration, settings );
  17848. };
  17849. $scrollTo.defaults = {
  17850. axis:'xy',
  17851. duration: parseFloat($.fn.jquery) >= 1.3 ? 0 : 1
  17852. };
  17853. // Returns the element that needs to be animated to scroll the window.
  17854. // Kept for backwards compatibility (specially for localScroll & serialScroll)
  17855. $scrollTo.window = function( scope ){
  17856. return $(window)._scrollable();
  17857. };
  17858. // Hack, hack, hack :)
  17859. // Returns the real elements to scroll (supports window/iframes, documents and regular nodes)
  17860. $.fn._scrollable = function(){
  17861. return this.map(function(){
  17862. var elem = this,
  17863. isWin = !elem.nodeName || $.inArray( elem.nodeName.toLowerCase(), ['iframe','#document','html','body'] ) != -1;
  17864. if( !isWin )
  17865. return elem;
  17866. var doc = (elem.contentWindow || elem).document || elem.ownerDocument || elem;
  17867. return $.browser.safari || doc.compatMode == 'BackCompat' ?
  17868. doc.body :
  17869. doc.documentElement;
  17870. });
  17871. };
  17872. $.fn.scrollTo = function( target, duration, settings ){
  17873. if( typeof duration == 'object' ){
  17874. settings = duration;
  17875. duration = 0;
  17876. }
  17877. if( typeof settings == 'function' )
  17878. settings = { onAfter:settings };
  17879. if( target == 'max' )
  17880. target = 9e9;
  17881. settings = $.extend( {}, $scrollTo.defaults, settings );
  17882. // Speed is still recognized for backwards compatibility
  17883. duration = duration || settings.speed || settings.duration;
  17884. // Make sure the settings are given right
  17885. settings.queue = settings.queue && settings.axis.length > 1;
  17886. if( settings.queue )
  17887. // Let's keep the overall duration
  17888. duration /= 2;
  17889. settings.offset = both( settings.offset );
  17890. settings.over = both( settings.over );
  17891. return this._scrollable().each(function(){
  17892. var elem = this,
  17893. $elem = $(elem),
  17894. targ = target, toff, attr = {},
  17895. win = $elem.is('html,body');
  17896. switch( typeof targ ){
  17897. // A number will pass the regex
  17898. case 'number':
  17899. case 'string':
  17900. if( /^([+-]=)?\d+(\.\d+)?(px|%)?$/.test(targ) ){
  17901. targ = both( targ );
  17902. // We are done
  17903. break;
  17904. }
  17905. // Relative selector, no break!
  17906. targ = $(targ,this);
  17907. case 'object':
  17908. // DOMElement / jQuery
  17909. if( targ.is || targ.style )
  17910. // Get the real position of the target
  17911. toff = (targ = $(targ)).offset();
  17912. }
  17913. $.each( settings.axis.split(''), function( i, axis ){
  17914. var Pos = axis == 'x' ? 'Left' : 'Top',
  17915. pos = Pos.toLowerCase(),
  17916. key = 'scroll' + Pos,
  17917. old = elem[key],
  17918. max = $scrollTo.max(elem, axis);
  17919. if( toff ){// jQuery / DOMElement
  17920. attr[key] = toff[pos] + ( win ? 0 : old - $elem.offset()[pos] );
  17921. // If it's a dom element, reduce the margin
  17922. if( settings.margin ){
  17923. attr[key] -= parseInt(targ.css('margin'+Pos)) || 0;
  17924. attr[key] -= parseInt(targ.css('border'+Pos+'Width')) || 0;
  17925. }
  17926. attr[key] += settings.offset[pos] || 0;
  17927. if( settings.over[pos] )
  17928. // Scroll to a fraction of its width/height
  17929. attr[key] += targ[axis=='x'?'width':'height']() * settings.over[pos];
  17930. }else{
  17931. var val = targ[pos];
  17932. // Handle percentage values
  17933. attr[key] = val.slice && val.slice(-1) == '%' ?
  17934. parseFloat(val) / 100 * max
  17935. : val;
  17936. }
  17937. // Number or 'number'
  17938. if( /^\d+$/.test(attr[key]) )
  17939. // Check the limits
  17940. attr[key] = attr[key] <= 0 ? 0 : Math.min( attr[key], max );
  17941. // Queueing axes
  17942. if( !i && settings.queue ){
  17943. // Don't waste time animating, if there's no need.
  17944. if( old != attr[key] )
  17945. // Intermediate animation
  17946. animate( settings.onAfterFirst );
  17947. // Don't animate this axis again in the next iteration.
  17948. delete attr[key];
  17949. }
  17950. });
  17951. animate( settings.onAfter );
  17952. function animate( callback ){
  17953. $elem.animate( attr, duration, settings.easing, callback && function(){
  17954. callback.call(this, target, settings);
  17955. });
  17956. };
  17957. }).end();
  17958. };
  17959. // Max scrolling position, works on quirks mode
  17960. // It only fails (not too badly) on IE, quirks mode.
  17961. $scrollTo.max = function( elem, axis ){
  17962. var Dim = axis == 'x' ? 'Width' : 'Height',
  17963. scroll = 'scroll'+Dim;
  17964. if( !$(elem).is('html,body') )
  17965. return elem[scroll] - $(elem)[Dim.toLowerCase()]();
  17966. var size = 'client' + Dim,
  17967. html = elem.ownerDocument.documentElement,
  17968. body = elem.ownerDocument.body;
  17969. return Math.max( html[scroll], body[scroll] )
  17970. - Math.min( html[size] , body[size] );
  17971. };
  17972. function both( val ){
  17973. return typeof val == 'object' ? val : { top:val, left:val };
  17974. };
  17975. })( jQuery );/*! SWFObject v2.2 <http://code.google.com/p/swfobject/>
  17976. is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
  17977. */
  17978. var swfobject = function() {
  17979. var UNDEF = "undefined",
  17980. OBJECT = "object",
  17981. SHOCKWAVE_FLASH = "Shockwave Flash",
  17982. SHOCKWAVE_FLASH_AX = "ShockwaveFlash.ShockwaveFlash",
  17983. FLASH_MIME_TYPE = "application/x-shockwave-flash",
  17984. EXPRESS_INSTALL_ID = "SWFObjectExprInst",
  17985. ON_READY_STATE_CHANGE = "onreadystatechange",
  17986. win = window,
  17987. doc = document,
  17988. nav = navigator,
  17989. plugin = false,
  17990. domLoadFnArr = [main],
  17991. regObjArr = [],
  17992. objIdArr = [],
  17993. listenersArr = [],
  17994. storedAltContent,
  17995. storedAltContentId,
  17996. storedCallbackFn,
  17997. storedCallbackObj,
  17998. isDomLoaded = false,
  17999. isExpressInstallActive = false,
  18000. dynamicStylesheet,
  18001. dynamicStylesheetMedia,
  18002. autoHideShow = true,
  18003. /* Centralized function for browser feature detection
  18004. - User agent string detection is only used when no good alternative is possible
  18005. - Is executed directly for optimal performance
  18006. */
  18007. ua = function() {
  18008. var w3cdom = typeof doc.getElementById != UNDEF && typeof doc.getElementsByTagName != UNDEF && typeof doc.createElement != UNDEF,
  18009. u = nav.userAgent.toLowerCase(),
  18010. p = nav.platform.toLowerCase(),
  18011. windows = p ? /win/.test(p) : /win/.test(u),
  18012. mac = p ? /mac/.test(p) : /mac/.test(u),
  18013. webkit = /webkit/.test(u) ? parseFloat(u.replace(/^.*webkit\/(\d+(\.\d+)?).*$/, "$1")) : false, // returns either the webkit version or false if not webkit
  18014. ie = !+"\v1", // feature detection based on Andrea Giammarchi's solution: http://webreflection.blogspot.com/2009/01/32-bytes-to-know-if-your-browser-is-ie.html
  18015. playerVersion = [0,0,0],
  18016. d = null;
  18017. if (typeof nav.plugins != UNDEF && typeof nav.plugins[SHOCKWAVE_FLASH] == OBJECT) {
  18018. d = nav.plugins[SHOCKWAVE_FLASH].description;
  18019. if (d && !(typeof nav.mimeTypes != UNDEF && nav.mimeTypes[FLASH_MIME_TYPE] && !nav.mimeTypes[FLASH_MIME_TYPE].enabledPlugin)) { // navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin indicates whether plug-ins are enabled or disabled in Safari 3+
  18020. plugin = true;
  18021. ie = false; // cascaded feature detection for Internet Explorer
  18022. d = d.replace(/^.*\s+(\S+\s+\S+$)/, "$1");
  18023. playerVersion[0] = parseInt(d.replace(/^(.*)\..*$/, "$1"), 10);
  18024. playerVersion[1] = parseInt(d.replace(/^.*\.(.*)\s.*$/, "$1"), 10);
  18025. playerVersion[2] = /[a-zA-Z]/.test(d) ? parseInt(d.replace(/^.*[a-zA-Z]+(.*)$/, "$1"), 10) : 0;
  18026. }
  18027. }
  18028. else if (typeof win.ActiveXObject != UNDEF) {
  18029. try {
  18030. var a = new ActiveXObject(SHOCKWAVE_FLASH_AX);
  18031. if (a) { // a will return null when ActiveX is disabled
  18032. d = a.GetVariable("$version");
  18033. if (d) {
  18034. ie = true; // cascaded feature detection for Internet Explorer
  18035. d = d.split(" ")[1].split(",");
  18036. playerVersion = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
  18037. }
  18038. }
  18039. }
  18040. catch(e) {}
  18041. }
  18042. return { w3:w3cdom, pv:playerVersion, wk:webkit, ie:ie, win:windows, mac:mac };
  18043. }(),
  18044. /* Cross-browser onDomLoad
  18045. - Will fire an event as soon as the DOM of a web page is loaded
  18046. - Internet Explorer workaround based on Diego Perini's solution: http://javascript.nwbox.com/IEContentLoaded/
  18047. - Regular onload serves as fallback
  18048. */
  18049. onDomLoad = function() {
  18050. if (!ua.w3) { return; }
  18051. if ((typeof doc.readyState != UNDEF && doc.readyState == "complete") || (typeof doc.readyState == UNDEF && (doc.getElementsByTagName("body")[0] || doc.body))) { // function is fired after onload, e.g. when script is inserted dynamically
  18052. callDomLoadFunctions();
  18053. }
  18054. if (!isDomLoaded) {
  18055. if (typeof doc.addEventListener != UNDEF) {
  18056. doc.addEventListener("DOMContentLoaded", callDomLoadFunctions, false);
  18057. }
  18058. if (ua.ie && ua.win) {
  18059. doc.attachEvent(ON_READY_STATE_CHANGE, function() {
  18060. if (doc.readyState == "complete") {
  18061. doc.detachEvent(ON_READY_STATE_CHANGE, arguments.callee);
  18062. callDomLoadFunctions();
  18063. }
  18064. });
  18065. if (win == top) { // if not inside an iframe
  18066. (function(){
  18067. if (isDomLoaded) { return; }
  18068. try {
  18069. doc.documentElement.doScroll("left");
  18070. }
  18071. catch(e) {
  18072. setTimeout(arguments.callee, 0);
  18073. return;
  18074. }
  18075. callDomLoadFunctions();
  18076. })();
  18077. }
  18078. }
  18079. if (ua.wk) {
  18080. (function(){
  18081. if (isDomLoaded) { return; }
  18082. if (!/loaded|complete/.test(doc.readyState)) {
  18083. setTimeout(arguments.callee, 0);
  18084. return;
  18085. }
  18086. callDomLoadFunctions();
  18087. })();
  18088. }
  18089. addLoadEvent(callDomLoadFunctions);
  18090. }
  18091. }();
  18092. function callDomLoadFunctions() {
  18093. if (isDomLoaded) { return; }
  18094. try { // test if we can really add/remove elements to/from the DOM; we don't want to fire it too early
  18095. var t = doc.getElementsByTagName("body")[0].appendChild(createElement("span"));
  18096. t.parentNode.removeChild(t);
  18097. }
  18098. catch (e) { return; }
  18099. isDomLoaded = true;
  18100. var dl = domLoadFnArr.length;
  18101. for (var i = 0; i < dl; i++) {
  18102. domLoadFnArr[i]();
  18103. }
  18104. }
  18105. function addDomLoadEvent(fn) {
  18106. if (isDomLoaded) {
  18107. fn();
  18108. }
  18109. else {
  18110. domLoadFnArr[domLoadFnArr.length] = fn; // Array.push() is only available in IE5.5+
  18111. }
  18112. }
  18113. /* Cross-browser onload
  18114. - Based on James Edwards' solution: http://brothercake.com/site/resources/scripts/onload/
  18115. - Will fire an event as soon as a web page including all of its assets are loaded
  18116. */
  18117. function addLoadEvent(fn) {
  18118. if (typeof win.addEventListener != UNDEF) {
  18119. win.addEventListener("load", fn, false);
  18120. }
  18121. else if (typeof doc.addEventListener != UNDEF) {
  18122. doc.addEventListener("load", fn, false);
  18123. }
  18124. else if (typeof win.attachEvent != UNDEF) {
  18125. addListener(win, "onload", fn);
  18126. }
  18127. else if (typeof win.onload == "function") {
  18128. var fnOld = win.onload;
  18129. win.onload = function() {
  18130. fnOld();
  18131. fn();
  18132. };
  18133. }
  18134. else {
  18135. win.onload = fn;
  18136. }
  18137. }
  18138. /* Main function
  18139. - Will preferably execute onDomLoad, otherwise onload (as a fallback)
  18140. */
  18141. function main() {
  18142. if (plugin) {
  18143. testPlayerVersion();
  18144. }
  18145. else {
  18146. matchVersions();
  18147. }
  18148. }
  18149. /* Detect the Flash Player version for non-Internet Explorer browsers
  18150. - Detecting the plug-in version via the object element is more precise than using the plugins collection item's description:
  18151. a. Both release and build numbers can be detected
  18152. b. Avoid wrong descriptions by corrupt installers provided by Adobe
  18153. c. Avoid wrong descriptions by multiple Flash Player entries in the plugin Array, caused by incorrect browser imports
  18154. - Disadvantage of this method is that it depends on the availability of the DOM, while the plugins collection is immediately available
  18155. */
  18156. function testPlayerVersion() {
  18157. var b = doc.getElementsByTagName("body")[0];
  18158. var o = createElement(OBJECT);
  18159. o.setAttribute("type", FLASH_MIME_TYPE);
  18160. var t = b.appendChild(o);
  18161. if (t) {
  18162. var counter = 0;
  18163. (function(){
  18164. if (typeof t.GetVariable != UNDEF) {
  18165. var d = t.GetVariable("$version");
  18166. if (d) {
  18167. d = d.split(" ")[1].split(",");
  18168. ua.pv = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
  18169. }
  18170. }
  18171. else if (counter < 10) {
  18172. counter++;
  18173. setTimeout(arguments.callee, 10);
  18174. return;
  18175. }
  18176. b.removeChild(o);
  18177. t = null;
  18178. matchVersions();
  18179. })();
  18180. }
  18181. else {
  18182. matchVersions();
  18183. }
  18184. }
  18185. /* Perform Flash Player and SWF version matching; static publishing only
  18186. */
  18187. function matchVersions() {
  18188. var rl = regObjArr.length;
  18189. if (rl > 0) {
  18190. for (var i = 0; i < rl; i++) { // for each registered object element
  18191. var id = regObjArr[i].id;
  18192. var cb = regObjArr[i].callbackFn;
  18193. var cbObj = {success:false, id:id};
  18194. if (ua.pv[0] > 0) {
  18195. var obj = getElementById(id);
  18196. if (obj) {
  18197. if (hasPlayerVersion(regObjArr[i].swfVersion) && !(ua.wk && ua.wk < 312)) { // Flash Player version >= published SWF version: Houston, we have a match!
  18198. setVisibility(id, true);
  18199. if (cb) {
  18200. cbObj.success = true;
  18201. cbObj.ref = getObjectById(id);
  18202. cb(cbObj);
  18203. }
  18204. }
  18205. else if (regObjArr[i].expressInstall && canExpressInstall()) { // show the Adobe Express Install dialog if set by the web page author and if supported
  18206. var att = {};
  18207. att.data = regObjArr[i].expressInstall;
  18208. att.width = obj.getAttribute("width") || "0";
  18209. att.height = obj.getAttribute("height") || "0";
  18210. if (obj.getAttribute("class")) { att.styleclass = obj.getAttribute("class"); }
  18211. if (obj.getAttribute("align")) { att.align = obj.getAttribute("align"); }
  18212. // parse HTML object param element's name-value pairs
  18213. var par = {};
  18214. var p = obj.getElementsByTagName("param");
  18215. var pl = p.length;
  18216. for (var j = 0; j < pl; j++) {
  18217. if (p[j].getAttribute("name").toLowerCase() != "movie") {
  18218. par[p[j].getAttribute("name")] = p[j].getAttribute("value");
  18219. }
  18220. }
  18221. showExpressInstall(att, par, id, cb);
  18222. }
  18223. else { // Flash Player and SWF version mismatch or an older Webkit engine that ignores the HTML object element's nested param elements: display alternative content instead of SWF
  18224. displayAltContent(obj);
  18225. if (cb) { cb(cbObj); }
  18226. }
  18227. }
  18228. }
  18229. else { // if no Flash Player is installed or the fp version cannot be detected we let the HTML object element do its job (either show a SWF or alternative content)
  18230. setVisibility(id, true);
  18231. if (cb) {
  18232. var o = getObjectById(id); // test whether there is an HTML object element or not
  18233. if (o && typeof o.SetVariable != UNDEF) {
  18234. cbObj.success = true;
  18235. cbObj.ref = o;
  18236. }
  18237. cb(cbObj);
  18238. }
  18239. }
  18240. }
  18241. }
  18242. }
  18243. function getObjectById(objectIdStr) {
  18244. var r = null;
  18245. var o = getElementById(objectIdStr);
  18246. if (o && o.nodeName == "OBJECT") {
  18247. if (typeof o.SetVariable != UNDEF) {
  18248. r = o;
  18249. }
  18250. else {
  18251. var n = o.getElementsByTagName(OBJECT)[0];
  18252. if (n) {
  18253. r = n;
  18254. }
  18255. }
  18256. }
  18257. return r;
  18258. }
  18259. /* Requirements for Adobe Express Install
  18260. - only one instance can be active at a time
  18261. - fp 6.0.65 or higher
  18262. - Win/Mac OS only
  18263. - no Webkit engines older than version 312
  18264. */
  18265. function canExpressInstall() {
  18266. return !isExpressInstallActive && hasPlayerVersion("6.0.65") && (ua.win || ua.mac) && !(ua.wk && ua.wk < 312);
  18267. }
  18268. /* Show the Adobe Express Install dialog
  18269. - Reference: http://www.adobe.com/cfusion/knowledgebase/index.cfm?id=6a253b75
  18270. */
  18271. function showExpressInstall(att, par, replaceElemIdStr, callbackFn) {
  18272. isExpressInstallActive = true;
  18273. storedCallbackFn = callbackFn || null;
  18274. storedCallbackObj = {success:false, id:replaceElemIdStr};
  18275. var obj = getElementById(replaceElemIdStr);
  18276. if (obj) {
  18277. if (obj.nodeName == "OBJECT") { // static publishing
  18278. storedAltContent = abstractAltContent(obj);
  18279. storedAltContentId = null;
  18280. }
  18281. else { // dynamic publishing
  18282. storedAltContent = obj;
  18283. storedAltContentId = replaceElemIdStr;
  18284. }
  18285. att.id = EXPRESS_INSTALL_ID;
  18286. if (typeof att.width == UNDEF || (!/%$/.test(att.width) && parseInt(att.width, 10) < 310)) { att.width = "310"; }
  18287. if (typeof att.height == UNDEF || (!/%$/.test(att.height) && parseInt(att.height, 10) < 137)) { att.height = "137"; }
  18288. doc.title = doc.title.slice(0, 47) + " - Flash Player Installation";
  18289. var pt = ua.ie && ua.win ? "ActiveX" : "PlugIn",
  18290. fv = "MMredirectURL=" + win.location.toString().replace(/&/g,"%26") + "&MMplayerType=" + pt + "&MMdoctitle=" + doc.title;
  18291. if (typeof par.flashvars != UNDEF) {
  18292. par.flashvars += "&" + fv;
  18293. }
  18294. else {
  18295. par.flashvars = fv;
  18296. }
  18297. // IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it,
  18298. // because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
  18299. if (ua.ie && ua.win && obj.readyState != 4) {
  18300. var newObj = createElement("div");
  18301. replaceElemIdStr += "SWFObjectNew";
  18302. newObj.setAttribute("id", replaceElemIdStr);
  18303. obj.parentNode.insertBefore(newObj, obj); // insert placeholder div that will be replaced by the object element that loads expressinstall.swf
  18304. obj.style.display = "none";
  18305. (function(){
  18306. if (obj.readyState == 4) {
  18307. obj.parentNode.removeChild(obj);
  18308. }
  18309. else {
  18310. setTimeout(arguments.callee, 10);
  18311. }
  18312. })();
  18313. }
  18314. createSWF(att, par, replaceElemIdStr);
  18315. }
  18316. }
  18317. /* Functions to abstract and display alternative content
  18318. */
  18319. function displayAltContent(obj) {
  18320. if (ua.ie && ua.win && obj.readyState != 4) {
  18321. // IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it,
  18322. // because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
  18323. var el = createElement("div");
  18324. obj.parentNode.insertBefore(el, obj); // insert placeholder div that will be replaced by the alternative content
  18325. el.parentNode.replaceChild(abstractAltContent(obj), el);
  18326. obj.style.display = "none";
  18327. (function(){
  18328. if (obj.readyState == 4) {
  18329. obj.parentNode.removeChild(obj);
  18330. }
  18331. else {
  18332. setTimeout(arguments.callee, 10);
  18333. }
  18334. })();
  18335. }
  18336. else {
  18337. obj.parentNode.replaceChild(abstractAltContent(obj), obj);
  18338. }
  18339. }
  18340. function abstractAltContent(obj) {
  18341. var ac = createElement("div");
  18342. if (ua.win && ua.ie) {
  18343. ac.innerHTML = obj.innerHTML;
  18344. }
  18345. else {
  18346. var nestedObj = obj.getElementsByTagName(OBJECT)[0];
  18347. if (nestedObj) {
  18348. var c = nestedObj.childNodes;
  18349. if (c) {
  18350. var cl = c.length;
  18351. for (var i = 0; i < cl; i++) {
  18352. if (!(c[i].nodeType == 1 && c[i].nodeName == "PARAM") && !(c[i].nodeType == 8)) {
  18353. ac.appendChild(c[i].cloneNode(true));
  18354. }
  18355. }
  18356. }
  18357. }
  18358. }
  18359. return ac;
  18360. }
  18361. /* Cross-browser dynamic SWF creation
  18362. */
  18363. function createSWF(attObj, parObj, id) {
  18364. var r, el = getElementById(id);
  18365. if (ua.wk && ua.wk < 312) { return r; }
  18366. if (el) {
  18367. if (typeof attObj.id == UNDEF) { // if no 'id' is defined for the object element, it will inherit the 'id' from the alternative content
  18368. attObj.id = id;
  18369. }
  18370. if (ua.ie && ua.win) { // Internet Explorer + the HTML object element + W3C DOM methods do not combine: fall back to outerHTML
  18371. var att = "";
  18372. for (var i in attObj) {
  18373. if (attObj[i] != Object.prototype[i]) { // filter out prototype additions from other potential libraries
  18374. if (i.toLowerCase() == "data") {
  18375. parObj.movie = attObj[i];
  18376. }
  18377. else if (i.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
  18378. att += ' class="' + attObj[i] + '"';
  18379. }
  18380. else if (i.toLowerCase() != "classid") {
  18381. att += ' ' + i + '="' + attObj[i] + '"';
  18382. }
  18383. }
  18384. }
  18385. var par = "";
  18386. for (var j in parObj) {
  18387. if (parObj[j] != Object.prototype[j]) { // filter out prototype additions from other potential libraries
  18388. par += '<param name="' + j + '" value="' + parObj[j] + '" />';
  18389. }
  18390. }
  18391. el.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"' + att + '>' + par + '</object>';
  18392. objIdArr[objIdArr.length] = attObj.id; // stored to fix object 'leaks' on unload (dynamic publishing only)
  18393. r = getElementById(attObj.id);
  18394. }
  18395. else { // well-behaving browsers
  18396. var o = createElement(OBJECT);
  18397. o.setAttribute("type", FLASH_MIME_TYPE);
  18398. for (var m in attObj) {
  18399. if (attObj[m] != Object.prototype[m]) { // filter out prototype additions from other potential libraries
  18400. if (m.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
  18401. o.setAttribute("class", attObj[m]);
  18402. }
  18403. else if (m.toLowerCase() != "classid") { // filter out IE specific attribute
  18404. o.setAttribute(m, attObj[m]);
  18405. }
  18406. }
  18407. }
  18408. for (var n in parObj) {
  18409. if (parObj[n] != Object.prototype[n] && n.toLowerCase() != "movie") { // filter out prototype additions from other potential libraries and IE specific param element
  18410. createObjParam(o, n, parObj[n]);
  18411. }
  18412. }
  18413. el.parentNode.replaceChild(o, el);
  18414. r = o;
  18415. }
  18416. }
  18417. return r;
  18418. }
  18419. function createObjParam(el, pName, pValue) {
  18420. var p = createElement("param");
  18421. p.setAttribute("name", pName);
  18422. p.setAttribute("value", pValue);
  18423. el.appendChild(p);
  18424. }
  18425. /* Cross-browser SWF removal
  18426. - Especially needed to safely and completely remove a SWF in Internet Explorer
  18427. */
  18428. function removeSWF(id) {
  18429. var obj = getElementById(id);
  18430. if (obj && obj.nodeName == "OBJECT") {
  18431. if (ua.ie && ua.win) {
  18432. obj.style.display = "none";
  18433. (function(){
  18434. if (obj.readyState == 4) {
  18435. removeObjectInIE(id);
  18436. }
  18437. else {
  18438. setTimeout(arguments.callee, 10);
  18439. }
  18440. })();
  18441. }
  18442. else {
  18443. obj.parentNode.removeChild(obj);
  18444. }
  18445. }
  18446. }
  18447. function removeObjectInIE(id) {
  18448. var obj = getElementById(id);
  18449. if (obj) {
  18450. for (var i in obj) {
  18451. if (typeof obj[i] == "function") {
  18452. obj[i] = null;
  18453. }
  18454. }
  18455. obj.parentNode.removeChild(obj);
  18456. }
  18457. }
  18458. /* Functions to optimize JavaScript compression
  18459. */
  18460. function getElementById(id) {
  18461. var el = null;
  18462. try {
  18463. el = doc.getElementById(id);
  18464. }
  18465. catch (e) {}
  18466. return el;
  18467. }
  18468. function createElement(el) {
  18469. return doc.createElement(el);
  18470. }
  18471. /* Updated attachEvent function for Internet Explorer
  18472. - Stores attachEvent information in an Array, so on unload the detachEvent functions can be called to avoid memory leaks
  18473. */
  18474. function addListener(target, eventType, fn) {
  18475. target.attachEvent(eventType, fn);
  18476. listenersArr[listenersArr.length] = [target, eventType, fn];
  18477. }
  18478. /* Flash Player and SWF content version matching
  18479. */
  18480. function hasPlayerVersion(rv) {
  18481. var pv = ua.pv, v = rv.split(".");
  18482. v[0] = parseInt(v[0], 10);
  18483. v[1] = parseInt(v[1], 10) || 0; // supports short notation, e.g. "9" instead of "9.0.0"
  18484. v[2] = parseInt(v[2], 10) || 0;
  18485. return (pv[0] > v[0] || (pv[0] == v[0] && pv[1] > v[1]) || (pv[0] == v[0] && pv[1] == v[1] && pv[2] >= v[2])) ? true : false;
  18486. }
  18487. /* Cross-browser dynamic CSS creation
  18488. - Based on Bobby van der Sluis' solution: http://www.bobbyvandersluis.com/articles/dynamicCSS.php
  18489. */
  18490. function createCSS(sel, decl, media, newStyle) {
  18491. if (ua.ie && ua.mac) { return; }
  18492. var h = doc.getElementsByTagName("head")[0];
  18493. if (!h) { return; } // to also support badly authored HTML pages that lack a head element
  18494. var m = (media && typeof media == "string") ? media : "screen";
  18495. if (newStyle) {
  18496. dynamicStylesheet = null;
  18497. dynamicStylesheetMedia = null;
  18498. }
  18499. if (!dynamicStylesheet || dynamicStylesheetMedia != m) {
  18500. // create dynamic stylesheet + get a global reference to it
  18501. var s = createElement("style");
  18502. s.setAttribute("type", "text/css");
  18503. s.setAttribute("media", m);
  18504. dynamicStylesheet = h.appendChild(s);
  18505. if (ua.ie && ua.win && typeof doc.styleSheets != UNDEF && doc.styleSheets.length > 0) {
  18506. dynamicStylesheet = doc.styleSheets[doc.styleSheets.length - 1];
  18507. }
  18508. dynamicStylesheetMedia = m;
  18509. }
  18510. // add style rule
  18511. if (ua.ie && ua.win) {
  18512. if (dynamicStylesheet && typeof dynamicStylesheet.addRule == OBJECT) {
  18513. dynamicStylesheet.addRule(sel, decl);
  18514. }
  18515. }
  18516. else {
  18517. if (dynamicStylesheet && typeof doc.createTextNode != UNDEF) {
  18518. dynamicStylesheet.appendChild(doc.createTextNode(sel + " {" + decl + "}"));
  18519. }
  18520. }
  18521. }
  18522. function setVisibility(id, isVisible) {
  18523. if (!autoHideShow) { return; }
  18524. var v = isVisible ? "visible" : "hidden";
  18525. if (isDomLoaded && getElementById(id)) {
  18526. getElementById(id).style.visibility = v;
  18527. }
  18528. else {
  18529. createCSS("#" + id, "visibility:" + v);
  18530. }
  18531. }
  18532. /* Filter to avoid XSS attacks
  18533. */
  18534. function urlEncodeIfNecessary(s) {
  18535. var regex = /[\\\"<>\.;]/;
  18536. var hasBadChars = regex.exec(s) != null;
  18537. return hasBadChars && typeof encodeURIComponent != UNDEF ? encodeURIComponent(s) : s;
  18538. }
  18539. /* Release memory to avoid memory leaks caused by closures, fix hanging audio/video threads and force open sockets/NetConnections to disconnect (Internet Explorer only)
  18540. */
  18541. var cleanup = function() {
  18542. if (ua.ie && ua.win) {
  18543. window.attachEvent("onunload", function() {
  18544. // remove listeners to avoid memory leaks
  18545. var ll = listenersArr.length;
  18546. for (var i = 0; i < ll; i++) {
  18547. listenersArr[i][0].detachEvent(listenersArr[i][1], listenersArr[i][2]);
  18548. }
  18549. // cleanup dynamically embedded objects to fix audio/video threads and force open sockets and NetConnections to disconnect
  18550. var il = objIdArr.length;
  18551. for (var j = 0; j < il; j++) {
  18552. removeSWF(objIdArr[j]);
  18553. }
  18554. // cleanup library's main closures to avoid memory leaks
  18555. for (var k in ua) {
  18556. ua[k] = null;
  18557. }
  18558. ua = null;
  18559. for (var l in swfobject) {
  18560. swfobject[l] = null;
  18561. }
  18562. swfobject = null;
  18563. });
  18564. }
  18565. }();
  18566. return {
  18567. /* Public API
  18568. - Reference: http://code.google.com/p/swfobject/wiki/documentation
  18569. */
  18570. registerObject: function(objectIdStr, swfVersionStr, xiSwfUrlStr, callbackFn) {
  18571. if (ua.w3 && objectIdStr && swfVersionStr) {
  18572. var regObj = {};
  18573. regObj.id = objectIdStr;
  18574. regObj.swfVersion = swfVersionStr;
  18575. regObj.expressInstall = xiSwfUrlStr;
  18576. regObj.callbackFn = callbackFn;
  18577. regObjArr[regObjArr.length] = regObj;
  18578. setVisibility(objectIdStr, false);
  18579. }
  18580. else if (callbackFn) {
  18581. callbackFn({success:false, id:objectIdStr});
  18582. }
  18583. },
  18584. getObjectById: function(objectIdStr) {
  18585. if (ua.w3) {
  18586. return getObjectById(objectIdStr);
  18587. }
  18588. },
  18589. embedSWF: function(swfUrlStr, replaceElemIdStr, widthStr, heightStr, swfVersionStr, xiSwfUrlStr, flashvarsObj, parObj, attObj, callbackFn) {
  18590. var callbackObj = {success:false, id:replaceElemIdStr};
  18591. if (ua.w3 && !(ua.wk && ua.wk < 312) && swfUrlStr && replaceElemIdStr && widthStr && heightStr && swfVersionStr) {
  18592. setVisibility(replaceElemIdStr, false);
  18593. addDomLoadEvent(function() {
  18594. widthStr += ""; // auto-convert to string
  18595. heightStr += "";
  18596. var att = {};
  18597. if (attObj && typeof attObj === OBJECT) {
  18598. for (var i in attObj) { // copy object to avoid the use of references, because web authors often reuse attObj for multiple SWFs
  18599. att[i] = attObj[i];
  18600. }
  18601. }
  18602. att.data = swfUrlStr;
  18603. att.width = widthStr;
  18604. att.height = heightStr;
  18605. var par = {};
  18606. if (parObj && typeof parObj === OBJECT) {
  18607. for (var j in parObj) { // copy object to avoid the use of references, because web authors often reuse parObj for multiple SWFs
  18608. par[j] = parObj[j];
  18609. }
  18610. }
  18611. if (flashvarsObj && typeof flashvarsObj === OBJECT) {
  18612. for (var k in flashvarsObj) { // copy object to avoid the use of references, because web authors often reuse flashvarsObj for multiple SWFs
  18613. if (typeof par.flashvars != UNDEF) {
  18614. par.flashvars += "&" + k + "=" + flashvarsObj[k];
  18615. }
  18616. else {
  18617. par.flashvars = k + "=" + flashvarsObj[k];
  18618. }
  18619. }
  18620. }
  18621. if (hasPlayerVersion(swfVersionStr)) { // create SWF
  18622. var obj = createSWF(att, par, replaceElemIdStr);
  18623. if (att.id == replaceElemIdStr) {
  18624. setVisibility(replaceElemIdStr, true);
  18625. }
  18626. callbackObj.success = true;
  18627. callbackObj.ref = obj;
  18628. }
  18629. else if (xiSwfUrlStr && canExpressInstall()) { // show Adobe Express Install
  18630. att.data = xiSwfUrlStr;
  18631. showExpressInstall(att, par, replaceElemIdStr, callbackFn);
  18632. return;
  18633. }
  18634. else { // show alternative content
  18635. setVisibility(replaceElemIdStr, true);
  18636. }
  18637. if (callbackFn) { callbackFn(callbackObj); }
  18638. });
  18639. }
  18640. else if (callbackFn) { callbackFn(callbackObj); }
  18641. },
  18642. switchOffAutoHideShow: function() {
  18643. autoHideShow = false;
  18644. },
  18645. ua: ua,
  18646. getFlashPlayerVersion: function() {
  18647. return { major:ua.pv[0], minor:ua.pv[1], release:ua.pv[2] };
  18648. },
  18649. hasFlashPlayerVersion: hasPlayerVersion,
  18650. createSWF: function(attObj, parObj, replaceElemIdStr) {
  18651. if (ua.w3) {
  18652. return createSWF(attObj, parObj, replaceElemIdStr);
  18653. }
  18654. else {
  18655. return undefined;
  18656. }
  18657. },
  18658. showExpressInstall: function(att, par, replaceElemIdStr, callbackFn) {
  18659. if (ua.w3 && canExpressInstall()) {
  18660. showExpressInstall(att, par, replaceElemIdStr, callbackFn);
  18661. }
  18662. },
  18663. removeSWF: function(objElemIdStr) {
  18664. if (ua.w3) {
  18665. removeSWF(objElemIdStr);
  18666. }
  18667. },
  18668. createCSS: function(selStr, declStr, mediaStr, newStyleBoolean) {
  18669. if (ua.w3) {
  18670. createCSS(selStr, declStr, mediaStr, newStyleBoolean);
  18671. }
  18672. },
  18673. addDomLoadEvent: addDomLoadEvent,
  18674. addLoadEvent: addLoadEvent,
  18675. getQueryParamValue: function(param) {
  18676. var q = doc.location.search || doc.location.hash;
  18677. if (q) {
  18678. if (/\?/.test(q)) { q = q.split("?")[1]; } // strip question mark
  18679. if (param == null) {
  18680. return urlEncodeIfNecessary(q);
  18681. }
  18682. var pairs = q.split("&");
  18683. for (var i = 0; i < pairs.length; i++) {
  18684. if (pairs[i].substring(0, pairs[i].indexOf("=")) == param) {
  18685. return urlEncodeIfNecessary(pairs[i].substring((pairs[i].indexOf("=") + 1)));
  18686. }
  18687. }
  18688. }
  18689. return "";
  18690. },
  18691. // For internal usage only
  18692. expressInstallCallback: function() {
  18693. if (isExpressInstallActive) {
  18694. var obj = getElementById(EXPRESS_INSTALL_ID);
  18695. if (obj && storedAltContent) {
  18696. obj.parentNode.replaceChild(storedAltContent, obj);
  18697. if (storedAltContentId) {
  18698. setVisibility(storedAltContentId, true);
  18699. if (ua.ie && ua.win) { storedAltContent.style.display = "block"; }
  18700. }
  18701. if (storedCallbackFn) { storedCallbackFn(storedCallbackObj); }
  18702. }
  18703. isExpressInstallActive = false;
  18704. }
  18705. }
  18706. };
  18707. }();
  18708. /**
  18709. * SWFUpload: http://www.swfupload.org, http://swfupload.googlecode.com
  18710. *
  18711. * mmSWFUpload 1.0: Flash upload dialog - http://profandesign.se/swfupload/, http://www.vinterwebb.se/
  18712. *
  18713. * SWFUpload is (c) 2006-2007 Lars Huring, Olov Nilz�n and Mammon Media and is released under the MIT License:
  18714. * http://www.opensource.org/licenses/mit-license.php
  18715. *
  18716. * SWFUpload 2 is (c) 2007-2008 Jake Roberts and is released under the MIT License:
  18717. * http://www.opensource.org/licenses/mit-license.php
  18718. *
  18719. */
  18720. /* ******************* */
  18721. /* Constructor & Init */
  18722. /* ******************* */
  18723. var SWFUpload;
  18724. if (SWFUpload == undefined) {
  18725. SWFUpload = function (settings) {
  18726. this.initSWFUpload(settings);
  18727. };
  18728. }
  18729. SWFUpload.prototype.initSWFUpload = function (settings) {
  18730. try {
  18731. this.customSettings = {}; // A container where developers can place their own settings associated with this instance.
  18732. this.settings = settings;
  18733. this.eventQueue = [];
  18734. this.movieName = "SWFUpload_" + SWFUpload.movieCount++;
  18735. this.movieElement = null;
  18736. // Setup global control tracking
  18737. SWFUpload.instances[this.movieName] = this;
  18738. // Load the settings. Load the Flash movie.
  18739. this.initSettings();
  18740. this.loadFlash();
  18741. this.displayDebugInfo();
  18742. } catch (ex) {
  18743. delete SWFUpload.instances[this.movieName];
  18744. throw ex;
  18745. }
  18746. };
  18747. /* *************** */
  18748. /* Static Members */
  18749. /* *************** */
  18750. SWFUpload.instances = {};
  18751. SWFUpload.movieCount = 0;
  18752. SWFUpload.version = "2.2.0 2009-03-25";
  18753. SWFUpload.QUEUE_ERROR = {
  18754. QUEUE_LIMIT_EXCEEDED : -100,
  18755. FILE_EXCEEDS_SIZE_LIMIT : -110,
  18756. ZERO_BYTE_FILE : -120,
  18757. INVALID_FILETYPE : -130
  18758. };
  18759. SWFUpload.UPLOAD_ERROR = {
  18760. HTTP_ERROR : -200,
  18761. MISSING_UPLOAD_URL : -210,
  18762. IO_ERROR : -220,
  18763. SECURITY_ERROR : -230,
  18764. UPLOAD_LIMIT_EXCEEDED : -240,
  18765. UPLOAD_FAILED : -250,
  18766. SPECIFIED_FILE_ID_NOT_FOUND : -260,
  18767. FILE_VALIDATION_FAILED : -270,
  18768. FILE_CANCELLED : -280,
  18769. UPLOAD_STOPPED : -290
  18770. };
  18771. SWFUpload.FILE_STATUS = {
  18772. QUEUED : -1,
  18773. IN_PROGRESS : -2,
  18774. ERROR : -3,
  18775. COMPLETE : -4,
  18776. CANCELLED : -5
  18777. };
  18778. SWFUpload.BUTTON_ACTION = {
  18779. SELECT_FILE : -100,
  18780. SELECT_FILES : -110,
  18781. START_UPLOAD : -120
  18782. };
  18783. SWFUpload.CURSOR = {
  18784. ARROW : -1,
  18785. HAND : -2
  18786. };
  18787. SWFUpload.WINDOW_MODE = {
  18788. WINDOW : "window",
  18789. TRANSPARENT : "transparent",
  18790. OPAQUE : "opaque"
  18791. };
  18792. // Private: takes a URL, determines if it is relative and converts to an absolute URL
  18793. // using the current site. Only processes the URL if it can, otherwise returns the URL untouched
  18794. SWFUpload.completeURL = function(url) {
  18795. if (typeof(url) !== "string" || url.match(/^https?:\/\//i) || url.match(/^\//)) {
  18796. return url;
  18797. }
  18798. var currentURL = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ":" + window.location.port : "");
  18799. var indexSlash = window.location.pathname.lastIndexOf("/");
  18800. if (indexSlash <= 0) {
  18801. path = "/";
  18802. } else {
  18803. path = window.location.pathname.substr(0, indexSlash) + "/";
  18804. }
  18805. return /*currentURL +*/ path + url;
  18806. };
  18807. /* ******************** */
  18808. /* Instance Members */
  18809. /* ******************** */
  18810. // Private: initSettings ensures that all the
  18811. // settings are set, getting a default value if one was not assigned.
  18812. SWFUpload.prototype.initSettings = function () {
  18813. this.ensureDefault = function (settingName, defaultValue) {
  18814. this.settings[settingName] = (this.settings[settingName] == undefined) ? defaultValue : this.settings[settingName];
  18815. };
  18816. // Upload backend settings
  18817. this.ensureDefault("upload_url", "");
  18818. this.ensureDefault("preserve_relative_urls", false);
  18819. this.ensureDefault("file_post_name", "Filedata");
  18820. this.ensureDefault("post_params", {});
  18821. this.ensureDefault("use_query_string", false);
  18822. this.ensureDefault("requeue_on_error", false);
  18823. this.ensureDefault("http_success", []);
  18824. this.ensureDefault("assume_success_timeout", 0);
  18825. // File Settings
  18826. this.ensureDefault("file_types", "*.*");
  18827. this.ensureDefault("file_types_description", "All Files");
  18828. this.ensureDefault("file_size_limit", 0); // Default zero means "unlimited"
  18829. this.ensureDefault("file_upload_limit", 0);
  18830. this.ensureDefault("file_queue_limit", 0);
  18831. // Flash Settings
  18832. this.ensureDefault("flash_url", "swfupload.swf");
  18833. this.ensureDefault("prevent_swf_caching", true);
  18834. // Button Settings
  18835. this.ensureDefault("button_image_url", "");
  18836. this.ensureDefault("button_width", 1);
  18837. this.ensureDefault("button_height", 1);
  18838. this.ensureDefault("button_text", "");
  18839. this.ensureDefault("button_text_style", "color: #000000; font-size: 16pt;");
  18840. this.ensureDefault("button_text_top_padding", 0);
  18841. this.ensureDefault("button_text_left_padding", 0);
  18842. this.ensureDefault("button_action", SWFUpload.BUTTON_ACTION.SELECT_FILES);
  18843. this.ensureDefault("button_disabled", false);
  18844. this.ensureDefault("button_placeholder_id", "");
  18845. this.ensureDefault("button_placeholder", null);
  18846. this.ensureDefault("button_cursor", SWFUpload.CURSOR.ARROW);
  18847. this.ensureDefault("button_window_mode", SWFUpload.WINDOW_MODE.WINDOW);
  18848. // Debug Settings
  18849. this.ensureDefault("debug", false);
  18850. this.settings.debug_enabled = this.settings.debug; // Here to maintain v2 API
  18851. // Event Handlers
  18852. this.settings.return_upload_start_handler = this.returnUploadStart;
  18853. this.ensureDefault("swfupload_loaded_handler", null);
  18854. this.ensureDefault("file_dialog_start_handler", null);
  18855. this.ensureDefault("file_queued_handler", null);
  18856. this.ensureDefault("file_queue_error_handler", null);
  18857. this.ensureDefault("file_dialog_complete_handler", null);
  18858. this.ensureDefault("upload_start_handler", null);
  18859. this.ensureDefault("upload_progress_handler", null);
  18860. this.ensureDefault("upload_error_handler", null);
  18861. this.ensureDefault("upload_success_handler", null);
  18862. this.ensureDefault("upload_complete_handler", null);
  18863. this.ensureDefault("debug_handler", this.debugMessage);
  18864. this.ensureDefault("custom_settings", {});
  18865. // Other settings
  18866. this.customSettings = this.settings.custom_settings;
  18867. // Update the flash url if needed
  18868. if (!!this.settings.prevent_swf_caching) {
  18869. this.settings.flash_url = this.settings.flash_url + (this.settings.flash_url.indexOf("?") < 0 ? "?" : "&") + "preventswfcaching=" + new Date().getTime();
  18870. }
  18871. if (!this.settings.preserve_relative_urls) {
  18872. //this.settings.flash_url = SWFUpload.completeURL(this.settings.flash_url); // Don't need to do this one since flash doesn't look at it
  18873. this.settings.upload_url = SWFUpload.completeURL(this.settings.upload_url);
  18874. this.settings.button_image_url = SWFUpload.completeURL(this.settings.button_image_url);
  18875. }
  18876. delete this.ensureDefault;
  18877. };
  18878. // Private: loadFlash replaces the button_placeholder element with the flash movie.
  18879. SWFUpload.prototype.loadFlash = function () {
  18880. var targetElement, tempParent;
  18881. // Make sure an element with the ID we are going to use doesn't already exist
  18882. if (document.getElementById(this.movieName) !== null) {
  18883. throw "ID " + this.movieName + " is already in use. The Flash Object could not be added";
  18884. }
  18885. // Get the element where we will be placing the flash movie
  18886. targetElement = document.getElementById(this.settings.button_placeholder_id) || this.settings.button_placeholder;
  18887. if (targetElement == undefined) {
  18888. throw "Could not find the placeholder element: " + this.settings.button_placeholder_id;
  18889. }
  18890. // Append the container and load the flash
  18891. tempParent = document.createElement("div");
  18892. tempParent.innerHTML = this.getFlashHTML(); // Using innerHTML is non-standard but the only sensible way to dynamically add Flash in IE (and maybe other browsers)
  18893. targetElement.parentNode.replaceChild(tempParent.firstChild, targetElement);
  18894. // Fix IE Flash/Form bug
  18895. if (window[this.movieName] == undefined) {
  18896. window[this.movieName] = this.getMovieElement();
  18897. }
  18898. };
  18899. // Private: getFlashHTML generates the object tag needed to embed the flash in to the document
  18900. SWFUpload.prototype.getFlashHTML = function () {
  18901. // Flash Satay object syntax: http://www.alistapart.com/articles/flashsatay
  18902. return ['<object id="', this.movieName, '" type="application/x-shockwave-flash" data="', this.settings.flash_url, '" width="', this.settings.button_width, '" height="', this.settings.button_height, '" class="swfupload">',
  18903. '<param name="wmode" value="', this.settings.button_window_mode, '" />',
  18904. '<param name="movie" value="', this.settings.flash_url, '" />',
  18905. '<param name="quality" value="high" />',
  18906. '<param name="menu" value="false" />',
  18907. '<param name="allowScriptAccess" value="always" />',
  18908. '<param name="flashvars" value="' + this.getFlashVars() + '" />',
  18909. '</object>'].join("");
  18910. };
  18911. // Private: getFlashVars builds the parameter string that will be passed
  18912. // to flash in the flashvars param.
  18913. SWFUpload.prototype.getFlashVars = function () {
  18914. // Build a string from the post param object
  18915. var paramString = this.buildParamString();
  18916. var httpSuccessString = this.settings.http_success.join(",");
  18917. // Build the parameter string
  18918. return ["movieName=", encodeURIComponent(this.movieName),
  18919. "&amp;uploadURL=", encodeURIComponent(this.settings.upload_url),
  18920. "&amp;useQueryString=", encodeURIComponent(this.settings.use_query_string),
  18921. "&amp;requeueOnError=", encodeURIComponent(this.settings.requeue_on_error),
  18922. "&amp;httpSuccess=", encodeURIComponent(httpSuccessString),
  18923. "&amp;assumeSuccessTimeout=", encodeURIComponent(this.settings.assume_success_timeout),
  18924. "&amp;params=", encodeURIComponent(paramString),
  18925. "&amp;filePostName=", encodeURIComponent(this.settings.file_post_name),
  18926. "&amp;fileTypes=", encodeURIComponent(this.settings.file_types),
  18927. "&amp;fileTypesDescription=", encodeURIComponent(this.settings.file_types_description),
  18928. "&amp;fileSizeLimit=", encodeURIComponent(this.settings.file_size_limit),
  18929. "&amp;fileUploadLimit=", encodeURIComponent(this.settings.file_upload_limit),
  18930. "&amp;fileQueueLimit=", encodeURIComponent(this.settings.file_queue_limit),
  18931. "&amp;debugEnabled=", encodeURIComponent(this.settings.debug_enabled),
  18932. "&amp;buttonImageURL=", encodeURIComponent(this.settings.button_image_url),
  18933. "&amp;buttonWidth=", encodeURIComponent(this.settings.button_width),
  18934. "&amp;buttonHeight=", encodeURIComponent(this.settings.button_height),
  18935. "&amp;buttonText=", encodeURIComponent(this.settings.button_text),
  18936. "&amp;buttonTextTopPadding=", encodeURIComponent(this.settings.button_text_top_padding),
  18937. "&amp;buttonTextLeftPadding=", encodeURIComponent(this.settings.button_text_left_padding),
  18938. "&amp;buttonTextStyle=", encodeURIComponent(this.settings.button_text_style),
  18939. "&amp;buttonAction=", encodeURIComponent(this.settings.button_action),
  18940. "&amp;buttonDisabled=", encodeURIComponent(this.settings.button_disabled),
  18941. "&amp;buttonCursor=", encodeURIComponent(this.settings.button_cursor)
  18942. ].join("");
  18943. };
  18944. // Public: getMovieElement retrieves the DOM reference to the Flash element added by SWFUpload
  18945. // The element is cached after the first lookup
  18946. SWFUpload.prototype.getMovieElement = function () {
  18947. if (this.movieElement == undefined) {
  18948. this.movieElement = document.getElementById(this.movieName);
  18949. }
  18950. if (this.movieElement === null) {
  18951. throw "Could not find Flash element";
  18952. }
  18953. return this.movieElement;
  18954. };
  18955. // Private: buildParamString takes the name/value pairs in the post_params setting object
  18956. // and joins them up in to a string formatted "name=value&amp;name=value"
  18957. SWFUpload.prototype.buildParamString = function () {
  18958. var postParams = this.settings.post_params;
  18959. var paramStringPairs = [];
  18960. if (typeof(postParams) === "object") {
  18961. for (var name in postParams) {
  18962. if (postParams.hasOwnProperty(name)) {
  18963. paramStringPairs.push(encodeURIComponent(name.toString()) + "=" + encodeURIComponent(postParams[name].toString()));
  18964. }
  18965. }
  18966. }
  18967. return paramStringPairs.join("&amp;");
  18968. };
  18969. // Public: Used to remove a SWFUpload instance from the page. This method strives to remove
  18970. // all references to the SWF, and other objects so memory is properly freed.
  18971. // Returns true if everything was destroyed. Returns a false if a failure occurs leaving SWFUpload in an inconsistant state.
  18972. // Credits: Major improvements provided by steffen
  18973. SWFUpload.prototype.destroy = function () {
  18974. try {
  18975. // Make sure Flash is done before we try to remove it
  18976. this.cancelUpload(null, false);
  18977. // Remove the SWFUpload DOM nodes
  18978. var movieElement = null;
  18979. movieElement = this.getMovieElement();
  18980. if (movieElement && typeof(movieElement.CallFunction) === "unknown") { // We only want to do this in IE
  18981. // Loop through all the movie's properties and remove all function references (DOM/JS IE 6/7 memory leak workaround)
  18982. for (var i in movieElement) {
  18983. try {
  18984. if (typeof(movieElement[i]) === "function") {
  18985. movieElement[i] = null;
  18986. }
  18987. } catch (ex1) {}
  18988. }
  18989. // Remove the Movie Element from the page
  18990. try {
  18991. movieElement.parentNode.removeChild(movieElement);
  18992. } catch (ex) {}
  18993. }
  18994. // Remove IE form fix reference
  18995. window[this.movieName] = null;
  18996. // Destroy other references
  18997. SWFUpload.instances[this.movieName] = null;
  18998. delete SWFUpload.instances[this.movieName];
  18999. this.movieElement = null;
  19000. this.settings = null;
  19001. this.customSettings = null;
  19002. this.eventQueue = null;
  19003. this.movieName = null;
  19004. return true;
  19005. } catch (ex2) {
  19006. return false;
  19007. }
  19008. };
  19009. // Public: displayDebugInfo prints out settings and configuration
  19010. // information about this SWFUpload instance.
  19011. // This function (and any references to it) can be deleted when placing
  19012. // SWFUpload in production.
  19013. SWFUpload.prototype.displayDebugInfo = function () {
  19014. this.debug(
  19015. [
  19016. "---SWFUpload Instance Info---\n",
  19017. "Version: ", SWFUpload.version, "\n",
  19018. "Movie Name: ", this.movieName, "\n",
  19019. "Settings:\n",
  19020. "\t", "upload_url: ", this.settings.upload_url, "\n",
  19021. "\t", "flash_url: ", this.settings.flash_url, "\n",
  19022. "\t", "use_query_string: ", this.settings.use_query_string.toString(), "\n",
  19023. "\t", "requeue_on_error: ", this.settings.requeue_on_error.toString(), "\n",
  19024. "\t", "http_success: ", this.settings.http_success.join(", "), "\n",
  19025. "\t", "assume_success_timeout: ", this.settings.assume_success_timeout, "\n",
  19026. "\t", "file_post_name: ", this.settings.file_post_name, "\n",
  19027. "\t", "post_params: ", this.settings.post_params.toString(), "\n",
  19028. "\t", "file_types: ", this.settings.file_types, "\n",
  19029. "\t", "file_types_description: ", this.settings.file_types_description, "\n",
  19030. "\t", "file_size_limit: ", this.settings.file_size_limit, "\n",
  19031. "\t", "file_upload_limit: ", this.settings.file_upload_limit, "\n",
  19032. "\t", "file_queue_limit: ", this.settings.file_queue_limit, "\n",
  19033. "\t", "debug: ", this.settings.debug.toString(), "\n",
  19034. "\t", "prevent_swf_caching: ", this.settings.prevent_swf_caching.toString(), "\n",
  19035. "\t", "button_placeholder_id: ", this.settings.button_placeholder_id.toString(), "\n",
  19036. "\t", "button_placeholder: ", (this.settings.button_placeholder ? "Set" : "Not Set"), "\n",
  19037. "\t", "button_image_url: ", this.settings.button_image_url.toString(), "\n",
  19038. "\t", "button_width: ", this.settings.button_width.toString(), "\n",
  19039. "\t", "button_height: ", this.settings.button_height.toString(), "\n",
  19040. "\t", "button_text: ", this.settings.button_text.toString(), "\n",
  19041. "\t", "button_text_style: ", this.settings.button_text_style.toString(), "\n",
  19042. "\t", "button_text_top_padding: ", this.settings.button_text_top_padding.toString(), "\n",
  19043. "\t", "button_text_left_padding: ", this.settings.button_text_left_padding.toString(), "\n",
  19044. "\t", "button_action: ", this.settings.button_action.toString(), "\n",
  19045. "\t", "button_disabled: ", this.settings.button_disabled.toString(), "\n",
  19046. "\t", "custom_settings: ", this.settings.custom_settings.toString(), "\n",
  19047. "Event Handlers:\n",
  19048. "\t", "swfupload_loaded_handler assigned: ", (typeof this.settings.swfupload_loaded_handler === "function").toString(), "\n",
  19049. "\t", "file_dialog_start_handler assigned: ", (typeof this.settings.file_dialog_start_handler === "function").toString(), "\n",
  19050. "\t", "file_queued_handler assigned: ", (typeof this.settings.file_queued_handler === "function").toString(), "\n",
  19051. "\t", "file_queue_error_handler assigned: ", (typeof this.settings.file_queue_error_handler === "function").toString(), "\n",
  19052. "\t", "upload_start_handler assigned: ", (typeof this.settings.upload_start_handler === "function").toString(), "\n",
  19053. "\t", "upload_progress_handler assigned: ", (typeof this.settings.upload_progress_handler === "function").toString(), "\n",
  19054. "\t", "upload_error_handler assigned: ", (typeof this.settings.upload_error_handler === "function").toString(), "\n",
  19055. "\t", "upload_success_handler assigned: ", (typeof this.settings.upload_success_handler === "function").toString(), "\n",
  19056. "\t", "upload_complete_handler assigned: ", (typeof this.settings.upload_complete_handler === "function").toString(), "\n",
  19057. "\t", "debug_handler assigned: ", (typeof this.settings.debug_handler === "function").toString(), "\n"
  19058. ].join("")
  19059. );
  19060. };
  19061. /* Note: addSetting and getSetting are no longer used by SWFUpload but are included
  19062. the maintain v2 API compatibility
  19063. */
  19064. // Public: (Deprecated) addSetting adds a setting value. If the value given is undefined or null then the default_value is used.
  19065. SWFUpload.prototype.addSetting = function (name, value, default_value) {
  19066. if (value == undefined) {
  19067. return (this.settings[name] = default_value);
  19068. } else {
  19069. return (this.settings[name] = value);
  19070. }
  19071. };
  19072. // Public: (Deprecated) getSetting gets a setting. Returns an empty string if the setting was not found.
  19073. SWFUpload.prototype.getSetting = function (name) {
  19074. if (this.settings[name] != undefined) {
  19075. return this.settings[name];
  19076. }
  19077. return "";
  19078. };
  19079. // Private: callFlash handles function calls made to the Flash element.
  19080. // Calls are made with a setTimeout for some functions to work around
  19081. // bugs in the ExternalInterface library.
  19082. SWFUpload.prototype.callFlash = function (functionName, argumentArray) {
  19083. argumentArray = argumentArray || [];
  19084. var movieElement = this.getMovieElement();
  19085. var returnValue, returnString;
  19086. // Flash's method if calling ExternalInterface methods (code adapted from MooTools).
  19087. try {
  19088. returnString = movieElement.CallFunction('<invoke name="' + functionName + '" returntype="javascript">' + __flash__argumentsToXML(argumentArray, 0) + '</invoke>');
  19089. returnValue = eval(returnString);
  19090. } catch (ex) {
  19091. throw "Call to " + functionName + " failed";
  19092. }
  19093. // Unescape file post param values
  19094. if (returnValue != undefined && typeof returnValue.post === "object") {
  19095. returnValue = this.unescapeFilePostParams(returnValue);
  19096. }
  19097. return returnValue;
  19098. };
  19099. /* *****************************
  19100. -- Flash control methods --
  19101. Your UI should use these
  19102. to operate SWFUpload
  19103. ***************************** */
  19104. // WARNING: this function does not work in Flash Player 10
  19105. // Public: selectFile causes a File Selection Dialog window to appear. This
  19106. // dialog only allows 1 file to be selected.
  19107. SWFUpload.prototype.selectFile = function () {
  19108. this.callFlash("SelectFile");
  19109. };
  19110. // WARNING: this function does not work in Flash Player 10
  19111. // Public: selectFiles causes a File Selection Dialog window to appear/ This
  19112. // dialog allows the user to select any number of files
  19113. // Flash Bug Warning: Flash limits the number of selectable files based on the combined length of the file names.
  19114. // If the selection name length is too long the dialog will fail in an unpredictable manner. There is no work-around
  19115. // for this bug.
  19116. SWFUpload.prototype.selectFiles = function () {
  19117. this.callFlash("SelectFiles");
  19118. };
  19119. // Public: startUpload starts uploading the first file in the queue unless
  19120. // the optional parameter 'fileID' specifies the ID
  19121. SWFUpload.prototype.startUpload = function (fileID) {
  19122. this.callFlash("StartUpload", [fileID]);
  19123. };
  19124. // Public: cancelUpload cancels any queued file. The fileID parameter may be the file ID or index.
  19125. // If you do not specify a fileID the current uploading file or first file in the queue is cancelled.
  19126. // If you do not want the uploadError event to trigger you can specify false for the triggerErrorEvent parameter.
  19127. SWFUpload.prototype.cancelUpload = function (fileID, triggerErrorEvent) {
  19128. if (triggerErrorEvent !== false) {
  19129. triggerErrorEvent = true;
  19130. }
  19131. this.callFlash("CancelUpload", [fileID, triggerErrorEvent]);
  19132. };
  19133. // Public: stopUpload stops the current upload and requeues the file at the beginning of the queue.
  19134. // If nothing is currently uploading then nothing happens.
  19135. SWFUpload.prototype.stopUpload = function () {
  19136. this.callFlash("StopUpload");
  19137. };
  19138. /* ************************
  19139. * Settings methods
  19140. * These methods change the SWFUpload settings.
  19141. * SWFUpload settings should not be changed directly on the settings object
  19142. * since many of the settings need to be passed to Flash in order to take
  19143. * effect.
  19144. * *********************** */
  19145. // Public: getStats gets the file statistics object.
  19146. SWFUpload.prototype.getStats = function () {
  19147. return this.callFlash("GetStats");
  19148. };
  19149. // Public: setStats changes the SWFUpload statistics. You shouldn't need to
  19150. // change the statistics but you can. Changing the statistics does not
  19151. // affect SWFUpload accept for the successful_uploads count which is used
  19152. // by the upload_limit setting to determine how many files the user may upload.
  19153. SWFUpload.prototype.setStats = function (statsObject) {
  19154. this.callFlash("SetStats", [statsObject]);
  19155. };
  19156. // Public: getFile retrieves a File object by ID or Index. If the file is
  19157. // not found then 'null' is returned.
  19158. SWFUpload.prototype.getFile = function (fileID) {
  19159. if (typeof(fileID) === "number") {
  19160. return this.callFlash("GetFileByIndex", [fileID]);
  19161. } else {
  19162. return this.callFlash("GetFile", [fileID]);
  19163. }
  19164. };
  19165. // Public: addFileParam sets a name/value pair that will be posted with the
  19166. // file specified by the Files ID. If the name already exists then the
  19167. // exiting value will be overwritten.
  19168. SWFUpload.prototype.addFileParam = function (fileID, name, value) {
  19169. return this.callFlash("AddFileParam", [fileID, name, value]);
  19170. };
  19171. // Public: removeFileParam removes a previously set (by addFileParam) name/value
  19172. // pair from the specified file.
  19173. SWFUpload.prototype.removeFileParam = function (fileID, name) {
  19174. this.callFlash("RemoveFileParam", [fileID, name]);
  19175. };
  19176. // Public: setUploadUrl changes the upload_url setting.
  19177. SWFUpload.prototype.setUploadURL = function (url) {
  19178. this.settings.upload_url = url.toString();
  19179. this.callFlash("SetUploadURL", [url]);
  19180. };
  19181. // Public: setPostParams changes the post_params setting
  19182. SWFUpload.prototype.setPostParams = function (paramsObject) {
  19183. this.settings.post_params = paramsObject;
  19184. this.callFlash("SetPostParams", [paramsObject]);
  19185. };
  19186. // Public: addPostParam adds post name/value pair. Each name can have only one value.
  19187. SWFUpload.prototype.addPostParam = function (name, value) {
  19188. this.settings.post_params[name] = value;
  19189. this.callFlash("SetPostParams", [this.settings.post_params]);
  19190. };
  19191. // Public: removePostParam deletes post name/value pair.
  19192. SWFUpload.prototype.removePostParam = function (name) {
  19193. delete this.settings.post_params[name];
  19194. this.callFlash("SetPostParams", [this.settings.post_params]);
  19195. };
  19196. // Public: setFileTypes changes the file_types setting and the file_types_description setting
  19197. SWFUpload.prototype.setFileTypes = function (types, description) {
  19198. this.settings.file_types = types;
  19199. this.settings.file_types_description = description;
  19200. this.callFlash("SetFileTypes", [types, description]);
  19201. };
  19202. // Public: setFileSizeLimit changes the file_size_limit setting
  19203. SWFUpload.prototype.setFileSizeLimit = function (fileSizeLimit) {
  19204. this.settings.file_size_limit = fileSizeLimit;
  19205. this.callFlash("SetFileSizeLimit", [fileSizeLimit]);
  19206. };
  19207. // Public: setFileUploadLimit changes the file_upload_limit setting
  19208. SWFUpload.prototype.setFileUploadLimit = function (fileUploadLimit) {
  19209. this.settings.file_upload_limit = fileUploadLimit;
  19210. this.callFlash("SetFileUploadLimit", [fileUploadLimit]);
  19211. };
  19212. // Public: setFileQueueLimit changes the file_queue_limit setting
  19213. SWFUpload.prototype.setFileQueueLimit = function (fileQueueLimit) {
  19214. this.settings.file_queue_limit = fileQueueLimit;
  19215. this.callFlash("SetFileQueueLimit", [fileQueueLimit]);
  19216. };
  19217. // Public: setFilePostName changes the file_post_name setting
  19218. SWFUpload.prototype.setFilePostName = function (filePostName) {
  19219. this.settings.file_post_name = filePostName;
  19220. this.callFlash("SetFilePostName", [filePostName]);
  19221. };
  19222. // Public: setUseQueryString changes the use_query_string setting
  19223. SWFUpload.prototype.setUseQueryString = function (useQueryString) {
  19224. this.settings.use_query_string = useQueryString;
  19225. this.callFlash("SetUseQueryString", [useQueryString]);
  19226. };
  19227. // Public: setRequeueOnError changes the requeue_on_error setting
  19228. SWFUpload.prototype.setRequeueOnError = function (requeueOnError) {
  19229. this.settings.requeue_on_error = requeueOnError;
  19230. this.callFlash("SetRequeueOnError", [requeueOnError]);
  19231. };
  19232. // Public: setHTTPSuccess changes the http_success setting
  19233. SWFUpload.prototype.setHTTPSuccess = function (http_status_codes) {
  19234. if (typeof http_status_codes === "string") {
  19235. http_status_codes = http_status_codes.replace(" ", "").split(",");
  19236. }
  19237. this.settings.http_success = http_status_codes;
  19238. this.callFlash("SetHTTPSuccess", [http_status_codes]);
  19239. };
  19240. // Public: setHTTPSuccess changes the http_success setting
  19241. SWFUpload.prototype.setAssumeSuccessTimeout = function (timeout_seconds) {
  19242. this.settings.assume_success_timeout = timeout_seconds;
  19243. this.callFlash("SetAssumeSuccessTimeout", [timeout_seconds]);
  19244. };
  19245. // Public: setDebugEnabled changes the debug_enabled setting
  19246. SWFUpload.prototype.setDebugEnabled = function (debugEnabled) {
  19247. this.settings.debug_enabled = debugEnabled;
  19248. this.callFlash("SetDebugEnabled", [debugEnabled]);
  19249. };
  19250. // Public: setButtonImageURL loads a button image sprite
  19251. SWFUpload.prototype.setButtonImageURL = function (buttonImageURL) {
  19252. if (buttonImageURL == undefined) {
  19253. buttonImageURL = "";
  19254. }
  19255. this.settings.button_image_url = buttonImageURL;
  19256. this.callFlash("SetButtonImageURL", [buttonImageURL]);
  19257. };
  19258. // Public: setButtonDimensions resizes the Flash Movie and button
  19259. SWFUpload.prototype.setButtonDimensions = function (width, height) {
  19260. this.settings.button_width = width;
  19261. this.settings.button_height = height;
  19262. var movie = this.getMovieElement();
  19263. if (movie != undefined) {
  19264. movie.style.width = width + "px";
  19265. movie.style.height = height + "px";
  19266. }
  19267. this.callFlash("SetButtonDimensions", [width, height]);
  19268. };
  19269. // Public: setButtonText Changes the text overlaid on the button
  19270. SWFUpload.prototype.setButtonText = function (html) {
  19271. this.settings.button_text = html;
  19272. this.callFlash("SetButtonText", [html]);
  19273. };
  19274. // Public: setButtonTextPadding changes the top and left padding of the text overlay
  19275. SWFUpload.prototype.setButtonTextPadding = function (left, top) {
  19276. this.settings.button_text_top_padding = top;
  19277. this.settings.button_text_left_padding = left;
  19278. this.callFlash("SetButtonTextPadding", [left, top]);
  19279. };
  19280. // Public: setButtonTextStyle changes the CSS used to style the HTML/Text overlaid on the button
  19281. SWFUpload.prototype.setButtonTextStyle = function (css) {
  19282. this.settings.button_text_style = css;
  19283. this.callFlash("SetButtonTextStyle", [css]);
  19284. };
  19285. // Public: setButtonDisabled disables/enables the button
  19286. SWFUpload.prototype.setButtonDisabled = function (isDisabled) {
  19287. this.settings.button_disabled = isDisabled;
  19288. this.callFlash("SetButtonDisabled", [isDisabled]);
  19289. };
  19290. // Public: setButtonAction sets the action that occurs when the button is clicked
  19291. SWFUpload.prototype.setButtonAction = function (buttonAction) {
  19292. this.settings.button_action = buttonAction;
  19293. this.callFlash("SetButtonAction", [buttonAction]);
  19294. };
  19295. // Public: setButtonCursor changes the mouse cursor displayed when hovering over the button
  19296. SWFUpload.prototype.setButtonCursor = function (cursor) {
  19297. this.settings.button_cursor = cursor;
  19298. this.callFlash("SetButtonCursor", [cursor]);
  19299. };
  19300. /* *******************************
  19301. Flash Event Interfaces
  19302. These functions are used by Flash to trigger the various
  19303. events.
  19304. All these functions a Private.
  19305. Because the ExternalInterface library is buggy the event calls
  19306. are added to a queue and the queue then executed by a setTimeout.
  19307. This ensures that events are executed in a determinate order and that
  19308. the ExternalInterface bugs are avoided.
  19309. ******************************* */
  19310. SWFUpload.prototype.queueEvent = function (handlerName, argumentArray) {
  19311. // Warning: Don't call this.debug inside here or you'll create an infinite loop
  19312. if (argumentArray == undefined) {
  19313. argumentArray = [];
  19314. } else if (!(argumentArray instanceof Array)) {
  19315. argumentArray = [argumentArray];
  19316. }
  19317. var self = this;
  19318. if (typeof this.settings[handlerName] === "function") {
  19319. // Queue the event
  19320. this.eventQueue.push(function () {
  19321. this.settings[handlerName].apply(this, argumentArray);
  19322. });
  19323. // Execute the next queued event
  19324. setTimeout(function () {
  19325. self.executeNextEvent();
  19326. }, 0);
  19327. } else if (this.settings[handlerName] !== null) {
  19328. throw "Event handler " + handlerName + " is unknown or is not a function";
  19329. }
  19330. };
  19331. // Private: Causes the next event in the queue to be executed. Since events are queued using a setTimeout
  19332. // we must queue them in order to garentee that they are executed in order.
  19333. SWFUpload.prototype.executeNextEvent = function () {
  19334. // Warning: Don't call this.debug inside here or you'll create an infinite loop
  19335. var f = this.eventQueue ? this.eventQueue.shift() : null;
  19336. if (typeof(f) === "function") {
  19337. f.apply(this);
  19338. }
  19339. };
  19340. // Private: unescapeFileParams is part of a workaround for a flash bug where objects passed through ExternalInterface cannot have
  19341. // properties that contain characters that are not valid for JavaScript identifiers. To work around this
  19342. // the Flash Component escapes the parameter names and we must unescape again before passing them along.
  19343. SWFUpload.prototype.unescapeFilePostParams = function (file) {
  19344. var reg = /[$]([0-9a-f]{4})/i;
  19345. var unescapedPost = {};
  19346. var uk;
  19347. if (file != undefined) {
  19348. for (var k in file.post) {
  19349. if (file.post.hasOwnProperty(k)) {
  19350. uk = k;
  19351. var match;
  19352. while ((match = reg.exec(uk)) !== null) {
  19353. uk = uk.replace(match[0], String.fromCharCode(parseInt("0x" + match[1], 16)));
  19354. }
  19355. unescapedPost[uk] = file.post[k];
  19356. }
  19357. }
  19358. file.post = unescapedPost;
  19359. }
  19360. return file;
  19361. };
  19362. // Private: Called by Flash to see if JS can call in to Flash (test if External Interface is working)
  19363. SWFUpload.prototype.testExternalInterface = function () {
  19364. try {
  19365. return this.callFlash("TestExternalInterface");
  19366. } catch (ex) {
  19367. return false;
  19368. }
  19369. };
  19370. // Private: This event is called by Flash when it has finished loading. Don't modify this.
  19371. // Use the swfupload_loaded_handler event setting to execute custom code when SWFUpload has loaded.
  19372. SWFUpload.prototype.flashReady = function () {
  19373. // Check that the movie element is loaded correctly with its ExternalInterface methods defined
  19374. var movieElement = this.getMovieElement();
  19375. if (!movieElement) {
  19376. this.debug("Flash called back ready but the flash movie can't be found.");
  19377. return;
  19378. }
  19379. this.cleanUp(movieElement);
  19380. this.queueEvent("swfupload_loaded_handler");
  19381. };
  19382. // Private: removes Flash added fuctions to the DOM node to prevent memory leaks in IE.
  19383. // This function is called by Flash each time the ExternalInterface functions are created.
  19384. SWFUpload.prototype.cleanUp = function (movieElement) {
  19385. // Pro-actively unhook all the Flash functions
  19386. try {
  19387. if (this.movieElement && typeof(movieElement.CallFunction) === "unknown") { // We only want to do this in IE
  19388. this.debug("Removing Flash functions hooks (this should only run in IE and should prevent memory leaks)");
  19389. for (var key in movieElement) {
  19390. try {
  19391. if (typeof(movieElement[key]) === "function") {
  19392. movieElement[key] = null;
  19393. }
  19394. } catch (ex) {
  19395. }
  19396. }
  19397. }
  19398. } catch (ex1) {
  19399. }
  19400. // Fix Flashes own cleanup code so if the SWFMovie was removed from the page
  19401. // it doesn't display errors.
  19402. window["__flash__removeCallback"] = function (instance, name) {
  19403. try {
  19404. if (instance) {
  19405. instance[name] = null;
  19406. }
  19407. } catch (flashEx) {
  19408. }
  19409. };
  19410. };
  19411. /* This is a chance to do something before the browse window opens */
  19412. SWFUpload.prototype.fileDialogStart = function () {
  19413. this.queueEvent("file_dialog_start_handler");
  19414. };
  19415. /* Called when a file is successfully added to the queue. */
  19416. SWFUpload.prototype.fileQueued = function (file) {
  19417. file = this.unescapeFilePostParams(file);
  19418. this.queueEvent("file_queued_handler", file);
  19419. };
  19420. /* Handle errors that occur when an attempt to queue a file fails. */
  19421. SWFUpload.prototype.fileQueueError = function (file, errorCode, message) {
  19422. file = this.unescapeFilePostParams(file);
  19423. this.queueEvent("file_queue_error_handler", [file, errorCode, message]);
  19424. };
  19425. /* Called after the file dialog has closed and the selected files have been queued.
  19426. You could call startUpload here if you want the queued files to begin uploading immediately. */
  19427. SWFUpload.prototype.fileDialogComplete = function (numFilesSelected, numFilesQueued, numFilesInQueue) {
  19428. this.queueEvent("file_dialog_complete_handler", [numFilesSelected, numFilesQueued, numFilesInQueue]);
  19429. };
  19430. SWFUpload.prototype.uploadStart = function (file) {
  19431. file = this.unescapeFilePostParams(file);
  19432. this.queueEvent("return_upload_start_handler", file);
  19433. };
  19434. SWFUpload.prototype.returnUploadStart = function (file) {
  19435. var returnValue;
  19436. if (typeof this.settings.upload_start_handler === "function") {
  19437. file = this.unescapeFilePostParams(file);
  19438. returnValue = this.settings.upload_start_handler.call(this, file);
  19439. } else if (this.settings.upload_start_handler != undefined) {
  19440. throw "upload_start_handler must be a function";
  19441. }
  19442. // Convert undefined to true so if nothing is returned from the upload_start_handler it is
  19443. // interpretted as 'true'.
  19444. if (returnValue === undefined) {
  19445. returnValue = true;
  19446. }
  19447. returnValue = !!returnValue;
  19448. this.callFlash("ReturnUploadStart", [returnValue]);
  19449. };
  19450. SWFUpload.prototype.uploadProgress = function (file, bytesComplete, bytesTotal) {
  19451. file = this.unescapeFilePostParams(file);
  19452. this.queueEvent("upload_progress_handler", [file, bytesComplete, bytesTotal]);
  19453. };
  19454. SWFUpload.prototype.uploadError = function (file, errorCode, message) {
  19455. file = this.unescapeFilePostParams(file);
  19456. this.queueEvent("upload_error_handler", [file, errorCode, message]);
  19457. };
  19458. SWFUpload.prototype.uploadSuccess = function (file, serverData, responseReceived) {
  19459. file = this.unescapeFilePostParams(file);
  19460. this.queueEvent("upload_success_handler", [file, serverData, responseReceived]);
  19461. };
  19462. SWFUpload.prototype.uploadComplete = function (file) {
  19463. file = this.unescapeFilePostParams(file);
  19464. this.queueEvent("upload_complete_handler", file);
  19465. };
  19466. /* Called by SWFUpload JavaScript and Flash functions when debug is enabled. By default it writes messages to the
  19467. internal debug console. You can override this event and have messages written where you want. */
  19468. SWFUpload.prototype.debug = function (message) {
  19469. this.queueEvent("debug_handler", message);
  19470. };
  19471. /* **********************************
  19472. Debug Console
  19473. The debug console is a self contained, in page location
  19474. for debug message to be sent. The Debug Console adds
  19475. itself to the body if necessary.
  19476. The console is automatically scrolled as messages appear.
  19477. If you are using your own debug handler or when you deploy to production and
  19478. have debug disabled you can remove these functions to reduce the file size
  19479. and complexity.
  19480. ********************************** */
  19481. // Private: debugMessage is the default debug_handler. If you want to print debug messages
  19482. // call the debug() function. When overriding the function your own function should
  19483. // check to see if the debug setting is true before outputting debug information.
  19484. SWFUpload.prototype.debugMessage = function (message) {
  19485. if (this.settings.debug) {
  19486. var exceptionMessage, exceptionValues = [];
  19487. // Check for an exception object and print it nicely
  19488. if (typeof message === "object" && typeof message.name === "string" && typeof message.message === "string") {
  19489. for (var key in message) {
  19490. if (message.hasOwnProperty(key)) {
  19491. exceptionValues.push(key + ": " + message[key]);
  19492. }
  19493. }
  19494. exceptionMessage = exceptionValues.join("\n") || "";
  19495. exceptionValues = exceptionMessage.split("\n");
  19496. exceptionMessage = "EXCEPTION: " + exceptionValues.join("\nEXCEPTION: ");
  19497. SWFUpload.Console.writeLine(exceptionMessage);
  19498. } else {
  19499. SWFUpload.Console.writeLine(message);
  19500. }
  19501. }
  19502. };
  19503. SWFUpload.Console = {};
  19504. SWFUpload.Console.writeLine = function (message) {
  19505. var console, documentForm;
  19506. try {
  19507. console = document.getElementById("SWFUpload_Console");
  19508. if (!console) {
  19509. documentForm = document.createElement("form");
  19510. document.getElementsByTagName("body")[0].appendChild(documentForm);
  19511. console = document.createElement("textarea");
  19512. console.id = "SWFUpload_Console";
  19513. console.style.fontFamily = "monospace";
  19514. console.setAttribute("wrap", "off");
  19515. console.wrap = "off";
  19516. console.style.overflow = "auto";
  19517. console.style.width = "700px";
  19518. console.style.height = "350px";
  19519. console.style.margin = "5px";
  19520. documentForm.appendChild(console);
  19521. }
  19522. console.value += message + "\n";
  19523. console.scrollTop = console.scrollHeight - console.clientHeight;
  19524. } catch (ex) {
  19525. alert("Exception: " + ex.name + " Message: " + ex.message);
  19526. }
  19527. };
  19528. /*
  19529. Copyright 2008-2009 University of Toronto
  19530. Copyright 2008-2009 University of California, Berkeley
  19531. Copyright 2010-2011 OCAD University
  19532. Copyright 2011 Lucendo Development Ltd.
  19533. Licensed under the Educational Community License (ECL), Version 2.0 or the New
  19534. BSD license. You may not use this file except in compliance with one these
  19535. Licenses.
  19536. You may obtain a copy of the ECL 2.0 License and BSD License at
  19537. https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt
  19538. */
  19539. // Declare dependencies
  19540. /*global window, fluid_1_5:true, jQuery*/
  19541. // JSLint options
  19542. /*jslint white: true, funcinvoke: true, undef: true, newcap: true, nomen: true, regexp: true, bitwise: true, browser: true, forin: true, maxerr: 100, indent: 4 */
  19543. var fluid_1_5 = fluid_1_5 || {};
  19544. /************
  19545. * Uploader *
  19546. ************/
  19547. (function ($, fluid) {
  19548. var fileOrFiles = function (that, numFiles) {
  19549. return (numFiles === 1) ? that.options.strings.progress.singleFile :
  19550. that.options.strings.progress.pluralFiles;
  19551. };
  19552. var enableElement = function (that, elm) {
  19553. elm.prop("disabled", false);
  19554. elm.removeClass(that.options.styles.dim);
  19555. };
  19556. var disableElement = function (that, elm) {
  19557. elm.prop("disabled", true);
  19558. elm.addClass(that.options.styles.dim);
  19559. };
  19560. var showElement = function (that, elm) {
  19561. elm.removeClass(that.options.styles.hidden);
  19562. };
  19563. var hideElement = function (that, elm) {
  19564. elm.addClass(that.options.styles.hidden);
  19565. };
  19566. var maxFilesUploaded = function (that) {
  19567. var fileUploadLimit = that.queue.getUploadedFiles().length + that.queue.getReadyFiles().length + that.queue.getErroredFiles().length;
  19568. return (fileUploadLimit === that.options.queueSettings.fileUploadLimit);
  19569. };
  19570. var setTotalProgressStyle = function (that, didError) {
  19571. didError = didError || false;
  19572. var indicator = that.totalProgress.indicator;
  19573. indicator.toggleClass(that.options.styles.totalProgress, !didError);
  19574. indicator.toggleClass(that.options.styles.totalProgressError, didError);
  19575. };
  19576. var setStateEmpty = function (that) {
  19577. disableElement(that, that.locate("uploadButton"));
  19578. // If the queue is totally empty, treat it specially.
  19579. if (that.queue.files.length === 0) {
  19580. that.locate("browseButtonText").text(that.options.strings.buttons.browse);
  19581. that.locate("browseButton").removeClass(that.options.styles.browseButton);
  19582. showElement(that, that.locate("instructions"));
  19583. }
  19584. };
  19585. // Only enable the browse button if the fileUploadLimit
  19586. // has not been reached
  19587. var enableBrowseButton = function (that) {
  19588. if (!maxFilesUploaded(that)) {
  19589. enableElement(that, that.locate("browseButton"));
  19590. that.strategy.local.enableBrowseButton();
  19591. }
  19592. };
  19593. var setStateDone = function (that) {
  19594. disableElement(that, that.locate("uploadButton"));
  19595. hideElement(that, that.locate("pauseButton"));
  19596. showElement(that, that.locate("uploadButton"));
  19597. enableBrowseButton(that);
  19598. };
  19599. var setStateLoaded = function (that) {
  19600. that.locate("browseButtonText").text(that.options.strings.buttons.addMore);
  19601. that.locate("browseButton").addClass(that.options.styles.browseButton);
  19602. hideElement(that, that.locate("pauseButton"));
  19603. showElement(that, that.locate("uploadButton"));
  19604. enableElement(that, that.locate("uploadButton"));
  19605. hideElement(that, that.locate("instructions"));
  19606. that.totalProgress.hide();
  19607. enableBrowseButton(that);
  19608. };
  19609. var setStateUploading = function (that) {
  19610. that.totalProgress.hide(false, false);
  19611. setTotalProgressStyle(that);
  19612. hideElement(that, that.locate("uploadButton"));
  19613. disableElement(that, that.locate("browseButton"));
  19614. that.strategy.local.disableBrowseButton();
  19615. enableElement(that, that.locate("pauseButton"));
  19616. showElement(that, that.locate("pauseButton"));
  19617. that.locate(that.options.focusWithEvent.afterUploadStart).focus();
  19618. };
  19619. var setStateFull = function (that) {
  19620. that.locate("browseButtonText").text(that.options.strings.buttons.addMore);
  19621. that.locate("browseButton").addClass(that.options.styles.browseButton);
  19622. hideElement(that, that.locate("pauseButton"));
  19623. showElement(that, that.locate("uploadButton"));
  19624. enableElement(that, that.locate("uploadButton"));
  19625. disableElement(that, that.locate("browseButton"));
  19626. that.strategy.local.disableBrowseButton();
  19627. hideElement(that, that.locate("instructions"));
  19628. that.totalProgress.hide();
  19629. };
  19630. var renderUploadTotalMessage = function (that) {
  19631. // Render template for the total file status message.
  19632. var numReadyFiles = that.queue.getReadyFiles().length;
  19633. var bytesReadyFiles = that.queue.sizeOfReadyFiles();
  19634. var fileLabelStr = fileOrFiles(that, numReadyFiles);
  19635. var totalStateStr = fluid.stringTemplate(that.options.strings.progress.toUploadLabel, {
  19636. fileCount: numReadyFiles,
  19637. fileLabel: fileLabelStr,
  19638. totalBytes: fluid.uploader.formatFileSize(bytesReadyFiles)
  19639. });
  19640. that.locate("totalFileStatusText").html(totalStateStr);
  19641. };
  19642. var renderFileUploadLimit = function (that) {
  19643. if (that.options.queueSettings.fileUploadLimit > 0) {
  19644. var fileUploadLimitText = fluid.stringTemplate(that.options.strings.progress.fileUploadLimitLabel, {
  19645. fileUploadLimit: that.options.queueSettings.fileUploadLimit,
  19646. fileLabel: fileOrFiles(that, that.options.queueSettings.fileUploadLimit)
  19647. });
  19648. that.locate("fileUploadLimitText").html(fileUploadLimitText);
  19649. }
  19650. };
  19651. var updateTotalProgress = function (that) {
  19652. var batch = that.queue.currentBatch;
  19653. var totalPercent = fluid.uploader.derivePercent(batch.totalBytesUploaded, batch.totalBytes);
  19654. var numFilesInBatch = batch.files.length;
  19655. var fileLabelStr = fileOrFiles(that, numFilesInBatch);
  19656. var totalProgressStr = fluid.stringTemplate(that.options.strings.progress.totalProgressLabel, {
  19657. curFileN: batch.fileIdx,
  19658. totalFilesN: numFilesInBatch,
  19659. fileLabel: fileLabelStr,
  19660. currBytes: fluid.uploader.formatFileSize(batch.totalBytesUploaded),
  19661. totalBytes: fluid.uploader.formatFileSize(batch.totalBytes)
  19662. });
  19663. that.totalProgress.update(totalPercent, totalProgressStr);
  19664. };
  19665. var updateTotalAtCompletion = function (that) {
  19666. var numErroredFiles = that.queue.getErroredFiles().length;
  19667. var numTotalFiles = that.queue.files.length;
  19668. var fileLabelStr = fileOrFiles(that, numTotalFiles);
  19669. var errorStr = "";
  19670. // if there are errors then change the total progress bar
  19671. // and set up the errorStr so that we can use it in the totalProgressStr
  19672. if (numErroredFiles > 0) {
  19673. var errorLabelString = (numErroredFiles === 1) ? that.options.strings.progress.singleError :
  19674. that.options.strings.progress.pluralErrors;
  19675. setTotalProgressStyle(that, true);
  19676. errorStr = fluid.stringTemplate(that.options.strings.progress.numberOfErrors, {
  19677. errorsN: numErroredFiles,
  19678. errorLabel: errorLabelString
  19679. });
  19680. }
  19681. var totalProgressStr = fluid.stringTemplate(that.options.strings.progress.completedLabel, {
  19682. curFileN: that.queue.getUploadedFiles().length,
  19683. totalFilesN: numTotalFiles,
  19684. errorString: errorStr,
  19685. fileLabel: fileLabelStr,
  19686. totalCurrBytes: fluid.uploader.formatFileSize(that.queue.sizeOfUploadedFiles())
  19687. });
  19688. that.totalProgress.update(100, totalProgressStr);
  19689. };
  19690. /*
  19691. * Summarizes the status of all the files in the file queue.
  19692. */
  19693. var updateQueueSummaryText = function (that) {
  19694. var fileQueueTable = that.locate("fileQueue");
  19695. if (that.queue.files.length === 0) {
  19696. fileQueueTable.attr("summary", that.options.strings.queue.emptyQueue);
  19697. } else {
  19698. var queueSummary = fluid.stringTemplate(that.options.strings.queue.queueSummary, {
  19699. totalUploaded: that.queue.getUploadedFiles().length,
  19700. totalInUploadQueue: that.queue.files.length - that.queue.getUploadedFiles().length
  19701. });
  19702. fileQueueTable.attr("summary", queueSummary);
  19703. }
  19704. };
  19705. var bindDOMEvents = function (that) {
  19706. that.locate("uploadButton").click(function () {
  19707. that.start();
  19708. });
  19709. that.locate("pauseButton").click(function () {
  19710. that.stop();
  19711. });
  19712. };
  19713. var updateStateAfterFileDialog = function (that) {
  19714. var queueLength = that.queue.getReadyFiles().length;
  19715. if (queueLength > 0) {
  19716. if (queueLength === that.options.queueSettings.fileUploadLimit) {
  19717. setStateFull(that);
  19718. } else {
  19719. setStateLoaded(that);
  19720. }
  19721. renderUploadTotalMessage(that);
  19722. that.locate(that.options.focusWithEvent.afterFileDialog).focus();
  19723. updateQueueSummaryText(that);
  19724. }
  19725. };
  19726. var updateStateAfterFileRemoval = function (that) {
  19727. if (that.queue.getReadyFiles().length === 0) {
  19728. setStateEmpty(that);
  19729. } else {
  19730. setStateLoaded(that);
  19731. }
  19732. renderUploadTotalMessage(that);
  19733. updateQueueSummaryText(that);
  19734. };
  19735. var updateStateAfterCompletion = function (that) {
  19736. if (that.queue.getReadyFiles().length === 0) {
  19737. setStateDone(that);
  19738. } else {
  19739. setStateLoaded(that);
  19740. }
  19741. updateTotalAtCompletion(that);
  19742. updateQueueSummaryText(that);
  19743. };
  19744. var uploadNextOrFinish = function (that) {
  19745. if (that.queue.shouldUploadNextFile()) {
  19746. that.strategy.remote.uploadNextFile();
  19747. } else {
  19748. that.events.afterUploadComplete.fire(that.queue.currentBatch.files);
  19749. that.queue.clearCurrentBatch();
  19750. }
  19751. };
  19752. var bindEvents = function (that) {
  19753. that.events.afterFileDialog.addListener(function () {
  19754. updateStateAfterFileDialog(that);
  19755. });
  19756. that.events.afterFileQueued.addListener(function (file) {
  19757. that.queue.addFile(file);
  19758. });
  19759. that.events.onFileRemoved.addListener(function (file) {
  19760. that.removeFile(file);
  19761. });
  19762. that.events.afterFileRemoved.addListener(function () {
  19763. updateStateAfterFileRemoval(that);
  19764. });
  19765. that.events.onUploadStart.addListener(function () {
  19766. setStateUploading(that);
  19767. });
  19768. that.events.onUploadStop.addListener(function () {
  19769. that.locate(that.options.focusWithEvent.onUploadStop).focus();
  19770. });
  19771. that.events.onFileStart.addListener(function (file) {
  19772. file.filestatus = fluid.uploader.fileStatusConstants.IN_PROGRESS;
  19773. that.queue.startFile();
  19774. });
  19775. that.events.onFileProgress.addListener(function (file, currentBytes, totalBytes) {
  19776. that.queue.updateBatchStatus(currentBytes);
  19777. updateTotalProgress(that);
  19778. });
  19779. that.events.onFileComplete.addListener(function (file) {
  19780. that.queue.finishFile(file);
  19781. that.events.afterFileComplete.fire(file);
  19782. uploadNextOrFinish(that);
  19783. });
  19784. that.events.onFileSuccess.addListener(function (file) {
  19785. file.filestatus = fluid.uploader.fileStatusConstants.COMPLETE;
  19786. if (that.queue.currentBatch.bytesUploadedForFile === 0) {
  19787. that.queue.currentBatch.totalBytesUploaded += file.size;
  19788. }
  19789. updateTotalProgress(that);
  19790. });
  19791. that.events.onFileError.addListener(function (file, error) {
  19792. if (error === fluid.uploader.errorConstants.UPLOAD_STOPPED) {
  19793. file.filestatus = fluid.uploader.fileStatusConstants.CANCELLED;
  19794. return;
  19795. } else {
  19796. // TODO: Avoid reaching directly into the filequeue and manipulating its state from here
  19797. file.filestatus = fluid.uploader.fileStatusConstants.ERROR;
  19798. if (that.queue.isUploading) {
  19799. that.queue.currentBatch.totalBytesUploaded += file.size;
  19800. that.queue.currentBatch.numFilesErrored++;
  19801. uploadNextOrFinish(that);
  19802. }
  19803. }
  19804. });
  19805. that.events.afterUploadComplete.addListener(function () {
  19806. that.queue.isUploading = false;
  19807. updateStateAfterCompletion(that);
  19808. });
  19809. };
  19810. var setupUploader = function (that) {
  19811. that.demo = fluid.typeTag(that.options.demo ? "fluid.uploader.demo" : "fluid.uploader.live");
  19812. fluid.initDependents(that);
  19813. // Upload button should not be enabled until there are files to upload
  19814. disableElement(that, that.locate("uploadButton"));
  19815. bindDOMEvents(that);
  19816. bindEvents(that);
  19817. updateQueueSummaryText(that);
  19818. that.statusUpdater();
  19819. renderFileUploadLimit(that);
  19820. // Uploader uses application-style keyboard conventions, so give it a suitable role.
  19821. that.container.attr("role", "application");
  19822. };
  19823. /**
  19824. * Instantiates a new Uploader component.
  19825. *
  19826. * @param {Object} container the DOM element in which the Uploader lives
  19827. * @param {Object} options configuration options for the component.
  19828. */
  19829. fluid.uploader = function (container, uploaderOptions) {
  19830. // Do not try to expand uploaderOptions here or else our subcomponents will end up
  19831. // nested inside uploaderImpl
  19832. var that = fluid.initView("fluid.uploader", container);
  19833. // Unsupported, non-API function fluid.uploader.transformOptions
  19834. if (fluid.uploader.transformOptions) {
  19835. uploaderOptions = fluid.uploader.transformOptions(uploaderOptions);
  19836. }
  19837. that.uploaderOptions = uploaderOptions;
  19838. fluid.initDependents(that);
  19839. return that.uploaderImpl;
  19840. };
  19841. fluid.uploaderImpl = function () {
  19842. fluid.fail("Error creating uploader component - please make sure that a " +
  19843. "progressiveCheckerForComponent for \"fluid.uploader\" is registered either in the " +
  19844. "static environment or else is visible in the current component tree");
  19845. };
  19846. fluid.defaults("fluid.uploader", {
  19847. gradeNames: ["fluid.viewComponent"],
  19848. components: {
  19849. uploaderContext: {
  19850. type: "fluid.progressiveCheckerForComponent",
  19851. options: {componentName: "fluid.uploader"}
  19852. },
  19853. uploaderImpl: {
  19854. type: "fluid.uploaderImpl",
  19855. container: "{uploader}.container",
  19856. options: "{uploader}.uploaderOptions"
  19857. }
  19858. },
  19859. progressiveCheckerOptions: {
  19860. checks: [
  19861. {
  19862. feature: "{fluid.browser.supportsBinaryXHR}",
  19863. contextName: "fluid.uploader.html5"
  19864. },
  19865. {
  19866. feature: "{fluid.browser.supportsFlash}",
  19867. contextName: "fluid.uploader.swfUpload"
  19868. }
  19869. ],
  19870. defaultContextName: "fluid.uploader.singleFile"
  19871. }
  19872. });
  19873. // Ensure that for all uploaders created via IoC, we bypass the wrapper and directly create the concrete uploader
  19874. fluid.alias("fluid.uploader", "fluid.uploaderImpl");
  19875. // This method has been deprecated as of Infusion 1.3. Use fluid.uploader() instead,
  19876. // which now includes built-in support for progressive enhancement.
  19877. fluid.progressiveEnhanceableUploader = function (container, enhanceable, options) {
  19878. return fluid.uploader(container, options);
  19879. };
  19880. /**
  19881. * Multiple file Uploader implementation. Use fluid.uploader() for IoC-resolved, progressively
  19882. * enhanceable Uploader, or call this directly if you don't want support for old-style single uploads
  19883. *
  19884. * @param {jQueryable} container the component's container
  19885. * @param {Object} options configuration options
  19886. */
  19887. fluid.uploader.multiFileUploader = function (container, options) {
  19888. var that = fluid.initView("fluid.uploader.multiFileUploader", container, options);
  19889. that.queue = fluid.uploader.fileQueue();
  19890. /**
  19891. * Opens the native OS browse file dialog.
  19892. */
  19893. that.browse = function () {
  19894. if (!that.queue.isUploading) {
  19895. that.strategy.local.browse();
  19896. }
  19897. };
  19898. /**
  19899. * Removes the specified file from the upload queue.
  19900. *
  19901. * @param {File} file the file to remove
  19902. */
  19903. that.removeFile = function (file) {
  19904. that.queue.removeFile(file);
  19905. that.strategy.local.removeFile(file);
  19906. that.events.afterFileRemoved.fire(file);
  19907. };
  19908. /**
  19909. * Starts uploading all queued files to the server.
  19910. */
  19911. that.start = function () {
  19912. that.queue.start();
  19913. that.events.onUploadStart.fire(that.queue.currentBatch.files);
  19914. that.strategy.remote.uploadNextFile();
  19915. };
  19916. /**
  19917. * Cancels an in-progress upload.
  19918. */
  19919. that.stop = function () {
  19920. that.events.onUploadStop.fire();
  19921. that.strategy.remote.stop();
  19922. };
  19923. setupUploader(that);
  19924. return that;
  19925. };
  19926. fluid.defaults("fluid.uploader.multiFileUploader", {
  19927. gradeNames: "fluid.viewComponent",
  19928. components: {
  19929. strategy: {
  19930. type: "fluid.uploader.progressiveStrategy"
  19931. },
  19932. errorPanel: {
  19933. type: "fluid.uploader.errorPanel"
  19934. },
  19935. fileQueueView: {
  19936. type: "fluid.uploader.fileQueueView",
  19937. options: {
  19938. model: "{multiFileUploader}.queue.files",
  19939. uploaderContainer: "{multiFileUploader}.container"
  19940. }
  19941. },
  19942. totalProgress: {
  19943. type: "fluid.uploader.totalProgressBar",
  19944. options: {
  19945. selectors: {
  19946. progressBar: ".flc-uploader-queue-footer",
  19947. displayElement: ".flc-uploader-total-progress",
  19948. label: ".flc-uploader-total-progress-text",
  19949. indicator: ".flc-uploader-total-progress",
  19950. ariaElement: ".flc-uploader-total-progress"
  19951. }
  19952. }
  19953. }
  19954. },
  19955. invokers: {
  19956. statusUpdater: "fluid.uploader.ariaLiveRegionUpdater"
  19957. },
  19958. queueSettings: {
  19959. uploadURL: "",
  19960. postParams: {},
  19961. fileSizeLimit: "20480",
  19962. fileTypes: null,
  19963. fileTypesDescription: null,
  19964. fileUploadLimit: 0,
  19965. fileQueueLimit: 0
  19966. },
  19967. demo: false,
  19968. selectors: {
  19969. fileQueue: ".flc-uploader-queue",
  19970. browseButton: ".flc-uploader-button-browse",
  19971. browseButtonText: ".flc-uploader-button-browse-text",
  19972. uploadButton: ".flc-uploader-button-upload",
  19973. pauseButton: ".flc-uploader-button-pause",
  19974. totalFileStatusText: ".flc-uploader-total-progress-text",
  19975. fileUploadLimitText: ".flc-uploader-upload-limit-text",
  19976. instructions: ".flc-uploader-browse-instructions",
  19977. statusRegion: ".flc-uploader-status-region",
  19978. errorsPanel: ".flc-uploader-errorsPanel"
  19979. },
  19980. // Specifies a selector name to move keyboard focus to when a particular event fires.
  19981. // Event listeners must already be implemented to use these options.
  19982. focusWithEvent: {
  19983. afterFileDialog: "uploadButton",
  19984. afterUploadStart: "pauseButton",
  19985. onUploadStop: "uploadButton"
  19986. },
  19987. styles: {
  19988. disabled: "fl-uploader-disabled",
  19989. hidden: "fl-uploader-hidden",
  19990. dim: "fl-uploader-dim",
  19991. totalProgress: "fl-uploader-total-progress-okay",
  19992. totalProgressError: "fl-uploader-total-progress-errored",
  19993. browseButton: "fl-uploader-browseMore"
  19994. },
  19995. events: {
  19996. afterReady: null,
  19997. onFileDialog: null,
  19998. onFilesSelected: null,
  19999. onFileQueued: null,
  20000. afterFileQueued: null,
  20001. onFileRemoved: null,
  20002. afterFileRemoved: null,
  20003. afterFileDialog: null,
  20004. onUploadStart: null,
  20005. onUploadStop: null,
  20006. onFileStart: null,
  20007. onFileProgress: null,
  20008. onFileError: null,
  20009. onQueueError: null,
  20010. onFileSuccess: null,
  20011. onFileComplete: null,
  20012. afterFileComplete: null,
  20013. afterUploadComplete: null
  20014. },
  20015. strings: {
  20016. progress: {
  20017. fileUploadLimitLabel: "%fileUploadLimit %fileLabel maximum",
  20018. toUploadLabel: "To upload: %fileCount %fileLabel (%totalBytes)",
  20019. totalProgressLabel: "Uploading: %curFileN of %totalFilesN %fileLabel (%currBytes of %totalBytes)",
  20020. completedLabel: "Uploaded: %curFileN of %totalFilesN %fileLabel (%totalCurrBytes)%errorString",
  20021. numberOfErrors: ", %errorsN %errorLabel",
  20022. singleFile: "file",
  20023. pluralFiles: "files",
  20024. singleError: "error",
  20025. pluralErrors: "errors"
  20026. },
  20027. buttons: {
  20028. browse: "Browse Files",
  20029. addMore: "Add More",
  20030. stopUpload: "Stop Upload",
  20031. cancelRemaning: "Cancel remaining Uploads",
  20032. resumeUpload: "Resume Upload"
  20033. },
  20034. queue: {
  20035. emptyQueue: "File list: No files waiting to be uploaded.",
  20036. queueSummary: "File list: %totalUploaded files uploaded, %totalInUploadQueue file waiting to be uploaded."
  20037. }
  20038. },
  20039. mergePolicy: {
  20040. "fileQueueView.options.model": "preserve"
  20041. }
  20042. });
  20043. fluid.demands("fluid.uploader.totalProgressBar", "fluid.uploader.multiFileUploader", {
  20044. funcName: "fluid.progress",
  20045. container: "{multiFileUploader}.container"
  20046. });
  20047. /** Demands blocks for binding to fileQueueView **/
  20048. fluid.demands("fluid.uploader.fileQueueView", "fluid.uploader.multiFileUploader", {
  20049. container: "{multiFileUploader}.dom.fileQueue",
  20050. options: {
  20051. events: {
  20052. onFileRemoved: "{multiFileUploader}.events.onFileRemoved"
  20053. }
  20054. }
  20055. });
  20056. fluid.demands("fluid.uploader.fileQueueView.eventBinder", [
  20057. "fluid.uploader.multiFileUploader",
  20058. "fluid.uploader.fileQueueView"
  20059. ], {
  20060. options: {
  20061. listeners: {
  20062. "{multiFileUploader}.events.afterFileQueued": "{fileQueueView}.addFile",
  20063. "{multiFileUploader}.events.onUploadStart": "{fileQueueView}.prepareForUpload",
  20064. "{multiFileUploader}.events.onFileStart": "{fileQueueView}.showFileProgress",
  20065. "{multiFileUploader}.events.onFileProgress": "{fileQueueView}.updateFileProgress",
  20066. "{multiFileUploader}.events.onFileSuccess": "{fileQueueView}.markFileComplete",
  20067. "{multiFileUploader}.events.onFileError": "{fileQueueView}.showErrorForFile",
  20068. "{multiFileUploader}.events.afterFileComplete": "{fileQueueView}.hideFileProgress",
  20069. "{multiFileUploader}.events.afterUploadComplete": "{fileQueueView}.refreshAfterUpload"
  20070. }
  20071. }
  20072. });
  20073. /**
  20074. * Pretty prints a file's size, converting from bytes to kilobytes or megabytes.
  20075. *
  20076. * @param {Number} bytes the files size, specified as in number bytes.
  20077. */
  20078. fluid.uploader.formatFileSize = function (bytes) {
  20079. if (typeof (bytes) === "number") {
  20080. if (bytes === 0) {
  20081. return "0.0 KB";
  20082. } else if (bytes > 0) {
  20083. if (bytes < 1048576) {
  20084. return (Math.ceil(bytes / 1024 * 10) / 10).toFixed(1) + " KB";
  20085. } else {
  20086. return (Math.ceil(bytes / 1048576 * 10) / 10).toFixed(1) + " MB";
  20087. }
  20088. }
  20089. }
  20090. return "";
  20091. };
  20092. fluid.uploader.derivePercent = function (num, total) {
  20093. return Math.round((num * 100) / total);
  20094. };
  20095. // TODO: Refactor this to be a general ARIA utility
  20096. fluid.uploader.ariaLiveRegionUpdater = function (statusRegion, totalFileStatusText, events) {
  20097. statusRegion.attr("role", "log");
  20098. statusRegion.attr("aria-live", "assertive");
  20099. statusRegion.attr("aria-relevant", "text");
  20100. statusRegion.attr("aria-atomic", "true");
  20101. var regionUpdater = function () {
  20102. statusRegion.text(totalFileStatusText.text());
  20103. };
  20104. events.afterFileDialog.addListener(regionUpdater);
  20105. events.afterFileRemoved.addListener(regionUpdater);
  20106. events.afterUploadComplete.addListener(regionUpdater);
  20107. };
  20108. fluid.demands("fluid.uploader.ariaLiveRegionUpdater", "fluid.uploader.multiFileUploader", {
  20109. funcName: "fluid.uploader.ariaLiveRegionUpdater",
  20110. args: [
  20111. "{multiFileUploader}.dom.statusRegion",
  20112. "{multiFileUploader}.dom.totalFileStatusText",
  20113. "{multiFileUploader}.events"
  20114. ]
  20115. });
  20116. /**************************************************
  20117. * Error constants for the Uploader *
  20118. * TODO: These are SWFUpload-specific error codes *
  20119. **************************************************/
  20120. // TODO: Change these opaque numerical constants into strings which are easy to interpret
  20121. fluid.uploader.queueErrorConstants = {
  20122. QUEUE_LIMIT_EXCEEDED: -100,
  20123. FILE_EXCEEDS_SIZE_LIMIT: -110,
  20124. ZERO_BYTE_FILE: -120,
  20125. INVALID_FILETYPE: -130
  20126. };
  20127. fluid.uploader.errorConstants = {
  20128. HTTP_ERROR: -200,
  20129. MISSING_UPLOAD_URL: -210,
  20130. IO_ERROR: -220,
  20131. SECURITY_ERROR: -230,
  20132. UPLOAD_LIMIT_EXCEEDED: -240,
  20133. UPLOAD_FAILED: -250,
  20134. SPECIFIED_FILE_ID_NOT_FOUND: -260,
  20135. FILE_VALIDATION_FAILED: -270,
  20136. FILE_CANCELLED: -280,
  20137. UPLOAD_STOPPED: -290
  20138. };
  20139. fluid.uploader.fileStatusConstants = {
  20140. QUEUED: -1,
  20141. IN_PROGRESS: -2,
  20142. ERROR: -3,
  20143. COMPLETE: -4,
  20144. CANCELLED: -5
  20145. };
  20146. var toggleVisibility = function (toShow, toHide) {
  20147. // For FLUID-2789: hide() doesn't work in Opera
  20148. if (window.opera) {
  20149. toShow.show().removeClass("hideUploaderForOpera");
  20150. toHide.show().addClass("hideUploaderForOpera");
  20151. } else {
  20152. toShow.show();
  20153. toHide.hide();
  20154. }
  20155. };
  20156. /**
  20157. * Single file Uploader implementation. Use fluid.uploader() for IoC-resolved, progressively
  20158. * enhanceable Uploader, or call this directly if you only want a standard single file uploader.
  20159. * But why would you want that?
  20160. *
  20161. * @param {jQueryable} container the component's container
  20162. * @param {Object} options configuration options
  20163. */
  20164. fluid.uploader.singleFileUploader = function (container, options) {
  20165. var that = fluid.initView("fluid.uploader.singleFileUploader", container, options);
  20166. // TODO: direct DOM fascism that will fail with multiple uploaders on a single page.
  20167. toggleVisibility($(that.options.selectors.basicUpload), that.container);
  20168. return that;
  20169. };
  20170. fluid.defaults("fluid.uploader.singleFileUploader", {
  20171. gradeNames: "fluid.viewComponent",
  20172. selectors: {
  20173. basicUpload: ".fl-progEnhance-basic"
  20174. }
  20175. });
  20176. fluid.demands("fluid.uploaderImpl", "fluid.uploader.singleFile", {
  20177. funcName: "fluid.uploader.singleFileUploader"
  20178. });
  20179. })(jQuery, fluid_1_5);
  20180. /*
  20181. Copyright 2008-2009 University of Toronto
  20182. Copyright 2008-2009 University of California, Berkeley
  20183. Copyright 2010-2011 OCAD University
  20184. Licensed under the Educational Community License (ECL), Version 2.0 or the New
  20185. BSD license. You may not use this file except in compliance with one these
  20186. Licenses.
  20187. You may obtain a copy of the ECL 2.0 License and BSD License at
  20188. https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt
  20189. */
  20190. // Declare dependencies
  20191. /*global fluid_1_5:true, jQuery, SWFUpload*/
  20192. // JSLint options
  20193. /*jslint white: true, funcinvoke: true, undef: true, newcap: true, nomen: true, regexp: true, bitwise: true, browser: true, forin: true, maxerr: 100, indent: 4 */
  20194. var fluid_1_5 = fluid_1_5 || {};
  20195. (function ($, fluid) {
  20196. fluid.uploader = fluid.uploader || {};
  20197. var filterFiles = function (files, filterFn) {
  20198. var filteredFiles = [];
  20199. for (var i = 0; i < files.length; i++) {
  20200. var file = files[i];
  20201. if (filterFn(file) === true) {
  20202. filteredFiles.push(file);
  20203. }
  20204. }
  20205. return filteredFiles;
  20206. };
  20207. fluid.uploader.fileQueue = function () {
  20208. var that = {};
  20209. that.files = [];
  20210. that.isUploading = false;
  20211. /********************
  20212. * Queue Operations *
  20213. ********************/
  20214. that.start = function () {
  20215. that.setupCurrentBatch();
  20216. that.isUploading = true;
  20217. that.shouldStop = false;
  20218. };
  20219. that.startFile = function () {
  20220. that.currentBatch.fileIdx++;
  20221. that.currentBatch.bytesUploadedForFile = 0;
  20222. that.currentBatch.previousBytesUploadedForFile = 0;
  20223. };
  20224. that.finishFile = function (file) {
  20225. that.currentBatch.numFilesCompleted++;
  20226. };
  20227. that.shouldUploadNextFile = function () {
  20228. return !that.shouldStop &&
  20229. that.isUploading &&
  20230. (that.currentBatch.numFilesCompleted + that.currentBatch.numFilesErrored)
  20231. < that.currentBatch.files.length;
  20232. };
  20233. /*****************************
  20234. * File manipulation methods *
  20235. *****************************/
  20236. that.addFile = function (file) {
  20237. that.files.push(file);
  20238. };
  20239. that.removeFile = function (file) {
  20240. var idx = $.inArray(file, that.files);
  20241. that.files.splice(idx, 1);
  20242. };
  20243. /**********************
  20244. * Queue Info Methods *
  20245. **********************/
  20246. that.totalBytes = function () {
  20247. return fluid.uploader.fileQueue.sizeOfFiles(that.files);
  20248. };
  20249. that.getReadyFiles = function () {
  20250. return filterFiles(that.files, function (file) {
  20251. return (file.filestatus === fluid.uploader.fileStatusConstants.QUEUED || file.filestatus === fluid.uploader.fileStatusConstants.CANCELLED);
  20252. });
  20253. };
  20254. that.getErroredFiles = function () {
  20255. return filterFiles(that.files, function (file) {
  20256. return (file.filestatus === fluid.uploader.fileStatusConstants.ERROR);
  20257. });
  20258. };
  20259. that.sizeOfReadyFiles = function () {
  20260. return fluid.uploader.fileQueue.sizeOfFiles(that.getReadyFiles());
  20261. };
  20262. that.getUploadedFiles = function () {
  20263. return filterFiles(that.files, function (file) {
  20264. return (file.filestatus === fluid.uploader.fileStatusConstants.COMPLETE);
  20265. });
  20266. };
  20267. that.sizeOfUploadedFiles = function () {
  20268. return fluid.uploader.fileQueue.sizeOfFiles(that.getUploadedFiles());
  20269. };
  20270. /*****************
  20271. * Batch Methods *
  20272. *****************/
  20273. that.setupCurrentBatch = function () {
  20274. that.clearCurrentBatch();
  20275. that.updateCurrentBatch();
  20276. };
  20277. that.clearCurrentBatch = function () {
  20278. that.currentBatch = {
  20279. fileIdx: 0,
  20280. files: [],
  20281. totalBytes: 0,
  20282. numFilesCompleted: 0,
  20283. numFilesErrored: 0,
  20284. bytesUploadedForFile: 0,
  20285. previousBytesUploadedForFile: 0,
  20286. totalBytesUploaded: 0
  20287. };
  20288. };
  20289. that.updateCurrentBatch = function () {
  20290. var readyFiles = that.getReadyFiles();
  20291. that.currentBatch.files = readyFiles;
  20292. that.currentBatch.totalBytes = fluid.uploader.fileQueue.sizeOfFiles(readyFiles);
  20293. };
  20294. that.updateBatchStatus = function (currentBytes) {
  20295. var byteIncrement = currentBytes - that.currentBatch.previousBytesUploadedForFile;
  20296. that.currentBatch.totalBytesUploaded += byteIncrement;
  20297. that.currentBatch.bytesUploadedForFile += byteIncrement;
  20298. that.currentBatch.previousBytesUploadedForFile = currentBytes;
  20299. };
  20300. return that;
  20301. };
  20302. fluid.uploader.fileQueue.sizeOfFiles = function (files) {
  20303. var totalBytes = 0;
  20304. for (var i = 0; i < files.length; i++) {
  20305. var file = files[i];
  20306. totalBytes += file.size;
  20307. }
  20308. return totalBytes;
  20309. };
  20310. })(jQuery, fluid_1_5);
  20311. /*
  20312. Copyright 2008-2009 University of Toronto
  20313. Copyright 2008-2009 University of California, Berkeley
  20314. Copyright 2008-2009 University of Cambridge
  20315. Copyright 2010-2011 OCAD University
  20316. Copyright 2011 Lucendo Development Ltd.
  20317. Licensed under the Educational Community License (ECL), Version 2.0 or the New
  20318. BSD license. You may not use this file except in compliance with one these
  20319. Licenses.
  20320. You may obtain a copy of the ECL 2.0 License and BSD License at
  20321. https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt
  20322. */
  20323. // Declare dependencies
  20324. /*global fluid_1_5:true, jQuery*/
  20325. // JSLint options
  20326. /*jslint white: true, funcinvoke: true, undef: true, newcap: true, nomen: true, regexp: true, bitwise: true, browser: true, forin: true, maxerr: 100, indent: 4 */
  20327. var fluid_1_5 = fluid_1_5 || {};
  20328. /*******************
  20329. * File Queue View *
  20330. *******************/
  20331. (function ($, fluid) {
  20332. // Real data binding would be nice to replace these two pairs.
  20333. var rowForFile = function (that, file) {
  20334. return that.locate("fileQueue").find("#" + file.id);
  20335. };
  20336. var errorRowForFile = function (that, file) {
  20337. return $("#" + file.id + "_error", that.container);
  20338. };
  20339. var fileForRow = function (that, row) {
  20340. var files = that.model;
  20341. var i;
  20342. for (i = 0; i < files.length; i++) {
  20343. var file = files[i];
  20344. if (file.id.toString() === row.prop("id")) {
  20345. return file;
  20346. }
  20347. }
  20348. return null;
  20349. };
  20350. var progressorForFile = function (that, file) {
  20351. var progressId = file.id + "_progress";
  20352. return that.fileProgressors[progressId];
  20353. };
  20354. var startFileProgress = function (that, file) {
  20355. var fileRowElm = rowForFile(that, file);
  20356. that.scroller.scrollTo(fileRowElm);
  20357. // update the progressor and make sure that it's in position
  20358. var fileProgressor = progressorForFile(that, file);
  20359. fileProgressor.refreshView();
  20360. fileProgressor.show();
  20361. };
  20362. var updateFileProgress = function (that, file, fileBytesComplete, fileTotalBytes) {
  20363. var filePercent = fluid.uploader.derivePercent(fileBytesComplete, fileTotalBytes);
  20364. var filePercentStr = filePercent + "%";
  20365. progressorForFile(that, file).update(filePercent, filePercentStr);
  20366. };
  20367. var hideFileProgress = function (that, file) {
  20368. var fileRowElm = rowForFile(that, file);
  20369. progressorForFile(that, file).hide();
  20370. if (file.filestatus === fluid.uploader.fileStatusConstants.COMPLETE) {
  20371. that.locate("fileIconBtn", fileRowElm).removeClass(that.options.styles.dim);
  20372. }
  20373. };
  20374. var removeFileProgress = function (that, file) {
  20375. var fileProgressor = progressorForFile(that, file);
  20376. if (!fileProgressor) {
  20377. return;
  20378. }
  20379. var rowProgressor = fileProgressor.displayElement;
  20380. rowProgressor.remove();
  20381. };
  20382. var animateRowRemoval = function (that, row) {
  20383. row.fadeOut("fast", function () {
  20384. row.remove();
  20385. that.refreshView();
  20386. });
  20387. };
  20388. var removeFileErrorRow = function (that, file) {
  20389. if (file.filestatus === fluid.uploader.fileStatusConstants.ERROR) {
  20390. animateRowRemoval(that, errorRowForFile(that, file));
  20391. }
  20392. };
  20393. var removeFileAndRow = function (that, file, row) {
  20394. // Clean up the stuff associated with a file row.
  20395. removeFileProgress(that, file);
  20396. removeFileErrorRow(that, file);
  20397. // Remove the file itself.
  20398. that.events.onFileRemoved.fire(file);
  20399. animateRowRemoval(that, row);
  20400. };
  20401. var removeFileForRow = function (that, row) {
  20402. var file = fileForRow(that, row);
  20403. if (!file || file.filestatus === fluid.uploader.fileStatusConstants.COMPLETE) {
  20404. return;
  20405. }
  20406. removeFileAndRow(that, file, row);
  20407. };
  20408. var removeRowForFile = function (that, file) {
  20409. var row = rowForFile(that, file);
  20410. removeFileAndRow(that, file, row);
  20411. };
  20412. var bindHover = function (row, styles) {
  20413. var over = function () {
  20414. if (row.hasClass(styles.ready) && !row.hasClass(styles.uploading)) {
  20415. row.addClass(styles.hover);
  20416. }
  20417. };
  20418. var out = function () {
  20419. if (row.hasClass(styles.ready) && !row.hasClass(styles.uploading)) {
  20420. row.removeClass(styles.hover);
  20421. }
  20422. };
  20423. row.hover(over, out);
  20424. };
  20425. var bindDeleteKey = function (that, row) {
  20426. var deleteHandler = function () {
  20427. removeFileForRow(that, row);
  20428. };
  20429. fluid.activatable(row, null, {
  20430. additionalBindings: [{
  20431. key: $.ui.keyCode.DELETE,
  20432. activateHandler: deleteHandler
  20433. }]
  20434. });
  20435. };
  20436. var bindRowHandlers = function (that, row) {
  20437. if ($.browser.msie && $.browser.version < 7) {
  20438. bindHover(row, that.options.styles);
  20439. }
  20440. that.locate("fileIconBtn", row).click(function () {
  20441. removeFileForRow(that, row);
  20442. });
  20443. bindDeleteKey(that, row);
  20444. };
  20445. var renderRowFromTemplate = function (that, file) {
  20446. var row = that.rowTemplate.clone(),
  20447. fileName = file.name,
  20448. fileSize = fluid.uploader.formatFileSize(file.size);
  20449. row.removeClass(that.options.styles.hiddenTemplate);
  20450. that.locate("fileName", row).text(fileName);
  20451. that.locate("fileSize", row).text(fileSize);
  20452. that.locate("fileIconBtn", row).addClass(that.options.styles.remove);
  20453. row.prop("id", file.id);
  20454. row.addClass(that.options.styles.ready);
  20455. bindRowHandlers(that, row);
  20456. fluid.updateAriaLabel(row, fileName + " " + fileSize);
  20457. return row;
  20458. };
  20459. var createProgressorFromTemplate = function (that, row) {
  20460. // create a new progress bar for the row and position it
  20461. var rowProgressor = that.rowProgressorTemplate.clone();
  20462. var rowId = row.prop("id");
  20463. var progressId = rowId + "_progress";
  20464. rowProgressor.prop("id", progressId);
  20465. rowProgressor.css("top", row.position().top);
  20466. rowProgressor.height(row.height()).width(5);
  20467. that.container.after(rowProgressor);
  20468. that.fileProgressors[progressId] = fluid.progress(that.options.uploaderContainer, {
  20469. selectors: {
  20470. progressBar: "#" + rowId,
  20471. displayElement: "#" + progressId,
  20472. label: "#" + progressId + " .fl-uploader-file-progress-text",
  20473. indicator: "#" + progressId
  20474. }
  20475. });
  20476. };
  20477. var addFile = function (that, file) {
  20478. var row = renderRowFromTemplate(that, file);
  20479. /* FLUID-2720 - do not hide the row under IE8 */
  20480. if (!($.browser.msie && ($.browser.version >= 8))) {
  20481. row.hide();
  20482. }
  20483. that.container.append(row);
  20484. row.attr("title", that.options.strings.status.remove);
  20485. row.fadeIn("slow");
  20486. createProgressorFromTemplate(that, row);
  20487. that.refreshView();
  20488. that.scroller.scrollTo("100%");
  20489. };
  20490. // Toggle keyboard row handlers on and off depending on the uploader state
  20491. var enableRows = function (rows, state) {
  20492. var i;
  20493. for (i = 0; i < rows.length; i++) {
  20494. fluid.enabled(rows[i], state);
  20495. }
  20496. };
  20497. var prepareForUpload = function (that) {
  20498. var rowButtons = that.locate("fileIconBtn", that.locate("fileRows"));
  20499. rowButtons.prop("disabled", true);
  20500. rowButtons.addClass(that.options.styles.dim);
  20501. enableRows(that.locate("fileRows"), false);
  20502. };
  20503. var refreshAfterUpload = function (that) {
  20504. var rowButtons = that.locate("fileIconBtn", that.locate("fileRows"));
  20505. rowButtons.prop("disabled", false);
  20506. rowButtons.removeClass(that.options.styles.dim);
  20507. enableRows(that.locate("fileRows"), true);
  20508. };
  20509. var changeRowState = function (that, row, newState) {
  20510. row.removeClass(that.options.styles.ready).removeClass(that.options.styles.error).addClass(newState);
  20511. };
  20512. var markRowAsComplete = function (that, file) {
  20513. // update styles and keyboard bindings for the file row
  20514. var row = rowForFile(that, file);
  20515. changeRowState(that, row, that.options.styles.uploaded);
  20516. row.attr("title", that.options.strings.status.success);
  20517. fluid.enabled(row, false);
  20518. // update the click event and the styling for the file delete button
  20519. var removeRowBtn = that.locate("fileIconBtn", row);
  20520. removeRowBtn.unbind("click");
  20521. removeRowBtn.removeClass(that.options.styles.remove);
  20522. removeRowBtn.attr("title", that.options.strings.status.success);
  20523. };
  20524. var renderErrorInfoRowFromTemplate = function (that, fileRow, error) {
  20525. // Render the row by cloning the template and binding its id to the file.
  20526. var errorRow = that.errorInfoRowTemplate.clone();
  20527. errorRow.prop("id", fileRow.prop("id") + "_error");
  20528. // Look up the error message and render it.
  20529. var errorType = fluid.keyForValue(fluid.uploader.errorConstants, error);
  20530. var errorMsg = that.options.strings.errors[errorType];
  20531. that.locate("errorText", errorRow).text(errorMsg);
  20532. fileRow.after(errorRow);
  20533. that.scroller.scrollTo(errorRow);
  20534. };
  20535. var showErrorForFile = function (that, file, error) {
  20536. hideFileProgress(that, file);
  20537. if (file.filestatus === fluid.uploader.fileStatusConstants.ERROR) {
  20538. var fileRowElm = rowForFile(that, file);
  20539. changeRowState(that, fileRowElm, that.options.styles.error);
  20540. renderErrorInfoRowFromTemplate(that, fileRowElm, error);
  20541. }
  20542. };
  20543. var addKeyboardNavigation = function (that) {
  20544. fluid.tabbable(that.container);
  20545. that.selectableContext = fluid.selectable(that.container, {
  20546. selectableSelector: that.options.selectors.fileRows,
  20547. onSelect: function (itemToSelect) {
  20548. $(itemToSelect).addClass(that.options.styles.selected);
  20549. },
  20550. onUnselect: function (selectedItem) {
  20551. $(selectedItem).removeClass(that.options.styles.selected);
  20552. }
  20553. });
  20554. };
  20555. var prepareTemplateElements = function (that) {
  20556. // Grab our template elements out of the DOM.
  20557. that.rowTemplate = that.locate("rowTemplate").remove();
  20558. that.errorInfoRowTemplate = that.locate("errorInfoRowTemplate").remove();
  20559. that.errorInfoRowTemplate.removeClass(that.options.styles.hiddenTemplate);
  20560. that.rowProgressorTemplate = that.locate("rowProgressorTemplate", that.options.uploaderContainer).remove();
  20561. };
  20562. fluid.registerNamespace("fluid.uploader.fileQueueView");
  20563. fluid.uploader.fileQueueView.finalInit = function (that) {
  20564. prepareTemplateElements(that);
  20565. addKeyboardNavigation(that);
  20566. };
  20567. /**
  20568. * Creates a new File Queue view.
  20569. *
  20570. * @param {jQuery|selector} container the file queue's container DOM element
  20571. * @param {fileQueue} queue a file queue model instance
  20572. * @param {Object} options configuration options for the view
  20573. */
  20574. fluid.uploader.fileQueueView.preInit = function (that) {
  20575. that.fileProgressors = {};
  20576. that.addFile = function (file) {
  20577. addFile(that, file);
  20578. };
  20579. that.removeFile = function (file) {
  20580. removeRowForFile(that, file);
  20581. };
  20582. that.prepareForUpload = function () {
  20583. prepareForUpload(that);
  20584. };
  20585. that.refreshAfterUpload = function () {
  20586. refreshAfterUpload(that);
  20587. };
  20588. that.showFileProgress = function (file) {
  20589. startFileProgress(that, file);
  20590. };
  20591. that.updateFileProgress = function (file, fileBytesComplete, fileTotalBytes) {
  20592. updateFileProgress(that, file, fileBytesComplete, fileTotalBytes);
  20593. };
  20594. that.markFileComplete = function (file) {
  20595. progressorForFile(that, file).update(100, "100%");
  20596. markRowAsComplete(that, file);
  20597. };
  20598. that.showErrorForFile = function (file, error) {
  20599. showErrorForFile(that, file, error);
  20600. };
  20601. that.hideFileProgress = function (file) {
  20602. hideFileProgress(that, file);
  20603. };
  20604. that.refreshView = function () {
  20605. that.selectableContext.refresh();
  20606. that.scroller.refreshView();
  20607. };
  20608. };
  20609. fluid.defaults("fluid.uploader.fileQueueView", {
  20610. gradeNames: ["fluid.viewComponent", "autoInit"],
  20611. preInitFunction: "fluid.uploader.fileQueueView.preInit",
  20612. finalInitFunction: "fluid.uploader.fileQueueView.finalInit",
  20613. components: {
  20614. scroller: {
  20615. type: "fluid.scrollableTable"
  20616. },
  20617. eventBinder: {
  20618. type: "fluid.uploader.fileQueueView.eventBinder"
  20619. }
  20620. },
  20621. selectors: {
  20622. fileRows: ".flc-uploader-file",
  20623. fileName: ".flc-uploader-file-name",
  20624. fileSize: ".flc-uploader-file-size",
  20625. fileIconBtn: ".flc-uploader-file-action",
  20626. errorText: ".flc-uploader-file-error",
  20627. rowTemplate: ".flc-uploader-file-tmplt",
  20628. errorInfoRowTemplate: ".flc-uploader-file-error-tmplt",
  20629. rowProgressorTemplate: ".flc-uploader-file-progressor-tmplt"
  20630. },
  20631. styles: {
  20632. hover: "fl-uploader-file-hover",
  20633. selected: "fl-uploader-file-focus",
  20634. ready: "fl-uploader-file-state-ready",
  20635. uploading: "fl-uploader-file-state-uploading",
  20636. uploaded: "fl-uploader-file-state-uploaded",
  20637. error: "fl-uploader-file-state-error",
  20638. remove: "fl-uploader-file-action-remove",
  20639. dim: "fl-uploader-dim",
  20640. hiddenTemplate: "fl-uploader-hidden-templates"
  20641. },
  20642. strings: {
  20643. progress: {
  20644. toUploadLabel: "To upload: %fileCount %fileLabel (%totalBytes)",
  20645. singleFile: "file",
  20646. pluralFiles: "files"
  20647. },
  20648. status: {
  20649. success: "File Uploaded",
  20650. error: "File Upload Error",
  20651. remove: "Press Delete key to remove file"
  20652. },
  20653. errors: {
  20654. HTTP_ERROR: "File upload error: a network error occured or the file was rejected (reason unknown).",
  20655. IO_ERROR: "File upload error: a network error occured.",
  20656. UPLOAD_LIMIT_EXCEEDED: "File upload error: you have uploaded as many files as you are allowed during this session",
  20657. UPLOAD_FAILED: "File upload error: the upload failed for an unknown reason.",
  20658. QUEUE_LIMIT_EXCEEDED: "You have as many files in the queue as can be added at one time. Removing files from the queue may allow you to add different files.",
  20659. FILE_EXCEEDS_SIZE_LIMIT: "One or more of the files that you attempted to add to the queue exceeded the limit of %fileSizeLimit.",
  20660. ZERO_BYTE_FILE: "One or more of the files that you attempted to add contained no data.",
  20661. INVALID_FILETYPE: "One or more files were not added to the queue because they were of the wrong type."
  20662. }
  20663. },
  20664. events: {
  20665. onFileRemoved: null
  20666. },
  20667. mergePolicy: {
  20668. model: "preserve"
  20669. }
  20670. });
  20671. /**
  20672. * EventBinder declaratively binds FileQueueView's methods as listeners to Uploader events using IoC.
  20673. */
  20674. fluid.defaults("fluid.uploader.fileQueueView.eventBinder", {
  20675. gradeNames: ["fluid.eventedComponent", "autoInit"]
  20676. });
  20677. fluid.demands("fluid.uploader.fileQueueView.eventBinder", [], {});
  20678. /**************
  20679. * Scrollable *
  20680. **************/
  20681. /**
  20682. * Simple component cover for the jQuery scrollTo plugin. Provides roughly equivalent
  20683. * functionality to Uploader's old Scroller plugin.
  20684. *
  20685. * @param {jQueryable} element the element to make scrollable
  20686. * @param {Object} options for the component
  20687. * @return the scrollable component
  20688. */
  20689. fluid.scrollable = function (element, options) {
  20690. var that = fluid.initView("fluid.scrollable", element, options);
  20691. that.scrollable = that.options.makeScrollableFn(that.container, that.options);
  20692. that.maxHeight = that.scrollable.css("max-height");
  20693. /**
  20694. * Programmatically scrolls this scrollable element to the region specified.
  20695. * This method is directly compatible with the underlying jQuery.scrollTo plugin.
  20696. */
  20697. that.scrollTo = function () {
  20698. that.scrollable.scrollTo.apply(that.scrollable, arguments);
  20699. };
  20700. /*
  20701. * Updates the view of the scrollable region. This should be called when the content of the scrollable region is changed.
  20702. */
  20703. that.refreshView = function () {
  20704. if ($.browser.msie && $.browser.version === "6.0") {
  20705. that.scrollable.css("height", "");
  20706. // Set height, if max-height is reached, to allow scrolling in IE6.
  20707. if (that.scrollable.height() >= parseInt(that.maxHeight, 10)) {
  20708. that.scrollable.css("height", that.maxHeight);
  20709. }
  20710. }
  20711. };
  20712. that.refreshView();
  20713. return that;
  20714. };
  20715. fluid.scrollable.makeSimple = function (element, options) {
  20716. return fluid.container(element);
  20717. };
  20718. fluid.scrollable.makeTable = function (table, options) {
  20719. table.wrap(options.wrapperMarkup);
  20720. return table.closest(".fl-scrollable-scroller");
  20721. };
  20722. fluid.defaults("fluid.scrollable", {
  20723. gradeNames: ["fluid.viewComponent"],
  20724. makeScrollableFn: fluid.scrollable.makeSimple
  20725. });
  20726. /**
  20727. * Wraps a table in order to make it scrollable with the jQuery.scrollTo plugin.
  20728. * Container divs are injected to allow cross-browser support.
  20729. *
  20730. * @param {jQueryable} table the table to make scrollable
  20731. * @param {Object} options configuration options
  20732. * @return the scrollable component
  20733. */
  20734. fluid.scrollableTable = function (table, options) {
  20735. options = $.extend({}, fluid.defaults("fluid.scrollableTable"), options);
  20736. return fluid.scrollable(table, options);
  20737. };
  20738. fluid.defaults("fluid.scrollableTable", {
  20739. gradeNames: "fluid.viewComponent",
  20740. makeScrollableFn: fluid.scrollable.makeTable,
  20741. wrapperMarkup: "<div class='fl-scrollable-scroller'><div class='fl-scrollable-inner'></div></div>"
  20742. });
  20743. fluid.demands("fluid.scrollableTable", "fluid.uploader.fileQueueView", {
  20744. funcName: "fluid.scrollableTable",
  20745. args: [
  20746. "{fileQueueView}.container"
  20747. ]
  20748. });
  20749. })(jQuery, fluid_1_5);
  20750. /*
  20751. Copyright 2011 OCAD University
  20752. Licensed under the Educational Community License (ECL), Version 2.0 or the New
  20753. BSD license. You may not use this file except in compliance with one these
  20754. Licenses.
  20755. You may obtain a copy of the ECL 2.0 License and BSD License at
  20756. https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt
  20757. */
  20758. // Declare dependencies
  20759. /*global window, fluid_1_5:true, jQuery*/
  20760. // JSLint options
  20761. /*jslint white: true, funcinvoke: true, undef: true, newcap: true, nomen: true, regexp: true, bitwise: true, browser: true, forin: true, maxerr: 100, indent: 4 */
  20762. var fluid_1_5 = fluid_1_5 || {};
  20763. (function ($, fluid) {
  20764. fluid.uploader = fluid.uploader || {};
  20765. fluid.defaults("fluid.uploader.errorPanel", {
  20766. gradeNames: ["fluid.viewComponent", "autoInit"],
  20767. preInitFunction: "fluid.uploader.errorPanel.preInit",
  20768. postInitFunction: "fluid.uploader.errorPanel.renderSectionTemplates",
  20769. finalInitFunction: "fluid.uploader.errorPanel.finalInit",
  20770. components: {
  20771. // TODO: This won't scale nicely with more types of errors.
  20772. fileSizeErrorSection: {
  20773. type: "fluid.uploader.errorPanel.section",
  20774. container: "{errorPanel}.dom.fileSizeErrorSection",
  20775. options: {
  20776. model: {
  20777. errorCode: fluid.uploader.queueErrorConstants.FILE_EXCEEDS_SIZE_LIMIT
  20778. },
  20779. strings: {
  20780. header: "{errorPanel}.options.strings.exceedsFileSize"
  20781. }
  20782. }
  20783. },
  20784. numFilesErrorSection: {
  20785. type: "fluid.uploader.errorPanel.section",
  20786. container: "{errorPanel}.dom.numFilesErrorSection",
  20787. options: {
  20788. model: {
  20789. errorCode: fluid.uploader.queueErrorConstants.QUEUE_LIMIT_EXCEEDED
  20790. },
  20791. strings: {
  20792. header: "{errorPanel}.options.strings.exceedsNumFilesLimit"
  20793. }
  20794. }
  20795. }
  20796. },
  20797. selectors: {
  20798. header: ".flc-uploader-errorPanel-header",
  20799. sectionTemplate: ".flc-uploader-errorPanel-section-tmplt",
  20800. fileSizeErrorSection: ".flc-uploader-errorPanel-section-fileSize",
  20801. numFilesErrorSection: ".flc-uploader-errorPanel-section-numFiles"
  20802. },
  20803. strings: {
  20804. headerText: "Warning(s)",
  20805. exceedsNumFilesLimit: "Too many files were selected. %numFiles were not added to the queue.",
  20806. exceedsFileSize: "%numFiles files were too large and were not added to the queue."
  20807. },
  20808. styles: {
  20809. hiddenTemplate: "fl-hidden-templates"
  20810. }
  20811. });
  20812. fluid.uploader.errorPanel.preInit = function (that) {
  20813. that.refreshView = function () {
  20814. for (var i = 0; i < that.sections.length; i++) {
  20815. if (that.sections[i].model.files.length > 0) {
  20816. // One of the sections has errors. Show them and bail immediately.
  20817. that.container.show();
  20818. return;
  20819. }
  20820. }
  20821. that.container.hide();
  20822. };
  20823. };
  20824. fluid.uploader.errorPanel.renderSectionTemplates = function (that) {
  20825. var sectionTmpl = that.locate("sectionTemplate").remove().removeClass(that.options.styles.hiddenTemplate);
  20826. that.locate("fileSizeErrorSection").append(sectionTmpl.clone());
  20827. that.locate("numFilesErrorSection").append(sectionTmpl.clone());
  20828. };
  20829. fluid.uploader.errorPanel.finalInit = function (that) {
  20830. that.sections = [that.fileSizeErrorSection, that.numFilesErrorSection];
  20831. that.locate("header").text(that.options.strings.headerText);
  20832. that.container.hide();
  20833. };
  20834. fluid.demands("fluid.uploader.errorPanel", "fluid.uploader.multiFileUploader", {
  20835. container: "{multiFileUploader}.dom.errorsPanel",
  20836. options: {
  20837. listeners: {
  20838. "{multiFileUploader}.events.afterFileDialog": "{errorPanel}.refreshView"
  20839. }
  20840. }
  20841. });
  20842. fluid.defaults("fluid.uploader.errorPanel.section", {
  20843. gradeNames: ["fluid.viewComponent", "autoInit"],
  20844. preInitFunction: "fluid.uploader.errorPanel.section.preInit",
  20845. finalInitFunction: "fluid.uploader.errorPanel.section.finalInit",
  20846. model: {
  20847. errorCode: undefined,
  20848. files: [],
  20849. showingDetails: false
  20850. },
  20851. events: {
  20852. afterErrorsCleared: null
  20853. },
  20854. selectors: {
  20855. errorTitle: ".fl-uploader-errorPanel-section-title",
  20856. deleteErrorButton: ".flc-uploader-errorPanel-section-removeButton",
  20857. errorDetails: ".flc-uploader-errorPanel-section-details",
  20858. erroredFiles: ".flc-uploader-errorPanel-section-files",
  20859. showHideFilesToggle: ".flc-uploader-errorPanel-section-toggleDetails"
  20860. },
  20861. strings: {
  20862. hideFiles: "Hide files",
  20863. showFiles: "Show files",
  20864. fileListDelimiter: ", "
  20865. }
  20866. });
  20867. fluid.uploader.errorPanel.section.preInit = function (that) {
  20868. that.toggleDetails = function () {
  20869. var detailsAction = that.model.showingDetails ? that.hideDetails : that.showDetails;
  20870. detailsAction();
  20871. };
  20872. that.showDetails = function () {
  20873. that.locate("errorDetails").show();
  20874. that.locate("showHideFilesToggle").text(that.options.strings.hideFiles);
  20875. that.model.showingDetails = true;
  20876. };
  20877. that.hideDetails = function () {
  20878. that.locate("errorDetails").hide();
  20879. that.locate("showHideFilesToggle").text(that.options.strings.showFiles);
  20880. that.model.showingDetails = false;
  20881. };
  20882. that.addFile = function (file, errorCode) {
  20883. if (errorCode === that.model.errorCode) {
  20884. that.model.files.push(file.name);
  20885. that.refreshView();
  20886. }
  20887. };
  20888. that.clear = function () {
  20889. that.model.files = [];
  20890. that.refreshView();
  20891. that.events.afterErrorsCleared.fire();
  20892. };
  20893. that.refreshView = function () {
  20894. fluid.uploader.errorPanel.section.renderHeader(that);
  20895. fluid.uploader.errorPanel.section.renderErrorDetails(that);
  20896. that.hideDetails();
  20897. if (that.model.files.length <= 0) {
  20898. that.container.hide();
  20899. } else {
  20900. that.container.show();
  20901. }
  20902. };
  20903. };
  20904. fluid.uploader.errorPanel.section.finalInit = function (that) {
  20905. // Bind delete button
  20906. that.locate("deleteErrorButton").click(that.clear);
  20907. // Bind hide/show error details link
  20908. that.locate("showHideFilesToggle").click(that.toggleDetails);
  20909. that.refreshView();
  20910. };
  20911. fluid.uploader.errorPanel.section.renderHeader = function (that) {
  20912. var errorTitle = fluid.stringTemplate(that.options.strings.header, {
  20913. numFiles: that.model.files.length
  20914. });
  20915. that.locate("errorTitle").text(errorTitle);
  20916. };
  20917. fluid.uploader.errorPanel.section.renderErrorDetails = function (that) {
  20918. var files = that.model.files;
  20919. var filesList = files.length > 0 ? files.join(that.options.strings.fileListDelimiter) : "";
  20920. that.locate("erroredFiles").text(filesList);
  20921. };
  20922. fluid.demands("fluid.uploader.errorPanel.section", [
  20923. "fluid.uploader.errorPanel",
  20924. "fluid.uploader.multiFileUploader"
  20925. ], {
  20926. options: {
  20927. listeners: {
  20928. "{multiFileUploader}.events.onQueueError": "{section}.addFile",
  20929. "{multiFileUploader}.events.onFilesSelected": "{section}.clear",
  20930. "{multiFileUploader}.events.onUploadStart": "{section}.clear",
  20931. "{section}.events.afterErrorsCleared": "{errorPanel}.refreshView"
  20932. }
  20933. }
  20934. });
  20935. })(jQuery, fluid_1_5);
  20936. /*
  20937. Copyright 2008-2009 University of Toronto
  20938. Copyright 2008-2009 University of California, Berkeley
  20939. Copyright 2010-2011 OCAD University
  20940. Copyright 2011 Lucendo Development Ltd.
  20941. Licensed under the Educational Community License (ECL), Version 2.0 or the New
  20942. BSD license. You may not use this file except in compliance with one these
  20943. Licenses.
  20944. You may obtain a copy of the ECL 2.0 License and BSD License at
  20945. https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt
  20946. */
  20947. // Declare dependencies
  20948. /*global fluid_1_5:true, jQuery, swfobject, SWFUpload */
  20949. // JSLint options
  20950. /*jslint white: true, funcinvoke: true, undef: true, newcap: true, nomen: true, regexp: true, bitwise: true, browser: true, forin: true, maxerr: 100, indent: 4 */
  20951. var fluid_1_5 = fluid_1_5 || {};
  20952. (function ($, fluid) {
  20953. fluid.uploader = fluid.uploader || {};
  20954. fluid.demands("fluid.uploaderImpl", "fluid.uploader.swfUpload", {
  20955. funcName: "fluid.uploader.multiFileUploader"
  20956. });
  20957. /**********************
  20958. * uploader.swfUpload *
  20959. **********************/
  20960. fluid.uploader.swfUploadStrategy = function (options) {
  20961. var that = fluid.initLittleComponent("fluid.uploader.swfUploadStrategy", options);
  20962. fluid.initDependents(that);
  20963. return that;
  20964. };
  20965. fluid.defaults("fluid.uploader.swfUploadStrategy", {
  20966. gradeNames: ["fluid.littleComponent"],
  20967. components: {
  20968. engine: {
  20969. type: "fluid.uploader.swfUploadStrategy.engine",
  20970. options: {
  20971. queueSettings: "{multiFileUploader}.options.queueSettings",
  20972. flashMovieSettings: "{swfUploadStrategy}.options.flashMovieSettings"
  20973. }
  20974. },
  20975. local: {
  20976. type: "fluid.uploader.local",
  20977. options: {
  20978. errorHandler: "{multiFileUploader}.dom.errorHandler"
  20979. }
  20980. },
  20981. remote: {
  20982. type: "fluid.uploader.remote"
  20983. }
  20984. },
  20985. // TODO: Rename this to "flashSettings" and remove the "flash" prefix from each option
  20986. flashMovieSettings: {
  20987. flashURL: "../../../lib/swfupload/flash/swfupload.swf",
  20988. flashButtonPeerId: "",
  20989. flashButtonAlwaysVisible: false,
  20990. flashButtonTransparentEvenInIE: true,
  20991. flashButtonImageURL: "../images/browse.png", // Used only when the Flash movie is visible.
  20992. flashButtonCursorEffect: SWFUpload.CURSOR.HAND,
  20993. debug: false
  20994. },
  20995. styles: {
  20996. browseButtonOverlay: "fl-uploader-browse-overlay",
  20997. flash9Container: "fl-uploader-flash9-container",
  20998. uploaderWrapperFlash10: "fl-uploader-flash10-wrapper"
  20999. }
  21000. });
  21001. fluid.demands("fluid.uploader.progressiveStrategy", "fluid.uploader.swfUpload", {
  21002. funcName: "fluid.uploader.swfUploadStrategy"
  21003. });
  21004. fluid.uploader.swfUploadStrategy.remote = function (swfUpload, queue, options) {
  21005. var that = fluid.initLittleComponent("fluid.uploader.swfUploadStrategy.remote", options);
  21006. that.swfUpload = swfUpload;
  21007. that.queue = queue;
  21008. that.uploadNextFile = function () {
  21009. that.swfUpload.startUpload();
  21010. };
  21011. that.stop = function () {
  21012. // FLUID-822: Instead of actually stopping SWFUpload right away, we wait until the current file
  21013. // is finished and then don't bother to upload any new ones. This is due an issue where SWFUpload
  21014. // appears to hang while Uploading a file that was previously stopped. I have a lingering suspicion
  21015. // that this may actually be a bug in our Image Gallery demo, rather than in SWFUpload itself.
  21016. that.queue.shouldStop = true;
  21017. };
  21018. return that;
  21019. };
  21020. fluid.demands("fluid.uploader.remote", "fluid.uploader.swfUploadStrategy", {
  21021. funcName: "fluid.uploader.swfUploadStrategy.remote",
  21022. args: [
  21023. "{engine}.swfUpload",
  21024. "{multiFileUploader}.queue",
  21025. "{options}"
  21026. ]
  21027. });
  21028. fluid.uploader.swfUploadStrategy.local = function (swfUpload, options) {
  21029. var that = fluid.initLittleComponent("fluid.uploader.swfUploadStrategy.local", options);
  21030. that.swfUpload = swfUpload;
  21031. that.browse = function () {
  21032. if (that.options.file_queue_limit === 1) {
  21033. that.swfUpload.selectFile();
  21034. } else {
  21035. that.swfUpload.selectFiles();
  21036. }
  21037. };
  21038. that.removeFile = function (file) {
  21039. that.swfUpload.cancelUpload(file.id);
  21040. };
  21041. that.enableBrowseButton = function () {
  21042. that.swfUpload.setButtonDisabled(false);
  21043. };
  21044. that.disableBrowseButton = function () {
  21045. that.swfUpload.setButtonDisabled(true);
  21046. };
  21047. return that;
  21048. };
  21049. fluid.demands("fluid.uploader.local", "fluid.uploader.swfUploadStrategy", {
  21050. funcName: "fluid.uploader.swfUploadStrategy.local",
  21051. args: [
  21052. "{engine}.swfUpload",
  21053. "{options}"
  21054. ]
  21055. });
  21056. fluid.uploader.swfUploadStrategy.engine = function (options) {
  21057. var that = fluid.initLittleComponent("fluid.uploader.swfUploadStrategy.engine", options);
  21058. // Get the Flash version from swfobject and setup a new context so that the appropriate
  21059. // Flash 9/10 strategies are selected.
  21060. var flashVersion = swfobject.getFlashPlayerVersion().major;
  21061. that.flashVersionContext = fluid.typeTag("fluid.uploader.flash." + flashVersion);
  21062. // Merge Uploader's generic queue options with our Flash-specific options.
  21063. that.config = $.extend({}, that.options.queueSettings, that.options.flashMovieSettings);
  21064. // Configure the SWFUpload subsystem.
  21065. fluid.initDependents(that);
  21066. that.flashContainer = that.setupDOM();
  21067. that.swfUploadConfig = that.setupConfig();
  21068. that.swfUpload = new SWFUpload(that.swfUploadConfig);
  21069. that.bindEvents();
  21070. return that;
  21071. };
  21072. fluid.defaults("fluid.uploader.swfUploadStrategy.engine", {
  21073. gradeNames: ["fluid.littleComponent"],
  21074. invokers: {
  21075. setupDOM: "fluid.uploader.swfUploadStrategy.setupDOM",
  21076. setupConfig: "fluid.uploader.swfUploadStrategy.setupConfig",
  21077. bindEvents: "fluid.uploader.swfUploadStrategy.eventBinder"
  21078. }
  21079. });
  21080. fluid.demands("fluid.uploader.swfUploadStrategy.engine", "fluid.uploader.swfUploadStrategy", {
  21081. funcName: "fluid.uploader.swfUploadStrategy.engine",
  21082. args: [
  21083. fluid.COMPONENT_OPTIONS
  21084. ]
  21085. });
  21086. /*
  21087. * Transform HTML5 MIME types into file types for SWFUpload.
  21088. */
  21089. fluid.uploader.swfUploadStrategy.fileTypeTransformer = function (model, expandSpec) {
  21090. var fileExts = "";
  21091. var mimeTypes = fluid.get(model, expandSpec.path);
  21092. var mimeTypesMap = fluid.uploader.mimeTypeRegistry;
  21093. if (!mimeTypes) {
  21094. return "*";
  21095. } else if (typeof (mimeTypes) === "string") {
  21096. return mimeTypes;
  21097. }
  21098. fluid.each(mimeTypes, function (mimeType) {
  21099. fluid.each(mimeTypesMap, function (mimeTypeForExt, ext) {
  21100. if (mimeTypeForExt === mimeType) {
  21101. fileExts += "*." + ext + ";";
  21102. }
  21103. });
  21104. });
  21105. return fileExts.length === 0 ? "*" : fileExts.substring(0, fileExts.length - 1);
  21106. };
  21107. /**********************
  21108. * swfUpload.setupDOM *
  21109. **********************/
  21110. fluid.uploader.swfUploadStrategy.flash10SetupDOM = function (uploaderContainer, browseButton, progressBar, styles) {
  21111. // Wrap the whole uploader first.
  21112. uploaderContainer.wrap("<div class='" + styles.uploaderWrapperFlash10 + "'></div>");
  21113. // Then create a container and placeholder for the Flash movie as a sibling to the uploader.
  21114. var flashContainer = $("<div><span></span></div>");
  21115. flashContainer.addClass(styles.browseButtonOverlay);
  21116. uploaderContainer.after(flashContainer);
  21117. progressBar.append(flashContainer);
  21118. browseButton.attr("tabindex", -1);
  21119. return flashContainer;
  21120. };
  21121. fluid.demands("fluid.uploader.swfUploadStrategy.setupDOM", [
  21122. "fluid.uploader.swfUploadStrategy.engine",
  21123. "fluid.uploader.flash.10"
  21124. ], {
  21125. funcName: "fluid.uploader.swfUploadStrategy.flash10SetupDOM",
  21126. args: [
  21127. "{multiFileUploader}.container",
  21128. "{multiFileUploader}.dom.browseButton",
  21129. "{totalProgress}.dom.progressBar",
  21130. "{swfUploadStrategy}.options.styles"
  21131. ]
  21132. });
  21133. /*********************************
  21134. * swfUpload.setupConfig *
  21135. *********************************/
  21136. // Maps SWFUpload's setting names to our component's setting names.
  21137. var swfUploadOptionsMap = {
  21138. uploadURL: "upload_url",
  21139. flashURL: "flash_url",
  21140. postParams: "post_params",
  21141. fileSizeLimit: "file_size_limit",
  21142. fileTypes: "file_types",
  21143. fileUploadLimit: "file_upload_limit",
  21144. fileQueueLimit: "file_queue_limit",
  21145. flashButtonPeerId: "button_placeholder_id",
  21146. flashButtonImageURL: "button_image_url",
  21147. flashButtonHeight: "button_height",
  21148. flashButtonWidth: "button_width",
  21149. flashButtonWindowMode: "button_window_mode",
  21150. flashButtonCursorEffect: "button_cursor",
  21151. debug: "debug"
  21152. };
  21153. // Maps SWFUpload's callback names to our component's callback names.
  21154. var swfUploadEventMap = {
  21155. afterReady: "swfupload_loaded_handler",
  21156. onFileDialog: "file_dialog_start_handler",
  21157. onFileQueued: "file_queued_handler",
  21158. onQueueError: "file_queue_error_handler",
  21159. afterFileDialog: "file_dialog_complete_handler",
  21160. onFileStart: "upload_start_handler",
  21161. onFileProgress: "upload_progress_handler",
  21162. onFileComplete: "upload_complete_handler",
  21163. onFileError: "upload_error_handler",
  21164. onFileSuccess: "upload_success_handler"
  21165. };
  21166. var mapNames = function (nameMap, source, target) {
  21167. var result = target || {};
  21168. for (var key in source) {
  21169. var mappedKey = nameMap[key];
  21170. if (mappedKey) {
  21171. result[mappedKey] = source[key];
  21172. }
  21173. }
  21174. return result;
  21175. };
  21176. // For each event type, hand the fire function to SWFUpload so it can fire the event at the right time for us.
  21177. // TODO: Refactor out duplication with mapNames()--should be able to use Engage's mapping tool
  21178. var mapSWFUploadEvents = function (nameMap, events, target) {
  21179. var result = target || {};
  21180. for (var eventType in events) {
  21181. var fireFn = events[eventType].fire;
  21182. var mappedName = nameMap[eventType];
  21183. if (mappedName) {
  21184. result[mappedName] = fireFn;
  21185. }
  21186. }
  21187. return result;
  21188. };
  21189. fluid.uploader.swfUploadStrategy.convertConfigForSWFUpload = function (flashContainer, config, events, queueSettings) {
  21190. config.flashButtonPeerId = fluid.allocateSimpleId(flashContainer.children().eq(0));
  21191. // Map the event and settings names to SWFUpload's expectations.
  21192. // Convert HTML5 MIME types into SWFUpload file types
  21193. config.fileTypes = fluid.uploader.swfUploadStrategy.fileTypeTransformer(queueSettings, {
  21194. path: "fileTypes"
  21195. });
  21196. var convertedConfig = mapNames(swfUploadOptionsMap, config);
  21197. // TODO: Same with the FLUID-3886 branch: Can these declarations be done elsewhere?
  21198. convertedConfig.file_upload_limit = 0;
  21199. convertedConfig.file_size_limit = 0;
  21200. return mapSWFUploadEvents(swfUploadEventMap, events, convertedConfig);
  21201. };
  21202. fluid.uploader.swfUploadStrategy.flash10SetupConfig = function (config, events, flashContainer, browseButton, queueSettings) {
  21203. var isTransparent = config.flashButtonAlwaysVisible ? false : (!$.browser.msie || config.flashButtonTransparentEvenInIE);
  21204. config.flashButtonImageURL = isTransparent ? undefined : config.flashButtonImageURL;
  21205. config.flashButtonHeight = config.flashButtonHeight || browseButton.outerHeight();
  21206. config.flashButtonWidth = config.flashButtonWidth || browseButton.outerWidth();
  21207. config.flashButtonWindowMode = isTransparent ? SWFUpload.WINDOW_MODE.TRANSPARENT : SWFUpload.WINDOW_MODE.OPAQUE;
  21208. return fluid.uploader.swfUploadStrategy.convertConfigForSWFUpload(flashContainer, config, events, queueSettings);
  21209. };
  21210. fluid.demands("fluid.uploader.swfUploadStrategy.setupConfig", [
  21211. "fluid.uploader.swfUploadStrategy.engine",
  21212. "fluid.uploader.flash.10"
  21213. ], {
  21214. funcName: "fluid.uploader.swfUploadStrategy.flash10SetupConfig",
  21215. args: [
  21216. "{engine}.config",
  21217. "{multiFileUploader}.events",
  21218. "{engine}.flashContainer",
  21219. "{multiFileUploader}.dom.browseButton",
  21220. "{multiFileUploader}.options.queueSettings"
  21221. ]
  21222. });
  21223. /*********************************
  21224. * swfUpload.eventBinder *
  21225. *********************************/
  21226. var unbindSWFUploadSelectFiles = function () {
  21227. // There's a bug in SWFUpload 2.2.0b3 that causes the entire browser to crash
  21228. // if selectFile() or selectFiles() is invoked. Remove them so no one will accidently crash their browser.
  21229. var emptyFunction = function () {};
  21230. SWFUpload.prototype.selectFile = emptyFunction;
  21231. SWFUpload.prototype.selectFiles = emptyFunction;
  21232. };
  21233. fluid.uploader.swfUploadStrategy.bindFileEventListeners = function (model, events) {
  21234. // Manually update our public model to keep it in sync with SWFUpload's insane,
  21235. // always-changing references to its internal model.
  21236. var manualModelUpdater = function (file) {
  21237. fluid.find(model, function (potentialMatch) {
  21238. if (potentialMatch.id === file.id) {
  21239. potentialMatch.filestatus = file.filestatus;
  21240. return true;
  21241. }
  21242. });
  21243. };
  21244. events.onFileStart.addListener(manualModelUpdater);
  21245. events.onFileProgress.addListener(manualModelUpdater);
  21246. events.onFileError.addListener(manualModelUpdater);
  21247. events.onFileSuccess.addListener(manualModelUpdater);
  21248. };
  21249. var filterErroredFiles = function (file, events, queue, queueSettings) {
  21250. var fileSizeLimit = queueSettings.fileSizeLimit * 1000;
  21251. var fileUploadLimit = queueSettings.fileUploadLimit;
  21252. var processedFiles = queue.getReadyFiles().length + queue.getUploadedFiles().length;
  21253. if (file.size > fileSizeLimit) {
  21254. file.filestatus = fluid.uploader.fileStatusConstants.ERROR;
  21255. events.onQueueError.fire(file, fluid.uploader.queueErrorConstants.FILE_EXCEEDS_SIZE_LIMIT);
  21256. } else if (processedFiles >= fileUploadLimit) {
  21257. events.onQueueError.fire(file, fluid.uploader.queueErrorConstants.QUEUE_LIMIT_EXCEEDED);
  21258. } else {
  21259. events.afterFileQueued.fire(file);
  21260. }
  21261. };
  21262. fluid.uploader.swfUploadStrategy.flash10EventBinder = function (queue, queueSettings, events) {
  21263. var model = queue.files;
  21264. unbindSWFUploadSelectFiles();
  21265. events.onFileQueued.addListener(function (file) {
  21266. filterErroredFiles(file, events, queue, queueSettings);
  21267. });
  21268. fluid.uploader.swfUploadStrategy.bindFileEventListeners(model, events);
  21269. };
  21270. fluid.demands("fluid.uploader.swfUploadStrategy.eventBinder", [
  21271. "fluid.uploader.swfUploadStrategy.engine",
  21272. "fluid.uploader.flash.10"
  21273. ], {
  21274. funcName: "fluid.uploader.swfUploadStrategy.flash10EventBinder",
  21275. args: [
  21276. "{multiFileUploader}.queue",
  21277. "{multiFileUploader}.queue.files",
  21278. "{multiFileUploader}.events"
  21279. ]
  21280. });
  21281. })(jQuery, fluid_1_5);
  21282. /*
  21283. Copyright 2008-2009 University of Toronto
  21284. Copyright 2010-2011 OCAD University
  21285. Copyright 2011 Lucendo Development Ltd.
  21286. Licensed under the Educational Community License (ECL), Version 2.0 or the New
  21287. BSD license. You may not use this file except in compliance with one these
  21288. Licenses.
  21289. You may obtain a copy of the ECL 2.0 License and BSD License at
  21290. https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt
  21291. */
  21292. // Declare dependencies
  21293. /*global fluid_1_5:true, jQuery*/
  21294. // JSLint options
  21295. /*jslint white: true, funcinvoke: true, undef: true, newcap: true, nomen: true, regexp: true, bitwise: true, browser: true, forin: true, maxerr: 100, indent: 4 */
  21296. var fluid_1_5 = fluid_1_5 || {};
  21297. (function ($, fluid) {
  21298. fluid.registerNamespace("fluid.uploader.swfUploadStrategy");
  21299. /**********************************************************************************
  21300. * The functions in this file, which provide support for Flash 9 in the Uploader, *
  21301. * have been deprecated as of Infusion 1.3. *
  21302. **********************************************************************************/
  21303. fluid.uploader.swfUploadStrategy.flash9SetupDOM = function (styles) {
  21304. var container = $("<div><span></span></div>");
  21305. container.addClass(styles.flash9Container);
  21306. $("body").append(container);
  21307. return container;
  21308. };
  21309. fluid.demands("fluid.uploader.swfUploadStrategy.setupDOM", [
  21310. "fluid.uploader.swfUploadStrategy.engine",
  21311. "fluid.uploader.flash.9"
  21312. ], {
  21313. funcName: "fluid.uploader.swfUploadStrategy.flash9SetupDOM",
  21314. args: [
  21315. "{swfUploadStrategy}.options.styles"
  21316. ]
  21317. });
  21318. fluid.uploader.swfUploadStrategy.flash9SetupConfig = function (flashContainer, config, events) {
  21319. return fluid.uploader.swfUploadStrategy.convertConfigForSWFUpload(flashContainer, config, events);
  21320. };
  21321. fluid.demands("fluid.uploader.swfUploadStrategy.setupConfig", [
  21322. "fluid.uploader.swfUploadStrategy.engine",
  21323. "fluid.uploader.flash.9"
  21324. ], {
  21325. funcName: "fluid.uploader.swfUploadStrategy.flash9SetupConfig",
  21326. args: [
  21327. "{engine}.flashContainer",
  21328. "{engine}.config",
  21329. "{multiFileUploader}.events"
  21330. ]
  21331. });
  21332. fluid.uploader.swfUploadStrategy.flash9EventBinder = function (model, events, local, browseButton) {
  21333. browseButton.click(function (e) {
  21334. local.browse();
  21335. e.preventDefault();
  21336. });
  21337. fluid.uploader.swfUploadStrategy.bindFileEventListeners(model, events);
  21338. };
  21339. fluid.demands("fluid.uploader.swfUploadStrategy.eventBinder", [
  21340. "fluid.uploader.swfUploadStrategy.engine",
  21341. "fluid.uploader.flash.9"
  21342. ], {
  21343. funcName: "fluid.uploader.swfUploadStrategy.flash9EventBinder",
  21344. args: [
  21345. "{multiFileUploader}.queue.files",
  21346. "{multiFileUploader}.events",
  21347. "{local}",
  21348. "{multiFileUploader}.dom.browseButton"
  21349. ]
  21350. });
  21351. })(jQuery, fluid_1_5);
  21352. /*
  21353. Copyright 2010-2011 OCAD University
  21354. Copyright 2011 Lucendo Development Ltd.
  21355. Licensed under the Educational Community License (ECL), Version 2.0 or the New
  21356. BSD license. You may not use this file except in compliance with one these
  21357. Licenses.
  21358. You may obtain a copy of the ECL 2.0 License and BSD License at
  21359. https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt
  21360. */
  21361. // Declare dependencies
  21362. /*global FormData, fluid_1_5:true, jQuery*/
  21363. // JSLint options
  21364. /*jslint white: true, funcinvoke: true, undef: true, newcap: true, nomen: true, regexp: true, bitwise: true, browser: true, forin: true, maxerr: 100, indent: 4 */
  21365. var fluid_1_5 = fluid_1_5 || {};
  21366. (function ($, fluid) {
  21367. fluid.demands("fluid.uploaderImpl", "fluid.uploader.html5", {
  21368. funcName: "fluid.uploader.multiFileUploader"
  21369. });
  21370. fluid.demands("fluid.uploader.progressiveStrategy", "fluid.uploader.html5", {
  21371. funcName: "fluid.uploader.html5Strategy"
  21372. });
  21373. fluid.defaults("fluid.uploader.html5Strategy", {
  21374. gradeNames: ["fluid.littleComponent", "autoInit"],
  21375. components: {
  21376. local: {
  21377. type: "fluid.uploader.local",
  21378. options: {
  21379. queueSettings: "{multiFileUploader}.options.queueSettings",
  21380. events: {
  21381. onFileDialog: "{multiFileUploader}.events.onFileDialog",
  21382. onFilesSelected: "{multiFileUploader}.events.onFilesSelected",
  21383. afterFileDialog: "{multiFileUploader}.events.afterFileDialog",
  21384. afterFileQueued: "{multiFileUploader}.events.afterFileQueued",
  21385. onQueueError: "{multiFileUploader}.events.onQueueError"
  21386. }
  21387. }
  21388. },
  21389. remote: {
  21390. type: "fluid.uploader.remote",
  21391. options: {
  21392. queueSettings: "{multiFileUploader}.options.queueSettings",
  21393. events: {
  21394. afterReady: "{multiFileUploader}.events.afterReady",
  21395. onFileStart: "{multiFileUploader}.events.onFileStart",
  21396. onFileProgress: "{multiFileUploader}.events.onFileProgress",
  21397. onFileSuccess: "{multiFileUploader}.events.onFileSuccess",
  21398. onFileError: "{multiFileUploader}.events.onFileError",
  21399. onFileComplete: "{multiFileUploader}.events.onFileComplete"
  21400. }
  21401. }
  21402. }
  21403. },
  21404. // Used for browsers that rely on File.getAsBinary(), such as Firefox 3.6,
  21405. // which load the entire file to be loaded into memory.
  21406. // Set this option to a sane limit (100MB) so your users won't experience crashes or slowdowns (FLUID-3937).
  21407. legacyBrowserFileLimit: 100000
  21408. });
  21409. // TODO: The following two or three functions probably ultimately belong on a that responsible for
  21410. // coordinating with the XHR. A fileConnection object or something similar.
  21411. fluid.uploader.html5Strategy.fileSuccessHandler = function (file, events, xhr) {
  21412. events.onFileSuccess.fire(file, xhr.responseText, xhr);
  21413. events.onFileComplete.fire(file);
  21414. };
  21415. fluid.uploader.html5Strategy.fileErrorHandler = function (file, events, xhr) {
  21416. events.onFileError.fire(file,
  21417. fluid.uploader.errorConstants.UPLOAD_FAILED,
  21418. xhr.status,
  21419. xhr);
  21420. events.onFileComplete.fire(file);
  21421. };
  21422. fluid.uploader.html5Strategy.fileStopHandler = function (file, events, xhr) {
  21423. events.onFileError.fire(file,
  21424. fluid.uploader.errorConstants.UPLOAD_STOPPED,
  21425. xhr.status,
  21426. xhr);
  21427. events.onFileComplete.fire(file);
  21428. };
  21429. fluid.uploader.html5Strategy.monitorFileUploadXHR = function (file, events, xhr) {
  21430. xhr.onreadystatechange = function () {
  21431. if (xhr.readyState === 4) {
  21432. var status = xhr.status;
  21433. // TODO: See a pattern here? Fix it.
  21434. if (status >= 200 && status <= 204) {
  21435. fluid.uploader.html5Strategy.fileSuccessHandler(file, events, xhr);
  21436. } else if (status === 0) {
  21437. fluid.uploader.html5Strategy.fileStopHandler(file, events, xhr);
  21438. } else {
  21439. fluid.uploader.html5Strategy.fileErrorHandler(file, events, xhr);
  21440. }
  21441. }
  21442. };
  21443. xhr.upload.onprogress = function (pe) {
  21444. events.onFileProgress.fire(file, pe.loaded, pe.total);
  21445. };
  21446. };
  21447. /*************************************
  21448. * HTML5 Strategy's remote behaviour *
  21449. *************************************/
  21450. fluid.uploader.html5Strategy.remote = function (queue, options) {
  21451. var that = fluid.initLittleComponent("fluid.uploader.html5Strategy.remote", options);
  21452. that.queue = queue;
  21453. that.queueSettings = that.options.queueSettings;
  21454. // Upload files in the current batch without exceeding the fileUploadLimit
  21455. that.uploadNextFile = function () {
  21456. var batch = that.queue.currentBatch;
  21457. var file = batch.files[batch.fileIdx];
  21458. that.uploadFile(file);
  21459. };
  21460. that.uploadFile = function (file) {
  21461. that.events.onFileStart.fire(file);
  21462. that.currentXHR = that.createXHR();
  21463. fluid.uploader.html5Strategy.monitorFileUploadXHR(file, that.events, that.currentXHR);
  21464. that.fileSender.send(file, that.queueSettings, that.currentXHR);
  21465. };
  21466. that.stop = function () {
  21467. that.queue.isUploading = false;
  21468. that.currentXHR.abort();
  21469. };
  21470. fluid.initDependents(that);
  21471. that.events.afterReady.fire();
  21472. return that;
  21473. };
  21474. fluid.defaults("fluid.uploader.html5Strategy.remote", {
  21475. gradeNames: ["fluid.eventedComponent"],
  21476. argumentMap: {
  21477. options: 1
  21478. },
  21479. components: {
  21480. fileSender: {
  21481. type: "fluid.uploader.html5Strategy.fileSender"
  21482. }
  21483. },
  21484. invokers: {
  21485. createXHR: "fluid.uploader.html5Strategy.createXHR"
  21486. }
  21487. });
  21488. fluid.demands("fluid.uploader.remote", ["fluid.uploader.html5Strategy", "fluid.uploader.live"], {
  21489. funcName: "fluid.uploader.html5Strategy.remote",
  21490. args: [
  21491. "{multiFileUploader}.queue",
  21492. fluid.COMPONENT_OPTIONS
  21493. ]
  21494. });
  21495. fluid.uploader.html5Strategy.createXHR = function () {
  21496. return new XMLHttpRequest();
  21497. };
  21498. fluid.uploader.html5Strategy.createFormData = function () {
  21499. return new FormData();
  21500. };
  21501. // Set additional POST parameters for xhr
  21502. var setPostParams = function (formData, postParams) {
  21503. $.each(postParams, function (key, value) {
  21504. formData.append(key, value);
  21505. });
  21506. };
  21507. /*******************************************************
  21508. * HTML5 FormData Sender, used by most modern browsers *
  21509. *******************************************************/
  21510. fluid.defaults("fluid.uploader.html5Strategy.formDataSender", {
  21511. gradeNames: ["fluid.littleComponent", "autoInit"],
  21512. finalInitFunction: "fluid.uploader.html5Strategy.formDataSender.init",
  21513. invokers: {
  21514. createFormData: "fluid.uploader.html5Strategy.createFormData"
  21515. }
  21516. });
  21517. fluid.uploader.html5Strategy.formDataSender.init = function (that) {
  21518. /**
  21519. * Uploads the file using the HTML5 FormData object.
  21520. */
  21521. that.send = function (file, queueSettings, xhr) {
  21522. var formData = that.createFormData();
  21523. formData.append("file", file);
  21524. setPostParams(formData, queueSettings.postParams);
  21525. xhr.open("POST", queueSettings.uploadURL, true);
  21526. xhr.send(formData);
  21527. return formData;
  21528. };
  21529. };
  21530. fluid.demands("fluid.uploader.html5Strategy.fileSender", [
  21531. "fluid.uploader.html5Strategy.remote",
  21532. "fluid.browser.supportsFormData"
  21533. ], {
  21534. funcName: "fluid.uploader.html5Strategy.formDataSender"
  21535. });
  21536. /********************************************
  21537. * Raw MIME Sender, required by Firefox 3.6 *
  21538. ********************************************/
  21539. fluid.uploader.html5Strategy.generateMultipartBoundary = function () {
  21540. var boundary = "---------------------------";
  21541. boundary += Math.floor(Math.random() * 32768);
  21542. boundary += Math.floor(Math.random() * 32768);
  21543. boundary += Math.floor(Math.random() * 32768);
  21544. return boundary;
  21545. };
  21546. fluid.uploader.html5Strategy.generateMultiPartContent = function (boundary, file) {
  21547. var CRLF = "\r\n";
  21548. var multipart = "";
  21549. multipart += "--" + boundary + CRLF;
  21550. multipart += "Content-Disposition: form-data;" +
  21551. " name=\"fileData\";" +
  21552. " filename=\"" + file.name +
  21553. "\"" + CRLF;
  21554. multipart += "Content-Type: " + file.type + CRLF + CRLF;
  21555. multipart += file.getAsBinary(); // Concatting binary data to JS String; yes, FF will handle it.
  21556. multipart += CRLF + "--" + boundary + "--" + CRLF;
  21557. return multipart;
  21558. };
  21559. fluid.defaults("fluid.uploader.html5Strategy.rawMIMESender", {
  21560. gradeNames: ["fluid.littleComponent", "autoInit"],
  21561. finalInitFunction: "fluid.uploader.html5Strategy.rawMIMESender.init"
  21562. });
  21563. fluid.uploader.html5Strategy.rawMIMESender.init = function (that) {
  21564. /**
  21565. * Uploads the file by manually creating the multipart/form-data request. Required by Firefox 3.6.
  21566. */
  21567. that.send = function (file, queueSettings, xhr) {
  21568. var boundary = fluid.uploader.html5Strategy.generateMultipartBoundary();
  21569. var multipart = fluid.uploader.html5Strategy.generateMultiPartContent(boundary, file);
  21570. xhr.open("POST", queueSettings.uploadURL, true);
  21571. xhr.setRequestHeader("Content-Type", "multipart/form-data; boundary=" + boundary);
  21572. xhr.sendAsBinary(multipart);
  21573. return multipart;
  21574. };
  21575. };
  21576. fluid.demands("fluid.uploader.html5Strategy.fileSender", "fluid.uploader.html5Strategy.remote", {
  21577. funcName: "fluid.uploader.html5Strategy.rawMIMESender"
  21578. });
  21579. /************************************
  21580. * HTML5 Strategy's Local Behaviour *
  21581. ************************************/
  21582. fluid.uploader.html5Strategy.local = function (queue, legacyBrowserFileLimit, options) {
  21583. var that = fluid.initLittleComponent("fluid.uploader.html5Strategy.local", options);
  21584. that.queue = queue;
  21585. that.queueSettings = that.options.queueSettings;
  21586. // Add files to the file queue without exceeding the fileUploadLimit and the fileSizeLimit
  21587. // NOTE: fileSizeLimit set to bytes for HTML5 Uploader (KB for SWF Uploader).
  21588. that.addFiles = function (files) {
  21589. // TODO: These look like they should be part of a real model.
  21590. var sizeLimit = (legacyBrowserFileLimit || that.queueSettings.fileSizeLimit) * 1024;
  21591. var fileLimit = that.queueSettings.fileUploadLimit;
  21592. var uploaded = that.queue.getUploadedFiles().length;
  21593. var queued = that.queue.getReadyFiles().length;
  21594. var remainingUploadLimit = fileLimit - uploaded - queued;
  21595. that.events.onFilesSelected.fire(files.length);
  21596. // Provide feedback to the user if the file size is too large and isn't added to the file queue
  21597. var numFilesAdded = 0;
  21598. for (var i = 0; i < files.length; i++) {
  21599. var file = files[i];
  21600. if (fileLimit && remainingUploadLimit === 0) {
  21601. that.events.onQueueError.fire(file, fluid.uploader.queueErrorConstants.QUEUE_LIMIT_EXCEEDED);
  21602. } else if (file.size >= sizeLimit) {
  21603. file.filestatus = fluid.uploader.fileStatusConstants.ERROR;
  21604. that.events.onQueueError.fire(file, fluid.uploader.queueErrorConstants.FILE_EXCEEDS_SIZE_LIMIT);
  21605. } else if (!fileLimit || remainingUploadLimit > 0) {
  21606. file.id = "file-" + fluid.allocateGuid();
  21607. file.filestatus = fluid.uploader.fileStatusConstants.QUEUED;
  21608. that.events.afterFileQueued.fire(file);
  21609. remainingUploadLimit--;
  21610. numFilesAdded++;
  21611. }
  21612. }
  21613. that.events.afterFileDialog.fire(numFilesAdded);
  21614. };
  21615. that.removeFile = function (file) {
  21616. };
  21617. that.enableBrowseButton = function () {
  21618. that.browseButtonView.enable();
  21619. };
  21620. that.disableBrowseButton = function () {
  21621. that.browseButtonView.disable();
  21622. };
  21623. fluid.initDependents(that);
  21624. return that;
  21625. };
  21626. fluid.defaults("fluid.uploader.html5Strategy.local", {
  21627. argumentMap: {
  21628. options: 2
  21629. },
  21630. gradeNames: ["fluid.eventedComponent"],
  21631. components: {
  21632. browseButtonView: {
  21633. type: "fluid.uploader.html5Strategy.browseButtonView",
  21634. options: {
  21635. queueSettings: "{multiFileUploader}.options.queueSettings",
  21636. selectors: {
  21637. browseButton: "{multiFileUploader}.options.selectors.browseButton"
  21638. },
  21639. listeners: {
  21640. onFilesQueued: "{local}.addFiles"
  21641. }
  21642. }
  21643. }
  21644. }
  21645. });
  21646. fluid.demands("fluid.uploader.local", "fluid.uploader.html5Strategy", {
  21647. funcName: "fluid.uploader.html5Strategy.local",
  21648. args: [
  21649. "{multiFileUploader}.queue",
  21650. "{html5Strategy}.options.legacyBrowserFileLimit",
  21651. "{options}"
  21652. ]
  21653. });
  21654. fluid.demands("fluid.uploader.local", [
  21655. "fluid.uploader.html5Strategy",
  21656. "fluid.browser.supportsFormData"
  21657. ], {
  21658. funcName: "fluid.uploader.html5Strategy.local",
  21659. args: [
  21660. "{multiFileUploader}.queue",
  21661. undefined,
  21662. "{options}"
  21663. ]
  21664. });
  21665. /********************
  21666. * browseButtonView *
  21667. ********************/
  21668. var bindEventsToFileInput = function (that, fileInput) {
  21669. fileInput.click(function () {
  21670. that.events.onBrowse.fire();
  21671. });
  21672. fileInput.change(function () {
  21673. var files = fileInput[0].files;
  21674. that.renderFreshMultiFileInput();
  21675. that.events.onFilesQueued.fire(files);
  21676. });
  21677. fileInput.focus(function () {
  21678. that.browseButton.addClass("focus");
  21679. });
  21680. fileInput.blur(function () {
  21681. that.browseButton.removeClass("focus");
  21682. });
  21683. };
  21684. var renderMultiFileInput = function (that) {
  21685. var multiFileInput = $(that.options.multiFileInputMarkup);
  21686. var fileTypes = that.options.queueSettings.fileTypes;
  21687. if (fluid.isArrayable(fileTypes)) {
  21688. fileTypes = fileTypes.join();
  21689. multiFileInput.attr("accept", fileTypes);
  21690. }
  21691. bindEventsToFileInput(that, multiFileInput);
  21692. return multiFileInput;
  21693. };
  21694. var setupBrowseButtonView = function (that) {
  21695. var multiFileInput = renderMultiFileInput(that);
  21696. that.browseButton.append(multiFileInput);
  21697. that.browseButton.attr("tabindex", -1);
  21698. };
  21699. fluid.uploader.html5Strategy.browseButtonView = function (container, options) {
  21700. var that = fluid.initView("fluid.uploader.html5Strategy.browseButtonView", container, options);
  21701. that.browseButton = that.locate("browseButton");
  21702. that.renderFreshMultiFileInput = function () {
  21703. var previousInput = that.locate("fileInputs").last();
  21704. previousInput.hide();
  21705. previousInput.attr("tabindex", -1);
  21706. var newInput = renderMultiFileInput(that);
  21707. previousInput.after(newInput);
  21708. };
  21709. that.enable = function () {
  21710. that.locate("fileInputs").prop("disabled", false);
  21711. };
  21712. that.disable = function () {
  21713. that.locate("fileInputs").prop("disabled", true);
  21714. };
  21715. that.isEnabled = function () {
  21716. return !that.locate("fileInputs").prop("disabled");
  21717. };
  21718. setupBrowseButtonView(that);
  21719. return that;
  21720. };
  21721. fluid.defaults("fluid.uploader.html5Strategy.browseButtonView", {
  21722. gradeNames: "fluid.viewComponent",
  21723. multiFileInputMarkup: "<input type='file' multiple='' class='flc-uploader-html5-input' />",
  21724. queueSettings: {},
  21725. selectors: {
  21726. browseButton: ".flc-uploader-button-browse",
  21727. fileInputs: ".flc-uploader-html5-input"
  21728. },
  21729. events: {
  21730. onBrowse: null,
  21731. onFilesQueued: null
  21732. }
  21733. });
  21734. fluid.demands("fluid.uploader.html5Strategy.browseButtonView", "fluid.uploader.html5Strategy.local", {
  21735. container: "{multiFileUploader}.container",
  21736. mergeOptions: {
  21737. events: {
  21738. onBrowse: "{local}.events.onFileDialog"
  21739. }
  21740. }
  21741. });
  21742. })(jQuery, fluid_1_5);/*
  21743. Copyright 2009 University of Toronto
  21744. Copyright 2009 University of California, Berkeley
  21745. Copyright 2010-2011 OCAD University
  21746. Copyright 2011 Lucendo Development Ltd.
  21747. Licensed under the Educational Community License (ECL), Version 2.0 or the New
  21748. BSD license. You may not use this file except in compliance with one these
  21749. Licenses.
  21750. You may obtain a copy of the ECL 2.0 License and BSD License at
  21751. https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt
  21752. */
  21753. // Declare dependencies
  21754. /*global fluid_1_5:true, jQuery*/
  21755. // JSLint options
  21756. /*jslint white: true, funcinvoke: true, undef: true, newcap: true, nomen: true, regexp: true, bitwise: true, browser: true, forin: true, maxerr: 100, indent: 4 */
  21757. var fluid_1_5 = fluid_1_5 || {};
  21758. (function ($, fluid) {
  21759. fluid.uploader = fluid.uploader || {};
  21760. var startUploading; // Define early due to subtle circular dependency.
  21761. var updateProgress = function (file, events, demoState, isUploading) {
  21762. if (!isUploading) {
  21763. return;
  21764. }
  21765. var chunk = Math.min(demoState.chunkSize, file.size);
  21766. demoState.bytesUploaded = Math.min(demoState.bytesUploaded + chunk, file.size);
  21767. events.onFileProgress.fire(file, demoState.bytesUploaded, file.size);
  21768. };
  21769. var finishAndContinueOrCleanup = function (that, file) {
  21770. that.queue.finishFile(file);
  21771. that.events.afterFileComplete.fire(file);
  21772. if (that.queue.shouldUploadNextFile()) {
  21773. startUploading(that);
  21774. } else {
  21775. that.events.afterUploadComplete.fire(that.queue.currentBatch.files);
  21776. if (file.status !== fluid.uploader.fileStatusConstants.CANCELLED) {
  21777. that.queue.clearCurrentBatch(); // Only clear the current batch if we're actually done the batch.
  21778. }
  21779. }
  21780. };
  21781. var finishUploading = function (that) {
  21782. if (!that.queue.isUploading) {
  21783. return;
  21784. }
  21785. var file = that.demoState.currentFile;
  21786. that.events.onFileSuccess.fire(file);
  21787. that.demoState.fileIdx++;
  21788. finishAndContinueOrCleanup(that, file);
  21789. };
  21790. var simulateUpload = function (that) {
  21791. if (!that.queue.isUploading) {
  21792. return;
  21793. }
  21794. var file = that.demoState.currentFile;
  21795. if (that.demoState.bytesUploaded < file.size) {
  21796. fluid.invokeAfterRandomDelay(function () {
  21797. updateProgress(file, that.events, that.demoState, that.queue.isUploading);
  21798. simulateUpload(that);
  21799. });
  21800. } else {
  21801. finishUploading(that);
  21802. }
  21803. };
  21804. startUploading = function (that) {
  21805. // Reset our upload stats for each new file.
  21806. that.demoState.currentFile = that.queue.files[that.demoState.fileIdx];
  21807. that.demoState.chunksForCurrentFile = Math.ceil(that.demoState.currentFile / that.demoState.chunkSize);
  21808. that.demoState.bytesUploaded = 0;
  21809. that.queue.isUploading = true;
  21810. that.events.onFileStart.fire(that.demoState.currentFile);
  21811. simulateUpload(that);
  21812. };
  21813. var stopDemo = function (that) {
  21814. var file = that.demoState.currentFile;
  21815. file.filestatus = fluid.uploader.fileStatusConstants.CANCELLED;
  21816. that.queue.shouldStop = true;
  21817. // In SWFUpload's world, pausing is a combinination of an UPLOAD_STOPPED error and a complete.
  21818. that.events.onFileError.fire(file,
  21819. fluid.uploader.errorConstants.UPLOAD_STOPPED,
  21820. "The demo upload was paused by the user.");
  21821. finishAndContinueOrCleanup(that, file);
  21822. that.events.onUploadStop.fire();
  21823. };
  21824. var setupDemo = function (that) {
  21825. if (that.simulateDelay === undefined || that.simulateDelay === null) {
  21826. that.simulateDelay = true;
  21827. }
  21828. // Initialize state for our upload simulation.
  21829. that.demoState = {
  21830. fileIdx: 0,
  21831. chunkSize: 200000
  21832. };
  21833. return that;
  21834. };
  21835. /**
  21836. * The demo remote pretends to upload files to the server, firing all the appropriate events
  21837. * but without sending anything over the network or requiring a server to be running.
  21838. *
  21839. * @param {FileQueue} queue the Uploader's file queue instance
  21840. * @param {Object} the Uploader's bundle of event firers
  21841. * @param {Object} configuration options
  21842. */
  21843. fluid.uploader.demoRemote = function (queue, options) {
  21844. var that = fluid.initLittleComponent("fluid.uploader.demoRemote", options);
  21845. that.queue = queue;
  21846. that.uploadNextFile = function () {
  21847. startUploading(that);
  21848. };
  21849. that.stop = function () {
  21850. stopDemo(that);
  21851. };
  21852. setupDemo(that);
  21853. return that;
  21854. };
  21855. /**
  21856. * Invokes a function after a random delay by using setTimeout.
  21857. * If the simulateDelay option is false, the function is invoked immediately.
  21858. * This is an odd function, but a potential candidate for central inclusion.
  21859. *
  21860. * @param {Function} fn the function to invoke
  21861. */
  21862. fluid.invokeAfterRandomDelay = function (fn) {
  21863. var delay = Math.floor(Math.random() * 1000 + 100);
  21864. setTimeout(fn, delay);
  21865. };
  21866. fluid.defaults("fluid.uploader.demoRemote", {
  21867. gradeNames: ["fluid.eventedComponent"],
  21868. argumentMap: {
  21869. options: 1
  21870. },
  21871. events: {
  21872. onFileProgress: "{multiFileUploader}.events.onFileProgress",
  21873. afterFileComplete: "{multiFileUploader}.events.afterFileComplete",
  21874. afterUploadComplete: "{multiFileUploader}.events.afterUploadComplete",
  21875. onFileSuccess: "{multiFileUploader}.events.onFileSuccess",
  21876. onFileStart: "{multiFileUploader}.events.onFileStart",
  21877. onFileError: "{multiFileUploader}.events.onFileError",
  21878. onUploadStop: "{multiFileUploader}.events.onUploadStop"
  21879. }
  21880. });
  21881. fluid.demands("fluid.uploader.remote", ["fluid.uploader.multiFileUploader", "fluid.uploader.demo"], {
  21882. funcName: "fluid.uploader.demoRemote",
  21883. args: [
  21884. "{multiFileUploader}.queue",
  21885. "{multiFileUploader}.events",
  21886. fluid.COMPONENT_OPTIONS
  21887. ]
  21888. });
  21889. })(jQuery, fluid_1_5);
  21890. /*
  21891. Copyright 2011 OCAD University
  21892. Licensed under the Educational Community License (ECL), Version 2.0 or the New
  21893. BSD license. You may not use this file except in compliance with one these
  21894. Licenses.
  21895. You may obtain a copy of the ECL 2.0 License and BSD License at
  21896. https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt
  21897. */
  21898. /*global fluid_1_5:true*/
  21899. // JSLint options
  21900. /*jslint white: true, funcinvoke: true, undef: true, newcap: true, nomen: true, regexp: true, bitwise: true, browser: true, forin: true, maxerr: 100, indent: 4 */
  21901. var fluid_1_5 = fluid_1_5 || {};
  21902. (function (fluid) {
  21903. fluid.uploader = fluid.uploader || {};
  21904. fluid.uploader.mimeTypeRegistry = {
  21905. // Images
  21906. jpg: "image/jpeg",
  21907. jpeg: "image/jpeg",
  21908. bmp: "image/bmp",
  21909. png: "image/png",
  21910. tif: "image/tiff",
  21911. tiff: "image/tiff",
  21912. // Audio
  21913. mp3: "audio/mpeg",
  21914. m4a: "audio/mp4a-latm",
  21915. ogg: "audio/ogg",
  21916. wav: "audio/x-wav",
  21917. aiff: "audio/x-aiff",
  21918. // Video
  21919. mpg: "video/mpeg",
  21920. mpeg: "video/mpeg",
  21921. m4v: "video/x-m4v",
  21922. ogv: "video/ogg",
  21923. mov: "video/quicktime",
  21924. avi: "video/x-msvideo",
  21925. // Text documents
  21926. html: "text/html",
  21927. htm: "text/html",
  21928. text: "text/plain",
  21929. // Office Docs.
  21930. doc: "application/msword",
  21931. docx: "application/msword",
  21932. xls: "application/vnd.ms-excel",
  21933. xlsx: "application/vnd.ms-excel",
  21934. ppt: "application/vnd.ms-powerpoint",
  21935. pptx: "application/vnd.ms-powerpoint"
  21936. };
  21937. })(fluid_1_5);