Comparing version 0.1.12 to 0.1.13
@@ -1,3 +0,5 @@ | ||
function DOMParser(pos){ | ||
this.recordPositions = pos; | ||
function DOMParser(options){ | ||
this.options = | ||
options != true && //To the version (0.1.12) compatible | ||
options ||{locator:{}}; | ||
@@ -7,11 +9,14 @@ } | ||
var sax = new XMLReader(); | ||
var handler = new DOMHandler(); | ||
var options = this.options; | ||
var domBuilder = options.domBuilder || new DOMHandler();//contentHandler and LexicalHandler | ||
var errorHandler = options.errorHandler; | ||
var locator = options.locator; | ||
var defaultNSMap = {}; | ||
var entityMap = {'lt':'<','gt':'>','amp':'&','quot':'"','apos':"'"} | ||
if(this.recordPositions){ | ||
handler.setDocumentLocator({}) | ||
if(locator){ | ||
domBuilder.setDocumentLocator(locator) | ||
} | ||
sax.contentHandler = handler; | ||
sax.lexicalHandler = handler; | ||
sax.errorHandler = handler; | ||
sax.errorHandler = buildErrorHandler(errorHandler,domBuilder,locator); | ||
sax.domBuilder = options.domBuilder || domBuilder; | ||
if(/\/x?html?$/.test(mimeType)){ | ||
@@ -23,4 +28,37 @@ entityMap.nbsp = '\xa0'; | ||
sax.parse(source,defaultNSMap,entityMap); | ||
return handler.document; | ||
return domBuilder.document; | ||
} | ||
function buildErrorHandler(errorImpl,domBuilder,locator){ | ||
if(!errorImpl){ | ||
if(domBuilder instanceof DOMHandler){ | ||
return domBuilder; | ||
} | ||
errorImpl = domBuilder ; | ||
} | ||
var errorHandler = {} | ||
var isCallback = errorImpl instanceof Function; | ||
locator = locator||{} | ||
function build(key){ | ||
var fn = errorImpl[key]; | ||
if(!fn){ | ||
if(isCallback){ | ||
fn = errorImpl.length == 2?function(msg){errorImpl(key,msg)}:errorImpl; | ||
}else{ | ||
var i=arguments.length; | ||
while(--i){ | ||
if(fn = errorImpl[arguments[i]]){ | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
errorHandler[key] = fn && function(msg){ | ||
fn(msg+_locator(locator)); | ||
}||function(){}; | ||
} | ||
build('warning','warn'); | ||
build('error','warn','warning'); | ||
build('fatalError','warn','warning','error'); | ||
return errorHandler; | ||
} | ||
/** | ||
@@ -36,3 +74,2 @@ * +ContentHandler+ErrorHandler | ||
function DOMHandler() { | ||
this.errors = []; | ||
this.cdata = false; | ||
@@ -67,3 +104,8 @@ } | ||
var qName = attrs.getQName(i); | ||
el.setAttributeNS(namespaceURI, qName, value); | ||
var attr = doc.createAttributeNS(namespaceURI, qName); | ||
if( attr.getOffset){ | ||
position(attr.getOffset(1),attr) | ||
} | ||
attr.value = attr.nodeValue = value; | ||
el.setAttributeNode(attr) | ||
} | ||
@@ -74,5 +116,2 @@ }, | ||
var tagName = current.tagName; | ||
if(qName != tagName){ | ||
console.warn("end tag name: "+qName+' is not match the current start tagName:'+tagName); | ||
} | ||
this.currentElement = current.parentNode; | ||
@@ -93,2 +132,3 @@ }, | ||
chars = _toString.apply(this,arguments) | ||
//console.log(chars) | ||
if(this.currentElement && chars){ | ||
@@ -111,3 +151,3 @@ if (this.cdata) { | ||
setDocumentLocator:function (locator) { | ||
if(this.locator = locator){ | ||
if(this.locator = locator){// && !('lineNumber' in locator)){ | ||
locator.lineNumber = 0; | ||
@@ -145,25 +185,25 @@ } | ||
warning:function(error) { | ||
console.warn(error); | ||
this.errors.push(error); | ||
console.warn(error,_locator(this.locator)); | ||
}, | ||
error:function(error) { | ||
console.error(error); | ||
this.errors.push(error); | ||
console.error(error,_locator(this.locator)); | ||
}, | ||
fatalError:function(error) { | ||
console.error(error); | ||
this.errors.push(error); | ||
console.error(error,_locator(this.locator)); | ||
throw error; | ||
} | ||
} | ||
function _locator(l){ | ||
if(l){ | ||
return '\n@'+(l.systemId ||'')+'#[line:'+l.lineNumber+',col:'+l.columnNumber+']' | ||
} | ||
} | ||
function _toString(chars,start,length){ | ||
if(typeof chars != 'string' && !(chars instanceof String)){ | ||
//print('@@@@@\n',chars.length >= start+length); | ||
if(chars.length >= start+length){ | ||
if(typeof chars == 'string'){ | ||
return chars.substr(start,length) | ||
}else{//java sax connect width xmldom on rhino(what about: "? && !(chars instanceof String)") | ||
if(chars.length >= start+length || start){ | ||
return new java.lang.String(chars,start,length)+''; | ||
} | ||
return chars; | ||
}else{ | ||
return chars.substr(start,length) | ||
} | ||
@@ -170,0 +210,0 @@ } |
{ | ||
"name": "xmldom", | ||
"version": "0.1.12", | ||
"version": "0.1.13", | ||
"description": "A W3C Standard XML DOM(Level2 CORE) implementation and parser(DOMParser/XMLSerializer).", | ||
"keywords": ["XML","DOM","parser","javascript","DOMParser","XMLSerializer"], | ||
"keywords": ["w3c","dom","xml","parser","javascript","DOMParser","XMLSerializer"], | ||
"author": "jindw <jindw@xidea.org> (http://www.xidea.org)", | ||
@@ -7,0 +7,0 @@ "homepage": "https://github.com/jindw/xmldom", |
@@ -9,19 +9,21 @@ Introduction | ||
------- | ||
npm install xmldom | ||
>npm install xmldom | ||
Example: | ||
------- | ||
var DOMParser = require('xmldom').DOMParser; | ||
var doc = new DOMParser().parseFromString( | ||
'<xml xmlns="a" xmlns:c="./lite">\n'+ | ||
'\t<child>test</child>\n'+ | ||
'\t<child></child>\n'+ | ||
'\t<child/>\n'+ | ||
'</xml>' | ||
,'text/xml'); | ||
doc.documentElement.setAttribute('x','y'); | ||
doc.documentElement.setAttributeNS('./lite','c:x','y2'); | ||
var nsAttr = doc.documentElement.getAttributeNS('./lite','x') | ||
console.info(nsAttr) | ||
console.info(doc) | ||
==== | ||
```javascript | ||
var DOMParser = require('xmldom').DOMParser; | ||
var doc = new DOMParser().parseFromString( | ||
'<xml xmlns="a" xmlns:c="./lite">\n'+ | ||
'\t<child>test</child>\n'+ | ||
'\t<child></child>\n'+ | ||
'\t<child/>\n'+ | ||
'</xml>' | ||
,'text/xml'); | ||
doc.documentElement.setAttribute('x','y'); | ||
doc.documentElement.setAttributeNS('./lite','c:x','y2'); | ||
var nsAttr = doc.documentElement.getAttributeNS('./lite','x') | ||
console.info(nsAttr) | ||
console.info(doc) | ||
``` | ||
API Reference | ||
@@ -31,9 +33,28 @@ ===== | ||
* [DOMParser](https://developer.mozilla.org/en/DOMParser): | ||
```javascript | ||
parseFromString(xmlsource,mimeType) | ||
``` | ||
* **options extension** _by xmldom_(not BOM standard!!) | ||
```javascript | ||
//added the options argument | ||
new DOMParser(options) | ||
//errorHandler is supported | ||
new DOMParser({ | ||
/** | ||
* youcan override the errorHandler for xml parser | ||
* @link http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html | ||
*/ | ||
errorHandler:{warning:callback,error:callback,fatalError:callback} | ||
}) | ||
parseFromString(xmlsource,mimeType) | ||
``` | ||
* [XMLSerializer](https://developer.mozilla.org/en/XMLSerializer) | ||
serializeToString(node) | ||
```javascript | ||
serializeToString(node) | ||
``` | ||
DOM level2 method and attribute: | ||
@@ -185,2 +206,11 @@ ------ | ||
lookupNamespaceURI(prefix) | ||
DOM extension by xmldom | ||
--- | ||
* [Node] | ||
Source position extension; | ||
attribute: | ||
//Numbered starting from '1' | ||
lineNumber | ||
//Numbered starting from '1' | ||
columnNumber |
385
sax.js
@@ -8,4 +8,15 @@ //[4] NameStartChar ::= ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF] | ||
//var tagNamePattern = /^[a-zA-Z_][\w\-\.]*(?:\:[a-zA-Z_][\w\-\.]*)?$/ | ||
//var handlers = 'resolveEntity,getExternalSubset,characters,endDocument,endElement,endPrefixMapping,ignorableWhitespace,processingInstruction,setDocumentLocator,skippedEntity,startDocument,startElement,startPrefixMapping,notationDecl,unparsedEntityDecl,error,fatalError,warning,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,comment,endCDATA,endDTD,endEntity,startCDATA,startDTD,startEntity'.split(',') | ||
//var handlers = 'resolveEntity,getExternalSubset,characters,endDocument,endElement,endPrefixMapping,ignorableWhitespace,processingInstruction,setDocumentLocator,skippedEntity,startDocument,startElement,startPrefixMapping,notationDecl,unparsedEntityDecl,error,fatalError,warning,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,comment,endCDATA,endDTD,endEntity,startCDATA,startDTD,startEntity'.split(',') | ||
//S_TAG, S_ATTR, S_EQ, S_V | ||
//S_ATTR_S, S_E, S_S, S_C | ||
var S_TAG = 0;//tag name offerring | ||
var S_ATTR = 1;//attr name offerring | ||
var S_ATTR_S=2;//attr name end and space offer | ||
var S_EQ = 3;//=space? | ||
var S_V = 4;//attr value(no quot value only) | ||
var S_E = 5;//attr value end and no space(quot end) | ||
var S_S = 6;//(attr value end || tag end ) && (space offer) | ||
var S_C = 7;//closed el<el /> | ||
function XMLReader(){ | ||
@@ -16,11 +27,11 @@ } | ||
parse:function(source,defaultNSMap,entityMap){ | ||
var contentHandler = this.contentHandler; | ||
contentHandler.startDocument(); | ||
var domBuilder = this.domBuilder; | ||
domBuilder.startDocument(); | ||
_copy(defaultNSMap ,defaultNSMap = {}) | ||
parse(source,defaultNSMap,entityMap, | ||
contentHandler,this.lexicalHandler,this.errorHandler); | ||
contentHandler.endDocument(); | ||
domBuilder,this.errorHandler); | ||
domBuilder.endDocument(); | ||
} | ||
} | ||
function parse(source,defaultNSMapCopy,entityMap,contentHandler,lexHandler,errorHandler){ | ||
function parse(source,defaultNSMapCopy,entityMap,domBuilder,errorHandler){ | ||
function entityReplacer(a){ | ||
@@ -37,6 +48,6 @@ var k = a.slice(1,-1); | ||
} | ||
function appendText(end){ | ||
function appendText(end){//has some bugs | ||
var xt = source.substring(start,end).replace(/&#?\w+;/g,entityReplacer); | ||
locator&&position(start); | ||
contentHandler.characters(xt,0,end-start); | ||
domBuilder.characters(xt,0,end-start); | ||
start = end | ||
@@ -53,8 +64,8 @@ } | ||
} | ||
var locator = contentHandler.locator; | ||
var linePattern = /.+(?:\r\n?|\n)|.*$/g | ||
var startPos = 0; | ||
var endPos = 0; | ||
var linePattern = /.+(?:\r\n?|\n)|.*$/g | ||
var locator = domBuilder.locator; | ||
var elStack = [{currentNSMap:defaultNSMapCopy}] | ||
var parseStack = [{currentNSMap:defaultNSMapCopy}] | ||
var closeMap = {}; | ||
@@ -71,8 +82,12 @@ var start = 0; | ||
var tagName = source.substring(i+2,end); | ||
var config = elStack.pop(); | ||
var config = parseStack.pop(); | ||
var localNSMap = config.localNSMap; | ||
contentHandler.endElement(config.uri,config.localName,tagName); | ||
if(config.tagName != tagName){ | ||
errorHandler.fatalError("end tag name: "+tagName+' is not match the current start tagName:'+config.tagName ); | ||
} | ||
domBuilder.endElement(config.uri,config.localName,tagName); | ||
if(localNSMap){ | ||
for(var prefix in localNSMap){ | ||
contentHandler.endPrefixMapping(prefix) ; | ||
domBuilder.endPrefixMapping(prefix) ; | ||
} | ||
@@ -85,7 +100,7 @@ } | ||
locator&&position(i); | ||
end = parseInstruction(source,i,lexHandler); | ||
end = parseInstruction(source,i,domBuilder); | ||
break; | ||
case '!':// <!doctype,<![CDATA,<!-- | ||
locator&&position(i); | ||
end = parseDCC(source,i,contentHandler,lexHandler); | ||
end = parseDCC(source,i,domBuilder); | ||
break; | ||
@@ -101,4 +116,27 @@ default: | ||
locator&&position(i); | ||
var end = parseElementAttribute(source,i,entityReplacer,contentHandler,lexHandler,closeMap,elStack); | ||
var el = new ElementAttributes(); | ||
//elStartEnd | ||
var end = parseElementStartPart(source,i,el,entityReplacer,errorHandler); | ||
var len = el.length; | ||
//position fixed | ||
if(len && locator){ | ||
var backup = copyLocator(locator,{}); | ||
for(var i = 0;i<len;i++){ | ||
var a = el[i]; | ||
position(a.offset); | ||
a.offset = copyLocator(locator,{}); | ||
} | ||
copyLocator(backup,locator); | ||
} | ||
el.closed = el.closed||fixSelfClosed(source,end,el.tagName,closeMap); | ||
appendElement(el,domBuilder,parseStack); | ||
if(el.uri === 'http://www.w3.org/1999/xhtml' && !el.closed){ | ||
end = parseHtmlSpecialContent(source,end,el.tagName,entityReplacer,domBuilder) | ||
}else{ | ||
end++; | ||
} | ||
}catch(e){ | ||
errorHandler.error('element parse error: '+e); | ||
end = -1; | ||
@@ -110,2 +148,3 @@ } | ||
if(end<0){ | ||
//TODO: 这里有可能sax回退,有位置错误风险 | ||
appendText(i+1); | ||
@@ -117,40 +156,35 @@ }else{ | ||
} | ||
function parseSpecialContent(el,source,p,entityReplacer,contentHandler,lexHandler){ | ||
var ns = el.uri; | ||
var tagName = el.tagName; | ||
if(ns === 'http://www.w3.org/1999/xhtml' &&/^(?:script|textarea)$/i.test(tagName)){ | ||
var end = source.indexOf('</'+tagName+'>',p); | ||
var text = source.substring(p+1,end); | ||
if(/[&<]/.test(text)){ | ||
if(/^script$/i.test(tagName)){ | ||
//if(!/\]\]>/.test(text)){ | ||
//lexHandler.startCDATA(); | ||
contentHandler.characters(text,0,text.length); | ||
//lexHandler.endCDATA(); | ||
return end; | ||
//} | ||
}//}else{//text area | ||
text = text.replace(/&#?\w+;/g,entityReplacer); | ||
contentHandler.characters(text,0,text.length); | ||
return end; | ||
//} | ||
} | ||
} | ||
function copyLocator(f,t){ | ||
t.lineNumber = f.lineNumber; | ||
t.columnNumber = f.columnNumber; | ||
return t; | ||
} | ||
function parseElementAttribute(source,start,entityReplacer,contentHandler,lexHandler,closeMap,elStack){ | ||
var el = new ElementAttributes(); | ||
var tagName; | ||
/** | ||
* @see #appendElement(source,elStartEnd,el,selfClosed,entityReplacer,domBuilder,parseStack); | ||
* @return end of the elementStartPart(end of elementEndPart for selfClosed el) | ||
*/ | ||
function parseElementStartPart(source,start,el,entityReplacer,errorHandler){ | ||
var attrName; | ||
var selfClosed; | ||
var value; | ||
var p = ++start; | ||
var index = 0; | ||
var s = 0;//status | ||
var s = S_TAG;//status | ||
while(true){ | ||
var c = source.charAt(p); | ||
switch(c){ | ||
case '=': | ||
if(s === S_ATTR){//attrName | ||
attrName = source.slice(start,p); | ||
s = S_EQ; | ||
}else if(s === S_ATTR_S){ | ||
s = S_EQ; | ||
}else{ | ||
//fatalError: equal must after attrName or space after attrName | ||
throw new Error('attribute equal must after attrName'); | ||
} | ||
break; | ||
case '\'': | ||
case '"': | ||
if(s === 4){//equal | ||
if(s === S_EQ){//equal | ||
start = p+1; | ||
@@ -160,57 +194,71 @@ p = source.indexOf(c,start) | ||
value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer); | ||
el[index++] = {qName:attrName,value:value} | ||
s = 6; | ||
el.add(attrName,value,start-1); | ||
s = S_E; | ||
}else{ | ||
//reportError | ||
//fatalError: no end quot match | ||
throw new Error('attribute value no end \''+c+'\' match'); | ||
} | ||
}else if(s == S_V){ | ||
value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer); | ||
//console.log(attrName,value,start,p) | ||
el.add(attrName,value,start); | ||
//console.dir(el) | ||
errorHandler.warning('attribute "'+attrName+'" missed start quot('+c+')!!'); | ||
start = p+1; | ||
s = S_E | ||
}else{ | ||
//reportError | ||
//fatalError: no equal before | ||
throw new Error('attribute value must after "="'); | ||
} | ||
break; | ||
case '=': | ||
if(s === 2){//attrName | ||
attrName = source.slice(start,p); | ||
s = 4; | ||
}else if(s === 3){ | ||
s = 4; | ||
}else{ | ||
//reportError | ||
case '/': | ||
switch(s){ | ||
case S_TAG: | ||
el.setTagName(source.slice(start,p)); | ||
case S_E: | ||
case S_S: | ||
case S_C: | ||
s = S_C; | ||
el.closed = true; | ||
case S_V: | ||
case S_ATTR: | ||
case S_ATTR_S: | ||
break; | ||
//case S_EQ: | ||
default: | ||
throw new Error("attribute invalid close char('/')") | ||
} | ||
break; | ||
//tagName:0,tagSpace:1, | ||
// attrName:2,attrSpace:3, | ||
// equal:4,equalSpace:4, | ||
// value:5,valueSpace:6,end/>: | ||
case '>': | ||
switch(s){ | ||
case 0: | ||
case 2: | ||
case 3: | ||
case 5: | ||
var value = source.slice(start,p); | ||
case S_TAG: | ||
el.setTagName(source.slice(start,p)); | ||
case S_E: | ||
case S_S: | ||
case S_C: | ||
break;//normal | ||
case S_V://Compatible state | ||
case S_ATTR: | ||
value = source.slice(start,p); | ||
if(value.slice(-1) === '/'){ | ||
selfClosed = true; | ||
el.closed = true; | ||
value = value.slice(0,-1) | ||
} | ||
if(value){ | ||
if(s == 2){ | ||
el[index++] = {qName:value,value:value} | ||
}else if(s==5){ | ||
el[index++] = {qName:attrName,value:value} | ||
}else if(s ==0){ | ||
tagName = value; | ||
} | ||
case S_ATTR_S: | ||
if(s === S_ATTR_S){ | ||
value = attrName; | ||
} | ||
if(s == S_V){ | ||
errorHandler.warning('attribute "'+value+'" missed quot(")!!'); | ||
el.add(attrName,value.replace(/&#?\w+;/g,entityReplacer),start) | ||
}else{ | ||
errorHandler.warning('attribute "'+value+'" missed value!! "'+value+'" instead!!') | ||
el.add(value,value,start) | ||
} | ||
break; | ||
case 1: | ||
default: | ||
//case 4://error | ||
//case 4://error | ||
case S_EQ: | ||
throw new Error('attribute value missed!!'); | ||
} | ||
// console.log(tagName,tagNamePattern,tagNamePattern.test(tagName)) | ||
el.length = index; | ||
selfClosed = selfClosed||fixSelfClosed(closeMap,source,tagName,p) | ||
appendElement(contentHandler,elStack,el,tagName,selfClosed); | ||
return selfClosed ?p+1: parseSpecialContent(el,source,p,entityReplacer,contentHandler,lexHandler) || p+1; | ||
return p; | ||
/*xml space '\x20' | #x9 | #xD | #xA; */ | ||
@@ -220,32 +268,51 @@ case '\u0080': | ||
default: | ||
if(c<= ' '){ | ||
if(c<= ' '){//space | ||
switch(s){ | ||
case 0: | ||
tagName = source.slice(start,p);//tagName | ||
s = 1; | ||
case S_TAG: | ||
el.setTagName(source.slice(start,p));//tagName | ||
s = S_S; | ||
break; | ||
case 2: | ||
case S_ATTR: | ||
attrName = source.slice(start,p) | ||
s = 3; | ||
s = S_ATTR_S; | ||
break; | ||
case 5: | ||
var value = source.slice(start,p); | ||
el[index++] = {qName:attrName,value:value} | ||
s = 6; | ||
case S_V: | ||
var value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer); | ||
errorHandler.warning('attribute "'+value+'" missed quot(")!!'); | ||
el.add(attrName,value,start) | ||
case S_E: | ||
s = S_S; | ||
break; | ||
//case S_S: | ||
//case S_EQ: | ||
//case S_ATTR_S: | ||
// void();break; | ||
//case S_C: | ||
//ignore warning | ||
} | ||
}else{ | ||
}else{//not space | ||
//S_TAG, S_ATTR, S_EQ, S_V | ||
//S_ATTR_S, S_E, S_S, S_C | ||
switch(s){ | ||
case 3: | ||
el[index++] = {qName:attrName,value:attrName}; | ||
//case S_TAG:void();break; | ||
//case S_ATTR:void();break; | ||
//case S_V:void();break; | ||
case S_ATTR_S: | ||
errorHandler.warning('attribute "'+attrName+'" missed value!! "'+attrName+'" instead!!') | ||
el.add(attrName,attrName,start); | ||
start = p; | ||
s = 2; | ||
s = S_ATTR; | ||
break; | ||
case 6: | ||
case S_E: | ||
errorHandler.warning('attribute space is required"'+attrName+'"!!') | ||
case S_S: | ||
s = S_ATTR; | ||
start = p; | ||
s = 2; | ||
break; | ||
case 1: | ||
case 4: | ||
case S_EQ: | ||
s = S_V; | ||
start = p; | ||
s++; | ||
break; | ||
case S_C: | ||
throw new Error("elements closed character '/' and '>' must be connected to"); | ||
} | ||
@@ -257,21 +324,10 @@ } | ||
} | ||
function fixSelfClosed(closeMap,source,tagName,p){ | ||
//if(tagName in closeMap){ | ||
var pos = closeMap[tagName]; | ||
if(pos == null){ | ||
//console.log(tagName) | ||
pos = closeMap[tagName] = source.lastIndexOf('</'+tagName+'>') | ||
} | ||
return pos<p; | ||
//} | ||
} | ||
function appendElement(contentHandler,elStack,el,tagName,selfClosed){ | ||
/** | ||
* @return end of the elementStartPart(end of elementEndPart for selfClosed el) | ||
*/ | ||
function appendElement(el,domBuilder,parseStack){ | ||
var tagName = el.tagName; | ||
var localNSMap = null; | ||
var currentNSMap = elStack[elStack.length-1].currentNSMap; | ||
var currentNSMap = parseStack[parseStack.length-1].currentNSMap; | ||
var i = el.length; | ||
if(!tagNamePattern.test(tagName)){ | ||
console.error('invalid tagName:',tagName) | ||
throw new Error(); | ||
} | ||
while(i--){ | ||
@@ -282,7 +338,2 @@ var a = el[i]; | ||
var nsp = qName.indexOf(':'); | ||
if(!tagNamePattern.test(qName)){ | ||
console.error('invalid attribute:',qName) | ||
throw new Error(); | ||
} | ||
if(nsp>0){ | ||
@@ -307,3 +358,3 @@ var prefix = a.prefix = qName.slice(0,nsp); | ||
a.uri = 'http://www.w3.org/2000/xmlns/' | ||
contentHandler.startPrefixMapping(nsPrefix, value) | ||
domBuilder.startPrefixMapping(nsPrefix, value) | ||
} | ||
@@ -332,21 +383,55 @@ } | ||
//no prefix element has default namespace | ||
contentHandler.startElement(el.uri = currentNSMap[prefix || ''],localName,tagName,el); | ||
if(selfClosed){ | ||
contentHandler.endElement(el.uri,localName,tagName); | ||
var ns = el.uri = currentNSMap[prefix || '']; | ||
domBuilder.startElement(ns,localName,tagName,el); | ||
//endPrefixMapping and startPrefixMapping have not any help for dom builder | ||
//localNSMap = null | ||
if(el.closed){ | ||
domBuilder.endElement(ns,localName,tagName); | ||
if(localNSMap){ | ||
for(prefix in localNSMap){ | ||
domBuilder.endPrefixMapping(prefix) | ||
} | ||
} | ||
}else{ | ||
el.tagName = tagName; | ||
el.currentNSMap = currentNSMap; | ||
el.localNSMap = localNSMap; | ||
elStack.push(el); | ||
parseStack.push(el); | ||
} | ||
if(localNSMap){ | ||
for(prefix in localNSMap){ | ||
contentHandler.endPrefixMapping(prefix) | ||
} | ||
function parseHtmlSpecialContent(source,elStartEnd,tagName,entityReplacer,domBuilder){ | ||
if(/^(?:script|textarea)$/i.test(tagName)){ | ||
var elEndStart = source.indexOf('</'+tagName+'>',elStartEnd); | ||
var text = source.substring(elStartEnd+1,elEndStart); | ||
if(/[&<]/.test(text)){ | ||
if(/^script$/i.test(tagName)){ | ||
//if(!/\]\]>/.test(text)){ | ||
//lexHandler.startCDATA(); | ||
domBuilder.characters(text,0,text.length); | ||
//lexHandler.endCDATA(); | ||
return elEndStart; | ||
//} | ||
}//}else{//text area | ||
text = text.replace(/&#?\w+;/g,entityReplacer); | ||
domBuilder.characters(text,0,text.length); | ||
return elEndStart; | ||
//} | ||
} | ||
} | ||
return elStartEnd+1; | ||
} | ||
function fixSelfClosed(source,elStartEnd,tagName,closeMap){ | ||
//if(tagName in closeMap){ | ||
var pos = closeMap[tagName]; | ||
if(pos == null){ | ||
//console.log(tagName) | ||
pos = closeMap[tagName] = source.lastIndexOf('</'+tagName+'>') | ||
} | ||
return pos<elStartEnd; | ||
//} | ||
} | ||
function _copy(source,target){ | ||
for(var n in source){target[n] = source[n]} | ||
} | ||
function parseDCC(source,start,contentHandler,lexHandler){//sure start with '<!' | ||
function parseDCC(source,start,domBuilder){//sure start with '<!' | ||
var next= source.charAt(start+2) | ||
@@ -358,3 +443,3 @@ switch(next){ | ||
//append comment source.substring(4,end)//<!-- | ||
lexHandler.comment(source,start+4,end-start-4); | ||
domBuilder.comment(source,start+4,end-start-4); | ||
return end+3; | ||
@@ -368,5 +453,5 @@ }else{ | ||
var end = source.indexOf(']]>',start+9); | ||
lexHandler.startCDATA(); | ||
contentHandler.characters(source,start+9,end-start-9); | ||
lexHandler.endCDATA() | ||
domBuilder.startCDATA(); | ||
domBuilder.characters(source,start+9,end-start-9); | ||
domBuilder.endCDATA() | ||
return end+3; | ||
@@ -383,4 +468,4 @@ } | ||
var lastMatch = matchs[len-1] | ||
lexHandler.startDTD(name,pubid,sysid); | ||
lexHandler.endDTD(); | ||
domBuilder.startDTD(name,pubid,sysid); | ||
domBuilder.endDTD(); | ||
@@ -395,3 +480,3 @@ return lastMatch.index+lastMatch[0].length | ||
function parseInstruction(source,start,contentHandler){ | ||
function parseInstruction(source,start,domBuilder){ | ||
var end = source.indexOf('?>',start); | ||
@@ -402,3 +487,3 @@ if(end){ | ||
var len = match[0].length; | ||
contentHandler.processingInstruction(match[1], match[2]) ; | ||
domBuilder.processingInstruction(match[1], match[2]) ; | ||
return end+2; | ||
@@ -419,2 +504,14 @@ }else{//error | ||
ElementAttributes.prototype = { | ||
setTagName:function(tagName){ | ||
if(!tagNamePattern.test(tagName)){ | ||
throw new Error('invalid tagName:'+tagName) | ||
} | ||
this.tagName = tagName | ||
}, | ||
add:function(qName,value,offset){ | ||
if(!tagNamePattern.test(qName)){ | ||
throw new Error('invalid attribute:'+qName) | ||
} | ||
this[this.length++] = {qName:qName,value:value,offset:offset} | ||
}, | ||
length:0, | ||
@@ -421,0 +518,0 @@ getLocalName:function(i){return this[i].localName}, |
@@ -11,6 +11,5 @@ var wows = require('vows'); | ||
function xmldom(data){ | ||
console.time('xmldom'); | ||
var doc = new DOMParser().parseFromString(data); | ||
var doc = new DOMParser({locator:null,checkLater:true}).parseFromString(data); | ||
console.timeEnd('xmldom'); | ||
@@ -50,4 +49,6 @@ doc.toString = function(){ | ||
} | ||
var maxRandomAttr =parseInt(Math.random()*60); | ||
console.log('maxRandomAttr',maxRandomAttr) | ||
function addAttributes(el){ | ||
var c =parseInt(Math.random()*10); | ||
var c =parseInt(Math.random()*maxRandomAttr); | ||
while(c--){ | ||
@@ -74,21 +75,59 @@ el.setAttribute('dynamic-attr'+c,c+new Array(c).join('.')); | ||
//data = "<?xml version=\"1.0\"?><xml><child> ![CDATA[v]] d &</child>\n</xml>" | ||
console.log('test simple xml') | ||
var t1 = new Date(); | ||
var doc1 = xmldom(data); | ||
var t2 = new Date(); | ||
var doc2 = domjs(data); | ||
var t3 = new Date(); | ||
var doc3 = libxml(data); | ||
var t4 = new Date(); | ||
var xmldomTime = t2-t1; | ||
var domjsTime = t3-t2; | ||
console.assert(domjsTime>xmldomTime,'xmldom performance must more height!!') | ||
doc1 = doc1.cloneNode(true); | ||
addAttributes(doc1.documentElement); | ||
data = doc1.toString(); | ||
console.log('test more attribute xml') | ||
var t1 = new Date(); | ||
var doc1 = xmldom(data); | ||
var t2 = new Date(); | ||
var doc2 = domjs(data); | ||
var t3 = new Date(); | ||
var doc3 = libxml(data); | ||
var t4 = new Date(); | ||
var xmldomTime = t2-t1; | ||
var domjsTime = t3-t2; | ||
console.assert(domjsTime>xmldomTime,'xmldom performance must more height!!') | ||
function xmlReplace(a,v){ | ||
switch(v){ | ||
case '&': | ||
return '&' | ||
case '<': | ||
return '<' | ||
default: | ||
if(v.length>1){ | ||
return v.replace(/([&<])/g,xmlReplace) | ||
} | ||
} | ||
} | ||
xmldomresult = (domjs(doc1+'')+'').replace(/^<\?.*?\?>\s*|<!\[CDATA\[([\s\S]*?)\]\]>/g,xmlReplace) | ||
domjsresult = (doc2+'').replace(/^<\?.*?\?>\s*|<!\[CDATA\[([\s\S]*?)\]\]>/g,xmlReplace) | ||
data = xmldomresult; | ||
//console.log(data.substring(100,200)) | ||
console.log('test more attribute xml without cdata') | ||
var t1 = new Date(); | ||
var doc1 = xmldom(data); | ||
var t2 = new Date(); | ||
var doc2 = domjs(data); | ||
var t3 = new Date(); | ||
var doc3 = libxml(data); | ||
var t4 = new Date(); | ||
var xmldomTime = t2-t1; | ||
var domjsTime = t3-t2; | ||
console.assert(domjsTime>xmldomTime,'xmldom performance must more height!!') | ||
xmldomresult = (domjs(doc1+'')+'').replace(/^<\?.*?\?>\s*|<!\[CDATA\[\]\]>/g,'') | ||
domjsresult = (doc2+'').replace(/^<\?.*?\?>\s*|<!\[CDATA\[\]\]>/g,'') | ||
//console.log(xmldomresult,domjsresult) | ||
@@ -95,0 +134,0 @@ |
@@ -37,2 +37,12 @@ var wows = require('vows'); | ||
}, | ||
'attrQute': function () { | ||
var dom = new DOMParser().parseFromString('<html test="123"/>','text/html'); | ||
console.assert(dom == '<html test="123"></html>',dom+'') | ||
// var dom = new DOMParser().parseFromString('<r><Label onClick="doClick..>Hello, World</Label></r>','text/html'); | ||
// console.assert(dom == '<r><Label onClick="doClick..">Hello, World</Label></r>',dom+'!!') | ||
// | ||
var dom = new DOMParser().parseFromString('<Label onClick=doClick..">Hello, World</Label>','text/html'); | ||
console.assert(dom == '<Label onClick="doClick..">Hello, World</Label>',dom+'') | ||
}, | ||
"unclosed":function(){ | ||
@@ -39,0 +49,0 @@ var dom = new DOMParser().parseFromString('<html><meta><link><img><br><hr><input></html>','text/html'); |
@@ -38,19 +38,27 @@ var XMLSerializer = require('xmldom').XMLSerializer; | ||
var doc = oldParser.apply(this,arguments); | ||
if(!/\/x?html?\b/.test(mimeType)){ | ||
try{ | ||
check(data,doc); | ||
}catch(e){console.dir(e)} | ||
function ck(){ | ||
if(!/\/x?html?\b/.test(mimeType)){ | ||
try{ | ||
check(data,doc); | ||
}catch(e){console.dir(e)} | ||
} | ||
} | ||
if(this.options.checkLater){ | ||
setTimeout(ck,1); | ||
}else{ck()} | ||
return doc; | ||
} | ||
console.log('test dom:') | ||
require('./dom'); | ||
console.log('test parse-element:') | ||
require('./parse-element'); | ||
console.log('test node:') | ||
require('./node'); | ||
console.log('test namespace:') | ||
require('./namespace'); | ||
console.log('test normalize:') | ||
require('./html/normalize'); | ||
//require('./big-file-performance'); | ||
function include(){ | ||
for(var i=0;i<arguments.length;i++){ | ||
var file = arguments[i] | ||
console.log('test ',file); | ||
require(file); | ||
} | ||
} | ||
include('./dom','./parse-element','./node','./namespace','./html/normalize' | ||
,'./error','./locator' | ||
,'./big-file-performance' | ||
) | ||
@@ -11,3 +11,3 @@ var wows = require('vows'); | ||
'node positions': function() { | ||
var parser = new DOMParser(true); | ||
var parser = new DOMParser({locator:{}}); | ||
var doc = parser.parseFromString('<?xml version="1.0"?><!-- aaa -->\n<test>\n <a attr="value"><![CDATA[1]]>something\n</a>x</test>', 'text/xml'); | ||
@@ -25,2 +25,28 @@ var test = doc.documentElement; | ||
}, | ||
'error positions':function(){ | ||
var error = [] | ||
var parser = new DOMParser({ | ||
locator:{systemId:'c:/test/1.xml'}, | ||
errorHandler:function(msg){ | ||
error.push(msg); | ||
} | ||
}); | ||
var doc = parser.parseFromString('<html><body title="1<2"><table><;test</body></body></html>', 'text/html'); | ||
console.assert(/\n@c\:\/test\/1\.xml#\[line\:\d+,col\:\d+\]/.test(error.join(' ')),'line,col must record:'+error) | ||
}, | ||
'error positions p':function(){ | ||
var error = [] | ||
var parser = new DOMParser({ | ||
locator:{}, | ||
errorHandler:function(msg){ | ||
error.push(msg); | ||
} | ||
}); | ||
var doc = parser.parseFromString('<root>\n\t<err</root>', 'text/html'); | ||
var root = doc.documentElement; | ||
var textNode = root.firstChild; | ||
console.log(root+'/'+textNode) | ||
console.assert(/\n@#\[line\:2,col\:2\]/.test(error.join(' ')),'line,col must record:'+error); | ||
console.log(textNode.lineNumber+'/'+textNode.columnNumber) | ||
} | ||
}).run(); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
No License Found
License(Experimental) License information could not be found.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Non-existent author
Supply chain riskThe package was published by an npm account that no longer exists.
Found 1 instance in 1 package
Copyleft License
License(Experimental) Copyleft license information was found.
Found 1 instance in 1 package
Non-permissive License
License(Experimental) A license not known to be considered permissive was found.
Found 1 instance in 1 package
28
1
100
214
0
1307369
2659
2