MediaWiki:Translit.js
Catatan: Cappu mattangkung, ida harusuk mallajo browser cache ida barak géppa mita perubahan na. Mozilla/Firefox/Safari:' tekan itta Shift rilante klik Reload, a klik Ctrl-Shift-R ( Cmd-Shift-R: Apple Mac); IE:, tekan itta Ctrl rilante klik Refresh, a tekan Ctrl-F-5; Konqueror: klik Reload, a klik F5; Opera: harusuk mallajo cache ri Tools→Preferences.
/*
This script is a modified version of Alex benenson's cyrillic translitarator and this version was created by user Kurniasan
*/
// default latin - lontara customized tranlit readable both ways
var defaultLatinicaTable = [["k", "\u1a00"], ["g", "\u1a01"], ["ng", "\u1a02"], ["nk", "\u1a03"], ["p", "\u1a04"], ["b", "\u1a05"], ["m", "\u1a06"], ["mp", "\u1a07"], ["t", "\u1a08"], ["d", "\u1a09"], ["n", "\u1a0a"], ["nr", "\u1a0b"], ["c", "\u1a0c"], ["j", "\u1a0d"], ["ny", "\u1a0e"], ["nc", "\u1a0f"], ["y", "\u1a10"], ["r", "\u1a11"], ["l", "\u1a12"], ["w", "\u1a13"], ["s", "\u1a14"], ["a", "\u1a15"], ["h", "\u1a16"], [".", "\u1a1e"], ["ki", "ᨀᨗ"], ["gi", "ᨁᨗ"], ["ngi", "ᨂᨗ"], ["nki", "ᨃᨗ"], ["pi", "ᨄᨗ"], ["bi", "ᨅᨗ"], ["mi", "ᨆᨗ"], ["mpi", "ᨇᨗ"], ["ti", "ᨈᨗ"], ["di", "ᨉᨗ"], ["ni", "ᨊᨗ"], ["nri", "ᨋᨗ"], ["ci", "ᨌᨗ"], ["ji", "ᨍᨗ"], ["nyi", "ᨎᨗ"], ["nci", "ᨏᨗ"], ["yi", "ᨐᨗ"], ["ri", "ᨑᨗ"], ["li", "ᨒᨗ"], ["wi", "ᨓᨗ"], ["si", "ᨔᨗ"], ["i", "ᨕᨗ"], ["hi", "ᨖᨗ"], ["ku", "ᨀᨘ"], ["gu", "ᨁᨘ"], ["ngu", "ᨂᨘ"], ["nku", "ᨃᨘ"], ["pu", "ᨄᨘ"], ["bu", "ᨅᨘ"], ["mu", "ᨆᨘ"], ["mpu", "ᨇᨘ"], ["tu", "ᨈᨘ"], ["du", "ᨉᨘ"], ["nu", "ᨊᨘ"], ["nru", "ᨋᨘ"], ["cu", "ᨌᨘ"], ["ju", "ᨍᨘ"], ["nyu", "ᨎᨘ"], ["ncu", "ᨏᨘ"], ["yu", "ᨐᨘ"], ["ru", "ᨑᨘ"], ["lu", "ᨒᨘ"], ["wu", "ᨓᨘ"], ["su", "ᨔᨘ"], ["u", "ᨕᨘ"], ["hu", "ᨖᨘ"], ["k`", "ᨀᨛ"], ["g`", "ᨁᨛ"], ["ng`", "ᨂᨛ"], ["nk`", "ᨃᨛ"], ["p`", "ᨄᨛ"], ["b`", "ᨅᨛ"], ["m`", "ᨆᨛ"], ["mp`", "ᨇᨛ"], ["t`", "ᨈᨛ"], ["d`", "ᨉᨛ"], ["n`", "ᨊᨛ"], ["nr`", "ᨋᨛ"], ["c`", "ᨌᨛ"], ["j`", "ᨍᨛ"], ["ny`", "ᨎᨛ"], ["nc`", "ᨏᨛ"], ["y`", "ᨐᨛ"], ["r`", "ᨑᨛ"], ["l`", "ᨒᨛ"], ["w`", "ᨓᨛ"], ["s`", "ᨔᨛ"], ["`", "ᨕᨛ"], ["h`", "ᨖᨛ"], ["ko", "ᨀᨚ"], ["go", "ᨁᨚ"], ["ngo", "ᨂᨚ"], ["nko", "ᨃᨚ"], ["po", "ᨄᨚ"], ["bo", "ᨅᨚ"], ["mo", "ᨆᨚ"], ["mpo", "ᨇᨚ"], ["to", "ᨈᨚ"], ["do", "ᨉᨚ"], ["no", "ᨊᨚ"], ["nro", "ᨋᨚ"], ["co", "ᨌᨚ"], ["jo", "ᨍᨚ"], ["nyo", "ᨎᨚ"], ["nco", "ᨏᨚ"], ["yo", "ᨐᨚ"], ["ro", "ᨑᨚ"], ["lo", "ᨒᨚ"], ["wo", "ᨓᨚ"], ["so", "ᨔᨚ"], ["o", "ᨕᨚ"], ["ho", "ᨖᨚ"], ["ke", "ᨙᨀ"], ["ge", "ᨙᨁ"], ["nge", "ᨙᨂ"], ["nke", "ᨙᨃ"], ["pe", "ᨙᨄ"], ["be", "ᨙᨅ"], ["me", "ᨙᨆ"], ["mpe", "ᨙᨇ"], ["te", "ᨙᨈ"], ["de", "ᨙᨉ"], ["ne", "ᨙᨊ"], ["nre", "ᨙᨋ"], ["ce", "ᨙᨌ"], ["je", "ᨙᨍ"], ["nye", "ᨙᨎ"], ["nce", "ᨙᨏ"], ["ye", "ᨙᨐ"], ["re", "ᨙᨑ"], ["le", "ᨙᨒ"], ["we", "ᨙᨓ"], ["se", "ᨙᨔ"], ["e", "ᨙᨕ"], ["he", "ᨙᨖ"], ["+i", "\u1a17"], ["+u", "\u1a18"], ["+e", "\u1a19"], ["+o", "\u1a1a"], ["+`", "\u1a1b"], ["+", "\u1a1f"]];
var conversionTable = defaultLatinicaTable;
// for compatibility with bookmarklets
function cyr_translit(src) {
return to_cyrillic(src);
}
var conversionHash = undefined;
var maxCyrLength = 0;
var maxLatLength = 0;
function getConversionHash() {
if (conversionHash == undefined) {
conversionHash = new Array();
for (var i = 0; i < conversionTable.length; i++) {
if (!conversionHash[conversionTable[i][0].toUpperCase()]) {
conversionHash[conversionTable[i][0].toUpperCase()] = conversionTable[i].slice(1);
maxLatLength = Math.max(maxLatLength, conversionTable[i][0].length);
}
}
}
return conversionHash;
}
function to_cyrillic(src, output, chunks) {
if (src == undefined || src == "" || src == null)
return src;
if (output == undefined)
output = new String();
var hash = getConversionHash();
var location = 0;
while (location < src.length) {
var len = Math.min(maxLatLength, src.length - location);
var arr = undefined;
var sub;
while (len > 0) {
sub = src.substr(location, len);
arr = hash[sub.toUpperCase()];
if (arr != undefined)
break;
else
len--;
}
// need this for translit on the fly
if (chunks != undefined)
chunks[chunks.length] = [sub, arr == undefined ? 0 : arr[0].length];
if (arr == undefined) {
output += sub;
location ++;
}
else {
// case analysis
var newChar = arr[0];
if (sub.toLowerCase() == sub.toUpperCase() && arr.length > 1 && arr[1] && (newChar.toUpperCase() != newChar.toLowerCase())) {
// need translit hash to determine if previous character (and possibly the one before it)
// were converted and are in upper case
// set prevDud to true previous is not a translated character or simply a blank
// set prevCap to true if previous was translated and was upper case
var prevCh = output.length == 0 ? null : output.substr(output.length - 1, 1);
var prevDud = !prevCh || !getTranslitString(prevCh);
var prevCap = (!prevDud && prevCh == prevCh.toUpperCase());
// sub is caseless but result isn't. case will depend on lookbehind and lookahead
if (prevDud || !prevCap) {
output += newChar.toLowerCase();
prevCap = false;
}
else {
var next = " ";
if (location + len < src.length)
next = src.substr(location + len, 1);
if (next != next.toUpperCase() && next == next.toLowerCase() ) {
//next is lowercase (and not caseless)
output += newChar.toLowerCase();
}
else if (next == next.toUpperCase() && next != next.toLowerCase() ) {
// next is uppercase (and not caseless)
output += newChar.toUpperCase();
}
else {
// next is caseless. output case determined by the case of output[length - 2]
var pprevCh = output.length == 1 ? null : output.substr(output.length - 2, 1);
var pprevDud = !pprevCh || !getTranslitString(pprevCh);
if (!pprevDud && (pprevCh == pprevCh.toUpperCase())) {
//pre-prev is in upper case. output is also uppercase
output += newChar.toUpperCase();
}
else {
output += newChar.toLowerCase();
}
}
}
}
else if ((sub.toLowerCase() == sub.toUpperCase()) && (arr.length < 2 || !arr[1])) {
// literal treatment of newChar
output += newChar;
}
else if (sub != sub.toLowerCase()) {
if (newChar.length > 1 && sub != sub.toUpperCase()) {
// capitalize first letter of newChar
output += newChar.substr(0, 1).toUpperCase() + newChar.substr(1).toLowerCase();
}
else {
output += newChar.toUpperCase();
}
}
else {
// sub is lowercase
output += newChar.toLowerCase();
}
location += len;
}
}
return output;
}
// split string on HTML tags, return array containing both the matches and the pieces of string between them, matches always in even positions - since IE does not support this in String.split
function splitHtmlString(string) {
var re = /<[\/]?[!A-Z][^>]*>/ig;
var result = new Array();
var lastIndex = 0;
var arr = null;
while ( (arr = re.exec(string)) != null) {
result[result.length] = string.substring(lastIndex, arr.index);
result[result.length] = string.substring(arr.index, re.lastIndex);
lastIndex = re.lastIndex;
}
result[result.length] = string.substr(lastIndex);
return result;
}
/* convert cyrillic to translit using to_translit-- similar to from_translit.... */
function to_translit_ext (src, skipHtml) {
return convertWithSkip(src, skipHtml, to_translit);
}
/* convert translit to cyrillic (using to_cyrillic above) */
function to_cyrillic_ext (src, skipHtml) {
return convertWithSkip(src, skipHtml, to_cyrillic);
}
function convertWithSkip(src, skipHtml, converter) {
if (src == "" || src == null)
return src;
if (!skipHtml)
return converter(src);
else {
var arr = splitHtmlString(src);
for (var i = 0; i < arr.length; i++) {
if ( (i % 2) == 0)
arr[i] = converter(arr[i]);
}
return arr.join("");
}
}
var translitHash = undefined;
function initTranslit() {
if (translitHash == undefined) {
translitHash = new Array();
for (var i = 0; i < conversionTable.length; i++) {
var ch = conversionTable[i][1];
maxCyrLength = Math.max(maxCyrLength, ch.length);
// if the translit string is not caseless, convert cyr string to upper case
// otherwise maintain its case
if (conversionTable[i][0].toUpperCase() != conversionTable[i][0].toLowerCase())
ch = ch.toUpperCase();
if (translitHash[ch] == undefined)
translitHash[ch] = conversionTable[i][0];
}
}
}
/* convert cyrillic to translit */
function getTranslitString(ch) {
initTranslit();
var value = translitHash[ch];
if (value == undefined)
value = translitHash[ch.toUpperCase()];
return value;
}
function to_translit(src) {
/*
if (src == undefined || src == "" || src == null)
return src;
var output = new String();
for (var i = 0; i < src.length; i++) {
var ch = src.substr(i, 1);
var value = getTranslitString(ch);
if (value != undefined) {
if (ch != ch.toUpperCase()) {
output += value.toLowerCase();
}
else {
prev = i == 0 ? null : src.substr(i - 1, 1);
next = i == src.length - 1 ? null : src.substr(i + 1, 1);
if ( value.length == 1 ||
(prev && prev == prev.toUpperCase()) ||
(next && next == next.toUpperCase())) {
// completely capitalize
output += value.toUpperCase();
}
else {
// capitalize first letter
output += value.substr(0, 1).toUpperCase() + value.substr(1).toLowerCase();
}
}
}
else
output += ch;
}
return output;
*/
if (src == undefined || src == "" || src == null)
return src;
//force initTranslit to get the value of maxCyrLength
getTranslitString("");
var output = new String();
var loc = 0;
while (loc < src.length) {
//for (var i = 0; i < src.length; i++) {
var len = Math.min(maxCyrLength, src.length - loc);
var value = undefined;
var str = undefined;
while (len > 0) {
str = src.substr(loc, len);
value = getTranslitString(str);
if (value == undefined)
len--;
else
break;
}
//var ch = src.substr(i, 1);
//var value = getTranslitString(ch);
if (value != undefined) {
if (str == str.toLowerCase()) {
output += value.toLowerCase();
}
else if (str != str.toUpperCase() && value.length > 1) {
// neither upper nor lower - upcase first letter and be done
output += value.substr(0, 1).toUpperCase() + value.substr(1).toLowerCase();
}
else {
// str is all upcase
var prev = loc == 0 ? null : src.substr(loc - value.length, 1);
var next = loc == src.length - str.length ? null : src.substr(loc + str.length, 1);
if ( value.length == 1 || str.length > 1 ||
(prev && prev == prev.toUpperCase()) ||
(next && next == next.toUpperCase())) {
// completely capitalize
output += value.toUpperCase();
}
else {
// capitalize first letter
output += value.substr(0, 1).toUpperCase() + value.substr(1).toLowerCase();
}
}
}
else
output += str;
loc += str.length;
}
return output;
}
//-- translit on-the-fly --
function replaceValue(node, value, stepback) {
if (stepback == undefined)
stepback = 0;
if (isExplorer()) {
var range = document.selection.createRange();
range.moveStart("character", -stepback);
range.text = value;
range.collapse(false);
range.select();
}
else {
var scrollTop = node.scrollTop;
var cursorLoc = node.selectionStart;
node.value = node.value.substring(0, node.selectionStart - stepback) + value +
node.value.substring(node.selectionEnd, node.value.length);
node.scrollTop = scrollTop;
node.selectionStart = cursorLoc + value.length - stepback;
node.selectionEnd = cursorLoc + value.length - stepback;
}
}
// compare positions
function positionIsEqual(other) {
if (isExplorer())
return this.position.isEqual(other.position);
else
return this.position == other.position;
}
function Position(node) {
if (node.selectionStart != undefined)
this.position = node.selectionStart;
else if (document.selection && document.selection.createRange())
this.position = document.selection.createRange();
this.isEqual = positionIsEqual;
}
function resetState() {
this.position = new Position(this.node);
this.transBuffer = "";
this.cyrBuffer = "";
}
function StateObject(node) {
this.node = node;
this.reset = resetState;
this.cyrBuffer = "";
this.transBuffer = "";
this.position = new Position(node);
}
var stateHash = new Array();
function isExplorer() {
return (document.selection != undefined && document.selection.createRange().isEqual != undefined);
}
function pressedKey(event) {
if (isExplorer())
return event.keyCode;
else
return event.which;
}
function translitonkey(event) {
/*
if ((event.keyCode == 255 && event.charCode > 0) || event.keyCode == 8) {
return;
}
*/
if (event == undefined)
event = window.event;
var node = null;
if (event.target)
node = event.target;
else if (event.srcElement)
node = event.srcElement;
// initialize state
var state = stateHash[node];
if (state == null) {
state = new StateObject(node);
stateHash[node] = state;
}
if ( (pressedKey(event) > 20) && !event.ctrlKey && !event.altKey && !event.metaKey) {
var c = String.fromCharCode(pressedKey(event));
// process input
var result = process_translit(state, c);
// finish up
if (c != result.out || result.replace != 0) {
if (isExplorer())
event.returnValue = false;
else
event.preventDefault();
replaceValue(node, result.out, result.replace);
state.position = new Position(node);
}
}
}
function TranslitResult() {
this.out = "";
this.replace = 0;
}
function process_translit(state, c) {
// reset state if position changed
if (!state.position.isEqual(new Position(state.node)))
state.reset();
var result = new TranslitResult();
// initial backbuffer. Add to it as characters are converted
var backbuffer = getBackBuffer(state.node, state.cyrBuffer.length, 2 * maxLatLength);
var chunks = new Array();
state.transBuffer = state.transBuffer + c
var str = to_cyrillic(state.transBuffer, backbuffer, chunks);
// remove backbuffer from output
str = str.substr(backbuffer.length);
result.out = str;
/* str is now left alone - it has the output matching contents of chunks and
will be used to reinitialize backbuffers, along with chunks and state.transBuffer
*/
// get the difference between state.cyrBuffer and output
for (var i = 0; i < Math.min(state.cyrBuffer.length, result.out.length); i++) {
if (state.cyrBuffer.substr(i, 1) != result.out.substr(i, 1)) {
result.replace = state.cyrBuffer.length - i;
result.out = result.out.substr(i);
break;
}
}
if (result.replace == 0) {
result.out = result.out.substr(Math.min(state.cyrBuffer.length, result.out.length));
}
// update state: backbuffer, bufferArray
if (chunks.length > 0 && chunks[chunks.length - 1] == result.out.substr(result.out.length - 1)) {
// no convertion took place, reset state
state.reset();
}
else {
while (state.transBuffer.length > maxLatLength) {
state.transBuffer = state.transBuffer.substr(chunks[0][0].length);
// chunks[i][1] evaluates to false if no conversion took place, otherwise holds the length of cyr string
str = str.substr(chunks[0][1] ? chunks[0][1] : chunks[0][0].length);
chunks.shift();
//chunks.shift();
//str = str.substr(1);
}
state.cyrBuffer = str;
}
return result;
}
function getBackBuffer(node, offset, count) {
if (isExplorer()) { //.tagName.toUpperCase() == "EDITOR") {
var range = document.selection.createRange();
range.moveStart("character", -offset);
var result = range.text.substr(-count);
if (!result)
result = "";
return result;
} else {
return node.value.substring(0, node.selectionStart - offset).substr(-count);
}
}
// need this for bookmarklets
function getSelectedNode() {
if (document.activeElement)
return document.activeElement;
else
if (window.getSelection && window.getSelection() && window.getSelection().rangeCount > 0) {
var range = window.getSelection().getRangeAt(0);
if (range.startContainer && range.startContainer.childNodes && range.startContainer.childNodes.length > range.startOffset)
return range.startContainer.childNodes[range.startOffset]
}
return null;
}
function toggleCyrMode() {
var node = getSelectedNode();
if (node) {
if (stateHash[node]) {
if (removeKeyEventListener(node))
delete stateHash[node];
}
else {
if (addKeyEventListener(node))
stateHash[node] = new StateObject(node);
}
}
}
function addKeyEventListener(node) {
if (node.addEventListener)
node.addEventListener("keypress", translitonkey, false);
else if (node.attachEvent)
node.attachEvent("onkeypress", translitonkey);
else return false;
return true;
}
function removeKeyEventListener(node) {
if (node.removeEventListener)
node.removeEventListener("keypress", translitonkey, false);
else if (node.detachEvent)
node.detachEvent("onkeypress", translitonkey);
else return false;
return true;
}
function getSelectedText() {
if (isExplorer()) {
return document.selection.createRange().text;
}
else {
var node = getSelectedNode();
if (node && node.value && node.selectionStart != undefined && node.selectionEnd != undefined)
return node.value.substring(node.selectionStart, node.selectionEnd);
}
return "";
}
function bmkToCyrillic() {
batchConverter(to_cyrillic_ext);
}
function bmkToTranslit() {
batchConverter(to_translit_ext);
}
function RangeConversionState(range, converter) {
this.range = range;
this.convert = converter;
this.started = false;
this.finished = false;
this.toString = function() {
return "started : " + this.started + ", finished: " + this.finished;
};
}
function convertRangeNode(node, state) {
if (state.started && state.finished)
return;
if (!state.started &&
( ( (state.range.startContainer.nodeType == node.TEXT_NODE ||
state.range.startContainer.nodeType == node.PROCESSING_INSTRUCTION_NODE ||
state.range.startContainer.nodeType == node.COMMENT_NODE )
&& node == state.range.startContainer)
||
( state.range.startContainer.childNodes && node == state.range.startContainer.childNodes[state.range.startOffset])
))
state.started = true;
if (node.nodeType == node.TEXT_NODE || node.nodeType == node.PROCESSING_INSTRUCTION_NODE || node.nodeType == node.COMMENT_NODE) {
if (state.started && !state.finished) {
// convert text
var start = (node == state.range.startContainer) ? state.range.startOffset : 0;
var end = (node == state.range.endContainer) ? state.range.endOffset : node.nodeValue.length;
var remainder = (node == state.range.endContainer) ? node.nodeValue.length - state.range.endOffset : 0;
node.nodeValue =
node.nodeValue.substring(0, start) +
state.convert(node.nodeValue.substring(start, end)) +
node.nodeValue.substr(end);
if (node == state.range.endContainer)
state.range.setEnd(node, node.nodeValue.length - remainder);
if (node == state.range.startContainer)
state.range.setStart(node, start);
}
}
else if (node.childNodes)
// walk the tree
for (var i = 0; i < node.childNodes.length; i++) {
convertRangeNode(node.childNodes[i], state);
if (state.started && state.finished)
break;
}
if (!state.finished &&
( ((state.range.endContainer.nodeType == node.TEXT_NODE ||
state.range.endContainer.nodeType == node.PROCESSING_INSTRUCTION_NODE ||
state.range.endContainer.nodeType == node.COMMENT_NODE )
&& node == state.range.endContainer)
||
( (state.range.endContainer.childNodes.length > 0) && node == state.range.endContainer.childNodes[state.range.endOffset - 1])
))
state.finished = true;
}
function convertSelection (selection, converter) {
if (selection == null) return;
for(var i = 0; i < selection.rangeCount; i++) {
convertRangeNode(selection.getRangeAt(i).commonAncestorContainer, new RangeConversionState(selection.getRangeAt(i), converter));
}
selection.collapseToEnd();
}
function batchConverter(convert) {
if (isExplorer()) {
var range = document.selection.createRange();
try {
range.pasteHTML(convert(range.htmlText, true));
}
catch (err) {
range.text = convert(range.text, true);
}
}
else if (window.getSelection) {
var node = getSelectedNode();
var sel = window.getSelection();
if (node && node.value && node.selectionStart != undefined && node.selectionEnd != undefined)
replaceValue(node, convert(node.value.substring(node.selectionStart, node.selectionEnd), true));
else if(sel && sel.toString() != "")
convertSelection(sel, convert);
}
}