313 lines
12 KiB
JavaScript
313 lines
12 KiB
JavaScript
(function () {
|
||
"use strict";
|
||
|
||
var scriptList = document.getElementById("scriptList");
|
||
var emptyState = document.getElementById("emptyState");
|
||
var statusText = document.getElementById("statusText");
|
||
var searchInput = document.getElementById("searchInput");
|
||
var refreshButton = document.getElementById("refreshButton");
|
||
var openFolderButton = document.getElementById("openFolderButton");
|
||
var runExternalButton = document.getElementById("runExternalButton");
|
||
var scripts = [];
|
||
var extensionPathToken = "";
|
||
|
||
function getExtensionPath() {
|
||
if (!window.__adobe_cep__ || !window.__adobe_cep__.getSystemPath) {
|
||
return "";
|
||
}
|
||
|
||
var path = window.__adobe_cep__.getSystemPath("extension") || "";
|
||
path = decodeURI(path);
|
||
|
||
if (/^file:\/\/\/[A-Za-z]:/i.test(path)) {
|
||
path = path.substring(8);
|
||
} else if (/^file:\/\//i.test(path)) {
|
||
path = path.substring(7);
|
||
}
|
||
|
||
return path;
|
||
}
|
||
|
||
function setStatus(message, isError) {
|
||
statusText.textContent = message || "";
|
||
statusText.className = isError ? "status error" : "status";
|
||
statusText.title = message || "";
|
||
}
|
||
|
||
function evalHost(code, callback) {
|
||
if (!window.__adobe_cep__ || !window.__adobe_cep__.evalScript) {
|
||
callback("ERROR|当前页面不在 Photoshop CEP 环境中运行");
|
||
return;
|
||
}
|
||
window.__adobe_cep__.evalScript(code, callback);
|
||
}
|
||
|
||
function buildListCode() {
|
||
return '(function(){try{' +
|
||
'var folder=new Folder(decodeURIComponent("' + extensionPathToken + '")+"/jsx/scripts");' +
|
||
'if(!folder.exists){return "ERROR|脚本文件夹不存在:"+folder.fsName;}' +
|
||
'var result=[];' +
|
||
'function walk(current,prefix,depth){' +
|
||
'if(depth>20){return;}' +
|
||
'var entries=current.getFiles();' +
|
||
'entries.sort(function(a,b){' +
|
||
'var af=a instanceof Folder;var bf=b instanceof Folder;' +
|
||
'if(af!==bf){return af?-1:1;}' +
|
||
'var left=File.decode(a.displayName).toLowerCase();' +
|
||
'var right=File.decode(b.displayName).toLowerCase();' +
|
||
'return left<right?-1:(left>right?1:0);' +
|
||
'});' +
|
||
'for(var i=0;i<entries.length;i++){' +
|
||
'var entry=entries[i];' +
|
||
'if(entry instanceof Folder){' +
|
||
'var folderName=File.decode(entry.name);' +
|
||
'if(!entry.alias&&folderName.charAt(0)!=="."){' +
|
||
'walk(entry,prefix+folderName+"/",depth+1);' +
|
||
'}' +
|
||
'}else if(entry.name&&/\\.jsx$/i.test(entry.name)){' +
|
||
'var fileName=File.decode(entry.name);' +
|
||
'var label=File.decode(entry.displayName)' +
|
||
'.replace(/\\.jsx$/i,"").replace(/_/g," ");' +
|
||
'result.push(encodeURIComponent(label)+"|"+' +
|
||
'encodeURIComponent(prefix+fileName));' +
|
||
'}' +
|
||
'}' +
|
||
'}' +
|
||
'walk(folder,"",0);' +
|
||
'return result.join("\\n");' +
|
||
'}catch(error){return "ERROR|读取脚本列表失败:"+error.message;}}())';
|
||
}
|
||
|
||
function buildRunCode(encodedRelativePath) {
|
||
return '(function(){try{' +
|
||
'var folder=new Folder(decodeURIComponent("' + extensionPathToken + '")+"/jsx/scripts");' +
|
||
'var relativePath=decodeURIComponent("' + encodedRelativePath + '");' +
|
||
'var parts=relativePath.split("/");' +
|
||
'if(relativePath.charAt(0)==="/"||relativePath.indexOf("\\\\")!==-1||' +
|
||
'!/\\.jsx$/i.test(relativePath)){' +
|
||
'return "ERROR|脚本路径不安全";' +
|
||
'}' +
|
||
'for(var i=0;i<parts.length;i++){' +
|
||
'if(!parts[i]||parts[i]==="."||parts[i]===".."){return "ERROR|脚本路径不安全";}' +
|
||
'}' +
|
||
'var file=new File(folder.fsName+"/"+relativePath);' +
|
||
'if(!file.exists){return "ERROR|找不到脚本:"+relativePath;}' +
|
||
'$.evalFile(file);return "OK";' +
|
||
'}catch(error){return "ERROR|运行失败:"+error.message+"(第 "+error.line+" 行)";}}())';
|
||
}
|
||
|
||
function buildOpenFolderCode() {
|
||
return '(function(){try{' +
|
||
'var folder=new Folder(decodeURIComponent("' + extensionPathToken + '")+"/jsx/scripts");' +
|
||
'if(!folder.exists&&!folder.create()){' +
|
||
'return "ERROR|无法创建脚本文件夹:"+folder.fsName;' +
|
||
'}' +
|
||
'if(!folder.execute()){return "ERROR|无法打开脚本文件夹:"+folder.fsName;}' +
|
||
'return "OK";' +
|
||
'}catch(error){return "ERROR|无法打开脚本文件夹:"+error.message;}}())';
|
||
}
|
||
|
||
function buildExternalScriptCode() {
|
||
return '(function(){try{' +
|
||
'var file=File.openDialog("选择要运行的 Photoshop JSX 脚本");' +
|
||
'if(file===null){return "CANCEL";}' +
|
||
'if(!/\\.jsx$/i.test(file.name)){return "ERROR|请选择扩展名为 .jsx 的文件";}' +
|
||
'$.evalFile(file);return "OK";' +
|
||
'}catch(error){return "ERROR|运行失败:"+error.message+"(第 "+error.line+" 行)";}}())';
|
||
}
|
||
|
||
function parseScriptList(raw) {
|
||
if (raw === "EvalScript error.") {
|
||
throw new Error("Photoshop 拒绝执行目录扫描,请重新打开面板");
|
||
}
|
||
if (!raw) {
|
||
return [];
|
||
}
|
||
if (raw.indexOf("ERROR|") === 0) {
|
||
throw new Error(raw.substring(6));
|
||
}
|
||
|
||
var result = [];
|
||
var lines = raw.split("\n");
|
||
for (var i = 0; i < lines.length; i++) {
|
||
if (!lines[i]) {
|
||
continue;
|
||
}
|
||
var parts = lines[i].split("|");
|
||
if (parts.length >= 2) {
|
||
result.push({
|
||
label: decodeURIComponent(parts[0]),
|
||
relativePath: decodeURIComponent(parts[1])
|
||
});
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
|
||
function runBundledScript(script) {
|
||
var buttons = scriptList.querySelectorAll("button");
|
||
for (var i = 0; i < buttons.length; i++) {
|
||
buttons[i].disabled = true;
|
||
}
|
||
setStatus("正在运行:" + script.label, false);
|
||
|
||
evalHost(buildRunCode(encodeURIComponent(script.relativePath)), function (result) {
|
||
for (var j = 0; j < buttons.length; j++) {
|
||
buttons[j].disabled = false;
|
||
}
|
||
if (result && result.indexOf("ERROR|") === 0) {
|
||
setStatus(result.substring(6), true);
|
||
} else if (result === "EvalScript error.") {
|
||
setStatus("脚本运行失败,请检查 JSX 语法", true);
|
||
} else {
|
||
setStatus("已完成:" + script.label, false);
|
||
}
|
||
});
|
||
}
|
||
|
||
function makeTreeNode() {
|
||
return {folders: {}, scripts: []};
|
||
}
|
||
|
||
function addScriptToTree(root, script) {
|
||
var parts = script.relativePath.split("/");
|
||
parts.pop();
|
||
var node = root;
|
||
for (var i = 0; i < parts.length; i++) {
|
||
if (!node.folders[parts[i]]) {
|
||
node.folders[parts[i]] = makeTreeNode();
|
||
}
|
||
node = node.folders[parts[i]];
|
||
}
|
||
node.scripts.push(script);
|
||
}
|
||
|
||
function countScripts(node) {
|
||
var count = node.scripts.length;
|
||
var names = Object.keys(node.folders);
|
||
for (var i = 0; i < names.length; i++) {
|
||
count += countScripts(node.folders[names[i]]);
|
||
}
|
||
return count;
|
||
}
|
||
|
||
function createScriptButton(script) {
|
||
var button = document.createElement("button");
|
||
button.type = "button";
|
||
button.className = "script-button";
|
||
button.textContent = script.label;
|
||
button.title = script.relativePath;
|
||
button.addEventListener("click", function () {
|
||
runBundledScript(script);
|
||
});
|
||
return button;
|
||
}
|
||
|
||
function appendTree(node, container, depth, expandAll) {
|
||
var folderNames = Object.keys(node.folders).sort(function (a, b) {
|
||
var left = a.toLowerCase();
|
||
var right = b.toLowerCase();
|
||
return left < right ? -1 : (left > right ? 1 : 0);
|
||
});
|
||
|
||
for (var i = 0; i < folderNames.length; i++) {
|
||
var folderName = folderNames[i];
|
||
var childNode = node.folders[folderName];
|
||
var details = document.createElement("details");
|
||
details.className = "folder-group";
|
||
details.open = expandAll;
|
||
|
||
var summary = document.createElement("summary");
|
||
summary.className = "folder-summary";
|
||
summary.textContent = folderName + "(" + countScripts(childNode) + ")";
|
||
|
||
var content = document.createElement("div");
|
||
content.className = "folder-content";
|
||
appendTree(childNode, content, depth + 1, expandAll);
|
||
|
||
details.appendChild(summary);
|
||
details.appendChild(content);
|
||
container.appendChild(details);
|
||
}
|
||
|
||
for (var j = 0; j < node.scripts.length; j++) {
|
||
container.appendChild(createScriptButton(node.scripts[j]));
|
||
}
|
||
}
|
||
|
||
function renderScripts() {
|
||
var query = searchInput.value.toLowerCase().replace(/^\s+|\s+$/g, "");
|
||
scriptList.innerHTML = "";
|
||
var visibleCount = 0;
|
||
var root = makeTreeNode();
|
||
|
||
for (var i = 0; i < scripts.length; i++) {
|
||
var searchable = (scripts[i].label + " " + scripts[i].relativePath).toLowerCase();
|
||
if (query && searchable.indexOf(query) === -1) {
|
||
continue;
|
||
}
|
||
visibleCount++;
|
||
addScriptToTree(root, scripts[i]);
|
||
}
|
||
|
||
appendTree(root, scriptList, 0, query.length > 0);
|
||
|
||
emptyState.hidden = visibleCount !== 0;
|
||
}
|
||
|
||
function loadScripts() {
|
||
refreshButton.disabled = true;
|
||
setStatus("正在读取脚本…", false);
|
||
evalHost(buildListCode(), function (raw) {
|
||
refreshButton.disabled = false;
|
||
try {
|
||
scripts = parseScriptList(raw);
|
||
renderScripts();
|
||
setStatus("已载入 " + scripts.length + " 个脚本", false);
|
||
} catch (error) {
|
||
scripts = [];
|
||
renderScripts();
|
||
setStatus(error.message, true);
|
||
}
|
||
});
|
||
}
|
||
|
||
refreshButton.addEventListener("click", loadScripts);
|
||
searchInput.addEventListener("input", renderScripts);
|
||
|
||
openFolderButton.addEventListener("click", function () {
|
||
evalHost(buildOpenFolderCode(), function (result) {
|
||
if (result && result.indexOf("ERROR|") === 0) {
|
||
setStatus(result.substring(6), true);
|
||
} else if (result === "EvalScript error.") {
|
||
setStatus("无法执行打开文件夹命令", true);
|
||
} else {
|
||
setStatus("已打开脚本文件夹", false);
|
||
}
|
||
});
|
||
});
|
||
|
||
runExternalButton.addEventListener("click", function () {
|
||
setStatus("请选择一个 JSX 文件…", false);
|
||
evalHost(buildExternalScriptCode(), function (result) {
|
||
if (result && result.indexOf("ERROR|") === 0) {
|
||
setStatus(result.substring(6), true);
|
||
} else if (result === "CANCEL") {
|
||
setStatus("已取消", false);
|
||
} else if (result === "EvalScript error.") {
|
||
setStatus("外部脚本运行失败", true);
|
||
} else {
|
||
setStatus("外部脚本已运行", false);
|
||
}
|
||
});
|
||
});
|
||
|
||
var extensionPath = getExtensionPath();
|
||
extensionPathToken = encodeURIComponent(extensionPath);
|
||
if (!extensionPath) {
|
||
setStatus("无法获取扩展目录,请重启 Photoshop", true);
|
||
} else {
|
||
loadScripts();
|
||
}
|
||
}());
|