Dynamic syntax highlighting in Javascript - javascript

I'm creating javascript editor with syntax highlighter based on contentEditable html element. I wrote a class that manages the editor and works like this:
function Editor() {
this.setContent = function(content) {
var pos = //Computes cursor position in current dom scope (number of characters from begining)
editor.innerHTML = content;
/* Here I set the cursor back, again, it is complicated and slow process*/
}
this.highlight = function() {
this.setContent(someHighlighter.highlight(this.getTextContent())); //Slow - highlights whole innerText of editor DOM and sets it as innerHTML
}
}
The code above is just concept. But even so, you can see that it is not effective and you may understand I'm looking for beter alternative.
On the other side I dont want to spend the rest of my life doing this editor.
Is there some javascript syntax highlighter that works with DOM instead of pure text?
Is there some better way how to change element content without loosing cursor position?

Related

How to add <span> within CodeMirror editor?

No jQuery please!
I'd like to add a styled span within the body of a CodeMirror editor. This is for the purposes of a mail merge like application where you can for instance do: (From Zapier)
I believe this may be possible using a CodeMirror Widget but I'm lost as to what direction to go in. I found an example of something similar (albeit far more complicated) here.
I also tried tagify which is extremely similar to what I'm after but doesn't have multi-line inputs, which is a requirement.
All I need is the ability to add and remove (via backspace with the caret just behind the tag) these spans but there doesn't appear to be a simple solution.
Also if there is a convenient library or some other direction I can go in not involving CodeMirror that'd also be fine.
CodeMirror is actually well suited for this.
First insert some placeholder into the document, such as [[tag]].
var lineNumber = 0;
var charNumber = 0;
var snippet = "[[tag]]"
editor.doc.replaceRange(snippet, {line:lineNumber, from: charNumber});
Then create your DOM element, I recommend a span.
var htmlNode = document.createElement("span");
//Style and add what you like to the span
Then use doc.markText to replace it with that node.
editor.doc.markText({line: lineNumber,ch: charNumber}, {line: lineNumber,ch: charNumber + snippet.length}, {
replacedWith: htmlNode
})

Measuring the string width in Javascript

I'm trying to precisely fit a string into a certain width. This means the font-size changes depending on the string. I now use the following function, using jQuery:
function fontResize ( )
{
for (var i = $("#date").css("font-size").slice(0, -2); $("#date").width() < $("#clock").width(); i++)
$("#date").css("font-size", i.toString() + "px");
}
The idea is that I set the font-size in the CSS to the lowest possible value. I initialize the loop with this value and increment the font-size with 1 until the string is wider than the width of the containing element. The string in this case is "date" and the containing element is "clock".
Although this works, I think the main disadvantage is that the string has to be first drawn before the width can be determined. This means I cannot call this function before loading the body.
If anyone knows a better way to do this please let me know! Thanks.
To make sure you're getting all the styles and such applied to it that will be applied when the page is fully rendered, yes, you do want to put the element in the DOM (and in the right place in the DOM) before you do your measurement stuff. But you don't have to wait until everything else is there (unless you think it will affect the styling of the string you're measuring). You can put your code in a script block immediately after the element in question — no waiting for ready. The date element will be there and accessible, according to Google's Closure library engineers. E.g., if date is a span:
<body>
...
<span id="date">December 13th</span>
<script>fontResize();</script>
...
...
</body>
It's unfortunate to intermix code and markup like that (particularly if you have separate teams doing the markup and the code), but if your requirement is to size the text absolutely as soon as possible, that's how.
The above also assumes your fontResize function is already loaded (e.g., in a script block higher on the page). This is another reason it's unfortunate to mix markup and code like this, because normally of course you want to put your scripts at the bottom, just before closing the body tag.
However: It may be worth experimenting to see if you can do your resizing in the script you put just before the closing body tag instead. There'd be a small window of opportunity for the page not to look right, but quite small and of course pages tend to look a little funny as they load anyway. Then you wouldn't have the intermixing problem and wouldn't have to load your scripts early. You may find that the just-before-the-closing-body-tag is soon enough.
How about using the canvas, and the measureText method?
$(function () {
var canvas = $("canvas")[0];
var context = canvas.getContext("2d");
var text = "hello world";
context.font = "40pt Calibri";
var metrics = context.measureText(text);
alert(metrics.width);
});

JavaScript - How to get the current selection 'index' within a document

My code here returns a JavaScript selection object from within an iFrame (the iFrame page is within the same domain, so no xss issue).
What I need to know is the index of the selection within the raw html code (not the dom).
UPDATE:
E.g.
If you have an html doc:
<html><body>ABC</body></html>
And in the UI, the user uses their mouse to select the text 'ABC', I want to be able to use JavaScript to determine the postion of the selected text in the html source. In this case the index of ABC is 13.
UPDATE 2
The reason I'm persisting with this madness, is that I need to create a tool that can revisit a page and pull text based on a selected text the user has identified at an earlier time. The user tells the system where the text is, and the system from that point on uses regular expressions to pull the text. Now, if the dom is not the same as the raw html, and there's no way to pinpoint the selection in the raw html - it's really difficult to know what reg ex to generate. I don't think there's another way around this.
// Returns the raw selection object currently
// selected in the UI
function getCurrentSelection() {
var selection = null;
var iFrame = document.getElementById('uc_iFrameGetPriceData');
try {
if (window.getSelection) { // Gecko
selection = iFrame.contentWindow.getSelection();
}
else { // IE
var iframeDoc = iFrame.contentWindow.document;
if (iframeDoc.selection) {
selection = iframeDoc.selection;
}
else {
selection = iframeDoc.contentWindow.getSelection();
}
}
}
catch (err) {
alert( 'Error: getCurrentSelection() - ' + err.description )
}
return selection;
}
You can access the index and offset of your selection by using selection.anchorOffset and selection.focusOffset.
Take a look at this:
http://help.dottoro.com/ljjmnrqr.php
And here's another well explaned article:
http://www.quirksmode.org/dom/range_intro.html
update to your update: I'm not sure why you're trying to get the index of the raw HTML code. But you can walk the DOM based on the selection kinda like this:
selection.anchorNode.nodeValue.replace(selection.anchorNode.nodeValue.substring(selection.anchorOffset, selection.focusOffset), 'replace value')
Note that it's still possible that anchorOffset is before focusOffset, based on whether you selected the text from left to right or from right to left.
If I understand correctly, you're looking to move around in the DOM. In that case, you can use these methods/properties:
parentNode
getChildNodes()
firstChild and lastChild
...and these links might help:
http://www.codingforums.com/archive/index.php/t-81035.html
http://www.sitepoint.com/forums/showthread.php?t=586034
The fastest way is probably
var node = document.getElementById('myElement');
alert(node.parentNode.indexOf(node));
(Sorry, for some reason the formatting buttons aren't showing up in my "Your Answer" area...)
I would be surprised if that information was available.
No DOM API is going to let you distinguish between
<html><body>ABC</body></html>
and
<html ><body >ABC</body></html>
The index in the raw HTML is different in each case, but the constructed DOM is identical.
You can't do this sensibly: the only possible method is to re-download the page's HTML via Ajax, parse the HTML and match the resulting DOM against the current DOM, which may itself have been altered by JavaScript. Besides, it's not a useful number anyway because once the page has been loaded, the original HTML string simply no longer exists in the DOM so offsets within that string have no meaning in JavaScript. Getting the selection in terms of nodes and offsets is much more sensible.

Highlight/Format textareas

I'm looking to implement a live diff inside a webpage that preserves whitespace.
I've done something similar in the past using a combination of TinyMCE and JQuery in IE6 (required for that project) but it didn't preserve whitespace. (It wasn't supposed to, but I didn't add anything to make it do that, it just did). TinyMCE wasn't ideal as I was dealing with plain text, and TinyMCE is a WYSIWYG style editor supporting rich text. Most of it's functionality wasn't being used and is/was hard to disable.
I've looked through most of the projects listed at http://en.wikipedia.org/wiki/Comparison_of_JavaScript-based_source_code_editors. EditArea seemed promising, but I cannot seem to determine which elements are actually being used to display the text.
$('#frame_modified').contents().find('#editor').text()
displays a lot of the line information (123456789101112 and so on) along with the line the cursor is under, but not the rest. I haven't figured out how to apply/modify the styles associated with that.
Lets say the user types foo, I'd like to wrap it so it displays as <span class="bar">foo</span> on the fly. What's the simplest solution to go about getting this sort of functionality?
IE8 is the target browser for this project.
You could do some action on the onKeyDown event (in one of your own plugins). If your cursor is already in a new span add the character just typed in else create a new span with the character entered. Something like this:
ed.onKeyDown.add(function(ed,evt){
char = getChar(evt.keyCode); // your function to get the character to be inserted depending on evt.keyCode
// if span already exists
node = ed.selection.getNode();
if (node.nodeName.toLowerCase() == 'span' && node.className == 'myclass'){
node.innerHTML += char;
}
// new span
else {
doc = ed.getDoc();
new_element = doc.createElement('span');
new_element.className = "myclass";
new_element.innerHTML = char;
tinymce.activeEditor.mceInsertContent(new_element);
}
})

determine when contents of html tag changes

Is there a way to determine when the contents of a HTML tag has changed? I would prefer to catch an event rather than polling it.
My use case is I have text enclosed in span tags within a rich text editor, and I need to remove the span tags when the enclosing text is modified by the user.
Are you using one of the typical WYSIWYG editors, and don't want to update their code to break updatability? Then maybe you could listen to the onTextChange event (or something similar) that the WYSIWYG editor is sending, check the contents of the change, and react on that.
Just an idea, given that you give a bit too little information in your question.
I don't think there is an event available (except for input elements), however you could poll it.
$element = $('#my-element');
var originalHtml = $element.html();
var pollHtml = setInterval(function() {
if (originalHtml !== $element.html()) {
alert('HTML changed!');
clearInterval(pollHtml);
};
}, 100);
I can't add a comment so I'm putting my suggestion here,
#Justin Johnson
$("#close-question-2114317").change(function() {alert(1);});
$("#close-question-2114317").text( function(){
$(this).trigger('change');
return "Close!!!";
});
I believe we can call the trigger if that's the case...
You must be using JavaScript to modify the DOM in some way. Polling is in most cases a bad idea. I have set up a demo here that gives you a good idea on how you might want to check the HTML for changes. Just call that function when needed... even if it is just polling.

Categories

Resources