I wrote the code below to set the first letter to upper case and set the data in CKEditor. The first letter is converting fine, but afterward, the cursor focus is set before the first letter. How can I set the cursor after the character?
CKEDITOR.on('instanceCreated', function (e) {
e.editor.on('contentDom', function () {
e.editor.document.on('keyup', function (event) {
var data = CKEDITOR.instances.editor1.getData();
var str = $(data).text();
var n = str.length;
if (str != null && n == 1) {
CKEDITOR.instances['editor1'].setData(titleCase(str))
function titleCase(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
}
});
});
});
this code is work for you :
$(document).ready(function() {
CKEDITOR.on('instanceReady', function(ev) {
ev.editor.focus();
var s = ev.editor.getSelection(); // getting selection
var selected_ranges = s.getRanges(); // getting ranges
var node = selected_ranges[0].startContainer; // selecting the starting node
var parents = node.getParents(true);
node = parents[parents.length - 2].getFirst();
while (true) {
var x = node.getNext();
if (x == null) {
break;
}
node = x;
}
s.selectElement(node);
selected_ranges = s.getRanges();
selected_ranges[0].collapse(false); // false collapses the range to the end of the selected node, true before the node.
s.selectRanges(selected_ranges); // putting the current selection there
});
});
Related
I started writing this piece of code for InDesign to underline all letters except from the one with descendants, and added a dialog window to chose stroke and offset of the line.
Now I have two problems:
the program underlines all letters
the stroke and offset won't change
I'm a beginner in Javascript and it's the first time coding for InDesign. Does someone have a clue? Thank you!
// UNDERLINE ALL BUT NO DESCENDANTS
//Make certain that user interaction (display of dialogs, etc.) is turned on.
app.scriptPreferences.userInteractionLevel = UserInteractionLevels.interactWithAll;
if (app.documents.length != 0){
try {
// Run script with single undo if supported
if (parseFloat(app.version) < 6) {
main();
} else {
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, "Expand State Abbreviations");
}
// Global error reporting
} catch ( error ) {
alert( error + " (Line " + error.line + " in file " + error.fileName + ")");
}
}else{
alert("Open a document first before running this script.");
}
///MAIN FUNCTION
function main(){
if(app.selection.length != 0){
myDisplayDialog();
}
}
//INTERFACE
function myDisplayDialog(){
//declare variables
//general
var myDoc = app.activeDocument;
var mS = myDoc.selection;
// dialog
var myDialog = app.dialogs.add({name:"Underliner"});
var myLabelWidth = 70;
with(myDialog.dialogColumns.add()){
with(borderPanels.add()){
with(dialogColumns.add()){
with(dialogRows.add()){
staticTexts.add({staticLabel:"Stroke:", minWidth:myLabelWidth});
staticTexts.add({staticLabel:"Offset:", minWidth:myLabelWidth});
}
}
with(dialogRows.add()){
staticTexts.add({staticLabel:""});
var myStroke = measurementEditboxes.add({editValue:1, editUnits:MeasurementUnits.points});
var myOffset = measurementEditboxes.add({editValue: 15, editUnits:MeasurementUnits.points});
}
}
}
var myResult = myDialog.show();
if(myResult == true){
var myStroke = myStroke.editValue;
var myOffset = myOffset.editValue;
myDialog.destroy();
underline(mS,myStroke,myOffset);
}
else{
myDialog.destroy();
alert("Invalid page range.");
}
}
//REAL FUNCTION
function underline(charList,stroke, offset){
var len = charList.length;
const doNotUnderline = ['g','j','p','q','y'];
for (var i=0; i < len; i++){
try{
var myChar = charList[i];
//console.log(typeof myText);
if (includes(myChar, doNotUnderline) == false)
{
myChar.underline = true;
myChar.underlineWeight == stroke;
myChar.underlineOffset == offset;
} else {
myChar.underline = false;
}
}catch(r){
alert(r.description);
break;
}
}
}
//function to know if char is in array
function includes(elemento,array)
{
var len = array.length;
for(var i=0; i<len ;i++)
{
if(array[i]==elemento){return true;}
}
return false;
}
Try these changes in the function underline():
//REAL FUNCTION
function underline(words,stroke, offset) { // <------ here 'words' instead of 'charList'
var charList = words[0].characters; // <------ here get 'characters' of the 'words'
var len = charList.length;
const doNotUnderline = ['g','j','p','q','y'].join(); // <------- here '.join()'
for (var i=0; i < len; i++){
try{
var myChar = charList[i];
// if (includes(myChar, doNotUnderline) == false) // <----- no need
if (doNotUnderline.indexOf(myChar.contents) < 0) // <------ 'indexOf()' instead of 'includes()'
{
myChar.underline = true;
myChar.underlineWeight = stroke; // <------- here '=' instead of '=='
myChar.underlineOffset = offset; // <------- here '=' instead of '=='
} else {
myChar.underline = false;
}
}catch(r){
alert(r.description);
break;
}
}
}
Probably there can be another improvements as well. It's need additional researches. But if you change these lines it should work to a degree.
And there is one little thing that improves user experience greatly: to keep last used values in the input fields. It can be done pretty easy, let me know it you need it.
Update
Here is the way I'm using to store and restore any preferences of my scripts.
Add somewhere at the start of your script these lines:
// get preferences
var PREFS = { stroke: 1, offset: 15 }; // set default prefs
var PREFS_FILE = File(Folder.temp + '/underline_prefs.json'); // the file with preferences
if (PREFS_FILE.exists) PREFS = $.evalFile(PREFS_FILE); // get the prefs from the file
Now you can use the global values PREFS.stroke and PREFS.offset anywhere you want. In your case they go here:
with(dialogRows.add()){
staticTexts.add({staticLabel:""});
var myStroke = measurementEditboxes.add({editValue:PREFS.stroke, editUnits:MeasurementUnits.points});
var myOffset = measurementEditboxes.add({editValue:PREFS.offset, editUnits:MeasurementUnits.points});
}
This way script will get the stroke and weight from the file underline_prefs.json that will be stored in the standard temporary folder of current user.
Final step is to save the values back into the file after the script got them from the dialog window.
I'd put this piece of code here:
if (myResult == true) {
var myStroke = myStroke.editValue;
var myOffset = myOffset.editValue;
myDialog.destroy();
underline(mS, myStroke, myOffset);
// save preferences here
PREFS.stroke = myStroke;
PREFS.offset = myOffset;
PREFS_FILE.open('w');
PREFS_FILE.write(PREFS.toSource());
PREFS_FILE.close();
} else {
myDialog.destroy();
alert("Invalid page range.");
}
Voilá. Now don't need to type the values every time they differ from default ones.
I use the javascript code below to find all occurrences of a string in UIWebView:
UIWebViewSearch.js:
var uiWebview_SearchResultCount = 0;
/*!
#method uiWebview_HighlightAllOccurencesOfStringForElement
#abstract // helper function, recursively searches in elements and their child nodes
#discussion // helper function, recursively searches in elements and their child nodes
element - HTML elements
keyword - string to search
*/
function uiWebview_HighlightAllOccurencesOfStringForElement(element,keyword) {
if (element) {
if (element.nodeType == 3) { // Text node
var count = 0;
var elementTmp = element;
while (true) {
var value = elementTmp.nodeValue; // Search for keyword in text node
var idx = value.toLowerCase().indexOf(keyword);
if (idx < 0) break;
count++;
elementTmp = document.createTextNode(value.substr(idx+keyword.length));
}
uiWebview_SearchResultCount += count;
var index = uiWebview_SearchResultCount;
while (true) {
var value = element.nodeValue; // Search for keyword in text node
var idx = value.toLowerCase().indexOf(keyword);
if (idx < 0) break; // not found, abort
//we create a SPAN element for every parts of matched keywords
var span = document.createElement("span");
var text = document.createTextNode(value.substr(idx,keyword.length));
span.appendChild(text);
span.setAttribute("class","uiWebviewHighlight");
span.style.backgroundColor="yellow";
span.style.color="black";
index--;
span.setAttribute("id", "SEARCH WORD"+(index));
//span.setAttribute("id", "SEARCH WORD"+uiWebview_SearchResultCount);
//element.parentNode.setAttribute("id", "SEARCH WORD"+uiWebview_SearchResultCount);
//uiWebview_SearchResultCount++; // update the counter
text = document.createTextNode(value.substr(idx+keyword.length));
element.deleteData(idx, value.length - idx);
var next = element.nextSibling;
//alert(element.parentNode);
element.parentNode.insertBefore(span, next);
element.parentNode.insertBefore(text, next);
element = text;
}
} else if (element.nodeType == 1) { // Element node
if (element.style.display != "none" && element.nodeName.toLowerCase() != 'select') {
for (var i=element.childNodes.length-1; i>=0; i--) {
uiWebview_HighlightAllOccurencesOfStringForElement(element.childNodes[i],keyword);
}
}
}
}
}
// the main entry point to start the search
function uiWebview_HighlightAllOccurencesOfString(keyword) {
uiWebview_RemoveAllHighlights();
uiWebview_HighlightAllOccurencesOfStringForElement(document.body, keyword.toLowerCase());
}
// helper function, recursively removes the highlights in elements and their childs
function uiWebview_RemoveAllHighlightsForElement(element) {
if (element) {
if (element.nodeType == 1) {
if (element.getAttribute("class") == "uiWebviewHighlight") {
var text = element.removeChild(element.firstChild);
element.parentNode.insertBefore(text,element);
element.parentNode.removeChild(element);
return true;
} else {
var normalize = false;
for (var i=element.childNodes.length-1; i>=0; i--) {
if (uiWebview_RemoveAllHighlightsForElement(element.childNodes[i])) {
normalize = true;
}
}
if (normalize) {
element.normalize();
}
}
}
}
return false;
}
// the main entry point to remove the highlights
function uiWebview_RemoveAllHighlights() {
uiWebview_SearchResultCount = 0;
uiWebview_RemoveAllHighlightsForElement(document.body);
}
function uiWebview_ScrollTo(idx) {
var scrollTo = document.getElementById("SEARCH WORD" + idx);
if (scrollTo) scrollTo.scrollIntoView();
}
And in my ViewController.swift file, I load the javascript file and perform a search like so:
func highlightAllOccurencesOfString(str:String) -> Int {
let path = Bundle.main.path(forResource: "UIWebViewSearch", ofType: "js")
var jsCode = ""
do{
jsCode = try String(contentsOfFile: path!)
myWebView.stringByEvaluatingJavaScript(from: jsCode)
let startSearch = "uiWebview_HighlightAllOccurencesOfString('\(str)')"
myWebView.stringByEvaluatingJavaScript(from: startSearch)
let result = myWebView.stringByEvaluatingJavaScript(from: "uiWebview_SearchResultCount")!
}catch
{
// do something
}
return Int(result)!
}
This finds and highlights all occurrences of a string within the webview, but I only want the first occurrence highlighted.
For example, with this code when I call:
highlightAllOccurencesOfString(str:"Hello")
All instances of "Hello" gets highlighted in the webview:
Hello Frank, Hello Noah
But I want this result:
Hello Frank, Hello Noah
How do I modify the javascript code to highlight only the first occurrence of a searched string?
UPDATE: I tried JonLuca's answer below but nothing got highlighted.
Any help would be greatly appreciated
The function is operating like a tree, where it checks if the element is a text node, and if not it calls itself on every one of its children.
You have to let the caller know that you found a single element to highlight, and then leave.
Also, the way this searches through it does not guarantee that what you consider is the "first" occurrence of the word will be the first occurence within the document.body tree.
Within the code, have it return true if it found a word to highlight, and false otherwise. That'll prevent it from continuing its search. Modify the js like so:
function uiWebview_HighlightAllOccurencesOfStringForElement(element, keyword) {
if (element) {
if (element.nodeType == 3) { // Text node
var count = 0;
var elementTmp = element;
var value = elementTmp.nodeValue; // Search for keyword in text node
var idx = value.toLowerCase().indexOf(keyword);
if (idx < 0) {
return false;
}
count++;
elementTmp = document.createTextNode(value.substr(idx + keyword.length));
uiWebview_SearchResultCount += count;
var index = uiWebview_SearchResultCount;
var value = element.nodeValue; // Search for keyword in text node
var idx = value.toLowerCase().indexOf(keyword);
if (idx < 0) {
return false;
} // not found, abort
//we create a SPAN element for every parts of matched keywords
var span = document.createElement("span");
var text = document.createTextNode(value.substr(idx, keyword.length));
span.appendChild(text);
span.setAttribute("class", "uiWebviewHighlight");
span.style.backgroundColor = "yellow";
span.style.color = "black";
index--;
span.setAttribute("id", "SEARCH WORD" + (index));
//span.setAttribute("id", "SEARCH WORD"+uiWebview_SearchResultCount);
//element.parentNode.setAttribute("id", "SEARCH WORD"+uiWebview_SearchResultCount);
//uiWebview_SearchResultCount++; // update the counter
text = document.createTextNode(value.substr(idx + keyword.length));
element.deleteData(idx, value.length - idx);
var next = element.nextSibling;
//alert(element.parentNode);
element.parentNode.insertBefore(span, next);
element.parentNode.insertBefore(text, next);
element = text;
return true;
} else if (element.nodeType == 1) { // Element node
if (element.style.display != "none" && element.nodeName.toLowerCase() != 'select') {
for (var i = element.childNodes.length - 1; i >= 0; i--) {
if (uiWebview_HighlightAllOccurencesOfStringForElement(element.childNodes[i], keyword)) {
return true;
}
}
}
}
}
}
This isn't guaranteed to be correct, or that it covers all of your use cases. However, I had it return true on the first occurrence of finding the highlighted word. It should stop executing and not highlight any other words.
Let me know if this works or not - I don't have the ability to test it right now.
How can I store the returned result into a variable without it being changed? I basically want the function to be only called once and be stored in the variable.
Right now the var word changes its value each time I press the button. I want it to get the retired value from the read() function and keep the value the same for validation
Here is my code:
function read() {
// var result gets a random line from a file
var lines = result.split(", ");
var randLineNum = Math.floor(Math.random() * lines.length);
return lines[randLineNum];
}
function Check(strParam){
var guessWord = strParam;
var word = read();
if(guessWord != ""){
if(guessWord !== word){
console.log(word); // I am checking here for the word and it keeps changing
}
}
}
$('#button').on('click', function() {
Check($('#txtbox').val());
});
Try
function read() {
// var result gets a random line from a file
var lines = result.split(", ");
var randLineNum = Math.floor(Math.random() * lines.length);
return lines[randLineNum];
}
var word = read();
function Check(strParam){
var guessWord = strParam;
if(guessWord != ""){
if(guessWord !== word){
// I am checking here for the word and it keeps changing
console.log(word);
}
}
}
$('#button').on('click', function() {
Check($('#txtbox').val());
});
How about this:
var lines = result.split(", ");
var randLineNum = Math.floor(Math.random() * lines.length);
var word = lines[randLineNum];
function Check(strParam){
var guessWord = strParam;
if(guessWord != ""){
if(guessWord !== word){
console.log(word); // I am checking here for the word and it keeps changing
}
}
}
$('#button').on('click', function() {
Check($('#txtbox').val());
});
Evertime you click the Button , the OnClick Function calls read which does what its exactly supposed to do . Choose a random line from the file . You need to cache the result before checking it .
function read() {
// var result gets a random line from a file
var lines = result.split(", ");
var randLineNum = Math.floor(Math.random() * lines.length);
return lines[randLineNum];
}
var word = read();
function Check(strParam){
var guessWord = strParam;
//var word = read();
if(guessWord != ""){
if(guessWord !== word){
console.log(word);
}
}
}
$('#button').on('click', function() {
Check($('#txtbox').val());
});
I have to make a small javascript function that adds a prefix and suffix to a selected text within a textbox.
This is what I have so far:
function AddTags(name, prefix, suffix) {
try
{
var textArea = document.getElementById(name).value;
var i = 0;
var textArray = textArea.split("\n");
if (textArray == null) {
document.getElementById(name).value += prefix + suffix
}
else {
for (i = 0; i < textArray.length; i++) {
textArray[i] = prefix + textArray[i] + suffix;
}
document.getElementById(name).value = textArray.join("\n");
}
}
catch (err) { }
}
Now this function adds the provided prefix and suffix to every line, but I need to find out how to break up my textbox's text in Text before selection, Selected text and Text after selection.
Anybody any experience on this?
EDIT:
TriniBoy's function set me on the right track. I didn't need the whole suggestion.
This is the edited version of my original code:
function AddTags(name, prefix, suffix) {
try
{
var textArea = document.getElementById(name);
var i = 0;
var selStart = textArea.selectionStart;
var selEnd = textArea.selectionEnd;
var textbefore = textArea.value.substring(0, selStart);
var selected = textArea.value.substring(selStart, selEnd);
var textAfter = textArea.value.substring(selEnd);
if (textAfter == "") {
document.getElementById(name).value += prefix + suffix
}
else {
document.getElementById(name).value = textbefore + prefix + selected + suffix + textAfter;
}
}
catch (err) { }
}
Thx TriniBoy, I'll mark your leg-up as answer.
Based on your demo and your explanation, hopefully I got your requirements correct.
See code comments for a break down.
See demo fiddle here
var PreSuffApp = PreSuffApp || {
selText: "",
selStart: 0,
selEnd: 0,
getSelectedText: function (id) {
var text = "",
docSel = document.selection, //For IE
winSel = window.getSelection,
P = PreSuffApp,
textArea = document.getElementById(id);
if (typeof winSel !== "undefined") {
text = winSel().toString(); //Grab the current selected text
if (typeof docSel !== "undefined" && docSel.type === "Text") {
text = docSel.createRange().text; //Grab the current selected text
}
}
P.selStart = textArea.selectionStart; //Get the start of the selection range
P.selEnd = textArea.selectionEnd; //Get the end of the selection range
P.selText = text; //Set the value of the current selected text
},
addTags: function (id, prefix, suffix) {
try {
var textArea = document.getElementById(id),
P = PreSuffApp,
range = P.selEnd - P.selStart; //Used to calculate the lenght of the selection
//Check to see if some valuable text is selected
if (P.selText.trim() !== "") {
textArea.value = textArea.value.splice(P.selStart, range, prefix + P.selText + suffix); //Call the splice method on your text area value
} else {
alert("You've selected a bunch of nothingness");
}
} catch (err) {}
}
};
//Extend the string obj to splice the string from a start character index to an end range, like an array.
String.prototype.splice = function (index, rem, s) {
return (this.slice(0, index) + s + this.slice(index + Math.abs(rem)));
};
I have a code like this
(function($, window, document, undefined) {
$.fn.quicksearch = function (target, opt) {
var timeout, cache, rowcache, jq_results, val = '', e = this, options = $.extend({
delay: 100,
selector: null,
stripeRows: null,
loader: null,
noResults: '',
bind: 'keyup',
onBefore: function () {
return;
},
onAfter: function () {
return;
},
show: function () {
this.style.display = "";
},
hide: function () {
this.style.display = "none";
},
prepareQuery: function (val) {
return val.toLowerCase().split(' ');
},
testQuery: function (query, txt, _row) {
for (var i = 0; i < query.length; i += 1) {
if (txt.indexOf(query[i]) === -1) {
return false;
}
}
return true;
}
}, opt);
this.go = function () {
var i = 0,
noresults = true,
query = options.prepareQuery(val),
val_empty = (val.replace(' ', '').length === 0);
for (var i = 0, len = rowcache.length; i < len; i++) {
if (val_empty || options.testQuery(query, cache[i], rowcache[i])) {
options.show.apply(rowcache[i]);
noresults = false;
} else {
options.hide.apply(rowcache[i]);
}
}
if (noresults) {
this.results(false);
} else {
this.results(true);
this.stripe();
}
this.loader(false);
options.onAfter();
return this;
};
this.stripe = function () {
if (typeof options.stripeRows === "object" && options.stripeRows !== null)
{
var joined = options.stripeRows.join(' ');
var stripeRows_length = options.stripeRows.length;
jq_results.not(':hidden').each(function (i) {
$(this).removeClass(joined).addClass(options.stripeRows[i % stripeRows_length]);
});
}
return this;
};
this.strip_html = function (input) {
var output = input.replace(new RegExp('<[^<]+\>', 'g'), "");
output = $.trim(output.toLowerCase());
return output;
};
this.results = function (bool) {
if (typeof options.noResults === "string" && options.noResults !== "") {
if (bool) {
$(options.noResults).hide();
} else {
$(options.noResults).show();
}
}
return this;
};
this.loader = function (bool) {
if (typeof options.loader === "string" && options.loader !== "") {
(bool) ? $(options.loader).show() : $(options.loader).hide();
}
return this;
};
this.cache = function () {
jq_results = $(target);
if (typeof options.noResults === "string" && options.noResults !== "") {
jq_results = jq_results.not(options.noResults);
}
var t = (typeof options.selector === "string") ? jq_results.find(options.selector) : $(target).not(options.noResults);
cache = t.map(function () {
return e.strip_html(this.innerHTML);
});
rowcache = jq_results.map(function () {
return this;
});
return this.go();
};
this.trigger = function () {
this.loader(true);
options.onBefore();
window.clearTimeout(timeout);
timeout = window.setTimeout(function () {
e.go();
}, options.delay);
return this;
};
this.cache();
this.results(true);
this.stripe();
this.loader(false);
return this.each(function () {
$(this).bind(options.bind, function () {
val = $(this).val();
e.trigger();
});
});
};
}(jQuery, this, document));
I try to figure out where and how I can make a split/add space between numbers and letters. Cause some people type for example "ip1500" and the script cant match the input with an element that is like "ip 1500". My problem ist that Im a js beginner.
I was trying and trying but i cant get it work. I also tried this
I found this spot and I think it can be done here where the everything get splitted by an " " (space):
prepareQuery: function (val) {
return val.toLowerCase().split(' ');
},
Would be very nice if somebody can help me.
If you want "123abc345def" to "123 abc 345 def". The replace function may help. The code is like this.
var str = "123abc345def";
str = str.replace(/(\d+)/g, function (_, num){
console.log(num);
return ' ' + num + ' ';
});
str = str.trim();
The code you linked didn't work mainly because it's using a different programming language to javascript. In theory, it should work, but javascript does not support regular expression lookbehinds (at this present time)..
Instead, I have re-wrote that fragment of code:
prepareQuery: function (val) {
function isNotLetter(a){
return (/[0-9-_ ]/.test(a));
}
var val=val.toLowerCase().split("");
var tempArray=val.join("").split("");
var currentIndex=1;
for (var i=0;i<val.length-1;i++){
if (isNotLetter(val[i]) !== isNotLetter(val[i+1])){
tempArray.splice(i+currentIndex, 0, " ");
currentIndex++;
}
}
return tempArray.join("");
}
Since you're new to javascript, I'm going to explain what it does.
It declares a function in prepareQuery to check whether or not a string contains a letter [this can be moved somewhere else]
It then splits val into an array and copies the content of val into tempArray
An index is declared (explained later)
A loop is made, which goes through every single character in val
The if statement detects whether or not the current character (val[i] as set by the loop) is the same as the character next to it (val[i+1]).
IF either one are different to the other (ie the current character is a letter while the next isn't) then a space is added to the tempArray at that "index"
The index is incremented and used as an offset in #6
The loop finishes, joins the "array" into a string and outputs the result.
DEMO:
http://jsbin.com/ebitus/1/edit
(JSFiddle was down....)
EDIT:
Sorry, but I completely misinterpreted your question... You failed to mention that you were using "quicksearch" and jQuery. In that case I'm assuming that you have a list of elements that have names and you want to search through them with the plugin...
A much easier way to match the user's query (if there is no space) is to strip the space from the search table along with the query itself - though original reverse method will work (just not as efficiently) [aka: expanding the user's query]
In this case, stripping the space from both the search table and user input would be a better method
prepareQuery: function (val) {
return val.toLowerCase().replace(/ /ig,'').split(" ");
},
testQuery: function (query, txt, _row) {
txt=txt.toLowerCase().replace(/ /ig,'');
for (var i = 0; i < query.length; i += 1) {
if (txt.indexOf(query[i]) === -1) {
return false;
}
}
return true;
}
DEMO:
http://jsfiddle.net/q9k9Y/3/
Edit 2:
It seems like your real intent is to create a fully functioning search feature on your website, not to just add spaces between letters and numbers. With this, I suggest using Quicksilver. I would love to work out an algorithm to extend quickSearcher but at the current time I cannot (timezones). Instead, I suggest using Quicksilver
http://jsbin.com/oruhet/12/