📄 xslt.js
字号:
// Copyright 2005 Google Inc.
// All Rights Reserved
//
//
// An XSL-T processor written in JavaScript. The implementation is NOT
// complete; some xsl element are left out.
//
// References:
//
// [XSLT] XSL-T Specification
// <http://www.w3.org/TR/1999/REC-xslt-19991116>.
//
// [ECMA] ECMAScript Language Specification
// <http://www.ecma-international.org/publications/standards/Ecma-262.htm>.
//
// The XSL processor API has one entry point, the function
// xsltProcessContext(). It receives as arguments the starting point in the
// input document as an XPath expression context, the DOM root node of
// the XSL-T stylesheet, and a DOM node that receives the output.
//
// NOTE: Actually, XSL-T processing according to the specification is
// defined as operation on text documents, not as operation on DOM
// trees. So, strictly speaking, this implementation is not an XSL-T
// processor, but the processing engine that needs to be complemented
// by an XML parser and serializer in order to be complete. Those two
// are found in the file xml.js.
//
//
// TODO(mesch): add jsdoc comments. Use more coherent naming. Finish
// remaining XSLT features.
//
//
// Author: Steffen Meschkat <mesch@google.com>
// The exported entry point of the XSL-T processor, as explained
// above.
//
// @param xmlDoc The input document root, as DOM node.
// @param template The stylesheet document root, as DOM node.
// @return the processed document, as XML text in a string.
function xsltProcess(xmlDoc, stylesheet) {
if (xsltdebug) {
Log.write('XML STYLESHEET:');
Log.writeXML(xmlText(stylesheet));
Log.write('XML INPUT:');
Log.writeXML(xmlText(xmlDoc));
}
var output = (new XDocument).createDocumentFragment();
xsltProcessContext(new ExprContext(xmlDoc), stylesheet, output);
var ret = xmlText(output);
if (xsltdebug) {
Log.write('HTML OUTPUT:');
Log.writeXML(ret);
}
return ret;
}
// The main entry point of the XSL-T processor, as explained above.
//
// @param input The input document root, as XPath ExprContext.
// @param template The stylesheet document root, as DOM node.
// @param the root of the generated output, as DOM node.
function xsltProcessContext(input, template, output) {
var nodename = template.nodeName.split(/:/);
if (nodename.length == 1 || nodename[0] != 'xsl') {
xsltPassThrough(input, template, output);
} else {
switch(nodename[1]) {
case 'apply-imports':
alert('not implemented: ' + nodename[1]);
break;
case 'apply-templates':
var select = xmlGetAttribute(template, 'select');
var nodes;
if (select) {
nodes = xpathEval(select,input).nodeSetValue();
} else {
nodes = input.node.childNodes;
}
var sortContext = input.clone(nodes[0], 0, nodes);
xsltWithParam(sortContext, template);
xsltSort(sortContext, template);
var mode = xmlGetAttribute(template, 'mode');
var top = template.ownerDocument.documentElement;
for (var i = 0; i < top.childNodes.length; ++i) {
var c = top.childNodes[i];
if (c.nodeType == DOM_ELEMENT_NODE &&
c.nodeName == 'xsl:template' &&
c.getAttribute('mode') == mode) {
for (var j = 0; j < sortContext.nodelist.length; ++j) {
var nj = sortContext.nodelist[j];
xsltProcessContext(sortContext.clone(nj, j), c, output);
}
}
}
break;
case 'attribute':
var nameexpr = xmlGetAttribute(template, 'name');
var name = xsltAttributeValue(nameexpr, input);
var node = output.ownerDocument.createDocumentFragment();
xsltChildNodes(input, template, node);
var value = xmlValue(node);
output.setAttribute(name, value);
break;
case 'attribute-set':
alert('not implemented: ' + nodename[1]);
break;
case 'call-template':
var name = xmlGetAttribute(template, 'name');
var top = template.ownerDocument.documentElement;
var paramContext = input.clone();
xsltWithParam(paramContext, template);
for (var i = 0; i < top.childNodes.length; ++i) {
var c = top.childNodes[i];
if (c.nodeType == DOM_ELEMENT_NODE &&
c.nodeName == 'xsl:template' &&
c.getAttribute('name') == name) {
xsltChildNodes(paramContext, c, output);
break;
}
}
break;
case 'choose':
xsltChoose(input, template, output);
break;
case 'comment':
var node = output.ownerDocument.createDocumentFragment();
xsltChildNodes(input, template, node);
var commentData = xmlValue(node);
var commentNode = output.ownerDocument.createComment(commentData);
output.appendChild(commentNode);
break;
case 'copy':
if (input.node.nodeType == DOM_ELEMENT_NODE) {
var node = output.ownerDocument.createElement(input.node.nodeName);
output.appendChild(node);
xsltChildNodes(input, template, node);
} else if (input.node.nodeType == DOM_ATTRIBUTE_NODE) {
var node = output.ownerDocument.createAttribute(input.node.nodeName);
node.nodeValue = input.node.nodeValue;
output.setAttribute(node);
}
break;
case 'copy-of':
var select = xmlGetAttribute(template, 'select');
var value = xpathEval(select, input);
if (value.type == 'node-set') {
var nodes = value.nodeSetValue();
for (var i = 0; i < nodes.length; ++i) {
xsltCopyOf(output, nodes[i]);
}
} else {
var node = output.ownerDocument.createTextNode(value.stringValue());
output.appendChild(node);
}
break;
case 'decimal-format':
alert('not implemented: ' + nodename[1]);
break;
case 'element':
var nameexpr = xmlGetAttribute(template, 'name');
var name = xsltAttributeValue(nameexpr, input);
var node = output.ownerDocument.createElement(name);
output.appendChild(node);
xsltChildNodes(input, template, node);
break;
case 'fallback':
alert('not implemented: ' + nodename[1]);
break;
case 'for-each':
var sortContext = input.clone();
xsltSort(sortContext, template);
xsltForEach(sortContext, template, output);
break;
case 'if':
var test = xmlGetAttribute(template, 'test');
if (xpathEval(test, input).booleanValue()) {
xsltChildNodes(input, template, output);
}
break;
case 'import':
alert('not implemented: ' + nodename[1]);
break;
case 'include':
alert('not implemented: ' + nodename[1]);
break;
case 'key':
alert('not implemented: ' + nodename[1]);
break;
case 'message':
alert('not implemented: ' + nodename[1]);
break;
case 'namespace-alias':
alert('not implemented: ' + nodename[1]);
break;
case 'number':
alert('not implemented: ' + nodename[1]);
break;
case 'otherwise':
alert('error if here: ' + nodename[1]);
break;
case 'output':
// Ignored. -- Since we operate on the DOM, and all further use
// of the output of the XSL transformation is determined by the
// browser that we run in, this parameter is not applicable to
// this implementation.
break;
case 'preserve-space':
alert('not implemented: ' + nodename[1]);
break;
case 'processing-instruction':
alert('not implemented: ' + nodename[1]);
break;
case 'sort':
// just ignore -- was handled by xsltSort()
break;
case 'strip-space':
alert('not implemented: ' + nodename[1]);
break;
case 'stylesheet':
case 'transform':
xsltChildNodes(input, template, output);
break;
case 'template':
var match = xmlGetAttribute(template, 'match');
if (match && xpathMatch(match, input)) {
xsltChildNodes(input, template, output);
}
break;
case 'text':
var text = xmlValue(template);
var node = output.ownerDocument.createTextNode(text);
output.appendChild(node);
break;
case 'value-of':
var select = xmlGetAttribute(template, 'select');
var value = xpathEval(select, input).stringValue();
var node = output.ownerDocument.createTextNode(value);
output.appendChild(node);
break;
case 'param':
xsltVariable(input, template, false);
break;
case 'variable':
xsltVariable(input, template, true);
break;
case 'when':
alert('error if here: ' + nodename[1]);
break;
case 'with-param':
alert('error if here: ' + nodename[1]);
break;
default:
alert('error if here: ' + nodename[1]);
break;
}
}
}
// Sets parameters defined by xsl:with-param child nodes of the
// current template node, in the current input context. This happens
// before the operation specified by the current template node is
// executed.
function xsltWithParam(input, template) {
for (var i = 0; i < template.childNodes.length; ++i) {
var c = template.childNodes[i];
if (c.nodeType == DOM_ELEMENT_NODE && c.nodeName == 'xsl:with-param') {
xsltVariable(input, c, true);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -