Cordova(PhoneGap)上で jQueryMobile と RequireJS を組み合わせてみた

[GitHub サンプル](https://github.com/opengl-8080/cordova_requirejs)

Cordova の上で jQuery Mobile と RequireJS を組み合わせて使えるか試した。
一応動いた（ただし確認したのは Android だけ）。

データベースのところ特に力入れた。

```html:index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="format-detection" content="telephone=no" />
    <meta name="msapplication-tap-highlight" content="no" />
    <!-- WARNING: for iOS 7, remove the width=device-width and height=device-height attributes. See https://issues.apache.org/jira/browse/CB-4323 -->
    <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />
    <link rel="stylesheet" type="text/css" href="css/jquery.mobile.min.css" />
    <title>Hello World</title>
  </head>
  <body>
    <div data-role="page">
      <div data-role="header">
        <h1>header</h1>
      </div>
      <div role="main">
        <p id="console">
        </p>
      </div>
    </div>

    <script type="text/javascript" src="cordova.js"></script>
    <script type="text/javascript" src="js/lib/require.js" data-main="js/index"></script>
  </body>
</html>
```

普通に `require.js` を読み込んで、 `data-main` に `index.js` を指定。

```js:index.js
requirejs.config({
    paths: {
        'jquery': 'lib/jquery.min',
        'jquery-mobile': 'lib/jquery.mobile.min'
    }
});

require(['root', 'database', 'jquery-mobile'], function(root) {
    root.deviceready(function() {
        root.log('Hello Cordova with RequireJS!!');

        root.db.readTransaction(function(tx) {
            root.deferredTransaction(tx)
                .first('SELECT * FROM SAMPLE_TABLE')
                .done(function(exec, result) {
                    for (var i=0; i<result.rows.length; i++) {
                        var row = result.rows.item(i);
                        root.log('[database rows] id=' + row.ID + ', message=' + row.MESSAGE);
                    }
                });
        });
    });
});
```

先頭で `jQuery` と `jQuery Mobile` のパスを定義して、
`require()` でデータベースの初期化をしているモジュール（`database`）と `jquery-mobile` を依存対象として定義。

```js:root.js
define(['jquery'], function($) {
    return {
        log: function(message) {
            // weinre console is not work!!
            $('#console').append(message + '<br>');
            // console.log(message);
        },

        deviceready: function(callback) {
            var root = this;
            document.addEventListener('deviceready', function() {
                try {
                    callback();
                } catch (e) {
                    root.log('error : ' + e);
                }
            });
        },

        deferredTransaction: function(transaction) {
            var root = this;
            exec.first = executeSql;

            return exec;

            function exec(sql, params) {
                return function(exec, result) {
                    return exec.first(sql, params);
                };
            }

            function executeSql(sql, params) {
                var d = new $.Deferred();

                root.log('execute sql : ' + sql + ', params=' + params);

                transaction.executeSql(
                    sql,
                    params || [],
                    function(tx, result) {
                        d.resolve(root.deferredTransaction(tx), result);
                    },
                    function(tx, result) {
                        d.reject(root.deferredTransaction(tx), result);
                    }
                );

                return d.promise();
            }
        }
    };
});
```

`root` は、共通処理とかをまとめたオブジェクトを定義したモジュール。

標準のデータベース API はコールバック地獄必至なので、 jQuery の Deferred を使ってシンプルに書けるように頑張った（`deferredTransaction()`）。

```js:database.js
define(['root', 'jquery'], function(root, $) {
    root.deviceready(function() {
        root.log('initializing database...');

        root.db = openDatabase('sampledb', '', 'Sample Database', 1024 * 1024 * 10);

        root.db.transaction(
            function(tx) {
                var exec = root.deferredTransaction(tx);

                exec.first('DROP TABLE IF EXISTS SAMPLE_TABLE')
                    .then(exec('CREATE TABLE SAMPLE_TABLE (ID INTEGER PRIMARY KEY, MESSAGE)'))
                    .then(exec('INSERT INTO SAMPLE_TABLE (MESSAGE) VALUES (?)', ['Hoge']))
                    .then(exec('INSERT INTO SAMPLE_TABLE (MESSAGE) VALUES (?)', ['Fuga']))
                    .then(exec('INSERT INTO SAMPLE_TABLE (MESSAGE) VALUES (?)', ['Piyo']))
                    .then(exec('SELECT * FROM SAMPLE_TABLE'))
                    .then(function(exec, result) {
                        root.log('count=' + result.rows.length);
                        return exec.first('DELETE FROM SAMPLE_TABLE WHERE MESSAGE=?', ['Fuga']);
                    })
                    .done(function() {
                        root.log('success to initialize database');
                    })
                    .fail(function(exec, result) {
                        root.log('fail to initialize database : ' + JSON.stringify(result));
                    });
            },
            function(error) {
                root.log('fail to initialize database');
            });

    });
});
```

**画面の様子**

![cordova_requirejs.png](https://qiita-image-store.s3.amazonaws.com/0/28302/1c89ae0d-64f0-70e7-13f2-7a1e1fcf348c.png)



結論： **Deferred って素晴らしい。**