1 jQuery.extend({
2 // 显示或操作在匹配元素上执行的函数队列
3 queue: function (elem, type, data) {
4 var queue;
5
6 if (elem) {
7 // type没定义的话就默认使用fxqueue
8 type = (type || 'fx') + 'queue';
9 // 先获取elem上的缓存数据
10 queue = jQuery._data(elem, type);
11
12 if (data) {
13 // 如果没有缓存数据或者data是数组
14 // 则给elem设置缓存数据
15 if (!queue || jQuery.isArray(data)) {
16 queue = jQuery._data(elem, type, jQuery.makeArray(data));
17 } else {
18 // 否则存在缓存数据数组直接入栈
19 queue.push(data);
20 }
21 }
22 return queue || [];
23 }
24 },
25 // 从队列最前端移除一个队列函数,并执行它
26 dequeue: function (elem, type) {
27 type = type || 'fx';
28
29 var
30 // 获取缓存队列
31 queue = jQuery.queue(elem, type),
32 startLength = queue.length,
33 // 取得队列的第一个元素
34 fn = queue.shift(),
35 // 钩子对象,如果获取不到缓存的钩子对象,就设置新的钩子对象并返回
36 hooks = jQuery._queueHooks(elem, type),
37 // 用来执行下一个队列
38 next = function () {
39 jQuery.dequeue(elem, type);
40 };
41
42 // 如果第一个元素是"inprogress",取下一个元素,长度相应减1
43 if (fn === 'inprogress') {
44 fn = queue.shift();
45 startLength--;
46 }
47
48 // 为钩子对象添加cur方法
49 hooks.cur = fn;
50 // 如果此时fn不为null || false || undefined
51 if (fn) {
52 // 如果type为“fx”,给队列添加"inprogress",防止自动出列(dequeue)
53 if (type === 'fx') {
54 queue.unshift('inprogress');
55 }
56
57 // 删除上一个队列的stop函数
58 delete hooks.stop;
59 // 执行fn函数,并且把下一个队列函数设置为fn的第一个参数
60 /*
61 可以这样使用:
62 $(document.body).queue('test', function(next){
63 console.log(11);
64 next();
65 });
66 $(document.body).queue('test', function(){
67 console.log(22);
68 });
69 $(document.body).dequeue('test');
70 */
71 fn.call(elem, next, hooks);
72 }
73
74 // 如果队列长度为0且存在钩子对象,则删除缓存
75 if (!startLength && hooks) {
76 hooks.empty.fire();
77 }
78 },
79 // 私有方法,生成一个队列钩子对象(即从缓存数据中获取的队列钩子)
80 // 或者设置缓存队列钩子对象,这个对象的empty属性是一个Callbacks对象,这里的作用是删除缓存队列和缓存队列钩子的数据
81 _queueHooks: function (elem, type) {
82 var key = type + 'queueHooks';
83 return jQuery._data(elem, key) || jQuery._data(elem, key, {
84 empty: jQuery.Callbacks('once memory').add(function () {
85 jQuery._removeData(elem, type + 'queue');
86 jQuery._removeData(elem, key);
87 })
88 });
89 }
90 });
91
92 jQuery.fn.extend({
93 queue: function (type, data) {
94 var setter = 2;
95
96 if (typeof type !== 'string') {
97 data = type;
98 type = 'fx';
99 setter--;
100 }
101
102 /*
103 当满足这个条件的有以下几个情况:
104 1.没有参数
105 2.参数只有一个,且type是字符串
106 */
107 if (arguments.length < setter) {
108 return jQuery.queue(this[0], type);
109 }
110
111 // 其他情况
112 return data === undefined ?
113 this :
114 this.each(function () {
115 var queue = jQuery.queue(this, type, data);
116
117 jQuery._queueHooks(this, type);
118
119 // 如果是动画队列且第一个元素不是"inprogres",
120 // 就出列并执行下一个元素
121 if (type === 'fx' && queue[0] !== 'inprogress') {
122 jQuery.dequeue(this, type);
123 }
124 });
125 },
126 dequeue: function (type) {
127 return this.each(function () {
128 jQuery.dequeue(this, type);
129 });
130 },
131 // TODO
132 delay: function (time, type) {
133 time = jQuery.fx ? jQuery.fx.speeds[time] || time : time;
134 type = type || 'fx';
135
136 return this.queue(type, function (next, hooks) {
137 var timeout = setTimeout(next, time);
138 hooks.stop = function () {
139 clearTimeout(timeout);
140 };
141 });
142 },
143 clearQueue: function (type) {
144 return this.queue(type || 'fx', []);
145 },
146 promise: function (type, obj) {
147 var tmp,
148 count = 1,
149 defer = jQuery.Deferred(),
150 elements = this,
151 i = this.length,
152 resolve = function () {
153 if (!(--count)) {
154 defer.resolveWith(elements, [elements]);
155 }
156 };
157
158 if (typeof type !== 'string') {
159 obj = type;
160 type = undefined;
161 }
162 type = type || 'fx';
163
164 while (i--) {
165 tmp = jQuery._data(elements[i], type + 'queueHooks');
166 if (tmp && tmp.empty) {
167 count++;
168 tmp.empty.add(resolve);
169 }
170 }
171 resolve();
172 return defer.promise(obj);
173 }
174 });