Superscripts and subscripts in HTML input - javascript

I am making a small JavaScript web app that deals with chemical formulas, so I need to allow users to input superscripts. The easiest thing for users would be having the text editor switch to superscript when they press ^. How can I do this with an HTML textarea?

Borrowing from the function in adeneo's answer
Check this fiddle here : fiddle
Usage : To type a superscript , press ^ --> type in the superscript --> press Esc , the superscript will then appear in the textarea.
$(document).ready(function() {
var temp = {}; // store keypresses here
var current_value = "";
$("#text_area").keydown(function(e) {
temp[e.which] = true;
});
$('#text_area').keyup(function(e) {
if (e.keyCode == 27 && current_value != "") {
var length_1 = current_value.length;
var length_without_sup = length_1 - 5;
var substr_superstring = $('#text_area').val().substr(length_without_sup);
var current_text_2 = current_value + substr_superstring;
current_text_2 = current_text_2 + "</sup>";
$('#text_area').val(current_text_2);
$('#text_area').superScript();
}
var flag_shift = false;
var flag_super = false;
for (var key in temp) {
if (key == 16) {
flag_shift = true;
} else if (key == 54) {
flag_super = true;
}
}
if (flag_shift == true && flag_super == true) {
var current_text = $('#text_area').val();
current_text_2 = current_text.substr(0, current_text.length - 1);
current_text_2 = current_text_2 + "<sup>";
$('#text_area').val(current_text_2);
current_value = hide_superscript_tag();
}
delete temp[e.which];
});
});
function hide_superscript_tag() {
var current_value = $('#text_area').val();
current_value_2 = current_value.substr(0, current_value.length - 5);
$('#text_area').val(current_value_2);
return current_value;
}
$.fn.superScript = function() {
var chars = '+−=()0123456789AaÆᴂɐɑɒBbcɕDdðEeƎəɛɜɜfGgɡɣhHɦIiɪɨᵻɩjJʝɟKklLʟᶅɭMmɱNnɴɲɳŋOoɔᴖᴗɵȢPpɸqrRɹɻʁsʂʃTtƫUuᴜᴝʉɥɯɰʊvVʋʌwWxyzʐʑʒꝯᴥβγδθφχнნʕⵡ',
sup = '⁺⁻⁼⁽⁾⁰¹²³⁴⁵⁶⁷⁸⁹ᴬᵃᴭᵆᵄᵅᶛᴮᵇᶜᶝᴰᵈᶞᴱᵉᴲᵊᵋᶟᵌᶠᴳᵍᶢˠʰᴴʱᴵⁱᶦᶤᶧᶥʲᴶᶨᶡᴷᵏˡᴸᶫᶪᶩᴹᵐᶬᴺⁿᶰᶮᶯᵑᴼᵒᵓᵔᵕᶱᴽᴾᵖᶲqʳᴿʴʵʶˢᶳᶴᵀᵗᶵᵁᵘᶸᵙᶶᶣᵚᶭᶷᵛⱽᶹᶺʷᵂˣʸᶻᶼᶽᶾꝰᵜᵝᵞᵟᶿᵠᵡᵸჼˤⵯ';
return this.each(function() {
this.value = this.value.replace(/<sup[^>]*>(.*?)<\/sup>/g, function(x) {
var str = '',
txt = $.trim($(x).unwrap().text());
for (var i = 0; i < txt.length; i++) {
var n = chars.indexOf(txt[i]);
str += (n != -1 ? sup[n] : txt[i]);
}
return str;
});
});
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea id="text_area">
</textarea>

I would recommend leveraging an existing solution. Take a look at MathQuill.
Regarding creating your own such system from scratch, there is no simple solution. You would need to do independent research and experimentation and return to Stack Overflow with more specific questions.

Related

How can i validate an elements existence by typing it into an input text field?

I have different id's on different elements that are used in an input field when the user can type in the letter (Element 1 = A, Element 2 = B.. Element 27 = AA). How can I properly scan these on input in order to determine if the element exists and if it does put it in a string that converts these id's to values which are later calculated?
I have an id system on a calculator where the user can generate different element (sliders, radio buttons, check boxes) that can be calculated. They all have a numeric id which is the translated into alphabetic characters in a progressive order ( Element 1 = A, Element 2 = B.. Element 27 = AA). I later have an input field where the user can create their own formula that will be calculated and put into a result tab.
Sample Formula: A+B*2
The reason I translate to letter is so that the user can use numbers in creating the formula. I have succeed in making this work for id's that are one letter but as soon as the id's start hitting AA and AB it doesn't work because currently I split this into an array and scan every element for it's value, which becomes problematic for two letters since they split into two different id's.
I have tried splitting the array based on the operators (+, -, *, /) but that removes them from the array.
function resultCalcInit(resultObject, resultFormulaObject) {
$('.createWrap').on('keyup input change', $(resultFormulaObject).find('.jvformbuilder-formula-panel-elements-result-field-formula'), function(e) {
var thisKey = new RegExp(String.fromCharCode(e.which).toLowerCase());
var keyNoRegEx = String.fromCharCode(e.which).toLowerCase();
var counter = 0;
var letters = /^[0-9a-zA-Z]+$/;
for (var call of $('.dropzone').find('.builder-elements')) {
if ($(call).find('.bf-number')[0]) {
var operators = ['»', '½', '/', '¿'];
if (String.fromCharCode(e.which) == $(call).find('.bf-number').attr("data-calcId").toUpperCase() || $.isNumeric(String.fromCharCode(e.which)) || wordInString(String.fromCharCode(e.which), operators)) {
counter++;
} else {}
} else if ($(call).find('.builder-list')[0]) {
var operators = ['»', '½', '/', '¿'];
if (String.fromCharCode(e.which) == $(call).find('.builder-list').attr("data-calcId").toUpperCase() || $.isNumeric(String.fromCharCode(e.which)) || wordInString(String.fromCharCode(e.which), operators)) {
counter++;
} else {}
} else if ($(call).find('.builder-radio')[0]) {
var operators = ['»', '½', '/', '¿'];
if (String.fromCharCode(e.which) == $(call).find('.builder-radio').attr("data-calcId").toUpperCase() || $.isNumeric(String.fromCharCode(e.which)) || wordInString(String.fromCharCode(e.which), operators)) {
counter++;
} else {}
} else if ($(call).find('.builderSlider')[0]) {
var operators = ['»', '½', '/', '¿'];
if (String.fromCharCode(e.which) == $(call).find('.builderSlider').attr("data-calcId").toUpperCase() || $.isNumeric(String.fromCharCode(e.which)) || wordInString(String.fromCharCode(e.which), operators)) {
counter++;
} else {}
} else if ($(call).find('.builder-checkboxes')[0]) {
var operators = ['»', '½', '/', '¿'];
if (String.fromCharCode(e.which) == $(call).find('.builder-checkboxes').attr("data-calcId").toUpperCase() || $.isNumeric(String.fromCharCode(e.which)) || wordInString(String.fromCharCode(e.which), operators)) {
counter++;
} else {}
}
}
if (String.fromCharCode(e.which).match(letters) && counter < 1) {
$(resultFormulaObject).find('.jvformbuilder-formula-panel-elements-result-field-formula').html($(resultFormulaObject).find('.jvformbuilder-formula-panel-elements-result-field-formula').html().replace(thisKey, ""));
var returnString = $(resultFormulaObject).find('.jvformbuilder-formula-panel-elements-result-field-formula').text();
$('#jvformbuilder-formula-panel').find('.jvformbuilder-formula-panel-elements').each(function() {
var formulaResultId = $(this).find('.jvformbuilder-formula-panel-elements-result-field-formula');
$('.builder-elements').each(function() {
if (formulaResultId.attr("id") == $(this).find('.result-number').attr("id")) {
var resultWindow = $(this).find('.result-number');
var formula = returnString.slice(1);
}
});
});
} else {
resultCalc(resultFormulaObject);
}
});
}
Here it check if the letter typed is an existing ID. If it isn't, it's removed. If it is, it stays and proceeds to be scanned for the value.
function resultCalc(resultFormulaObject) {
var returnString = $(resultFormulaObject).find('.jvformbuilder-formula-panel-elements-result-field-formula').text();
$('#jvformbuilder-formula-panel').find('.jvformbuilder-formula-panel-elements').each(function() {
var formulaResultId = $(this).find('.jvformbuilder-formula-panel-elements-result-field-formula');
$('.builder-elements').each(function() {
if (formulaResultId.attr("id") == $(this).find('.result-number').attr("id")) {
var resultWindow = $(this).find('.result-number');
var formula = returnString.slice(1).split("");
var formulaNbr = returnString.slice(1).split("");
var alphabet = ("abcdefghijklmnopqrstuvwxyz").split("");
var calculationArray = returnString.slice(1).split("");
var tempArr = formula;
for (var i = 0; i < formula.length; i++) {
$('.builder-elements').each(function() {
if ($(this).find('.builder-list').attr("data-calcid") == formula[i]) {
formulaNbr[i] = $(this).find('.builder-list').children("option:selected").val();
calculationArray[i] = "parseInt(ID" + alphabet.indexOf(formula[i]) + ").value)";
} else if ($(this).find('.builder-field').attr("data-calcid") == formula[i]) {
if ($(this).find('.bf-text')[0]) {
console.log(tempArr);
if (tempArr.indexOf(tempArr[i]) == 0) {
tempArr.splice(i, 2);
calculationArray.splice(i, 2);
} else {
tempArr.splice(i - 1, 2);
calculationArray.splice(i - 1, 2);
}
var formulaString = "";
for (var j = 0; j < formula.length; j++) {
formulaString += tempArr[j];
}
formulaResultId.empty();
formulaResultId.html("=" + formulaString);
} else if ($(this).find('.bf-telNum')[0]) {
if (tempArr.indexOf(tempArr[i]) == 0) {
tempArr.splice(i, 2);
calculationArray.splice(i, 2);
} else {
tempArr.splice(i - 1, 2);
calculationArray.splice(i - 1, 2);
}
var formulaString = "";
for (var j = 0; j < formula.length; j++) {
formulaString += tempArr[j];
}
formulaResultId.empty();
formulaResultId.html("=" + formulaString);
} else if ($(this).find('.bf-date')[0]) {
if (tempArr.indexOf(tempArr[i]) == 0) {
tempArr.splice(i, 2);
calculationArray.splice(i, 2);
} else {
tempArr.splice(i - 1, 2);
calculationArray.splice(i - 1, 2);
}
var formulaString = "";
for (var j = 0; j < formula.length; j++) {
formulaString += tempArr[j];
}
formulaResultId.empty();
formulaResultId.html("=" + formulaString);
} else if ($(this).find('.bf-number')[0]) {
if (!$(this).find('.bf-number').val()) {
formulaNbr[i] = 0;
} else {
formulaNbr[i] = $(this).find('.bf-number').val();
}
calculationArray[i] = "parseInt(ID" + alphabet.indexOf(formula[i]) + ").value)";
}
} else if ($(this).find('.builder-textarea').attr("data-calcid") == formula[i]) {
if (tempArr.indexOf(tempArr[i]) == 0) {
tempArr.splice(i, 2);
calculationArray.splice(i, 2);
} else {
tempArr.splice(i - 1, 2);
calculationArray.splice(i - 1, 2);
}
var formulaString = "";
for (var j = 0; j < formula.length; j++) {
formulaString += tempArr[j];
}
formulaResultId.empty();
formulaResultId.html("=" + formulaString);
} else if ($(this).find('.builder-radio').attr("data-calcid") == formula[i]) {
var resultRadio = [];
$(this).find('.builder-radio-input').each(function(i) {
resultRadio[i] = parseInt($(this).val());
});
var sum = resultRadio.reduce(add);
formulaNbr[i] = sum;
calculationArray[i] = "parseInt(ID" + alphabet.indexOf(formula[i]) + ").value)";
} else if ($(this).find('.builder-checkboxes').attr("data-calcid") == formula[i]) {
var resultCheck = [];
$(this).find('.builderCB').each(function(i) {
resultCheck[i] = parseInt($(this).val());
});
var sum = resultCheck.reduce(add);
formulaNbr[i] = sum;
calculationArray[i] = "parseInt(ID" + alphabet.indexOf(formula[i]) + ").value)";
} else if ($(this).find('.builderSlider').attr("data-calcid") == formula[i]) {
formulaNbr[i] = $(this).find('.builder-slider').val();
calculationArray[i] = "parseInt(ID" + alphabet.indexOf(formula[i]) + ").value)";
}
});
}
var calculationString = "";
for (var i = 0; i < calculationArray.length; i++) {
calculationString += calculationArray[i];
}
returnString = "";
for (var i = 0; i < formulaNbr.length; i++) {
returnString += formulaNbr[i];
}
if (returnString) {
printRes(returnString, resultWindow, calculationString);
}
}
});
});
}
Here it takes different values from the different objects that relates to the id written inside the formula tab. Later it is printed into the result tab.
function printRes(resString, resArea, calcString) {
resArea.empty();
var result = eval(resString);
if (!result) {
resArea.append(0)
resArea.attr("data-calcForm", "");
} else {
resArea.append(result)
resArea.attr("data-calcForm", calcString);
}
}
It completely crashes if the id becomes doubled. That's where I need you guys to help me. How can I make it scan after double characters id's as well as single ones, and triple ones and how ever many the user decides to generate.
There is no way to reproduce the issue you have from the big code bunch you posted. And I have to admit that what you try to achieve is unclear to me.
But what is really clear is that you need to segregate the formula's elements from the operator. You can achieve this with 2 regexes. I see you already use the first one: /[0-9a-zA-Z]+/, but I don't get how you use it...
Anyway, here is a real simple demo showing that you can have two arrays, one for the formula's elements and the other for the operators. Once you have that, you should be able to use it to do whatever you wish to.
$("button").on("click",function(){
// The input value
var input_val = $("input").val();
// Regexes
var elements_regex = /[0-9a-zA-Z]+/g;
var operators_regex = /[\+\-*\/]/g;
// Create the arrays
var elements_array = input_val.match(elements_regex);
var operators_array = input_val.match(operators_regex);
// Needed just for this demo
var regex_validation = $(".regex_validation");
var elements = $(".elements");
var operators = $(".operators");
// RESULTS
// Regex validation
for(i=0;i<elements_array.length;i++){
// Element
if(typeof(elements_array[i])!="undefined"){
regex_validation.append("<span class='element'>"+elements_array[i]+"<span>");
}
// Operator
if(typeof(operators_array[i])!="undefined"){
regex_validation.append("<span class='operator'>"+operators_array[i]+"<span>");
}
}
// Elements
elements.html(JSON.stringify(elements_array));
// Operators
operators.html(JSON.stringify(operators_array));
});
.result{
height: 50px;
width: 500px;
border: 1px solid black;
}
.element{
background: cyan;
}
.operator{
background: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Enter your formula: <input value="AA+B*2"><button type="button">Check</button><br>
<br>
Regex validation: (cyan for elements, yellow for operators)
<div class="result regex_validation"></div>
<br>
Elements array:
<div class="result elements"></div>
<br>
Operators array:
<div class="result operators"></div>
<br>
CodePen

Change case of text with hotkey (like Shift+F3 in MS-Word)

I am using tinymce plugin & I am trying to do a change letter case functionality on a hotkey as like in MS Word (shift + f3).
I have managed to do it with selections, but MS Word works even if a word isn't selected.
It takes cursor's current position separates out a word at that position and applies functionality to that word. I need the same for tinymce.
So far I have this:
editor.addShortcut("ctrl+e", "ll", function () {
var sel = editor.dom.decode(editor.selection.getContent());
if (/^[a-zа-я]+$/g.test(sel)) {
sel = sel.substr(0, 1).toUpperCase() + sel.substr(1);
}
else if (/^[А-ЯA-Z]+$/g.test(sel)) {
sel = sel.toLowerCase();
}
else {
sel = sel.toUpperCase();
}
editor.selection.setContent(sel);
console.log(editor.selection);
editor.save();
editor.isNotDirty = true;
});
The following code may give you the result that you want. It should work in these situations :
One or more words are fully selected
One or more words are partially selected
No text is selected
The selected text belongs to more than one paragraph
Various text attributes are set inside words
tinymce.init({
selector: "textarea",
plugins: [
"advlist autolink lists link image charmap print preview anchor",
"searchreplace visualblocks code fullscreen",
"insertdatetime media table contextmenu paste"
],
toolbar: "insertfile undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image",
setup: function (editor) {
editor.addShortcut("ctrl+e", "ll", function () {
var isWordChar = function (chr) {
return /\w/.test(chr);
};
var isTextNode = function (node) {
return node.nodeType == 3;
};
var getAllTextNodes = function (result, node) {
if (isTextNode(node)) {
result.push(node);
}
else if (node.childNodes) {
if (node.tagName == 'P') {
result.push(node);
}
var children = node.childNodes;
for (var i = 0; i < children.length; i++) {
getAllTextNodes(result, children[i]);
}
}
return result;
}
// Get current selection parameters
var range = editor.selection.getRng();
var isCollapsed = editor.selection.isCollapsed();
var selStartChildNode = range.startContainer;
var selEndChildNode = range.endContainer;
var selStartOffset = range.startOffset;
var selEndOffset = range.endOffset;
// Retrieve all the text nodes in the editor
var textNodes = [];
getAllTextNodes(textNodes, editor.dom.getRoot());
var selStartNodeIndex = textNodes.indexOf(selStartChildNode);
var selEndNodeIndex = textNodes.indexOf(selEndChildNode);
var wordStartNodeIndex, wordEndNodeIndex;
var wordStartOffset, wordEndOffset;
var wordTextContent = '';
var found = false;
var node, chr, lastCharIndex;
var i, j;
// Find the start of the first selected word
for (i = selStartNodeIndex; i >= 0 && !found; i--)
{
node = textNodes[i];
if (isTextNode(node)) {
wordStartNodeIndex = i;
lastCharIndex = node.textContent.length - 1;
wordStartOffset = Math.max(0, Math.min(lastCharIndex, node == selStartChildNode ? selStartOffset - 1 : lastCharIndex));
for (; wordStartOffset >= 0; wordStartOffset--) {
chr = node.textContent[wordStartOffset];
if (isWordChar(chr)) {
wordTextContent = chr + wordTextContent;
} else {
found = true;
break;
}
}
} else {
found = true;
break;
}
}
wordStartOffset = Math.max(0, wordStartOffset);
var endNodeFound = false;
var pastEndNode = false;
var isAfterSelection = false;
found = false;
// Find the end of the last selected word
for (i = selStartNodeIndex; i < textNodes.length && !found; i++) {
node = textNodes[i];
pastEndNode = endNodeFound;
if (isTextNode(node)) {
wordEndNodeIndex = i;
wordEndOffset = Math.min(node == selStartChildNode ? selStartOffset : 0, node.textContent.length - 1);
endNodeFound = endNodeFound || node == selEndChildNode;
for (; wordEndOffset < node.textContent.length; wordEndOffset++) {
chr = node.textContent[wordEndOffset];
isAfterSelection = pastEndNode || (endNodeFound && wordEndOffset >= selEndOffset - (isCollapsed ? 0 : 1));
if (isWordChar(chr) || !isAfterSelection) {
wordTextContent = wordTextContent + chr;
} else {
found = true;
break;
}
}
} else if (pastEndNode) {
found = true;
break;
}
}
// Determine the case style to be applied
var caseMode = '';
if (/^([a-z0-9]|\W)+$/g.test(wordTextContent)) {
caseMode = 'CapitalizeWords';
}
else if (/^([A-Z0-9]|\W)+$/g.test(wordTextContent)) {
caseMode = 'LowerCase';
}
else {
caseMode = 'UpperCase';
}
var startCharIndex, endCharIndex, currentIsWordChar;
var prevIsWordChar = false;
var content = '';
// Apply the new case style to the selected nodes
for (i = wordStartNodeIndex; i <= wordEndNodeIndex; i++) {
node = textNodes[i];
if (isTextNode(node)) {
startCharIndex = (i == wordStartNodeIndex ? wordStartOffset : 0);
endCharIndex = (i == wordEndNodeIndex ? wordEndOffset : node.textContent.length);
content = node.textContent.substring(0, startCharIndex);
switch (caseMode) {
case 'CapitalizeWords':
for (j = startCharIndex; j < endCharIndex; j++) {
chr = node.textContent[j];
currentIsWordChar = /\w/.test(chr);
content += (currentIsWordChar && !prevIsWordChar ? chr.toUpperCase() : chr);
prevIsWordChar = currentIsWordChar;
}
break;
case 'LowerCase':
content += node.textContent.substring(startCharIndex, endCharIndex).toLowerCase();
break;
case 'UpperCase':
content += node.textContent.substring(startCharIndex, endCharIndex).toUpperCase();
break;
}
content += node.textContent.substring(endCharIndex);
node.textContent = content;
} else {
prevIsWordChar = false;
}
}
// Restore the selection range
range.setStart(selStartChildNode, selStartOffset);
range.setEnd(selEndChildNode, selEndOffset);
editor.selection.setRng(range);
editor.save();
editor.isNotDirty = true;
});
}
});
You can try it in this jsfiddle. In order to simplify testing, the regular expressions consider only the standard US characters; you may want to put back the special characters that appear in your own code.
editor.selection.getRng().startOffset returns the cursor position
Source:
https://drujoopress.wordpress.com/2014/08/06/find-cursor-position-inside-tinymce-editor/
Fiddle:
https://jsfiddle.net/16ssbdrn/

Determing caret position in tokenized html input?

(no jquery please)
Given an html input whose value is split by /\s+/g, how would you find the current token that the caret is positioned at?
For instance, if your input value is
abc ab monkey
And you split it, it will become
["abc, "ab", "monkey"]
But if your caret position in the input is here...
abc ab monk^(caret here)ey
How would you determine which token the caret is currently in?
The api I'm looking for would be something like
var currentToken = getPosition(inputEl.value); // { index: 2, token: "monkey" }
I have most of it down, but when I start backtracking inside the input with the left arrow it gets messed up.
http://jsfiddle.net/dlizik/zmbpq5hz/
html
<input id="input" />
<pre id="test"></pre>
cursor at current token: <span id="res"></span>
js
(function($doc) {
"use strict";
var single = /\s/g;
var spacer = /\s+/g;
var disp = $doc.getElementById("test");
var input = $doc.getElementById("input");
var res = $doc.getElementById("res");
function keyListen(e) {
var tokens = this.value.split(spacer);
var tokenLengths = tokens.map(function(i) { return i.length; });
var cumulative = tokenLengths.map(function(i, n) {
return tokenLengths.slice(0, n + 1).reduce(function(a, b) {
return a + b;
});
});
var cursor = caretPos(this);
var currToken = tokens[getPos(cursor, cumulative)];
res.textContent = currToken;
disp.textContent = JSON.stringify(this.value.split(spacer), null, 2);
}
function getPos(curr, arr) {
for (var i = 0; i < arr.length; i++) {
if (curr <= arr[i]) return i;
}
}
function caretPos(el) {
var val = el.value;
var extra = val.match(single);
var whitespace = extra == null ? 0 : extra.length;
var pos = 0;
if ($doc.selection) {
el.focus();
var sel = $doc.selection.createRange();
sel.moveStart('character', -val.length);
pos = sel.text.length;
}
else if (el.selectionStart || el.selectionStart == '0') pos = el.selectionStart;
return (pos - whitespace);
}
input.addEventListener("keyup", keyListen.bind(input), false);
})(document);
Ok so you can just use regular expressions...wasn't that hard now that I think about it. You just have to find the number of words left of the caret position. That will give you the current token index as well as the string itself. However, you just have to account for a caret whose adjacent strings are both empty spaces.
http://jsfiddle.net/dlizik/zmbpq5hz/1/
This is the function you want to run
(function($doc) {
"use strict";
var single = /\s/g;
var tokenize = /[^\s+]/g;
var ledge = /[\s]*[^\s]+[\s]*/g;
var disp = $doc.getElementById("test");
var input = $doc.getElementById("input");
var res = $doc.getElementById("res");
function keyListen(e) {
var tokens = this.value.match(tokenize);
var pos = caretPos(this);
var adj = this.value.substring(pos - 1, pos + 1);
var left = this.value.slice(0, pos + 1).match(ledge).length - 1;
var curr = adj === " " ? null : tokens[left];
res.textContent = curr + "";
disp.textContent = JSON.stringify(this.value.split(spacer), null, 2);
}
function caretPos(el) {
var pos = 0;
if ($doc.selection) {
el.focus();
var sel = $doc.selection.createRange();
sel.moveStart('character', -el.value.length);
pos = sel.text.length;
}
else if (el.selectionStart || el.selectionStart == '0') pos = el.selectionStart;
return (pos);
}
input.addEventListener("keyup", keyListen.bind(input), false);
})(document);

javascript - Change the font color of the last word typed

Ok I am making a text editor and have to try and make the last word typed change font color based on if it's a keyword or not... I have tried multiple solutions to this but nothing has prevailed... Here is what I have tried so far
function getLastWord() {
var input = document.getElementById("my_text").value;
//var input = document.getElementById(textArea.value);
var inputValue = input.value;
var lastWordTyped
var changeColorOfWord;
var ele = document.querySelector("#my_text");
//ele.style.color = "blue"
if (input == null) {
input == " ";
}
lastWordTyped = input.substr(input.trim().lastIndexOf(" ") + 1);
//lastWordTyped = inputValue.substr(inputValue.trim().lastIndexOf(" ") + 1);
if (input != null) {
for (var i = 0; i < reservedKeyWords.length; i++) {
if ( lastWordTyped == reservedKeyWords[i] ) {
//changeColor(lastWordTyped);
//my_text.replace(inputValue, lastWordTyped);
//ele.fieldNameElement.innerHTML = lastWordTyped;
//ele.innerHTML = lastWordTyped;
ele.innerHTML.fontcolor = 'Blue';
return;
} else if (lastWordTyped !== reservedKeyWords[i]) {
//ele.innerHTML = ele.innerHTML.replace(lastWordTyped, '<span style="color:black"></span>');
//resetFontColor();
}
}
}
}
I have tried this function (found from SO)
function changeColor(word) {
var ele = document.querySelector("my_text");
ele.onkeypress = function () {
setTimeout(function () {
//the setTimeout is so the content is inserted before execution
document.getElementById('view_text').value = ele.textContent;
if (ele.innerHTML.indexOf(word) !== -1) {
ele.innerHTML = ele.innerHTML.replace(word, '<span style="color:blue">' + word + '</span>');
}
}, 50);
}
}
Also I have tried this one:
function colorMyKeyword(keywordColor, text) {
return '<span style="color:' + keywordColor + '>' + text + '</span>';
}
None of these functions have gotten the job done though. I have it now so that it will change the text color to blue but then the problem is that it changes ALL of the text to blue after that word...
I would prefer this to be in javascript as I do not know how to use JQuery, or really CSS for that matter or even know how to write it..
Thank you for any responses.
Updated code based on comment(s) below (changed to div from input)
Not the best code in the world but it should work. The CSS should probably be done by adding a class instead of changing the style attribute.
<div id="my_text">This is some text</div>
var isKeyword = false;
var el = document.getElementById('my_text');
var arr = el.innerHTML.split(' ');
var lastWordTyped = arr.pop();
/* replace with yours*/
var reservedKeyWords = ['text','another','word', 'here'];
for (var i = 0, len = reservedKeyWords.length ; i < len ; i++) {
if ( lastWordTyped == reservedKeyWords[i] ) {
lastWordTyped = '<span style="color:blue">'+lastWordTyped +'</span>'; //update color
arr.push(lastWordTyped);
isKeyword = true;
}
}
if (!isKeyword) { arr.push(lastWordTyped); } //put original back
el.innerHTML = arr.join(' ');
UPDATED: Do the whole thing on keyup
here's a simple example that you can use: DEMO
$('#text').keyup(function(){
$('#result').html($('#text').val());
var splittedText=$('#result').html().split(/\s/);
var lastWord=splittedText[splittedText.length-1];
$('#result').html($('#result').html().replace(lastWord,'<span>'+lastWord+'</span>'));
$('#result').children('span').css('color',$('#color').val());
});
you need to write a sentence in the first input and a hexa-deciaml color in the second one.(including the # at the beginning)
Here is an attempt to answer your questions :
I am using #GaryStorey's answer as it was a better starting point than what I add (I do like pop&push).
The problem with his answer were that it only showed how to change the color but it wasn't relevant on how to do it in an input nor in a contenteditable element.
So here are my adjustements, with a setCaret function to deal with the fact that caret always returns to start if we do change the innerHTML of edited element.
Be carefull, it is still very buggy and you should not use it in any production,
however it can give you a good starting point.
var reservedKeyWords = ['text', 'another', 'word', 'here'];
var el = document.getElementById('my_text');
el.addEventListener('keyup', function (evt) {
if (evt.keyCode == 32 || evt.keyCode == 13) {
var isKeyword = false;
var arr = el.innerHTML.split(/\s/);
var lastWordTyped = arr.pop();
lastWordTyped = lastWordTyped.replace(' ', '');
for (var i = 0, len = reservedKeyWords.length; i < len; i++) {
if (lastWordTyped == reservedKeyWords[i]) {
lastWordTyped = '<span style="color:blue">' + lastWordTyped + '</span>'; //update color
arr.push(lastWordTyped);
isKeyword = true;
}
}
if (!isKeyword) {
arr.push(lastWordTyped);
} //put original back
el.innerHTML = arr.join(' ') + ' ';
setCaret(el);
}
});
function setCaret(el) {
var range = document.createRange();
var endNode = el.lastChild;
range.selectNodeContents(endNode);
range.setStart(endNode, range.endOffset);
range.setEnd(endNode, range.endOffset);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}
#my_text {
min-width: 100%;
min-height: 2em;
padding: 0.5em;
display: block;
border : dashed 0.5px grey;
}
<p> list of KeyWords : 'text', 'another', 'word', 'here';</p>
<span id="my_text" contenteditable="true">Edit me using "reservedKeyWords" defined in JS</span>
Oh and note that I am using a span instead of a div, because div tag adds some <br> from nowhere after the textNode.

JavaScript Tag Cloud with IBM Cognos - IE is null or not an object

I followed a tutorial/modified the code to get a javascript tag cloud working in IBM Cognos (BI software). The tag cloud works fine in FireFox but in Internet Explorer I get the error:
"Message: '1' is null or not an object"
The line of code where this is present is 225 which is:
var B = b[1].toLowerCase();
I have tried many different solutions that I have seen but have been unable to get this working correctly, the rest of the code is as follows:
<script>
// JavaScript Document
// ====================================
// params that might need changin.
// DON'T forget to include a drill url in the href section below (see ###) if you want this report to be drillable
var delimit = "|";
var subdelimit = "[]"; // change this as needed (ex: Smith, Michael[]$500,000.00|)
var labelColumnNumber = 0; // first column is 0
var valueColumnNumber = 1;
var columnCount = 2; // how many columns are there in the list?
// ====================================
/*
function formatCurrency(num) {
num = num.toString().replace(/\$|\,/g,'');
if(isNaN(num))
num = "0";
sign = (num == (num = Math.abs(num)));
num = Math.floor(num*100+0.50000000001);
cents = num%100;
num = Math.floor(num/100).toString();
if(cents<10)
cents = "0" + cents;
for (var i = 0; i < Math.floor((num.length-(1+i))/3); i++)
num = num.substring(0,num.length-(4*i+3))+','+ num.substring(num.length-(4*i+3));
return (((sign)?'':'-') + '$' + num + '.' + cents);
}
*/
function formatCurrency(num) {
num = num.toString().replace(/\$|\,/g,'');
if(isNaN(num))
num = "0";
for (var i = 0; i < Math.floor((num.length-(1+i))/3); i++)
num = num.substring(0,num.length-(4*i+3))+','+ num.substring(num.length-(4*i+3));
return ( num );
}
function filterNum(str) {
re = /\$|,|#|#|~|`|\%|\*|\^|\&|\(|\)|\+|\=|\[|\-|\_|\]|\[|\}|\{|\;|\:|\'|\"|\<|\>|\?|\||\\|\!|\$|/g;
// remove special characters like "$" and "," etc...
return str.replace(re, "");
}
table = document.getElementById("dg");
if ( table.style.visibility != 'hidden'){ //only for visible
/*alert('Visible');*/
tags = document.getElementById("dg").getElementsByTagName("SPAN");
txt = "";
var newText = "a";
for (var i=columnCount; i<tags.length; i++) {
/*
valu = filterNum(tags[i+valueColumnNumber].innerHTML);
txt += valu;
txt += subdelimit+tags[i+labelColumnNumber].innerHTML+delimit;
i = i+columnCount;
*/
if(i%2!=0){
var newValue = filterNum(tags[i].innerHTML);
}else var newName =tags[i].innerHTML;
if((i>2) & (i%2!=0)){
newText = newText+newValue+subdelimit+newName+delimit;
if(typeof newText != 'undefined'){
txt = newText;
txt = txt.substr(9);
/* alert(txt);*/
}
}
}
}/*else alert ('Hidden');*/
function getFontSize(min,max,val) {
return Math.round((150.0*(1.0+(1.5*val-max/2)/max)));
}
function generateCloud(txt) {
//var txt = "48.1[]Google|28.1[]Yahoo!|10.5[]Live/MSN|4.9[]Ask|5[]AOL";
var logarithmic = false;
var lines = txt.split(delimit);
var min = 10000000000;
var max = 0;
for(var i=0;i<lines.length;i++) {
var line = lines[i];
var data = line.split(subdelimit);
if(data.length != 2) {
lines.splice(i,1);
continue;
}
data[0] = parseFloat(data[0]);
lines[i] = data;
if(data[0] > max)
max = data[0];
if(data[0] < min)
min = data[0];
}lines.sort(function (a,b) {
var A = a[1].toLowerCase();
var B = b[1].toLowerCase();
return A>B ? 1 : (A<B ? -1 : 0);
});
var html = "<style type='text/css'>#jscloud a:hover { text-decoration: underline; }</style> <div id='jscloud'>";
if(logarithmic) {
max = Math.log(max);
min = Math.log(min);
}
for(var i=0;i<lines.length;i++) {
var val = lines[i][0];
if(logarithmic) val = Math.log(val);
var fsize = getFontSize(min,max,val);
dollar = formatCurrency(lines[i][0]);
html += " <a href='###Some drillthrough url which includes the param "+lines[i][1]+"' style='font-size:"+fsize+"%;' title='"+dollar+"'>"+lines[i][1]+"</a> ";
}
html += "</div>";
var cloud = document.getElementById("cloud");
cloud.innerHTML = html;
var cloudhtml = document.getElementById("cloudhtml");
cloudhtml.value = html;
}
function setClass(layer,cls) {
layer.setAttribute("class",cls);
layer.setAttribute("className",cls);
}
function show(display) {
var cloud = document.getElementById("cloud");
var cloudhtml = document.getElementById("cloudhtml");if(display == "cloud") {
setClass(cloud,"visible");
setClass(cloudhtml,"hidden");
}
else if(display == "html") {
setClass(cloud,"hidden");
setClass(cloudhtml,"visible");
}
}
generateCloud(txt);
</script>
Any help or explanations is much appreciated
Sorry, I'm not seeing where a[] and b[] are defined, is this done elsewhere? Firefox and IE may be responding differently to the problem of an undefined array.

Categories