html-css-scope-loader
Version:
html add css scope ( html template add css module)
246 lines (212 loc) • 5.27 kB
JavaScript
//捕获attribute的正则
// key = /^\s*([^\s"'<>\/=]+)/
// assing = /?:\s*((?:=))/
// value = /\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+))/
var attributeReg = /^\s*([^\s"'<>\/=]+)(?:\s*((?:=))\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/
//标签名
var ncname = '[a-zA-Z_][\\w\\-\\.]*';
var qnameCapture = '((?:'+ ncname +'\\:)?'+ ncname +')';
//开始标签开头
var startTagOpen = new RegExp('^<'+qnameCapture);
//开始标签结束
var startTagClose = /^\s*(\/?)>/;
//结束标签
var endTag = new RegExp('^<\\/'+qnameCapture+'[^>]*>');
//doctype
var doctype = /^<!DOCTYPE [^>]+>/i;
//comment
var comment = /^<!--/;
//conditional
var conditionalComment = /^<!\[/
//sign
var signReg = /^[\r\n\s\t]+$/;
//为了解决某些版本 编译转化的问题
var decodingMap = {
'<': '<',
'>': '>',
'"': '"',
'&': '&',
' ': '\n'
}
//不编译换行
var encodedAttr = /&(?:lt|gt|quot|amp);/g
//编译换行
var encodedAttrWithNewLines = /&(?:lt|gt|quot|amp|#10);/g
//火狐正则bug
var IS_REGEX_CAPTURING_BROKEN = false;
'x'.replace(/x(.)?/g,function(m,g){
IS_REGEX_CAPTURING_BROKEN = g === ''
});
//解析html
var parseHTML = function(html, option){
var stack = [];
//是否不可嵌套节点
var canBeleftOpenTag = option.canBeleftOpenTag;
//是否自闭和节点
var isUnaryTag = option.isUnaryTag;
//不可包裹节点
var isNonPhrasingTag = option.isNonPhrasingTag;
//是否是IE
var isIE = option.isIE;
//解析下标
var index = 0;
var last , lastTag;
//解析开始
while(html){
last = html;
//找到标签开始
var textEnd = html.indexOf('<');
//开始标签
if(textEnd === 0){
//判断是否是注释
if( comment.test(html) ){
var commentEnd = html.indexOf('-->');
if(commentEnd >= 0){
advance(commentEnd + 3);
continue;
}
}
//判断条件标签
if( conditionalComment.test(html) ){
var conditionalEnd = html.indexOf(']>');
if( conditionalEnd >=0 ){
advance(conditionalEnd + 2);
continue;
}
}
//DOCTYPE
var doctypeMatch = html.match(doctype);
if( doctypeMatch ){
advance( doctypeMatch[0].length );
continue;
}
//结束标签
var endTagMatch = html.match(endTag);
if( endTagMatch ){
var curIndex = index;
advance( endTagMatch[0].length );
parseEndTag(endTagMatch[1]);
continue;
}
//开始标签
var startTagMatch = parseStartTag();
if(startTagMatch){
handleStartTag(startTagMatch);
continue;
}
}
//文本内容
var text, rest , next;
if(textEnd >= 0){
//文本内容
rest = html.slice(textEnd);
//推进到下阶段字符串
while(
!endTag.test(rest)&&
!startTagOpen.test(rest)&&
!comment.test(rest)&&
!conditionalComment.test(rest)
){
next = rest.indexOf('<',1)
if(next < 0) break;
textEnd += next;
rest = html.slice(textEnd);
}
text = html.substring(0,textEnd);
advance(textEnd);
}
if(textEnd < 0){
text = html;
html = '';
}
if(option.chars && text && !signReg.test(text)){
option.chars(text)
}
}
//向前推进
function advance(n){
index += n;
html = html.substring(n);
}
//解析开头标签
function parseStartTag(){
var tagMatch = html.match(startTagOpen)
var end , attr ;
if( tagMatch ){
var match = {
tagName:tagMatch[1].toLowerCase(),
attrs:[],
start:index,
}
advance(tagMatch[0].length)
while( !(end = html.match(startTagClose)) && (attr = html.match(attributeReg)) ){
if(attr){
advance(attr[0].length);
match.attrs.push(attr);
}
}
if(end){
match.unarySlash = end[1];
match.end = index;
advance(end[0].length)
}
return match;
}
}
//解析结束标签
function parseEndTag(tagName){
var tagName = tagName.toLowerCase();
var index = -1;
for(var i=0; i<stack.length ;i++){
if(tagName = stack[i].tagName){
index = i;
break;
}
}
if(index >= 0){
//结束标签
if(option.end){
option.end(stack[i].tagName);
}
//解析stack弹出
lastTag = stack.pop();
}else{
throw('标签解析出错 '+tagName+'未找到结束标签')
return false;
}
}
//处理标签内容
function handleStartTag(match){
var tagName = match.tagName;
var unarySlash = match.unarySlash;
//P标签并且当前标签不可包裹
if( lastTag === 'p' && isNonPhrasingTag(tagName) ){
parseEndTag(lastTag);
}
//不可嵌套标签
if( lastTag === tagName && canBeleftOpenTag(tagName) ){
parseEndTag(tagName);
}
var isUnary = isUnaryTag(tagName) || !!unarySlash;
var attrs = [];
for(var i=0 ;i<match.attrs.length ;i++){
if( IS_REGEX_CAPTURING_BROKEN && match.attrs[i] ){
if(!match.attrs[i][3]){ delete match.attrs[i][3] }
if(!match.attrs[i][4]){ delete match.attrs[i][4] }
if(!match.attrs[i][5]){ delete match.attrs[i][5] }
}
var value = match.attrs[i][3] || match.attrs[i][4] || match.attrs[i][5] || ''
var re = encodedAttr;
attrs.push({
name: match.attrs[i][1],
value: value.replace(re, function(match){ return decodingMap[match] }),
})
}
if(option.start){
stack.push(match);
lastTag = tagName
option.start(tagName,attrs,isUnary);
}
}
}
module.exports = parseHTML