MediaWiki:WatchlistMessageCreator.js
Jump to navigation
Jump to search
Note: After saving, you have to bypass your browser's cache to see the changes. Internet Explorer: press Ctrl-F5, Mozilla: hold down Shift while clicking Reload (or press Ctrl-Shift-R), Opera/Konqueror: press F5, Safari: hold down Shift + Alt while clicking Reload, Chrome: hold down Shift while clicking Reload.
Documentation for this user script can be added at MediaWiki:WatchlistMessageCreator. |
/**
* [[MediaWiki:WatchlistMessageCreator.js]]
* //commons.wikimedia.org/w/index.php?title=MediaWiki:WatchlistMessageCreator.js&action=raw
*
* This script is made to be run at [[Help:Watchlist messages/Wizard]].
* It depends on gadgets (see last lines) and [[Template:WatchlistNotice/Wizard/Data]].
*
*
* @rev 2019-03-20
* @author Rillke, 2013
*
* <nowiki>
*/
// List the global variables for jsHint-Validation. Please make sure that it passes http://jshint.com/
// Scheme: globalVariable:allowOverwriting[, globalVariable:allowOverwriting][, globalVariable:allowOverwriting]
/*global jQuery:false, mediaWiki:false, Geo:false*/
// Set jsHint-options. You should not set forin or undef to false if your script does not validate.
/*jshint forin:true, noarg:true, noempty:true, eqeqeq:true, bitwise:true, strict:true, undef:true, curly:false, browser:true, smarttabs:true*/
(function($, mw) {
'use strict';
if ('Help:Watchlist_messages/Wizard' !== mw.config.get('wgPageName')) return;
// Since we do not register this as a gadget, we can not
// take the advantage of CSS-Janus
// Therefore we have to flip ourserlf (nothing complicated, however)
var isRTL = $(document.body).hasClass('rtl'),
right = 'right', left = 'left', ltr = 'ltr', css;
if (isRTL) {
right = 'left';
left = 'right';
ltr = 'rtl';
}
css = [
'#wlc-maincontainer input { direction:' + ltr + ' }',
'ul.wlc-steps { background:white; font-size:larger; }',
'li.arrow.head { font-weight:bold; }',
'button.wlc-backbutton { float:' + left + ' }',
'button.wlc-nextbutton { float:' + right + ' }',
'.wlc-input-wrap { display:inline-block; margin:1.5em }',
'.wlc-input-label { display:block }',
'.wlc-text-wrap { margin:1.5em }',
'ul.wlc-autocomplete { max-height: 300px; overflow-y: auto; overflow-x: hidden; }',
'img.wlc-listimage { margin-' + right + ': 1em; }',
'div.wlc-autocomplete-desc { max-width:45em; font-size:smaller }',
'div.wlc-autocomplete-text { display:inline-block; direction:ltr }',
'#wlc-maincontainer input { padding: 5px }',
'img.wlc-group-icon { float:' + right + '; margin-' + left + ':1em; }',
'div.wlc-selectionrect { border: 1px solid #99f; background: #bbf; position: absolute; z-index: 50; cursor: move; }',
'div.wlc-mapcontainer { position:relative; overflow: hidden; float:' + left + '; }',
'iframe.wlc-dschwen { background: url(\'//upload.wikimedia.org/wikipedia/commons/thumb/b/b0/Openstreetmap_logo.svg/256px-Openstreetmap_logo.svg.png\') ' +
'no-repeat center #def; margin: 0; padding: 0; }',
'div.wlc-geoctrl { margin:1.5em 0.5em }',
'div.wlc-map-overlay { background:#eee; position:absolute; top:0; ' + left + ':0; cursor:crosshair }',
'div.wlc-parser-output { margin-top: 10px; box-shadow: 1px 1px 3px rgba(0,0,0,0.7); boder 1px solid #ddd; padding: 1em; min-height: 200px }',
'div.wlc-confirm-msg { border: 1px solid #aaa; backgound: #ddd; font-family: monospace; ' +
'white-space: pre-wrap; width: 98%; height: 300px; overflow: auto }'
].join('\n');
mw.util.addCSS(css);
// The CSS that will be used for blocking the wizard
// while publishing the message
var blockCSS = {
border: 'none',
padding: '15px',
backgroundColor: '#000',
'border-radius': '10px',
'-webkit-border-radius': '10px',
'-moz-border-radius': '10px',
opacity: 0.5
};
// These messages will be overwritten by localized versions
// The loacalized versions are shiped with the page ([[:commons:Help:Watchlist messages/Wizard]])
// They are inlcuded in a hidden div and make use of [[Help:Autotranslate]]
var i18n = {
'wlc-multiple': '<sup><abbr title="Multiple values are possible. At least one of them must be matched to fulfill this condition.">multi</abbr></sup>',
'wlc-select-area': "Select area",
'wlc-button-proceed': "Next",
'wlc-button-back': "Back",
'wlc-button-publish': "Publish",
'wlc-taglist-placeholder': "Type to get a list of suggestions",
'wlc-float-placeholder': "Number (e.g. 12.84)",
'wlc-date-placeholder': "Date",
'wlc-select-placeholder': "Please select …",
'wlc-l10n-text-placeholder': "Select the correct language and enter text in that language here",
'wlc-freetext-placeholder': "Enter free text here",
'wlc-geo-loading-map': "Retrieving map from $1 …",
'wlc-geo-usage': "Note that when an area is selected, you can\'t zoom or move the map.",
'wlc-geo-info': "Your GeoIP information: Lat: $1, Lon: $2, City: $3, Country: $4",
'wlc-input-required': '<sup title="required" style="color:red; font-weight:bold; cursor:help;">*</sup>',
'wlc-unable-to-proceed': "Due to issues, we are unable to proceed. Click on the listed issues to resolve them.",
'wlc-issue-doublelang': "2 texts in the same language were specified. Either select the correct language or blank the textbox.",
'wlc-issue-invalid-number': "The number for '$1' has an invalid format.",
'wlc-issue-invalid': "An invalid value was specified for '$1'.",
'wlc-issue-required': "A value for the field '$1' is required.",
'wlc-page-sub': "Wizard version: $1",
'wlc-changes-will-be-lost': "When going back to another step, all changes made to this text will be lost.",
'wlc-preview': "Message preview",
'wlc-missing-token': "Unequal opening and closing token count detected. Too many $1 or missing $2.",
'wlc-publishing': "$1 is publishing your message",
'wlc-post-success': "Message successfully posted",
'wlc-how-to-proceed': "How would you like to proceed?",
'wlc-visit-watchlist': "Visit your watchlist",
'wlc-visit-listing': "Go to the listing",
'wlc-show-diff': "Show a diff",
'wlc-post-error': "Error posting while the message",
'wlc-post-error-desc': "Something went wrong posting your message. Please copy & paste the message. Here is a detailed error description: $1",
'wlc-confirm': "Please confirm that everything is correct",
'wlc-editsummary': "Edit summary",
// If you localize it, you have to adapt the template's name
'wlc-templatename': 'WatchlistNotice',
'wlc-langtemplatename': 'LangSwitch',
'wlc-name': "Create-a-watchlist-message Wizard",
'wlc-title': "$1 – $2"
};
mw.messages.set(i18n);
// Small helper functions returning the message text for a given key
// The _msgp parses the message (so e.g. wikilinks are resolved)
var _msg = function(params) {
var args = Array.prototype.slice.call(arguments, 0);
args[0] = 'wlc-' + args[0];
return mw.msg.apply(mw, args);
},
_msgp = function(params) {
var args = Array.prototype.slice.call(arguments, 0);
args[0] = 'wlc-' + args[0];
var msg = mw.message.apply(mw, args);
return msg.parse();
},
userlang = mw.config.get('wgUserLanguage'),
spinnerURL = '//upload.wikimedia.org/wikipedia/commons/e/ed/Cursor_Windows_Vista.gif',
listingPage = 'MediaWiki:WatchlistNotice',
discussionPage = 'MediaWiki talk:WatchlistNotice',
isAuthorized = $.inArray('sysop', mw.config.get('wgUserGroups')) > -1,
watchlistPage = 'Special:Watchlist',
$win = $(window),
hadClick = false,
delay = function () {
var timeoutID = 0;
return function (cb, ms) {
if (timeoutID) clearTimeout(timeoutID);
timeoutID = setTimeout(cb, ms);
};
},
navDelay = delay(),
geoDelay = delay(),
tbDelay = delay();
var wlc = {
version: '0.0.2.2',
$currentStepAnchor: $(),
issues: [],
stepCallbacks: [],
navigation: {
_step: function($pg, cb) {
wlc.stepCallbacks.push({
$pg: $pg,
cb: cb
});
},
_go: function(e) {
if (e) hadClick = true;
var $a = $(this),
$li = $a.data('$li'),
$pg = $a.data('$pg'),
hash = $a.data('hash');
wlc.$currentStepAnchor = $a;
wlc.$pages.hide();
$pg.show();
navDelay(function() {
if (location.hash !== hash) {
location.hash = hash;
}
document.title = _msgp('title', $a.text(), _msg('name'));
}, 10);
wlc.$steps.arrowStepsHighlight($li);
wlc.stepCallbacks = $.grep(wlc.stepCallbacks, function(el, i) {
if (el.$pg[0] === $pg[0]) {
try {
return el.cb(el.$pg);
} catch (ex) {
// callback failed
}
} else {
return true;
}
});
},
_hash: function() {
// Click event is fired before hash change; if navigation was through a link,
// (not via browser history, the correct page is already shown)
if (hadClick) {
hadClick = false;
return;
}
wlc.navigation._go.apply(wlc.$steps.byHash[window.location.hash][0]);
},
_next: function() {
var $as = wlc.$steps.$as,
$nextA = $as.eq($as.index(wlc.$currentStepAnchor) + 1);
if ($nextA.length) {
$nextA.click();
} else {
wlc.navigation.publishHandler();
}
},
_prev: function() {
var $as = wlc.$steps.$as;
$as.eq($as.index(wlc.$currentStepAnchor) - 1).click();
},
_processText: function(txt) {
// This logic is not read from the page.
var nt = $.trim(wlc.val) + '\n',
_ucFirst = function (s) {
return s[0].toUpperCase() + s.substr(1);
},
msg = mw.libs.wikiDOM.parser.text2Obj(nt),
page = mw.libs.wikiDOM.parser.text2Obj(txt),
tlname = _ucFirst(_msg('templatename')),
untilRE = /^\s*until\s*=\s*(\d{4}\-\d{2}-\d{2}(?: \d{2}:\d{2}:\d{2})?)/,
inserted = false,
getUntil = function(tl) {
var u;
$.each(tl.parts, function(i, arr) {
var m = arr[0].match(untilRE);
if (m && m[1]) {
u = m[1];
return false;
}
});
return u;
},
eachWLT = function(templatelist, cb) {
$.each(templatelist, function(i, tl) {
if ( !_ucFirst(tl.parts[0][0]).replace(/_/g, ' ').indexOf(tlname) ) {
return cb(i, tl);
}
});
},
date = (function() {
var d;
eachWLT(msg.nodesByType.template, function(i, tl) {
d = getUntil(tl);
});
return d;
}()),
exploreTemplate = function(tl) {
var u = getUntil(tl);
return (u && u > date);
};
eachWLT(page.nodesByType.template, function(i, tl) {
if (exploreTemplate(tl)) {
tl.before(msg);
inserted = true;
return false;
}
});
if (!inserted) page.append(msg);
return {
text: mw.libs.wikiDOM.parser.obj2Text(page),
summary: wlc.editsummary
};
},
showDialog: function(title, $html) {
mw.loader.using('jquery.ui', function() {
if (!($html instanceof $)) $html = $('<div>').html($html);
$html.dialog({
title: title,
modal: true,
close: function() {
$html.remove();
}
});
});
},
publishHandler: function() {
mw.loader.using(['ext.gadget.libAPI', 'ext.gadget.libWikiDOM', 'ext.gadget.jquery.blockUI'], wlc.navigation._publishHandler);
},
_publishHandler: function() {
var $dlg = $('<div>'),
$summaryL = $('<label for="wlc-editsummary"></label>').text(_msg('editsummary')).appendTo($dlg),
$summary = $('<input type="textbox" maxlength="150" size="50" style="width:98%" id="wlc-editsummary"/>')
.attr('placeholder', _msg('editsummary'))
.val('[[Help:Watchlist messages/Wizard|Wizard]] is ' +
(isAuthorized ? 'adding' : 'suggesting') + ' a new [[Help:Watchlist messages|watchlist message]].')
.appendTo($dlg),
val = isAuthorized ? wlc.val : "\n== New watchlist message ==\n{{edit request}}\n" +
"Please add the following watchlist message:\n<pre>" +
wlc.val + "</pre>\n~~" + "~~",
$text = $('<div>').addClass('wlc-confirm-msg').text(val).appendTo($dlg),
dlgButtons = {};
var _listingDone = function(r) {
var $div = $('<div>').text(_msg('how-to-proceed')),
$ul = $('<ul>').appendTo($div),
makeLink = function(t, l) {
$('<li>').append( $('<a>').text(_msg(t)).attr({
href: l,
target: '_blank'
}) ).appendTo($ul);
};
makeLink('visit-watchlist', mw.util.getUrl(watchlistPage));
makeLink('visit-listing', mw.util.getUrl(listingPage));
if (r && r.edit) makeLink('show-diff', mw.util.wikiScript() + '?' + $.param({
title: r.edit.title,
oldid: r.edit.oldrevid,
diff: r.edit.newrevid
}));
$.unblockUI({
onUnblock: function() {
wlc.navigation.showDialog(_msg('post-success'), $div);
}
});
};
var _listingFailed = function(e) {
$.unblockUI();
wlc.navigation.showDialog(_msg('post-error'), _msgp('post-error-desc', e));
};
dlgButtons[_msg('button-publish')] = function() {
wlc.editsummary = $summary.val();
$dlg.dialog('close');
setTimeout(function() {
$.blockUI({
css: blockCSS,
message: $('<div>').css('color', '#fff').text(_msgp('publishing', _msg('name')))
});
}, 10);
setTimeout(function() {
$.unblockUI();
}, 12000);
if (isAuthorized) {
mw.libs.commons.api.$changeText(listingPage, wlc.navigation._processText).done(_listingDone).fail(_listingFailed);
} else {
mw.libs.commons.api.editPage({
title: discussionPage,
editType: 'appendtext',
text: val,
summary: wlc.editsummary,
cb: function() {
document.location = mw.util.getUrl(discussionPage) + '#footer';
},
errCb: _listingFailed
});
}
};
$dlg.dialog({
title: _msg('confirm'),
modal: true,
buttons: dlgButtons,
width: Math.min($win.width(), 750),
close: function() {
$dlg.remove();
}
});
},
lastPageHandler: function($pg) {
// Validation
wlc.inputFactory.validate();
var $issues = $('<div class="wlc-issue-container ui-state-highlight"></div>').text(_msg('unable-to-proceed')),
$issueList = $('<ul>').appendTo($issues);
$pg.$body.empty();
if (wlc.issues.length) {
wlc.$publishButton.button({
disabled: true
});
$.each(wlc.issues, function(i, issue) {
var $li = $('<li>').appendTo($issueList),
$a = $('<a>').attr({
href: issue.p.hash
}).text(issue.reason).appendTo($li).on('click', function(e) {
e.preventDefault();
wlc.$steps.byHash[issue.p.hash].click();
var $parent = issue.$input.parent();
$parent.addClass('ui-state-highlight');
setTimeout(function() {
$parent.removeClass('ui-state-highlight');
}, 5000);
$('body,html').animate({
'scrollTop': issue.$input.offset().top - 100
});
});
});
$pg.$body.append($issues);
return true;
}
// Template creation
var tl = wlc.inputFactory.getValue(),
$pOut,
onChange,
validateText,
oldText,
$taWrap = $('<div>').appendTo($pg.$body),
$taWarining = $('<div>').addClass('ui-state-highlight').css('visibility', 'hidden').text(_msg('changes-will-be-lost')).appendTo($taWrap),
$taWarining2Wrap = $('<div>').addClass('ui-state-highlight').css({
'visibility': 'hidden',
'min-height': '2em'
}).appendTo($taWrap),
$taWarining2 = $('<ul>').appendTo($taWarining2Wrap),
$ta = $('<textarea>').css('width', '100%').val(tl).appendTo($taWrap);
$taWarining2.$wrap = $taWarining2Wrap;
$taWarining2.addWarning = function(w) {
$taWarining2Wrap.css('visibility', 'visible');
$('<li>').text(w).appendTo(this);
};
var toTest = [['(', ')'], ['[', ']'], ['{', '}'], ['<now' + 'iki>', '</now' + 'iki>'], ['<pre>', '</pre>']];
$.each(toTest, function(e, el) {
el[3] = new RegExp(mw.RegExp.escape(el[0]), 'g');
el[4] = new RegExp(mw.RegExp.escape(el[1]), 'g');
});
validateText = function(val, $errNode) {
$errNode.empty().$wrap.css('visibility', 'hidden');
$.each(toTest, function(e, el) {
var m1 = val.match(el[3]) || [],
m2 = val.match(el[4]) || [];
if (m1.length > m2.length) {
$errNode.addWarning(_msgp('missing-token', el[0], el[1]));
} else if (m1.length < m2.length) {
$errNode.addWarning(_msgp('missing-token', el[1], el[0]));
}
});
};
onChange = function() {
var val = wlc.val = $ta.val();
if (oldText === val) return;
if (tl !== val) {
$taWarining.css('visibility', 'visible');
} else {
$taWarining.css('visibility', 'hidden');
}
wlc.$publishButton.button({
disabled: true
});
$pOut.text(_msg('preview')).css('background', 'url(' + spinnerURL + ') no-repeat center');
validateText(val, $taWarining2);
mw.loader.using('ext.gadget.libAPI', function() {
mw.libs.commons.api.parse(val, userlang, listingPage + '/render', function(r) {
$pOut.css('background', 'none').html(r);
wlc.$publishButton.button({
disabled: false
});
oldText = val;
});
});
};
// Timeout is a IE8 fix
setTimeout(function() {
$ta.height(Math.max($ta[0].scrollHeight, 200));
}, 1);
$pOut = $('<div>').addClass('wlc-parser-output').appendTo($pg.$body);
$ta.on('input change', function() {
tbDelay(onChange, 500);
}).triggerHandler('change');
return true;
}
},
groupFactory: (function() {
var gf = {
groupsById: {},
$get: function(id) {
return this.groupsById[id];
}
};
$('.wlc-groupinfo').find('tr').each(function(i, r) {
if (0 === i) return;
var $tds = $(r).find('td'),
id = $tds.eq(0).text(),
heading = $tds.eq(1).text(),
intro = $tds.eq(2).text(),
outro = $tds.eq(3).text(),
$img = $tds.eq(4).find('img'),
$fs = $('<fieldset>'),
$l = $('<legend>').text(heading).appendTo($fs),
$intro = $('<div>').text(intro).appendTo($fs),
$inputArea = $('<div>').appendTo($fs),
$outro = $('<div>').text(outro).appendTo($fs);
$img.clone().addClass('wlc-group-icon').prependTo($fs);
$fs.$inputArea = $inputArea;
gf.groupsById[id] = $fs;
});
return gf;
}()),
readTable: function(selector, onRow) {
var $trs = $(selector).find('tr');
$trs.each(function(i, r) {
if (0 === i) return;
var $tds = $(r).find('td');
onRow.apply(this, [i, r, $tds]);
});
},
inputFactory: (function() {
var _ify = {
inputs: [],
register: function($input, p, validate, patternOrCallback) {
_ify.inputs.push({
$input: $input,
p: p,
validate: validate,
pcb: patternOrCallback
});
},
getValue: function() {
var out = '';
$.each(_ify.inputs, function(i, el) {
if ($.isFunction(el.pcb)) {
out += '\n ' + el.pcb();
} else {
var val = $.trim(el.$input.val());
if (!val) return;
if ($.isArray(val)) val = val.join(el.p.output);
if ('string' === typeof el.pcb) {
out += '\n ' + el.pcb.replace('$1', val);
} else {
out += '\n |' + el.p.name + ' = ' + val;
}
}
});
out = '\n |id = ' + $.now() + out;
out = '{{' + _msg('templatename') + out + '\n}}';
return out;
},
validate: function() {
wlc.issues = [];
$.each(_ify.inputs, function(i, el) {
if ($.isFunction(el.validate)) {
if (!el.validate(el)) wlc.issues.push(el);
} else if ($.isFunction(el.validate.test)) {
var val = el.$input.val();
if (val.length < 1 && el.p.required) {
wlc.issues.push(el);
el.reason = _msgp('issue-required', el.p.label);
}
if (val.length && !(el.validate.test(val))) {
wlc.issues.push(el);
el.reason = _msgp('issue-invalid', el.p.label);
}
}
});
}
};
var internal = {
floatRE: /^\-?\d+(?:\.\d+)?$/,
taglistcache: {},
_numbersonly: function($i) {
return $i.on('input', function() {
var o_val = $i.val(),
n_val = o_val.replace(/[^\d.]/g, '');
if (o_val !== n_val) $i.val(n_val);
});
},
_rch: null,
_requestCoords: function($iframe, $rect, $ctrl) {
var _g360 = function(x) {
return x % 360;
},
_g180 = function(x) {
if (x > 180) x -= 360;
return x;
},
_l0 = function(x) {
if (x < 0) x = 360 - x;
return x;
};
geoDelay(function() {
if (internal._rch) $win.off('message', internal._rch);
internal._rch = function(e) {
var r = JSON.parse(e.originalEvent.data).response,
tl = r.topleft,
rb = r.rightbottom,
latdiff = rb.lat - tl.lat,
londiff = _l0(rb.lon - tl.lon),
iw = $iframe.width(),
ih = $iframe.height(),
latPerPx = latdiff / ih,
lonPerPx = londiff / iw,
rw = $rect.width(),
rh = $rect.height(),
rpos = $rect.position(),
rt = rpos.top,
rl = rpos.left,
lonLeft = tl.lon + lonPerPx * rl,
lonRight = _g180(_g360(lonLeft + lonPerPx * rw)),
latTop = tl.lat + latPerPx * rt,
latBottom = latTop + latPerPx * rh;
lonLeft = _g180(_g360(lonLeft));
$ctrl.lon.from.val(lonLeft);
$ctrl.lon.to.val(lonRight);
$ctrl.lat.from.val(latTop);
$ctrl.lat.to.val(latBottom);
};
$win.on('message', internal._rch);
// Fetch the coords of the iframe borders
$iframe[0].contentWindow.postMessage(JSON.stringify({
getcoords: 1
}), location.protocol + $iframe.attr('src'));
}, 500);
}
};
_ify.create = {
localized_text: function(p) {
var ld = 0,
lds = [],
createLangDesc,
$ctrl = $('<div>').attr('class', 'wlc-input-wrap'),
$sel = $('<select>').attr('size', 1),
$opt = $('<option>'),
$ta = $('<textarea>').attr({
'id': 'wlc-ip-' + p.name + '_' + ld,
'class': 'wlc-input',
'style': 'height:6em; min-width:15em',
'placeholder': p.placeholder || _msg('l10n-text-placeholder')
}),
$l = $('<label>').attr({
'for': 'wlc-ip-' + p.name,
'class': 'wlc-input-label'
}).text(p.label).appendTo($ctrl);
$ctrl.$label = $l;
$.each(window.wpAvailableLanguages, function(k, v) {
$opt.clone().attr('value', k).text(v).appendTo($sel);
});
createLangDesc = function(e) {
ld++;
if (e) $(this).off('input change');
var $ld = $('<div>').attr('class', 'wlc-input-wrap'),
$selI = $sel.clone().appendTo($ld),
$taI = $ta.clone().appendTo($ld).one('input change', createLangDesc);
$selI.val(userlang);
$ld.appendTo($ctrl);
lds.push({
$sel: $selI,
$ta: $taI
});
};
createLangDesc();
_ify.register($ctrl, p, function(e) {
var seenLang = {},
txt = '',
totalTextLen = 0,
isValid = true;
e.reason = _msg('issue-doublelang');
$.each(lds, function(i, el) {
txt = $.trim(el.$ta.val());
totalTextLen += txt.length;
if (txt) {
var l = el.$sel.val();
if (l in seenLang) return (isValid = false);
seenLang[l] = true;
}
});
if (isValid && totalTextLen < 20) {
e.reason = _msgp('issue-required', p.label);
isValid = false;
}
return isValid;
}, function() {
var out = '',
txts = [],
hadDefault = false;
$.each(lds, function(i, el) {
var txt = $.trim(el.$ta.val()),
l = el.$sel.val();
if (txt) {
out += '\n | ' + l + ' = ' + txt;
txts.push(txt);
if (l === 'en') hadDefault = true;
}
});
if (!hadDefault) {
out += '\n | default = ' + txts[0];
}
out = '|' + p.name + ' = ' + '{{' + _msg('langtemplatename') + out + '\n }}';
return out;
});
return $ctrl;
},
select: function(p) {
var $ctrl = $('<div>').attr('class', 'wlc-input-wrap'),
$l = $('<label>').attr({
'for': 'wlc-ip-' + p.name,
'class': 'wlc-input-label'
}).text(p.label).appendTo($ctrl),
$selConfirm = $('<div>').css('min-height', '1.4em').appendTo($ctrl),
$sel = $('<select size="1"></select>').attr('id', 'wlc-ip-' + p.name).appendTo($ctrl),
listtype = p.paraminfo.replace(/^(.+?)\:.+$/, '$1'),
listvalue = p.paraminfo.replace(/^.+?\:(.+)$/, '$1');
$ctrl.$label = $l;
p.placeholder = p.placeholder || _msg('select-placeholder');
$('<option>').attr({
'value': ''
}).text(p.placeholder).data('sel', p.placeholder).appendTo($sel);
var _onHover = function() {
$selConfirm.text($(this).data('sel'));
},
_onChange = function() {
$selConfirm.text($sel.find('option:selected').data('sel'));
};
$sel.change(_onChange).on('mouseenter', 'option', _onHover);
switch (listtype) {
case 'window':
break;
case 'table':
wlc.readTable('.' + listvalue, function(i, r, $tds) {
$('<option>').attr({
'value': $.trim($tds.eq(0).text())
}).text($tds.eq(1).text()).data('sel', $tds.eq(2).text()).appendTo($sel);
});
break;
}
_ify.register($sel, p, /.*/, 0);
return $ctrl;
},
date: function(p) {
var $ctrl = $('<div>').attr('class', 'wlc-input-wrap'),
vRE = /^\d{4}\-\d{2}-\d{2}(?: \d{2}:\d{2}:\d{2})?$/,
$l = $('<label>').attr({
'for': 'wlc-ip-' + p.name,
'class': 'wlc-input-label'
}).text(p.label).appendTo($ctrl),
$i = $('<input type="text" size="40"/>').attr({
'id': 'wlc-ip-' + p.name,
'class': 'wlc-input',
'pattern': vRE.source,
'placeholder': p.placeholder || _msg('date-placeholder'),
'value': p.defaultVal
}).appendTo($ctrl).datepicker({
changeYear: true,
'dateFormat': p.paraminfo || 'yy-mm-dd 12:00:00',
showWeek: true,
firstDay: 1
});
if (p.required) $i.attr('required', true);
$ctrl.$label = $l;
_ify.register($i, p, vRE, 0);
return $ctrl;
},
taglist: function(p) {
var availableTags = [],
supportsChosen = window.AbstractChosen && window.AbstractChosen.browser_is_supported(),
placeholder = p.placeholder || _msg('taglist-placeholder'),
k, l, o, st, v,
listtype = p.paraminfo.replace(/^(.+?)\:.+$/, '$1'),
listvalue = p.paraminfo.replace(/^.+?\:(.+)$/, '$1');
var $ctrl = $('<div>').attr('class', 'wlc-input-wrap'),
$l = $('<label>').attr({
'for': 'wlc-ip-' + p.name,
'class': 'wlc-input-label'
}).text(p.label).append($.parseHTML(_msg('multiple'))).appendTo($ctrl),
$i = $(supportsChosen ? '<select>' : '<input size="50">').attr({
'multiple': 'multiple',
'id': 'wlc-ip-' + p.name,
'class': 'wlc-input',
'placeholder': placeholder,
'data-placeholder': placeholder
}).appendTo($ctrl),
$text = $('<div>').attr('class', 'wlc-autocomplete-text'),
$ti = $('<span>').attr('class', 'wlc-autocomplete-text-inner'),
$desc = $('<div>').attr('class', 'wlc-autocomplete-desc'),
_onDataAvailable = function(d, immediate) {
if (supportsChosen) {
$.each(d, function(i, item) {
var $t = $text.clone();
$ti.clone().text(item.label).appendTo($t);
if (item.desc) $desc.clone().text(item.desc).appendTo($t);
if (item.$icon) item.$icon.clone().addClass('wlc-listimage').prependTo($t);
$('<option>').attr({
value: item.value
}).append($t).appendTo($i);
});
var finish = function() {
$i.chosen();
_ify.register($i, p, /.+/, 0);
};
if (immediate) return finish();
setTimeout(finish, 1000);
} else {
// http://jqueryui.com/autocomplete/#multiple
var re = new RegExp(mw.RegExp.escape(p.output) + '\\s*');
var split = function(val) {
return val.split(re);
};
var extractLast = function(term) {
return split(term).pop();
};
// don't navigate away from the field on tab when selecting an item
$i.on('keydown', function(e) {
if (e.keyCode === $.ui.keyCode.TAB &&
$i.data('ui-autocomplete').menu.active) {
e.preventDefault();
}
}).autocomplete({
minLength: 0,
source: function(request, response) {
// delegate back to autocomplete, but extract the last term
response($.ui.autocomplete.filter(
availableTags, extractLast(request.term)));
},
focus: function() {
// prevent value inserted on focus
return false;
},
select: function(event, ui) {
var terms = split(this.value);
// remove the current input
terms.pop();
// add the selected item
terms.push(ui.item.value);
// add placeholder to get the comma-and-space at the end
terms.push('');
this.value = terms.join(p.output);
return false;
}
});
var $autocomplete = $i.data('ui-autocomplete') || $i.data('uiAutocomplete') || $i.data('autocomplete'),
ri = $autocomplete._renderItem;
$autocomplete._renderItem = function(ul, item) {
var $li = ri.apply($autocomplete, [ul, item]),
$a = $li.find('a');
var $t = $text.clone().append($a.contents()).appendTo($a);
if (item.desc) $desc.clone().text(item.desc).appendTo($t);
if (item.$icon) item.$icon.clone().addClass('wlc-listimage').prependTo($a);
return $li;
};
$i.autocomplete('widget').filter('ul.ui-autocomplete').find('ul.ui-autocomplete').addBack().addClass('wlc-autocomplete');
_ify.register($i, p, /.+/, 0);
}
};
$ctrl.$label = $l;
p.output = $.trim(p.output).replace('space', ' ');
var taglistcache = internal.taglistcache;
taglistcache[listtype] = taglistcache[listtype] || {};
if (listvalue in taglistcache[listtype]) {
availableTags = taglistcache[listtype][listvalue];
_onDataAvailable(availableTags);
} else {
switch (listtype) {
case 'window':
l = window[listvalue];
if ('object' === typeof l) {
for (k in l) {
if (Object.prototype.hasOwnProperty.call(l, k)) {
v = l[k];
availableTags.push({
label: v + ' (' + k + ')',
value: k
});
}
}
}
_onDataAvailable(availableTags);
break;
case 'obj':
o = window;
st = listvalue.split('.');
while (st.length) {
o = o[st.shift()];
}
for (k in o) {
if (Object.prototype.hasOwnProperty.call(o, k)) {
v = o[k];
try {
v = v + '';
} catch (ex) { /* converting to string failed */ }
if (k.indexOf(p.output) >= 0 || (v && $.isFunction(v.indexOf) && v.indexOf(p.output) >= 0)) continue;
availableTags.push({
label: k + ' (' + v.slice(0, 30) + (v.length > 30 ? '…' : '') + ')',
value: k + '=' + v,
k: k
});
}
}
if (listvalue === 'mw.user.options.values') {
// Handle this special case,
// Fetch description from [[Special:Preferences]]
// @todo: This used to fetch the HTML page of Special:Preferences
// via $.ajax() and then look for `[for="mw-input-wp' + el.k + '"]')`
// elements and get the text of the label after it.
// This has not been working for a while, and was thus removed
// to simplify the code. --Krinkle 20 March 2019.
_onDataAvailable(availableTags, true);
} else {
_onDataAvailable(availableTags);
}
break;
case 'table':
wlc.readTable('.' + listvalue, function(i, r, $tds) {
v = $tds.eq(0).text();
availableTags.push({
value: v,
label: $tds.eq(1).text() + ' (' + v + ')',
$icon: $tds.eq(2).find('img')
});
});
_onDataAvailable(availableTags);
break;
}
taglistcache[listtype][listvalue] = availableTags;
}
return $ctrl;
},
geoedit: function(p, $pg) {
window.Geo = Geo || {};
var _this = this,
w = 600,
h = 400,
lat = Geo.lat || 0,
lon = Geo.lon || 0;
var $createInputs = function(id, label, key, $geoCtrl) {
var $l = $('<label class="wlc-input-label"></label>').text(label),
idn = 'wlc-ip-' + id + '-from',
idx = 'wlc-ip-' + id + '-to',
$d1 = $('<div>').css('display', 'inline-block'),
$d2 = $('<div>').css('display', 'inline-block'),
$l1 = $('<label>').attr('for', idn).text('From ').appendTo($d1),
$l2 = $('<label>').attr('for', idx).text('To ').appendTo($d2),
$i1 = internal._numbersonly($('<input type="text" size="20"/>').attr({
'id': idn,
pattern: internal.floatRE.source,
placeholder: _msg('float-placeholder')
})).appendTo($d1),
$i2 = internal._numbersonly($('<input type="text" size="20"/>').attr({
'id': idx,
pattern: internal.floatRE.source,
placeholder: _msg('float-placeholder')
})).appendTo($d2);
$geoCtrl[key] = {
from: $i1,
to: $i2
};
return $('<div>').append($l, ' ', $d1, ' ', $d2);
};
var $ctrl = $('<div>').attr({
'class': 'wlc-input-wrap',
'dir': ltr
}).css('display', 'block'),
$map = $('<div class="wlc-mapcontainer"></div>').height(h).width(w).prependTo($ctrl),
$iframe, $button,
$usage = $('<div>').text(_msg('geo-usage')).hide().appendTo($ctrl),
$waiting = $('<div class="wlc-geoctrl wlc-input-wrap"></div>').text(_msgp('geo-loading-map', 'WMFLabs (OSM/Dschwen/Dispenser)')).appendTo($ctrl),
$selection, $overlay, _requestCoords,
$geoCtrl = $('<div class="wlc-geoctrl wlc-input-wrap"></div>').appendTo($ctrl),
$lat = $createInputs('lat', "Latitude", 'lat', $geoCtrl).appendTo($geoCtrl),
$lon = $createInputs('lon', "Longitude", 'lon', $geoCtrl).appendTo($geoCtrl),
$geoCtrls = $geoCtrl.find('input'),
$info = $('<div>').text(_msgp('geo-info', Geo.lat, Geo.lon, Geo.city, Geo.country)).appendTo($ctrl);
// Ident_ify as Wikipedia (we don't want to see an image collection but labels!)
$iframe = $('<iframe>').attr({
scrolling: 'no',
frameBorder: 0,
'class': 'wlc-dschwen'
}).css({
width: w,
height: h
}).appendTo($map);
wlc.navigation._step($pg, function() {
// IE: You must append the iframe before setting the src attribute
$iframe.on( 'load', function() {
$waiting.fadeOut();
if ($selection) _requestCoords();
}).attr('src', '//wma.wmflabs.org/iframe.html?' + $.param({
wma: lat + '_' + lon + '_' + w + '_' + h + '_' + userlang + '_3_' + userlang,
globe: 'Earth',
lang: userlang,
page: '',
awt: 0
}));
return false;
});
var _cleanUp = function(e, resetCtrls) {
$('body').off('mouseup.wlc-selection');
if ($selection) $selection.remove();
if ($overlay) $overlay.remove();
$overlay = $selection = null;
if (resetCtrls !== false) $geoCtrls.val('');
$button.button({
disabled: false
});
$usage.hide();
return false;
};
$geoCtrls.on('input change', function() {
_cleanUp(null, false);
});
$('<button role="button" type="button"></button>').text('Remove selection').button({
icons: {
primary: 'ui-icon-circle-close'
}
}).addClass('ui-button-red').on('click', _cleanUp).insertAfter($map);
$button = $('<button role="button" type="button"></button>').text('Select area').button({
icons: {
primary: 'ui-icon-circle-plus'
}
}).addClass('ui-button-blue').on('click', function() {
$button.button({
disabled: true
});
_cleanUp();
$usage.show();
_requestCoords = function() {
internal._requestCoords($iframe, $selection, $geoCtrl);
};
var parentOffset = $map.offset(),
relXs, relYs, relX, relY, mousedown, isNegX, isNegY,
_registerMouseUp = function() {
$('body').one('mouseup.wlc-selection', function() {
mousedown = false;
$button.button({
disabled: false
});
if ($overlay) $overlay.css('cursor', 'default');
_requestCoords();
});
};
$selection = $('<div class="wlc-selectionrect"></div>')
.attr('title', 'You can drag me around and resize me').fadeTo(0, 0.7).prependTo($map);
$overlay = $('<div class="wlc-map-overlay"></div>')
.height(h).width(w).fadeTo(0, 0).prependTo($map).one('mousedown', function(e) {
e.preventDefault();
if (e.which !== 1) return;
_registerMouseUp();
relXs = e.pageX - parentOffset.left;
relYs = e.pageY - parentOffset.top;
$selection.css({
top: relYs,
left: relXs
});
mousedown = true;
}).mousemove(function(e) {
if (!mousedown) return;
relX = e.pageX - parentOffset.left;
relY = e.pageY - parentOffset.top;
var diffX = relX - relXs,
diffY = relY - relYs;
if (diffX < 0) {
isNegX = true;
$selection.css('left', relX);
} else if (isNegX) {
isNegX = false;
$selection.css('left', relXs);
}
if (diffY < 0) {
isNegY = true;
$selection.css('top', relY);
} else if (isNegY) {
isNegY = false;
$selection.css('top', relYs);
}
$selection.width(Math.abs(diffX)).height(Math.abs(diffY));
});
$selection.draggable({
containment: 'parent',
stop: _requestCoords
}).resizable({
stop: _requestCoords
});
}).insertAfter($map);
var reg = function(n) {
_ify.register($geoCtrls.eq(n), $.extend({}, p, {
name: p.name.split('|')[n]
}), internal.floatRE, 0);
};
reg(0);
reg(1);
reg(2);
reg(3);
return $ctrl;
},
'float': function(p) {
var $ctrl = $('<div>').attr('class', 'wlc-input-wrap'),
$l = $('<label>').attr({
'for': 'wlc-ip-' + p.name,
'class': 'wlc-input-label'
}).text(p.label).appendTo($ctrl),
$i = $('<input type="text" size="40"/>').attr({
'id': 'wlc-ip-' + p.name,
'class': 'wlc-input',
'pattern': internal.floatRE.source,
'placeholder': p.placeholder || _msg('float-placeholder')
}).appendTo($ctrl);
$ctrl.$label = $l;
internal._numbersonly($i);
_ify.register($i, p, internal.floatRE, 0);
return $ctrl;
},
'freetext': function(p) {
var $ctrl = $('<div>').attr('class', 'wlc-input-wrap'),
$l = $('<label>').attr({
'for': 'wlc-ip-' + p.name,
'class': 'wlc-input-label'
}).text(p.label).appendTo($ctrl),
$i = $('<input type="text" size="60"/>').attr({
'id': 'wlc-ip-' + p.name,
'class': 'wlc-input',
'placeholder': p.placeholder || _msg('freetext-placeholder')
}).appendTo($ctrl);
$ctrl.$label = $l;
_ify.register($i, p, function() { return true; }, 0);
return $ctrl;
}
};
return _ify;
}()),
$pages: $(),
$publishButton: null,
$createPage: function(id, hash, intro, outro) {
var $pg = $('<div>'),
$top = $('<div>').addClass('wlc-text-wrap').html(intro).appendTo($pg),
$body = $('<div>').addClass('wlc-text-wrap').appendTo($pg),
$bottom = $('<div>').addClass('wlc-text-wrap').html(outro).appendTo($pg),
$buttons = $('<div>').addClass('wlc-text-wrap').appendTo($pg);
wlc.$pages = wlc.$pages.add($pg);
$pg.$body = $body;
// Now, create the inputs...
$('.wlc-paraminfo').find('tr').each(function(i, r) {
if (0 === i) return;
var $tds = $(r).find('td'),
step = $tds.eq(0).text(),
group = $tds.eq(1).text(),
type = $tds.eq(3).text(),
params = {
name: $tds.eq(2).text(),
paraminfo: $tds.eq(4).text(),
output: $tds.eq(5).text(),
label: $tds.eq(6).text(),
placeholder: $tds.eq(7).text(),
required: !! $.trim($tds.eq(8).text()),
defaultVal: $tds.eq(9).text(),
hash: hash
};
if (id !== step) return;
var $group = wlc.groupFactory.$get(group),
$ip = wlc.inputFactory.create[type](params, $pg);
if (params.required && $ip.$label) $ip.$label.append($.parseHTML(_msg('input-required')));
if ($group) {
if ($ip) $group.$inputArea.append($ip);
if (!$group.attached) {
$group.attached = true;
$body.append($group);
}
} else {
if ($ip) $body.append($ip);
}
});
$('<button role="button" type="button" class="wlc-backbutton">').text(_msg('button-back')).button({
disabled: (wlc.$steps.$lis.length === 0)
}).on('click', wlc.navigation._prev).appendTo($buttons);
var buttonLabel;
if (wlc.$steps.count - 1 === Number(id)) {
buttonLabel = _msg('button-publish');
wlc.navigation._step($pg, wlc.navigation.lastPageHandler);
} else {
buttonLabel = _msg('button-proceed');
}
wlc.$publishButton = $('<button role="button" type="button" class="wlc-nextbutton">')
.text(buttonLabel).button({
icons: {
primary: 'ui-icon-circle-check'
}
}).on('click', wlc.navigation._next).appendTo($buttons);
return $pg;
},
createSteps: function() {
var $ul = wlc.$steps = $('<ul>').attr('class', "ui-helper-clearfix ui-state-default ui-widget ui-helper-reset wlc-steps"),
$trs = $('.wlc-stepinfo').find('tr'),
$contentText = $('<div id="wlc-maincontainer">').attr('dir', ltr).appendTo($('#mw-content-text').contents().hide().parent());
$ul.plain = [];
$ul.$lis = $();
$ul.$as = $();
$ul.byHash = {};
$ul.count = $trs.length - 1;
$trs.each(function(i, r) {
if (!i) return;
var $tds = $(r).find('td'),
txt = $tds.eq(1).text(),
id = $tds.eq(0).text(),
intro = $tds.eq(2).html(),
outro = $tds.eq(3).html(),
hash = '#step' + id,
$pg = wlc.$createPage(id, hash, intro, outro).hide().appendTo($contentText),
$li = $('<li>').appendTo($ul),
$div = $('<div>').appendTo($li),
$a = $('<a>').attr({
href: '#step' + id
}).text(txt).data({
'$pg': $pg,
'$li': $li,
'id': id,
'hash': hash
}).on('click', wlc.navigation._go).appendTo($div);
$ul.plain.push(txt);
$ul.$lis = $ul.$lis.add($li);
$ul.$as = $ul.$as.add($a);
$ul.byHash[hash] = $a;
});
$ul.arrowSteps();
$ul.prependTo($contentText);
if (!location.hash) {
$ul.$lis.eq(0).find('a').click();
} else {
this.navigation._hash();
}
},
install: function() {
wlc.readTranslation();
$.datepicker.setDefaults($.datepicker.regional[userlang]);
$('#siteSub').text(_msgp('page-sub', wlc.version));
$('#firstHeading').find('span').text(_msg('name'));
$('table.wikitable td').each(function(){$(this).text($(this).text().trim());});
wlc.createSteps();
$win.on('hashchange', wlc.navigation._hash);
},
/**
* Reads the translation from the HTML-DOM and overwrites the English default
* translation
*
* @example
* wlc.readTranslation();
*
* @context {any} May be called in and from all contexts.
* @return {undefined}
*/
readTranslation: function() {
var i18nNew = {};
$.each(i18n, function(k, v) {
var t = $('#' + k).html();
if (t) {
i18nNew[k] = t;
}
});
mw.messages.set(i18nNew);
}
};
// Expose globally
window.watchlistMessageCreator = wlc;
$(function () {
mw.loader.using([
// 'ext.gadget.jquery.arrowSteps',
'ext.uploadWizard', // .jquery.arrowSteps phab:T144974
'jquery.ui',
'jquery.ui',
'jquery.ui',
'jquery.ui',
'jquery.ui',
'mediawiki.util'
]).then(wlc.install);
});
}(jQuery, mediaWiki));
// </nowiki>