I am using Ace Editor in my web app. Wonder if it's possible to copy the text inside Ace Editor to clipboard with highlight. With default configurations, if I copy the selected text in Ace Editor to clipboard, it seems that only text content is copied with no html styles.
Thanks a lot for your help!
Unfortunately there is no api for this. you'll need to modify https://github.com/ajaxorg/ace/blob/v1.2.5/lib/ace/keyboard/textinput.js#L290 to also set text/html mime type to some html, rendered similar to https://github.com/ajaxorg/ace/blob/v1.2.5/lib/ace/layer/text.js#L442.
Also you'll need to include the css for the theme in the copied html
I know this is late, but this might be helpful for someone like me who stumbled upon this problem this late.
The basic idea is to get the text that is being copied and use Ace's tokenizer to generate HTML from it:
Add an event listener on the copy/cut event on the editor's container.
You can use clipboard object in event to get the data currently being copied: event.clipboardData?.getData('text/plain')
Rest steps are in code below
// get current tokenizer
const tokenizer = aceSession.getMode().getTokenizer();
// get `Text` object from ace , this will help in generating HTML
const Text = ace.require('ace/layer/text').Text;
// create a wrapper div, all your resultant HTML will come inside this
// also this will contain the basic HTML required to initialize the editor
const root = document.createElement('div');
// this is the main magic object
const rootText = new Text(root);
lines.forEach(line => {
// this converts your text to tokens
const tokens = tokenizer.getLineTokens(line, 'start') as any;
const leadingSpacesCount = (line.match(/^\s*/) || [])[0].length;
const lineGroupEl = document.createElement('div');
lineGroupEl.className = 'ace_line_group';
const lineEl = document.createElement('div');
lineEl.className = 'ace_line';
const spaceSpan = document.createElement('span');
if (tokens && tokens.tokens.length) {
//////////////////////////////////////////////////////
// Magic Happens here, this line is responsible for converting our tokens to HTML elements
rootText.$renderSimpleLine(lineEl, tokens.tokens);
//////////////////////////////////////////////////////
// Leading spaces do not get translated to HTML, add them separately
spaceSpan.innerHTML = ' '.repeat(leadingSpacesCount);
lineEl.insertBefore(spaceSpan, lineEl.children[0]);
} else {
spaceSpan.innerHTML = ' ';
lineEl.appendChild(spaceSpan);
}
lineGroupEl.appendChild(lineEl);
// `root` has a wrapper div, inside which our main div (with class "ace_layer ace_text-layer") lies
root.children[0].appendChild(lineGroupEl);
});
return root.innerHTML;
Now finally, in your eventlistener you can wrap this with any div to give your own custom color to it, and put it to clipboardData with text\html mime type:
event.clipboardData?.setData('text/html', htmlContent);
Related
In an MVC application I have to use #HTML.TextAreaFor to display some text from a database, the trouble is sometimes that text may have HTML tags within it and I can't see a way to remove those for display only.
Is it possible to do this in the view (maybe with CSS?) without having to strip the tags in the controller first?
EDIT
The data coming from the controller contains html tags which I do not want to remove, I just don't want to display them
Normally I would use #HTML.Raw but it has to work in a #HTML.TextAreaFor control.
If you want to decode Html returned from the Controller you can use the following JavaScript method:
This method decodes "Chris' corner" to "Chris' corner".
var decodeEntities = (function () {
// this prevents any overhead from creating the object each time
var element = document.createElement('div');
function decodeHTMLEntities(str) {
if (str && typeof str === 'string') {
// strip script/html tags
str = str.replace(/<script[^>]*>([\S\s]*?)<\/script>/gmi, '');
str = str.replace(/<\/?\w(?:[^"'>]|"[^"]*"|'[^']*')*>/gmi, '');
element.innerHTML = str;
str = element.textContent;
element.textContent = '';
}
return str;
}
return decodeHTMLEntities;
})();
You can do this by using a razor code in your view.
#Html.Raw(HttpUtility.HtmlDecode(Model.Content))
if I set Model.Content to this string "<strong>This is me</strong><button>click</button>", the code above will render it like HTML code and will have a strong text next to a button as an output like the image below:
There's some nice rich text editors libraries like CK Editor, Quill, or TinyMCE that can display HTML while still maintaining the editor capabilities of being a text editor. All of these libraries have capabilities of being read-only as well if that's necessary.
Example from Quill -
Sorted this by changing TextAreaFor toTextBoxFor and setting a formatted value.
#Html.TextBoxFor(x => Model.MyItem, new { #class = "form-control", #required = "true", Value = Regex.Replace(Model.MyItem, "<.*?>", String.Empty) })
I need to extract parts of string from the text which was written in the field (input) on UI (This text is not in HTML code).
I am trying sth like this (but it does not work).
const textInput = await model.inputtTittle.textContent;
console.log(textInput)
Nothing return probably textContent take text from the selector, I was trying with .innerText but it also returned nothing.
And then I would like to write sth like this:
if (textInput.length > 32)
await t.typeText(model.inputTittle, textInput.substr(0, 30));
I hope that it will be work if I have the content of the field inputTittle.
Additional question:
This answer is hidden. This answer was deleted via review 16 hours ago by Jason Aller, Mark Rotteveel, Nico Haase, Botje.
This code works:
const textTittle = await model.inputTittle.value;
const textlength = textTittle.length
if (textlength>32)
{
console.log(textTittle.substr(0,30));
}
why i can not to writte shorter:
if (await model.inputTittle.value.length >32)
{ console.log(await model.inputTittle.value.substr(0,30));}
You can obtain the entire DOM Node Snapshot with all properties in one object to check what properties you need. It is likely you need the value property.
I'm trying to copy a Header/Footer from the active Doc acting as a template to a newly created Doc. I'm able to get the text easy enough, but I'm not able to get the formatting, fonts or horizontal alignment.
My theory was that I could do something like
newDocHeader.setAttributes(activeDocHeader.getAttributes());
But, I still only see plain text that is left aligned. When inspecting the attributes object on the header i get the following:
({
FONT_SIZE:null,
ITALIC:null,
STRIKETHROUGH:null,
FOREGROUND_COLOR:null,
BOLD:null,
LINK_URL:null,
UNDERLINE:null,
FONT_FAMILY:null,
BACKGROUND_COLOR:null
})
I tried to loop through the Child objects of the Header and perform a similar setAttributes(getAttributes) on each child, but to no avail.
I also thought the copy() function on the Header/Footer object would be promising, but when I tried
newDocFooter = activeDocFooter.copy();
But, this produces a blank footer with no text or formatting.
Is there a good way to copy the formatting, font and horizontal alignment from one Header/Footer to another?
I'm entirely unfamiliar with DocumentApp, but this broadly worked for me:
/**
* Copies headers from one document to another.
* #param {string} source The source document URL.
* #param {string} target The target document URL.
*/
function copyHeader(source, target) {
// Used to map from child types to required "append<X>" method
var functionMap = {
PARAGRAPH: 'appendParagraph',
LIST_ITEM: 'appendListItem',
HORIZONTAL_RULE: 'appendHorizontalRule',
IMAGE: 'appendImage',
TABLE: 'appendTable'
};
var t = DocumentApp.openByUrl(target);
var s = DocumentApp.openByUrl(source);
var sourceHeader = s.getHeader();
var targetHeader = t.getHeader();
// If there is no header in the target doc, add one
if (!targetHeader) {
targetHeader = t.addHeader();
}
targetHeader.clear();
// Docs requires one child element, so one will remain even
// after clearing. Get a reference to it so it can be removed
var targetNumChild = targetHeader.getNumChildren();
if (targetNumChild === 1) {
var placeholderElement = targetHeader.getChild(0);
}
// Copy across each element to the target doc
var c = sourceHeader.getNumChildren();
for (var i = 0; i < c; i++) {
var element = sourceHeader.getChild(i).copy();
var method = functionMap[element.getType()];
targetHeader[method](element);
}
// Remove the saved element if required
if (targetHeader.getNumChildren() > 1 && placeholderElement) {
targetHeader.removeChild(placeholderElement);
}
}
I say broadly only because formatting such as bold, horizontal centering, horizontal rules etc all copied across fine, but bizarrely, lists seem to change from being numbered to being bulleted, so something was lost in translation.
It might need a little tweaking, and surely there is an easier way, but in the absence of anything else, this might be a start.
Source document:
Target document, note that the list type isn't quite right!:
Hope it helps.
I searched stackoverflow , similar question doesn't exist.
Just wanted to know if there was any short html shortcut to activate hyperlinks whenever there is http://www. between div tags. thanks for info.
No. HTML provides no methods to search an element for text that looks like it might be a URL and turn it into a link.
There are, however, numerous questions about using programming languages to modify documents in a similar fashion.
try this code that detects url in text
jsfiddle: http://jsfiddle.net/6u5eM/1/
function findUrls( text ) //the text you want to search a url for
{
var source = (text || '').toString();
var urlArray = [];
var url;
var matchArray;
// Regular expression to find FTP, HTTP(S) and email URLs.
var regexToken = /(((ftp|https?):\/\/)[\-\w#:%_\+.~#?,&\/\/=]+)|((mailto:)?[_.\w-]+#([\w][\w\-]+\.)+[a-zA-Z]{2,3})/g;
// Iterate through any URLs in the text.
while( (matchArray = regexToken.exec( source )) !== null )
{
var token = matchArray[0];
urlArray.push( token );
}
return urlArray;
}
alert( findUrls( 'http://www.site.com fejisojfiosf fsehiofes ' ) ); //returns url from text
I have a SFW embedded in a PHP page. There is also a div on the page with id="target".
I want to access the content of that div (ie: the characters inside it) and hold them as a String variable in AS3. How can I do this?
My attempt so far
import flash.external.ExternalInterface;
var myDivContent = ExternalInterface.call("function(){ return document.GetElementById('target');}");
var myDivContent2:String = myDivContent.toString();
test_vars.text = myDivContent2; //Dynamic text output
I don't think you can define a function in the ExternalInterface.call() method. You have to call a function by name which already exists in the JavaScript.
So I'd create some JavaScript code like this:
function getTargetContent()
{
return document.getElementById('target').innerHTML;
}
And then in your Flash,
var myDivContent = ExternalInterface.call("getTargetContent");
Note that document.getElementById('target') only returns the reference to that div, not the contents within. So if you don't return .innerHTML then the Flash will get an object which may not be usable (although I haven't actually tried doing this).
The easiest way to do this is as Allan describes, write a Javascript function to sit on the page and return the required value to you.
Of course, if you can't edit the page content, only the flash, then you do need to pass the function itself, which will actually have to be forced into the page though JavaScript injection. An example for your case, which I have not tested:
//prepare the JavaSctipt as an XML object for Dom insertion
var injectCode:XML =
<script>
<![CDATA[
function() {
getElementContent = function(elementID) {
return document.getElementById(elementID).innerHTML;
}
}
]]>
</script>;
//inject code
ExternalInterface.call(injectCode);
//get contents of 'divA'
var divAContent:String = ExternalInterface.call('getElementContent','divA') as String;
//get contents of 'spanB'
var spanBContent:String = ExternalInterface.call('getElementContent','spanB') as String;
You're almost there :
var res : String = ExternalInterface.call("function(){return document.getElementById('target').outerHTML}");
If you only want the content of your target, use innerHTML instead of outerHTML.