jquery.ui.widget.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. /*
  2. * https://github.com/blueimp/jQuery-File-Upload
  3. *
  4. * Copyright 2013 jQuery Foundation and other contributors
  5. * Released under the MIT license.
  6. * http://jquery.org/license
  7. *
  8. * http://api.jqueryui.com/jQuery.widget/
  9. */
  10. (function (factory) {
  11. if (typeof define === "function" && define.amd) {
  12. // Register as an anonymous AMD module:
  13. define(["jquery"], factory);
  14. } else {
  15. // Browser globals:
  16. factory(jQuery);
  17. }
  18. }(function( $, undefined ) {
  19. var uuid = 0,
  20. slice = Array.prototype.slice,
  21. _cleanData = $.cleanData;
  22. $.cleanData = function( elems ) {
  23. for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
  24. try {
  25. $( elem ).triggerHandler( "remove" );
  26. // http://bugs.jquery.com/ticket/8235
  27. } catch( e ) {}
  28. }
  29. _cleanData( elems );
  30. };
  31. $.widget = function( name, base, prototype ) {
  32. var fullName, existingConstructor, constructor, basePrototype,
  33. // proxiedPrototype allows the provided prototype to remain unmodified
  34. // so that it can be used as a mixin for multiple widgets (#8876)
  35. proxiedPrototype = {},
  36. namespace = name.split( "." )[ 0 ];
  37. name = name.split( "." )[ 1 ];
  38. fullName = namespace + "-" + name;
  39. if ( !prototype ) {
  40. prototype = base;
  41. base = $.Widget;
  42. }
  43. // create selector for plugin
  44. $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) {
  45. return !!$.data( elem, fullName );
  46. };
  47. $[ namespace ] = $[ namespace ] || {};
  48. existingConstructor = $[ namespace ][ name ];
  49. constructor = $[ namespace ][ name ] = function( options, element ) {
  50. // allow instantiation without "new" keyword
  51. if ( !this._createWidget ) {
  52. return new constructor( options, element );
  53. }
  54. // allow instantiation without initializing for simple inheritance
  55. // must use "new" keyword (the code above always passes args)
  56. if ( arguments.length ) {
  57. this._createWidget( options, element );
  58. }
  59. };
  60. // extend with the existing constructor to carry over any static properties
  61. $.extend( constructor, existingConstructor, {
  62. version: prototype.version,
  63. // copy the object used to create the prototype in case we need to
  64. // redefine the widget later
  65. _proto: $.extend( {}, prototype ),
  66. // track widgets that inherit from this widget in case this widget is
  67. // redefined after a widget inherits from it
  68. _childConstructors: []
  69. });
  70. basePrototype = new base();
  71. // we need to make the options hash a property directly on the new instance
  72. // otherwise we'll modify the options hash on the prototype that we're
  73. // inheriting from
  74. basePrototype.options = $.widget.extend( {}, basePrototype.options );
  75. $.each( prototype, function( prop, value ) {
  76. if ( !$.isFunction( value ) ) {
  77. proxiedPrototype[ prop ] = value;
  78. return;
  79. }
  80. proxiedPrototype[ prop ] = (function() {
  81. var _super = function() {
  82. return base.prototype[ prop ].apply( this, arguments );
  83. },
  84. _superApply = function( args ) {
  85. return base.prototype[ prop ].apply( this, args );
  86. };
  87. return function() {
  88. var __super = this._super,
  89. __superApply = this._superApply,
  90. returnValue;
  91. this._super = _super;
  92. this._superApply = _superApply;
  93. returnValue = value.apply( this, arguments );
  94. this._super = __super;
  95. this._superApply = __superApply;
  96. return returnValue;
  97. };
  98. })();
  99. });
  100. constructor.prototype = $.widget.extend( basePrototype, {
  101. // TODO: remove support for widgetEventPrefix
  102. // always use the name + a colon as the prefix, e.g., draggable:start
  103. // don't prefix for widgets that aren't DOM-based
  104. widgetEventPrefix: existingConstructor ? basePrototype.widgetEventPrefix : name
  105. }, proxiedPrototype, {
  106. constructor: constructor,
  107. namespace: namespace,
  108. widgetName: name,
  109. widgetFullName: fullName
  110. });
  111. // If this widget is being redefined then we need to find all widgets that
  112. // are inheriting from it and redefine all of them so that they inherit from
  113. // the new version of this widget. We're essentially trying to replace one
  114. // level in the prototype chain.
  115. if ( existingConstructor ) {
  116. $.each( existingConstructor._childConstructors, function( i, child ) {
  117. var childPrototype = child.prototype;
  118. // redefine the child widget using the same prototype that was
  119. // originally used, but inherit from the new version of the base
  120. $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto );
  121. });
  122. // remove the list of existing child constructors from the old constructor
  123. // so the old child constructors can be garbage collected
  124. delete existingConstructor._childConstructors;
  125. } else {
  126. base._childConstructors.push( constructor );
  127. }
  128. $.widget.bridge( name, constructor );
  129. };
  130. $.widget.extend = function( target ) {
  131. var input = slice.call( arguments, 1 ),
  132. inputIndex = 0,
  133. inputLength = input.length,
  134. key,
  135. value;
  136. for ( ; inputIndex < inputLength; inputIndex++ ) {
  137. for ( key in input[ inputIndex ] ) {
  138. value = input[ inputIndex ][ key ];
  139. if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {
  140. // Clone objects
  141. if ( $.isPlainObject( value ) ) {
  142. target[ key ] = $.isPlainObject( target[ key ] ) ?
  143. $.widget.extend( {}, target[ key ], value ) :
  144. // Don't extend strings, arrays, etc. with objects
  145. $.widget.extend( {}, value );
  146. // Copy everything else by reference
  147. } else {
  148. target[ key ] = value;
  149. }
  150. }
  151. }
  152. }
  153. return target;
  154. };
  155. $.widget.bridge = function( name, object ) {
  156. var fullName = object.prototype.widgetFullName || name;
  157. $.fn[ name ] = function( options ) {
  158. var isMethodCall = typeof options === "string",
  159. args = slice.call( arguments, 1 ),
  160. returnValue = this;
  161. // allow multiple hashes to be passed on init
  162. options = !isMethodCall && args.length ?
  163. $.widget.extend.apply( null, [ options ].concat(args) ) :
  164. options;
  165. if ( isMethodCall ) {
  166. this.each(function() {
  167. var methodValue,
  168. instance = $.data( this, fullName );
  169. if ( !instance ) {
  170. return $.error( "cannot call methods on " + name + " prior to initialization; " +
  171. "attempted to call method '" + options + "'" );
  172. }
  173. if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) {
  174. return $.error( "no such method '" + options + "' for " + name + " widget instance" );
  175. }
  176. methodValue = instance[ options ].apply( instance, args );
  177. if ( methodValue !== instance && methodValue !== undefined ) {
  178. returnValue = methodValue && methodValue.jquery ?
  179. returnValue.pushStack( methodValue.get() ) :
  180. methodValue;
  181. return false;
  182. }
  183. });
  184. } else {
  185. this.each(function() {
  186. var instance = $.data( this, fullName );
  187. if ( instance ) {
  188. instance.option( options || {} )._init();
  189. } else {
  190. $.data( this, fullName, new object( options, this ) );
  191. }
  192. });
  193. }
  194. return returnValue;
  195. };
  196. };
  197. $.Widget = function( /* options, element */ ) {};
  198. $.Widget._childConstructors = [];
  199. $.Widget.prototype = {
  200. widgetName: "widget",
  201. widgetEventPrefix: "",
  202. defaultElement: "<div>",
  203. options: {
  204. disabled: false,
  205. // callbacks
  206. create: null
  207. },
  208. _createWidget: function( options, element ) {
  209. element = $( element || this.defaultElement || this )[ 0 ];
  210. this.element = $( element );
  211. this.uuid = uuid++;
  212. this.eventNamespace = "." + this.widgetName + this.uuid;
  213. this.options = $.widget.extend( {},
  214. this.options,
  215. this._getCreateOptions(),
  216. options );
  217. this.bindings = $();
  218. this.hoverable = $();
  219. this.focusable = $();
  220. if ( element !== this ) {
  221. $.data( element, this.widgetFullName, this );
  222. this._on( true, this.element, {
  223. remove: function( event ) {
  224. if ( event.target === element ) {
  225. this.destroy();
  226. }
  227. }
  228. });
  229. this.document = $( element.style ?
  230. // element within the document
  231. element.ownerDocument :
  232. // element is window or document
  233. element.document || element );
  234. this.window = $( this.document[0].defaultView || this.document[0].parentWindow );
  235. }
  236. this._create();
  237. this._trigger( "create", null, this._getCreateEventData() );
  238. this._init();
  239. },
  240. _getCreateOptions: $.noop,
  241. _getCreateEventData: $.noop,
  242. _create: $.noop,
  243. _init: $.noop,
  244. destroy: function() {
  245. this._destroy();
  246. // we can probably remove the unbind calls in 2.0
  247. // all event bindings should go through this._on()
  248. this.element
  249. .unbind( this.eventNamespace )
  250. // 1.9 BC for #7810
  251. // TODO remove dual storage
  252. .removeData( this.widgetName )
  253. .removeData( this.widgetFullName )
  254. // support: jquery <1.6.3
  255. // http://bugs.jquery.com/ticket/9413
  256. .removeData( $.camelCase( this.widgetFullName ) );
  257. this.widget()
  258. .unbind( this.eventNamespace )
  259. .removeAttr( "aria-disabled" )
  260. .removeClass(
  261. this.widgetFullName + "-disabled " +
  262. "ui-state-disabled" );
  263. // clean up events and states
  264. this.bindings.unbind( this.eventNamespace );
  265. this.hoverable.removeClass( "ui-state-hover" );
  266. this.focusable.removeClass( "ui-state-focus" );
  267. },
  268. _destroy: $.noop,
  269. widget: function() {
  270. return this.element;
  271. },
  272. option: function( key, value ) {
  273. var options = key,
  274. parts,
  275. curOption,
  276. i;
  277. if ( arguments.length === 0 ) {
  278. // don't return a reference to the internal hash
  279. return $.widget.extend( {}, this.options );
  280. }
  281. if ( typeof key === "string" ) {
  282. // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
  283. options = {};
  284. parts = key.split( "." );
  285. key = parts.shift();
  286. if ( parts.length ) {
  287. curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
  288. for ( i = 0; i < parts.length - 1; i++ ) {
  289. curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
  290. curOption = curOption[ parts[ i ] ];
  291. }
  292. key = parts.pop();
  293. if ( value === undefined ) {
  294. return curOption[ key ] === undefined ? null : curOption[ key ];
  295. }
  296. curOption[ key ] = value;
  297. } else {
  298. if ( value === undefined ) {
  299. return this.options[ key ] === undefined ? null : this.options[ key ];
  300. }
  301. options[ key ] = value;
  302. }
  303. }
  304. this._setOptions( options );
  305. return this;
  306. },
  307. _setOptions: function( options ) {
  308. var key;
  309. for ( key in options ) {
  310. this._setOption( key, options[ key ] );
  311. }
  312. return this;
  313. },
  314. _setOption: function( key, value ) {
  315. this.options[ key ] = value;
  316. if ( key === "disabled" ) {
  317. this.widget()
  318. .toggleClass( this.widgetFullName + "-disabled ui-state-disabled", !!value )
  319. .attr( "aria-disabled", value );
  320. this.hoverable.removeClass( "ui-state-hover" );
  321. this.focusable.removeClass( "ui-state-focus" );
  322. }
  323. return this;
  324. },
  325. enable: function() {
  326. return this._setOption( "disabled", false );
  327. },
  328. disable: function() {
  329. return this._setOption( "disabled", true );
  330. },
  331. _on: function( suppressDisabledCheck, element, handlers ) {
  332. var delegateElement,
  333. instance = this;
  334. // no suppressDisabledCheck flag, shuffle arguments
  335. if ( typeof suppressDisabledCheck !== "boolean" ) {
  336. handlers = element;
  337. element = suppressDisabledCheck;
  338. suppressDisabledCheck = false;
  339. }
  340. // no element argument, shuffle and use this.element
  341. if ( !handlers ) {
  342. handlers = element;
  343. element = this.element;
  344. delegateElement = this.widget();
  345. } else {
  346. // accept selectors, DOM elements
  347. element = delegateElement = $( element );
  348. this.bindings = this.bindings.add( element );
  349. }
  350. $.each( handlers, function( event, handler ) {
  351. function handlerProxy() {
  352. // allow widgets to customize the disabled handling
  353. // - disabled as an array instead of boolean
  354. // - disabled class as method for disabling individual parts
  355. if ( !suppressDisabledCheck &&
  356. ( instance.options.disabled === true ||
  357. $( this ).hasClass( "ui-state-disabled" ) ) ) {
  358. return;
  359. }
  360. return ( typeof handler === "string" ? instance[ handler ] : handler )
  361. .apply( instance, arguments );
  362. }
  363. // copy the guid so direct unbinding works
  364. if ( typeof handler !== "string" ) {
  365. handlerProxy.guid = handler.guid =
  366. handler.guid || handlerProxy.guid || $.guid++;
  367. }
  368. var match = event.match( /^(\w+)\s*(.*)$/ ),
  369. eventName = match[1] + instance.eventNamespace,
  370. selector = match[2];
  371. if ( selector ) {
  372. delegateElement.delegate( selector, eventName, handlerProxy );
  373. } else {
  374. element.bind( eventName, handlerProxy );
  375. }
  376. });
  377. },
  378. _off: function( element, eventName ) {
  379. eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + this.eventNamespace;
  380. element.unbind( eventName ).undelegate( eventName );
  381. },
  382. _delay: function( handler, delay ) {
  383. function handlerProxy() {
  384. return ( typeof handler === "string" ? instance[ handler ] : handler )
  385. .apply( instance, arguments );
  386. }
  387. var instance = this;
  388. return setTimeout( handlerProxy, delay || 0 );
  389. },
  390. _hoverable: function( element ) {
  391. this.hoverable = this.hoverable.add( element );
  392. this._on( element, {
  393. mouseenter: function( event ) {
  394. $( event.currentTarget ).addClass( "ui-state-hover" );
  395. },
  396. mouseleave: function( event ) {
  397. $( event.currentTarget ).removeClass( "ui-state-hover" );
  398. }
  399. });
  400. },
  401. _focusable: function( element ) {
  402. this.focusable = this.focusable.add( element );
  403. this._on( element, {
  404. focusin: function( event ) {
  405. $( event.currentTarget ).addClass( "ui-state-focus" );
  406. },
  407. focusout: function( event ) {
  408. $( event.currentTarget ).removeClass( "ui-state-focus" );
  409. }
  410. });
  411. },
  412. _trigger: function( type, event, data ) {
  413. var prop, orig,
  414. callback = this.options[ type ];
  415. data = data || {};
  416. event = $.Event( event );
  417. event.type = ( type === this.widgetEventPrefix ?
  418. type :
  419. this.widgetEventPrefix + type ).toLowerCase();
  420. // the original event may come from any element
  421. // so we need to reset the target on the new event
  422. event.target = this.element[ 0 ];
  423. // copy original event properties over to the new event
  424. orig = event.originalEvent;
  425. if ( orig ) {
  426. for ( prop in orig ) {
  427. if ( !( prop in event ) ) {
  428. event[ prop ] = orig[ prop ];
  429. }
  430. }
  431. }
  432. this.element.trigger( event, data );
  433. return !( $.isFunction( callback ) &&
  434. callback.apply( this.element[0], [ event ].concat( data ) ) === false ||
  435. event.isDefaultPrevented() );
  436. }
  437. };
  438. $.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
  439. $.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
  440. if ( typeof options === "string" ) {
  441. options = { effect: options };
  442. }
  443. var hasOptions,
  444. effectName = !options ?
  445. method :
  446. options === true || typeof options === "number" ?
  447. defaultEffect :
  448. options.effect || defaultEffect;
  449. options = options || {};
  450. if ( typeof options === "number" ) {
  451. options = { duration: options };
  452. }
  453. hasOptions = !$.isEmptyObject( options );
  454. options.complete = callback;
  455. if ( options.delay ) {
  456. element.delay( options.delay );
  457. }
  458. if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {
  459. element[ method ]( options );
  460. } else if ( effectName !== method && element[ effectName ] ) {
  461. element[ effectName ]( options.duration, options.easing, callback );
  462. } else {
  463. element.queue(function( next ) {
  464. $( this )[ method ]();
  465. if ( callback ) {
  466. callback.call( element[ 0 ] );
  467. }
  468. next();
  469. });
  470. }
  471. };
  472. });
  473. }));