File: ia\utils\TextSubstitution.js
/**
* The <code>TextSubstitution</code> class contains functions for
* variable subtitution in strings.
*
* <p>All variables are indicated by the name of the variable. This class registers these names
* with variable values, allowing the lookup of variable values
* by the variable name. The set of available variables can be extended by using
* the static <code>setVariable</code> method to register a new variable name and
* value.</p>
*
* @author J Clare
* @class ia.TextSubstitution
* @constructor
*/
ia.TextSubstitution = function()
{
// For removing whitespace when formatting text.
this._TAB = 9;
this._LINEFEED = 10;
this._CARRIAGE = 13;
this._SPACE = 32;
// An object that holds the values for substitution variables.
// An example might be: {themeName:"Theme 1"; indicatorName:"Indicator 1"}.
this._substitutionVariables = new Object();
};
/**
* Sets the value for the given substitution variable.
*
* @method setVariable
* @param {String} name The name of the variable
* @param {String} value The value of the variable.
*/
ia.TextSubstitution.prototype.setVariable = function(name, value)
{
this._substitutionVariables[name] = value;
};
/**
* Clears the variables.
*
* @method clearVariables
*/
ia.TextSubstitution.prototype.clearVariables = function(n)
{
this._substitutionVariables = new Object();
};
/**
* Replaces all instances of a variable name in a string with the 'real' values.
*
* @method formatMessage
* @param {String} msg The input string which may contain variable names, for example: 'Current theme is: ${themeName1}'.
* @return {String} The input string with all variables substituted out.
*/
ia.TextSubstitution.prototype.formatMessage = function(msg)
{
var s = msg + '';
// Iterate through the variable strings.
while (s.indexOf('${') >= 0)
{
// Get the variable string
var start = s.indexOf('${');
var end = s.indexOf('}', start);
var variableString = s.substring(start, end + 1);
// Look for and / or statements.
if ((variableString.indexOf('&&') !== -1
|| variableString.indexOf('||') !== -1)
&& variableString.indexOf('==') !== -1)
{
var arr = variableString.split("==");
var trueFalseValues = this._trim(arr[1]);
trueFalseValues = trueFalseValues.substring(trueFalseValues.indexOf("(") + 1, trueFalseValues.indexOf(')'))
var andOrString = this._trim(arr[0]);
var variableArray = andOrString.split(/&&|\|\|/);
for (var i = 0; i < variableArray.length; i++)
{
var nestedVariableString = variableArray[i];
var replacementString;
if (i === 0)
replacementString = this._processVariable(this._trim(nestedVariableString) + '}');
else
replacementString = this._processVariable('${' + this._trim(nestedVariableString) + '}');
andOrString = this._replace(andOrString, nestedVariableString, replacementString);
}
// The replacement string.
var replacementString = this._processVariable("${andOrTest('" + andOrString + "'," + trueFalseValues + ")}");
// Substitute.
s = this._replace(s, variableString, replacementString);
}
else
{
// The replacement string.
var replacementString = this._processVariable(variableString);
// Substitute.
s = this._replace(s, variableString, replacementString);
}
}
return s;
};
/**
* Processes a conditional statement.
*
* @method _processVariable
* @param {String} variableString The variable string.
* @private
*/
ia.TextSubstitution.prototype._processVariable = function(variableString)
{
// The test to carry out.
var test = variableString.substring(variableString.indexOf("{") + 1, variableString.indexOf('('));
// Nested Themes
if (test.indexOf('themeName') !== -1)
{
// Theme names as stored as arrays of nested theme names.
var themeNames = this._substitutionVariables[test];
var replacementString = "";
if (themeNames !== undefined)
{
if (themeNames.length === 1) // Just one theme.
{
replacementString = themeNames[0];
}
else // Its a nested theme
{
var separator = variableString.substring(variableString.indexOf("'") + 1, variableString.lastIndexOf("'"));
var index = 0;
for (var i = 0; i < themeNames.length; i++)
{
var t = themeNames[i];
if (index === 0) replacementString = t;
else replacementString = replacementString + separator + t;
index++;
}
}
}
return replacementString;
}
// Its a simple variable with no conditional statement.
if (test === "${")
{
var variableName = variableString.substring(2, variableString.length - 1);
return this._formatValue(variableName);
}
// Trim the variable string and create an array.
variableString = this._trim(variableString.substring(variableString.indexOf('(') + 1, variableString.lastIndexOf(')}')));
var variableArray = variableString.split(',');
test = test.toLowerCase();
var testValue; // The value being tested.
var comparisonValue; // The value to test against.
var trueValue; // The value to substitute if test returns true.
var falseValue; // The value to substitute if test returns false.
// And / Or test.
if (test === "andortest")
{
testValue = this._formatValue(variableArray[0]);
trueValue = this._formatValue(variableArray[1]);
falseValue = this._formatValue(variableArray[2]);
try
{
var test = eval(testValue);
if (test === true) return trueValue;
else if (test === false) return falseValue;
else return "";
}
catch (e)
{
return "";
}
}
// Conditional.
else if (test === "empty")
{
testValue = this._formatValue(variableArray[0]);
trueValue = this._formatValue(variableArray[1]);
falseValue = this._formatValue(variableArray[2]);
if (variableArray.length < 2) // true / false test with no output value.
{
trueValue = true;
falseValue = false;
}
if ((testValue === undefined) || (testValue.toString() === '')) return trueValue;
else if (falseValue !== undefined) return falseValue;
}
else if (test === "notempty")
{
testValue = this._formatValue(variableArray[0]);
trueValue = this._formatValue(variableArray[1]);
falseValue = this._formatValue(variableArray[2]);
if (variableArray.length < 2) // true / false test with no output value.
{
trueValue = true;
falseValue = false;
}
if ((testValue !== undefined) && (testValue.toString() !== '')) return trueValue;
else if (falseValue !== undefined) return falseValue;
}
else if (test === "equals")
{
testValue = this._formatValue(variableArray[0]);
comparisonValue = this._formatValue(variableArray[1]);
trueValue = this._formatValue(variableArray[2]);
falseValue = this._formatValue(variableArray[3]);
if (variableArray.length < 3) // true / false test with no output value.
{
trueValue = true;
falseValue = false;
}
if (testValue === comparisonValue) return trueValue;
else if (falseValue !== undefined) return falseValue;
}
else if (test === "notequals")
{
testValue = this._formatValue(variableArray[0]);
comparisonValue = this._formatValue(variableArray[1]);
trueValue = this._formatValue(variableArray[2]);
falseValue = this._formatValue(variableArray[3]);
if (variableArray.length < 3) // true / false test with no output value.
{
trueValue = true;
falseValue = false;
}
if (testValue !== comparisonValue) return trueValue;
else if (falseValue !== undefined) return falseValue;
}
else if (test === "greaterthan")
{
testValue = this._formatValue(variableArray[0]);
comparisonValue = this._formatValue(variableArray[1]);
trueValue = this._formatValue(variableArray[2]);
falseValue = this._formatValue(variableArray[3]);
if (variableArray.length < 3) // true / false test with no output value.
{
trueValue = true;
falseValue = false;
}
if (testValue > comparisonValue) return trueValue;
else if (falseValue !== undefined) return falseValue;
}
else if (test === "greaterthanorequalto")
{
testValue = this._formatValue(variableArray[0]);
comparisonValue = this._formatValue(variableArray[1]);
trueValue = this._formatValue(variableArray[2]);
falseValue = this._formatValue(variableArray[3]);
if (variableArray.length < 3) // true / false test with no output value.
{
trueValue = true;
falseValue = false;
}
if (testValue >= comparisonValue) return trueValue;
else if (falseValue !== undefined) return falseValue;
}
else if (test === "lessthan")
{
testValue = this._formatValue(variableArray[0]);
comparisonValue = this._formatValue(variableArray[1]);
trueValue = this._formatValue(variableArray[2]);
falseValue = this._formatValue(variableArray[3]);
if (variableArray.length < 3) // true / false test with no output value.
{
trueValue = true;
falseValue = false;
}
if (testValue < comparisonValue) return trueValue;
else if (falseValue !== undefined) return falseValue;
}
else if (test === "lessthanorequalto")
{
testValue = this._formatValue(variableArray[0]);
comparisonValue = this._formatValue(variableArray[1]);
trueValue = this._formatValue(variableArray[2]);
falseValue = this._formatValue(variableArray[3]);
if (variableArray.length < 3) // true / false test with no output value.
{
trueValue = true;
falseValue = false;
}
if (testValue <= comparisonValue) return trueValue;
else if (falseValue !== undefined) return falseValue;
}
else if (test === "even")
{
testValue = this._formatValue(variableArray[0]);
trueValue = this._formatValue(variableArray[1]);
falseValue = this._formatValue(variableArray[2]);
if (variableArray.length < 2) // true / false test with no output value.
{
trueValue = true;
falseValue = false;
}
if (testValue%2 === 0)
return trueValue;
else
return falseValue;
}
else if (test === "odd")
{
testValue = this._formatValue(variableArray[0]);
trueValue = this._formatValue(variableArray[1]);
falseValue = this._formatValue(variableArray[2]);
if (variableArray.length < 2) // true / false test with no output value.
{
trueValue = true;
falseValue = false;
}
if (testValue%2 === 0)
return falseValue;
else
return trueValue;
}
// Math.
else if (test === "multiply")
{
testValue = this._formatValue(variableArray[0]);
comparisonValue = this._formatValue(variableArray[1]);
return (testValue * comparisonValue);
}
else if (test === "divide")
{
testValue = this._formatValue(variableArray[0]);
comparisonValue = this._formatValue(variableArray[1]);
return (testValue / comparisonValue);
}
else if (test === "add")
{
testValue = this._formatValue(variableArray[0]);
comparisonValue = this._formatValue(variableArray[1]);
return (testValue + comparisonValue);
}
else if (test === "subtract")
{
testValue = this._formatValue(variableArray[0]);
comparisonValue = this._formatValue(variableArray[1]);
return (testValue - comparisonValue);
}
else if (test === "min")
{
testValue = this._formatValue(variableArray[0]);
comparisonValue = this._formatValue(variableArray[1]);
return Math.min(testValue, comparisonValue);
}
else if (test === "max")
{
testValue = this._formatValue(variableArray[0]);
comparisonValue = this._formatValue(variableArray[1]);
return Math.max(testValue, comparisonValue);
}
else if (test === "pow")
{
testValue = this._formatValue(variableArray[0]);
comparisonValue = this._formatValue(variableArray[1]);
return Math.pow(testValue, comparisonValue);
}
else if (test === "sqrt")
{
testValue = this._formatValue(variableArray[0]);
return Math.sqrt(testValue);
}
else if (test === "exp")
{
testValue = this._formatValue(variableArray[0]);
return Math.exp(testValue);
}
else if (test === "log")
{
testValue = this._formatValue(variableArray[0]);
return Math.log(testValue);
}
else if (test === "abs")
{
testValue = this._formatValue(variableArray[0]);
return Math.abs(testValue);
}
return "";
};
/**
* Extracts a variable / string / number from a conditional statement.
*
* @method _formatValue
* @param {String} variableString The conditional statement.
* @param {Number} index The position of the variable in the statement.
* @return {String} The variable or an empty string.
* @private
*/
ia.TextSubstitution.prototype._formatValue = function(value)
{
if (value !== undefined)
{
var inValue = this._trim(value);
var outValue = "";
// Check if its a string array, variable, string or number.
// Its a string array variable.
if (inValue.indexOf("[") !== -1 && inValue.indexOf("]") !== -1)
{
var variableName = inValue.substring(0, inValue.indexOf("["));
var index = inValue.substring(inValue.indexOf("[")+1, inValue.indexOf("]"));
var arr = this._substitutionVariables[variableName];
if (arr.indexOf(";") !== -1) outValue = arr.split(";")[index];
else if (arr.indexOf(",") !== -1) outValue = arr.split(",")[index];
}
// Its a number so parse it.
else if (ia.isNumber(inValue)) outValue = parseFloat(inValue);
// Its a string so remove quotes.
else if ((inValue.charAt(0) === '"') || (inValue.charAt(0) === '\'')) outValue = inValue.substring(1, inValue.length - 1);
// Its a variable so get its substitution value.
else outValue = this._substitutionVariables[inValue];
if (outValue === undefined) return "";
else return outValue;
}
return ""; // Empty.
};
/**
* Removes whitespace from a string.
*
* @method _trim
* @param {String} s The string.
* @return {String} The input string with whitespace removed.
* @private
*/
ia.TextSubstitution.prototype._trim = function(s)
{
return this._leftTrim(this._rightTrim(s));
};
/**
* Removes whitespace from the right side of a string.
*
* @method _rightTrim
* @param {String} s The string.
* @return {String} The input string with whitespace removed.
* @private
*/
ia.TextSubstitution.prototype._rightTrim = function(s)
{
var i = s.length - 1;
while(s.charCodeAt(i) === this._SPACE
|| s.charCodeAt(i) === this._CARRIAGE
|| s.charCodeAt(i) === this._LINEFEED
|| s.charCodeAt(i) === this._TAB)
{
i--;
}
return s.substring(0,i+1);
};
/**
* Removes whitespace from the left side of a string.
*
* @method _leftTrim
* @param {String} s The string.
* @return {String} String with whitespace removed.
* @private
*/
ia.TextSubstitution.prototype._leftTrim = function(s)
{
var i = 0;
while(s.charCodeAt(i) === this._SPACE
|| s.charCodeAt(i) === this._CARRIAGE
|| s.charCodeAt(i) === this._LINEFEED
|| s.charCodeAt(i) === this._TAB)
{
i++;
}
return s.substring(i,s.length);
};
/**
* Replaces a pattern in a string with a new pattern.
* eg. All occurrences of ";" may be replaced with ";".
*
* @method _replace
* @param {String} s The string.
* @param {String} pattern The pattern.
* @param {String} replacement The pattern replacements.
* @return {String} The input string with the pattern replaced.
* @private
*/
ia.TextSubstitution.prototype._replace = function(s, pattern, replacement)
{
return s.split(pattern).join(replacement);
};