User:Ebrahames/All-in-one.js
Appearance
Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. A guide to help you find broken scripts is available. If you are unsure whether code you are adding to this page is safe, you can ask at the appropriate village pump. This code will be executed when previewing this page. |
![]() | Documentation for this user script can be added at User:Ebrahames/All-in-one. |
/*
This is an experimental attempt to integrate various user scripts,
which the user can switch on or off in the preferences page.
Read more at http://wiki.riteme.site/wiki/User:Cameltrader/All-in-one.js/Description
This page was designed with the hope to make scripts more readable.
It is both valid javascript and valid wikimarkup.
*/
<!-- --><div class="NavFrame" style="text-align: left; background-color: #dde;"><div class="NavHead" style="display: inline; padding-right: 4em; background-color: transparent;">Main.js</div><div class="NavContent" style="text-align: left"><pre><nowiki>
$(function () {
Ajax.Responders.register({
onException: function (exception) {
alert("exception=" + exception + "; message=" + exception.message);
}
});
new CameltraderPluginManager();
});
/*
// Probably a future user script
function fixCyrillicAnchors() {
// Precompute utf-8-to-codepoint conversion table
var cyr = {};
for (var ch = 0x0400; ch <= 0x052f; ch++) {
var chUtf8 = new Number( ((ch << 2) & 0x1f00) + (ch & 0x3f) + 0xc080 ).toString(16);
var chPercent = "%" + chUtf8.substring(0, 2) + "%" + chUtf8.substring(2);
cyr[chPercent] = cyr[chPercent.toUpperCase()] = String.fromCharCode(ch);
}
for (var i = 0; i < document.links.length; i++) {
var href = document.links[i].href;
var re = /%(D[01])%([0-9a-fA-F]{2})/gi;
while (true) {
re.lastIndex = 0;
var m = re.exec(href);
if (m == null) {
break;
}
href = href.substring(0, m.index) + cyr[m[0]]
+ href.substring(m.index + m[0].length);
}
document.links[i].href = href;
}
}
*/
//</nowiki></pre></div></div>
<!-- --><div class="NavFrame" style="text-align: left; background-color: #dde;"><div class="NavHead" style="display: inline; padding-right: 4em; background-color: transparent;">prototype.js</div><div class="NavContent" style="text-align: left"><pre><nowiki>
/* Prototype JavaScript framework, version 1.5.0
* (c) 2005-2007 Sam Stephenson
*
* Prototype is freely distributable under the terms of an MIT-style license.
* For details, see the Prototype web site: http://prototype.conio.net/
*
/*--------------------------------------------------------------------------*/
var Prototype = {
Version: '1.5.0',
BrowserFeatures: {
XPath: !!document.evaluate
},
ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
emptyFunction: function() {},
K: function(x) { return x }
}
var Class = {
create: function() {
return function() {
this.initialize.apply(this, arguments);
}
}
}
var Abstract = new Object();
Object.extend = function(destination, source) {
for (var property in source) {
destination[property] = source[property];
}
return destination;
}
Object.extend(Object, {
inspect: function(object) {
try {
if (object === undefined) return 'undefined';
if (object === null) return 'null';
return object.inspect ? object.inspect() : object.toString();
} catch (e) {
if (e instanceof RangeError) return '...';
throw e;
}
},
keys: function(object) {
var keys = [];
for (var property in object)
keys.push(property);
return keys;
},
values: function(object) {
var values = [];
for (var property in object)
values.push(object[property]);
return values;
},
clone: function(object) {
return Object.extend({}, object);
}
});
Function.prototype.bind = function() {
var __method = this, args = $A(arguments), object = args.shift();
return function() {
return __method.apply(object, args.concat($A(arguments)));
}
}
Function.prototype.bindAsEventListener = function(object) {
var __method = this, args = $A(arguments), object = args.shift();
return function(event) {
return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments)));
}
}
Object.extend(Number.prototype, {
toColorPart: function() {
var digits = this.toString(16);
if (this < 16) return '0' + digits;
return digits;
},
succ: function() {
return this + 1;
},
times: function(iterator) {
$R(0, this, true).each(iterator);
return this;
}
});
var Try = {
these: function() {
var returnValue;
for (var i = 0, length = arguments.length; i < length; i++) {
var lambda = arguments[i];
try {
returnValue = lambda();
break;
} catch (e) {}
}
return returnValue;
}
}
/*--------------------------------------------------------------------------*/
var PeriodicalExecuter = Class.create();
PeriodicalExecuter.prototype = {
initialize: function(callback, frequency) {
this.callback = callback;
this.frequency = frequency;
this.currentlyExecuting = false;
this.registerCallback();
},
registerCallback: function() {
this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
},
stop: function() {
if (!this.timer) return;
clearInterval(this.timer);
this.timer = null;
},
onTimerEvent: function() {
if (!this.currentlyExecuting) {
try {
this.currentlyExecuting = true;
this.callback(this);
} finally {
this.currentlyExecuting = false;
}
}
}
}
String.interpret = function(value){
return value == null ? '' : String(value);
}
Object.extend(String.prototype, {
gsub: function(pattern, replacement) {
var result = '', source = this, match;
replacement = arguments.callee.prepareReplacement(replacement);
while (source.length > 0) {
if (match = source.match(pattern)) {
result += source.slice(0, match.index);
result += String.interpret(replacement(match));
source = source.slice(match.index + match[0].length);
} else {
result += source, source = '';
}
}
return result;
},
sub: function(pattern, replacement, count) {
replacement = this.gsub.prepareReplacement(replacement);
count = count === undefined ? 1 : count;
return this.gsub(pattern, function(match) {
if (--count < 0) return match[0];
return replacement(match);
});
},
scan: function(pattern, iterator) {
this.gsub(pattern, iterator);
return this;
},
truncate: function(length, truncation) {
length = length || 30;
truncation = truncation === undefined ? '...' : truncation;
return this.length > length ?
this.slice(0, length - truncation.length) + truncation : this;
},
strip: function() {
return this.replace(/^\s+/, '').replace(/\s+$/, '');
},
stripTags: function() {
return this.replace(/<\/?[^>]+>/gi, '');
},
stripScripts: function() {
return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
},
extractScripts: function() {
var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
return (this.match(matchAll) || []).map(function(scriptTag) {
return (scriptTag.match(matchOne) || ['', ''])[1];
});
},
evalScripts: function() {
return this.extractScripts().map(function(script) { return eval(script) });
},
escapeHTML: function() {
var div = document.createElement('div');
var text = document.createTextNode(this);
div.appendChild(text);
return div.innerHTML;
},
unescapeHTML: function() {
var div = document.createElement('div');
div.innerHTML = this.stripTags();
return div.childNodes[0] ? (div.childNodes.length > 1 ?
$A(div.childNodes).inject('',function(memo,node){ return memo+node.nodeValue }) :
div.childNodes[0].nodeValue) : '';
},
toQueryParams: function(separator) {
var match = this.strip().match(/([^?#]*)(#.*)?$/);
if (!match) return {};
return match[1].split(separator || '&').inject({}, function(hash, pair) {
if ((pair = pair.split('='))[0]) {
var name = decodeURIComponent(pair[0]);
var value = pair[1] ? decodeURIComponent(pair[1]) : undefined;
if (hash[name] !== undefined) {
if (hash[name].constructor != Array)
hash[name] = [hash[name]];
if (value) hash[name].push(value);
}
else hash[name] = value;
}
return hash;
});
},
toArray: function() {
return this.split('');
},
succ: function() {
return this.slice(0, this.length - 1) +
String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
},
camelize: function() {
var parts = this.split('-'), len = parts.length;
if (len == 1) return parts[0];
var camelized = this.charAt(0) == '-'
? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
: parts[0];
for (var i = 1; i < len; i++)
camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
return camelized;
},
capitalize: function(){
return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
},
underscore: function() {
return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
},
dasherize: function() {
return this.gsub(/_/,'-');
},
inspect: function(useDoubleQuotes) {
var escapedString = this.replace(/\\/g, '\\\\');
if (useDoubleQuotes)
return '"' + escapedString.replace(/"/g, '\\"') + '"';
else
return "'" + escapedString.replace(/'/g, '\\\'') + "'";
}
});
String.prototype.gsub.prepareReplacement = function(replacement) {
if (typeof replacement == 'function') return replacement;
var template = new Template(replacement);
return function(match) { return template.evaluate(match) };
}
String.prototype.parseQuery = String.prototype.toQueryParams;
var Template = Class.create();
Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
Template.prototype = {
initialize: function(template, pattern) {
this.template = template.toString();
this.pattern = pattern || Template.Pattern;
},
evaluate: function(object) {
return this.template.gsub(this.pattern, function(match) {
var before = match[1];
if (before == '\\') return match[2];
return before + String.interpret(object[match[3]]);
});
}
}
var $break = new Object();
var $continue = new Object();
var Enumerable = {
each: function(iterator) {
var index = 0;
try {
this._each(function(value) {
try {
iterator(value, index++);
} catch (e) {
if (e != $continue) throw e;
}
});
} catch (e) {
if (e != $break) throw e;
}
return this;
},
eachSlice: function(number, iterator) {
var index = -number, slices = [], array = this.toArray();
while ((index += number) < array.length)
slices.push(array.slice(index, index+number));
return slices.map(iterator);
},
all: function(iterator) {
var result = true;
this.each(function(value, index) {
result = result && !!(iterator || Prototype.K)(value, index);
if (!result) throw $break;
});
return result;
},
any: function(iterator) {
var result = false;
this.each(function(value, index) {
if (result = !!(iterator || Prototype.K)(value, index))
throw $break;
});
return result;
},
collect: function(iterator) {
var results = [];
this.each(function(value, index) {
results.push((iterator || Prototype.K)(value, index));
});
return results;
},
detect: function(iterator) {
var result;
this.each(function(value, index) {
if (iterator(value, index)) {
result = value;
throw $break;
}
});
return result;
},
findAll: function(iterator) {
var results = [];
this.each(function(value, index) {
if (iterator(value, index))
results.push(value);
});
return results;
},
grep: function(pattern, iterator) {
var results = [];
this.each(function(value, index) {
var stringValue = value.toString();
if (stringValue.match(pattern))
results.push((iterator || Prototype.K)(value, index));
})
return results;
},
include: function(object) {
var found = false;
this.each(function(value) {
if (value == object) {
found = true;
throw $break;
}
});
return found;
},
inGroupsOf: function(number, fillWith) {
fillWith = fillWith === undefined ? null : fillWith;
return this.eachSlice(number, function(slice) {
while(slice.length < number) slice.push(fillWith);
return slice;
});
},
inject: function(memo, iterator) {
this.each(function(value, index) {
memo = iterator(memo, value, index);
});
return memo;
},
invoke: function(method) {
var args = $A(arguments).slice(1);
return this.map(function(value) {
return value[method].apply(value, args);
});
},
max: function(iterator) {
var result;
this.each(function(value, index) {
value = (iterator || Prototype.K)(value, index);
if (result == undefined || value >= result)
result = value;
});
return result;
},
min: function(iterator) {
var result;
this.each(function(value, index) {
value = (iterator || Prototype.K)(value, index);
if (result == undefined || value < result)
result = value;
});
return result;
},
partition: function(iterator) {
var trues = [], falses = [];
this.each(function(value, index) {
((iterator || Prototype.K)(value, index) ?
trues : falses).push(value);
});
return [trues, falses];
},
pluck: function(property) {
var results = [];
this.each(function(value, index) {
results.push(value[property]);
});
return results;
},
reject: function(iterator) {
var results = [];
this.each(function(value, index) {
if (!iterator(value, index))
results.push(value);
});
return results;
},
sortBy: function(iterator) {
return this.map(function(value, index) {
return {value: value, criteria: iterator(value, index)};
}).sort(function(left, right) {
var a = left.criteria, b = right.criteria;
return a < b ? -1 : a > b ? 1 : 0;
}).pluck('value');
},
toArray: function() {
return this.map();
},
zip: function() {
var iterator = Prototype.K, args = $A(arguments);
if (typeof args.last() == 'function')
iterator = args.pop();
var collections = [this].concat(args).map($A);
return this.map(function(value, index) {
return iterator(collections.pluck(index));
});
},
size: function() {
return this.toArray().length;
},
inspect: function() {
return '#<Enumerable:' + this.toArray().inspect() + '>';
}
}
Object.extend(Enumerable, {
map: Enumerable.collect,
find: Enumerable.detect,
select: Enumerable.findAll,
member: Enumerable.include,
entries: Enumerable.toArray
});
var $A = Array.from = function(iterable) {
if (!iterable) return [];
if (iterable.toArray) {
return iterable.toArray();
} else {
var results = [];
for (var i = 0, length = iterable.length; i < length; i++)
results.push(iterable[i]);
return results;
}
}
Object.extend(Array.prototype, Enumerable);
if (!Array.prototype._reverse)
Array.prototype._reverse = Array.prototype.reverse;
Object.extend(Array.prototype, {
_each: function(iterator) {
for (var i = 0, length = this.length; i < length; i++)
iterator(this[i]);
},
clear: function() {
this.length = 0;
return this;
},
first: function() {
return this[0];
},
last: function() {
return this[this.length - 1];
},
compact: function() {
return this.select(function(value) {
return value != null;
});
},
flatten: function() {
return this.inject([], function(array, value) {
return array.concat(value && value.constructor == Array ?
value.flatten() : [value]);
});
},
without: function() {
var values = $A(arguments);
return this.select(function(value) {
return !values.include(value);
});
},
indexOf: function(object) {
for (var i = 0, length = this.length; i < length; i++)
if (this[i] == object) return i;
return -1;
},
reverse: function(inline) {
return (inline !== false ? this : this.toArray())._reverse();
},
reduce: function() {
return this.length > 1 ? this : this[0];
},
uniq: function() {
return this.inject([], function(array, value) {
return array.include(value) ? array : array.concat([value]);
});
},
clone: function() {
return [].concat(this);
},
size: function() {
return this.length;
},
inspect: function() {
return '[' + this.map(Object.inspect).join(', ') + ']';
}
});
Array.prototype.toArray = Array.prototype.clone;
function $w(string){
string = string.strip();
return string ? string.split(/\s+/) : [];
}
if(window.opera){
Array.prototype.concat = function(){
var array = [];
for(var i = 0, length = this.length; i < length; i++) array.push(this[i]);
for(var i = 0, length = arguments.length; i < length; i++) {
if(arguments[i].constructor == Array) {
for(var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
array.push(arguments[i][j]);
} else {
array.push(arguments[i]);
}
}
return array;
}
}
var Hash = function(obj) {
Object.extend(this, obj || {});
};
Object.extend(Hash, {
toQueryString: function(obj) {
var parts = [];
this.prototype._each.call(obj, function(pair) {
if (!pair.key) return;
if (pair.value && pair.value.constructor == Array) {
var values = pair.value.compact();
if (values.length < 2) pair.value = values.reduce();
else {
key = encodeURIComponent(pair.key);
values.each(function(value) {
value = value != undefined ? encodeURIComponent(value) : '';
parts.push(key + '=' + encodeURIComponent(value));
});
return;
}
}
if (pair.value == undefined) pair[1] = '';
parts.push(pair.map(encodeURIComponent).join('='));
});
return parts.join('&');
}
});
Object.extend(Hash.prototype, Enumerable);
Object.extend(Hash.prototype, {
_each: function(iterator) {
for (var key in this) {
var value = this[key];
if (value && value == Hash.prototype[key]) continue;
var pair = [key, value];
pair.key = key;
pair.value = value;
iterator(pair);
}
},
keys: function() {
return this.pluck('key');
},
values: function() {
return this.pluck('value');
},
merge: function(hash) {
return $H(hash).inject(this, function(mergedHash, pair) {
mergedHash[pair.key] = pair.value;
return mergedHash;
});
},
remove: function() {
var result;
for(var i = 0, length = arguments.length; i < length; i++) {
var value = this[arguments[i]];
if (value !== undefined){
if (result === undefined) result = value;
else {
if (result.constructor != Array) result = [result];
result.push(value)
}
}
delete this[arguments[i]];
}
return result;
},
toQueryString: function() {
return Hash.toQueryString(this);
},
inspect: function() {
return '#<Hash:{' + this.map(function(pair) {
return pair.map(Object.inspect).join(': ');
}).join(', ') + '}>';
}
});
function $H(object) {
if (object && object.constructor == Hash) return object;
return new Hash(object);
};
ObjectRange = Class.create();
Object.extend(ObjectRange.prototype, Enumerable);
Object.extend(ObjectRange.prototype, {
initialize: function(start, end, exclusive) {
this.start = start;
this.end = end;
this.exclusive = exclusive;
},
_each: function(iterator) {
var value = this.start;
while (this.include(value)) {
iterator(value);
value = value.succ();
}
},
include: function(value) {
if (value < this.start)
return false;
if (this.exclusive)
return value < this.end;
return value <= this.end;
}
});
var $R = function(start, end, exclusive) {
return new ObjectRange(start, end, exclusive);
}
var Ajax = {
getTransport: function() {
return Try.these(
function() {return new XMLHttpRequest()},
function() {return new ActiveXObject('Msxml2.XMLHTTP')},
function() {return new ActiveXObject('Microsoft.XMLHTTP')}
) || false;
},
activeRequestCount: 0
}
Ajax.Responders = {
responders: [],
_each: function(iterator) {
this.responders._each(iterator);
},
register: function(responder) {
if (!this.include(responder))
this.responders.push(responder);
},
unregister: function(responder) {
this.responders = this.responders.without(responder);
},
dispatch: function(callback, request, transport, json) {
this.each(function(responder) {
if (typeof responder[callback] == 'function') {
try {
responder[callback].apply(responder, [request, transport, json]);
} catch (e) {}
}
});
}
};
Object.extend(Ajax.Responders, Enumerable);
Ajax.Responders.register({
onCreate: function() {
Ajax.activeRequestCount++;
},
onComplete: function() {
Ajax.activeRequestCount--;
}
});
Ajax.Base = function() {};
Ajax.Base.prototype = {
setOptions: function(options) {
this.options = {
method: 'post',
asynchronous: true,
contentType: 'application/x-www-form-urlencoded',
encoding: 'UTF-8',
parameters: ''
}
Object.extend(this.options, options || {});
this.options.method = this.options.method.toLowerCase();
if (typeof this.options.parameters == 'string')
this.options.parameters = this.options.parameters.toQueryParams();
}
}
Ajax.Request = Class.create();
Ajax.Request.Events =
['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
_complete: false,
initialize: function(url, options) {
this.transport = Ajax.getTransport();
this.setOptions(options);
this.request(url);
},
request: function(url) {
this.url = url;
this.method = this.options.method;
var params = this.options.parameters;
if (!['get', 'post'].include(this.method)) {
// simulate other verbs over post
params['_method'] = this.method;
this.method = 'post';
}
params = Hash.toQueryString(params);
if (params && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) params += '&_='
// when GET, append parameters to URL
if (this.method == 'get' && params)
this.url += (this.url.indexOf('?') > -1 ? '&' : '?') + params;
try {
Ajax.Responders.dispatch('onCreate', this, this.transport);
this.transport.open(this.method.toUpperCase(), this.url,
this.options.asynchronous);
if (this.options.asynchronous)
setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);
this.transport.onreadystatechange = this.onStateChange.bind(this);
this.setRequestHeaders();
var body = this.method == 'post' ? (this.options.postBody || params) : null;
this.transport.send(body);
/* Force Firefox to handle ready state 4 for synchronous requests */
if (!this.options.asynchronous && this.transport.overrideMimeType)
this.onStateChange();
}
catch (e) {
this.dispatchException(e);
}
},
onStateChange: function() {
var readyState = this.transport.readyState;
if (readyState > 1 && !((readyState == 4) && this._complete))
this.respondToReadyState(this.transport.readyState);
},
setRequestHeaders: function() {
var headers = {
'X-Requested-With': 'XMLHttpRequest',
'X-Prototype-Version': Prototype.Version,
'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
};
if (this.method == 'post') {
headers['Content-type'] = this.options.contentType +
(this.options.encoding ? '; charset=' + this.options.encoding : '');
/* Force "Connection: close" for older Mozilla browsers to work
* around a bug where XMLHttpRequest sends an incorrect
* Content-length header. See Mozilla Bugzilla #246651.
*/
if (this.transport.overrideMimeType &&
(navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
headers['Connection'] = 'close';
}
// user-defined headers
if (typeof this.options.requestHeaders == 'object') {
var extras = this.options.requestHeaders;
if (typeof extras.push == 'function')
for (var i = 0, length = extras.length; i < length; i += 2)
headers[extras[i]] = extras[i+1];
else
$H(extras).each(function(pair) { headers[pair.key] = pair.value });
}
for (var name in headers)
this.transport.setRequestHeader(name, headers[name]);
},
success: function() {
return !this.transport.status
|| (this.transport.status >= 200 && this.transport.status < 300);
},
respondToReadyState: function(readyState) {
var state = Ajax.Request.Events[readyState];
var transport = this.transport, json = this.evalJSON();
if (state == 'Complete') {
try {
this._complete = true;
(this.options['on' + this.transport.status]
|| this.options['on' + (this.success() ? 'Success' : 'Failure')]
|| Prototype.emptyFunction)(transport, json);
} catch (e) {
this.dispatchException(e);
}
if ((this.getHeader('Content-type') || 'text/javascript').strip().
match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
this.evalResponse();
}
try {
(this.options['on' + state] || Prototype.emptyFunction)(transport, json);
Ajax.Responders.dispatch('on' + state, this, transport, json);
} catch (e) {
this.dispatchException(e);
}
if (state == 'Complete') {
// avoid memory leak in MSIE: clean up
this.transport.onreadystatechange = Prototype.emptyFunction;
}
},
getHeader: function(name) {
try {
return this.transport.getResponseHeader(name);
} catch (e) { return null }
},
evalJSON: function() {
try {
var json = this.getHeader('X-JSON');
return json ? eval('(' + json + ')') : null;
} catch (e) { return null }
},
evalResponse: function() {
try {
return eval(this.transport.responseText);
} catch (e) {
this.dispatchException(e);
}
},
dispatchException: function(exception) {
(this.options.onException || Prototype.emptyFunction)(this, exception);
Ajax.Responders.dispatch('onException', this, exception);
}
});
Ajax.Updater = Class.create();
Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
initialize: function(container, url, options) {
this.container = {
success: (container.success || container),
failure: (container.failure || (container.success ? null : container))
}
this.transport = Ajax.getTransport();
this.setOptions(options);
var onComplete = this.options.onComplete || Prototype.emptyFunction;
this.options.onComplete = (function(transport, param) {
this.updateContent();
onComplete(transport, param);
}).bind(this);
this.request(url);
},
updateContent: function() {
var receiver = this.container[this.success() ? 'success' : 'failure'];
var response = this.transport.responseText;
if (!this.options.evalScripts) response = response.stripScripts();
if (receiver = $(receiver)) {
if (this.options.insertion)
new this.options.insertion(receiver, response);
else
receiver.update(response);
}
if (this.success()) {
if (this.onComplete)
setTimeout(this.onComplete.bind(this), 10);
}
}
});
Ajax.PeriodicalUpdater = Class.create();
Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
initialize: function(container, url, options) {
this.setOptions(options);
this.onComplete = this.options.onComplete;
this.frequency = (this.options.frequency || 2);
this.decay = (this.options.decay || 1);
this.updater = {};
this.container = container;
this.url = url;
this.start();
},
start: function() {
this.options.onComplete = this.updateComplete.bind(this);
this.onTimerEvent();
},
stop: function() {
this.updater.options.onComplete = undefined;
clearTimeout(this.timer);
(this.onComplete || Prototype.emptyFunction).apply(this, arguments);
},
updateComplete: function(request) {
if (this.options.decay) {
this.decay = (request.responseText == this.lastText ?
this.decay * this.options.decay : 1);
this.lastText = request.responseText;
}
this.timer = setTimeout(this.onTimerEvent.bind(this),
this.decay * this.frequency * 1000);
},
onTimerEvent: function() {
this.updater = new Ajax.Updater(this.container, this.url, this.options);
}
});
function $(element) {
if (arguments.length > 1) {
for (var i = 0, elements = [], length = arguments.length; i < length; i++)
elements.push($(arguments[i]));
return elements;
}
if (typeof element == 'string')
element = document.getElementById(element);
return Element.extend(element);
}
if (Prototype.BrowserFeatures.XPath) {
document._getElementsByXPath = function(expression, parentElement) {
var results = [];
var query = document.evaluate(expression, $(parentElement) || document,
null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
for (var i = 0, length = query.snapshotLength; i < length; i++)
results.push(query.snapshotItem(i));
return results;
};
}
document.getElementsByClassName = function(className, parentElement) {
if (Prototype.BrowserFeatures.XPath) {
var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
return document._getElementsByXPath(q, parentElement);
} else {
var children = ($(parentElement) || document.body).getElementsByTagName('*');
var elements = [], child;
for (var i = 0, length = children.length; i < length; i++) {
child = children[i];
if (Element.hasClassName(child, className))
elements.push(Element.extend(child));
}
return elements;
}
};
/*--------------------------------------------------------------------------*/
if (!window.Element)
var Element = new Object();
Element.extend = function(element) {
if (!element || _nativeExtensions || element.nodeType == 3) return element;
if (!element._extended && element.tagName && element != window) {
var methods = Object.clone(Element.Methods), cache = Element.extend.cache;
if (element.tagName == 'FORM')
Object.extend(methods, Form.Methods);
if (['INPUT', 'TEXTAREA', 'SELECT'].include(element.tagName))
Object.extend(methods, Form.Element.Methods);
Object.extend(methods, Element.Methods.Simulated);
for (var property in methods) {
var value = methods[property];
if (typeof value == 'function' && !(property in element))
element[property] = cache.findOrStore(value);
}
}
element._extended = true;
return element;
};
Element.extend.cache = {
findOrStore: function(value) {
return this[value] = this[value] || function() {
return value.apply(null, [this].concat($A(arguments)));
}
}
};
Element.Methods = {
visible: function(element) {
return $(element).style.display != 'none';
},
toggle: function(element) {
element = $(element);
Element[Element.visible(element) ? 'hide' : 'show'](element);
return element;
},
hide: function(element) {
$(element).style.display = 'none';
return element;
},
show: function(element) {
$(element).style.display = '';
return element;
},
remove: function(element) {
element = $(element);
element.parentNode.removeChild(element);
return element;
},
update: function(element, html) {
html = typeof html == 'undefined' ? '' : html.toString();
$(element).innerHTML = html.stripScripts();
setTimeout(function() {html.evalScripts()}, 10);
return element;
},
replace: function(element, html) {
element = $(element);
html = typeof html == 'undefined' ? '' : html.toString();
if (element.outerHTML) {
element.outerHTML = html.stripScripts();
} else {
var range = element.ownerDocument.createRange();
range.selectNodeContents(element);
element.parentNode.replaceChild(
range.createContextualFragment(html.stripScripts()), element);
}
setTimeout(function() {html.evalScripts()}, 10);
return element;
},
inspect: function(element) {
element = $(element);
var result = '<' + element.tagName.toLowerCase();
$H({'id': 'id', 'className': 'class'}).each(function(pair) {
var property = pair.first(), attribute = pair.last();
var value = (element[property] || '').toString();
if (value) result += ' ' + attribute + '=' + value.inspect(true);
});
return result + '>';
},
recursivelyCollect: function(element, property) {
element = $(element);
var elements = [];
while (element = element[property])
if (element.nodeType == 1)
elements.push(Element.extend(element));
return elements;
},
ancestors: function(element) {
return $(element).recursivelyCollect('parentNode');
},
descendants: function(element) {
return $A($(element).getElementsByTagName('*'));
},
immediateDescendants: function(element) {
if (!(element = $(element).firstChild)) return [];
while (element && element.nodeType != 1) element = element.nextSibling;
if (element) return [element].concat($(element).nextSiblings());
return [];
},
previousSiblings: function(element) {
return $(element).recursivelyCollect('previousSibling');
},
nextSiblings: function(element) {
return $(element).recursivelyCollect('nextSibling');
},
siblings: function(element) {
element = $(element);
return element.previousSiblings().reverse().concat(element.nextSiblings());
},
match: function(element, selector) {
if (typeof selector == 'string')
selector = new Selector(selector);
return selector.match($(element));
},
up: function(element, expression, index) {
return Selector.findElement($(element).ancestors(), expression, index);
},
down: function(element, expression, index) {
return Selector.findElement($(element).descendants(), expression, index);
},
previous: function(element, expression, index) {
return Selector.findElement($(element).previousSiblings(), expression, index);
},
next: function(element, expression, index) {
return Selector.findElement($(element).nextSiblings(), expression, index);
},
getElementsBySelector: function() {
var args = $A(arguments), element = $(args.shift());
return Selector.findChildElements(element, args);
},
getElementsByClassName: function(element, className) {
return document.getElementsByClassName(className, element);
},
readAttribute: function(element, name) {
element = $(element);
if (document.all && !window.opera) {
var t = Element._attributeTranslations;
if (t.values[name]) return t.values[name](element, name);
if (t.names[name]) name = t.names[name];
var attribute = element.attributes[name];
if(attribute) return attribute.nodeValue;
}
return element.getAttribute(name);
},
getHeight: function(element) {
return $(element).getDimensions().height;
},
getWidth: function(element) {
return $(element).getDimensions().width;
},
classNames: function(element) {
return new Element.ClassNames(element);
},
hasClassName: function(element, className) {
if (!(element = $(element))) return;
var elementClassName = element.className;
if (elementClassName.length == 0) return false;
if (elementClassName == className ||
elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
return true;
return false;
},
addClassName: function(element, className) {
if (!(element = $(element))) return;
Element.classNames(element).add(className);
return element;
},
removeClassName: function(element, className) {
if (!(element = $(element))) return;
Element.classNames(element).remove(className);
return element;
},
toggleClassName: function(element, className) {
if (!(element = $(element))) return;
Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className);
return element;
},
observe: function() {
Event.observe.apply(Event, arguments);
return $A(arguments).first();
},
stopObserving: function() {
Event.stopObserving.apply(Event, arguments);
return $A(arguments).first();
},
// removes whitespace-only text node children
cleanWhitespace: function(element) {
element = $(element);
var node = element.firstChild;
while (node) {
var nextNode = node.nextSibling;
if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
element.removeChild(node);
node = nextNode;
}
return element;
},
empty: function(element) {
return $(element).innerHTML.match(/^\s*$/);
},
descendantOf: function(element, ancestor) {
element = $(element), ancestor = $(ancestor);
while (element = element.parentNode)
if (element == ancestor) return true;
return false;
},
scrollTo: function(element) {
element = $(element);
var pos = Position.cumulativeOffset(element);
window.scrollTo(pos[0], pos[1]);
return element;
},
getStyle: function(element, style) {
element = $(element);
if (['float','cssFloat'].include(style))
style = (typeof element.style.styleFloat != 'undefined' ? 'styleFloat' : 'cssFloat');
style = style.camelize();
var value = element.style[style];
if (!value) {
if (document.defaultView && document.defaultView.getComputedStyle) {
var css = document.defaultView.getComputedStyle(element, null);
value = css ? css[style] : null;
} else if (element.currentStyle) {
value = element.currentStyle[style];
}
}
if((value == 'auto') && ['width','height'].include(style) && (element.getStyle('display') != 'none'))
value = element['offset'+style.capitalize()] + 'px';
if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
if (Element.getStyle(element, 'position') == 'static') value = 'auto';
if(style == 'opacity') {
if(value) return parseFloat(value);
if(value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
if(value[1]) return parseFloat(value[1]) / 100;
return 1.0;
}
return value == 'auto' ? null : value;
},
setStyle: function(element, style) {
element = $(element);
for (var name in style) {
var value = style[name];
if(name == 'opacity') {
if (value == 1) {
value = (/Gecko/.test(navigator.userAgent) &&
!/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 0.999999 : 1.0;
if(/MSIE/.test(navigator.userAgent) && !window.opera)
element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
} else if(value == '') {
if(/MSIE/.test(navigator.userAgent) && !window.opera)
element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
} else {
if(value < 0.00001) value = 0;
if(/MSIE/.test(navigator.userAgent) && !window.opera)
element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') +
'alpha(opacity='+value*100+')';
}
} else if(['float','cssFloat'].include(name)) name = (typeof element.style.styleFloat != 'undefined') ? 'styleFloat' : 'cssFloat';
element.style[name.camelize()] = value;
}
return element;
},
getDimensions: function(element) {
element = $(element);
var display = $(element).getStyle('display');
if (display != 'none' && display != null) // Safari bug
return {width: element.offsetWidth, height: element.offsetHeight};
// All *Width and *Height properties give 0 on elements with display none,
// so enable the element temporarily
var els = element.style;
var originalVisibility = els.visibility;
var originalPosition = els.position;
var originalDisplay = els.display;
els.visibility = 'hidden';
els.position = 'absolute';
els.display = 'block';
var originalWidth = element.clientWidth;
var originalHeight = element.clientHeight;
els.display = originalDisplay;
els.position = originalPosition;
els.visibility = originalVisibility;
return {width: originalWidth, height: originalHeight};
},
makePositioned: function(element) {
element = $(element);
var pos = Element.getStyle(element, 'position');
if (pos == 'static' || !pos) {
element._madePositioned = true;
element.style.position = 'relative';
// Opera returns the offset relative to the positioning context, when an
// element is position relative but top and left have not been defined
if (window.opera) {
element.style.top = 0;
element.style.left = 0;
}
}
return element;
},
undoPositioned: function(element) {
element = $(element);
if (element._madePositioned) {
element._madePositioned = undefined;
element.style.position =
element.style.top =
element.style.left =
element.style.bottom =
element.style.right = '';
}
return element;
},
makeClipping: function(element) {
element = $(element);
if (element._overflow) return element;
element._overflow = element.style.overflow || 'auto';
if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
element.style.overflow = 'hidden';
return element;
},
undoClipping: function(element) {
element = $(element);
if (!element._overflow) return element;
element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
element._overflow = null;
return element;
}
};
Object.extend(Element.Methods, {childOf: Element.Methods.descendantOf});
Element._attributeTranslations = {};
Element._attributeTranslations.names = {
colspan: "colSpan",
rowspan: "rowSpan",
valign: "vAlign",
datetime: "dateTime",
accesskey: "accessKey",
tabindex: "tabIndex",
enctype: "encType",
maxlength: "maxLength",
readonly: "readOnly",
longdesc: "longDesc"
};
Element._attributeTranslations.values = {
_getAttr: function(element, attribute) {
return element.getAttribute(attribute, 2);
},
_flag: function(element, attribute) {
return $(element).hasAttribute(attribute) ? attribute : null;
},
style: function(element) {
return element.style.cssText.toLowerCase();
},
title: function(element) {
var node = element.getAttributeNode('title');
return node.specified ? node.nodeValue : null;
}
};
Object.extend(Element._attributeTranslations.values, {
href: Element._attributeTranslations.values._getAttr,
src: Element._attributeTranslations.values._getAttr,
disabled: Element._attributeTranslations.values._flag,
checked: Element._attributeTranslations.values._flag,
readonly: Element._attributeTranslations.values._flag,
multiple: Element._attributeTranslations.values._flag
});
Element.Methods.Simulated = {
hasAttribute: function(element, attribute) {
var t = Element._attributeTranslations;
attribute = t.names[attribute] || attribute;
return $(element).getAttributeNode(attribute).specified;
}
};
// IE is missing .innerHTML support for TABLE-related elements
if (document.all && !window.opera){
Element.Methods.update = function(element, html) {
element = $(element);
html = typeof html == 'undefined' ? '' : html.toString();
var tagName = element.tagName.toUpperCase();
if (['THEAD','TBODY','TR','TD'].include(tagName)) {
var div = document.createElement('div');
switch (tagName) {
case 'THEAD':
case 'TBODY':
div.innerHTML = '<table><tbody>' + html.stripScripts() + '</tbody></table>';
depth = 2;
break;
case 'TR':
div.innerHTML = '<table><tbody><tr>' + html.stripScripts() + '</tr></tbody></table>';
depth = 3;
break;
case 'TD':
div.innerHTML = '<table><tbody><tr><td>' + html.stripScripts() + '</td></tr></tbody></table>';
depth = 4;
}
$A(element.childNodes).each(function(node){
element.removeChild(node)
});
depth.times(function(){ div = div.firstChild });
$A(div.childNodes).each(
function(node){ element.appendChild(node) });
} else {
element.innerHTML = html.stripScripts();
}
setTimeout(function() {html.evalScripts()}, 10);
return element;
}
};
Object.extend(Element, Element.Methods);
var _nativeExtensions = false;
if(/Konqueror|Safari|KHTML/.test(navigator.userAgent))
['', 'Form', 'Input', 'TextArea', 'Select'].each(function(tag) {
var className = 'HTML' + tag + 'Element';
if(window[className]) return;
var klass = window[className] = {};
klass.prototype = document.createElement(tag ? tag.toLowerCase() : 'div').__proto__;
});
Element.addMethods = function(methods) {
Object.extend(Element.Methods, methods || {});
function copy(methods, destination, onlyIfAbsent) {
onlyIfAbsent = onlyIfAbsent || false;
var cache = Element.extend.cache;
for (var property in methods) {
var value = methods[property];
if (!onlyIfAbsent || !(property in destination))
destination[property] = cache.findOrStore(value);
}
}
if (typeof HTMLElement != 'undefined') {
copy(Element.Methods, HTMLElement.prototype);
copy(Element.Methods.Simulated, HTMLElement.prototype, true);
copy(Form.Methods, HTMLFormElement.prototype);
[HTMLInputElement, HTMLTextAreaElement, HTMLSelectElement].each(function(klass) {
copy(Form.Element.Methods, klass.prototype);
});
_nativeExtensions = true;
}
}
var Toggle = new Object();
Toggle.display = Element.toggle;
/*--------------------------------------------------------------------------*/
Abstract.Insertion = function(adjacency) {
this.adjacency = adjacency;
}
Abstract.Insertion.prototype = {
initialize: function(element, content) {
this.element = $(element);
this.content = content.stripScripts();
if (this.adjacency && this.element.insertAdjacentHTML) {
try {
this.element.insertAdjacentHTML(this.adjacency, this.content);
} catch (e) {
var tagName = this.element.tagName.toUpperCase();
if (['TBODY', 'TR'].include(tagName)) {
this.insertContent(this.contentFromAnonymousTable());
} else {
throw e;
}
}
} else {
this.range = this.element.ownerDocument.createRange();
if (this.initializeRange) this.initializeRange();
this.insertContent([this.range.createContextualFragment(this.content)]);
}
setTimeout(function() {content.evalScripts()}, 10);
},
contentFromAnonymousTable: function() {
var div = document.createElement('div');
div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
return $A(div.childNodes[0].childNodes[0].childNodes);
}
}
var Insertion = new Object();
Insertion.Before = Class.create();
Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
initializeRange: function() {
this.range.setStartBefore(this.element);
},
insertContent: function(fragments) {
fragments.each((function(fragment) {
this.element.parentNode.insertBefore(fragment, this.element);
}).bind(this));
}
});
Insertion.Top = Class.create();
Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
initializeRange: function() {
this.range.selectNodeContents(this.element);
this.range.collapse(true);
},
insertContent: function(fragments) {
fragments.reverse(false).each((function(fragment) {
this.element.insertBefore(fragment, this.element.firstChild);
}).bind(this));
}
});
Insertion.Bottom = Class.create();
Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
initializeRange: function() {
this.range.selectNodeContents(this.element);
this.range.collapse(this.element);
},
insertContent: function(fragments) {
fragments.each((function(fragment) {
this.element.appendChild(fragment);
}).bind(this));
}
});
Insertion.After = Class.create();
Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
initializeRange: function() {
this.range.setStartAfter(this.element);
},
insertContent: function(fragments) {
fragments.each((function(fragment) {
this.element.parentNode.insertBefore(fragment,
this.element.nextSibling);
}).bind(this));
}
});
/*--------------------------------------------------------------------------*/
Element.ClassNames = Class.create();
Element.ClassNames.prototype = {
initialize: function(element) {
this.element = $(element);
},
_each: function(iterator) {
this.element.className.split(/\s+/).select(function(name) {
return name.length > 0;
})._each(iterator);
},
set: function(className) {
this.element.className = className;
},
add: function(classNameToAdd) {
if (this.include(classNameToAdd)) return;
this.set($A(this).concat(classNameToAdd).join(' '));
},
remove: function(classNameToRemove) {
if (!this.include(classNameToRemove)) return;
this.set($A(this).without(classNameToRemove).join(' '));
},
toString: function() {
return $A(this).join(' ');
}
};
Object.extend(Element.ClassNames.prototype, Enumerable);
var Selector = Class.create();
Selector.prototype = {
initialize: function(expression) {
this.params = {classNames: []};
this.expression = expression.toString().strip();
this.parseExpression();
this.compileMatcher();
},
parseExpression: function() {
function abort(message) { throw 'Parse error in selector: ' + message; }
if (this.expression == '') abort('empty expression');
var params = this.params, expr = this.expression, match, modifier, clause, rest;
while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) {
params.attributes = params.attributes || [];
params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''});
expr = match[1];
}
if (expr == '*') return this.params.wildcard = true;
while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)) {
modifier = match[1], clause = match[2], rest = match[3];
switch (modifier) {
case '#': params.id = clause; break;
case '.': params.classNames.push(clause); break;
case '':
case undefined: params.tagName = clause.toUpperCase(); break;
default: abort(expr.inspect());
}
expr = rest;
}
if (expr.length > 0) abort(expr.inspect());
},
buildMatchExpression: function() {
var params = this.params, conditions = [], clause;
if (params.wildcard)
conditions.push('true');
if (clause = params.id)
conditions.push('element.readAttribute("id") == ' + clause.inspect());
if (clause = params.tagName)
conditions.push('element.tagName.toUpperCase() == ' + clause.inspect());
if ((clause = params.classNames).length > 0)
for (var i = 0, length = clause.length; i < length; i++)
conditions.push('element.hasClassName(' + clause[i].inspect() + ')');
if (clause = params.attributes) {
clause.each(function(attribute) {
var value = 'element.readAttribute(' + attribute.name.inspect() + ')';
var splitValueBy = function(delimiter) {
return value + ' && ' + value + '.split(' + delimiter.inspect() + ')';
}
switch (attribute.operator) {
case '=': conditions.push(value + ' == ' + attribute.value.inspect()); break;
case '~=': conditions.push(splitValueBy(' ') + '.include(' + attribute.value.inspect() + ')'); break;
case '|=': conditions.push(
splitValueBy('-') + '.first().toUpperCase() == ' + attribute.value.toUpperCase().inspect()
); break;
case '!=': conditions.push(value + ' != ' + attribute.value.inspect()); break;
case '':
case undefined: conditions.push('element.hasAttribute(' + attribute.name.inspect() + ')'); break;
default: throw 'Unknown operator ' + attribute.operator + ' in selector';
}
});
}
return conditions.join(' && ');
},
compileMatcher: function() {
this.match = new Function('element', 'if (!element.tagName) return false; \
element = $(element); \
return ' + this.buildMatchExpression());
},
findElements: function(scope) {
var element;
if (element = $(this.params.id))
if (this.match(element))
if (!scope || Element.childOf(element, scope))
return [element];
scope = (scope || document).getElementsByTagName(this.params.tagName || '*');
var results = [];
for (var i = 0, length = scope.length; i < length; i++)
if (this.match(element = scope[i]))
results.push(Element.extend(element));
return results;
},
toString: function() {
return this.expression;
}
}
Object.extend(Selector, {
matchElements: function(elements, expression) {
var selector = new Selector(expression);
return elements.select(selector.match.bind(selector)).map(Element.extend);
},
findElement: function(elements, expression, index) {
if (typeof expression == 'number') index = expression, expression = false;
return Selector.matchElements(elements, expression || '*')[index || 0];
},
findChildElements: function(element, expressions) {
return expressions.map(function(expression) {
return expression.match(/[^\s"]+(?:"[^"]*"[^\s"]+)*/g).inject([null], function(results, expr) {
var selector = new Selector(expr);
return results.inject([], function(elements, result) {
return elements.concat(selector.findElements(result || element));
});
});
}).flatten();
}
});
function $$() {
return Selector.findChildElements(document, $A(arguments));
}
var Form = {
reset: function(form) {
$(form).reset();
return form;
},
serializeElements: function(elements, getHash) {
var data = elements.inject({}, function(result, element) {
if (!element.disabled && element.name) {
var key = element.name, value = $(element).getValue();
if (value != undefined) {
if (result[key]) {
if (result[key].constructor != Array) result[key] = [result[key]];
result[key].push(value);
}
else result[key] = value;
}
}
return result;
});
return getHash ? data : Hash.toQueryString(data);
}
};
Form.Methods = {
serialize: function(form, getHash) {
return Form.serializeElements(Form.getElements(form), getHash);
},
getElements: function(form) {
return $A($(form).getElementsByTagName('*')).inject([],
function(elements, child) {
if (Form.Element.Serializers[child.tagName.toLowerCase()])
elements.push(Element.extend(child));
return elements;
}
);
},
getInputs: function(form, typeName, name) {
form = $(form);
var inputs = form.getElementsByTagName('input');
if (!typeName && !name) return $A(inputs).map(Element.extend);
for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
var input = inputs[i];
if ((typeName && input.type != typeName) || (name && input.name != name))
continue;
matchingInputs.push(Element.extend(input));
}
return matchingInputs;
},
disable: function(form) {
form = $(form);
form.getElements().each(function(element) {
element.blur();
element.disabled = 'true';
});
return form;
},
enable: function(form) {
form = $(form);
form.getElements().each(function(element) {
element.disabled = '';
});
return form;
},
findFirstElement: function(form) {
return $(form).getElements().find(function(element) {
return element.type != 'hidden' && !element.disabled &&
['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
});
},
focusFirstElement: function(form) {
form = $(form);
form.findFirstElement().activate();
return form;
}
}
Object.extend(Form, Form.Methods);
/*--------------------------------------------------------------------------*/
Form.Element = {
focus: function(element) {
$(element).focus();
return element;
},
select: function(element) {
$(element).select();
return element;
}
}
Form.Element.Methods = {
serialize: function(element) {
element = $(element);
if (!element.disabled && element.name) {
var value = element.getValue();
if (value != undefined) {
var pair = {};
pair[element.name] = value;
return Hash.toQueryString(pair);
}
}
return '';
},
getValue: function(element) {
element = $(element);
var method = element.tagName.toLowerCase();
return Form.Element.Serializers[method](element);
},
clear: function(element) {
$(element).value = '';
return element;
},
present: function(element) {
return $(element).value != '';
},
activate: function(element) {
element = $(element);
element.focus();
if (element.select && ( element.tagName.toLowerCase() != 'input' ||
!['button', 'reset', 'submit'].include(element.type) ) )
element.select();
return element;
},
disable: function(element) {
element = $(element);
element.disabled = true;
return element;
},
enable: function(element) {
element = $(element);
element.blur();
element.disabled = false;
return element;
}
}
Object.extend(Form.Element, Form.Element.Methods);
var Field = Form.Element;
var $F = Form.Element.getValue;
/*--------------------------------------------------------------------------*/
Form.Element.Serializers = {
input: function(element) {
switch (element.type.toLowerCase()) {
case 'checkbox':
case 'radio':
return Form.Element.Serializers.inputSelector(element);
default:
return Form.Element.Serializers.textarea(element);
}
},
inputSelector: function(element) {
return element.checked ? element.value : null;
},
textarea: function(element) {
return element.value;
},
select: function(element) {
return this[element.type == 'select-one' ?
'selectOne' : 'selectMany'](element);
},
selectOne: function(element) {
var index = element.selectedIndex;
return index >= 0 ? this.optionValue(element.options[index]) : null;
},
selectMany: function(element) {
var values, length = element.length;
if (!length) return null;
for (var i = 0, values = []; i < length; i++) {
var opt = element.options[i];
if (opt.selected) values.push(this.optionValue(opt));
}
return values;
},
optionValue: function(opt) {
// extend element because hasAttribute may not be native
return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
}
}
/*--------------------------------------------------------------------------*/
Abstract.TimedObserver = function() {}
Abstract.TimedObserver.prototype = {
initialize: function(element, frequency, callback) {
this.frequency = frequency;
this.element = $(element);
this.callback = callback;
this.lastValue = this.getValue();
this.registerCallback();
},
registerCallback: function() {
setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
},
onTimerEvent: function() {
var value = this.getValue();
var changed = ('string' == typeof this.lastValue && 'string' == typeof value
? this.lastValue != value : String(this.lastValue) != String(value));
if (changed) {
this.callback(this.element, value);
this.lastValue = value;
}
}
}
Form.Element.Observer = Class.create();
Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
getValue: function() {
return Form.Element.getValue(this.element);
}
});
Form.Observer = Class.create();
Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
getValue: function() {
return Form.serialize(this.element);
}
});
/*--------------------------------------------------------------------------*/
Abstract.EventObserver = function() {}
Abstract.EventObserver.prototype = {
initialize: function(element, callback) {
this.element = $(element);
this.callback = callback;
this.lastValue = this.getValue();
if (this.element.tagName.toLowerCase() == 'form')
this.registerFormCallbacks();
else
this.registerCallback(this.element);
},
onElementEvent: function() {
var value = this.getValue();
if (this.lastValue != value) {
this.callback(this.element, value);
this.lastValue = value;
}
},
registerFormCallbacks: function() {
Form.getElements(this.element).each(this.registerCallback.bind(this));
},
registerCallback: function(element) {
if (element.type) {
switch (element.type.toLowerCase()) {
case 'checkbox':
case 'radio':
Event.observe(element, 'click', this.onElementEvent.bind(this));
break;
default:
Event.observe(element, 'change', this.onElementEvent.bind(this));
break;
}
}
}
}
Form.Element.EventObserver = Class.create();
Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
getValue: function() {
return Form.Element.getValue(this.element);
}
});
Form.EventObserver = Class.create();
Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
getValue: function() {
return Form.serialize(this.element);
}
});
if (!window.Event) {
var Event = new Object();
}
Object.extend(Event, {
KEY_BACKSPACE: 8,
KEY_TAB: 9,
KEY_RETURN: 13,
KEY_ESC: 27,
KEY_LEFT: 37,
KEY_UP: 38,
KEY_RIGHT: 39,
KEY_DOWN: 40,
KEY_DELETE: 46,
KEY_HOME: 36,
KEY_END: 35,
KEY_PAGEUP: 33,
KEY_PAGEDOWN: 34,
element: function(event) {
return event.target || event.srcElement;
},
isLeftClick: function(event) {
return (((event.which) && (event.which == 1)) ||
((event.button) && (event.button == 1)));
},
pointerX: function(event) {
return event.pageX || (event.clientX +
(document.documentElement.scrollLeft || document.body.scrollLeft));
},
pointerY: function(event) {
return event.pageY || (event.clientY +
(document.documentElement.scrollTop || document.body.scrollTop));
},
stop: function(event) {
if (event.preventDefault) {
event.preventDefault();
event.stopPropagation();
} else {
event.returnValue = false;
event.cancelBubble = true;
}
},
// find the first node with the given tagName, starting from the
// node the event was triggered on; traverses the DOM upwards
findElement: function(event, tagName) {
var element = Event.element(event);
while (element.parentNode && (!element.tagName ||
(element.tagName.toUpperCase() != tagName.toUpperCase())))
element = element.parentNode;
return element;
},
observers: false,
_observeAndCache: function(element, name, observer, useCapture) {
if (!this.observers) this.observers = [];
if (element.addEventListener) {
this.observers.push([element, name, observer, useCapture]);
element.addEventListener(name, observer, useCapture);
} else if (element.attachEvent) {
this.observers.push([element, name, observer, useCapture]);
element.attachEvent('on' + name, observer);
}
},
unloadCache: function() {
if (!Event.observers) return;
for (var i = 0, length = Event.observers.length; i < length; i++) {
Event.stopObserving.apply(this, Event.observers[i]);
Event.observers[i][0] = null;
}
Event.observers = false;
},
observe: function(element, name, observer, useCapture) {
element = $(element);
useCapture = useCapture || false;
if (name == 'keypress' &&
(navigator.appVersion.match(/Konqueror|Safari|KHTML/)
|| element.attachEvent))
name = 'keydown';
Event._observeAndCache(element, name, observer, useCapture);
},
stopObserving: function(element, name, observer, useCapture) {
element = $(element);
useCapture = useCapture || false;
if (name == 'keypress' &&
(navigator.appVersion.match(/Konqueror|Safari|KHTML/)
|| element.detachEvent))
name = 'keydown';
if (element.removeEventListener) {
element.removeEventListener(name, observer, useCapture);
} else if (element.detachEvent) {
try {
element.detachEvent('on' + name, observer);
} catch (e) {}
}
}
});
/* prevent memory leaks in IE */
if (navigator.appVersion.match(/\bMSIE\b/))
Event.observe(window, 'unload', Event.unloadCache, false);
var Position = {
// set to true if needed, warning: firefox performance problems
// NOT neeeded for page scrolling, only if draggable contained in
// scrollable elements
includeScrollOffsets: false,
// must be called before calling withinIncludingScrolloffset, every time the
// page is scrolled
prepare: function() {
this.deltaX = window.pageXOffset
|| document.documentElement.scrollLeft
|| document.body.scrollLeft
|| 0;
this.deltaY = window.pageYOffset
|| document.documentElement.scrollTop
|| document.body.scrollTop
|| 0;
},
realOffset: function(element) {
var valueT = 0, valueL = 0;
do {
valueT += element.scrollTop || 0;
valueL += element.scrollLeft || 0;
element = element.parentNode;
} while (element);
return [valueL, valueT];
},
cumulativeOffset: function(element) {
var valueT = 0, valueL = 0;
do {
valueT += element.offsetTop || 0;
valueL += element.offsetLeft || 0;
element = element.offsetParent;
} while (element);
return [valueL, valueT];
},
positionedOffset: function(element) {
var valueT = 0, valueL = 0;
do {
valueT += element.offsetTop || 0;
valueL += element.offsetLeft || 0;
element = element.offsetParent;
if (element) {
if(element.tagName=='BODY') break;
var p = Element.getStyle(element, 'position');
if (p == 'relative' || p == 'absolute') break;
}
} while (element);
return [valueL, valueT];
},
offsetParent: function(element) {
if (element.offsetParent) return element.offsetParent;
if (element == document.body) return element;
while ((element = element.parentNode) && element != document.body)
if (Element.getStyle(element, 'position') != 'static')
return element;
return document.body;
},
// caches x/y coordinate pair to use with overlap
within: function(element, x, y) {
if (this.includeScrollOffsets)
return this.withinIncludingScrolloffsets(element, x, y);
this.xcomp = x;
this.ycomp = y;
this.offset = this.cumulativeOffset(element);
return (y >= this.offset[1] &&
y < this.offset[1] + element.offsetHeight &&
x >= this.offset[0] &&
x < this.offset[0] + element.offsetWidth);
},
withinIncludingScrolloffsets: function(element, x, y) {
var offsetcache = this.realOffset(element);
this.xcomp = x + offsetcache[0] - this.deltaX;
this.ycomp = y + offsetcache[1] - this.deltaY;
this.offset = this.cumulativeOffset(element);
return (this.ycomp >= this.offset[1] &&
this.ycomp < this.offset[1] + element.offsetHeight &&
this.xcomp >= this.offset[0] &&
this.xcomp < this.offset[0] + element.offsetWidth);
},
// within must be called directly before
overlap: function(mode, element) {
if (!mode) return 0;
if (mode == 'vertical')
return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
element.offsetHeight;
if (mode == 'horizontal')
return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
element.offsetWidth;
},
page: function(forElement) {
var valueT = 0, valueL = 0;
var element = forElement;
do {
valueT += element.offsetTop || 0;
valueL += element.offsetLeft || 0;
// Safari fix
if (element.offsetParent==document.body)
if (Element.getStyle(element,'position')=='absolute') break;
} while (element = element.offsetParent);
element = forElement;
do {
if (!window.opera || element.tagName=='BODY') {
valueT -= element.scrollTop || 0;
valueL -= element.scrollLeft || 0;
}
} while (element = element.parentNode);
return [valueL, valueT];
},
clone: function(source, target) {
var options = Object.extend({
setLeft: true,
setTop: true,
setWidth: true,
setHeight: true,
offsetTop: 0,
offsetLeft: 0
}, arguments[2] || {})
// find page position of source
source = $(source);
var p = Position.page(source);
// find coordinate system to use
target = $(target);
var delta = [0, 0];
var parent = null;
// delta [0,0] will do fine with position: fixed elements,
// position:absolute needs offsetParent deltas
if (Element.getStyle(target,'position') == 'absolute') {
parent = Position.offsetParent(target);
delta = Position.page(parent);
}
// correct by body offsets (fixes Safari)
if (parent == document.body) {
delta[0] -= document.body.offsetLeft;
delta[1] -= document.body.offsetTop;
}
// set position
if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
if(options.setWidth) target.style.width = source.offsetWidth + 'px';
if(options.setHeight) target.style.height = source.offsetHeight + 'px';
},
absolutize: function(element) {
element = $(element);
if (element.style.position == 'absolute') return;
Position.prepare();
var offsets = Position.positionedOffset(element);
var top = offsets[1];
var left = offsets[0];
var width = element.clientWidth;
var height = element.clientHeight;
element._originalLeft = left - parseFloat(element.style.left || 0);
element._originalTop = top - parseFloat(element.style.top || 0);
element._originalWidth = element.style.width;
element._originalHeight = element.style.height;
element.style.position = 'absolute';
element.style.top = top + 'px';
element.style.left = left + 'px';
element.style.width = width + 'px';
element.style.height = height + 'px';
},
relativize: function(element) {
element = $(element);
if (element.style.position == 'relative') return;
Position.prepare();
element.style.position = 'relative';
var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
element.style.top = top + 'px';
element.style.left = left + 'px';
element.style.height = element._originalHeight;
element.style.width = element._originalWidth;
}
}
// Safari returns margins on body which is incorrect if the child is absolutely
// positioned. For performance reasons, redefine Position.cumulativeOffset for
// KHTML/WebKit only.
if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
Position.cumulativeOffset = function(element) {
var valueT = 0, valueL = 0;
do {
valueT += element.offsetTop || 0;
valueL += element.offsetLeft || 0;
if (element.offsetParent == document.body)
if (Element.getStyle(element, 'position') == 'absolute') break;
element = element.offsetParent;
} while (element);
return [valueL, valueT];
}
}
Element.addMethods();
//</nowiki></pre></div></div>
<!-- --><div class="NavFrame" style="text-align: left; background-color: #dde;"><div class="NavHead" style="display: inline; padding-right: 4em; background-color: transparent;">I18nManager.js</div><div class="NavContent" style="text-align: left"><pre><nowiki>
/*
This class handles internationalization.
I18nManager is intended to be used through:
_.KEY
where "KEY" is an internationalization key and "_" is the short for
I18nManager.bundle.en (or whatever language the user has chosen).
"_.KEY" will give the internationalized string.
The convention I use for parameterized strings is inspired by Java.
For instance, the value:
s = "User {0} overwrote {1} of your edits."
whould be filled in by using:
s.replace(/\{0\}/g, someUsername).replace(/\{1\}/g, nEdits)
*/
var I18nManager = {
bundle: {
en: { // English language
BUTTON_OK: "OK",
BUTTON_CANCEL: "Cancel",
BUTTON_CLOSE: "Close",
BUTTON_SHOWALL: "Show All",
BUTTON_REFRESH: "Refresh",
BUTTON_EDIT: "Edit",
BUTTON_ADD: "Add",
BUTTON_REMOVE: "Remove",
BUTTON_RESCAN: "Re-scan",
BUTTON_FIX: "Fix",
BUTTON_MYUSERSCRIPTS: "My User Scripts",
LABEL_CONTACTS: "Contacts",
LABEL_EMPTY: "Empty.",
LABEL_ONESUGGESTION: "1 suggestion: ",
LABEL_SUGGESTIONS: " suggestions: ",
LABEL_ADDTOSUMMARY: "Add to summary",
LABEL_CLICKTOFILLSUMMARY: "Click to fill the proposed summary into the input field below",
LABEL_TALK: "Talk",
LABEL_CONTRIBS: "Contribs",
INFO_REFRESHING: "Refreshing...",
INFO_NOSUGGESTIONS: "No suggestions.",
INFO_REPEATANALYSIS: "Repeat the analysis of the text",
INFO_PREFSINCOOKIE: "Your selection of user scripts will be remembered in a cookie. \
If you use a different computer or a different browser, you have to configure them again.",
INFO_PLUGIN_ADVISOR: "Advisor (suggests replacements while you edit)",
INFO_PLUGIN_CONTACTLIST: "Contact list (monitors the online presence of selected users)",
INFO_PLUGIN_ZOCKYLINKCOMPLETE: "Zocky's \"Link Complete\" (suggests names of existing articles after \
you type \"[[foo\" and press TAB)",
INFO_PLUGIN_CYRILLIC: "Helps display Cyrillic URL-s in the address bar",
INFO_PLUGIN_SUBSTITUTIONS: "Replaces sequences of keystrokes with certain strings, similar to VIM's \":map\" command.",
ERROR_APPLICABLETEXTCHANGED: "Error - The applicable text has changed. Fix failed.",
ERROR_EDITSUMMARYLIMIT: "Error - If the proposed text is added to the summary, \
its length will exceed the {0}-character maximum by {1} characters.",
DUMMY: ""
},
bg: { // Bulgarian language
BUTTON_OK: "ОК",
BUTTON_CANCEL: "Отказ",
BUTTON_CLOSE: "Затвори",
BUTTON_SHOWALL: "Покажи Всички",
BUTTON_REFRESH: "Обнови",
BUTTON_EDIT: "Редактирай",
BUTTON_ADD: "Добави",
BUTTON_REMOVE: "Махни",
BUTTON_RESCAN: "Провери Пак",
BUTTON_FIX: "Поправи",
BUTTON_MYUSERSCRIPTS: "Моите Скриптове",
LABEL_CONTACTS: "Контакти",
LABEL_EMPTY: "Няма.",
LABEL_ONESUGGESTION: "1 предложение: ",
LABEL_SUGGESTIONS: " предложения: ",
LABEL_ADDTOSUMMARY: "Добави към описанието",
LABEL_CLICKTOFILLSUMMARY: "Добави предложеното описание към долното поле",
LABEL_TALK: "Беседка",
LABEL_CONTRIBS: "Приноси",
INFO_REFRESHING: "Обновяване...",
INFO_NOSUGGESTIONS: "Няма предложения.",
INFO_REPEATANALYSIS: "Повтори анализа на текста",
INFO_PREFSINCOOKIE: "Вашият избор на скриптове ще бъде запомнен в cookie. \
Ако ползвате друг компютър или друг браузър, ще трябва пак да ги конфигурирате.",
INFO_PLUGIN_ADVISOR: "Advisor (предлага промени на части от текста докато редактирате)",
INFO_PLUGIN_CONTACTLIST: "Contact list (наблюдава онлайн-присъствието на избрани потребители)",
INFO_PLUGIN_ZOCKYLINKCOMPLETE: "Zocky's \"Link Complete\" (предлага имена съществуващи статии след \
като напишете \"[[нещо\" и натиснете TAB)",
INFO_PLUGIN_CYRILLIC: "Помага да се показват кирилски адреси в адресната лента на браузъра",
INFO_PLUGIN_SUBSTITUTIONS: "Подменя някои поредици от клавиши с определени низове, подобно на командата \":map\" във VIM",
ERROR_APPLICABLETEXTCHANGED: "Грешка - Съответният текст е променен. Поправянето е неуспешно.",
ERROR_EDITSUMMARYLIMIT: "Грешка - Ако предложеният текст се добави към описанието, \
дължината му ще надвиши {0}-символното ограничение с {1} символа.",
DUMMY: ""
}
}
};
var _ = I18nManager.bundle[window.wgUserLanguage || "en"] // in case wgUserLanguage is not set
|| I18nManager.bundle.en; // in case we don't have a bundle for the user's language
// todo: Add a sanity self-check that every internationalization key is translated into every language
//</nowiki></pre></div></div>
<!-- --><div class="NavFrame" style="text-align: left; background-color: #dde;"><div class="NavHead" style="display: inline; padding-right: 4em; background-color: transparent;">DOMExtensions.js</div><div class="NavContent" style="text-align: left"><pre><nowiki>
/*
These are shortcuts for frequently-used operations on DOM nodes.
*/
function $elem(tagName/*, Element... children*/) {
var e = $(document.createElement(tagName));
if (arguments.length > 1) {
e.appendChildren.apply(e, $A(arguments).splice(1));
}
return e;
}
/**
* The second parameter is optional and can be either a function (attached as an
* "onclick" observer) or a string (used as a "href" attribute).
*
* "title" is optional, too.
*/
function $anchor(text, hrefOrOnclick, title) {
var e = $elem("A", text);
e.title = title || null;
if ((typeof hrefOrOnclick) == "string") {
e.href = hrefOrOnclick;
} else if ((typeof hrefOrOnclick) == "function") {
e.href = "javascript: void(0);";
Event.observe(e, "click", function (hrefOrOnclick) {
hrefOrOnclick();
return false;
}.bind(null, hrefOrOnclick));
} else {
e.href = "#";
}
return e;
}
Element.addMethods({
removeAllChildren: function (e) {
while (e.firstChild) {
e.removeChild(e.firstChild);
}
return e;
},
appendText: function (e, text) {
e.appendChild(document.createTextNode(text));
return e;
},
appendChildren: function (e) {
for (var i = 1; i < arguments.length; i++) {
var x = arguments[i];
e.appendChild(((typeof x) == "string") ? document.createTextNode(x) : x);
}
return e;
},
scrollToSelection: function (e) {
if (e.nodeName != "TEXTAREA") {
throw new Exception("scrollToSelection() is available only for TEXTAREA-s.");
}
var value = e.value;
var selectionStart = e.selectionStart;
var selectionEnd = e.selectionEnd;
var pad = "";
var h = "\n";
var x = e.rows;
while (x != 0) {
if (x & 1) {
pad += h;
}
x >>>= 1;
h += h;
}
e.value = pad + value.substring(0, selectionStart);
var yOffset = e.scrollHeight;
e.value = value;
e.selectionStart = selectionStart;
e.selectionEnd = selectionEnd;
e.scrollTop = (yOffset > e.clientHeight) ? (yOffset - (3 * e.clientHeight / 2)) : 0;
return e;
}
});
//</nowiki></pre></div></div>
<!-- --><div class="NavFrame" style="text-align: left; background-color: #dde;"><div class="NavHead" style="display: inline; padding-right: 4em; background-color: transparent;">RegExpExtensions.js</div><div class="NavContent" style="text-align: left"><pre><nowiki>
/*
Regular expressions can sometimes become inconveniently large.
In order to make complex ones easier to understand, we introduce
a set of macros. Names enclosed with '{' and '}' will be replaced
according to the hashtable below.
In order to do the replacements, one must call .fix() on the
regex object and use the result instead, like this:
var re = /It happened in {month}/.fix();
Also, for the sake of convenience, we add the "getAllMatches(s)"
method, which is a quick means to find all occurrences of a
regex in some text. The result is an array containing the results
of applying RegExp.exec(..).
*/
RegExp.REPLACEMENTS = {
letter: // all Unicode letters
// http://www.codeproject.com/dotnet/UnicodeCharCatHelper.asp
"\\u0041-\\u005a\\u0061-\\u007a\\u00aa"
+ "\\u00b5\\u00ba\\u00c0-\\u00d6"
+ "\\u00d8-\\u00f6\\u00f8-\\u01ba\\u01bc-\\u01bf"
+ "\\u01c4-\\u02ad\\u0386\\u0388-\\u0481\\u048c-\\u0556"
+ "\\u0561-\\u0587\\u10a0-\\u10c5\\u1e00-\\u1fbc\\u1fbe"
+ "\\u1fc2-\\u1fcc\\u1fd0-\\u1fdb\\u1fe0-\\u1fec"
+ "\\u1ff2-\\u1ffc\\u207f\\u2102\\u2107\\u210a-\\u2113"
+ "\\u2115\\u2119-\\u211d\\u2124\\u2126\\u2128"
+ "\\u212a-\\u212d\\u212f-\\u2131\\u2133\\u2134\\u2139"
+ "\\ufb00-\\ufb17\\uff21-\\uff3a\\uff41-\\uff5a",
letter_bg: // note that this is not the full set of Cyrillic letters
"АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЮЯабвгдежзийклмнопрстуфхцчшщъьюя",
month: //
"(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec|"
+ "January|February|March|April|June|July|August|September|"
+ "October|November|December)"
};
RegExp.escape = function (s) {
var r = "";
var hex = "0123456789abcdef";
for (var i = 0; i < s.length; i++) {
var ch = s.charCodeAt(i);
r += "\\u" + hex[(ch >>> 12) & 0x0f]
+ hex[(ch >>> 8) & 0x0f]
+ hex[(ch >>> 4) & 0x0f]
+ hex[ch & 0x0f];
}
return r;
};
RegExp.prototype.getAllMatches = function (s) { // : Match[]
var p = 0;
var a = [];
while (true) {
this.lastIndex = 0;
var m = this.exec(s.substring(p));
if (m == null) {
return a;
}
m.start = p + m.index;
m.end = p + m.index + m[0].length;
a.push(m);
p = m.end;
}
};
RegExp.prototype.fix = function () { // : RegExp
if (this.fixedRE != null) {
return this.fixedRE;
}
var s = this.source;
for (var alias in RegExp.REPLACEMENTS) {
s = s.replace(
new RegExp("{" + alias + "}", "g"),
RegExp.REPLACEMENTS[alias]
);
}
var re = new RegExp(s);
re.global = this.global;
re.ignoreCase = this.ignoreCase;
re.multiline = this.multiline;
this.fixedRE = re; // the fixed copy is cached
return re;
};
//</nowiki></pre></div></div>
<!-- --><div class="NavFrame" style="text-align: left; background-color: #dde;"><div class="NavHead" style="display: inline; padding-right: 4em; background-color: transparent;">CookieManager.js</div><div class="NavContent" style="text-align: left"><pre><nowiki>
var CookieManager = Class.create();
CookieManager.getCookie = function (name) {
var a = document.cookie.split(/; +/);
var prefix = escape(name) + "=";
for (var i = 0; i < a.length; i++) {
if (a[i].substring(0, prefix.length) == prefix) {
return unescape(a[i].substring(prefix.length));
}
}
return null;
};
CookieManager.setCookie = function (name, value) {
if ((value == null) || ("" + value == "")) {
CookieManager.deleteCookie(name);
return;
}
var expirationDate = new Date(new Date().getTime() + (5 * 365 * 24 * 60 * 60 * 1000)).toGMTString(); // in 5 years
document.cookie = escape(name) + "=" + escape(value) + "; Expires=" + expirationDate + "; Path=/;";
};
CookieManager.deleteCookie = function (name) {
document.cookie = escape(name) + "=; Expires=" + new Date().toGMTString() + "; Path=/;";
};
//</nowiki></pre></div></div>
<!-- --><div class="NavFrame" style="text-align: left; background-color: #dde;"><div class="NavHead" style="display: inline; padding-right: 4em; background-color: transparent;">ModalDialogManager.js</div><div class="NavContent" style="text-align: left"><pre><nowiki>
/*
Displays a DOM node on top of the rest of the page.
The rest of the page is visible (or at least semi-transparent), but not clickable.
*/
var ModalDialogManager = Class.create();
ModalDialogManager.show = function (e) {
if (ModalDialogManager.dimmer != null) {
return false;
}
var whitebox = $elem("DIV", e);
Object.extend(whitebox.style, {
position: "fixed",
backgroundColor: "white",
border: "solid black 1px",
padding: "1em",
left: Math.floor(innerWidth / 6) + "px",
top: Math.floor(innerHeight / 6) + "px",
width: Math.floor(innerWidth * 2 / 3) + "px",
height: Math.floor(innerHeight * 2 / 3) + "px",
overflow: "scroll"
});
var dimmer = ModalDialogManager.dimmer = $elem("DIV", whitebox);
Object.extend(dimmer.style, {
position: "absolute",
width: document.body.clientWidth + "px",
height: document.body.clientHeight + "px",
left: "0",
top: "0",
zIndex: 999,
textAlign: "center"
/*
, // transparency seems to slow rendering down too much
filter: "alpha(opacity=60)",
"-moz-opacity": 0.6,
opacity: 0.6,
backgroundColor: "grey"
*/
});
document.body.appendChild(dimmer);
return true;
};
ModalDialogManager.hide = function () {
if (ModalDialogManager.dimmer != null) {
ModalDialogManager.dimmer.remove();
ModalDialogManager.dimmer = null;
}
};
//</nowiki></pre></div></div>
<!-- --><div class="NavFrame" style="text-align: left; background-color: #dde;"><div class="NavHead" style="display: inline; padding-right: 4em; background-color: transparent;">MediawikiManager.js</div><div class="NavContent" style="text-align: left"><pre><nowiki>
/*
Hides mediawiki quirks behind a convenient interface
and encapsulates interaction with the surrounding DOM.
Some of my classes live happily without using MediawikiManager.
I thought it is a good idea to encapsulate at least some of the interaction, though.
*/
var MediawikiManager = {};
MediawikiManager.isPreferencesPage = function () {
return ($("preferences") != null);
};
MediawikiManager.addPreferenceTab = function (e) {
// Assuming we are at the "preferences" page now, we must first add our stuff.
// the "tabbedprefs()" function has already messed things up.
// We'll just undo some of its actions, and make it work again
// and include our new tab
var index = $("preftoc").childNodes.length;
$("preferences").insertBefore($elem("FIELDSET", $elem("LEGEND", _.BUTTON_MYUSERSCRIPTS), e), $("prefcontrol"));
$("preftoc").remove(); // tabbedprefs() will re-generate the toc
$("prefcontrol").id = "prefsubmit"; // tabbedprefs() will swap the id-s back
tabbedprefs();
};
MediawikiManager.observePreferencesSubmit = function (f) {
Event.observe($("prefcontrol").descendants("INPUT")[0], "click", f);
};
MediawikiManager.importScript = function (s) {
importScript.apply(window, arguments);
};
//</nowiki></pre></div></div>
<!-- --><div class="NavFrame" style="text-align: left; background-color: #dde;"><div class="NavHead" style="display: inline; padding-right: 4em; background-color: transparent;">CameltraderPluginManager.js</div><div class="NavContent" style="text-align: left"><pre><nowiki>
var CameltraderPluginManager = Class.create();
// Static info about plugins
CameltraderPluginManager.PLUGINS = $H({
advisor: {
description: _.INFO_PLUGIN_ADVISOR,
implementation: function () { /*new AdvisorHighlighterPlugin(*/new AdvisorPlugin()/*)*/; }
},
contactList: {
description: _.INFO_PLUGIN_CONTACTLIST,
implementation: function () { new ContactListPlugin(); }
},
zockyLinkComplete: {
description: _.INFO_PLUGIN_ZOCKYLINKCOMPLETE,
implementation: function () { new ZockyLinkCompletePlugin(); }
},
/*
lupinPopups: {
description: "User:Lupin's popups.js",
implementation: function () { new LupinPopupsPlugin(); }
},
cacycleDiff: {
description: "User:Cacycle's diff.js",
implementation: function () { new CacycleDiffPlugin(); }
},
*/
cyrillic: {
description: _.INFO_PLUGIN_CYRILLIC,
implementation: function () { new CyrillicPlugin(); }
},
substitutions: {
description: _.INFO_PLUGIN_SUBSTITUTIONS,
implementation: function () { new SubstitutionsPlugin(); }
}
});
CameltraderPluginManager.prototype = {
initialize: function () {
CameltraderPluginManager.INSTANCE = this;
this.aConfiguredPluginNames = (CookieManager.getCookie("plugins") || "").split(",").compact().uniq();
this.hConfiguredPluginNames = $H();
this.aConfiguredPluginNames._each(function (name) {
if (CameltraderPluginManager.PLUGINS[name] == null) {
return;
}
this.hConfiguredPluginNames[name] = true;
CameltraderPluginManager.PLUGINS[name].implementation();
}.bind(this));
if (!MediawikiManager.isPreferencesPage()) {
return;
}
this.hSumbittablePluginNames = $H().merge(this.hConfiguredPluginNames);
var eDiv = $elem("DIV", _.INFO_PREFSINCOOKIE);
CameltraderPluginManager.PLUGINS.keys()._each(function (name) {
var eCheckbox = $elem("INPUT");
eCheckbox.id = "checkbox-" + name;
eCheckbox.type = "checkbox";
if (this.hConfiguredPluginNames[name]) {
eCheckbox.checked = true;
}
var eLabel = $elem("LABEL", CameltraderPluginManager.PLUGINS[name].description);
eLabel.htmlFor = eCheckbox.id;
eLabel.id = "label-" + name;
Event.observe(eCheckbox, "click", function (name, eCheckbox, eLabel) {
if (eCheckbox.checked) {
this.hSumbittablePluginNames[name] = true;
} else {
this.hSumbittablePluginNames.remove(name);
}
var isConfigured = !!this.hConfiguredPluginNames[name];
if (eCheckbox.checked && !isConfigured) {
eLabel.style.backgroundColor = "#afa"; // green
} else if (isConfigured && !eCheckbox.checked) {
eLabel.style.backgroundColor = "#faa"; // red
} else {
eLabel.style.backgroundColor = "";
}
}.bind(this, name, eCheckbox, eLabel));
eDiv.appendChild($elem("DIV", eCheckbox, eLabel));
}.bind(this));
MediawikiManager.addPreferenceTab(eDiv);
MediawikiManager.observePreferencesSubmit(function () {
var value = this.hSumbittablePluginNames.keys().join(",");
CookieManager.setCookie("plugins", value);
}.bind(this));
}
};
//</nowiki></pre></div></div>
<!-- --><div class="NavFrame" style="text-align: left; background-color: #dde;"><div class="NavHead" style="display: inline; padding-right: 4em; background-color: transparent;">AdvisorPlugin.js</div><div class="NavContent" style="text-align: left"><pre><nowiki>
var AdvisorPlugin = Class.create();
AdvisorPlugin.DEFAULT_MAX_SUGGESTIONS = 8;
AdvisorPlugin.prototype = {
initialize: function () {
this.wpTextbox1 = $("wpTextbox1");
this.wpSummary = $("wpSummary");
var editform = $("editform");
var wpSummaryLabel = $("wpSummaryLabel");
//
this.maxSuggestions = AdvisorPlugin.DEFAULT_MAX_SUGGESTIONS;
this.suggestions = null; // : AdvisorSuggestion[]
this.appliedSuggestions = {}; // : Map<String, int>
if (this.wpTextbox1 == null) {
// This is not an "?action=edit" page
return;
}
this.root = $elem("DIV"); // : Element; that's where suggestions are rendered
var e = editform;
while ((e.previousSibling != null) && (e.previousSibling.id != "toolbar")) {
e = e.previousSibling;
}
e.parentNode.insertBefore(this.root, e);
this.root2 = $elem("DIV"); // : Element; the proposed edit summary appears there
wpSummaryLabel.parentNode.insertBefore(this.root2, wpSummaryLabel);
this.root2.hide();
this.scan();
this.root.style.border = "dashed #ccc 1px";
this.root2.style.border = "dashed #ccc 1px";
this.root.style.color = "#888";
this.root2.style.color = "#888";
},
scan: function () {
var s = this.wpTextbox1.value;
this.suggestions = [];
for (var i = 0; i < AdvisorRules.RULES.length; i++) {
var a = AdvisorRules.RULES[i](s);
if (a.constructor == AdvisorSuggestion) {
a = [ a ];
}
for (var j = 0; j < a.length; j++) {
this.suggestions.push(a[j]);
}
}
this.suggestions.sort(function (x, y) {
return (x.start < y.start) ? -1 :
(x.start > y.start) ? 1 :
(x.end < y.end) ? -1 :
(x.end > y.end) ? 1 : 0;
});
this.root.removeAllChildren();
if (this.suggestions.length == 0) {
this.root.appendText(_.INFO_NOSUGGESTIONS);
} else {
var nSuggestions = Math.min(this.maxSuggestions, this.suggestions.length);
this.root.appendText(
(this.suggestions.length == 1)
? _.LABEL_ONESUGGESTION
: (this.suggestions.length + _.LABEL_SUGGESTIONS)
);
for (var i = 0; i < nSuggestions; i++) {
this.root.appendChildren(
$anchor(
this.suggestions[i].name,
this.markSuggestion.bind(this, this.suggestions[i]),
this.suggestions[i].description
),
$elem("SUP", $anchor(_.BUTTON_FIX, this.fixSuggestion.bind(this, this.suggestions[i]))),
" "
);
}
if (this.suggestions.length > this.maxSuggestions) {
this.root.appendChildren(
$anchor(
"...",
function () {
this.maxSuggestions = 1000;
this.scan();
this.maxSuggestions = AdvisorPlugin.DEFAULT_MAX_SUGGESTIONS;
return false;
}.bind(this),
_.BUTTON_SHOWALL
), " "
);
}
}
this.root.appendChildren("(", $anchor(_.BUTTON_RESCAN, this.scan.bind(this), _.INFO_REPEATANALYSIS), ")");
},
fixSuggestion: function (suggestion) {
if (this.wpTextbox1.value.substring(suggestion.start, suggestion.end)
!= suggestion.originalText) {
alert(_.ERROR_APPLICABLETEXTCHANGED);
this.scan();
return;
}
this.wpTextbox1.value = this.wpTextbox1.value.substring(0, suggestion.start)
+ suggestion.replacementText
+ this.wpTextbox1.value.substring(suggestion.end);
this.wpTextbox1.selectionStart = suggestion.start;
this.wpTextbox1.selectionEnd = suggestion.start + suggestion.replacementText.length;
this.wpTextbox1.scrollToSelection();
// Propose an edit summary
if (this.appliedSuggestions[suggestion.name] == null) {
this.appliedSuggestions[suggestion.name] = 1;
} else {
this.appliedSuggestions[suggestion.name]++;
}
var a = [];
for (var i in this.appliedSuggestions) {
a.push(i);
}
a.sort(function (x, y) {
var u = this.appliedSuggestions;
return (u[x] > u[y]) ? -1 : (u[x] < u[y]) ? 1 : (x < y) ? -1 : (x > y) ? 1 : 0;
}.bind(this));
var s = "";
for (var i = 0; i < a.length; i++) {
var count = this.appliedSuggestions[a[i]];
s += ", " + ((count == 1) ? a[i] : (count + "x " + a[i]));
}
// Cut off the leading ", " and add "formatting: " and "using Advisor.js"
s = "formatting: " + s.substring(2) + " (using [[User:Cameltrader#Advisor.js|Advisor.js]])";
// Render in DOM
this.root2.removeAllChildren().appendChildren(
$anchor(_.LABEL_ADDTOSUMMARY, this.addToSummary.bind(this, s), _.LABEL_CLICKTOFILLSUMMARY),
": \"" + s + "\""
);
this.root2.show();
// Re-scan
this.scan();
},
addToSummary: function (summary) {
var wpSummary = this.wpSummary;
if (wpSummary.value != "") {
summary = wpSummary.value + "; " + summary;
}
if ((wpSummary.maxLength > 0) && (summary.length > wpSummary.maxLength)) {
alert(_.ERROR_EDITSUMMARYLIMIT
.replace(/\{0\}/g, wpSummary.maxLength)
.replace(/\{1\}/g, summary.length - wpSummary.maxLength)
);
return;
}
wpSummary.value = summary;
this.root2.hide();
},
markSuggestion: function (suggestion) {
if (this.wpTextbox1.value.substring(suggestion.start, suggestion.end)
!= suggestion.originalText) {
// The text has changed - just do another scan and don't change selection
this.scan();
return;
}
this.wpTextbox1.selectionStart = suggestion.start;
this.wpTextbox1.selectionEnd = suggestion.end;
this.wpTextbox1.scrollToSelection();
}
};
//</nowiki></pre></div></div>
<!-- --><div class="NavFrame" style="text-align: left; background-color: #dde;"><div class="NavHead" style="display: inline; padding-right: 4em; background-color: transparent;">AdvisorRules.js</div><div class="NavContent" style="text-align: left"><pre><nowiki>
var AdvisorRules = {};
AdvisorRules.RULES = []; // : Function[]
AdvisorRules.LANGUAGE_MAP = { // : Hashtable<String, String>
// From http://wiki.riteme.site/wiki/List_of_ISO_639-1_codes
// Note, that not all of these have a lang-xx template, but finding a reference
// to such a language is a good reason to create the template.
aa: "Afar", ab: "Abkhazian", ae: "Avestan", af: "Afrikaans", ak: "Akan",
am: "Amharic", an: "Aragonese", ar: "Arabic", as: "Assamese", av: "Avaric",
ay: "Aymara", az: "Azerbaijani", ba: "Bashkir", be: "Belarusian",
bg: "Bulgarian", bh: "Bihari", bi: "Bislama", bm: "Bambara", bn: "Bengali",
bo: "Tibetan", br: "Breton", bs: "Bosnian", ca: "Catalan", ce: "Chechen",
ch: "Chamorro", co: "Corsican", cr: "Cree", cs: "Czech",
cu: "Church Slavic", cv: "Chuvash", cy: "Welsh", da: "Danish", de: "German",
dv: "Divehi", dz: "Dzongkha", ee: "Ewe", el: "Greek", en: "English",
eo: "Esperanto", es: "Spanish", et: "Estonian", eu: "Basque", fa: "Persian",
ff: "Fulah", fi: "Finnish", fj: "Fijian", fo: "Faroese", fr: "French",
fy: "Western Frisian", ga: "Irish", gd: "Gaelic", gl: "Galician",
gn: "Guaraní", gu: "Gujarati", gv: "Manx", ha: "Hausa", he: "Hebrew",
hi: "Hindi", ho: "Hiri Motu", hr: "Croatian", ht: "Haitian",
hu: "Hungarian", hy: "Armenian", hz: "Herero",
ia: "Interlingua (International Auxiliary Language Association)",
id: "Indonesian", ie: "Interlingue", ig: "Igbo", ii: "Sichuan Yi",
ik: "Inupiaq", io: "Ido", is: "Icelandic", it: "Italian", iu: "Inuktitut",
ja: "Japanese", jv: "Javanese", ka: "Georgian", kg: "Kongo", ki: "Kikuyu",
kj: "Kuanyama", kk: "Kazakh", kl: "Kalaallisut", km: "Khmer", kn: "Kannada",
ko: "Korean", kr: "Kanuri", ks: "Kashmiri", ku: "Kurdish", kv: "Komi",
kw: "Cornish", ky: "Kirghiz", la: "Latin", lb: "Luxembourgish", lg: "Ganda",
li: "Limburgish", ln: "Lingala", lo: "Lao", lt: "Lithuanian",
lu: "Luba-Katanga", lv: "Latvian", mg: "Malagasy", mh: "Marshallese",
mi: "Māori", mk: "Macedonian", ml: "Malayalam", mn: "Mongolian",
mo: "Moldavian", mr: "Marathi", ms: "Malay", mt: "Maltese", my: "Burmese",
na: "Nauru", nb: "Norwegian Bokmål", nd: "North Ndebele", ne: "Nepali",
ng: "Ndonga", nl: "Dutch", nn: "Norwegian Nynorsk", no: "Norwegian",
nr: "South Ndebele", nv: "Navajo", ny: "Chichewa", oc: "Occitan",
oj: "Ojibwa", om: "Oromo", or: "Oriya", os: "Ossetian", pa: "Panjabi",
pi: "Pāli", pl: "Polish", ps: "Pashto", pt: "Portuguese", qu: "Quechua",
rm: "Raeto-Romance", rn: "Kirundi", ro: "Romanian", ru: "Russian",
rw: "Kinyarwanda", sa: "Sanskrit", sc: "Sardinian", sd: "Sindhi",
se: "Northern Sami", sg: "Sango", sh: "Serbo-Croatian", si: "Sinhala",
sk: "Slovak", sl: "Slovenian", sm: "Samoan", sn: "Shona", so: "Somali",
sq: "Albanian", sr: "Serbian", ss: "Swati", st: "Southern Sotho",
su: "Sundanese", sv: "Swedish", sw: "Swahili", ta: "Tamil", te: "Telugu",
tg: "Tajik", th: "Thai", ti: "Tigrinya", tk: "Turkmen", tl: "Tagalog",
tn: "Tswana", to: "Tonga", tr: "Turkish", ts: "Tsonga", tt: "Tatar",
tw: "Twi", ty: "Tahitian", ug: "Uighur", uk: "Ukrainian", ur: "Urdu",
uz: "Uzbek", ve: "Venda", vi: "Vietnamese", vo: "Volapük", wa: "Walloon",
wo: "Wolof", xh: "Xhosa", yi: "Yiddish", yo: "Yoruba", za: "Zhuang",
zh: "Chinese", zu: "Zulu"
};
AdvisorRules.REVERSE_LANGUAGE_MAP = {}; // : Hashtable<String, String>
for (var i in AdvisorRules.LANGUAGE_MAP) {
AdvisorRules.REVERSE_LANGUAGE_MAP[AdvisorRules.LANGUAGE_MAP[i]] = i;
}
AdvisorRules.MONTH_MAP = {
Jan: "January", Feb: "February", Mar: "March", Apr: "April", May: "May",
Jun: "June", Jul: "July", Aug: "August", Sep: "September", Oct: "October",
Nov: "November", Dec: "December", January: "January", February: "February",
March: "March", April: "April", June: "June", July: "July",
August: "August", September: "September", October: "October",
November: "November", December: "December"
};
/*
Rules
*/
AdvisorRules.RULES.push(function (s) {
var re = /^[ ':]*(?:(?:Further|More) +info(?:rmation))[ ']*:[ ']*([^\]]+)[ ']*$/mig;
var a = re.getAllMatches(s);
var b = [];
for (var i = 0; i < a.length; i++) {
var m = a[i];
if ((m[1] != null) && (m[1] != "")) {
b.push(new AdvisorSuggestion(
m.start, m.end, m[0],
"{{futher|" + m[2] + "}}",
"template-further",
"Better use the {{futher|...}} template."
));
}
}
return b;
});
AdvisorRules.RULES.push(function (s) {
var re = /^[ ':]*(?:Main +article)[ ']*:[ ']*\[\[([^\]]+)\]\][ ']*$/mig;
var a = re.getAllMatches(s);
var b = [];
for (var i = 0; i < a.length; i++) {
var m = a[i];
if ((m[1] != null) && (m[1] != "")) {
b.push(new AdvisorSuggestion(
m.start, m.end, m[0],
"{{main|" + m[1] + "}}",
"template-main",
"Better use the {{main|...}} template."
));
}
}
return b;
});
AdvisorRules.RULES.push(function (s) {
// This will match either a date+year or just a year,
// and will not match solitary dates.
// The rule only controls the transition from linked to unlinked,
// as practice has shown that improper linking is significantly
// more common than leaving linkable dates as plain text.
var re = /(?:\[\[((?:(\d\d?) +({month}))|(?:({month}) +(\d\d?)))\]\],?( )? *)?\[\[([12]\d\d\d)\]\]/;
var a = re.fix().getAllMatches(s);
var b = [];
for (var i = 0; i < a.length; i++) {
var m = a[i];
var date = ((m[1] == null) || (m[1] == "")) ? null : m[1];
var year = ((m[7] == null) || (m[7] == "")) ? null : m[7];
if (date == null) {
b.push(new AdvisorSuggestion(
m.start, m.end, m[0],
year,
"year link",
"It is useless to link a year unless it is preceded by a day and month."
));
} else {
var isAmerican = ((m[2] == null) || (m[2] == ""));
var day = (isAmerican) ? m[5] : m[2];
var month = AdvisorRules.MONTH_MAP[(isAmerican) ? m[4] : m[3]];
var ws = m[6]; // whitespace between date and year
var replacement = (isAmerican)
? ("[[" + month + " " + day + "]]," + ws + "[[" + year + "]]")
: ("[[" + day + " " + month + "]]" + ws + "[[" + year + "]]");
if (replacement != m[0]) {
b.push(new AdvisorSuggestion(
m.start, m.end, m[0],
replacement,
"date",
"Date conventions."
));
}
}
}
return b;
});
AdvisorRules.RULES.push(function (s) {
var re = /\[\[(\w+) language\|\1\]\] *: (\'+)*([{letter} \"\'\„\“\/—–\-]+)(?:\2)/g;
var a = re.fix().getAllMatches(s);
var b = [];
for (var i = 0; i < a.length; i++) {
var m = a[i];
var code = AdvisorRules.REVERSE_LANGUAGE_MAP[m[1]] || null;
if (code == null) {
continue;
}
// Markers for italics and bold are stripped off
b.push(new AdvisorSuggestion(
m.start, m.end, m[0],
"{{lang-" + code + "|" + m[3] + "}}",
"lang-" + code,
"The {{lang-" + code + "}} template can be applied for this text."
));
}
return b;
});
AdvisorRules.RULES.push(function (s) {
// Only accept years 1000...2999
var re = /[^0-9]([1-2][0-9]{3}) *(?:-|—|—|--) *([1-2][0-9]{3})[^0-9]/g;
var a = re.getAllMatches(s);
for (var i = 0; i < a.length; i++) {
var m = a[i];
a[i] = new AdvisorSuggestion(
m.start + 1, m.end - 1, m[0].substring(1, m[0].length - 1),
m[1] + "–" + m[2], // the char in the middle is an ndash
"ndash",
"Year ranges look better with an n-dash."
);
}
return a;
});
AdvisorRules.RULES.push(function (s) {
var re = /[{letter}]( +- +)[{letter}]/g;
var a = re.fix().getAllMatches(s);
for (var i = 0; i < a.length; i++) {
var m = a[i];
a[i] = new AdvisorSuggestion(
m.start + 1, m.end - 1, m[1],
" — ", // mdash
"mdash",
"In a sentence, a hyphen surrounded by spaces means almost certainly an mdash."
);
}
return a;
});
AdvisorRules.RULES.push(function (s) {
var re = /^(?: *)(==+)( *)([^=]*[^= ])(?: *)\1/gm;
var a = re.getAllMatches(s);
var b = [];
var level = 0; // == Level 1 ==, === Level 2 ===, ==== Level 3 ====, etc.
/* If we are editing a section, we have to be tolerant to the first heading's level */
var isSection = $("editform")
&& ($("editform")["wpSection"] != null)
&& ($("editform")["wpSection"].value != "");
for (var i = 0; i < a.length; i++) {
var m = a[i];
var suggestion = new AdvisorSuggestion(
m.start, m.end, m[0],
m[1] + m[2] + m[3] + m[2] + m[1],
"heading",
"Heading style should be \"== Heading ==\" or \"==Heading==\"."
);
var oldLevel = level;
level = m[1].length - 1;
if ( (level - oldLevel > 1) && (!isSection || (oldLevel > 0)) ) {
// Adjust suggestion for the rare case of improper heading hesting
suggestion.name = "heading-nesting";
suggestion.description = "Improper heading nesting";
var h = "=======".substring(0, oldLevel + 2);
suggestion.replacementText = h + m[2] + m[3] + m[2] + h;
b.push(suggestion);
continue;
}
var frequentMistakes = [
{ code: "see-also", wrong: /^see *al+so$/i, correct: "See also" },
{ code: "ext-links", wrong: /^external links?$/i, correct: "External links" },
{ code: "refs", wrong: /^ref+e?r+en(c|s)es?$/i, correct: "References" }
];
for (var j = 0; j < frequentMistakes.length; j++) {
var fm = frequentMistakes[j];
if (fm.wrong.test(m[3]) && (m[3] != fm.correct)) {
suggestion.replacementText = m[1] + m[2] + fm.correct + m[2] + m[1];
suggestion.name = fm.code;
suggestion.description = "The correct spelling/capitalization is \""
+ fm.correct + "\".";
}
}
if (suggestion.originalText != suggestion.replacementText) {
b.push(suggestion);
}
}
return b;
});
AdvisorRules.RULES.push(function (s) {
var re = /\[\[([{letter} ,\(\)\-]+)\|\1\]\]/g;
var a = re.fix().getAllMatches(s);
for (var i = 0; i < a.length; i++) {
var m = a[i];
a[i] = new AdvisorSuggestion(
m.start, m.end, m[0],
"[[" + m[1] + "]]",
"A|A",
"\"[[A|A]]\" can be simplified to [[A]]."
);
}
return a;
});
AdvisorRules.RULES.push(function (s) {
var re = /\[\[([{letter} ,\(\)\-]+)\|\1([{letter}]+)\]\]/g;
var a = re.fix().getAllMatches(s);
for (var i = 0; i < a.length; i++) {
var m = a[i];
a[i] = new AdvisorSuggestion(
m.start, m.end, m[0],
"[[" + m[1] + "]]" + m[2],
"A|AB",
"\"[[A|AB]]\" can be simplified to [[A]]B."
);
}
return a;
});
// This rule has to be removed. The text could be in another
// language using the Cyrillic alphabet.
/*
AdvisorRules.RULES.push(function (s) {
// For this rule to apply, at least four of the characters within the
// brackets must be Bulgarian Cyrillic letters, and the rest are allowed
// to be some limited punctuation
var re = /\((([ ,.;:\"\'\„\“\-\—\–]|–|—|&|̀)*({letter_bg}+([ ,.;:\"\'\„\“\-\—\–]|–|—|&|̀)*){4,})\)/g;
var a = re.fix().getAllMatches(s); // the previous line must be monolithic because of MSIE
for (var i = 0; i < a.length; i++) {
var m = a[i];
a[i] = new AdvisorSuggestion(
m.start, m.end, m[0],
"({{lang|bg|" + m[1] + "}})",
"lang|bg",
"Bulgarian text should be marked with \"{{lang|bg|}}\", "
+ "even when there is no [[Bulgarian language]] link."
);
}
return a;
});
*/
// With the introduction of the magic word "DEFAULTSORT" it became the
// preferred means to do category sorting.
/*
AdvisorRules.RULES.push(function (s) {
var exceptions = {}; // sure, there are many
exceptions["Dolni Bogrov"] = true;
exceptions["Gorni Bogrov"] = true;
if (exceptions[window.wgTitle]) {
return;
}
var re0 = /^([{letter}\-]+(?: [{letter}\-]+\.?)?) ([{letter}\-]+(?:ov|ev|ski))$/;
var m0 = re0.exec(window.wgTitle);
if (m0 == null) {
return [];
}
var firstNames = m0[1];
var lastName = m0[2];
// Should be "lastName, firstName" in categories
var re1 = /\[\[(Category:[\w _\(\),\-]+)\]\]/gi;
var a = re1.getAllMatches(s);
for (var i = 0; i < a.length; i++) {
var m = a[i];
a[i] = new AdvisorSuggestion(
m.start, m.end, m[0],
"[[" + m[1] + "|" + lastName + ", " + firstNames + "]]",
"cat-sort",
"The sort key for categories should be the family name."
// Wikipedia:Categorization_of_people#Ordering_names_in_a_category
);
}
return a;
});
*/
AdvisorRules.RULES.push(function (s) {
var exceptions = {};
exceptions["Dolni Bogrov"] = true;
exceptions["Gorni Bogrov"] = true;
if (exceptions[window.wgTitle]) {
return [];
}
var re0 = /^([{letter}\-]+(?: [{letter}\-]+\.?)?) ([{letter}\-]+(?:ov|ev|ski))$/;
var m0 = re0.fix().exec(window.wgTitle);
if (m0 == null) {
return [];
}
if (s.indexOf("DEFAULTSORT") != -1) {
return [];
}
var firstNames = m0[1];
var lastName = m0[2];
/*var re1 = /\[\[(Category:[\w _\(\),\-]+)\|([\w _\(\),\-]+)\]\]/gi;*/
var re1 = new RegExp("\\[\\[(Category:[\\w _\\(\\),\\-]+)\\| *"
+ RegExp.escape(lastName) + ", *" + RegExp.escape(firstNames)
+ " *\\]\\]", "gi");
var a = re1.getAllMatches(s);
if (a.length == 0) {
return [];
}
var aStart = a[0].start;
var aEnd = a[a.length - 1].end;
var original = s.substring(aStart, aEnd);
var replacement = "{{DEFAULTSORT:" + lastName + ", " + firstNames + "}}\n"
+ original.replace(re1, "[[$1]]");
return [
new AdvisorSuggestion(
aStart, aEnd, original, replacement, "default-sort",
"The magic word DEFAULTSORT can be used to specify sort keys for categories."
)
];
});
AdvisorRules.RULES.push(function (s) {
var re = /\( *(?:b\.? *)?((?:19|20)[0-9][0-9]) *(?:[\-\—\–]|–|—|--) *\)/g;
var a = re.getAllMatches(s);
for (var i = 0; i < a.length; i++) {
var m = a[i];
a[i] = new AdvisorSuggestion(
m.start, m.end, m[0],
"(born " + m[1] + ")",
"born",
"The word \"born\" should be fully written."
// WP:DATE#Dates_of_birth_and_death
);
}
return a;
});
//</nowiki></pre></div></div>
<!-- --><div class="NavFrame" style="text-align: left; background-color: #dde;"><div class="NavHead" style="display: inline; padding-right: 4em; background-color: transparent;">AdvisorSuggestion.js</div><div class="NavContent" style="text-align: left"><pre><nowiki>
/*
It makes sense to have a designated class for suggestions, and not use bare { name: value } syntax.
Thus we have a clear concept of a suggestion, and we can validate parameters in the constructor.
*/
var AdvisorSuggestion = Class.create();
AdvisorSuggestion.prototype = {
initialize: function (start, end, originalText, replacementText, name, description) {
// todo: validation
this.start = start;
this.end = end;
this.originalText = originalText;
this.replacementText = replacementText;
this.name = name;
this.description = description;
}
};
//</nowiki></pre></div></div>
<!-- --><div class="NavFrame" style="text-align: left; background-color: #dde;"><div class="NavHead" style="display: inline; padding-right: 4em; background-color: transparent;">AdvisorHighlighterPlugin.js</div><div class="NavContent" style="text-align: left"><pre><nowiki>
var AdvisorHighlighterPlugin = Class.create();
AdvisorHighlighterPlugin.prototype = {
initialize: function (advisorPlugin) {
if (advisorPlugin == null) {
throw new Exception("An instance of AdvisorPlugin must be passed to the constructor of AdvisorHighlighterPlugin.");
}
this.advisorPlugin = advisorPlugin;
if (advisorPlugin.wpTextbox1 == null) {
// nothing to do, this is not an "?action=edit" page
return;
}
this.overlays = [ advisorPlugin.wpTextbox1 ];
this.addOverlay("yellow");
this.addOverlay("pink");
Event.observe(this.overlays[0], "keyup", this.processInputEvent.bind(this));
Event.observe(this.overlays[0], "mouseover", this.processInputEvent.bind(this));
},
addOverlay: function (colour) {
var overlay = this.overlays[0].cloneNode(true);
var previousOverlay = this.overlays.last();
overlay.id += "overlay" + this.overlays.length;
overlay.readOnly = true;
overlay.style.color = colour;
overlay.style.position = "";
previousOverlay.style.position = "absolute";
// previousOverlay.style.width = overlay.clientWidth + "px";
previousOverlay.style.zIndex = 1000 - this.overlays.length;
previousOverlay.parentNode.insertBefore(overlay, previousOverlay.nextSibling);
previousOverlay.style.backgroundColor = "transparent";
this.overlays.push(overlay);
},
processInputEvent: function (event) {
var a = this.overlays[0];
if (a.value == a.oldValue) {
var scrollTop = a.scrollTop;
if (a.oldScrollTop != scrollTop) {
a.oldScrollTop = scrollTop;
for (var i = 0; i < this.overlays.length; i++) {
this.overlays[i].scrollTop = scrollTop;
}
}
var scrollLeft = a.scrollLeft;
if (a.oldScrollLeft != scrollLeft) {
a.oldScrollLeft = scrollLeft;
for (var i = 0; i < this.overlays.length; i++) {
this.overlays[i].scrollLeft = scrollLeft;
}
}
return;
}
a.oldValue = a.value;
this.updateHighlighting.bind(this).delay(1000);
},
updateHighlighting: function () {
this.advisorPlugin.scan();
var suggestions = this.advisorPlugin.suggestions;
var s = this.overlays[0].value.replace(/[^ \n\r\t]/g, "\u00a0"); // U+00a0 is a non-breaking space
var values = [ "" ];
for (var i = 1; i < this.overlays.length; i++) {
values[i] = s;
}
for (var i = 0; i < suggestions.length; i++) {
var x = suggestions[i];
var overlayIndex = 2; // todo
var v = values[overlayIndex];
values[overlayIndex] = v.substring(0, x.start)
+ v.substring(x.start, x.end)
.replace(/\u00a0/g, "\u2588") // U+2588 is a full solid block
+ v.substring(x.end);
}
for (var i = 1; i < this.overlays.length; i++) {
this.overlays[i].value = values[i];
this.overlays[i].scrollTop = this.overlays[0].scrollTop;
this.overlays[i].scrollLeft = this.overlays[0].scrollLeft;
}
}
};
// A candidate for FunctionExtensions.js
Object.extend(Function.prototype, {
delay: function (t) {
var a = $A(arguments);
a.splice(1);
clearTimeout(this.timeoutId);
this.timeoutId = setTimeout(this.bind.apply(this, a), t);
}
});
//</nowiki></pre></div></div>
<!-- --><div class="NavFrame" style="text-align: left; background-color: #dde;"><div class="NavHead" style="display: inline; padding-right: 4em; background-color: transparent;">ContactListPlugin.js</div><div class="NavContent" style="text-align: left"><pre><nowiki>
var ContactListPlugin = Class.create();
ContactListPlugin.prototype = {
initialize: function () {
var eH5 = $elem("H5").appendChildren(
_.LABEL_CONTACTS + " (",
$anchor(_.BUTTON_REFRESH, this.refresh.bind(this)), ", ",
$anchor(_.BUTTON_EDIT, this.edit.bind(this)), ")"
);
var ePBody = $elem("DIV").addClassName("pBody");
ePBody.id = "contact-list-body";
$("column-one").appendChild($elem("DIV", eH5, ePBody).addClassName("portlet"));
this.refresh();
},
refresh: function () {
$("contact-list-body").removeAllChildren();
var contacts = CookieManager.getCookie("contacts");
if ((contacts == null) || (contacts == "")) {
$("contact-list-body").appendText(_.LABEL_EMPTY);
return;
}
$("contact-list-body").appendText(_.INFO_REFRESHING);
new Ajax.Request("/w/query.php", { // unfortunately, "/w/api.php" doesn't offer retrieval of user contributions yet
method: "get",
parameters: {
format: "json",
what: "usercontribs",
uclimit: 20,
uccomments: "",
titles: "User:" + contacts.split("|").join("|User:")
},
onSuccess: function (transport) {
var result = new Function("return " + transport.responseText)();
var a = [];
for (var i in result.pages) {
var p = result.pages[i];
a[a.length] = {
user: p.title,
timestamp: this.parseWPDate(p.contributions[0].timestamp)
};
}
// Order by last edit
a.sort(function (x, y) {
return y.timestamp.getTime() - x.timestamp.getTime();
});
var eUL = $elem("UL");
for (var i = 0; i < a.length; i++) {
var u = a[i].user;
if (u.substring(0, 5) == "User:") {
u = u.substring(5);
}
eUL.appendChild($elem("LI",
$anchor(u, "/wiki/Special:Contributions/" + escape(u)),
" " + this.getHumanReadableTimestamp(a[i].timestamp).replace(/ /g, "\u00a0") // nbsp
));
}
$("contact-list-body").removeAllChildren().appendChild(eUL);
}.bind(this)
});
},
parseWPDate: function (s) {
// "s" is like "2007-04-15T11:06:53Z"
return new Date(Date.UTC(
parseInt(s.substring(0, 4)),
parseInt(s.substring(5, 7) - 1), // months are zero based in js
parseInt(s.substring(8, 10)),
parseInt(s.substring(11, 13)),
parseInt(s.substring(14, 16)),
parseInt(s.substring(17, 19))
));
},
getHumanReadableTimestamp: function (dateThen) {
// todo: i18n
var dateNow = new Date();
// Calculate difference between now and then
var diff = dateNow.getTime() - dateThen.getTime();
if (diff < 2 * 60 * 1000) { // less than a two minutes
return "just now";
} else if (diff < 59 * 60 * 1000) { // minutes ago
return Math.round(diff / (60 * 1000)) + "m ago";
} else if (diff < 4 * 60 * 60 * 1000) { // hours and minutes ago
var h = Math.floor(diff / (60 * 60 * 1000));
var m = Math.round((diff - (h * 60 * 60 * 1000)) / (5 * 60 * 1000)) * 5; // round at 5m
return (m == 0) ? (h + "h ago") : (h + "h " + m + "m ago");
}
// Calculate difference between last midnight (browser's timezone) and then
dateNow.setHours(0);
dateNow.setMinutes(0);
dateNow.setSeconds(0);
dateNow.setMilliseconds(0);
var diffDays = Math.floor((dateNow.getTime() - dateThen.getTime()) / (24 * 60 * 60 * 1000));
if (diffDays < 1) {
return Math.floor(diff / (60 * 60 * 1000)) + "h ago";
} else if (diffDays == 1) {
return "yesterday";
} else {
return diffDays + "d ago";
}
},
// What follows are methods related to the "edit" dialog
edit: function () {
this.eSettingsInnerDiv = $elem("DIV");
this.eSettingsOuterDiv = $elem("DIV",
this.eSettingsInnerDiv,
$anchor(_.BUTTON_CLOSE, this.closeEditDialog.bind(this))
);
this.renderEditDialog();
this.isModified = false;
ModalDialogManager.show(this.eSettingsOuterDiv);
},
closeEditDialog: function () {
ModalDialogManager.hide();
if (this.isModified) {
this.refresh();
this.isModified = false;
}
},
addContact: function (s) {
if (s == "") {
return;
}
var contactsCookie = CookieManager.getCookie("contacts");
var contacts = ((contactsCookie == null) || (contactsCookie == ""))
? [] : contactsCookie.split("|");
contacts.sort();
contacts.push(s);
CookieManager.setCookie("contacts", contacts.join("|"));
this.isModified = true;
this.renderEditDialog();
},
addContactFromInputField: function () {
this.addContact(this.eInput.value);
},
removeContact: function (index) {
var contactsCookie = CookieManager.getCookie("contacts");
var contacts = ((contactsCookie == null) || (contactsCookie == ""))
? [] : contactsCookie.split("|");
contacts.splice(index, 1);
CookieManager.setCookie("contacts", contacts.join("|"));
this.renderEditDialog();
this.isModified = true;
},
renderEditDialog: function () {
this.eSettingsInnerDiv.removeAllChildren();
var contactsCookie = CookieManager.getCookie("contacts");
var contacts = ((contactsCookie == null) || (contactsCookie == ""))
? [] : contactsCookie.split("|");
contacts.sort();
this.eInput = $elem("INPUT");
Event.observe(this.eInput, "keyup", function (event) {
switch (event.keyCode) {
case Event.KEY_ESC: {
this.closeEditDialog();
return false;
}
case Event.KEY_RETURN: {
this.addContactFromInputField();
return false;
}
default: {
// let go
}
}
}.bindAsEventListener(this));
this.eSettingsInnerDiv.appendChildren(
this.eInput, $anchor(_.BUTTON_ADD, this.addContactFromInputField.bind(this))
);
if (contacts.length != 0) {
var eUL = $elem("UL");
for (var i = 0; i < contacts.length; i++) {
eUL.appendChild($elem("LI",
$anchor(contacts[i], wgServer + "/wiki/User:" + contacts[i]),
" (", $anchor(_.LABEL_TALK, wgServer + "/wiki/User talk:" + contacts[i]),
"·", $anchor(_.LABEL_CONTRIBS, wgServer + "/wiki/Special:Contributions/" + contacts[i]),
") [", $anchor(_.BUTTON_REMOVE, this.removeContact.bind(this, i)), "]"
));
}
this.eSettingsInnerDiv.appendChild(eUL);
}
setTimeout(this.eInput.focus.bind(this.eInput), 10);
}
};
//</nowiki></pre></div></div>
<!-- --><div class="NavFrame" style="text-align: left; background-color: #dde;"><div class="NavHead" style="display: inline; padding-right: 4em; background-color: transparent;">ZockyLinkCompletePlugin.js</div><div class="NavContent" style="text-align: left"><pre><nowiki>
/*
This is a modified version of User:Zocky's excellent "Link Complete" script.
http://wiki.riteme.site/wiki/User:Zocky/Link_Complete
I made it look a bit more object-oriented and prototype.js-based.
Surprisingly, the thing is still working after my chaotic refactoring.
*/
var ZockyLinkCompletePlugin = Class.create();
ZockyLinkCompletePlugin.prototype = {
initialize: function () {
// Fields
this.status = "idle";
this.find = "";
this.matches = [];
this.nextPage = null;
this.nextMatch = 0;
this.start = 0;
this.target = null;
this.regExp = [];
//
var wpTextbox1 = $("wpTextbox1");
if (wpTextbox1) {
Event.observe(wpTextbox1, "keydown", this.keyHandler.bind(this));
Event.observe(wpTextbox1, "keyup", this.keyIgnorer.bind(this));
Event.observe(wpTextbox1, "keypress", this.keyIgnorer.bind(this));
this.regExp = window.linkCompleteTriggers || [ /\[\[([^\[\]\|\n]*?)$/ ];
}
},
insert: function (s) {
var top = this.target.scrollTop;
this.target.value =
this.target.value.substr(0, this.start)
+ s
+ this.target.value.substr(this.selectionGetStart(this.target));
this.target.scrollTop = top;
this.selectionSet(this.target, this.start + s.length, this.start + s.length);
},
insertMatch: function () {
if (this.nextMatch >= this.matches.length) {
return false;
}
this.insert(this.matches[this.nextMatch]);
this.nextMatch++;
return true;
},
getMatches: function (from) {
this.status = "waiting";
new Ajax.Request(wgScriptPath + "/api.php", {
parameters: {
format: "json",
action: "query",
list: "allpages",
apfrom: from,
aplimit: 50,
apfilterredir: "nonredirects",
apnamespace: window.linkCompleteNamespace || 0
},
method: "get",
onSuccess: function (transport) {
this.ajaxHandler(transport.responseText.parseJSON());
}.bind(this)
});
this.insert(this.find + "...");
},
reset: function () {
this.status = "idle";
this.insert(this.find);
this.matches = [];
},
keyIgnorer: function (e) {
var keynum = e.charCode || e.keyCode;
if (keynum == Event.KEY_TAB) {
Event.stop(e);
}
},
keyHandler: function (e) {
var keynum = e.charCode || e.keyCode;
var target = Event.element(e);
if ( ((keynum == Event.KEY_TAB) || (e.ctrlKey && (keynum == 32)))
&& (this.selectionGetStart(target) == this.selectionGetEnd(target)) ) {
if (target != this.target) {
this.target = target;
this.status = "idle";
}
switch (this.status) {
case "idle": {
var find;
for (var i in this.regExp) {
find = (target.value.substr(0, this.selectionGetStart(target)).match(this.regExp[i]) || [])[1];
if (find) break;
}
if (find) {
this.matches = [];
this.nextMatch = 0;
this.find = find;
this.start = this.selectionGetStart(target) - find.length;
this.getMatches(find.capitalize());
}
break;
}
case "waiting": {
break;
}
case "loaded": {
if (this.nextMatch < this.matches.length) {
if (!this.insertMatch()) {
this.reset();
}
} else {
if (this.nextPage) {
this.getMatches(this.nextPage);
} else {
this.nextMatch = 0;
if (!this.insertMatch()) {
this.reset();
}
}
}
break;
}
default: {
break;
}
}
Event.stop(e);
} else {
if (this.status == "waiting") {
this.reset();
}
this.status = "idle";
}
},
// JSON callback
ajaxHandler: function (obj) {
if (obj) {
if (this.status == "waiting") {
for (var i in obj.query.allpages) {
page = obj.query.allpages[i];
if (page.title
&& (page.ns && page.title.replace(/^.*?:/, "").substr(0, this.find.length) == this.find.capitalize())
|| (page.title.substr(0, this.find.length) == this.find.capitalize())) {
this.matches[this.matches.length]
= (page.ns) ? page.title : this.find + page.title.substr(this.find.length);
}
}
this.nextPage = (obj["query-continue"])
? (
(obj["query-continue"].allpages.apfrom.substr(0, this.find.length) == this.find.capitalize())
? obj["query-continue"].allpages.apfrom
: false
)
: false;
if (this.insertMatch()) {
this.status = "loaded";
} else {
this.reset();
}
}
} else {
this.reset();
}
},
selectionSet: function (input, start, end) {
if (input.setSelectionRange) {
input.setSelectionRange(start, end)
input.selectionEnd = end;
} else {
var range = input.createTextRange();
range.collapse(true);
range.moveStart("character", start);
range.moveEnd("character", end - start);
range.select();
}
},
selectionGetStart: function (input) {
if (input.setSelectionRange) {
return input.selectionStart;
} else {
var range = document.selection.createRange();
var isCollapsed = range.compareEndPoints("StartToEnd", range) == 0;
if (!isCollapsed) {
range.collapse(true);
}
var b = range.getBookmark();
return b.charCodeAt(2) - 2;
}
},
selectionGetEnd: function (input) {
if (input.setSelectionRange) {
return input.selectionEnd;
} else {
var range = document.selection.createRange();
var isCollapsed = range.compareEndPoints("StartToEnd", range) == 0;
if (!isCollapsed) {
range.collapse(false);
}
var b = range.getBookmark();
return b.charCodeAt(2) - 2;
}
}
};
// todo: Cameltrader - decide what to do with these
//from http://www.json.org/json.js
String.prototype.parseJSON = function () {
try {
return !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
this.replace(/"(\\.|[^"\\])*"/g, ""))) &&
eval("(" + this + ")");
} catch (e) {
return false;
}
};
//"foo".capitalize() -> "Foo"
String.prototype.capitalize = function () {
return this.substring(0, 1).toUpperCase() + this.substring(1)
};
//</nowiki></pre></div></div>
<!-- --><div class="NavFrame" style="text-align: left; background-color: #dde;"><div class="NavHead" style="display: inline; padding-right: 4em; background-color: transparent;">CyrillicPlugin.js</div><div class="NavContent" style="text-align: left"><pre><nowiki>
/*
Contains enhancements which are suitable for the Bulgarian-language wikipedia
*/
var CyrillicPlugin = Class.create();
CyrillicPlugin.prototype = {
initialize: function () {
// Precompute utf-8-to-codepoint conversion table
var cyr = {};
for (var ch = 0x0400; ch <= 0x052f; ch++) {
var chUtf8 = new Number( ((ch << 2) & 0x1f00) + (ch & 0x3f) + 0xc080 ).toString(16);
var chPercent = "%" + chUtf8.substring(0, 2) + "%" + chUtf8.substring(2);
cyr[chPercent] = cyr[chPercent.toUpperCase()] = String.fromCharCode(ch);
}
// Fix anchors
for (var i = 0; i < document.links.length; i++) {
var href = document.links[i].href;
var re = /%(D[01])%([0-9a-fA-F]{2})/gi;
while (true) {
re.lastIndex = 0;
var m = re.exec(href);
if (m == null) {
break;
}
href = href.substring(0, m.index) + cyr[m[0]]
+ href.substring(m.index + m[0].length);
}
document.links[i].href = href;
}
}
};
//</nowiki></pre></div></div>
<!-- --><div class="NavFrame" style="text-align: left; background-color: #dde;"><div class="NavHead" style="display: inline; padding-right: 4em; background-color: transparent;">SubstitutionsPlugin.js</div><div class="NavContent" style="text-align: left"><pre><nowiki>
var SubstitutionsPlugin = Class.create();
SubstitutionsPlugin.SUBSTITUTIONS = [
{ charCodeSequence: "\u0448\u0448", // sh-sh
oldText: /\u0448\u0448$/, newText: "[[" },
{ charCodeSequence: "\u0449\u0449", // sht-sht
oldText: /\u0449\u0449$/, newText: "]]" },
{ charCodeSequence: "\u0428\u0428", // Sh-Sh
oldText: /\u0428\u0428$/, newText: "{{" },
{ charCodeSequence: "\u0429\u0429", // Sht-Sht
oldText: /\u0429\u0429$/, newText: "}}" },
{ charCodeSequence: "\u044e\u044e", // yu-yu
oldText: /\u044e\u044e$/, newText: "|" },
{ charCodeSequence: "",
oldText: /(^|[ \n-])"([{letter}])$/.fix(), newText: "$1\u201e$2" },
{ charCodeSequence: "\"",
oldText: /([{letter}])"$/.fix(), newText: "$1\u201c" }
];
// number of letters before cursor required for replacements
// or the size of the longest charCode sequence
SubstitutionsPlugin.CONTEXT_SIZE = 20;
SubstitutionsPlugin.prototype = {
initialize: function () {
if ($("wpTextbox1") == null) {
return;
}
this.recentCharCodes = "";
Event.observe("wpTextbox1", "keypress", this.processKeyStroke.bind(this));
/*
var f = function (event) {
$("out").value = "{type=" + event.type
+ ",keyCode=0x" + event.keyCode.toString(16)
+ ",charCode=0x" + event.charCode.toString(16)
+ "}\n" + $("out").value;
}
Event.observe("wpTextbox1", "keyup", f);
Event.observe("wpTextbox1", "keypress", f);
Event.observe("wpTextbox1", "keydown", f);
*/
},
processKeyStroke: function (event) {
if (event.charCode == 0) {
this.recentCharCodes = "";
return true;
}
this.recentCharCodes += String.fromCharCode(event.charCode);
if (this.recentCharCodes.length > 2 * SubstitutionsPlugin.CONTEXT_SIZE) {
this.recentCharCodes = this.recentCharCodes.substring(SubstitutionsPlugin.CONTEXT_SIZE);
}
var ta = $("wpTextbox1");
var p = ta.selectionStart;
var s0 = ta.value.substring(p - SubstitutionsPlugin.CONTEXT_SIZE, p);
var t = ta.scrollTop;
for (var i = 0; i < SubstitutionsPlugin.SUBSTITUTIONS.length; i++) {
var x = SubstitutionsPlugin.SUBSTITUTIONS[i];
if ((x.charCodeSequence == "")
|| (this.recentCharCodes.substr(-x.charCodeSequence.length) == x.charCodeSequence)) {
var s1 = (s0 + String.fromCharCode(event.charCode)).replace(x.oldText, x.newText);
if (s0 + String.fromCharCode(event.charCode) != s1) {
ta.value = ta.value.substring(0, p - s0.length) + s1 + ta.value.substring(p);
ta.selectionStart = ta.selectionEnd = p - s0.length + s1.length;
ta.scrollTop = t;
Event.stop(event);
this.recentCharCodes = "";
break;
}
}
}
}
};
//</nowiki></pre></div></div>