User:V111P/SKLChanger.js
Note: After saving, you have to bypass your browser's cache to see the changes. Internet Explorer: hold down the Ctrl key and click the Refresh or Reload button. Firefox: hold down the Shift key while clicking Reload (or press Ctrl-Shift-R). Google Chrome and Safari users can just click the Reload button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
/* 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: '<s>OFF</s>', 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 = $('<a/>', { 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, '&').replace(/</g, '<').replace(/>/g, '>')); } 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);