easy-typing-input-tool
Version:
A tool utilizing google input tool for the use of easy typing repositories such as ENT, EHt & EBT
996 lines (836 loc) • 36.7 kB
JavaScript
/**
* Pre-requirement : JQuery
*
* Usage:
*
* Minimum Requirement:
* 1. On HTML:
* ===================================
*
* Minimum:
* ----------------------------------
*
* <!-- REQUIRED! for displaying message -->
* <div id="input_tool_helper">
* <em>Additional options appears here!</em>
* </div>
* <textarea id="input_tool" <!-- REQUIRED! -->
* dir="ltr|rtl" <!-- Optional (required to specify script with right to left for e.g. Urdu, Arabic)-->
* >
* <!-- REQUIRED! -->
* <input type="hidden" name="googleInputKeyCode" value="specify input key code for e.g., (ne-t-i0-und)">
*
*
* For ENT (Based on Mobile Version):
* ----------------------------------
*
* <div id="input_tool_helper">
* <em>Additional options appears here!</em>
* </div>
* <div id="TextareaWrapper">
* <div id="CopyTool"
* class="tool-button-theme light-grey"
* style="right: 10px; bottom: 8px"
* >Copy</div>
*
* <textarea id="input_tool"
* class="mobileLanguageTextarea copiable storable"
* dir="<?= $textDirection ?>"
* ></textarea>
* <input type="hidden" name="googleInputKeyCode" value="<?= $androidInputKeyCode; ?>">
* </div>
*
*
*
*
* 2. Javascript:
* ===================================
* <script type="text/javascript" src="dist/google-input-tool-bundle.js?ver=<?=$version?>"></script>
*
*
*
*
* 3. Style Sheet:
* ===================================
* Apply own style
*
*
* OR (ENT Mobile Style):
* ----------------------------------
* #input_tool_helper {
* height: 33px;
* width: 100%;
* text-align: right;
* color: #ccc;
* }
* #input_tool_helper > .btn {
* margin: 0px 1px;
*
* // Remove on select highlight border
* outline: 0;
* }
* #input_tool_helper > em {
* font-size: 12px;
* }
*/
/**
* npm install google-input-tool --save
*/
const googleInputTool = require('google-input-tool');
// $(document).ready(googleInputTool.transliterate());
module.exports = {transliterate: googleTransliterationTool};
function googleTransliterationTool(args) {
args = args || {};
const inputTextareaIds = args.inputTextareaIds || '#input_tool, #languageTextarea, #emailContents, #translationTextarea';
const inputHelperToolId = args.inputHelperTool || '#input_tool_helper';
const additionalPopUpTopMargin = args.additionalPopUpTopMargin || 0;
const debug = args.debug || false;
if (debug) {
console.log('google transliteration tool', args);
}
/**
* Applies to both space and backspace.
* @type {boolean}
*/
const suggestionPopupAfterSpace = args.suggestionPopupAfterSpace || false;
const suggestionPopupOnClick = args.suggestionPopupOnClick || false;
let googleInputKeyCode = args.googleInputKeyCode || $("input[name=googleInputKeyCode]").val();
let googleTransliterationArea = $(inputTextareaIds);
const inputToolHelper = $(inputHelperToolId);
const maxResult = args.maxResult || 6;
let request = new XMLHttpRequest();
let ctrlGPressed = false;
const suggestionDropdown = document.createElement('div');
suggestionDropdown.id = 'InputSuggestions';
const suggestionDropdownContent = document.createTextNode('Option');
googleTransliterationArea.on('input', function (inputEvent) {
suggestionDropdown.remove();
});
if (!googleInputKeyCode) {
googleInputKeyCode = 'ne-t-i0-und';
console.error('Google Input Key Code not defined. So defaulting to Nepali (ne-t-i0-und). ' +
'Please specify hidden input: ' +
'<input type="hidden" name="googleInputKeyCode" value="inputKeyCode(ne-t-i0-und)">'
)
}
/**
* Detect if CTRL + G key is pressed
*/
googleTransliterationArea.on('keydown', function(event) {
if (isCtrlG(event)) {
ctrlGPressed = !ctrlGPressed;
}
});
/**
* Keeps the mapping of transliterated input word and its english word
*
* {
* सम्भु : "sambhu",
* राज : "raj"
* }
*/
let transliteratedWordEnglishMapping = {};
/**
* Backspace is not detected on keypress, so key up used
*/
googleTransliterationArea.on('keyup', function (e) {
// On clicking backspace remove the helper
if (e.which === 8) {
inputToolHelper.empty();
if (ctrlGPressed) {
inputToolHelper.append('<em>English mode enabled. Press (CTRL + G) to switch back.</em>');
} else {
inputToolHelper.append('<em>Additional options appears here!</em>');
}
}
});
/**
* On clicking the word on the textarea.
* 1. Get the selected word for e.g.: सम्भु
* 2. Get the english version of the selected word from the transliteratedWordEnglishMapping.
* In this case, it will be 'sambhu'
* 3. Get the additional options as an array:
* For example, ["सम्भु", "संभु", "समभु", "सम्भू", "संभू", "sambhu"]
* 4. Now on clicking the option :
* a) Update the transliteratedWordEnglishMapping
* b) replaced the selectedWord with the one clicked.
*/
googleTransliterationArea.click(function(e) {
console.log('1. Backup Google Input 1...');
removeSuggestionDropdown();
const stopCharacters = [' ', '\n', '\r', '\t'];
const text = $(this).val();
let start = $(this)[0].selectionStart;
let end = $(this)[0].selectionEnd;
while (start > 0) {
if (stopCharacters.indexOf(text[start]) == -1) {
--start;
} else {
break;
}
}
++start;
while (end < text.length) {
if (stopCharacters.indexOf(text[end]) == -1) {
++end;
} else {
break;
}
}
// 1. Get the selected word for e.g.: सम्भु
const selectedWord = text.substr(start, end - start);
if (selectedWord) {
//2. Get the english version of the selected word from the transliteratedWordEnglishMapping.
let selectedWordEnglishVersion = transliteratedWordEnglishMapping[selectedWord];
/**
* Some time user would choose to enter the text in english and click it.
* In this case, selected word is the selected word in english version.
*/
if (typeof selectedWordEnglishVersion === 'undefined') {
selectedWordEnglishVersion = selectedWord;
}
//3. Get the additional options as an array:
googleInputTool(request, selectedWordEnglishVersion, googleInputKeyCode, maxResult)
.then(function(response) {
// makeTransliterationInventoryPostRequest(
// 'https://www.api.phplab.info/v1/transliteration-inventory',
// transliterationPostData(response, lastWord, googleInputKeyCode)
// )
// .then(data => {
// console.log('Success', data);
// })
// .catch(error => {
// console.error('Error:', errors)
// });
/**
* Add all the response word to a mapping
*/
response.forEach(function(responseWordAsKey) {
transliteratedWordEnglishMapping[responseWordAsKey[0]] = selectedWordEnglishVersion;
});
const selectedWordWithSpecialCharacter = selectedWordEnglishVersion.match(/[A-Za-z0-9\u00C0-\u024F\u1E00-\u1EFF]+/)[0];
/**
* Add english word on the list to be displayed on additional options.
*/
response.push([selectedWordEnglishVersion, selectedWordWithSpecialCharacter]);
/**
* Clear the content of the div
*/
inputToolHelper.empty();
response.forEach(function(input) {
const inputHelperToolBtnOnClick = document.createElement('button');
inputHelperToolBtnOnClick.setAttribute('value', input[0]);
inputHelperToolBtnOnClick.innerHTML = input[1];
inputHelperToolBtnOnClick.classList.add('btn');
inputToolHelper.append(inputHelperToolBtnOnClick);
});
if (suggestionPopupOnClick === true) {
popUpSuggestionDropdown(
response,
googleTransliterationArea,
suggestionDropdown,
suggestionDropdownContent,
additionalPopUpTopMargin
);
}
//4. Now on clicking the option :
$(inputHelperToolId + ' .btn, #InputSuggestions .suggestion-btn').on('click', function(optionClickedEvent) {
let buttonClickedValue = (typeof this.value !== 'undefined') ? this.value : this.innerHTML;
//4 a) Update the transliteratedWordEnglishMapping
transliteratedWordEnglishMapping[buttonClickedValue] = selectedWordEnglishVersion;
/**
* Using start and end value slice the text and add the buttonClickedValue
*/
const modifiedTexts = text.slice(0, start) + buttonClickedValue + text.slice(end);
// b) replaced the selectedWord with the one clicked.
googleTransliterationArea.val(htmlDecode(modifiedTexts));
removeSuggestionDropdown();
});
}
);
}
});
googleTransliterationArea.on('keyup', function (e) {
/**
* If Ctrl G then don't transliterate
*/
if (ctrlGPressed) {
inputToolHelper.empty();
inputToolHelper.append('<em>English mode enabled. Press (CTRL + G) to switch back.</em>');
return;
}
if (e.which === 32 || e.which === 229 || e.which === 8) { //Is space or backspace
let words = $(this).val().split(" ");
let splitTextareaContent = false;
let cursorEndToEndOfTextPart = '';
let totalLengthOfCurrentCursorEndPosition = 0;
const currentCursorPosition = getInputSelection(e.currentTarget);
const textareaText = e.target.value;
const fullLengthOfTheString = textareaText.length;
let lastWord = null;
if (e.which === 8) { // If it is a backspace
if (currentCursorPosition.end !== fullLengthOfTheString) {
// console.log('to add logic to retrieve word');
}
//Find the last word which is in locale language. For e.g. हाम्रो
let lastLocaleWord = words[words.length - 1];
//Now find the pronunciation in english for e.g. hamro
lastWord = transliteratedWordEnglishMapping[lastLocaleWord];
if (typeof lastWord === 'undefined') {
return;
}
return;
} else {
//Need to take care of space, plus index 0 - so need to deduct 2 to get last word
lastWord = words[words.length - 2];
}
/**
* Don't transliterate if last word is undefined.
*/
if (lastWord === null) {
return;
}
if (currentCursorPosition.end !== fullLengthOfTheString) {
totalLengthOfCurrentCursorEndPosition = currentCursorPosition.end;
const endOfCursorTextPart = textareaText.substr(0, currentCursorPosition.end);
cursorEndToEndOfTextPart = textareaText.substr(currentCursorPosition.end, fullLengthOfTheString);
splitTextareaContent = true;
words = endOfCursorTextPart.split(' ');
lastWord = words[words.length - 2];
totalLengthOfCurrentCursorEndPosition -= lastWord.length;
}
googleInputTool(request, lastWord, googleInputKeyCode, maxResult)
.then(function(response) {
// makeTransliterationInventoryPostRequest(
// 'https://www.api.phplab.info/v1/transliteration-inventory',
// transliterationPostData(response, lastWord, googleInputKeyCode)
// )
// .then(data => {
// console.log('Success', data);
// })
// .catch(error => {
// console.error('Error:', errors)
// });
/**
* Add all the response word to a mapping
*/
response.forEach(function(responseWordAsKey) {
transliteratedWordEnglishMapping[responseWordAsKey[0]] = lastWord;
});
const lastWordWithSpecialCharacter = lastWord.match(/[A-Za-z0-9\u00C0-\u024F\u1E00-\u1EFF]+/)[0];
/**
* Add english word on the list to be displayed on additional options.
*/
response.push([lastWord, lastWordWithSpecialCharacter]);
/**
* Clear the content of the div
*/
inputToolHelper.empty();
let i = 0;
response.forEach(function(input) {
i++;
const inputHelperToolBtn = document.createElement('button');
inputHelperToolBtn.setAttribute('value', input[0]);
inputHelperToolBtn.innerHTML = input[1];
inputHelperToolBtn.classList.add('btn');
if (i === 1) {
inputHelperToolBtn.classList.add('btn-success');
}
inputToolHelper.append(inputHelperToolBtn);
});
if (suggestionPopupAfterSpace === true) {
popUpSuggestionDropdown(
response,
googleTransliterationArea,
suggestionDropdown,
suggestionDropdownContent,
additionalPopUpTopMargin
);
}
/**
* On pressing enter on the selected work on suggestion box.
*/
googleTransliterationArea.on('keydown', function(keyUpEvent) {
if ($('#InputSuggestions .selected') && keyUpEvent.key === "Enter") {
const inputSuggestionDiv = document.getElementById('InputSuggestions');
if (inputSuggestionDiv !== null
&& inputSuggestionDiv.style.display === 'block'
) {
keyUpEvent.preventDefault();
}
const inputSuggestionsDropdownDiv = document.getElementById('InputSuggestions');
if (inputSuggestionsDropdownDiv === null) {
return;
}
const selectedInputSuggestionButton = inputSuggestionsDropdownDiv.getElementsByClassName('selected');
if (typeof selectedInputSuggestionButton === 'undefined'
|| typeof selectedInputSuggestionButton[0] === 'undefined') {
return;
}
const btnSelectedValue = selectedInputSuggestionButton[0];
enterSelectedWordOnTextarea(
e,
transliteratedWordEnglishMapping,
lastWord,
googleTransliterationArea,
(typeof btnSelectedValue.value !== 'undefined') ? btnSelectedValue.value : btnSelectedValue.innerHTML,
inputHelperToolId
);
removeSuggestionDropdown();
}
});
/**
* On clicking the button, replace the word.
*/
$(inputHelperToolId + ' .btn, #InputSuggestions .suggestion-btn').on('click', function(btnElementEvent) {
enterSelectedWordOnTextarea(
e,
transliteratedWordEnglishMapping,
lastWord,
googleTransliterationArea,
(this.value !== '') ? this.value : this.innerHTML,
inputHelperToolId
);
});
if (e.which === 8) {
/**
* Replace the last word on the textarea with the first suggestion
*/
words[words.length - 1] = response[0][0];
} else {
/**
* Replace the last word on the textarea with the first suggestion
*/
words[words.length - 2] = response[0][0];
}
// words[words.length - 2] = response[0];
words = words.join(' ');
if (splitTextareaContent === true) {
totalLengthOfCurrentCursorEndPosition += response[0][0].length;
words += cursorEndToEndOfTextPart;
}
/**
* Populate the mapping for later access.
*/
transliteratedWordEnglishMapping[response[0][0]] = lastWord;
googleTransliterationArea.val(htmlDecode(words));
if (splitTextareaContent === true) {
setCaretPosition(e.currentTarget, totalLengthOfCurrentCursorEndPosition);
}
});
} else {
inputToolHelper.empty();
if (ctrlGPressed) {
inputToolHelper.append('<em>English mode enabled. Press (CTRL + G) to switch back.</em>');
} else {
inputToolHelper.append('<em>Additional options appears here!</em>');
}
}
});
}
/**
* For e.g. when button is clicked or press enter on selected word
* @param mainEvent
* @param transliteratedWordEnglishMapping
* @param lastWord
* @param googleTransliterationArea
* @param buttonClickedValue
* @param inputHelperToolId
*/
function enterSelectedWordOnTextarea(
mainEvent,
transliteratedWordEnglishMapping,
lastWord,
googleTransliterationArea,
buttonClickedValue,
inputHelperToolId
) {
const currentCursorPositionOnBtnClick = getInputSelection(mainEvent.currentTarget);
const textareaTextOnBtnClick = mainEvent.target.value;
const fullLengthOfTheStringOnBtnClick = textareaTextOnBtnClick.length;
let cursorEndToEndOfTextPartOnBtnClick = '';
let totalLengthOfCurrentCursorEndPositionOnBtnClicked = currentCursorPositionOnBtnClick.end;
const endOfCursorTextPartOnBtnClick = textareaTextOnBtnClick.substr(0, currentCursorPositionOnBtnClick.end);
cursorEndToEndOfTextPartOnBtnClick = textareaTextOnBtnClick.substr(currentCursorPositionOnBtnClick.end, fullLengthOfTheStringOnBtnClick);
if (typeof buttonClickedValue !== 'undefined') {
let wordsBeforeCursorEnd = endOfCursorTextPartOnBtnClick.split(' ');
let lastWordBeforeCursorEnd = 0;
if (wordsBeforeCursorEnd.includes("")) {
lastWordBeforeCursorEnd = wordsBeforeCursorEnd[wordsBeforeCursorEnd.length - 2];
} else {
lastWordBeforeCursorEnd = wordsBeforeCursorEnd[wordsBeforeCursorEnd.length - 1];
}
/**
* Populate the mapping for later access.
*/
transliteratedWordEnglishMapping[buttonClickedValue] = transliteratedWordEnglishMapping[lastWordBeforeCursorEnd];
if (typeof lastWordBeforeCursorEnd === 'undefined') {
console.log('last word before cursor end is not defined');
return;
}
totalLengthOfCurrentCursorEndPositionOnBtnClicked -= lastWordBeforeCursorEnd.length;
let toMoveCursorByOneCharacter = false;
if (wordsBeforeCursorEnd.includes("")) {
wordsBeforeCursorEnd[wordsBeforeCursorEnd.length - 2] = buttonClickedValue;
} else {
toMoveCursorByOneCharacter = true;
wordsBeforeCursorEnd[wordsBeforeCursorEnd.length - 1] = buttonClickedValue;
}
wordsBeforeCursorEnd = wordsBeforeCursorEnd.join(' ');
totalLengthOfCurrentCursorEndPositionOnBtnClicked += buttonClickedValue.length;
wordsBeforeCursorEnd += cursorEndToEndOfTextPartOnBtnClick;
if (toMoveCursorByOneCharacter) {
totalLengthOfCurrentCursorEndPositionOnBtnClicked++;
wordsBeforeCursorEnd += ' ';
}
googleTransliterationArea.val(htmlDecode(wordsBeforeCursorEnd));
setCaretPosition(mainEvent.currentTarget, totalLengthOfCurrentCursorEndPositionOnBtnClicked);
$(inputHelperToolId + ' .btn, #InputSuggestions .suggestion-btn').removeClass('btn-success');
$(this).addClass('btn-success');
removeSuggestionDropdown();
}
}
/**
* Detect if CTRL + g key combination is pressed.
*/
function isCtrlG(keyEvent) {
if (keyEvent.which === 71 && keyEvent.ctrlKey) {
keyEvent.preventDefault();
return true;
}
return false;
}
function removeSuggestionDropdown()
{
const inputSuggestions = document.getElementById('InputSuggestions');
if (inputSuggestions !== null) {
document.getElementById('InputSuggestions').remove();
}
}
function removeClassFromElements(elements, classNameToRemove)
{
for (let index in elements) {
const element = elements[index];
if (typeof element.classList !== 'undefined' && element.classList.contains(classNameToRemove)) {
element.classList.remove(classNameToRemove);
}
}
}
function addClassOnElement(element, classNameToAdd)
{
if (typeof element !== 'undefined') {
element.classList.add(classNameToAdd);
}
}
function popUpSuggestionDropdown(
response,
googleTransliterationArea,
suggestionDropdown,
suggestionDropdownContent,
additionalPopUpTopMargin
) {
// Clear whatever it has
suggestionDropdown.innerHTML = '';
let suggestionDropdownHeight = 0;
let buttonHeight = 25;
let numberOfButton = 0;
// For each Input create a button and append on suggestion dropdown
response.forEach(function (input) {
// const alphaNumWithLatin = input.replace(/[^A-Za-z0-9\u00C0-\u024F\u1E00-\u1EFF]/, '');
numberOfButton++;
suggestionDropdownHeight += buttonHeight;
const button = document.createElement('button');
button.setAttribute('value', input[0]);
button.innerHTML = input[1];
button.classList.add('suggestion-btn');
button.style.height = buttonHeight + 'px';
button.style.display = 'block';
button.style.backgroundColor = '#fefefe';
button.style.border = '0px';
button.style.width = '100%';
button.style.fontSize = '15px';
button.style.textAlign = 'left';
if (numberOfButton === 1) {
button.classList.add('selected');
}
suggestionDropdown.append(button);
suggestionDropdown.style.height = suggestionDropdownHeight + 'px';
});
suggestionDropdown.remove();
const currentTextarea = googleTransliterationArea[0];
const point = getDropdownPosition(currentTextarea);
addSuggestionDropdown(currentTextarea, suggestionDropdown, suggestionDropdownContent, point.x, point.y, additionalPopUpTopMargin);
let suggestionBtnIndex = 0;
$(document).keydown(function (keydownEvent) {
const inputSuggestionsDiv = document.getElementById('InputSuggestions');
if (inputSuggestionsDiv === null) {
return;
}
const inputSuggestionButtons = inputSuggestionsDiv.getElementsByTagName('button');
const numberOfInputSuggestionButtons = inputSuggestionButtons.length;
const inputSuggestionDiv = document.getElementById('InputSuggestions');
if (inputSuggestionDiv !== null
&& inputSuggestionDiv.style.display === 'block'
) {
switch (keydownEvent.which) {
case 39: //right
case 38: //up
keydownEvent.preventDefault();
suggestionBtnIndex--;
if (suggestionBtnIndex === -1) {
suggestionBtnIndex = numberOfInputSuggestionButtons - 1;
}
removeClassFromElements(inputSuggestionButtons, 'selected');
addClassOnElement(inputSuggestionButtons[suggestionBtnIndex], 'selected');
break;
case 37: //left
case 40: //down
keydownEvent.preventDefault();
suggestionBtnIndex++;
if (suggestionBtnIndex === numberOfInputSuggestionButtons) {
suggestionBtnIndex = 0;
}
removeClassFromElements(inputSuggestionButtons, 'selected');
addClassOnElement(inputSuggestionButtons[suggestionBtnIndex], 'selected');
break;
}
}
});
}
function addSuggestionDropdown(
currentTextarea,
suggestionDropdown,
suggestionDropdownContent,
leftPos,
topPos,
additionalPopUpTopMargin
) {
suggestionDropdown.style.position = 'absolute';
suggestionDropdown.style.left = leftPos + 'px';
suggestionDropdown.style.top = additionalPopUpTopMargin + topPos + 'px';
suggestionDropdown.style.display = 'block';
suggestionDropdown.style.border = '1px solid #999';
const parentNodeOfCurrentTextarea = currentTextarea.parentNode;
parentNodeOfCurrentTextarea.style.position = 'relative';
parentNodeOfCurrentTextarea.appendChild(suggestionDropdown);
}
function getAreaOfTextareaElement(textareaElement) {
let area = {height: 0, width: 0};
area.height = textareaElement.clientHeight + 1;
area.width = textareaElement.clientWidth + 1;
return area;
}
function getDropdownPosition(currentTextarea)
{
let point = {x: 0, y: 0};
const cursorCoordinates = getCaretCoordinates(currentTextarea, currentTextarea.selectionEnd);
const fontSize = currentTextarea.style.fontSize.replace('px', '');
const areaOfTextareaElement = getAreaOfTextareaElement(currentTextarea);
const widthPercentOfCursor = cursorCoordinates.left * 100 / areaOfTextareaElement.width;
if (widthPercentOfCursor < 70) {
point.x = cursorCoordinates.left;
} else {
point.x = cursorCoordinates.left - 50;
}
point.y = cursorCoordinates.top + fontSize * 2;
return point;
}
/**
* Return an
* @param {DOMElement} el A dom element of a textarea or input text.
* @return {Object} reference Object with 2 properties (start and end) with the identifier of the location of the cursor and selected text.
**/
function getInputSelection(el) {
let start = 0, end = 0, normalizedValue, range,
textInputRange, len, endRange;
if (typeof el.selectionStart == "number" && typeof el.selectionEnd == "number") {
start = el.selectionStart;
end = el.selectionEnd;
} else {
range = document.selection.createRange();
if (range && range.parentElement() == el) {
len = el.value.length;
normalizedValue = el.value.replace(/\r\n/g, "\n");
// Create a working TextRange that lives only in the input
textInputRange = el.createTextRange();
textInputRange.moveToBookmark(range.getBookmark());
// Check if the start and end of the selection are at the very end
// of the input, since moveStart/moveEnd doesn't return what we want
// in those cases
endRange = el.createTextRange();
endRange.collapse(false);
if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) {
start = end = len;
} else {
start = -textInputRange.moveStart("character", -len);
start += normalizedValue.slice(0, start).split("\n").length - 1;
if (textInputRange.compareEndPoints("EndToEnd", endRange) > -1) {
end = len;
} else {
end = -textInputRange.moveEnd("character", -len);
end += normalizedValue.slice(0, end).split("\n").length - 1;
}
}
}
}
return {
start: start,
end: end
};
}
function setCaretPosition(elem, caretPos) {
let range;
if (elem.createTextRange) {
range = elem.createTextRange();
range.move('character', caretPos);
range.select();
} else {
elem.focus();
if (elem.selectionStart !== undefined) {
elem.setSelectionRange(caretPos, caretPos);
}
}
}
function htmlDecode(input){
let e = document.createElement('div');
e.innerHTML = input;
return e.childNodes.length === 0 ? "" : e.childNodes[0].nodeValue;
}
/** Get Caret Position **/
/* jshint browser: true */
// The properties that we copy into a mirrored div.
// Note that some browsers, such as Firefox,
// do not concatenate properties, i.e. padding-top, bottom etc. -> padding,
// so we have to do every single property specifically.
let properties = [
'direction', // RTL support
'boxSizing',
'width', // on Chrome and IE, exclude the scrollbar, so the mirror div wraps exactly as the textarea does
'height',
'overflowX',
'overflowY', // copy the scrollbar for IE
'borderTopWidth',
'borderRightWidth',
'borderBottomWidth',
'borderLeftWidth',
'paddingTop',
'paddingRight',
'paddingBottom',
'paddingLeft',
// https://developer.mozilla.org/en-US/docs/Web/CSS/font
'fontStyle',
'fontVariant',
'fontWeight',
'fontStretch',
'fontSize',
'fontSizeAdjust',
'lineHeight',
'fontFamily',
'textAlign',
'textTransform',
'textIndent',
'textDecoration', // might not make a difference, but better be safe
'letterSpacing',
'wordSpacing'
];
let isFirefox = !(window.mozInnerScreenX == null);
// module.exports = function (textarea, position, recalculate) {
function getCaretCoordinates(element, position, recalculate) {
// mirrored div
let div = document.createElement('div');
div.id = 'input-textarea-caret-position-mirror-div';
document.body.appendChild(div);
let style = div.style;
let computed = window.getComputedStyle? getComputedStyle(element) : element.currentStyle; // currentStyle for IE < 9
// default textarea styles
style.whiteSpace = 'pre-wrap';
if (element.nodeName !== 'INPUT')
style.wordWrap = 'break-word'; // only for textarea-s
// position off-screen
style.position = 'absolute'; // required to return coordinates properly
style.visibility = 'hidden'; // not 'display: none' because we want rendering
// transfer the element's properties to the div
properties.forEach(function (prop) {
style[prop] = computed[prop];
});
if (isFirefox) {
style.width = parseInt(computed.width) - 2 + 'px' // Firefox adds 2 pixels to the padding - https://bugzilla.mozilla.org/show_bug.cgi?id=753662
// Firefox lies about the overflow property for textareas: https://bugzilla.mozilla.org/show_bug.cgi?id=984275
if (element.scrollHeight > parseInt(computed.height))
style.overflowY = 'scroll';
} else {
style.overflow = 'hidden'; // for Chrome to not render a scrollbar; IE keeps overflowY = 'scroll'
}
div.textContent = element.value.substring(0, position);
// the second special handling for input type="text" vs textarea: spaces need to be replaced with non-breaking spaces - http://stackoverflow.com/a/13402035/1269037
if (element.nodeName === 'INPUT') {
div.textContent = div.textContent.replace(/\s/g, "\u00a0");
}
let span = document.createElement('span');
// Wrapping must be replicated *exactly*, including when a long word gets
// onto the next line, with whitespace at the end of the line before (#7).
// The *only* reliable way to do that is to copy the *entire* rest of the
// textarea's content into the <span> created at the caret position.
// for inputs, just '.' would be enough, but why bother?
span.textContent = element.value.substring(position) || '.'; // || because a completely empty faux span doesn't render at all
div.appendChild(span);
const coordinates = {
top: span.offsetTop + parseInt(computed['borderTopWidth']),
left: span.offsetLeft + parseInt(computed['borderLeftWidth'])
};
document.body.removeChild(div);
return coordinates;
}
/**
*
* @param url : http://www.api.phplab.info/v1/transliteration-inventory
* @param postData
* {
* "origin-url" : "https://www.easynepalityping.com",
* "two-digit-language" : "ne",
* "selected-word" : "hamro",
* "transliteration" : [
* ["हाम्रो", "हाम्रो"],
* ["हम्रो", "हम्रो"],
* ["हमरो", "हमरो"],
* ["हामरो", "हामरो"],
* ["हम्रों", "हम्रों"],
* ["हमरों", "हमरों"],
* ["hamro", "hamro"]
* ]
* }
*
* @returns {Promise<unknown> | Promise<unknown>}
*/
function makeTransliterationInventoryPostRequest(url, postData) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('POST', url);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onload = function () {
if (xhr.status >= 200 && xhr.status < 300) {
reject('pass', xhr.responseText);
resolve(JSON.parse(xhr.responseText));
} else {
reject(new Error('Request failed: ' + xhr.statusText));
}
};
xhr.onerror = function () {
reject(new Error('Network error occurred'));
};
xhr.send(JSON.stringify(postData));
});
}
function transliterationPostData(response, lastWord, googleInputKeyCode) {
const transliterationData = response.slice(); // copy array value
const selectedWord = lastWord.slice().toLowerCase().trim(); // copy array value
transliterationData.push([selectedWord, selectedWord]);
const postData = {
"origin-url": window.location.hostname,
"language-code": googleInputKeyCode.split('-')[0],
"selected-word": selectedWord,
"transliteration": transliterationData,
"transliteration_source": "GOOGLE_TRANSLITERATION"
};
return postData;
}