Revision 14 (by dpavlin, 2007/04/14 12:32:36) update to latest upstream version and migrate my wiki changes and rand= reload
<?xml version="1.0" encoding="UTF-8"?>
<!--
	高橋メソッドなプレゼンツール in XUL リターンズ
	made by Piro
	http://piro.sakura.ne.jp/

	based on
	高橋メソッドなプレゼン作成ツール ver.2 made by mala
	http://la.ma.la/blog/diary_200504080545.htm
	もんたメソッドなプレゼン作成ツール made by mala
	http://la.ma.la/blog/diary_200505310749.htm
-->

<!-- ***** BEGIN LICENSE BLOCK *****
   - Version: MPL 1.1
   -
   - The contents of this file are subject to the Mozilla Public License Version
   - 1.1 (the "License"); you may not use this file except in compliance with
   - the License. You may obtain a copy of the License at
   - http://www.mozilla.org/MPL/
   -
   - Software distributed under the License is distributed on an "AS IS" basis,
   - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
   - for the specific language governing rights and limitations under the
   - License.
   -
   - The Original Code is the Takahashi-Method-based Presentation Tool
   - in XUL/Returns.
   -
   - The Initial Developer of the Original Code is SHIMODA Hiroshi.
   - Portions created by the Initial Developer are Copyright (C) 2005-2006
   - the Initial Developer. All Rights Reserved.
   -
   - Contributor(s): SHIMODA Hiroshi <piro@p.club.ne.jp>
   -                 dynamis <dynamis@mozilla-japan.org>
   -                 matobaa <matobaa@lily.freemail.ne.jp>
   -
   - ***** END LICENSE BLOCK ***** -->


<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="takahashi.css" type="text/css"?>

<page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
	id="presentation"
	xmlns:html="http://www.w3.org/1999/xhtml"
	orient="vertical"
	onkeypress="Presentation.onKeyPress(event);">


<html:textarea id="builtinCode" style="visibility: collapse">
TITLE::高橋メソッドなプレゼンツール in XUL リターンズ
高橋メソッド
in XUL
[[EM:RETURNS]]
----
HEADER::高橋メソッドなプレゼンツール in XUL リターンズ
CHAPTER::操作説明
操作
----
HEADER::操作
ALIGN::left
基本的な操作
|次のページ|→,↓,Enter,Page Down,左クリック
|前のページ|←,↑,Back Space,Page Up,右クリック
|最初のページ|Home
|最後のページ|End
|リロード|Ctrl-R
|編集モード|Ctrl-E
----
ALIGN::center
キーボード操作時は
次のページへ進む操作で
[ラベル]をめくれます
(めくっていない
[ラベル]がなければ
そのまま次のページへ)
----
一度めくったラベルは
状態を保持するので
----
ALIGN::right
前のページに戻して
「奥さん、いいですか!
ここですよここ!」
ができる
----
HEADER::
CHAPTER::編集機能
編集
----
HEADER::編集
編集モード時の基本操作
|新しいページの追加|Ctrl-N
----
HEADER::機能
部分
強調
----
テキストを部分的に
[[EM:強調]]できる
[[PRE:
[[EM:強調テキスト]&#173;]
[[EM:強調テキスト:EM]&#173;]
]]
----
整形済み
テキスト
[[PRE:
[[PRE:整形済み]&#173;]
[[PRE:整形済み:PRE]&#173;]
]]
----
行内に[[PRE:preformatted text]]を
書ける(けどあんまり意味ない)
----
複数行にまたがる整形済みテキスト
[[PRE:&lt;html&gt;
  &lt;head&gt;
    &lt;title&gt;サンプル&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;p&gt;サンプル&lt;/p&gt;
  &lt;/body&gt;
&lt;/html&gt;:PRE]]
ソースコードの例示などに使える
----
表
|~見出し&lt;br/&gt;セル|セル1|セル2
|~見出し&lt;br/&gt;セル|セル3|セル4
データの例示などにどうぞ。
[[PRE:
|~見出し&lt;br/&gt;セル|セル1|セル2
|~見出し&lt;br/&gt;セル|セル3|セル4
]]
----
XUL
[[RAW:
&lt;vbox align="center"&gt;
&lt;button label="ボタン"/&gt;

&lt;hbox&gt;

&lt;menulist label="既定の値" style="width: 10em;"&gt;
	&lt;menupopup&gt;
		&lt;menuitem label="既定の値"/&gt;
		&lt;menuseparator/&gt;
		&lt;menuitem label="項目1"/&gt;
		&lt;menuitem label="項目2"/&gt;
		&lt;menuitem label="項目3"/&gt;
	&lt;/menupopup&gt;
&lt;/menulist&gt;

&lt;menulist label="既定の値" style="width: 10em;" editable="true"&gt;
	&lt;menupopup&gt;
		&lt;menuitem label="既定の値"/&gt;
		&lt;menuseparator/&gt;
		&lt;menuitem label="項目1"/&gt;
		&lt;menuitem label="項目2"/&gt;
		&lt;menuitem label="項目3"/&gt;
	&lt;/menupopup&gt;
&lt;/menulist&gt;

&lt;/hbox&gt;

&lt;toolbar style="background: ThreeDFace; border: 1px -moz-bg-outset;"&gt;

&lt;toolbarbutton label="ボタン"/&gt;

&lt;toolbarbutton label="ボタン" type="menu"&gt;
	&lt;menupopup&gt;
		&lt;menuitem label="項目1"/&gt;
		&lt;menuitem label="項目2"/&gt;
		&lt;menuitem label="項目3"/&gt;
		&lt;menu label="サブメニュー"&gt;
			&lt;menupopup&gt;
				&lt;menuitem label="項目1"/&gt;
				&lt;menuitem label="項目2"/&gt;
				&lt;menuitem label="項目3"/&gt;
			&lt;/menupopup&gt;
		&lt;/menu&gt;
	&lt;/menupopup&gt;
&lt;/toolbarbutton&gt;

&lt;toolbarbutton label="ボタン" type="menu-button"&gt;
	&lt;menupopup&gt;
		&lt;menuitem label="項目1"/&gt;
		&lt;menuitem label="項目2"/&gt;
		&lt;menuitem label="項目3"/&gt;
	&lt;/menupopup&gt;
&lt;/toolbarbutton&gt;

&lt;/toolbar&gt;

&lt;/vbox&gt;
]]
----
SVG
[[RAW:
&lt;svg xmlns="http://www.w3.org/2000/svg" xml:space="default" width="220" height="200"&gt;
        &lt;rect x="60" y="60" width="100" height="80" fill="lime" stroke="red" 
stroke-width="5"/&gt;
        &lt;rect x="30" y="20" width="80" opacity="0.6" height="100" fill="aqua" stroke="blue" 
stroke-width="5"/&gt;
&lt;/svg&gt;
]]
----
XHTML
[[RAW:
&lt;table xmlns="http://www.w3.org/1999/xhtml" border="1"&gt;
	&lt;tbody&gt;
		&lt;tr&gt;&lt;th&gt;10進数&lt;/th&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;51&lt;/td&gt;&lt;td&gt;102&lt;/td&gt;&lt;td&gt;153&lt;/td&gt;&lt;td&gt;204&lt;/td&gt;&lt;td&gt;255&lt;/td&gt;&lt;/tr&gt;
		&lt;tr&gt;&lt;th&gt;16進数&lt;/th&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;33&lt;/td&gt;&lt;td&gt;66&lt;/td&gt;&lt;td&gt;99&lt;/td&gt;&lt;td&gt;CC&lt;/td&gt;&lt;td&gt;FF&lt;/td&gt;&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
]]
----
HIDDEN::true
MathML
[[RAW:
&lt;math xmlns="http://www.w3.org/1998/Math/MathML"&gt;
    &lt;mrow&gt;
      &lt;mrow&gt;
        &lt;munder&gt;
          &lt;mo form="prefix" movablelimits="false"&gt;lim&lt;/mo&gt;
          &lt;mrow&gt;
            &lt;mi&gt;n&lt;/mi&gt;
            &lt;mo&gt;→&lt;/mo&gt;

            &lt;mi&gt;N&lt;/mi&gt;
          &lt;/mrow&gt;
        &lt;/munder&gt;
        &lt;msup&gt;
          &lt;mrow&gt;
            &lt;mo&gt;(&lt;/mo&gt;
            &lt;mrow&gt;
              &lt;mn&gt;1&lt;/mn&gt;

              &lt;mo&gt;+&lt;/mo&gt;
              &lt;mfrac&gt;
                &lt;mn&gt;1&lt;/mn&gt;
                &lt;mi&gt;n&lt;/mi&gt;
              &lt;/mfrac&gt;
            &lt;/mrow&gt;
            &lt;mo&gt;)&lt;/mo&gt;

          &lt;/mrow&gt;
          &lt;mi&gt;n&lt;/mi&gt;
        &lt;/msup&gt;
      &lt;/mrow&gt;
      &lt;mo&gt;?&lt;/mo&gt;
      &lt;msup&gt;
        &lt;mi&gt;e&lt;/mi&gt;

        &lt;mi&gt;N&lt;/mi&gt;
      &lt;/msup&gt;
    &lt;/mrow&gt;
&lt;/math&gt;
]]

----
XMLの埋め込み
なんかもできる
[[PRE:
[[raw:
&lt;toolbar xmlns="http://www.mozilla.org/keymaster
                    /gatekeeper/there.is.only.xul"&gt;
    &lt;toolbarbutton type="menu" label="メニュー1"/&gt;
      &lt;menupopup&gt;
        &lt;menuitem label="項目1"/&gt;
        &lt;menuitem label="項目2"/&gt;
        &lt;menuitem label="項目3"/&gt;
      &lt;/menupopup&gt;
    &lt;/toolbarbutton&gt;
&lt;/toolbar&gt;
]&#173;]
]]
(でもちょっとバギー)
----
リンク
[[PRE:
[[URI]&#173;]
[[ラベル|URI]&#173;]
]]
----
[[リンクも埋め込める|http://piro.sakura.ne.jp/]]
ラベル無しリンクも可能↓
[[http://piro.sakura.ne.jp/]]
----
[[image src="takahashi.png" width="300" height="168"]]
----
画像
----
[[PRE:
[[IMAGE src="*" width="*" height="*"]&#173;]
[[IMG src="*" width="*" height="*"]&#173;]
]]
----
インライン[[image src="takahashi.png" width="300" height="168"]]で
[[image src="takahashi.png" width="300" height="168"]]
いくつでも
埋め込める
----
リンクや
画像のパスの
相対指定は
----
データファイルの
あるディレクトリ
もしくは
本体のある
ディレクトリを
基準として解釈
----
カスタム
スタイル
----
自分だけのカスタム
指定も可能
[[PRE:
[[#クラス名:文字列]&#173;]
]]
このように↓解釈
[[PRE:
&lt;description
  class="クラス名"
  value="文字列"/&gt;
]]
----
複数行にまたがる
指定も可能
[[PRE:
[[#クラス名:
1行目
2行目
]&#173;]
]]
このように↓解釈
[[PRE:
&lt;vbox class="クラス名 block"&gt;
  &lt;description value="1行目"/&gt;
  &lt;description value="2行目"/&gt;
&lt;/vbox&gt;
]]
----
takahashi.css
に自分で好きな
スタイル指定を
追加しよう!
----
[[装飾記号|http://homepage2.nifty.com/k_maeda/code/uni/uni44.html]]などの
[[Unicodeの記号文字|http://homepage2.nifty.com/k_maeda/code/unicode.html]]
と組み合わせると
色々できるかも?
----
HEADER::ヘッダ
FOOTER::フッタ
ヘッダとフッタも
自由に指定できる
[[PRE:
&#173;HEADER::ヘッダ
&#173;FOOTER::フッタ
]]
----
HEADER::
FOOTER::
テキストの水平配置
(ページ単位)
[[PRE:
&#173;ALIGN::left
&#173;ALIGN::right
&#173;ALIGN::center
]]
----
テキストの水平配置
(以後のページすべて)
[[PRE:
&#173;GLOBAL-ALIGN::left
&#173;GLOBAL-ALIGN::right
&#173;GLOBAL-ALIGN::center
]]
----
HEADER::機能
FOOTER::
CHAPTER::起動パラメータ
起動
パラ
メータ
----
|表示ページを指定して起動|?page=[数値]
|外部データファイルを使用|?data=[パス]
|編集モードで起動|?edit=true
----
HEADER::
CHAPTER::このアプリケーションについて
ファイ
ル構成
----
HEADER::ファイル構成
[[takahashi.xul]]
[[takahashi.css]]
[[monta-label.png]]
----
HEADER::
ライセンスは
[MPL1.1]
ってことで
----
ご自由に
お使い
下さい
----
説明
おわり
----
Let's Enjoy
Takahashi
Method
Life !!
</html:textarea>


<deck flex="1" id="deck">

<vbox flex="1"
	onmouseup="Presentation.handleEvent(event);"
	onmousedown="Presentation.handleEvent(event);"
	onmousemove="Presentation.handleEvent(event);">
	<toolbox id="canvasToolbar">
		<toolbar>
			<toolbarbutton oncommand="Presentation.home()" label="|&lt;&lt;"
				observes="canBack"/>
			<toolbarbutton oncommand="Presentation.back()" label="&lt;"
				observes="canBack"/>
			<toolbarbutton oncommand="Presentation.forward()" label="&gt;"
				observes="canForward"/>
			<toolbarbutton oncommand="Presentation.end()" label="&gt;&gt;|"
				observes="canForward"/>
			<toolbarseparator/>
			<hbox align="center">
				<textbox id="current_page" size="4"
					oninput="if (this.value) Presentation.showPage(parseInt(this.value)-1);"/>
				<toolbarbutton id="pages-list-button"
					type="menu"
					label="Select Page"
					tooltiptext="Select Page"
					class="dropmarker-button">
					<menupopup id="pages-list"
						onpopupshowing="if (event.target == this) Presentation.preventToShowHideToolbar = true;"
						onpopuphiding="if (event.target == this) Presentation.preventToShowHideToolbar = false;"
						oncommand="Presentation.showPage(parseInt(event.target.value));"/>
				</toolbarbutton>
				<description value="/"/>
				<description id="max_page"/>
			</hbox>
			<toolbarseparator/>
			<vbox flex="2">
				<spacer flex="1"/>
				<scrollbar id="scroller"
					align="center" orient="horizontal"
					oncommand="Presentation.showPage(parseInt(event.target.getAttribute('curpos')));"
					onclick="Presentation.showPage(parseInt(event.target.getAttribute('curpos')));"
					onmousedown="Presentation.onScrollerDragStart();"
					onmousemove="Presentation.onScrollerDragMove();"
					onmouseup="Presentation.onScrollerDragDrop();"/>
				<spacer flex="1"/>
			</vbox>
			<toolbarseparator/>
			<toolbarbutton label="Pen"
				id="penButton"
				type="checkbox"
				autoCheck="false"
				oncommand="StrokablePresentationService.toggleCheck();"/>
			<spacer flex="1"/>
			<toolbarbutton id="func-menu-button"
				type="menu"
				label="Function">
				<menupopup
					onpopupshowing="Presentation.preventToShowHideToolbar = true;"
					onpopuphiding="Presentation.preventToShowHideToolbar = false;">
					<menuitem label="Set Timer"
						oncommand="Presentation.setTimer();" />
					<menuseparator/>
					<menuitem label="Start Auto-Cruise"
						id="autoButton"
						type="checkbox"
						autoCheck="false"
						oncommand="Presentation.toggleAutoCruiseMode();" />
					<menu id="auto-interval-button"
						label="Change Interval">
						<menupopup id="auto-interval-list"
							onpopupshowing="(this.getElementsByAttribute('value', Presentation.autoCruiseInterval)[0] || this.lastChild).setAttribute('checked', true);"
							oncommand="Presentation.changeAutoCruiseInterval(parseInt(event.target.value));">
							<menuitem type="radio" radiogroup="autocruise-interval"
								label="1 sec" value="1000"/>
							<menuitem type="radio" radiogroup="autocruise-interval"
								label="2 sec" value="2000"/>
							<menuitem type="radio" radiogroup="autocruise-interval"
								label="3 sec" value="3000"/>
							<menuitem type="radio" radiogroup="autocruise-interval"
								label="4 sec" value="4000"/>
							<menuitem type="radio" radiogroup="autocruise-interval"
								label="5 sec" value="5000"/>
							<menuseparator/>
							<menuitem type="radio" radiogroup="autocruise-interval"
								label="1 min" value="60000"/>
							<menuitem type="radio" radiogroup="autocruise-interval"
								label="2 min" value="120000"/>
							<menuitem type="radio" radiogroup="autocruise-interval"
								label="3 min" value="180000"/>
							<menuitem type="radio" radiogroup="autocruise-interval"
								label="4 min" value="240000"/>
							<menuitem type="radio" radiogroup="autocruise-interval"
								label="5 min" value="300000"/>
							<menuseparator/>
							<menuitem type="radio" radiogroup="autocruise-interval"
								label="Custom"
								oncommand="
									var current = Presentation.autoCruiseInterval;
									var val = parseInt(prompt('input interval (sec)', parseInt(current/1000)));
									if (isNaN(val)) {
										event.preventBubble();
										return;
									}
									else
										val = val * 1000;
									this.value = val;
								"/>
						</menupopup>
					</menu>
					<menuseparator/>
					<menuitem oncommand="Presentation.print();" label="Print"/>
					<menu id="auto-interval-button"
						label="Thumbnail Format">
						<menupopup
							onpopupshowing="(this.getElementsByAttribute('value', Presentation.imageType)[0] || this.firstChild).setAttribute('checked', true);"
							oncommand="Presentation.imageType = event.target.value;">
							<menuitem type="radio" radiogroup="print-image-type"
								label="PNG" value="png"/>
							<menuitem type="radio" radiogroup="print-image-type"
								label="JPEG (50%)" value="jpeg"/>
						</menupopup>
					</menu>
					<menuseparator/>
					<menuitem id="toggleEva" label="Eva Mode"
						type="checkbox"
						autoCheck="false"
						oncommand="Presentation.toggleEvaMode();"/>
				</menupopup>
			</toolbarbutton>
			<toolbarseparator/>
			<toolbarbutton label="Edit"
				oncommand="Presentation.toggleEditMode();"/>
			<toolbarbutton oncommand="Presentation.reload();" label="Reload"/>
		</toolbar>
	</toolbox>
	<vbox flex="1" id="canvas">
		<stack flex="1">
			<vbox flex="1">
				<hbox id="headerBox" flex="1">
					<label id="header"/>
					<spacer flex="1"/>
					<vbox>
						<image id="logo"/>
						<spacer flex="1"/>
					</vbox>
				</hbox>
				<spacer flex="19"/>
				<hbox id="footerBox" flex="1">
					<spacer flex="1"/>
					<label id="footer"/>
				</hbox>
			</vbox>
			<vbox flex="1"
				onclick="Presentation.onPresentationClick(event);">
				<spacer flex="1"/>
				<hbox flex="1" id="contentBox">
					<spacer flex="1"/>
					<vbox id="content"/>
					<spacer flex="1"/>
				</hbox>
				<spacer flex="1"/>
			</vbox>
		</stack>
	</vbox>
	<hbox id="indicatorBar"
		onclick="Presentation.onIndicatorBarClick(event);">
		<stack flex="1">
			<vbox>
				<progressmeter id="remainingPageIndicator"
					type="determined" value="0"
					flex="1"/>
				<progressmeter id="remainingTimeIndicator"
					type="determined" value="0"
					flex="1"
					collapsed="true"/>
			</vbox>
			<hbox flex="1">
				<label value="Next:"/>
				<description id="nextPage" flex="1" crop="end"/>
			</hbox>
		</stack>
	</hbox>
</vbox>


<vbox flex="1" id="edit">
	<toolbox>
		<toolbar>
			<menubar flex="1">
				<menu label="File">
					<menupopup>
						<menuitem label="Save"
							key="key_save"
							oncommand="Presentation.output()"/>
						<menuitem label="Reload"
							key="key_reload"
							oncommand="Presentation.reload()"/>
					</menupopup>
				</menu>
				<menu label="Insert">
					<menupopup>
						<menuitem label="New Page"
							key="key_insert_newpage"
							oncommand="Presentation.insert('page')"/>
						<menuseparator/>
						<menuitem label="Header"
							oncommand="Presentation.insert('header')"/>
						<menuitem label="Footer"
							oncommand="Presentation.insert('footer')"/>
						<menuseparator/>
						<menuitem label="Link"
							oncommand="Presentation.insert('link')"/>
						<menuitem label="Emphasis"
							oncommand="Presentation.insert('em')"/>
						<menuitem label="Preformatted"
							oncommand="Presentation.insert('pre')"/>
						<menuitem label="Monta"
							oncommand="Presentation.insert('monta')"/>
						<menuitem label="Image"
							oncommand="Presentation.insert('img')"/>
					</menupopup>
				</menu>
			</menubar>
			<toolbarseparator/>
			<toolbarbutton label="View"
				oncommand="Presentation.toggleEditMode();"/>
			<toolbarbutton oncommand="Presentation.reload();" label="Reload"/>
		</toolbar>
	</toolbox>
	<textbox id="textField" flex="1" multiline="true"
		oninput="Presentation.onEdit()"/>
</vbox>

</deck>


<broadcasterset>
	<broadcaster id="canBack"/>
	<broadcaster id="canForward"/>
</broadcasterset>

<commandset>
	<command id="cmd_forward"
		oncommand="if (Presentation.isPresentationMode) Presentation.forward();"/>
	<command id="cmd_forwardStep"
		oncommand="if (Presentation.isPresentationMode) Presentation.forwardStep();"/>
	<command id="cmd_back"
		oncommand="if (Presentation.isPresentationMode) Presentation.back();"/>
	<command id="cmd_home"
		oncommand="if (Presentation.isPresentationMode) Presentation.home();"/>
	<command id="cmd_end"
		oncommand="if (Presentation.isPresentationMode) Presentation.end();"/>
</commandset>
<keyset>
	<key keycode="VK_ENTER"      command="cmd_forwardStep"/>
	<key keycode="VK_RETURN"     command="cmd_forwardStep"/>
	<key keycode="VK_PAGE_DOWN"  command="cmd_forwardStep"/>
	<key keycode="VK_RIGHT"      command="cmd_forwardStep"/>
	<key keycode="VK_DOWN"       command="cmd_forwardStep"/>
	<!--key keycode="VK_BACK_SPACE" command="cmd_back"/-->
	<key keycode="VK_PAGE_UP"    command="cmd_back"/>
	<key keycode="VK_UP"         command="cmd_back"/>
	<key keycode="VK_LEFT"       command="cmd_back"/>
	<key keycode="VK_HOME"       command="cmd_home"/>
	<key keycode="VK_END"        command="cmd_end"/>

	<key id="key_insert_newpage"
		key="n" modifiers="accel" oncommand="Presentation.insert('page');"/>

	<key id="key_save"
		key="s" modifiers="accel" oncommand="Presentation.output();"/>
	<key id="key_reload"
		key="r" modifiers="accel" oncommand="Presentation.reload();"/>
	<key id="key_reload"
		key="p" modifiers="accel" oncommand="Presentation.print();"/>

	<key key="e" modifiers="accel" oncommand="Presentation.toggleEditMode();"/>
	<key key="e" modifiers="accel,shift" oncommand="Presentation.toggleEvaMode();"/>
</keyset>


<script type="application/x-javascript; e4x=1"><![CDATA[

const XULNS   = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul';
const XHTMLNS = 'http://www.w3.org/1999/xhtml';

var Presentation = {

	size            : 9,
	montaLabelImage : 'monta-label.png',

	phraseOpenParen  : '[',
	phraseCloseParen : ']',
	makePhraseRegExp : function(aPattern, aFlags)
	{
		return new RegExp(
				aPattern.replace(/%o(pen)?/gi, '\\'+this.phraseOpenParen)
					.replace(/%c(lose)?/gi, '\\'+this.phraseCloseParen),
				aFlags
			);
	},

	dragStartDelta : 8,

	imageType : 'jpeg',


	initialized : false,

	preventToShowHideToolbar : false,

	showMontaKeywordTimeout : 100,
	autoCruiseInterval      : 2000,
	timerUpdatingInterval   : 30000,

	cachedContents : [],

	init : function(option){
		if (this.initialized) {
			this.startPresentation();
			return;
		}
		this.initialized = true;


		this._offset  = 0;
		this.canvas    = document.getElementById('canvas');
		this.content   = document.getElementById('content');
		this.header    = document.getElementById('header');
		this.footer    = document.getElementById('footer');
		this.logo      = document.getElementById('logo');
		this.next      = document.getElementById('nextPage');

		this.indicatorBar = document.getElementById('indicatorBar');
		this.remainder    = document.getElementById('remainingPageIndicator');
		this.timer        = document.getElementById('remainingTimeIndicator');

		this.list     = document.getElementById('pages-list');
		this.textbox  = document.getElementById('textField');
		this.deck     = document.getElementById('deck');
		this.scroller = document.getElementById('scroller');

		this.toolbar         = document.getElementById('canvasToolbar');
		this.toolbarHeight   = this.toolbar.boxObject.height;
		this.isToolbarHidden = true;
		this.toolbar.setAttribute('style', 'margin-top:'+(0-this.toolbarHeight)+'px;margin-bottom:0px;');

		this.preloadImage(this.montaLabelImage);

		window.addEventListener('resize', this, false);
		window.addEventListener('contextmenu', this, false);
		window.addEventListener('CanvasContentAdded', this, false);

		this.canvas.addEventListener('DOMMouseScroll', this, false);
		this.indicatorBar.addEventListener('DOMMouseScroll', this, false);

		if(option){
			for(var i in option){this[i] = option[i]}
		}

		this.cachedContents = [];

		if (this.readParameter()) {
			this.startPresentation();
		}

		document.documentElement.focus();
	},

	startPresentation : function()
	{
		if (this.data.length)
			document.title = this.data[0].title || this.data[0].header || this.data[0].text.join(' ');

		this.takahashi();
	},

	takahashi : function() {
		this.isRendering = true;

		if(!this.data[this.offset]){
			this.offset = this.data.length-1;
		}
		document.getElementById("current_page").value = this.offset+1;
		document.getElementById("max_page").value     = this.data.length;

		this.scroller.setAttribute('maxpos', this.data.length-1);
		this.scroller.setAttribute('curpos', this.offset);

		var broadcaster = document.getElementById('canBack');
		if (!this.offset)
			broadcaster.setAttribute('disabled', true);
		else
			broadcaster.removeAttribute('disabled');

		var broadcaster = document.getElementById('canForward');
		if (this.offset == this.data.length-1)
			broadcaster.setAttribute('disabled', true);
		else
			broadcaster.removeAttribute('disabled');

		this.canvas.setAttribute('rendering', true);


		this.header.removeAttribute('style');
		this.footer.removeAttribute('style');
		this.content.removeAttribute('style');

		this.clickableNodes     = [];


		if ('title' in this.data[this.offset])
			document.title = this.data[this.offset].title;

		this.header.setAttribute('style', 'font-size:10px;');
		this.header.value = this.data[this.offset].header;
		this.footer.setAttribute('style', 'font-size:10px;');
		this.footer.value = this.data[this.offset].footer;

		var page = this.content.getAttribute('page');
		var range = document.createRange();
		range.selectNodeContents(this.content);
		this.cachedContents[page] = {
			fragment     : range.extractContents(),
			offsetWidth  : parseInt(this.content.getAttribute('offsetWidth')),
			offsetHeight : parseInt(this.content.getAttribute('offsetHeight'))
		};
		range.detach();


		if (this.data[this.offset].load) {
			this.isRendering = false;
			location.href = location.href.split('?')[0] + '?'+this.data[this.offset].load;
			return;
		}

		var content = (this.offset in this.cachedContents) ?
							this.cachedContents[this.offset] :
							this.createContent();

		this.content.setAttribute('style', 'font-size:10px;');
		this.content.setAttribute('page',         this.offset);
		this.content.setAttribute('offsetWidth',  content.offsetWidth);
		this.content.setAttribute('offsetHeight', content.offsetHeight);

		this.content.appendChild(content.fragment);

		this.clickableNodes.push(this.content);


		this.fitHeaderFooterToCanvas();
		this.fitMainContentToCanvas();


		try {
			var checkedItems = this.list.getElementsByAttribute('checked', 'true');
			max = checkedItems.length;
			for (i = max-1; i > -1 ; i--)
				checkedItems[i].removeAttribute('checked');
		}
		catch(e) {
		}

		this.list.getElementsByAttribute('value', this.offset)[0].setAttribute('checked', true);

		this.canvas.removeAttribute('rendering');
		this.isRendering = false;
		this.setHash('page', 'page'+(this.offset+1));

		this.next.value = (this.offset <= this.data.length-2) ? (this.data[this.offset+1].plain.join('') || this.data[this.offset+1].text.join('')).replace(/\s+/g, ' ') : '(no page)' ;
		this.remainder.setAttribute('value', this.offset == 0 ? 0 : parseInt(((this.offset)/(this.data.length-1))*100));

		var event = document.createEvent('Events');
		event.initEvent('PresentationRedraw', false, true);
		this.canvas.dispatchEvent(event);
	},
	createContent : function()
	{
		var retVal = {
				offsetWidth  : 0,
				offsetHeight : 0
			};
		var text = this.data[this.offset].text;
		var line;
		var newLine;
		var uri;
		var image_width;
		var image_height;
		var image_src;

		var labelId = 0;
		var lineRegExp = this.makePhraseRegExp('^([^%O]+)?(%O%Oem:((.+?)(:em)?%C%C)?|%O%O(raw|encoded):((.+?)(:raw|:encoded)?%C%C)?|%O%Opre:((.+?)(:pre)?%C%C)?|%O%O\#[^:]+:((.+?)%C%C)?|%O%Oima?ge? +src="([^"]+)" +width="([0-9]+)" +height="([0-9]+)"[^%C]*%C%C|%O%O(([^\|]+)?\\||)([^%C]+)%C%C|%O([^%C]+)%C)(.+)?', 'i');

		var emRegExp         = this.makePhraseRegExp('^([^%O]+)?%O%Oem:(.+?)()?%C%C', 'i');
		var emStartRegExp    = this.makePhraseRegExp('^([^%O]+)?%O%Oem:(.*)', 'i');
		var emEndRegExp      = this.makePhraseRegExp('^(.*?)((:em)?%C%C)', 'i');

		var preRegExp        = this.makePhraseRegExp('^([^%O]+)?%O%Opre:(.+?)(:pre)?%C%C', 'i');
		var preStartRegExp   = this.makePhraseRegExp('^([^%O]+)?%O%Opre:(.*)', 'i');
		var preEndRegExp     = this.makePhraseRegExp('^(.*?)((:pre)?%C%C)', 'i');

		var rawRegExp        = this.makePhraseRegExp('^([^%O]+)?%O%O(raw|encoded):(.+?)(:raw|:encoded)?%C%C', 'i');
		var rawStartRegExp   = this.makePhraseRegExp('^([^%O]+)?%O%O(raw|encoded):(.*)', 'i');
		var rawEndRegExp     = this.makePhraseRegExp('^(.*?)((:raw|:encoded)?%C%C)', 'i');

		var styleRegExp      = this.makePhraseRegExp('^([^%O]+)?%O%O\\#([^:]+):(.+?)%C%C', '');
		var styleStartRegExp = this.makePhraseRegExp('^([^%O]+)?%O%O\\#([^:]+):(.*)', '');
		var styleEndRegExp   = this.makePhraseRegExp('^(.*?)(%C%C)', '');

		var imagesRegExp     = this.makePhraseRegExp('^([^%O]+)?%O%Oima?ge? +src="([^"]+)" +width="([0-9]+)" +height="([0-9]+)"[^%C]*%C%C', 'i');

		var linksRegExp      = this.makePhraseRegExp('^([^%O]+)?%O%O(([^|]+)?\\||)([^%C]+)%C%C', '');

		var wikiRegExp       = this.makePhraseRegExp('^([^%O]+)?%O([0-9]*)([#!])?([:\/\*_-])([^%C]+)[:\/\*_-]%C', '');

		var montaRegExp      = this.makePhraseRegExp('^([^%O]+)?%O([^%C]+)%C', '');

		var inBlock       = false,
			blockClass    = '',
			blockContents = [];

		var inGrid       = false,
			gridContents = null;

		var fragment = document.createDocumentFragment();

		var lineBox;
		for (var i = 0, max = text.length; i < max; i++)
		{
			lineBox = document.createElement('hbox');
			lineBox.setAttribute('align', 'center');
			lineBox.setAttribute('pack', this.data[this.offset].align);

			line = text[i];
			image_width  = 0;
			image_height = 0;
			if (!line) line = ' ';

			if (inBlock) {
				if (blockClass == 'raw' &&
					rawEndRegExp.test(line)) {
					inBlock = false;
					blockContents.push(RegExp.$1);
					line = line.substring((RegExp.$1+RegExp.$2).length);

					eval('var xml = <hbox class="raw" onclick="event.stopPropagation();" onkeypress="event.stopPropagation();">'+blockContents.join('\n')+'</hbox>;');
					importNodeTreeWithDelay(importE4XNode(xml, document, XULNS), lineBox, XULNS);

					blockClass    = '';
					blockContents = [];
				}
				else if (blockClass == 'preformatted-text' &&
					preEndRegExp.test(line)) {
					inBlock = false;
					blockContents.push(RegExp.$1);
					line = line.substring((RegExp.$1+RegExp.$2).length);

					lineBox.appendChild(document.createElement('description'));
					lineBox.lastChild.setAttribute('class', 'preformatted-text block');
					lineBox.lastChild.appendChild(document.createTextNode(
						blockContents.join('\n')
							.replace(/^[\r\n\s]+/, '')
							.replace(/[\r\n\s]+$/, '')
							.replace(/&amp;/g, '&')
							.replace(/&quot;/g, '"')
							.replace(/&gt;/g, '>')
							.replace(/&lt;/g, '<')
					));

					blockClass    = '';
					blockContents = [];
				}
				else if (emEndRegExp.test(line) || styleEndRegExp.test(line)) {
					inBlock = false;
					blockContents.push(RegExp.$1);
					line = line.substring((RegExp.$1+RegExp.$2).length);

					lineBox.appendChild(document.createElement('vbox'));
					lineBox.lastChild.setAttribute('class', blockClass+' block');
					lineBox.lastChild.setAttribute('align', this.data[this.offset].align);
					blockContents = blockContents.join('\n')
						.replace(/^[\r\n\s]+/, '')
						.replace(/[\r\n\s]+$/, '')
						.split('\n');
					for (var j = 0, jmax = blockContents.length; j < jmax; j++)
					{
						lineBox.lastChild.appendChild(document.createElement('description'));
						lineBox.lastChild.lastChild.setAttribute('value', blockContents[j]);
					}

					blockClass    = '';
					blockContents = [];
				}
				else {
					blockContents.push(line);
					continue;
				}
			}

			if (line.indexOf('|') == 0) {
				fragment.appendChild(lineBox);

				if (fragment.childNodes.length == 1 ||
					!fragment.childNodes[fragment.childNodes.length-2] ||
					!fragment.childNodes[fragment.childNodes.length-2].lastChild ||
					fragment.childNodes[fragment.childNodes.length-2].lastChild.localName != 'grid') {
					fragment.lastChild.appendChild(document.createElement('grid'));
					fragment.lastChild.lastChild.appendChild(document.createElement('columns'));
					fragment.lastChild.lastChild.appendChild(document.createElement('rows'));
				}
				else {
					fragment.removeChild(fragment.lastChild);
				}
				fragment.lastChild.lastChild.lastChild.appendChild(document.createElement('row'));
				fragment.lastChild.lastChild.lastChild.lastChild.setAttribute('flex', 1);

				line = line.split('|');
				for (var j = 1, jmax = line.length; j < jmax; j++)
				{
					fragment.lastChild.lastChild.lastChild.lastChild.appendChild(document.createElement('vbox'));
					fragment.lastChild.lastChild.lastChild.lastChild.lastChild.setAttribute('align', this.data[this.offset].align);
					fragment.lastChild.lastChild.lastChild.lastChild.lastChild.setAttribute('pack', 'center');
					if (line[j].charAt(0) == '~') {
						fragment.lastChild.lastChild.lastChild.lastChild.lastChild.setAttribute('class', 'special');
						line[j] = line[j].substring(1);
					}
					line[j] = line[j].split(/<br\s*\/>/g);
					for (var k = 0, kmax = line[j].length; k < kmax; k++)
					{
						fragment.lastChild.lastChild.lastChild.lastChild.lastChild.appendChild(document.createElement('description'));
						fragment.lastChild.lastChild.lastChild.lastChild.lastChild.lastChild.appendChild(document.createTextNode(line[j][k].replace(/^\s+|\s+$/g, '')));
					}
					if (fragment.lastChild.lastChild.firstChild.childNodes.length < j) {
						fragment.lastChild.lastChild.firstChild.appendChild(document.createElement('column'));
						fragment.lastChild.lastChild.firstChild.lastChild.setAttribute('flex', 1);
					}
				}
				continue;
			}


			while (line.match(lineRegExp))
			{
				if (RegExp.$1) {
					lineBox.appendChild(document.createElement('description'));
					lineBox.lastChild.setAttribute('value', RegExp.$1);
				}
				newLine = line.substring((RegExp.$1+RegExp.$2).length);

				// Raw Codes: Parsed as XML
				if (rawRegExp.test(line)) {
					eval('var xml = <hbox class="raw" onclick="event.stopPropagation();" onkeypress="event.stopPropagation();">'+RegExp.$3+'</hbox>;');
					importNodeTreeWithDelay(importE4XNode(xml, document, XULNS), lineBox, XULNS);
				}
				else if (rawStartRegExp.test(line)) {
					inBlock       = true;
					blockClass    = 'raw';
					blockContents = [RegExp.$3];
					newLine       = '';
				}

				// Preformatted Text
				if (preRegExp.test(line)) {
					lineBox.appendChild(document.createElement('description'));
					lineBox.lastChild.setAttribute('value', RegExp.$2);
					lineBox.lastChild.setAttribute('class', 'preformatted-text');
				}
				else if (preStartRegExp.test(line)) {
					inBlock       = true;
					blockClass    = 'preformatted-text';
					blockContents = [RegExp.$2];
					newLine       = '';
				}

				// Emphasis
				else if (emRegExp.test(line)) {
					lineBox.appendChild(document.createElement('description'));
					lineBox.lastChild.setAttribute('value', RegExp.$2);
					lineBox.lastChild.setAttribute('class', 'em-text');
				}
				else if (emStartRegExp.test(line)) {
					inBlock       = true;
					blockClass    = 'em-text';
					blockContents = [RegExp.$2];
					newLine       = '';
				}

				// User-defined Styles
				else if (styleRegExp.test(line)) {
					lineBox.appendChild(document.createElement('description'));
					lineBox.lastChild.setAttribute('value', RegExp.$3);
					lineBox.lastChild.setAttribute('class', RegExp.$2);
				}
				else if (styleStartRegExp.test(line)) {
					inBlock       = true;
					blockClass    = RegExp.$2;
					blockContents = [RegExp.$3];
					newLine       = '';
				}

				// Images
				else if (imagesRegExp.test(line)) {
					lineBox.appendChild(document.createElement('image'));
					image_src = RegExp.$2;
					if (image_src.indexOf('http://') < 0 &&
						image_src.indexOf('https://') < 0)
						image_src = this.dataFolder+image_src;
					lineBox.lastChild.setAttribute('src', image_src);
					lineBox.lastChild.setAttribute('width', parseInt(RegExp.$3 || '0'));
					lineBox.lastChild.setAttribute('height', parseInt(RegExp.$4 || '0'));
					image_width  += parseInt(RegExp.$3 || '0');
					image_height = Math.max(image_height, parseInt(RegExp.$4 || '0'));
				}

				// Links
				else if (linksRegExp.test(line)) {
					uri = RegExp.$4;
					if (uri.indexOf('://') < 0)
						uri = this.dataFolder+uri;
					lineBox.appendChild(document.createElement('description'));
					lineBox.lastChild.setAttribute('value', RegExp.$3 || RegExp.$4);
					lineBox.lastChild.setAttribute('href', uri);
					lineBox.lastChild.setAttribute('tooltiptext', uri);
					lineBox.lastChild.setAttribute('statustext', uri);
					lineBox.lastChild.setAttribute('class', 'link-text');

					this.clickableNodes.push(lineBox.lastChild);
				}

				// modify font size and wiki-style
				else if (wikiRegExp.test(line)) {
					lineBox.appendChild(document.createElement('description'));
					lineBox.lastChild.setAttribute('value', RegExp.$5);
					var style = 'font-size:'+ ( RegExp.$2 || 100 ) +'%';
					if (RegExp.$4 == '*') {
						style += '; font-weight: bold;';
					} else if (RegExp.$4 == '/') {
						style += '; font-style: italic;';
					} else if (RegExp.$4 == '_') {
						style += '; text-decoration: underline;';
					} else if (RegExp.$4 == '-') {
						style += '; text-decoration: line-through;';
					}
					lineBox.lastChild.setAttribute('style', style);
					if (RegExp.$3 == '!') lineBox.lastChild.setAttribute('class', 'em-text');
					else if (RegExp.$3 == '#') lineBox.lastChild.setAttribute('class', 'preformatted-text');
				}

				// Monta
				else if (montaRegExp.test(line)) {
					lineBox.appendChild(document.createElement('stack'));

					lineBox.lastChild.appendChild(document.createElement('description'));
					lineBox.lastChild.lastChild.setAttribute('value', RegExp.$2);
					lineBox.lastChild.lastChild.setAttribute('class', 'monta-text');

					lineBox.lastChild.appendChild(document.createElement('spacer'));
					lineBox.lastChild.lastChild.setAttribute('flex', 1);
					lineBox.lastChild.lastChild.setAttribute('class', 'monta-label');

					lineBox.lastChild.lastChild.setAttribute('label-id', 'label-' + (++labelId));

					lineBox.lastChild.lastChild.setAttribute('monta-hidden', 'true');

					this.clickableNodes.push(lineBox.lastChild.lastChild);
				}

				line = newLine;
			}

			if (line) {
				lineBox.appendChild(document.createElement('description'));
				lineBox.lastChild.setAttribute('value', line);
			}

			retVal.offsetWidth = Math.max(retVal.offsetWidth, image_width);
			retVal.offsetHeight += image_height;

			if (lineBox.hasChildNodes())
				fragment.appendChild(lineBox);
		}

		retVal.fragment = fragment;
		return retVal;
	},

	fitToCanvas : function(aContent, aCanvas, aOffsetWidth, aOffsetHeight)
	{
		aContent.removeAttribute('style');
		aContent.setAttribute('style', 'font-size:10px;');

		var grids      = aContent.getElementsByTagName('grid');
		var gridsCount = grids.length;

		if (!aContent.boxObject.width) return;

		var canvas_w  = aCanvas.boxObject.width;
		var canvas_h  = aCanvas.boxObject.height-aOffsetHeight;

		var content_w = aContent.boxObject.width;
		var new_fs = Math.round((canvas_w/content_w) * this.size);
		aContent.setAttribute('style', 'font-size:'+ new_fs + "px");

		for (var i = 0; i < gridsCount; i++)
		{
			grids[i].firstChild.lastChild.removeAttribute('flex', 1);
			grids[i].firstChild.lastChild.setAttribute('flex', 1);
		}

		if (aContent.boxObject.width < aOffsetWidth) {
			content_w = aOffsetWidth;
			new_fs = Math.round((canvas_w/content_w) * this.size);
			aContent.setAttribute('style', 'font-size:'+ new_fs + "px");

			for (var i = 0; i < gridsCount; i++)
			{
				grids[i].firstChild.lastChild.removeAttribute('flex', 1);
				grids[i].firstChild.lastChild.setAttribute('flex', 1);
			}
		}

		var content_h = aContent.boxObject.height;
		if(content_h >= canvas_h){
			state='height';
			content_h = aContent.boxObject.height;
			new_fs = Math.round((canvas_h/content_h) * new_fs);
			aContent.setAttribute('style', 'font-size:'+ new_fs + "px");

			for (var i = 0; i < gridsCount; i++)
			{
				grids[i].firstChild.lastChild.removeAttribute('flex', 1);
				grids[i].firstChild.lastChild.setAttribute('flex', 1);
			}
		}
	},
	fitMainContentToCanvas : function()
	{
		this.fitToCanvas(
			this.content,
			this.canvas,
			parseInt(this.content.getAttribute('offsetWidth')),
			parseInt(this.content.getAttribute('offsetHeight'))
			+this.header.boxObject.height
			+this.footer.boxObject.height
		);
	},
	fitHeaderFooterToCanvas : function()
	{
		this.fitToCanvas(this.header, this.header.parentNode, 0, 0);
		this.fitToCanvas(this.footer, this.footer.parentNode, 0, 0);
	},

	reload : function() {
		var file = String(location.href).replace(/#.+$/, '');
		if (this.dataPath != file) {
			var path = this.dataPath;
			var request = new XMLHttpRequest();
			request.open('GET', path + '?rand=' + Math.random() );
			request.onload = function() {
				Presentation.textbox.value = request.responseText;
				Presentation.data = Presentation.textbox.value;
				Presentation.init();

				path = null;
				request = null;
			};
			request.send(null);
		}
		else
			window.location.reload();
	},

	forward : function(){
		if (!this.canForward) return;
		this.offset++;
		this.takahashi();
	},
	forwardStep : function(){
		if (!this.canForward) return;
		var monta = document.getElementsByAttribute('monta-hidden', 'true');
		if (monta && monta.length) {
			this.showMontaKeyword(monta[0]);
		}
		else
			this.forward();
	},
	back : function(){
		if (!this.canBack) return;
		this.offset--;
		if(this.offset < 0){this.offset = 0}
		this.takahashi();
	},
	home : function(){
		if (!this.canMove) return;
		this.offset = 0;
		this.takahashi();
	},
	end : function(){
		if (!this.canMove) return;
		this.offset = this.data.length-1;
		this.takahashi();
	},
	showPage : function(aPageOffset){
		if (!this.canMove) return;
		this.offset = aPageOffset ? aPageOffset : 0 ;
		this.takahashi();
	},

	get canMove()
	{
		return (
				this.isRendering ||
				importNodeTreeWithDelayTimers ||
				this.montaAnimating
			) ? false : true ;
	},
	get canBack()
	{
		return this.canMove;
	},
	get canForward()
	{
		return this.canMove;
	},


	insert : function(aType) {
		switch (aType)
		{
			case 'page':
				this.insertTextFor('\n----\n', this.textbox, 6);
				break;
			case 'header':
				this.insertTextFor('\nHEADER::\n', this.textbox, 9);
				break;
			case 'footer':
				this.insertTextFor('\nFOOTER::\n', this.textbox, 9);
				break;

			case 'em':
			case 'emphasis':
				this.insertTextFor(this.phraseOpenParen+this.phraseOpenParen+'EM:'+this.phraseCloseParen+this.phraseCloseParen, this.textbox, 5);
				break;
			case 'pre':
			case 'preformatted':
				this.insertTextFor(this.phraseOpenParen+this.phraseOpenParen+'PRE:'+this.phraseCloseParen+this.phraseCloseParen, this.textbox, 6);
				break;
			case 'monta':
				this.insertTextFor(this.phraseOpenParen+this.phraseCloseParen, this.textbox, 1);
				break;
			case 'link':
				this.insertTextFor(this.phraseOpenParen+this.phraseOpenParen+'|http://'+this.phraseCloseParen+this.phraseCloseParen, this.textbox, 2);
				break;
			case 'img':
			case 'image':
				this.insertTextFor(this.phraseOpenParen+this.phraseOpenParen+'image src="" width="" height=""'+this.phraseCloseParen+this.phraseCloseParen, this.textbox, 13);
				break;

			default:
				return;
		}
		this.onEdit();
	},
	insertTextFor : function(aString, aNode, aPosOffset)
	{
		var pos = aNode.selectionStart;
		var value = aNode.value;
		aNode.value = [value.substring(0, pos), aString, value.substring(pos, value.length)].join('');
		aNode.selectionEnd = aNode.selectionStart = pos + (aPosOffset || 0);
	},


	output : function()
	{
		location.href = 'data:application/octet-stream,'+encodeURIComponent(this.textbox.value);
	},


	toggleEditMode : function(){
		this.deck.selectedIndex = this.deck.selectedIndex == 0 ? 1 : 0 ;
		this.setHash('edit', this.deck.selectedIndex == 0 ? '' : 'edit' );
	},
	toggleEvaMode : function(){
		var check = document.getElementById('toggleEva');
		if (this.canvas.getAttribute('eva') == 'true') {
			this.canvas.removeAttribute('eva');
			this.logo.removeAttribute('eva');
			check.checked = false;
		}
		else {
			this.canvas.setAttribute('eva', true);
			this.logo.setAttribute('eva',true);
			check.checked = true;
		}
		this.setHash('eva', check.checked ? 'eva' : '' );
	},



	toggleAutoCruiseMode : function()
	{
		var autoCruise = document.getElementById('autoButton');
		if(!autoCruise.checked)
			this.startAutoCruise();
		else
			autoCruise.checked = false;
	},
	startAutoCruise : function()
	{
		var autoCruise = document.getElementById('autoButton');
		autoCruise.checked = true;

		if (this.autoCruiseTimer) {
			window.clearTimeout(this.autoCruiseTimer);
		}
		this.autoCruiseTimer = window.setTimeout(this.autoCruise, this.autoCruiseInterval);
	},

	changeAutoCruiseInterval : function(aInterval)
	{
		this.autoCruiseInterval = aInterval;
		this.startAutoCruise();
	},

	autoCruise : function()
	{
		var autoCruise = document.getElementById('autoButton');
		if (!autoCruise.checked) return;

		if (Presentation.offset == Presentation.data.length-1) {
			if (Presentation.canMove) 
				Presentation.home();
		}
		else {
			if (Presentation.canForward) 
				Presentation.forwardStep();
		}
		Presentation.autoCruiseTimer = window.setTimeout(arguments.callee, Presentation.autoCruiseInterval);
	},
	autoCruiseTimer : null,



	setHash : function(aKey, aValue)
	{
		aKey = String(aKey).toLowerCase();
		var hashArray = String(location.hash).replace(/^#/, '').toLowerCase().split(',');

		for (var i = hashArray.length-1; i > -1; i--)
			if (!hashArray[i] || hashArray[i].indexOf(aKey) == 0)
				hashArray.splice(i, 1);

		if (aValue) hashArray.push(aValue);
		hashArray.sort();

		location.hash = hashArray.length ? hashArray.join(',') : '' ;
	},


	showMontaKeyword : function(aNode, aWithoutAnimation) {
		if (aNode.getAttribute('monta-hidden') != 'true') return;

		if (aWithoutAnimation) {
			aNode.setAttribute('monta-hidden', 'false');
			return;
		}

		aNode.setAttribute('monta-hidden', 'progress');

		this.montaAnimating = true;

		window.setTimeout(this.showMontaKeywordCallback, 0, {
			position : -100,
			node     : aNode,
			interval : this.showMontaKeywordTimeout/10
		});
	},
	showMontaKeywordCallback : function(aInfo) {
		if (aInfo.position >= aInfo.node.boxObject.width) {
			aInfo.node.setAttribute('monta-hidden', 'false');
			Presentation.montaAnimating = false;
			return;
		}

		aInfo.position += (aInfo.node.boxObject.width/10);
		aInfo.node.setAttribute('style', 'background-position: '+aInfo.position+'px 0 !important;');
		window.setTimeout(arguments.callee, aInfo.interval, aInfo);
	},
	montaAnimating : false,



	onPresentationClick : function(aEvent)
	{
		if (this.isPrinting) {
			if (confirm('Do you want printing operation to be stopped?')) {
				this.stopPrint();
			}
			return;
		}

		if (!this.isToolbarHidden)
			this.showHideToolbar();

		switch(aEvent.button)
		{
			case 0:
				switch (aEvent.target.getAttribute('class'))
				{
					case 'link-text':
						var uri = aEvent.target.getAttribute('href');
						if (uri) {
							window.open(uri);
							return;
						}
						break;

					case 'monta-label':
						if (aEvent.target.getAttribute('monta-hidden') == 'true') {
							this.showMontaKeyword(aEvent.target);
							aEvent.preventBubble();
							return;
						}

					default:
						break;
				}
				this.forward();
				document.documentElement.focus();
				break;
			case 2:
				this.back();
				document.documentElement.focus();
				break;
			default:
				break;
		}
	},

	onScrollerDragStart : function(){
		if (this.isPrinting) return;

		this.scroller.dragging = true;
	},
	onScrollerDragMove : function(){
		if (this.isPrinting) return;

		if (this.scroller.dragging)
			this.showPage(parseInt(this.scroller.getAttribute('curpos')));
	},
	onScrollerDragDrop : function(){
		if (this.isPrinting) return;

		this.onScrollerDragMove();
		this.scroller.dragging = false;
	},

	onIndicatorBarClick : function(aEvent)
	{
		if (this.isPrinting) return;

		var bar = this.indicatorBar;
		this.showPage(Math.round((aEvent.screenX - bar.boxObject.screenX) / bar.boxObject.width * this.data.length));
	},
	onIndicatorBarDragStart : function()
	{
		if (this.isPrinting) return;

		this.indicatorBar.dragging = true;
	},
	onIndicatorBarDragMove : function(aEvent)
	{
		if (this.isPrinting) return;

		var bar = this.indicatorBar;
		this.showPage(Math.round((aEvent.screenX - bar.boxObject.screenX) / bar.boxObject.width * this.data.length));
	},
	onIndicatorBarDragEnd : function(aEvent)
	{
		if (this.isPrinting) return;

		this.onIndicatorBarDragMove(aEvent);
		this.indicatorBar.dragging = false;
	},

	onEdit : function() {
		if (this.isPrinting) return;

		this.data = this.textbox.value;
		this.init();
	},

	onKeyPress : function(aEvent) {
		if (this.isPrinting) return;

		switch(aEvent.keyCode)
		{
			case aEvent.DOM_VK_BACK_SPACE:
				if (this.isPresentationMode) {
					aEvent.preventBubble();
					aEvent.preventDefault();
					Presentation.back();
				}
				break;
			default:
				break;
		}
	},



	handleEvent : function(aEvent)
	{
		if (this.isPrinting) return;

		var node = aEvent.target;
		var inRawContents = false;
		do {
			if (node.nodeType == Node.ELEMENT_NODE &&
				/\braw\b/i.test(node.getAttribute('class'))) {
				inRawContents = true;
				break;
			}

			node = node.parentNode;
		}
		while (node.parentNode)


		switch (aEvent.type)
		{
			default:
				break;

			case 'resize':
				this.takahashi(); // redrwa
				break;

			case 'contextmenu':
				aEvent.stopPropagation();
				aEvent.preventCapture();
				aEvent.preventDefault();
				aEvent.preventBubble();
				break;


			case 'mouseup':
				if (inRawContents) return;
				this.dragStartX = -1;
				this.dragStartY = -1;
				if (this.indicatorBar.dragging)
					this.onIndicatorBarDragEnd(aEvent);
				break;

			case 'mousedown':
				if (inRawContents) return;
				if (this.dragStartX < 0) {
					this.dragStartX = aEvent.clientX;
					this.dragStartY = aEvent.clientY;
				}
				var box = this.indicatorBar.boxObject;
				if (!(aEvent.screenX < box.screenX ||
					aEvent.screenY < box.screenY ||
					aEvent.screenX > box.screenX+box.width ||
					aEvent.screenY > box.screenY+box.height))
					this.onIndicatorBarDragStart();
				break;

			case 'mousemove':
				if (inRawContents) return;
				this.checkShowHideToolbar(aEvent);
				if (this.indicatorBar.dragging) {
					this.onIndicatorBarDragMove(aEvent);
					return;
				}
				if (this.dragStartX > -1) {
					if (Math.abs(this.dragStartX-aEvent.clientX) > Math.abs(this.dragStartDelta) ||
						Math.abs(this.dragStartY-aEvent.clientY) > Math.abs(this.dragStartDelta)) {
						var event = document.createEvent('Events');
						event.initEvent('StartDragOnCanvas', false, true);
						this.canvas.dispatchEvent(event);
					}
				}
				break;

			case 'CanvasContentAdded':
				if (this.fitToCanvasTimer) {
					window.clearTimeout(this.fitToCanvasTimer);
					this.fitToCanvasTimer = null;
				}
				this.fitToCanvasTimer = window.setTimeout('Presentation.fitMainContentToCanvas()', 100);
				break;

			case 'DOMMouseScroll':
				if (
					(aEvent.detail > 0 && this.scrollCounter < 0) ||
					(aEvent.detail < 0 && this.scrollCounter > 0)
					)
					this.scrollCounter = 0;

				this.scrollCounter += aEvent.detail;
				if (Math.abs(this.scrollCounter) >= this.scrollThreshold) {
					if (aEvent.detail > 0)
						Presentation.forwardStep();
					else
						Presentation.back();

					this.scrollCounter = 0;
				}
				break;
		}
	},
	dragStartX : -1,
	dragStartY : -1,
	scrollCounter : 0,
	scrollThreshold : 10,



	onToolbarArea   : false,
	toolbarHeight   : 0,
	toolbarDelay    : 300,
	toolbarTimer    : null,
	isToolbarHidden : false,
	checkShowHideToolbar : function(aEvent) {
		if (!this.scroller || this.scroller.dragging || this.preventToShowHideToolbar) return;

		this.onToolbarArea = (aEvent.clientY < this.toolbarHeight);

		if (this.isToolbarHidden == this.onToolbarArea) {
			if (this.toolbarTimer) window.clearTimeout(this.toolbarTimer);
			this.toolbarTimer = window.setTimeout('Presentation.checkShowHideToolbarCallback()', this.toolbarDelay);
		}
	},
	checkShowHideToolbarCallback : function() {
		if (this.isToolbarHidden == this.onToolbarArea)
			this.showHideToolbar();
	},

	toolbarAnimationDelay : 100,
	toolbarAnimationSteps : 5,
	toolbarAnimationInfo  : null,
	toolbarAnimationTimer : null,
	showHideToolbar : function(aWithoutAnimation)
	{
		if (this.isPrinting) return;

		if (this.toolbarAnimationTimer) window.clearTimeout(this.toolbarAnimationTimer);

		this.toolbarAnimationInfo = { count : 0 };
		if (this.isToolbarHidden) {
			this.toolbarAnimationInfo.start = 0;
			this.toolbarAnimationInfo.end   = this.toolbarHeight;
		}
		else {
			this.toolbarAnimationInfo.start = this.toolbarHeight;
			this.toolbarAnimationInfo.end   = 0;
		}
		this.toolbarAnimationInfo.current = 0;

		this.toolbar.setAttribute('style', 'margin-top:'+(0-(this.toolbarHeight-this.toolbarAnimationInfo.start))+'px; margin-bottom:'+(0-this.toolbarAnimationInfo.start)+'px;');

		if (aWithoutAnimation) {
			this.toolbarAnimationInfo.current = this.toolbarHeight;
			Presentation.animateToolbar();
		}
		else {
			this.toolbarAnimationTimer = window.setTimeout('Presentation.animateToolbar()', this.toolbarAnimationDelay/this.toolbarAnimationSteps);
		}
	},
	animateToolbar : function()
	{
		this.toolbarAnimationInfo.current += parseInt(this.toolbarHeight/this.toolbarAnimationSteps);

		var top, bottom;
		if (this.toolbarAnimationInfo.start < this.toolbarAnimationInfo.end) {
			top    = this.toolbarHeight-this.toolbarAnimationInfo.current;
			bottom = this.toolbarAnimationInfo.current;
		}
		else {
			top    = this.toolbarAnimationInfo.current;
			bottom = this.toolbarHeight-this.toolbarAnimationInfo.current;
		}

		top    = Math.min(Math.max(top, 0), this.toolbarHeight);
		bottom = Math.min(Math.max(bottom, 0), this.toolbarHeight);

		this.toolbar.setAttribute('style', 'margin-top:'+(0-top)+'px; margin-bottom:'+(0-bottom)+'px');

		if (this.toolbarAnimationInfo.count < this.toolbarAnimationSteps) {
			this.toolbarAnimationInfo.count++;
			this.toolbarAnimationTimer = window.setTimeout('Presentation.animateToolbar()', this.toolbarAnimationDelay/this.toolbarAnimationSteps);
		}
		else
			this.isToolbarHidden = !this.isToolbarHidden;
	},



	get offset(){
		return this._offset;
	},
	set offset(aValue){
		this._offset = parseInt(aValue || 0);
		document.documentElement.setAttribute('lastoffset', this.offset);
		return this.offset;
	},

	get data(){
		var codes = document.getElementById('builtinCode');
		if (!this._data) {
			// mozilla splits very long text node into multiple text nodes whose length is less than 4096 bytes.
			// so, we must concat all the text nodes.
			this.textbox.value = "";
			for (var i = 0; i < codes.childNodes.length; i++) {
				this.textbox.value += codes.childNodes[i].nodeValue;
			}

			this._data = this.textbox.value.split(/----+/);
			this.initData();
		}
		if (codes)
			codes.parentNode.removeChild(codes);

		return this._data;
	},
	set data(aValue){
		this._data = aValue.split(/----+/);
		this.initData();
		return aValue;
	},
	initData : function()
	{
		var range = document.createRange();
		range.selectNodeContents(this.list);
		range.deleteContents();


		var regexp = [
				/^[\r\n\s]+/g,
				/[\r\n\s]+$/g,
				/(\r\n|[\r\n])/g
			];

		var title;
		var titleRegExp   = /^(TITLE::)([^\n]*)\n?/im;
		var header        = '';
		var headerRegExp  = /^(HEADER::)([^\n]*)\n?/im;
		var footer        = '';
		var footerRegExp  = /^(FOOTER::)([^\n]*)\n?/im;
		var chapter       = '';
		var chapterRegExp = /^(CHAPTER::)([^\n]*)\n?/im;
		var lastChapter;
		var alignGlobal   = 'center';
		var align;
		var alignRegExp   = /^((GLOBAL-)?ALIGN::)(left|right|center|start|end)?\n?/im;

		var imageMatchResults;
		var imagesRegExp  = this.makePhraseRegExp('%O%Oima?ge? +src="[^"]+" +width="[0-9]+" +height="[0-9]+"[^%C]*%C%C', 'gi');
		var imagesRegExp2 = this.makePhraseRegExp('%O%Oima?ge? +src="([^"]+)"', 'i');
		var image_src;

		var plainTextRegExp = this.makePhraseRegExp('(%O%O\#[^:]+:(.+)%C%C|%O%OEM:(.+)(:EM)?%C%C|%O%OPRE:(.+)(:PRE)?%C%C|%O%Oima?ge? +src="[^"]*"[^%C]+%C%C|%O%O([^\\|%C]+)(\\|[^%C]+)?%C%C|%O([^%O]+)%C)', 'gi');

		var hiddenRegExp = /^(HIDDEN|IGNORE)::true\n?/im;

		var loadRegExp = /^LOAD::(.+)\n?/im;

		var dataObj;
		var i, j,
			max = this._data.length;
		var fragment = document.createDocumentFragment();
		var popup;
		var dataPath;
		for (i = 0; i < max; i++)
		{
			image_src = null;
			align     = null;
			dataPath  = '';

			this._data[i] = this._data[i]
				.replace(regexp[0], '')
				.replace(regexp[1], '')
				.replace(regexp[2], '\n');

			if (loadRegExp.test(this._data[i])) {
				this._data[i] = this._data[i].replace(loadRegExp, '');
				dataPath = RegExp.$1;
			}

			if (hiddenRegExp.test(this._data[i])) {
				this._data.splice(i, 1);
				max--;
				i--;
				continue;
			}

			while (titleRegExp.test(this._data[i])) {
				this._data[i] = this._data[i].replace(titleRegExp, '');
				if (String(RegExp.$1).toUpperCase() == 'TITLE::')
					title = RegExp.$2 || '' ;
			}

			while (headerRegExp.test(this._data[i])) {
				this._data[i] = this._data[i].replace(headerRegExp, '');
				if (String(RegExp.$1).toUpperCase() == 'HEADER::')
					header = RegExp.$2 || '' ;
			}

			while (footerRegExp.test(this._data[i])) {
				this._data[i] = this._data[i].replace(footerRegExp, '');
				if (String(RegExp.$1).toUpperCase() == 'FOOTER::')
					footer = RegExp.$2 || '' ;
			}

			while (chapterRegExp.test(this._data[i])) {
				this._data[i] = this._data[i].replace(chapterRegExp, '');
				if (String(RegExp.$1).toUpperCase() == 'CHAPTER::')
					chapter = RegExp.$2 || '' ;
			}

			while (alignRegExp.test(this._data[i])) {
				this._data[i] = this._data[i].replace(alignRegExp, '');

				align = (RegExp.$3 || '').toLowerCase();
				if (align == 'left')
					align = 'start';
				else if (align == 'right')
					align = 'end';

				if (String(RegExp.$1).toUpperCase() == 'GLOBAL-ALIGN::') {
					alignGlobal = align;
					align = null;
				}
			}

			imageMatchResults = this._data[i].match(imagesRegExp);
			if (imageMatchResults) {
				for (j = imageMatchResults.length-1; j > -1; j--)
					image_src = this.preloadImage(imageMatchResults[j].match(imagesRegExp2)[1]);
			}

			this._data[i] = {
				load   : dataPath,
				header : header,
				footer : footer,
				text   : this._data[i].split('\n'),
				image  : image_src,
				align  : align || alignGlobal
			};
			this._data[i].plain = this._data[i].text
							.join('\n')
							.replace(plainTextRegExp, '$2$3$5$7$9')
							.split('\n');
			if (title !== void(0))
				this._data[i].title = title;

			this._data[i].chapter = chapter || title || '';
			if (lastChapter === void(0) ||
				lastChapter != this._data[i].chapter) {
				lastChapter = this._data[i].chapter;

				if (popup && popup.childNodes.length == 1) {
					fragment.removeChild(fragment.lastChild);
					fragment.appendChild(popup.removeChild(popup.lastChild));
				}

				popup = document.createElement('menupopup');
				fragment.appendChild(document.createElement('menu'));
				fragment.lastChild.setAttribute('label', this._data[i].chapter);
				fragment.lastChild.appendChild(popup);
			}

			popup.appendChild(document.createElement('menuitem'));
			popup.lastChild.setAttribute('type', 'radio');
			popup.lastChild.setAttribute('radiogroup', 'pages');
			popup.lastChild.setAttribute('label', (i+1)+': '+(
				(this._data[i].plain.join('') || this._data[i].text.join(' ')).replace(/\s+/g, ' ')
			));
			popup.lastChild.setAttribute('value', i);

//			if (image_src) {
//				popup.lastChild.setAttribute('image', image_src);
//				popup.lastChild.setAttribute('class', 'menuitem-iconic');
//			}
		}

		if (fragment.childNodes.length == 1) {
			range.selectNodeContents(fragment.firstChild.firstChild);
			fragment = range.extractContents();
		}
		this.list.appendChild(fragment);

		range.detach();


		this.shownMontaLabels = [];
	},

	preloadImage : function(aURI)
	{
		if (aURI in this.imageRequests) return;

		if (aURI.indexOf('http://') < 0 &&
			aURI.indexOf('https://') < 0)
			aURI = this.dataFolder+aURI;

		this.imageRequests[aURI] = new XMLHttpRequest();
		try {
			this.imageRequests[aURI].open('GET', aURI);
			this.imageRequests[aURI].onload = function() {
				Presentation.imageRequests[aURI] = null;
			};
			this.imageRequests[aURI].send(null);
		}
		catch(e) {
			this.imageRequests[aURI] = null;
		}
		return aURI;
	},
	imageRequests : {},


	get isPresentationMode(){
		return (this.deck.selectedIndex == 0);
	},


	get dataPath(){
		if (!this._dataPath)
			this.dataPath = String(location.href).replace(/#.+$/, '');
		return this._dataPath;
	},
	set dataPath(aValue){
		var oldDataPath = this._dataPath;
		this._dataPath = aValue;
		if (oldDataPath != aValue) {
			this._dataFolder = this._dataPath.split('?')[0].replace(/[^\/]+$/, '');
		}
		return this._dataPath;
	},

	get dataFolder(){
		if (!this._dataFolder)
			this.dataPath = this.dataPath;
		return this._dataFolder;
	},
	set dataFolder(aValue){
		this._dataFolder = aValue;
		return this._dataFolder;
	},

	readParameter : function() {
		if (location.search || location.hash) {
			var param = location.search.replace(/^\?/, '');

			if (location.hash.match(/page([0-9]+)/i) ||
				param.match(/page=([0-9]+)/i))
				this.offset = parseInt(RegExp.$1)-1;

			if (location.hash.match(/edit/i) ||
				param.match(/edit=(1|true|yes)/i))
				this.toggleEditMode();

			if (location.hash.match(/eva/i) ||
				param.match(/eva=(1|true|yes)/i))
				this.toggleEvaMode();

			if (location.hash.match(/timer(\d+)\-(\d+)/i))
				this.setTimer(RegExp.$1, RegExp.$2);

			if (param.match(/(style|css)=([^&;]+)/i)) {
				var style = unescape(RegExp.$2);
				var pi = document.createProcessingInstruction('xml-stylesheet', 'href="'+style+'" type="text/css"');
				document.insertBefore(pi, document.documentElement);
			}

			if (param.match(/data=([^&;]+)/i)) {
				this.loadData(RegExp.$1);
				return false;
			}
		}
		return true;
	},
	loadData : function(aPath)
	{
		this.dataPath = aPath;
		var request = new XMLHttpRequest();
		request.open('GET', aPath);
		request.onload = function() {
			Presentation.textbox.value = request.responseText;
			Presentation.data = Presentation.textbox.value;
			Presentation.init();
		};
		request.send(null);
	},



	resetTimer : function()
	{
		if (this.timerTimer) {
			window.clearInterval(this.timerTimer);
			this.timerTimer = null;
		}
		this.timer.setAttribute('value', 0);
		this.timer.setAttribute('collapsed', true);
		this.setHash('timer', '');
	},
	setTimer : function(aStart, aEnd)
	{
		var now = (new Date()).getTime();
		if (aStart === void(0) || aEnd === void(0)) {
			var rest = prompt('Remaining Time (minits)');
			if (rest == '') {
				this.resetTimer();
				return;
			}
			else {
				rest = Number(rest);
				if (!rest || isNaN(rest)) return;
			}

			rest = Math.abs(rest);
			this.timerStart = now;
			this.timerEnd   = this.timerStart + (rest * 60000);
		}
		else {
			aStart = Number(aStart);
			aEnd   = Number(aEnd);
			if (isNaN(aStart) || isNaN(aEnd)) return;

			this.timerStart = Math.min(aStart, aEnd);
			this.timerEnd   = Math.max(aStart, aEnd);

			if (this.timerStart >= now || this.timerEnd <= now) return;
		}

		this.resetTimer();

		this.timer.removeAttribute('collapsed');
		this.setHash('timer', 'timer'+this.timerStart+'-'+this.timerEnd);

		if (now != this.timerStart)
			this.updateTimer(this);

		window.setInterval(this.updateTimer, Math.min(this.timerUpdatingInterval, (this.timerEnd-this.timerStart)/(this.data.length*2)), this);
	},
	updateTimer : function(aThis)
	{
		var now = (new Date()).getTime();
		if (now >= aThis.timerEnd) {
			aThis.resetTimer();
			aThis.timer.setAttribute('value', 100);
			aThis.timer.removeAttribute('collapsed');
			aThis.setHash('timer', '');
		}
		else {
			var value = parseInt(((now - aThis.timerStart) / (aThis.timerEnd - aThis.timerStart)) * 100);
			aThis.timer.setAttribute('value', value);
		}
	},
	timerStart : 0,
	timerEnd   : 0,
	timerTimer : null,



	print : function()
	{
		if (!this.canMove) {
			alert('Please wait for a while, and retry later.');
			return;
		}

		this.stopPrint();
		if (this.printWindow) {
			this.printWindow.close();
			this.printWindow = null;
		}

		if (!this.isToolbarHidden)
			this.showHideToolbar(true);

		this.printWindow = window.open('output.htm', 'PresentationPrint', 'dependent=yes,hotkeys=yes,location=yes,menubar=yes,personalbar=yes,scrollbars=yes,status=yes,toolbar=yes');
		if (!this.printWindow) return;

		this.isPrinting = true;

		if (!this.printCanvas)
			this.printCanvas = document.createElementNS(XHTMLNS, 'canvas');

		this.printWindow.document.write('<html><head><title>'+document.title+'</title></head><body></body></html>');
		this.home();
		this.printTimer = window.setInterval(this.printCallback, 0, this);
	},
	printCallback : function(aThis)
	{
		if (
			!aThis.canMove
			)
			return;

		var monta = document.getElementsByAttribute('monta-hidden', 'true');
		if (monta && monta.length) {
			for (var i = monta.length-1; i > -1; i--)
				aThis.showMontaKeyword(monta[i], true);
		}

		var doc  = aThis.printWindow.document;
		var body = doc.getElementsByTagName('body')[0];
		var img  = doc.createElement('img');

		if ((aThis.offset+1) % 2 == 1) {
			body.appendChild(doc.createElement('div'));
//			body.lastChild.style.clear = 'both';
		}
		var box = doc.createElement('div');
		box.appendChild(doc.createElement('div'));
		box.lastChild.appendChild(document.createTextNode(aThis.offset+1));
		body.lastChild.appendChild(box);

		var w = window.innerWidth;
		var h = window.innerHeight;
		var canvasW = parseInt(w * aThis.printSize);
		var canvasH = parseInt(h * aThis.printSize);

		aThis.printCanvas.width  = canvasW;
		aThis.printCanvas.height = canvasH;
		aThis.printCanvas.style.border = 'black solid medium';

		img.style.border = 'black solid medium';
		img.style.width  = canvasW+'px';
		img.style.height = canvasH+'px';

		box.style.margin = '1em';
		box.style.width  = parseInt(w * aThis.printSize)+'px';
		box.style.cssFloat  = ((aThis.offset+1) % 2 == 1) ? 'left' : 'right' ;

		try {
			netscape.security.PrivilegeManager.enablePrivilege('UniversalBrowserRead');

			var ctx = aThis.printCanvas.getContext('2d');
			ctx.clearRect(0, 0, canvasW, canvasH);
			ctx.save();
			ctx.scale(aThis.printSize, aThis.printSize);
			ctx.drawWindow(window, 0, 0, w, h, 'rgb(255,255,255)');
			ctx.restore();
			try {
				if (aThis.imageType == 'jpeg')
					img.src = aThis.printCanvas.toDataURL('image/jpeg', 'quality=50');
				else
					img.src = aThis.printCanvas.toDataURL('image/png', 'transparency=none');

				box.appendChild(img);
			}
			catch(e) {
				box.appendChild(aThis.printCanvas.cloneNode(true));
				ctx = box.lastChild.getContext('2d');
				ctx.clearRect(0, 0, canvasW, canvasH);
				ctx.save();
				ctx.scale(aThis.printSize, aThis.printSize);
				ctx.drawWindow(window, 0, 0, w, h, 'rgb(255,255,255)');
				ctx.restore();
			}
		}
		catch(e) {
			alert('Error: Failed to create a document for printing.\n\n------\n'+e);
			aThis.stopPrint();
			return;
		}

		if (aThis.offset == aThis.data.length-1) {
			aThis.stopPrint();
			aThis.printWindow.focus();
		}
		else {
			aThis.forward();
		}
	},
	stopPrint : function()
	{
		window.clearInterval(this.printTimer);
		this.printTimer = null;
		this.isPrinting = false;
	},
	printSize   : 0.4,
	printTimer  : null,
	printWindow : null,
	printCanvas : null

};





var StrokeService = {
	className      : 'stroke-dot',
	dragStartDelta : 8,
	lineColor      : 'red',
	lineWidth      : 3,

	initialized : false,


	mode          : null,
	canvas        : null,
	canvasContext : null,
	startX        : -1,
	startY        : -1,

	init : function(aCanvas)
	{
		this.initialized = true;

		this.canvas = aCanvas;

		var canvas = document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas');
		this.canvas.appendChild(canvas);
		if (!('getContext' in canvas) || !canvas.getContext) {
			this.canvas.removeChild(canvas);
			this.mode = 'box';
		}
		else {
			this.canvas        = canvas;
			this.canvasContext = this.canvas.getContext('2d');
			this.mode          = 'canvas';
		}

		document.documentElement.addEventListener('PresentationRedraw', this, false);
		window.addEventListener('resize', this, false);
		this.canvas.addEventListener('mouseup',   this, false);
		this.canvas.addEventListener('mousedown', this, false);
		this.canvas.addEventListener('mousemove', this, false);

		this.canvas.addEventListener('click', this, false);
		this.canvas.addEventListener('dblclick', this, false);

		this.clear();
	},

	destroy : function()
	{
		document.documentElement.removeEventListener('PresentationRedraw', this, false);
		window.removeEventListener('resize', this, false);
		this.canvas.removeEventListener('mouseup', this, false);
		this.canvas.removeEventListener('mousedown', this, false);
		this.canvas.removeEventListener('mousemove', this, false);
		this.canvas.removeEventListener('click', this, false);

		this.cliclableNodesManager = null;
		this.canvas = null;

		this.initialized = false;
	},



	handleEvent : function(aEvent)
	{
		switch(aEvent.type)
		{
			default:
				break;

			case 'mouseup':
				this.finish(aEvent);
				this.startX = -1;
				this.startY = -1;
				window.setTimeout('StrokeService.preventToSendClickEvent = false', 10);
				break;

			case 'mousedown':
				if (this.startX < 0) {
					this.startX = aEvent.clientX;
					this.startY = aEvent.clientY;
				}
				break;

			case 'mousemove':
				if (this.startX > -1 && !this.active) {
					if (Math.abs(this.startX-aEvent.clientX) > Math.abs(this.dragStartDelta) ||
						Math.abs(this.startY-aEvent.clientY) > Math.abs(this.dragStartDelta)) {
						this.start(aEvent, this.startX, this.startY);
						this.preventToSendClickEvent = true;
					}
				}
				else
					this.trace(aEvent);

				break;

			case 'PresentationRedraw':
			case 'resize':
				this.clear();
				break;

			case 'click':
				if (this.preventToSendClickEvent) {
					aEvent.stopPropagation();
					aEvent.preventCapture();
					aEvent.preventDefault();
					aEvent.preventBubble();
					this.preventToSendClickEvent = false;
				}
				else if (this.cliclableNodesManager && this.cliclableNodesManager.clickableNodes) {
					var nodes = this.cliclableNodesManager.clickableNodes;
					var max = nodes.length;
					var x, y, width, height
					for (var i = 0; i < max; i++)
					{
						if (nodes[i].boxObject) {
							x      = nodes[i].boxObject.x;
							y      = nodes[i].boxObject.y;
							width  = nodes[i].boxObject.width;
							height = nodes[i].boxObject.height;
						}
						else {
							x      = nodes[i].offsetLeft;
							y      = nodes[i].offsetTop;
							width  = nodes[i].offsetWidth;
							height = nodes[i].offsetHeight;
						}
						if (aEvent.clientX < x || 
							aEvent.clientX > x+width ||
							aEvent.clientY < y || 
							aEvent.clientY > y+height)
							continue;

						var event = document.createEvent('MouseEvents');
						event.initMouseEvent(
							aEvent.type, aEvent.canBubble, aEvent.cancelable, aEvent.view,
							aEvent.detail,
							aEvent.screenX, aEvent.screenY, aEvent.clientX, aEvent.clientY,
							aEvent.ctrlKey, aEvent.altKey, aEvent.shiftKey, aEvent.metaKey,
							aEvent.button,
							aEvent.relatedTarget
						);
						nodes[i].dispatchEvent(event);
						break;
					}
				}
				break;
		}
	},
	preventToSendClickEvent : false,



	start : function(aEvent, aX, aY)
	{
		this.active = true;
		this.trace(aEvent, aX, aY);
	},

	finish : function(aEvent)
	{
		if (!this.active) return;
		this.trace(aEvent);
		this.finishStroke();
	},

	trace : function(aEvent, aX, aY)
	{
		if (!this.active) return;
		this.addPoint((aX === void(0) ? aEvent.clientX : aX ), (aY === void(0) ? aEvent.clientY : aY ));
	},


	finishStroke : function()
	{
		this.active = false;
		this.lastX = -1;
		this.lastY = -1;
	},


	addPoint : function(aX, aY)
	{
		if (this.lastX != -1)
			this.drawLine(this.lastX, this.lastY, aX, aY);
		else
			this.drawDot(aX, aY);

		this.lastX = aX;
		this.lastY = aY;
	},



	clear : function()
	{
		this.active = false;
		this.lastX = -1;
		this.lastY = -1;

		if (this.mode == 'canvas') {
			if (this.canvas.lastWindowWidth != window.innerWidth ||
				this.canvas.lastWindowHeight != window.innerHeight) {
				this.canvas.width  = this.canvas.parentNode.boxObject.width-2;
				this.canvas.height = this.canvas.parentNode.boxObject.height-2;

				this.canvas.lastWindowWidth  = window.innerWidth;
				this.canvas.lastWindowHeight = window.innerHeight;
			}
			this.canvasContext.clearRect(0, 0, this.canvas.width, this.canvas.height);
			this.canvasContext.strokeStyle = this.lineColor;
			this.canvasContext.lineWidth   = this.lineWidth;
		}
		else {
			var dotes = this.canvas.getElementsByAttribute('class', this.className);
			if (!dotes.length) return;

			var range = document.createRange();
			range.selectNodeContents(this.canvas);
			range.setStartBefore(dotes[0]);
			range.setEndAfter(dotes[dotes.length-1]);
			range.deleteContents();
			range.detach();
		}
	},

	drawDot : function(aX, aY, aParent)
	{
		if (this.mode == 'canvas') {
			this.canvasContext.strokeRect(aX, aY, 0, 0);
			this.canvasContext.stroke();
		}
		else {
			var dot = document.createElement('spacer');
			dot.setAttribute('style', 'left:'+aX+'px; top:'+aY+'px');
			dot.setAttribute('class', this.className);
			(aParent || this.canvas).appendChild(dot);
		}
	},
	drawLine : function(aX1, aY1, aX2, aY2)
	{
		if (aX1 == aX2 && aY1 == aY2) return;


		if (this.mode == 'canvas') {
			this.canvasContext.beginPath();
			this.canvasContext.moveTo(aX1, aY1);
			this.canvasContext.lineTo(aX2, aY2);
/*
			this.canvasContext.bezierCurveTo(
				parseInt(aX1+((aX2-this.lastX)*0.3)), parseInt(aY1+((aY2-this.lastY)*0.3)),
				parseInt(aX1+((aX2-this.lastX)*0.6)), parseInt(aY1+((aY2-this.lastY)*0.6)),
				aX2, aY2
			);
*/
			this.canvasContext.closePath();
			this.canvasContext.stroke();
		}
		else {
			var x_move = aX2 - aX1;
			var y_move = aY2 - aY1;
			var x_diff = x_move < 0 ? 1 : -1;
			var y_diff = y_move < 0 ? 1 : -1;

			var fragment = document.createDocumentFragment();
			if (Math.abs(x_move) >= Math.abs(y_move)) {
				for (var i = x_move; i != 0; i += x_diff)
					this.drawDot(aX2 - i, aY2 - Math.round(y_move * i / x_move), fragment);
			}
			else {
				for (var i = y_move; i != 0; i += y_diff)
					this.drawDot(aX2 - Math.round(x_move * i / y_move), aY2 - i, fragment);
			}
			this.canvas.appendChild(fragment);
		}
	}
};





var StrokablePresentationService = {

	id : 'stroke-canvas-box',

	strokeService         : null,
	cliclableNodesManager : null,
	canvasContainer       : null,
	canvas                : null,

	autoStart : false,

	init : function(aPresentation, aStrokeService)
	{
		this.cliclableNodesManager = aPresentation;
		this.strokeService         = aStrokeService;
		this.canvasContainer       = document.getElementById('canvas').firstChild;
		this.check = document.getElementById('penButton');

		document.documentElement.addEventListener('StartDragOnCanvas', this, false);
		document.documentElement.addEventListener('PresentationRedraw', this, false);
	},

	toggle : function(aEnable)
	{
		if (aEnable)
			this.start();
		else
			this.end();
	},

	start : function()
	{
		if (!this.strokeService || !this.canvasContainer) return;

		this.strokeService.cliclableNodesManager = this.cliclableNodesManager;
		var box = document.createElement('vbox');
		box.setAttribute('flex', 1);
		box.setAttribute('id', this.id);
		this.canvas = this.canvasContainer.appendChild(box);
		this.strokeService.init(this.canvas);

		this.canvas.addEventListener('dblclick', this, false);
	},

	end : function()
	{
		this.canvas.removeEventListener('dblclick', this, false);

		this.strokeService.destroy();
		this.canvasContainer.removeChild(this.canvas);
		this.canvas = null;
	},

	handleEvent : function(aEvent)
	{
		switch (aEvent.type)
		{
			default:
				break;

			case 'StartDragOnCanvas':
				if (!this.check.checked) {
					this.toggleCheck();
					this.strokeService.startX = Presentation.dragStartX;
					this.strokeService.startY = Presentation.dragStartY;

					this.autoStart = true;
				}
				break;

			case 'PresentationRedraw':
				if (this.autoStart && this.check.checked) {
					this.autoStart = false;
					this.toggleCheck();
				}
				break;

			case 'dblclick':
				if (this.canvas)
					this.end();
				break;
		}
	},

	toggleCheck : function()
	{
		var enable = !this.check.checked;
		this.toggle(enable);
		this.check.checked = enable;

		this.autoStart = false;
	}

};




function init()
{
	window.removeEventListener('load', init, false);

	Presentation.init();
	StrokablePresentationService.init(Presentation, StrokeService);
}
window.addEventListener('load', init, false);




// http://ecmanaut.blogspot.com/2006/03/e4x-and-dom.html


function importE4XNode( e4x, doc, aDefaultNS )
{
  aDefaultNS = aDefaultNS || XHTMLNS;
  var root, domTree, importMe;
  this.Const = this.Const || { mimeType: 'text/xml' };
  this.Static = this.Static || {};
  this.Static.parser = this.Static.parser || new DOMParser;
  eval('root = <testing xmlns="'+aDefaultNS+'" />;');
  root.test = e4x;
  domTree = this.Static.parser.parseFromString( root.toXMLString(),
           this.Const.mimeType );
  importMe = domTree.documentElement.firstChild;
  while( importMe && importMe.nodeType != 1 )
    importMe = importMe.nextSibling;
  if( !doc ) doc = document;
  return importMe ? doc.importNode( importMe, true ) : null;
}

function appendE4XTo( e4x, node, doc, aDefaultNS )
{
  return node.appendChild( importE4XNode( e4x, (doc || node.ownerDocument), aDefaultNS ) );
}

function setE4XContent( e4x, node, aDefaultNS )
{
  while( node.firstChild )
    node.removeChild( node.firstChild );
  appendE4XTo( e4x, node, aDefaultNS );
}

// importE4XNodeで得たノードツリーを埋め込むと、XULでバインディングが適用されないことがある。
// 遅延処理でこの問題を一部避けることができる(が、これでもまだダメな場合がある。menuとか。)
// とりあえずXULとSVGとXHTMLはいけた。MathMLはダメだった。
function importNodeTreeWithDelay(aNode, aParent, aDefaultNS, aFromTimeout)
{
	if (aFromTimeout) {
		importNodeTreeWithDelayTimers--;
	}

	var node;
	var delay = 1;
	switch (aNode.nodeType)
	{
		case Node.ELEMENT_NODE:
			var ns = (aNode.namespaceURI || aDefaultNS);
			node = document.createElementNS(ns, aNode.localName);
			aParent.appendChild(node);

			var attr = aNode.attributes;
			for (var i = 0, maxi = attr.length; i < maxi; i++)
				node.setAttribute(attr[i].name, attr[i].value);

			if (ns == XULNS) delay = 1; else delay = 0;

			var children = aNode.childNodes;
			for (var i = 0, maxi = children.length; i < maxi; i++)
				if (delay) {
					importNodeTreeWithDelayTimers++;
					window.setTimeout(importNodeTreeWithDelay, delay, children[i], node, aDefaultNS, true);
				}
				else
					importNodeTreeWithDelay(children[i], node, aDefaultNS);
			break;

		default:
			if (
				aNode.nodeType == Node.TEXT_NODE &&
				/^\s*$/.test(aNode.nodeValue) &&
				(aNode.parentNode.namespaceURI || aDefaultNS) != XHTMLNS
				)
				return;
			node = aParent.appendChild(aNode.cloneNode(true));
			break;
	}

	var event = document.createEvent('Events');
	event.initEvent('CanvasContentAdded', true, true);
	node.dispatchEvent(event);

	return node;
}
var importNodeTreeWithDelayTimers = 0;

]]></script>

</page>