jQuery UI widget源码解析

来源:互联网 发布:休闲单机游戏 知乎 编辑:程序博客网 时间:2024/05/16 05:02

jquery ui 的所有组件都是基于一个简单,可重用的widget。

这个widget是jquery ui的核心部分,实用它能实现一致的API,创建有状态的插件,而无需关心插件的内部转换。

$.widget( name, base, prototype )

widget一共有2或3个参数。base为可选。

这里之所以把base放在第二个参数里,主要是因为这样写代码更直观一些。(因为后面的prototype 是个代码非常长的大对象)。

name:第一个参数是一个包含一个命名空间和组件名称的字符串,通过”.”来分割。
命名空间必须有,它指向widget prototype存储的全局jQuery对象。
如果命名空间没有,widget factory将会为你生成。widget name是插件函数和原型的真实名称,
比如: jQuery.widget( “demo.multi”, {…} ) 将会生成 jQuery.demo , jQuery.demo.multi , and jQuery.demo.multi.prototype .

base:第二个参数(可选)是 widget prototype继承于什么对象。
例如jQuery UI有一个“mouse”的插件,它可以作为其他的插件提供的基础。
为了实现这个所有的基于mouse的插件比如draggable,
droppable可以这么做: jQuery.widget( "ui.draggable", $.ui.mouse, {...} );
如果没有这个参数,widget默认继承自“base widget” jQuery.Widget(注意jQuery.widget 和 jQuery.Widget不同) 。

prototype:最后一个参数是一个对象文字,它会转化为所有widget实例的prototype。widget factory会生成属性链,连接到她继承的widget的prototype。一直到最基本的 jQuery.Widget。

一旦你调用jQuery.widget,它会在jQuery prototype ( jQuery.fn )上生成一个新的可用方法对应于widget的名字,比如我们这个例子jQuery.fn.multi。 .fn方法是包含Dom元素的jquery对象和你生成的 widget prototyp实例的接口,为每一个jQuery对象生成一个新的widget的实例。

[javascript] view plaincopy
  1. /*! 
  2.  * jQuery UI Widget @VERSION 
  3.  * http://jqueryui.com 
  4.  * 
  5.  * Copyright 2014 jQuery Foundation and other contributors 
  6.  * Released under the MIT license. 
  7.  * http://jquery.org/license 
  8.  * 
  9.  * http://api.jqueryui.com/jQuery.widget/ 
  10.  */  
  11.   
  12. //这里判定是否支持amd or cmd 模式  
  13. (function(factory) {  
  14.     if (typeof define === "function" && define.amd) {  
  15.   
  16.         // AMD. Register as an anonymous module.  
  17.         define(["jquery"], factory);  
  18.     } else {  
  19.   
  20.         // Browser globals  
  21.         factory(jQuery);  
  22.     }  
  23. }(function($) {  
  24.   
  25.     var widget_uuid = 0,  
  26.         //插件的实例化数量  
  27.         widget_slice = Array.prototype.slice; //数组的slice方法,这里的作用是将参赛arguments 转为真正的数组  
  28.   
  29.     //清除插件的数据及缓存  
  30.     $.cleanData = (function(orig) {  
  31.         return function(elems) {  
  32.             for (var i = 0, elem;  
  33.             (elem = elems[i]) != null; i++) {  
  34.                 try {  
  35.                     // 重写cleanData方法,调用后触发每个元素的remove事件  
  36.                     $(elem).triggerHandler("remove");  
  37.                     // http://bugs.jquery.com/ticket/8235  
  38.                 } catch (e) {}  
  39.             }  
  40.             orig(elems);  
  41.         };  
  42.     })($.cleanData);  
  43.   
  44.     /** 
  45.      * widget工厂方法,用于创建插件 
  46.      * @param name 包含命名空间的插件名称,格式 xx.xxx 
  47.      * @param base 需要继承的ui组件 
  48.      * @param prototype 插件的实际代码 
  49.      * @returns {Function} 
  50.      */  
  51.     $.widget = function(name, base, prototype) {  
  52.         var fullName, //插件全称  
  53.         existingConstructor, //原有的构造函数  
  54.         constructor, //当前构造函数  
  55.         basePrototype, //父类的Prototype  
  56.         // proxiedPrototype allows the provided prototype to remain unmodified  
  57.         // so that it can be used as a mixin for multiple widgets (#8876)  
  58.         proxiedPrototype = {},  
  59.             //可调用父类方法_spuer的prototype对象,扩展于prototype  
  60.             namespace = name.split(".")[0];  
  61.   
  62.         name = name.split(".")[1];  
  63.         fullName = namespace + "-" + name;  
  64.         //如果只有2个参数  base默认为Widget类,组件默认会继承base类的所有方法  
  65.         if (!prototype) {  
  66.             prototype = base;  
  67.             base = $.Widget;  
  68.         }  
  69.   
  70.         //    console.log(base, $.Widget)  
  71.   
  72.         // create selector for plugin  
  73.         //创建一个自定义的伪类选择器  
  74.         //如 $(':ui-menu') 则表示选择定义了ui-menu插件的元素  
  75.         $.expr[":"][fullName.toLowerCase()] = function(elem) {  
  76.             return !!$.data(elem, fullName);  
  77.         };  
  78.   
  79.         // 判定命名空间对象是否存在,没有的话 则创建一个空对象  
  80.         $[namespace] = $[namespace] || {};  
  81.         //这里存一份旧版的插件,如果这个插件已经被使用或者定义了  
  82.         existingConstructor = $[namespace][name];  
  83.         //这个是插件实例化的主要部分  
  84.         //constructor存储了插件的实例,同时也创建了基于命名空间的对象  
  85.         //如$.ui.menu  
  86.         constructor = $[namespace][name] = function(options, element) {  
  87.             // allow instantiation without "new" keyword  
  88.             //允许直接调用命名空间上的方法来创建组件  
  89.             //比如:$.ui.menu({},'#id') 这种方式创建的话,默认没有new 实例化。因为_createWidget是prototype上的方法,需要new关键字来实例化  
  90.             //通过 调用 $.ui.menu 来实例化插件  
  91.             if (!this._createWidget) {  
  92.                 console.info(this)  
  93.                 return new constructor(options, element);  
  94.             }  
  95.   
  96.             // allow instantiation without initializing for simple inheritance  
  97.             // must use "new" keyword (the code above always passes args)  
  98.             //如果存在参数,则说明是正常调用插件  
  99.             //_createWidget是创建插件的核心方法  
  100.             if (arguments.length) {  
  101.                 this._createWidget(options, element);  
  102.             }  
  103.         };  
  104.         // extend with the existing constructor to carry over any static properties  
  105.         //合并对象,将旧插件实例,及版本号、prototype合并到constructor  
  106.         $.extend(constructor, existingConstructor, {  
  107.   
  108.             version: prototype.version,  
  109.             // copy the object used to create the prototype in case we need to  
  110.             // redefine the widget later  
  111.             //创建一个新的插件对象  
  112.             //将插件实例暴露给外部,可用户修改及覆盖  
  113.             _proto: $.extend({}, prototype),  
  114.             // track widgets that inherit from this widget in case this widget is  
  115.             // redefined after a widget inherits from it  
  116.             _childConstructors: []  
  117.         });  
  118.   
  119.         //实例化父类 获取父类的  prototype  
  120.         basePrototype = new base();  
  121.         // we need to make the options hash a property directly on the new instance  
  122.         // otherwise we'll modify the options hash on the prototype that we're  
  123.         // inheriting from  
  124.         //这里深复制一份options  
  125.         basePrototype.options = $.widget.extend({}, basePrototype.options);  
  126.         //在传入的ui原型中有方法调用this._super 和this.__superApply会调用到base上(最基类上)的方法  
  127.         $.each(prototype, function(prop, value) {  
  128.             //如果val不是function 则直接给对象赋值字符串  
  129.             if (!$.isFunction(value)) {  
  130.                 proxiedPrototype[prop] = value;  
  131.                 return;  
  132.             }  
  133.             //如果val是function  
  134.             proxiedPrototype[prop] = (function() {  
  135.                 //两种调用父类函数的方法  
  136.                 var _super = function() {  
  137.                         //将当期实例调用父类的方法  
  138.                         return base.prototype[prop].apply(this, arguments);  
  139.                     },  
  140.                     _superApply = function(args) {  
  141.                         return base.prototype[prop].apply(this, args);  
  142.                     };  
  143.                 return function() {  
  144.                     var __super = this._super,  
  145.                         __superApply = this._superApply,  
  146.                         returnValue;  
  147.                     //                console.log(prop, value,this,this._super,'===')  
  148.                     //                debugger;  
  149.                     //在这里调用父类的函数  
  150.                     this._super = _super;  
  151.                     this._superApply = _superApply;  
  152.   
  153.                     returnValue = value.apply(this, arguments);  
  154.   
  155.                     this._super = __super;  
  156.                     this._superApply = __superApply;  
  157.                     //                console.log(this,value,returnValue,prop,'===')  
  158.                     return returnValue;  
  159.                 };  
  160.             })();  
  161.         });  
  162.         //    console.info(proxiedPrototype)  
  163.         //    debugger;  
  164.         //这里是实例化获取的内容  
  165.         constructor.prototype = $.widget.extend(basePrototype, {  
  166.             // TODO: remove support for widgetEventPrefix  
  167.             // always use the name + a colon as the prefix, e.g., draggable:start  
  168.             // don't prefix for widgets that aren't DOM-based  
  169.             widgetEventPrefix: existingConstructor ? (basePrototype.widgetEventPrefix || name) : name  
  170.         }, proxiedPrototype, {  
  171.             //重新把constructor指向 constructor 变量  
  172.             constructor: constructor,  
  173.             namespace: namespace,  
  174.             widgetName: name,  
  175.             widgetFullName: fullName  
  176.         });  
  177.   
  178.         // If this widget is being redefined then we need to find all widgets that  
  179.         // are inheriting from it and redefine all of them so that they inherit from  
  180.         // the new version of this widget. We're essentially trying to replace one  
  181.         // level in the prototype chain.  
  182.         //这里判定插件是否被使用了。一般来说,都不会被使用的。  
  183.         //因为插件的开发者都是我们自己,呵呵  
  184.         if (existingConstructor) {  
  185.             $.each(existingConstructor._childConstructors, function(i, child) {  
  186.                 var childPrototype = child.prototype;  
  187.   
  188.                 // redefine the child widget using the same prototype that was  
  189.                 // originally used, but inherit from the new version of the base  
  190.                 $.widget(childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto);  
  191.             });  
  192.             // remove the list of existing child constructors from the old constructor  
  193.             // so the old child constructors can be garbage collected  
  194.             delete existingConstructor._childConstructors;  
  195.         } else {  
  196.             //父类添加当前插件的实例 主要用于作用域链查找 不至于断层  
  197.             base._childConstructors.push(constructor);  
  198.         }  
  199.   
  200.         //将此方法挂在jQuery对象上  
  201.         $.widget.bridge(name, constructor);  
  202.   
  203.         return constructor;  
  204.     };  
  205.   
  206.     //扩展jq的extend方法,实际上类似$.extend(true,..) 深复制  
  207.     $.widget.extend = function(target) {  
  208.         var input = widget_slice.call(arguments, 1),  
  209.             inputIndex = 0,  
  210.             inputLength = input.length,  
  211.             key, value;  
  212.         for (; inputIndex < inputLength; inputIndex++) {  
  213.             for (key in input[inputIndex]) {  
  214.                 value = input[inputIndex][key];  
  215.                 if (input[inputIndex].hasOwnProperty(key) && value !== undefined) {  
  216.                     // Clone objects  
  217.                     if ($.isPlainObject(value)) {  
  218.                         target[key] = $.isPlainObject(target[key]) ? $.widget.extend({}, target[key], value) :  
  219.                         // Don't extend strings, arrays, etc. with objects  
  220.                         $.widget.extend({}, value);  
  221.                         // Copy everything else by reference  
  222.                     } else {  
  223.                         target[key] = value;  
  224.                     }  
  225.                 }  
  226.             }  
  227.         }  
  228.         return target;  
  229.     };  
  230.   
  231.     //bridge 是设计模式的一种,这里将对象转为插件调用  
  232.     $.widget.bridge = function(name, object) {  
  233.         var fullName = object.prototype.widgetFullName || name;  
  234.         //这里就是插件了  
  235.         //这部分的实现主要做了几个工作,也是制作一个优雅的插件的主要代码  
  236.         //1、初次实例化时将插件对象缓存在dom上,后续则可直接调用,避免在相同元素下widget的多实例化。简单的说,就是一个单例方法。  
  237.         //2、合并用户提供的默认设置选项options  
  238.         //3、可以通过调用插件时传递字符串来调用插件内的方法。如:$('#id').menu('hide') 实际就是实例插件并调用hide()方法。  
  239.         //4、同时限制外部调用“_”下划线的私有方法  
  240.         $.fn[name] = function(options) {  
  241.             var isMethodCall = typeof options === "string",  
  242.                 args = widget_slice.call(arguments, 1),  
  243.                 returnValue = this;  
  244.   
  245.             // allow multiple hashes to be passed on init.  
  246.             //可以简单认为是$.extend(true,options,args[0],...),args可以是一个参数或是数组  
  247.             options = !isMethodCall && args.length ? $.widget.extend.apply(null, [options].concat(args)) : options;  
  248.             //这里对字符串和对象分别作处理  
  249.             if (isMethodCall) {  
  250.                 this.each(function() {  
  251.                     var methodValue, instance = $.data(this, fullName);  
  252.                     //如果传递的是instance则将this返回。  
  253.                     if (options === "instance") {  
  254.                         returnValue = instance;  
  255.                         return false;  
  256.                     }  
  257.                     if (!instance) {  
  258.                         return $.error("cannot call methods on " + name + " prior to initialization; " + "attempted to call method '" + options + "'");  
  259.                     }  
  260.                     //这里对私有方法的调用做了限制,直接调用会抛出异常事件  
  261.                     if (!$.isFunction(instance[options]) || options.charAt(0) === "_") {  
  262.                         return $.error("no such method '" + options + "' for " + name + " widget instance");  
  263.                     }  
  264.                     //这里是如果传递的是字符串,则调用字符串方法,并传递对应的参数.  
  265.                     //比如插件有个方法hide(a,b); 有2个参数:a,b  
  266.                     //则调用时$('#id').menu('hide',1,2);//1和2 分别就是参数a和b了。  
  267.                     methodValue = instance[options].apply(instance, args);  
  268.                     if (methodValue !== instance && methodValue !== undefined) {  
  269.                         returnValue = methodValue && methodValue.jquery ? returnValue.pushStack(methodValue.get()) : methodValue;  
  270.                         return false;  
  271.                     }  
  272.                 });  
  273.             } else {  
  274.                 this.each(function() {  
  275.                     var instance = $.data(this, fullName);  
  276.   
  277.                     if (instance) {  
  278.                         instance.option(options || {});  
  279.                         //这里每次都调用init方法  
  280.                         if (instance._init) {  
  281.                             instance._init();  
  282.                         }  
  283.                     } else {  
  284.                         //缓存插件实例  
  285.                         $.data(this, fullName, new object(options, this));  
  286.                     }  
  287.                 });  
  288.             }  
  289.   
  290.             return returnValue;  
  291.         };  
  292.     };  
  293.   
  294.     //这里是真正的widget基类  
  295.     $.Widget = function/* options, element */ ) {};  
  296.     $.Widget._childConstructors = [];  
  297.   
  298.     $.Widget.prototype = {  
  299.         widgetName: "widget",  
  300.         //用来决定事件的名称和插件提供的callbacks的关联。  
  301.         // 比如dialog有一个close的callback,当close的callback被执行的时候,一个dialogclose的事件被触发。  
  302.         // 事件的名称和事件的prefix+callback的名称。widgetEventPrefix 默认就是控件的名称,但是如果事件需要不同的名称也可以被重写。  
  303.         // 比如一个用户开始拖拽一个元素,我们不想使用draggablestart作为事件的名称,我们想使用dragstart,所以我们可以重写事件的prefix。  
  304.         // 如果callback的名称和事件的prefix相同,事件的名称将不会是prefix。  
  305.         // 它阻止像dragdrag一样的事件名称。  
  306.         widgetEventPrefix: "",  
  307.         defaultElement: "<div>",  
  308.         //属性会在创建模块时被覆盖  
  309.         options: {  
  310.             disabled: false,  
  311.   
  312.             // callbacks  
  313.             create: null  
  314.         },  
  315.         _createWidget: function(options, element) {  
  316.             element = $(element || this.defaultElement || this)[0];  
  317.             this.element = $(element);  
  318.             this.uuid = widget_uuid++;  
  319.             this.eventNamespace = "." + this.widgetName + this.uuid;  
  320.             this.options = $.widget.extend({}, this.options, this._getCreateOptions(), options);  
  321.   
  322.             this.bindings = $();  
  323.             this.hoverable = $();  
  324.             this.focusable = $();  
  325.   
  326.             if (element !== this) {  
  327.                 //            debugger  
  328.                 $.data(element, this.widgetFullName, this);  
  329.                 this._on(truethis.element, {  
  330.                     remove: function(event) {  
  331.                         if (event.target === element) {  
  332.                             this.destroy();  
  333.                         }  
  334.                     }  
  335.                 });  
  336.                 this.document = $(element.style ?  
  337.                 // element within the document  
  338.                 element.ownerDocument :  
  339.                 // element is window or document  
  340.                 element.document || element);  
  341.                 this.window = $(this.document[0].defaultView || this.document[0].parentWindow);  
  342.             }  
  343.   
  344.             this._create();  
  345.             //创建插件时,有个create的回调  
  346.             this._trigger("create"nullthis._getCreateEventData());  
  347.             this._init();  
  348.         },  
  349.         _getCreateOptions: $.noop,  
  350.         _getCreateEventData: $.noop,  
  351.         _create: $.noop,  
  352.         _init: $.noop,  
  353.         //销毁模块:去除绑定事件、去除数据、去除样式、属性  
  354.         destroy: function() {  
  355.             this._destroy();  
  356.             // we can probably remove the unbind calls in 2.0  
  357.             // all event bindings should go through this._on()  
  358.             this.element.unbind(this.eventNamespace).removeData(this.widgetFullName)  
  359.             // support: jquery <1.6.3  
  360.             // http://bugs.jquery.com/ticket/9413  
  361.             .removeData($.camelCase(this.widgetFullName));  
  362.             this.widget().unbind(this.eventNamespace).removeAttr("aria-disabled").removeClass(  
  363.             this.widgetFullName + "-disabled " + "ui-state-disabled");  
  364.   
  365.             // clean up events and states  
  366.             this.bindings.unbind(this.eventNamespace);  
  367.             this.hoverable.removeClass("ui-state-hover");  
  368.             this.focusable.removeClass("ui-state-focus");  
  369.         },  
  370.         _destroy: $.noop,  
  371.   
  372.         widget: function() {  
  373.             return this.element;  
  374.         },  
  375.         //设置选项函数  
  376.         option: function(key, value) {  
  377.             var options = key,  
  378.                 parts, curOption, i;  
  379.   
  380.             if (arguments.length === 0) {  
  381.                 // don't return a reference to the internal hash  
  382.                 //返回一个新的对象,不是内部数据的引用  
  383.                 return $.widget.extend({}, this.options);  
  384.             }  
  385.   
  386.             if (typeof key === "string") {  
  387.                 // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }  
  388.                 options = {};  
  389.                 parts = key.split(".");  
  390.                 key = parts.shift();  
  391.                 if (parts.length) {  
  392.                     curOption = options[key] = $.widget.extend({}, this.options[key]);  
  393.                     for (i = 0; i < parts.length - 1; i++) {  
  394.                         curOption[parts[i]] = curOption[parts[i]] || {};  
  395.                         curOption = curOption[parts[i]];  
  396.                     }  
  397.                     key = parts.pop();  
  398.                     if (arguments.length === 1) {  
  399.                         return curOption[key] === undefined ? null : curOption[key];  
  400.                     }  
  401.                     curOption[key] = value;  
  402.                 } else {  
  403.                     if (arguments.length === 1) {  
  404.                         return this.options[key] === undefined ? null : this.options[key];  
  405.                     }  
  406.                     options[key] = value;  
  407.                 }  
  408.             }  
  409.   
  410.             this._setOptions(options);  
  411.   
  412.             return this;  
  413.         },  
  414.         _setOptions: function(options) {  
  415.             var key;  
  416.   
  417.             for (key in options) {  
  418.                 this._setOption(key, options[key]);  
  419.             }  
  420.   
  421.             return this;  
  422.         },  
  423.         _setOption: function(key, value) {  
  424.             this.options[key] = value;  
  425.   
  426.             if (key === "disabled") {  
  427.                 this.widget().toggleClass(this.widgetFullName + "-disabled", !! value);  
  428.   
  429.                 // If the widget is becoming disabled, then nothing is interactive  
  430.                 if (value) {  
  431.                     this.hoverable.removeClass("ui-state-hover");  
  432.                     this.focusable.removeClass("ui-state-focus");  
  433.                 }  
  434.             }  
  435.   
  436.             return this;  
  437.         },  
  438.   
  439.         enable: function() {  
  440.             return this._setOptions({  
  441.                 disabled: false  
  442.             });  
  443.         },  
  444.         disable: function() {  
  445.             return this._setOptions({  
  446.                 disabled: true  
  447.             });  
  448.         },  
  449.   
  450.         _on: function(suppressDisabledCheck, element, handlers) {  
  451.             var delegateElement, instance = this;  
  452.   
  453.             // no suppressDisabledCheck flag, shuffle arguments  
  454.             if (typeof suppressDisabledCheck !== "boolean") {  
  455.                 handlers = element;  
  456.                 element = suppressDisabledCheck;  
  457.                 suppressDisabledCheck = false;  
  458.             }  
  459.   
  460.             // no element argument, shuffle and use this.element  
  461.             if (!handlers) {  
  462.                 handlers = element;  
  463.                 element = this.element;  
  464.                 delegateElement = this.widget();  
  465.             } else {  
  466.                 // accept selectors, DOM elements  
  467.                 element = delegateElement = $(element);  
  468.                 this.bindings = this.bindings.add(element);  
  469.             }  
  470.   
  471.             $.each(handlers, function(event, handler) {  
  472.                 function handlerProxy() {  
  473.                     // allow widgets to customize the disabled handling  
  474.                     // - disabled as an array instead of boolean  
  475.                     // - disabled class as method for disabling individual parts  
  476.                     if (!suppressDisabledCheck && (instance.options.disabled === true || $(this).hasClass("ui-state-disabled"))) {  
  477.                         return;  
  478.                     }  
  479.                     return (typeof handler === "string" ? instance[handler] : handler).apply(instance, arguments);  
  480.                 }  
  481.   
  482.                 // copy the guid so direct unbinding works  
  483.                 if (typeof handler !== "string") {  
  484.                     handlerProxy.guid = handler.guid = handler.guid || handlerProxy.guid || $.guid++;  
  485.                 }  
  486.   
  487.                 var match = event.match(/^([\w:-]*)\s*(.*)$/),  
  488.                     eventName = match[1] + instance.eventNamespace,  
  489.                     selector = match[2];  
  490.                 if (selector) {  
  491.                     delegateElement.delegate(selector, eventName, handlerProxy);  
  492.                 } else {  
  493.                     element.bind(eventName, handlerProxy);  
  494.                 }  
  495.             });  
  496.         },  
  497.   
  498.         _off: function(element, eventName) {  
  499.             eventName = (eventName || "").split(" ").join(this.eventNamespace + " ") + this.eventNamespace;  
  500.             element.unbind(eventName).undelegate(eventName);  
  501.         },  
  502.   
  503.         _delay: function(handler, delay) {  
  504.             function handlerProxy() {  
  505.                 return (typeof handler === "string" ? instance[handler] : handler).apply(instance, arguments);  
  506.             }  
  507.             var instance = this;  
  508.             return setTimeout(handlerProxy, delay || 0);  
  509.         },  
  510.   
  511.         _hoverable: function(element) {  
  512.             this.hoverable = this.hoverable.add(element);  
  513.             this._on(element, {  
  514.                 mouseenter: function(event) {  
  515.                     $(event.currentTarget).addClass("ui-state-hover");  
  516.                 },  
  517.                 mouseleave: function(event) {  
  518.                     $(event.currentTarget).removeClass("ui-state-hover");  
  519.                 }  
  520.             });  
  521.         },  
  522.   
  523.         _focusable: function(element) {  
  524.             this.focusable = this.focusable.add(element);  
  525.             this._on(element, {  
  526.                 focusin: function(event) {  
  527.                     $(event.currentTarget).addClass("ui-state-focus");  
  528.                 },  
  529.                 focusout: function(event) {  
  530.                     $(event.currentTarget).removeClass("ui-state-focus");  
  531.                 }  
  532.             });  
  533.         },  
  534.   
  535.         _trigger: function(type, event, data) {  
  536.             var prop, orig, callback = this.options[type];  
  537.   
  538.             data = data || {};  
  539.             event = $.Event(event);  
  540.             event.type = (type === this.widgetEventPrefix ? type : this.widgetEventPrefix + type).toLowerCase();  
  541.             // the original event may come from any element  
  542.             // so we need to reset the target on the new event  
  543.             event.target = this.element[0];  
  544.   
  545.             // copy original event properties over to the new event  
  546.             orig = event.originalEvent;  
  547.             if (orig) {  
  548.                 for (prop in orig) {  
  549.                     if (!(prop in event)) {  
  550.                         event[prop] = orig[prop];  
  551.                     }  
  552.                 }  
  553.             }  
  554.   
  555.             this.element.trigger(event, data);  
  556.             return !($.isFunction(callback) && callback.apply(this.element[0], [event].concat(data)) === false || event.isDefaultPrevented());  
  557.         }  
  558.     };  
  559.   
  560.     $.each({  
  561.         show: "fadeIn",  
  562.         hide: "fadeOut"  
  563.     }, function(method, defaultEffect) {  
  564.         $.Widget.prototype["_" + method] = function(element, options, callback) {  
  565.             if (typeof options === "string") {  
  566.                 options = {  
  567.                     effect: options  
  568.                 };  
  569.             }  
  570.             var hasOptions, effectName = !options ? method : options === true || typeof options === "number" ? defaultEffect : options.effect || defaultEffect;  
  571.             options = options || {};  
  572.             if (typeof options === "number") {  
  573.                 options = {  
  574.                     duration: options  
  575.                 };  
  576.             }  
  577.             hasOptions = !$.isEmptyObject(options);  
  578.             options.complete = callback;  
  579.             if (options.delay) {  
  580.                 element.delay(options.delay);  
  581.             }  
  582.             if (hasOptions && $.effects && $.effects.effect[effectName]) {  
  583.                 element[method](options);  
  584.             } else if (effectName !== method && element[effectName]) {  
  585.                 element[effectName](options.duration, options.easing, callback);  
  586.             } else {  
  587.                 element.queue(function(next) {  
  588.                     $(this)[method]();  
  589.                     if (callback) {  
  590.                         callback.call(element[0]);  
  591.                     }  
  592.                     next();  
  593.                 });  
  594.             }  
  595.         };  
  596.     });  
  597.   
  598.     return $.widget;  
  599.   
  600. }));  
0 0
原创粉丝点击