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的实例。

/*!
* jQuery UI Widget @VERSION
* http://jqueryui.com
*
* Copyright 2014 jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*
* http://api.jqueryui.com/jQuery.widget/
*/ //这里判定是否支持amd or cmd 模式
(function(factory) {
if (typeof define === "function" && define.amd) { // AMD. Register as an anonymous module.
define(["jquery"], factory);
} else { // Browser globals
factory(jQuery);
}
}(function($) { var widget_uuid = 0,
//插件的实例化数量
widget_slice = Array.prototype.slice; //数组的slice方法,这里的作用是将參赛arguments 转为真正的数组 //清除插件的数据及缓存
$.cleanData = (function(orig) {
return function(elems) {
for (var i = 0, elem;
(elem = elems[i]) != null; i++) {
try {
// 重写cleanData方法,调用后触发每一个元素的remove事件
$(elem).triggerHandler("remove");
// http://bugs.jquery.com/ticket/8235
} catch (e) {}
}
orig(elems);
};
})($.cleanData); /**
* widget工厂方法。用于创建插件
* @param name 包括命名空间的插件名称。格式 xx.xxx
* @param base 须要继承的ui组件
* @param prototype 插件的实际代码
* @returns {Function}
*/
$.widget = function(name, base, prototype) {
var fullName, //插件全称
existingConstructor, //原有的构造函数
constructor, //当前构造函数
basePrototype, //父类的Prototype
// proxiedPrototype allows the provided prototype to remain unmodified
// so that it can be used as a mixin for multiple widgets (#8876)
proxiedPrototype = {},
//可调用父类方法_spuer的prototype对象,扩展于prototype
namespace = name.split(".")[0]; name = name.split(".")[1];
fullName = namespace + "-" + name;
//假设仅仅有2个參数 base默觉得Widget类,组件默认会继承base类的全部方法
if (!prototype) {
prototype = base;
base = $.Widget;
} // console.log(base, $.Widget) // create selector for plugin
//创建一个自己定义的伪类选择器
//如 $(':ui-menu') 则表示选择定义了ui-menu插件的元素
$.expr[":"][fullName.toLowerCase()] = function(elem) {
return !!$.data(elem, fullName);
}; // 判定命名空间对象是否存在,没有的话 则创建一个空对象
$[namespace] = $[namespace] || {};
//这里存一份旧版的插件,假设这个插件已经被使用或者定义了
existingConstructor = $[namespace][name];
//这个是插件实例化的主要部分
//constructor存储了插件的实例,同一时候也创建了基于命名空间的对象
//如$.ui.menu
constructor = $[namespace][name] = function(options, element) {
// allow instantiation without "new" keyword
//同意直接调用命名空间上的方法来创建组件
//比方:$.ui.menu({},'#id') 这样的方式创建的话,默认没有new 实例化。 由于_createWidget是prototype上的方法,须要new关键字来实例化
//通过 调用 $.ui.menu 来实例化插件
if (!this._createWidget) {
console.info(this)
return new constructor(options, element);
} // allow instantiation without initializing for simple inheritance
// must use "new" keyword (the code above always passes args)
//假设存在參数,则说明是正常调用插件
//_createWidget是创建插件的核心方法
if (arguments.length) {
this._createWidget(options, element);
}
};
// extend with the existing constructor to carry over any static properties
//合并对象。将旧插件实例,及版本、prototype合并到constructor
$.extend(constructor, existingConstructor, { version: prototype.version,
// copy the object used to create the prototype in case we need to
// redefine the widget later
//创建一个新的插件对象
//将插件实例暴露给外部,可用户改动及覆盖
_proto: $.extend({}, prototype),
// track widgets that inherit from this widget in case this widget is
// redefined after a widget inherits from it
_childConstructors: []
}); //实例化父类 获取父类的 prototype
basePrototype = new base();
// we need to make the options hash a property directly on the new instance
// otherwise we'll modify the options hash on the prototype that we're
// inheriting from
//这里深复制一份options
basePrototype.options = $.widget.extend({}, basePrototype.options);
//在传入的ui原型中有方法调用this._super 和this.__superApply会调用到base上(最基类上)的方法
$.each(prototype, function(prop, value) {
//假设val不是function 则直接给对象赋值字符串
if (!$.isFunction(value)) {
proxiedPrototype[prop] = value;
return;
}
//假设val是function
proxiedPrototype[prop] = (function() {
//两种调用父类函数的方法
var _super = function() {
//将当期实例调用父类的方法
return base.prototype[prop].apply(this, arguments);
},
_superApply = function(args) {
return base.prototype[prop].apply(this, args);
};
return function() {
var __super = this._super,
__superApply = this._superApply,
returnValue;
// console.log(prop, value,this,this._super,'===')
// debugger;
//在这里调用父类的函数
this._super = _super;
this._superApply = _superApply; returnValue = value.apply(this, arguments); this._super = __super;
this._superApply = __superApply;
// console.log(this,value,returnValue,prop,'===')
return returnValue;
};
})();
});
// console.info(proxiedPrototype)
// debugger;
//这里是实例化获取的内容
constructor.prototype = $.widget.extend(basePrototype, {
// TODO: remove support for widgetEventPrefix
// always use the name + a colon as the prefix, e.g., draggable:start
// don't prefix for widgets that aren't DOM-based
widgetEventPrefix: existingConstructor ? (basePrototype.widgetEventPrefix || name) : name
}, proxiedPrototype, {
//又一次把constructor指向 constructor 变量
constructor: constructor,
namespace: namespace,
widgetName: name,
widgetFullName: fullName
}); // If this widget is being redefined then we need to find all widgets that
// are inheriting from it and redefine all of them so that they inherit from
// the new version of this widget. We're essentially trying to replace one
// level in the prototype chain.
//这里判定插件是否被使用了。一般来说,都不会被使用的。 //由于插件的开发人员都是我们自己,呵呵
if (existingConstructor) {
$.each(existingConstructor._childConstructors, function(i, child) {
var childPrototype = child.prototype; // redefine the child widget using the same prototype that was
// originally used, but inherit from the new version of the base
$.widget(childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto);
});
// remove the list of existing child constructors from the old constructor
// so the old child constructors can be garbage collected
delete existingConstructor._childConstructors;
} else {
//父类加入当前插件的实例 主要用于作用域链查找 不至于断层
base._childConstructors.push(constructor);
} //将此方法挂在jQuery对象上
$.widget.bridge(name, constructor); return constructor;
}; //扩展jq的extend方法,实际上相似$.extend(true,..) 深复制
$.widget.extend = function(target) {
var input = widget_slice.call(arguments, 1),
inputIndex = 0,
inputLength = input.length,
key, value;
for (; inputIndex < inputLength; inputIndex++) {
for (key in input[inputIndex]) {
value = input[inputIndex][key];
if (input[inputIndex].hasOwnProperty(key) && value !== undefined) {
// Clone objects
if ($.isPlainObject(value)) {
target[key] = $.isPlainObject(target[key]) ? $.widget.extend({}, target[key], value) :
// Don't extend strings, arrays, etc. with objects
$.widget.extend({}, value);
// Copy everything else by reference
} else {
target[key] = value;
}
}
}
}
return target;
}; //bridge 是设计模式的一种,这里将对象转为插件调用
$.widget.bridge = function(name, object) {
var fullName = object.prototype.widgetFullName || name;
//这里就是插件了
//这部分的实现主要做了几个工作,也是制作一个优雅的插件的主要代码
//1、初次实例化时将插件对象缓存在dom上,兴许则可直接调用,避免在同样元素下widget的多实例化。 简单的说,就是一个单例方法。
//2、合并用户提供的默认设置选项options
//3、能够通过调用插件时传递字符串来调用插件内的方法。 如:$('#id').menu('hide') 实际就是实例插件并调用hide()方法。 //4、同一时候限制外部调用“_”下划线的私有方法
$.fn[name] = function(options) {
var isMethodCall = typeof options === "string",
args = widget_slice.call(arguments, 1),
returnValue = this; // allow multiple hashes to be passed on init.
//能够简单觉得是$.extend(true,options,args[0],...),args能够是一个參数或是数组
options = !isMethodCall && args.length ? $.widget.extend.apply(null, [options].concat(args)) : options;
//这里对字符串和对象分别作处理
if (isMethodCall) {
this.each(function() {
var methodValue, instance = $.data(this, fullName);
//假设传递的是instance则将this返回。
if (options === "instance") {
returnValue = instance;
return false;
}
if (!instance) {
return $.error("cannot call methods on " + name + " prior to initialization; " + "attempted to call method '" + options + "'");
}
//这里对私有方法的调用做了限制,直接调用会抛出异常事件
if (!$.isFunction(instance[options]) || options.charAt(0) === "_") {
return $.error("no such method '" + options + "' for " + name + " widget instance");
}
//这里是假设传递的是字符串,则调用字符串方法,并传递相应的參数.
//比方插件有个方法hide(a,b); 有2个參数:a。b
//则调用时$('#id').menu('hide',1,2);//1和2 分别就是參数a和b了。
methodValue = instance[options].apply(instance, args);
if (methodValue !== instance && methodValue !== undefined) {
returnValue = methodValue && methodValue.jquery ? returnValue.pushStack(methodValue.get()) : methodValue;
return false;
}
});
} else {
this.each(function() {
var instance = $.data(this, fullName); if (instance) {
instance.option(options || {});
//这里每次都调用init方法
if (instance._init) {
instance._init();
}
} else {
//缓存插件实例
$.data(this, fullName, new object(options, this));
}
});
} return returnValue;
};
}; //这里是真正的widget基类
$.Widget = function( /* options, element */ ) {};
$.Widget._childConstructors = []; $.Widget.prototype = {
widgetName: "widget",
//用来决定事件的名称和插件提供的callbacks的关联。
// 比方dialog有一个close的callback,当close的callback被运行的时候,一个dialogclose的事件被触发。
// 事件的名称和事件的prefix+callback的名称。widgetEventPrefix 默认就是控件的名称。可是假设事件须要不同的名称也能够被重写。
// 比方一个用户開始拖拽一个元素,我们不想使用draggablestart作为事件的名称,我们想使用dragstart,所以我们能够重写事件的prefix。
// 假设callback的名称和事件的prefix同样,事件的名称将不会是prefix。 // 它阻止像dragdrag一样的事件名称。 widgetEventPrefix: "",
defaultElement: "<div>",
//属性会在创建模块时被覆盖
options: {
disabled: false, // callbacks
create: null
},
_createWidget: function(options, element) {
element = $(element || this.defaultElement || this)[0];
this.element = $(element);
this.uuid = widget_uuid++;
this.eventNamespace = "." + this.widgetName + this.uuid;
this.options = $.widget.extend({}, this.options, this._getCreateOptions(), options); this.bindings = $();
this.hoverable = $();
this.focusable = $(); if (element !== this) {
// debugger
$.data(element, this.widgetFullName, this);
this._on(true, this.element, {
remove: function(event) {
if (event.target === element) {
this.destroy();
}
}
});
this.document = $(element.style ?
// element within the document
element.ownerDocument :
// element is window or document
element.document || element);
this.window = $(this.document[0].defaultView || this.document[0].parentWindow);
} this._create();
//创建插件时,有个create的回调
this._trigger("create", null, this._getCreateEventData());
this._init();
},
_getCreateOptions: $.noop,
_getCreateEventData: $.noop,
_create: $.noop,
_init: $.noop,
//销毁模块:去除绑定事件、去除数据、去除样式、属性
destroy: function() {
this._destroy();
// we can probably remove the unbind calls in 2.0
// all event bindings should go through this._on()
this.element.unbind(this.eventNamespace).removeData(this.widgetFullName)
// support: jquery <1.6.3
// http://bugs.jquery.com/ticket/9413
.removeData($.camelCase(this.widgetFullName));
this.widget().unbind(this.eventNamespace).removeAttr("aria-disabled").removeClass(
this.widgetFullName + "-disabled " + "ui-state-disabled"); // clean up events and states
this.bindings.unbind(this.eventNamespace);
this.hoverable.removeClass("ui-state-hover");
this.focusable.removeClass("ui-state-focus");
},
_destroy: $.noop, widget: function() {
return this.element;
},
//设置选项函数
option: function(key, value) {
var options = key,
parts, curOption, i; if (arguments.length === 0) {
// don't return a reference to the internal hash
//返回一个新的对象。不是内部数据的引用
return $.widget.extend({}, this.options);
} if (typeof key === "string") {
// handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
options = {};
parts = key.split(".");
key = parts.shift();
if (parts.length) {
curOption = options[key] = $.widget.extend({}, this.options[key]);
for (i = 0; i < parts.length - 1; i++) {
curOption[parts[i]] = curOption[parts[i]] || {};
curOption = curOption[parts[i]];
}
key = parts.pop();
if (arguments.length === 1) {
return curOption[key] === undefined ? null : curOption[key];
}
curOption[key] = value;
} else {
if (arguments.length === 1) {
return this.options[key] === undefined ? null : this.options[key];
}
options[key] = value;
}
} this._setOptions(options); return this;
},
_setOptions: function(options) {
var key; for (key in options) {
this._setOption(key, options[key]);
} return this;
},
_setOption: function(key, value) {
this.options[key] = value; if (key === "disabled") {
this.widget().toggleClass(this.widgetFullName + "-disabled", !! value); // If the widget is becoming disabled, then nothing is interactive
if (value) {
this.hoverable.removeClass("ui-state-hover");
this.focusable.removeClass("ui-state-focus");
}
} return this;
}, enable: function() {
return this._setOptions({
disabled: false
});
},
disable: function() {
return this._setOptions({
disabled: true
});
}, _on: function(suppressDisabledCheck, element, handlers) {
var delegateElement, instance = this; // no suppressDisabledCheck flag, shuffle arguments
if (typeof suppressDisabledCheck !== "boolean") {
handlers = element;
element = suppressDisabledCheck;
suppressDisabledCheck = false;
} // no element argument, shuffle and use this.element
if (!handlers) {
handlers = element;
element = this.element;
delegateElement = this.widget();
} else {
// accept selectors, DOM elements
element = delegateElement = $(element);
this.bindings = this.bindings.add(element);
} $.each(handlers, function(event, handler) {
function handlerProxy() {
// allow widgets to customize the disabled handling
// - disabled as an array instead of boolean
// - disabled class as method for disabling individual parts
if (!suppressDisabledCheck && (instance.options.disabled === true || $(this).hasClass("ui-state-disabled"))) {
return;
}
return (typeof handler === "string" ? instance[handler] : handler).apply(instance, arguments);
} // copy the guid so direct unbinding works
if (typeof handler !== "string") {
handlerProxy.guid = handler.guid = handler.guid || handlerProxy.guid || $.guid++;
} var match = event.match(/^([\w:-]*)\s*(.*)$/),
eventName = match[1] + instance.eventNamespace,
selector = match[2];
if (selector) {
delegateElement.delegate(selector, eventName, handlerProxy);
} else {
element.bind(eventName, handlerProxy);
}
});
}, _off: function(element, eventName) {
eventName = (eventName || "").split(" ").join(this.eventNamespace + " ") + this.eventNamespace;
element.unbind(eventName).undelegate(eventName);
}, _delay: function(handler, delay) {
function handlerProxy() {
return (typeof handler === "string" ? instance[handler] : handler).apply(instance, arguments);
}
var instance = this;
return setTimeout(handlerProxy, delay || 0);
}, _hoverable: function(element) {
this.hoverable = this.hoverable.add(element);
this._on(element, {
mouseenter: function(event) {
$(event.currentTarget).addClass("ui-state-hover");
},
mouseleave: function(event) {
$(event.currentTarget).removeClass("ui-state-hover");
}
});
}, _focusable: function(element) {
this.focusable = this.focusable.add(element);
this._on(element, {
focusin: function(event) {
$(event.currentTarget).addClass("ui-state-focus");
},
focusout: function(event) {
$(event.currentTarget).removeClass("ui-state-focus");
}
});
}, _trigger: function(type, event, data) {
var prop, orig, callback = this.options[type]; data = data || {};
event = $.Event(event);
event.type = (type === this.widgetEventPrefix ? type : this.widgetEventPrefix + type).toLowerCase();
// the original event may come from any element
// so we need to reset the target on the new event
event.target = this.element[0]; // copy original event properties over to the new event
orig = event.originalEvent;
if (orig) {
for (prop in orig) {
if (!(prop in event)) {
event[prop] = orig[prop];
}
}
} this.element.trigger(event, data);
return !($.isFunction(callback) && callback.apply(this.element[0], [event].concat(data)) === false || event.isDefaultPrevented());
}
}; $.each({
show: "fadeIn",
hide: "fadeOut"
}, function(method, defaultEffect) {
$.Widget.prototype["_" + method] = function(element, options, callback) {
if (typeof options === "string") {
options = {
effect: options
};
}
var hasOptions, effectName = !options ? method : options === true || typeof options === "number" ? defaultEffect : options.effect || defaultEffect;
options = options || {};
if (typeof options === "number") {
options = {
duration: options
};
}
hasOptions = !$.isEmptyObject(options);
options.complete = callback;
if (options.delay) {
element.delay(options.delay);
}
if (hasOptions && $.effects && $.effects.effect[effectName]) {
element[method](options);
} else if (effectName !== method && element[effectName]) {
element[effectName](options.duration, options.easing, callback);
} else {
element.queue(function(next) {
$(this)[method]();
if (callback) {
callback.call(element[0]);
}
next();
});
}
};
}); return $.widget; }));

jquery ui widget 源代码分析的更多相关文章

  1. Jquery ui widget开发

    Jquery ui 提供了一些基本的widget,但是他提供了很好的机制来创建widget.在jquery css framework中包含了基本的css样式(视觉和感觉诸如颜色,字体大小,图标等), ...

  2. 使用 jQuery UI Widget Factory 编写有状态的插件(Stateful Plugins)

    使用 jQuery UI Widget Factory 编写有状态的插件(Stateful Plugins) Note 这一章节的内容是基于 Scott Gonzalez 一篇博客 Building ...

  3. JQuery UI Widget Factory官方Demo

    <!doctype html> <html lang="en"> <head> <meta charset="utf-8&quo ...

  4. jQuery UI Widget(1.8.1)工作原理--转载

    先看下代码的相关注释: /*! * jQuery UI Widget 1.8.1 * * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/abo ...

  5. jQuery UI Widget 原理

    先看下代码的相关注释: /*! * jQuery UI Widget 1.8.1 * * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/abo ...

  6. jquery的widget源代码剖析

    dialog_obj(别名): Widget_obj(别名): 调用widget方法 $.widget("ui.dialog",dialog_obj); // jquery.ui. ...

  7. 通过扩展jQuery UI Widget Factory实现手动调整Accordion高度

    □ 实现Accordion高度一致 <head> <meta name="viewport" content="width=device-width&q ...

  8. jQuery UI Widget(1.8.1)工作原理

    /*! * jQuery UI Widget 1.8.1 * * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) * Dual l ...

  9. jQuery UI Widget Factory

    https://learn.jquery.com/jquery-ui/widget-factory/ The jQuery UI Widget Factory is an extensible bas ...

随机推荐

  1. Cocos2D研究院之CCNode详解(三)

    http://www.xuanyusong.com/archives/950 上一章我们了解了cocos2d的项目路径以及工作原理,这次作者要真刀真枪地讲解代码了,咱们先来看看cocos2d最常用.也 ...

  2. 第五步:Lucene创建索引

    package cn.lucene; import java.io.IOException; import java.nio.file.Paths; import java.util.Date; im ...

  3. Python爬取中国天气网

    Python爬取中国天气网 基于requests库制作的爬虫. 使用方法:打开终端输入 “python3 weather.py 北京(或你所在的城市)" 程序正常运行需要在同文件夹下加入一个 ...

  4. xcode里面使用Memory Leaks和Instruments检测内存泄漏

    教程截图: 作为一名无证程序员,无论你多么精通Objective-C的内存管理,随着时间的推移,你也不可避免的犯内存相关的错误.但通常因为代码量太大,以至于你不可能一行一行的去排除(等你解决完,你设计 ...

  5. ios中调用WCF

    例子比较简单 记录下思路 1.接口中定义 实体和方法声明 //登录信息        [OperationContract]        [WebInvoke(UriTemplate = " ...

  6. 磁盘爆满导致MySQL无法启动:Disk is full writing './mysql-bin.~rec~' (Errcode: 28). Waiting for someone to free space...

    今天收到监控邮件说博客访问失败.打开页面一看,硕大的502 Bad Gateway,ping了一下VPS发现是通的,SSH连接上去看了下Nginx日志发现没问题,重启lnmp的时候发现Mysql起不来 ...

  7. 愤怒的TryCatch

    本文地址:http://www.cnblogs.com/likeli/p/5719230.html 前言 本文不提供任何搭梯子之类的内容,我在这里仅仅讨论网络爬虫遇到的IP封杀,然后使用Tor如何对抗 ...

  8. CURL简单使用

    学习地址:https://yq.aliyun.com/articles/33262 curl的简单使用步骤 要使用cURL来发送url请求,具体步骤大体分为以下四步: 1.初始化2.设置请求选项3.执 ...

  9. centos7 安装LNMP(php7)之php7.0 yum安装

    http://www.jianshu.com/p/35f21210668a 安装过程参考上面的网址

  10. 超级惊艳 10款HTML5动画特效推荐[转]

    ylbtech_html5_demo 今天我们要来推荐 10 款超级惊艳的 HTML5 动画特效,有一些是基于 CSS3 和 jQuery 的,比较实用,特别是前几个 HTML5 动画,简直酷毙了,现 ...