Railsアプリにtimelineを組みこむ
レビュアブルマインドにSIMILE | Timelineを入れてみたので、そのメモ。
ダウンロードと組みこみ
$ svn checkout http://simile.mit.edu/repository/timeline/
timeline/tags/1.2/src/webapp/api 以下を public/javascript/timelineにコピー
layoutの修正
%= javascript_include_tag 'timeline/api/timeline-api' if @need_timeline
bodyタグに、onloadとonresizeを入れる。
% onload = onresize = nil % if @need_timeline % onload = "onLoad();" % onresize = "onResize();" % end <<body :| Attr[:onload=>:onload, :onresize=>:onresize ] <
コントローラの修正
ページ全体を表示する timeline というメソッドと、スケジュール項目をtimeline-apiの指定する形式のXMLで返す schedules という二つメソッドを追加。
class SchedulesController < ApplicationController before_filter :authorize around_filter :with_users_scope def timeline @need_timeline = true end def schedules @schedules = @user.schedules response.content_type = Mime::XML render :partial=>'schedules' end end
ビューの修正
ビューは、タイムラインを表示するdivと二つのjavascript関数定義です。
<<div< <<div< This is an experimental feature. <div id="my-timeline" style="height: 400px; border: 1px solid #aaa"></div> <<%< <script type="text/javascript"> (onLoadとonResizeとイベントハンドラを定義、下記参照)
javascript部分は、ほとんど本家のチュートリアルのまる写しですが、Timeline.loadXMLという所のパスだけ、今回定義したアクションに変更しました。
// (schedule/timeline.a2html) var tl; function onLoad() { var eventSource = new Timeline.DefaultEventSource(); var bandInfos = [ Timeline.createBandInfo({ eventSource: eventSource, width: "70%", intervalUnit: Timeline.DateTime.DAY, intervalPixels: 100 }), Timeline.createBandInfo({ eventSource: eventSource, showEventText: false, trackHeight: 0.5, trackGap: 0.2, width: "30%", intervalUnit: Timeline.DateTime.MONTH, intervalPixels: 200 }) ]; bandInfos[1].syncWith = 0; bandInfos[1].highlight = true; tl = Timeline.create(document.getElementById("my-timeline"), bandInfos); Timeline.loadXML("schedules", function(xml, url) { eventSource.loadXML(xml, url); }); } var resizeTimerID = null; function onResize() { if (resizeTimerID == null) { resizeTimerID = window.setTimeout(function() { resizeTimerID = null; tl.layout(); }, 500); } }
// (schedule/_schedules.a2html) <<data< <<event :schedules | ModuleExtendFilter[TaskHelper] | Attr[:start=>:sch_date, :title=>:text, :taskId=>:id] < <<:text>>
このビューがこんな感じのXMLを生成します。
<data> <event title="ロードマップ作成" taskId="428" start="Mon Jan 14 00:00:00 2008">ロードマップ作成</event> <event title="日本 Ruby 会議 2008 - FrontPage" taskId="217" start="Sun Feb 17 00:00:00 2008">日本 Ruby 会議 2008 - FrontPage</event> ... </data>
taskIdという属性値は、この後のカスタマイズの為に設定しています。
クリックした時の表示内容をカスタマイズ
以上で、timeline自体は表示されますが、そのままだとtimeline内のイベントをクリックした時に、アプリケーション側に制御を渡すことなく、既定のポップアップウィンドウでevent要素のデータが表示されてしまいます。
そこで、イベントハンドラを入れ替えて、アプリケーション側で指定した処理をさせています。
Timeline.DurationEventPainter.prototype._onClickInstantEvent = function(icon, domEvt, evt) { var task = evt._node.getAttribute("taskId") ; new Ajax.Updater('task_work', '../main/show_task_window/' + task.toString(), { asynchronous:true, evalScripts:true, method:'post', onSuccess: function(o) { TaskWindow.show(); } } ); }
evtというのは、ソースとして指定したXMLのevent要素です。これが、イベントのコールバックに渡されるので、アプリケーション側の処理に必要な情報(この場合はtaskID)を含めておいて、それを取り出して処理しています。