JSX duplicate layer Adobe Photoshop - javascript

I have a problem with duplicating layers from one document to another. I have this code (.jsx script inside my Photoshop document)
var docRef = app.activeDocument;
app.activeDocument.selection.selectAll();
var calcWidth = app.activeDocument.selection.bounds[2] -app.activeDocument.selection.bounds[0];
var calcHeight = app.activeDocument.selection.bounds[3] - app.activeDocument.selection.bounds[1];
var docResolution = app.activeDocument.resolution;
var document = app.documents.add(calcWidth, calcHeight, docResolution);
app.activeDocument = docRef;
try {
dupObj.artLayers[i].duplicate(document, ElementPlacement.INSIDE);
}
catch(e) {
alert(e)
}
But I am still receiving an error
Error: You can only duplicate layers from the frontmost document.
Have you any ideas how to make it work?

The reason you're getting an error is dupObj is never defined. I think you mean to use docRef, the reference to your source document in line 1. This seems to work fine now:
var docRef = app.activeDocument;
app.activeDocument.selection.selectAll();
var calcWidth = app.activeDocument.selection.bounds[2] -app.activeDocument.selection.bounds[0];
var calcHeight = app.activeDocument.selection.bounds[3] - app.activeDocument.selection.bounds[1];
var docResolution = app.activeDocument.resolution;
var document = app.documents.add(calcWidth, calcHeight, docResolution);
app.activeDocument = docRef;
try {
docRef.artLayers[i].duplicate(document, ElementPlacement.INSIDE); // ** changed to docRef **
}
catch(e) {
alert(e)
}
That being said there might be a few hidden bugs in there you should look at. In this line:
docRef.artLayers[i].duplicate(document, ElementPlacement.INSIDE);
i is never defined, and apparently defaults to 0 without throwing an error. The result is you will only ever duplicate the first layer in the artLayers array.
Also, since you are selecting the entire document using app.activeDocument.selection.selectAll(); there is no need to calculate the size of the selection. It will always be the same size as the original document. You could just use docRef.width and docRef.height as the width and height for the new document. Besides, when you duplicate a layer it will copy the whole layer regardless of the selection, as far as I know.
If you just want to make a new document the same size as the layer you are duplicating try using artLayers[i].bounds instead of selection.bounds

You're not calling the active document: You need to call a reference to the active document and the one your using - hence the error.
var docRef = app.activeDocument;
docRef.selection.selectAll();
var calcWidth = docRef.selection.bounds[2] -app.activeDocument.selection.bounds[0];
var calcHeight = docRef.selection.bounds[3] - app.activeDocument.selection.bounds[1];
var docResolution = docRef.resolution;
var document = app.documents.add(calcWidth, calcHeight, docResolution);
app.activeDocument = docRef;
try {
dupObj.artLayers[i].duplicate(document, ElementPlacement.INSIDE);
}
catch(e) {
alert(e)
}
I've not used dupObj before as I use CS and script listener code for duplicating documents
And I've not checked the code, but give it a go.

The problem is that you're trying to use a variable called document which is reserved in JS.
As Sergey pointed out, document is (amazingly) not a reserved word in JSX because Adobe JSX is not 'regular' JSX
Although it doesn't address the exact syntax error I'll leave this here because it's a quick way to solve the overall problem of copying layers between documents.
// Grab docs
const doc1 = app.activeDocument
const doc2 = app.documents.add(100, 100)
const outputLayer = doc1.layers[0]
const inputLayer = doc2.layers[0]
inputLayer.duplicate(outputLayer, ElementPlacement.INSIDE)

Related

JavaScript works when setTimeout() is used, but it isn't working when document.eventListener('DOMContentLoaded', x) is used on a WordPress page. Why?

I have a few lines of JavaScript code that pick up heading texts from separate sections and place them into their respective input fields. They are also executed on single pages using wp_enqueue_script.
It works absolutely fine when setTimeout() is used:
function passengerElevator() {
var getProductName = document.querySelectorAll('[data-id="6657316"]');
getProductName.forEach(function(item) {
var productName = item.querySelector('.lift');
var inputArea = item.querySelector('input[name=product]');
inputArea.value = productName.innerText;
});
var getProductName = document.querySelectorAll('[data-id="e9c06d5"]');
getProductName.forEach(function(item) {
var productName = item.querySelector('.lift');
var inputArea = item.querySelector('input[name=product]');
inputArea.value = productName.innerText;
});
setTimeout(function() { passengerElevator() },3000);
However, there is problem of page size (some pages have more than 10 input fields) and I don't want to set an astronomically high ms to delay the script. So I decided to fire it on DOMContentLoaded:
document.addEventListener("DOMContentLoaded", passengerElevator);
function passengerElevator() {
var getProductName = document.querySelectorAll('[data-id="6657316"]');
getProductName.forEach(function(item) {
var productName = item.querySelector('.lift'); // heading text (ex:Panoramic Lift)
var inputArea = item.querySelector('input[name=product]');
inputArea.value = productName.innerText; //ouput here
});
var getProductName = document.querySelectorAll('[data-id="e9c06d5"]');
getProductName.forEach(function(item) {
var productName = item.querySelector('.lift'); // Heading text (ex:Home Lift)
var inputArea = item.querySelector('input[name=product]');
inputArea.value = productName.innerText; // Output here
});
}
As you may have already guessed, it is not working. Is my code too messy to be executed faster or is there any other problem I am missing?
I know similar questions have been asked previously, however, no existing answer I found was able to help me.
It seems like you try to loop through elements that are still not loaded. Perhaps they are being appended to the page via Ajax, so DOMContentLoaded can't help there.
You can create your own check for those elements using setInterval, so use something like this:
let dataIdCheck = setInterval(() => {
if (document.querySelectorAll('[data-id="6657316"]').length > 0 && document.querySelectorAll('[data-id="e9c06d5"]').length > 0) {
clearInterval(dataIdCheck);
// your code here
}
}, 500);
This code will run every 500 milliseconds and check if those two elements exists, using .length. Once they do exists, we stop the interval and run the code.
I also suggest to do console.log('in') to check that our interval stop running once the elements are found.

Is there a way I can make this work in Google Apps Script

I have tried innerHTML instead of getBody() but still get an error, and any help with debugging on apps-script, mine does not seem to work.
function findText(findme,colour,desc,rule_name) {
var body = DocumentApp.getActiveDocument().getBody();
var regExp = case_insensitive(findme);
var foundElement = body.findText(regExp);
while (foundElement != null) {
var foundText = foundElement.getElement().asText();
var start = foundElement.getStartOffset();
var end = foundElement.getEndOffsetInclusive();
foundText.setBackgroundColor(start, end, colour);
number_oresults++;
foundElement = body.findText(regExp, foundElement);
var pusher = '<p><span style="background-color:'+colour+'"><b>'+rule_name+'</b> - '+ desc +'</span></p>';
results.push(pusher);
}
}
Looks like you're running a standalone script, but DocumentApp.getActiveDocument() is only available in container-bound scripts.
You can copy-paste your script into a new script bound to that Google Doc or use one of the other open methods:
DocumentApp.openById()
DocumentApp.openByUrl()
Sometimes the active methods when used in method chainging can throw errors like the one shown in your screenshot (`can't read property something of null) See Why Class Range getValues sometimes returns [[]] when chained to Class Sheet getActiveRange?
Try replacing
var body = DocumentApp.getActiveDocument().getBody();
by
var doc = DocumentApp.getActiveDocument();
var body = doc.getBody();
It's worthy to note that DocumentApp.getActiveDocument() can only be used on bounded scripts and on standalone scripts when be executed as add-on either by using Run > Test as add-on... or by publishing the script as a G Suite Editor add-on for a Google Documents and executing the script from the UI.
NOTE: The following works fine on a standalone script executed Run > Test as add-on...
function onOpen(e) {
DocumentApp.getUi()
.createAddonMenu()
.addItem('Test 1', 'doSomething1')
.addItem('Test 2', 'doSomething2')
.addToUi()
}
function doSomething1(){
var body = DocumentApp.getActiveDocument().getBody();
body.appendParagraph('Test 1');
}
function doSomething2(){
var doc = DocumentApp.getActiveDocument();
var body = doc.getBody();
body.appendParagraph('Test 2');
}

How to count all non-blank cells using Excel Javascript Addins?

I'm creating an office js addin that inserts data from the bottom of Table 1 into Table 2 but I am unable to find a method of doing this that works.
I have tried using Excel.Functions.countA() but I can't seem to get a value other than NaN out of it. Here is the code I'm using:
async function run() {
try {
await Excel.run(async context => {
var sheet1Name = "Sheet1";
var sheet1RangeAddress = "B:B";
var sheet2Name = "Sheet2";
var sheet2RangeAddress = "A2:P2";
var sheet2Range = context.workbook.worksheets.getItem(sheet2Name).getRange(sheet2RangeAddress);
sheet2Range.insert("Down");
var sheet1CellAddress = context.workbook.worksheets.getItem(sheet1Name).getRange(sheet1RangeAddress).load("address");
var sheet1RangeLength = Number(context.workbook.functions.countA(sheet1CellAddress));
var sheet1LastCell = context.workbook.worksheets.getItem(sheet1Name).getRangeByIndexes(3,1,sheet1RangeLength,1).getLastCell();
var sheet2Cell = context.workbook.worksheets.getItem(sheet2Name).getRange("A2");
sheet2Cell.values = [[ context.workbook.worksheets.getItem(sheet2Name).getRange("A2").copyFrom(sheet1LastCell) ]]
await context.sync();
});
} catch (error) {
console.error(error);
}
}
I can't find anything useful in Microsoft's documentation or a working example online. Does anyone know what I'm doing wrong?
This line in your code looks problematic:
var sheet1RangeLength = Number(context.workbook.functions.countA(sheet1CellAddress));
The Functions.countA method returns an Excel.FunctionResult object which I don't think can be cast to a Number. The count returned by the function will be in the value property of the returned object. You need to load that value to read it. Try these two lines as a replacement:
var sheet1RangeLength = context.workbook.functions.countA(sheet1CellAddress).load("value");
await context.sync();
BTW, the following line is returning a Range object, not an address. That's OK because countA accepts a Range object parameter, but your variable is misleadingly named. Also, I don't think the load("address") on the end is serving any purpose.
var sheet1CellAddress = context.workbook.worksheets.getItem(sheet1Name).getRange(sheet1RangeAddress).load("address");
If you haven't already, please see this article: Call built-in Excel worksheet functions.

ReferenceError: Image is not defined

I am trying to make a website, in which I include images via links. If an image is non-existent, or just 1 pixel wide, the website should display an alternative image. I am using Jade/Pug and JS.
I try to make a list of links beforehand, before rendering them on the website. That way I can just iterate threw my link-list in the .pug file afterwards.
So what I am trying to do is, to check, if an image has a certain size, using JS only. If it does, I add the link to my list, if not, then I add an alternative link.
This is the important part of my code in the app.js-file:
app.get("/", function (req, res) {
//get all the books
var isbns = [];
var links = [];
dbClient.query("SELECT isbn FROM book WHERE id < 20", function (dbError, dbItemsResponse){
isbns = dbItemsResponse.rows;
var linkk = 0;
for(i=0;i<20;i++){
linkk = "http://covers.openlibrary.org/b/isbn/" + Object.values(isbns[i]) + "-M.jpg";
var wid = getMeta(linkk);
if(wid < 2){
links[i]="https://i.ibb.co/7JnVtcB/NoCover.png";
} else {
links[i]=linkk;
}
}
});
});
function getMeta(url){
var img = new Image(); //or document.createElement("img");
img.onload = function(){
alert(this.width);
};
img.src = url;
}
This gives me a ReferenceError: Image() is not defined. If I try to use document.createElement("img"); it says "document is not defined".
How can i check on the server-side if an Image is existent? Or how can I use the Image-Constructor in my app.js file? Without using my Jade/Pug/HTML file in any way.
Sorry If it's a dumb question, but I am trying to figure this out since 20 hours non-stop, and I can't get it to work.
You are mixing up nodejs and javascript. Your code is nodejs and therefore on the sererside. window and Image are only available in the browser, resp. on the client side.
For checking if a file exists, (Only on the serverside!=) you can use fs => fs.access.
var fs = require("fs");
// Check if the file exists in the current directory.
fs.access(file, fs.constants.F_OK, (err) => {
console.log(`${file} ${err ? 'does not exist' : 'exists'}`);
});
Note
There isn't something like a "dumb question" :=)

Google Script not Appending Spreadsheet

I'm trying to write a little script to make my coworkers and mine lives easier. I am trying to append lines to a spreadsheet based on information entered into a custom form. The code posted below just the doPost block which should be appending the google spreadsheet.
function doPost(form) {
var PN = form.PartNumber;
var REV = form.Revision;
var DATE = form.RevisionDate;
var DESC = form.Description;
var NOTE = form.PartNotes;
var URL = form.myFile.getURL();
var ss = SpreadsheetApp.openById("ID HERE"); // removed ID for sake of safety (let me be paranoid)
var sheet = ss.getSheetName('Uploads');
sheet.appendRow([PN,REV,DATE,DESC,NOTE,URL]);
}
I am unsure why it isn't writing to the spreadsheet but it isn't throwing me any errors. If you can offer any insight as to what is wrong I would greatly appreciate it; there are many guides online but most seem to be based on deprecated functions/code/etc.
Thanks for your time.
Instead of using doPost, set up a "On form submit" trigger.
You need to get the namedValues to be able to pull specific values and take the first output.
Also, it should be "getSheetByName('Uploads')" .
As pointed out in the previous answer, it is unclear what you are trying to achieve by "form.myFile.getURL();" If you want to get the form url you might as well create it as a string, as it always stays the same.
Here is a working example of your code:
function doPost(form) {
var formResponses = form.namedValues;
var PN = formResponses.PartNumber[0];
var REV = formResponses.Revision[0];
var DATE = formResponses.RevisionDate[0];
var DESC = formResponses.Description[0];
var NOTE = formResponses.PartNotes[0];
//var URL = form.myFile.getURL(); //Not sure what you are tyring to get here as form URL will always be the same.
var URL = "Your form's url"; //You can put the form url in here so it will be pushed in to every row.
var ss = SpreadsheetApp.openById("ID HERE"); // removed ID for sake of safety (let me be paranoid)
var sheet = ss.getSheetByName('Uploads');
sheet.appendRow([PN,REV,DATE,DESC,NOTE,URL]);
}
The form fields are nested in a "parameter" property in the doPost parameter.
So, you should access them using:
function doPost(form) {
var actualForm = form.parameter;
var PN = actualForm.PartNumber;
//etc
To double check all parameters your receiving and their names, you could append to your sheet everything stringfied, like this:
sheet.appendRow([JSON.stringify(form)]);
--edit
This form.myFile.getURL() also looks odd. I guess another good debugging trick you could do is to wrap everything in a try-catch and email yourself any errors you get. For example:
function doPost(form) {
try {
//all your code
} catch(err) {
MailApp.sendMail('yourself#etc', 'doPost error', err+'\n\n'+JSON.stringify(form));
}
}
On form submit
onFormSubmit works. "doPost" looks wrong.
Simple example:
function Initialize() {
var triggers = ScriptApp.getProjectTriggers();
for(var i in triggers) {
ScriptApp.deleteTrigger(triggers[i]);
}
ScriptApp.newTrigger("SendGoogleForm")
.forSpreadsheet(SpreadsheetApp.getActiveSpreadsheet())
.onFormSubmit()
.create();
}
function SendGoogleForm(e)
{
try
{
Full example - Scroll down to the code http://www.labnol.org/internet/google-docs-email-form/20884/ (Note: example sends email)
Trigger docs: https://developers.google.com/apps-script/guides/triggers/events
Notes: I think the problem is doPost, Does it work with google Forms? Never seen it used with google forms.
First and foremost, thank you everyone who has responded with information thus far. None of the solutions posted here worked for my particular implementation (my implementation is probably to blame, it is very crude), but they definitely set me down the path to a working version of my form which we now lightly use. I have posted some of the code below:
function sheetFill(form, link) {
try {
var formResponses = form.namedValues;
var toForm = [0,0,0,0,0,0,0];
for (i=0;i < form.PartNumber.length;i++){
toForm[0] = toForm[0]+form.PartNumber[i];
}
... (several for loops later)
var d = new Date();
var ss = SpreadsheetApp.openById("IDHERE");
var sheet = ss.getCurrentSheet;
ss.appendRow([toForm[0], toForm[1], toForm[2], toForm[3], toForm[4], toForm[5], toForm[6], link, d]);
} catch(err) {
MailApp.sendEmail('EMAIL', 'doPost error', err+'\n\n'+JSON.stringify(form));
}
}
It is not very versatile or robust and isn't elegant, but it is a starting point.

Categories

Resources