node10-libxslt
Version:
[Fork] Node.js bindings for libxslt compatible with libxmljs
196 lines (178 loc) • 6.67 kB
JavaScript
/**
* Node.js bindings for libxslt compatible with libxmljs
* @module libxslt
*/
var fs = require('fs');
var libxmljs = require('node1-libxmljsmt');
var binding = require('bindings')('node-libxslt');
binding.registerEXSLT();
/**
* The libxmljs module. Prevents the need for a user's code to require it a second time. Also prevent weird bugs.
*/
exports.libxmljs = libxmljs;
/**
* A compiled stylesheet. Do not call this constructor, instead use parse or parseFile.
*
* store both the source document and the parsed stylesheet
* if we don't store the stylesheet doc it will be deleted by garbage collector and it will result in segfaults.
*
* @constructor
* @param {Document} stylesheetDoc - XML document source of the stylesheet
* @param {Document} stylesheetObj - Simple wrapper of a libxslt stylesheet
*/
var Stylesheet = function(stylesheetDoc, stylesheetObj){
this.stylesheetDoc = stylesheetDoc;
this.stylesheetObj = stylesheetObj;
};
/**
* Parse a XSL stylesheet
*
* If no callback is given the function will run synchronously and return the result or throw an error.
*
* @param {string|Document} source - The content of the stylesheet as a string or a [libxmljs document]{@link https://github.com/polotek/libxmljs/wiki/Document}
* @param {parseCallback} [callback] - The callback that handles the response. Expects err and Stylesheet object.
* @return {Stylesheet} Only if no callback is given.
*/
exports.parse = function(source, callback) {
// stylesheet can be given as a string or a pre-parsed xml document
if (typeof source === 'string') {
try {
source = libxmljs.parseXml(source, { nocdata: true });
} catch (err) {
if (callback) return callback(err);
throw err;
}
}
if (callback) {
binding.stylesheetAsync(source, function(err, stylesheet){
if (err) return callback(err);
callback(null, new Stylesheet(source, stylesheet));
});
} else {
return new Stylesheet(source, binding.stylesheetSync(source));
}
};
/**
* Callback to the parse function
* @callback parseCallback
* @param {error} [err]
* @param {Stylesheet} [stylesheet]
*/
/**
* Parse a XSL stylesheet
*
* @param {stringPath} sourcePath - The path of the file
* @param {parseFileCallback} callback - The callback that handles the response. Expects err and Stylesheet object.
*/
exports.parseFile = function(sourcePath, callback) {
fs.readFile(sourcePath, 'utf8', function(err, data){
if (err) return callback(err);
exports.parse(data, callback);
});
};
/**
* Callback to the parseFile function
* @callback parseFileCallback
* @param {error} [err]
* @param {Stylesheet} [stylesheet]
*/
/**
* Options for applying a stylesheet
* @typedef applyOptions
* @property {String} outputFormat - Force the type of the output, either 'document' or 'string'. Default is to use the type of the input.
* @property {boolean} noWrapParams - If true then the parameters are XPath expressions, otherwise they are treated as strings. Default is false.
*/
/**
* Apply a stylesheet to a XML document
*
* If no callback is given the function will run synchronously and return the result or throw an error.
*
* @param {string|Document} source - The XML content to apply the stylesheet to given as a string or a [libxmljs document]{@link https://github.com/polotek/libxmljs/wiki/Document}
* @param {object} [params] - Parameters passed to the stylesheet ({@link http://www.w3schools.com/xsl/el_with-param.asp})
* @param {applyOptions} [options] - Options
* @param {applyCallback} [callback] - The callback that handles the response. Expects err and result of the same type as the source param passed to apply.
* @return {string|Document} Only if no callback is given. Type is the same as the source param.
*/
Stylesheet.prototype.apply = function(source, params, options, callback) {
// params and options parameters are optional
if (typeof options === 'function') {
callback = options;
options = {};
}
if (typeof params === 'function') {
callback = params;
params = {};
options = {};
}
params = params || {};
options = options || {};
if (!options.noWrapParams) {
var wrappedParams = {};
for(var p in params) {
// string parameters must be surrounded by quotes to be usable by the stylesheet
if (typeof params[p] === 'string') wrappedParams[p] = '"' + params[p] + '"';
else wrappedParams[p] = params[p];
}
params = wrappedParams;
}
// Output format can be passed as explicit option or
// is implicit and mapped to the input format
var outputString;
if (options.outputFormat) {
outputString = (options.outputFormat === 'string');
} else {
outputString = (typeof source === 'string');
}
// xml can be given as a string or a pre-parsed xml document
if (typeof source === 'string') {
try {
source = libxmljs.parseXml(source);
} catch (err) {
if (callback) return callback(err);
throw err;
}
}
// flatten the params object in an array
var paramsArray = [];
for(var key in params) {
paramsArray.push(key);
paramsArray.push(params[key]);
}
var docResult = new libxmljs.Document();
if (callback) {
binding.applyAsync(this.stylesheetObj, source, paramsArray, outputString, docResult, function(err, strResult){
if (err) return callback(err);
callback(null, outputString ? strResult : docResult);
});
} else {
var strResult = binding.applySync(this.stylesheetObj, source, paramsArray, outputString, docResult);
return outputString ? strResult : docResult;
}
};
/**
* Callback to the Stylesheet.apply function
* @callback applyCallback
* @param {error} [err] - Error either from parsing the XML document if given as a string or from applying the styleshet
* @param {string|Document} [result] Result of the same type as the source param passed to apply
*/
/**
* Apply a stylesheet to a XML file
*
* @param {string} sourcePath - The path of the file to read
* @param {object} [params] - Parameters passed to the stylesheet ({@link http://www.w3schools.com/xsl/el_with-param.asp})
* @param {applyOptions} [options] - Options
* @param {applyToFileCallback} callback The callback that handles the response. Expects err and result as string.
*/
Stylesheet.prototype.applyToFile = function(sourcePath, params, options, callback) {
var that = this;
fs.readFile(sourcePath, 'utf8', function(err, data){
if (err) return callback(err);
that.apply(data, params, options, callback);
});
};
/**
* Callback to the Stylesheet.applyToFile function
* @callback applyToFileCallback
* @param {error} [err] - Error either from reading the file, parsing the XML document or applying the styleshet
* @param {string} [result]
*/