zepto.extend.js
17.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
/**
* @name zepto.extend
* @file 对Zepto做了些扩展,以下所有JS都依赖与此文件
* @desc 对Zepto一些扩展,组件必须依赖
* @import core/zepto.js
*/
(function($){
$.extend($, {
contains: function(parent, node) {
/**
* modified by chenluyang
* @reason ios4 safari下,无法判断包含文字节点的情况
* @original return parent !== node && parent.contains(node)
*/
return parent.compareDocumentPosition
? !!(parent.compareDocumentPosition(node) & 16)
: parent !== node && parent.contains(node)
}
});
})(Zepto);
//Core.js
;(function($, undefined) {
//扩展在Zepto静态类上
$.extend($, {
/**
* @grammar $.toString(obj) ⇒ string
* @name $.toString
* @desc toString转化
*/
toString: function(obj) {
return Object.prototype.toString.call(obj);
},
/**
* @desc 从集合中截取部分数据,这里说的集合,可以是数组,也可以是跟数组性质很像的对象,比如arguments
* @name $.slice
* @grammar $.slice(collection, [index]) ⇒ array
* @example (function(){
* var args = $.slice(arguments, 2);
* console.log(args); // => [3]
* })(1, 2, 3);
*/
slice: function(array, index) {
return Array.prototype.slice.call(array, index || 0);
},
/**
* @name $.later
* @grammar $.later(fn, [when, [periodic, [context, [data]]]]) ⇒ timer
* @desc 延迟执行fn
* **参数:**
* - ***fn***: 将要延时执行的方法
* - ***when***: *可选(默认 0)* 什么时间后执行
* - ***periodic***: *可选(默认 false)* 设定是否是周期性的执行
* - ***context***: *可选(默认 undefined)* 给方法设定上下文
* - ***data***: *可选(默认 undefined)* 给方法设定传入参数
* @example $.later(function(str){
* console.log(this.name + ' ' + str); // => Example hello
* }, 250, false, {name:'Example'}, ['hello']);
*/
later: function(fn, when, periodic, context, data) {
return window['set' + (periodic ? 'Interval' : 'Timeout')](function() {
fn.apply(context, data);
}, when || 0);
},
/**
* @desc 解析模版
* @grammar $.parseTpl(str, data) ⇒ string
* @name $.parseTpl
* @example var str = "<p><%=name%></p>",
* obj = {name: 'ajean'};
* console.log($.parseTpl(str, data)); // => <p>ajean</p>
*/
parseTpl: function(str, data) {
var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' + 'with(obj||{}){__p.push(\'' + str.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/<%=([\s\S]+?)%>/g, function(match, code) {
return "'," + code.replace(/\\'/g, "'") + ",'";
}).replace(/<%([\s\S]+?)%>/g, function(match, code) {
return "');" + code.replace(/\\'/g, "'").replace(/[\r\n\t]/g, ' ') + "__p.push('";
}).replace(/\r/g, '\\r').replace(/\n/g, '\\n').replace(/\t/g, '\\t') + "');}return __p.join('');";
var func = new Function('obj', tmpl);
return data ? func(data) : func;
},
/**
* @desc 减少执行频率, 多次调用,在指定的时间内,只会执行一次。
* **options:**
* - ***delay***: 延时时间
* - ***fn***: 被稀释的方法
* - ***debounce_mode***: 是否开启防震动模式, true:start, false:end
*
* <code type="text">||||||||||||||||||||||||| (空闲) |||||||||||||||||||||||||
* X X X X X X X X X X X X</code>
*
* @grammar $.throttle(delay, fn) ⇒ function
* @name $.throttle
* @example var touchmoveHander = function(){
* //....
* }
* //绑定事件
* $(document).bind('touchmove', $.throttle(250, touchmoveHander));//频繁滚动,每250ms,执行一次touchmoveHandler
*
* //解绑事件
* $(document).unbind('touchmove', touchmoveHander);//注意这里面unbind还是touchmoveHander,而不是$.throttle返回的function, 当然unbind那个也是一样的效果
*
*/
throttle: function(delay, fn, debounce_mode) {
var last = 0,
timeId;
if (typeof fn !== 'function') {
debounce_mode = fn;
fn = delay;
delay = 250;
}
function wrapper() {
var that = this,
period = Date.now() - last,
args = arguments;
function exec() {
last = Date.now();
fn.apply(that, args);
};
function clear() {
timeId = undefined;
};
if (debounce_mode && !timeId) {
// debounce模式 && 第一次调用
exec();
}
timeId && clearTimeout(timeId);
if (debounce_mode === undefined && period > delay) {
// throttle, 执行到了delay时间
exec();
} else {
// debounce, 如果是start就clearTimeout
timeId = setTimeout(debounce_mode ? clear : exec, debounce_mode === undefined ? delay - period : delay);
}
};
// for event bind | unbind
wrapper._zid = fn._zid = fn._zid || $.proxy(fn)._zid;
return wrapper;
},
/**
* @desc 减少执行频率, 在指定的时间内, 多次调用,只会执行一次。
* **options:**
* - ***delay***: 延时时间
* - ***fn***: 被稀释的方法
* - ***t***: 指定是在开始处执行,还是结束是执行, true:start, false:end
*
* 非at_begin模式
* <code type="text">||||||||||||||||||||||||| (空闲) |||||||||||||||||||||||||
* X X</code>
* at_begin模式
* <code type="text">||||||||||||||||||||||||| (空闲) |||||||||||||||||||||||||
* X X </code>
*
* @grammar $.debounce(delay, fn[, at_begin]) ⇒ function
* @name $.debounce
* @example var touchmoveHander = function(){
* //....
* }
* //绑定事件
* $(document).bind('touchmove', $.debounce(250, touchmoveHander));//频繁滚动,只要间隔时间不大于250ms, 在一系列移动后,只会执行一次
*
* //解绑事件
* $(document).unbind('touchmove', touchmoveHander);//注意这里面unbind还是touchmoveHander,而不是$.debounce返回的function, 当然unbind那个也是一样的效果
*/
debounce: function(delay, fn, t) {
return fn === undefined ? $.throttle(250, delay, false) : $.throttle(delay, fn, t === undefined ? false : t !== false);
}
});
/**
* 扩展类型判断
* @param {Any} obj
* @see isString, isBoolean, isRegExp, isNumber, isDate, isObject, isNull, isUdefined
*/
/**
* @name $.isString
* @grammar $.isString(val) ⇒ Boolean
* @desc 判断变量类型是否为***String***
* @example console.log($.isString({}));// => false
* console.log($.isString(123));// => false
* console.log($.isString('123'));// => true
*/
/**
* @name $.isBoolean
* @grammar $.isBoolean(val) ⇒ Boolean
* @desc 判断变量类型是否为***Boolean***
* @example console.log($.isBoolean(1));// => false
* console.log($.isBoolean('true'));// => false
* console.log($.isBoolean(false));// => true
*/
/**
* @name $.isRegExp
* @grammar $.isRegExp(val) ⇒ Boolean
* @desc 判断变量类型是否为***RegExp***
* @example console.log($.isRegExp(1));// => false
* console.log($.isRegExp('test'));// => false
* console.log($.isRegExp(/test/));// => true
*/
/**
* @name $.isNumber
* @grammar $.isNumber(val) ⇒ Boolean
* @desc 判断变量类型是否为***Number***
* @example console.log($.isNumber('123'));// => false
* console.log($.isNumber(true));// => false
* console.log($.isNumber(123));// => true
*/
/**
* @name $.isDate
* @grammar $.isDate(val) ⇒ Boolean
* @desc 判断变量类型是否为***Date***
* @example console.log($.isDate('123'));// => false
* console.log($.isDate('2012-12-12'));// => false
* console.log($.isDate(new Date()));// => true
*/
/**
* @name $.isObject
* @grammar $.isObject(val) ⇒ Boolean
* @desc 判断变量类型是否为***Object***
* @example console.log($.isObject('123'));// => false
* console.log($.isObject(true));// => false
* console.log($.isObject({}));// => true
*/
/**
* @name $.isNull
* @grammar $.isNull(val) ⇒ Boolean
* @desc 判断变量类型是否为***null***
* @example console.log($.isNull(false));// => false
* console.log($.isNull(0));// => false
* console.log($.isNull(null));// => true
*/
/**
* @name $.isUndefined
* @grammar $.isUndefined(val) ⇒ Boolean
* @desc 判断变量类型是否为***undefined***
* @example
* console.log($.isUndefined(false));// => false
* console.log($.isUndefined(0));// => false
* console.log($.isUndefined(a));// => true
*/
$.each("String Boolean RegExp Number Date Object Null Undefined".split(" "), function( i, name ){
var fn;
if( 'is' + name in $ ) return;//already defined then ignore.
switch (name) {
case 'Null':
fn = function(obj){ return obj === null; };
break;
case 'Undefined':
fn = function(obj){ return obj === undefined; };
break;
default:
fn = function(obj){ return new RegExp(name + ']', 'i').test( toString(obj) )};
}
$['is'+name] = fn;
});
var toString = $.toString;
})(Zepto);
//Support.js
(function($, undefined) {
var ua = navigator.userAgent,
na = navigator.appVersion,
br = $.browser;
/**
* @name $.browser
* @desc 扩展zepto中对browser的检测
*
* **可用属性**
* - ***qq*** 检测qq浏览器
* - ***chrome*** 检测chrome浏览器
* - ***uc*** 检测uc浏览器
* - ***version*** 检测浏览器版本
*
* @example
* if ($.browser.qq) { //在qq浏览器上打出此log
* console.log('this is qq browser');
* }
*/
$.extend( br, {
qq: /qq/i.test(ua),
uc: /UC/i.test(ua) || /UC/i.test(na)
} );
br.uc = br.uc || !br.qq && !br.chrome && !br.firefox && !/safari/i.test(ua);
try {
br.version = br.uc ? na.match(/UC(?:Browser)?\/([\d.]+)/)[1] : br.qq ? ua.match(/MQQBrowser\/([\d.]+)/)[1] : br.version;
} catch (e) {}
/**
* @name $.support
* @desc 检测设备对某些属性或方法的支持情况
*
* **可用属性**
* - ***orientation*** 检测是否支持转屏事件,UC中存在orientaion,但转屏不会触发该事件,故UC属于不支持转屏事件(iOS 4上qq, chrome都有这个现象)
* - ***touch*** 检测是否支持touch相关事件
* - ***cssTransitions*** 检测是否支持css3的transition
* - ***has3d*** 检测是否支持translate3d的硬件加速
*
* @example
* if ($.support.has3d) { //在支持3d的设备上使用
* console.log('you can use transtion3d');
* }
*/
$.support = $.extend($.support || {}, {
orientation: !(br.uc || (parseFloat($.os.version)<5 && (br.qq || br.chrome))) && !($.os.android && parseFloat($.os.version) > 3) && "orientation" in window && "onorientationchange" in window,
touch: "ontouchend" in document,
cssTransitions: "WebKitTransitionEvent" in window,
has3d: 'WebKitCSSMatrix' in window && 'm11' in new WebKitCSSMatrix()
});
})(Zepto);
//Event.js
(function($) {
/**
* @name $.matchMedia
* @grammar $.matchMedia(query) ⇒ MediaQueryList
* @desc 是原生的window.matchMedia方法的polyfill,对于不支持matchMedia的方法系统和浏览器,按照[w3c window.matchMedia](http://www.w3.org/TR/cssom-view/#dom-window-matchmedia)的接口
* 定义,对matchMedia方法进行了封装。原理是用css media query及transitionEnd事件来完成的。在页面中插入media query样式及元素,当query条件满足时改变该元素样式,同时这个样式是transition作用的属性,
* 满足条件后即会触发transitionEnd,由此创建MediaQueryList的事件监听。由于transition的duration time为0.001ms,故若直接使用MediaQueryList对象的matches去判断当前是否与query匹配,会有部分延迟,
* 建议注册addListener的方式去监听query的改变。$.matchMedia的详细实现原理及采用该方法实现的转屏统一解决方案详见
* [GMU Pages: 转屏解决方案($.matchMedia)](https://github.com/gmuteam/GMU/wiki/%E8%BD%AC%E5%B1%8F%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88$.matchMedia)
*
* **MediaQueryList对象包含的属性**
* - ***matches*** 是否满足query
* - ***query*** 查询的css query,类似\'screen and (orientation: portrait)\'
* - ***addListener*** 添加MediaQueryList对象监听器,接收回调函数,回调参数为MediaQueryList对象
* - ***removeListener*** 移除MediaQueryList对象监听器
*
* @example
* $.matchMedia('screen and (orientation: portrait)').addListener(fn);
*/
$.matchMedia = (function() {
var mediaId = 0,
cls = 'gmu-media-detect',
transitionEnd = $.fx.transitionEnd,
cssPrefix = $.fx.cssPrefix,
$style = $('<style></style>').append('.' + cls + '{' + cssPrefix + 'transition: width 0.001ms; width: 0; position: relative; bottom: -999999px;}\n').appendTo('head');
return function (query) {
var id = cls + mediaId++,
$mediaElem = $('<div class="' + cls + '" id="' + id + '"></div>').appendTo('body'),
listeners = [],
ret;
$style.append('@media ' + query + ' { #' + id + ' { width: 100px; } }\n') ; //原生matchMedia也需要添加对应的@media才能生效
// if ('matchMedia' in window) {
// return window.matchMedia(query);
// }
$mediaElem.on(transitionEnd, function() {
ret.matches = $mediaElem.width() === 100;
$.each(listeners, function (i,fn) {
$.isFunction(fn) && fn.call(ret, ret);
});
});
ret = {
matches: $mediaElem.width() === 100 ,
media: query,
addListener: function (callback) {
listeners.push(callback);
return this;
},
removeListener: function (callback) {
var index = listeners.indexOf(callback);
~index && listeners.splice(index, 1);
return this;
}
};
return ret;
};
}());
$(function () {
var handleOrtchange = function (mql) {
if ( state !== mql.matches ) {
$( window ).trigger( 'ortchange' );
state = mql.matches;
}
},
state = true;
$.mediaQuery = {
ortchange: 'screen and (width: ' + window.innerWidth + 'px)'
};
$.matchMedia($.mediaQuery.ortchange).addListener(handleOrtchange);
});
/**
* @name Trigger Events
* @theme event
* @desc 扩展的事件
* - ***scrollStop*** : scroll停下来时触发, 考虑前进或者后退后scroll事件不触发情况。
* - ***ortchange*** : 当转屏的时候触发,兼容uc和其他不支持orientationchange的设备,利用css media query实现,解决了转屏延时及orientation事件的兼容性问题
* @example $(document).on('scrollStop', function () { //scroll停下来时显示scrollStop
* console.log('scrollStop');
* });
*
* $(window).on('ortchange', function () { //当转屏的时候触发
* console.log('ortchange');
* });
*/
/** dispatch scrollStop */
function _registerScrollStop(){
$(window).on('scroll', $.debounce(80, function() {
$(document).trigger('scrollStop');
}, false));
}
//在离开页面,前进或后退回到页面后,重新绑定scroll, 需要off掉所有的scroll,否则scroll时间不触发
function _touchstartHander() {
$(window).off('scroll');
_registerScrollStop();
}
_registerScrollStop();
$(window).on('pageshow', function(e){
if(e.persisted) {//如果是从bfcache中加载页面
$(document).off('touchstart', _touchstartHander).one('touchstart', _touchstartHander);
}
});
})(Zepto);