2017年1月26日木曜日

TensorFlowの可視化機能を使ってみる

まずは、可視化用のソースコードを準備しました。Tutrialで用いられているソースコードを少々いじり、
扱いやすいコードにしました。
https://tensorflow.googlesource.com/tensorflow/+/master/tensorflow/g3doc/tutorials/mnist/fully_connected_feed.py

可視化用ソースコード

import tensorflow as tf
import time
import input_data
import math
 
BATCH_SIZE = 150
LOGDIR = '/tmp/mnist_data_log'
 
def placeholder_inputs(batch_size):
         input_placeholder = tf.placeholder(tf.float32, shape=[BATCH_SIZE,784])
         label_placeholder = tf.placeholder(tf.float32,shape=[BATCH_SIZE,10])
 
         return input_placeholder,label_placeholder
 
def fill_feed_dict(data_set, images_pl, labels_pl):
         images_feed, labels_feed = data_set.next_batch(BATCH_SIZE)
         feed_dict = {
         images_pl: images_feed,
         labels_pl: labels_feed
         }
         return feed_dict
 
def loss_function(y_,y):
         return -tf.reduce_sum(y_*tf.log(y))
 
def full_connect_model(images):
         # hidden1
         with tf.name_scope("hidden_layer1") as scope:
                 weights = tf.Variable(tf.truncated_normal([784, 500],
                            stddev=1.0 / math.sqrt(float(784))),name='weights')
                 biases = tf.Variable(tf.zeros([500]),name='biases')
 
                 hidden1 = tf.nn.sigmoid(tf.matmul(images, weights) + biases)
         # hidden2
         with tf.name_scope("hidden_layer2") as scope:
                 weights = tf.Variable(tf.truncated_normal([500, 300],
                            stddev=1.0 / math.sqrt(float(500))),name='weights')
                 biases = tf.Variable(tf.zeros([300]),name='biases')
 
                 hidden2 = tf.nn.sigmoid(tf.matmul(hidden1, weights) + biases)
         # softmax layer
         with tf.name_scope("softmax_layer") as scope:
                 weights = tf.Variable(tf.truncated_normal([300, 10],
                            stddev=1.0 / math.sqrt(float(300))),name='weights')
                 biases = tf.Variable(tf.zeros([10]),name='biases')
 
                 logits = tf.nn.softmax((tf.matmul(hidden2, weights) + biases))
         return logits
 
def training(loss):
         tf.scalar_summary(loss.op.name, loss)
 
         optimizer = tf.train.GradientDescentOptimizer(0.01)
         #global_step = tf.Variable(0, name='global_step', trainable=False)
         train_op = optimizer.minimize(loss)
 
         return train_op
 
def run_training():
         data_sets = input_data.read_data_sets('MNIST_data',one_hot=True)
 
         with tf.Graph().as_default():
                 images_placeholder, labels_placeholder = placeholder_inputs(BATCH_SIZE)
                 logits = full_connect_model(images_placeholder)
 
                 loss = loss_function(labels_placeholder,logits)
 
                 sess = tf.Session()
                 train_step = training(loss)
 
                  # get the summary data.
                 summary_op = tf.merge_all_summaries()
 
                 sess.run(tf.initialize_all_variables())
 
                 summary_writer = tf.train.SummaryWriter(LOGDIR,
                                                             graph_def=sess.graph_def)
                  for step in xrange(20000):
                          start_time = time.time()
                          feed_dict = fill_feed_dict(data_sets.train,
                                                           images_placeholder,
                                                           labels_placeholder)
                          duration = time.time() - start_time
 
                          _, loss_value = sess.run([train_step, loss],
                                                         feed_dict=feed_dict)
 
                          duration = time.time() - start_time
 
                          if step % 100 == 0:
                                   print('Step %d: loss = %.2f (%.3f sec)' % (step, loss_value, duration))
                                   # Update the events file.
                                   summary_str = sess.run(summary_op, feed_dict=feed_dict)
                                   summary_writer.add_summary(summary_str, step)
 
if __name__ == '__main__':
         run_training()

ソースコード解説

前回と似ている箇所はすっ飛ばし(前回の記事を御覧ください)、今回の必要な部分について解説します。

グラフの定義です。

§  with tf.Graph().as_default():の内部にモデルを記述し、セッションにinputすることによりグラフのデータを保存することができます。

§  full_connect_modelで構築するモデルの中でname_scopeを使用しています。これはGRAPHで表示するのに使用します。

§  tf.merge_all_summaries()にて、単一のオペレーションを合体し、サマリデータを取得するのだそうです。

§  SummaryWriterを使って、情報をディレクトリに書き留めています。この宣言時に現在のグラフを記録しています。

§  100stepごとに、結果を記録する作業を行っています。

可視化

さて、上記の内容を可視化する場合にはTensorBoardと呼ばれる可視化機能を
使うことができます。
実際に使ってみましょう。いくらか方法があるそうです。以下は、https://github.com/tensorflow/tensorflow/tree/master/tensorflow/tensorboard:title:公式より抜粋。

python tensorflow/tensorboard/tensorboard.py --logdir=path/to/logs
# if installed via pip
tensorboard --logdir=path/to/logs
 
# if building from source
bazel build tensorflow/tensorboard:tensorboard
./bazel-bin/tensorflow/tensorboard/tensorboard --logdir=path/to/logs

私はPyEnvを使っているせいなのかわからず、うまくいきませんでした。find
検索をかけて探してそのコマンドを使っています。
今回の方法では2つの機能を扱えます。一つは、EVENT機能、もうひとつはGRAPH機能です

EVENT


時系列ごとのイベントの表示も可能です。実際にこのイベントの解析をするのにはよいでしょうか
x
軸を変更することができます。STEPRELATIVEWALLです。yは誤差ですね。

STEP:回数
RELATIVE:

WALL:
時間推移

学習時間どれくらいかかるのか、何回のステップで計算は十分だったのかのようなことを知ることができるでしょう。
Validation
EVENTも同時に記述できると、便利ですね。(多分できると思いますが・・)

GRAPH

自分が作成したネットワーク構造を可視化することができます。
ニューラルネットワークの層に名前をつけましたが、その名前の表示もされています。
中を展開することができます。試しに隠れ層(hidden_layer1)を展開してみました。

この中を見ると、計算のフローが書いてあります。
hidden_layer1
の中のforward部分を解読すると次のように計算していることが見て取れると思います。

1.    weightsMatMulを行う。

2.    上記のに、biasを足す。

3.    sigmoidの計算をする。

これを見ると全体がどんな感じで流れているのか、その内部で何を計算しているのかをひと目で確認することができます。

 

2017年1月25日水曜日

jQuery UI Widget(プラグイン)の作り方について

ウィジェット作成のための前準備

当然ですが jQuery UI のプラグインなのだから、作成には jQuery エンジンだけでなくjQuery UI ライブラリが必要となります。

手順A | CDN で手軽にロードする

学習目的やちょっとしたテクニカル調査といった場合は、わざわざファイルをダウンロードするのも大げさなので、CDN (コンテンツ・デリバリ・ネットワーク)を利用させてもらうのが妥当です。

jQuery UI オフィシャルページのフッター部分に必要なURLがすべて記載されているので、これらをアナタが作成する HTML ファイルにコピペすれば準備完了です。

このような感じでHTML ファイルのヘッダに記述すればOK

1

2

3

4

5

6

7

8

9

<!DOCTYPE html>

<html>

<head>

<!-- <meta charset="UTF-8"> -->

<title>jQuery UI Widget Sample</title>

<link rel="stylesheet" href="//code.jquery.com/ui/1.10.0/themes/base/jquery-ui.css">

<script src="//code.jquery.com/jquery-1.9.0.js"></script>

<script src="//code.jquery.com/ui/1.10.0/jquery-ui.js"></script>

</head>

手順B | 必要なコンポーネントのみ選択してダウンロード

CDN を利用すれば実ファイルを手元に置くことなく URL HTML 内で指定するだけで使用することができます。また、とりあえず jQuery UI コンポーネントフルセットをダウンロードしておけば、余計な考慮などをせずに間違いなく全ての機能を使用することができます。しかし本来なら必要のないコンポーネントも含めてダウンロードしてしまうのは、Web コンテンツ作成において必ずしもスマートな手法とは言えません。ページの読み込み速度を1ミリ秒でも速くするために、不要なコードは1バイトでも削るべきです。

jQuery UI のダウンロードページは必要な機能のみを選択して、それのみが実装されたファイルを生成することができます。

今回のところは $.widget のみが使用出来れば良いので、Core UI widget p にのみチェックをけてダウンロードします。

バージョンは1.10.0を選択します。

ダウンロードした zip ファイルを解凍すると、こんな感じになっています。jQuery UI が対応している jQuery の最新版もセットになっています。

参考までに、今回のように Core UI widget のみを選択した場合とフルセットでダウンロードした場合のファイルサイズは以下のようになりました。

jQuery UI - ファイルサイズ比較

Core UI Widget のみ

フルセット

通常版

15 KB

433 KB

Minified

7 KB

227 KB

jQuery UI Widget - 事始め

ノーマルな jQuery プラグイン(以下、ノーマルプラグイン) jQuery UI Widget とでは記述ルールにかなりの違いがあります。簡単なコードで双方の構造を比較してみます。

ノーマルプラグインの書き方

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

;(function($) {

    //このPluginの名前 - (1)

    $.fn.myPlugin = function(options) {

        //要素をいったん変数に退避 - (2)

        var elements = this;

 

        // 渡されたオプションとデフォルトをマージ - (3)

        var opts = $.extend({}, $.fn.myPlugin.defaults, options);

 

        // 要素をひとつずつ処理 - (4)

        elements.each(function() {

            ・・・

        });

 

        // method chain用に要素を返す - (5)

        return this;

    };

 

    // プラグインのデフォルトオプション - (6)

    $.fn.myPlugin.defaults = {

        param: 'value',

        ・・・

    };

})(jQuery);

大まかな構造はこんな感じです。

1.       $.fn オブジェクトをプラグイン名(カスタム jQuery メソッド)で拡張

2.       this にはプラグイン呼び出し時に指定された要素が格納されており、これを変数に退避しておく thisをそのまま使用しても構いません

3.       プラグイン内で定義したデフォルトのオプションと呼び出し時に渡されたオプション値をマージする

4.       指定された要素全てに処理が適用されるように、each() を使って1つずつに同様の処理を行う

5.       method chain が途切れないように、最後にreturn this を書く

6.       プラグイン内にデフォルトオプションを定義する

ノーマルプラグインの作り方に関しては、以下の記事にて詳しく解説しています。

o    jQueryプラグインの作り方について詳しく

では jQuery UI Widget の方はどうでしょうか。

jQuery UI Widget の書き方

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

;(function($) {

    // このウィジェットの名前(名前空間つき) ※1

    $.widget('ui.myPlugin', {

 

        //ウィジェットのデフォルトオプション

        options: {

            key1 : 'value'

        },

 

        // 一番最初に呼ばれる内部関数

        _create: function() {

            // セットアップ処理

            ・・・

        },

 

        // _create()後に呼ばれる内部関数

        _init: function() {

            ・・・

        },

         

        // オプションパラメータ変更時に呼ばれる内部関数

        _setOption: function(key, value) {

            // 変更したパラメータに応じて任意の処理を行う

            switch(key) {

                case 'key1':

                break;

            }

            $._super('_setOption', this, arguments);

        },

         

        // インスタンスを破棄

        destroy: function() {

            ・・・

            $._super('destroy', this, arguments);

        }

    });

})(jQuery);

もはや別物と言ってよいくらいにノーマルプラグインとは異なった構造化がなされています。

では実際にサンプルコードを書きながら一つずつ解説していきます。

jQuery UI Widget の第一歩

まずは最小規模なモノからはじめます。

ウィジェットの定義

1

2

3

4

// プラグインの定義

$.widget('ui.captionator', {

  // オプション、メソッドの定義

});

jQuery UI Widget  $.widget() というファクトリーメソッドを用いて作成します。第一引数に作成したいウィジェット名を名前空間付きで渡します。名前空間を付けないとエラーとなって作成できません。そして第二引数にウィジェットが持つオプションやメソッドといった機能を定義したオブジェクトを渡します。

おっと匿名関数でウィジェット全体をラップするのを忘れずに!

1

2

3

4

5

6

;(function($) {

  // プラグインの定義

  $.widget('ui.captionator', {

    // オプション、メソッドの定義

  });

})(jQuery);

ノーマルプラグインと同様に jQuery UI Widget においても全体を匿名関数でラップして堅牢な作りにします。1行目冒頭のセミコロンも忘れずに。

はじめての jQuery UI Widget

初期化メソッド

jQuery UI Widget には _create  _init という2種類の初期化メソッドが予め用意されており、これらはウィジェットのインスタンス生成時に1回ずつ自動で呼び出されます。

jquery.captionator1.js

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

;(function($) {

  // プラグインの定義

  $.widget('ui.captionator', {

    // 初期化処理として一番最初に呼び出される

    _create: function() {

      var self = this,  // ウィジェットインスタンスを変数に待避 - (※1)

        element = self.element; // 要素を変数に待避 - (※2)

      alert(element.attr('alt'));

    },

    // _create の次に呼び出される

    _init: function() {

      var self = this,

        element = self.element;

      alert(element.attr('title'));

    }

  });

})(jQuery);

まず最初に this を変数に待避していますが(1)$.widget() 第二引数の中での this ウィジェットオブジェクト自身となります。ノーマルプラグインの場合だと this には指定された要素が格納されていましたが、 jQuery UI Widget においては this.element に格納されています。

1

2

3

4

5

$('#hoge').myPlugin();

・・・

_create: function() {

  this.element; // => $('#hoge')

},

では jQuery セレクタで複数要素を選択した場合は?

ノーマルプラグインでは選択された要素は全て this に配列形式で格納されており、each() を使ってそれらに対して1つずつ処理を適用していくというフローとなっていました。

jQuery UI Widget において要素が複数選択されていた場合は、一つ一つの要素に対して _create _init が実行され、 this.element には処理対象となる要素のみが格納されます。

1

2

3

4

5

$('#hoge, #foo').myPlugin();

・・・

_create: function() {

  this.element; // 一回目は$('#hoge')、二回目は$('#foo')が格納される

},

このあたりは自前でループ処理を書くといった複数要素に対する配慮を気にしなくて良い作りになっており、作り手はより機能の作り込みに取り組むことが出来る優れた構造と言えます。

メソッドチェーンについて

ノーマルプラグインではメソッドチェーンを保つために、コードの最後にreturn this; を記述してjQueryオブジェクトを返すというセオリーが有りました。

一方 jQuery UI Widget ではこの辺りを内部的に処理してくれるので、return this を明記する必要はありません。

sample1.html

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

<!DOCTYPE html>

<html>

<head>

<!-- <meta charset="UTF-8"> -->

<title>#1 | jQuery UI Widget</title>

・・・

<script src="js/jquery.captionator1.js"></script>

<script>

$(function() {

  $('img').captionator();

  $('img').captionator().fadeOut(); // メソッドチェーンも正常に動きます

});

</script>

</head>

<body>

・・・

  <div class="center">

    <img src="../common/img/myLogo.jpg" height="251" width="251" alt="wakamsha logo." title="本名は山田直樹です。" />

  </div>

 

</body>

</html>

o    View demo (sample #1)

jQuery UI Widget にオプションを渡す

オプションを渡すことで、ウィジェットの利便性を何倍にも高めることができます。当然ウィジェット側にもオプションのデフォルト値を定義するわけですが、ノーマルプラグインと違って $.widget() の第二引数内に options という名前で定義し、その中に必要なパラメータを設定します。

jquery.captionator2.js

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

;(function($) {

  // プラグインの定義

  $.widget('ui.captionator', {

    options: {

      backgroundColor: '#fff'

    },

    // 初期化処理として一番最初に呼び出される

    _create: function() {

      var self = this,  // ウィジェットインスタンスを変数に待避 - (※1)

        element = self.element, // 要素を変数に待避 - (※2)

        opts = self.options;  // マージ済みのオプションを変数に待避 - (※3)

 

      element.addClass('polaroid')

        .css('backgroundColor', opts.backgroundColor);

    }

  });

})(jQuery);

デフォルトオプションは上記のようにして定義します。ノーマルプラグインの場合だと、ユーザーが指定したオプションとデフォルトの値をマージする処理も自前で記述する必要がありましたが、 jQuery UI Widget ではコレに関しても内部で自動的に処理してくれます。

1

2

3

4

5

// オプションを指定

$('img').captionator({ backgroundColor: '#aeee00' });

 

// 指定しない

$('img').captionator();

o    View demo (sample #2)

また、オプションの値は以下の式で取得することができます。

1

$('img').captionator('option', 'backgroundColor'); // => #aeee00

パブリックメソッドとプライベートメソッド

jQuery UI Widget にはウィジェット内部からのみアクセス可能なプライベートメソッドと、外部からもアクセス可能なパブリックメソッドが用意されています。

ここまでに出てきた _create _init にはどちらもメソッド名の最初に_(アンダースコア)が付けられているのでプライベートメソッドとして定義され、付いていないモノに対してはパブリックメソッドとして定義されます。

以下はイメージ画像にキャプションを自動で生成して付与するというウィジェットのコードです。

jquery.captionator3.js

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

;(function($) {

  // プラグインの定義

  $.widget('ui.captionator', {

    // デフォルト値

    options: {

      location: 'bottom',

      color: '#fff',

      backgroundColor: '#000'

    },

    _captionClassName: 'ui-caption',

 

    // 初期化処理として一番最初に呼び出される

    _create: function() {

      var self = this,  // ウィジェットインスタンスを変数に待避 - (※1)

        element = self.element, // 要素を変数に待避 - (※2)

        opts = self.options;  // マージ済みのオプションを変数に待避 - (※3)

 

      element.addClass('polaroid').wrap('<div class="ui-caption-container"/>');

      var container = element.parent().width(element.width()),

        cap = $('<span/>').text(element.attr('alt'))

          .addClass(this._captionClassName)

          .css({

            backgroundColor: opts.backgroundColor,

            color: opts.color,

            width: element.width()

          }).insertAfter(element),

        capWidth = element.width() - parseInt(cap.css('padding')) * 2,

        capHeight = cap.outerHeight();

      cap.css({

        width: capWidth,

        top: self._setTopCoordinate(opts.location, element, capHeight),

        left: (element.outerWidth() - element.width()) / 2

      });

    },

     

    // キャプションのtop値を算出

    _setTopCoordinate: function(value, element, capHeight) {

      var offset = (element.outerHeight() - element.height()) / 2;

      return (value === 'top') ? 0 : element.outerHeight() - offset - capHeight;

    },

 

    // キャプションのパラメータ値を取得

    getCaptionAttr: function(keys) {

      var vals = [],

        cap = this.element.next();

      for (var i=0, len=keys.length; i<len; i++) {

        vals.push($(cap).css(keys[i]));

      }

      return vals;

    }

  });

})(jQuery);

_setTopCoordinate() はプライベート、getCaptionAttr() はパブリックとしてそれぞれ定義しています。パブリックメソッドを外部から呼び出すには以下のように記述します。

sample3.html (JS部分のみ)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

$(function() {

  $('img').captionator({

    backgroundColor: '#aeee00'

  });

 

  $('#getCapParamBtn').click(function(e) {

    var keys = new Array('width', 'height', 'backgroundColor', 'color'),

      vals = $('img').captionator('getCaptionAttr', keys),

      $valueList = $('#valueList');

    for (var i=0, len=vals.length; i<len; i++) {

      $valueList.append($('<li/>').text(vals[i]));

    }

  });

});

jQuery UI Widget でインスタンス化した後、第一引数にパブリックメソッド名、第二引数以降にそのメソッドに必要な引数を指定することで、パブリックメソッドを呼び出すことができます。また、コレに関しても自動的にjQUeryオブジェクトを返してくれるので、メソッドチェーンは保たれます。

o    View demo (sample #3)

※補足その1 | プライベートメソッドを外部から呼び出す裏ワザ

以下のような記述でプライベートメソッドを外部から呼び出すという裏ワザがかつてありましたが、この方法は jQuery UI 1.10.0 以降使えなくなりました。

1

$('img').data('captionator')._setTopCoordinate();

裏ワザとはいえ、外部から呼び出せてしまってはプライベートメソッドの意味がないですし、そもそもなぜ今まで呼び出せていたのかが不思議なくらいです

destroy メソッド

破壊という名の通り内部で保持されたインスタンスが破棄され、パブリックメソッドといったウィジェットの API が全て使用できなくなります。

sample4.html (JS部分のみ)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

$(function() {

  $('img').captionator({

    backgroundColor: '#aeee00'

  });

 

  $('#getCapParamBtn').click(function(e) {

    var keys = new Array('width', 'height', 'backgroundColor', 'color'),

      vals = $('img').captionator('getCaptionAttr', keys),

      $valueList = $('#valueList');

    for (var i=0, len=vals.length; i<len; i++) {

      $valueList.append($('<li/>').text(vals[i]));

    }

  });

 

  $('#destroyBtn').click(function(e) {

    e.preventDefault();

    $('img').captionator('destroy');

  });

});

先の sample #3 destroy を呼び出す処理を追加しただけのものです。#getCapParamBtn をクリックする度にキャプションのスタイルを取得するというパブリックメソッドを呼び出す処理がありますが、 destroy メソッドを呼び出して実行すると、この処理が一切呼び出されなくなります。

o    View demo (sample #4)

disable enable

disable メソッドを呼び出すと、 jQuery UI Widget の実行対象となった要素に以下の2つのクラスが追加されます。

1.       "名前空間"-"ウィジェット名"-"disabled"

2.       "名前空間"-"ウィジェット名"-"state-disabled"

enable はこれら2つのクラスを要素から排除します。あくまでもクラスを付け外しするだけであって、APIが一時的に無効化されるといったことにはなりません。そのため以下のサンプルでは disable 時のクラスが付与されているかを評価してから処理を行うと記述しています。

sample5.html (JS部分のみ)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

$(function() {

  $('img').captionator({

    backgroundColor: '#aeee00'

  });

 

  $('#getCapParamBtn').click(function(e) {

    var disabledClassName = $('img').captionator('getDisabledClassName');

    if ($('img').hasClass(disabledClassName)) {

      console.log(disabledClassName);

      return false;

    }

    var keys = new Array('width', 'height', 'backgroundColor', 'color'),

      vals = $('img').captionator('getCaptionAttr', keys),

      $valueList = $('#valueList');

    for (var i=0, len=vals.length; i<len; i++) {

      $valueList.append($('<li/>').text(vals[i]));

    }

  });

 

  $('#destroyBtn').click(function(e) {

    e.preventDefault();

    $('img').captionator('destroy');

  });

 

  $('.btn-condition').click(function(e) {

    e.preventDefault();

    $('img').captionator($(this).text().toLowerCase());

  });

});

o    View demo (sample #5)

※補足その2 | いろいろなプロパティ

名前空間というのが再び出て来ましたが、jQuery UI Widget にはオプションや名前空間をはじめとした色々なプロパティが参照できます。

1

2

3

4

5

6

$.widget('ui.captionator', {

  ・・・

  this.namespace;     // => ui 名前空間

  this.widgetName;    // => captionator ウィジェット名

  this.widgetEventPrefix;   // => captionator イベント名に付く接頭辞

});

オプション値を更新する

インスタンス化した要素に対し、個々のオプション値が新たに渡される(更新)と、内部で _setOption メソッドが呼び出されます。

jquery.captionator6.js

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

;(function($) {

  // プラグインの定義

  $.widget('ui.captionator', {

    // デフォルト値

    options: {

      location: 'bottom',

      color: '#fff',

      backgroundColor: '#000'

    },

     

    ・・・・・・

 

    // オプションの値が変更されると呼び出される

    _setOption: function(key, value) {

      var element = this.element,

        cap = element.next();

      cap.css(key, value);

      this._super('_setOption', this, arguments);

    }

  });

})(jQuery);

メソッドには変更されたオプションの key value が渡され、実装者はコレを使って任意の処理を記述することができます。

o    View demo (sample #6)

イベントを定義する

イベントに関する機能も随分とお手軽に操作できるように作りこまれています。まずイベントの発火方法ですが、発火させたい箇所に以下の一行を記述します。

1

this._trigger(callbackName, [event], [uiObject]);

引数は以下の3つです。

callbackName

発火させるイベントの名前です。好きなのを付けられます。

event

イベントオブジェクトです。 イベントタイプの値はwidgetEventPrefix とイベント名を全て小文字にして結合したものになります。

uiObject

発火対象となるオブジェクトを格納します。

イベントが発火されると、options 内の callbackName に対応する関数が呼び出されます。

jquery.captionator7.js

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

;(function($) {

  // プラグインの定義

  $.widget('ui.captionator', {

    // デフォルト値

    options: {

      location: 'bottom',

      color: '#fff',

      backgroundColor: '#000',

      changed: function(e, cap) {

        console.log(e, cap)

      }

    },

 

    ・・・・・・

 

    // オプションの値が変更されると呼び出される

    _setOption: function(key, value) {

      var element = this.element,

        cap = element.next();

      cap.css(key, value);

      this._super('_setOption', this, arguments);

 

      // オプションの値変更後にイベントを発火

      this._trigger('changed', event, cap);

    }

  });

})(jQuery);

呼び出される関数はオプション内に設定されているので、呼び出し側からここで実行する処理を更に設定することができます。

sample7.html (JS部分のみ)

1

2

3

4

5

6

7

8

9

10

$(function() {

  $('img').captionator({

    backgroundColor: '#aeee00',

    changed: function(e, cap) {

      alert(e.type);

    }

  });

 

  ・・・・・・

});

o    View demo (sample #7)

jQuery UI Widget のオーバーライド

ゼロから大規模なウィジェットを作ったり、要件の仕様に適した必要最低限の機能のみを持つ軽量なウィジェットを作るのもアリですが、jQuery UI には既に数多くのウィジェットが標準コンポーネントとして用意されています。これらを拡張して独自のUIコンポーネントを作ることで、より少ないコストで開発を進めることが出来ます。

jquery.customDialog.js

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

;(function($) {

  // プラグインの定義 - オーバーライド

  $.widget('ui.customDialog', $.ui.dialog, {

    // デフォルトのオプションをオーバーライド

    options: {

      width: 500,

      height: 250,

      autoOpen: false

    },

    // createメソッドをオーバーライド

    _create: function() {

      // 継承元の_createメソッドを呼び出す

      this._super('_create', this, arguments);

 

      // コンテンツを入れ替える

      this.element.find('p').text('Hi! My name is Wakamsha. I am a Web depeloper. My favourite languages are HTML5, CSS3, JavaScript etc...');

    },

 

    // openメソッドをオーバーライド

    open: function() {

      // 継承元のopenメソッドを呼び出す

      this._super('open', this, arguments);

      // 非表示にしてフェードイン表示させる

      this.element.hide().slideDown(1000);

    },

     

    // closeメソッドをオーバーライド

    close: function() {

      if (confirm('Is it closing time?')) {

        this._super('close', this, arguments);

      }

    }

  });

})(jQuery);

$.widget() の第二引数に機能オブジェクトでなく継承したいウィジェットを指定します。以降はオーバーライドしたいメソッドを定義して任意の機能を記述していくだけですが、ただ単に自分のコードだけを書いていては継承元にあったオリジナルの処理が実行されないままになってしまいます。そこで13行目30行目にあるように _suer() を使って継承元のメソッドを呼び出すようにします。コレを忘れてしまうと正常に動作しなくなるので必ずつけるようにします。

o    View demo (sample #8)

※補足3 | バージョンによって異なる呼び出し方

上記のサンプルでは継承元のメソッドを呼び出すのに _super() を使っていましたが、これは jQuery UI 1.9 から実装された機能です。したがって 1.8x までのバージョンを使う必要がある場合は、以下のように書かなくてはなりません。

1

2

3

// 継承元の_createメソッドを呼び出す

$.ui.dialog.prototype._create.apply(this, arguments); // ※1.8までの書き方

this._super('_create', this, arguments);    // 1.9以降の書き方

おわりに

とりあえず思いつくままに jQuery UI Widget の基本的な内容を書き連ねてきました。これで全て網羅できたかどうか定かではありませんが、余程のことがない限りは当記事の内容を元に作ることは可能かと思います。