【サクラエディタマクロ】【jScript】共通処理を呼び出す方法

# 共通処理の呼び出しとは？
* マクロからもjScript側からも処理を別ファイルから呼び出す機構が用意されていない
* 配列でmapやforEachで書きたいけど、毎回ポリフィル書くの面倒
* 似たような処理をコピペで持っていくのも面倒。

## 注意事項
* evalを使ってグローバル関数を読み込むだけのものなのであまり良いものでもない。
* とはいえ無いよりはあった方が楽にはなるので、マクロユーザにとってはメリットはあるはず。

## やりかた

### cookie.jsの用意

* サクラエディタマクロのクッキー機能を用いて、暗黙的なimport/exportを実現する
* 必ず、confフォルダの配下に作成してください。

```js:conf/cookie.js
var readAll = function(filePath,charset){
  var adTypeText = 2;
  var adReadAll = -1; //全行モード
  var adCRLF = -1;
  var stream = new ActiveXObject("ADODB.Stream");
  stream.type = adTypeText;
  stream.charset = charset || 'Shift-jis';
  stream.LineSeparator = adCRLF;
  stream.open();
  stream.loadFromFile(filePath);
  try{
      var text = stream.readText(adReadAll);
  }finally{
      stream.close();
  }
  return text;
}

function saveData(keyId,data){
    var result = Editor.SetCookie("document",keyId,data);
    if(result != 0){
        throw new Error("set cookie Error!! result=" + result);
    }
}

/*
* マクロのホームディレクトリを取得する
* このマクロの配置している１階層上を取得しiniファイルに書き出す。
*/
function loadMacroHomeDir(){
	var path = Editor.ExpandParameter("$M").split("\\conf\\cookie.js").join("")
    //cookieの保存
    Editor.setCookie("document","szMACROFOLDER",path)
    return path
}

(function(){
    //マクロホームフォルダの取得
    var szMACROFOLDER = loadMacroHomeDir();
    // load用モジュール
    saveData("module",readAll(szMACROFOLDER + "/conf/module.js"));

    // 共通関数モジュール・・・ここにパスを書けば関数の追加が可能
    saveData("util",szMACROFOLDER + "/common/util.js");
})();

```

### module.jsの用意

* Cookieに読み込んだファイル情報やパスを元に読み込み処理を実現する。
* module.load('util')等で、呼び出しができる。
* パスの構成上、confフォルダ配下に作成してください。

```conf/module.js
(function(){
    module = {}

    var adTypeText = 2;
    var adReadAll = -1; //全行モード
    var adReadLine = -2; //行単位モード
    var adCRLF = -1;
    module.load = function(keyId){
        var filePath = Editor.GetCookie("document",keyId);
        if(!filePath){
            throw new Error("failed to load module!!["  + keyId + "]");
        }
        eval(module.readAll(filePath));
    }
    
    /**
    *   全行取得 
    *   KB単位のファイルに使用。
    **/
    module.readAll = function(filePath,charset){
      var stream = new ActiveXObject("ADODB.Stream");
      stream.type = adTypeText;
      stream.charset = charset || 'Shift-jis';
      stream.LineSeparator = adCRLF;
      stream.open();
      stream.loadFromFile(filePath);
      try{
          var text = stream.readText(adReadAll);
      }finally{
          stream.close();
      }
      return text;
    }

    /**
    * 行単位に取得
    * 大容量でも処理可能
    */
    module.readLines = function(filePath,charset,callBack) {
      var stream = new ActiveXObject("ADODB.Stream");
      stream.type = adTypeText;
      stream.charset = charset || 'Shift-jis';
      stream.open();
      stream.LineSeparator = adCRLF;
      stream.loadFromFile(filePath);
      try{
          var count = 1; // 行番号
          while (!stream.EOS) {
            callBack(stream.readText(adReadLine),count++);
          }
      }finally{
          stream.close();
      }
    }
})();
```

### 呼び出される共通処理の定義
* パスの構成上、common配下に作成してください。

```common/util.js
(function(){
    var splitValue = "\t"
    util = {}
    
    util.topUpper = function(value){
        var head = value.substring(0,1).toUpperCase();
        var body = value.substring(1,value.length);
        return head + body;
    }
    util.topLower = function(value){
        var head = value.substring(0,1).toLowerCase();
        var body = value.substring(1,value.length);
        return head + body;
    }
    //単語単位のコブ文字を生成
    var humpType = function(value,key){
        var arrayParam = value.split(key);
        var strResult = "";
        for (var i = 0; i < arrayParam.length; i++){
            var head = arrayParam[i].substring(0, 1).toUpperCase()
            var inner = arrayParam[i].substring(1, (arrayParam[i].length)).toLowerCase();
            strResult += head + inner;
        }
        return strResult;
    }
    
    util.isSnake = function(value){
        if(!util.isSplit(value)){
            return false;
        }
        return value.indexOf("_") >= 0;
    }
    util.isKebab = function(value){
        if(!util.isSplit(value)){
            return false;
        }
        return value.indexOf("-") >= 0;
    }
    
    util.isSplit = function(value){
        if(value.indexOf(splitValue) >= 0){
            return false;
        }
        return true;
    }
    util.isHump = function(value){
        if(!util.isSplit(value)){
            return false;
        }
        //大文字小文字が混在しているか
        return value.toUpperCase () != value && value.toLowerCase();
    }
    var humpCalc = function(value_,func){
        var value = value_;
        var snake = util.isSnake(value);
        var kebab = util.isKebab(value)
        if(snake || kebab){
            if(snake){
                value = humpType(value,"_");
            }
            if(kebab){
                value = humpType(value,"-");
            }
        } else {
            value = util.isHump(value) ? value : func(value.toLowerCase());
        }
        value = func(value)
        return value
    }
    var splitCalc = function(value,callBack){
        var list = value.split(splitValue);
        var result = [];
        for(var i=0;i < list.length;i++){
            // result.push(humpCalc(list[i],func));
            result.push(callBack(list[i]));
        }
        return result.join(splitValue);
    }
    
    util.camel = function(value){
        return splitCalc(value,function(value){
            return humpCalc(value,util.topLower);
        });
    }
    util.pascal = function(value){
        return splitCalc(value,function(value_){
            return humpCalc(value_,util.topUpper);
        });
    }
    util.snake = function(value){
        return splitCalc(value,function(value_){
            if(util.isSnake(value_)){
                return value_.toLowerCase()
            }else if(util.isKebab(value_)){
                return value.split("-").join("_").toLowerCase();
            }else{
                var tmpStr = value_.substring(0, 1) + value_.substring(1).replace(/([A-Z])/g, "_$1");
                return tmpStr.toLowerCase(); //　一旦小文字化してしまう
            }
        });
        
    }
    util.setter = function(value){
        return splitCalc(value,function(value_){
            return "set" + util.pascal(value_);
        });
    }
    util.getter = function(value){
        return splitCalc(value,function(value_){
            return "get" + util.pascal(value_);
        });
    }
    
    util.convertMember = function(value){
        var obj = {};
        obj.camel  = util.camel(value);
        obj.pascal = util.pascal(value);
        obj.snake  = util.snake(value);
        obj.set    = util.setter(value);
        obj.get    = util.getter(value);
        return obj;
    }
    
    util.template = function(obj){
        if(!(obj instanceof Function)){
            return null;
        }
        var list = obj.toString().split("\r\n");
        list.pop();
        list.shift();
        return list.join("\r\n")
    }

    util.maxLineCount = function(){
        return Editor.GetLineCount(0) + 1;
    }
    
    
    util.loopC = function(callBack){
        var maxCount = util.maxLineCount();
        var results = [];
        for(var i=1;i < maxCount;i++){
            var origin = Editor.GetLineStr(i);
            var strLine = origin.split(/[\r\n]/g).join('')
            var value = callBack(strLine,i,origin);
            if(value === false){
                break;
            }
            results.push(value);
        }
        return results;
    }
    util.loop = function(callBack){
        var maxCount = util.maxLineCount();
        var results = [];
        for(var i=1;i < maxCount;i++){
            var origin = Editor.GetLineStr(i);
            var strLine = origin.split(/[\r\n]/g).join('')
            var value = callBack(strLine,i,origin)
            results.push(value);
        }
        return results
    }
    
    
    puts = function(value){
        Editor.TraceOut(value);
        //常に表示
        Editor.ActivateWinOutput();
    }
        
    echo = function(value){
        Editor.TraceOut(value,0x02);
        //常に表示
        Editor.ActivateWinOutput();
    }
})();
```

## Cookie.jsの設定

* 今回のキモとなる部分で、設定しだいではうまく動かない。
* Cookie.js自体は自動実行機能を用いて実行される。
* 実行された結果、Cookieに必要な情報を格納しておく。
* 各マクロを実行時にそのCookieを参照して実行する流れ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/53550/14cd1c6d-24f1-0079-ab32-8ebaa4b1c724.png)

## 実際に実行する

```hoge.js
eval(Editor.GetCookie("document","module"))
module.load("util");


// sample code
var list = util.loopC(function(value,index,original){
	return value;
});

puts(list);
```

### 実行結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/53550/67c95e64-006e-0b28-d290-6903a9291dc2.png)


### json2.jsを試す。
* jScriptだけだと、JSONのparseやstringfyが出来ません。（IE上は出来たか忘れた）
* よって、JSONのポリフィルのjson2.jsを入れることで、JSONの操作が可能となります。

### jsを任意フォルダに配置する
https://gist.githubusercontent.com/atheken/654510/raw/51f9363689d9c7764f6fab31581103f95d0e5cff/json2.js



```js:conf/cookie.js 抜粋
(function(){
    //マクロホームフォルダの取得
    var szMACROFOLDER = loadMacroHomeDir();
    // load用モジュール
    saveData("module",readAll(szMACROFOLDER + "/conf/module.js"));

    // 共通関数モジュール・・・ここにパスを書けば関数の追加が可能
    saveData("util",szMACROFOLDER + "/common/util.js");
    // 以下を追加
    saveData("util",szMACROFOLDER + "/common/json2.js"); // pathは配置したパスに設定してください
})();

```
### hoge.jsを修正

```js:hoge.js
eval(Editor.GetCookie("document","module"))
module.load("util");
module.load("JSON"); // loadを追加

// sample code
var list = util.loopC(function(value,index,original){
	return value;
});

var str = JSON.stringify(list,null,3)
puts(str);

var ary = JSON.parse(str);
puts(ary);
```

### 実行結果

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/53550/25e28aeb-a56a-a749-d70b-293b24242a57.png)


### 補足

* Cookieの呼び出しで気をつける点として、アウトプットウィンドウ上だと自動実行されない仕様らしく読み込まれないため、エラーになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/53550/70d0fb14-62d2-815c-db78-7cf522a04843.png)

* 一応、Cookieさえ読めれば動くので、Cookieマクロを実行すれば実行はできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/53550/035aafd8-f5df-8172-e119-fbf783cbaba5.png)