Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 
 
 
 

757 řádky
20 KiB

  1. /*!
  2. * jQuery UI Widget 1.13.1
  3. * http://jqueryui.com
  4. *
  5. * Copyright jQuery Foundation and other contributors
  6. * Released under the MIT license.
  7. * http://jquery.org/license
  8. */
  9. //>>label: Widget
  10. //>>group: Core
  11. //>>description: Provides a factory for creating stateful widgets with a common API.
  12. //>>docs: http://api.jqueryui.com/jQuery.widget/
  13. //>>demos: http://jqueryui.com/widget/
  14. ( function( factory ) {
  15. "use strict";
  16. if ( typeof define === "function" && define.amd ) {
  17. // AMD. Register as an anonymous module.
  18. define( [ "jquery", "./version" ], factory );
  19. } else {
  20. // Browser globals
  21. factory( jQuery );
  22. }
  23. } )( function( $ ) {
  24. "use strict";
  25. var widgetUuid = 0;
  26. var widgetHasOwnProperty = Array.prototype.hasOwnProperty;
  27. var widgetSlice = Array.prototype.slice;
  28. $.cleanData = ( function( orig ) {
  29. return function( elems ) {
  30. var events, elem, i;
  31. for ( i = 0; ( elem = elems[ i ] ) != null; i++ ) {
  32. // Only trigger remove when necessary to save time
  33. events = $._data( elem, "events" );
  34. if ( events && events.remove ) {
  35. $( elem ).triggerHandler( "remove" );
  36. }
  37. }
  38. orig( elems );
  39. };
  40. } )( $.cleanData );
  41. $.widget = function( name, base, prototype ) {
  42. var existingConstructor, constructor, basePrototype;
  43. // ProxiedPrototype allows the provided prototype to remain unmodified
  44. // so that it can be used as a mixin for multiple widgets (#8876)
  45. var proxiedPrototype = {};
  46. var namespace = name.split( "." )[ 0 ];
  47. name = name.split( "." )[ 1 ];
  48. var fullName = namespace + "-" + name;
  49. if ( !prototype ) {
  50. prototype = base;
  51. base = $.Widget;
  52. }
  53. if ( Array.isArray( prototype ) ) {
  54. prototype = $.extend.apply( null, [ {} ].concat( prototype ) );
  55. }
  56. // Create selector for plugin
  57. $.expr.pseudos[ fullName.toLowerCase() ] = function( elem ) {
  58. return !!$.data( elem, fullName );
  59. };
  60. $[ namespace ] = $[ namespace ] || {};
  61. existingConstructor = $[ namespace ][ name ];
  62. constructor = $[ namespace ][ name ] = function( options, element ) {
  63. // Allow instantiation without "new" keyword
  64. if ( !this || !this._createWidget ) {
  65. return new constructor( options, element );
  66. }
  67. // Allow instantiation without initializing for simple inheritance
  68. // must use "new" keyword (the code above always passes args)
  69. if ( arguments.length ) {
  70. this._createWidget( options, element );
  71. }
  72. };
  73. // Extend with the existing constructor to carry over any static properties
  74. $.extend( constructor, existingConstructor, {
  75. version: prototype.version,
  76. // Copy the object used to create the prototype in case we need to
  77. // redefine the widget later
  78. _proto: $.extend( {}, prototype ),
  79. // Track widgets that inherit from this widget in case this widget is
  80. // redefined after a widget inherits from it
  81. _childConstructors: []
  82. } );
  83. basePrototype = new base();
  84. // We need to make the options hash a property directly on the new instance
  85. // otherwise we'll modify the options hash on the prototype that we're
  86. // inheriting from
  87. basePrototype.options = $.widget.extend( {}, basePrototype.options );
  88. $.each( prototype, function( prop, value ) {
  89. if ( typeof value !== "function" ) {
  90. proxiedPrototype[ prop ] = value;
  91. return;
  92. }
  93. proxiedPrototype[ prop ] = ( function() {
  94. function _super() {
  95. return base.prototype[ prop ].apply( this, arguments );
  96. }
  97. function _superApply( args ) {
  98. return base.prototype[ prop ].apply( this, args );
  99. }
  100. return function() {
  101. var __super = this._super;
  102. var __superApply = this._superApply;
  103. var returnValue;
  104. this._super = _super;
  105. this._superApply = _superApply;
  106. returnValue = value.apply( this, arguments );
  107. this._super = __super;
  108. this._superApply = __superApply;
  109. return returnValue;
  110. };
  111. } )();
  112. } );
  113. constructor.prototype = $.widget.extend( basePrototype, {
  114. // TODO: remove support for widgetEventPrefix
  115. // always use the name + a colon as the prefix, e.g., draggable:start
  116. // don't prefix for widgets that aren't DOM-based
  117. widgetEventPrefix: existingConstructor ? ( basePrototype.widgetEventPrefix || name ) : name
  118. }, proxiedPrototype, {
  119. constructor: constructor,
  120. namespace: namespace,
  121. widgetName: name,
  122. widgetFullName: fullName
  123. } );
  124. // If this widget is being redefined then we need to find all widgets that
  125. // are inheriting from it and redefine all of them so that they inherit from
  126. // the new version of this widget. We're essentially trying to replace one
  127. // level in the prototype chain.
  128. if ( existingConstructor ) {
  129. $.each( existingConstructor._childConstructors, function( i, child ) {
  130. var childPrototype = child.prototype;
  131. // Redefine the child widget using the same prototype that was
  132. // originally used, but inherit from the new version of the base
  133. $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor,
  134. child._proto );
  135. } );
  136. // Remove the list of existing child constructors from the old constructor
  137. // so the old child constructors can be garbage collected
  138. delete existingConstructor._childConstructors;
  139. } else {
  140. base._childConstructors.push( constructor );
  141. }
  142. $.widget.bridge( name, constructor );
  143. return constructor;
  144. };
  145. $.widget.extend = function( target ) {
  146. var input = widgetSlice.call( arguments, 1 );
  147. var inputIndex = 0;
  148. var inputLength = input.length;
  149. var key;
  150. var value;
  151. for ( ; inputIndex < inputLength; inputIndex++ ) {
  152. for ( key in input[ inputIndex ] ) {
  153. value = input[ inputIndex ][ key ];
  154. if ( widgetHasOwnProperty.call( input[ inputIndex ], key ) && value !== undefined ) {
  155. // Clone objects
  156. if ( $.isPlainObject( value ) ) {
  157. target[ key ] = $.isPlainObject( target[ key ] ) ?
  158. $.widget.extend( {}, target[ key ], value ) :
  159. // Don't extend strings, arrays, etc. with objects
  160. $.widget.extend( {}, value );
  161. // Copy everything else by reference
  162. } else {
  163. target[ key ] = value;
  164. }
  165. }
  166. }
  167. }
  168. return target;
  169. };
  170. $.widget.bridge = function( name, object ) {
  171. var fullName = object.prototype.widgetFullName || name;
  172. $.fn[ name ] = function( options ) {
  173. var isMethodCall = typeof options === "string";
  174. var args = widgetSlice.call( arguments, 1 );
  175. var returnValue = this;
  176. if ( isMethodCall ) {
  177. // If this is an empty collection, we need to have the instance method
  178. // return undefined instead of the jQuery instance
  179. if ( !this.length && options === "instance" ) {
  180. returnValue = undefined;
  181. } else {
  182. this.each( function() {
  183. var methodValue;
  184. var instance = $.data( this, fullName );
  185. if ( options === "instance" ) {
  186. returnValue = instance;
  187. return false;
  188. }
  189. if ( !instance ) {
  190. return $.error( "cannot call methods on " + name +
  191. " prior to initialization; " +
  192. "attempted to call method '" + options + "'" );
  193. }
  194. if ( typeof instance[ options ] !== "function" ||
  195. options.charAt( 0 ) === "_" ) {
  196. return $.error( "no such method '" + options + "' for " + name +
  197. " widget instance" );
  198. }
  199. methodValue = instance[ options ].apply( instance, args );
  200. if ( methodValue !== instance && methodValue !== undefined ) {
  201. returnValue = methodValue && methodValue.jquery ?
  202. returnValue.pushStack( methodValue.get() ) :
  203. methodValue;
  204. return false;
  205. }
  206. } );
  207. }
  208. } else {
  209. // Allow multiple hashes to be passed on init
  210. if ( args.length ) {
  211. options = $.widget.extend.apply( null, [ options ].concat( args ) );
  212. }
  213. this.each( function() {
  214. var instance = $.data( this, fullName );
  215. if ( instance ) {
  216. instance.option( options || {} );
  217. if ( instance._init ) {
  218. instance._init();
  219. }
  220. } else {
  221. $.data( this, fullName, new object( options, this ) );
  222. }
  223. } );
  224. }
  225. return returnValue;
  226. };
  227. };
  228. $.Widget = function( /* options, element */ ) {};
  229. $.Widget._childConstructors = [];
  230. $.Widget.prototype = {
  231. widgetName: "widget",
  232. widgetEventPrefix: "",
  233. defaultElement: "<div>",
  234. options: {
  235. classes: {},
  236. disabled: false,
  237. // Callbacks
  238. create: null
  239. },
  240. _createWidget: function( options, element ) {
  241. element = $( element || this.defaultElement || this )[ 0 ];
  242. this.element = $( element );
  243. this.uuid = widgetUuid++;
  244. this.eventNamespace = "." + this.widgetName + this.uuid;
  245. this.bindings = $();
  246. this.hoverable = $();
  247. this.focusable = $();
  248. this.classesElementLookup = {};
  249. if ( element !== this ) {
  250. $.data( element, this.widgetFullName, this );
  251. this._on( true, this.element, {
  252. remove: function( event ) {
  253. if ( event.target === element ) {
  254. this.destroy();
  255. }
  256. }
  257. } );
  258. this.document = $( element.style ?
  259. // Element within the document
  260. element.ownerDocument :
  261. // Element is window or document
  262. element.document || element );
  263. this.window = $( this.document[ 0 ].defaultView || this.document[ 0 ].parentWindow );
  264. }
  265. this.options = $.widget.extend( {},
  266. this.options,
  267. this._getCreateOptions(),
  268. options );
  269. this._create();
  270. if ( this.options.disabled ) {
  271. this._setOptionDisabled( this.options.disabled );
  272. }
  273. this._trigger( "create", null, this._getCreateEventData() );
  274. this._init();
  275. },
  276. _getCreateOptions: function() {
  277. return {};
  278. },
  279. _getCreateEventData: $.noop,
  280. _create: $.noop,
  281. _init: $.noop,
  282. destroy: function() {
  283. var that = this;
  284. this._destroy();
  285. $.each( this.classesElementLookup, function( key, value ) {
  286. that._removeClass( value, key );
  287. } );
  288. // We can probably remove the unbind calls in 2.0
  289. // all event bindings should go through this._on()
  290. this.element
  291. .off( this.eventNamespace )
  292. .removeData( this.widgetFullName );
  293. this.widget()
  294. .off( this.eventNamespace )
  295. .removeAttr( "aria-disabled" );
  296. // Clean up events and states
  297. this.bindings.off( this.eventNamespace );
  298. },
  299. _destroy: $.noop,
  300. widget: function() {
  301. return this.element;
  302. },
  303. option: function( key, value ) {
  304. var options = key;
  305. var parts;
  306. var curOption;
  307. var i;
  308. if ( arguments.length === 0 ) {
  309. // Don't return a reference to the internal hash
  310. return $.widget.extend( {}, this.options );
  311. }
  312. if ( typeof key === "string" ) {
  313. // Handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
  314. options = {};
  315. parts = key.split( "." );
  316. key = parts.shift();
  317. if ( parts.length ) {
  318. curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
  319. for ( i = 0; i < parts.length - 1; i++ ) {
  320. curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
  321. curOption = curOption[ parts[ i ] ];
  322. }
  323. key = parts.pop();
  324. if ( arguments.length === 1 ) {
  325. return curOption[ key ] === undefined ? null : curOption[ key ];
  326. }
  327. curOption[ key ] = value;
  328. } else {
  329. if ( arguments.length === 1 ) {
  330. return this.options[ key ] === undefined ? null : this.options[ key ];
  331. }
  332. options[ key ] = value;
  333. }
  334. }
  335. this._setOptions( options );
  336. return this;
  337. },
  338. _setOptions: function( options ) {
  339. var key;
  340. for ( key in options ) {
  341. this._setOption( key, options[ key ] );
  342. }
  343. return this;
  344. },
  345. _setOption: function( key, value ) {
  346. if ( key === "classes" ) {
  347. this._setOptionClasses( value );
  348. }
  349. this.options[ key ] = value;
  350. if ( key === "disabled" ) {
  351. this._setOptionDisabled( value );
  352. }
  353. return this;
  354. },
  355. _setOptionClasses: function( value ) {
  356. var classKey, elements, currentElements;
  357. for ( classKey in value ) {
  358. currentElements = this.classesElementLookup[ classKey ];
  359. if ( value[ classKey ] === this.options.classes[ classKey ] ||
  360. !currentElements ||
  361. !currentElements.length ) {
  362. continue;
  363. }
  364. // We are doing this to create a new jQuery object because the _removeClass() call
  365. // on the next line is going to destroy the reference to the current elements being
  366. // tracked. We need to save a copy of this collection so that we can add the new classes
  367. // below.
  368. elements = $( currentElements.get() );
  369. this._removeClass( currentElements, classKey );
  370. // We don't use _addClass() here, because that uses this.options.classes
  371. // for generating the string of classes. We want to use the value passed in from
  372. // _setOption(), this is the new value of the classes option which was passed to
  373. // _setOption(). We pass this value directly to _classes().
  374. elements.addClass( this._classes( {
  375. element: elements,
  376. keys: classKey,
  377. classes: value,
  378. add: true
  379. } ) );
  380. }
  381. },
  382. _setOptionDisabled: function( value ) {
  383. this._toggleClass( this.widget(), this.widgetFullName + "-disabled", null, !!value );
  384. // If the widget is becoming disabled, then nothing is interactive
  385. if ( value ) {
  386. this._removeClass( this.hoverable, null, "ui-state-hover" );
  387. this._removeClass( this.focusable, null, "ui-state-focus" );
  388. }
  389. },
  390. enable: function() {
  391. return this._setOptions( { disabled: false } );
  392. },
  393. disable: function() {
  394. return this._setOptions( { disabled: true } );
  395. },
  396. _classes: function( options ) {
  397. var full = [];
  398. var that = this;
  399. options = $.extend( {
  400. element: this.element,
  401. classes: this.options.classes || {}
  402. }, options );
  403. function bindRemoveEvent() {
  404. var nodesToBind = [];
  405. options.element.each( function( _, element ) {
  406. var isTracked = $.map( that.classesElementLookup, function( elements ) {
  407. return elements;
  408. } )
  409. .some( function( elements ) {
  410. return elements.is( element );
  411. } );
  412. if ( !isTracked ) {
  413. nodesToBind.push( element );
  414. }
  415. } );
  416. that._on( $( nodesToBind ), {
  417. remove: "_untrackClassesElement"
  418. } );
  419. }
  420. function processClassString( classes, checkOption ) {
  421. var current, i;
  422. for ( i = 0; i < classes.length; i++ ) {
  423. current = that.classesElementLookup[ classes[ i ] ] || $();
  424. if ( options.add ) {
  425. bindRemoveEvent();
  426. current = $( $.uniqueSort( current.get().concat( options.element.get() ) ) );
  427. } else {
  428. current = $( current.not( options.element ).get() );
  429. }
  430. that.classesElementLookup[ classes[ i ] ] = current;
  431. full.push( classes[ i ] );
  432. if ( checkOption && options.classes[ classes[ i ] ] ) {
  433. full.push( options.classes[ classes[ i ] ] );
  434. }
  435. }
  436. }
  437. if ( options.keys ) {
  438. processClassString( options.keys.match( /\S+/g ) || [], true );
  439. }
  440. if ( options.extra ) {
  441. processClassString( options.extra.match( /\S+/g ) || [] );
  442. }
  443. return full.join( " " );
  444. },
  445. _untrackClassesElement: function( event ) {
  446. var that = this;
  447. $.each( that.classesElementLookup, function( key, value ) {
  448. if ( $.inArray( event.target, value ) !== -1 ) {
  449. that.classesElementLookup[ key ] = $( value.not( event.target ).get() );
  450. }
  451. } );
  452. this._off( $( event.target ) );
  453. },
  454. _removeClass: function( element, keys, extra ) {
  455. return this._toggleClass( element, keys, extra, false );
  456. },
  457. _addClass: function( element, keys, extra ) {
  458. return this._toggleClass( element, keys, extra, true );
  459. },
  460. _toggleClass: function( element, keys, extra, add ) {
  461. add = ( typeof add === "boolean" ) ? add : extra;
  462. var shift = ( typeof element === "string" || element === null ),
  463. options = {
  464. extra: shift ? keys : extra,
  465. keys: shift ? element : keys,
  466. element: shift ? this.element : element,
  467. add: add
  468. };
  469. options.element.toggleClass( this._classes( options ), add );
  470. return this;
  471. },
  472. _on: function( suppressDisabledCheck, element, handlers ) {
  473. var delegateElement;
  474. var instance = this;
  475. // No suppressDisabledCheck flag, shuffle arguments
  476. if ( typeof suppressDisabledCheck !== "boolean" ) {
  477. handlers = element;
  478. element = suppressDisabledCheck;
  479. suppressDisabledCheck = false;
  480. }
  481. // No element argument, shuffle and use this.element
  482. if ( !handlers ) {
  483. handlers = element;
  484. element = this.element;
  485. delegateElement = this.widget();
  486. } else {
  487. element = delegateElement = $( element );
  488. this.bindings = this.bindings.add( element );
  489. }
  490. $.each( handlers, function( event, handler ) {
  491. function handlerProxy() {
  492. // Allow widgets to customize the disabled handling
  493. // - disabled as an array instead of boolean
  494. // - disabled class as method for disabling individual parts
  495. if ( !suppressDisabledCheck &&
  496. ( instance.options.disabled === true ||
  497. $( this ).hasClass( "ui-state-disabled" ) ) ) {
  498. return;
  499. }
  500. return ( typeof handler === "string" ? instance[ handler ] : handler )
  501. .apply( instance, arguments );
  502. }
  503. // Copy the guid so direct unbinding works
  504. if ( typeof handler !== "string" ) {
  505. handlerProxy.guid = handler.guid =
  506. handler.guid || handlerProxy.guid || $.guid++;
  507. }
  508. var match = event.match( /^([\w:-]*)\s*(.*)$/ );
  509. var eventName = match[ 1 ] + instance.eventNamespace;
  510. var selector = match[ 2 ];
  511. if ( selector ) {
  512. delegateElement.on( eventName, selector, handlerProxy );
  513. } else {
  514. element.on( eventName, handlerProxy );
  515. }
  516. } );
  517. },
  518. _off: function( element, eventName ) {
  519. eventName = ( eventName || "" ).split( " " ).join( this.eventNamespace + " " ) +
  520. this.eventNamespace;
  521. element.off( eventName );
  522. // Clear the stack to avoid memory leaks (#10056)
  523. this.bindings = $( this.bindings.not( element ).get() );
  524. this.focusable = $( this.focusable.not( element ).get() );
  525. this.hoverable = $( this.hoverable.not( element ).get() );
  526. },
  527. _delay: function( handler, delay ) {
  528. function handlerProxy() {
  529. return ( typeof handler === "string" ? instance[ handler ] : handler )
  530. .apply( instance, arguments );
  531. }
  532. var instance = this;
  533. return setTimeout( handlerProxy, delay || 0 );
  534. },
  535. _hoverable: function( element ) {
  536. this.hoverable = this.hoverable.add( element );
  537. this._on( element, {
  538. mouseenter: function( event ) {
  539. this._addClass( $( event.currentTarget ), null, "ui-state-hover" );
  540. },
  541. mouseleave: function( event ) {
  542. this._removeClass( $( event.currentTarget ), null, "ui-state-hover" );
  543. }
  544. } );
  545. },
  546. _focusable: function( element ) {
  547. this.focusable = this.focusable.add( element );
  548. this._on( element, {
  549. focusin: function( event ) {
  550. this._addClass( $( event.currentTarget ), null, "ui-state-focus" );
  551. },
  552. focusout: function( event ) {
  553. this._removeClass( $( event.currentTarget ), null, "ui-state-focus" );
  554. }
  555. } );
  556. },
  557. _trigger: function( type, event, data ) {
  558. var prop, orig;
  559. var callback = this.options[ type ];
  560. data = data || {};
  561. event = $.Event( event );
  562. event.type = ( type === this.widgetEventPrefix ?
  563. type :
  564. this.widgetEventPrefix + type ).toLowerCase();
  565. // The original event may come from any element
  566. // so we need to reset the target on the new event
  567. event.target = this.element[ 0 ];
  568. // Copy original event properties over to the new event
  569. orig = event.originalEvent;
  570. if ( orig ) {
  571. for ( prop in orig ) {
  572. if ( !( prop in event ) ) {
  573. event[ prop ] = orig[ prop ];
  574. }
  575. }
  576. }
  577. this.element.trigger( event, data );
  578. return !( typeof callback === "function" &&
  579. callback.apply( this.element[ 0 ], [ event ].concat( data ) ) === false ||
  580. event.isDefaultPrevented() );
  581. }
  582. };
  583. $.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
  584. $.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
  585. if ( typeof options === "string" ) {
  586. options = { effect: options };
  587. }
  588. var hasOptions;
  589. var effectName = !options ?
  590. method :
  591. options === true || typeof options === "number" ?
  592. defaultEffect :
  593. options.effect || defaultEffect;
  594. options = options || {};
  595. if ( typeof options === "number" ) {
  596. options = { duration: options };
  597. } else if ( options === true ) {
  598. options = {};
  599. }
  600. hasOptions = !$.isEmptyObject( options );
  601. options.complete = callback;
  602. if ( options.delay ) {
  603. element.delay( options.delay );
  604. }
  605. if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {
  606. element[ method ]( options );
  607. } else if ( effectName !== method && element[ effectName ] ) {
  608. element[ effectName ]( options.duration, options.easing, callback );
  609. } else {
  610. element.queue( function( next ) {
  611. $( this )[ method ]();
  612. if ( callback ) {
  613. callback.call( element[ 0 ] );
  614. }
  615. next();
  616. } );
  617. }
  618. };
  619. } );
  620. return $.widget;
  621. } );