- /
/**
- Cross-browser implementation of ECMAScript String.prototype.split function.
-
- @param {String} str input string to split
- @param separator separator to split on, as RegExp or String
- @param {Number} limit limit on number of splits. If the parameter is absent, no limit is imposed.
- @return {Array} array resulting from split
- /
split: function (str, separator, limit)
{
// if `separator` is not a regex, use the native `split`
if (Object.prototype.toString.call(separator) !== "[object RegExp]") {
return proveit.split._nativeSplit.call(str, separator, limit);
}  
var output = [],
lastLastIndex = 0,
flags = (separator.ignoreCase ? "i" : "") +
(separator.multiline ? "m" : "") +
(separator.sticky ? "y" : ""),
separator = RegExp(separator.source, flags + "g"), // make `global` and avoid `lastIndex` issues by working with a copy
separator2, match, lastIndex, lastLength;  
str = str + ""; // type conversion
if (!proveit.split._compliantExecNpcg) {
separator2 = RegExp("^" + separator.source + "$(?!\\s)", flags); // doesn't need /g or /y, but they don't hurt
}  
/* behavior for `limit`: if it's...
- `undefined`: no limit.
- `NaN` or zero: return an empty array.
- a positive number: use `Math.floor(limit)`.
- a negative number: no limit.
- other: type-convert, then use the above rules. */
if (limit === undefined || +limit < 0) {
limit = Infinity;
} else {
limit = Math.floor(+limit);
if (!limit) {
return [];
}
}  
while (match = separator.exec(str)) {
lastIndex = match.index + match[0].length; // `separator.lastIndex` is not reliable cross-browser  
if (lastIndex > lastLastIndex) {
output.push(str.slice(lastLastIndex, match.index));  
// fix browsers whose `exec` methods don't consistently return `undefined` for nonparticipating capturing groups
if (!proveit.split._compliantExecNpcg && match.length > 1) {
match[0].replace(separator2, function {
for (var i = 1; i < arguments.length - 2; i++) {
if (arguments[i] === undefined) {
match[i] = undefined;
}
}
});
}  
if (match.length > 1 && match.index < str.length) {
Array.prototype.push.apply(output, match.slice(1));
}  
lastLength = match[0].length;
lastLastIndex = lastIndex;  
if (output.length >= limit) {
break;
}
}  
if (separator.lastIndex === match.index) {
separator.lastIndex++; // avoid an infinite loop
}
}  
if (lastLastIndex === str.length) {
if (lastLength || !separator.test("")) {
output.push("");
}
} else {
output.push(str.slice(lastLastIndex));
}  
return output.length > limit ? output.slice(0, limit) : output;
},  
// TODO: Remove the split code, and just use a regular regex (with two main groups for name and val), iteratively. Regex.find? Make name and val indices match, and rework calling code as needed. Also, check how this was done in the original code.
/**
- Overly clever regex to parse template string (e.g. |last=Smith|first=John|title=My Life Story) into name and value pairs.
-
- names is an array of all names, and values is an array of all values. They have equal lengths.
-
- @param {String} workingString template string to parse.
- @return {Object} object with two properties, names and values.
- /
splitNameVals: function (workingString)
{
var split = {};
// The first component is "ordinary" text (no pipes), while the second is a correctly balanced wikilink, with optional pipe. Any combination of the two can appear.
split.names = proveit.split(workingString.substring(workingString.indexOf("|") + 1), /=(?:[^|]*?(?:\[\\*(?:\|(?:[^|\]]*))?\]\])?)+(?:\||\}\})/);
split.names.length--; // Remove single empty element at end  
split.values = proveit.split(workingString.substring(workingString.indexOf("=") + 1, workingString.indexOf("}}")), /\|[^|=]*=/);
return split;
},  
/**
- Scan for references in the MWEditBox, and create a reference object and refBoxRow for each.
- /
scanForRefs: function
{
// these are strings used to allow the correct parsing of the ref
var workingstring;
var cutupstring;  
this.clearRefBox;  
var textValue = this.getMWEditValue;
// since we should pick the name out before we get to the reference type, here's a variable to hold it
var name;  
// key - name
// value -
// object - key - "reference", value - reference obj . Avoids repeating same object in references array.
// key - "strings", value - array of orig strings
var citations = {};  
// Array of reference objects. At end of function, addNewElement called on each.
var references = [];
// allRefs should count opening refs, but not ref citation (not
var typeEnd = rightCurly != -1 ? rightCurly : beforeBar.length;
// grab the type, then trim it.
var type = $.trim(beforeBar.substring(typeStart + 1, typeEnd));
}
}
// type may be undefined, but that's okay.
var citation = new citeFunction({"name": name, "type": type, "save": true, "inMWEditBox": true, "orig": refText});  
if(citeFunction != this.RawReference)
{
var split = this.splitNameVals(workingstring);
var names = split.names;
var values = split.values;  
for (var j = 0; j < names.length; j++)
{
/* Drop blank space, and |'s without params, which are never correct for
citation templates.*/
var paramName = $.trim(names[j]).replace(/(?:\s*\|)*(.*)/, "$1");
var paramVal = $.trim(values[j]);
// Should there be a setParam function? It could handle empty values, and even drop (siliently or otherwise) invalid parameters. Alternatively, should params be passed in the constructor?
if (paramVal != "")
{
citation.params[paramName] = paramVal;
}
}
}
return citation;
},  
// TODO: Use https://gerrit.wikimedia.org/r/#/c/67166/ when merged.
/**
- Date-formatting class
-
- @param {String} format one of the format strings supported by MW core.
- Currently, the supported values are 'default', 'mdy', 'dmy', 'ymd', and 'ISO 8601'.
- If an unknown value is passed, it will use wgDefaultDateFormat.
- /
DateFormatter: function (format) {
// 1-indexed (due to filler item at 0), in page content language.
var monthNames = mw.config.get('wgMonthNames');  
var supportedFormats = ['mdy', 'dmy', 'ymd', 'ISO 8601'];  
if($.inArray(format, supportedFormats) === -1) {
format = mw.config.get('wgDefaultDateFormat');
}  
function getMonthName(date) {
return monthNames[date.getMonth + 1];;
}  
this.formatAsMdy = function(date) {
return getMonthName(date) + ' ' + date.getDate + ', ' + date.getFullYear;
};  
this.formatAsDmy = function(date) {
return date.getDate + ' ' + getMonthName(date) + ' ' + date.getFullYear;
};  
this.formatAsYmd = function(date) {
return date.getFullYear + ' ' + getMonthName(date) + ' ' + date.getDate;
};  
this.formatAsISO8601 = function(date) {
return date.getFullYear + '-' +
(date.getMonth < 9 ? '0' : '') + (date.getMonth + 1) + '-' +
(date.getDate < 10 ? '0' : '') + date.getDate;
};  
var formatters =
{
mdy: this.formatAsMdy,
dmy: this.formatAsDmy,
ymd: this.formatAsYmd,
'ISO 8601': this.formatAsISO8601
};  
/**
- Formats date according to stored preference
-
- @param {Date} date date to format
-
- @return {String} formatted date as String
- /
this.format = function(date)
{
return formatters[format](date);
};
},  
/**
- Root reference type. Parent of RawReference, CiteReference, and CitationReference.
- @class AbstractReference
- @for proveit
- @constructor
- @param {Object} argObj argument object with keys for each option
- /
AbstractReference: function(argObj)
{
// CiteReference has a non-trivial override of this. This is defined early (and conditionally) because it is used in the constructor.
if(!this.setType)
{
/**
- @param {String} type type of reference
- /
this.setType = function(type)
{
this.type = type;
};
}  
/**
- Update citation strings after changing reference. This runs after modifying a reference's fields (name, params), but before changing orig
- /
this.update = function
{
var newCiteText = this.toString;
var strings = this.getCitationStrings;  
/*
- Update main citation in strings list.
-
- TODO:
- Use strings array here to find and update citations that are not main references. As is, they are orphaned.
- Both array and textbox should be updated.