I have a word count div outside the tinymce which shows the word count but not using the wordCount plugin but using regex to count the words.
But this count is not showing correct value when i add bullet or apply bold to the already typed text[It is showing count as 3 while i have entered only one word at the time of using bullet and increases the count by 2 while highlighting the already typed text]
Can any one can suggest what to do in the regex to get correct count when using the bold or,italic,underline or bullets or using wordCount plugin to use it's output outside the stauts bar[In this case in my word count div]
Here is the code :
tinymceConfig = {
mode:"exact",
elements:"essay",
menubar: false,
statusbar: false,
plugins: "autoresize",
content_css : '../../theme/css/Language/editor.css',
toolbar : "bold italic underline bullist",
resize:"height",
autoresize_max_height: 325,
setup : function(editor) {
if ($('#essay').prop('readonly')) {
editor.settings.readonly = true;
}
editor.on('keydown', function (evt) {
var wordCount = 0;
var valid_keys = [8, 46];
text = editor.getContent().replace(/(< ([^>]+)<)/g, '').replace(/\s+/g, ' ');
text = text.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
wordCount = text.split(' ').length-1;
if(wordCount >= Helpers.constants.MAX_WORDS && valid_keys.indexOf(evt.keyCode) == -1)
{
evt.preventDefault();
Helpers.prompt('You have reached the maximum word limit.');
//evt.stopPropagation();
return false;
}
});
editor.on('keyup', function (evt) {
var text = '';
clearTimeout(saveEssayIntervalId);
saveEssayIntervalId = setTimeout(function() {
saveEssay('silent');
}, 3000);
text = editor.getContent().replace(/(< ([^>]+)<)/g, '').replace(/\s+/g, ' ');
text = text.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
var wordCount = text.split(' ').length;
$("#essayContainer .textAreaAfter").html("[ Words entered: "+wordCount+" ]");
});
} };
tinyMCE.init(tinymceConfig);
You can get the current word count from TinyMCE's WordCount plugin - you should not need to calculate this yourself.
theEditor = tinymce.activeEditor;
wordCount = theEditor.plugins.wordcount.getCount();
If you have an old tinyMCE version, you may not have the getCount() function, in this case you can write, for the active editor (else pass on the editor's object):
var editor = tinyMCE.activeEditor,
words = editor.plugins.wordcount._getCount(editor);
Related
Okay. I've used Markjs.io and TinyMCE to create a little tool highlighting long sentences (above n-amount of words).
I currently have the text highlighted correctly on load.
I then tried to add som .on('keyup',function()) and .on('change', function()) to run the function and recalculate the highlights / marks as I type.
However. This approach apparently keeps returning the original text. And I can't add any new text to the field.
What I wanna do:
So what I wanna do is figure out how my function should run to keep recalculating and highlight long sentences. But in a way, so that it actually also add in the new text I'm typing or current edits I do in the TinyMCE editor.
Available on CodePen here: http://codepen.io/MarkBuskbjerg/pen/rWWRbX
HTML:
<div id="myTextArea" contenteditable="true">
Any text will do. Above 16 words in a single sentence - from dot to dot - will be highlightet for all the world to see.
</div>
JavaScript (jQuery):
tinymce.init({
selector: '#myTextArea',
height: 300,
setup: function(ed) {
ed.on('change', myCustomInitInstance);
ed.on('keyup', myCustomInitInstance);
ed.on('paste', myCustomInitInstance);
ed.on('cut', myCustomInitInstance);
},
init_instance_callback: "myCustomInitInstance",
});
function myCustomInitInstance(inst) {
var rawText = tinyMCE.get('myTextArea').getContent({
format: 'text'
});
var sentenceArray = rawText.split(".");
var matchWarning = [];
var longSentence = 16;
var words;
var wordCounter;
var output;
for (var i in sentenceArray) {
words = sentenceArray[i].split(" ");
wordCounter = words.length;
if (wordCounter > longSentence) {
matchWarning.push(sentenceArray[i]);
}
}
var $clone = $("#myTextArea").clone();
$clone.mark(matchWarning, {
"separateWordSearch": false,
});
tinyMCE.activeEditor.setContent($clone.html());
}):
The issue is the line:
var $clone = $("#myTextArea").clone();
Which just gets the original value from the textarea each time which is why it isn't updating.
Using makejs on the the body element of the WYSIWYG iframe instead should work:
function myCustomInitInstance(inst) {
var rawText = tinyMCE.get('myTextArea').getContent({
format: 'text'
});
var sentenceArray = rawText.split(".");
var matchWarning = [];
var longSentence = 16;
var words;
var wordCounter;
var output;
for (var i in sentenceArray) {
words = sentenceArray[i].split(" ");
wordCounter = words.length;
if (wordCounter > longSentence) {
matchWarning.push(sentenceArray[i]);
}
}
var editor = tinyMCE.activeEditor;
// Store the selection
var bookmark = editor.selection.getBookmark();
// Remove previous marks and add new ones
$(editor.getBody()).unmark().mark(matchWarning, {
acrossElements: true,
"separateWordSearch": false,
});
// Restore the selection
editor.selection.moveToBookmark(bookmark);
}
I am using jQuery highlightTextarea plugin to highlight words in an html textarea.
jQuery highlightTextarea
It currently highlights those words IN the array,
var words = ['google', 'facebook', 'github', 'microsoft', 'yahoo', 'stackoverflow'];
But what I want to do is highlight all words which are NOT IN the array. Is there any way I can do this?
My jsfiddle: http://jsfiddle.net/fr7wb2b4/11/
Stupid I have not tought of it, what about this:
$(document).ready(function () {
var words = ['google', 'facebook', 'github', 'microsoft', 'yahoo', 'stackoverflow'];
$('#textarea').highlightTextarea({
words: [
{ color : '#F00',
words : ['(.+)']
}, // Make everything highlighted
{ color : '#FFF',
words : words
} // Make white what matched
]
});
});
Fiddle with it over here
You could also use words : ['(\\w+)'] to only highlight words initially
You can take a look at the library source, the highlight code: https://github.com/mistic100/jquery-highlighttextarea/blob/master/jquery.highlighttextarea.js#L67
The snippet that does the actual highlighting is:
$.each(this.settings.words, function(color, words) {
text = text.replace(
new RegExp(that.spacer+'('+ words.join('|') +')'+that.spacer, that.regParam),
'<mark style="background-color:'+ color +';">$1</mark>'
);
});
So you can replace the highlight function with your own function, and do whatever logic you choose for highlighting (hurray for open source!)
For example you can copy-paste of the entire function just replace the $.each(words... loop with something along:
var replaced = '',
tokens = text.split(' ');
$.each(tokens, function(ind, token) {
if (this.settings.words.indexOf(token) != -1) {
// this token in the text is part of the 'words' - do not highlight
replaced += token;
}
else {
// this token isn't part of the words - highlight it
replaced += '<span style="...">'+token+'</span>';
}
replaced += ' ';
}
text = replaced;
I have a monospaced textarea (not unlike the stackexchange editor). When my user clicks, I need a character to automagically appear on the previous line using jQuery. I know I need to use .click() to bind a function to that event, but the logic of the function eludes me.
Desired Behavior...user will click at position of the asterisk *
Here is some text in my editor.
When I double click at a position*
I want to insert a new line above, with a new character at the same position
The above text should become the following after the function gets run
Here is some text in my editor.
*
When I double click at a position*
I want to insert a new blank line above, at the same position
What I have tried:
I have found the caret jQuery plugin, which has a function called caret() that I can get to find the position of the the asterisk when I click (the position is 74).
<script src='jquery.caret.js'></script>
$('textarea').click(function(e) {
if (e.altKey){
alert($("textarea").caret());
}
});
But I really need to know the position of the character within the line, not the entire textarea. So far this eludes me.
Here's something without using caret.js
$('textarea').dblclick(function(e){
var text = this.value;
var newLinePos = text.lastIndexOf('\n', this.selectionStart);
var lineLength = this.selectionStart - newLinePos;
var newString = '\n';
for(var i=1; i < lineLength; ++i){
newString += ' ';
}
newString += text.substr(this.selectionStart,this.selectionEnd-this.selectionStart);
this.value = [text.slice(0, newLinePos), newString, text.slice(newLinePos)].join('');
});
Here's a fiddle. Credit to this post for 'inserting string into a string at specified position'.
Just realised that doing that on the top line is a bit broken, I'll have a look when I get home!
Update
Fixed the top-line problem.
if(newLinePos == -1){
this.value = newString + '\n' + this.value;
} else {
this.value = [text.slice(0, newLinePos), '\n'+newString, text.slice(newLinePos)].join('');
}
http://jsfiddle.net/daveSalomon/3dr8k539/4/
Assuming you know the position of the caret in the whole text area here's something you might do with it.
function getCaretPosition(text, totalOffset) {
var line = 0, pos = 0;
for (var i = 0; i < Math.min(totalOffset, text.length); i++) {
if (text[i] === '\n') {
line++;
pos = 0;
} else {
pos++;
}
}
return { row: line, col: pos };
}
var caretPosition = getCaretPosititon($("textarea").val(), $("textarea").caret());
I have the following script which allows me to select text, and will then visually highlight it by wrapping the selected text in a span tag.
This normally works fine, but if there is a highlight tag separated from another highlight tag by only a space, it joins the two highlights together.
Javascript
var HVleftPanelContent = $("#highlight-view .top .content");
HVoutputUl = $("#highlight-view .contentBottom ul");
$("p").on("copy", highlight);
function highlight() {
var text = window.getSelection().toString();
var selection = window.getSelection().getRangeAt(0);
var selectedText = selection.extractContents();
var textStr = selectedText.textContent;
if (textStr == "\n") {
clearSelection();
return false;
} else if (textStr[textStr.length - 1] == "\n") {
textStr = textStr.slice(0, -1);
var reg = new RegExp("\n", "g");
textStr = textStr.replace(reg, "\n<b data='
'></b>") + "\n";
} else if (textStr.indexOf("\n") >= 0) {
var reg = new RegExp("\n", "g");
textStr = textStr.replace(reg, "\n<b data='
'></b>");
}
var span = $("<span class='highlight'>" + textStr + "</span>");
selection.insertNode(span[0]);
if (selectedText.childNodes[1] != undefined) {
$(selectedText.childNodes[1]).remove();
}
var txt = HVleftPanelContent.html();
HVleftPanelContent.html(txt.replace(/<\/span>(?:\s)*<span class="highlight">/g, ''));
HVoutputUl.html("");
$("#highlight-view .top .content .highlight").each(function () {
$("#highlight-view .contentBottom ul").append("<li><span>" + $(this).html() + "</span></li>");
});
saveIt();
clearSelection();
}
Recap
If HTML looks like this:
This is a short paragraph
And I highlight "is", the markup changes to:
This <span>is</span> a short paragraph
And then I highlight either "this" or "a", the markup erroneously changes to:
This <span>isa</short> paragraph
Instead of how it should change:
This <span>is</span> <span>a</span> paragraph
Potential Problem
I assume the problem lays in this line:
HVleftPanelContent.html(txt.replace(/<\/span>(?:\s)*<span class="highlight">/g, ''));
Where the Regex statement is joining <span> tags that are next to each other, which it should so that if two span tags are directly next to each other, it becomes one span, but the Regex isn't limiting the joining to only when they're directly next to each other.
So, basically, how can I change the Regex to only join span tags if they're directly next to each other.
Fairly simple, replace:
HVleftPanelContent.html(txt.replace(/<\/span>(?:\s)*<span class="highlight">/g, ''));
With:
HVleftPanelContent.html(txt.replace(/<\/span><span class="highlight">/g, ''));
The problem was (?:\s)*, which means match any white space 0 or more times, which means that it would match even the spans that are separated with spaces.
I currently have a textarea which I requires control over text that has been pasted in,
essentially I need to be able to take whatever the user wants to paste into a textarea and place it into a variable.
I will then work out the position in which they pasted the text and the size of the string to remove it from the textarea,
Then at the end deal with the text thats is in the variable in my own way.
My question: how would I go about getting a copy of the text in a variable that was just pasted in by the user?
Thanks.
I answered a similar question a few days ago: Detect pasted text with ctrl+v or right click -> paste. This time I've included quite a long function that accurately gets selection boundaries in textarea in IE; the rest is relatively simple.
You can use the paste event to detect the paste in most browsers (notably not Firefox 2 though). When you handle the paste event, record the current selection, and then set a brief timer that calls a function after the paste has completed. This function can then compare lengths to know where to look for the pasted content. Something like the following:
function getSelectionBoundary(el, start) {
var property = start ? "selectionStart" : "selectionEnd";
var originalValue, textInputRange, precedingRange, pos, bookmark, isAtEnd;
if (typeof el[property] == "number") {
return el[property];
} else if (document.selection && document.selection.createRange) {
el.focus();
var range = document.selection.createRange();
if (range) {
// Collapse the selected range if the selection is not a caret
if (document.selection.type == "Text") {
range.collapse(!!start);
}
originalValue = el.value;
textInputRange = el.createTextRange();
precedingRange = el.createTextRange();
pos = 0;
bookmark = range.getBookmark();
textInputRange.moveToBookmark(bookmark);
if (/[\r\n]/.test(originalValue)) {
// Trickier case where input value contains line breaks
// Test whether the selection range is at the end of the
// text input by moving it on by one character and
// checking if it's still within the text input.
try {
range.move("character", 1);
isAtEnd = (range.parentElement() != el);
} catch (ex) {
log.warn("Error moving range", ex);
isAtEnd = true;
}
range.moveToBookmark(bookmark);
if (isAtEnd) {
pos = originalValue.length;
} else {
// Insert a character in the text input range and use
// that as a marker
textInputRange.text = " ";
precedingRange.setEndPoint("EndToStart", textInputRange);
pos = precedingRange.text.length - 1;
// Delete the inserted character
textInputRange.moveStart("character", -1);
textInputRange.text = "";
}
} else {
// Easier case where input value contains no line breaks
precedingRange.setEndPoint("EndToStart", textInputRange);
pos = precedingRange.text.length;
}
return pos;
}
}
return 0;
}
function getTextAreaSelection(textarea) {
var start = getSelectionBoundary(textarea, true),
end = getSelectionBoundary(textarea, false);
return {
start: start,
end: end,
length: end - start,
text: textarea.value.slice(start, end)
};
}
function detectPaste(textarea, callback) {
textarea.onpaste = function() {
var sel = getTextAreaSelection(textarea);
var initialLength = textarea.value.length;
window.setTimeout(function() {
var val = textarea.value;
var pastedTextLength = val.length - (initialLength - sel.length);
var end = sel.start + pastedTextLength;
callback({
start: sel.start,
end: end,
length: pastedTextLength,
text: val.slice(sel.start, end),
replacedText: sel.text
});
}, 1);
};
}
window.onload = function() {
var textarea = document.getElementById("your_textarea");
detectPaste(textarea, function(pasteInfo) {
var val = textarea.value;
// Delete the pasted text and restore any previously selected text
textarea.value = val.slice(0, pasteInfo.start) +
pasteInfo.replacedText + val.slice(pasteInfo.end);
alert(pasteInfo.text);
});
};
You might now use FilteredPaste.js (http://willemmulder.github.com/FilteredPaste.js/) instead. It will let you control what content gets pasted into a textarea or contenteditable and you will be able to filter/change/extract content at will.
A quick search shows me that there are different methods for different browsers. I'm not sure if jQuery has a solution. Prototype.js does not appear to have one. Maybe YUI can do this for you?
You can also use TinyMCE, since it does have a gazillion of different event triggers. It is a full fledged word processor, but you can use it as plain text if you want. It might be a bit too much weight to add though. For example, upon initiation, it turns your <textarea> into an iFrame with several sub. But it will do what you ask.
--Dave