I've created a very basic script that allows the user to replace a specific font in all the document (even LayerSets) by another one.
A practice example: I want to substitute all Arial-Bold by Arial-Italic, but some of the TextLayers have Arial-Bold and Arial-Regular inside the same Layer, how can make that the script only changes the Arial-Bold part of the TextLayer and not the whole layer?
Code I'm currently using:
var inFont = prompt("write inFont","Write inFont");
var outFont = prompt("write outFont","Write outFont");
app.preferences.typeUnits = TypeUnits.PIXELS;
var doc = app.activeDocument;
function changeFonts(target){
var layers = target.layers;
for(var i=0;i<layers.length;i++){
if(layers[i].typename == "LayerSet"){
changeFonts(layers[i]);
} else {
if((layers[i].kind == LayerKind.TEXT) && (layers[i].textItem.font == inFont)) {
layers[i].textItem.font = outFont;
};
};
};
};
changeFonts(doc);
I would assume you would have to crawl through the text and look at each individual character. Here is an article that that talks about formatting specific character ranges. Formatting text ranges.
You could use something like this to loop through the text one character at a time, check the formatting and change it if needed. I can't think of any other way to approach this.
Related
I'd like to implement transient highlighting in a Quill document.
For example, imagine a SEARCH button where the user can highlight all instances of a keyword in the current document, by setting the text color for matching text ranges.
I can do that today, with something like this:
var keyword = "hello";
var text = quill.getText();
var matchIndex = text.indexOf(keyword);
while (matchIndex >= 0) {
quill.formatText(matchIndex, keyword.length, { "color" : "#f00" });
matchIndex = text.indexOf(keyword, matchIndex + keyword.length);
}
But I don't want the resultant deltas to be incorporated into the official change history of this document. These are just transient highlights, and I'd like to be able to clear them all away with something like this...
quill.clearTransientFormats();
I'd even like to give the user the choice of leaving the transient highlights enabled, while they continue to edit and modify the document with their own destructive changes.
Essentially, I want to have two different kinds of formatting:
Destructive formatting is always recorded in the sequence of deltas persisted in the document history.
Transient formatting is ignored in the document history, since it only applies to the current view.
What's the best way to implement something like this?
I would recommend just post-processing the delta before saving. It can be achieved fairly easily with compose:
var length = aboutToBeStored.length();
var toStore = aboutToBeStored.compose(new Delta().retain(0, length, { color: null }));
I am trying to implement a rudimentary site search for a page of images. The search function should go through each element in a specific class looking for a word match in the image's alt text.
I think my issue is with binding the function to a form submit but I can't seem to figure out where I went wrong.
I have tried this with jQuery (fiddle:http://jsfiddle.net/u2oewez4/)
$(document).ready(function(){
$("#search-form").click(function() {
var searchQuery = "Text";
$.each('.single-image', function(){
$(this).children("img").attr("alt"):contains(searchQuery).hide("slow");
});
});
});
As well as with JavaScript (fiddle:http://jsfiddle.net/m3LkxL1c/)
function submitSearch(){
// create regex with query
var searchQuery = new RegExp(document.getElementById('search-input').value);
// create array of content to look for query in
var content = document.getElementsByClassName("single-image");
// create an array to put the results to hide in
var hideResults = [];
var imagesToHide = document.getElementsByClassName("single-image-hide");
// get the current display value
var displaySetting = imagesToHide.style.display;
for (i=0; i<content.length; i++) {
if (! searchQuery.test(content[i].firstChild.firstChild.nodeValue)) {
// if the query not found for this result in query
hideResults.push(content[i]);
// push to the hideResults array
content[i].className = "single-image-hide";
// change class name so CSS can take care of hiding element
document.getElementById("form-success").style.display = 'inline-block';
alert(searchQuery); // for debugging
return false; // results will not stick without this?
}
}
// set display to hidden
if(displaySetting == 'inline-block'){
imagesToHide.style.display = 'none'; // map is visible, so hide it
}
else{
imagesToHide.style.display = 'inline-block'; // map is hidden so show it
}
}
FYI I have built the JQuery off a few StackOverflow threads, so I've definitely tried my best to find a similar example. (Similar functions: here, and here)
Okay, various bug fixes, most of which I noted in the comments already because I wanted to make sure not to miss any. You need to read the details for in the jQuery documentation much more closely. That'll fix a lot of the problems you're having, like using the wrong each function. Other things will come with time. Keep studying, and READ THE DOCUMENTATION.
$("#search-form").click(function() {
//this gets the val for the search box, then puts the Imgs in a variable so we don't have to use the selector multiple times. Selectors are expensive time-wise, stored variables are not.
var searchQuery = $("#search-text").val(),
singleImgs = $('.single-image');
//you have to show the images for each new iteration, or they'll remain hidden
singleImgs.show();
singleImgs.each(function(){
//get alt attribute
var thisAlt = $(this).find("img").attr("alt");
//if thisAlt does not contains searchQuery (use > instead of === for does)
if(thisAlt.indexOf(searchQuery) === -1){
$(this).hide();
}
});
});
working fiddle
i'm trying to live edit a text box value so that the result will be split every two character,
adding a column and starting from some default character.
what i have till now is this code, that obviously doesn't work:
$('#textboxtext').keyup(function (){
var text = $("#textboxtext").val();
//$(text).attr('maxlength', '12');
var splitted = text.match(/.{2}|.{1,2}/g);
var result = ("B8:27:EB:" + splitted.join(':'));
});
i need the live split and the default character inside the textbox but i really don't know where to start...
From your code, it seems like you're trying to create a text box that has some very specific behavior. It looks like it needs to format its value in such a way that it always begins with certain 'prefix' of B8:27:EB:, and every subsequent pair of characters is is separated by a :. This is actually a very complex behavior and you have to consider a number of different interactions (e.g. what happens when the user attempts to delete or modify the prefix). I usually try to avoid such complex controls if possible, however here is a quick implementation:
$('#textboxtext').keyup(function (e){
var prefix = "B8:27:EB:",
text = $(this).val(),
splitted, result;
if (text.indexOf(prefix) == 0)
text = text.substr(9);
else if (prefix.indexOf(text) == 0)
text = "";
text = text.replace(/:/g, '');
splitted = text.match(/.{1,2}/g) || [];
result = prefix + splitted.join(':');
$(this).val(result);
});
Demonstration
Type inside the text box and see what happens. Also note, there are all kinds of interaction that this implementation doesn't account for (e.g. right-clicking and pasting into the text box), but it's a start.
Considering features like EditArea's and CodeMirror's autocomplete, I was wondering if, like Dreamweaver, there is a way to detect if the last word you entered is in a certain list then provide the same kind of suggestion box but with the function's arguments. I imagine you would use a regular expression on the entire field or possibly split() the whole thing (or the current line) then use the length attribute of the array to find the last bit of text, then do something involving an indexOf-like operation; however, this seems like it would get a bit resource-intensive. It almost looks like I've answered my own question, but it always helps to fully explain one's quandary, especially publicly. There's gotta be a better solution than mine. I appreciate any input. Thank you.
Put the list of words to match in an object, have the text or options to display as the value. Then on keyup or keypress you can get the last word of the text area using a function like:
function showLastWord(id){
var el = document.getElementById(id);
var lastWord = el.value.match(/\w+$/);
return lastWord? lastWord[0] : '';
}
Then check if the word is in the list and do stuff appropriately.
Edit
A small example is:
<textarea onkeyup="showHelp(this);"></textarea>
<script>
var getLastWord = (function() {
re = /\w+$/;
return function (s){
var lastWord = s.match(re);
return lastWord? lastWord[0] : '';
}
}());
var keyWords = {foo:'foo was typed',bar:'bar was typed'};
function showHelp(el) {
var lastWord = getLastWord(el.value);
// Check for matching own property of keyWords
if (keyWords.hasOwnProperty(lastWord)) {
// Do stuff
console.log(keyWords[lastWord]);
}
}
The Problem
I am trying to figure out the offset of a selection from a particular node with javascript.
Say I have the following HTML
<p>Hi there. This <strong>is blowing my mind</strong> with difficulty.</p>
If I select from blowing to difficulty, it gives me the offset from the #text node inside of the <strong>. I need the string offset from the <p>'s innerHTML and the length of the selection. In this case, the offset would be 26 and the length would be 40.
My first thought was to do something with string offsets, etc. but you could easily have something like
<p> Hi there. This <strong>is awesome</strong>. For real. It <strong>is awesome</strong>.</p>
which would break that method because there are identical nodes. I also need the option to throw out nodes. Say I have something like this
<p>Hi there. This <strong>is blowing my mind</strong> with difficulty.</p>
I want to throw out an elements with rel="inserted" when I do the calculation. I still want 26 and 40 as the result.
What I'm looking for
The solution needs to be recursive. If there was a <span> with a <strong> in it, it would still need to traverse to the <p>.
The solution needs to remove the length of any element with rel="inserted". The contents are important, but the tags themselves are not. All other tags are important. I'd strongly prefer not to remove any elements from the DOM when I do all of this.
I am using document.getSelection() to get the selection object. This solution only has to work in WebKit. jQuery is an option, but I'd prefer to it without it if possible.
Any ideas would be greatly appreciated.
I have no control over the HTML I doing all of this on.
I think I solved my issue. I ended not calculating the offset like I originally planned. I am storing the "path" from the chunk (aka <p>). Here is the code:
function isChunk(node) {
if (node == undefined || node == null) {
return false;
}
return node.nodeName == "P";
}
function pathToChunk(node) {
var components = new Array();
// While the last component isn't a chunk
var found = false;
while (found == false) {
var childNodes = node.parentNode.childNodes;
var children = new Array(childNodes.length);
for (var i = 0; i < childNodes.length; i++) {
children[i] = childNodes[i];
}
components.unshift(children.indexOf(node));
if (isChunk(node.parentNode) == true) {
found = true
} else {
node = node.parentNode;
}
}
return components.join("/");
}
function nodeAtPathFromChunk(chunk, path) {
var components = path.split("/");
var node = chunk;
for (i in components) {
var component = components[i];
node = node.childNodes[component];
}
return node;
}
With all of that, you can do something like this:
var p = document.getElementsByTagName('p')[0];
var piece = nodeAtPathFromChunk(p, "1/0"); // returns desired node
var path = pathToChunk(piece); // returns "1/0"
Now I just need to expand all of that to support the beginning and the end of a selection. This is a great building block though.
What does this offset actually mean? An offset within the innerHTML of an element is going to be extremely fragile: any insertion of a new node or change to an attribute of an element preceding the point in the document the offset represents is going to make that offset invalid.
I strongly recommend using the browser's built-in support for this in the form of DOM Range. You can get hold of a range representing the current selection as follows:
var range = window.getSelection().getRangeAt(0);
If you're going to be manipulating the DOM based on this offset that you want, you're best off doing so using nodes instead of string representations of those nodes.
you can use the following java script code:
var text = window.getSelection();
var start = text.anchorOffset;
alert(start);
var end = text.focusOffset - text.anchorOffset;
alert(end);
Just check if your selected element is a paragraph, and if not use something like Prototype's Element.up() method to select the first paragraph parent.
For example:
if(selected_element.nodeName != 'P') {
parent_paragraph = $(selected_element).up('p');
}
Then just find the difference between the parent_paragraph's text offset and your selected_element's text offset.
Maybe you could use the jQuery selectors to ignore the rel="inserted"?
$('a[rel!=inserted]').doSomething();
http://api.jquery.com/attribute-not-equal-selector/
What code are you using now to select from blowing to difficulty?