/** * project:javascript DOM Advanced */ (function () { 'use strict'; //ADS命名空间 //判断ADS对象是否存在,否则创建 if (!window.ADS) { window.ADS = {}; } //确定当前浏览器是否与整个库兼容 function isCompatible(other) { //使用能力检测来检查必要条件 if (other === false || !Array.prototype.push || !Object.hasOwnProperty || !document.createElement || !document.getElementsByTagName) { return false; } return true; } window.ADS.isCompatible = isCompatible; function $() { var elements = []; //查找作为参数提供的所有元素 for (var i = 0; i < arguments.length; i++) { var element = arguments[i]; //如果这参数是一个字符串那假设它是一个id if (typeof element === "string") { element = document.getElementById(element); } //如果只提供一个参数 //则立即返回这个元素 if (arguments.length === 1) { return element; } //否则,将它添加到数组中 elements.push(element); } //返回包含多个被请求元素的数组 return elements; } window.ADS.$ = $; /** * addEvent(node, type, listener) 添加事件程序处理 * @param node DOM节点元素 * @param type 事件类型 * @param listener 事件函数 * @return {Boolean} */ function addEvent(node, type, listener) { //使用方法检查兼容性以保证平稳退化 if (!isCompatible()) { return false; } //检查是不是DOM对象的引用 if (!(node = $(node))) { return false; } if (node.addEventListener) { //W3C的方法 node.addEventListener(type, listener, false); //第三个参数为true时则启用捕获阶段 return true; } else { //MSIE的方法 //修正ie事件中this指向window的问题 //把执行函数赋给自定义对象属性 node["e" + type + listener] = listener; //把事件函数赋给自定义对象属性 node[type + listener] = function () { node["e" + type + listener](window.event); }; node.attachEvent("on" + type, node[type + listener]); return true; } //若两种方法都不具备则返回false return false; } window.ADS.addEvent = addEvent; /** * load事件在嵌入图像载入完成之前运行 * @param {Function} loadEvent * @param {Boolean} waitForImages 当为true时,调用原始的addEvent()方法,等待加载完毕后才执行 * demo: * ADS.addLoadEvent(function(w3cEvent){ * //... * }); */ function addLoadEvent(loadEvent, waitForImages) { if (!isCompatible()) { return false; } //如果等待标记是true则使用常规的添加事件的方法 if (waitForImages) { return addEvent(window, 'load', loadEvent); } //否则使用一些不同的方式包装loadEvent()方法 //以便为this关键字制定正确的内容,同时确定 //事件不会被执行两次 var init = function () { //如果这个函数已经被调用过了则返回 if (init.done) { return; } //标记这个函数以便检测它是否运行过 init.done = true; //在document的环境中运行载入事件 loadEvent.apply(document, arguments); }; //为DOMContentLoaded事件注册事件侦听器 if (document.addEventListener) { document.addEventListener('DOMContentLoaded', init, false); } //对于safari,使用setInterval()函数检测 //document是否载入完成 if (/WebKit/i.test(navigator.userAgent)) { var _timer = setInterval(function () { if (/loaded|complete/.test(document.readyState)) { clearInterval(_timer); init(); } }, 10); } //对于IE(使用条件注释) //附加一个在载入过程最后执行的脚本 //并检测该脚本是否载入完成 /*@cc_on @*/ /*@if (@_win32) document.write("<script id=_ie_onload defer src=javascript:void(0)><\/script>"); var script = document.getElementById('_ie_onload'); script.onreadystatechange = function(){ if((this.readyState === 'complete') || this.readyState === 'loaded'){ init(); } }; /*@end @*/ return true; } window.ADS.addLoadEvent = addLoadEvent; function removeEvent(node, type, listener) { if (!(node = $(node))) { return false; } if (node.removeEventListener) { //W3C 方法 node.removeEventListener(type, listener, false); return true; } else if (node.detachEvent) { //MSIE方法 node.detachEvent("on" + type, node[type + listener]); node[type + listener] = null; return true; } //若两种方法都不具备则返回false return false; } window.ADS.removeEvent = removeEvent; /** * 获取event事件对象 * @param w3cEvent * @return {*|Event} */ function getEventObject(w3cEvent) { return w3cEvent || window.event; } window.ADS.getEventObject = getEventObject; function getTarget(event) { event = event || getEventObject(event); //如果是w3c或MSIE的模型 var target = event.target || event.srcElement; //如果像Safari中一样是一个文本节点 //重新将目标对象指定为父元素 if (target.nodeType === 3) { target = target.parentNode; } return target; } window.ADS.getTarget = getTarget; function getMouseButton(event) { event = event || getEventObject(event); //使用适当的属性初始化一个对象变量 var buttons = { left: false, middle: false, right: false }; //检查event对象的toString()方法的值 //w3c有toString方法并且此时该方法的 // 返回应该是MouseEvent if (event.toString() && event.toString().indexOf('MouseEvent') !== -1) { //w3c switch (event.button) { case 0: buttons.left = true; break; case 1: buttons.middle = true; break; case 2: buttons.right = true; break; default: break; } } else if (event.button) { //MSIE switch (event.button) { case 1: buttons.left = true; break; case 2: buttons.right = true; break; case 3: buttons.left = true; buttons.right = true; break; case 4: buttons.middle = true; break; case 5: buttons.left = true; buttons.middle = true; break; case 6: buttons.middle = true; buttons.right = true; break; case 7: buttons.left = true; buttons.middle = true; buttons.right = true; break; default: break; } } else { return false; } return buttons; } window.ADS.getMouseButton = getMouseButton; /** * 光标相对于文档原点的位置 * @param {event} event * @return {Object} */ function getPointerPositionInDocument(event) { event = event || getEventObject(event); var x = event.pageX || (event.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft)); var y = event.pageY || (event.clientY + (document.documentElement.scrollTop || document.body.scrollTop)); //现在x和y中包含着鼠标 //相对于文档原点的坐标 return { x: x, y: y }; } window.ADS.getPointerPositionInDocument = getPointerPositionInDocument; /** * 获取键盘按键的Unicode值 * @param event * @return {Object} */ function getKeyPressed(event) { event = event || getEventObject(event); //charCode for FF var code = (typeof event.charCode === 'number') ? event.charCode : event.keyCode; var value = String.fromCharCode(code); return { code: code, value: value }; } window.ADS.getKeyPressed = getKeyPressed; /** * getElementsByClassName(className, tag, parent) 获取class为className的DOM元素 * @param searchClass 类名 (第二个函数可实现多类名查询) * @param node DOM元素 * @param tag 标签名 * @return {*} */ /*原书 function getElementsByClassName(className, tag, parent) { parent = parent || document; if (!(parent = $(parent))) { return false; } //查找所有匹配的标签 //由于IE5不支持document.getElementsByTagName("*"),要使用分支document.all以防错误 var allTags = (tag == "*" && parent.all) ? parent.all : parent.getElementsByTagName(tag); var matchingElements = []; //创建一个正则表达式,来判断className是否正确 className = className.replace(/\-/g, "\\-"); var regex = new RegExp("(^|\\s)" + className + "(\\s|$)"); var element; if (document.getElementsByClassName) { return document.getElementsByClassName(className); } else { //检查每个元素 for (var i = 0; i < allTags.length; i++) { element = allTags[i]; if (regex.test(element.className)) { matchingElements.push(element); } } //返回任何匹配的元素 return matchingElements; } }*/ function getElementsByClassName(searchClass, tag, node) { node = node || document; tag = tag || "*"; if (!(node = $(node))) { return false; } //如果支持原生getElementsByClassName if (document.getElementsByClassName) { //获取所有匹配的className的DOM元素 var nodes = node.getElementsByClassName(searchClass), result = nodes; //如果tag被定义了 if (tag !== '*') { result = []; for (var i = 0; node = nodes[i]; i++) { //匹配标签名为非“*”的标签 if (node.tagName.toLowerCase() === tag.toLowerCase()) { result.push(node); } } } return result; } else { //不支持原生getElementsByClassName的 var classes = searchClass.split(" "), //要查询的所有className elements = (tag === "*" && node.all) ? node.all : node.getElementsByTagName(tag), //获取所有匹配的标签DOM元素 patterns = [], returnElements = [], current, match; var i = classes.length; while (--i >= 0) { patterns.push(new RegExp("(^|\\s)" + classes[i] + "(\\s|$)")); } var j = elements.length; while (--j >= 0) { current = elements[j]; match = false; //每个元素的className分别和要查询的className对比 for (var k = 0, kl = patterns.length; k < kl; k++) { match = patterns[k].test(current.className); if (!match) { break; } } if (match) { returnElements.push(current); } } return returnElements; } } window.ADS.getElementsByClassName = getElementsByClassName; //运用:var a = ADS.getElementsByClassName("li", "li", document); function toggleDisplay(node, value) { if (!(node = $(node))) { return false; } if (node.style.display !== "none") { node.style.display = "none"; } else { node.style.display = value || ""; } return true; } window.ADS.toggleDisplay = toggleDisplay; /** * DOM操作:将节点插入到目标节点之后 * @param node 新节点 * @param referenceNode 目标节点 * @return {*} * etc: ADS.insertAfter("l1","l2"); */ function insertAfter(node, referenceNode) { if (!(node = $(node))) { return false; } if (!(referenceNode = $(referenceNode))) { return false; } return referenceNode.parentNode.insertBefore(node, referenceNode.nextSibling); } window.ADS.insertAfter = insertAfter; /** * 删除所有子节点 * @param parent 父节点 * @return {*} */ function removeChildren(parent) { if (!(parent = $(parent))) { return false; } //当存在子节点时删除该子节点 while (parent.firstChild) { parent.firstChild.parentNode.removeChild(parent.firstChild); } //再返回父元素,以便实现方法连缀 return parent; } window.ADS.removeChildren = removeChildren; /** * 添加为节点第一个子节点 * @param parent 父节点 * @param newChild 新节点 * @return {*} */ function prependChild(parent, newChild) { if (!(parent = $(parent))) { return false; } if (!(newChild = $(newChild))) { return false; } if (parent.firstChild) { //如果存在一个子节点,则在这个字节点之前插入 parent.insertBefore(newChild, parent.firstChild); } else { //如果没有子节点则直接添加 parent.appendChild(newChild); } //再返回父元素,以便实现方法连缀 return parent; } window.ADS.prependChild = prependChild; /** * 绑定函数到指定作用域 * @param obj 对象 * @param func 函数 * @return {Function} */ function bindFunction(obj, func) { return function () { //调用func函数,指定环境为obj,添加arguments参数 func.apply(obj, arguments); }; } window.ADS.bindFunction = bindFunction; /** * 获取浏览器窗口大小 * @return {Object} */ function getWinSize() { var de = document.documentElement; return { 'width': (window.innerWidth || (de && de.clientWidth) || document.body.clientWidth), 'height': (window.innerHeight || (de && de.clientHeight) || document.body.clientHeight) }; } window.ADS.getWinSize = getWinSize; /** * 定义DOM常量 * @type {Object} */ window.ADS.node = { ELEMENT_NODE: 1, ATTRIBUTE_NODE: 2, TEXT_NODE: 3, CDATA_SECTION_NODE: 4, ENTITY_REFERENCE_NODE: 5, ENTITY_NODE: 6, PROCESSING_INSTRUCTION_NODE: 7, COMMENT_NODE: 8, DOCUMENT_NODE: 9, DOCUMENT_TYPE_NODE: 10, DOCUMENT_FRAGMENT_NODE: 11, NOTATION_NODE: 12 }; /** * 遍历DOM,并调用函数(效率最慢) * @param {Function} func * @param {Node} node */ function walkElementsLinear(func, node) { var root = node || window.document; var nodes = root.getElementsByTagName('*'); for (var i = 0, len = nodes.length; i < len; i++) { func.call(nodes[i]); } } window.ADS.walkElementsLinear = walkElementsLinear; /** * 迭代DOM * @param {Function} func * @param {Node} node * @param {Number} depth * @param returnedFromParent */ function walkTheDOMRecursive(func, node, depth, returnedFromParent) { var root = node || window.document; //父节点调用函数的返回值,作为子节点调用函数时候的参数 returnedFromParent = func.call(root, depth++, returnedFromParent); node = root.firstChild; while (node) { //迭代该节点后代元素 walkTheDOMRecursive(func, node, depth, returnedFromParent); //移向下一个子节点 node = node.nextSibling; } } window.ADS.walkTheDOMRecursive = walkTheDOMRecursive; /** * 迭代DOM节点的属性 * @param {Node} node * @param {Function} func * @param {Number} depth * @param returnedFromParent */ function walkTheDOMWithAttributes(node, func, depth, returnedFromParent) { var root = node || window.document; returnedFromParent = func(root, depth++, returnedFromParent); if (root.attributes) { for (var i = 0; i < root.attributes.length; i++) { walkTheDOMWithAttributes(root.attributes[i], func, depth - 1, returnedFromParent); } } if (root.nodeType !== window.ADS.node.ATTRIBUTE_NODE) { node = root.firstChild; while (node) { walkTheDOMWithAttributes(node, func, depth, returnedFromParent); node = node.nextSibling; } } } window.ADS.walkTheDOMWithAttributes = walkTheDOMWithAttributes; function walkTheDOM(node, func) { func(node); node = node.firstChild; while (node) { walkTheDOM(node, func); node = node.nextSibling; } } window.ADS.walkTheDOM = walkTheDOM; /** * 把word-word转换为wordWord * @param {String} s * @return {String|XML|void} */ function camelize(s) { return s.replace(/-(\w)/g, function (strMatch, p1) { //strMatch为正则匹配的字符 //p1为\w匹配的一个字符 return p1.toUpperCase(); }); } window.ADS.camelize = camelize; /** * 阻止事件冒泡 * @param {event} eventObject */ function stopPropagation(eventObject) { eventObject = eventObject || getEventObject(eventObject); if (eventObject.stopPropagation) { eventObject.stopPropagation(); } else { eventObject.cancelBubble = true; } } window.ADS.stopPropagation = stopPropagation; /** * 取消默认事件 * @param {event} eventObject * 可被取消的DOM事件: * click, mousedown, mouseup, mouseover, mouseout, submit, DOMActive及键盘事件 */ function preventDefault(eventObject) { eventObject = eventObject || getEventObject(eventObject); if (eventObject.preventDefault) { eventObject.preventDefault(); } else { eventObject.returnValue = false; } } window.ADS.preventDefault = preventDefault; function uncamelize(property, splitChar) { return property.replace(/([a-z])([A-Z])/, '$1' + splitChar + '$2').toLowerCase(); } window.ADS.uncamelize = uncamelize; /** * 用过ID修改单个元素的样式 * @param {Node} element 元素节点 * @param {Object} styles 样式对象 */ function setStyleById(element, styles) { //取得对象的引用 if (!(element = $(element))) { return false; } //循环遍历styles对象并应用每个属性 for (var property in styles) { //检测是否是样式属性 if (!styles.hasOwnProperty(property)) { continue; } try { //DOM2样式规范方法 element.style.setProperty(uncamelize(property, '-'), styles[property], null); } catch (ex) { //备用方法 element.style[camelize(property)] = styles[property]; } } return true; } window.ADS.setStyle = setStyleById; window.ADS.setStyleById = setStyleById; /** * 通过类名修改多个元素的样式 * @param {Node} parent * @param {String} tag * @param {String} className * @param {Object} styles */ function setStylesByClassName(parent, tag, className, styles) { if (!(parent = $(parent))) { return false; } var elements = getElementsByClassName(className, parent, tag); for (var i = 0; i < elements.length; i++) { setStyleById(elements[i], styles); } return true; } window.ADS.setStylesByClassName = setStylesByClassName; /** * 涌过标签名修改多个元素的样式 * @param {String} tagname * @param {Object} styles * @param {Node} parent */ function setStylesByTagName(tagname, styles, parent) { parent = $(parent) || document; var elements = parent.getElementsByTagName(tagname); for (var i = 0, len = elements.length; i < len; i++) { setStyleById(elements[i], styles); } } window.ADS.setStylesByTagName = setStylesByTagName; /** * 取得包含元素className的数组 * @param {Node} element */ function getClassName(element) { if (!(element = $(element))) { return false; } return element.className.replace(/\s+/g, ' ').split(' '); } window.ADS.getClassName = getClassName; /** * 检察院苏中是否存在某个className * @param {Node} element * @param {String} className * @return {Boolean} */ function hasClassName(element, className) { if (!(element = $(element))) { return false; } var classes = getClassName(element); for (var i = 0; i < classes.length; i++) { //检测className是否匹配,如果是则返回true if (classes[i] === className) { return true; } } return false; } window.ADS.hasClassName = hasClassName; /** * 为元素添加className * @param element * @param className * @return {Boolean} */ function addClassName(element, className) { if (!(element = $(element))) { return false; } //将className添加到当前className的末尾 //如果没有className,则不包含空格 element.className += (element.className ? ' ' : '') + className; return true; } window.ADS.addClassName = addClassName; /** * 删除一个或多个类名 * @param element * @param className * @return {Boolean} */ function removeClassName(element, className) { if (!(element = $(element))) { return false; } className = (className || '').trim().replace(/\s+/g, ' '); if (className === '') { //未定义或者空的情况为删除所有类名 element.className = ''; } else if (className.indexOf(' ') !== -1) { //对多个以空格为分隔的类名进行处理,然后迭代 var classarr = className.split(' '); for (var i = 0; i < classarr.length; i++) { removeClassName(element, classarr[i]); } } else { //对单个类名的处理 className = (' ' + className + ' '); var elemClass = ' ' + element.className + ' '; if (elemClass.indexOf(className) !== -1) { element.className = elemClass.replace(className, ' ').replace(/\s+/g, ' ').trim(); } else { return false; } } return true; } window.ADS.removeClassName = removeClassName; /** * 切换类名 * @param element * @param className */ function toggleClassName(element, className) { if (!hasClassName(element, className)) { addClassName(element, className); } else { removeClassName(element, className); } } window.ADS.toggleClassName = toggleClassName; /** * 添加样式表 * @param {String} url url地址 * @param {String} media 媒介类型 */ function addStyleSheet(url, media) { media = media || 'screen'; var link = document.createElement('link'); link.setAttribute('rel', 'stylesheet'); link.setAttribute('type', 'stylesheet'); link.setAttribute('href', url); link.setAttribute('media', media); link.disabled = false; document.getElementsByTagName('head')[0].appendChild(link); } window.ADS.addStyleSheet = addStyleSheet; /** * 禁用样式表 * @param url * @param media */ function removeStyleSheet(url, media) { var styles = getStyleSheets(url, media); for (var i = 0; i < styles.length; i++) { // for standard and IE var node = styles[i].ownerNode || styles[i].owningElement; //禁用样式表 styles[i].disabled = true; //移除节点 node.parentNode.removeChild(node); } } window.ADS.removeStyleSheet = removeStyleSheet; /** * 通过URL取得包含所有样式表的数组 * @param url * @param media * @return {Array} */ function getStyleSheets(url, media) { var sheets = []; for (var i = 0; i < document.styleSheets.length; i++) { if (url && document.styleSheets[i].href.indexOf(url) === -1) { continue; } if (media) { //规范化media字符串 media = media.replace(/,\s*/, ','); var sheetMedia; if (document.styleSheets[i].media.mediaText) { //DOM方法 sheetMedia = document.styleSheets[i].media.mediaText.replace(/,\s*/, ','); //Safari会添加额外的都好和空格 sheetMedia = sheetMedia.replace(/,\s*$/, ''); } else { // MSIE sheetMedia = document.styleSheets[i].media.replace(/,\s*/, ','); } //如果media不匹配则跳过 if (media !== sheetMedia) { continue; } } sheets.push(document.styleSheets[i]); } return sheets; } window.ADS.getStyleSheets = getStyleSheets; /** * 编辑一条样式规则 * @param {String} selector 选择器 * @param {Object} styles 样式对象 * @param {String || Array} url url地址或者styleSheets数组 * @param {String} media 媒介查询 */ function editCSSRule(selector, styles, url, media) { var styleSheets = (typeof url === 'array') ? url : getStyleSheets(url, media); for (var i = 0; i < styleSheets.length; i++) { // 取得规则列表 // DOM2样式规范方法是styleSheets[i].cssRules // MSIE方法是styleSheets[i].rules var rules = styleSheets[i].cssRules || styleSheets[i].rules; if (!rules) { continue; } // 由于MSIE默认使用大写故转换为大写形式 // 如果拟使用的是区分大小写的id,则可能会 // 导致冲突 selector = selector.toUpperCase(); for (var j = 0; j < rules.length; j++) { // 检查是否匹配 if (rules[i].selectorText.toUpperCase() === selector) { for (var property in styles) { if (!styles.hasOwnProperty(property)) { continue; } // 设置新的样式属性 rules[i].style[camelize(property)] = styles[property]; } } } } } window.ADS.editCSSRule = editCSSRule; /** * 添加一条css规则 * @param selector * @param styles * @param {Number} index 列表规则的序号 * @param url * @param media */ function addCSSRule(selector, styles, index, url, media) { var declaration = ''; // 根据styles参数(样式对象)构建声明字符串 for (var property in styles) { if (!styles.hasOwnProperty(property)) { continue; } declaration += property + ':' + styles[property] + ';'; } var styleSheets = (typeof url === 'array') ? url : getStyleSheets(url, media); var newIndex; for (var i = 0; i < styleSheets.length; i++) { // 添加规则 if (styleSheets[i].insertRule) { // DOM2样式规范方法 // index = length 是列表末尾 newIndex = (index >= 0 ? index : styleSheets[i].cssRules.length); styleSheets[i].insertRule( selector + ' { ' + declaration + ' } ', newIndex ); } else if (styleSheets[i].addRule) { // MSIE的方法 // index = -1 是列表末尾 newIndex = (index >= 0 ? index : -1); styleSheets[i].addRule(selector, declaration, newIndex); } } } window.ADS.addCSSRule = addCSSRule; /** * 取得一个元素的计算样式 * @param {Node} element 元素节点 * @param {String} property css属性 * 格式:font-size * @return {*} */ function getStyle(element, property) { if (!(element = $(element)) || !property) { return false; } // 检测元素style属性中的值 // 只能获取嵌入方式定义的样式 var value = element.style[camelize(property)]; if (!value) { // 取得计算的样式值 if (document.defaultView && document.defaultView.getComputedStyle) { // DOM方法 var css = document.defaultView.getComputedStyle(element, null); // 也可以使用css[camelize(property)] value = css ? css.getPropertyValue(property) : null; } else if (element.currentStyle) { // MSIE方法 value = element.currentStyle[camelize(property)]; } } // 返回字符串不包括auto的值 return value === 'auto' ? '' : value; } window.ADS.getStyle = getStyle; window.ADS.getStyleById = getStyle; /** * 检查JSON文本,确保安全 * @param {String} s JSON字符串 * @param {Function} filter 过滤方法 * @return {*} */ function parseJSON(s, filter) { var j; /** * 递归地遍历了新生成的结构 而且将每个名/值对传递给一个过滤函数,以便进行 可能的转换 * @param k * @param v * @return {*} */ function walk(k, v) { if (v && typeof v === 'object') { for (var i in v) { if (v.hasOwnProperty(i)) { v[i] = walk(i, v[i]); } } } return filter(k, v); } /* 解析通过3个阶段进行。第一阶段,通过正则表达式 检测JSON文本,查找非JSON字符串。其中,特别关注 “()”和"new",因为它们会引起语句的调用,还有“=”, 因为它会导致变量的值发生改变。不过,为安全起见 这里会拒绝所有不希望出现的字符串 */ /* 首先这个串分成两部分,看中间的或符号(|) "(\\.|[^\\\n\r])*?"和[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t] 先分解"(\\.|[^\\\n\r])*?" 它匹配一个双引号的字符串,两边引号不说了括号内一个“|”又分成两段 “\\.“匹配一个转义字符 比如js字符串里的\n,\r,\',\"等。[^\\\n\r]匹配一个非\,回车换行的字符 其实它就是js里字符串的规则---不包含回车换行,回车换行用 \n\r表示,\后面跟一个字符表示转义 其次看[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t] 它匹配一个单个字符,这个字符可以是 ,,:,{,},[,],数字,除 "\n" 之外的任何单个字符,-,+,E,a,e,f,l,n,r-u之间的字符,回车,换行,制表符, */ if (/^("(\\.|[^"\\\n\r])*?"|[,:\{\}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.test(s)) { /* 第二阶段,使用eval()函数将JSON文本编译为js 结构。其中的“{”操作符具有语法上的二义性,即它可 以定义一个语句块,也可以表示对象字面量。这里将 JSON文本用括号括起来是为了消除这种二义性 */ try { j = eval('(' + s + ')'); } catch (e) { throw new SyntaxError('parseJSON'); } } else { throw new SyntaxError('parseJSON'); } /* 在可选的第三阶段,代码递归地遍历了新生成的结构 而且将每个名/值对传递给一个过滤函数,以便进行 可能的转换 */ if (typeof filter === 'function') { j = walk('', j); } return j; } /** * 设置XMLHttpRequest对象的各个不同的部分 * @param url * @param options * 参数options的成员属性 * method, 适用于请求的方法,默认为GET。 * send, 是一个包含在XMLHttpRequest.send()中的可选字符串,默认为null。 * loadListener, 是当readyState为1时调用的onreadystatechange侦听器 * loadedListener, 是当readyState为2时调用的onreadystatechange侦听器。 * interactiveListener, 是当readyState为3时调用的onreadystatechange侦听器。 * jsResponseListener, 是当请求成功并且响应的Content-Type为application/javascript或text/javascript时调用的侦听器,这个侦听器将从响应中取得js字符串作为其第一个参数。如果要执行该js字符串,必须使用eval()方法。 * jsonResponseListener, 是当请求成功并且响应的Content-Type为application/json时调用的侦听器。这个侦听器将从响应中取得JSON对象作为其第一个参数。 * xmlResponseListener, 是当请求成功并且响应的Content-Type为application/xml或application/xhtml+xml时调用的侦听器。这个侦听器将从响应中取得的XML DOM文档作为其第一个参数。 * htmlResponseListener, 是当请求成功并且响应的content-Type为text/html时调用的侦听器。这个侦听器将从响应中取得的HTML字符串作为其第一个参数。 * completeListener, 是当上面所列的针对Content-Type的响应侦听器调用之后被调用的侦听器。这个方法总是在成功的响应最后被调用,也就是说如果相应中没有适当的Content-Type头部信息,那么你可以制定这个方法作为兜底儿的侦听器。 * errorListener, 是当响应状态值不是200也不是0时被调用的侦听器。如果是在不会提供适当响应代码的系统上运行(如硬盘驱动器中的本地文件系统)XMLHttpRequest,那么状态值将始终为0.在这种情况下,只有completeListener会被调用。 */ /* demo: ADS.ajaxRequest('/path/to/script/',{ method:'GET', completeListener:function(){ alert(this.responseText); } }); */ // 因为使用了call和apply方法,此时的this引用的是 // 请求对象而不是onreadystatechange方法。 function getRequestObject(url, options) { // 初始化请求对象 var req = false; if (window.XMLHttpRequest) { req = getRequestObject.req = new window.XMLHttpRequest(); } else if (window.ActiveXObject) { req = getRequestObject.req = new ActiveXObject('Microsoft.XMLHTTP'); } if (!req) { return false; } // 定义默认的选项 options = options || {}; options.method = options.method || 'GET'; options.send = options.send || null; // 为请求的每个阶段定义不同的侦听器 req.onreadystatechange = function () { switch (req.readyState) { case 1: // 载入中 if (options.loadListener) { options.loadListener.apply(req, arguments); } break; case 2: // 载入完成 if (options.loadedListener) { options.loadedListener.apply(req, arguments); } break; case 3: // 交互 if (options.interactiveListener) { options.interactiveListener.apply(req, arguments); } break; case 4: // 完成 // 如果失败则抛出错误 try { if (req.status && (req.status >= 200 && req.status < 300) || req.status === 304) { // 针对Content-type的特殊侦听器 // 由于Content-Type头部中可能包含字符集,如: // Content-Type: text/html; charset=ISO-8859-4 // 因此通过正则表达式提取出所需的部分 var contentType = req.getResponseHeader('Content-Type'); var mimeType = contentType.match(/\s*([^;]+)\s*(;|$)/i)[1]; switch (mimeType) { case 'text/javascript': case 'application/javascript': // 响应时javascript,因此以 // req.responseText作为回调函数 if (options.jsResponseListener) { options.jsResponseListener.call(req, req.responseText); } break; case 'application/json': // 响应是JSON,因此需要用匿名函数对 // req.responseText进行解析 // 已返回作为回调参数的JSON对象 if (options.jsonResponseListener) { var json; try { json = parseJSON(req.responseText); } catch (e) { json = false; } options.jsonResponseListener.call(req, json); } break; case 'text/xml': case 'application/xml': case 'application/xhtml+xml': // 响应是XML,因此以 // req.responseXML作为 // 回调的参数 // 此时是Document对象 if (options.xmlResponseListener) { options.xmlResponseListener.call(req, req.responseText); } break; case 'text/html': // 响应是HTML,因此以 // req.responseText作为 // 回调的参数 if (options.htmlResponseListener) { options.htmlResponseListener.call(req, req.responseText); } break; default: break; } // 针对响应成功完成的侦听器 if (options.completeListener) { options.completeListener.apply(req, arguments); } } else { // 相应完成但却存在错误 if (options.errorListener) { options.errorListener.apply(req, arguments); } } } catch (e) { // 忽略错误 } break; } }; // 开启请求 req.open(options.method, url, true); // 添加特殊的头部信息以标识请求 req.setRequestHeader('X-ADS-Ajax-Request', 'AjaxRequest'); return req; } window.ADS.getRequestObject = getRequestObject; // 通过简单的包装getRequestObject()和send() // 方法发送XMLHttpRequest对象的请求 function ajaxRequest(url, options) { var req = getRequestObject(url, options); req.send(options.send); return req; } window.ADS.ajaxRequest = ajaxRequest; /** * 此处的XssHttpRequest对象在模拟了某些XMLHttpRequest对象属性的基础上,还添加了以下一些属性 * responseJSON。 包含相应的结果,这个属性已经是一个js对象了,因此不需要再使用eval(); * status. 在这里可能的值有如下两个 * 1: 表示成功 * 2: 表示存在错误 * statusText。 包含错误的原因。 * 可以通过在下列侦听器内使用this关键字来访问以上属性 * loadedListener。 当对象处于载入完成状态时调用。 * waitListener。 当对象等待响应时调用。 * completeListener。 当对象取得成功的响应后调用。 * errorListener。 当脚本载入外部脚本失败或载入的脚本格式不正确时调用。 * * 为了保证对象正确运行,相应必须要调用GET变量中所引用的js函数,一般而言,最好的方式就是想这个函数中传递一个JSON对象,但如果你想传递XML文件或其他信息,也可以简单的将这些信息包含在JSON对象中,然后用过响应侦听器来按照需要解析这些信息。 */ // XssHttpRequest对象的计数器 var XssHttpRequestCount = 0; // XssHttpRequest对象的一个 // 跨站点<script>标签的实现 var XssHttpRequest = function () { this.requestID = 'XSS_HTTP_REQUEST_' + (++XssHttpRequestCount); }; XssHttpRequest.prototype = { url: null, scriptObject: null, responseJSON: null, status: 0, readyState: 0, timeout: 30000, onreadystatechange: function () { }, setReadyState: function (newReadyState) { // 如果比当前状态更新 // 则只更新就绪状态 if (this.readyState < newReadyState || newReadyState === 0) { this.readyState = newReadyState; this.onreadystatechange(); } }, open: function (url, timeout) { this.timeout = timeout || this.timeout; // 将以革名为XSS_HTTP_REQUEST_CALLBACK // 的特殊变量附加给URL,其中包含本次请求的 // 回调函数的名称 this.url = url + ((url.indexOf('?') !== -1) ? '&' : '?') + 'XSS_HTTP_REQUEST_CALLBACK=' + this.requestID + '_CALLBACK'; this.setReadyState(0); }, send: function () { var requestObject = this; // 创建一个载入外部数据的新script对象 this.scriptObject = document.createElement('script'); this.scriptObject.setAttribute('id', this.requestID); this.scriptObject.setAttribute('type', 'text/javascript'); // 汕尾设置src属性,也先不降其添加到文档 // 创建一个在给定的毫秒数之后触发的 // setTimeout()方法。如果在给定的时间 // 内脚本没有载入完成,则取消载入 var timeoutWatcher = setTimeout(function () { // 在脚本晚于我们假定的停止时间之后载入的情况下 // 通过一个空方法来重新为window方法赋值 window[requestObject.requestID + '_CALLBACK'] = function () { }; // 移除脚本以防止进一步载入 requestObject.scriptObject.parentNode.removeChild(requestObject.scriptObject); // 将状态设置为错误 requestObject.status = 2; requestObject.statusText = 'Timeout after: ' + requestObject.timeout + ' milliseconds.'; // 更新就绪状态 requestObject.setReadyState(2); requestObject.setReadyState(3); requestObject.setReadyState(4); }, this.timeout); // 在window对象中创建一个与 // 请求中的回调方法匹配的方法 // 在调用时负责处理请求的其他部分 window[this.requestID + '_CALLBACK'] = function (JSON) { // 当脚本载入时将执行这个方法 // 同时传入预期的JSON对象 // 在请求载入成功时 // 清除timeoutWatcher方法 clearTimeout(timeoutWatcher); // 更新就绪状态 requestObject.setReadyState(2); requestObject.setReadyState(3); // 将状态设置为成功 requestObject.responseJSON = JSON; requestObject.status = 1; requestObject.statusText = 'Loaded.'; // 更新就绪状态 requestObject.setReadyState(4); }; // 设置初始就绪状态 this.setReadyState(1); // 现在再设置src属性并将其添加 // 到文档头部。这样会载入脚本 this.scriptObject.setAttribute('src', this.url); var head = document.getElementsByTagName('head')[0]; head.appendChild(this.scriptObject); } }; window.ADS.XssHttpRequest = XssHttpRequest; // 设置XssHttpRequest对象的不同部分 function getXssRequestObject(url, options) { var req = new XssHttpRequest(); options = options || {}; // 默认中断时间为30秒 options.timeout = options.timeout || 30000; req.onreadystatechange = function () { switch (req.readyState) { // 载入中 case 1: if (options.loadListener) { options.loadListener.apply(req, arguments); } break; case 2: // 载入完成 if (options.loadedListener) { options.loadedListener.apply(req, arguments); } break; case 3: // 交互 if (options.interactiveListener) { options.interactiveListener.apply(req, arguments); } break; case 4: // 完成 if (req.status === 1) { if (options.completeListener) { options.completeListener.apply(req, arguments); } } else { if (options.errorListener) { options.errorListener.apply(req, arguments); } } break; default: break; } }; req.open(url, options.timeout); return req; } window.ADS.getXssRequestObject = getXssRequestObject; // 发送XssHttpRequest请求 function xssRequest(url, options) { var req = getXssRequestObject(url, options); req.send(null); return req; } window.ADS.xssRequest = xssRequest; //跟踪hash地址变化 /* 声称回调函数的一个辅助方法 */ function makeCallback(method, target) { return function () { method.apply(target, arguments); }; } /* 一个用来基于hash触发注册的方法的URL hash侦听器 */ var actionPager = { // 前一个hash lastHash: '', // 为hash模式注册的方法列表 callbacks: [], // Safari历史记录列表 safariHistory: false, // 对为IE准备的iframe的引用 msieHistory: false, // 应该被转换的链接的类名 ajaxifyClassName: '', // 应用程序的根目录。当创建hash时 // 它将是被清理后的URL ajaxifyRoot: '', /** * 初始化页面(pager)功能 * @param {String} ajaxifyClass 预定义的类名,只有符合该类名的链接才具备功能,默认为 ADSActionLink * @param {String} ajaxifyRoot 预定义的根路径 * @param {String} startingHash 预定义的hash值,默认为start * @example actionPager.init(); */ init: function (ajaxifyClass, ajaxifyRoot, startingHash) { this.ajaxifyClassName = ajaxifyClass || 'ADSActionLink'; this.ajaxifyRoot = ajaxifyRoot || ''; var ua = navigator.userAgent; if (/Safari/i.test(ua) && /Version\/3/.test(ua)) { this.safariHistory = []; } else if (/MSIE/i.test(ua) && (parseInt(ua.split('MSIE')[1],10) < 8)) { // 如果是MSIE 6/7,添加一个iframe以便 // 跟踪重写(override)后退按钮 this.msieHistory = document.createElement('iframe'); this.msieHistory.setAttribute('id', 'msieHistory'); this.msieHistory.setAttribute('name', 'msieHistory'); this.msieHistory.setAttribute('src', 'fakepage'); setStyleById(this.msieHistory, { 'width': '100px', 'height': '100px', 'border': '1px solid black', 'display': 'none', 'z-index': -1 }); document.body.appendChild(this.msieHistory); this.msieHistory = frames.msieHistory; } // 将链接转换为Ajax链接 this.ajaxifyLinks(); // 取得当前的地址 var locat = this.getLocation(); // 检测地址中是否包含hash(来自书签) // 或者是否已经提供了hash if (!locat.hash && !startingHash) { startingHash = 'start'; } // 按照需要保存hash var ajaxHash = this.getHashFromURL(window.location.href) || startingHash; this.addBackButtonHash(ajaxHash); // 添加监视事件以观察地址栏中的变化 var watcherCallback = makeCallback(this.watchLocationForChange, this); if (/MSIE/i.test(ua) && (parseInt(ua.split('MSIE')[1],10) < 8)) { window.setInterval(watcherCallback, 200); } else { addEvent(window, 'hashchange', watcherCallback); } }, /** * 会自动将任何带标识的锚标签转换为页面(pager)可以识别的链接 */ ajaxifyLinks: function () { // 将链接转换为锚以便Ajax进行处理 var links = getElementsByClassName(this.ajaxifyClassName, 'a'); for (var i = 0, len = links.length; i < len; i++) { var curLink = links[i]; if (hasClassName(curLink, 'ADSActionPagerModified')) { continue; } // 将href属性转换为#value形式 curLink.setAttribute('href', this.convertURLToHash(curLink.getAttribute('href'))); addClassName(curLink, 'ADSActionPagerModified'); // 注册单击事件以便在必要时添加历史纪录 addEvent(curLink, 'click', function () { if (this.href && this.href.indexOf('#') > -1) { actionPager.addBackButtonHash(actionPager.getHashFromURL(this.href)); } }); } }, /** * 记录后退行为 * @param {String} ajaxHash hash值 * @return {Boolean} */ addBackButtonHash: function (ajaxHash) { // 保存hash if (!ajaxHash) { return false; } if (this.safariHistory) { // 为Safari使用特殊数组 if (this.safariHistory.length === 0) { this.safariHistory[window.history.length] = ajaxHash; } else { this.safariHistory[window.history.length + 1] = ajaxHash; } return true; } else if (this.msieHistory) { // 在MSIE中通过导航iframe this.msieHistory.document.execCommand('Stop'); this.msieHistory.location.href = '/fakepage?hash=' + ajaxHash + '&title=' + document.title; return true; } else { // 通过改变地址的值 // 使用makeCallback包装函数 // 以便在超时方法内部使this // 引用actionPager var timeoutCallback = makeCallback(function () { if (this.getHashFromURL(window.location.href) !== ajaxHash) { location.replace(location.href + '#' + ajaxHash); } }, this); setTimeout(timeoutCallback, 200); return true; } return false; }, /** * 间隔地检测hash的变化,还可执行注册的侦听器 */ watchLocationForChange: function () { var newHash; // 取得新的hash值 if (this.safariHistory) { // 在Safari中从历史记录数组中取得 if (this.safariHistory[history.length]) { newHash = this.safariHistory[history.length]; } } else if (this.msieHistory) { // 在MSIE中从iframe的地址中取得 newHash = this.msieHistory.location.href.split('&')[0].split('=')[1]; } else if (location.hash !== '') { // 对其他浏览器从window.location中取得 newHash = this.getHashFromURL(window.location.href); } // 如果新hash与最后一次的hash不相同,则更新页面 if (newHash && this.lastHash !== newHash) { if (this.msieHistory && this.getHashFromURL(window.location.href) !== newHash) { // 修复MSIE中的地址栏 // 以便能适当地加上标签(或加入收藏夹) location.hash = newHash; } // 在发生异常的情况下使用try/catch // 结构尝试执行任何注册的侦听器 try { if (this.callbacks.length) { this.executeListeners(newHash); } // 在通过处理程序添加任何 // 新链接的情况下进行更新 this.ajaxifyLinks(); } catch (ex) { // 这里将捕获到回调函数中的任何异常JS alert(ex); } // 将其保存为最后一个hash this.lastHash = newHash; } }, /** * 用于根据特殊的hash来注册侦听器 * @param {RegExp || String} regex 正则表达式 * @param {Function} method 执行的方法 * @param {Object || Element} context 执行环境,上下文 */ register: function (regex, method, context) { var obj = {'regex': regex}; context = context || window; // 一个已经指定的环境 obj.callback = function (matches) { method.apply(context, matches); }; // 将侦听器添加到回调函数数则中 this.callbacks.push(obj); }, /** * 把链接的URL地址转换为hash值 * @param {String} url * @return {*} */ convertURLToHash: function (url) { if (!url) { // 没有url,因而返回一个'#' return '#'; } else if (url.indexOf('#') > -1) { // 存在hash,因而返回它 return '#' + url.split('#')[1]; } else { // ie67会自动添加域名 // 如果URL中包含域名(MSIE)则去掉它 if (url.indexOf('://') > -1) { url = url.match(/:\/\/[^\/]+(.*)/)[1]; } // 按照init()约定去掉根目录 return '#' + url.substring(this.ajaxifyRoot.length); } }, /** * 从指定的URL中提取出hash值 * @param url * @return {*} */ getHashFromURL: function (url) { if (!url || url.indexOf('#') === -1) { return ''; } return url.split('#')[1]; }, /** * 获取当前URL地址 * @return {*} */ getLocation: function () { // 检查hash if (!window.location.hash) { // 没有则生成一个 var url = { domain: null, hash: null }; if (window.location.href.indexOf('#') > -1) { var parts = window.location.href.split('#')[1]; url.domain = parts[0]; url.hash = parts[1]; } else { url.domain = window.location; } return url; } return window.location; }, /** * 执行侦听器 * @param hash */ executeListeners: function (hash) { var matches; // 执行与hash匹配的任何侦听器 for (var i = 0, len = this.callbacks.length; i < len; i++) { if ((matches = hash.match(this.callbacks[i].regex))) { this.callbacks[i].callback(matches); } } } }; window.ADS.actionPager = actionPager; /* 一个复制对象的辅助方法 */ function clone(myObj) { if (typeof myObj !== 'object') { return myObj; } if (myObj === null) { return myObj; } var myNewObj = {}; for (var i in myObj) { myNewObj[i] = clone(myObj[i]); } return myNewObj; } /* 用于保存队列的数组 */ var requestQueue = []; /** * 为ADS.ajaxRequest方法启用排队功能的包装对象 * @param url * @param options * @param queue * @example * ADS.ajaxRequestQueue('/your/script/', { * completeListener: function(){ * alert(this.responseText); * } * }, 'Queue1'); */ function ajaxRequestQueue(url, options, queue) { queue = queue || 'default'; // 这个对象将把可选的侦听器包装在另一个函数中 // 因此,可选的对象必须唯一。否则,如果该方法 // 被调用时使用的是共享的可选对象,那么会导致 // 陷入递归中 options = clone(options) || {}; if (!requestQueue[queue]) { requestQueue[queue] = []; } // 当前一次请求完成时,需要使用completeListener // 调用队列中的下一次请求。如果完成侦听器已经 // 有定义,那么需要首先调用它 // 取得旧侦听器 var userCompleteListener = options.completeListener; // 添加新侦听器 options.completeListener = function () { // 如果存在旧的侦听器则首先调用它 if (userCompleteListener) { // this引用的是情求对象 userCompleteListener.apply(this, arguments); } // 从队列中移除这个请求 requestQueue[queue].shift(); // 调用队列中的下一项 if (requestQueue[queue][0]) { // 请求保存在req属性中,但为防止它是 // 一个POST请求,故也需包含send选项 var q = requestQueue[queue][0].req.send( requestQueue[queue][0].send ); } }; // 如果发生了错误,应该通过调用相应的 // 错误处理方法取消队列中的其他请求 // 取得旧侦听器 var userErrorListener = options.errorListener; // 添加新侦听器 options.errorListener = function () { if (userErrorListener) { userErrorListener.apply(this, arguments); } // 由于已经调用了错误侦听器 // 股从队列中移除这个请求 requestQueue[queue].shift(); // 由于出错需要取消队列中的其余请求,但首先要调用 // 每个请求的errorListener。通过调用队列中 // 下一项的错误侦听器就会才清楚所有排队的请求,因为在 // 链中的调研那个是一次发生的 // 检测队列中是否还存在请求 if (requestQueue[queue].length) { // 取得下一项 var q = requestQueue[queue].shift(); // 中断请求 q.req.abort(); // 伪造请求对象,以便errorListener // 认为请求已经完成并相应地运行 var fakeRequest = {}; // 将status设置为0,将readyState设置为4 // 就好像请求虽然完成但却失败了一样 fakeRequest.status = 0; fakeRequest.readyState = 4; fakeRequest.responseText = null; fakeRequest.responseXML = null; // 设置错误信息,以便需要时显示 fakeRequest.statusText = 'A request in the queue received an error'; // 调用状态改变,如果readyState是4,而 // status不是200,则会调用errorListener q.error.apply(fakeRequest); } }; // 将这个请求添加到队列中 requestQueue[queue].push({ req: getRequestObject(url, options), send: options.send, error: options.errorListener }); // 如果队列的长度表明只有一个 // 项(即第一个)则调用请求 if (requestQueue[queue].length === 1) { ajaxRequest(url, options); } } window.ADS.ajaxRequestQueue = ajaxRequestQueue; //客户端检测 var client = (function(){ //呈现引擎 var engine = { ie: 0, gecko: 0, webkit: 0, khtml: 0, opera: 0, ver: null //具体版本号 }; //浏览器 var browser = { //主要浏览器 ie: 0, firefox: 0, kong: 0, opera: 0, chrome: 0, safari: 0, ver: null //具体的版本 }; //平台,设备和操作系统 var system = { win: false, mac: false, x11: false, //移动设备 iphone: false, ipod: false, nokiaN: false, winMobile: false, macMobile: false, //游戏系统 wii: false, ps: false }; //在此检测呈现引擎,平台和设备 var ua = navigator.userAgent; if (window.opera) { //第一步检测opera,因为它的用户代理字符串有可能完全模仿其他浏览器 //要识别Opera,必须的检测window.opera对象 engine.ver = browser.ver = window.opera.version(); engine.opera = browser.opera = parseFloat(engine.ver); } else if (/AppleWebkit\/(\S+)/.test(ua)) { //第二个要检测的是Webkit,因为Webkit的永华代理字符串中包含“Gecko”和“KHTML” //Webkit的用户代理字符串中的“AppleWebkit”是独一无二的 engine.ver = RegExp.$1; engine.webkit = parseFloat(engine.ver); //确定是Chrome还是Safari if (/Chrome\/(\S+)/.test(ua)) { browser.ver = RegExp.$1; browser.chrome = parseFloat(browser.ver); } else if (/Version\/(\S+)/.test(ua)) { browser.ver = RegExp.$1; browser.safari = parseFloat(browser.ver); } else { //近似地确定版本号 var safariVersion = 1; if (engine.webkit < 100) { safariVersion = 1; } else if (engine.webkit < 312) { safariVersion = 1.2; } else if (engine.webkit < 412) { safariVersion = 1.3; } else { safariVersion = 2; } browser.safari = browser.ver = safariVersion; } } else if (/KHTML\/(\S+)/.test(ua) || /Konqueror\/([^;]+)/.test(ua)) { //接下来测试KHTML,KHTML的用户代理字符串也包含“Gecko” //使用Konqueror检测Konqueror3.1及更早版本 engine.ver = browser.ver = RegExp.$1; engine.khtml = browser.kong = parseFloat(engine.ver); } else if (/rv:([^\)]+)\) Gecko\/\d{8}/.test(ua)) { //检测Gecko,Gecko版本号会出现在字符串"rv:"的后面 engine.ver = RegExp.$1; engine.gecko = parseFloat(engine.ver); //确定是不是Firefox if (/Firefox\/(\S+)/.test(ua)) { browser.ver = RegExp.$1; browser.firefox = parseFloat(browser.ver); } } else if (/MSIE ([^;]+)/.test(ua)) { //检测IE,IE版本号位于字符串“MSIE”之后,一个分号的前面 engine.ver = browser.ver = RegExp.$1; engine.ie = browser.ie = parseFloat(engine.ver); } //检测浏览器 browser.ie = engine.ie; browser.opera = engine.opera; //识别平台 var p = navigator.platform; system.win = p.indexOf("Win") > -1; system.mac = p.indexOf("Mac") > -1; system.x11 = (p.indexOf("x11") > -1) || (p.indexOf("Linux") > -1); //识别windows操作系统 if (system.win) { if (/Win(?:dows )?([^do]{2})\s?(\d+\.\d+)?/.test(ua)) { if (RegExp.$1 === "NT") { switch (RegExp.$2) { case "5.0": system.win = "2000"; break; case "5.1": system.win = "XP"; break; case "6.0": system.win = "Vista"; break; case "6.1": system.win = "WIN7"; break; default: system.win = "NT"; break; } } else if (RegExp.$1 === "9x") { system.win = "ME"; } else { system.win = RegExp.$1; } } } system.iphone = ua.indexOf('iPhone') > -1; system.ipod = ua.indexOf('iPod') > -1; system.ipad = ua.indexOf('iPad') > -1; system.nokiaN = ua.indexOf('NokiaN') > -1; // windows mobile if (system.win === 'CE') { system.winMobile = system.win; } else if (system.win === 'Ph') { if (/Windows Phone OS (\d+\.\d+)/.test(ua)) { system.win = 'Phone'; system.winMobile = parseFloat(RegExp.$1); } } // 检测IOS版本 if (system.mac && ua.indexOf('Mobile') > -1) { if (/CPU (?:iPhone )?OS (\d+_\d+)/.test(ua)) { system.ios = parseFloat(RegExp.$1.replace('_', '.')); } else { // 不能真正检测出来,所以只能猜测 system.ios = 2; } } // 检测android版本 if (/Android (\d+\.\d+)/.test(ua)) { system.android = parseFloat(RegExp.$1); } // 游戏系统 system.wil = ua.indexOf('Wii') > -1; system.ps = /playstation/i.test(ua); // 返回这些对象 return { engine: engine, browser: browser, system: system }; })(); window.ADS.client = client; //对非DOM元素实现自定义事件 function EventTarget() { //存储事件处理程序的属性对象 this.handlers = {}; } EventTarget.prototype = { //重新将constructor指向EventTarget构造函数 constructor: EventTarget, /** * 注册给定类型的事件处理程序 * @param type 自定义的事件类型 * @param handler 处理该事件类型的函数 */ addHandler: function (type, handler) { //如果handlers属性中没有存在一个针对该事件类型的数组 //则创建一个新的。(一个事件类型可以对应多个事件处理函数,因此要用数组保存) //然后使用push()将该处理程序添加到数组的末尾 if (typeof this.handlers[type] === "undefined") { this.handlers[type] = []; } this.handlers[type].push(handler); }, /** * 触发事件 * @param event 一个至少包含type属性的对象 */ fire: function (event) { //给event对象设置一个target属性 if (!event.target) { event.target = this; } //如果该事件类型的执行函数存在, //调用各个函数,并给出event对象 if (this.handlers[event.type] instanceof Array) { var handlers = this.handlers[event.type]; for (var i = 0, len = handlers.length; i < len; i++) { handlers[i](event); } } }, /** * 注销事件类型的事件处理程序 * @param type 事件类型 * @param handler 执行的函数 */ removeHandler: function (type, handler) { if (this.handlers[type] instanceof Array) { var handlers = this.handlers[type]; //搜索事件处理程序的数组找到要删除的处理程序的位置 //找到了就退出循环,然后将该项目丛数组中删除 for (var i = 0, len = handlers.length; i < len; i++) { if (handlers[i] === handler) { break; } } handlers.splice(i, 1); } } }; window.ADS.EventTarget = EventTarget; /** * 封装cookie的操作 * @type {Object} */ var CookieUtil = { /** * 根据cookie的名字获取相应的值 * @param name cookie名字 * @return {*} */ get: function (name) { //对name进行URL编码 var cookieName = encodeURIComponent(name) + '=', cookieStart = document.cookie.indexOf(cookieName), cookieValue = null; //找到cookieName if (cookieStart > -1) { //以cookieStart为起点找到最近的";" var cookieEnd = document.cookie.indexOf(';', cookieStart); //没找到";",则是document.cookie的最后一个值 if (cookieEnd === -1) { cookieEnd = document.cookie.length; } //提取相应value字段 cookieValue = decodeURIComponent(document.cookie.substring(cookieStart + cookieName.length, cookieEnd)); } //返回 return cookieValue; }, /** * 设置一个cookie * @param name cookie名字 * @param value 相应的值 * @param expire 生存周期 Date * @param path 路径 * @param domain 域名 * @param secure Boolean */ set: function (name, value, expire, path, domain, secure) { //必须先进行URL编码 var cookieText = encodeURIComponent(name) + '=' + encodeURIComponent(value); if (expire instanceof Date) { //toGMTString()方法正确格式化Date对象 cookieText += '; expire=' + expire.toGMTString(); } if (path) { cookieText += '; path=' + path; } if (domain) { cookieText += '; domain=' + domain; } if (secure) { cookieText += '; secure'; } document.cookie = cookieText; }, /** * 删除cookie * @param name * @param path * @param domain * @param secure */ unset: function (name, path, domain, secure) { this.set(name, '', new Date(0), path, domain, secure); } }; window.ADS.CookieUtil = CookieUtil; //子cookie的操作 //为了绕开浏览器的单域名下的cookie数限制 //子cookie是存放在单个cookie中的更小段的数据 var SubCookieUtil = { /** * 获取单个子cookie的值 * @param name cookie名称 * @param subName 子cookie名称 * @return {*} */ get: function (name, subName) { var subCookies = this.getAll(name); if (subCookies) { return subCookies(subName); } else { return null; } }, /** * 获取所有子cookie并将它们放入一个对象中返回 * @param name cookie名称 * @return {*} 返回对象或null */ getAll: function (name) { var cookieName = encodeURIComponent(name) + "=", cookieStart = document.cookie.indexOf(cookieName), cookieValue = null, result = {}; if (cookieStart > -1) { var cookieEnd = document.cookie.indexOf(";", cookieName); if (cookieEnd === -1) { cookieEnd = document.cookie.length; } //没有进行解码,因为要对子cookie分离操作 cookieValue = document.cookie.substring(cookieStart + cookieName.length, cookieEnd); // if (cookieValue.length > 0) { //分离出子cookie的对象 var subCookies = cookieValue.split("&"); //遍历分隔出子cookie的名称和值,解码后返回对象 for (var i = 0, len = subCookies.length; i < len; i++) { var parts = subCookies[i].split("="); result[decodeURIComponent(parts[0])] = decodeURIComponent(parts[1]); } return result; } } //没找到,返回空 return null; }, /** * 存储单个子cookie * @param name cookie名称 * @param subName 子cookie名称 * @param value 子cookie值 * @param expires 失效日期 * @param path 路径 * @param domain 域名 * @param secure Boolean安全作用域 */ set: function (name, subName, value, expires, path, domain, secure) { //获取name名称的所有子cookie,没有则创建空对象 var subcookies = this.getAll(name) || {}; //给对象添加属性和值 subcookies[subName] = value; //调用setAll()方法 this.setAll(name, subcookies, expires, path, domain, secure); }, /** * 存储所有子cookie * @param name cookie名称 * @param subcookies 子cookie名称 * @param expires 失效日期 * @param path 路径 * @param domain 域名 * @param secure Boolean安全作用域 */ setAll: function (name, subcookies, expires, path, domain, secure) { var cookieText = encodeURIComponent(name) + "="; //创建数组,用于保存子cookie var subcookieParts = []; //遍历对象属性 for (var subName in subcookies) { //如果存在,则把编码后的名称和值保存到数组 if (subName.length > 0 && subcookies.hasOwnProperty(subName)) { subcookieParts.push(encodeURIComponent(subName) + "=" + encodeURIComponent(subcookies[subName])); } } //存在子cookie if (subcookieParts.length > 0) { //连接子cookie cookieText += subcookieParts.join("& "); if (expires instanceof Date) { cookieText += ";expires=" + expires.toGMTString(); } if (path) { cookieText += ";path=" + path; } if (domain) { cookieText += ";domain=" + domain; } if (secure) { cookieText += ";secure"; } } else { //相当于删除cookie操作 cookieText += ";expires=" + (new Date(0)).toGMTString(); } document.cookie = cookieText; }, /** * 删除单个子cookie的名称和值 * @param name * @param subName * @param path * @param domain * @param secure */ unset: function (name, subName, path, domain, secure) { var subcookies = this.getAll(name); if (subcookies) { //删除对应的属性和值 delete subcookies[subName]; //重新设置cookie this.setAll(name, subcookies, null, path, domain, secure); } }, /** * 删除所有子cookie * @param name * @param path * @param domain * @param secure */ unsetAll: function (name, path, domain, secure) { this.setAll(name, null, new Date(0), path, domain, secure); } }; window.ADS.SubCookieUtil = SubCookieUtil; })(); //重复一个字符串 if (!String.repeat) { String.prototype.repeat = function (times) { return new Array(times + 1).join(this); }; } //清除结尾和开头处的空白符 if (!String.trim) { String.prototype.trim = function () { return this.replace(/^\s+|\s+$/g, ''); }; } // 扩展链式调用方法 Function.prototype.method = function (name, fn) { this.prototype[name] = fn; return this; }; // 扩展数组方法 if (!Array.prototype.forEach) { Array.method('forEach', function (fn, thisObj) { var scope = thisObj || window; for (var i = 0, len = this.length; i < len; i++) { fn.call(scope, this[i], i, this); } }); } if (!Array.prototype.filter) { Array.method('filter', function (fn, thisObj) { var scope = thisObj || window; var a = []; for (var i = 0, len = this.length; i < len; i++) { if (!fn.call(scope, this[i], i, this)) { continue; } a.push(this[i]); } return a; }); } /* Object-oriented Helper functions. */ function clone(object) { function F() { } F.prototype = object; return new F(); } function extend(subClass, superClass) { var F = function () { }; F.prototype = superClass.prototype; subClass.prototype = new F(); subClass.prototype.constructor = subClass; subClass.superclass = superClass.prototype; if (superClass.prototype.constructor === Object.prototype.constructor) { superClass.prototype.constructor = superClass; } } function augment(receivingClass, givingClass) { if (arguments[2]) { // Only give certain methods. for (var i = 2, len = arguments.length; i < len; i++) { receivingClass.prototype[arguments[i]] = givingClass.prototype[arguments[i]]; } } else { // Give all methods. for (var methodName in givingClass.prototype) { if (!receivingClass.prototype[methodName]) { receivingClass.prototype[methodName] = givingClass.prototype[methodName]; } } } }
本文参考链接:https://www.cnblogs.com/webFrontDev/archive/2012/12/16/2820399.html