/**
* 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];
}
}
}
}