下面是对Jquery几个经常用到的地方进行的增强。
功能是参考百度七巧板JS框架来完成的。
一、页面属性
$.page.getHeight():获取页面高度
$.page.getWidth():获取页面宽度$.url.queryToJson(url):解析目标URL中的参数成json对象
插件源码:
;(function($){ $.extend({ hasClass:function(targetClassName){ var className = $(this).attr("class"); var classArray = className.split(" "); if(className && classArray.length == 0){ if(targetClassName == className) return true; }else if(className && classArray.length > 0){ for(var i=0; i<=classArray.length-1; i++){ if(classArray[i] == targetClassName) return true; } } return false; }, getDocument:function (element) { return $.lang.isElement(element) ? element : element.ownerDocument || element.document; } }); })(jQuery); ;(function($){ $.lang = $.lang || {}; $.lang.isFunction = function (source) { // chrome下,'function' == typeof /a/ 为true. return '[object Function]' == Object.prototype.toString.call(source); }; $.lang.isString = function (source) { return '[object String]' == Object.prototype.toString.call(source); }; })(jQuery); ;(function($){ $.page = $.page || {}; $.page.xy = {x:0, y:0};//当前鼠标坐标 $.page.getHeight = $.page.getHeight || function(){ var doc = document, body = doc.body, html = doc.documentElement, client = doc.compatMode == 'BackCompat' ? body : doc.documentElement; return Math.max(html.scrollHeight, body.scrollHeight, client.clientHeight); }; $.page.getWidth = $.page.getWidth || function () { var doc = document, body = doc.body, html = doc.documentElement, client = doc.compatMode == 'BackCompat' ? body : doc.documentElement; return Math.max(html.scrollWidth, body.scrollWidth, client.clientWidth); }; $.page.createStyleSheet = $.page.createStyleSheet || function(options){ var op = options || {}, doc = op.document || document, s; if ($.browser.ie) { //修复ie下会请求一个undefined的bug berg 2010/08/27 if(!op.url) op.url = ""; return doc.createStyleSheet(op.url, op.index); } else { s = "<style type='text/css'></style>"; op.url && (s="<link type='text/css' rel='stylesheet' href='"+op.url+"'/>"); $("HEAD:eq(0)").after(s); } }; $.page.getScrollTop = $.page.getScrollTop || function(){ var d = document; return window.pageYOffset || d.documentElement.scrollTop || d.body.scrollTop; }; $.page.getScrollLeft= $.page.getScrollLeft || function () { var d = document; return window.pageXOffset || d.documentElement.scrollLeft || d.body.scrollLeft; }; $.page.getViewHeight = $.page.getViewHeight || function () { var doc = document, client = doc.compatMode == 'BackCompat' ? doc.body : doc.documentElement; return client.clientHeight; }; $.page.getViewWidth = function () { var doc = document, client = doc.compatMode == 'BackCompat' ? doc.body : doc.documentElement; return client.clientWidth; }; $(document).bind("mousemove", function(e){ e = window.event || e; $.page.xy.x = e.clientX; $.page.xy.y = e.clientY; //console.log(e.clientX+","+e.clientY); }); $.page.getMousePosition = $.page.getMousePosition || function(){ return { x : $.page.getScrollLeft() + $.page.xy.x, y : $.page.getScrollTop() + $.page.xy.y }; }; /** * 获取目标元素相对于整个文档左上角的位置 * @grammar $.dom.getPosition(element) * @param {HTMLElement|string} element 目标元素或目标元素的id * * @returns {Object} 目标元素的位置,键值为top和left的Object。 */ $.page.getPosition = function (element) { var doc = $.getDocument(element), browser = $.browser // Gecko 1.9版本以下用getBoxObjectFor计算位置 // 但是某些情况下是有bug的 // 对于这些有bug的情况 // 使用递归查找的方式 BUGGY_GECKO_BOX_OBJECT = browser.isGecko > 0 && doc.getBoxObjectFor && $(element).css('position') == 'absolute' && (element.style.top === '' || element.style.left === ''), pos = {"left":0,"top":0}, viewport = (browser.ie && !browser.isStrict) ? doc.body : doc.documentElement, parent; var box; if(element == viewport){ return pos; } if(element.getBoundingClientRect){ // IE and Gecko 1.9+ //当HTML或者BODY有border width时, 原生的getBoundingClientRect返回值是不符合预期的 //考虑到通常情况下 HTML和BODY的border只会设成0px,所以忽略该问题. doc = document; box = element.getBoundingClientRect(); pos.left = Math.floor(box.left) + Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft); pos.top = Math.floor(box.top) + Math.max(doc.documentElement.scrollTop, doc.body.scrollTop); // IE会给HTML元素添加一个border,默认是medium(2px) // 但是在IE 6 7 的怪异模式下,可以被html { border: 0; } 这条css规则覆盖 // 在IE7的标准模式下,border永远是2px,这个值通过clientLeft 和 clientTop取得 // 但是。。。在IE 6 7的怪异模式,如果用户使用css覆盖了默认的medium // clientTop和clientLeft不会更新 pos.left -= doc.documentElement.clientLeft; pos.top -= doc.documentElement.clientTop; var htmlDom = doc.body, // 在这里,不使用element.style.borderLeftWidth,只有computedStyle是可信的 htmlBorderLeftWidth = parseInt($(htmlDom).css('borderLeftWidth')), htmlBorderTopWidth = parseInt($(htmlDom).css('borderTopWidth')); if(browser.ie && !browser.isStrict){ pos.left -= isNaN(htmlBorderLeftWidth) ? 2 : htmlBorderLeftWidth; pos.top -= isNaN(htmlBorderTopWidth) ? 2 : htmlBorderTopWidth; } /* * 因为firefox 3.6和4.0在特定页面下(场景待补充)都会出现1px偏移,所以暂时移除该逻辑分支 * 如果 2.0版本时firefox仍存在问题,该逻辑分支将彻底移除. by rocy 2011-01-20 } else if (doc.getBoxObjectFor && !BUGGY_GECKO_BOX_OBJECT){ // gecko 1.9- // 1.9以下的Gecko,会忽略ancestors的scroll值 // https://bugzilla.mozilla.org/show_bug.cgi?id=328881 and // https://bugzilla.mozilla.org/show_bug.cgi?id=330619 box = doc.getBoxObjectFor(element); var vpBox = doc.getBoxObjectFor(viewport); pos.left = box.screenX - vpBox.screenX; pos.top = box.screenY - vpBox.screenY; */ } else { // safari/opera/firefox parent = element; do { pos.left += parent.offsetLeft; pos.top += parent.offsetTop; // safari里面,如果遍历到了一个fixed的元素,后面的offset都不准了 if (browser.isWebkit > 0 && $(parent).css('position') == 'fixed') { pos.left += doc.body.scrollLeft; pos.top += doc.body.scrollTop; break; } parent = parent.offsetParent; } while (parent && parent != element); // 对body offsetTop的修正 if(browser.opera > 0 || (browser.isWebkit > 0 && $(parent).css('position') == 'absolute')){ pos.top -= doc.body.offsetTop; } // 计算除了body的scroll parent = element.offsetParent; while (parent && parent != doc.body) { pos.left -= parent.scrollLeft; // see https://bugs.opera.com/show_bug.cgi?id=249965 // if (!b.opera || parent.tagName != 'TR') { if (!browser.opera || parent.tagName != 'TR') { pos.top -= parent.scrollTop; } parent = parent.offsetParent; } } return pos; }; /** * 延迟加载图片. 默认只加载可见高度以上的图片, 随着窗口滚动加载剩余图片.注意: 仅支持垂直方向. * @grammar $.page.lazyLoadImage([options]) * @param {Object} options * @param {String} [options.className] 延迟加载的IMG的className,如果不传入该值将延迟加载所有IMG. * @param {Number} [options.preloadHeight] 预加载的高度, 可见窗口下该高度内的图片将被加载. * @param {String} [options.placeHolder] 占位图url. * @param {Function} [options.onlazyload] 延迟加载回调函数,在实际加载时触发. */ $.page.lazyLoadImage = function(options) { options = options || {}; options.preloadHeight = options.preloadHeight || 0; $(document).ready(function() { var imgs = document.getElementsByTagName('IMG'), targets = imgs, len = imgs.length, i = 0, viewOffset = getLoadOffset(), srcAttr = 'data-tangram-ori-src', target; //避免循环中每次都判断className if (options.className) { targets = []; for (; i < len; ++i) { if ($(imgs[i]).hasClass(options.className)) { targets.push(imgs[i]); } } } //计算需要加载图片的页面高度 function getLoadOffset() { return $.page.getScrollTop() + $.page.getViewHeight() + options.preloadHeight; } //加载可视图片 for (i = 0, len = targets.length; i < len; ++i) { target = targets[i]; if ($.page.getPosition(target).top > viewOffset) { target.setAttribute(srcAttr, target.src); options.placeHolder ? target.src = options.placeHolder : target.removeAttribute('src'); } } //处理延迟加载 var loadNeeded = function() { var viewOffset = getLoadOffset(), imgSrc, finished = true, i = 0, len = targets.length; for (; i < len; ++i) { target = targets[i]; imgSrc = target.getAttribute(srcAttr); imgSrc && (finished = false); if ($.page.getPosition(target).top < viewOffset && imgSrc) { target.src = imgSrc; target.removeAttribute(srcAttr); $.lang.isFunction(options.onlazyload) && options.onlazyload(target); } } //当全部图片都已经加载, 去掉事件监听 finished && $(window).unbind('scroll', loadNeeded); }; $(window).bind('scroll', loadNeeded); }); }; /** * 动态在页面上加载一个外部css文件 * @grammar $.page.loadCssFile(path) * @param {string} path css文件路径 */ $.page.loadCssFile = function (path) { var element = document.createElement("link"); element.setAttribute("rel", "stylesheet"); element.setAttribute("type", "text/css"); element.setAttribute("href", path); document.getElementsByTagName("head")[0].appendChild(element); }; /** * 动态在页面上加载一个外部js文件 * @grammar $.page.loadJsFile(path) * @param {string} path js文件路径 */ $.page.loadJsFile = function (path) { var element = document.createElement('script'); element.setAttribute('type', 'text/javascript'); element.setAttribute('src', path); element.setAttribute('defer', 'defer'); document.getElementsByTagName("head")[0].appendChild(element); }; /** * * 加载一组资源,支持多种格式资源的串/并行加载,支持每个文件有单独回调函数。 * * @name $.page.load * @function * @grammar $.page.load(resources[, options]) * * @param {Array} resources 资源描述数组,单个resource含如下属性. * @param {String} resources.url 链接地址. * @param {String} [resources.type] 取值["css","js","html"],默认参考文件后缀. * @param {String} [resources.requestType] 取值["dom","ajax"],默认js和css用dom标签,html用ajax. * @param {Function} resources.onload 当前resource加载完成的回调函数,若requestType为ajax,参数为xhr(可能失效),responseText;若requestType为dom,无参数,执行时this为相应dom标签。. * * @param {Object} [options] 可选参数. * @param {Function} [options.onload] 资源全部加载完成的回调函数,无参数。. * @param {Boolean} [options.parallel] 是否并行加载,默认为false,串行。. * @param {Boolean} [ignoreAllLoaded] 全部加载之后不触发回调事件.主要用于内部实现. * * * @remark * //串行实例 * baidu.page.load([ * { url : "http://img.baidu.com/js/tangram-1.3.2.js" }, * {url : "http://xxx.baidu.com/xpath/logicRequire.js", * onload : fnOnRequireLoaded * }, * { url : "http://xxx.baidu.com/xpath/target.js" } * ],{ * onload : fnWhenTargetOK * }); * //并行实例 * baidu.page.load([ * { * url : "http://xxx.baidu.com/xpath/template.html", * onload : fnExtractTemplate * }, * { url : "http://xxx.baidu.com/xpath/style.css"}, * { * url : "http://xxx.baidu.com/xpath/import.php?f=baidu.*", * type : "js" * }, * { * url : "http://xxx.baidu.com/xpath/target.js", * }, * { * url : "http://xxx.baidu.com/xpath/jsonData.js", * requestType : "ajax", * onload : fnExtractData * } * ],{ * parallel : true, * onload : fnWhenEverythingIsOK * }); */ $.page.load = function(resources, options, ignoreAllLoaded) { options = options || {}; var self = $.page.load, cache = self._cache = self._cache || {}, loadingCache = self._loadingCache = self._loadingCache || {}, parallel = options.parallel; function allLoadedChecker() { for (var i = 0, len = resources.length; i < len; ++i) { if (! cache[resources[i].url]) { setTimeout(arguments.callee, 10); return; } } options.onload(); }; function loadByDom(res, callback) { var node, loaded, onready; switch (res.type.toLowerCase()) { case 'css' : node = document.createElement('link'); node.setAttribute('rel', 'stylesheet'); node.setAttribute('type', 'text/css'); break; case 'js' : node = document.createElement('script'); node.setAttribute('type', 'text/javascript'); node.setAttribute('charset', res.charset || self.charset); break; case 'html' : node = document.createElement('iframe'); node.frameBorder = 'none'; break; default : return; } // HTML,JS works on all browsers, CSS works only on IE. onready = function() { if (!loaded && (!this.readyState || this.readyState === 'loaded' || this.readyState === 'complete')) { loaded = true; // 防止内存泄露 $(node).unbind('load', onready); $(node).unbind('readystatechange', onready); //node.onload = node.onreadystatechange = null; callback.call(window, node); } }; $(node).bind('load', onready); $(node).bind('readystatechange', onready); //CSS has no onload event on firefox and webkit platform, so hack it. if (res.type == 'css') { (function() { //避免重复加载 if (loaded) return; try { node.sheet.cssRule; } catch (e) { setTimeout(arguments.callee, 20); return; } loaded = true; callback.call(window, node); })(); } node.href = node.src = res.url; document.getElementsByTagName('head')[0].appendChild(node); } //兼容第一个参数直接是资源地址. $.lang.isString(resources) && (resources = [{url: resources}]); //避免递归出错,添加容错. if (! (resources && resources.length)) return; function loadResources(res) { var url = res.url, shouldContinue = !!parallel, cacheData, callback = function(textOrNode) { //ajax存入responseText,dom存入节点,用于保证onload的正确执行. cache[res.url] = textOrNode; delete loadingCache[res.url]; if ($.lang.isFunction(res.onload)) { //若返回false, 则停止接下来的加载. if (false === res.onload.call(window, textOrNode)) { return; } } //串行时递归执行 !parallel && self(resources.slice(1), options, true); if ((! ignoreAllLoaded) && $.lang.isFunction(options.onload)) { allLoadedChecker(); } }; //默认用后缀名, 并防止后缀名大写 res.type = res.type || url.replace(/^[^\?#]+\.(css|js|html)(\?|#| |$)[^\?#]*/i, '$1'); //[bugfix]修改xxx.js?v这种情况下取不到js的问题。 //默认html格式用ajax请求,其他都使用dom标签方式请求. res.requestType = res.requestType || (res.type == 'html' ? 'ajax' : 'dom'); if (cacheData = cache[res.url]) { callback(cacheData); return shouldContinue; } if (!options.refresh && loadingCache[res.url]) { setTimeout(function() {loadResources(res);}, 10); return shouldContinue; } loadingCache[res.url] = true; if (res.requestType.toLowerCase() == 'dom') { loadByDom(res, callback); }else {//ajax $.getScript(res.url, function(responseText){ callback(responseText); }); } //串行模式,通过callback方法执行后续 return shouldContinue; }; $.each(resources, function(k, item){ loadResources(item); }); }; //默认编码设置为UTF8 $.page.load.charset = 'UTF8'; })(jQuery); ;(function($){ /** * 判断浏览器类型 */ $.browser = $.browser || {}; $.browser.ie = /msie (\d+\.\d+)/i.test(navigator.userAgent) ? (document.documentMode || + RegExp['\x241']) : undefined; $.browser.firefox = /firefox\/(\d+\.\d+)/i.test(navigator.userAgent) ? + RegExp['\x241'] : undefined; $.browser.chrome = /chrome\/(\d+\.\d+)/i.test(navigator.userAgent) ? + RegExp['\x241'] : undefined; //判断是否为gecko内核 $.browser.isGecko = /gecko/i.test(navigator.userAgent) && !/like gecko/i.test(navigator.userAgent); //判断是否为webkit内核 $.browser.isWebkit = /webkit/i.test(navigator.userAgent); //判断是否严格标准的渲染模式 $.browser.isStrict = document.compatMode == "CSS1Compat"; $.browser.opera = /opera(\/| )(\d+(\.\d+)?)(.+?(version\/(\d+(\.\d+)?)))?/i.test(navigator.userAgent) ? + ( RegExp["\x246"] || RegExp["\x242"] ) : undefined; (function(){ var ua = navigator.userAgent; /** * 判断是否为safari浏览器, 支持ipad * @property safari safari版本号 * @grammar $.browser.safari */ $.browser.safari = /(\d+\.\d)?(?:\.\d)?\s+safari\/?(\d+\.\d+)?/i.test(ua) && !/chrome/i.test(ua) ? + (RegExp['\x241'] || RegExp['\x242']) : undefined; })(); })(jQuery); ;(function($){ /** * 判断平台类型和特性的属性 */ $.platform = $.platform || {}; /** * 判断是否为android平台 */ $.platform.isAndroid = /android/i.test(navigator.userAgent); /** * 判断是否为ipad平台 */ $.platform.isIpad = /ipad/i.test(navigator.userAgent); /** * 判断是否为iphone平台 */ $.platform.isIphone = /iphone/i.test(navigator.userAgent); /** * 判断是否为macintosh平台 */ $.platform.isMacintosh = /macintosh/i.test(navigator.userAgent); /** * 判断是否为windows平台 */ $.platform.isWindows = /windows/i.test(navigator.userAgent); /** * 判断是否为x11平台 */ $.platform.isX11 = /x11/i.test(navigator.userAgent); })(jQuery); ;(function($){ /** * String增强 */ $.string = $.string || {}; /** * 对目标字符串进行html解码 * @grammar $.string.decodeHTML(source) * @param {string} source 目标字符串 * * @returns {string} html解码后的字符串 */ $.string.decodeHTML = function (source) { var str = String(source) .replace(/"/g,'"') .replace(/</g,'<') .replace(/>/g,'>') .replace(/&/g, "&"); //处理转义的中文和实体字符 return str.replace(/&#([\d]+);/g, function(_0, _1){ return String.fromCharCode(parseInt(_1, 10)); }); }; /** * 对目标字符串进行html编码 * @grammar $.string.encodeHTML(source) * @param {string} source 目标字符串 * 编码字符有5个:&<>"' * * @returns {string} html编码后的字符串 */ $.string.encodeHTML = function (source) { return String(source) .replace(/&/g,'&') .replace(/</g,'<') .replace(/>/g,'>') .replace(/"/g, """) .replace(/'/g, "'"); }; /** * 去掉字符串中的html标签 * @function * @grammar $.string.stripTags(source) * @param {string} source 要处理的字符串. * @return {String} */ $.string.stripTags = function(source) { return String(source || '').replace(/<[^>]+>/g, ''); }; /** * 将目标字符串中可能会影响正则表达式构造的字符串进行转义。 * @grammar $.string.escapeReg(source) * @param {string} source 目标字符串 * @remark * 给以下字符前加上“\”进行转义:.*+?^=!:${}()|[]/\ * * @returns {string} 转义后的字符串 */ $.string.escapeReg = function (source) { return String(source) .replace(new RegExp("([.*+?^=!:\x24{}()|[\\]\/\\\\])", "g"), '\\\x241'); }; /** * 删除目标字符串两端的空白字符 * @grammar $.string.trim(source) * @param {string} source 目标字符串 * @remark * 不支持删除单侧空白字符 * * @returns {string} 删除两端空白字符后的字符串 */ (function () { var trimer = new RegExp("(^[\\s\\t\\xa0\\u3000]+)|([\\u3000\\xa0\\s\\t]+\x24)", "g"); $.string.trim = function (source) { return String(source) .replace(trimer, ""); }; })(); })(jQuery); ;(function($){ /** * GUID */ $.lang = $.lang || {}; /** * 返回一个当前页面的唯一标识字符串。 * @grammar $.lang.guid() * * @returns {String} 当前页面的唯一标识字符串 */ $.lang.guid = function() { return "TANGRAM$" + $.lang._counter ++; }; //不直接使用window,可以提高3倍左右性能 $.lang._counter = $.lang._counter || 1; $.guid = $.lang.guid;//设置$.page.guid别名$.guid /** * 判断目标参数是否为function或Function实例 * @grammar $.lang.isFunction(source) * @param {Any} source 目标参数 * @returns {boolean} 类型判断结果 */ $.lang.isFunction = function (source) { // chrome下,'function' == typeof /a/ 为true. return '[object Function]' == Object.prototype.toString.call(source); }; /** * 判断目标参数是否为Element对象 * @grammar $.lang.isElement(source) * @param {Any} source 目标参数 * @returns {boolean} 类型判断结果 */ $.lang.isElement = function (source) { return !!(source && source.nodeName && source.nodeType == 1); }; /** * 判断目标参数是否Array对象 * @grammar $.lang.isArray(source) * @param {Any} source 目标参数 * @returns {boolean} 类型判断结果 */ $.lang.isArray = function (source) { return '[object Array]' == Object.prototype.toString.call(source); }; })(jQuery); ;(function($){ /** * Array增强 */ $.array = $.array || {}; /** * 查询数组中指定元素的索引位置 * @grammar $.array.indexOf(source, match[, fromIndex]) * @param {Array} source 需要查询的数组 * @param {Any} match 查询项 * @param {number} [fromIndex] 查询的起始位索引位置,如果为负数,则从source.length+fromIndex往后开始查找 * * @returns {number} 指定元素的索引位置,查询不到时返回-1 */ $.array.indexOf = function (source, match, fromIndex) { var len = source.length, iterator = match; fromIndex = fromIndex | 0; if(fromIndex < 0){//小于0 fromIndex = Math.max(0, len + fromIndex) } for ( ; fromIndex < len; fromIndex++) { if(fromIndex in source && source[fromIndex] === match) { return fromIndex; } } return -1; }; /** * 判断一个数组中是否包含给定元素 * @grammar $.array.contains(source, obj) * @param {Array} source 需要判断的数组. * @param {Any} obj 要查找的元素. * @return {boolean} 判断结果. * @author berg */ $.array.contains = function(source, obj) { return ($.array.indexOf(source, obj) >= 0); }; /** * 清空一个数组 * @grammar $.array.empty(source) * @param {Array} source 需要清空的数组. * @author berg */ $.array.empty = function(source) { source.length = 0; }; /** * 从后往前,查询数组中指定元素的索引位置 * @grammar $.array.lastIndexOf(source, match) * @param {Array} source 需要查询的数组 * @param {Any} match 查询项 * @param {number} [fromIndex] 查询的起始位索引位置,如果为负数,则从source.length+fromIndex往前开始查找 * * @returns {number} 指定元素的索引位置,查询不到时返回-1 */ $.array.lastIndexOf = function (source, match, fromIndex) { var len = source.length; fromIndex = fromIndex | 0; if(!fromIndex || fromIndex >= len){ fromIndex = len - 1; } if(fromIndex < 0){ fromIndex += len; } for(; fromIndex >= 0; fromIndex --){ if(fromIndex in source && source[fromIndex] === match){ return fromIndex; } } return -1; }; /** * 遍历数组中所有元素,将每一个元素应用方法进行转换,并返回转换后的新数组。 * @grammar $.array.map(source, iterator[, thisObject]) * @param {Array} source 需要遍历的数组. * @param {Function} iterator 对每个数组元素进行处理的函数. * @param {Object} [thisObject] 函数调用时的this指针,如果没有此参数,默认是当前遍历的数组 * @return {Array} map后的数组. */ $.array.map = function(source, iterator, thisObject) { var results = [], i = 0, l = source.length; for (; i < l; i++) { results[i] = iterator.call(thisObject || source, source[i], i); } return results; }; /** * 遍历数组中所有元素,将每一个元素应用方法进行合并,并返回合并后的结果。 * @grammar $.array.reduce(source, iterator[, initializer]) * @param {Array} source 需要遍历的数组. * @param {Function} iterator 对每个数组元素进行处理的函数,函数接受四个参数:上一次reduce的结果(或初始值),当前元素值,索引值,整个数组. * @param {Object} [initializer] 合并的初始项,如果没有此参数,默认用数组中的第一个值作为初始值. * @return {Array} reduce后的值. */ $.array.reduce = function(source, iterator, initializer) { var i = 0, l = source.length, found = 0; if( arguments.length < 3){ //没有initializer的情况,找到第一个可用的值 for(; i < l; i++){ initializer = source[i++]; found = 1; break; } if(!found){ return ; } } for (; i < l; i++) { if( i in source){ initializer = iterator(initializer, source[i] , i , source); } } return initializer; }; /** * 移除数组中的项 * @grammar $.array.remove(source, match) * @param {Array} source 需要移除项的数组 * @param {Any} match 要移除的项 * * @returns {Array} 移除后的数组 */ $.array.remove = function (source, match) { var len = source.length; while (len--) { if (len in source && source[len] === match) { source.splice(len, 1); } } return source; }; /** * 移除数组中的项 * @grammar $.array.removeAt(source, index) * @param {Array} source 需要移除项的数组 * @param {number} index 要移除项的索引位置 * @returns {Any} 被移除的数组项 */ $.array.removeAt = function (source, index) { return source.splice(index, 1)[0]; }; /** * 过滤数组中的相同项。如果两个元素相同,会删除后一个元素。 * @grammar $.array.unique(source[, compareFn]) * @param {Array} source 需要过滤相同项的数组 * @param {Function} [compareFn] 比较两个数组项是否相同的函数,两个数组项作为函数的参数。 * * @returns {Array} 过滤后的新数组 */ $.array.unique = function (source, compareFn) { var len = source.length, result = source.slice(0), i, datum; if ('function' != typeof compareFn) { compareFn = function (item1, item2) { return item1 === item2; }; } // 从后往前双重循环比较 // 如果两个元素相同,删除后一个 while (--len > 0) { datum = result[len]; i = len; while (i--) { if (compareFn(datum, result[i])) { result.splice(len, 1); break; } } } return result; }; })(jQuery); ;(function(){ /** * 操作cookie的方法 * @namespace $.cookie */ $.cookie = $.cookie || {}; /** * 验证字符串是否合法的cookie键名 * * @param {string} source 需要遍历的数组 * @meta standard * @return {boolean} 是否合法的cookie键名 */ $.cookie._isValidKey = function (key) { // http://www.w3.org/Protocols/rfc2109/rfc2109 // Syntax: General // The two state management headers, Set-Cookie and Cookie, have common // syntactic properties involving attribute-value pairs. The following // grammar uses the notation, and tokens DIGIT (decimal digits) and // token (informally, a sequence of non-special, non-white space // characters) from the HTTP/1.1 specification [RFC 2068] to describe // their syntax. // av-pairs = av-pair *(";" av-pair) // av-pair = attr ["=" value] ; optional value // attr = token // value = word // word = token | quoted-string // http://www.ietf.org/rfc/rfc2068.txt // token = 1*<any CHAR except CTLs or tspecials> // CHAR = <any US-ASCII character (octets 0 - 127)> // CTL = <any US-ASCII control character // (octets 0 - 31) and DEL (127)> // tspecials = "(" | ")" | "<" | ">" | "@" // | "," | ";" | ":" | "\" | <"> // | "/" | "[" | "]" | "?" | "=" // | "{" | "}" | SP | HT // SP = <US-ASCII SP, space (32)> // HT = <US-ASCII HT, horizontal-tab (9)> return (new RegExp("^[^\\x00-\\x20\\x7f\\(\\)<>@,;:\\\\\\\"\\[\\]\\?=\\{\\}\\/\\u0080-\\uffff]+\x24")).test(key); }; /** * 获取cookie的值,不对值进行解码 * @grammar $.cookie.getRaw(key) * @param {string} key 需要获取Cookie的键名 * @returns {string|null} 获取的Cookie值,获取不到时返回null */ $.cookie.getRaw = function (key) { if ($.cookie._isValidKey(key)) { var reg = new RegExp("(^| )" + key + "=([^;]*)(;|\x24)"), result = reg.exec(document.cookie); if (result) { return result[2] || null; } } return null; }; /** * 设置cookie的值,不对值进行编码 * @grammar $.cookie.setRaw(key, value[, options]) * @param {string} key 需要设置Cookie的键名 * @param {string} value 需要设置Cookie的值 * @param {Object} [options] 设置Cookie的其他可选参数 * @config {string} [path] cookie路径 * @config {Date|number} [expires] cookie过期时间,如果类型是数字的话, 单位是毫秒 * @config {string} [domain] cookie域名 * @config {string} [secure] cookie是否安全传输 * @remark * <b>options参数包括:</b><br> path:cookie路径<br> expires:cookie过期时间,Number型,单位为毫秒。<br> domain:cookie域名<br> secure:cookie是否安全传输 */ $.cookie.setRaw = function (key, value, options) { if (!$.cookie._isValidKey(key)) { return; } options = options || {}; //options.path = options.path || "/"; // meizz 20100402 设定一个初始值,方便后续的操作 //berg 20100409 去掉,因为用户希望默认的path是当前路径,这样和浏览器对cookie的定义也是一致的 // 计算cookie过期时间 var expires = options.expires; if ('number' == typeof options.expires) { expires = new Date(); expires.setTime(expires.getTime() + options.expires); } document.cookie = key + "=" + value + (options.path ? "; path=" + options.path : "") + (expires ? "; expires=" + expires.toGMTString() : "") + (options.domain ? "; domain=" + options.domain : "") + (options.secure ? "; secure" : ''); }; /** * 删除cookie的值 * @grammar $.cookie.remove(key, options) * @param {string} key 需要删除Cookie的键名 * @param {Object} options 需要删除的cookie对应的 path domain 等值 */ $.cookie.remove = function (key, options) { options = options || {}; options.expires = new Date(0); $.cookie.setRaw(key, '', options); }; /** * 获取cookie的值,用decodeURIComponent进行解码 * @grammar $.cookie.get(key) * @param {string} key 需要获取Cookie的键名 * @remark * <b>注意:</b>该方法会对cookie值进行decodeURIComponent解码。如果想获得cookie源字符串,请使用getRaw方法。 * * @returns {string|null} cookie的值,获取不到时返回null */ $.cookie.get = function (key) { var value = $.cookie.getRaw(key); if ('string' == typeof value) { value = decodeURIComponent(value); return value; } return null; }; /** * 设置cookie的值,用encodeURIComponent进行编码 * @grammar $.cookie.set(key, value[, options]) * @param {string} key 需要设置Cookie的键名 * @param {string} value 需要设置Cookie的值 * @param {Object} [options] 设置Cookie的其他可选参数 * @config {string} [path] cookie路径 * @config {Date|number} [expires] cookie过期时间,如果类型是数字的话, 单位是毫秒 * @config {string} [domain] cookie域名 * @config {string} [secure] cookie是否安全传输 * @remark * 1. <b>注意:</b>该方法会对cookie值进行encodeURIComponent编码。如果想设置cookie源字符串,请使用setRaw方法。<br><br> 2. <b>options参数包括:</b><br> path:cookie路径<br> expires:cookie过期时间,Number型,单位为毫秒。<br> domain:cookie域名<br> secure:cookie是否安全传输 */ $.cookie.set = function (key, value, options) { $.cookie.setRaw(key, encodeURIComponent(value), options); }; })(jQuery); ;(function($){ $.number = $.number || {}; /** * 为目标数字添加逗号分隔 * @grammar $.number.comma(source[, length]) * @param {number} source 需要处理的数字 * @param {number} [length] 两次逗号之间的数字位数,默认为3位 * * @returns {string} 添加逗号分隔后的字符串 */ $.number.comma = function (source, length) { if (!length || length < 1) { length = 3; } source = String(source).split("."); source[0] = source[0].replace(new RegExp('(\\d)(?=(\\d{'+length+'})+$)','ig'),"$1,"); return source.join("."); }; /** * 生成随机整数,范围是[min, max] * @grammar $.number.randomInt(min, max) * * @param {number} min 随机整数的最小值 * @param {number} max 随机整数的最大值 * @return {number} 生成的随机整数 */ $.number.randomInt = function(min, max){ return Math.floor(Math.random() * (max - min + 1) + min); }; /** * 对目标数字进行0补齐处理 * @grammar $.number.pad(source, length) * @param {number} source 需要处理的数字 * @param {number} length 需要输出的长度 * * @returns {string} 对目标数字进行0补齐处理后的结果 */ $.number.pad = function (source, length) { var pre = "", negative = (source < 0), string = String(Math.abs(source)); if (string.length < length) { pre = (new Array(length - string.length + 1)).join('0'); } return (negative ? "-" : "") + pre + string; }; })(jQuery); ;(function($){ $.sio = $.sio || {}; /** * 通过请求一个图片的方式令服务器存储一条日志 * @grammar $.sio.log(url) * @param {string} url 要发送的地址. */ $.sio.log = function(url) { var img = new Image(), key = 'tangram_sio_log_' + Math.floor(Math.random() * 2147483648).toString(36); // 这里一定要挂在window下 // 在IE中,如果没挂在window下,这个img变量又正好被GC的话,img的请求会abort // 导致服务器收不到日志 window[key] = img; img.onload = img.onerror = img.onabort = function() { // 下面这句非常重要 // 如果这个img很不幸正好加载了一个存在的资源,又是个gif动画 // 则在gif动画播放过程中,img会多次触发onload // 因此一定要清空 img.onload = img.onerror = img.onabort = null; window[key] = null; // 下面这句非常重要 // new Image创建的是DOM,DOM的事件中形成闭包环引用DOM是典型的内存泄露 // 因此这里一定要置为null img = null; }; // 一定要在注册了事件之后再设置src // 不然如果图片是读缓存的话,会错过事件处理 // 最后,对于url最好是添加客户端时间来防止缓存 // 同时服务器也配合一下传递Cache-Control: no-cache; img.src = url; }; })(jQuery); ;(function($){ $.url = $.url || {}; /** * 对字符串进行%#&+=以及和\s匹配的所有字符进行url转义 * @grammar $.url.escapeSymbol(source) * @param {string} source 需要转义的字符串. * @return {string} 转义之后的字符串. * @remark * 用于get请求转义。在服务器只接受gbk,并且页面是gbk编码时,可以经过本转义后直接发get请求。 * * @return {string} 转义后的字符串 */ $.url.escapeSymbol = function(source) { //TODO: 之前使用\s来匹配任意空白符 //发现在ie下无法匹配中文全角空格和纵向指标符\v,所以改\s为\f\r\n\t\v以及中文全角空格和英文空格 //但是由于ie本身不支持纵向指标符\v,故去掉对其的匹配,保证各浏览器下效果一致 return String(source).replace(/[#%&+=\/\\\ \ \f\r\n\t]/g, function(all) { return '%' + (0x100 + all.charCodeAt()).toString(16).substring(1).toUpperCase(); }); }; /** * 根据参数名从目标URL中获取参数值 * @grammar $.url.getQueryValue(url, key) * @param {string} url 目标URL * @param {string} key 要获取的参数名 * * @returns {string|null} - 获取的参数值,其中URI编码后的字符不会被解码,获取不到时返回null */ $.url.getQueryValue = function (url, key) { var reg = new RegExp( "(^|&|\\?|#)" + $.string.escapeReg(key) + "=([^&#]*)(&|\x24|#)", ""); var match = url.match(reg); if (match) { return match[2]; } return null; }; /** * 将json对象解析成query字符串 * @grammar $.url.jsonToQuery(json[, replacer]) * @param {Object} json 需要解析的json对象 * @param {Function=} replacer_opt 对值进行特殊处理的函数,function (value, key) * * @return {string} - 解析结果字符串,其中值将被URI编码,{a:'&1 '} ==> "a=%261%20"。 */ $.url.jsonToQuery = function (json, replacer_opt) { var result = [], itemLen, replacer = replacer_opt || function (value) { return $.url.escapeSymbol(value); }; $.each(json, function(item, key){ // 这里只考虑item为数组、字符串、数字类型,不考虑嵌套的object if ($.lang.isArray(item)) { itemLen = item.length; // value的值需要encodeURIComponent转义吗? // FIXED 优化了escapeSymbol函数 while (itemLen--) { result.push(key + '=' + replacer(item[itemLen], key)); } } else { result.push(key + '=' + replacer(item, key)); } }); return result.join('&'); }; /** * 解析目标URL中的参数成json对象 * @grammar $.url.queryToJson(url) * @param {string} url 目标URL * * @returns {Object} - 解析为结果对象,其中URI编码后的字符不会被解码,'a=%20' ==> {a:'%20'}。 */ $.url.queryToJson = function (url) { var query = url.substr(url.lastIndexOf('?') + 1), params = query.split('&'), len = params.length, result = {}, i = 0, key, value, item, param; for (; i < len; i++) { if(!params[i]){ continue; } param = params[i].split('='); key = param[0]; value = param[1]; item = result[key]; if ('undefined' == typeof item) { result[key] = value; } else if ($.lang.isArray(item)) { item.push(value); } else { // 这里只可能是string了 result[key] = [item, value]; } } return result; }; })(jQuery);