User:V111P/SKLChanger.js

/* Simple Keyboard Layout Changer v. 2013-11-17 * http://en.wikipedia.org/wiki/User:V111P/js/Simple_Keyboard_Layout_Changer * * With this script you can transform the characters you type in input boxes and textareas * into different characters - for example, into characters from a different language. * The script supports multiple groups of characters to convert to and you can switch * between the different groups with a click on a link. * Warning: Test the undo function (Ctrl-Z) in your browser to see if you like it. * Undo doesn't work in Google Chrome 30 at all. * * CC0 Public Domain Dedication: * http://creativecommons.org/publicdomain/zero/1.0/ */

window.sKLChanger = (function ($) { 'use strict';

// US keyboard layout is used by default var defaultCharsToConvert = '`-=' + 'qwertyuiop[]\\' + 'asdfghjkl;\'' + 'zxcvbnm,./' // with shift + '~!@#$%^&*_+'	+ 'QWERTYUIOP{}|' + 'ASDFGHJKL:"'	+ 'ZXCVBNM<>?';

var lang = mw.config.get('wgUserLanguage'); var msgs = { indicatorLabelWhenTurnedOff_short: (lang == 'bg' ? 'Изкл.' : 'OFF'), indicatorLabelWhenTurnedOff_long: (lang == 'bg'		? 'Превключвателят е изключен'		: 'The Keyboard Layout Changer is turned off'), nonsupportedBrowserMsg_short: ' OFF ', nonsupportedBrowserMsg_long: (lang == 'bg'		? 'Превключвателят на клавиатурни подредби няма да работи с вашия браузър'		: 'Simple Keyboard Layout Changer: Browser not supported') };

var defaultIndicatorPos = { nearElementSelector: '#editform', //'prepend' on #editform doesn't (always) work in IE	position: 'before' // 'before', 'after', 'prepend', 'append' };

var inputBoxesSelector = 'textarea, input[type="text"], input[type="password"], ' + 'input:not([type]), input[name="search"], input#searchInput'; // Some MediaWiki skins don't have type="text" attribute on their search input boxes. // :not not supported in older browsers

var inputBoxesDeselector = 'input#wpAntispam'; // #wpAntispam is a hidden input box in MediaWiki

var showLayoutHint; // if true, the string with the characters will be shown // in the title attribute of the long-label indicator link

var longLabelClassName = 'sKL_longIndicator';

var keyboardLayouts = []; var currLayoutN = -1; // none initially var charsToConvertMap = {}; var keyboardLayoutIndicator = $([]); // create an empty jQuery object var browser; // 'ok', 'ie', 'other'

// init - auto called if a window.sKLChangerConfig object exists at the time of the loading of this file function init(configObj) { // jQuery is passing a function as an argument on $(init) configObj = (typeof configObj == 'object' ? configObj : (window.sKLChangerConfig || {}));

var inputArea = $(inputBoxesSelector).not(inputBoxesDeselector) .filter(':visible')[0]; // hidden elements cause an error in Firefox 6

if (!inputArea) return;

setCharsToConvert(configObj.charsToConvert || defaultCharsToConvert); showLayoutHint = (configObj.showLayoutHint === false ? false : true);

// add-and-replace the passed messages to the msgs object $.extend(msgs, configObj.messages); if (typeof configObj.offName == 'string') msgs.indicatorLabelWhenTurnedOff_short = configObj.offName; if (typeof configObj.offLabel == 'string') msgs.indicatorLabelWhenTurnedOff_long = configObj.offLabel;

if ((window.wikEd && wikEd.useWikEd)		|| (inputArea && typeof inputArea.selectionStart != 'undefined')	) browser = 'ok'; else if (document.selection && document.selection.createRange) browser = 'ie'; // old IE & IE in Compatibility View mode else { browser = 'other'; // won't work msgs.indicatorLabelWhenTurnedOff_long = msgs.nonsupportedBrowserMsg_long; }

removeAllLayouts;

if (configObj.layouts) $.each(configObj.layouts, function (i, val) {			addLayout(val.name, val.label, val.chars);		});

addIndicator(configObj.indicatorPosition); var defaultLayout = configObj.defaultLayout; if (typeof defaultLayout != 'undefined') turnOn(defaultLayout); }

function addIndicator(positionObj) { var longIndicatorID = 'sKL_indicator2'; var label_short = msgs.indicatorLabelWhenTurnedOff_short; var label_long = msgs.indicatorLabelWhenTurnedOff_long; if (browser == 'other') { label_short = msgs.nonsupportedBrowserMsg_short; label_long = msgs.nonsupportedBrowserMsg_long; }

keyboardLayoutIndicator.remove; // remove any indicators already on the page keyboardLayoutIndicator = $([]); $('#sKLChangerPortlet').remove; $('#'+longIndicatorID).remove;

// first add the indicator in the "personal bar" mw.util.addPortletLink('p-personal', '#', '[' + label_short + ']',			'sKLChangerPortlet', label_long); var indicatorLink = $('#sKLChangerPortlet').find('a'); keyboardLayoutIndicator = keyboardLayoutIndicator.add(indicatorLink);

// then add the indicator/switch near the specified element, if it exists

var nearElementSelector = defaultIndicatorPos.nearElementSelector; var position = defaultIndicatorPos.position;

if (positionObj			&& typeof positionObj.nearElementSelector == 'string'			&& typeof positionObj.position == 'string'			&& $(positionObj.nearElementSelector).length != 0	) { nearElementSelector = positionObj.nearElementSelector; position = positionObj.position; }

if ($(nearElementSelector).length > 0) { var indicator2 = $('', {				href: '#',				id: longIndicatorID,				'class': longLabelClassName,				text: '[' + label_long + ']'			});

switch (position) { case 'before' : indicator2.insertBefore(nearElementSelector); break; case 'after' : indicator2.insertAfter(nearElementSelector); break; case 'prepend': indicator2.prependTo(nearElementSelector); break; case 'append' : default: indicator2.appendTo(nearElementSelector); }

keyboardLayoutIndicator = keyboardLayoutIndicator.add(indicator2); }

// finally, attach the event handler to all indicators keyboardLayoutIndicator.bind('click', function(event) {				event.preventDefault;				nextLayout;			}); }

function removeLayout(layoutToRemove) { if (typeof layoutToRemove == 'string') { $.each(keyboardLayouts, function(indexInArray, layout) {			if (layout.name == layoutToRemove) {				turnOff;				keyboardLayouts.splice(indexInArray, 1);				return false;			}		}); }	else if (typeof layoutToRemove == 'number') { turnOff; keyboardLayouts.splice(layoutToRemove, 1); } }

function removeAllLayouts { turnOff; keyboardLayouts = []; }

function getAllLayouts { return keyboardLayouts; }

function addLayout(name, label, layoutStr, position) { if (!name || !label || !layoutStr) return;

if (typeof position == 'undefined'		|| position >= keyboardLayouts.length		|| position < 0	) position = keyboardLayouts.length;

var layout = {name:	name, label: label, layoutStr: layoutStr}; keyboardLayouts.splice(position, 0, layout); if (position <= currLayoutN) currLayoutN++; // keep current layout }

function setCharsToConvert(charStr) { charStr = charStr || '';

// each character code of the chars in charStr is converted // to a property name in charsToConvertMap with the value being // the position of the char in charStr. for (var i = 0; i < charStr.length; i++) { charsToConvertMap[charStr.charCodeAt(i)] = i;	} }

function error(msg) { if (console && console.error) console.error('Simple Keyboard Layout Changer: ' + msg); }

function layoutNameToIndex(layoutName) { if (typeof layoutName != 'string') { error('layoutNameToIndex: layout-name string expected, got: ' + layoutName); return -1; }	for (var i = 0; i < keyboardLayouts.length; i++) if (keyboardLayouts[i].name === layoutName) return i;	error('layoutNameToIndex: Can\'t find layout with name "' + layoutName + '"'); return -1; }

function turnOn(layoutName) { if (keyboardLayouts.length == 0) { error('No keyboard layouts defined.'); return; }

if (typeof layoutName == 'undefined') currLayoutN = 0; else { currLayoutN = layoutNameToIndex(layoutName); if (currLayoutN == -1) { turnOff; return; }	}

$(inputBoxesSelector).on('keypress.sKLChanger', keyPressed); if (window.wikEd) { wikEd.AddEventListener(wikEd.frameDocument, 'keypress', keyPressed, true); }	setIndicatorLabel; }

function turnOff { $(inputBoxesSelector).off('keypress.sKLChanger'); currLayoutN = -1; setIndicatorLabel; if (window.wikEd) { wikEd.RemoveEventListener(wikEd.frameDocument, 'keypress', keyPressed, true); } }

function nextLayout { if (currLayoutN + 1 == keyboardLayouts.length) turnOff; else if (currLayoutN == -1) turnOn; else { currLayoutN++; setIndicatorLabel; } }

function setIndicatorLabel { setTimeout(safeSetIndicatorLabel, 1); }

function safeSetIndicatorLabel { var layoutName, layoutLabel;

var longIndicatorTitleAttr = (currLayoutN > -1 && showLayoutHint			? keyboardLayouts[currLayoutN].layoutStr			: '');

if (currLayoutN > -1) { layoutName = keyboardLayouts[currLayoutN].name; layoutLabel = keyboardLayouts[currLayoutN].label; }	else { layoutName = msgs.indicatorLabelWhenTurnedOff_short; layoutLabel = msgs.indicatorLabelWhenTurnedOff_long; }

keyboardLayoutIndicator.each(function(index, indicatorEl) {		var indicator = $(indicatorEl);		var txt, title;

if (indicator.hasClass(longLabelClassName)) { txt = layoutLabel; title = longIndicatorTitleAttr; }		else { txt = layoutName; title = layoutLabel; }

setTimeout(function {			indicator.text('[' + txt + ']').attr('title', title);		}, 1); }); }

function keyPressed(event) { var charCode = event.charCode || event.keyCode; if (event.charCode === 0) charCode = 0; // Firefox non-printable

if (charCode == 0 || event.ctrlKey || event.altKey || event.metaKey) return true;

switch (charCode) { case 0: //Non-printable case 13: //Enter case 27: //Esc (for IE) case 32: //Space return true; }

var currLayoutStr = keyboardLayouts[currLayoutN].layoutStr; var index = charsToConvertMap[charCode]; if (typeof index == 'undefined') return true; var charToReplaceWith = currLayoutStr.charAt(index);

if (charToReplaceWith) { event.preventDefault; var inputBox = this; setTimeout(function { // if the textarea value is changed directly, IE clears its undo history			insertStr(charToReplaceWith, inputBox);		}, 1); } }

function insertStr(str, inputBox) { if (window.wikEd && wikEd.useWikEd && inputBox == wikEd.frameDocument) { wikEd.FrameExecCommand('inserthtml',			str.replace(/&/g, '&amp;').replace(//g, '&gt;')); }	else if (browser == 'ok') { var startPos = inputBox.selectionStart; var endPos = inputBox.selectionEnd; var scrollTop = inputBox.scrollTop; var pos = startPos + str.length; inputBox.value = inputBox.value.substring(0, startPos) + str + inputBox.value.substring(endPos, inputBox.value.length); inputBox.focus; inputBox.setSelectionRange(pos, pos); inputBox.scrollTop = scrollTop; }	else if (browser == 'ie') { var range = document.selection.createRange; if (range.text) document.selection.clear; range.text = str; } }

if (window.sKLChangerConfig) { if (document.readyState == 'complete' || document.readyState == 'interactive') init; else $(init); // but $ doesn't work after errors }

return { init: init, removeLayout: removeLayout, removeAllLayouts: removeAllLayouts, getAllLayouts: getAllLayouts, addLayout: addLayout, setCharsToConvert: setCharsToConvert, turnOn: turnOn, turnOff: turnOff, nextLayout: nextLayout, // variables / constants: defaultCharsToConvert: defaultCharsToConvert };

})(jQuery);