mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2024-11-24 10:37:20 +00:00
a1c8ac624f
There are still some whitespace and attribute ordering issues, but the result runs correctly.
299 lines
8.8 KiB
JavaScript
Executable File
299 lines
8.8 KiB
JavaScript
Executable File
//--
|
|
//-- Augmented methods for the JavaScript String() object
|
|
//--
|
|
|
|
// Get characters from the right end of a string
|
|
String.prototype.right = function(n)
|
|
{
|
|
return n < this.length ? this.slice(this.length-n) : this;
|
|
};
|
|
|
|
// Trim whitespace from both ends of a string
|
|
String.prototype.trim = function()
|
|
{
|
|
return this.replace(/^\s*|\s*$/g,"");
|
|
};
|
|
|
|
// Convert a string from a CSS style property name to a JavaScript style name ("background-color" -> "backgroundColor")
|
|
String.prototype.unDash = function()
|
|
{
|
|
var t,s = this.split("-");
|
|
if(s.length > 1) {
|
|
for(t=1; t<s.length; t++)
|
|
s[t] = s[t].substr(0,1).toUpperCase() + s[t].substr(1);
|
|
}
|
|
return s.join("");
|
|
};
|
|
|
|
// Substitute substrings from an array into a format string that includes '%1'-type specifiers
|
|
String.prototype.format = function(s)
|
|
{
|
|
var substrings = s && s.constructor == Array ? s : arguments;
|
|
var subRegExp = /(?:%(\d+))/mg;
|
|
var currPos = 0;
|
|
var match,r = [];
|
|
do {
|
|
match = subRegExp.exec(this);
|
|
if(match && match[1]) {
|
|
if(match.index > currPos)
|
|
r.push(this.substring(currPos,match.index));
|
|
r.push(substrings[parseInt(match[1],10)]);
|
|
currPos = subRegExp.lastIndex;
|
|
}
|
|
} while(match);
|
|
if(currPos < this.length)
|
|
r.push(this.substring(currPos,this.length));
|
|
return r.join("");
|
|
};
|
|
|
|
// Escape any special RegExp characters with that character preceded by a backslash
|
|
String.prototype.escapeRegExp = function()
|
|
{
|
|
var s = "\\^$*+?()=!|,{}[].";
|
|
var t,c = this;
|
|
for(t=0; t<s.length; t++)
|
|
c = c.replace(new RegExp("\\" + s.substr(t,1),"g"),"\\" + s.substr(t,1));
|
|
return c;
|
|
};
|
|
|
|
// Convert "\" to "\s", newlines to "\n" (and remove carriage returns)
|
|
String.prototype.escapeLineBreaks = function()
|
|
{
|
|
return this.replace(/\\/mg,"\\s").replace(/\n/mg,"\\n").replace(/\r/mg,"");
|
|
};
|
|
|
|
// Convert "\n" to newlines, "\b" to " ", "\s" to "\" (and remove carriage returns)
|
|
String.prototype.unescapeLineBreaks = function()
|
|
{
|
|
return this.replace(/\\n/mg,"\n").replace(/\\b/mg," ").replace(/\\s/mg,"\\").replace(/\r/mg,"");
|
|
};
|
|
|
|
// Convert & to "&", < to "<", > to ">" and " to """
|
|
String.prototype.htmlEncode = function()
|
|
{
|
|
return this.replace(/&/mg,"&").replace(/</mg,"<").replace(/>/mg,">").replace(/\"/mg,""");
|
|
};
|
|
|
|
// Convert "&" to &, "<" to <, ">" to > and """ to "
|
|
String.prototype.htmlDecode = function()
|
|
{
|
|
return this.replace(/</mg,"<").replace(/>/mg,">").replace(/"/mg,"\"").replace(/&/mg,"&");
|
|
};
|
|
|
|
// Parse a space-separated string of name:value parameters
|
|
//# where:
|
|
//# - the name or the value can be optional (in which case separate defaults are used instead)
|
|
//# - in case of ambiguity, a lone word is taken to be a value
|
|
//# - if 'cascadeDefaults' is set to true, then the defaults are modified by updated by each specified name or value
|
|
//# - name prefixes are not allowed if the 'noNames' parameter is true
|
|
//# - if both the name and value are present they must be separated by a colon
|
|
//# - the name and the value may both be quoted with single- or double-quotes, double-square brackets
|
|
//# - names or values quoted with {{double-curly braces}} are evaluated as a JavaScript expression
|
|
//# - as long as the 'allowEval' parameter is true
|
|
// The result is an array of objects:
|
|
// result[0] = object with a member for each parameter name, value of that member being an array of values
|
|
// result[1..n] = one object for each parameter, with 'name' and 'value' members
|
|
String.prototype.parseParams = function(defaultName,defaultValue,allowEval,noNames,cascadeDefaults)
|
|
{
|
|
var parseToken = function(match,p) {
|
|
var n;
|
|
if(match[p]) // Double quoted
|
|
n = match[p];
|
|
else if(match[p+1]) // Single quoted
|
|
n = match[p+1];
|
|
else if(match[p+2]) // Double-square-bracket quoted
|
|
n = match[p+2];
|
|
else if(match[p+3]) // Double-brace quoted
|
|
try {
|
|
n = match[p+3];
|
|
if(allowEval && config.evaluateMacroParameters != "none") {
|
|
if(config.evaluateMacroParameters == "restricted") {
|
|
if(window.restrictedEval) {
|
|
n = window.restrictedEval(n);
|
|
}
|
|
} else {
|
|
n = window.eval(n);
|
|
}
|
|
}
|
|
} catch(ex) {
|
|
throw "Unable to evaluate {{" + match[p+3] + "}}: " + exceptionText(ex);
|
|
}
|
|
else if(match[p+4]) // Unquoted
|
|
n = match[p+4];
|
|
else if(match[p+5]) // empty quote
|
|
n = "";
|
|
return n;
|
|
};
|
|
var r = [{}];
|
|
var dblQuote = "(?:\"((?:(?:\\\\\")|[^\"])+)\")";
|
|
var sngQuote = "(?:'((?:(?:\\\\\')|[^'])+)')";
|
|
var dblSquare = "(?:\\[\\[((?:\\s|\\S)*?)\\]\\])";
|
|
var dblBrace = "(?:\\{\\{((?:\\s|\\S)*?)\\}\\})";
|
|
var unQuoted = noNames ? "([^\"'\\s]\\S*)" : "([^\"':\\s][^\\s:]*)";
|
|
var emptyQuote = "((?:\"\")|(?:''))";
|
|
var skipSpace = "(?:\\s*)";
|
|
var token = "(?:" + dblQuote + "|" + sngQuote + "|" + dblSquare + "|" + dblBrace + "|" + unQuoted + "|" + emptyQuote + ")";
|
|
var re = noNames ? new RegExp(token,"mg") : new RegExp(skipSpace + token + skipSpace + "(?:(\\:)" + skipSpace + token + ")?","mg");
|
|
var match;
|
|
do {
|
|
match = re.exec(this);
|
|
if(match) {
|
|
var n = parseToken(match,1);
|
|
if(noNames) {
|
|
r.push({name:"",value:n});
|
|
} else {
|
|
var v = parseToken(match,8);
|
|
if(v == null && defaultName) {
|
|
v = n;
|
|
n = defaultName;
|
|
} else if(v == null && defaultValue) {
|
|
v = defaultValue;
|
|
}
|
|
r.push({name:n,value:v});
|
|
if(cascadeDefaults) {
|
|
defaultName = n;
|
|
defaultValue = v;
|
|
}
|
|
}
|
|
}
|
|
} while(match);
|
|
// Summarise parameters into first element
|
|
var t;
|
|
for(t=1; t<r.length; t++) {
|
|
if(r[0][r[t].name])
|
|
r[0][r[t].name].push(r[t].value);
|
|
else
|
|
r[0][r[t].name] = [r[t].value];
|
|
}
|
|
return r;
|
|
};
|
|
|
|
// Process a string list of macro parameters into an array. Parameters can be quoted with "", '',
|
|
// [[]], {{ }} or left unquoted (and therefore space-separated). Double-braces {{}} results in
|
|
// an *evaluated* parameter: e.g. {{config.options.txtUserName}} results in the current user's name.
|
|
String.prototype.readMacroParams = function(notAllowEval)
|
|
{
|
|
var p = this.parseParams("list",null,!notAllowEval,true);
|
|
var t,n = [];
|
|
for(t=1; t<p.length; t++)
|
|
n.push(p[t].value);
|
|
return n;
|
|
};
|
|
|
|
// Process a string list of unique tiddler names into an array. Tiddler names that have spaces in them must be [[bracketed]]
|
|
String.prototype.readBracketedList = function(unique)
|
|
{
|
|
var p = this.parseParams("list",null,false,true);
|
|
var t,n = [];
|
|
for(t=1; t<p.length; t++) {
|
|
if(p[t].value)
|
|
n.pushUnique(p[t].value,unique);
|
|
}
|
|
return n;
|
|
};
|
|
|
|
// Returns array with start and end index of chunk between given start and end marker, or undefined.
|
|
String.prototype.getChunkRange = function(start,end)
|
|
{
|
|
var s = this.indexOf(start);
|
|
if(s != -1) {
|
|
s += start.length;
|
|
var e = this.indexOf(end,s);
|
|
if(e != -1)
|
|
return [s,e];
|
|
}
|
|
};
|
|
|
|
// Replace a chunk of a string given start and end markers
|
|
String.prototype.replaceChunk = function(start,end,sub)
|
|
{
|
|
var r = this.getChunkRange(start,end);
|
|
return r ? this.substring(0,r[0]) + sub + this.substring(r[1]) : this;
|
|
};
|
|
|
|
// Returns a chunk of a string between start and end markers, or undefined
|
|
String.prototype.getChunk = function(start,end)
|
|
{
|
|
var r = this.getChunkRange(start,end);
|
|
if(r)
|
|
return this.substring(r[0],r[1]);
|
|
};
|
|
|
|
|
|
// Static method to bracket a string with double square brackets if it contains a space
|
|
String.encodeTiddlyLink = function(title)
|
|
{
|
|
return title.indexOf(" ") == -1 ? title : "[[" + title + "]]";
|
|
};
|
|
|
|
// Static method to encodeTiddlyLink for every item in an array and join them with spaces
|
|
String.encodeTiddlyLinkList = function(list)
|
|
{
|
|
if(list) {
|
|
var t,results = [];
|
|
for(t=0; t<list.length; t++)
|
|
results.push(String.encodeTiddlyLink(list[t]));
|
|
return results.join(" ");
|
|
} else {
|
|
return "";
|
|
}
|
|
};
|
|
|
|
// Convert a string as a sequence of name:"value" pairs into a hashmap
|
|
String.prototype.decodeHashMap = function()
|
|
{
|
|
var fields = this.parseParams("anon","",false);
|
|
var t,r = {};
|
|
for(t=1; t<fields.length; t++)
|
|
r[fields[t].name] = fields[t].value;
|
|
return r;
|
|
};
|
|
|
|
// Static method to encode a hashmap into a name:"value"... string
|
|
String.encodeHashMap = function(hashmap)
|
|
{
|
|
var t,r = [];
|
|
for(t in hashmap)
|
|
r.push(t + ':"' + hashmap[t] + '"');
|
|
return r.join(" ");
|
|
};
|
|
|
|
// Static method to left-pad a string with 0s to a certain width
|
|
String.zeroPad = function(n,d)
|
|
{
|
|
var s = n.toString();
|
|
if(s.length < d)
|
|
s = "000000000000000000000000000".substr(0,d-s.length) + s;
|
|
return s;
|
|
};
|
|
|
|
String.prototype.startsWith = function(prefix)
|
|
{
|
|
return !prefix || this.substring(0,prefix.length) == prefix;
|
|
};
|
|
|
|
// Returns the first value of the given named parameter.
|
|
//#
|
|
//# @param params
|
|
//# as returned by parseParams or null/undefined
|
|
//# @return [may be null/undefined]
|
|
//#
|
|
function getParam(params,name,defaultValue)
|
|
{
|
|
if(!params)
|
|
return defaultValue;
|
|
var p = params[0][name];
|
|
return p ? p[0] : defaultValue;
|
|
}
|
|
|
|
// Returns the first value of the given boolean named parameter.
|
|
//#
|
|
//# @param params
|
|
//# as returned by parseParams or null/undefined
|
|
//#
|
|
function getFlag(params,name,defaultValue)
|
|
{
|
|
return !!getParam(params,name,defaultValue);
|
|
}
|
|
|