How to make javascript handle 'ESCAPE(\u001B)' character? - javascript

I want to make website that generate code for Windows 10 Command Prompt which supports 'ESCAPE'(\u001B) character to style string.
You can see examples on HERE.
NOTE: Since this site cannot show ``(\u001B) correctly, I will use ← instead.
The problem is, when I tried to make code that make code for Command Prompt like this:
function cdGenerate() {
var input = document.getElementById('ipText').value;
console.log(`input: ${input}`);
input = checkStyle(input);
console.log(`checkStyle input : ${input}`);
input = checkForeground(input);
console.log(`checkForeground input : ${input}`);
input = checkBackground(input);
console.log(`checkBackground input : ${input}`);
str = input;
console.log(`str: ${str}`);
document.getElementById('spResult').innerHTML = `<code>${str}</code>`;
copy(str);
}
function checkStyle(ori) {
var rtb = '←[';
if (document.getElementById('g-style-reset').checked) { rtb += '0;' }
if (document.getElementById('g-style-bold').checked) { rtb += '1;' }
if (document.getElementById('g-style-underline').checked) { rtb += '4;' }
if (document.getElementById('g-style-inverse').checked) { rtb += '7;' }
rtb = rtb.slice(0, -1);
rtb += 'm';
var rtn = rtb.concat(ori);
return rtn;
}
The result isn't not what I wanted.
I entered TEST in ipText HTML input element and execute cdGenerate(), what I should get is ←[7mTEST like thing in ipResult HTML input element. But I can only get TEST, not ←[7mTEST.
I've tested these codes with ← instead of \u001B and nothing changed.
Furthermore, I need to copy those ←[7mTEST to user's clipboard.
How can I make my javascript code to handle ←(\u001B) correctly(as-is)?

OK. I found how to do it.
I need to show 'ESCAPE' character
I decided to show ← instead. And added comment to <input> 'DO NOT COPY THIS TEXT' like thing. All internal functions uses ← instead of 'ESCAPE' because I only need it when copied to clipboard.
I need to copy 'ESCAPE' character
After copy string into <input>, same variable is used when converting ← to 'ESCAPE'. And I used hidden <div> to paste the string('ESCAPE' included) and copy it to clipboard. If <input> is used, 'ESCAPE' character is not copied properly, so I used <div>. Anyway, the <div>'s content is cleared after copying its content to clipboard.
function copyToClipboard(string) {
$id('hide').innerHTML = string;
var temp = document.createElement('input');
document.body.appendChild(temp);
temp.setAttribute('value', string);
temp.select();
document.execCommand('copy');
document.body.removeChild(temp);
}
I hope my workaround help other people who suffers same problem.

Related

How to determine if cursor comes after certain characters in a contenteditable div

I'm building an autosuggest/autocomplete feature in a contenteditable div and I'm having a problem with determining when the cursor comes after characters that don't qualify for autocompletion. What I want to do is run my autosuggest ajax request only if the user is typing an "#" handle, such as "#someUser". The problem is that contenteditable divs contain actual html, so I'm tripping up when trying to determine if the last character before the cursor is one of my approved characters. My approved characters are: A-z0-9. I'm using this regex: /( $| $|\s$)/, but it only checks for spaces. I can't simply negate my approved characters (something like [^A-z0-9]) because the HTML of the contenteditable would cause false negatives (the contenteditable div can have something like <div>test</div> as its innerHTML). I created this demo to try to showcase the problem.
Here is the code for it:
document.querySelector('button').addEventListener('click', handleClick);
function handleClick(e) {
const inputField = document.querySelector('#edit-me');
const text = inputField.innerHTML;
if (!text) {
alert('no text');
}
const afterInvalidChar = isAfterInvalidChar(text, getCaretIndex(inputField));
if (afterInvalidChar) {
alert('cursor is not after an accepted char');
} else {
alert('cursor is after an accepted char');
}
}
function isAfterInvalidChar(text, caretPosition) {
// first get text from the beginning until the caret
let termToSearch = text.slice(0, caretPosition);
console.log(termToSearch);
alert('content before cursor: ' + termToSearch);
const rgxToCheckEnding = /( $| $|\s$)/; // <-- this is where I'm tripping up, that regex only checks for spaces, and it's still not great
if (rgxToCheckEnding.test(termToSearch)) {
// the cursor is after a space, but I also need to check for anything
// that's not one of my accepted contents
return true;
} else {
return false;
}
}
// source: https://stackoverflow.com/a/46902361/7987987
function getCaretIndex (node) {
var range = window.getSelection().getRangeAt(0),
preCaretRange = range.cloneRange(),
caretIndex,
tmp = document.createElement("div");
preCaretRange.selectNodeContents(node);
preCaretRange.setEnd(range.endContainer, range.endOffset);
tmp.appendChild(preCaretRange.cloneContents());
caretIndex = tmp.innerHTML.length;
return caretIndex;
}
#edit-me {
width: 200px;
height: 200px;
background-color: lightblue;
}
<h2>Cursor of contenteditable div</h2>
<p>Accepted chars: <code>A-z0-9</code></p>
<button type="button">is cursor after an accepted char?</button>
<div contenteditable id="edit-me"><div>
An accceptable solution should work with the following text in the contenteditable div (just basic tests but you get the point):
"test": correctly say the last char is approved
"test ": correctly say the last char is not approved
"test-": correctly say the last char is not approved
"test?": correctly say the last char is not approved
How can get this working for only my approved chars? I'm open to an entirely different strategy if it gets the job done. Thanks!
The problem is that you need to decode HTML entities.
You can easily do it with this function
function decodeHTML(htmlString) {
var txt = document.createElement('div');
txt.innerHTML = htmlString;
return txt.textContent;
};
basically what it does is it makes an imaginary div and places the html code in the content of the div, and you get plain text when you request the contents using textContent.
Now all you have to do is change termToSearch to decodeHTML(text.slice(0, caretPosition)); and make your regex to check the ending
Please note that the function will not acctually make a div tag, but will still return the decoded values. The reason for this is because document.createElement() just makes an object of an HTML element rather than an actual element.

Removing newline characters from copy and pasting images

I'm creating a an html/javascript application as a project where I essentially am creating a markup language and converting the text to pictures. One thing I am doing for this is making it possible to copy and paste the string of pictures into the text area and get the markup version so that I can save it in text format.
I accomplish this by setting the alt attribute of my html images to what I want to be copied. Something I'm having trouble with is that when I set it up so that it prints the images "down" instead of to the right, accomplished by creating a sequence of <div><img alt="words" src="picture"></img></div> it interprets it as having newline characters between each individual pictures alt when I copy and paste.
I've tried adding a backspace character but that obviously failed, does anybody have a solution for getting rid of these newline characters?
Here's a picture of the output and what you get when you copy and paste to help make it more clear
var pasteArea = document.getElementById('pasteArea');
pasteArea.addEventListener('input', function () {
var value = pasteArea.value;
var spaceRemoved = value.replace(/\n/g, '');
if (value !== spaceRemoved) {
pasteArea.value = spaceRemoved;
}
});
You can attach an event to your textarea that removes any whitespace (or just newlines if that's preferable) when the value changes:
var pasteArea = document.getElementById('pasteArea');
pasteArea.addEventListener('input', function () {
var value = pasteArea.value;
var spaceRemoved = value.replace(/\s/g, '');
if (value !== spaceRemoved) {
pasteArea.value = spaceRemoved;
}
});
textarea {
height: 10em;
width: 300px;
}
<textarea id="pasteArea"></textarea>

Wrap an html tag after clicking a toolbar button using js or jquery

I want to do something similar to what this website and wordpress does. When a user highlights text on the screen, then clicks a button on the toolbar it will wrap an html tag around the text. In jquery I would probably use the .wrap class but how would I detect if the user highlighted something.
For example, when the user writes Hello World then clicks on the bold button it will say <b>Hello World</b>
This mainly requires (1) accessing the selectionStart and selectionEnd properties of the input/textarea element and (2) replacing the substring of the value property across that range with the same text, but wrapped in the desired start and end tags. Also, I think it makes sense to reselect the replaced text, which requires a couple of calls to select() and setSelectionRange(). Also, if there's no selection (meaning start equals end) it's probably a good idea to do nothing at all.
window.selWrapBold = function(id) { selWrap(id,'<b>','</b>'); };
window.selWrapItalic = function(id) { selWrap(id,'<i>','</i>'); };
window.selWrap = function(id,startTag,endTag) {
let elem = document.getElementById(id);
let start = elem.selectionStart;
let end = elem.selectionEnd;
let sel = elem.value.substring(start,end);
if (sel==='') return;
let replace = startTag+sel+endTag;
elem.value = elem.value.substring(0,start)+replace+elem.value.substring(end);
elem.select();
elem.setSelectionRange(start,start+replace.length);
} // end selWrap()
<input type="button" value="bold" onclick="selWrapBold('ta1');"/>
<input type="button" value="italic" onclick="selWrapItalic('ta1');"/>
<br/>
<textarea id="ta1"></textarea>
Get the text of the html element which is wrapping the text, then add as html the text embedded in the <b> tag.
See jQuery DOM Manipulation for tutorials.
I used this question to get the selected text. And this question to
get the element with selected text in it. I combined them in a single function.
function updateHighlightedText() {
var text = "";
if (window.getSelection) {
text = window.getSelection().toString();
} else if (document.selection && document.selection.type != "Control") {
text = document.selection.createRange().text;
}
var node = $(window.getSelection().anchorNode.parentNode); //Get the selected node
node.html(node.text().replace(text, "<b>"+text+"</b>")); //Update the node
}

Text in clipboard with protractor js

How can I copy a specific text with protractor ?
I would like to load a text to paste after with this command :
return browser.actions().sendKeys(Keys.CONTROL, 'v').perform();
Sample :
Load my text "test" and after with this command, paste "test"
I would like put a text in my clipboard
can I put a value directly in my ng-model, not use sendKeys ?
Yes, you can directly set the model value via .evaluate():
var elm = element(by.model("mymodel.field"));
elm.evaluate("mymodel.field = 'test';");
Putting a text into clipboard
The idea is to use an existing or dynamically create an input element where you would send the text to, select all the text in the input and copy it with a CTRL/COMMAND + C shortcut.
Sample:
var textToBeCopied = "my text";
// creating a new input element
browser.executeScript(function () {
var el = document.createElement('input');
el.setAttribute('id', 'customInput');
document.getElementsByTagName('body')[0].appendChild(el);
});
// set the input value to a desired text
var newInput = $("#customInput");
newInput.sendKeys(textToBeCopied);
// select all and copy
newInput.sendKeys(protractor.Key.chord(browser.controlKey, "a"));
newInput.sendKeys(protractor.Key.chord(browser.controlKey, "c"));
where browser.controlKey is a cross-platform way to handle CTRL/COMMAND keys:
Using cross-platform keyboard shortcuts in end-to-end testing
Somewhat related to this: I needed to test a dialog, which placed some text in the clipboard when the user pressed the 'Copy' button in the dialog. I wanted to test that the text really got copied to the clipboard. I found this 'copy-paste' library: https://www.npmjs.com/package/copy-paste . "A command line utility that allows read/write (i.e copy/paste) access to the system clipboard. It does this by wrapping pbcopy/pbpaste (for OSX), xclip (for Linux and OpenBSD), and clip (for Windows)." I would say it is a javascript module rather than a command line utility. Anyway, I started using it, as the depenency on xclip (for Linux) was not a problem for me.
Here's the snippet I use with protractor to copy text to the clipboard. I need tabs to be accepted because most of my testing involves cut-and-paste from spreadsheets, where tab is the default column delimiter.
Further, it accommodates nuances in the html body's layout better (overflow: hidden).
function copyToClipboard(browser, text) {
var id = 'someCustomIdToAvoidAliasing';
var newInput = browser.element(by.css("#" + id));
return browser.executeScript(function () {
var el = document.createElement('textarea');
el.setAttribute('id', 'someCustomIdToAvoidAliasing');
el.setAttribute('style', 'position:fixed;z-index:10000;top:0;left:0');
el.onkeydown = function(e) {
if (e.keyCode === 9) {
this.value = this.value + "\t";
return false;
}
};
document.getElementsByTagName('body')[0].appendChild(el);
})
.then(function() {
return newInput.sendKeys(text);
})
.then(function() {
return newInput.sendKeys(protractor.Key.CONTROL, "a", protractor.Key.NULL);
})
.then(function() {
return newInput.sendKeys(protractor.Key.CONTROL, "c", protractor.Key.NULL);
})
.then(function() {
return browser.executeScript(function() {
var el = document.getElementById('someCustomIdToAvoidAliasing');
el.remove();
});
});
}

Clean Microsoft Word Pasted Text using JavaScript

I am using a 'contenteditable' <div/> and enabling PASTE.
It is amazing the amount of markup code that gets pasted in from a clipboard copy from Microsoft Word. I am battling this, and have gotten about 1/2 way there using Prototypes' stripTags() function (which unfortunately does not seem to enable me to keep some tags).
However, even after that, I wind up with a mind-blowing amount of unneeded markup code.
So my question is, is there some function (using JavaScript), or approach I can use that will clean up the majority of this unneeded markup?
Here is the function I wound up writing that does the job fairly well (as far as I can tell anyway).
I am certainly open for improvement suggestions if anyone has any. Thanks.
function cleanWordPaste( in_word_text ) {
var tmp = document.createElement("DIV");
tmp.innerHTML = in_word_text;
var newString = tmp.textContent||tmp.innerText;
// this next piece converts line breaks into break tags
// and removes the seemingly endless crap code
newString = newString.replace(/\n\n/g, "<br />").replace(/.*<!--.*-->/g,"");
// this next piece removes any break tags (up to 10) at beginning
for ( i=0; i<10; i++ ) {
if ( newString.substr(0,6)=="<br />" ) {
newString = newString.replace("<br />", "");
}
}
return newString;
}
Hope this is helpful to some of you.
You can either use the full CKEditor which cleans on paste, or look at the source.
I am using this:
$(body_doc).find('body').bind('paste',function(e){
var rte = $(this);
_activeRTEData = $(rte).html();
beginLen = $.trim($(rte).html()).length;
setTimeout(function(){
var text = $(rte).html();
var newLen = $.trim(text).length;
//identify the first char that changed to determine caret location
caret = 0;
for(i=0;i < newLen; i++){
if(_activeRTEData[i] != text[i]){
caret = i-1;
break;
}
}
var origText = text.slice(0,caret);
var newText = text.slice(caret, newLen - beginLen + caret + 4);
var tailText = text.slice(newLen - beginLen + caret + 4, newLen);
var newText = newText.replace(/(.*(?:endif-->))|([ ]?<[^>]*>[ ]?)|( )|([^}]*})/g,'');
newText = newText.replace(/[·]/g,'');
$(rte).html(origText + newText + tailText);
$(rte).contents().last().focus();
},100);
});
body_doc is the editable iframe, if you are using an editable div you could drop out the .find('body') part. Basically it detects a paste event, checks the location cleans the new text and then places the cleaned text back where it was pasted. (Sounds confusing... but it's not really as bad as it sounds.
The setTimeout is needed because you can't grab the text until it is actually pasted into the element, paste events fire as soon as the paste begins.
How about having a "paste as plain text" button which displays a <textarea>, allowing the user to paste the text in there? that way, all tags will be stripped for you. That's what I do with my CMS; I gave up trying to clean up Word's mess.
You can do it with regex
Remove head tag
Remove script tags
Remove styles tag
let clipboardData = event.clipboardData || window.clipboardData;
let pastedText = clipboardData.getData('text/html');
pastedText = pastedText.replace(/\<head[^>]*\>([^]*)\<\/head/g, '');
pastedText = pastedText.replace(/\<script[^>]*\>([^]*)\<\/script/g, '');
pastedText = pastedText.replace(/\<style[^>]*\>([^]*)\<\/style/g, '');
// pastedText = pastedText.replace(/<(?!(\/\s*)?(b|i|u)[>,\s])([^>])*>/g, '');
here the sample : https://stackblitz.com/edit/angular-u9vprc
I did something like that long ago, where i totally cleaned up the stuff in a rich text editor and converted font tags to styles, brs to p's, etc, to keep it consistant between browsers and prevent certain ugly things from getting in via paste. I took my recursive function and ripped out most of it except for the core logic, this might be a good starting point ("result" is an object that accumulates the result, which probably takes a second pass to convert to a string), if that is what you need:
var cleanDom = function(result, n) {
var nn = n.nodeName;
if(nn=="#text") {
var text = n.nodeValue;
}
else {
if(nn=="A" && n.href)
...;
else if(nn=="IMG" & n.src) {
....
}
else if(nn=="DIV") {
if(n.className=="indent")
...
}
else if(nn=="FONT") {
}
else if(nn=="BR") {
}
if(!UNSUPPORTED_ELEMENTS[nn]) {
if(n.childNodes.length > 0)
for(var i=0; i<n.childNodes.length; i++)
cleanDom(result, n.childNodes[i]);
}
}
}
This works great to remove any comments from HTML text, including those from Word:
function CleanWordPastedHTML(sTextHTML) {
var sStartComment = "<!--", sEndComment = "-->";
while (true) {
var iStart = sTextHTML.indexOf(sStartComment);
if (iStart == -1) break;
var iEnd = sTextHTML.indexOf(sEndComment, iStart);
if (iEnd == -1) break;
sTextHTML = sTextHTML.substring(0, iStart) + sTextHTML.substring(iEnd + sEndComment.length);
}
return sTextHTML;
}
Had a similar issue with line-breaks being counted as characters and I had to remove them.
$(document).ready(function(){
$(".section-overview textarea").bind({
paste : function(){
setTimeout(function(){
//textarea
var text = $(".section-overview textarea").val();
// look for any "\n" occurences and replace them
var newString = text.replace(/\n/g, '');
// print new string
$(".section-overview textarea").val(newString);
},100);
}
});
});
Could you paste to a hidden textarea, copy from same textarea, and paste to your target?
Hate to say it, but I eventually gave up making TinyMCE handle Word crap the way I want. Now I just have an email sent to me every time a user's input contains certain HTML (look for <span lang="en-US"> for example) and I correct it manually.

Categories