/**
* 注册命名空间
*/
baidu.namespace.register("baidu.html");
/**
* html相关处理
* @author zhaoxianlie
*/
baidu.html = (function(){
/**
* 页面源代码
*/
var _pageSource = '';
/**
* 结果集
*/
var _summaryInformation = null;
/**
* 初始化侦测结果
*/
var _initSummaryInformation = function(){
_summaryInformation = {
HTMLBase: {
HTMLDeprecatedAttribute: {}, //过期的属性
HTMLDeprecatedTag: {} //过期的标签
},
documentMode: {
hasDocType: false, //是否设置了doctype
compatMode: {
IE: 'Q', //IE中的compatMode
WebKit: 'Q' //Webkit中的compatMode
},
publicId: '', //doctype中的publicId
hasComment: false, //doctype前是否有注释
hasConditionalComment: false, //doctype前是否有条件注释
isUnusualDocType: false //是否为怪异的doctype
},
DOM: {
IECondComm: [], //所有的IE条件注释
FFNotSptComm: [], //Firefox中不支持的注释:不能出现‘--’
allComm:[], //所有的注释
count : 0, //所有节点的数量
invalidInput:{
count:0, //不合法的input数量
input:[] //不合法的input集合
},
maxDepth : {
xpath : '', //xpath
depth : 1 //深度
}
},
title: [], //HTML的title检测
LINK: {
notInHead: [] //不在Head标签内部的Link标签
},
ID: {
ids: {}, //重复的ID
count : 0 //出现重复ID的个数
},
tagInclude : [], //标签的包含关系
unClosedTags : [], //未闭合的标签
htmlMinified : true , //HTML是否压缩过
imgTag : [] //Img标签的检测,src为空否
};
};
/**
* 检测某个标签是否为过时的标签
* @param {Object} tagName
*/
var _isHTMLDeprecatedTag = function(tagName) {
return HTML_DEPRECATED_TAGS[tagName.toLowerCase()];
};
/**
* 判断某个属性是否已过时
* @param {Object} tagName 待检测的标签
* @param {Object} attrName 待检测的属性
*/
var _isHTMLDeprecatedAttribute = function(tagName, attrName){
tagName = tagName.toLowerCase();
attrName = attrName.toLowerCase();
return (HTML_DEPRECATED_ATTRIBUTES[attrName] && HTML_DEPRECATED_ATTRIBUTES[attrName][tagName]);
};
/**
* 将检测到的过时标签记录到结果集中
* @param {Object} element
*/
var _detectDeprecatedTag = function(element){
var tagName = element.tagName.toLowerCase();
if (_isHTMLDeprecatedTag(tagName)) {
var HTMLDeprecatedTag = _summaryInformation.HTMLBase.HTMLDeprecatedTag;
if (!HTMLDeprecatedTag[tagName]) {
HTMLDeprecatedTag[tagName] = 0;
}
HTMLDeprecatedTag[tagName]++;
}
};
/**
* 将检测到的过时属性记录到结果集中
* @param {Object} element
*/
var _detectDeprecatedAttribute = function(element){
var tagName = element.tagName.toLowerCase();
var attributes = element.attributes;
var HTMLDeprecatedAttribute = _summaryInformation.HTMLBase.HTMLDeprecatedAttribute;
for (var j = 0, c = attributes.length; j < c; ++j) {
var attrName = attributes[j].name;
if (_isHTMLDeprecatedAttribute(tagName, attrName)) {
if (!HTMLDeprecatedAttribute[attrName]) {
HTMLDeprecatedAttribute[attrName] = {};
}
if(!HTMLDeprecatedAttribute[attrName][tagName]) {
HTMLDeprecatedAttribute[attrName][tagName] = 0;
}
HTMLDeprecatedAttribute[attrName][tagName]++;
}
}
};
/**
* 获取页面上的符合过滤条件的所有节点,可以是TEXT、COMMENT、HTMLELEMENT等
* @param {Object} rootNode 以该节点作为根节点开始进行搜索
* @param {Integer} nodeFilter 过滤器,从NodeFilter中获得
*/
var _getNodes = function(rootNode, nodeFilter){
var nodeIterator = document.createNodeIterator(rootNode, nodeFilter, null, false);
var nodes = [];
var node = nodeIterator.nextNode();
while (node) {
nodes.push(node);
node = nodeIterator.nextNode();
}
return nodes;
};
/**
* 侦测IE条件注释
*/
var _detectIECondComm = function(){
var nodes = _getNodes(document.documentElement, NodeFilter.SHOW_COMMENT);
//仅IE支持的注释
var ieCondCommRegExp = /\[\s*if\s*[^\]][\s\w]*\]/i;
//FF的注释中不能出现'--'
var ffNotSupportComReg = /--/g;
for (var i = 0, c = nodes.length; i < c; ++i) {
var currentNode = nodes[i];
if (ieCondCommRegExp.test(currentNode.nodeValue)) {
_summaryInformation.DOM.IECondComm.push(currentNode.nodeValue);
}
if(ffNotSupportComReg.test(currentNode.nodeValue)) {
_summaryInformation.DOM.FFNotSptComm.push(currentNode.nodeValue);
}
_summaryInformation.DOM.allComm.push(currentNode.nodeValue);
}
};
/**
* 侦测documentMode
*/
var _detectCompatMode = function() {
_summaryInformation.documentMode = baidu.doctype.getDocMode();
};
/**
* 检测重复的ID
*/
var _detectDuplicatedID = function(ids){
var ID = _summaryInformation.ID;
for(var id in ids) {
if(ids[id] > 1) {
ID.ids[id] = ids[id];
ID['count']++;
}
}
};
/**
* 检测页面DOM节点的最大深度
*/
var _detectDomMaxDepth = function(dom){
//如果不是html节点,则直接退出
if(dom.nodeType !== 1) return;
//扩展屏蔽
if(dom.id === 'fe-helper-tab-box' || dom.id === 'fe-helper-pb-mask') return;
//最大深度记录
var maxDepth = _summaryInformation.DOM.maxDepth;
var depth = 1;
var curTag = '';
if(dom.id) { //如果该节点有id,则拼接id
curTag = dom.tagName.toLowerCase() + '#' + dom.id + '';
} else if(dom.className) { //没有id,但有class,则拼接class
curTag = dom.tagName.toLowerCase() + '.' + dom.className.split(/\s+/).join('.') + '';
} else { //没有id也没有class,就只要标签名
curTag = dom.tagName.toLowerCase();
}
var xpath = curTag;
//深度遍历
while((dom = dom.parentNode) && dom.nodeType === 1) {
//扩展屏蔽
if(dom.id === 'fe-helper-tab-box' || dom.id === 'fe-helper-pb-mask') return;
if(dom.id) { //如果该节点有id,则拼接id
curTag = dom.tagName.toLowerCase() + '#' + dom.id + '';
} else if(dom.className) { //没有id,但有class,则拼接class
curTag = dom.tagName.toLowerCase() + '.' + dom.className.split(/\s+/).join('.') + '';
} else { //没有id也没有class,就只要标签名
curTag = dom.tagName.toLowerCase();
}
depth++;
xpath = curTag + '>' + xpath;
}
//判断当前这个dom节点是否为最大深度
if(depth > maxDepth.depth) {
maxDepth.depth = depth;
maxDepth.xpath = xpath;
}
};
/**
* 扫描整个页面的所有元素,侦测并记录结果
*/
var _scanAllElements = function(){
//所有节点
var elementList = _getNodes(document.documentElement, NodeFilter.SHOW_ELEMENT);
//所有节点个数
_summaryInformation.DOM.count = elementList.length;
//定义一个对象,用来标记节点的ID,当某一个节点的ID值大于1时,表示ID重复
var objDomId = {};
//页面扫描
for (var i = 0, len = elementList.length; i < len; ++i) {
var element = elementList[i];
//侦测过时的标签
_detectDeprecatedTag(element);
//侦测过时的属性
_detectDeprecatedAttribute(element);
//最大深度检测
_detectDomMaxDepth(element);
//ID记录
if(!!element.id) {
if(!objDomId[element.id]) objDomId[element.id] = 0;
objDomId[element.id]++;
}
}
//侦测重复的ID
_detectDuplicatedID(objDomId);
};
/**
* 检测页面上的link标签
*/
var _detectLink = function(){
//获取页面上所有的link标签
var allLink = document.querySelectorAll('link');
//获取head标签内的link标签
var inHeadLink = document.querySelectorAll('head link');
//不在Head标签内的Link
var notInHeadLink = [];
jQuery.each(allLink,function(i,link){
var isNotInHead = true;
jQuery.each(inHeadLink,function(j,temp){
if(link.href == temp.href) {
isNotInHead = false;
}
});
isNotInHead ? notInHeadLink.push(link) : false;
});
//记录未标记在head标签中的link
_summaryInformation.LINK.notInHead = notInHeadLink;
};
/**
* 侦测页面上的title标签
*/
var _detectTitle = function(){
var allTitle = document.querySelectorAll('title');
var inHeadTitle = document.querySelectorAll('head title');
var flag = false;
var titles = [];
jQuery.each(allTitle,function(i,t){
flag = false;
jQuery.each(inHeadTitle,function(j,k){
if(t == k) {
flag = true;
return false;
}
});
titles.push({
dom : t,
isInHead : flag
});
});
_summaryInformation.title = titles;
};
/**
* 检测页面上是否存在src未空的img标签
*/
var _detectImgTags = function(){
//这里只检测src属性为空的img标签,如果img标签没有设置src属性,如
,则跳过检测
var allImgTags = document.querySelectorAll('img[src]');
var imgTags = [];
var reg = /.*src=\"(.*)\".*/;
var arr = [];
jQuery.each(allImgTags,function(i,k){
arr = reg.exec(k.outerHTML);
if(!arr || arr[1].trim() == '') {
imgTags.push(k);
}
});
_summaryInformation.imgTag = imgTags;
};
/**
* 对input[type=text],input[type=password]进行监测
* 不能以size属性来确定其尺寸
*/
var _detectInputBox = function(){
var inputBoxs = document.querySelectorAll('input[type=text],input[type=password]');
var invalidInput = _summaryInformation.DOM.invalidInput;
jQuery.each(inputBoxs,function(i,input){
if(input.getAttribute('size')) {
invalidInput.count++;
invalidInput.input.push(input);
}
});
};
/**
* 检测标签的包含情况:是否有inline-tag包含了block-tag
*/
var _detectTagIncludeCase = function(){
var tagInclude = _summaryInformation.tagInclude;
var tempArr = null;
var inlineElm = null;
//遍历inline-tag
jQuery.each(INLINE_HTML_ELEMENT,function(i,inlineTag){
//遍历block-tag
jQuery.each(BLOCK_HTML_ELEMENT,function(j,blockTag){
tempArr = document.querySelectorAll(inlineTag + '>' + blockTag);
if(tempArr.length > 0) {
inlineElm = getOuterHtmlEllipsis(tempArr[0].parentNode);
jQuery.each(tempArr,function(k,item){
tagInclude.push({
inline : inlineElm, //包含了block-tag的inline-tag
block : getOuterHtmlEllipsis(item) //被包含的block-tag
});
});
}
});
});
};
/**
* 检测页面上是否有没有闭合的标签
* Chrome会自动补全未闭合的标签,所以通过innerHTML获取到的HTML内容已经是闭合的了
*/
var _detectTagUnClosed = function(){
var html = _pageSource;
//开始进行html代码词法分析
var htmlInstance = new baidu.htmlAnalytic();
var rst = htmlInstance.getUnclosedTags(html);
for(var i = 0;i < rst.length;i++){
_summaryInformation.unClosedTags.push(rst[i].outerHTML.replace(//g,'>'));
}
};
/**
* 检测HTML代码是否压缩过
*/
var _detectHtmlMinify = function(){
var lines = _pageSource.split(/\n/);
var average_length_perline = _pageSource.length / lines.length;
if (average_length_perline < 150) {
_summaryInformation.htmlMinified = false;
}
};
/**
* 获取本页面的源代码
*/
var _getPageSource = function(callback){
chrome.extension.sendMessage({
type : MSG_TYPE.GET_HTML,
link : location.href.split('#')[0]
},function(respData){
//保存源代码
_pageSource = respData.content;
//html就绪
chrome.extension.sendMessage({
type : MSG_TYPE.HTML_READY
});
callback && callback();
});
};
/**
* 初始化
*/
var _init = function(callback){
//获取本页源代码
_getPageSource(callback);
};
/**
* 执行html侦测
* @param {Function} callback 侦测完毕后的回调方法,形如:function(data){}
* @config {Object} data 就是_summaryInformation
*/
var _detect = function (callback){
//初始化结果集
_initSummaryInformation();
//扫描整个页面
_scanAllElements();
//侦测title标签
_detectTitle();
//侦测link标签
_detectLink();
//检测页面上的img标签是否src=''
_detectImgTags();
//侦测compatmode
_detectCompatMode();
//侦测IE条件注释
_detectIECondComm();
//问题Input,使用了size来确定其尺寸,不合法
_detectInputBox();
//检测是否有inline-tag包含了block-tag
_detectTagIncludeCase();
//检测未闭合的标签
_detectTagUnClosed();
//检测HTML代码是否压缩过
_detectHtmlMinify();
//执行回调
if(callback && typeof callback == "function") {
callback.call(null,_summaryInformation);
}
};
return {
init : _init,
detect : _detect
};
})();