adxutil
Version:
Utilities tools for Askia Design eXtension
1,605 lines (1,481 loc) • 65.1 kB
JavaScript
const fs = require('fs');
const path = require('path');
const et = require('elementtree');
const subElement = et.SubElement;
const common = require('../common/common.js');
const errMsg = common.messages.error;
/**
* Object used to read and manipulate the config.xml file of an ADX
*
* const ADX = require('adxutil').ADX;
*
* const myAdx = new ADX('path/to/adx/');
* myAdx.load(function (err) {
* if (err) {
* throw err;
* }
*
* // Get the instance of the Configurator
* const conf = myAdx.configurator;
*
* console.log(conf.info.name());
*
* });
*
*
* @class Configurator
* @param {String} dir Path of the ADX directory
*/
function Configurator(dir) {
if (!dir) {
throw new Error(errMsg.invalidPathArg);
}
/**
* Path of the ADX directory
*
* @name Configurator#path
* @type {String}
*/
this.path = dir;
/**
* Type of the project (`adc` or `adp`)
*
* @name Configurator#projectType
* @type {String|"adc"|"adp"}
*/
this.projectType = null;
/**
* Version of the ADX project
*
* @name Configurator#projectVersion
* @type {String}
*/
this.projectVersion = null;
/**
* XML document (ElementTree)
*
* @name Configurator#xmlDoc
* @type {Object}
* @private
*/
this.xmldoc = null;
/**
* Info of the ADX
*
* @name Configurator#info
* @type {Configurator.Info}
*/
this.info = null;
/**
* Outputs of the ADX
*
* @name Configurator#outputs
* @type {Configurator.Outputs}
*/
this.outputs = null;
/**
* Properties of the ADX
*
* @name Configurator#properties
* @type {Configurator.Properties}
*/
this.properties = null;
}
/**
* Create a new instance of the ADX configurator object
*
* @ignore
*/
Configurator.prototype.constructor = Configurator;
/**
* Read the config.xml file and initialize all properties of the current instance object
*
* // Load the config file
* configurator.load(function (err) {
* if (err) {
* throw err;
* }
* console.log(adxInfo.name());
* });
*
* @param {Function} [callback] Callback function
* @param {Error} [callback.err] Error
*/
Configurator.prototype.load = function load(callback) {
callback = callback || function () {};
const self = this;
common.dirExists(this.path, (err, isExist) => {
if (err) {
callback(err);
return;
}
if (!isExist) {
callback(errMsg.noSuchFileOrDirectory);
return;
}
const filePath = path.join(self.path, common.CONFIG_FILE_NAME);
fs.readFile(filePath, (err, data) => {
if (err) {
callback(err);
return;
}
self.fromXml(data.toString());
callback(null);
});
});
};
/**
* Get the entire configuration as object
*
* // Get the info object
* configurator.get();
* // {
* // info : { // .... },
* // outputs : { // ... },
* // properties : { // ...}
* // }
*
* @return {Object}
*/
Configurator.prototype.get = function get() {
return {
info : this.info.get(),
outputs : this.outputs.get(),
properties : this.properties.get()
};
};
/**
* Set th configuration using an object
*
* // Get the info object
* configurator.set(
* info {
* name : "My ADC"
* version : "2.2.0.beta1",
* date : "2015-06-25",
* guid : "the-guid",
* description : "Description of the ADC"
* author : "The author name",
* company : "The company name",
* site : "http://website.url.com",
* helpURL : "http://help.url.com",
* constraints : {
* questions : {
* single : true,
* multiple : true
* },
* controls : {
* responseBlock : true
* },
* responses : {
* max : 10
* }
* }
* },
* outputs : {
* defaultOutput : "main",
* outputs : [
* {
* id : "main",
* description : "Main output",
* contents : [
* {
* fileName : 'main.css',
* type : 'css',
* mode : 'static',
* position : 'head'
* },
* {
* fileName : 'main.html',
* type : 'html',
* mode : 'dynamic',
* position : 'placeholder'
* },
* {
* fileName : 'main.js',
* type : 'javascript',
* mode : 'static',
* position: 'foot'
* }
* ]
* },
* {
* id : "second",
* description : "Second output",
* condition : "Browser.Support(\"javascript\")",
* contents : [
* {
* fileName : 'second.css',
* type : 'css',
* mode : 'static',
* position : 'head'
* },
* {
* fileName : 'second.html',
* type : 'html',
* mode : 'dynamic',
* position : 'placeholder'
* },
* {
* fileName : 'second.js',
* type : 'javascript',
* mode : 'static',
* position : 'foot'
* }
* ]
* },
* {
* id : "third",
* description : "Third output",
* maxIterations : 12,
* defaultGeneration : false,
* contents : [
* {
* fileName : "third.css",
* type : "css",
* mode : "static",
* position : "head",
* attributes : [
* {
* name : "rel",
* value : "alternate"
* },
* {
* name : "media",
* value : "print"
* }
* ]
* },
* {
* fileName : 'HTML5Shim.js',
* type : 'javascript',
* mode : 'static',
* position : 'head',
* yieldValue : '<!--[if lte IE 9]><script type="text/javascript" src="{%= CurrentADC.URLTo("static/HTML5Shim.js") %}" ></script><![endif]-->'
* }
* ]
* }
* },
* properties : {
* categories : [
* {
* id : "general",
* description : "General",
* properties : [
* {
* id : "background",
* name : "Background color",
* type : "color",
* description : "Color of the ADC background",
* colorFormat : "rgb",
* value : "255,255,255"
* }
* ]
* }
* ]
* }
* });
*
* @param {Object} data Data to set
* @param {Object} [data.info] Info data
* @param {Object} [data.outputs] Outputs data
* @param {Object} [data.properties] Properties data
*/
Configurator.prototype.set = function set(data) {
if (data.info) {
this.info.set(data.info);
}
if (data.outputs) {
this.outputs.set(data.outputs);
}
if (data.properties) {
this.properties.set(data.properties);
}
};
/**
* Return the configuration as xml
*
* // Serialize the config to XML
* configurator.toXml();
* // -> <?xml version="1.0" encoding="utf-8"?>
* <control xmlns="http://www.askia.com/2.1.0/ADCSchema"
* xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
* xsi:schemaLocation="http://www.askia.com/2.1.0/ADCSchema https://raw.githubusercontent.com/AskiaADX/ADXSchema/2.1.0/ADCSchema.xsd"
* version="2.1.0"
* askiaCompat="5.4.2">
* <info>
* <name>My Name</name>
* <guid>the-guid</guid>
* ....
* </info>
* <outputs defaultOutput="default">
* ....
* </outputs>
* <properties>
* ....
* </properties>
* </control>
*
* @return {String}
*/
Configurator.prototype.toXml = function toXml() {
const xml = [];
const projectType = this.projectType;
const projectVersion = this.projectVersion;
const rootName = (projectType === 'adc') ? 'control' : 'page';
const namespaceURI = 'http://www.askia.com/' + projectVersion + '/' + projectType.toUpperCase() + 'Schema';
const schemaURI = 'https://raw.githubusercontent.com/AskiaADX/ADXSchema/' + projectVersion + '/' + projectType.toUpperCase() + 'Schema.xsd';
const infoXml = this.info.toXml();
const outputsXml = this.outputs.toXml();
const propertiesXml = this.properties.toXml();
let askiaCompat;
switch(projectVersion) {
case '2.1.0':
askiaCompat = "5.4.2";
break;
case '2.0.0':
default:
askiaCompat = "5.3.3";
break;
}
xml.push('<?xml version="1.0" encoding="utf-8"?>');
xml.push('<' + rootName + ' xmlns="' + namespaceURI + '"' +
'\n xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' +
'\n xsi:schemaLocation="' + namespaceURI + ' ' + schemaURI + '"' +
'\n version="' + projectVersion + '"' +
'\n askiaCompat="' + askiaCompat + '">');
if (infoXml) {
xml.push(infoXml);
}
if (outputsXml) {
xml.push(outputsXml);
}
if (propertiesXml) {
xml.push(propertiesXml);
}
xml.push('</' + rootName + '>');
return xml.join('\n');
};
/**
* Re-init the configurator using the xml string
*
* // Load the configuration using an xml string
* // xmlString contains information from config.xml
* configurator.fromXml(xmlString);
*
*/
Configurator.prototype.fromXml = function fromXml(xml) {
this.xmldoc = et.parse(xml);
const rootEl = this.xmldoc.getroot();
switch (rootEl && rootEl.tag) {
case 'control':
this.projectType = 'adc';
this.projectVersion = rootEl.get("version") || "2.0.0";
break;
case 'page':
this.projectType = 'adp';
this.projectVersion = rootEl.get("version") || "2.1.0";
break;
default:
throw new Error(errMsg.invalidConfigFile);
}
this.info = new ADXInfo(this);
this.outputs = new ADXOutputs(this);
this.properties = new ADXProperties(this);
};
/**
* Save the current configuration
*
* @param {Function} [callback]
* @param {Error} callback.err
*/
Configurator.prototype.save = function save(callback) {
const filePath = path.join(this.path, common.CONFIG_FILE_NAME);
const self = this;
fs.writeFile(filePath, this.toXml(), {encoding : 'utf8'}, (err) => {
if (!err) {
self.load(callback);
} else {
if (typeof callback === 'function') {
callback(err);
}
}
});
};
/**
* Provide an object to manipulate the meta-information of the ADX (config.xml > info)
*
* const ADX = require('adxutil').ADX;
*
* const myAdx = new ADX('path/to/adx/');
* myAdx.load(function (err) {
* if (err) {
* throw err;
* }
*
* // Get the instance of the Info
* const info = myAdx.configurator.info;
*
* console.log(info.get());
*
* });
*
* @class Configurator.Info
* @param {ADX.Configurator} configurator Instance of the configurator
*/
function ADXInfo(configurator) {
/**
* Parent configurator
*
* @name Configurator.Info#configurator
* @type {Configurator}
*/
this.configurator = configurator;
}
/**
* Creates a new instance of ADX Info
*
* @ignore
*/
ADXInfo.prototype.constructor = ADXInfo;
/**
* Get the entire information as object
*
* // Get the info object
* adxInfo.get();
* // {
* // name : "My ADC"
* // version : "2.2.0.beta1",
* // date : "2015-06-25",
* // guid : "the-guid",
* // description : "Description of the ADC"
* // author : "The author name",
* // company : "The company name",
* // site : "http://website.url.com",
* // helpURL : "http://help.url.com",
* // constraints : {
* // questions : {
* // single : true,
* // multiple : true
* // },
* // controls : {
* // responseBlock : true
* // },
* // responses : {
* // max : 10
* // }
* // }
* // }
*
* @name Configurator.Info#get
* @function
* @return {Object}
*/
ADXInfo.prototype.get = function get() {
const self = this;
const result = {};
const projectType = this.configurator.projectType;
const projectVersion = this.configurator.projectVersion;
const info = ["name", "guid", "version", "date", "description", "company", "author", "site", "helpURL"];
if (projectType === 'adc') {
info.push("categories");
if (projectVersion === '2.0.0') {
info.push("style");
}
info.push("constraints");
}
info.forEach((methodName) => {
result[methodName] = self[methodName]();
});
return result;
};
/**
* Set the information using a plain object
*
* // Get the info object
* adxInfo.set({
* name : "My ADC"
* version : "2.2.0.beta1",
* date : "2015-06-25",
* guid : "the-guid",
* description : "Description of the ADC"
* author : "The author name",
* company : "The company name",
* site : "http://website.url.com",
* helpURL : "http://help.url.com",
* constraints : {
* questions : {
* single : true,
* multiple : true
* },
* controls : {
* responseBlock : true
* },
* responses : {
* max : 10
* }
* }
* });
*
*
* @name Configurator.Info#set
* @function
* @param {Object} data Data to set
* @param {String} [data.name] Name of the ADX
* @param {String} [data.version] Version of the ADX
* @param {String} [data.date] Date of the ADX (YYYY-MM-dd)
* @param {String} [data.guid] GUID of the ADX
* @param {String} [data.description] Description of the ADX
* @param {String} [data.author] Author(s) of the ADX (name1 <name1@email.com, name2 <name2@email.com>)
* @param {String} [data.company] Company name of the creator
* @param {String} [data.site] Web site URL of the creator
* @param {String} [data.helpURL] URL to the ADX help
* @param {Object} [data.style] [DEPRECATED] Style of the ADC
* @param {Number} [data.style.width] [DEPRECATED] Width of the ADC (in pixel)
* @param {Number} [data.style.height] [DEPRECATED] Height of the ADC (in pixel)
* @param {String[]} [data.categories] [DEPREACATED] Categories of the ADC
* @param {Object} [data.constraints] Constraints of the ADC (ADC ONLY)
* @param {Object} [data.constraints.questions] Questions constraints of the ADC (ADC ONLY)
* @param {Boolean} [data.constraints.questions.chapter] Allow or not on chapter
* @param {Boolean} [data.constraints.questions.single] Allow or not on single questions
* @param {Boolean} [data.constraints.questions.multiple] Allow or not on multi-coded questions
* @param {Boolean} [data.constraints.questions.numeric] Allow or not on numeric questions
* @param {Boolean} [data.constraints.questions.open] Allow or not on open-ended questions
* @param {Boolean} [data.constraints.questions.date] Allow or not on date questions
* @param {Boolean} [data.constraints.questions.requireParentLoop] Require or not on a parent loop question
* @param {Object} [data.constraints.controls] Controls constraints of the ADC (ADC ONLY)
* @param {Boolean} [data.constraints.controls.responseBlock] Allow or not on response-block
* @param {Boolean} [data.constraints.controls.label] Allow or not on label
* @param {Boolean} [data.constraints.controls.textbox] Allow or not on text-box
* @param {Boolean} [data.constraints.controls.listbox] Allow or not on list-box
* @param {Boolean} [data.constraints.controls.checkbox] Allow or not on checkbox
* @param {Boolean} [data.constraints.controls.radiobutton] Allow or not on radio button
* @param {Object} [data.constraints.responses] Responses constraints of the ADC (ADC ONLY)
* @param {Number} [data.constraints.responses.min] Minimum allowed responses
* @param {Number} [data.constraints.responses.max] Maximum allowed responses
*/
ADXInfo.prototype.set = function set(data) {
const self = this;
if (!data) {
return;
}
["name", "guid", "version", "date", "description", "company", "author", "site",
"helpURL", "categories", "style", "constraints"].forEach((methodName) => {
if (data.hasOwnProperty(methodName)) {
self[methodName](data[methodName]);
}
});
};
["name", "guid", "version", "date", "description", "company", "author", "site", "helpURL"].forEach(function (propName) {
/**
* Get or set the name of the ADX
*
* // Get the name of the ADX
* adxInfo.name();
*
* // Set the name of the ADX
* adxInfo.name("New name");
*
* @name Configurator.Info#name
* @function
* @param {String} [data] Name of the ADX to set
* @returns {String} Name of the ADX
*/
/**
* Get or set the GUID of the ADX
*
* // Get the guid of the ADX
* adxInfo.guid();
*
* // Set the guid of the ADC
* const uuid = require('uuid'');
* adxInfo.guid(uuid.v4());
*
* @name Configurator.Info#guid
* @function
* @param {String} [data] GUID of the ADX to set
* @returns {String} GUID of the ADX
*/
/**
* Get or set the version of the ADX
*
* // Get the version of the ADX
* adxInfo.version();
*
* // Set the version of the ADX
* adxInfo.version("2.0.0.beta1");
*
* @name Configurator.Info#version
* @function
* @param {String} [data] Version of the ADX to set
* @returns {String} Version of the ADX
*/
/**
* Get or set the description of the ADX
*
* // Get the description of the ADX
* adxInfo.description();
*
* // Set the description of the ADX
* adxInfo.description("This is the description of the ADX");
*
* @name Configurator.Info#description
* @function
* @param {String} [data] Description of the ADX to set
* @returns {String} Description of the ADX
*/
/**
* Get or set the company name of the ADX creator
*
* // Get the company of the ADX
* adxInfo.company();
*
* // Set the company of the ADX
* adxInfo.company("Askia SAS");
*
* @name Configurator.Info#company
* @function
* @param {String} [data] Company name to set
* @returns {String} Company of the ADX creator
*/
/**
* Get or set the author(s) of the ADX
*
* // Get the author(s) of the ADX
* adxInfo.author();
*
* // Set the author(s) of the ADX
* adxInfo.author("John Doe <john.doe@unknow.com>, Foo Bar <foo@bar.com>");
*
* @name Configurator.Info#author
* @function
* @param {String} [data] Author(s) to set
* @returns {String} Author(s)
*/
/**
* Get or set the date creation of the ADX
*
* // Get the date
* adxInfo.date();
*
* // Set the date
* adxInfo.date("2015-06-25");
*
* @name Configurator.Info#date
* @function
* @param {String} [data] Date to set
* @returns {String} Date
*/
/**
* Get or set the website URL of the ADX creator
*
* // Get the site
* adxInfo.site();
*
* // Set the site URL
* adxInfo.site("http://my.website.com");
*
* @name Configurator.Info#site
* @function
* @param {String} [data] URL to set
* @returns {String} Site URL
*/
/**
* Get or set the help URL of the ADX
*
* // Get the help URL
* adxInfo.helpURL();
*
* // Set the help URL
* adxInfo.helpURL("http://my.help.file.com");
*
* @name Configurator.Info#helpURL
* @function
* @param {String} [data] URL to set
* @returns {String} Help URL
*/
ADXInfo.prototype[propName] = function (data) {
const xmldoc = this.configurator.xmldoc;
let elInfo = xmldoc.find('info');
const isSetter = data !== undefined;
// No root info
if (!elInfo && isSetter) {
elInfo = subElement(xmldoc.getroot(), 'info');
} else if (!elInfo && !isSetter) {
return '';
}
let el = elInfo.find(propName);
// No element
if (!el && isSetter) {
el = subElement(elInfo, propName);
} else if (!el && !isSetter) {
return '';
}
if (isSetter) {
el.text = data;
}
return el.text;
};
});
/**
* Get or set the style
*
* // Get the style of the ADC
* adxInfo.style();
*
* // Set the style of the ADC
* adxInfo.style({
* width : 400,
* height : 200
* });
*
* @deprecated
* @name Configurator.Info#style
* @function
* @param {Object} [data] Style to set
* @param {Number} [data.width] Style width
* @param {Number} [data.height] Style height
* @returns {Object}
*/
ADXInfo.prototype.style = function style(data) {
if (this.configurator.projectType !== 'adc') {
return;
}
if (this.configurator.projectVersion !== "2.0.0") {
return;
}
const xmldoc = this.configurator.xmldoc;
let elInfo = xmldoc.find("info");
const isSetter = (data !== undefined);
if (!elInfo && isSetter) {
elInfo = subElement(xmldoc.getroot(), "info");
} else if (!elInfo && !isSetter) {
return { width : 0,height : 0 };
}
let el = elInfo.find("style");
if (!el && isSetter) {
el = subElement(elInfo, "style");
} else if (!el && !isSetter) {
return {width : 0, height : 0};
}
const result = {};
let w, h;
if (isSetter) {
if (data.width !== undefined) {
el.set("width", data.width);
}
if (data.height !== undefined) {
el.set("height", data.height);
}
}
w = el.get("width") || "0";
h = el.get("height") || "0";
result.width = parseInt(w, 10);
result.height = parseInt(h, 10);
return result;
};
/**
* Get or set the categories
*
* // Get the categories of the ADC
* adxInfo.categories();
*
* // Set the categories of the ADC
* adxInfo.categories(["General", "Slider", "Single"]);
*
* @deprecated
* @name Configurator.Info#categories
* @function
* @param {String[]} [data] Array of string which represent the categories to set
* @returns {String[]} Name of categories
*/
ADXInfo.prototype.categories = function categories(data) {
if (this.configurator.projectType !== 'adc') {
return;
}
const xmldoc = this.configurator.xmldoc;
let elInfo = xmldoc.find('info');
const isSetter = Array.isArray(data);
// No root info
if (!elInfo && isSetter) {
elInfo = subElement(xmldoc.getroot(), 'info');
} else if (!elInfo && !isSetter) {
return [];
}
let el = elInfo.find("categories");
// No categories
if (!el && isSetter) {
el = subElement(elInfo, 'categories');
} else if (!el && !isSetter) {
return [];
}
const result = [];
if (isSetter) {
el.delSlice(0, el.len());
data.forEach((text) => {
const cat = subElement(el, 'category');
cat.text = text;
});
}
el.iter('category', (cat) => {
result.push(cat.text);
});
return result;
};
/**
* Get or set the constraints
*
* // Get the constraints of the ADC
* adxInfo.constraints();
*
* // Set the constraints of the ADC
* adxInfo.constraints({
* questions : {
* single : true,
* multiple : true
* },
* controls : {
* responseBlock : true,
* label : false
* },
* responses : {
* max : 25
* }
* });
*
* @name Configurator.Info#constraints
* @function
* @param {Object} [data] Constraint data to set (ADC ONLY)
* @return {Object} Constraints
*/
ADXInfo.prototype.constraints = function constraints(data) {
if (this.configurator.projectType !== 'adc') {
return;
}
const xmldoc = this.configurator.xmldoc;
let elInfo = xmldoc.find("info");
// No root info
if (!elInfo && data) {
elInfo = subElement(xmldoc.getroot(), 'info')
} else if (!elInfo && !data) {
return {};
}
let el = elInfo.find("constraints");
// No constraints
if (!el && data) {
el = subElement(elInfo, 'constraints');
} else if (!el && !data) {
return {};
}
const result = {};
if (data) {
Object.keys(data).forEach((on) => {
if (on !== 'questions' && on !== 'responses' && on !== 'controls') {
return;
}
let node = el.find("constraint[@on='" + on + "']");
if (!node) {
node = subElement(el, "constraint");
node.set("on", on);
}
Object.keys(data[on]).forEach((attName) => {
const value = data[on][attName].toString();
node.set(attName, value);
});
});
}
el.iter('constraint', (constraint) => {
const on = constraint.get("on");
const value = {};
constraint.keys().forEach((attName) => {
if (attName === 'on') {
return;
}
let v = constraint.get(attName);
if (attName === 'min' || attName === 'max') {
if (v !== '*') {
v = parseInt(v, 10);
}
} else {
v = v !== undefined && (v !== 'false' && v !== '0' );
}
value[attName] = v;
});
result[on] = value;
});
return result;
};
/**
* Get or set the constraint (ADC Only)
*
* // Get the constraint 'single' on questions
* adxInfo.constraint('questions', 'single');
*
* // Set the constraint 'single' on questions
* adxInfo.constraint('questions', 'single', true);
*
* @name Configurator.Info#constraint
* @function
* @param {String} where Which constraint to target
* @param {String} attName Name of the constraint attribute to get or set
* @param {Boolean|Number} [attValue] Value of the attribute to set
* @return {Boolean|Number} Value of the attribute
*/
ADXInfo.prototype.constraint = function constraint(where, attName, attValue) {
const xmldoc = this.configurator.xmldoc;
let el = xmldoc.find("info/constraints/constraint[@on='" + where + "']");
let result;
if (attValue !== undefined) {
if (!el) {
const parent = xmldoc.find('info/constraints');
if (!parent) {
throw new Error("Unable to find the `constraints` node ");
}
el = subElement(parent, 'constraint');
el.set("on", where);
}
el.set(attName, attValue.toString());
}
if (!el) {
return (attName === 'min' || attName === 'max') ? Infinity : false;
}
result = el.get(attName);
// Some properties are treat as number instead of boolean
if (attName === 'min' || attName === 'max') {
if (result === '*') {
return Infinity;
}
return parseInt(result, 10);
}
if (result === undefined) {
return false;
}
return (result !== "false" && result !== "0");
};
/**
* Return the info as xml string
*
* // Serialize the info to XML
* adxInfo.toXml();
* // -> <info><name>MyADC</name><guid>the-guid</guid>....</info>
*
* @name Configurator.Info#toXml
* @function
* @return {String}
*/
ADXInfo.prototype.toXml = function toXml() {
const xml = [];
const self = this;
const projectType = this.configurator.projectType;
const projectVersion = this.configurator.projectVersion;
const constraintsKeys = ['questions', 'controls', 'responses'];
let style;
let constraints;
xml.push(' <info>');
["name", "guid", "version", "date", "description", "company", "author", "site",
"helpURL"].forEach((methodName) => {
let data = self[methodName]();
if (methodName === 'description' || methodName === 'author') {
data = '<![CDATA[' + data + ']]>';
}
xml.push(' <' + methodName + '>' + data + '</' + methodName + '>');
});
// ADC Only
if (projectType === 'adc') {
xml.push(' <categories>');
self.categories().forEach((cat) => {
xml.push(' <category>' + cat + '</category>');
});
xml.push(' </categories>');
if (projectVersion === '2.0.0') {
style = self.style();
xml.push(' <style width="' + style.width + '" height="' + style.height + '" />' );
}
constraints = self.constraints();
xml.push(' <constraints>');
constraintsKeys.forEach((on) => {
if (!constraints[on]) {
return;
}
let str = ' <constraint on="' + on + '"';
let constraint = constraints[on];
for(let key in constraint) {
if (constraint.hasOwnProperty(key)) {
str += ' ' + key + '="' + constraint[key].toString() + '"';
}
}
str += ' />';
xml.push(str);
});
xml.push(' </constraints>');
}
xml.push(' </info>');
return xml.join('\n');
};
/**
* Provide an object to manipulate the outputs of the ADC (config.xml > outputs)
*
* const ADX = require('adxutil').ADX;
*
* const myAdx = new ADC('path/to/adx/');
* myAdx.load(function (err) {
* if (err) {
* throw err;
* }
*
* // Get the instance of the Outputs
* const outputs = myAdx.configurator.outputs;
*
* console.log(outputs.get());
*
* });
*
* @class Configurator.Outputs
* @param {ADX.Configurator} configurator Instance of the configurator
*/
function ADXOutputs(configurator) {
/**
* Parent configurator
*
* @name Configurator.Outputs#configurator
* @type {Configurator}
*/
this.configurator = configurator;
}
/**
* Creates a new instance of ADX Outputs
*
* @ignore
*/
ADXOutputs.prototype.constructor = ADXOutputs;
/**
* Get or set the default ADX output
*
* // Get the id default output
* adxOutputs.defaultOutput();
*
* // Set the default output id
* adxOutputs.defaultOutput("without_javascript");
*
* @name Configurator.Outputs#defaultOutput
* @function
* @param {String} [data] Id of the default output to set
* @returns {String} Id of the default output
*/
ADXOutputs.prototype.defaultOutput = function defaultOutput(data) {
const xmldoc = this.configurator.xmldoc;
let el = xmldoc.find("outputs");
if (!el) {
el = subElement(xmldoc.getroot(), 'outputs');
}
if (data && typeof data === 'string') {
el.set('defaultOutput', data);
}
return el.get('defaultOutput');
};
/**
* Get outputs as an object
*
* // Get the outputs object
* adxOutputs.get();
* // {
* // defaultOutput : "main",
* // outputs : [{
* // id : 'main',
* // description : "Description of the output"
* // condition : "Condition of the output",
* // contents : [{
* // fileName : "default.html",
* // type : "html",
* // mode : "dynamic",
* // position : "placeholder"
* // }]
* // }]
*
* @name Configurator.Outputs#get
* @function
* @returns {Object}
*/
ADXOutputs.prototype.get = function get() {
const xmldoc = this.configurator.xmldoc;
const projectType = this.configurator.projectType;
const el = xmldoc.find("outputs");
const outputs = [];
if (!el) {
return null;
}
el.iter('output', (output) => {
// Output element
const item = {
id : output.get("id")
};
const descEl = output.find("description");
if (descEl) {
item.description = descEl.text;
}
const conditionEl = output.find("condition");
if (conditionEl) {
item.condition = conditionEl.text;
}
// ADC Only
if (projectType === 'adc') {
const defaultGeneration = output.get("defaultGeneration");
if (defaultGeneration) {
item.defaultGeneration = (defaultGeneration === "1" || defaultGeneration === "true");
}
const maxIter = output.get("maxIterations");
if (maxIter) {
item.maxIterations = (maxIter === "*") ? "*" : parseInt(maxIter, 10);
}
}
// ADP Only
else if (projectType === 'adp') {
const masterPage = output.get("masterPage");
if (masterPage) {
item.masterPage = masterPage;
}
}
// Contents
const contents = [];
output.iter('content', (content) => {
const itemContent = {};
const fileName = content.get('fileName');
if (fileName) {
itemContent.fileName = fileName;
}
const type = content.get('type');
if (type) {
itemContent.type = type;
}
const mode = content.get('mode');
if (mode) {
itemContent.mode = mode;
}
const position = content.get('position');
if (position) {
itemContent.position = position;
}
// ADC Only
if (projectType === 'adp' && itemContent.position === 'placeholder') {
return;
}
// Attributes
const attributes = [];
content.iter('attribute', (attribute) => {
const itemAttr = {};
itemAttr.name = attribute.get("name");
const value = attribute.find("value");
if (value) {
itemAttr.value = value.text;
}
attributes.push(itemAttr);
});
if (attributes.length) {
itemContent.attributes = attributes;
}
// Yield
const yieldNode = content.find('yield');
if (yieldNode) {
itemContent.yieldValue = yieldNode.text;
}
contents.push(itemContent);
});
if (contents.length) {
item.contents = contents;
}
outputs.push(item);
});
return {
defaultOutput : this.defaultOutput(),
outputs : outputs
};
};
/**
* Set the outputs using a plain object
*
* // Get the outputs object
* adxOutputs.set({
* defaultOutput : "main",
* outputs : [
* {
* id : "main",
* description : "Main output",
* contents : [
* {
* fileName : 'main.css',
* type : 'css',
* mode : 'static',
* position : 'head'
* },
* {
* fileName : 'main.html',
* type : 'html',
* mode : 'dynamic',
* position : 'placeholder'
* },
* {
* fileName : 'main.js',
* type : 'javascript',
* mode : 'static',
* position: 'foot'
* }
* ]
* },
* {
* id : "second",
* description : "Second output",
* condition : "Browser.Support(\"javascript\")",
* contents : [
* {
* fileName : 'second.css',
* type : 'css',
* mode : 'static',
* position : 'head'
* },
* {
* fileName : 'second.html',
* type : 'html',
* mode : 'dynamic',
* position : 'placeholder'
* },
* {
* fileName : 'second.js',
* type : 'javascript',
* mode : 'static',
* position : 'foot'
* }
* ]
* },
* {
* id : "third",
* description : "Third output",
* maxIterations : 12,
* defaultGeneration : false,
* contents : [
* {
* fileName : "third.css",
* type : "css",
* mode : "static",
* position : "head",
* attributes : [
* {
* name : "rel",
* value : "alternate"
* },
* {
* name : "media",
* value : "print"
* }
* ]
* },
* {
* fileName : 'HTML5Shim.js',
* type : 'javascript',
* mode : 'static',
* position : 'head',
* yieldValue : '<!--[if lte IE 9]><script type="text/javascript" src="{%= CurrentADC.URLTo("static/HTML5Shim.js") %}" ></script><![endif]-->'
* }
* ]
* }
*
* });
*
* @name Configurator.Outputs#set
* @function
* @param {Object} data Data to set
* @param {String} [data.defaultOutput] Id of the default output
* @param {Object[]} [data.outputs] Outputs
* @param {String} [data.outputs.id] Id of the output
* @param {String} [data.outputs.masterPage] Master page to use for the ADP output
* @param {String} [data.outputs.description] Description of the output
* @param {String} [data.outputs.condition] AskiaScript condition to use the output
* @param {Object[]} [data.outputs.contents] List of contents (files) used by the output
* @param {String} [data.outputs.contents.fileName] Name of the file
* @param {String|"text"|"html"|"css"|"javascript"|"binary"|"image"|"audio"|"video"|"flash"} [data.outputs.contents.type] Name of the file
* @param {String|"dynamic"|"static"|"share"} [data.outputs.contents.mode] Extract mode
* @param {String|"none"|"head"|"placeholder"|"foot"} [data.outputs.contents.position] Position in the final page document
* @param {Object[]} [data.outputs.contents.attributes] List of HTML attributes
* @param {String} [data.outputs.contents.attributes.name] Name of the HTML attribute
* @param {String} [data.outputs.contents.attributes.value] Value of the HTML attribute
* @param {String} [data.outputs.contents.yieldValue] Yield value, used to override the auto-generation
*/
ADXOutputs.prototype.set = function set(data) {
const xmldoc = this.configurator.xmldoc;
let el = xmldoc.find("outputs");
const projectType = this.configurator.projectType;
if (!data) {
return;
}
if (!el) {
el = subElement(xmldoc.getroot(), 'outputs');
}
if (data.defaultOutput) {
el.set("defaultOutput", data.defaultOutput);
}
if (!data.outputs || !Array.isArray(data.outputs)) {
return;
}
el.delSlice(0, el.len());
data.outputs.forEach((output) => {
const item = subElement(el, 'output');
// All output xml attributes
item.set("id", output.id || "");
// ADC Only
if (projectType === 'adc') {
if (typeof output.defaultGeneration === 'boolean') {
item.set("defaultGeneration", output.defaultGeneration.toString());
}
if (output.maxIterations) {
item.set("maxIterations", output.maxIterations);
}
}
// ADP Only
else if (projectType === 'adp') {
if (output.masterPage) {
item.set("masterPage", output.masterPage);
}
}
// All output sub-nodes
if (output.description) {
var desc = subElement(item, 'description');
desc.text = output.description;
}
if (output.condition) {
var cond = subElement(item, 'condition');
cond.text = output.condition;
}
if (!output.contents || !Array.isArray(output.contents)) {
return;
}
output.contents.forEach((content) => {
// ADP don't use content with "placeholder" position
if (projectType === 'adp' && content.position === 'placeholder') {
return;
}
const itemContent = subElement(item, 'content');
itemContent.set("fileName", content.fileName || "");
itemContent.set("type", content.type || "");
itemContent.set("mode", content.mode || "");
itemContent.set("position", content.position || "");
if (content.attributes && Array.isArray(content.attributes)) {
content.attributes.forEach((attribute) => {
const itemAttr = subElement(itemContent, 'attribute');
itemAttr.set('name', attribute.name || "");
if (typeof attribute.value === 'string') {
const itemAttrVal = subElement(itemAttr, 'value');
itemAttrVal.text = attribute.value;
}
});
}
if (content.yieldValue) {
const itemYield = subElement(itemContent, 'yield');
itemYield.text = content.yieldValue;
}
});
});
};
/**
* Return the outputs as xml string
*
* // Serialize the outputs to XML
* adxOutputs.toXml();
* // -> <outputs defaultOutput="main"><output id="main"> ...</outputs>
*
* @name Configurator.Outputs#toXml
* @function
* @return {String}
*/
ADXOutputs.prototype.toXml = function toXml() {
const xml = [];
const data = this.get();
const projectType = this.configurator.projectType;
if (!data) {
return '';
}
xml.push(' <outputs defaultOutput="' + data .defaultOutput + '">');
if (Array.isArray(data.outputs)) {
data.outputs.forEach((output) => {
let outputAttr = '';
// ADC Only
if (projectType === 'adc') {
if (typeof output.defaultGeneration === 'boolean') {
outputAttr += ' defaultGeneration="' + output.defaultGeneration.toString() + '"';
}
if (output.maxIterations) {
outputAttr += ' maxIterations="' + output.maxIterations + '"';
}
}
// ADP Only
else if (projectType ==='adp') {
if (output.masterPage) {
outputAttr += ' masterPage="' + output.masterPage + '"';
}
}
xml.push(' <output id="' + output.id + '"' + outputAttr + '>');
if (output.description) {
xml.push(' <description><![CDATA[' + output.description + ']]></description>');
}
if (output.condition) {
xml.push(' <condition><![CDATA[' + output.condition + ']]></condition>');
}
if (Array.isArray(output.contents)) {
output.contents.forEach((content) => {
// ADC Only
if (projectType === 'adp' && content.position === 'placeholder'){
return;
}
const xmlContent = [];
xmlContent.push(' <content');
xmlContent.push(' fileName="', content.fileName || "", '"');
xmlContent.push(' type="', content.type || "", '"');
xmlContent.push(' mode="', content.mode || "", '"');
xmlContent.push(' position="', content.position || "", '"');
if (!content.attributes && !content.yieldValue) {
xmlContent.push(' />');
} else {
xmlContent.push('>');
if (Array.isArray(content.attributes)) {
content.attributes.forEach((attr) => {
xmlContent.push('\n <attribute name="' + attr.name + '">');
if (attr.value) {
xmlContent.push('\n <value><![CDATA[' + (attr.value || "") + ']]></value>');
}
xmlContent.push('\n </attribute>');
});
}
if (content.yieldValue) {
xmlContent.push('\n <yield><![CDATA[' + content.yieldValue + ']]></yield>');
}
xmlContent.push('\n </content>');
}
xml.push(xmlContent.join(''));
});
}
xml.push(' </output>');
});
}
xml.push(' </outputs>');
return xml.join('\n');
};
/**
* Provide an object to manipulate the propertues of the ADC (config.xml > properties)
*
* const ADX = require('adxutil').ADX;
*
* const myAdx = new ADX('path/to/adc/');
* myAdx.load(function (err) {
* if (err) {
* throw err;
* }
*
* // Get the instance of the Properties
* const properties = myAdx.configurator.properties;
*
* console.log(properties.get());
*
* });
*
* @class Configurator.Properties
* @param {ADX.Configurator} configurator Instance of the configurator
*/
function ADXProperties(configurator