mirror of
https://github.com/marko-js/marko.git
synced 2025-12-08 19:26:05 +00:00
167 lines
4.5 KiB
JavaScript
167 lines
4.5 KiB
JavaScript
/*
|
|
* Copyright 2011 eBay Software Foundation
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
'use strict';
|
|
|
|
var TextNode = require('./TextNode');
|
|
var ElementNode = require('./ElementNode');
|
|
var charProps = require('char-props');
|
|
|
|
|
|
|
|
function Pos(filePath, line, column) {
|
|
this.filePath = filePath;
|
|
this.line = line;
|
|
this.column = column;
|
|
}
|
|
|
|
Pos.prototype = {
|
|
toString: function() {
|
|
return this.filePath + ":" + this.line + ":" + this.column;
|
|
}
|
|
};
|
|
|
|
function ParseTreeBuilder(taglibs) {
|
|
this.taglibs = taglibs;
|
|
|
|
this.rootNode = null;
|
|
this.prevTextNode = null;
|
|
this.parentNode = null;
|
|
this.src = null;
|
|
this.filePath = null;
|
|
this.charProps = null;
|
|
|
|
this.nsStack = [];
|
|
}
|
|
|
|
ParseTreeBuilder.prototype = {
|
|
createPos: function(line, column) {
|
|
if (arguments.length === 1) {
|
|
var index = arguments[0];
|
|
if (!this.charProps) {
|
|
this.charProps = charProps(this.src);
|
|
}
|
|
line = this.charProps.lineAt(index)+1;
|
|
column = this.charProps.columnAt(index);
|
|
}
|
|
|
|
return new Pos(this.filePath, line, column);
|
|
},
|
|
parse: function(src, filePath) {
|
|
this.src = src;
|
|
this.filePath = filePath;
|
|
|
|
this.doParse(src, filePath);
|
|
|
|
var rootNode = this.rootNode;
|
|
|
|
// Cleanup
|
|
this.src = null;
|
|
this.filePath = null;
|
|
this.charProps = null;
|
|
this.rootNode = null;
|
|
this.prevTextNode = null;
|
|
this.parentNode = null;
|
|
this.nsStack = [];
|
|
|
|
return rootNode;
|
|
},
|
|
|
|
handleCharacters: function(t) {
|
|
if (!this.parentNode) {
|
|
return; //Some bad XML parsers allow text after the ending element...
|
|
}
|
|
|
|
if (this.prevTextNode) {
|
|
this.prevTextNode.text += t;
|
|
} else {
|
|
this.prevTextNode = new TextNode(t);
|
|
this.prevTextNode.pos = this.getPos();
|
|
this.parentNode.appendChild(this.prevTextNode);
|
|
}
|
|
},
|
|
|
|
handleStartElement: function(el, attributes) {
|
|
this.prevTextNode = null;
|
|
|
|
var namespaceMappings = this.nsStack.length ? Object.create(this.nsStack[this.nsStack.length-1]) : {};
|
|
this.nsStack.push(namespaceMappings);
|
|
|
|
attributes.forEach(function (attr) {
|
|
if (attr.prefix === 'xmlns') {
|
|
var nsPrefix = attr.localName;
|
|
var targetNS = attr.value;
|
|
namespaceMappings[nsPrefix] = targetNS;
|
|
}
|
|
}, this);
|
|
|
|
function getNS(node) {
|
|
if (node.namespace) {
|
|
return node.namespace;
|
|
} else if (node.prefix) {
|
|
if (node.prefix === 'xml') {
|
|
return 'http://www.w3.org/XML/1998/namespace';
|
|
}
|
|
return namespaceMappings[node.prefix] || node.prefix;
|
|
}
|
|
else {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
var elNS = getNS(el);
|
|
|
|
var elementNode = new ElementNode(
|
|
el.localName,
|
|
elNS,
|
|
el.prefix);
|
|
|
|
elementNode.pos = this.getPos();
|
|
|
|
if (this.parentNode) {
|
|
this.parentNode.appendChild(elementNode);
|
|
} else {
|
|
|
|
elementNode.setRoot(true);
|
|
|
|
if (!elNS && el.localName === 'template') {
|
|
elementNode.localName = 'c-template';
|
|
}
|
|
|
|
this.rootNode = elementNode;
|
|
}
|
|
|
|
attributes.forEach(function (attr) {
|
|
var attrNS = getNS(attr);
|
|
var attrLocalName = attr.localName;
|
|
var attrPrefix = attr.prefix;
|
|
elementNode.setAttributeNS(attrNS, attrLocalName, attr.value, attrPrefix);
|
|
}, this);
|
|
|
|
this.parentNode = elementNode;
|
|
},
|
|
|
|
handleEndElement: function() {
|
|
this.prevTextNode = null;
|
|
this.parentNode = this.parentNode.parentNode;
|
|
this.nsStack.pop();
|
|
},
|
|
|
|
getRootNode: function () {
|
|
return this.rootNode;
|
|
}
|
|
};
|
|
|
|
module.exports = ParseTreeBuilder; |