jquery插件 源码

下面是对Jquery几个经常用到的地方进行的增强。

功能是参考百度七巧板JS框架来完成的。

一、页面属性

$.page.getHeight():获取页面高度

$.page.getWidth():获取页面宽度
$.page.createStyleSheet(options):在页面中创建样式表对象
$.page.getScrollTop():获取纵向滚动量
$.page.getScrollLeft():获取横向滚动量
$.page.getViewHeight():获取页面视觉区域高度
$.page.getViewWidth():获取页面视觉区域宽度
$.page.getMousePosition(element):获得页面里的目前鼠标所在的坐标
$.page.getPosition(element):获取目标元素相对于整个文档左上角的位置
$.page.lazyLoadImage(options):延迟加载图片. 默认只加载可见高度以上的图片, 随着窗口滚动加载剩余图片.注意: 仅支持垂直方向.
$.page.loadCssFile(path):动态在页面上加载一个外部css文件
$.page.loadJsFile(path):动态在页面上加载一个外部js文件
$.page.load(resources, options, ignoreAllLoaded):加载一组资源,支持多种格式资源的串/并行加载,支持每个文件有单独回调函数。

二、浏览器判断
$.browser.ie
$.browser.firefox
$.browser.chrome
$.browser.isGecko
$.browser.isWebkit
$.browser.isStrict
$.browser.opera
$.browser.safari



三、平台判断
$.platform.isAndroid
$.platform.isIpad
$.platform.isIphone
$.platform.isMacintosh
$.platform.isWindows
$.platform.isX11



四、字符串处理
$.string.decodeHTML(source):对目标字符串进行html解码
$.string.encodeHTML(source):对目标字符串进行html编码
$.string.stripTags(source):去掉字符串中的html标签
$.string.escapeReg(source):将目标字符串中可能会影响正则表达式构造的字符串进行转义。
$.string.trim(source):删除目标字符串两端的空白字符



五、类型
$.lang.guid():返回一个当前页面的唯一标识字符串。
$.lang.isFunction(source):判断目标参数是否为function或Function实例
$.lang.isElement(source):判断目标参数是否为Element对象
$.lang.isArray(source):判断目标参数是否Array对象



六、数组
$.array.indexOf(source, match, fromIndex):查询数组中指定元素的索引位置
$.array.contains(source, obj):判断一个数组中是否包含给定元素
$.array.empty(source):清空一个数组
$.array.lastIndexOf(source, match, fromIndex):从后往前,查询数组中指定元素的索引位置
$.array.map(source, iterator, thisObject):遍历数组中所有元素,将每一个元素应用方法进行转换,并返回转换后的新数组。
$.array.reduce(source, iterator, initializer):遍历数组中所有元素,将每一个元素应用方法进行合并,并返回合并后的结果。
$.array.remove(source, match):移除数组中的项
$.array.removeAt(source, index):移除数组中的项
$.array.unique(source, compareFn):过滤数组中的相同项。如果两个元素相同,会删除后一个元素。



七、cookie
$.cookie.getRaw(key):获取cookie的值,不对值进行解码
$.cookie.setRaw(key, value, options):设置cookie的值,不对值进行编码
$.cookie.remove(key, options):删除cookie的值
$.cookie.get(key):获取cookie的值,用decodeURIComponent进行解码
$.cookie.set(key, value, options):设置cookie的值,用encodeURIComponent进行编码



八、整数
$.number.comma(source, length):为目标数字添加逗号分隔
$.number.randomInt(min, max):生成随机整数,范围是[min, max]
$.number.pad(source, length):对目标数字进行0补齐处理



九、日志
$.sio.log(url):通过请求一个图片的方式令服务器存储一条日志,常用于记录用户点击位置,进行分析统计



十、URL
$.url.escapeSymbol(source):对字符串进行%#&+=以及和s匹配的所有字符进行url转义
$.url.getQueryValue(url, key):根据参数名从目标URL中获取参数值
$.url.jsonToQuery(json, replacer_opt):将json对象解析成query字符串

$.url.queryToJson(url):解析目标URL中的参数成json对象

   1 ;(function($){
   2     $.extend({
   3         hasClass:function(targetClassName){
   4             var className = $(this).attr("class");
   5             var classArray = className.split(" ");
   6             if(className && classArray.length == 0){
   7                 if(targetClassName == className) return true;
   8             }else if(className && classArray.length > 0){
   9                 for(var i=0; i<=classArray.length-1; i++){
  10                     if(classArray[i] == targetClassName) return true;
  11                 }
  12             }
  13 
  14             return false;
  15         },
  16         
  17         getDocument:function (element) {
  18             return $.lang.isElement(element) ? element : element.ownerDocument || element.document;
  19         }
  20     });
  21 })(jQuery);
  22 
  23 ;(function($){
  24     $.lang = $.lang || {};
  25     
  26     $.lang.isFunction = function (source) {
  27         // chrome下,'function' == typeof /a/ 为true.
  28         return '[object Function]' == Object.prototype.toString.call(source);
  29     };
  30     
  31     $.lang.isString = function (source) {
  32         return '[object String]' == Object.prototype.toString.call(source);
  33     };
  34 })(jQuery);
  35 
  36 ;(function($){
  37     
  38     $.page = $.page || {};
  39     $.page.xy = {x:0, y:0};//当前鼠标坐标
  40 
  41     $.page.getHeight = $.page.getHeight || function(){
  42         var doc = document,
  43         body = doc.body,
  44         html = doc.documentElement,
  45         client = doc.compatMode == 'BackCompat' ? body : doc.documentElement;
  46 
  47         return Math.max(html.scrollHeight, body.scrollHeight, client.clientHeight);
  48     };
  49 
  50     $.page.getWidth  = $.page.getWidth || function () {
  51         var doc = document,
  52         body = doc.body,
  53         html = doc.documentElement,
  54         client = doc.compatMode == 'BackCompat' ? body : doc.documentElement;
  55 
  56         return Math.max(html.scrollWidth, body.scrollWidth, client.clientWidth);
  57     };
  58 
  59     $.page.createStyleSheet = $.page.createStyleSheet || function(options){
  60         var op = options || {},
  61             doc = op.document || document,
  62             s;
  63 
  64         if ($.browser.ie) {
  65             //修复ie下会请求一个undefined的bug  berg 2010/08/27 
  66             if(!op.url)
  67                 op.url = "";
  68             return doc.createStyleSheet(op.url, op.index);
  69         } else {
  70             s = "<style type='text/css'></style>";
  71             op.url && (s="<link type='text/css' rel='stylesheet' href='"+op.url+"'/>");
  72             $("HEAD:eq(0)").after(s);
  73         }
  74     };
  75 
  76     $.page.getScrollTop =  $.page.getScrollTop || function(){
  77         var d = document;
  78         return window.pageYOffset || d.documentElement.scrollTop || d.body.scrollTop;
  79     };
  80 
  81     $.page.getScrollLeft= $.page.getScrollLeft || function () {
  82         var d = document;
  83         return window.pageXOffset || d.documentElement.scrollLeft || d.body.scrollLeft;
  84     };
  85 
  86     $.page.getViewHeight = $.page.getViewHeight || function () {
  87         var doc = document,
  88             client = doc.compatMode == 'BackCompat' ? doc.body : doc.documentElement;
  89 
  90         return client.clientHeight;
  91     };
  92 
  93     $.page.getViewWidth = function () {
  94         var doc = document,
  95             client = doc.compatMode == 'BackCompat' ? doc.body : doc.documentElement;
  96 
  97         return client.clientWidth;
  98     };
  99     
 100     $(document).bind("mousemove", function(e){
 101         e = window.event || e;
 102         $.page.xy.x = e.clientX;
 103         $.page.xy.y = e.clientY;
 104         //console.log(e.clientX+","+e.clientY);
 105     });
 106 
 107     $.page.getMousePosition = $.page.getMousePosition || function(){
 108         return {
 109             x : $.page.getScrollLeft() + $.page.xy.x,
 110             y : $.page.getScrollTop() + $.page.xy.y
 111         };
 112     };
 113 
 114     /**
 115      * 获取目标元素相对于整个文档左上角的位置
 116      * @grammar $.dom.getPosition(element)
 117      * @param {HTMLElement|string} element 目标元素或目标元素的id
 118      *             
 119      * @returns {Object} 目标元素的位置,键值为top和left的Object。
 120      */
 121     $.page.getPosition = function (element) {
 122         var doc = $.getDocument(element),
 123             browser = $.browser
 124         // Gecko 1.9版本以下用getBoxObjectFor计算位置
 125         // 但是某些情况下是有bug的
 126         // 对于这些有bug的情况
 127         // 使用递归查找的方式
 128             BUGGY_GECKO_BOX_OBJECT = browser.isGecko > 0 && 
 129                                      doc.getBoxObjectFor &&
 130                                      $(element).css('position') == 'absolute' &&
 131                                      (element.style.top === '' || element.style.left === ''),
 132             pos = {"left":0,"top":0},
 133             viewport = (browser.ie && !browser.isStrict) ? doc.body : doc.documentElement,
 134             parent;
 135         var box;
 136         
 137         if(element == viewport){
 138             return pos;
 139         }
 140 
 141         if(element.getBoundingClientRect){ // IE and Gecko 1.9+
 142             //当HTML或者BODY有border width时, 原生的getBoundingClientRect返回值是不符合预期的
 143             //考虑到通常情况下 HTML和BODY的border只会设成0px,所以忽略该问题.
 144             doc = document;
 145             box = element.getBoundingClientRect();
 146 
 147             pos.left = Math.floor(box.left) + Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft);
 148             pos.top  = Math.floor(box.top)  + Math.max(doc.documentElement.scrollTop,  doc.body.scrollTop);
 149             
 150             // IE会给HTML元素添加一个border,默认是medium(2px)
 151             // 但是在IE 6 7 的怪异模式下,可以被html { border: 0; } 这条css规则覆盖
 152             // 在IE7的标准模式下,border永远是2px,这个值通过clientLeft 和 clientTop取得
 153             // 但是。。。在IE 6 7的怪异模式,如果用户使用css覆盖了默认的medium
 154             // clientTop和clientLeft不会更新
 155             pos.left -= doc.documentElement.clientLeft;
 156             pos.top  -= doc.documentElement.clientTop;
 157             
 158             var htmlDom = doc.body,
 159                 // 在这里,不使用element.style.borderLeftWidth,只有computedStyle是可信的
 160                 htmlBorderLeftWidth = parseInt($(htmlDom).css('borderLeftWidth')),
 161                 htmlBorderTopWidth = parseInt($(htmlDom).css('borderTopWidth'));
 162             if(browser.ie && !browser.isStrict){
 163                 pos.left -= isNaN(htmlBorderLeftWidth) ? 2 : htmlBorderLeftWidth;
 164                 pos.top  -= isNaN(htmlBorderTopWidth) ? 2 : htmlBorderTopWidth;
 165             }
 166         /*
 167          * 因为firefox 3.6和4.0在特定页面下(场景待补充)都会出现1px偏移,所以暂时移除该逻辑分支
 168          * 如果 2.0版本时firefox仍存在问题,该逻辑分支将彻底移除. by rocy 2011-01-20
 169         } else if (doc.getBoxObjectFor && !BUGGY_GECKO_BOX_OBJECT){ // gecko 1.9-
 170 
 171             // 1.9以下的Gecko,会忽略ancestors的scroll值
 172             // https://bugzilla.mozilla.org/show_bug.cgi?id=328881 and
 173             // https://bugzilla.mozilla.org/show_bug.cgi?id=330619
 174 
 175             box = doc.getBoxObjectFor(element);
 176             var vpBox = doc.getBoxObjectFor(viewport);
 177             pos.left = box.screenX - vpBox.screenX;
 178             pos.top  = box.screenY - vpBox.screenY;
 179             */
 180         } else { // safari/opera/firefox
 181             parent = element;
 182 
 183             do {
 184                 pos.left += parent.offsetLeft;
 185                 pos.top  += parent.offsetTop;
 186                 // safari里面,如果遍历到了一个fixed的元素,后面的offset都不准了
 187                 if (browser.isWebkit > 0 && $(parent).css('position') == 'fixed') {
 188                     pos.left += doc.body.scrollLeft;
 189                     pos.top  += doc.body.scrollTop;
 190                     break;
 191                 }
 192                 
 193                 parent = parent.offsetParent;
 194             } while (parent && parent != element);
 195 
 196             // 对body offsetTop的修正
 197             if(browser.opera > 0 || (browser.isWebkit > 0 && $(parent).css('position') == 'absolute')){
 198                 pos.top  -= doc.body.offsetTop;
 199             }
 200 
 201             // 计算除了body的scroll
 202             parent = element.offsetParent;
 203             while (parent && parent != doc.body) {
 204                 pos.left -= parent.scrollLeft;
 205                 // see https://bugs.opera.com/show_bug.cgi?id=249965
 206     //            if (!b.opera || parent.tagName != 'TR') {
 207                 if (!browser.opera || parent.tagName != 'TR') {
 208                     pos.top -= parent.scrollTop;
 209                 }
 210                 parent = parent.offsetParent;
 211             }
 212         }
 213 
 214         return pos;
 215     };
 216 
 217     /**
 218      * 延迟加载图片. 默认只加载可见高度以上的图片, 随着窗口滚动加载剩余图片.注意: 仅支持垂直方向.
 219      * @grammar $.page.lazyLoadImage([options])
 220      * @param {Object} options
 221      * @param {String} [options.className] 延迟加载的IMG的className,如果不传入该值将延迟加载所有IMG.
 222      * @param {Number} [options.preloadHeight] 预加载的高度, 可见窗口下该高度内的图片将被加载.
 223      * @param {String} [options.placeHolder] 占位图url.
 224      * @param {Function} [options.onlazyload] 延迟加载回调函数,在实际加载时触发.
 225      */
 226     $.page.lazyLoadImage = function(options) {
 227         options = options || {};
 228         options.preloadHeight = options.preloadHeight || 0;
 229 
 230         $(document).ready(function() {
 231             var imgs = document.getElementsByTagName('IMG'),
 232                     targets = imgs,
 233                     len = imgs.length,
 234                     i = 0,
 235                     viewOffset = getLoadOffset(),
 236                     srcAttr = 'data-tangram-ori-src',
 237                     target;
 238             //避免循环中每次都判断className
 239             if (options.className) {
 240                 targets = [];
 241                 for (; i < len; ++i) {
 242                     if ($(imgs[i]).hasClass(options.className)) {
 243                         targets.push(imgs[i]);
 244                     }
 245                 }
 246             }
 247             //计算需要加载图片的页面高度
 248             function getLoadOffset() {
 249                 return $.page.getScrollTop() + $.page.getViewHeight() + options.preloadHeight;
 250             }
 251             //加载可视图片
 252             for (i = 0, len = targets.length; i < len; ++i) {
 253                 target = targets[i];
 254                 if ($.page.getPosition(target).top > viewOffset) {
 255                     target.setAttribute(srcAttr, target.src);
 256                     options.placeHolder ? target.src = options.placeHolder : target.removeAttribute('src');
 257                 }
 258             }
 259             //处理延迟加载
 260             var loadNeeded = function() {
 261                 var viewOffset = getLoadOffset(),
 262                     imgSrc,
 263                     finished = true,
 264                     i = 0,
 265                     len = targets.length;
 266                 for (; i < len; ++i) {
 267                     target = targets[i];
 268                     imgSrc = target.getAttribute(srcAttr);
 269                     imgSrc && (finished = false);
 270                     if ($.page.getPosition(target).top < viewOffset && imgSrc) {
 271                         target.src = imgSrc;
 272                         target.removeAttribute(srcAttr);
 273                         $.lang.isFunction(options.onlazyload) && options.onlazyload(target);
 274                     }
 275                 }
 276                 //当全部图片都已经加载, 去掉事件监听
 277                 finished && $(window).unbind('scroll', loadNeeded);
 278             };
 279 
 280             $(window).bind('scroll', loadNeeded);
 281         });
 282     };
 283 
 284     /**
 285      * 动态在页面上加载一个外部css文件
 286      * @grammar $.page.loadCssFile(path)
 287      * @param {string} path css文件路径
 288      */
 289     $.page.loadCssFile = function (path) {
 290         var element = document.createElement("link");
 291         
 292         element.setAttribute("rel", "stylesheet");
 293         element.setAttribute("type", "text/css");
 294         element.setAttribute("href", path);
 295 
 296         document.getElementsByTagName("head")[0].appendChild(element);        
 297     };
 298 
 299 
 300     /**
 301      * 动态在页面上加载一个外部js文件
 302      * @grammar $.page.loadJsFile(path)
 303      * @param {string} path js文件路径
 304      */
 305     $.page.loadJsFile = function (path) {
 306         var element = document.createElement('script');
 307 
 308         element.setAttribute('type', 'text/javascript');
 309         element.setAttribute('src', path);
 310         element.setAttribute('defer', 'defer');
 311 
 312         document.getElementsByTagName("head")[0].appendChild(element);    
 313     };
 314 
 315     /**
 316      *
 317      * 加载一组资源,支持多种格式资源的串/并行加载,支持每个文件有单独回调函数。
 318      *
 319      * @name $.page.load
 320      * @function
 321      * @grammar $.page.load(resources[, options])
 322      *
 323      * @param {Array} resources               资源描述数组,单个resource含如下属性.
 324      * @param {String} resources.url           链接地址.
 325      * @param {String} [resources.type]        取值["css","js","html"],默认参考文件后缀.
 326      * @param {String} [resources.requestType] 取值["dom","ajax"],默认js和css用dom标签,html用ajax.
 327      * @param {Function} resources.onload        当前resource加载完成的回调函数,若requestType为ajax,参数为xhr(可能失效),responseText;若requestType为dom,无参数,执行时this为相应dom标签。.
 328      *
 329      * @param {Object} [options]               可选参数.
 330      * @param {Function} [options.onload]        资源全部加载完成的回调函数,无参数。.
 331      * @param {Boolean} [options.parallel]      是否并行加载,默认为false,串行。.
 332      * @param {Boolean} [ignoreAllLoaded]       全部加载之后不触发回调事件.主要用于内部实现.
 333      *
 334      *
 335      * @remark
 336      *  //串行实例
 337      *  baidu.page.load([
 338      *      { url : "http://img.baidu.com/js/tangram-1.3.2.js" },
 339      *      {url : "http://xxx.baidu.com/xpath/logicRequire.js",
 340      *          onload : fnOnRequireLoaded
 341      *      },
 342      *      { url : "http://xxx.baidu.com/xpath/target.js" }
 343      *  ],{
 344      *      onload : fnWhenTargetOK
 345      *  });
 346      *  //并行实例
 347      *  baidu.page.load([
 348      *      {
 349      *          url : "http://xxx.baidu.com/xpath/template.html",
 350      *          onload : fnExtractTemplate
 351      *      },
 352      *      { url : "http://xxx.baidu.com/xpath/style.css"},
 353      *      {
 354      *          url : "http://xxx.baidu.com/xpath/import.php?f=baidu.*",
 355      *          type : "js"
 356      *      },
 357      *      {
 358      *          url : "http://xxx.baidu.com/xpath/target.js",
 359      *      },
 360      *      {
 361      *          url : "http://xxx.baidu.com/xpath/jsonData.js",
 362      *          requestType : "ajax",
 363      *          onload : fnExtractData
 364      *      }
 365      *  ],{
 366      *      parallel : true,
 367      *      onload : fnWhenEverythingIsOK
 368      * });
 369      */
 370     $.page.load = function(resources, options, ignoreAllLoaded) {
 371         options = options || {};
 372         var self = $.page.load,
 373             cache = self._cache = self._cache || {},
 374             loadingCache = self._loadingCache = self._loadingCache || {},
 375             parallel = options.parallel;
 376 
 377         function allLoadedChecker() {
 378             for (var i = 0, len = resources.length; i < len; ++i) {
 379                 if (! cache[resources[i].url]) {
 380                     setTimeout(arguments.callee, 10);
 381                     return;
 382                 }
 383             }
 384             options.onload();
 385         };
 386 
 387         function loadByDom(res, callback) {
 388             var node, loaded, onready;
 389             switch (res.type.toLowerCase()) {
 390                 case 'css' :
 391                     node = document.createElement('link');
 392                     node.setAttribute('rel', 'stylesheet');
 393                     node.setAttribute('type', 'text/css');
 394                     break;
 395                 case 'js' :
 396                     node = document.createElement('script');
 397                     node.setAttribute('type', 'text/javascript');
 398                     node.setAttribute('charset', res.charset || self.charset);
 399                     break;
 400                 case 'html' :
 401                     node = document.createElement('iframe');
 402                     node.frameBorder = 'none';
 403                     break;
 404                 default :
 405                     return;
 406             }
 407 
 408             // HTML,JS works on all browsers, CSS works only on IE.
 409             onready = function() {
 410                 if (!loaded && (!this.readyState ||
 411                         this.readyState === 'loaded' ||
 412                         this.readyState === 'complete')) {
 413                     loaded = true;
 414                     // 防止内存泄露
 415                     $(node).unbind('load', onready);
 416                     $(node).unbind('readystatechange', onready);
 417                     //node.onload = node.onreadystatechange = null;
 418                     callback.call(window, node);
 419                 }
 420             };
 421             $(node).bind('load', onready);
 422             $(node).bind('readystatechange', onready);
 423             //CSS has no onload event on firefox and webkit platform, so hack it.
 424             if (res.type == 'css') {
 425                 (function() {
 426                     //避免重复加载
 427                     if (loaded) return;
 428                     try {
 429                         node.sheet.cssRule;
 430                     } catch (e) {
 431                         setTimeout(arguments.callee, 20);
 432                         return;
 433                     }
 434                     loaded = true;
 435                     callback.call(window, node);
 436                 })();
 437             }
 438 
 439             node.href = node.src = res.url;
 440             document.getElementsByTagName('head')[0].appendChild(node);
 441         }
 442 
 443         //兼容第一个参数直接是资源地址.
 444         $.lang.isString(resources) && (resources = [{url: resources}]);
 445         //避免递归出错,添加容错.
 446         if (! (resources && resources.length)) return;
 447 
 448         function loadResources(res) {
 449             var url = res.url,
 450                 shouldContinue = !!parallel,
 451                 cacheData,
 452                 callback = function(textOrNode) {
 453                     //ajax存入responseText,dom存入节点,用于保证onload的正确执行.
 454                     cache[res.url] = textOrNode;
 455                     delete loadingCache[res.url];
 456 
 457                     if ($.lang.isFunction(res.onload)) {
 458                         //若返回false, 则停止接下来的加载.
 459                         if (false === res.onload.call(window, textOrNode)) {
 460                             return;
 461                         }
 462                     }
 463                     //串行时递归执行
 464                     !parallel && self(resources.slice(1), options, true);
 465                     if ((! ignoreAllLoaded) && $.lang.isFunction(options.onload)) {
 466                         allLoadedChecker();
 467                     }
 468                 };
 469             //默认用后缀名, 并防止后缀名大写
 470             res.type = res.type || url.replace(/^[^?#]+.(css|js|html)(?|#| |$)[^?#]*/i, '$1'); //[bugfix]修改xxx.js?v这种情况下取不到js的问题。 
 471             //默认html格式用ajax请求,其他都使用dom标签方式请求.
 472             res.requestType = res.requestType || (res.type == 'html' ? 'ajax' : 'dom');
 473 
 474             if (cacheData = cache[res.url]) {
 475                 callback(cacheData);
 476                 return shouldContinue;
 477             }
 478             if (!options.refresh && loadingCache[res.url]) {
 479                 setTimeout(function() {loadResources(res);}, 10);
 480                 return shouldContinue;
 481             }
 482             loadingCache[res.url] = true;
 483             if (res.requestType.toLowerCase() == 'dom') {
 484                 loadByDom(res, callback);
 485             }else {//ajax
 486                 $.getScript(res.url, function(responseText){
 487                     callback(responseText);
 488                 });
 489             }
 490             //串行模式,通过callback方法执行后续
 491             return shouldContinue;
 492         };
 493 
 494         $.each(resources, function(k, item){
 495             loadResources(item);
 496         });
 497     };
 498     //默认编码设置为UTF8
 499     $.page.load.charset = 'UTF8';
 500 })(jQuery);
 501 
 502 ;(function($){
 503     /**
 504      * 判断浏览器类型
 505      */
 506     $.browser = $.browser || {};
 507 
 508     $.browser.ie = /msie (d+.d+)/i.test(navigator.userAgent) ? (document.documentMode || + RegExp['x241']) : undefined;
 509     
 510     $.browser.firefox = /firefox/(d+.d+)/i.test(navigator.userAgent) ? + RegExp['x241'] : undefined;
 511 
 512     $.browser.chrome = /chrome/(d+.d+)/i.test(navigator.userAgent) ? + RegExp['x241'] : undefined;
 513     
 514     //判断是否为gecko内核
 515     $.browser.isGecko = /gecko/i.test(navigator.userAgent) && !/like gecko/i.test(navigator.userAgent);
 516     
 517     //判断是否为webkit内核
 518     $.browser.isWebkit = /webkit/i.test(navigator.userAgent);
 519     
 520     //判断是否严格标准的渲染模式
 521     $.browser.isStrict = document.compatMode == "CSS1Compat";
 522 
 523     $.browser.opera = /opera(/| )(d+(.d+)?)(.+?(version/(d+(.d+)?)))?/i.test(navigator.userAgent) ?  + ( RegExp["x246"] || RegExp["x242"] ) : undefined;
 524 
 525     (function(){
 526         var ua = navigator.userAgent;
 527         /**
 528          * 判断是否为safari浏览器, 支持ipad
 529          * @property safari safari版本号
 530          * @grammar $.browser.safari
 531          */
 532         $.browser.safari = /(d+.d)?(?:.d)?s+safari/?(d+.d+)?/i.test(ua) && !/chrome/i.test(ua) ? + (RegExp['x241'] || RegExp['x242']) : undefined;
 533     })();
 534 })(jQuery);
 535 
 536 
 537 ;(function($){
 538     /**
 539      * 判断平台类型和特性的属性
 540      */
 541     $.platform = $.platform || {};
 542 
 543     /**
 544      * 判断是否为android平台
 545      */
 546     $.platform.isAndroid = /android/i.test(navigator.userAgent);
 547 
 548 
 549     /**
 550      * 判断是否为ipad平台
 551      */
 552     $.platform.isIpad = /ipad/i.test(navigator.userAgent);
 553 
 554     /**
 555      * 判断是否为iphone平台
 556      */
 557     $.platform.isIphone = /iphone/i.test(navigator.userAgent);
 558 
 559 
 560     /**
 561      * 判断是否为macintosh平台
 562      */
 563     $.platform.isMacintosh = /macintosh/i.test(navigator.userAgent);
 564 
 565 
 566 
 567     /**
 568      * 判断是否为windows平台
 569      */
 570     $.platform.isWindows = /windows/i.test(navigator.userAgent);
 571 
 572 
 573 
 574     /**
 575      * 判断是否为x11平台
 576      */
 577     $.platform.isX11 = /x11/i.test(navigator.userAgent);
 578 })(jQuery);
 579 
 580 
 581 ;(function($){
 582     /**
 583      * String增强
 584      */
 585     $.string = $.string || {};
 586 
 587     /**
 588      * 对目标字符串进行html解码
 589      * @grammar $.string.decodeHTML(source)
 590      * @param {string} source 目标字符串
 591      *             
 592      * @returns {string} html解码后的字符串
 593      */
 594     $.string.decodeHTML = function (source) {
 595         var str = String(source)
 596                     .replace(/"/g,'"')
 597                     .replace(/</g,'<')
 598                     .replace(/>/g,'>')
 599                     .replace(/&/g, "&");
 600         //处理转义的中文和实体字符
 601         return str.replace(/&#([d]+);/g, function(_0, _1){
 602             return String.fromCharCode(parseInt(_1, 10));
 603         });
 604     };
 605 
 606     /**
 607      * 对目标字符串进行html编码
 608      * @grammar $.string.encodeHTML(source)
 609      * @param {string} source 目标字符串
 610      * 编码字符有5个:&<>"'
 611      *             
 612      * @returns {string} html编码后的字符串
 613      */
 614     $.string.encodeHTML = function (source) {
 615         return String(source)
 616                     .replace(/&/g,'&')
 617                     .replace(/</g,'<')
 618                     .replace(/>/g,'>')
 619                     .replace(/"/g, """)
 620                     .replace(/'/g, "'");
 621     };
 622 
 623     /**
 624      * 去掉字符串中的html标签
 625      * @function
 626      * @grammar $.string.stripTags(source)
 627      * @param {string} source 要处理的字符串.
 628      * @return {String}
 629      */
 630     $.string.stripTags = function(source) {
 631         return String(source || '').replace(/<[^>]+>/g, '');
 632     };
 633 
 634     /**
 635      * 将目标字符串中可能会影响正则表达式构造的字符串进行转义。
 636      * @grammar $.string.escapeReg(source)
 637      * @param {string} source 目标字符串
 638      * @remark
 639      * 给以下字符前加上“”进行转义:.*+?^=!:${}()|[]/
 640      *             
 641      * @returns {string} 转义后的字符串
 642      */
 643     $.string.escapeReg = function (source) {
 644         return String(source)
 645                 .replace(new RegExp("([.*+?^=!:x24{}()|[\]/\\])", "g"), '\x241');
 646     };
 647 
 648     /**
 649      * 删除目标字符串两端的空白字符
 650      * @grammar $.string.trim(source)
 651      * @param {string} source 目标字符串
 652      * @remark
 653      * 不支持删除单侧空白字符
 654      *             
 655      * @returns {string} 删除两端空白字符后的字符串
 656      */
 657 
 658     (function () {
 659         var trimer = new RegExp("(^[\s\t\xa0\u3000]+)|([\u3000\xa0\s\t]+x24)", "g");
 660         
 661         $.string.trim = function (source) {
 662             return String(source)
 663                     .replace(trimer, "");
 664         };
 665     })();
 666 })(jQuery);
 667 
 668 
 669 ;(function($){
 670     /**
 671      * GUID
 672      */
 673     $.lang = $.lang || {};
 674     /**
 675      * 返回一个当前页面的唯一标识字符串。
 676      * @grammar $.lang.guid()
 677      *             
 678      * @returns {String} 当前页面的唯一标识字符串
 679      */
 680     $.lang.guid = function() {
 681         return "TANGRAM$" + $.lang._counter ++;
 682     };
 683 
 684     //不直接使用window,可以提高3倍左右性能
 685     $.lang._counter = $.lang._counter || 1;
 686     $.guid = $.lang.guid;//设置$.page.guid别名$.guid
 687 
 688 
 689     /**
 690      * 判断目标参数是否为function或Function实例
 691      * @grammar $.lang.isFunction(source)
 692      * @param {Any} source 目标参数
 693      * @returns {boolean} 类型判断结果
 694      */
 695     $.lang.isFunction = function (source) {
 696         // chrome下,'function' == typeof /a/ 为true.
 697         return '[object Function]' == Object.prototype.toString.call(source);
 698     };
 699 
 700     /**
 701      * 判断目标参数是否为Element对象
 702      * @grammar $.lang.isElement(source)
 703      * @param {Any} source 目标参数
 704      * @returns {boolean} 类型判断结果
 705      */
 706     $.lang.isElement = function (source) {
 707         return !!(source && source.nodeName && source.nodeType == 1);
 708     };
 709 
 710     /**
 711      * 判断目标参数是否Array对象
 712      * @grammar $.lang.isArray(source)
 713      * @param {Any} source 目标参数   
 714      * @returns {boolean} 类型判断结果
 715      */
 716     $.lang.isArray = function (source) {
 717         return '[object Array]' == Object.prototype.toString.call(source);
 718     };
 719 })(jQuery);
 720 
 721 ;(function($){
 722     /**
 723      * Array增强
 724      */
 725      $.array = $.array || {};
 726 
 727     /**
 728      * 查询数组中指定元素的索引位置
 729      * @grammar $.array.indexOf(source, match[, fromIndex])
 730      * @param {Array} source 需要查询的数组
 731      * @param {Any} match 查询项
 732      * @param {number} [fromIndex] 查询的起始位索引位置,如果为负数,则从source.length+fromIndex往后开始查找
 733      *             
 734      * @returns {number} 指定元素的索引位置,查询不到时返回-1
 735      */
 736     $.array.indexOf = function (source, match, fromIndex) {
 737         var len = source.length,
 738             iterator = match;
 739             
 740         fromIndex = fromIndex | 0;
 741         if(fromIndex < 0){//小于0
 742             fromIndex = Math.max(0, len + fromIndex)
 743         }
 744         for ( ; fromIndex < len; fromIndex++) {
 745             if(fromIndex in source && source[fromIndex] === match) {
 746                 return fromIndex;
 747             }
 748         }
 749         
 750         return -1;
 751     };
 752 
 753     /**
 754      * 判断一个数组中是否包含给定元素
 755      * @grammar $.array.contains(source, obj)
 756      * @param {Array} source 需要判断的数组.
 757      * @param {Any} obj 要查找的元素.
 758      * @return {boolean} 判断结果.
 759      * @author berg
 760      */
 761     $.array.contains = function(source, obj) {
 762         return ($.array.indexOf(source, obj) >= 0);
 763     };
 764 
 765     /**
 766      * 清空一个数组
 767      * @grammar $.array.empty(source)
 768      * @param {Array} source 需要清空的数组.
 769      * @author berg
 770      */
 771     $.array.empty = function(source) {
 772         source.length = 0;
 773     };
 774 
 775     /**
 776      * 从后往前,查询数组中指定元素的索引位置
 777      * @grammar $.array.lastIndexOf(source, match)
 778      * @param {Array} source 需要查询的数组
 779      * @param {Any} match 查询项
 780      * @param {number} [fromIndex] 查询的起始位索引位置,如果为负数,则从source.length+fromIndex往前开始查找
 781      *             
 782      * @returns {number} 指定元素的索引位置,查询不到时返回-1
 783      */
 784 
 785     $.array.lastIndexOf = function (source, match, fromIndex) {
 786         var len = source.length;
 787 
 788         fromIndex = fromIndex | 0;
 789 
 790         if(!fromIndex || fromIndex >= len){
 791             fromIndex = len - 1;
 792         }
 793         if(fromIndex < 0){
 794             fromIndex += len;
 795         }
 796         for(; fromIndex >= 0; fromIndex --){
 797             if(fromIndex in source && source[fromIndex] === match){
 798                 return fromIndex;
 799             }
 800         }
 801         
 802         return -1;
 803     };
 804 
 805     /**
 806      * 遍历数组中所有元素,将每一个元素应用方法进行转换,并返回转换后的新数组。
 807      * @grammar $.array.map(source, iterator[, thisObject])
 808      * @param {Array}    source   需要遍历的数组.
 809      * @param {Function} iterator 对每个数组元素进行处理的函数.
 810      * @param {Object} [thisObject] 函数调用时的this指针,如果没有此参数,默认是当前遍历的数组
 811      * @return {Array} map后的数组.
 812      */
 813     $.array.map = function(source, iterator, thisObject) {
 814         var results = [],
 815             i = 0,
 816             l = source.length;
 817         for (; i < l; i++) {
 818             results[i] = iterator.call(thisObject || source, source[i], i);
 819         }
 820         return results;
 821     };
 822 
 823     /**
 824      * 遍历数组中所有元素,将每一个元素应用方法进行合并,并返回合并后的结果。
 825      * @grammar $.array.reduce(source, iterator[, initializer])
 826      * @param {Array}    source 需要遍历的数组.
 827      * @param {Function} iterator 对每个数组元素进行处理的函数,函数接受四个参数:上一次reduce的结果(或初始值),当前元素值,索引值,整个数组.
 828      * @param {Object}   [initializer] 合并的初始项,如果没有此参数,默认用数组中的第一个值作为初始值.
 829      * @return {Array} reduce后的值.
 830      */
 831     $.array.reduce = function(source, iterator, initializer) {
 832         var i = 0,
 833             l = source.length,
 834             found = 0;
 835 
 836         if( arguments.length < 3){
 837             //没有initializer的情况,找到第一个可用的值
 838             for(; i < l; i++){
 839                 initializer = source[i++];
 840                 found = 1;
 841                 break;
 842             }
 843             if(!found){
 844                 return ;
 845             }
 846         }
 847 
 848         for (; i < l; i++) {
 849             if( i in source){
 850                 initializer = iterator(initializer, source[i] , i , source);
 851             }
 852         }
 853         return initializer;
 854     };
 855 
 856     /**
 857      * 移除数组中的项
 858      * @grammar $.array.remove(source, match)
 859      * @param {Array} source 需要移除项的数组
 860      * @param {Any} match 要移除的项
 861      *             
 862      * @returns {Array} 移除后的数组
 863      */
 864     $.array.remove = function (source, match) {
 865         var len = source.length;
 866             
 867         while (len--) {
 868             if (len in source && source[len] === match) {
 869                 source.splice(len, 1);
 870             }
 871         }
 872         return source;
 873     };
 874 
 875     /**
 876      * 移除数组中的项
 877      * @grammar $.array.removeAt(source, index)
 878      * @param {Array} source 需要移除项的数组
 879      * @param {number} index 要移除项的索引位置
 880      * @returns {Any} 被移除的数组项
 881      */
 882     $.array.removeAt = function (source, index) {
 883         return source.splice(index, 1)[0];
 884     };
 885 
 886     /**
 887      * 过滤数组中的相同项。如果两个元素相同,会删除后一个元素。
 888      * @grammar $.array.unique(source[, compareFn])
 889      * @param {Array} source 需要过滤相同项的数组
 890      * @param {Function} [compareFn] 比较两个数组项是否相同的函数,两个数组项作为函数的参数。
 891      *             
 892      * @returns {Array} 过滤后的新数组
 893      */
 894     $.array.unique = function (source, compareFn) {
 895         var len = source.length,
 896             result = source.slice(0),
 897             i, datum;
 898             
 899         if ('function' != typeof compareFn) {
 900             compareFn = function (item1, item2) {
 901                 return item1 === item2;
 902             };
 903         }
 904         
 905         // 从后往前双重循环比较
 906         // 如果两个元素相同,删除后一个
 907         while (--len > 0) {
 908             datum = result[len];
 909             i = len;
 910             while (i--) {
 911                 if (compareFn(datum, result[i])) {
 912                     result.splice(len, 1);
 913                     break;
 914                 }
 915             }
 916         }
 917 
 918         return result;
 919     };
 920 })(jQuery);
 921 
 922 ;(function(){
 923     /**
 924      * 操作cookie的方法
 925      * @namespace $.cookie
 926      */
 927     $.cookie = $.cookie || {};
 928 
 929     /**
 930      * 验证字符串是否合法的cookie键名
 931      * 
 932      * @param {string} source 需要遍历的数组
 933      * @meta standard
 934      * @return {boolean} 是否合法的cookie键名
 935      */
 936     $.cookie._isValidKey = function (key) {
 937         // http://www.w3.org/Protocols/rfc2109/rfc2109
 938         // Syntax:  General
 939         // The two state management headers, Set-Cookie and Cookie, have common
 940         // syntactic properties involving attribute-value pairs.  The following
 941         // grammar uses the notation, and tokens DIGIT (decimal digits) and
 942         // token (informally, a sequence of non-special, non-white space
 943         // characters) from the HTTP/1.1 specification [RFC 2068] to describe
 944         // their syntax.
 945         // av-pairs   = av-pair *(";" av-pair)
 946         // av-pair    = attr ["=" value] ; optional value
 947         // attr       = token
 948         // value      = word
 949         // word       = token | quoted-string
 950         
 951         // http://www.ietf.org/rfc/rfc2068.txt
 952         // token      = 1*<any CHAR except CTLs or tspecials>
 953         // CHAR       = <any US-ASCII character (octets 0 - 127)>
 954         // CTL        = <any US-ASCII control character
 955         //              (octets 0 - 31) and DEL (127)>
 956         // tspecials  = "(" | ")" | "<" | ">" | "@"
 957         //              | "," | ";" | ":" | "" | <">
 958         //              | "/" | "[" | "]" | "?" | "="
 959         //              | "{" | "}" | SP | HT
 960         // SP         = <US-ASCII SP, space (32)>
 961         // HT         = <US-ASCII HT, horizontal-tab (9)>
 962             
 963         return (new RegExp("^[^\x00-\x20\x7f\(\)<>@,;:\\\"\[\]\?=\{\}\/\u0080-\uffff]+x24")).test(key);
 964     };
 965 
 966     /**
 967      * 获取cookie的值,不对值进行解码
 968      * @grammar $.cookie.getRaw(key)
 969      * @param {string} key 需要获取Cookie的键名
 970      * @returns {string|null} 获取的Cookie值,获取不到时返回null
 971      */
 972     $.cookie.getRaw = function (key) {
 973         if ($.cookie._isValidKey(key)) {
 974             var reg = new RegExp("(^| )" + key + "=([^;]*)(;|x24)"),
 975                 result = reg.exec(document.cookie);
 976                 
 977             if (result) {
 978                 return result[2] || null;
 979             }
 980         }
 981 
 982         return null;
 983     };
 984 
 985     /**
 986      * 设置cookie的值,不对值进行编码
 987      * @grammar $.cookie.setRaw(key, value[, options])
 988      * @param {string} key 需要设置Cookie的键名
 989      * @param {string} value 需要设置Cookie的值
 990      * @param {Object} [options] 设置Cookie的其他可选参数
 991      * @config {string} [path] cookie路径
 992      * @config {Date|number} [expires] cookie过期时间,如果类型是数字的话, 单位是毫秒
 993      * @config {string} [domain] cookie域名
 994      * @config {string} [secure] cookie是否安全传输
 995      * @remark
 996      * 
 997     <b>options参数包括:</b><br>
 998     path:cookie路径<br>
 999     expires:cookie过期时间,Number型,单位为毫秒。<br>
1000     domain:cookie域名<br>
1001     secure:cookie是否安全传输
1002      */
1003     $.cookie.setRaw = function (key, value, options) {
1004         if (!$.cookie._isValidKey(key)) {
1005             return;
1006         }
1007         
1008         options = options || {};
1009         //options.path = options.path || "/"; // meizz 20100402 设定一个初始值,方便后续的操作
1010         //berg 20100409 去掉,因为用户希望默认的path是当前路径,这样和浏览器对cookie的定义也是一致的
1011         
1012         // 计算cookie过期时间
1013         var expires = options.expires;
1014         if ('number' == typeof options.expires) {
1015             expires = new Date();
1016             expires.setTime(expires.getTime() + options.expires);
1017         }
1018         
1019         document.cookie =
1020             key + "=" + value
1021             + (options.path ? "; path=" + options.path : "")
1022             + (expires ? "; expires=" + expires.toGMTString() : "")
1023             + (options.domain ? "; domain=" + options.domain : "")
1024             + (options.secure ? "; secure" : ''); 
1025     };
1026 
1027     /**
1028      * 删除cookie的值
1029      * @grammar $.cookie.remove(key, options)
1030      * @param {string} key 需要删除Cookie的键名
1031      * @param {Object} options 需要删除的cookie对应的 path domain 等值
1032      */
1033     $.cookie.remove = function (key, options) {
1034         options = options || {};
1035         options.expires = new Date(0);
1036         $.cookie.setRaw(key, '', options);
1037     };
1038 
1039     /**
1040      * 获取cookie的值,用decodeURIComponent进行解码
1041      * @grammar $.cookie.get(key)
1042      * @param {string} key 需要获取Cookie的键名
1043      * @remark
1044      * <b>注意:</b>该方法会对cookie值进行decodeURIComponent解码。如果想获得cookie源字符串,请使用getRaw方法。
1045      *             
1046      * @returns {string|null} cookie的值,获取不到时返回null
1047      */
1048     $.cookie.get = function (key) {
1049         var value = $.cookie.getRaw(key);
1050         if ('string' == typeof value) {
1051             value = decodeURIComponent(value);
1052             return value;
1053         }
1054         return null;
1055     };
1056 
1057     /**
1058      * 设置cookie的值,用encodeURIComponent进行编码
1059      * @grammar $.cookie.set(key, value[, options])
1060      * @param {string} key 需要设置Cookie的键名
1061      * @param {string} value 需要设置Cookie的值
1062      * @param {Object} [options] 设置Cookie的其他可选参数
1063      * @config {string} [path] cookie路径
1064      * @config {Date|number} [expires] cookie过期时间,如果类型是数字的话, 单位是毫秒
1065      * @config {string} [domain] cookie域名
1066      * @config {string} [secure] cookie是否安全传输
1067      * @remark
1068      * 
1069     1. <b>注意:</b>该方法会对cookie值进行encodeURIComponent编码。如果想设置cookie源字符串,请使用setRaw方法。<br><br>
1070     2. <b>options参数包括:</b><br>
1071     path:cookie路径<br>
1072     expires:cookie过期时间,Number型,单位为毫秒。<br>
1073     domain:cookie域名<br>
1074     secure:cookie是否安全传输
1075      */
1076     $.cookie.set = function (key, value, options) {
1077         $.cookie.setRaw(key, encodeURIComponent(value), options);
1078     };
1079 })(jQuery);
1080 
1081 ;(function($){
1082     $.number = $.number || {};
1083 
1084     /**
1085      * 为目标数字添加逗号分隔
1086      * @grammar $.number.comma(source[, length])
1087      * @param {number} source 需要处理的数字
1088      * @param {number} [length] 两次逗号之间的数字位数,默认为3位
1089      *             
1090      * @returns {string} 添加逗号分隔后的字符串
1091      */
1092     $.number.comma = function (source, length) {
1093         if (!length || length < 1) {
1094             length = 3;
1095         }
1096 
1097         source = String(source).split(".");
1098         source[0] = source[0].replace(new RegExp('(\d)(?=(\d{'+length+'})+$)','ig'),"$1,");
1099         return source.join(".");
1100     };
1101 
1102     /**
1103      * 生成随机整数,范围是[min, max]
1104      * @grammar $.number.randomInt(min, max) 
1105      * 
1106      * @param     {number} min     随机整数的最小值
1107      * @param     {number} max     随机整数的最大值
1108      * @return     {number}         生成的随机整数
1109      */
1110     $.number.randomInt = function(min, max){
1111         return Math.floor(Math.random() * (max - min + 1) + min);
1112     };
1113 
1114     /**
1115      * 对目标数字进行0补齐处理
1116      * @grammar $.number.pad(source, length)
1117      * @param {number} source 需要处理的数字
1118      * @param {number} length 需要输出的长度
1119      *             
1120      * @returns {string} 对目标数字进行0补齐处理后的结果
1121      */
1122     $.number.pad = function (source, length) {
1123         var pre = "",
1124             negative = (source < 0),
1125             string = String(Math.abs(source));
1126 
1127         if (string.length < length) {
1128             pre = (new Array(length - string.length + 1)).join('0');
1129         }
1130 
1131         return (negative ?  "-" : "") + pre + string;
1132     };
1133 })(jQuery);
1134 
1135 ;(function($){
1136     $.sio = $.sio || {};
1137     /**
1138      * 通过请求一个图片的方式令服务器存储一条日志
1139      * @grammar $.sio.log(url)
1140      * @param {string} url 要发送的地址.
1141      */
1142     $.sio.log = function(url) {
1143       var img = new Image(),
1144           key = 'tangram_sio_log_' + Math.floor(Math.random() *
1145                 2147483648).toString(36);
1146 
1147       // 这里一定要挂在window下
1148       // 在IE中,如果没挂在window下,这个img变量又正好被GC的话,img的请求会abort
1149       // 导致服务器收不到日志
1150       window[key] = img;
1151 
1152       img.onload = img.onerror = img.onabort = function() {
1153         // 下面这句非常重要
1154         // 如果这个img很不幸正好加载了一个存在的资源,又是个gif动画
1155         // 则在gif动画播放过程中,img会多次触发onload
1156         // 因此一定要清空
1157         img.onload = img.onerror = img.onabort = null;
1158 
1159         window[key] = null;
1160 
1161         // 下面这句非常重要
1162         // new Image创建的是DOM,DOM的事件中形成闭包环引用DOM是典型的内存泄露
1163         // 因此这里一定要置为null
1164         img = null;
1165       };
1166 
1167       // 一定要在注册了事件之后再设置src
1168       // 不然如果图片是读缓存的话,会错过事件处理
1169       // 最后,对于url最好是添加客户端时间来防止缓存
1170       // 同时服务器也配合一下传递Cache-Control: no-cache;
1171       img.src = url;
1172     };
1173 })(jQuery);
1174 
1175 
1176 ;(function($){
1177     $.url = $.url || {};
1178     /**
1179      * 对字符串进行%#&+=以及和s匹配的所有字符进行url转义
1180      * @grammar $.url.escapeSymbol(source)
1181      * @param {string} source 需要转义的字符串.
1182      * @return {string} 转义之后的字符串.
1183      * @remark
1184      * 用于get请求转义。在服务器只接受gbk,并且页面是gbk编码时,可以经过本转义后直接发get请求。
1185      *
1186      * @return {string} 转义后的字符串
1187      */
1188     $.url.escapeSymbol = function(source) {
1189         
1190         //TODO: 之前使用s来匹配任意空白符
1191         //发现在ie下无法匹配中文全角空格和纵向指标符v,所以改s为f
	v以及中文全角空格和英文空格
1192         //但是由于ie本身不支持纵向指标符v,故去掉对其的匹配,保证各浏览器下效果一致
1193         return String(source).replace(/[#%&+=/\  f
	]/g, function(all) {
1194             return '%' + (0x100 + all.charCodeAt()).toString(16).substring(1).toUpperCase();
1195         });
1196     };
1197 
1198     /**
1199      * 根据参数名从目标URL中获取参数值
1200      * @grammar $.url.getQueryValue(url, key)
1201      * @param {string} url 目标URL
1202      * @param {string} key 要获取的参数名
1203      *             
1204      * @returns {string|null} - 获取的参数值,其中URI编码后的字符不会被解码,获取不到时返回null
1205      */
1206     $.url.getQueryValue = function (url, key) {
1207         var reg = new RegExp(
1208                             "(^|&|\?|#)" 
1209                             + $.string.escapeReg(key) 
1210                             + "=([^&#]*)(&|x24|#)", 
1211                         "");
1212         var match = url.match(reg);
1213         if (match) {
1214             return match[2];
1215         }
1216         
1217         return null;
1218     };
1219 
1220     /**
1221      * 将json对象解析成query字符串
1222      * @grammar $.url.jsonToQuery(json[, replacer])
1223      * @param {Object} json 需要解析的json对象
1224      * @param {Function=} replacer_opt 对值进行特殊处理的函数,function (value, key)
1225      *             
1226      * @return {string} - 解析结果字符串,其中值将被URI编码,{a:'&1 '} ==> "a=%261%20"。
1227      */
1228     $.url.jsonToQuery = function (json, replacer_opt) {
1229         var result = [], 
1230             itemLen,
1231             replacer = replacer_opt || function (value) {
1232               return $.url.escapeSymbol(value);
1233             };
1234             
1235         $.each(json, function(item, key){
1236             // 这里只考虑item为数组、字符串、数字类型,不考虑嵌套的object
1237             if ($.lang.isArray(item)) {
1238                 itemLen = item.length;
1239                 // value的值需要encodeURIComponent转义吗?
1240                 // FIXED 优化了escapeSymbol函数
1241                 while (itemLen--) {
1242                     result.push(key + '=' + replacer(item[itemLen], key));
1243                 }
1244             } else {
1245                 result.push(key + '=' + replacer(item, key));
1246             }
1247         });
1248         
1249         return result.join('&');
1250     };
1251 
1252 
1253     /**
1254      * 解析目标URL中的参数成json对象
1255      * @grammar $.url.queryToJson(url)
1256      * @param {string} url 目标URL
1257      *             
1258      * @returns {Object} - 解析为结果对象,其中URI编码后的字符不会被解码,'a=%20' ==> {a:'%20'}。
1259      */
1260     $.url.queryToJson = function (url) {
1261         var query   = url.substr(url.lastIndexOf('?') + 1),
1262             params  = query.split('&'),
1263             len     = params.length,
1264             result  = {},
1265             i       = 0,
1266             key, value, item, param;
1267         
1268         for (; i < len; i++) {
1269             if(!params[i]){
1270                 continue;
1271             }
1272             param   = params[i].split('=');
1273             key     = param[0];
1274             value   = param[1];
1275             
1276             item = result[key];
1277             if ('undefined' == typeof item) {
1278                 result[key] = value;
1279             } else if ($.lang.isArray(item)) {
1280                 item.push(value);
1281             } else { // 这里只可能是string了
1282                 result[key] = [item, value];
1283             }
1284         }
1285         
1286         return result;
1287     };
1288 })(jQuery);
原文地址:https://www.cnblogs.com/chris-oil/p/4166792.html