The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
<?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
   - 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:EM]]
----
HEADER::高橋メソッドなプレゼンツール in XUL リターンズ
CHAPTER::操作説明
操作
----
HEADER::操作
基本的に
高橋メソッド
in XUL
と同じ
----
Enterか
Page Downか
左クリック
で[次のページ]
----
→でも
↓でも
次のページ
----
キーボード操作時は
次のページへ進む操作で
ラベルをめくれます
(めくっていない
ラベルがなければ
そのまま次のページへ)
----
一度めくったラベルは
状態を保持するので
----
前のページに戻して
「奥さん、いいですか!
ここですよここ!」
ができる
----
Back Spaceか
Page Upか
右クリック
で[前のページ]
----
←でも
↑でも
前のページ
----
Homeで
最初の
ページ
----
Endで
最後の
ページ
----
Ctrl-R
で[リロード]
----
Ctrl-Eで
[編集]モード
突入
----
HEADER::
CHAPTER::編集機能
編集
----
HEADER::編集
編集モード時は
Ctrl-Nで新しい
ページの追加
----
HEADER::機能
部分
強調
----
テキストを部分的に
[[EM:強調:EM]]できる
----
整形済み
テキスト
----
行内に[[PRE:preformatted text:PRE]]を
書ける(けどあんまり意味ない)
----
複数行にまたがる整形済みテキスト
[[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]]
ソースコードの例示などに使える
----
リンク
----
[[リンクも埋め込める|http://piro.sakura.ne.jp/]]
ラベル無しリンクも可能↓
[[http://piro.sakura.ne.jp/]]
----
[[image src="takahashi.png" width="300" height="168"]]
----
画像
----
インライン[[image src="takahashi.png" width="300" height="168"]]で
[[image src="takahashi.png" width="300" height="168"]]
いくつでも
埋め込める
----
リンクや
画像のパスの
相対指定は
----
データファイルの
あるディレクトリ
もしくは
本体のある
ディレクトリを
基準として解釈
----
HEADER::ヘッダ
FOOTER::フッタ
ヘッダとフッタも
自由に指定できる
----
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/>
			<spacer flex="1"/>
			<hbox>
				<toolbarbutton label="auto"
					id="autoButton"
					type="checkbox"
					autoCheck="false"
					oncommand="Presentation.toggleAutoCruiseMode();" />
				<toolbarbutton id="auto-interval-button"
					type="menu"
					label="Interval"
					tooltiptext="Change Interval"
					class="dropmarker-button">
					<menupopup id="auto-interval-list"
						onpopupshowing="
							Presentation.preventToShowHideToolbar = true;
							(this.getElementsByAttribute('value', Presentation.autoCruiseInterval)[0] || this.lastChild).setAttribute('checked', true);
						"
						onpopuphiding="Presentation.preventToShowHideToolbar = false;"
						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>
				</toolbarbutton>
			</hbox>
			<toolbarbutton label="Pen"
				id="penButton"
				type="checkbox"
				autoCheck="false"
				oncommand="StrokablePresentationService.toggleCheck();"/>
			<toolbarseparator/>
			<toolbarbutton id="toggleEva" label="Eva"
				type="checkbox"
				autoCheck="false"
				oncommand="Presentation.toggleEvaMode();"/>
			<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"/>
				</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>
</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 key="e" modifiers="accel" oncommand="Presentation.toggleEditMode();"/>
	<key key="e" modifiers="accel,shift" oncommand="Presentation.toggleEvaMode();"/>
</keyset>


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

var Presentation = {

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

	dragStartDelta : 8,


	initialized : false,

	preventToShowHideToolbar : false,

	showMontaKeywordTimeout : 100,
	autoCruiseInterval      : 2000,
	standByMontaLabels : [],
	shownMontaLabels   : [],

	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.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);


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

		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() {
		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.standByMontaLabels = [];
		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 text = this.data[this.offset].text;
		var range = document.createRange();
		range.selectNodeContents(this.content);
		range.deleteContents();
		range.detach();

		var line;
		var newLine;
		var uri;
		var image_width;
		var image_total_width  = 0;
		var image_height;
		var image_total_height = 0;
		var image_src;

		var labelId = 0;

		var lineRegExp = /^([^\[]+)?(\[\[em:(.+?):em\]\]|\[\[pre:((.+?):pre\]\])?|\[\[ima?ge? +src="([^"]+)" +width="([0-9]+)" +height="([0-9]+)"[^\]]*\]\]|\[\[(([^\|]+)?\||)([^\]]+)\]\]|\[([^\]]+)\])(.+)?/i;

		var emRegExp       = /^([^\[]+)?\[\[em:(.+?):em\]\]/i;
		var preRegExp      = /^([^\[]+)?\[\[pre:(.+?):pre\]\]/i;
		var preStartRegExp = /^([^\[]+)?\[\[pre:(.*)/i;
		var preEndRegExp   = /^(.*?)(:pre\]\])/i;
		var imagesRegExp   = /^([^\[]+)?\[\[ima?ge? +src="([^"]+)" +width="([0-9]+)" +height="([0-9]+)"[^\]]*\]\]/i;
		var linksRegExp    = /^([^\[]+)?\[\[(([^\|]+)?\||)([^\]]+)\]\]/;
		var montaRegExp    = /^([^\[]+)?\[([^\]]+)\]/;

		var inPreBlock  = false;
		var preContents = [];

		var i,
			max = text.length;

		var fragment = document.createDocumentFragment();

		for (i = 0; i < max; i++)
		{
			fragment.appendChild(document.createElement('hbox'));
			fragment.lastChild.setAttribute('align', 'center');
			fragment.lastChild.setAttribute('pack', 'center');

			line = text[i];
			image_width  = 0;
			image_height = 0;

			if (inPreBlock) {
				if (preEndRegExp.test(line)) {
					inPreBlock = false;
					preContents.push(RegExp.$1);
					line = line.substring((RegExp.$1+RegExp.$2).length);

					fragment.lastChild.appendChild(document.createElement('description'));
					fragment.lastChild.lastChild.setAttribute('class', 'preformatted-text block');
					fragment.lastChild.lastChild.appendChild(document.createTextNode(
						preContents.join('\n')
							.replace(/^[\r\n\s]+/, '')
							.replace(/[\r\n\s]+$/, '')
							.replace(/&amp;/g, '&')
							.replace(/&quot;/g, '"')
							.replace(/&gt;/g, '>')
							.replace(/&lt;/g, '<')
					));
				}
				else {
					preContents.push(line);
					continue;
				}
			}

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

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

				// Emphasis
				else if (emRegExp.test(line)) {
					fragment.lastChild.appendChild(document.createElement('description'));
					fragment.lastChild.lastChild.setAttribute('value', RegExp.$2);
					fragment.lastChild.lastChild.setAttribute('class', 'em-text');
				}

				// Images
				else if (imagesRegExp.test(line)) {
					fragment.lastChild.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;
					fragment.lastChild.lastChild.setAttribute('src', image_src);
					fragment.lastChild.lastChild.setAttribute('width', parseInt(RegExp.$3 || '0'));
					fragment.lastChild.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;
					fragment.lastChild.appendChild(document.createElement('description'));
					fragment.lastChild.lastChild.setAttribute('value', RegExp.$3 || RegExp.$4);
					fragment.lastChild.lastChild.setAttribute('href', uri);
					fragment.lastChild.lastChild.setAttribute('tooltiptext', uri);
					fragment.lastChild.lastChild.setAttribute('statustext', uri);
					fragment.lastChild.lastChild.setAttribute('class', 'link-text');

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

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

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

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

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

					if (!this.shownMontaLabels[this.offset] || !this.shownMontaLabels[this.offset]['label-'+labelId]) {
						fragment.lastChild.lastChild.lastChild.setAttribute('monta-hidden', 'true');
						this.standByMontaLabels[this.standByMontaLabels.length] = fragment.lastChild.lastChild.lastChild;
					}
					else {
						fragment.lastChild.lastChild.lastChild.setAttribute('monta-hidden', 'false');
					}

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

				line = newLine;
			}

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

			image_total_width = Math.max(image_total_width, image_width);
			image_total_height += image_height;
		}

		this.content.setAttribute('style', 'font-size:10px;');
		this.content.appendChild(fragment);

		this.clickableNodes.push(this.content);


		this.fitToCanvas(this.header, this.header.parentNode, 0, 0);
		this.fitToCanvas(this.footer, this.footer.parentNode, 0, 0);
		this.fitToCanvas(
			this.content,
			this.canvas,
			image_total_width,
			image_total_height
			+(this.header.boxObject.height*2)
//			+(this.footer.boxObject.height*2)
		);


		var checkedItems = this.list.getElementsByAttribute('checked', 'true');
		max = checkedItems.length;
		for (i = 0; i < max; i++)
			checkedItems[i].removeAttribute('checked');

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

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


		var event = document.createEvent('Events');
		event.initEvent('PresentationRedraw', false, true);
		this.canvas.dispatchEvent(event);
	},
	fitToCanvas : function(aContent, aCanvas, aOffsetWidth, aOffsetHeight)
	{
		aContent.removeAttribute('style');
		aContent.setAttribute('style', 'font-size:10px;');

		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");

		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");
		}

		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");
		}
	},

	reload : function() {
		var file = String(location.href).replace(/#.+$/, '');
		if (this.dataPath != file) {
			var path = this.dataPath;
			var request = new XMLHttpRequest();
			request.open('GET', path);
			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(){
		this.offset++;
		this.takahashi();
	},
	forwardStep : function(){
		if (this.standByMontaLabels.length > 0) {
			while (this.standByMontaLabels.length &&
					this.standByMontaLabels[0].getAttribute('monta-hidden') != 'true')
				this.standByMontaLabels.splice(0, 1);

			if (this.standByMontaLabels.length) {
				this.showMontaKeyword(this.standByMontaLabels[0]);
				this.standByMontaLabels.splice(0, 1);
			}
		}
		else
			this.forward();
	},
	back : function(){
		this.offset--;
		if(this.offset < 0){this.offset = 0}
		this.takahashi();
	},
	home : function(){
		this.offset = 0;
		this.takahashi();
	},
	end : function(){
		this.offset = this.data.length-1;
		this.takahashi();
	},
	showPage : function(aPageOffset){
		this.offset = aPageOffset ? aPageOffset : 0 ;
		this.takahashi();
	},

	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('[[EM::EM]]', this.textbox, 5);
				break;
			case 'pre':
			case 'preformatted':
				this.insertTextFor('[[PRE::PRE]]', this.textbox, 6);
				break;
			case 'monta':
				this.insertTextFor('[]', this.textbox, 1);
				break;
			case 'link':
				this.insertTextFor('[[|http://]]', this.textbox, 2);
				break;
			case 'img':
			case 'image':
				this.insertTextFor('[[image src="" width="" height=""]]', 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');
			check.checked = false;
		}
		else {
			this.canvas.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) {
			Presentation.home();
		}
		else {
			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) {
		if (aNode.getAttribute('monta-hidden') != 'true') return;

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

		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');
			if (!Presentation.shownMontaLabels[Presentation.offset])
				Presentation.shownMontaLabels[Presentation.offset] = [];
			Presentation.shownMontaLabels[Presentation.offset][aInfo.node.getAttribute('label-id')] = true;
			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);
	},



	onPresentationClick : function(aEvent)
	{
		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(){
		this.scroller.dragging = true;
	},
	onScrollerDragMove : function(){
		if (this.scroller.dragging)
			this.showPage(parseInt(this.scroller.getAttribute('curpos')));
	},
	onScrollerDragDrop : function(){
		if (this.scroller.dragging) {
			this.showPage(parseInt(this.scroller.getAttribute('curpos')));
		}
		 this.scroller.dragging = false;
	},
	onEdit : function() {
		this.data = this.textbox.value;
		this.init();
	},

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



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

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

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


			case 'mouseup':
				this.dragStartX = -1;
				this.dragStartY = -1;
				break;

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

			case 'mousemove':
				this.checkShowHideToolbar(aEvent);
				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;


		}
	},
	dragStartX : -1,
	dragStartY : -1,



	onToolbarArea   : false,
	toolbarHeight   : 0,
	toolbarDelay    : 300,
	toolbarTimer    : null,
	isToolbarHidden : false,
	checkShowHideToolbar : function(aEvent) {
		if (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()
	{
		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;');

		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(){
		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 < document.getElementById('builtinCode').childNodes.length; i++) {
				this.textbox.value += document.getElementById('builtinCode').childNodes[i].nodeValue;
			}

			this._data = this.textbox.value.split(/----+/);
			this.initData();
		}

		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   = /\b(TITLE::)([^\n]*)\n?/i;
		var header        = '';
		var headerRegExp  = /\b(HEADER::)([^\n]*)\n?/i;
		var footer        = '';
		var footerRegExp  = /\b(FOOTER::)([^\n]*)\n?/i;
		var chapter       = '';
		var chapterRegExp = /\b(CHAPTER::)([^\n]*)\n?/i;
		var lastChapter;

		var imageMatchResults;
		var imagesRegExp  = /\[\[ima?ge? +src="[^"]+" +width="[0-9]+" +height="[0-9]+"[^\]]*\]\]/gi;
		var imagesRegExp2 = /\[\[ima?ge? +src="([^"]+)"/i;
		var image_src;

		var plainTextRegExp = /(\[\[EM:(.+):EM\]\]|\[\[PRE:(.+):PRE\]\]|\[\[ima?ge? +src="[^"]*"[^\]]+\]\]|\[\[([^\|\]]+)(\|[^\]]+)?\]\]|\[([^\[]+)\])/gi;

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

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

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

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

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

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

			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] = {
				header : header,
				footer : footer,
				text   : this._data[i].split('\n'),
				image  : image_src
			};
			this._data[i].plain = this._data[i].text
							.join('\n')
							.replace(plainTextRegExp, '$2$3$4$6')
							.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].plain.join(' ') :
					this._data[i].text.join(' ')
				).replace(/\s\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 (param.match(/data=([^&;]+)/i)) {
				var path = unescape(RegExp.$1);
				this.dataPath = path;
				var request = new XMLHttpRequest();
				request.open('GET', path);
				request.onload = function() {
					Presentation.textbox.value = request.responseText;
					Presentation.data = Presentation.textbox.value;
					Presentation.init();
				};
				request.send(null);
				return false;
			}
		}
		return true;
	}
};





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.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);
	},

	end : function()
	{
		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;
		}
	},

	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);


]]></script>

</page>