Ajax.PeriodicalUpdaterクラス

【抜粋】一部省略
Ajax.PeriodicalUpdater = Class.create();
Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
  initialize: function(container, url, options) {
(省略)
  },
(省略)
  onTimerEvent: function() {
    this.updater = new Ajax.Updater(this.container, this.url, this.options);
  }
});

Ajax.PeriodicalUpdaterクラスは定期的にデータを受信して表示するためのクラスです。Ajax.Baseクラスを継承しています。Ajax.Updaterクラスは継承していませんが、インスタンス生成時の引数を引き継いで、内部でAjax.Updaterのインスタンスを作成しています。

initializeメソッド

【抜粋】
  initialize: function(container, url, options) {
    this.setOptions(options);
    this.onComplete = this.options.onComplete;

    this.frequency = (this.options.frequency || 2);
    this.decay = (this.options.decay || 1);

    this.updater = {};
    this.container = container;
    this.url = url;

    this.start();
  },

Ajax.Baseクラスから継承したsetOptionsメソッドにより、optionsプロパティにメンバを登録しているのはAjax.Request、Ajax.Updaterクラスと同じです。

その後、options.onCompleteに登録されたメソッドを、onCompleteメソッドとして保持します。

Ajax.PeriodicalUpdaterの引数optionsのオブジェクトには、Ajax.Updaterと同じメンバを登録でき、更にfrequencyとdecayが登録できます。frequencyはリクエスト間隔秒数を指定します。デフォルトは2秒になります。decayは、受信データに変更がない場合にリクエスト間隔を広げるための値です。具体的には、受信データに変更がないとき、frequency*dedayが次のリクエストまでの秒数となり、次回さらに変更がなければ、(frequency*deday)*dedayが次のリクエストまでの秒数となります。これにより不要なトラフィックを軽減させることができます。(例は後述updateCompleteメソッドで記述)

次のupdaterプロパティには、後述onTimerEventメソッドで作成するAjax.Updaterクラスのインスタンスが格納されます。デフォルトを空オブジェクトにしているのは分かりやすくするためでしょうか。ただ、updaterのオブジェクトは有効に活用されておらず、意味が薄い気がします。

引数containerをcontainerプロパティに、引数urlをurlプロパティに格納します。後述onTimerEventメソッドでAjax.Updaterクラスのインスタンス生成時に使用します。

その後、startメソッドを呼び出します。

startメソッド

【抜粋】
  start: function() {
    this.options.onComplete = this.updateComplete.bind(this);
    this.onTimerEvent();
  },

options.onCompleteにupdateCompleteメソッドをbind*1を使用して登録しています。それからonTimerEventメソッドを呼び出しています。

options.onCompleteへの登録はinitializeメソッドでもいい気がするのですが。また、「Prototype.jsの使い方」ではstartメソッドは外部から呼ばれることはない、と書かれていますが、外部からでも呼ぶことはできると思います。その動作については、次のstopメソッドで述べます。

stopメソッド

【抜粋】
  stop: function() {
    this.updater.onComplete = undefined;
    clearTimeout(this.timer);
    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
  },

updaterプロパティは、後述onTimerEventメソッドで作成するAjax.Updaterクラスのインスタンスが設定されます。このonCompleteメソッドを初期化しています。が、前述したように、Ajax.Updaterクラス直下にはonCompleteメソッドは存在しませんし、登録手段もないと思います。ちょっと謎です・・・。

timerプロパティは、後述updateCompleteメソッドで作成するタイマ識別子です。これをclearTimeout関数で停止します。

最後にonCompleteメソッドを実行します。これはinitilizeでoptionsプロパティオブジェクトから保持したメソッドになります*2。stopメソッドの引数がそのまま渡されています。omCompleteメソッドがなければ何もしません。

【例】
[test.htm]
<html>
<head>
<title></title>
<script language="javascript" src="prototype.js" charset="utf-8"></script>
<script language="javascript">
<!--
var apu = null;
function test(){
  $('test').style.backgroundColor = '#00FFFF';
  apu = new Ajax.PeriodicalUpdater(
    'test',
    'test.jsp',
    { onComplete:function(req){
        $('test').style.backgroundColor = '#FFFF00';
      }
    });
}
//-->
</script>
</head>
<body>
<div id="test"></div>
<button onclick="test();">TEST</button>
<!--startメソッドを外部から呼び出してみる-->
<button onclick="if(apu)apu.start();">START</button>
<button onclick="if(apu)apu.stop();">STOP</button>
</body>
</html>
---------------------------------------------------------------
[test.jsp]
<%@ page contentType="text/plain;charset=utf-8" %>
<%@ page import="java.util.*, java.io.*, java.text.DateFormat" %>
<%
//キャッシュ無効化
Calendar today = new GregorianCalendar();
response.setDateHeader("Last-Modified", today.getTime().getTime());
response.setDateHeader("Expires", 0);
response.setHeader("Pragma","no-cache");
response.setHeader("Cache-Control","no-cache");

//現在時刻表示
out.print(
  DateFormat
  .getDateTimeInstance(DateFormat.FULL, DateFormat.FULL)
  .format(today.getTime())
);
%>

TESTボタンを押すと日付と時刻が表示されます。2秒ごとに更新されていきます。PeriodicalUpdaterに初期処理は登録できないので、インスタンス作成前に処理を記述するしかないと思います。ここではtest関数の最初で背景を水色にしています。STOPボタンを押すと停止します。onCompleteで背景を黄色にしています。再度TESTボタンを押すと再開します。

STARTボタンはstartメソッドのテスト用です。stop後、startボタンを押しても再開します。ただし、test関数は通っていないので背景は黄色のままです。

ここで実験。TEST関数を続けて2回押します。すると、2秒間隔の更新が1秒くらいになります。その後、stopボタンを2回押すと、2秒間隔に戻りますが、止まりません。これは変数apuが2回目にボタンを押したときのインスタンスとなり、最初のインスタンスへの参照がなくなるからです。

次に、TEST→STOP後、STARTボタンを2回押します。するとこちらも、2秒間隔の更新が1秒くらいになります。その後、stopボタンを2回押すと、ちゃんと止まります。これは変数apuのインスタンスが変わっていないからです。

実際には、上のコードは以下のようにしないと使い物にならないかもしれません。

【例】
[test.htm]
<html>
<head>
<title></title>
<script language="javascript" src="prototype.js" charset="utf-8"></script>
<script language="javascript">
<!--
var apu = null;
function test(){
  if(apu) return; //インスタンスがあるときは処理しない
  $('test').style.backgroundColor = '#00FFFF';
  apu = new Ajax.PeriodicalUpdater(
    'test',
    'test.jsp',
    { onComplete:function(req){
        $('test').style.backgroundColor = '#FFFF00';
        apu = null; //stopしたらインスタンスを消す
      }
    });
}
//-->
</script>
</head>
<body>
<div id="test"></div>
<button onclick="test();">TEST</button>
<!--startメソッドを外部から呼び出してみる-->
<button onclick="if(apu)apu.start();">START</button>
<button onclick="if(apu)apu.stop();">STOP</button>
</body>
</html>

test関数の先頭で変数apuがnullであるかを確認し、nullでなければ処理しません。onCompleteで変数apuをnullにしています。このとき、STARTボタンは使い物になりません(STOP後は動作せず。TEST後に使用するとSTARTを押した分はSTOPで止められなくなる)。

Ajax.PeriodicalUpdaterクラスを、ボタン等のユーザ起因のイベントで使う場合には注意が必要ですね。

updateCompleteメソッド

【抜粋】
  updateComplete: function(request) {
    if (this.options.decay) {
      this.decay = (request.responseText == this.lastText ?
        this.decay * this.options.decay : 1);

      this.lastText = request.responseText;
    }
    this.timer = setTimeout(this.onTimerEvent.bind(this),
      this.decay * this.frequency * 1000);
  },

options.decayプロパティがあり、かつ受信データが前回受信データと同じ場合は、decayプロパティにoptions.decayを積算して設定しています。受信データが前回と違えばdecayプロパティは1にします。lastTextプロパティに今回受信データを保持します。

onTimerEventメソッドを、setTimeout関数によりdecay*frequencyの秒数後に実行します。timerプロパティにそのタイマー識別子を保持します。

【例】Webサーバ経由でないとテストできません
[test.htm]
<html>
<head>
<title></title>
<script language="javascript" src="prototype.js" charset="utf-8"></script>
<script language="javascript">
<!--
var apu = null;
function test(){
  if(apu) return; //インスタンスがあるときは処理しない
  $('test').style.backgroundColor = '#00FFFF';
  apu = new Ajax.PeriodicalUpdater(
    'test',
    'test.txt',
    { frequency:1, //1秒間隔
      decay:2, //受信データに変更ない場合は間隔を2倍に伸ばしていく
      //onSuccessはAjax.Updaterの通信ごとに処理される
      onSuccess:function(){
        $('time').innerHTML = new Date();
      },
      //onCompleteのみ、Ajax.PeriodicalUpdaterのstop時に処理される
      onComplete:function(){
        $('test').style.backgroundColor = '#FFFF00';
        apu = null; //stopしたらインスタンスを消す
      }
    });
}
//-->
</script>
</head>
<body>
<div id="test"></div>
<div id="time"></div>
<button onclick="test();">TEST</button>
<button onclick="if(apu)apu.stop();">STOP</button>
</body>
</html>
-----------------------------------------------------------
[test.txt]UTF-8で記述
Ajax.PeriodicalUpdaterのテスト

DIV要素testの内容は変わりませんが、DIV要素timeの内容は通信ごとに時刻が表示されます。1秒間隔が2秒、4秒、8秒・・・と倍に増えていきます。onSuccessメソッドはHTTPステータスコードがないと実行されないため、ローカル環境では動作しないので注意してください。

onTimerEventメソッド

【抜粋】
  onTimerEvent: function() {
    this.updater = new Ajax.Updater(this.container, this.url, this.options);
  }

前述startメソッドとupdateCompleteメソッドから呼ばれます。Ajax.updaterクラスのインスタンスを作成するメソッドです。インスタンスはupdaterプロパティにに上書き保存されていきます。ただ、これを有効に使用している場所が見当たらないのは前述したとおりです・・・。

*1:Functionクラスのメソッド

*2:Ajax.Updaterのメソッドではないです^^;