MediaWiki:Gadget-nuxTBKeys.js
Uwaga: aby zobaczyć zmiany po opublikowaniu, może zajść potrzeba wyczyszczenia pamięci podręcznej przeglądarki.
- Firefox / Safari: Przytrzymaj Shift podczas klikania Odśwież bieżącą stronę, lub naciśnij klawisze Ctrl+F5, lub Ctrl+R (⌘-R na komputerze Mac)
- Google Chrome: Naciśnij Ctrl-Shift-R (⌘-Shift-R na komputerze Mac)
- Internet Explorer / Edge: Przytrzymaj Ctrl, jednocześnie klikając Odśwież, lub naciśnij klawisze Ctrl+F5
- Opera: Naciśnij klawisze Ctrl+F5.
/* ------------------------------------------------------------------------ *\
copyright: ©2010-2014 Maciej Jaros (pl:User:Nux, en:User:EcceNux)
license:
GNU General Public License v2,
http://opensource.org/licenses/gpl-license.php
OR
CC-BY-SA
http://creativecommons.org/licenses/by-sa/3.0/deed.pl
Libraries used:
* jQuery (keypress, preventDefault)
* sel_t
* wikibits
Best <del>viewed</del> edited in Netbeans 7.3+. ;-)
Jest be sure to add:
-J-Dfile.encoding=UTF-8
in [netbeans installation dir]\etc\netbeans.conf
in netbeans_default_options
\* ------------------------------------------------------------------------ */
/**
* Text box keys listener class that allows to add actions for certain key combinations.
*
* @param {String} textBoxId Id of the text box.
* @returns {nuxTBKeys}
*/
function nuxTBKeys(textBoxId)
{
/** @type nuxTBKeys */
var _this = this;
/**
* Version - just for show ;-)
* @type String
*/
this.ver = this.version = '1.0.3';
// Colectors and helpers
var $input, input; // jQuery and DOM input
var pressedCharsBuffer = ''; // pressed chars buffer
var interestingChars = ''; // chars of interest within the script
var maxChars = 0; // max chars that might be interesting for the script
var isDisabled = false;
/**
* Replacements array (filled by addReplacements).
*
* @type Array
*/
var arrReplacements = [];
/**
* Escape string before creating a RegExp.
*
* @param {String} str String to be used in new RegExp.
* @returns {String}
*/
var escapeStr4RegExp = function(str)
{
return str.replace(/([\[\]\{\}\|\.\*\?\(\)\$\^\\])/g,'\\$1');
};
/**
* Adds a single replacement rule, should be called before init()
*
* Note: rules added at the begining will be checked first.
* Also: rule match will prevent any following from matching.
*
* from - string to be replaced
* to - string to insert instead
* beforeForbidden - string that must not be appear just before or text before nust not match: `{re:/.../,maxCheck:123}`
* beforeMustHave - string that must appear just before or text before must match `{re:/.../,maxCheck:123}`
* use `$` in `re` to match just before string to be replaced
*
* @param {type} atr
* @returns {undefined}
*/
this.addReplacements = function(atr/*{from, to}*/)
{
// add the replacement rule with some precalculated attributes
var atrRewritten = jQuery.extend({}, atr);
atrRewritten.from = atr.from.substr(0, atr.from.length-1);
arrReplacements[arrReplacements.length] = {
chLastChar : atr.from.charAt(atr.from.length-1),
reMatch : new RegExp(escapeStr4RegExp(atr.from)+'$'),
objReplaceAtrs : atrRewritten
};
// re-calculte some main attributes
if (maxChars<atr.from.length)
{
maxChars=atr.from.length;
}
for (var i=0; i<atr.from.length; i++)
{
var ch = atr.from.charAt(i);
if (interestingChars.indexOf(ch)===-1)
{
interestingChars += ch;
}
}
};
/**
* Init must be run after the text box is available
* @returns {unresolved}
*/
this.init = function()
{
if (!jQuery)
return;
var _this = this;
$input = jQuery('#'+textBoxId);
input = document.getElementById(textBoxId);
// main collector
$input.keypress(function(evt) {onKeyPress(evt);});
// remove anything collected on the way (so that moving a cursor with a mouse wouldn't cause anything weird to happen)
$input.click(function(evt) {pressedCharsBuffer = '';});
};
/**
* Enable/disable listener.
*/
this.toggleDisablance = function()
{
isDisabled = !isDisabled;
};
/**
* Set disablance of the listener.
*
* @param {Boolean} is2BeDisabled
*/
this.setDisablance = function(is2BeDisabled)
{
isDisabled = is2BeDisabled;
};
/**
* Get disablance state of the listener.
*
* @return {Boolean} Current state.
*/
this.getDisablance = function()
{
return isDisabled;
};
/**
* Collecting and checking characters.
*
* @param {Event} evt KeyPress event data
*/
var onKeyPress = function(evt)
{
// no rules added or disabled?
if (arrReplacements.length<=0 || isDisabled)
{
return;
}
//
// collect chars
//
var ch;
// normal key? (for a PgDown issue in FF)
if (typeof(evt.which)=='undefined' || evt.which)
{
ch = evt.charCode || evt.keyCode;
}
else
{
return;
}
ch = String.fromCharCode(ch);
pressedCharsBuffer += ch;
//
// check for matches
//
for (var i=0; i<arrReplacements.length; i++)
{
// single replacement rule check
var repRule = arrReplacements[i];
if (ch==repRule.chLastChar && pressedCharsBuffer.search(repRule.reMatch)!=-1)
{
if (replaceLatestStr(repRule.objReplaceAtrs, ch)) // note ch is not out yet!
{
evt.preventDefault(); // prevent output of ch
break; // prevent other replacements
}
}
}
//
// Check for finish
//
// overflow?
if (pressedCharsBuffer.length>maxChars)
{
pressedCharsBuffer = pressedCharsBuffer.substr(1); // cut out least important character
}
// unneeded char
if (interestingChars.indexOf(ch)==-1)
{
pressedCharsBuffer = '';
}
};
/**
* Replace text that was just put in the input from `atr.from` to `atr.to`.
*
* @param {type} atr
* @param {type} chLastChar
* @returns {Boolean} true if replaced
*/
var replaceLatestStr = function(atr/*{from, to}*/, chLastChar)
{
// get current position
var startScroll = input.scrollY;
var bounds = sel_t.getSelBound(input);
/**
* Get string just before match.
*/
var getStringBeforeMatch = function(numOfChars)
{
var start = bounds.end - atr.from.length - numOfChars;
var length = numOfChars;
if (start < 0) {
start = 0;
length = bounds.end - atr.from.length;
}
return input.value.substr(start, length);
};
// check if the selected text would be correct
var strBeforeCursor = input.value.substr(bounds.end - atr.from.length, atr.from.length);
if (strBeforeCursor != atr.from) // something went wrong (probably cursor jumped somehow)
{
return false;
}
// bail out when matched (mostly for quotes)
if ('beforeForbidden' in atr)
{
var o = atr.beforeForbidden;
if (typeof(o) === 'string')
{
if (o == getStringBeforeMatch(o.length))
{
return false;
}
}
// `{re:/.../,maxCheck:123}`
else
{
if (o.re.test(getStringBeforeMatch(o.maxCheck)))
{
return false;
}
}
}
// bail out when NOT matched (mostly for quotes)
if ('beforeMustHave' in atr)
{
var o = atr.beforeMustHave;
if (typeof(o) === 'string')
{
if (o != getStringBeforeMatch(o.length))
{
return false;
}
}
// `{re:/.../,maxCheck:123}`
else
{
if (!o.re.test(getStringBeforeMatch(o.maxCheck)))
{
return false;
}
}
}
// add last character to enable undo (note: it might replace selected text)
sel_t.setSelStr(input, chLastChar, false);
//bounds = sel_t.getSelBound(input); // re-read bounds // don't work as expected on Opera (Opera seems to reset bounds to the end of the whole text of the input)
bounds.start += chLastChar.length;
bounds.end = bounds.start;
// prepare new bounds
bounds.start = bounds.end - atr.from.length - chLastChar.length;
if (bounds.start<0) // probably interpretation error
{
return false;
}
// select chars to be replaced
sel_t.setSelBound(input, bounds, false);
// replace
sel_t.setSelStr(input, atr.to, false);
// reset cursor to the end of inserted text
//bounds = sel_t.getSelBound(inp); // don't work as expected on IE (IE seems to reset bounds to a start of the selection)
bounds.start += atr.to.length;
bounds.end = bounds.start;
sel_t.setSelBound(input, bounds, false);
// reset position
input.scrollY = startScroll;
return true;
};
}
// ----------------------------------------------------------------
//
// Autokorekta (pl)
//
//
// init object
//
window.oAutokorekta = new nuxTBKeys('wpTextbox1');
// minus/dywiz na myślnik
oAutokorekta.addReplacements ({from:' - ', to:' – '});
// arrows
oAutokorekta.addReplacements ({from:' -> ', to:' → '});
oAutokorekta.addReplacements ({from:' <- ', to:' ← '});
// quotes
oAutokorekta.addReplacements ({from:' "', to:' „'});
oAutokorekta.addReplacements ({from:'"', to:'”',
beforeForbidden:'=', beforeMustHave:{re:/„[^"”]*$/, maxCheck:5000}
});
/**
* Button settings
*/
oAutokorekta.oImgbtn = {
on : {
src : '//upload.wikimedia.org/wikipedia/commons/thumb/7/77/Vista-clean.png/22px-Vista-clean.png',
alt : 'włączona',
title : 'Autokorekta jest włączona'
},
off : {
src : '//upload.wikimedia.org/wikipedia/commons/thumb/6/65/Gnome-window-close.svg/22px-Gnome-window-close.svg.png',
alt : 'wyłączona',
title : 'Autokorekta jest wyłączona'
}
};
/**
* Button adder
* @returns {Boolean}
*/
oAutokorekta.addDisableButon = function()
{
// add a group in a standard way
jQuery('#wpTextbox1').wikiEditor('addToToolbar', {
'section': 'advanced',
'groups': {
'autorepcontrol': {
'label': 'Autokorekta'
}
}
});
// get added group
var elGroup = $("#wikiEditor-ui-toolbar div.section-advanced div.group-autorepcontrol")[0];
if (!elGroup)
{
return false;
}
// add button
var nel = document.createElement('img');
if (!oAutokorekta.getDisablance())
{
nel.src = oAutokorekta.oImgbtn.on.src;
nel.alt = oAutokorekta.oImgbtn.on.alt;
nel.title = oAutokorekta.oImgbtn.on.title;
}
else
{
nel.src = oAutokorekta.oImgbtn.off.src;
nel.alt = oAutokorekta.oImgbtn.off.alt;
nel.title = oAutokorekta.oImgbtn.off.title;
}
nel.className = "tool tool-button";
nel.style.cssText = "width: 22px; height: 22px; padding-top:2px";
nel.onclick = oAutokorekta.onClickDisableBtn;
elGroup.appendChild(nel);
// show group
elGroup.style.display = '';
// border
elGroup.style.cssText = "border-left: 1px solid #DDDDDD";
// redraw
oAutokorekta.redrawWikiEditorGroup('advanced', 'autorepcontrol');
// seems OK
return true;
};
/**
* Redraw Group after manipulation
*/
oAutokorekta.redrawWikiEditorGroup = function(section, group) {
// add whatever button
$( '#wpTextbox1' ).wikiEditor( 'addToToolbar', {
'section': section,
'group': group,
'tools': {
'smile': {
label: 'Smile!',
type: 'button',
icon: '//upload.wikimedia.org/wikipedia/commons/thumb/a/a4/Gnome-face-smile.svg/22px-Gnome-face-smile.svg.png',
action: {
type: 'encapsulate',
options: {
pre: ":)" // text to be inserted
}
}
}
}
} );
// remove whatever button
$( '#wpTextbox1' ).wikiEditor( 'removeFromToolbar', {
'section': section,
'group': group,
'tool': 'smile'
});
}
/**
* Button click.
*/
oAutokorekta.onClickDisableBtn = function()
{
oAutokorekta.toggleDisablance();
if (this.alt == oAutokorekta.oImgbtn.on.alt)
{
this.src = oAutokorekta.oImgbtn.off.src;
this.alt = oAutokorekta.oImgbtn.off.alt;
this.title = oAutokorekta.oImgbtn.off.title;
jQuery.cookie("js_oAutokorekta_setup", "off", { expires: 7 });
}
else
{
this.src = oAutokorekta.oImgbtn.on.src;
this.alt = oAutokorekta.oImgbtn.on.alt;
this.title = oAutokorekta.oImgbtn.on.title;
jQuery.cookie("js_oAutokorekta_setup", "on", { expires: 7 });
}
// re-set focus
try
{
input.focus();
}
catch (e) {}
};
/**
* Sets up the gadget.
*/
oAutokorekta.setup = function()
{
// Cookie setup stuff
var js_oAutokorekta_setup = '';
if (document.cookie.search(/(^|[ ;])js_oAutokorekta_setup=/) != -1)
{
document.cookie.replace(/(^|[ ;])js_oAutokorekta_setup=(.*?)(;|$)/, function( a, s, val, e )
{
js_oAutokorekta_setup = val;
});
// re-set cookie
jQuery.cookie("js_oAutokorekta_setup", js_oAutokorekta_setup, {expires: 7});
}
// setup default state
if (js_oAutokorekta_setup != 'on') // defaults to off
{
oAutokorekta.setDisablance(true);
}
if (mw.config.get('wgNamespaceNumber' ) < 0)
{
return;
}
// Check if the new toolbar is available
if (mw.user.options.get( 'usebetatoolbar' ) !== 1) {
return;
}
mw.loader.using("ext.wikiEditor", function()
{
$(function()
{
// wyłączone przez użytkownika
if (window.isAutokorektaWylaczona)
{
return;
}
if (oAutokorekta.addDisableButon() || window.isAutokorektaAlwaysOn)
{
oAutokorekta.init();
}
});
});
};
if (mw.config.get( 'wgAction' ) == 'edit' || mw.config.get( 'wgAction' ) == 'submit')
{
oAutokorekta.setup();
}