I am trying to create a plugin for TinyMCE, so user can add floating tips for selected area.
All i need is to put selected content into span, i do it this way:
var formated =
'<span id="tooltip_widget_' + id + '">'
+ selectedtext
+ '</span>;
ed.selection.setContent(formated);
And it works well in Firefox, i can add text to span and continue typing out of it.
But in Chrome and IE all the following text goes into the added span. And the only way to continue typing out of it - via html editor.
I tried include additional caret placeholder span after the insertion and collapse after it and then delete this placeholder to make sure the caret will be after the inserted span.
But it doesn't help. In chrome and IE if caret is at the end of span, it continues typing inside this span.
Please advice how to solve this.
Solution can be found in this fiddle or as code here:
<script type="text/javascript">
function plugin_work(){
var ed = tinymce.editors[0];
var innerSpanId = 0;
var node = ed.selection.getNode();
// Get the selected contents as text and place it in the input
if (ed.selection.getNode().className == "tooltipedurl") {
var innerSpanId = node.id;
if (innerSpanId != 0) {
var innerSpanHTML = ed.dom.get('data_' + innerSpanId).innerHTML;
}
} else {
var innerSpanHTML = ed.selection.getContent({format : 'html'});
}
var id = new Date().getTime();
var node = ed.selection.getNode();
if (node.className == "tooltipedurl") {
var innerSpanId = node.id;
if (innerSpanHTML != 0) {
ed.dom.get('data_' + innerSpanId).innerHTML = innerSpanHTML;
} else {
ed.dom.remove(node);
ed.selection.setContent(node.innerHTML);
ed.dom.remove(ed.dom.get('data_' + innerSpanId));
ed.focus();
}
} else {
var formated =
'<span id=\"tooltip_widget_' + id + '\" class=\"tooltipedurl\" name="tip">'
+ innerSpanHTML
+ '</span><span id="caret_placeholder_' + id + '" name="caret_placeholder">\u200b</span>';
ed.selection.setContent(formated);
var rng = tinymce.DOM.createRng(); // the range obj
var $caret_placeholder = $(ed.getBody()).find ('#caret_placeholder_'+id); // find the correct selector so that caret_placeholder is the element of your editor text
// rng.setStart(caret_placeholder.firstChild, f.textFragment.value.length); // 0 is the offset : here it will be at the beginning of the line.
// rng.setEnd(caret_placeholder.firstChild, f.textFragment.value.length);
//console.log(caret_placeholder);
rng.setStartAfter($caret_placeholder.get(0));
rng.setEndAfter($caret_placeholder.get(0));
ed.selection.setRng(rng);
//ed.selection.select(caret_placeholder);
//$(caret_placeholder).html('');
ed.focus();
tinyMCE.activeEditor.dom.add(tinyMCE.activeEditor.getBody(), 'span', {id : 'data_tooltip_widget_' + id, style : 'display:none'}, innerSpanHTML);
ed.onKeyUp.add(function(ed, e) {
if ($caret_placeholder = $(ed.getBody()).find ('#caret_placeholder_'+id)) {
var content = $caret_placeholder.get(0).innerHTML;
ed.dom.remove($caret_placeholder.get(0));
ed.selection.setContent(content);
}
});
}
}
tinymce.create('tinymce.plugins.floatingtipsPlugin', {
init : function(ed, url) {
// Register the command so that it can be invoked by using tinyMCE.activeEditor.execCommand('mcefloatingtips');
ed.addCommand('mcefloatingtips', function() {
var se = ed.selection;
// No selection and not in span
if (se.isCollapsed() && se.getNode().className != "tooltipedurl") {
return;
}
ed.windowManager.open({
file : url + '/dialog.htm',
width : 320 + parseInt(ed.getLang('floatingtips.delta_width', 0)),
height : 120 + parseInt(ed.getLang('floatingtips.delta_height', 0)),
inline : 1
}, {
plugin_url : url, // Plugin absolute URL
some_custom_arg : 'custom arg' // Custom argument
});
});
// Register floatingtips button
ed.addButton('floatingtips', {
title : 'Add/Edit floating tip',
cmd : 'mcefloatingtips',
image : url + '/img/floating_tips.gif'
});
// Add a node change handler
ed.onNodeChange.add(function(ed, cm, n) {
var se = ed.selection;
cm.setActive('floatingtips', se.getNode().className == "tooltipedurl");
cm.setDisabled('floatingtips', se.isCollapsed() && se.getNode().className != "tooltipedurl");
});
ed.contentCSS.push(url + '/css/floatingtips.css');
},
createControl : function(n, cm) {
return null;
},
getInfo : function() {
return {
longname : 'floatingtips plugin',
author : 'Some author',
authorurl : 'http://tinymce.moxiecode.com',
infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/floatingtips',
version : "1.0"
};
}
});
tinymce.PluginManager.add('floatingtips', tinymce.plugins.floatingtipsPlugin);
/**
*
* Here goes dialog.js
* What exactly my plugin does
*
* ================== GOAL =====================
* Goal - to wrap selected text with span like this:
* <span class="tooltipedurl", id="tooltip_widget_" + {someuniueID}> SELECTED TEXT </span>
* Add to the end of the editor hidden element with tooltip text like this:
* <span id="data_tooltip_widget_ + {someuniueID} style="display:none"> TOOLTIP TEXT </span>
*
*/
/**
* ================= ALGORITHM ==================
* If some text selected, or if we have caret inside span with class tooltipedurl.
* We got plugin buton - active.
* On click we see popup form with two fields.
* first with selected text, (it is hidden, user shouldn't be able to modify it from popup form)
* second is the tip input(textarea filed) we load here old tip if the caret inside the span with tip.
*
* if text selected:
* we got it from the popup form {f.textFragment.value} and replace it with
* <span id=\"tooltip_widget_' + id + '\" class=\"tooltipedurl\" name="tip">'+ f.textFragment.value + '</span>
* also we create hidden span in the end of editor with tip value from the form {f.newTip.value}
* tinyMCE.activeEditor.dom.add(tinyMCE.activeEditor.getBody(), 'span', {id : 'data_tooltip_widget_' + id, style : 'display:none'}, f.newTip.value);
*
* if caret inside the span which already has tooltip
* we have in popup form current tooltip message and can edit it and put new value to the corresponding hidden span
* ed.dom.get('data_' + innerSpanId).innerHTML = f.newTip.value;
*
* if the new tip value = 0, we delete span which contains the text and delete correspondig
* hiden element with tooltip text
*
* more detailed comments i added to the script below
*/
var ExampleDialog = {
init : function() {
var ed = tinyMCEPopup.editor;
// f is a form from PopUp
var f = document.forms[0];
// if selection inside the span with class tooltipedurl we will put here the id of this span
var innerSpanId = 0;
var node = ed.selection.getNode();
// Get the selected contents as text and place it in the input fields if we are inside the span with class tooltipedurl
if (ed.selection.getNode().className == "tooltipedurl") {
var innerSpanId = node.id;
//taking all the text to the input from the span even if only part of it was selected
f.textFragment.value = node.innerText;
if (innerSpanId != 0) {
//taking current tooltip text from the corresponding hidden span
f.newTip.value = ed.dom.get('data_' + innerSpanId).innerHTML;
}
} else {
//if selected text is not in the span with class tooltipedurl we just get as html to apply tooltip later
f.textFragment.value = ed.selection.getContent({format : 'html'});
}
},
insert : function() {
var ed = tinyMCEPopup.editor;
var f = document.forms[0];
var id = new Date().getTime();
var node = ed.selection.getNode();
//if selection is inside the span#tooltipedurl - we gonna edit tooltip text
if (node.className == "tooltipedurl") {
var innerSpanId = node.id;
if (f.newTip.value != 0) {
//If new tooltip is not 0, we replace inner html of correspondig span in the end of editor
ed.dom.get('data_' + innerSpanId).innerHTML = f.newTip.value;
} else {
//If new tooltip value is 0 - we delete correspondig span in the end of editor and remove <span> tags from text
ed.dom.remove(node);
ed.selection.setContent(node.innerHTML);
ed.dom.remove(ed.dom.get('data_' + innerSpanId));
}
} else {
//if selection doesn't have tooltip, we place it in the tags with unique id and class tooltipedurl
var formated =
'<span id=\"tooltip_widget_' + id + '\" class=\"tooltipedurl\" name="tip">'
+ f.textFragment.value
+ '</span>';
ed.selection.setContent(formated);
/**
* THE PROBLEM IS
* when we add this span and user trying to continue typing.
* in Chrome and IE all the following text goes in this new span.
* In firefox it works OK.
*
*/
//Here i am trying to put caret right after added span with dom.range
var rng = tinymce.DOM.createRng(); // the range obj
var caret_placeholder = ed.dom.get('tooltip_widget_' + id);
// rng.setStart(caret_placeholder.firstChild, f.textFragment.value.length);
// rng.setEnd(caret_placeholder.firstChild, f.textFragment.value.length);
rng.setStartAfter(caret_placeholder);
rng.setEndAfter(caret_placeholder);
ed.selection.setRng(rng);
//But it doesn't help...
//creating invisible element with tooltip message in the end of the editor
tinyMCE.activeEditor.dom.add(tinyMCE.activeEditor.getBody(), 'span', {id : 'data_tooltip_widget_' + id, style : 'display:none'}, f.newTip.value);
}
tinyMCEPopup.close();
}
};
/**
* I don't know how to include popup window from my plugin here
* But ih has 2 fields
* f.textFragment.value ==> it is the selected text (this field is hidden)
* f.newTip.value ==> text input with the tip.
*
* so when user selects some text and clicks add floating tip button. he sees the form with 1 text field and can add new tooltip or edit old one
*
*/
//tinyMCEPopup.onInit.add(ExampleDialog.init, ExampleDialog);
// Initialize TinyMCE with the new plugin and listbox
tinyMCE.init({
plugins : '-example, floatingtips', // - tells TinyMCE to skip the loading of the plugin
mode : "textareas",
theme : "advanced",
theme_advanced_buttons1 : "code,mylistbox,mysplitbutton,bold,italic,underline,separator,strikethrough,justifyleft,justifycenter,justifyright,justifyfull,bullist,numlist,undo,redo,link,unlink, floatingtips",
theme_advanced_buttons2 : "",
theme_advanced_buttons3 : "",
theme_advanced_toolbar_location : "top",
theme_advanced_toolbar_align : "left",
theme_advanced_statusbar_location : "bottom"
});
</script>
<form method="post" action="dump.php">
<textarea name="content">
test
</textarea>
</form>
<div onclick="plugin_work();" style="cursor:pointer;background-color:green;width:70px;">Button</div>
Related
I have several images as slide. On clicking next button, images are being slide shown but i want to add a text for each image over the image.
javacsript code:
afterShow: function( instance, current ) {
//alert($(this).find('img').attr('alt'));
var url = window.location.href;
var cat = url.substring(url.lastIndexOf('#') + 1);
var cats = cat.split('-');
catId= cats[0];
var index=catId+$("[data-fancybox-index]").html();
//alert($("[data-fancybox-index]").html());
//$('.fancybox-slide').children(".imagecontainer").remove();
//alert($("#d_" + index).length);
if($("#d_" + index).length == 0) {
var strDiv='<div id="d_'+index+'" class="centerdivCont"></div>';
$(".fancybox-content").append(strDiv);
return false;
}
/*if($("#d_" + index).length> 0) {
var strDiv='<div id="d_'+index+'" class="centerdivCont"></div>';
$(".fancybox-content").remove(strDiv);
}else{
var strDiv='<div id="d_'+index+'" class="centerdivCont"></div>';
$(".fancybox-content").append(strDiv);
}*/
//alert($("#d_" + index).length);
//$(".fancybox-slide").append($('#'+index).html());
},
I'm struggling to make a textarea adjust its height if needed after some text is added to it by click.
So, I use eventlistener for my textarea in order to determine when some amount of characters is added and a new line is needed further so that textarea is resized vertically. It works fine with manually added characters, but I want to use the so-called "bb-tags" as well for formatting reasons. Say, I use a button that adds red color formatting tag: [color=red][/color].
The problem is that when I add [color=red][/color] by click, my textarea won't add new lines automatically.
I made the following snippet for testing my codes.
// Autoresize textarea
const textarea = document.getElementById('shoutbox-comment');
textarea.addEventListener('input', function () {
this.rows = 2;
this.rows = countRows(this.scrollHeight);
});
function countRows(scrollHeight) {
return Math.floor(scrollHeight / 18); // 18px = line-height
}
// bbtags formatting
function bbtags(h, a, i) {
var g = document.getElementById(h);
g.focus();
if (g.setSelectionRange) {
var c = g.scrollTop;
var e = g.selectionStart;
var f = g.selectionEnd;
g.value = g.value.substring(0, g.selectionStart) + a + g.value.substring(g.selectionStart, g.selectionEnd) + i + g.value.substring(g.selectionEnd, g.value.length);
g.selectionStart = e;
g.selectionEnd = f + a.length + i.length;
g.scrollTop = c;
} else {
if (document.selection && document.selection.createRange) {
g.focus();
var b = document.selection.createRange();
if (b.text != "") {
b.text = a + b.text + i;
} else {
b.text = a + "REPLACE" + i;
}
g.focus();
}
}
}
// insert bbtag on click
bb_red.onclick = function() {
javascript:bbtags("shoutbox-comment", "[color=red]", "[/color]");
}
#shoutbox-comment {
width: 270px;
line-height: 18px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea id="shoutbox-comment"></textarea>
<input type="button" id="bb_red" class="bbtag-color-red" value="red">
I wonder if I need to specify something for eventlistener or else.
Jquery may be used.
Thank you.
replace code of function bb_red.onclick with this :
bb_red.onclick = function() {
javascript:bbtags("shoutbox-comment", "[color=red]", "[/color]");
textarea.rows = countRows(textarea.scrollHeight);
}
I have a div with text using monospace font, and I need to display a cursor in the place where I click, I have functions that display text with cursor:
function draw() {
var text = textarea.val();
var html;
if (pos == text.length) {
html = encode(text) + '<span class="cursor"> </span>';
} else {
html = encode(text.slice(0, pos)) + '<span class="cursor">' +
encode(text[pos+1]) + '</span>' + encode(text.slice(pos+1));
}
output.html(html);
}
and function that get cursor position based on x/y coordinate of the mouse event:
function get_char_pos(div, text, event) {
var num_chars = get_num_chars(div);
var cursor = div.find('.cursor');
var rect = cursor[0].getBoundingClientRect();
var width = rect.width;
var height = rect.height;
var offset = div.offset();
var col = Math.floor((event.pageX-offset.left)/width);
var row = Math.floor((event.pageY-offset.top)/height);
var try_pos = col + (row > 0 ? num_chars * row : 0);
return try_pos;
}
It almost working except when text contain tabs (tabs are replaced by 4 spaces by encode function). I've try to fix tabs using this:
var before = text.slice(0, try_pos);
var tabs = before.match(/\t/g);
var fix = tabs ? tabs * 3 : 0;
try_pos += fix;
return try_pos > text.length ? text.lenght : try_pos;
but this don't work. It should also work for a case when I click on space that may be part of tab. How to fix it when text contain tabs?
Here is codepen demo
The tab character is the issue. It's a single character which means the string it's not calculated as four characters in the text.slice. If you replace \t with four spaces your issue is solved.
I'm having trouble with my function. I made a text editor with BBcode.
Its working very well but the cursor always get back to the end of the textarea.
Here is how it works;
var element = document.getElementById('textEdit');
var lastFocus;
$(document.body).on('click','.editBout', function(e) {
e.preventDefault();
e.stopPropagation();
var style = ($(this).attr("data-style"));
// Depending on the button I set the BBcode
switch (style) {
case 'bold':
avS = "[b]";
apS = "[/b]";
break;
}
if (lastFocus) {
setTimeout(function () { lastFocus.focus() }, 10);
var textEdit = document.getElementById('textEdit');
var befSel = textEdit.value.substr(0, textEdit.selectionStart);
var aftSel = textEdit.value.substr(textEdit.selectionEnd, textEdit.length);
var select = textEdit.value.substring(textEdit.selectionStart, textEdit.selectionEnd);
textEdit.value = befSel + avS + select + apS + aftSel;
textEdit = textEdit.value
textEdit = BBcoder(textEdit);
document.getElementById('editorPreview').innerHTML = textEdit;
}
return (false);
});
This last part here triggers the preview event
$(document.body).on('blur', '#textEdit', function() {
lastFocus = this;
});
So i want it to come back to last focus but at a given position computed out of my selection + added bbcode length.
Before trying to build something, I would like to determine if it is possible.
Start with a text area which can be pre-populated with text and which the user can add/delete text. Now, There are some small elements to the side. They can either be images or HTML elements such as a button or anchor links, whatever is easier. The user can drag an elements into the text area, and it will be inserted at the mouse cursor location and take up text space by pushing the existing text around it (the nearby element will also remain so the user can drop a second). The elements will remain as an element which can later be dragged elsewhere in the document or outside of the view port in which it will be removed. When the elements are positioned as desired, the location of the elements can be identified through some means (regex, dom, etc) so that they can be replaced with different content.
Line breaks will be needed. Ideally, it will work with jQuery and IE7+, however, the IE7 desire might need to be changed.
I’ve come across the following which are close but not quite.
http://skfox.com/jqExamples/insertAtCaret.html
http://jsbin.com/egefi (reference jQuery Drag & Drop into a Text Area)
http://jqueryui.com/droppable/#method-option
If you think it could be built and you have suggestions where I should start, please advise.
I did something very similar yesterday so why not sharing :)
My goal was to drop elements of text onto my textarea, in the middle of two lines of text while showing an indicator to where it would be dropped. I believe it will be useful to you ! ;)
$(document).ready(function() {
var lineHeight = parseInt($('#textarea').css('line-height').replace('px',''));
var previousLineNo = 0;
var content = $('#textarea').val();
var linesArray = content.length > 0 ? content.split('\n') : [];
var lineNo = 0;
var emptyLineAdded = false;
$('.draggable').draggable({
revert: function(is_valid_drop) {
if (!is_valid_drop) {
$('#textarea').val(content);
return true;
}
},
drag: function(event, ui) {
lineNo = getLineNo(ui, lineHeight);
if (linesArray.length > 0 && previousLineNo != lineNo) {
insertWhiteLine(lineNo, linesArray);
}
previousLineNo = lineNo;
}
});
$("#textarea").droppable({
accept: ".draggable",
drop: function( event, ui ) {
appendAtLine(lineNo, linesArray, ui.draggable.text());
$(ui.draggable).remove();
content = $('#textarea').val();
linesArray = content.split('\n');
if (linesArray[linesArray.length - 1] == '')
linesArray.pop(); //remove empty line
}
});
$('#textarea').on('input', function() {
if (!emptyLineAdded) {
console.log('input !');
console.log($('#textarea').val());
content = $('#textarea').val();
linesArray = content.split('\n');
if (linesArray[linesArray.length - 1] == '')
linesArray.pop(); //remove empty line
}
});
});
//Returns the top position of a draggable element,
//relative to the textarea. (0 means at the very top of the textarea)
function getYPosition(element, lineHeight) {
var participantIndex = $(element.helper.context).index();
var initPos = participantIndex * lineHeight;
var actualPos = initPos + element.position.top;
return actualPos;
}
//Returns the line number corresponding to where the element
//would be dropped
function getLineNo(element, lineHeight) {
return Math.round(getYPosition(element, lineHeight) / lineHeight);
}
//Inserts a white line at the given line number,
//to show where the element would be dropped in the textarea
function insertWhiteLine(lineNo, linesArray) {
$('#textarea').val('');
$(linesArray).each(function(index, value) {
if (index < lineNo)
$('#textarea').val($('#textarea').val() + value + '\n');
});
emptyLineAdded = true;
$('#textarea').val($('#textarea').val() + '_____________\n'); //white line
emptyLineAdded = false;
$(linesArray).each(function(index, value) {
if (index >= lineNo)
$('#textarea').val($('#textarea').val() + value + '\n');
});
}
//Inserts content of draggable at the given line number
function appendAtLine(lineNo, linesArray, content) {
$('#textarea').val('');
$(linesArray).each(function(index, value) {
if (index < lineNo)
$('#textarea').val($('#textarea').val() + value + '\n');
});
$('#textarea').val($('#textarea').val() + content + '\n'); //content to be added
$(linesArray).each(function(index, value) {
if (index >= lineNo)
$('#textarea').val($('#textarea').val() + value + '\n');
});
}