docxtemplater-link-module-free
Version:
Open Source Link Module for docxtemplater 3.x
1,556 lines (1,464 loc) • 251 kB
JavaScript
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ImageModule = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
'use strict';
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var DocUtils = require('docxtemplater').DocUtils;
var rels = {
getPrefix: function getPrefix(fileType) {
return fileType === 'docx' ? 'word' : 'ppt';
},
getFileTypeName: function getFileTypeName(fileType) {
return fileType === 'docx' ? 'document' : 'presentation';
},
getRelsFileName: function getRelsFileName(fileName) {
return fileName.replace(/^.*?([a-zA-Z0-9]+)\.xml$/, '$1') + '.xml.rels';
},
getRelsFilePath: function getRelsFilePath(fileName, fileType) {
var relsFileName = rels.getRelsFileName(fileName);
var prefix = fileType === 'pptx' ? 'ppt/slides' : 'word';
return prefix + '/_rels/' + relsFileName;
}
};
module.exports = function () {
function LinkManager(zip, fileName, xmlDocuments, fileType) {
_classCallCheck(this, LinkManager);
this.nbLinks = 1;
this.fileName = fileName;
this.prefix = rels.getPrefix(fileType);
this.zip = zip;
this.xmlDocuments = xmlDocuments;
this.fileTypeName = rels.getFileTypeName(fileType);
var relsFilePath = rels.getRelsFilePath(fileName, fileType);
this.relsDoc = xmlDocuments[relsFilePath] || this.createEmptyRelsDoc(xmlDocuments, relsFilePath);
}
_createClass(LinkManager, [{
key: 'createEmptyRelsDoc',
value: function createEmptyRelsDoc(xmlDocuments, relsFileName) {
var mainRels = this.prefix + '/_rels/' + this.fileTypeName + '.xml.rels';
var doc = xmlDocuments[mainRels];
if (!doc) {
var err = new Error('Could not copy from empty relsdoc');
err.properties = {
mainRels: mainRels,
relsFileName: relsFileName,
files: Object.keys(this.zip.files)
};
throw err;
}
var relsDoc = DocUtils.str2xml(DocUtils.xml2str(doc));
var relationships = relsDoc.getElementsByTagName('Relationships')[0];
var relationshipChilds = relationships.getElementsByTagName('Relationship');
for (var i = 0, l = relationshipChilds.length; i < l; i++) {
relationships.removeChild(relationshipChilds[i]);
}
xmlDocuments[relsFileName] = relsDoc;
return relsDoc;
}
}, {
key: 'loadLinkRels',
value: function loadLinkRels() {
var iterable = this.relsDoc.getElementsByTagName('Relationship');
return Array.prototype.reduce.call(iterable, function (max, relationship) {
var id = relationship.getAttribute('Id');
if (/^rId[0-9]+$/.test(id)) {
return Math.max(max, parseInt(id.substr(3), 10));
}
return max;
}, 0);
}
}, {
key: 'addLinkRels',
value: function addLinkRels(linkValue) {
var relationships = this.relsDoc.getElementsByTagName('Relationships')[0];
var relationshipChilds = relationships.getElementsByTagName('Relationship');
var newTag = this.relsDoc.createElement('Relationship');
newTag.namespaceURI = null;
var rId = 'link_generated_' + (relationshipChilds.length + 1);
newTag.setAttribute('Id', rId);
newTag.setAttribute('Type', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink');
newTag.setAttribute('Target', linkValue);
newTag.setAttribute('TargetMode', 'External');
relationships.appendChild(newTag);
return rId;
}
}]);
return LinkManager;
}();
},{"docxtemplater":10}],2:[function(require,module,exports){
'use strict'
/**
* "Shallow freezes" an object to render it immutable.
* Uses `Object.freeze` if available,
* otherwise the immutability is only in the type.
*
* Is used to create "enum like" objects.
*
* @template T
* @param {T} object the object to freeze
* @param {Pick<ObjectConstructor, 'freeze'> = Object} oc `Object` by default,
* allows to inject custom object constructor for tests
* @returns {Readonly<T>}
*
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze
*/
function freeze(object, oc) {
if (oc === undefined) {
oc = Object
}
return oc && typeof oc.freeze === 'function' ? oc.freeze(object) : object
}
/**
* Since we can not rely on `Object.assign` we provide a simplified version
* that is sufficient for our needs.
*
* @param {Object} target
* @param {Object | null | undefined} source
*
* @returns {Object} target
* @throws TypeError if target is not an object
*
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
* @see https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.assign
*/
function assign(target, source) {
if (target === null || typeof target !== 'object') {
throw new TypeError('target is not an object')
}
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key]
}
}
return target
}
/**
* All mime types that are allowed as input to `DOMParser.parseFromString`
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/DOMParser/parseFromString#Argument02 MDN
* @see https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#domparsersupportedtype WHATWG HTML Spec
* @see DOMParser.prototype.parseFromString
*/
var MIME_TYPE = freeze({
/**
* `text/html`, the only mime type that triggers treating an XML document as HTML.
*
* @see DOMParser.SupportedType.isHTML
* @see https://www.iana.org/assignments/media-types/text/html IANA MimeType registration
* @see https://en.wikipedia.org/wiki/HTML Wikipedia
* @see https://developer.mozilla.org/en-US/docs/Web/API/DOMParser/parseFromString MDN
* @see https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-domparser-parsefromstring WHATWG HTML Spec
*/
HTML: 'text/html',
/**
* Helper method to check a mime type if it indicates an HTML document
*
* @param {string} [value]
* @returns {boolean}
*
* @see https://www.iana.org/assignments/media-types/text/html IANA MimeType registration
* @see https://en.wikipedia.org/wiki/HTML Wikipedia
* @see https://developer.mozilla.org/en-US/docs/Web/API/DOMParser/parseFromString MDN
* @see https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-domparser-parsefromstring */
isHTML: function (value) {
return value === MIME_TYPE.HTML
},
/**
* `application/xml`, the standard mime type for XML documents.
*
* @see https://www.iana.org/assignments/media-types/application/xml IANA MimeType registration
* @see https://tools.ietf.org/html/rfc7303#section-9.1 RFC 7303
* @see https://en.wikipedia.org/wiki/XML_and_MIME Wikipedia
*/
XML_APPLICATION: 'application/xml',
/**
* `text/html`, an alias for `application/xml`.
*
* @see https://tools.ietf.org/html/rfc7303#section-9.2 RFC 7303
* @see https://www.iana.org/assignments/media-types/text/xml IANA MimeType registration
* @see https://en.wikipedia.org/wiki/XML_and_MIME Wikipedia
*/
XML_TEXT: 'text/xml',
/**
* `application/xhtml+xml`, indicates an XML document that has the default HTML namespace,
* but is parsed as an XML document.
*
* @see https://www.iana.org/assignments/media-types/application/xhtml+xml IANA MimeType registration
* @see https://dom.spec.whatwg.org/#dom-domimplementation-createdocument WHATWG DOM Spec
* @see https://en.wikipedia.org/wiki/XHTML Wikipedia
*/
XML_XHTML_APPLICATION: 'application/xhtml+xml',
/**
* `image/svg+xml`,
*
* @see https://www.iana.org/assignments/media-types/image/svg+xml IANA MimeType registration
* @see https://www.w3.org/TR/SVG11/ W3C SVG 1.1
* @see https://en.wikipedia.org/wiki/Scalable_Vector_Graphics Wikipedia
*/
XML_SVG_IMAGE: 'image/svg+xml',
})
/**
* Namespaces that are used in this code base.
*
* @see http://www.w3.org/TR/REC-xml-names
*/
var NAMESPACE = freeze({
/**
* The XHTML namespace.
*
* @see http://www.w3.org/1999/xhtml
*/
HTML: 'http://www.w3.org/1999/xhtml',
/**
* Checks if `uri` equals `NAMESPACE.HTML`.
*
* @param {string} [uri]
*
* @see NAMESPACE.HTML
*/
isHTML: function (uri) {
return uri === NAMESPACE.HTML
},
/**
* The SVG namespace.
*
* @see http://www.w3.org/2000/svg
*/
SVG: 'http://www.w3.org/2000/svg',
/**
* The `xml:` namespace.
*
* @see http://www.w3.org/XML/1998/namespace
*/
XML: 'http://www.w3.org/XML/1998/namespace',
/**
* The `xmlns:` namespace
*
* @see https://www.w3.org/2000/xmlns/
*/
XMLNS: 'http://www.w3.org/2000/xmlns/',
})
exports.assign = assign;
exports.freeze = freeze;
exports.MIME_TYPE = MIME_TYPE;
exports.NAMESPACE = NAMESPACE;
},{}],3:[function(require,module,exports){
var conventions = require("./conventions");
var dom = require('./dom')
var entities = require('./entities');
var sax = require('./sax');
var DOMImplementation = dom.DOMImplementation;
var NAMESPACE = conventions.NAMESPACE;
var ParseError = sax.ParseError;
var XMLReader = sax.XMLReader;
/**
* Normalizes line ending according to https://www.w3.org/TR/xml11/#sec-line-ends:
*
* > XML parsed entities are often stored in computer files which,
* > for editing convenience, are organized into lines.
* > These lines are typically separated by some combination
* > of the characters CARRIAGE RETURN (#xD) and LINE FEED (#xA).
* >
* > To simplify the tasks of applications, the XML processor must behave
* > as if it normalized all line breaks in external parsed entities (including the document entity)
* > on input, before parsing, by translating all of the following to a single #xA character:
* >
* > 1. the two-character sequence #xD #xA
* > 2. the two-character sequence #xD #x85
* > 3. the single character #x85
* > 4. the single character #x2028
* > 5. any #xD character that is not immediately followed by #xA or #x85.
*
* @param {string} input
* @returns {string}
*/
function normalizeLineEndings(input) {
return input
.replace(/\r[\n\u0085]/g, '\n')
.replace(/[\r\u0085\u2028]/g, '\n')
}
/**
* @typedef Locator
* @property {number} [columnNumber]
* @property {number} [lineNumber]
*/
/**
* @typedef DOMParserOptions
* @property {DOMHandler} [domBuilder]
* @property {Function} [errorHandler]
* @property {(string) => string} [normalizeLineEndings] used to replace line endings before parsing
* defaults to `normalizeLineEndings`
* @property {Locator} [locator]
* @property {Record<string, string>} [xmlns]
*
* @see normalizeLineEndings
*/
/**
* The DOMParser interface provides the ability to parse XML or HTML source code
* from a string into a DOM `Document`.
*
* _xmldom is different from the spec in that it allows an `options` parameter,
* to override the default behavior._
*
* @param {DOMParserOptions} [options]
* @constructor
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/DOMParser
* @see https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-parsing-and-serialization
*/
function DOMParser(options){
this.options = options ||{locator:{}};
}
DOMParser.prototype.parseFromString = function(source,mimeType){
var options = this.options;
var sax = new XMLReader();
var domBuilder = options.domBuilder || new DOMHandler();//contentHandler and LexicalHandler
var errorHandler = options.errorHandler;
var locator = options.locator;
var defaultNSMap = options.xmlns||{};
var isHTML = /\/x?html?$/.test(mimeType);//mimeType.toLowerCase().indexOf('html') > -1;
var entityMap = isHTML ? entities.HTML_ENTITIES : entities.XML_ENTITIES;
if(locator){
domBuilder.setDocumentLocator(locator)
}
sax.errorHandler = buildErrorHandler(errorHandler,domBuilder,locator);
sax.domBuilder = options.domBuilder || domBuilder;
if(isHTML){
defaultNSMap[''] = NAMESPACE.HTML;
}
defaultNSMap.xml = defaultNSMap.xml || NAMESPACE.XML;
var normalize = options.normalizeLineEndings || normalizeLineEndings;
if (source && typeof source === 'string') {
sax.parse(
normalize(source),
defaultNSMap,
entityMap
)
} else {
sax.errorHandler.error('invalid doc source')
}
return domBuilder.doc;
}
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 && isCallback){
fn = errorImpl.length == 2?function(msg){errorImpl(key,msg)}:errorImpl;
}
errorHandler[key] = fn && function(msg){
fn('[xmldom '+key+']\t'+msg+_locator(locator));
}||function(){};
}
build('warning');
build('error');
build('fatalError');
return errorHandler;
}
//console.log('#\n\n\n\n\n\n\n####')
/**
* +ContentHandler+ErrorHandler
* +LexicalHandler+EntityResolver2
* -DeclHandler-DTDHandler
*
* DefaultHandler:EntityResolver, DTDHandler, ContentHandler, ErrorHandler
* DefaultHandler2:DefaultHandler,LexicalHandler, DeclHandler, EntityResolver2
* @link http://www.saxproject.org/apidoc/org/xml/sax/helpers/DefaultHandler.html
*/
function DOMHandler() {
this.cdata = false;
}
function position(locator,node){
node.lineNumber = locator.lineNumber;
node.columnNumber = locator.columnNumber;
}
/**
* @see org.xml.sax.ContentHandler#startDocument
* @link http://www.saxproject.org/apidoc/org/xml/sax/ContentHandler.html
*/
DOMHandler.prototype = {
startDocument : function() {
this.doc = new DOMImplementation().createDocument(null, null, null);
if (this.locator) {
this.doc.documentURI = this.locator.systemId;
}
},
startElement:function(namespaceURI, localName, qName, attrs) {
var doc = this.doc;
var el = doc.createElementNS(namespaceURI, qName||localName);
var len = attrs.length;
appendElement(this, el);
this.currentElement = el;
this.locator && position(this.locator,el)
for (var i = 0 ; i < len; i++) {
var namespaceURI = attrs.getURI(i);
var value = attrs.getValue(i);
var qName = attrs.getQName(i);
var attr = doc.createAttributeNS(namespaceURI, qName);
this.locator &&position(attrs.getLocator(i),attr);
attr.value = attr.nodeValue = value;
el.setAttributeNode(attr)
}
},
endElement:function(namespaceURI, localName, qName) {
var current = this.currentElement
var tagName = current.tagName;
this.currentElement = current.parentNode;
},
startPrefixMapping:function(prefix, uri) {
},
endPrefixMapping:function(prefix) {
},
processingInstruction:function(target, data) {
var ins = this.doc.createProcessingInstruction(target, data);
this.locator && position(this.locator,ins)
appendElement(this, ins);
},
ignorableWhitespace:function(ch, start, length) {
},
characters:function(chars, start, length) {
chars = _toString.apply(this,arguments)
//console.log(chars)
if(chars){
if (this.cdata) {
var charNode = this.doc.createCDATASection(chars);
} else {
var charNode = this.doc.createTextNode(chars);
}
if(this.currentElement){
this.currentElement.appendChild(charNode);
}else if(/^\s*$/.test(chars)){
this.doc.appendChild(charNode);
//process xml
}
this.locator && position(this.locator,charNode)
}
},
skippedEntity:function(name) {
},
endDocument:function() {
this.doc.normalize();
},
setDocumentLocator:function (locator) {
if(this.locator = locator){// && !('lineNumber' in locator)){
locator.lineNumber = 0;
}
},
//LexicalHandler
comment:function(chars, start, length) {
chars = _toString.apply(this,arguments)
var comm = this.doc.createComment(chars);
this.locator && position(this.locator,comm)
appendElement(this, comm);
},
startCDATA:function() {
//used in characters() methods
this.cdata = true;
},
endCDATA:function() {
this.cdata = false;
},
startDTD:function(name, publicId, systemId) {
var impl = this.doc.implementation;
if (impl && impl.createDocumentType) {
var dt = impl.createDocumentType(name, publicId, systemId);
this.locator && position(this.locator,dt)
appendElement(this, dt);
this.doc.doctype = dt;
}
},
/**
* @see org.xml.sax.ErrorHandler
* @link http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html
*/
warning:function(error) {
console.warn('[xmldom warning]\t'+error,_locator(this.locator));
},
error:function(error) {
console.error('[xmldom error]\t'+error,_locator(this.locator));
},
fatalError:function(error) {
throw new ParseError(error, this.locator);
}
}
function _locator(l){
if(l){
return '\n@'+(l.systemId ||'')+'#[line:'+l.lineNumber+',col:'+l.columnNumber+']'
}
}
function _toString(chars,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;
}
}
/*
* @link http://www.saxproject.org/apidoc/org/xml/sax/ext/LexicalHandler.html
* used method of org.xml.sax.ext.LexicalHandler:
* #comment(chars, start, length)
* #startCDATA()
* #endCDATA()
* #startDTD(name, publicId, systemId)
*
*
* IGNORED method of org.xml.sax.ext.LexicalHandler:
* #endDTD()
* #startEntity(name)
* #endEntity(name)
*
*
* @link http://www.saxproject.org/apidoc/org/xml/sax/ext/DeclHandler.html
* IGNORED method of org.xml.sax.ext.DeclHandler
* #attributeDecl(eName, aName, type, mode, value)
* #elementDecl(name, model)
* #externalEntityDecl(name, publicId, systemId)
* #internalEntityDecl(name, value)
* @link http://www.saxproject.org/apidoc/org/xml/sax/ext/EntityResolver2.html
* IGNORED method of org.xml.sax.EntityResolver2
* #resolveEntity(String name,String publicId,String baseURI,String systemId)
* #resolveEntity(publicId, systemId)
* #getExternalSubset(name, baseURI)
* @link http://www.saxproject.org/apidoc/org/xml/sax/DTDHandler.html
* IGNORED method of org.xml.sax.DTDHandler
* #notationDecl(name, publicId, systemId) {};
* #unparsedEntityDecl(name, publicId, systemId, notationName) {};
*/
"endDTD,startEntity,endEntity,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,resolveEntity,getExternalSubset,notationDecl,unparsedEntityDecl".replace(/\w+/g,function(key){
DOMHandler.prototype[key] = function(){return null}
})
/* Private static helpers treated below as private instance methods, so don't need to add these to the public API; we might use a Relator to also get rid of non-standard public properties */
function appendElement (hander,node) {
if (!hander.currentElement) {
hander.doc.appendChild(node);
} else {
hander.currentElement.appendChild(node);
}
}//appendChild and setAttributeNS are preformance key
exports.__DOMHandler = DOMHandler;
exports.normalizeLineEndings = normalizeLineEndings;
exports.DOMParser = DOMParser;
},{"./conventions":2,"./dom":4,"./entities":5,"./sax":7}],4:[function(require,module,exports){
var conventions = require("./conventions");
var NAMESPACE = conventions.NAMESPACE;
/**
* A prerequisite for `[].filter`, to drop elements that are empty
* @param {string} input
* @returns {boolean}
*/
function notEmptyString (input) {
return input !== ''
}
/**
* @see https://infra.spec.whatwg.org/#split-on-ascii-whitespace
* @see https://infra.spec.whatwg.org/#ascii-whitespace
*
* @param {string} input
* @returns {string[]} (can be empty)
*/
function splitOnASCIIWhitespace(input) {
// U+0009 TAB, U+000A LF, U+000C FF, U+000D CR, U+0020 SPACE
return input ? input.split(/[\t\n\f\r ]+/).filter(notEmptyString) : []
}
/**
* Adds element as a key to current if it is not already present.
*
* @param {Record<string, boolean | undefined>} current
* @param {string} element
* @returns {Record<string, boolean | undefined>}
*/
function orderedSetReducer (current, element) {
if (!current.hasOwnProperty(element)) {
current[element] = true;
}
return current;
}
/**
* @see https://infra.spec.whatwg.org/#ordered-set
* @param {string} input
* @returns {string[]}
*/
function toOrderedSet(input) {
if (!input) return [];
var list = splitOnASCIIWhitespace(input);
return Object.keys(list.reduce(orderedSetReducer, {}))
}
/**
* Uses `list.indexOf` to implement something like `Array.prototype.includes`,
* which we can not rely on being available.
*
* @param {any[]} list
* @returns {function(any): boolean}
*/
function arrayIncludes (list) {
return function(element) {
return list && list.indexOf(element) !== -1;
}
}
function copy(src,dest){
for(var p in src){
dest[p] = src[p];
}
}
/**
^\w+\.prototype\.([_\w]+)\s*=\s*((?:.*\{\s*?[\r\n][\s\S]*?^})|\S.*?(?=[;\r\n]));?
^\w+\.prototype\.([_\w]+)\s*=\s*(\S.*?(?=[;\r\n]));?
*/
function _extends(Class,Super){
var pt = Class.prototype;
if(!(pt instanceof Super)){
function t(){};
t.prototype = Super.prototype;
t = new t();
copy(pt,t);
Class.prototype = pt = t;
}
if(pt.constructor != Class){
if(typeof Class != 'function'){
console.error("unknown Class:"+Class)
}
pt.constructor = Class
}
}
// Node Types
var NodeType = {}
var ELEMENT_NODE = NodeType.ELEMENT_NODE = 1;
var ATTRIBUTE_NODE = NodeType.ATTRIBUTE_NODE = 2;
var TEXT_NODE = NodeType.TEXT_NODE = 3;
var CDATA_SECTION_NODE = NodeType.CDATA_SECTION_NODE = 4;
var ENTITY_REFERENCE_NODE = NodeType.ENTITY_REFERENCE_NODE = 5;
var ENTITY_NODE = NodeType.ENTITY_NODE = 6;
var PROCESSING_INSTRUCTION_NODE = NodeType.PROCESSING_INSTRUCTION_NODE = 7;
var COMMENT_NODE = NodeType.COMMENT_NODE = 8;
var DOCUMENT_NODE = NodeType.DOCUMENT_NODE = 9;
var DOCUMENT_TYPE_NODE = NodeType.DOCUMENT_TYPE_NODE = 10;
var DOCUMENT_FRAGMENT_NODE = NodeType.DOCUMENT_FRAGMENT_NODE = 11;
var NOTATION_NODE = NodeType.NOTATION_NODE = 12;
// ExceptionCode
var ExceptionCode = {}
var ExceptionMessage = {};
var INDEX_SIZE_ERR = ExceptionCode.INDEX_SIZE_ERR = ((ExceptionMessage[1]="Index size error"),1);
var DOMSTRING_SIZE_ERR = ExceptionCode.DOMSTRING_SIZE_ERR = ((ExceptionMessage[2]="DOMString size error"),2);
var HIERARCHY_REQUEST_ERR = ExceptionCode.HIERARCHY_REQUEST_ERR = ((ExceptionMessage[3]="Hierarchy request error"),3);
var WRONG_DOCUMENT_ERR = ExceptionCode.WRONG_DOCUMENT_ERR = ((ExceptionMessage[4]="Wrong document"),4);
var INVALID_CHARACTER_ERR = ExceptionCode.INVALID_CHARACTER_ERR = ((ExceptionMessage[5]="Invalid character"),5);
var NO_DATA_ALLOWED_ERR = ExceptionCode.NO_DATA_ALLOWED_ERR = ((ExceptionMessage[6]="No data allowed"),6);
var NO_MODIFICATION_ALLOWED_ERR = ExceptionCode.NO_MODIFICATION_ALLOWED_ERR = ((ExceptionMessage[7]="No modification allowed"),7);
var NOT_FOUND_ERR = ExceptionCode.NOT_FOUND_ERR = ((ExceptionMessage[8]="Not found"),8);
var NOT_SUPPORTED_ERR = ExceptionCode.NOT_SUPPORTED_ERR = ((ExceptionMessage[9]="Not supported"),9);
var INUSE_ATTRIBUTE_ERR = ExceptionCode.INUSE_ATTRIBUTE_ERR = ((ExceptionMessage[10]="Attribute in use"),10);
//level2
var INVALID_STATE_ERR = ExceptionCode.INVALID_STATE_ERR = ((ExceptionMessage[11]="Invalid state"),11);
var SYNTAX_ERR = ExceptionCode.SYNTAX_ERR = ((ExceptionMessage[12]="Syntax error"),12);
var INVALID_MODIFICATION_ERR = ExceptionCode.INVALID_MODIFICATION_ERR = ((ExceptionMessage[13]="Invalid modification"),13);
var NAMESPACE_ERR = ExceptionCode.NAMESPACE_ERR = ((ExceptionMessage[14]="Invalid namespace"),14);
var INVALID_ACCESS_ERR = ExceptionCode.INVALID_ACCESS_ERR = ((ExceptionMessage[15]="Invalid access"),15);
/**
* DOM Level 2
* Object DOMException
* @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/ecma-script-binding.html
* @see http://www.w3.org/TR/REC-DOM-Level-1/ecma-script-language-binding.html
*/
function DOMException(code, message) {
if(message instanceof Error){
var error = message;
}else{
error = this;
Error.call(this, ExceptionMessage[code]);
this.message = ExceptionMessage[code];
if(Error.captureStackTrace) Error.captureStackTrace(this, DOMException);
}
error.code = code;
if(message) this.message = this.message + ": " + message;
return error;
};
DOMException.prototype = Error.prototype;
copy(ExceptionCode,DOMException)
/**
* @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-536297177
* The NodeList interface provides the abstraction of an ordered collection of nodes, without defining or constraining how this collection is implemented. NodeList objects in the DOM are live.
* The items in the NodeList are accessible via an integral index, starting from 0.
*/
function NodeList() {
};
NodeList.prototype = {
/**
* The number of nodes in the list. The range of valid child node indices is 0 to length-1 inclusive.
* @standard level1
*/
length:0,
/**
* Returns the indexth item in the collection. If index is greater than or equal to the number of nodes in the list, this returns null.
* @standard level1
* @param index unsigned long
* Index into the collection.
* @return Node
* The node at the indexth position in the NodeList, or null if that is not a valid index.
*/
item: function(index) {
return this[index] || null;
},
toString:function(isHTML,nodeFilter){
for(var buf = [], i = 0;i<this.length;i++){
serializeToString(this[i],buf,isHTML,nodeFilter);
}
return buf.join('');
}
};
function LiveNodeList(node,refresh){
this._node = node;
this._refresh = refresh
_updateLiveList(this);
}
function _updateLiveList(list){
var inc = list._node._inc || list._node.ownerDocument._inc;
if(list._inc != inc){
var ls = list._refresh(list._node);
//console.log(ls.length)
__set__(list,'length',ls.length);
copy(ls,list);
list._inc = inc;
}
}
LiveNodeList.prototype.item = function(i){
_updateLiveList(this);
return this[i];
}
_extends(LiveNodeList,NodeList);
/**
* Objects implementing the NamedNodeMap interface are used
* to represent collections of nodes that can be accessed by name.
* Note that NamedNodeMap does not inherit from NodeList;
* NamedNodeMaps are not maintained in any particular order.
* Objects contained in an object implementing NamedNodeMap may also be accessed by an ordinal index,
* but this is simply to allow convenient enumeration of the contents of a NamedNodeMap,
* and does not imply that the DOM specifies an order to these Nodes.
* NamedNodeMap objects in the DOM are live.
* used for attributes or DocumentType entities
*/
function NamedNodeMap() {
};
function _findNodeIndex(list,node){
var i = list.length;
while(i--){
if(list[i] === node){return i}
}
}
function _addNamedNode(el,list,newAttr,oldAttr){
if(oldAttr){
list[_findNodeIndex(list,oldAttr)] = newAttr;
}else{
list[list.length++] = newAttr;
}
if(el){
newAttr.ownerElement = el;
var doc = el.ownerDocument;
if(doc){
oldAttr && _onRemoveAttribute(doc,el,oldAttr);
_onAddAttribute(doc,el,newAttr);
}
}
}
function _removeNamedNode(el,list,attr){
//console.log('remove attr:'+attr)
var i = _findNodeIndex(list,attr);
if(i>=0){
var lastIndex = list.length-1
while(i<lastIndex){
list[i] = list[++i]
}
list.length = lastIndex;
if(el){
var doc = el.ownerDocument;
if(doc){
_onRemoveAttribute(doc,el,attr);
attr.ownerElement = null;
}
}
}else{
throw DOMException(NOT_FOUND_ERR,new Error(el.tagName+'@'+attr))
}
}
NamedNodeMap.prototype = {
length:0,
item:NodeList.prototype.item,
getNamedItem: function(key) {
// if(key.indexOf(':')>0 || key == 'xmlns'){
// return null;
// }
//console.log()
var i = this.length;
while(i--){
var attr = this[i];
//console.log(attr.nodeName,key)
if(attr.nodeName == key){
return attr;
}
}
},
setNamedItem: function(attr) {
var el = attr.ownerElement;
if(el && el!=this._ownerElement){
throw new DOMException(INUSE_ATTRIBUTE_ERR);
}
var oldAttr = this.getNamedItem(attr.nodeName);
_addNamedNode(this._ownerElement,this,attr,oldAttr);
return oldAttr;
},
/* returns Node */
setNamedItemNS: function(attr) {// raises: WRONG_DOCUMENT_ERR,NO_MODIFICATION_ALLOWED_ERR,INUSE_ATTRIBUTE_ERR
var el = attr.ownerElement, oldAttr;
if(el && el!=this._ownerElement){
throw new DOMException(INUSE_ATTRIBUTE_ERR);
}
oldAttr = this.getNamedItemNS(attr.namespaceURI,attr.localName);
_addNamedNode(this._ownerElement,this,attr,oldAttr);
return oldAttr;
},
/* returns Node */
removeNamedItem: function(key) {
var attr = this.getNamedItem(key);
_removeNamedNode(this._ownerElement,this,attr);
return attr;
},// raises: NOT_FOUND_ERR,NO_MODIFICATION_ALLOWED_ERR
//for level2
removeNamedItemNS:function(namespaceURI,localName){
var attr = this.getNamedItemNS(namespaceURI,localName);
_removeNamedNode(this._ownerElement,this,attr);
return attr;
},
getNamedItemNS: function(namespaceURI, localName) {
var i = this.length;
while(i--){
var node = this[i];
if(node.localName == localName && node.namespaceURI == namespaceURI){
return node;
}
}
return null;
}
};
/**
* The DOMImplementation interface represents an object providing methods
* which are not dependent on any particular document.
* Such an object is returned by the `Document.implementation` property.
*
* __The individual methods describe the differences compared to the specs.__
*
* @constructor
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/DOMImplementation MDN
* @see https://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-102161490 DOM Level 1 Core (Initial)
* @see https://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-102161490 DOM Level 2 Core
* @see https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-102161490 DOM Level 3 Core
* @see https://dom.spec.whatwg.org/#domimplementation DOM Living Standard
*/
function DOMImplementation() {
}
DOMImplementation.prototype = {
/**
* The DOMImplementation.hasFeature() method returns a Boolean flag indicating if a given feature is supported.
* The different implementations fairly diverged in what kind of features were reported.
* The latest version of the spec settled to force this method to always return true, where the functionality was accurate and in use.
*
* @deprecated It is deprecated and modern browsers return true in all cases.
*
* @param {string} feature
* @param {string} [version]
* @returns {boolean} always true
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/DOMImplementation/hasFeature MDN
* @see https://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-5CED94D7 DOM Level 1 Core
* @see https://dom.spec.whatwg.org/#dom-domimplementation-hasfeature DOM Living Standard
*/
hasFeature: function(feature, version) {
return true;
},
/**
* Creates an XML Document object of the specified type with its document element.
*
* __It behaves slightly different from the description in the living standard__:
* - There is no interface/class `XMLDocument`, it returns a `Document` instance.
* - `contentType`, `encoding`, `mode`, `origin`, `url` fields are currently not declared.
* - this implementation is not validating names or qualified names
* (when parsing XML strings, the SAX parser takes care of that)
*
* @param {string|null} namespaceURI
* @param {string} qualifiedName
* @param {DocumentType=null} doctype
* @returns {Document}
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/DOMImplementation/createDocument MDN
* @see https://www.w3.org/TR/DOM-Level-2-Core/core.html#Level-2-Core-DOM-createDocument DOM Level 2 Core (initial)
* @see https://dom.spec.whatwg.org/#dom-domimplementation-createdocument DOM Level 2 Core
*
* @see https://dom.spec.whatwg.org/#validate-and-extract DOM: Validate and extract
* @see https://www.w3.org/TR/xml/#NT-NameStartChar XML Spec: Names
* @see https://www.w3.org/TR/xml-names/#ns-qualnames XML Namespaces: Qualified names
*/
createDocument: function(namespaceURI, qualifiedName, doctype){
var doc = new Document();
doc.implementation = this;
doc.childNodes = new NodeList();
doc.doctype = doctype || null;
if (doctype){
doc.appendChild(doctype);
}
if (qualifiedName){
var root = doc.createElementNS(namespaceURI, qualifiedName);
doc.appendChild(root);
}
return doc;
},
/**
* Returns a doctype, with the given `qualifiedName`, `publicId`, and `systemId`.
*
* __This behavior is slightly different from the in the specs__:
* - this implementation is not validating names or qualified names
* (when parsing XML strings, the SAX parser takes care of that)
*
* @param {string} qualifiedName
* @param {string} [publicId]
* @param {string} [systemId]
* @returns {DocumentType} which can either be used with `DOMImplementation.createDocument` upon document creation
* or can be put into the document via methods like `Node.insertBefore()` or `Node.replaceChild()`
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/DOMImplementation/createDocumentType MDN
* @see https://www.w3.org/TR/DOM-Level-2-Core/core.html#Level-2-Core-DOM-createDocType DOM Level 2 Core
* @see https://dom.spec.whatwg.org/#dom-domimplementation-createdocumenttype DOM Living Standard
*
* @see https://dom.spec.whatwg.org/#validate-and-extract DOM: Validate and extract
* @see https://www.w3.org/TR/xml/#NT-NameStartChar XML Spec: Names
* @see https://www.w3.org/TR/xml-names/#ns-qualnames XML Namespaces: Qualified names
*/
createDocumentType: function(qualifiedName, publicId, systemId){
var node = new DocumentType();
node.name = qualifiedName;
node.nodeName = qualifiedName;
node.publicId = publicId || '';
node.systemId = systemId || '';
return node;
}
};
/**
* @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1950641247
*/
function Node() {
};
Node.prototype = {
firstChild : null,
lastChild : null,
previousSibling : null,
nextSibling : null,
attributes : null,
parentNode : null,
childNodes : null,
ownerDocument : null,
nodeValue : null,
namespaceURI : null,
prefix : null,
localName : null,
// Modified in DOM Level 2:
insertBefore:function(newChild, refChild){//raises
return _insertBefore(this,newChild,refChild);
},
replaceChild:function(newChild, oldChild){//raises
this.insertBefore(newChild,oldChild);
if(oldChild){
this.removeChild(oldChild);
}
},
removeChild:function(oldChild){
return _removeChild(this,oldChild);
},
appendChild:function(newChild){
return this.insertBefore(newChild,null);
},
hasChildNodes:function(){
return this.firstChild != null;
},
cloneNode:function(deep){
return cloneNode(this.ownerDocument||this,this,deep);
},
// Modified in DOM Level 2:
normalize:function(){
var child = this.firstChild;
while(child){
var next = child.nextSibling;
if(next && next.nodeType == TEXT_NODE && child.nodeType == TEXT_NODE){
this.removeChild(next);
child.appendData(next.data);
}else{
child.normalize();
child = next;
}
}
},
// Introduced in DOM Level 2:
isSupported:function(feature, version){
return this.ownerDocument.implementation.hasFeature(feature,version);
},
// Introduced in DOM Level 2:
hasAttributes:function(){
return this.attributes.length>0;
},
/**
* Look up the prefix associated to the given namespace URI, starting from this node.
* **The default namespace declarations are ignored by this method.**
* See Namespace Prefix Lookup for details on the algorithm used by this method.
*
* _Note: The implementation seems to be incomplete when compared to the algorithm described in the specs._
*
* @param {string | null} namespaceURI
* @returns {string | null}
* @see https://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespacePrefix
* @see https://www.w3.org/TR/DOM-Level-3-Core/namespaces-algorithms.html#lookupNamespacePrefixAlgo
* @see https://dom.spec.whatwg.org/#dom-node-lookupprefix
* @see https://github.com/xmldom/xmldom/issues/322
*/
lookupPrefix:function(namespaceURI){
var el = this;
while(el){
var map = el._nsMap;
//console.dir(map)
if(map){
for(var n in map){
if(map[n] == namespaceURI){
return n;
}
}
}
el = el.nodeType == ATTRIBUTE_NODE?el.ownerDocument : el.parentNode;
}
return null;
},
// Introduced in DOM Level 3:
lookupNamespaceURI:function(prefix){
var el = this;
while(el){
var map = el._nsMap;
//console.dir(map)
if(map){
if(prefix in map){
return map[prefix] ;
}
}
el = el.nodeType == ATTRIBUTE_NODE?el.ownerDocument : el.parentNode;
}
return null;
},
// Introduced in DOM Level 3:
isDefaultNamespace:function(namespaceURI){
var prefix = this.lookupPrefix(namespaceURI);
return prefix == null;
}
};
function _xmlEncoder(c){
return c == '<' && '<' ||
c == '>' && '>' ||
c == '&' && '&' ||
c == '"' && '"' ||
'&#'+c.charCodeAt()+';'
}
copy(NodeType,Node);
copy(NodeType,Node.prototype);
/**
* @param callback return true for continue,false for break
* @return boolean true: break visit;
*/
function _visitNode(node,callback){
if(callback(node)){
return true;
}
if(node = node.firstChild){
do{
if(_visitNode(node,callback)){return true}
}while(node=node.nextSibling)
}
}
function Document(){
}
function _onAddAttribute(doc,el,newAttr){
doc && doc._inc++;
var ns = newAttr.namespaceURI ;
if(ns === NAMESPACE.XMLNS){
//update namespace
el._nsMap[newAttr.prefix?newAttr.localName:''] = newAttr.value
}
}
function _onRemoveAttribute(doc,el,newAttr,remove){
doc && doc._inc++;
var ns = newAttr.namespaceURI ;
if(ns === NAMESPACE.XMLNS){
//update namespace
delete el._nsMap[newAttr.prefix?newAttr.localName:'']
}
}
/**
* Updates `el.childNodes`, updating the indexed items and it's `length`.
* Passing `newChild` means it will be appended.
* Otherwise it's assumed that an item has been removed,
* and `el.firstNode` and it's `.nextSibling` are used
* to walk the current list of child nodes.
*
* @param {Document} doc
* @param {Node} el
* @param {Node} [newChild]
* @private
*/
function _onUpdateChild (doc, el, newChild) {
if(doc && doc._inc){
doc._inc++;
//update childNodes
var cs = el.childNodes;
if (newChild) {
cs[cs.length++] = newChild;
} else {
var child = el.firstChild;
var i = 0;
while (child) {
cs[i++] = child;
child = child.nextSibling;
}
cs.length = i;
delete cs[cs.length];
}
}
}
/**
* Removes the connections between `parentNode` and `child`
* and any existing `child.previousSibling` or `child.nextSibling`.
*
* @see https://github.com/xmldom/xmldom/issues/135
* @see https://github.com/xmldom/xmldom/issues/145
*
* @param {Node} parentNode
* @param {Node} child
* @returns {Node} the child that was removed.
* @private
*/
function _removeChild (parentNode, child) {
var previous = child.previousSibling;
var next = child.nextSibling;
if (previous) {
previous.nextSibling = next;
} else {
parentNode.firstChild = next;
}
if (next) {
next.previousSibling = previous;
} else {
parentNode.lastChild = previous;
}
child.parentNode = null;
child.previousSibling = null;
child.nextSibling = null;
_onUpdateChild(parentNode.ownerDocument, parentNode);
return child;
}
/**
* preformance key(refChild == null)
*/
function _insertBefore(parentNode,newChild,nextChild){
var cp = newChild.parentNode;
if(cp){
cp.removeChild(newChild);//remove and update
}
if(newChild.nodeType === DOCUMENT_FRAGMENT_NODE){
var newFirst = newChild.firstChild;
if (newFirst == null) {
return newChild;
}
var newLast = newChild.lastChild;
}else{
newFirst = newLast = newChild;
}
var pre = nextChild ? nextChild.previousSibling : parentNode.lastChild;
newFirst.previousSibling = pre;
newLast.nextSibling = nextChild;
if(pre){
pre.nextSibling = newFirst;
}else{
parentNode.firstChild = newFirst;
}
if(nextChild == null){
parentNode.lastChild = newLast;
}else{
nextChild.previousSibling = newLast;
}
do{
newFirst.parentNode = parentNode;
}while(newFirst !== newLast && (newFirst= newFirst.nextSibling))
_onUpdateChild(parentNode.ownerDocument||parentNode,parentNode);
//console.log(parentNode.lastChild.nextSibling == null)
if (newChild.nodeType == DOCUMENT_FRAGMENT_NODE) {
newChild.firstChild = newChild.lastChild = null;
}
return newChild;
}
/**
* Appends `newChild` to `parentNode`.
* If `newChild` is already connected to a `parentNode` it is first removed from it.
*
* @see https://github.com/xmldom/xmldom/issues/135
* @see https://github.com/xmldom/xmldom/issues/145
* @param {Node} parentNode
* @param {Node} newChild
* @returns {Node}
* @private
*/
function _appendSingleChild (parentNode, newChild) {
if (newChild.parentNode) {
newChild.parentNode.removeChild(newChild);
}
newChild.parentNode = parentNode;
newChild.previousSibling = parentNode.lastChild;
newChild.nextSibling = null;
if (newChild.previousSibling) {
newChild.previousSibling.nextSibling = newChild;
} else {
parentNode.firstChild = newChild;
}
parentNode.lastChild = newChild;
_onUpdateChild(parentNode.ownerDocument, parentNode, newChild);
return newChild;
}
Document.prototype = {
//implementation : null,
nodeName : '#document',
nodeType : DOCUMENT_NODE,
/**
* The DocumentType node of the document.
*
* @readonly
* @type DocumentType
*/
doctype : null,
documentElement : null,
_inc : 1,
insertBefore : function(newChild, refChild){//raises
if(newChild.nodeType == DOCUMENT_FRAGMENT_NODE){
var child = newChild.firstChild;
while(child){
var next = child.nextSibling;
this.insertBefore(child,refChild);
child = next;
}
return newChild;
}
if(this.documentElement == null && newChild.nodeType == ELEMENT_NODE){
this.documentElement = newChild;
}
return _insertBefore(this,newChild,refChild),(newChild.ownerDocument = this),newChild;
},
removeChild : function(oldChild){
if(this.documentElement == oldChild){
this.documentElement = null;
}
return _removeChild(this,oldChild);
},
// Introduced in DOM Level 2:
importNode : function(importedNode,deep){
return importNode(this,importedNode,deep);
},
// Introduced in DOM Level 2:
getElementById : function(id){
var rtv = null;
_visitNode(this.documentElement,function(node){
if(node.nodeType == ELEMENT_NODE){
if(node.getAttribute('id') == id){
rtv = node;
return true;
}
}
})
return rtv;
},
/**
* The `getElementsByClassName` method of `Document` interface returns an array-like object
* of all child elements which have **all** of the given class name(s).
*
* Returns an empty list if `classeNames` is an empty string or only contains HTML white space characters.
*
*
* Warning: This is a live LiveNodeList.
* Changes in the DOM will reflect in the array as the changes occur.
* If an element selected by this array no longer qualifies for the selector,
* it will automatically be removed. Be aware of this for iteration purposes.
*
* @param {string} classNames is a string representing the class name(s) to match; multiple class names are separated by (ASCII-)whitespace
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByClassName
* @see https://dom.spec.whatwg.org/#concept-getelementsbyclassname
*/
getElementsByClassName: function(classNames) {
var classNamesSet = toOrderedSet(classNames)
return new LiveNodeList(this, function(base) {
var ls = [];
if (classNamesSet.length > 0) {
_visitNode(base.documentElement, function(node) {
if(node !== base && node.nodeType === ELEMENT_NODE) {
var nodeClassNames = node.getAttribute('class')
// can be null if the attribute does not exist
if (nodeClassNames) {
// before splitting and iterating just compare them for the most common case
var matches = classNames === nodeClassNames;
if (!matches) {
var nodeClassNamesSet = toOrderedSet(nodeClassNames)
matches = classNamesSet.every(arrayIncludes(nodeClassNamesSet))
}
if(matches) {
ls.push(node);
}
}
}
});
}
return ls;
});
},
//document factory method:
createElement : function(tagName){
var node = new Element();
node.ownerDocument = this;
node.nodeName = tagName;
node.tagName = tagName;
node.localName = tagName;
node.childNodes = new NodeList();
var attrs = node.attributes = new NamedNodeMap();
attrs._ownerElement = node;
return node;
},
createDocumentFragment : function(){
var node = new DocumentFragment();
node.ownerDocument = this;
node.childNodes = new NodeList();
return node;
},
createTextNode : function(data){
var node = new Text();
node.ownerDocument = this;
node.appendData(data)
return node;
},
createComment : function(data){
var node = new Comment();
node.ownerDocument = this;
node.appendData(data)
return node;
},
createCDATASection : function(data){
var node = new CDATASection();
node.ownerDocument = this;
node.appendData(data)
return node;
},
createProcessingInstruction : function(target,data){
var node = new ProcessingInstruction();
node.ownerDocument = this;
node.tagName = node.target = target;
node.nodeValue= node.data = data;
return node;
},
createAttribute : function(name){
var node = new Attr();
node.ownerDocument = this;
node.name = name;
node.nodeName = name;
node.localName = name;
node.specified = true;
return node;
},
createEntityReference : function(name){
var node = new EntityReference();
node.ownerDocument = this;
node.nodeName = name;
return node;
},
// Introduced in DOM Level 2:
createElementNS : function(namespaceURI,qualifiedName){
var node = new Element();
var pl = qualifiedName.split(':');
var attrs = node.attributes = new NamedNodeMap();
node.childNodes = new NodeList();
node.ownerDocument = this;
node.nodeName = qualifiedName;
node.tagName = qualifiedName;
node.namespaceURI = namespaceURI;
if(pl.length == 2){
node.prefix = pl[0];
node.localName = pl[1];
}else{
//el.prefix = null;
node.localName = qualifiedName;
}
attrs._ownerElement = node;
return node;
},
// Introduced in DOM Level 2:
createAttributeNS : function(namespaceURI,qualifiedName){
var node = new Attr();
var pl = qualifiedName.split(':');
node.ownerDocument = this;
node.nodeName = qualifiedName;
node.name = qualifiedName;
node.namespaceURI = namespaceURI;
node.specified = true;
if(pl.length == 2){
node.prefix = pl[0];
node.localName = pl[1];
}else{
//el.prefix = null;
node.localName = qualifiedName;
}
return node;
}
};
_extends(Document,Node);
function Element() {
this._nsMap = {};
};
Element.prototype = {
nodeType : ELEMENT_NODE,
hasAttribute : function(name){
return this.getAttributeNode(name)!=null;
},
getAttribute : function(name){
var attr = this.getAttributeNode(name);
return attr && attr.value || '';
},
getAttributeNode : function(name){
return this.attributes.getNamedItem(name);
},
setAttribute : function(name, value){
var attr = this.ownerDocument.createAttribute(name);
attr.value = attr.nodeValue = "" + value;
this.setAttributeNode(attr)
},
removeAttribute : function(name){
var attr = this.getAttributeNode(name)
attr && this.removeAttributeNode(attr);
},
//four real opeartion method
appendChild:function(newChild){
if(newChild.nodeType === DOCUMENT_FRAGMENT_NODE){
return this.insertBefore(newChild,null);
}else{
return _appendSingleChild(this,newChild);
}
},
setAttributeNode : function(newAttr){
return this.attributes.setNamedItem(newAttr);
},
setAttributeNodeNS : function(newAttr){
return this.attributes.setNamedIte