JSXLauncher(AI&PS)

皆様、いかがお過ごしでしょうか?マルベリーも全部ジャムになっちゃいました。アスパラもそろそろ終わりに近づきますが、その横ではきゅうりとごーや、トマトがすくすくと育っています。
ところで皆様、Adobe Add-Ons はチェックしましたでしょうか。CCのニューバージョンがリリース直前ですから続々と新しものやアップデートが到着しています。中には重宝するものも多いですから是非ともチェックしてみてください。

https://creative.adobe.com/addons

今回はAdobe Extension SDKサンプルとして、Adobe Add-Onsにて公開中のExtensionのフルコードで解説してみましょう。ソースはgithubでも公開しています。

https://github.com/ten-A/CreativeSuiteSDK_Experimentals/tree/master/net.sytes.chuwa.jsxlauncher

まずはエクステンションの構造です。

この様なファイル構成となります。これはミニマムな構成です。cssに関して言えば純粋にHTMLのスタイルシートですので解説は割愛します。また、icons内のpngファイルは20px×20pxのrgbファイルです。これはエクステンションパネルをリトラクトした時(アイコン化)に表示されるアイコンです。基本は通常のものとロールオーバー時のものを用意します。こちらは省略することも可能です。その場合、デフォルトのエクステンションアイコンが適用されます。
それでは各コードの解説に移りましょう。まず解説するのはCEP側の要、main.jsです。

※main.js
(function () {
     ‘use strict’;
     var fs = require(“fs”);
     var csInterface = new CSInterface();
     var rb = csInterface.initResourceBundle();
     var fls, pth;

    function setPanelCallback(event) {
          if (event.data.menuId==”menuItemId1″) {
               var result = window.cep.fs.showOpenDialogEx(false, true, rb.key1);
               createButton(result.data.toString());
          }
     }

     function createButton(scpath) {
          var st = “”;
          localStorage.setItem(‘aiScriptPath’, scpath);
          document.getElementById(“contents”).innerHTML =””;
          fls = fs.readdirSync(scpath);
          for (var i=0;i<fls.length;i++){
               pth = scpath + ‘/’ + fls[i];
               if (fls[i].indexOf(‘.jsx’)<0) continue;
               st = “<input type=’button’ class=’btn’ value='” + fls[i] + “‘ ” +
                    ‘onClick=”doscript(‘ + “‘” + pth + “‘” + ‘)” /><br />’;
               document.getElementById(“contents”).innerHTML += st;
          }
     }

     function init() {
          var menuXML  = ‘<Menu><MenuItem Id=”menuItemId1″ Label=”‘ + rb.key1 + ‘” Enabled=”true” Checked=”false”/></Menu>’;
          csInterface.setPanelFlyoutMenu(menuXML);
          csInterface.addEventListener(“com.adobe.csxs.events.flyoutMenuClicked”, setPanelCallback);
          if (window.localStorage){
               if (localStorage.getItem(‘aiScriptPath’)==undefined) {
                    var result = window.cep.fs.showOpenDialogEx(false, true, rb.key1);
                    createButton(result.data.toString());
               } else {
                    createButton(localStorage.getItem(‘aiScriptPath’));
               }
          }
     }

     init();
     themeManager.init();
}());

function doscript(fl) {
     var csInterface = new CSInterface();
     csInterface.evalScript(“callScript(‘” + fl + “‘)”);
}

CEP側のJavascriptは、わりとコンパクトにまとめられています。しかしながら、ローカルストレージやサブメニューを組み込んだりと一通りの機能は盛り込んであります。
1点だけnode.jsのモジュールを利用しています。これはjsxファイルが収められたディレクトリをスキャンするための「fs」です。NODEに対応したのはver.5.2以降ですから、このエクステンションはCC2014以降対応となります。また、後に解説するサブメニュー関連も機能追加されたのがver5.2以降です。
ではFlyoutMenu(サブメニュー)から解説しましょう。メニューの構成要素はCEP5ではXMLフォーマットにて与えることになっています。この部分ですが、CEP次期バージョンでJSON化されます。勿論XMLも利用できるようですが。

    var menuXML  = ‘<Menu><MenuItem Id=”menuItemId1″ Label=”‘ +
                    rb.key1 + ‘” Enabled=”true” Checked=”false”/></Menu>’; 

この部分ですね。ここでrb.key1というオブジェクトが挟み込まれているのがわかりますね。

var rb = csInterface.initResourceBundle();

これは先に定義されているリソースバンドルのkey1を読み込んで居ます。リソースバンドルの読み込みはパネル開始時に行われ、アプリケーションのロケールをチェックし、そのロケールに合わせたmessage.propertiesファイルよりkey値を読み出します。これがマルチロケールに対応する為の仕組みです。
続けましょう。

csInterface.setPanelFlyoutMenu(menuXML);

メニューの登録は CSInterfaceクラスのsetPanelFlyoutMenuメソッドを利用します。こちらはcsinterface.js ver.5.2よりの実装です。引数にメニュー構築のためのxmlを指定します。その後メニューが選択された時にトリガされるイベントリスナを指定します。

csInterface.addEventListener(“com.adobe.csxs.events.flyoutMenuClicked”, setPanelCallback);

こちらはリスナとコールバック関数が引数となります。続いてコールバック関数を見てみましょう。

    function setPanelCallback(event) {
          if (event.data.menuId==”menuItemId1″) {
               var result = window.cep.fs.showOpenDialogEx(false, true, rb.key1);
               createButton(result.data.toString());
          }
     }

イベントリスナからのコールバックには選択されたメニューIDが含まれます。このIDでどのメニュー項目が選択されたのかが判断できます。今回のものはメニューが1つですから特に分岐処理は必要ありません。ディレクトリの選択はCEPエンジンが提供するネイティブCPPライブラリの選択ダイアログを利用します。初期リリースではExtendscriptのfolderSelectDialogメソッドを利用していましたが、Windowsで読み込みエラーを起こすと(原因はエスケープせずにJS側に投げ込んだため…)shsPageさんよりご報告いただき、紆余曲折の末ネイティブライブラリの利用となった次第です。報告だけでなくテストまで付きあわせてしまいとても恐縮なのです。shsPageさん、その節は大変お世話になりました。ありがとうございます。
戻って、コールバックの処理です。

     function createButton(scpath) {
          var st = “”;
          localStorage.setItem(‘aiScriptPath’, scpath);
          document.getElementById(“contents”).innerHTML =””;
          fls = fs.readdirSync(scpath);
          for (var i=0;i<fls.length;i++){
               pth = scpath + ‘/’ + fls[i];
               if (fls[i].indexOf(‘.jsx’)<0) continue;
               st = “<input type=’button’ class=’btn’ value='” + fls[i] + “‘ ” +
                    ‘onClick=”doscript(‘ + “‘” + pth + “‘” + ‘)” /><br />’;
               document.getElementById(“contents”).innerHTML += st;
          }
     } <fls.length;i++){ pth="scpath" +="" '="" fls[i];="" if="" (fls[i].indexof('.jsx')<0)="" continue;="" st="

初期ではHTML自体空っぽです。ここにはIDを割り振ったdivコンテナが設けられています。このコンテナに対し当該ディレクトの内容をフィルタしながら読み出しボタンを生成していきます。ここでファイルリストの取得にNODEのFile System APIを利用しています。
ボタンがクリックされた時にコールされるのが以下の関数

function doscript(fl) {
     var csInterface = new CSInterface();
     csInterface.evalScript(“callScript(‘” + fl + “‘)”);
}

単純にボタンに割り当てられているファイルパスを引数としてExtendscriptのファンクションをコールします。

     if(window.localStorage){
          if (localStorage.getItem(‘aiScriptPath’)==undefined) {
               csInterface.evalScript(‘getFilePath()’, function(cb){createButton(cb);});
          } else {
          createButton(localStorage.getItem(‘aiScriptPath’));
          }
     }

こちらの部分はローカルストレージの取り扱いです。CEPのコアはCEF3ですからindexedDB、LocalStorage、クッキーといったものも利用可能です。このエクステンションではローカルストレージにスクリプトパスを記録してエクステンション開始時に自動的に読み込む様にしています。

さて、Extendscript側です。こちらは引数に与えられたjsxファイルを丸ごと実行する関数です。

※hostscript.jsx
function callScript(fl){
    $.level = 0;
     try {
          $.evalFile (fl);
          } catch(e) {
               alert (e);
               }
     }

Extendscriptには便利なヘルパーオブジェクトがあり、その中にはjsxファイルを指定するとまるごと実行してくれる物があります。今回はそれを使いました。ファイルを読み出してevalする手もあるのですが、コメント処理等が煩雑になりますので今回は回避しました。
次にindex.htmlです。

※index.html
<!doctype html>
<html>
    <head>
        <meta charset=”utf-8″>
        <link rel=”stylesheet” href=”css/styles.css”/>
        <link id=”hostStyle” rel=”stylesheet” href=”css/theme.css”/>
        http://js/libs/jquery-2.0.2.min.js
        http://js/libs/CSInterface.js
        http://js/themeManager.js
    </head>
    <body>
       

       

    </body>
    http://js/main.js
</html>

各種cssやscriptを読み込む以外は空っぽです。
以下の2ファイルはロケール処理に利用されるファイルです。この様にキー番号と内容をイコールで結びます。

※message.properties(en)
key1=Select Script Folder..

※message.properties(ja)
key1=スクリプトフォルダを選択

最後にマニフェストです。

manifest.xml
<?xml version=”1.0″ encoding=”UTF-8″ standalone=”no”?>
<ExtensionManifest xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” ExtensionBundleId=”net.sytes.chuwa.jsxlauncher” ExtensionBundleVersion=”0.1.0″ Version=”5.0″>
     <Author>
          <![CDATA[Ten]]>
     </Author>     <ExtensionList>
          <Extension Id=”net.sytes.chuwa.jsxlauncher.extension” Version=”1.0″/>
     </ExtensionList>
     <ExecutionEnvironment>
          <HostList>
               <Host Name=”ILST” Version=”[18.0,19.9]”/>
               <Host Name=”PHSP” Version=”[15.0,16.9]”/>
               <Host Name=”PHXS” Version=”[15.0,16.9]”/>
          </HostList>
          <LocaleList>
               <Locale Code=”All”/>
          </LocaleList>
          <RequiredRuntimeList>
               <RequiredRuntime Name=”CSXS” Version=”5.0″/>
          </RequiredRuntimeList>
     </ExecutionEnvironment>
     <DispatchInfoList>
          <Extension Id=”net.sytes.chuwa.jsxlauncher.extension”>
               <DispatchInfo>
                    <Resources>
                         <MainPath>./index.html</MainPath>
                         <ScriptPath>./jsx/hostscript.jsx</ScriptPath>
                    </Resources>
                    <Lifecycle>
                         <AutoVisible>true</AutoVisible>
                    </Lifecycle>
                    <UI>
                         <Type>Panel</Type>
                         <Menu>Jsx Launcher</Menu>
                         <Geometry>
                              <Size>
                                   <Height>308</Height>
                                   <Width>180</Width>
                              </Size>
                         </Geometry>
                         <Icons>
                              <Icon Type=”Normal”>./icons/spfa.png</Icon>
                              <Icon Type=”RollOver”>./icons/spfa_dk.png</Icon>
                         </Icons>
                    </UI>
               </DispatchInfo>
          </Extension>
     </DispatchInfoList>
</ExtensionManifest>

CSInterface5.2以降に対応できるのはCC2014以降となりますのでこの様な構成になります。なおHostNameに「<Host Name=”IDSN” Version=”[10.0,11.9]”/>」なんかを追加するとIndesignでも使えるようになったりします。

おしまいにイメージです。

これを


こうして

こうなります。パスはローカルストレージに保存されますのでAI起動時に前回のパスを読み出してボタンをつくります。

ten_a

Graphic Designer, Scripter and Coder. Adobe Community Professional.

シェアする