Я работаю с Adobe КЭП (это позволяет разработчикам создавать оконные расширения для продуктов Adobe CC). Основная часть моего кода — это современный JavaScript (платформа использует Chromium 57, Node.js 7.7.4). Однако, чтобы получить доступ к DOM, мне нужно написать некоторые функции в Adobe Расширить скрипт и выполнить их из обычного JS. Единственный способ — выполнить скрипт, используя их предоставленные csInterface.evalScript(script, callback)
. script
должна быть строкой, которая в моем случае представляет собой вызов функции, преобразованный в строку. Я хочу иметь возможность передавать объект в и из ExtendScript через evalScript
, но evalScript
принимает и возвращает только строку.
В настоящее время я передаю каждое свойство объекта как отдельный аргумент. Это громоздко, но это работает.
Моим первым был JSON.stringify()
, но, к сожалению, ExtendScript — это диалект ECMAScript 3, что означает нет поддержки JSON.parse()
.
Я не могу просто объединить аргумент объекта с вызовом функции скрипта, потому что тогда строка оценивается как foo([object Object])
.
Я видел такие функции, как eval()
/uneval()
или Object.toSource()
, но они не поддерживаются Chromium.
Вот пример, похожий на мой текущий метод:
functions.js (ES3/ExtendScript)
function drawCircle(x, y, name) {
// pick a layer
var layer = app.activeDocument.layers[0];
var diameter = 10;
var top = y + diameter / 2;
var left = x - diameter / 2;
// draw ellipse in layer
var circle = layer.pathItems.ellipse(top, left, diameter, diameter);
circle.name = name;
circle.filled = true;
return true;
}
app.js (ES6)
const csInterface = new CSInterface(); // provided by Adobe
async function circle() {
const dataObject = {x: 10, y: 10, name: 'Hello world!'};
// the script to call
// evaluates to drawCircle(10,10,'Hello world!');
const script = "drawCircle(" + dataObject.x + "," + dataObject.y + ",'" + dataObject.name + "');";
return new Promise((resolve, reject) => {
csInterface.evalScript(script, (result) => {
resolve(result);
});
});
}
Как и ожидалось, circle()
вызывает drawCircle()
просто отлично, и в документе, над которым я работаю, появляется многоточие. Однако выполнение скрипта/вызов функции путем конкатенации выглядит очень неправильно. Итак, в целом,
dataObject
в строку и передать ее drawCircle()
через evalScript()
,dataObject
из drawCircle()
и получить его обратно как объект. В настоящее время возврат объекта приводит только к возвращаемому значению "[object Object]"
.Единственный способ передать объекты из Javascript в ExtendScript — отправить их в виде строки JSON с JSON.stringify()
.
Да, вы правы насчет отсутствия JSON.parse()
поддержки, однако, тебе не нужно.
Вы по-прежнему можете отправить строковый объект, и он прибудет в ExtendScript как объект.
const dataObject = {x: 10, y: 10, name: 'Hello world!'};
const script = "drawCircle(" + JSON.stringify(dataObject) + ")";
И затем в ExtendScript вы можете сделать что-то вроде этого:
function drawCircle(obj) {
var layer = app.activeDocument.layers[0];
var radius = 10;
var top = obj.y + 5;
var left = obj.x - 5;
var circle = layer.pathItems.ellipse(top, left, radius, radius);
circle.name = obj.name;
circle.filled = true;
return true;
}
Вам понадобится этот модуль ExtendScript, скопированный в ту же папку, что и ваш jsx.
Ссылка на модуль Indiscripts ExtendScript JSON
Затем включите его с помощью #include 'json.jsx';
(или //@include 'json.jsx'
, чтобы избежать ошибок линтера) в верхней части вашего jsx. Это добавляет глобальную функцию JSON, которая предоставляет два метода: JSON.eval()
и JSON.lave()
.
Нам нужен метод lave()
, который позволяет преобразовать объект обратно в Javascript. Считайте это более дружелюбной версией JSON.stringify()
.
function drawCircle(obj) {
var layer = app.activeDocument.layers[0];
var radius = 10;
var top = obj.y + 5;
var left = obj.x - 5;
// draw ellipse in layer
var circle = layer.pathItems.ellipse(top, left, radius, radius);
circle.name = obj.name;
circle.filled = true;
return JSON.lave(circle);
}
Затем в javascript вы можете снова проанализировать объект:
const dataObject = {x: 10, y: 10, name: 'Hello world!'};
const script = "drawCircle(" + JSON.stringify(dataObject) + ")";
csInterface.evalScript(script, (result) => {
console.info(JSON.parse(result));
});
Я тестировал это в последней версии CEP Runtime (v9).
Если у вас есть специальные символы внутри отправляемой вами строки, вам нужно escape
их. Если вы используете только decodeURIComponent
, он не будет охватывать некоторые символы. Особенно, если вам нужно поддерживать более старые версии, например, Premiere 2019... (я знаю, что escape
устарела)
Спасибо, я понятия не имел о возможности прямой передачи строковых объектов! Я также нашел более общий Полифил JSON. Оба метода изменяют
$.global
, поэтому мне пришлось перезапустить Illustrator, чтобы очиститьJSON.stringify()
, прежде чем я смогу использоватьJSON.lave()
.