PDA

View Full Version : Appending data to a post via javascript


MarkFL
06-08-2014, 06:21 PM
Hello all,

I help administrate a math forum running vBulletin 4.2.1. I have recently added an element to the editpost, newreply and newthread templates that introduces a live preview of LaTeX to aid in debugging code more efficiently than using the "Preview Post" function repeatedly.

Within this element, I have an input button that, using the onclick event, will copy the value of an internal textarea element to the post body using the javascript code:

document.getElementsByClassName('cke_source')[0].value += document.getElementById('MathInput').value

This works fine unless the user has opted for the WYSIWYG editor, in which case the DOM is constructed differently.

My question: is there a way to append data to the post message that will work regardless of the editor chosen by the user? I have tried manipulating {vb:raw post.message} to no avail.

I appreciate any insights into how to get this to work. :)

Best Regards,

Mark.

MarkFL
06-27-2014, 04:06 AM
I figured out how to accomplish inserting data into a post message regardless of editor mode (that works with Firefox 29 and Chrome 35), and thought it would be good etiquette to share my findings here.

I have a textarea element with id="MathInput" whose contents I wish to insert into the post message at the current cursor location within the post message when a "Copy to Post" button is clicked. I want the cursor to be located at the end of the inserted data after the operation. This is the function that does this:

function copyMathToPost()
{
var content = document.getElementById('MathInput').value, el = document.getElementsByClassName('cke_source')[0];
if (el)
{
var pos = getCaretPosition(el);
el.value = el.value.substr(0, pos) + content + el.value.substr(pos);
setCaretPosition(el,pos + content.length);
el.focus();
}
else
{
var iFrame = document.getElementsByTagName('iframe')[0];
insertHtmlAtCursor(iFrame,content);
moveCaret(iFrame, -1);
moveCaret(iFrame, 1);
}
}


Here are the supporting functions:

function getCaretPosition(el)
{
if (el.selectionStart)
{
return el.selectionStart;
}
else if (document.selection)
{
el.focus();
var r = document.selection.createRange();
if (r == null)
{
return 0;
}
var re = el.createTextRange(), rc = re.duplicate();
re.moveToBookmark(r.getBookmark());
rc.setEndPoint('EndToStart', re);
return rc.text.length;
}
return 0;
}

function setCaretPosition(ctrl,pos)
{
if(ctrl.setSelectionRange)
{
ctrl.focus();
ctrl.setSelectionRange(pos,pos);
}
else if(ctrl.createTextRange)
{
var range = ctrl.createTextRange();
range.collapse(true);
range.moveEnd('character',pos);
range.moveStart('character', pos);
range.select();
}
}

function insertHtmlAtCursor(iFrame,html)
{
var sel, range, node;
html = html.replace(/\r\n?|\n/g, '<br>');
if (iFrame.contentWindow.getSelection && iFrame.contentWindow.getSelection().getRangeAt)
{
sel = iFrame.contentWindow.getSelection();
try{range = sel.getRangeAt(0);}
catch (e){alert("This function is not supported by your browser. Please switch editor to Source Mode.");}
node = range.createContextualFragment(html);
range.insertNode(node);
range = range.cloneRange();
range.collapse();
sel.removeAllRanges();
sel.addRange(range);
}
else if (iFrame.contentWindow.selection && iFrame.contentWindow.selection.createRange)
{
iFrame.contentWindow.selection.createRange().paste HTML(html);
}
else
{
alert("This function is not supported by your browser. Please switch editor to Source Mode.");
}
iFrame.focus();
}

function moveCaret(win, charCount)
{
var sel, range;
if (win.contentWindow.getSelection)
{
sel = win.contentWindow.getSelection();
if (sel.rangeCount > 0)
{
var textNode = sel.focusNode;
var newOffset = sel.focusOffset + charCount;
sel.collapse(textNode, newOffset);
}
}
else if (sel = win.contentDocument.selection)
{
if (sel.type != "Control")
{
range = sel.createRange();
range.move("character", charCount);
range.select();
}
}
}

tbworld
06-27-2014, 07:59 AM
Nice work, as soon as I have time I am going to check this out in detail. Thank you so much for sharing. :)

I have been working on a pretty easy model by using the existing vb javascript files "vbulletin_quick_reply.js", "vbulletin_quick_edit.js", ... anyway, you already know the problem with that method -- so many different editor modes to modify. After staring at the code for awhile, I too was thinking that I should just position the data via the DOM and be done with it. Since essentially that is all that is being done in the vb JavaScript files.

Sorry, I missed your first (previous) post somehow. I must have been traveling for work or I would have answered, but it looks like you were a bit ahead of my work. I just finished "quick_reply" and "quick_editor" two days ago and still needed to verify caret positioning among the browsers.

Good work! -- I am impressed. :)

MarkFL
06-27-2014, 02:15 PM
Because of an issue reported to me by one of my fellow administrators, who uses a different OS (I use Windows 7 and he uses Xubuntu and we both use Firefox), I have moved the focus method to the end of the moveCaret function, and this alleviates the need to make two calls to the moveCaret function (which I used previously so that Chrome did not lose the cursor).

So, the updated functions are:

function copyMathToPost()
{
var content = document.getElementById('MathInput').value, el = document.getElementsByClassName('cke_source')[0];
if (el)
{
var pos = getCaretPosition(el);
el.value = el.value.substr(0, pos) + content + el.value.substr(pos);
setCaretPosition(el,pos + content.length);
el.focus();
}
else
{
var iFrame = document.getElementsByTagName('iframe')[0];
insertHtmlAtCursor(iFrame,content);
moveCaret(iFrame, 0);
}
}

function insertHtmlAtCursor(iFrame,html)
{
var sel, range, node;
html = html.replace(/\r\n?|\n/g, '<br>');
if (iFrame.contentWindow.getSelection && iFrame.contentWindow.getSelection().getRangeAt)
{
sel = iFrame.contentWindow.getSelection();
try{range = sel.getRangeAt(0);}
catch (e){alert("This function is not supported by your browser. Please switch editor to Source Mode.");}
node = range.createContextualFragment(html);
range.insertNode(node);
range = range.cloneRange();
range.collapse();
sel.removeAllRanges();
sel.addRange(range);
}
else if (iFrame.contentWindow.selection && iFrame.contentWindow.selection.createRange)
{
iFrame.contentWindow.selection.createRange().paste HTML(html);
}
else
{
alert("This function is not supported by your browser. Please switch editor to Source Mode.");
}
}

function moveCaret(win, charCount)
{
var sel, range;
if (win.contentWindow.getSelection)
{
sel = win.contentWindow.getSelection();
if (sel.rangeCount > 0)
{
var textNode = sel.focusNode;
var newOffset = sel.focusOffset + charCount;
sel.collapse(textNode, newOffset);
}
}
else if (sel = win.contentDocument.selection)
{
if (sel.type != "Control")
{
range = sel.createRange();
range.move("character", charCount);
range.select();
}
}
win.focus();
}