MediaWiki:Gadget-morebits.js: Difference between revisions
Content added Content deleted
imported>MusikAnimal (v2.0 at 9b61d36: Remove deprecated functions and unneeded shims) |
(Repo at a999149: add documentation; more documentation) |
||
Line 40: | Line 40: | ||
* **************** Morebits.userIsInGroup() **************** |
* **************** Morebits.userIsInGroup() **************** |
||
* Simple helper function to see what groups a user might belong |
* Simple helper function to see what groups a user might belong |
||
* @param {string} group eg. `sysop`, `extendedconfirmed`, etc |
|||
* @returns {boolean} |
|||
*/ |
*/ |
||
Morebits.userIsInGroup = function ( group ) { |
Morebits.userIsInGroup = function ( group ) { |
||
return mw.config.get( 'wgUserGroups' ).indexOf( group ) !== -1; |
return mw.config.get( 'wgUserGroups' ).indexOf( group ) !== -1; |
||
Line 54: | Line 55: | ||
* Converts an IPv6 address to the canonical form stored and used by MediaWiki. |
* Converts an IPv6 address to the canonical form stored and used by MediaWiki. |
||
*/ |
*/ |
||
Morebits.sanitizeIPv6 = function ( address ) { |
Morebits.sanitizeIPv6 = function ( address ) { |
||
address = address.trim(); |
address = address.trim(); |
||
Line 148: | Line 148: | ||
*/ |
*/ |
||
/** |
|||
* @constructor |
|||
* @param {event} event Function to execute when form is submitted |
|||
* @param {*} eventType |
|||
*/ |
|||
Morebits.quickForm = function QuickForm( event, eventType ) { |
Morebits.quickForm = function QuickForm( event, eventType ) { |
||
this.root = new Morebits.quickForm.element( { type: 'form', event: event, eventType:eventType } ); |
this.root = new Morebits.quickForm.element( { type: 'form', event: event, eventType:eventType } ); |
||
}; |
}; |
||
/** |
|||
* Renders the HTML output of the quickForm |
|||
* @returns {HTMLElement} |
|||
*/ |
|||
Morebits.quickForm.prototype.render = function QuickFormRender() { |
Morebits.quickForm.prototype.render = function QuickFormRender() { |
||
var ret = this.root.render(); |
var ret = this.root.render(); |
||
Line 158: | Line 167: | ||
}; |
}; |
||
/** |
|||
* Append element to the form |
|||
* @param {Morebits.quickForm.element} data |
|||
*/ |
|||
Morebits.quickForm.prototype.append = function QuickFormAppend( data ) { |
Morebits.quickForm.prototype.append = function QuickFormAppend( data ) { |
||
return this.root.append( data ); |
return this.root.append( data ); |
||
}; |
}; |
||
/** |
|||
* @constructor |
|||
* @param {Object} |
|||
*/ |
|||
Morebits.quickForm.element = function QuickFormElement( data ) { |
Morebits.quickForm.element = function QuickFormElement( data ) { |
||
this.data = data; |
this.data = data; |
||
Line 170: | Line 187: | ||
Morebits.quickForm.element.id = 0; |
Morebits.quickForm.element.id = 0; |
||
/** |
|||
* Appends an element to current element |
|||
* @param {Morebits.quickForm.element} data A quickForm element or the object required to |
|||
* create the quickForm element |
|||
* @returns {Morebits.quickForm.element} The same element passed in |
|||
*/ |
|||
Morebits.quickForm.element.prototype.append = function QuickFormElementAppend( data ) { |
Morebits.quickForm.element.prototype.append = function QuickFormElementAppend( data ) { |
||
var child; |
var child; |
||
Line 181: | Line 204: | ||
}; |
}; |
||
/** |
|||
// This should be called without parameters: form.render() |
|||
* Renders the HTML output for the quickForm element |
|||
* This should be called without parameters: form.render() |
|||
* @returns {HTMLElement} |
|||
*/ |
|||
Morebits.quickForm.element.prototype.render = function QuickFormElementRender( internal_subgroup_id ) { |
Morebits.quickForm.element.prototype.render = function QuickFormElementRender( internal_subgroup_id ) { |
||
var currentNode = this.compute( this.data, internal_subgroup_id ); |
var currentNode = this.compute( this.data, internal_subgroup_id ); |
||
Line 649: | Line 676: | ||
}; |
}; |
||
/** |
|||
* @param {HTMLElement} node |
|||
* @param {Object} data |
|||
*/ |
|||
Morebits.quickForm.element.generateTooltip = function QuickFormElementGenerateTooltip( node, data ) { |
Morebits.quickForm.element.generateTooltip = function QuickFormElementGenerateTooltip( node, data ) { |
||
$('<span/>', { |
$('<span/>', { |
||
Line 661: | Line 692: | ||
}); |
}); |
||
}; |
}; |
||
// Some utility methods for manipulating quickForms after their creation: |
|||
// (None of these work for "dyninput" type fields at present) |
|||
/** |
/** |
||
* Returns all form elements with a given field name or ID |
|||
* Some utility methods for manipulating quickForms after their creation |
|||
* @returns {HTMLElement[]} |
|||
* (None of them work for "dyninput" type fields at present) |
|||
* |
|||
* Morebits.quickForm.getElements(form, fieldName) |
|||
* Returns all form elements with a given field name or ID |
|||
* |
|||
* Morebits.quickForm.getCheckboxOrRadio(elementArray, value) |
|||
* Searches the array of elements for a checkbox or radio button with a certain |value| attribute |
|||
* |
|||
* Morebits.quickForm.getElementContainer(element) |
|||
* Returns the <div> containing the form element, or the form element itself |
|||
* May not work as expected on checkboxes or radios |
|||
* |
|||
* Morebits.quickForm.getElementLabelObject(element) |
|||
* Gets the HTML element that contains the label of the given form element (mainly for internal use) |
|||
* |
|||
* Morebits.quickForm.getElementLabel(element) |
|||
* Gets the label text of the element |
|||
* |
|||
* Morebits.quickForm.setElementLabel(element, labelText) |
|||
* Sets the label of the element to the given text |
|||
* |
|||
* Morebits.quickForm.overrideElementLabel(element, temporaryLabelText) |
|||
* Stores the element's current label, and temporarily sets the label to the given text |
|||
* |
|||
* Morebits.quickForm.resetElementLabel(element) |
|||
* Restores the label stored by overrideElementLabel |
|||
* |
|||
* Morebits.quickForm.setElementVisibility(element, visibility) |
|||
* Shows or hides a form element plus its label and tooltip |
|||
* |
|||
* Morebits.quickForm.setElementTooltipVisibility(element, visibility) |
|||
* Shows or hides the "question mark" icon next to a form element |
|||
*/ |
*/ |
||
Morebits.quickForm.getElements = function QuickFormGetElements(form, fieldName) { |
Morebits.quickForm.getElements = function QuickFormGetElements(form, fieldName) { |
||
var $form = $(form); |
var $form = $(form); |
||
Line 711: | Line 715: | ||
}; |
}; |
||
/** |
|||
* Searches the array of elements for a checkbox or radio button with a certain |
|||
* `value` attribute, and returns the first such element. Returns null if not found. |
|||
* @param {HTMLInputElement[]} elementArray |
|||
* @param {string} value |
|||
* @returns {HTMLInputElement} |
|||
*/ |
|||
Morebits.quickForm.getCheckboxOrRadio = function QuickFormGetCheckboxOrRadio(elementArray, value) { |
Morebits.quickForm.getCheckboxOrRadio = function QuickFormGetCheckboxOrRadio(elementArray, value) { |
||
var found = $.grep(elementArray, function(el) { |
var found = $.grep(elementArray, function(el) { |
||
Line 721: | Line 732: | ||
}; |
}; |
||
/** |
|||
* Returns the <div> containing the form element, or the form element itself |
|||
* May not work as expected on checkboxes or radios |
|||
* @param {HTMLElement} element |
|||
* @returns {HTMLElement} |
|||
*/ |
|||
Morebits.quickForm.getElementContainer = function QuickFormGetElementContainer(element) { |
Morebits.quickForm.getElementContainer = function QuickFormGetElementContainer(element) { |
||
// for divs, headings and fieldsets, the container is the element itself |
// for divs, headings and fieldsets, the container is the element itself |
||
Line 732: | Line 749: | ||
}; |
}; |
||
/** |
|||
* Gets the HTML element that contains the label of the given form element |
|||
* (mainly for internal use) |
|||
* @param {(HTMLElement|Morebits.quickForm.element)} element |
|||
* @returns {HTMLElement} |
|||
*/ |
|||
Morebits.quickForm.getElementLabelObject = function QuickFormGetElementLabelObject(element) { |
Morebits.quickForm.getElementLabelObject = function QuickFormGetElementLabelObject(element) { |
||
// for buttons, divs and headers, the label is on the element itself |
// for buttons, divs and headers, the label is on the element itself |
||
Line 752: | Line 775: | ||
}; |
}; |
||
/** |
|||
* Gets the label text of the element |
|||
* @param {(HTMLElement|Morebits.quickForm.element)} element |
|||
* @returns {string} |
|||
*/ |
|||
Morebits.quickForm.getElementLabel = function QuickFormGetElementLabel(element) { |
Morebits.quickForm.getElementLabel = function QuickFormGetElementLabel(element) { |
||
var labelElement = Morebits.quickForm.getElementLabelObject(element); |
var labelElement = Morebits.quickForm.getElementLabelObject(element); |
||
Line 761: | Line 789: | ||
}; |
}; |
||
/** |
|||
* Sets the label of the element to the given text |
|||
* @param {(HTMLElement|Morebits.quickForm.element)} element |
|||
* @param {string} labelText |
|||
* @returns {boolean} true if succeeded, false if the label element is unavailable |
|||
*/ |
|||
Morebits.quickForm.setElementLabel = function QuickFormSetElementLabel(element, labelText) { |
Morebits.quickForm.setElementLabel = function QuickFormSetElementLabel(element, labelText) { |
||
var labelElement = Morebits.quickForm.getElementLabelObject(element); |
var labelElement = Morebits.quickForm.getElementLabelObject(element); |
||
Line 771: | Line 805: | ||
}; |
}; |
||
/** |
|||
* Stores the element's current label, and temporarily sets the label to the given text |
|||
* @param {(HTMLElement|Morebits.quickForm.element)} element |
|||
* @param {string} temporaryLabelText |
|||
* @returns {boolean} true if succeeded, false if the label element is unavailable |
|||
*/ |
|||
Morebits.quickForm.overrideElementLabel = function QuickFormOverrideElementLabel(element, temporaryLabelText) { |
Morebits.quickForm.overrideElementLabel = function QuickFormOverrideElementLabel(element, temporaryLabelText) { |
||
if (!element.hasAttribute("data-oldlabel")) { |
if (!element.hasAttribute("data-oldlabel")) { |
||
Line 778: | Line 818: | ||
}; |
}; |
||
/** |
|||
* Restores the label stored by overrideElementLabel |
|||
* @param {(HTMLElement|Morebits.quickForm.element)} element |
|||
* @returns {boolean} true if succeeded, false if the label element is unavailable |
|||
*/ |
|||
Morebits.quickForm.resetElementLabel = function QuickFormResetElementLabel(element) { |
Morebits.quickForm.resetElementLabel = function QuickFormResetElementLabel(element) { |
||
if (element.hasAttribute("data-oldlabel")) { |
if (element.hasAttribute("data-oldlabel")) { |
||
Line 785: | Line 830: | ||
}; |
}; |
||
/** |
|||
* Shows or hides a form element plus its label and tooltip |
|||
* @param {(HTMLElement|jQuery|string)} element HTML/jQuery element, or jQuery selector string |
|||
* @param {boolean} [visibility] Skip this to toggle visibility |
|||
*/ |
|||
Morebits.quickForm.setElementVisibility = function QuickFormSetElementVisibility(element, visibility) { |
Morebits.quickForm.setElementVisibility = function QuickFormSetElementVisibility(element, visibility) { |
||
$(element).toggle(visibility); |
$(element).toggle(visibility); |
||
}; |
}; |
||
/** |
|||
* Shows or hides the "question mark" icon (which displays the tooltip) next to a form element |
|||
* @param {(HTMLElement|jQuery)} element |
|||
* @param {boolean} [visibility] Skip this to toggle visibility |
|||
*/ |
|||
Morebits.quickForm.setElementTooltipVisibility = function QuickFormSetElementTooltipVisibility(element, visibility) { |
Morebits.quickForm.setElementTooltipVisibility = function QuickFormSetElementTooltipVisibility(element, visibility) { |
||
$(Morebits.quickForm.getElementContainer(element)).find(".morebits-tooltip").toggle(visibility); |
$(Morebits.quickForm.getElementContainer(element)).find(".morebits-tooltip").toggle(visibility); |
||
Line 797: | Line 852: | ||
/** |
/** |
||
* **************** HTMLFormElement **************** |
* **************** HTMLFormElement **************** |
||
* |
|||
* getChecked: |
|||
* XXX Doesn't seem to work reliably across all browsers at the moment. -- see getChecked2 in twinkleunlink.js, which is better |
|||
* |
|||
* Returns an array containing the values of elements with the given name, that has it's |
|||
* checked property set to true. (i.e. a checkbox or a radiobutton is checked), or select options |
|||
* that have selected set to true. (don't try to mix selects with radio/checkboxes, please) |
|||
* Type is optional and can specify if either radio or checkbox (for the event |
|||
* that both checkboxes and radiobuttons have the same name. |
|||
*/ |
*/ |
||
/** |
|||
* Returns an array containing the values of elements with the given name, that has it's |
|||
* checked property set to true. (i.e. a checkbox or a radiobutton is checked), or select |
|||
* options that have selected set to true. (don't try to mix selects with radio/checkboxes, |
|||
* please) |
|||
* Type is optional and can specify if either radio or checkbox (for the event |
|||
* that both checkboxes and radiobuttons have the same name. |
|||
* |
|||
* XXX: Doesn't seem to work reliably across all browsers at the moment. -- see getChecked2 |
|||
* in twinkleunlink.js, which is better |
|||
*/ |
|||
HTMLFormElement.prototype.getChecked = function( name, type ) { |
HTMLFormElement.prototype.getChecked = function( name, type ) { |
||
var elements = this.elements[name]; |
var elements = this.elements[name]; |
||
Line 856: | Line 913: | ||
* **************** RegExp **************** |
* **************** RegExp **************** |
||
* |
* |
||
* |
* Escapes a string to be used in a RegExp |
||
* @param {string} text - string to be escaped |
|||
* @param {boolean} [space_fix] - Set this true to replace spaces and underscore with `[ _]` as they are often equivalent |
|||
*/ |
*/ |
||
Line 990: | Line 1,049: | ||
return str.substr( 0, 1 ).toLowerCase() + str.substr( 1 ); |
return str.substr( 0, 1 ).toLowerCase() + str.substr( 1 ); |
||
}, |
}, |
||
splitWeightedByKeys: function( str, start, end, skip ) { |
|||
/** |
|||
* Gives an array of substrings of `str` starting with `start` and |
|||
* ending with `end`, which is not in `skiplist` |
|||
* @param {string} str |
|||
* @param {string} start |
|||
* @param {string} end |
|||
* @param {(string[]|string)} [skiplist] |
|||
* @returns {String[]} |
|||
*/ |
|||
splitWeightedByKeys: function( str, start, end, skiplist ) { |
|||
if( start.length !== end.length ) { |
if( start.length !== end.length ) { |
||
throw new Error( 'start marker and end marker must be of the same length' ); |
throw new Error( 'start marker and end marker must be of the same length' ); |
||
Line 997: | Line 1,066: | ||
var initial = null; |
var initial = null; |
||
var result = []; |
var result = []; |
||
if( ! Array.isArray( |
if( ! Array.isArray( skiplist ) ) { |
||
if( |
if( skiplist === undefined ) { |
||
skiplist = []; |
|||
} else if( typeof |
} else if( typeof skiplist === 'string' ) { |
||
skiplist = [ skiplist ]; |
|||
} else { |
} else { |
||
throw new Error( "non-applicable |
throw new Error( "non-applicable skiplist parameter" ); |
||
} |
} |
||
} |
} |
||
for( var i = 0; i < str.length; ++i ) { |
for( var i = 0; i < str.length; ++i ) { |
||
for( var j = 0; j < |
for( var j = 0; j < skiplist.length; ++j ) { |
||
if( str.substr( i, |
if( str.substr( i, skiplist[j].length ) === skiplist[j] ) { |
||
i += |
i += skiplist[j].length - 1; |
||
continue; |
continue; |
||
} |
} |
||
Line 1,031: | Line 1,100: | ||
return result; |
return result; |
||
}, |
}, |
||
// for deletion/other templates taking a freeform "reason" from a textarea (e.g. PROD, XFD, RPP) |
|||
/** |
|||
* Formats freeform "reason" (from a textarea) for deletion/other templates |
|||
* that are going to be substituted, (e.g. PROD, XFD, RPP) |
|||
* @param {string} str |
|||
* @returns {string} |
|||
*/ |
|||
formatReasonText: function( str ) { |
formatReasonText: function( str ) { |
||
var result = str.toString().trimRight(); |
var result = str.toString().trimRight(); |
||
Line 1,039: | Line 1,114: | ||
return unbinder.rebind(); |
return unbinder.rebind(); |
||
}, |
}, |
||
// a replacement for String.prototype.replace() when the second parameter (the |
|||
/** |
|||
// replacement string) is arbitrary, such as a username or freeform user input, |
|||
* Replacement for `String.prototype.replace()` when the second parameter |
|||
// and may contain dollar signs |
|||
* (the replacement string) is arbitrary, such as a username or freeform user input, |
|||
* and may contain dollar signs |
|||
*/ |
|||
safeReplace: function morebitsStringSafeReplace(string, pattern, replacement) { |
safeReplace: function morebitsStringSafeReplace(string, pattern, replacement) { |
||
return string.replace(pattern, replacement.replace(/\$/g, "$$$$")); |
return string.replace(pattern, replacement.replace(/\$/g, "$$$$")); |
||
} |
} |
||
}; |
}; |
||
/** |
/** |
||
* **************** Morebits.array **************** |
* **************** Morebits.array **************** |
||
* |
|||
* uniq(arr): returns a copy of the array with duplicates removed |
|||
* |
|||
* dups(arr): returns a copy of the array with the first instance of each value |
|||
* removed; subsequent instances of those values (duplicates) remain |
|||
* |
|||
* chunk(arr, size): breaks up |arr| into smaller arrays of length |size|, and |
|||
* returns an array of these "chunked" arrays |
|||
*/ |
*/ |
||
Morebits.array = { |
Morebits.array = { |
||
/** |
|||
* @returns {Array} a copy of the array with duplicates removed |
|||
*/ |
|||
uniq: function(arr) { |
uniq: function(arr) { |
||
if ( ! Array.isArray( arr ) ) { |
if ( ! Array.isArray( arr ) ) { |
||
Line 1,075: | Line 1,147: | ||
return result; |
return result; |
||
}, |
}, |
||
/** |
|||
* @returns {Array} a copy of the array with the first instance of each value |
|||
* removed; subsequent instances of those values (duplicates) remain |
|||
*/ |
|||
dups: function(arr) { |
dups: function(arr) { |
||
if ( ! Array.isArray( arr ) ) { |
if ( ! Array.isArray( arr ) ) { |
||
Line 1,091: | Line 1,168: | ||
return result; |
return result; |
||
}, |
}, |
||
/** |
|||
* breaks up `arr` into smaller arrays of length `size`, and |
|||
* @returns an array of these "chunked" arrays |
|||
* @param {array} arr |
|||
* @param {number} size |
|||
* @returns {Array} |
|||
*/ |
|||
chunk: function( arr, size ) { |
chunk: function( arr, size ) { |
||
if ( ! Array.isArray( arr ) ) { |
if ( ! Array.isArray( arr ) ) { |
||
Line 1,110: | Line 1,196: | ||
} |
} |
||
}; |
}; |
||
Line 1,119: | Line 1,204: | ||
*/ |
*/ |
||
Morebits.pageNameNorm = mw.config.get('wgPageName').replace(/_/g, ' '); |
Morebits.pageNameNorm = mw.config.get('wgPageName').replace(/_/g, ' '); |
||
/** |
/** |
||
* **************** Morebits.unbinder **************** |
* **************** Morebits.unbinder **************** |
||
* Used for temporarily hiding a part of a string while processing the rest of it. |
|||
* |
|||
* eg. var u = new Morebits.unbinder("Hello world <!-- world --> world"); |
|||
* u.unbind('<!--','-->'); |
|||
* u.content = u.content.replace(/world/g, 'earth'); |
|||
* u.rebind() // gives "Hello earth <!-- world --> earth" |
|||
* |
|||
* Text within the 'unbinded' part (in this case, the HTML comment) remains intact |
|||
* unbind() can be called multiple times to unbind multiple parts of the string. |
|||
* |
|||
* Used by Morebits.wikitext.page.commentOutImage |
* Used by Morebits.wikitext.page.commentOutImage |
||
*/ |
*/ |
||
/** |
|||
* @constructor |
|||
* @param {string} string |
|||
*/ |
|||
Morebits.unbinder = function Unbinder( string ) { |
Morebits.unbinder = function Unbinder( string ) { |
||
if( typeof string !== 'string' ) { |
if( typeof string !== 'string' ) { |
||
Line 1,139: | Line 1,237: | ||
Morebits.unbinder.prototype = { |
Morebits.unbinder.prototype = { |
||
/** |
|||
* @param {string} prefix |
|||
* @param {string} postfix |
|||
*/ |
|||
unbind: function UnbinderUnbind( prefix, postfix ) { |
unbind: function UnbinderUnbind( prefix, postfix ) { |
||
var re = new RegExp( prefix + '(.*?)' + postfix, ' |
var re = new RegExp( prefix + '(.*?)' + postfix, 'gs' ); |
||
this.content = this.content.replace( re, Morebits.unbinder.getCallback( this ) ); |
this.content = this.content.replace( re, Morebits.unbinder.getCallback( this ) ); |
||
}, |
}, |
||
/** |
|||
* @returns {string} The output |
|||
*/ |
|||
rebind: function UnbinderRebind() { |
rebind: function UnbinderRebind() { |
||
var content = this.content; |
var content = this.content; |
||
Line 1,179: | Line 1,285: | ||
*/ |
*/ |
||
Date.monthNames = [ |
Date.monthNames = ['January', 'February', 'March', 'April', 'May', 'June', |
||
'July', 'August', 'September', 'October', 'November','December' ]; |
|||
'January', |
|||
'February', |
|||
'March', |
|||
'April', |
|||
'May', |
|||
'June', |
|||
'July', |
|||
'August', |
|||
'September', |
|||
'October', |
|||
'November', |
|||
'December' |
|||
]; |
|||
Date.monthNamesAbbrev = [ |
Date.monthNamesAbbrev = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; |
||
'Jan', |
|||
'Feb', |
|||
'Mar', |
|||
'Apr', |
|||
'May', |
|||
'Jun', |
|||
'Jul', |
|||
'Aug', |
|||
'Sep', |
|||
'Oct', |
|||
'Nov', |
|||
'Dec' |
|||
]; |
|||
Date.prototype.getMonthName = function() { |
Date.prototype.getMonthName = function() { |
||
Line 1,304: | Line 1,385: | ||
* Various objects for wiki editing and API access |
* Various objects for wiki editing and API access |
||
*/ |
*/ |
||
Morebits.wiki = {}; |
Morebits.wiki = {}; |
||
/** |
|||
// Determines whether the current page is a redirect or soft redirect |
|||
* Determines whether the current page is a redirect or soft redirect |
|||
// (fails to detect soft redirects on edit, history, etc. pages) |
|||
* (fails to detect soft redirects on edit, history, etc. pages) |
|||
*/ |
|||
Morebits.wiki.isPageRedirect = function wikipediaIsPageRedirect() { |
Morebits.wiki.isPageRedirect = function wikipediaIsPageRedirect() { |
||
return !!(mw.config.get("wgIsRedirect") || document.getElementById("softredirect")); |
return !!(mw.config.get("wgIsRedirect") || document.getElementById("softredirect")); |
||
Line 1,318: | Line 1,400: | ||
* **************** Morebits.wiki.actionCompleted **************** |
* **************** Morebits.wiki.actionCompleted **************** |
||
* |
* |
||
* Use of Morebits.wiki.actionCompleted(): |
* Use of Morebits.wiki.actionCompleted(): |
||
* Every call to Morebits.wiki.api.post() results in the dispatch of |
* Every call to Morebits.wiki.api.post() results in the dispatch of |
||
* an asynchronous callback. Each callback can in turn |
* an asynchronous callback. Each callback can in turn |
||
Line 1,386: | Line 1,468: | ||
* **************** Morebits.wiki.api **************** |
* **************** Morebits.wiki.api **************** |
||
* An easy way to talk to the MediaWiki API. |
* An easy way to talk to the MediaWiki API. |
||
* |
*/ |
||
* Constructor parameters: |
|||
/** |
|||
* currentAction: the current action (required) |
|||
* @constructor |
|||
* query: the query (required) |
|||
* @param {string} currentAction - The current action (required) |
|||
* onSuccess: the function to call when request gotten |
|||
* @param {Object} query - The query (required) |
|||
* statusElement: a Morebits.status object to use for status messages (optional) |
|||
* |
* @param {Function} onSuccess - The function to call when request gotten |
||
* @param {Object} [statusElement] - A Morebits.status object to use for status messages (optional) |
|||
* @param {Function} [onError] - The function to call if an error occurs (optional) |
|||
*/ |
*/ |
||
Morebits.wiki.api = function( currentAction, query, onSuccess, statusElement, onError ) { |
Morebits.wiki.api = function( currentAction, query, onSuccess, statusElement, onError ) { |
||
Line 1,422: | Line 1,506: | ||
errorText: null, // full error description, if any |
errorText: null, // full error description, if any |
||
/** |
|||
// post(): carries out the request |
|||
* Carries out the request. |
|||
// do not specify a parameter unless you really really want to give jQuery some extra parameters |
|||
* @param {Object} callerAjaxParameters Do not specify a parameter unless you really |
|||
* really want to give jQuery some extra parameters |
|||
*/ |
|||
post: function( callerAjaxParameters ) { |
post: function( callerAjaxParameters ) { |
||
Line 1,514: | Line 1,601: | ||
var morebitsWikiApiUserAgent = 'morebits.js/2.0 ([[w:WT:TW]])'; |
var morebitsWikiApiUserAgent = 'morebits.js/2.0 ([[w:WT:TW]])'; |
||
/** |
|||
// Sets the custom user agent header |
|||
* Sets the custom user agent header |
|||
* @param {string} ua User agent |
|||
*/ |
|||
Morebits.wiki.api.setApiUserAgent = function( ua ) { |
Morebits.wiki.api.setApiUserAgent = function( ua ) { |
||
morebitsWikiApiUserAgent = ( ua ? ua + ' ' : '' ) + 'morebits.js/2.0 ([[w:WT:TW]])'; |
morebitsWikiApiUserAgent = ( ua ? ua + ' ' : '' ) + 'morebits.js/2.0 ([[w:WT:TW]])'; |
||
Line 1,536: | Line 1,626: | ||
* |
* |
||
* |
* |
||
* HIGHLIGHTS: |
|||
* NOTE: This list of member functions is incomplete. |
|||
* |
* |
||
* Constructor: Morebits.wiki.page(pageName, currentAction) |
* Constructor: Morebits.wiki.page(pageName, currentAction) |
||
Line 1,543: | Line 1,633: | ||
* currentAction - a string describing the action about to be undertaken (optional) |
* currentAction - a string describing the action about to be undertaken (optional) |
||
* |
* |
||
* |
* onSuccess and onFailure are callback functions called when the operation is a success or failure |
||
* if enclosed in [brackets], it indicates that it is optional |
|||
* onSuccess - callback function which is called when the load has succeeded |
|||
* onFailure - callback function which is called when the load fails (optional) |
|||
* |
* |
||
* |
* load(onSuccess, [onFailure]): Loads the text for the page |
||
* |
|||
* onSuccess - callback function which is called when the save has succeeded (optional) |
|||
* getPageText(): returns a string containing the text of the page after a successful load() |
|||
* onFailure - callback function which is called when the save fails (optional) |
|||
* |
|||
* save([onSuccess], [onFailure]): Saves the text set via setPageText() for the page. |
|||
* Must be preceded by calling load(). |
|||
* Warning: Calling save() can result in additional calls to the previous load() callbacks to |
* Warning: Calling save() can result in additional calls to the previous load() callbacks to |
||
* recover from edit conflicts! |
* recover from edit conflicts! |
||
Line 1,555: | Line 1,647: | ||
* This behavior can be disabled with setMaxConflictRetries(0). |
* This behavior can be disabled with setMaxConflictRetries(0). |
||
* |
* |
||
* append(onSuccess, onFailure): Adds the text provided via setAppendText() to the end of |
* append([onSuccess], [onFailure]): Adds the text provided via setAppendText() to the end of |
||
* |
* the page. Does not require calling load() first. |
||
* onSuccess - callback function which is called when the method has succeeded (optional) |
|||
* onFailure - callback function which is called when the method fails (optional) |
|||
* |
* |
||
* prepend(onSuccess, onFailure): Adds the text provided via setPrependText() to the start |
* prepend([onSuccess], [onFailure]): Adds the text provided via setPrependText() to the start |
||
* |
* of the page. Does not require calling load() first. |
||
* onSuccess - callback function which is called when the method has succeeded (optional) |
|||
* onFailure - callback function which is called when the method fails (optional) |
|||
* |
* |
||
* move(onSuccess, [onFailure]): Moves a page to another title |
|||
* getPageName(): returns a string containing the name of the loaded page, including the namespace |
|||
* |
* |
||
* deletePage(onSuccess, [onFailure]): Deletes a page (for admins only) |
|||
* getPageText(): returns a string containing the text of the page after a successful load() |
|||
* |
* |
||
* protect(onSuccess, [onFailure]): Protects a page |
|||
* setPageText(pageText) |
|||
* pageText - string containing the updated page text that will be saved when save() is called |
|||
* |
* |
||
* getPageName(): returns a string containing the name of the loaded page, including the namespace |
|||
* setAppendText(appendText) |
|||
* appendText - string containing the text that will be appended to the page when append() is called |
|||
* |
|||
* setPrependText(prependText) |
|||
* prependText - string containing the text that will be prepended to the page when prepend() is called |
|||
* |
|||
* setEditSummary(summary) |
|||
* summary - string containing the text of the edit summary that will be used when save() is called |
|||
* |
|||
* setMinorEdit(minorEdit) |
|||
* minorEdit is a boolean value: |
|||
* true - When save is called, the resulting edit will be marked as "minor". |
|||
* false - When save is called, the resulting edit will not be marked as "minor". (default) |
|||
* |
|||
* setBotEdit(botEdit) |
|||
* botEdit is a boolean value: |
|||
* true - When save is called, the resulting edit will be marked as "bot". |
|||
* false - When save is called, the resulting edit will not be marked as "bot". (default) |
|||
* |
* |
||
* setPageText(pageText) sets the updated page text that will be saved when save() is called |
|||
* setPageSection(pageSection) |
|||
* pageSection - integer specifying the section number to load or save. The default is |null|, which means |
|||
* that the entire page will be retrieved. |
|||
* |
* |
||
* setAppendText(appendText) sets the text that will be appended to the page when append() is called |
|||
* setMaxConflictRetries(maxRetries) |
|||
* maxRetries - number of retries for save errors involving an edit conflict or loss of edit token |
|||
* default: 2 |
|||
* |
* |
||
* setPrependText(prependText) sets the text that will be prepended to the page when prepend() is called |
|||
* setMaxRetries(maxRetries) |
|||
* maxRetries - number of retries for save errors not involving an edit conflict or loss of edit token |
|||
* default: 2 |
|||
* |
* |
||
* setCallbackParameters(callbackParameters) |
* setCallbackParameters(callbackParameters) |
||
Line 1,616: | Line 1,680: | ||
* getStatusElement(): returns the Status element created by the constructor |
* getStatusElement(): returns the Status element created by the constructor |
||
* |
* |
||
* exists(): returns true if the page existed on the wiki when it was last loaded |
|||
* setFollowRedirect(followRedirect) |
|||
* followRedirect is a boolean value: |
|||
* true - a maximum of one redirect will be followed. |
|||
* In the event of a redirect, a message is displayed to the user and |
|||
* the redirect target can be retrieved with getPageName(). |
|||
* false - the requested pageName will be used without regard to any redirect. (default) |
|||
* |
* |
||
* getCurrentID(): returns a string containing the current revision ID of the page |
|||
* setWatchlist(watchlistOption) |
|||
* watchlistOption is a boolean value: |
|||
* true - page will be added to the user's watchlist when save() is called |
|||
* false - watchlist status of the page will not be changed (default) |
|||
* |
|||
* setWatchlistFromPreferences(watchlistOption) |
|||
* watchlistOption is a boolean value: |
|||
* true - page watchlist status will be set based on the user's |
|||
* preference settings when save() is called |
|||
* false - watchlist status of the page will not be changed (default) |
|||
* |
|||
* Watchlist notes: |
|||
* 1. The MediaWiki API value of 'unwatch', which explicitly removes the page from the |
|||
* user's watchlist, is not used. |
|||
* 2. If both setWatchlist() and setWatchlistFromPreferences() are called, |
|||
* the last call takes priority. |
|||
* 3. Twinkle modules should use the appropriate preference to set the watchlist options. |
|||
* 4. Most Twinkle modules use setWatchlist(). |
|||
* setWatchlistFromPreferences() is only needed for the few Twinkle watchlist preferences |
|||
* that accept a string value of 'default'. |
|||
* |
|||
* setCreateOption(createOption) |
|||
* createOption is a string value: |
|||
* 'recreate' - create the page if it does not exist, or edit it if it exists |
|||
* 'createonly' - create the page if it does not exist, but return an error if it |
|||
* already exists |
|||
* 'nocreate' - don't create the page, only edit it if it already exists |
|||
* null - create the page if it does not exist, unless it was deleted in the moment |
|||
* between retrieving the edit token and saving the edit (default) |
|||
* |
|||
* exists(): returns true if the page existed on the wiki when it was last loaded |
|||
* |
* |
||
* lookupCreator(onSuccess): Retrieves the username of the user who created the page |
* lookupCreator(onSuccess): Retrieves the username of the user who created the page |
||
Line 1,660: | Line 1,689: | ||
* |
* |
||
* getCreator(): returns the user who created the page following lookupCreator() |
* getCreator(): returns the user who created the page following lookupCreator() |
||
* |
|||
* getCurrentID(): returns a string containing the current revision ID of the page |
|||
* |
|||
* patrol(): marks the page as patrolled, if possible |
|||
* |
|||
* move(onSuccess, onFailure): Moves a page to another title |
|||
* |
|||
* deletePage(onSuccess, onFailure): Deletes a page (for admins only) |
|||
* |
* |
||
*/ |
*/ |
||
Line 1,699: | Line 1,720: | ||
*/ |
*/ |
||
/** |
|||
* @constructor |
|||
* @param {string} pageName The name of the page, prefixed by the namespace (if any) |
|||
* For the current page, use mw.config.get('wgPageName') |
|||
* @param {string} [currentAction] A string describing the action about to be undertaken (optional) |
|||
*/ |
|||
Morebits.wiki.page = function(pageName, currentAction) { |
Morebits.wiki.page = function(pageName, currentAction) { |
||
Line 1,797: | Line 1,824: | ||
/** |
/** |
||
* Loads the text for the page |
|||
* Public interface accessors |
|||
* @param {Function} onSuccess - callback function which is called when the load has succeeded |
|||
* @param {Function} onFailure - callback function which is called when the load fails (optional) |
|||
*/ |
*/ |
||
this.getPageName = function() { |
|||
return ctx.pageName; |
|||
}; |
|||
this.getPageText = function() { |
|||
return ctx.pageText; |
|||
}; |
|||
this.setPageText = function(pageText) { |
|||
ctx.editMode = 'all'; |
|||
ctx.pageText = pageText; |
|||
}; |
|||
this.setAppendText = function(appendText) { |
|||
ctx.editMode = 'append'; |
|||
ctx.appendText = appendText; |
|||
}; |
|||
this.setPrependText = function(prependText) { |
|||
ctx.editMode = 'prepend'; |
|||
ctx.prependText = prependText; |
|||
}; |
|||
this.setEditSummary = function(summary) { |
|||
ctx.editSummary = summary; |
|||
}; |
|||
this.setCreateOption = function(createOption) { |
|||
ctx.createOption = createOption; |
|||
}; |
|||
this.setMinorEdit = function(minorEdit) { |
|||
ctx.minorEdit = minorEdit; |
|||
}; |
|||
this.setBotEdit = function(botEdit) { |
|||
ctx.botEdit = botEdit; |
|||
}; |
|||
this.setPageSection = function(pageSection) { |
|||
ctx.pageSection = pageSection; |
|||
}; |
|||
this.setMaxConflictRetries = function(maxRetries) { |
|||
ctx.maxConflictRetries = maxRetries; |
|||
}; |
|||
this.setMaxRetries = function(maxRetries) { |
|||
ctx.maxRetries = maxRetries; |
|||
}; |
|||
this.setCallbackParameters = function(callbackParameters) { |
|||
ctx.callbackParameters = callbackParameters; |
|||
}; |
|||
this.getCallbackParameters = function() { |
|||
return ctx.callbackParameters; |
|||
}; |
|||
this.getCreator = function() { |
|||
return ctx.creator; |
|||
}; |
|||
this.setOldID = function(oldID) { |
|||
ctx.revertOldID = oldID; |
|||
}; |
|||
this.getCurrentID = function() { |
|||
return ctx.revertCurID; |
|||
}; |
|||
this.getRevisionUser = function() { |
|||
return ctx.revertUser; |
|||
}; |
|||
this.setMoveDestination = function(destination) { |
|||
ctx.moveDestination = destination; |
|||
}; |
|||
this.setMoveTalkPage = function(flag) { |
|||
ctx.moveTalkPage = !!flag; |
|||
}; |
|||
this.setMoveSubpages = function(flag) { |
|||
ctx.moveSubpages = !!flag; |
|||
}; |
|||
this.setMoveSuppressRedirect = function(flag) { |
|||
ctx.moveSuppressRedirect = !!flag; |
|||
}; |
|||
this.setEditProtection = function(level, expiry) { |
|||
ctx.protectEdit = { level: level, expiry: expiry }; |
|||
}; |
|||
this.setMoveProtection = function(level, expiry) { |
|||
ctx.protectMove = { level: level, expiry: expiry }; |
|||
}; |
|||
this.setCreateProtection = function(level, expiry) { |
|||
ctx.protectCreate = { level: level, expiry: expiry }; |
|||
}; |
|||
this.setCascadingProtection = function(flag) { |
|||
ctx.protectCascade = !!flag; |
|||
}; |
|||
this.setFlaggedRevs = function(level, expiry) { |
|||
ctx.flaggedRevs = { level: level, expiry: expiry }; |
|||
}; |
|||
this.getStatusElement = function() { |
|||
return ctx.statusElement; |
|||
}; |
|||
this.setFollowRedirect = function(followRedirect) { |
|||
if (ctx.pageLoaded) { |
|||
ctx.statusElement.error("Internal error: cannot change redirect setting after the page has been loaded!"); |
|||
return; |
|||
} |
|||
ctx.followRedirect = followRedirect; |
|||
}; |
|||
this.setWatchlist = function(flag) { |
|||
if (flag) { |
|||
ctx.watchlistOption = 'watch'; |
|||
} else { |
|||
ctx.watchlistOption = 'nochange'; |
|||
} |
|||
}; |
|||
this.setWatchlistFromPreferences = function(flag) { |
|||
if (flag) { |
|||
ctx.watchlistOption = 'preferences'; |
|||
} else { |
|||
ctx.watchlistOption = 'nochange'; |
|||
} |
|||
}; |
|||
this.suppressProtectWarning = function() { |
|||
ctx.suppressProtectWarning = true; |
|||
}; |
|||
this.exists = function() { |
|||
return ctx.pageExists; |
|||
}; |
|||
this.load = function(onSuccess, onFailure) { |
this.load = function(onSuccess, onFailure) { |
||
ctx.onLoadSuccess = onSuccess; |
ctx.onLoadSuccess = onSuccess; |
||
Line 1,988: | Line 1,870: | ||
}; |
}; |
||
/** |
|||
// Save updated .pageText to Wikipedia |
|||
* Saves the text for the page to Wikipedia |
|||
// Only valid after successful .load() |
|||
* Must be preceded by successfully calling load(). |
|||
* |
|||
* Warning: Calling save() can result in additional calls to the previous load() callbacks |
|||
* to recover from edit conflicts! |
|||
* In this case, callers must make the same edit to the new pageText and reinvoke save(). |
|||
* This behavior can be disabled with setMaxConflictRetries(0). |
|||
* @param {Function} [onSuccess] - callback function which is called when the save has |
|||
* succeeded (optional) |
|||
* @param {Function} [onFailure] - callback function which is called when the save fails |
|||
* (optional) |
|||
*/ |
|||
this.save = function(onSuccess, onFailure) { |
this.save = function(onSuccess, onFailure) { |
||
ctx.onSaveSuccess = onSuccess; |
ctx.onSaveSuccess = onSuccess; |
||
Line 2,081: | Line 1,974: | ||
}; |
}; |
||
/** |
|||
* Adds the text provided via setAppendText() to the end of the page. |
|||
* Does not require calling load() first. |
|||
* @param {Function} onSuccess - callback function which is called when the method has succeeded (optional) |
|||
* @param {Function} onFailure - callback function which is called when the method fails (optional) |
|||
*/ |
|||
this.append = function(onSuccess, onFailure) { |
this.append = function(onSuccess, onFailure) { |
||
ctx.editMode = 'append'; |
ctx.editMode = 'append'; |
||
Line 2,093: | Line 1,992: | ||
}; |
}; |
||
/** |
|||
* Adds the text provided via setPrependText() to the start of the page. |
|||
* Does not require calling load() first. |
|||
* @param {Function} onSuccess - callback function which is called when the method has succeeded (optional) |
|||
* @param {Function} onFailure - callback function which is called when the method fails (optional) |
|||
*/ |
|||
this.prepend = function(onSuccess, onFailure) { |
this.prepend = function(onSuccess, onFailure) { |
||
ctx.editMode = 'prepend'; |
ctx.editMode = 'prepend'; |
||
Line 2,105: | Line 2,010: | ||
}; |
}; |
||
/** |
|||
* @returns {string} string containing the name of the loaded page, including the namespace |
|||
*/ |
|||
this.getPageName = function() { |
|||
return ctx.pageName; |
|||
}; |
|||
/** |
|||
* @returns {string} string containing the text of the page after a successful load() |
|||
*/ |
|||
this.getPageText = function() { |
|||
return ctx.pageText; |
|||
}; |
|||
/** |
|||
* `pageText` - string containing the updated page text that will be saved when save() is called |
|||
*/ |
|||
this.setPageText = function(pageText) { |
|||
ctx.editMode = 'all'; |
|||
ctx.pageText = pageText; |
|||
}; |
|||
/** |
|||
* `appendText` - string containing the text that will be appended to the page when append() is called |
|||
*/ |
|||
this.setAppendText = function(appendText) { |
|||
ctx.editMode = 'append'; |
|||
ctx.appendText = appendText; |
|||
}; |
|||
/** |
|||
* `prependText` - string containing the text that will be prepended to the page when prepend() is called |
|||
*/ |
|||
this.setPrependText = function(prependText) { |
|||
ctx.editMode = 'prepend'; |
|||
ctx.prependText = prependText; |
|||
}; |
|||
// Edit-related setter methods: |
|||
/** |
|||
* `summary` - string containing the text of the edit summary that will be used when save() is called |
|||
*/ |
|||
this.setEditSummary = function(summary) { |
|||
ctx.editSummary = summary; |
|||
}; |
|||
/** |
|||
* `createOption` is a string value: |
|||
* 'recreate' - create the page if it does not exist, or edit it if it exists |
|||
* 'createonly' - create the page if it does not exist, but return an error if it |
|||
* already exists |
|||
* 'nocreate' - don't create the page, only edit it if it already exists |
|||
* null - create the page if it does not exist, unless it was deleted in the moment |
|||
* between retrieving the edit token and saving the edit (default) |
|||
* |
|||
*/ |
|||
this.setCreateOption = function(createOption) { |
|||
ctx.createOption = createOption; |
|||
}; |
|||
/** |
|||
* `minorEdit` - boolean value: |
|||
* True - When save is called, the resulting edit will be marked as "minor". |
|||
* False - When save is called, the resulting edit will not be marked as "minor". (default) |
|||
*/ |
|||
this.setMinorEdit = function(minorEdit) { |
|||
ctx.minorEdit = minorEdit; |
|||
}; |
|||
/** |
|||
* `botEdit` is a boolean value: |
|||
* True - When save is called, the resulting edit will be marked as "bot". |
|||
* False - When save is called, the resulting edit will not be marked as "bot". (default) |
|||
*/ |
|||
this.setBotEdit = function(botEdit) { |
|||
ctx.botEdit = botEdit; |
|||
}; |
|||
/** |
|||
* `pageSection` - integer specifying the section number to load or save. |
|||
* The default is `null`, which means that the entire page will be retrieved. |
|||
*/ |
|||
this.setPageSection = function(pageSection) { |
|||
ctx.pageSection = pageSection; |
|||
}; |
|||
/** |
|||
* `maxRetries` - number of retries for save errors involving an edit conflict or loss of edit token.Default: 2 |
|||
*/ |
|||
this.setMaxConflictRetries = function(maxRetries) { |
|||
ctx.maxConflictRetries = maxRetries; |
|||
}; |
|||
/** |
|||
* `maxRetries` - number of retries for save errors not involving an edit conflict or loss of edit token |
|||
* Default: 2 |
|||
*/ |
|||
this.setMaxRetries = function(maxRetries) { |
|||
ctx.maxRetries = maxRetries; |
|||
}; |
|||
/** |
|||
* `watchlistOption` is a boolean value: |
|||
* True - page will be added to the user's watchlist when save() is called |
|||
* False - watchlist status of the page will not be changed (default) |
|||
*/ |
|||
this.setWatchlist = function(watchlistOption) { |
|||
if (watchlistOption) { |
|||
ctx.watchlistOption = 'watch'; |
|||
} else { |
|||
ctx.watchlistOption = 'nochange'; |
|||
} |
|||
}; |
|||
/** |
|||
* `watchlistOption` is a boolean value: |
|||
* True - page watchlist status will be set based on the user's |
|||
* preference settings when save() is called |
|||
* False - watchlist status of the page will not be changed (default) |
|||
* |
|||
* Watchlist notes: |
|||
* 1. The MediaWiki API value of 'unwatch', which explicitly removes the page from the |
|||
* user's watchlist, is not used. |
|||
* 2. If both setWatchlist() and setWatchlistFromPreferences() are called, |
|||
* the last call takes priority. |
|||
* 3. Twinkle modules should use the appropriate preference to set the watchlist options. |
|||
* 4. Most Twinkle modules use setWatchlist(). |
|||
* setWatchlistFromPreferences() is only needed for the few Twinkle watchlist preferences |
|||
* that accept a string value of 'default'. |
|||
*/ |
|||
this.setWatchlistFromPreferences = function(watchlistOption) { |
|||
if (watchlistOption) { |
|||
ctx.watchlistOption = 'preferences'; |
|||
} else { |
|||
ctx.watchlistOption = 'nochange'; |
|||
} |
|||
}; |
|||
/** |
|||
* `followRedirect` is a boolean value: |
|||
* True - a maximum of one redirect will be followed. |
|||
* In the event of a redirect, a message is displayed to the user and |
|||
* the redirect target can be retrieved with getPageName(). |
|||
* False - the requested pageName will be used without regard to any redirect. (default) |
|||
*/ |
|||
this.setFollowRedirect = function(followRedirect) { |
|||
if (ctx.pageLoaded) { |
|||
ctx.statusElement.error("Internal error: cannot change redirect setting after the page has been loaded!"); |
|||
return; |
|||
} |
|||
ctx.followRedirect = followRedirect; |
|||
}; |
|||
// Move-related setter functions |
|||
this.setMoveDestination = function(destination) { |
|||
ctx.moveDestination = destination; |
|||
}; |
|||
this.setMoveTalkPage = function(flag) { |
|||
ctx.moveTalkPage = !!flag; |
|||
}; |
|||
this.setMoveSubpages = function(flag) { |
|||
ctx.moveSubpages = !!flag; |
|||
}; |
|||
this.setMoveSuppressRedirect = function(flag) { |
|||
ctx.moveSuppressRedirect = !!flag; |
|||
}; |
|||
// Protect-related setter functions |
|||
this.setEditProtection = function(level, expiry) { |
|||
ctx.protectEdit = { level: level, expiry: expiry }; |
|||
}; |
|||
this.setMoveProtection = function(level, expiry) { |
|||
ctx.protectMove = { level: level, expiry: expiry }; |
|||
}; |
|||
this.setCreateProtection = function(level, expiry) { |
|||
ctx.protectCreate = { level: level, expiry: expiry }; |
|||
}; |
|||
this.setCascadingProtection = function(flag) { |
|||
ctx.protectCascade = !!flag; |
|||
}; |
|||
// Revert-related getters/setters: |
|||
this.setOldID = function(oldID) { |
|||
ctx.revertOldID = oldID; |
|||
}; |
|||
/** |
|||
* @returns {string} string containing the current revision ID of the page |
|||
*/ |
|||
this.getCurrentID = function() { |
|||
return ctx.revertCurID; |
|||
}; |
|||
this.getRevisionUser = function() { |
|||
return ctx.revertUser; |
|||
}; |
|||
// Miscellaneous getters/setters: |
|||
/** |
|||
* `callbackParameters` - an object for use in a callback function |
|||
* |
|||
* Callback notes: callbackParameters is for use by the caller only. The parameters |
|||
* allow a caller to pass the proper context into its callback function. |
|||
* Callers must ensure that any changes to the callbackParameters object |
|||
* within a load() callback still permit a proper re-entry into the |
|||
* load() callback if an edit conflict is detected upon calling save(). |
|||
*/ |
|||
this.setCallbackParameters = function(callbackParameters) { |
|||
ctx.callbackParameters = callbackParameters; |
|||
}; |
|||
/** |
|||
* @returns the object previous set by setCallbackParameters() |
|||
*/ |
|||
this.getCallbackParameters = function() { |
|||
return ctx.callbackParameters; |
|||
}; |
|||
/** |
|||
* @returns {Morebits.status} Status element created by the constructor |
|||
*/ |
|||
this.getStatusElement = function() { |
|||
return ctx.statusElement; |
|||
}; |
|||
this.setFlaggedRevs = function(level, expiry) { |
|||
ctx.flaggedRevs = { level: level, expiry: expiry }; |
|||
}; |
|||
/** |
|||
* @returns {boolean} true if the page existed on the wiki when it was last loaded |
|||
*/ |
|||
this.exists = function() { |
|||
return ctx.pageExists; |
|||
}; |
|||
/** |
|||
* @returns {string} the user who created the page following lookupCreator() |
|||
*/ |
|||
this.getCreator = function() { |
|||
return ctx.creator; |
|||
}; |
|||
/** |
|||
* Retrieves the username of the user who created the page |
|||
* @param {Function} onSuccess - callback function (required) which is |
|||
* called when the username is found within the callback, the username |
|||
* can be retrieved using the getCreator() function |
|||
*/ |
|||
this.lookupCreator = function(onSuccess) { |
this.lookupCreator = function(onSuccess) { |
||
if (!onSuccess) { |
if (!onSuccess) { |
||
Line 2,130: | Line 2,294: | ||
}; |
}; |
||
/** |
|||
* marks the page as patrolled, if possible |
|||
*/ |
|||
this.patrol = function() { |
this.patrol = function() { |
||
// There's no patrol link on page, so we can't patrol |
// There's no patrol link on page, so we can't patrol |
||
Line 2,155: | Line 2,322: | ||
}; |
}; |
||
/** |
|||
* Reverts a page to revertOldID |
|||
* @param {Function} onSuccess - callback function to run on success |
|||
* @param {Function} [onFailure] - callback function to run on failure (optional) |
|||
*/ |
|||
this.revert = function(onSuccess, onFailure) { |
this.revert = function(onSuccess, onFailure) { |
||
ctx.onSaveSuccess = onSuccess; |
ctx.onSaveSuccess = onSuccess; |
||
Line 2,169: | Line 2,341: | ||
}; |
}; |
||
/** |
|||
* Moves a page to another title |
|||
* @param {Function} onSuccess - callback function to run on success |
|||
* @param {Function} [onFailure] - callback function to run on failure (optional) |
|||
*/ |
|||
this.move = function(onSuccess, onFailure) { |
this.move = function(onSuccess, onFailure) { |
||
ctx.onMoveSuccess = onSuccess; |
ctx.onMoveSuccess = onSuccess; |
||
Line 2,203: | Line 2,380: | ||
// |delete| is a reserved word in some flavours of JS |
// |delete| is a reserved word in some flavours of JS |
||
/** |
|||
* Deletes a page (for admins only) |
|||
* @param {Function} onSuccess - callback function to run on success |
|||
* @param {Function} [onFailure] - callback function to run on failure (optional) |
|||
*/ |
|||
this.deletePage = function(onSuccess, onFailure) { |
this.deletePage = function(onSuccess, onFailure) { |
||
ctx.onDeleteSuccess = onSuccess; |
ctx.onDeleteSuccess = onSuccess; |
||
Line 2,239: | Line 2,421: | ||
}; |
}; |
||
/** |
|||
* Protects a page (for admins only) |
|||
* @param {Function} onSuccess - callback function to run on success |
|||
* @param {Function} [onFailure] - callback function to run on failure (optional) |
|||
*/ |
|||
this.protect = function(onSuccess, onFailure) { |
this.protect = function(onSuccess, onFailure) { |
||
ctx.onProtectSuccess = onSuccess; |
ctx.onProtectSuccess = onSuccess; |
||
Line 2,318: | Line 2,505: | ||
}; |
}; |
||
/* |
|||
/* Private member functions |
|||
* Private member functions |
|||
* |
|||
* These are not exposed outside |
* These are not exposed outside |
||
*/ |
*/ |
||
Line 2,330: | Line 2,517: | ||
* |
* |
||
* @param {string} action The action being undertaken, e.g. "edit", "delete". |
* @param {string} action The action being undertaken, e.g. "edit", "delete". |
||
* @returns {boolean} |
|||
*/ |
*/ |
||
var fnCanUseMwUserToken = function(action) { |
var fnCanUseMwUserToken = function(action) { |
||
Line 2,848: | Line 3,036: | ||
}; // end Morebits.wiki.page |
}; // end Morebits.wiki.page |
||
/ |
/* Morebits.wiki.page TODO: (XXX) |
||
* - Should we retry loads also? |
|||
* - Need to reset current action before the save? |
|||
* - Deal with action.completed stuff |
|||
* - Need to reset all parameters once done (e.g. edit summary, move destination, etc.) |
|||
*/ |
|||
Line 2,860: | Line 3,048: | ||
* **************** Morebits.wiki.preview **************** |
* **************** Morebits.wiki.preview **************** |
||
* Uses the API to parse a fragment of wikitext and render it as HTML. |
* Uses the API to parse a fragment of wikitext and render it as HTML. |
||
* |
|||
* Constructor: Morebits.wiki.preview(previewbox, currentAction) |
|||
* previewbox - the <div> element that will contain the rendered HTML |
|||
* |
|||
* beginRender(wikitext): Displays the preview box, and begins an asynchronous attempt |
|||
* to render the specified wikitext. |
|||
* wikitext - wikitext to render; most things should work, including subst: and ~~~~ |
|||
* pageTitle - optional parameter for the page this should be rendered as being on |
|||
* |
|||
* closePreview(): Hides the preview box and clears it. |
|||
* |
* |
||
* The suggested implementation pattern (in Morebits.simpleWindow + Morebits.quickForm situations) is to |
* The suggested implementation pattern (in Morebits.simpleWindow + Morebits.quickForm situations) is to |
||
Line 2,877: | Line 3,055: | ||
*/ |
*/ |
||
/** |
|||
* @constructor |
|||
* @param {HTMLDivElement} previewbox - the <div> element that will contain the rendered HTML |
|||
*/ |
|||
Morebits.wiki.preview = function(previewbox) { |
Morebits.wiki.preview = function(previewbox) { |
||
this.previewbox = previewbox; |
this.previewbox = previewbox; |
||
$(previewbox).addClass("morebits-previewbox").hide(); |
$(previewbox).addClass("morebits-previewbox").hide(); |
||
/** |
|||
* Displays the preview box, and begins an asynchronous attempt |
|||
* to render the specified wikitext. |
|||
* @param {string} wikitext - wikitext to render; most things should work, including subst: and ~~~~ |
|||
* @param {string} [pageTitle] - optional parameter for the page this should be rendered as being on |
|||
*/ |
|||
this.beginRender = function(wikitext, pageTitle) { |
this.beginRender = function(wikitext, pageTitle) { |
||
$(previewbox).show(); |
$(previewbox).show(); |
||
Line 2,907: | Line 3,095: | ||
} |
} |
||
previewbox.innerHTML = html; |
previewbox.innerHTML = html; |
||
$(previewbox).find("a").attr("target", "_blank"); |
$(previewbox).find("a").attr("target", "_blank"); // this makes links open in new tab |
||
}; |
}; |
||
/** |
|||
* Hides the preview box and clears it. |
|||
*/ |
|||
this.closePreview = function() { |
this.closePreview = function() { |
||
$(previewbox).empty().hide(); |
$(previewbox).empty().hide(); |
||
Line 2,958: | Line 3,149: | ||
} |
} |
||
if( test2 === ']]' ) { |
if( test2 === ']]' ) { |
||
current += |
current += ']]'; |
||
++i; |
++i; |
||
--level; |
--level; |
||
Line 3,016: | Line 3,207: | ||
}; |
}; |
||
/** |
|||
* @constructor |
|||
* @param {string} text |
|||
*/ |
|||
Morebits.wikitext.page = function mediawikiPage( text ) { |
Morebits.wikitext.page = function mediawikiPage( text ) { |
||
this.text = text; |
this.text = text; |
||
Line 3,022: | Line 3,217: | ||
Morebits.wikitext.page.prototype = { |
Morebits.wikitext.page.prototype = { |
||
text: '', |
text: '', |
||
/** |
|||
* Removes links to `link_target` from the page text. |
|||
* @param {string} link_target |
|||
*/ |
|||
removeLink: function( link_target ) { |
removeLink: function( link_target ) { |
||
var first_char = link_target.substr( 0, 1 ); |
var first_char = link_target.substr( 0, 1 ); |
||
Line 3,035: | Line 3,235: | ||
this.text = this.text.replace( link_simple_re, "$1" ).replace( link_named_re, "$1" ); |
this.text = this.text.replace( link_simple_re, "$1" ).replace( link_named_re, "$1" ); |
||
}, |
}, |
||
/** |
|||
* Comments out images from page text. If used in a gallery, deletes the whole line. |
|||
* If used as a template argument (not necessarily with File: prefix), the template parameter is commented out. |
|||
* @param {string} image - Image name without File: prefix |
|||
* @param {string} reason - Reason to be included in comment, alongside the commented-out image |
|||
*/ |
|||
commentOutImage: function( image, reason ) { |
commentOutImage: function( image, reason ) { |
||
var unbinder = new Morebits.unbinder( this.text ); |
var unbinder = new Morebits.unbinder( this.text ); |
||
Line 3,043: | Line 3,250: | ||
var image_re_string = "[" + first_char.toUpperCase() + first_char.toLowerCase() + ']' + RegExp.escape( image.substr( 1 ), true ); |
var image_re_string = "[" + first_char.toUpperCase() + first_char.toLowerCase() + ']' + RegExp.escape( image.substr( 1 ), true ); |
||
// Check for normal image links, i.e. [[File:Foobar.png|...]] |
|||
/* |
|||
// Will eat the whole link |
|||
* Check for normal image links, i.e. [[Image:Foobar.png|...]] |
|||
* Will eat the whole link |
|||
*/ |
|||
var links_re = new RegExp( "\\[\\[(?:[Ii]mage|[Ff]ile):\\s*" + image_re_string ); |
var links_re = new RegExp( "\\[\\[(?:[Ii]mage|[Ff]ile):\\s*" + image_re_string ); |
||
var allLinks = Morebits.array.uniq(Morebits.string.splitWeightedByKeys( unbinder.content, '[[', ']]' )); |
var allLinks = Morebits.array.uniq(Morebits.string.splitWeightedByKeys( unbinder.content, '[[', ']]' )); |
||
Line 3,058: | Line 3,263: | ||
unbinder.unbind( '<!--', '-->' ); |
unbinder.unbind( '<!--', '-->' ); |
||
// Check for gallery images, i.e. instances that must start on a new line, |
|||
/* |
|||
// eventually preceded with some space, and must include File: prefix |
|||
// Will eat the whole line. |
|||
*/ |
|||
var gallery_image_re = new RegExp( "(^\\s*(?:[Ii]mage|[Ff]ile):\\s*" + image_re_string + ".*?$)", 'mg' ); |
var gallery_image_re = new RegExp( "(^\\s*(?:[Ii]mage|[Ff]ile):\\s*" + image_re_string + ".*?$)", 'mg' ); |
||
unbinder.content = unbinder.content.replace( gallery_image_re, "<!-- " + reason + "$1 -->" ); |
unbinder.content = unbinder.content.replace( gallery_image_re, "<!-- " + reason + "$1 -->" ); |
||
Line 3,067: | Line 3,271: | ||
// unbind the newly created comments |
// unbind the newly created comments |
||
unbinder.unbind( '<!--', '-->' ); |
unbinder.unbind( '<!--', '-->' ); |
||
/* |
|||
// Check free image usages, for example as template arguments, might have the File: prefix excluded, but must be preceeded by an | |
|||
// Will only eat the image name and the preceeding bar and an eventual named parameter |
|||
*/ |
|||
var free_image_re = new RegExp( "(\\|\\s*(?:[\\w\\s]+\\=)?\\s*(?:(?:[Ii]mage|[Ff]ile):\\s*)?" + image_re_string + ")", 'mg' ); |
var free_image_re = new RegExp( "(\\|\\s*(?:[\\w\\s]+\\=)?\\s*(?:(?:[Ii]mage|[Ff]ile):\\s*)?" + image_re_string + ")", 'mg' ); |
||
unbinder.content = unbinder.content.replace( free_image_re, "<!-- " + reason + "$1 -->" ); |
unbinder.content = unbinder.content.replace( free_image_re, "<!-- " + reason + "$1 -->" ); |
||
// Rebind the content now, we are done! |
// Rebind the content now, we are done! |
||
this.text = unbinder.rebind(); |
this.text = unbinder.rebind(); |
||
}, |
}, |
||
/** |
|||
* Converts first usage of [[File:`image`]] to [[File:`image`|`data`]] |
|||
* @param {string} image - Image name without File: prefix |
|||
* @param {string} data |
|||
*/ |
|||
addToImageComment: function( image, data ) { |
addToImageComment: function( image, data ) { |
||
var first_char = image.substr( 0, 1 ); |
var first_char = image.substr( 0, 1 ); |
||
Line 3,098: | Line 3,306: | ||
this.text = this.text.replace( gallery_re, newtext ); |
this.text = this.text.replace( gallery_re, newtext ); |
||
}, |
}, |
||
/** |
|||
* Removes transclusions of template from page text |
|||
* @param {string} template - Page name whose transclusions are to be removed, |
|||
* include namespace prefix only if not in template namespace |
|||
*/ |
|||
removeTemplate: function( template ) { |
removeTemplate: function( template ) { |
||
var first_char = template.substr( 0, 1 ); |
var first_char = template.substr( 0, 1 ); |
||
Line 3,109: | Line 3,323: | ||
} |
} |
||
}, |
}, |
||
getText: function() { |
getText: function() { |
||
return this.text; |
return this.text; |
||
Line 3,118: | Line 3,333: | ||
/** |
/** |
||
* **************** Morebits.queryString **************** |
* **************** Morebits.queryString **************** |
||
* Maps the querystring to an object |
* Maps the querystring to an JS object |
||
* |
* |
||
* In static context, the value of location.search.substring(1), else the value given |
|||
* Functions: |
|||
* to the constructor is going to be used. The mapped hash is saved in the object. |
|||
* |
|||
* Morebits.queryString.exists(key) |
|||
* returns true if the particular key is set |
|||
* Morebits.queryString.get(key) |
|||
* returns the value associated to the key |
|||
* Morebits.queryString.equals(key, value) |
|||
* returns true if the value associated with given key equals given value |
|||
* Morebits.queryString.toString() |
|||
* returns the query string as a string |
|||
* Morebits.queryString.create( hash ) |
|||
* creates an querystring and encodes strings via encodeURIComponent and joins arrays with | |
|||
* |
|||
* In static context, the value of location.search.substring(1), else the value given to the constructor is going to be used. The mapped hash is saved in the object. |
|||
* |
* |
||
* Example: |
* Example: |
||
Line 3,140: | Line 3,343: | ||
* var obj = new Morebits.queryString('foo=bar&baz=quux'); |
* var obj = new Morebits.queryString('foo=bar&baz=quux'); |
||
* value = obj.get('foo'); |
* value = obj.get('foo'); |
||
*/ |
|||
/** |
|||
* @constructor |
|||
* @param {string} qString The query string |
|||
*/ |
*/ |
||
Morebits.queryString = function QueryString(qString) { |
Morebits.queryString = function QueryString(qString) { |
||
Line 3,175: | Line 3,383: | ||
Morebits.queryString.staticInit(); |
Morebits.queryString.staticInit(); |
||
return Morebits.queryString.staticstr.get(key); |
return Morebits.queryString.staticstr.get(key); |
||
}; |
|||
Morebits.queryString.prototype.get = function(key) { |
|||
return this.params[key] ? this.params[key] : null; |
|||
}; |
}; |
||
Line 3,184: | Line 3,388: | ||
Morebits.queryString.staticInit(); |
Morebits.queryString.staticInit(); |
||
return Morebits.queryString.staticstr.exists(key); |
return Morebits.queryString.staticstr.exists(key); |
||
}; |
|||
Morebits.queryString.prototype.exists = function(key) { |
|||
return this.params[key] ? true : false; |
|||
}; |
}; |
||
Line 3,193: | Line 3,393: | ||
Morebits.queryString.staticInit(); |
Morebits.queryString.staticInit(); |
||
return Morebits.queryString.staticstr.equals(key, value); |
return Morebits.queryString.staticstr.equals(key, value); |
||
}; |
|||
Morebits.queryString.prototype.equals = function(key, value) { |
|||
return this.params[key] === value ? true : false; |
|||
}; |
}; |
||
Line 3,202: | Line 3,398: | ||
Morebits.queryString.staticInit(); |
Morebits.queryString.staticInit(); |
||
return Morebits.queryString.staticstr.toString(); |
return Morebits.queryString.staticstr.toString(); |
||
}; |
|||
Morebits.queryString.prototype.toString = function() { |
|||
return this.string ? this.string : null; |
|||
}; |
}; |
||
Line 3,236: | Line 3,428: | ||
return resarr.join('&'); |
return resarr.join('&'); |
||
}; |
}; |
||
/** |
|||
* @returns {string} the value associated to the given `key` |
|||
* @param {string} key |
|||
*/ |
|||
Morebits.queryString.prototype.get = function(key) { |
|||
return this.params[key] || null; |
|||
}; |
|||
/** |
|||
* @returns {boolean} true if the given `key` is set |
|||
* @param {string} key |
|||
*/ |
|||
Morebits.queryString.prototype.exists = function(key) { |
|||
return !!this.params[key]; |
|||
}; |
|||
/** |
|||
* @returns {boolean} true if the value associated with given `key` equals given `value` |
|||
* @param {string} key |
|||
* @param {string} value |
|||
*/ |
|||
Morebits.queryString.prototype.equals = function(key, value) { |
|||
return this.params[key] === value; |
|||
}; |
|||
/** |
|||
* @returns {string} |
|||
*/ |
|||
Morebits.queryString.prototype.toString = function() { |
|||
return this.string || null; |
|||
}; |
|||
/** |
|||
* Creates a querystring and encodes strings via `encodeURIComponent` and joins arrays with `|` |
|||
* @param {Array} arr |
|||
* @returns {string} |
|||
*/ |
|||
Morebits.queryString.prototype.create = Morebits.queryString.create; |
Morebits.queryString.prototype.create = Morebits.queryString.create; |
||
Line 3,371: | Line 3,601: | ||
}; |
}; |
||
/** |
|||
// display the user's rationale, comments, etc. back to them after a failure, |
|||
* Display the user's rationale, comments, etc. back to them after a failure, |
|||
// so they don't use it |
|||
* so they don't use it |
|||
*/ |
|||
Morebits.status.printUserText = function( comments, message ) { |
Morebits.status.printUserText = function( comments, message ) { |
||
var p = document.createElement( 'p' ); |
var p = document.createElement( 'p' ); |
||
Line 3,452: | Line 3,684: | ||
} |
} |
||
$(jQuerySelector, jQueryContext).click(clickHandler); |
|||
}; |
}; |
||
Line 3,490: | Line 3,722: | ||
*/ |
*/ |
||
/** |
|||
* @constructor |
|||
* @param {string} [currentAction] |
|||
*/ |
|||
Morebits.batchOperation = function(currentAction) { |
Morebits.batchOperation = function(currentAction) { |
||
var ctx = { |
var ctx = { |
||
Line 3,515: | Line 3,751: | ||
}; |
}; |
||
/** |
|||
* Sets the list of pages to work on |
|||
* @param {String[]} pageList Array of page names (strings) |
|||
*/ |
|||
this.setPageList = function(pageList) { |
this.setPageList = function(pageList) { |
||
ctx.pageList = pageList; |
ctx.pageList = pageList; |
||
}; |
}; |
||
/** |
|||
* Sets a known option: |
|||
* - chunkSize (integer): |
|||
* The size of chunks to break the array into (default 50). |
|||
* Setting this to a small value (<5) can cause problems. |
|||
* - preserveIndividualStatusLines (boolean): |
|||
* Keep each page's status element visible when worker is complete? |
|||
*/ |
|||
this.setOption = function(optionName, optionValue) { |
this.setOption = function(optionName, optionValue) { |
||
ctx.options[optionName] = optionValue; |
ctx.options[optionName] = optionValue; |
||
}; |
}; |
||
/** |
|||
* Runs the given callback for each page in the list. |
|||
* The callback must call workerSuccess when succeeding, or workerFailure when failing. |
|||
* @param {Function} worker |
|||
*/ |
|||
this.run = function(worker) { |
this.run = function(worker) { |
||
if (ctx.running) { |
if (ctx.running) { |
||
Line 3,646: | Line 3,899: | ||
*/ |
*/ |
||
/** |
|||
// The height passed in here is the maximum allowable height for the content area. |
|||
* @constructor |
|||
* @param {number} width |
|||
* @param {number} height The maximum allowable height for the content area. |
|||
*/ |
|||
Morebits.simpleWindow = function SimpleWindow( width, height ) { |
Morebits.simpleWindow = function SimpleWindow( width, height ) { |
||
var content = document.createElement( 'div' ); |
var content = document.createElement( 'div' ); |
||
Line 3,656: | Line 3,913: | ||
$(this.content).dialog({ |
$(this.content).dialog({ |
||
autoOpen: false, |
|||
buttons: { "Placeholder button": function() {} }, |
|||
dialogClass: 'morebits-dialog', |
|||
width: Math.min(parseInt(window.innerWidth, 10), parseInt(width ? width : 800, 10)), |
|||
// give jQuery the given height value (which represents the anticipated height of the dialog) here, so |
|||
// it can position the dialog appropriately |
|||
// the 20 pixels represents adjustment for the extra height of the jQuery dialog "chrome", compared |
|||
// to that of the old SimpleWindow |
|||
height: height + 20, |
|||
close: function(event) { |
|||
// dialogs and their content can be destroyed once closed |
|||
$(event.target).dialog("destroy").remove(); |
|||
}, |
|||
resizeStart: function() { |
|||
this.scrollbox = $(this).find(".morebits-scrollbox")[0]; |
|||
if (this.scrollbox) { |
|||
this.scrollbox.style.maxHeight = "none"; |
|||
} |
|||
}, |
|||
resizeEnd: function() { |
|||
this.scrollbox = null; |
|||
}, |
|||
resize: function() { |
|||
this.style.maxHeight = ""; |
|||
if (this.scrollbox) { |
|||
this.scrollbox.style.width = ""; |
|||
} |
|||
} |
} |
||
} |
}, |
||
resizeEnd: function() { |
|||
this.scrollbox = null; |
|||
}, |
|||
resize: function() { |
|||
this.style.maxHeight = ""; |
|||
if (this.scrollbox) { |
|||
this.scrollbox.style.width = ""; |
|||
} |
|||
} |
|||
}); |
|||
var $widget = $(this.content).dialog("widget"); |
var $widget = $(this.content).dialog("widget"); |
||
Line 3,715: | Line 3,972: | ||
scriptName: null, |
scriptName: null, |
||
/** |
|||
// Focuses the dialog. This might work, or on the contrary, it might not. |
|||
* Focuses the dialog. This might work, or on the contrary, it might not. |
|||
* @returns {Morebits.simpleWindow} |
|||
*/ |
|||
focus: function() { |
focus: function() { |
||
$(this.content).dialog("moveToTop"); |
$(this.content).dialog("moveToTop"); |
||
return this; |
return this; |
||
}, |
}, |
||
// Closes the dialog. If this is set as an event handler, it will stop the event from doing anything more. |
|||
/** |
|||
* Closes the dialog. If this is set as an event handler, it will stop the event |
|||
* from doing anything more. |
|||
* @returns {Morebits.simpleWindow} |
|||
*/ |
|||
close: function(event) { |
close: function(event) { |
||
if (event) { |
if (event) { |
||
Line 3,727: | Line 3,991: | ||
} |
} |
||
$(this.content).dialog("close"); |
$(this.content).dialog("close"); |
||
return this; |
return this; |
||
}, |
}, |
||
// Shows the dialog. Calling display() on a dialog that has previously been closed might work, but it is not guaranteed. |
|||
/** |
|||
* Shows the dialog. Calling display() on a dialog that has previously been closed |
|||
* might work, but it is not guaranteed. |
|||
* @returns {Morebits.simpleWindow} |
|||
*/ |
|||
display: function() { |
display: function() { |
||
if (this.scriptName) { |
if (this.scriptName) { |
||
Line 3,747: | Line 4,015: | ||
} |
} |
||
this.setHeight( this.height ); // init height algorithm |
this.setHeight( this.height ); // init height algorithm |
||
return this; |
return this; |
||
}, |
}, |
||
// Sets the dialog title. |
|||
/** |
|||
* Sets the dialog title. |
|||
* @param {string} title |
|||
* @returns {Morebits.simpleWindow} |
|||
*/ |
|||
setTitle: function( title ) { |
setTitle: function( title ) { |
||
$(this.content).dialog("option", "title", title); |
$(this.content).dialog("option", "title", title); |
||
return this; |
return this; |
||
}, |
}, |
||
// Sets the script name, appearing as a prefix to the title to help users determine which |
|||
/** |
|||
// user script is producing which dialog. For instance, Twinkle modules set this to "Twinkle". |
|||
* Sets the script name, appearing as a prefix to the title to help users determine which |
|||
* user script is producing which dialog. For instance, Twinkle modules set this to "Twinkle". |
|||
* @param {string} name |
|||
* @returns {Morebits.simpleWindow} |
|||
*/ |
|||
setScriptName: function( name ) { |
setScriptName: function( name ) { |
||
this.scriptName = name; |
this.scriptName = name; |
||
return this; |
return this; |
||
}, |
}, |
||
// Sets the dialog width. |
|||
/** |
|||
* Sets the dialog width. |
|||
* @param {number} width |
|||
* @returns {Morebits.simpleWindow} |
|||
*/ |
|||
setWidth: function( width ) { |
setWidth: function( width ) { |
||
$(this.content).dialog("option", "width", width); |
$(this.content).dialog("option", "width", width); |
||
return this; |
return this; |
||
}, |
}, |
||
// Sets the dialog's maximum height. The dialog will auto-size to fit its contents, |
|||
/** |
|||
// but the content area will grow no larger than the height given here. |
|||
* Sets the dialog's maximum height. The dialog will auto-size to fit its contents, |
|||
* but the content area will grow no larger than the height given here. |
|||
* @param {number} height |
|||
* @returns {Morebits.simpleWindow} |
|||
*/ |
|||
setHeight: function( height ) { |
setHeight: function( height ) { |
||
this.height = height; |
this.height = height; |
||
// from display time onwards, let the browser determine the optimum height, |
// from display time onwards, let the browser determine the optimum height, |
||
// and instead limit the height at the given value |
|||
// note that the given height will exclude the approx. 20px that the jQuery UI chrome has in height in addition to the height |
|||
// note that the given height will exclude the approx. 20px that the jQuery UI |
|||
// of an equivalent "classic" Morebits.simpleWindow |
|||
// chrome has in height in addition to the height of an equivalent "classic" |
|||
// Morebits.simpleWindow |
|||
if (parseInt(getComputedStyle($(this.content).dialog("widget")[0], null).height, 10) > window.innerHeight) { |
if (parseInt(getComputedStyle($(this.content).dialog("widget")[0], null).height, 10) > window.innerHeight) { |
||
$(this.content).dialog("option", "height", window.innerHeight - 2).dialog("option", "position", "top"); |
$(this.content).dialog("option", "height", window.innerHeight - 2).dialog("option", "position", "top"); |
||
Line 3,783: | Line 4,069: | ||
} |
} |
||
$(this.content).dialog("widget").find(".morebits-dialog-content")[0].style.maxHeight = parseInt(this.height - 30, 10) + "px"; |
$(this.content).dialog("widget").find(".morebits-dialog-content")[0].style.maxHeight = parseInt(this.height - 30, 10) + "px"; |
||
return this; |
return this; |
||
}, |
}, |
||
// Sets the content of the dialog to the given element node, usually from rendering a Morebits.quickForm. |
|||
/** |
|||
// Re-enumerates the footer buttons, but leaves the footer links as they are. |
|||
* Sets the content of the dialog to the given element node, usually from rendering |
|||
// Be sure to call this at least once before the dialog is displayed... |
|||
* a Morebits.quickForm. |
|||
* Re-enumerates the footer buttons, but leaves the footer links as they are. |
|||
* Be sure to call this at least once before the dialog is displayed... |
|||
* @param {HTMLElement} content |
|||
* @returns {Morebits.simpleWindow} |
|||
*/ |
|||
setContent: function( content ) { |
setContent: function( content ) { |
||
this.purgeContent(); |
this.purgeContent(); |
||
this.addContent( content ); |
this.addContent( content ); |
||
return this; |
return this; |
||
}, |
}, |
||
/** |
|||
* Adds the given element node to the dialog content. |
|||
* @param {HTMLElement} content |
|||
* @returns {Morebits.simpleWindow} |
|||
*/ |
|||
addContent: function( content ) { |
addContent: function( content ) { |
||
this.content.appendChild( content ); |
this.content.appendChild( content ); |
||
Line 3,801: | Line 4,097: | ||
var thisproxy = this; |
var thisproxy = this; |
||
$(this.content).find('input[type="submit"], button[type="submit"]').each(function(key, value) { |
$(this.content).find('input[type="submit"], button[type="submit"]').each(function(key, value) { |
||
value.style.display = "none"; |
|||
var button = document.createElement("button"); |
|||
button.textContent = (value.hasAttribute("value") ? value.getAttribute("value") : (value.textContent ? value.textContent : "Submit Query")); |
|||
// here is an instance of cheap coding, probably a memory-usage hit in using a closure here |
|||
button.addEventListener("click", function() { value.click(); }, false); |
|||
thisproxy.buttons.push(button); |
|||
}); |
|||
// remove all buttons from the button pane and re-add them |
// remove all buttons from the button pane and re-add them |
||
if (this.buttons.length > 0) { |
if (this.buttons.length > 0) { |
||
Line 3,814: | Line 4,110: | ||
$(this.content).dialog("widget").find(".morebits-dialog-buttons")[0].setAttribute("data-empty", "data-empty"); // used by CSS |
$(this.content).dialog("widget").find(".morebits-dialog-buttons")[0].setAttribute("data-empty", "data-empty"); // used by CSS |
||
} |
} |
||
return this; |
return this; |
||
}, |
}, |
||
/** |
|||
* Removes all contents from the dialog, barring any footer links |
|||
* @returns {Morebits.simpleWindow} |
|||
*/ |
|||
purgeContent: function() { |
purgeContent: function() { |
||
this.buttons = []; |
this.buttons = []; |
||
Line 3,825: | Line 4,125: | ||
this.content.removeChild( this.content.firstChild ); |
this.content.removeChild( this.content.firstChild ); |
||
} |
} |
||
return this; |
return this; |
||
}, |
}, |
||
// Adds a link in the bottom-right corner of the dialog. |
|||
/** |
|||
// This can be used to provide help or policy links. |
|||
* Adds a link in the bottom-right corner of the dialog. |
|||
// For example, Twinkle's CSD module adds a link to the CSD policy page, |
|||
* This can be used to provide help or policy links. |
|||
// as well as a link to Twinkle's documentation. |
|||
* For example, Twinkle's CSD module adds a link to the CSD policy page, |
|||
* as well as a link to Twinkle's documentation. |
|||
* @param {string} text Link's text content |
|||
* @param {string} wikiPage Link target |
|||
* @returns {Morebits.simpleWindow} |
|||
*/ |
|||
addFooterLink: function( text, wikiPage ) { |
addFooterLink: function( text, wikiPage ) { |
||
var $footerlinks = $(this.content).dialog("widget").find(".morebits-dialog-footerlinks"); |
var $footerlinks = $(this.content).dialog("widget").find(".morebits-dialog-footerlinks"); |
||
Line 3,846: | Line 4,151: | ||
$footerlinks.append(link); |
$footerlinks.append(link); |
||
this.hasFooterLinks = true; |
this.hasFooterLinks = true; |
||
return this; |
return this; |
||
}, |
}, |
||
/** |
|||
* Set whether the window should be modal or not. |
|||
* If set to true, other items on the page will be disabled, i.e., cannot be |
|||
* interacted with. Modal dialogs create an overlay below the dialog but above |
|||
* other page elements. |
|||
* This must be used (if necessary) before calling display() |
|||
* Default: false |
|||
* @param {boolean} modal |
|||
* @returns {Morebits.simpleWindow} |
|||
*/ |
|||
setModality: function( modal ) { |
setModality: function( modal ) { |
||
$(this.content).dialog("option", "modal", modal); |
$(this.content).dialog("option", "modal", modal); |
||
return this; |
return this; |
||
} |
} |
||
}; |
}; |
||
/** |
|||
// Enables or disables all footer buttons on all Morebits.simpleWindows in the current page. |
|||
* Enables or disables all footer buttons on all Morebits.simpleWindows in the current page. |
|||
// This should be called with |false| when the button(s) become irrelevant (e.g. just before Morebits.status.init is called). |
|||
* This should be called with `false` when the button(s) become irrelevant (e.g. just before |
|||
// This is not an instance method so that consumers don't have to keep a reference to the original |
|||
* Morebits.status.init is called). |
|||
// Morebits.simpleWindow object sitting around somewhere. Anyway, most of the time there will only be one |
|||
* This is not an instance method so that consumers don't have to keep a reference to the |
|||
// Morebits.simpleWindow open, so this shouldn't matter. |
|||
* original Morebits.simpleWindow object sitting around somewhere. Anyway, most of the time |
|||
* there will only be one Morebits.simpleWindow open, so this shouldn't matter. |
|||
* @param {boolean} enabled |
|||
*/ |
|||
Morebits.simpleWindow.setButtonsEnabled = function( enabled ) { |
Morebits.simpleWindow.setButtonsEnabled = function( enabled ) { |
||
$(".morebits-dialog-buttons button").prop("disabled", !enabled); |
$(".morebits-dialog-buttons button").prop("disabled", !enabled); |