kintone PC・モバイル JSソース共通化の試作

kintone の JavaScript カスタマイズでは、PCとモバイル対応に別々のカスタマイズが必要になる。
PCとモバイルで、JavaScript ソースを共有できないか試作してみた。

# 概要

開発・テストがやりやすいので、サンプルプラグインをベースにソースを共有化を行います。

- はじめに、kintone のPCとモバイル対応のJavaScript カスタマイズの違いについて調査。
- 次に共通化の対応策を検討。
- 共通化の試作

# サンプルプラグインのソース

[create-pluginを使ってプラグインの雛形を作成しよう！](https://developer.cybozu.io/hc/ja/articles/360000877946) を使ってプラグインを生成すると作成される JavaScript ソースをベースにします。
処理内容は、一覧表示時にヘッダー部にメッセージを表示するものです。

PC版とモバイル版のソースを比較すると、イベント名と JavaScript API 名が異なります。
プラグイン関連の API は、共通ですね。
<img width="896" alt="2020-06-07_12h50_29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/2d44d9ba-3c27-c73e-7c86-467c03e8df3c.png">

|---|PC             |モバイル           |
|:---|:---------------|:-----------------|
|イベント名|app.record.index.show|mobile.app.record.index.show|
|API名|kintone.app.getHeaderSpaceElement|kintone.mobile.app.getHeaderSpaceElement|

ソースを共通化するには、この違いを吸収する必要があります。


# イベント名と JavaScript API 名の違い

[PC/スマートフォン 早見表](https://developer.cybozu.io/hc/ja/articles/202738940) に、まとめられています。

基本的には、PC版に'mobile' が付くとモバイル対応です。
一覧編集関連イベントがモバイル版では未サポートです。
あと「[URL内のクエリで、表示するレコードの条件を指定](https://developer.cybozu.io/hc/ja/articles/202388664) 」がなぜか、モバイル版にありませんが、これもサポートしてほしいですね。

# PC表示とモバイル表示の判定

モバイル版で未サポートの API があるので、モバイル表示では実行しないように判定をいれます。
ただし PC表示かモバイル表示かを判定する関数などがないので、独自に判定するしかありません。

- グローバル変数（cybozu.data.IS_MOBILE_DEVICE）で判定
    - モバイル表示の時に true、PC表示の時に FALSE
- URL に"/m/" があるかで判定

どちらも公式に公開されている情報ではないと思いますので、kintone の仕様変更のリスクが伴います。
同じようなリスクであれば、より簡単な cybozu.data.IS_MOBILE_DEVICE を使います。
また、仕様変更があっても影響が少なくて済むように、別の変数に入れておきます。

```.js
var ktcom = {
    IS_MOBILE: cybozu.data.IS_MOBILE_DEVICE,
}
...
if (ktcom.IS_MOBILE) {
// モバイル処理
}
else {
// PC処理
}

```

# イベント名の共通化

対応策として考えられるのは、PC版とモバイル版のイベント名を並べるか、どちらかのイベント名を入れた変数に置き換えるかです。
わかりやすさの点で「イベント名を変数に置き換える」ほうがよさそうです。

- PC版とモバイル版のイベント名を並べる

```.js
kintone.events.on(['app.record.index.show','mobile.app.record.index.show'], function() {
...
});
```

PC表示で、'mobile.app.record.index.show'があってもエラーにはならず発火もしませんが、処理するイベントが増えると長くなってソースが読みにくく間違いやすそうです。

- イベント名を入れた変数に置き換える

```.js
var index_show = ktcom.IS_MOBILE ? 'mobile.app.record.index.show' : 'app.record.index.show';
kintone.events.on(index_show, function() {
...
});
```

仮に INDEX_SHOW の変数に、PC・モバイル別にイベント名を入れてみましたが、すべてのイベントをまとめて入れておけば共通に使えそうです。


# JavaScript API の共通化

JavaScript API は、JavaScript 関数なので、変数に入れておけば、変数名で呼び出せます。
ktcom.app に、PC・モバイル対応のAPI を入れて、呼び出してみます。

```.js
var ktcom = {
    IS_MOBILE: cybozu.data.IS_MOBILE_DEVICE,
}
if (ktcom.IS_MOBILE) {
    ktcom.app = kintone.mobile.app;
}
else {
    ktcom.app = kintone.app;
}

var spaceElement = ktcom.app.getHeaderSpaceElement();
```


# まとめてグローバル変数化（ktcom1.js）

イベント名と API 名を共通化して、まとめてグロバール変数に入れて共有して使えるようにします。
ver は、複数のプラグインで異なるバージョンの共通化が使われていた時に、最新のバージョンを残すためのものです。

- 使用例
    - イベント名：　ktcom.ev.index_show
    - API名：　ktcom.app.getHeaderSpaceElement()

ktcom1.js のソース

```ktcom1.js
(function() {
    'use strict';

    const PC_EVENTS = {
        index_show:'app.record.index.show',
        index_edit: 'app.record.index.edit.show',
        index_edit_change: 'app.record.index.edit.change.',
        index_edit_submit: 'app.record.index.edit.submit',
        index_edit_submit_success: 'app.record.index.edit.submit.success',
        index_delete_submit: 'app.record.index.delete.submit',
        detail_show:'app.record.detail.show',
        detail_delete_submit: 'app.record.detail.delete.submit',
        detail_process_proceed: 'app.record.detail.process.proceed',
        create_show: 'app.record.create.show',
        create_change: 'app.record.create.change.',
        create_submit: 'app.record.create.submit',
        create_submit_success: 'app.record.create.submit.success',
        edit_show: 'app.record.edit.show',
        edit_change: 'app.record.edit.change.',
        edit_submit: 'app.record.edit.submit',
        edit_submit_success: 'app.record.edit.submit.success',
        print_show: 'app.record.print.show',
        report_show: 'app.record.report.show',
        portal_show: 'portal.show',
    }
    const MOBILE_EVENTS = {
        index_show:'mobile.app.record.index.show',
        index_edit: 'mobile.app.record.index.edit.show',
        index_edit_change: 'mobile.app.record.index.edit.change.',
        index_edit_submit: 'mobile.app.record.index.edit.submit',
        index_edit_submit_success: 'mobile.app.record.index.edit.submit.success',
        index_delete_submit: 'mobile.app.record.index.delete.submit',
        detail_show:'mobile.app.record.detail.show',
        detail_delete_submit: 'mobile.app.record.detail.delete.submit',
        detail_process_proceed: 'mobile.app.record.detail.process.proceed',
        create_show: 'mobile.app.record.create.show',
        create_change: 'mobile.app.record.create.change.',
        create_submit: 'mobile.app.record.create.submit',
        create_submit_success: 'mobile.app.record.create.submit.success',
        edit_show: 'mobile.app.record.edit.show',
        edit_change: 'mobile.app.record.edit.change.',
        edit_submit: 'mobile.app.record.edit.submit',
        edit_submit_success: 'mobile.app.record.edit.submit.success',
        print_show: 'mobile.app.record.print.show',
        report_show: 'mobile.app.record.report.show',
        portal_show: 'portal.show',
    }

    var wkcom = {
        ver: 1,
        IS_MOBILE: cybozu.data.IS_MOBILE_DEVICE,
    }

    if (wkcom.IS_MOBILE) {
        wkcom.ev = MOBILE_EVENTS;
        wkcom.app = kintone.mobile.app;
    }
    else {
        wkcom.ev = PC_EVENTS;
        wkcom.app = kintone.app;
    }

    if (!window.ktcom || window.ktcom.ver < wkcom.ver) {
        window.ktcom = wkcom;
    }

  })();

```

# JS ソース共通化

ktcom1.js を使った、イベント処理の例です。

```common.js
jQuery.noConflict();

(function($, PLUGIN_ID) {
  'use strict';

  kintone.events.on(ktcom.ev.index_show, function(event) {
    var config = kintone.plugin.app.getConfig(PLUGIN_ID);

    var spaceElement = ktcom.app.getHeaderSpaceElement();
    var fragment = document.createDocumentFragment();
    var headingEl = document.createElement('h3');
    var messageEl = document.createElement('p');

    messageEl.classList.add('plugin-space-message');
    messageEl.textContent = config.message;
    if (ktcom.IS_MOBILE)
        messageEl.textContent += ' モバイル表示';
    else
        messageEl.textContent += ' PC表示';
    headingEl.classList.add('plugin-space-heading');
    headingEl.textContent = 'Hello kintone plugin!';

    fragment.appendChild(headingEl);
    fragment.appendChild(messageEl);
    spaceElement.appendChild(fragment);

    return event;
  });

})(jQuery, kintone.$PLUGIN_ID);
```

# manifest.json 例

PC版とモバイル版で、同じcommon.jsを利用しています。

~~~manifest.json
  "desktop": {
    "js": [
      "https://js.cybozu.com/jquery/3.3.1/jquery.min.js",
      "js/ktcom1.js",
      "js/common.js"
    ],
    "css": [
      "css/51-modern-default.css",
      "css/desktop.css"
    ]
  },
  "mobile": {
    "js": [
      "https://js.cybozu.com/jquery/3.3.1/jquery.min.js",
      "js/ktcom1.js",
      "js/common.js"
    ],
    "css": [
      "css/mobile.css"
    ]
  },
~~~

# PC表示・モバイル表示例

どちらも、ちゃんとヘッダー部に文字が表示されました。

<img width="978" alt="2020-06-07_15h22_20.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/3dc87f02-94fe-d3a3-c2c8-c870c025b5b6.png">


# あとがき

とりあえず、試作は出来ました。
実際の開発に使うかどうかは、しばらく検証してからですね。