How do I focus another document with photoshop javascript scripting? - javascript

This following script almost does what I need. What I'm trying to do is go through the opened documents, 139 of them, and save them as jpeg. However what it's lacking is moving from one opened document to the other, so it saved the same image over 139 times. I assumed doc.close() would close the opened document and give a new one focus, but it doesn't.
Here's the code:
var destination = "C:/Documents and Settings/Administrator/My Documents/small images"
for(var i = 0; i < 5; i++)
{
doc = documents[i];
name_ = doc.name.substring(0, doc.name.indexOf('.'))
saveForWebPNG(destination, name_);
doc.close();
}
function saveForWebPNG(outputFolderStr, filename)
{
var opts, file;
opts = new ExportOptionsSaveForWeb();
opts.format = SaveDocumentType.JPEG;
opts.quality = 60;
if (filename.length > 27) {
file = new File(outputFolderStr + "/temp.jpg");
activeDocument.exportDocument(file, ExportType.SAVEFORWEB, opts);
file.rename(filename + ".jpg");
}
else {
file = new File(outputFolderStr + "/" + filename + ".jpg");
activeDocument.exportDocument(file, ExportType.SAVEFORWEB, opts);
}
}

According to the Adobe Photoshop CS2 JavaScript Scripting Guide it looks like you need to assign to the Application.activeDocument property to make that document the currently selected one for any actions. This makes sense since you're using that property in the saveForWebPNG function without explicitly activating the document in the iterator in the first block. It might be as simple as the following change:
for (var i = 0; i < 5; i++) {
var doc = documents[i];
app.activeDocument = doc; // Select that document.
var name = doc.name.substring(0, doc.name.indexOf('.'))
saveForWebPNG(destination, name);
doc.close();
}
However, I don't have a copy of Photoshop and haven't verified this solution.

I know this is a lot late, but I just came across this question and feel like I might have a solution that someone down the road may be able to use. So here goes.
One solution is to adjust the for loop. Let's say there are 5 documents open. app.documents.length would be 5. When you close one, the length is now 4, then 3, etc. i is counting up as app.documents.length is decreasing. When i = 3, app.documents.length = 2. I think that iterating backwards would do the trick.
for (i = app.documents.length - 1; i >= 0; i--) { ... }

The answer from #maerics is correct.
However, if you don't know the index of the given document you can reference it by name like in this example:
// Assume 'hello.psd' and 'world.psd' are in same folder at script.
// Open the first file. It is the active document.
var scriptFile = new File($.fileName)
var scriptPath = scriptFile.parent.fsName
var file1obj = File(scriptPath + '/hello.psd')
var file1 = open(file1obj)
// Open the second file. The second file is now the active document.
var file2obj = File(scriptPath + '/world.psd')
var file2 = open(file2obj)
// Make the first file the active document
app.activeDocument = file1
// Make the second file the active document
app.activeDocument = file2

+1 for jbiz's solution. I couldn't figure out why my loop wouldn't complete.
for (i=app.documents.length-1; i >= 0; i--) {
doc = app.documents[i];
app.activeDocument = doc;
...
do whatever you need, save & close
...
}
app.documents.length gets shorter with every iteration though the loop if you close the document when you're done.

You want to close the current selected (or active) document:
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);

Related

How to paste clipboard into a channel using a photoshop script?

I'm trying to create a image RGB or CMYK using separate greyscale documents for each channel.
My current code copy all from greyscale document and paste in a new document creating a new layer for eachone. I don't want that, I want copy from each document and paste into eachone channels new document, same as manual way, selecting each channel and clicking Edit->paste->paste especial->paste in place.
// Loop to copy each separate channel into a new merged document
for (var doc_index = 0; doc_index < 3; doc_index++ ){
//Switch documents
app.activeDocument = split_channels[0];
// copy active layer from separate channel document
app.activeDocument.selection.selectAll();
app.activeDocument.selection.copy();
// close document whithout save
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
//select activeDocument
app.activeDocument = mergedDoc;
// reference to whole channels
//var channelIndex = doc_index;
var myChannels = app.activeDocument.channels;
// the channel must be visible to paste greyscale layer
myChannels[doc_index].visible= true;
// turn off the rest of channels
for (var secondaryIndex = 0; secondaryIndex < myChannels.length; secondaryIndex++) {
if (doc_index != secondaryIndex) {
myChannels[secondaryIndex].visible= false
}
}
//paste layer for each new document channel (no works correctly)
app.activeDocument.paste();
//reset the channel count after copy and close each document
var split_channels = app.documents;
}
thanks a lot for any suggestion
When copying the layer contents, you do want the layer contents to be visible so they are captured by the selection when you copy. When pasting, however, the destination of the selection is determined on the active layer/channel. Note that the document.activeChannels member requires an array when assigning to it, even if you are only wanting a single channel.
Also, be mindful of changing the underlying array while iterating over it (aka closing documents before exiting the for-loop). In my code below, I'm specifically iterating over a reference array, instead of the actual app.documents collection, to ensure the length of my array doesn't change when a document is closed.
For the sake of brevity, the following code assumes all documents are of equal dimensions/color modes/resolutions, the three source documents are flattened, and there's at least one pixel on each opposite extremity of the canvas to ensure the size/position is the same after pasting.
const colorMode = NewDocumentMode.RGB;
const numChannels = 3;
var srcDocs = new Array();
if (app.documents.length == numChannels) {
for (var i = 0; i < app.documents.length; i++)
srcDocs.push(app.documents[i]);
} else throw new Error("Channel merging for " + colorMode + " requires " + numChannels + " source documents.");
var doc = app.activeDocument;
var destDoc = app.documents.add(doc.width, doc.height, doc.resolution, "mergedDoc", colorMode);
var destChannels = new Array();
for (var i = 0; i < numChannels; i++)
destChannels.push(destDoc.channels[i]);
for (var i in srcDocs) {
// copy
app.activeDocument = srcDocs[i];
app.activeDocument.selection.selectAll();
app.activeDocument.selection.copy();
app.activeDocument.selection.deselect();
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
// pasta
app.activeDocument = destDoc;
var targetChannels = [destChannels[i]];
app.activeDocument.activeChannels = targetChannels;
app.activeDocument.paste();
}
app.activeDocument.selection.deselect();
app.activeDocument.activeChannels = destChannels;

JavaScript FileReader and Dynamic Tables

I am trying to upload a file and read its contents and then output the contents to a table. The information needs to be place on a new row whenever there is a > char in the string.
I am having a bit of an issue wrapping my head around how I can create a new 'tr' and then add data to the a cell 'td' in that row.
I am stuck on the <tr> and <td> and adding them dynamically with the contents from the file. I am sure I can use regex to look for the > char but that isn't really what I need help with. I am struggling with how I take the information after the > char and add it to a new row in the table.
UPDATE: Ok, so I am still not fully functional on what I am trying to do. I am uploading the file, reading it, and storing the information as an object. However, I can only do this for one instance. When I upload a text file there will be multiple DNA sequences in the file. Each sequence will have a sequence_id like this:
9013e1
ACAAGATGCCATTGTCCCCCGGCCTCCTGCTGCTGCTGCTCTCCGGGGCCACGGCCACCGCTGCCCTGCC
CCTGGAGGGTGGCCCCACCGGCCGAGACAGCGAGCATATGCAGGAAGCGGCAGGAATAAGGAAAAGCAGC
CTCCTGACTTTCCTCGCTTGGTGGTTTGAGTGGACCTCCCAGGCCAGTGCCGGGCCCCTCATAGGAGAGG
AAGCTCGGGAGGTGGCCAGGCGGCAGGAAGGCGCACCCCCCCAGCAATCCGCGCGCCGGGACAGAATGCC
CTGCAGGAACTTCTTCTGGAAGACCTTCTCCTCCTGCAAATAAAACCTCACCCATGAATGCTCACGCAAG
TTTAATTACAGACCTGAA
So, I am trying to read the file, find all sequence ID's and then sequences and I want an editable leading and trailing trim like so:
var objArray = [
{
'id': '>9013e1',
'sequence': 'ACAAGATGCCATTGTCCCCCGGCCT...',
'lead_trim': //get the value from a input from the user,
'trail_trim': //same as above
},
{
//another obj like above
}
]
The sequence also needs to have a line break inserted after every 60 characters. Once I have processed the data in the text file correctly I then need to output the data to a table like I stated in my original post. The problem I am having is I am getting stuck on only being able to store information for one obj in my objArray.
Here is a look at my code...
function scanForSequences(event) {
//Get the file from HTML input tag
var file = event.target.files[0];
var output = document.getElementById('table');
if(file) {
var sequenceArray = [];
var objArray = [];
var obj = {};
var str = '';
var subStr = '';
//Create a new file reader
var reader = new FileReader();
//When the file reader loads
reader.onload = function(evt) {
//Add the contents of file to variable contents
var contentsByLine = evt.target.result.split('\n');
//Alert user the file upload has succeeded
alert('File ' + file.name + ' has been uploaded!');
for(var i = 0; i < contentsByLine.length; i++){
if(contentsByLine[i].charAt(i) == '>'){
obj['id'] = contentsByLine[i];
}else{
sequenceArray.push(contentsByLine[i]);
str = sequenceArray.toString();
subStr += str.substring(0, 60) + '\n';
str = str.substring(60);
obj['sequence'] = subStr;
obj['lead_trim'] = 0;
obj['trail_trim'] = 0;
}
objArray.push(obj);
console.log(objArray);
}
}
reader.readAsText(file);
} else {
alert('Failed to upload file!');
}
console.log(obj);
}
document.getElementById('fileItem').addEventListener('change', scanForSequences, false);
Please find my proposed solution in the fiddle below:
https://jsfiddle.net/w6jbuqfy/
Here's an explanation of what's going on:
First we have a input of type file:
<input id="input" type="file">
We then attach an event listener to it to execute a function once a user has selected a file
var inputElement = document.getElementById("input");
inputElement.addEventListener("change", handleFile, false);
Inside the handleFile function, we use a FileReader to read the file.
var fileList = this.files;
var file = fileList[0];
var fr = new FileReader();
fr.readAsText(file);
Now fileReaders are asynchronous in nature, here i've got a simple interval that checks on the status of the filereader every 100ms.
var checkReadyId = setInterval(function(){
if(fr.readyState === 2){ //done
window.clearInterval(checkReadyId);
addFileDataToResults(fr.result);
} else{
console.log('not done yet');
}
}, 100);
FileReaders are done reading when their readyState is 2. So we check for that and once we are done, we can access the result from FileReader.result. As we read this as a text earlier above, we'll get a string back. We then pass this to our addFileDataToResults function.
function addFileDataToResults(fileAsString){
var resultsDiv = document.getElementById('results');
var tr = document.createElement('tr');
var td = document.createElement('td');
var linesInFile = fileAsString.split('\n');
console.log(linesInFile);
td.textContent = linesInFile[0];
tr.appendChild(td);
resultsDiv.appendChild(tr);
}
What is happening here is that we grab the resultsDiv which is a real node in our HTML. We then use createElement which creates virtual nodes and put data into them. IN this case, we are simply putting the text of the first line into our file. Once we are done creating this virtual nodes, we use appendChild to our real node which turns the virtual node into a real node and you can see it in the html.
Hope this helps
:)

Extracting img urls from webpage using Google Apps Script

This is an Apps Script that goes through a webpage and collects img urls that are inside some div of a special class.
function getIMGs(url){
var url = 'url'
var result = UrlFetchApp.fetch(url);
if (result.getResponseCode() == 200) {
var doc = Xml.parse(result, true);
var bodyHtml = doc.html.body.toXmlString();
var doc = XmlService.parse(bodyHtml);
var html = doc.getRootElement();
var thumbs = getElementsByClassName(html, 'thumb');
var sheet = SpreadsheetApp.getActiveSheet();
for (i in Thumbs) {
var output = '';
var linksInMenu = getElementsByTagName(thumbs[i], 'img');
for(i in linksInMenu) {
output += XmlService.getRawFormat().format(linksInMenu[i]);
}
var linkRegExp = /data-src="(.*?)"/;
var dataSrc = linkRegExp.exec(output);
sheet.appendRow([dataSrc[1]]);
}
}
So first the code gets the html, and uses an auxiliary function to get certain elements, which look like this:
<div class="thumb"><div class="loader"><span class="icon-uniE611"></span></div><img src="//xxx" data-src="https://xxx/8491a83b1cacc2401907997b5b93e433c03c91f.JPG" data-target="#image-slider" data-slide-to="0"></div>
Then the code gets the img elements, and finally extracts the data-src address via RegExp.
While this kinda works, I have a problem:
1) After 9 loops it crashes, on the appendRow line, as the last 4 Thumbs elements don't have data-src, hence what i'm trying to write into the spreadsheet is null.
Any solution for this? I have fixed it for the moment by just doing 9 iterations only of the For loop, but this is far from optimal, as it's not automated and required me to go through the page to count the elements with data-src.
Also, any suggestion of a more elegant solution will be appreciated! I will be really grateful for any helping hand!
Cheers

How to change the URL of an image tag on every click on the image using JavaScript?

I have an element displaying an image on an HTML page. This element's source is one of many different images in a JavaScript array.
I already have a script for looping through the images, creating a slideshow effect, but now I want to manually flick through the images with buttons.
This is my code so far, but I get no response when clicking the button.
function nextup()
{
imgs = [];
imgs[0] = "/snakelane/assets/images/thumb/_1.jpg"; imgs[10] = "/snakelane/assets/images/thumb/_19.jpg";
imgs[1] = "/snakelane/assets/images/thumb/_2.jpg"; imgs[11] = "/snakelane/assets/images/thumb/_20.jpg";
imgs[2] = "/snakelane/assets/images/thumb/_3.jpg"; imgs[12] = "/snakelane/assets/images/thumb/_21.jpg";
imgs[3] = "/snakelane/assets/images/thumb/_4.jpg"; imgs[13] = "/snakelane/assets/images/thumb/_22.jpg";
imgs[4] = "/snakelane/assets/images/thumb/_5.jpg"; imgs[14] = "/snakelane/assets/images/thumb/_23.jpg";
imgs[5] = "/snakelane/assets/images/thumb/_6.jpg"; imgs[15] = "/snakelane/assets/images/thumb/_24.jpg";
imgs[6] = "/snakelane/assets/images/thumb/_7.jpg"; imgs[16] = "/snakelane/assets/images/thumb/_25.jpg";
imgs[7] = "/snakelane/assets/images/thumb/_8.jpg"; imgs[17] = "/snakelane/assets/images/thumb/_26.jpg";
imgs[8] = "/snakelane/assets/images/thumb/_9.jpg"; imgs[18] = "/snakelane/assets/images/thumb/_27.jpg";
imgs[9] = "/snakelane/assets/images/thumb/_32.jpg"; imgs[19] = "/snakelane/assets/images/thumb/_28.jpg";
var pic = document.getElementById("picbox");
for(i =0; i < imgs.length; i++) {
var current = indexOf(pic.src);
var next = Math.round(current + 1);
pic.src = imgs[next];
}
}
Can anyone tell me what's wrong with my code or suggest a better way?
Multiple problems in the approach you had used. Have a look at the modified function below. Let me know if you need explanation with anything.
The following code will use an array containing image URLs and later assign in a sequential manner to an img tag on click. Enjoy!
Here you can try to see the output.
function nextup(){
//Initialized img array with 10 images, you can do it any way you want to.
var imgs = [];
for(i=0;i<10;i++){
imgs[i] = "http://lorempixel.com/output/cats-q-c-100-100-"+(i+1)+".jpg";
}
//Fetch the pic DOM element by ID
var pic = document.getElementById("picbox");
//Know what is position of currently assigned image in array.
var current = imgs.indexOf(pic.src);
var next = 0;
//Handle case if no image is present, the initial case.
if(current!=-1){
next = (current + 1)%(imgs.length);
}
//Assign the next src
pic.src = imgs[next];
}
//Scoped outside to call the function first time on load.
nextup();
I found the following problems in your code:
You tried to use indexOf without specifying the array in which the search has to be performed. Imagine s school principal asking someone to go find if John is present in the classroom without specifying a specific classroom.
For iterating through array you used a next variable which could have been a good idea if you needed an endless loop. But here since we are limited to 10 or 20 images we need to make sure that if the currently selected image is the last one, we find that next goes to 21 (assuming a total of 20 images.) and this would try to access a variable out of bounds.
Hence I've used the mod operator %.
For reference in JavaScript, 5%10 would return 5 , 15%10 would return 5 and so on. Read more about the mod operator HERE.

Javascript for google image ripping broke with update

I grabbed a few small scripts and threw them together to take google's new image layout and turn back into the old one, then take the images and replace them with the full size versions. Worked great until about last week. Not sure what changed on the server side.
(function() {
// Get list of all anchor tags that have an href attribute containing the start and stop key strings.
var fullImgUrls = selectNodes(document, document.body, "//a[contains(#href,'/imgres?imgurl\x3d')][contains(#href,'\x26imgrefurl=')]");
//clear existing markup
var imgContent = document.getElementById('ImgContent');
imgContent.innerHTML = "";
for(var x=1; x<=fullImgUrls.length; x++) {
//reverse X to show images in correct order using .insertBefore imgContent.nextSibling
var reversedX = (fullImgUrls.length) - x;
// get url using regexp
var fullUrl = fullImgUrls[reversedX].href.match( /\/imgres\?imgurl\=(.*?)\&imgrefurl\=(.*?)\&usg/ );
// if url was fetched, create img with fullUrl src
if(fullUrl) {
newLink = document.createElement('a');
imgContent.parentNode.insertBefore(newLink , imgContent.nextSibling);
newLink.href = unescape(fullUrl[2]);
newElement = document.createElement('img');
newLink.appendChild(newElement);
newElement.src = decodeURI(fullUrl[1]);
newElement.border = 0;
newElement.title = fullUrl[2];
}
}
function selectNodes(document, context, xpath) {
var nodes = document.evaluate(xpath, context, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
var result = [];
for (var x=0; x<nodes.snapshotLength; x++) {
result.push(nodes.snapshotItem(x));
}
return result;
}
})();
Google changed the 'ImgContent' id for the image table holder to something slightly more obscure. A quick change had everything working again. I made a simple problem complicated by looking past the easy stuff. Thanks to darvids0n for the enabling, he ultimately pointed out what I was missing.
the script is not going to work as said by bobby .
try this grease monkey script from user script repository.
rip Google image search :- http://userscripts.org/scripts/show/111342

Categories

Resources