DTPab

DTPにまつわるあれこれ

File.openDialog で複数のファイル種別を指定する

はじめに

フォーラムで標題のようなスクリプトをどう書くの? って質問を見つけて回答した*1のでブログにも残しておきます。

File.openDialog()メソッド

最初にこのメソッドについておさらいしましょう。公式ドキュメント*2によると下記のように書かれています。

f:id:uske_S:20200625221136p:plain
File.openDialog

メソッドの引数は3つで、どれも省略可能(optional)です。

  • prompt:ダイアログに表示させるテキストです(String
  • filter:選択を許可するファイル種別です(FunctionString*3
  • multiSelect:複数選択を許容するかどうかです(Boolean、デフォルトはfalse*4

promptは大抵「ファイルを選んでください」とか書きます。好きな文字列を渡してください。
multiSelectをtrueにするとファイル選択時に複数を同時に選択することができます。
filterが今回の肝です。これの書き方次第でファイル種別を特定することができます。

第2引数の書き方

実はこの第2引数、先のドキュメントにある通りOSによって書き方を変えなければなりません。

  • In Windows, a filter expression, such as "JavaScript: *. jsx;All files: *.*"
  • In Mac OS, a filter function that takes a File instance and returns true if the file should be included in the display, false if it should not.

ということでそれぞれ紹介します。

Windowsの場合

Winの場合は非常に簡単です。ワイルドカードをセミコロン;で区切るだけで大丈夫です。

File.openDialog("ファイルを選択してください", "*.indd;*.jsx", false);

こうすると .indd と .jsx だけがダイアログに表示されるようになります。

macOSの場合

macの場合はコールバック関数を渡す必要があります。選択を許容するファイル種別の場合はtrue、そうでなければfalseが返るような関数です。
ただし、注意しなければいけないのはフォルダの扱いです。フォルダの選択も許容してあげないとダイアログに表示されたフォルダが選択できず、階層を潜れません。
コードとしては下記のようになります。

File.openDialog("ファイルを選択してください", callback, false);
function callback (F) {
    var re = /\.(indd|jsx)$/i;
    return (F instanceof Folder || re.test(F.fsName));
}

このように拡張子でファイル種別を識別するのが簡便かと思います。

callback関数の引数Fですが、File.openDialog()メソッドからコールバック関数に渡されるFileオブジェクトです。要するにFile.openDialog()のダイアログに表示されるファイルそれぞれが引数として得られるということです。

まずこれがフォルダかどうかをF instanceof Folderで確認します。instanceofメソッドは左辺のオブジェクトが右辺のコンストラクタのprototypeプロパティを持っているかを確認します*5

次にファイルのfsNameプロパティでファイル名を参照し、特定の拡張子を持っているかを確認しています。

どちらのOSにも対応した書き方は?

上述のようにOSによって第2引数を書き分ける必要があるため、ひとつのコードで済ませるにはちょっと工夫がいります。

var tgtFiles = File.openDialog("ファイルを選択してください", callback(), false);

function callback () {
    // 許容するファイル拡張子
    var acceptExtension = [
        "indd",
        "docx",
        "doc"
    ];
    if (/^win/i.test($.os)) {
        for (var i=0; i<acceptExtension.length; i++) {
            acceptExtension[i] = "*." + acceptExtension[i];
        }
        return acceptExtension.join(";");
    }
    return function (F) {
        var re = RegExp("\\.(" + acceptExtension.join("|") + ")$", "i");
        return (F instanceof Folder || re.test(F.fsName));
    };
}

コードとしてはこんな感じで実装できます。

$.osでOSが文字列として得られるので、それでOSを識別しています。Windowsはfor文でまだるっこしい実装していますが、例えばArray.map()メソッドなどをポリフィルしていればわざわざfor文を書く必要はないですね。
OSがWindowsならここでワイルドカード文字列を返しておしまいです。

macOSの場合は関数を返す必要がありました。

return function (F) {
    var re = RegExp("\\.(" + acceptExtension.join("|") + ")$", "i");
    return (F instanceof Folder || re.test(F.fsName));
};

この部分です。

return文に関数を直接書いていてその中にもreturn文があるから少しややこしいかもしれませんが、要するに関数そのものを返しています。

というわけでFile.openDialogで複数のファイル種別を指定する方法でした。

*1:参照:Solved: Re: [jsx] File.openDialog: How to allow multiple f... - Adobe Support Community - 11238713

*2:JavaScript Tools Guide:https://www.adobe.com/content/dam/acom/en/devnet/Images/JavaScript-Tools-Guide-CC.pdf

*3:macOSは関数型、Windowsでは文字列型になります

*4:trueで「複数選択可」の意味

*5:詳しくはこちらを参照:instanceof - JavaScript | MDN