resources/lib/jquery-ui/ui/widget.js   F
last analyzed

Complexity

Total Complexity 147
Complexity/F 2.67

Size

Lines of Code 718
Function Count 55

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 0
nc 0
dl 0
loc 718
rs 2.1818
c 0
b 0
f 0
wmc 147
mnd 5
bc 125
fnc 55
bpm 2.2727
cpm 2.6727
noi 5

1 Function

Rating   Name   Duplication   Size   Complexity  
B widget.js ➔ ?!? 0 708 1

How to fix   Complexity   

Complexity

Complex classes like resources/lib/jquery-ui/ui/widget.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/*!
2
 * jQuery UI Widget 1.12.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
10
//>>label: Widget
11
//>>group: Core
12
//>>description: Provides a factory for creating stateful widgets with a common API.
13
//>>docs: http://api.jqueryui.com/jQuery.widget/
14
//>>demos: http://jqueryui.com/widget/
15
16
( function( factory ) {
17
	if ( typeof define === "function" && define.amd ) {
18
19
		// AMD. Register as an anonymous module.
20
		define( [ "jquery", "./version" ], factory );
21
	} else {
22
23
		// Browser globals
24
		factory( jQuery );
25
	}
26
}( function( $ ) {
27
28
var widgetUuid = 0;
29
var widgetSlice = Array.prototype.slice;
30
31
$.cleanData = ( function( orig ) {
32
	return function( elems ) {
33
		var events, elem, i;
34
		for ( i = 0; ( elem = elems[ i ] ) != null; i++ ) {
35
			try {
36
37
				// Only trigger remove when necessary to save time
38
				events = $._data( elem, "events" );
39
				if ( events && events.remove ) {
40
					$( elem ).triggerHandler( "remove" );
41
				}
42
43
			// Http://bugs.jquery.com/ticket/8235
44
			} catch ( e ) {}
45
		}
46
		orig( elems );
47
	};
48
} )( $.cleanData );
49
50
$.widget = function( name, base, prototype ) {
51
	var existingConstructor, constructor, basePrototype;
52
53
	// ProxiedPrototype allows the provided prototype to remain unmodified
54
	// so that it can be used as a mixin for multiple widgets (#8876)
55
	var proxiedPrototype = {};
56
57
	var namespace = name.split( "." )[ 0 ];
58
	name = name.split( "." )[ 1 ];
59
	var fullName = namespace + "-" + name;
60
61
	if ( !prototype ) {
62
		prototype = base;
63
		base = $.Widget;
64
	}
65
66
	if ( $.isArray( prototype ) ) {
67
		prototype = $.extend.apply( null, [ {} ].concat( prototype ) );
68
	}
69
70
	// Create selector for plugin
71
	$.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) {
72
		return !!$.data( elem, fullName );
73
	};
74
75
	$[ namespace ] = $[ namespace ] || {};
76
	existingConstructor = $[ namespace ][ name ];
77
	constructor = $[ namespace ][ name ] = function( options, element ) {
78
79
		// Allow instantiation without "new" keyword
80
		if ( !this._createWidget ) {
81
			return new constructor( options, element );
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like constructor should be capitalized.
Loading history...
82
		}
83
84
		// Allow instantiation without initializing for simple inheritance
85
		// must use "new" keyword (the code above always passes args)
86
		if ( arguments.length ) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if arguments.length is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
87
			this._createWidget( options, element );
88
		}
89
	};
90
91
	// Extend with the existing constructor to carry over any static properties
92
	$.extend( constructor, existingConstructor, {
93
		version: prototype.version,
94
95
		// Copy the object used to create the prototype in case we need to
96
		// redefine the widget later
97
		_proto: $.extend( {}, prototype ),
98
99
		// Track widgets that inherit from this widget in case this widget is
100
		// redefined after a widget inherits from it
101
		_childConstructors: []
102
	} );
103
104
	basePrototype = new base();
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like base should be capitalized.
Loading history...
105
106
	// We need to make the options hash a property directly on the new instance
107
	// otherwise we'll modify the options hash on the prototype that we're
108
	// inheriting from
109
	basePrototype.options = $.widget.extend( {}, basePrototype.options );
110
	$.each( prototype, function( prop, value ) {
111
		if ( !$.isFunction( value ) ) {
112
			proxiedPrototype[ prop ] = value;
113
			return;
114
		}
115
		proxiedPrototype[ prop ] = ( function() {
116
			function _super() {
117
				return base.prototype[ prop ].apply( this, arguments );
118
			}
119
120
			function _superApply( args ) {
121
				return base.prototype[ prop ].apply( this, args );
122
			}
123
124
			return function() {
125
				var __super = this._super;
126
				var __superApply = this._superApply;
127
				var returnValue;
128
129
				this._super = _super;
130
				this._superApply = _superApply;
131
132
				returnValue = value.apply( this, arguments );
133
134
				this._super = __super;
135
				this._superApply = __superApply;
136
137
				return returnValue;
138
			};
139
		} )();
140
	} );
141
	constructor.prototype = $.widget.extend( basePrototype, {
142
143
		// TODO: remove support for widgetEventPrefix
144
		// always use the name + a colon as the prefix, e.g., draggable:start
145
		// don't prefix for widgets that aren't DOM-based
146
		widgetEventPrefix: existingConstructor ? ( basePrototype.widgetEventPrefix || name ) : name
147
	}, proxiedPrototype, {
148
		constructor: constructor,
149
		namespace: namespace,
150
		widgetName: name,
151
		widgetFullName: fullName
152
	} );
153
154
	// If this widget is being redefined then we need to find all widgets that
155
	// are inheriting from it and redefine all of them so that they inherit from
156
	// the new version of this widget. We're essentially trying to replace one
157
	// level in the prototype chain.
158
	if ( existingConstructor ) {
159
		$.each( existingConstructor._childConstructors, function( i, child ) {
160
			var childPrototype = child.prototype;
161
162
			// Redefine the child widget using the same prototype that was
163
			// originally used, but inherit from the new version of the base
164
			$.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor,
165
				child._proto );
166
		} );
167
168
		// Remove the list of existing child constructors from the old constructor
169
		// so the old child constructors can be garbage collected
170
		delete existingConstructor._childConstructors;
171
	} else {
172
		base._childConstructors.push( constructor );
173
	}
174
175
	$.widget.bridge( name, constructor );
176
177
	return constructor;
178
};
179
180
$.widget.extend = function( target ) {
181
	var input = widgetSlice.call( arguments, 1 );
182
	var inputIndex = 0;
183
	var inputLength = input.length;
184
	var key;
185
	var value;
186
187
	for ( ; inputIndex < inputLength; inputIndex++ ) {
188
		for ( key in input[ inputIndex ] ) {
189
			value = input[ inputIndex ][ key ];
190
			if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {
191
192
				// Clone objects
193
				if ( $.isPlainObject( value ) ) {
194
					target[ key ] = $.isPlainObject( target[ key ] ) ?
195
						$.widget.extend( {}, target[ key ], value ) :
196
197
						// Don't extend strings, arrays, etc. with objects
198
						$.widget.extend( {}, value );
199
200
				// Copy everything else by reference
201
				} else {
202
					target[ key ] = value;
203
				}
204
			}
205
		}
206
	}
207
	return target;
208
};
209
210
$.widget.bridge = function( name, object ) {
211
	var fullName = object.prototype.widgetFullName || name;
212
	$.fn[ name ] = function( options ) {
213
		var isMethodCall = typeof options === "string";
214
		var args = widgetSlice.call( arguments, 1 );
215
		var returnValue = this;
216
217
		if ( isMethodCall ) {
218
219
			// If this is an empty collection, we need to have the instance method
220
			// return undefined instead of the jQuery instance
221
			if ( !this.length && options === "instance" ) {
222
				returnValue = undefined;
223
			} else {
224
				this.each( function() {
225
					var methodValue;
226
					var instance = $.data( this, fullName );
227
228
					if ( options === "instance" ) {
229
						returnValue = instance;
230
						return false;
231
					}
232
233
					if ( !instance ) {
234
						return $.error( "cannot call methods on " + name +
235
							" prior to initialization; " +
236
							"attempted to call method '" + options + "'" );
237
					}
238
239
					if ( !$.isFunction( instance[ options ] ) || options.charAt( 0 ) === "_" ) {
240
						return $.error( "no such method '" + options + "' for " + name +
241
							" widget instance" );
242
					}
243
244
					methodValue = instance[ options ].apply( instance, args );
245
246
					if ( methodValue !== instance && methodValue !== undefined ) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if methodValue !== instance...thodValue !== undefined is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
247
						returnValue = methodValue && methodValue.jquery ?
248
							returnValue.pushStack( methodValue.get() ) :
249
							methodValue;
250
						return false;
251
					}
252
				} );
253
			}
254
		} else {
255
256
			// Allow multiple hashes to be passed on init
257
			if ( args.length ) {
258
				options = $.widget.extend.apply( null, [ options ].concat( args ) );
259
			}
260
261
			this.each( function() {
262
				var instance = $.data( this, fullName );
263
				if ( instance ) {
264
					instance.option( options || {} );
265
					if ( instance._init ) {
266
						instance._init();
267
					}
268
				} else {
269
					$.data( this, fullName, new object( options, this ) );
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like object should be capitalized.
Loading history...
270
				}
271
			} );
272
		}
273
274
		return returnValue;
275
	};
276
};
277
278
$.Widget = function( /* options, element */ ) {};
279
$.Widget._childConstructors = [];
280
281
$.Widget.prototype = {
282
	widgetName: "widget",
283
	widgetEventPrefix: "",
284
	defaultElement: "<div>",
285
286
	options: {
287
		classes: {},
288
		disabled: false,
289
290
		// Callbacks
291
		create: null
292
	},
293
294
	_createWidget: function( options, element ) {
295
		element = $( element || this.defaultElement || this )[ 0 ];
296
		this.element = $( element );
297
		this.uuid = widgetUuid++;
298
		this.eventNamespace = "." + this.widgetName + this.uuid;
299
300
		this.bindings = $();
301
		this.hoverable = $();
302
		this.focusable = $();
303
		this.classesElementLookup = {};
304
305
		if ( element !== this ) {
306
			$.data( element, this.widgetFullName, this );
307
			this._on( true, this.element, {
308
				remove: function( event ) {
309
					if ( event.target === element ) {
310
						this.destroy();
311
					}
312
				}
313
			} );
314
			this.document = $( element.style ?
315
316
				// Element within the document
317
				element.ownerDocument :
318
319
				// Element is window or document
320
				element.document || element );
321
			this.window = $( this.document[ 0 ].defaultView || this.document[ 0 ].parentWindow );
322
		}
323
324
		this.options = $.widget.extend( {},
325
			this.options,
326
			this._getCreateOptions(),
327
			options );
328
329
		this._create();
330
331
		if ( this.options.disabled ) {
332
			this._setOptionDisabled( this.options.disabled );
333
		}
334
335
		this._trigger( "create", null, this._getCreateEventData() );
336
		this._init();
337
	},
338
339
	_getCreateOptions: function() {
340
		return {};
341
	},
342
343
	_getCreateEventData: $.noop,
344
345
	_create: $.noop,
346
347
	_init: $.noop,
348
349
	destroy: function() {
350
		var that = this;
351
352
		this._destroy();
353
		$.each( this.classesElementLookup, function( key, value ) {
354
			that._removeClass( value, key );
355
		} );
356
357
		// We can probably remove the unbind calls in 2.0
358
		// all event bindings should go through this._on()
359
		this.element
360
			.off( this.eventNamespace )
361
			.removeData( this.widgetFullName );
362
		this.widget()
363
			.off( this.eventNamespace )
364
			.removeAttr( "aria-disabled" );
365
366
		// Clean up events and states
367
		this.bindings.off( this.eventNamespace );
368
	},
369
370
	_destroy: $.noop,
371
372
	widget: function() {
373
		return this.element;
374
	},
375
376
	option: function( key, value ) {
377
		var options = key;
378
		var parts;
379
		var curOption;
380
		var i;
381
382
		if ( arguments.length === 0 ) {
383
384
			// Don't return a reference to the internal hash
385
			return $.widget.extend( {}, this.options );
386
		}
387
388
		if ( typeof key === "string" ) {
389
390
			// Handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
391
			options = {};
392
			parts = key.split( "." );
393
			key = parts.shift();
394
			if ( parts.length ) {
395
				curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
396
				for ( i = 0; i < parts.length - 1; i++ ) {
397
					curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
398
					curOption = curOption[ parts[ i ] ];
399
				}
400
				key = parts.pop();
401
				if ( arguments.length === 1 ) {
402
					return curOption[ key ] === undefined ? null : curOption[ key ];
403
				}
404
				curOption[ key ] = value;
405
			} else {
406
				if ( arguments.length === 1 ) {
407
					return this.options[ key ] === undefined ? null : this.options[ key ];
408
				}
409
				options[ key ] = value;
410
			}
411
		}
412
413
		this._setOptions( options );
414
415
		return this;
416
	},
417
418
	_setOptions: function( options ) {
419
		var key;
420
421
		for ( key in options ) {
422
			this._setOption( key, options[ key ] );
423
		}
424
425
		return this;
426
	},
427
428
	_setOption: function( key, value ) {
429
		if ( key === "classes" ) {
430
			this._setOptionClasses( value );
431
		}
432
433
		this.options[ key ] = value;
434
435
		if ( key === "disabled" ) {
436
			this._setOptionDisabled( value );
437
		}
438
439
		return this;
440
	},
441
442
	_setOptionClasses: function( value ) {
443
		var classKey, elements, currentElements;
444
445
		for ( classKey in value ) {
446
			currentElements = this.classesElementLookup[ classKey ];
447
			if ( value[ classKey ] === this.options.classes[ classKey ] ||
448
					!currentElements ||
449
					!currentElements.length ) {
450
				continue;
451
			}
452
453
			// We are doing this to create a new jQuery object because the _removeClass() call
454
			// on the next line is going to destroy the reference to the current elements being
455
			// tracked. We need to save a copy of this collection so that we can add the new classes
456
			// below.
457
			elements = $( currentElements.get() );
458
			this._removeClass( currentElements, classKey );
459
460
			// We don't use _addClass() here, because that uses this.options.classes
461
			// for generating the string of classes. We want to use the value passed in from
462
			// _setOption(), this is the new value of the classes option which was passed to
463
			// _setOption(). We pass this value directly to _classes().
464
			elements.addClass( this._classes( {
465
				element: elements,
466
				keys: classKey,
467
				classes: value,
468
				add: true
469
			} ) );
470
		}
471
	},
472
473
	_setOptionDisabled: function( value ) {
474
		this._toggleClass( this.widget(), this.widgetFullName + "-disabled", null, !!value );
475
476
		// If the widget is becoming disabled, then nothing is interactive
477
		if ( value ) {
478
			this._removeClass( this.hoverable, null, "ui-state-hover" );
479
			this._removeClass( this.focusable, null, "ui-state-focus" );
480
		}
481
	},
482
483
	enable: function() {
484
		return this._setOptions( { disabled: false } );
485
	},
486
487
	disable: function() {
488
		return this._setOptions( { disabled: true } );
489
	},
490
491
	_classes: function( options ) {
492
		var full = [];
493
		var that = this;
494
495
		options = $.extend( {
496
			element: this.element,
497
			classes: this.options.classes || {}
498
		}, options );
499
500
		function processClassString( classes, checkOption ) {
501
			var current, i;
502
			for ( i = 0; i < classes.length; i++ ) {
503
				current = that.classesElementLookup[ classes[ i ] ] || $();
504
				if ( options.add ) {
505
					current = $( $.unique( current.get().concat( options.element.get() ) ) );
506
				} else {
507
					current = $( current.not( options.element ).get() );
508
				}
509
				that.classesElementLookup[ classes[ i ] ] = current;
510
				full.push( classes[ i ] );
511
				if ( checkOption && options.classes[ classes[ i ] ] ) {
512
					full.push( options.classes[ classes[ i ] ] );
513
				}
514
			}
515
		}
516
517
		this._on( options.element, {
518
			"remove": "_untrackClassesElement"
519
		} );
520
521
		if ( options.keys ) {
522
			processClassString( options.keys.match( /\S+/g ) || [], true );
523
		}
524
		if ( options.extra ) {
525
			processClassString( options.extra.match( /\S+/g ) || [] );
526
		}
527
528
		return full.join( " " );
529
	},
530
531
	_untrackClassesElement: function( event ) {
532
		var that = this;
533
		$.each( that.classesElementLookup, function( key, value ) {
534
			if ( $.inArray( event.target, value ) !== -1 ) {
535
				that.classesElementLookup[ key ] = $( value.not( event.target ).get() );
536
			}
537
		} );
538
	},
539
540
	_removeClass: function( element, keys, extra ) {
541
		return this._toggleClass( element, keys, extra, false );
542
	},
543
544
	_addClass: function( element, keys, extra ) {
545
		return this._toggleClass( element, keys, extra, true );
546
	},
547
548
	_toggleClass: function( element, keys, extra, add ) {
549
		add = ( typeof add === "boolean" ) ? add : extra;
550
		var shift = ( typeof element === "string" || element === null ),
551
			options = {
552
				extra: shift ? keys : extra,
553
				keys: shift ? element : keys,
554
				element: shift ? this.element : element,
555
				add: add
556
			};
557
		options.element.toggleClass( this._classes( options ), add );
558
		return this;
559
	},
560
561
	_on: function( suppressDisabledCheck, element, handlers ) {
562
		var delegateElement;
563
		var instance = this;
564
565
		// No suppressDisabledCheck flag, shuffle arguments
566
		if ( typeof suppressDisabledCheck !== "boolean" ) {
567
			handlers = element;
568
			element = suppressDisabledCheck;
569
			suppressDisabledCheck = false;
570
		}
571
572
		// No element argument, shuffle and use this.element
573
		if ( !handlers ) {
574
			handlers = element;
575
			element = this.element;
576
			delegateElement = this.widget();
577
		} else {
578
			element = delegateElement = $( element );
579
			this.bindings = this.bindings.add( element );
580
		}
581
582
		$.each( handlers, function( event, handler ) {
583
			function handlerProxy() {
584
585
				// Allow widgets to customize the disabled handling
586
				// - disabled as an array instead of boolean
587
				// - disabled class as method for disabling individual parts
588
				if ( !suppressDisabledCheck &&
589
						( instance.options.disabled === true ||
590
						$( this ).hasClass( "ui-state-disabled" ) ) ) {
591
					return;
592
				}
593
				return ( typeof handler === "string" ? instance[ handler ] : handler )
594
					.apply( instance, arguments );
595
			}
596
597
			// Copy the guid so direct unbinding works
598
			if ( typeof handler !== "string" ) {
599
				handlerProxy.guid = handler.guid =
600
					handler.guid || handlerProxy.guid || $.guid++;
601
			}
602
603
			var match = event.match( /^([\w:-]*)\s*(.*)$/ );
604
			var eventName = match[ 1 ] + instance.eventNamespace;
605
			var selector = match[ 2 ];
606
607
			if ( selector ) {
608
				delegateElement.on( eventName, selector, handlerProxy );
609
			} else {
610
				element.on( eventName, handlerProxy );
611
			}
612
		} );
613
	},
614
615
	_off: function( element, eventName ) {
616
		eventName = ( eventName || "" ).split( " " ).join( this.eventNamespace + " " ) +
617
			this.eventNamespace;
618
		element.off( eventName ).off( eventName );
619
620
		// Clear the stack to avoid memory leaks (#10056)
621
		this.bindings = $( this.bindings.not( element ).get() );
622
		this.focusable = $( this.focusable.not( element ).get() );
623
		this.hoverable = $( this.hoverable.not( element ).get() );
624
	},
625
626
	_delay: function( handler, delay ) {
627
		function handlerProxy() {
628
			return ( typeof handler === "string" ? instance[ handler ] : handler )
629
				.apply( instance, arguments );
630
		}
631
		var instance = this;
632
		return setTimeout( handlerProxy, delay || 0 );
633
	},
634
635
	_hoverable: function( element ) {
636
		this.hoverable = this.hoverable.add( element );
637
		this._on( element, {
638
			mouseenter: function( event ) {
639
				this._addClass( $( event.currentTarget ), null, "ui-state-hover" );
640
			},
641
			mouseleave: function( event ) {
642
				this._removeClass( $( event.currentTarget ), null, "ui-state-hover" );
643
			}
644
		} );
645
	},
646
647
	_focusable: function( element ) {
648
		this.focusable = this.focusable.add( element );
649
		this._on( element, {
650
			focusin: function( event ) {
651
				this._addClass( $( event.currentTarget ), null, "ui-state-focus" );
652
			},
653
			focusout: function( event ) {
654
				this._removeClass( $( event.currentTarget ), null, "ui-state-focus" );
655
			}
656
		} );
657
	},
658
659
	_trigger: function( type, event, data ) {
660
		var prop, orig;
661
		var callback = this.options[ type ];
662
663
		data = data || {};
664
		event = $.Event( event );
665
		event.type = ( type === this.widgetEventPrefix ?
666
			type :
667
			this.widgetEventPrefix + type ).toLowerCase();
668
669
		// The original event may come from any element
670
		// so we need to reset the target on the new event
671
		event.target = this.element[ 0 ];
672
673
		// Copy original event properties over to the new event
674
		orig = event.originalEvent;
675
		if ( orig ) {
676
			for ( prop in orig ) {
677
				if ( !( prop in event ) ) {
678
					event[ prop ] = orig[ prop ];
679
				}
680
			}
681
		}
682
683
		this.element.trigger( event, data );
684
		return !( $.isFunction( callback ) &&
685
			callback.apply( this.element[ 0 ], [ event ].concat( data ) ) === false ||
686
			event.isDefaultPrevented() );
687
	}
688
};
689
690
$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
691
	$.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
692
		if ( typeof options === "string" ) {
693
			options = { effect: options };
694
		}
695
696
		var hasOptions;
697
		var effectName = !options ?
698
			method :
699
			options === true || typeof options === "number" ?
700
				defaultEffect :
701
				options.effect || defaultEffect;
702
703
		options = options || {};
704
		if ( typeof options === "number" ) {
705
			options = { duration: options };
706
		}
707
708
		hasOptions = !$.isEmptyObject( options );
709
		options.complete = callback;
710
711
		if ( options.delay ) {
712
			element.delay( options.delay );
713
		}
714
715
		if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {
716
			element[ method ]( options );
717
		} else if ( effectName !== method && element[ effectName ] ) {
718
			element[ effectName ]( options.duration, options.easing, callback );
719
		} else {
720
			element.queue( function( next ) {
721
				$( this )[ method ]();
722
				if ( callback ) {
723
					callback.call( element[ 0 ] );
724
				}
725
				next();
726
			} );
727
		}
728
	};
729
} );
730
731
return $.widget;
732
733
} ) );
734