import moose website
[gitmo/moose-htdocs.git] / moose_120308.xul
1 <?xml version="1.0" encoding="UTF-8"?> 
2 <!--
3         高橋メソッドなプレゼンツール in XUL リターンズ
4         made by Piro
5         http://piro.sakura.ne.jp/
6
7         based on
8         高橋メソッドなプレゼン作成ツール ver.2 made by mala
9         http://la.ma.la/blog/diary_200504080545.htm
10         もんたメソッドなプレゼン作成ツール made by mala
11         http://la.ma.la/blog/diary_200505310749.htm
12 -->
13  
14 <!-- ***** BEGIN LICENSE BLOCK ***** 
15    - Version: MPL 1.1
16    -
17    - The contents of this file are subject to the Mozilla Public License Version
18    - 1.1 (the "License"); you may not use this file except in compliance with
19    - the License. You may obtain a copy of the License at
20    - http://www.mozilla.org/MPL/
21    -
22    - Software distributed under the License is distributed on an "AS IS" basis,
23    - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
24    - for the specific language governing rights and limitations under the
25    - License.
26    -
27    - The Original Code is the Takahashi-Method-based Presentation Tool
28    - in XUL/Returns.
29    -
30    - The Initial Developer of the Original Code is SHIMODA Hiroshi.
31    - Portions created by the Initial Developer are Copyright (C) 2005-2007
32    - the Initial Developer. All Rights Reserved.
33    -
34    - Contributor(s): SHIMODA Hiroshi <piro@p.club.ne.jp>
35    -                 dynamis <dynamis@mozilla-japan.org>
36    -                 matobaa <matobaa@lily.freemail.ne.jp>
37    -
38    - ***** END LICENSE BLOCK ***** -->
39  
40 <?xml-stylesheet href="chrome://global/skin/" type="text/css"?> 
41 <?xml-stylesheet href="#builtinStyle" type="text/css"?>
42  
43 <page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" 
44         id="presentation"
45         xmlns:html="http://www.w3.org/1999/xhtml"
46         orient="vertical"
47         onkeypress="Presentation.onKeyPress(event);">
48          
49 <!-- Built-in Content --> 
50 <html:textarea id="builtinCode" style="visibility: collapse">
51 TITLE::Moose in the wild
52 GLOBAL-SIZE::9
53 riding 
54 wild
55 Moose 
56
57 ----
58 %%%%EM:Moose%%%%?
59
60 ----
61 hype:
62
63 ----
64 'postmodern'
65 object
66 system
67 for Perl 5
68 <!--
69 theres an explanation on the cpan page... It's a ref to Larry talking about perl as a postmodern language... let's move on.
70 -->
71
72 ----
73 reality:
74
75 ----
76 'academically'
77 correct
78 p5 OO
79 <!--
80 by 'borrowing' features
81 from other OO systems
82 - Lisp/CLOS
83 - Perl6
84 - ... ect
85 - I do not really know these that well so I can not really speak to there cribbed features
86 -->
87
88 ----
89 so what?
90 <!--
91 %%%%IMG src="http://bp2.blogger.com/_dH0q9hvpVHg/R86WXUE5b2I/AAAAAAAABh4/N9A5fc5KPnI/s320/thumb.jpg" height='*' width='*' %%%%
92 -->
93
94 ----
95 my concept
96 of OO was
97 ~off~
98 <!--
99 For me the biggest thing was to strictly wrap my head on how OO was ~meant~ to be.
100 I have an art school background, I missed out on all the CS OO classes.
101 I had been thinking of OO backwards
102 -->
103
104 ----
105 package as a 
106 collection of subs 
107 with access to 
108 common data
109 <!-- 
110 My pre-moose days had a really hard time with OO. I understood the basics
111 like a $car has $wheels, ect. But things were very fuzzy in my head. 
112 It's not really wrong, per say, but it's just not a very clear way to see the world.
113 Basically my OO world view said that $car $wheels were completely different from $bike $wheels 
114 -->
115
116 ----
117 moose changed
118 all that for me
119 <!--
120 Moose really flipped things for me.
121 It forced me to think of objects as attributes and subs that modify them.
122 -->
123
124 ----
125 build 
126 objects 
127 by attributes
128 <!--
129 It made me focus on the $wheels as the thing that defines the $car or the $bike. 
130 It's easy to talk about but, but for me, it was a completely different way to think about my code. 
131 -->
132
133
134 ---- 
135 different?
136 how?
137 <!--
138 a quick blow thru on what features you will be hearing more about
139 -->
140
141
142 ----
143 %%%%PRE:
144 use strict;
145 use warnings;
146 %%%%
147 included
148 by default
149 <!--
150 first, simple things, like why should some one opt-in for the proper way to do something?
151 -->
152
153 ----
154 %%%%PRE:
155 sub new {}
156 %%%%
157 built-in
158 <!--
159 ever get tired of writing the same 7 lines of code, now you don't have to.
160 -->
161
162 ----
163 free
164 accessors
165 <!--
166 once you define an attribute, why not have a getter/setter built for you?
167 -->
168
169 ----
170 types
171 <!--
172 Extend the basic perl data types to be much more specific
173 -->
174
175 ----
176 roles
177 <!--
178 Hello eye opeining experence.
179 -->
180
181 ----
182 ... ehh, not convinced
183 I'd have to see it in action.
184 <!--
185 don't worry it took me a while to wrap my head around all of this stuff too
186 -->
187
188 ----
189 what does Moose look like?
190 <!-- 
191 let's look at some code
192 -->
193
194 ----
195 let's define
196 a simple
197 example:
198 <!--
199 really simple example, but it will illustrate the general overview.
200 -->
201
202 ----
203 describe
204 a moose
205 (the animal)
206
207 ----
208 moose 
209 have 
210 names
211
212 ----
213 if no name
214 is given
215 default to 
216 'Bullwinkle'
217
218 ----
219 %%%%PRE:
220 my $bullwinkle = 
221    Animal::Moose->new;
222 print $bullwinkle->name; 
223    # 'Bullwinkle'
224
225 my $skippy = 
226    Animal::Moose->new( name => 'Skippy' )
227 print $skippy->name; 
228    # 'Skippy'
229 %%%%
230 <!--
231 yup thats what we want, now let's take a look at what Animal::Moose looks like.
232 -->
233
234 ----
235 pre-Moose:
236 %%%%PRE:
237 package Animal::Moose;
238 use strict;
239 use warnings;
240
241 sub new {
242    my ($class, %opts) = @_;
243    my $self = {
244       name => $opts{name} || 'Bullwinkle',
245    };
246    return bless $self, $class;
247 }
248
249 sub name {
250    my ($self, $new_name) = @_;
251    if ( defined $new_name ) {
252       $self->{name} = $new_name;
253    }
254    return $self->{name};                                                        
255 }
256 1;
257 %%%%
258 <!--
259 any questions?
260 -->
261
262 ----
263 Moose:
264 %%%%PRE:
265 package Animal::Moose;
266 use Moose;
267
268 has name => (
269    is => 'rw',
270    isa => 'Str',
271    default => 'Bullwinkle',
272 );
273 1;
274 %%%%
275 <!--
276 see no constructor, no strict, no warnings
277 if you want show the test
278 -->
279
280 ----
281 what is 
282 this 'has'?
283 <!--
284 ok So there you have it... Moose in the... err... code.
285 So, what is this 'has' thing? Moose runs with the 
286 theory that an object is a collection of attributes. 
287 A moose has a name, thus name is an attribute. 
288 -->
289
290 ----
291 all about 
292 attributes
293
294 ----
295 standard format:
296 %%%%PRE:
297 has "attribute name" => ( 
298    #attribute definition 
299 );
300 %%%%
301 <!--
302 an attribute has a name, and it has to define something in some manner. 
303 Here are the definitions that I use the most:
304 I think that these first three are required.
305 -->
306
307 ----
308 access level
309 %%%%PRE:
310 is => 'rw',
311 is => 'ro',
312 %%%%
313 <!--
314 if you need something to be updated, then it's 'rw'.
315 If it will never changed once set, it's 'ro'. 
316 really this just specifies what accessors are built for you.
317 -->
318
319 ----
320 specify type
321 %%%%PRE:
322 isa => 'Str', 
323 isa => 'Int', 
324 isa => 'ArrayRef', 
325 isa => 'HashRef', 
326 isa => 'CGI::Simple',
327 %%%%
328 <!--
329 let's moose know what type of thing to expect to be here
330 we'll get in to types a bit more later
331 -->
332
333 ----
334 need a default?
335 %%%%PRE:
336 default => 'string',
337 default => 12, 
338 default => sub{ [] },
339 default => sub{ {} },
340 %%%%
341 <!--
342 scalars can just be specified any thing else needs to be wrapped in a sub {}
343 -->
344
345 ----
346 required attribute
347 %%%%PRE:
348 required => 1,
349 %%%%
350 <!--
351 If this attribute is not specified on construction then die
352 -->
353
354 ----
355 'optional' attribute
356 %%%%PRE:
357 lazy => 1,
358 %%%%
359 <!--
360 I use this mostly as a timing safety net, IE if one attribute needs another to be built, then make the second one lazy.
361 Lazy attributes get built right be fore they are used, so this is also a really good way to keep your code 'lite' by 
362 setting things up so that you only use what you really need.
363 -->
364
365 ----
366 %%%% there's more | http://search.cpan.org/~stevan/Moose-0.38/lib/Moose.pm %%%%
367 <!--
368
369 -->
370
371 ----
372 why types
373 are cool?
374
375 ----
376 built-in 
377 assertions
378 <!--
379 Moose's type structure is basically a built-in assertion for the content of an attribute.
380 -->
381 ----
382 advantage?
383 they make my code die!
384 <!--
385 This is super handy as Moose is making sure that you know what your dealing with,
386 you do not have to have everything check to make sure that $input_data is going to be > 0 or what ever.
387 At first this was really annoying for me. As I was porting my instal run of Search things would always die. 
388 it was another symptom of bad code, Again Moose is forcing me to think about what I really want, and staying on task. 
389 -->
390
391 ----
392 my options?
393 %%%%PRE:
394   Any
395   Item
396       Bool
397       Maybe[`a]
398       Undef
399       Defined
400           Value
401               Num
402                 Int
403               Str
404                 ClassName
405           Ref
406               ScalarRef
407               ArrayRef[`a]
408               HashRef[`a]
409               CodeRef
410               RegexpRef
411               GlobRef
412                 FileHandle
413               Object
414                   Role
415 %%%%
416 %%%% more info on types | http://search.cpan.org/~stevan/Moose-0.38/lib/Moose/Util/TypeConstraints.pm %%%%
417 <!--
418 There are many types that Moose provides by default, mostly there an extension of the existing perl data objects.
419 -->
420
421 ----
422 ... they're missing one ...
423 <!--
424 but theres more... You can write your own.
425 -->
426
427 ----
428 ... so make your own ...
429 <!--
430 Here are some examples from work, ignore the role bit for now.
431 -->
432
433 ----
434 %%%%PRE:
435 package PowellsData::Types::Web;
436 use Moose::Role;
437 use Moose::Util::TypeConstraints;
438
439 type 'WebObject' 
440    => where { 
441       ref($_) =~ 
442          m/^(Apache2::Request|CGI::Simple)$/ 
443       }
444    => message { 
445       sprintf(
446          '(%s) needs to be an Apache2::Request 
447           object or a CGI::Simple object and 
448           it is a %s', 
449          $_, 
450          ref($_) 
451       ) } 
452    ;
453 %%%%
454 <!-- 
455 OK so this is from the Search code... There are a many ways to access this code... many are web based, let's specify that the object that holds our CGI session is of the right type and not just some weird chunk of data.
456 So as you can see, it's very much an assertion... is this thing the right type. ( I forget why I'm using ref vs isa there might be a reason, there might not be).
457 A type has a {where} block that returns 0|1, if one were good, if 0 bail, 
458 you can also specify a {message} if you would like to gussy up your error log with more helpful errors.
459 -->
460 ----
461 ... you can also
462 extend types 
463 via subtypes.
464
465 ----
466 %%%%PRE:
467 package PowellsData::Types::SQL;
468 use Moose::Role;
469 use Moose::Util::TypeConstraints;
470
471 subtype 'SQL_Select' 
472    => as 'Str'
473    => where { 
474          $_ =~ m/\bSELECT\b.+\bFROM\b/i 
475       };
476
477 subtype 'SQL_Insert' 
478    => as 'Str'
479    => where { 
480          $_ =~ 
481          m/\bINSERT\b.+(?:\bINTO\b)?.+\b(?:VALUES|SET|SELECT)\b/i 
482       };
483
484 subtype 'SQL_Update' 
485    => as 'Str'
486    => where { 
487          $_ =~ m/\bUPDATE\b.+\bSET\b/i 
488       };
489
490 subtype 'SQL_Delete' 
491    => as 'Str'
492    => where { 
493          $_ =~ m/\bDELETE\b.+\bFROM\b/i };
494
495 subtype 'SQL' 
496    => as q{SQL_Select|SQL_Insert|SQL_Update|SQL_Delete} ;
497 %%%%
498 <!--
499 In this example, I just built out some regexes that sit on top of the string type. 
500 So if you were to pass in an arrayref for example, these would die long before the regex were to be checked.
501 Things to note here: 
502 - subtypes just extend an existing type (as) 
503 - where is the same, something that returns (0|1)
504 - you can extend any other defined type/subtype and even group them (SQL)
505 -->
506
507 ----
508 so roles?
509
510 ----
511 how to eat a moose
512
513 ----
514 ... small pieces
515
516 ----
517 roles ~ includes
518 <!--
519 Roles are another thing that blew my mind. At it's most basic you can think of a role as 
520 a include in to the current file. 
521 -->
522
523 ----
524 usage (theory):
525 1) abstract the common
526 <!--
527 Before we really jump to some example, theres basically two 
528 ways that I find my self using roles. The first is as 
529 an abstarction layer to something very common. 
530 -->
531
532 ----
533 example:
534 %%%%PRE:
535 package PowellsData::Setup::Database;
536 use Moose::Role;
537 use DBHost;
538
539 sub d;
540 has d  => ( is => 'rw', 
541             isa => 'DBI::db',
542             required => 1,
543             default => sub{ my $d = _dbiconnect('current'); 
544                             $d->{mysql_auto_reconnect} = 1; 
545                             $d->do("SET NAMES utf8");
546                             return $d;
547                           }
548           );
549
550
551
552 1;
553 %%%%
554 <!-- 
555 So this basically sets up a database handle for us. It's an abstraction of 
556 DBHost, a Powells rapper around DBI that manages what server to use and
557 what user:password to use. This layer does some minor set up on top of that. 
558 We check the type, we are saying that this is required and default will build 
559 one for us if there was not passed in at construction.
560 Things to note:
561 - use Moose::Role is the only thing that has changed from a 'Moose' Object.
562 - this sub hack... 
563 -->
564
565 ----
566 now in our code just say:
567 %%%%PRE:
568 package My::Bad::DB::Example;
569 use Moose;
570
571 with q{PowellsData::Setup::Database};
572
573 sub run_query {
574    my ($self, $query) = @_;
575    return $self->d->do($query);
576 }
577 %%%%
578 <!--
579 this one line now gives me an attribute 'd' that hast to be a 'DBI::db', 
580 everything gets set up for us, it's all in the role. So now I never have to 
581 remember specifics, I just get a DB handle.
582 it's like I had written all of that code in-line.
583 -->
584
585 ----
586 usage (theory):
587 2) big idea as small chunks
588 <!-- 
589 the other way that I often find my self using roles is to really
590 lean on on that 'include' nature. Because the code ends up as though
591 its one big file, then I take large files and 'break-them-up'.
592 It's got it's pros and cons, First why I like this:
593 -->
594
595 ----
596 %%%%EM:pro:%%%%
597 write only 
598 what you need
599 <!-- 
600 In that set up my DB example, we only had to write only the part
601 about setting up the DB handle. 
602 -->
603
604 ----
605 %%%%PRE:
606 package PowellsData::Setup::Database;
607 use Moose::Role;
608 use DBHost;
609
610 sub d;
611 has d  => ( is => 'rw', 
612             isa => 'DBI::db',
613             required => 1,
614             default => sub{ my $d = _dbiconnect('current'); 
615                             $d->{mysql_auto_reconnect} = 1; 
616                             $d->do("SET NAMES utf8");
617                             return $d;
618                           }
619           );
620
621
622
623 1;
624 %%%%
625 <!--
626 Search is web based, but in this code we don't care, we can focus just on one
627 simple part of a very complex problem.
628 -->
629
630 ----
631 %%%%EM:con:%%%%
632 lost context
633 <!--
634 with roles you just get stuff for 'free', it's almost like magic. Santa just showed up and you 
635 got a DB handle. ~Woo~. 
636 -->
637
638 ----
639 %%%%PRE:
640 with q{PowellsData::Setup::Database};
641 %%%%
642 vs.
643 %%%%PRE:
644 use Some::Other::Magic::DB::Manager q{d};
645 %%%%
646 <!-- 
647 when you set up a role next to the use qw{import} way of things, you can maintain context
648 you are stating the origin of things. Theres so many times that ack has saved me time because
649 I have completely lost track where method or attribute is.
650 --> 
651
652 ----
653 %%%%EM:pro:%%%%
654 group logical actions
655 <!--
656 If I take advantage of the Roles as includes then I can take that section of code
657 that does that one thing and pull it off to it's own file, even if it's only used
658 once. 
659 -->
660
661 ----
662 %%%%PRE:
663 package Search::Runtime::FPSearch::Section;
664 use Moose;
665 use Carp::Assert::More;
666
667 with qw{
668    Search::Runtime::Common
669    PowellsData::Setup::Database
670    PowellsData::Tools::String
671 };
672
673    
674 sub find {
675    my ( $self, $needle) = @_;
676    if ( $self->_has_valid_value($needle) ) {
677       my $needles = $self->array_of_words( lc( $needle ) );
678       my $query = _query($needles); 
679       my $r = $self->d->selectall_arrayref($query, { Slice => {} }, @$needles, @$need
680 les);
681       my $score_limit = scalar( @$needles);
682
683       if ( scalar( @$r ) > 0 ) {
684          #we have resutls
685          my $out = [];
686          foreach my $s ( @$r ) {
687             if ( $s->{score} >= $score_limit ) {
688                push @$out, {$s->{name} => $s->{link}};
689             }
690          }
691          return $out;
692       }
693    }
694    return undef;
695 }
696 sub _query {
697    my ($needles) = @_;
698    assert_listref($needles);
699    return sprintf( q{ 
700          (
701             SELECT DISTINCT X.MAJORSec as name, 
702                    CONCAT( 'psection/',X.HTML ) AS link, 
703                    X.score, 
704                    X.type
705             FROM (
706                SELECT C.MAJORSec, 
707                       C.MINORSec, 
708                       count(SWC.word_id) AS score, 
709                       SWC.type, 
710                       C.HTML, 
711                       C.sectionkey
712                  FROM Sections.section_word_category SWC
713                  JOIN Sections.category C
714                    ON (SWC.category_id = C.id)
715                  JOIN Sections.section_words SW
716                    ON (SWC.word_id = SW.id)
717                 WHERE SW.word IN (%s)
718                   AND C.MAJORSec NOT LIKE '%%Reader eBook%%'
719                   AND C.MAJORSec NOT LIKE '%%sale%%'
720                   AND C.MAJORSec != '$7 or Less'
721                   AND C.MAJORSec != 'At the Movies'
722                   AND C.MAJORSec != 'New Arrivals'
723                   AND C.MAJORSec != 'Coming Soon!'
724                   AND SWC.type = 'major'
725                 GROUP BY C.id
726                 ORDER BY score DESC
727             ) AS X
728          )
729          UNION
730          (
731             SELECT CONCAT( Y.MAJORSec, ' - ', Y.MINORSec ) as name, 
732                    CONCAT( 'subsecti on/', Y.sectionkey, '.html' ) AS link, 
733                    Y.score, 
734                    Y.type
735             FROM (
736                SELECT C.MAJORSec, 
737                       C.MINORSec, 
738                       count(SWC.word_id) AS score, 
739                       SWC.type, 
740                       C.HTML, 
741                       C.sectionkey
742                  FROM Sections.section_word_category SWC
743                  JOIN Sections.category C
744                    ON (SWC.category_id = C.id)
745                  JOIN Sections.section_words SW
746                    ON (SWC.word_id = SW.id)
747                 WHERE SW.word IN (%s)
748                   AND C.MAJORSec NOT LIKE '%%Reader eBook%%'
749                   AND C.MAJORSec NOT LIKE '%%sale%%'
750                   AND C.MAJORSec != '$7 or Less'
751                   AND C.MAJORSec != 'At the Movies'
752                   AND C.MAJORSec != 'New Arrivals'
753                   AND C.MAJORSec != 'Coming Soon!'
754                   AND SWC.type = 'minor'
755                 GROUP BY C.id
756                 ORDER BY score DESC
757             ) AS Y
758
759          )
760          ORDER BY type = 'major' DESC , score DESC 
761       }, 
762       _soq(@$needles),
763       _soq(@$needles),
764    );
765 }
766
767
768 1;
769 %%%%
770 <!--
771 I don't know if this is readable but what is going on here is a section search. I take in 
772 a $needle and then build up a query based on that $needle and try and find it in the DB.
773 This code only does one specific thing, So I pulled it out. It could have become it's own object
774 but it really doesn't need to hold on to any thing, it just needs a DB handle and a needle, this
775 a quick handy way to keep things grouped by logical tasks.
776 -->
777
778 ----
779 %%%%EM:con:%%%%
780 things get 
781 messy fast
782 <!--
783 As cool as this is, you really need to be careful, things can get really messy really fast.
784 Here let's just take a quick gander at the dir for Search:
785 -->
786
787 ----
788 %%%%PRE:
789 benh@noodleboy:~/svn/lib_common/dev/Search$ tree .
790 .
791 |-- CompleteResults.pm
792 |-- CompleteResults_distance.pm
793 |-- Core
794 |   |-- Actions
795 |   |   |-- Pages.pm
796 |   |   `-- Results.pm
797 |   |-- Actions.pm
798 |   |-- CompleteResults
799 |   |   |-- Common.pm
800 |   |   |-- LimitedQuery.pm
801 |   |   |-- OLDQuery.pm
802 |   |   |-- OrderBy.pm
803 |   |   |-- Query.pm
804 |   |   `-- Restrictions.pm
805 |   |-- CompleteResults.pm
806 |   `-- Shortcut.pm
807 |-- Core.pm
808 |-- DB
809 |   |-- Current
810 |   |   |-- NoStem.pm
811 |   |   |-- PublisherKw.pm
812 |   |   |-- PublisherKwOld.pm
813 |   |   |-- Search.pm
814 |   |   |-- SearchCacheClasses.pm
815 |   |   |-- SearchCacheCounter.pm
816 |   |   |-- SearchCacheMeta.pm
817 |   |   |-- SearchCacheParams.pm
818 |   |   |-- SearchCacheResults.pm
819 |   |   |-- SearchCacheSections.pm
820 |   |   |-- SearchKw.pm
821 |   |   |-- SearchKwOld.pm
822 |   |   |-- SearchOld.pm
823 |   |   |-- SearchTitle.pm
824 |   |   |-- SearchTitleOld.pm
825 |   |   |-- SectionsKw.pm
826 |   |   |-- SectionsKwOld.pm
827 |   |   |-- Stock.pm
828 |   |   |-- StockOld.pm
829 |   |   |-- ZqDb.pm
830 |   |   |-- ZqKw.pm
831 |   |   `-- ZqTitle.pm
832 |   |-- Current.pm
833 |   `-- Profiler.pm
834 |-- DB.pm
835 |-- Debug
836 |   `-- Stopwatch.pm
837 |-- Debug.pm
838 |-- Runtime
839 |   |-- Attr.pm
840 |   |-- Common.pm
841 |   |-- Cookies.pm
842 |   |-- Defaults.pm
843 |   |-- FPSearch
844 |   |   |-- Author.pm
845 |   |   |-- Help.pm
846 |   |   `-- Section.pm
847 |   |-- FPSearch.pm
848 |   |-- Internal.pm
849 |   |-- Lookup.pm
850 |   |-- Promote.pm
851 |   |-- Spelling.pm
852 |   |-- Tracking.pm
853 |   |-- Translate.pm
854 |   |-- Values
855 |   |   |-- Clean.pm
856 |   |   `-- Organize.pm
857 |   |-- Values.pm
858 |   `-- Websearch.pm
859 |-- Runtime.pm
860 |-- Test
861 |   |-- pound.pl
862 |   `-- runsearch_lite.pl
863 |-- Test.pm
864 `-- playground.pl
865 %%%%}
866 <!--
867 Lets see what is really an object in all of this...
868 - Runtime
869 - Runtime::Default
870 - Core
871
872 Runtime is called from Search Core, it extends Default, but everything else is a role, it is just included.
873 I've tried to keep things organized, but roles are like kudzu. So just like most things, with great power comes great responsibly.
874 This is one of the biggest reasons I've been in some level of constant refactoring of this code base.
875 -->
876
877 ----
878 %%%%EM:pro:%%%%
879 delegate code
880 <!-- 
881 Because there actions in separate files it's really handy to be able to hand out parts of the puzzle 
882 to other people and then just come back with a big working object. It's a dream that I have at work, 
883 so far not much has come of it.
884 --> 
885
886 ----
887 %%%%EM:con:%%%%
888 moose roles
889 are moose only
890 <!--
891 Moose roles are only use-able via moose. You can write mini-object thats collect your roles, 
892 It's not ~hard~ to get around, just need to mention it though. Also to note, Moose is not the only 
893 way to implement 'roles', check cpan for 'traits' or 'mix-ins'. There all kinda the same thing
894 -->
895
896 ----
897 there's a lot more to roles
898 %%%% CookBook Example | http://search.cpan.org/~stevan/Moose-0.38/lib/Moose/Cookbook/Recipe6.pod %%%%
899
900 ----
901 <!--
902 how we doing on time?
903 -->
904
905 ----
906 kinda-FAQ
907 <!--
908 ** possible filler if needed **
909 -->
910
911 ----
912 how to extend a Moose?
913 <!-- 
914 let's see how am I doing on time... cause I can just skip this if I'm over
915 -->
916
917 ----
918 It's easy:
919 %%%%PRE:
920 extends q{object};
921 %%%%
922 vs
923 %%%%PRE:
924 use base q{object};
925 %%%%%
926 <!--
927 Basically does the same thing but makes sure that all the Moose stuff stays in the right place 
928 in the @ISA stack. Also I have not played with it, but extends will take an array of parents
929 and should handle any weird multiple inheritance juju for you.
930 -->
931
932 ----
933 with out new, 
934 how can I 
935 have something
936 run on creation?
937 <!--
938 I still have not completely wrapped my head around what bits of Moose are compile time
939 and what happens at run time. Theres so many layers to things. 
940 There are often times that you will find your self missing that constructor. 
941 just need something to be run at creation time? 
942 -->
943
944 ----
945 %%%%PRE:
946 sub BUILD {
947    #... code to be run on create
948 };
949 %%%%
950
951 ----
952 wow free lunch?
953 cost?
954 <!-- 
955 sadly no, this is not a free lunch. There is a hit on performance, I have not done any 
956 testing, no hard number. Though I have not really noticed any significant hit. Though
957 this is mostly sidelined as Search runs in mod_perl so things only get built once. 
958 There are all sorts of warnings all over the place about not using Moose for a 
959 simple CGI.pm application.
960 -->
961
962
963 ----
964 <!--
965 step #{number}
966 Things that I'm still learning
967 - meta, it all just perl
968 - coerce best uses.
969 - triggers, almost a method modifier
970 - method modification events 
971    - before
972    - around
973    - after
974 - unimport ( 'no moose', when to use? )
975    - mostly just a way to clean up things
976    - %%%% namespace::clean | http://search.cpan.org/~phaylon/namespace-clean-0.08/lib/namespace/clean.pm %%%%
977       - insure that there are no funky issues down stream
978 - super?
979 - override, makes sense just have not run in to a need to use
980 - inner
981 - augment
982 - Moose specific testing
983    - mostly how to test roles by them self?
984    - %%%% Test::Moose | http://search.cpan.org/~stevan/Moose-0.38/lib/Test/Moose.pm %%%%
985    - %%%% Test::Moose::MockObjectCompile  | http://search.cpan.org/~zaphar/Test-Moose-MockObjectCompile-0.2.1/lib/Test/Moose/MockObjectCompile.pm %%%%
986 -->
987
988
989 <!--
990  * a perl object system
991  * meta syntax for object/class declaration
992  * simple example
993  * not *that* weird
994 * Ride the Moose (code in "the real world")
995  * Constructors for free
996  * BUILD
997  * under the hood - the meta() method
998  * getters and setters
999    * example
1000    * possible name space collisions
1001    * 'rw' vs 'ro'
1002    * timing issues ( lazy => 1 )
1003  * strict types
1004    * things die if they are wrong, just like they should (assertion)
1005  * roles
1006  * less code to test
1007 * Love the Moose (techniques and practices)
1008  * composition / modularization / encapsulation
1009  * layout of logical file structure with roles
1010    * easier team workflow / merging
1011  * QA notes
1012 -->
1013
1014 ----
1015 thanks to Stevan Little 
1016 and the whole moose crew #moose
1017 <!--
1018 including eric
1019 -->
1020
1021 ----
1022 Thanks for putting up with my yammering 
1023
1024 ----
1025 More info:
1026 - %%%% CPAN | http://search.cpan.org/~stevan/Moose-0.38/ %%%%
1027 - %%%% Cookbook | http://search.cpan.org/~stevan/Moose-0.38/lib/Moose/Cookbook.pod %%%%
1028 - %%%% Moose Site | http://www.iinteractive.com/moose/ %%%%
1029 - IRC: irc.perl.org #moose
1030  
1031 ----
1032 -end-
1033
1034
1035 </html:textarea>
1036  
1037 <!-- Built-in Style --> 
1038 <html:style id="builtinStyle" type="text/css" style="visibility: collapse">
1039 <![CDATA[
1040 @namespace url(http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul);
1041 @namespace html url(http:/www.w3.org/1999/xhtml);
1042
1043 #canvas {
1044         background: white !important;
1045         color: black !important;
1046         font-family:
1047       "Florencesans Black"
1048       "futura"
1049                 "ゴシックMB101U"
1050                 "ニューセザンヌ-EB"
1051                 "セザンヌ-EB"
1052                 "DHP極太ゴシック体"
1053                 "DFP極太ゴシック体"
1054                 "DHPGothic UB"
1055                 "DFPGothic UB"
1056                 "DHP特太ゴシック体"
1057                 "DFP特太ゴシック体"
1058                 "DHPGothic EB"
1059                 "DFPGothic EB"
1060                 "セイビイサラゴUB-P"
1061                 "AR Pゴシック体 S"
1062                 "Arphic PGothic SuperBold JIS"
1063 /*              "Arial Black"*/
1064                 "DF特太ゴシック体"
1065                 "DFGothic EB"
1066                 "セイビイサラゴUB"
1067                 "AR ゴシック体 S"
1068                 "Arphic Gothic SuperBold JIS"
1069                 "HGP創英角ゴシックUB"
1070                 "HGPSoeiKakugothicUB"
1071                 "HGS創英角ゴシックUB"
1072                 "HG創英角ゴシックUB"
1073                 "HGSoeiKakugothicUB"
1074                 "小塚ゴシック Std H"
1075                 "モトヤゴシック 6"
1076                 "ヒラギノ角ゴ Std W8"
1077                 sans-serif !important;
1078 }
1079
1080 .em-text {
1081         color: red !important;
1082         font-size: 120%;
1083 }
1084
1085 .link-text {
1086         color: blue !important;
1087         text-decoration: underline !important;
1088 }
1089 .link-text:hover {
1090         color: #3366FF !important;
1091 }
1092 .link-text:active {
1093         color: red !important;
1094 }
1095
1096 .preformatted-text.block {
1097         border: 1px dashed gray;
1098         padding: 0.3em;
1099         margin: 0.3em;
1100         font-size: 80%;
1101 }
1102
1103 .monta-label {
1104         background-color: white;
1105         background-repeat: no-repeat;
1106 /*      background-image: url("./monta-label.png");*/
1107 }
1108
1109
1110
1111 #canvas column,
1112 #canvas row {
1113         border: 1px solid gray;
1114 }
1115
1116 #canvas grid .special {
1117         background: #CFCFCF;
1118 }
1119
1120
1121
1122
1123
1124 #header,
1125 #footer {
1126         color: #666;
1127 }
1128
1129
1130 #indicatorBar {
1131         color: #BFBFBF;
1132         background: black;
1133 }
1134
1135 progressmeter {
1136         margin: 0;
1137         padding: 0;
1138 }
1139 progressmeter,
1140 progressmeter .progress-bar,
1141 progressmeter .progress-remainder {
1142         -moz-appearance: none;
1143         background: transparent;
1144         border: none;
1145         outline: none;
1146         min-height: 0;
1147 }
1148 #remainingTimeIndicator .progress-bar {
1149         background: #333333;
1150 }
1151 #remainingPageIndicator .progress-bar {
1152         background: #444444;
1153 }
1154
1155
1156 #plainTextBox {
1157         background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH1wYGCSMfVbRtUAAAAB10RVh0Q29tbWVudABDcmVhdGVkIHdpdGggVGhlIEdJTVDvZCVuAAAAGElEQVR42mNkYGCoZyACMDEQCUYVUkchANsIAJN3G7QXAAAAAElFTkSuQmCC');
1158 }
1159
1160 #plainTextField {
1161         font-family: monospace;
1162         font-size: medium;
1163         color: black;
1164         background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH1wYGCTURrpT1gAAAAB10RVh0Q29tbWVudABDcmVhdGVkIHdpdGggVGhlIEdJTVDvZCVuAAAAGUlEQVR42mP8////fgYiABMDkWBUIXUUAgAEGgPQ5w4kBAAAAABJRU5ErkJggg==');
1165         border: 2px solid gray;
1166         border-color: black white white black;
1167         -moz-appearance: none;
1168 }
1169
1170
1171 /* Eva Mode */
1172
1173 #canvas[eva="true"] {
1174         background: black !important;
1175         color: white !important;
1176         font-family:
1177                 "リュウミンU-KL"
1178                 "リュウミンH-KL"
1179                 "マティス-EB"
1180                 "RFファイン-ME"
1181                 "DHP極太明朝体"
1182                 "DFP極太明朝体"
1183                 "DHPMincho UB"
1184                 "DFPMincho UB"
1185                 "AR P明朝体U"
1186                 "Arphic PMincho Ultra JIS"
1187                 "AR明朝体U"
1188                 "Arphic Mincho Ultra JIS"
1189                 "HGP平成明朝体W9"
1190                 "HGPHeiseiMinchotaiW9"
1191                 "HGS平成明朝体W9"
1192                 "HGSHeiseiMinchotaiW9"
1193                 "HG平成明朝体W9"
1194                 "HGHeiseiMinchotaiW9"
1195                 "ヒラギノ明朝 Pro W6"
1196                 serif !important;
1197 }
1198 #canvas[eva="true"] .em-text {
1199         color: red !important;
1200 /*      color: green !important;*/
1201 }
1202 #canvas[eva="true"] .link-text {
1203         color: red !important;
1204         text-decoration: none !important;
1205 }
1206 #canvas[eva="true"] .link-text:hover {
1207         color: pink !important;
1208 }
1209 #canvas[eva="true"] .link-text:active {
1210         color: orange !important;
1211 }
1212
1213 #canvas[eva="true"] #header,
1214 #canvas[eva="true"] #footer {
1215         color: #AAA;
1216 }
1217
1218
1219 #canvas[eva="true"] .monta-label {
1220         background-color: black;
1221 }
1222
1223
1224
1225
1226
1227 .stroke-dot {
1228         background: red;
1229         width: 3px;
1230         height: 3px;
1231 }
1232
1233
1234
1235
1236
1237
1238
1239
1240 /* System:: DO NOT CHANGE FOLLOWING LINES!! */
1241
1242
1243 #canvas * {
1244         cursor: pointer !important;
1245 }
1246
1247 #canvas image {
1248         width: auto;
1249         height: auto;
1250 }
1251 #canvas[rendering="true"] image {
1252         display: none;
1253 }
1254
1255 #canvas[rendering="true"] *,
1256 #canvas[rendering="true"] .text-link {
1257         color: inherit !important;
1258 }
1259
1260 #canvas[rendering="true"] #contentBox {
1261         visibility: hidden;
1262 }
1263
1264
1265
1266
1267
1268
1269 .preformatted-text {
1270         font-family: -moz-fixed !important;
1271         white-space: pre !important;
1272 }
1273
1274
1275
1276 .raw {
1277         font-family: sans-serif !important;
1278         font-size: medium !important;
1279 }
1280 #canvas .raw * {
1281         cursor: default !important;
1282 }
1283
1284
1285
1286
1287
1288 tabbox, tabpanels, tabpanel {
1289         margin: 0;
1290         padding: 0;
1291 }
1292
1293
1294 .dropmarker-button > image {
1295         display: none !important;
1296 }
1297 .dropmarker-button > label {
1298         width: 0 !important;
1299         overflow: hidden !important;
1300 }
1301
1302 #pages-list-button {
1303         min-width: 0;
1304 }
1305 #pages-list-button > label {
1306         display: none;
1307 }
1308 #pages-list-button menupopup {
1309         max-width: 20em;
1310 }
1311 #pages-list-button menuitem image {
1312         max-width: 32px;
1313         max-height: 32px;
1314 }
1315
1316
1317
1318
1319 #headerBox,
1320 #footerBox {
1321         margin: 1em;
1322 }
1323
1324
1325 #indicatorBar,
1326 #indicatorBar hbox,
1327 #nextPage {
1328         -moz-box-align: center;
1329         -moz-box-pack: center;
1330 }
1331
1332
1333
1334 .monta-label {
1335         padding: 0.05em;
1336         margin: -0.05em;
1337 }
1338 .monta-label[monta-hidden="true"],
1339 .monta-label[monta-hidden="progress"] {
1340         background-position: -100px 0;
1341 }
1342 .monta-label[monta-hidden="progress"] {
1343         background-color: transparent;
1344 }
1345 .monta-label[monta-hidden="false"] {
1346         background: transparent !important;
1347 }
1348
1349
1350
1351 #canvas row description {
1352         border: 1px solid transparent;
1353         white-space: pre;
1354 }
1355
1356
1357
1358 #canvasToolbar {
1359         position: relative;
1360         z-index: 1000;
1361 }
1362
1363 #sourceEditField,
1364 #pageEditFields textbox {
1365         font-family: -moz-fixed !important;
1366         font-size: medium;
1367 }
1368
1369 #pageEditFields {
1370         overflow: auto;
1371 }
1372 #pageEditFields textbox {
1373         height: 6em;
1374 }
1375
1376
1377
1378
1379 #stroke-canvas-box {
1380         position: relative;
1381         display: block;
1382 }
1383 #stroke-canvas-box,
1384 #stroke-canvas-box *|canvas {
1385         line-height: 1;
1386 }
1387
1388 .stroke-dot {
1389         position: absolute;
1390         display: block;
1391         z-index: 100;
1392 }
1393
1394
1395
1396 .monta-label {
1397         background-image: url("data:image/png;base64,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AAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwgRAAAAAAAgTAAEAAAAgTAAEAAAAgDABEAAAAADCBEAAAAAACBMAAQAAACBMAAQAAACAMAEQAAAAAMIEQAAAAAAIEwABAAAAIEwABAAAAIAwARAAAAAAwv60rDzN2YM6WgAAAABJRU5ErkJggg==");
1398 }
1399
1400 ]]>
1401 </html:style>
1402  
1403 <!-- Interface --> 
1404         
1405 <deck flex="1" id="deck"> 
1406          
1407 <vbox flex="1"> 
1408         <toolbox id="canvasToolbar">
1409                 <toolbar>
1410                         <toolbarbutton oncommand="Presentation.home()" label="|&lt;&lt;"
1411                                 observes="canBack"/>
1412                         <toolbarbutton oncommand="Presentation.back()" label="&lt;"
1413                                 observes="canBack"/>
1414                         <toolbarbutton oncommand="Presentation.forward()" label="&gt;"
1415                                 observes="canForward"/>
1416                         <toolbarbutton oncommand="Presentation.end()" label="&gt;&gt;|"
1417                                 observes="canForward"/>
1418                         <toolbarseparator/>
1419                         <hbox align="center">
1420                                 <textbox id="current_page" size="4"
1421                                         oninput="if (this.value) Presentation.showPage(parseInt(this.value)-1);"/>
1422                                 <toolbarbutton id="pages-list-button"
1423                                         type="menu"
1424                                         label="Select Page"
1425                                         tooltiptext="Select Page"
1426                                         class="dropmarker-button">
1427                                         <menupopup id="pages-list"
1428                                                 onpopupshowing="if (event.target == this) Presentation.preventToShowHideToolbar = true;"
1429                                                 onpopuphiding="if (event.target == this) Presentation.preventToShowHideToolbar = false;"
1430                                                 oncommand="Presentation.showPage(parseInt(event.target.value));"/>
1431                                 </toolbarbutton>
1432                                 <description value="/"/>
1433                                 <description id="max_page"/>
1434                         </hbox>
1435                         <toolbarseparator/>
1436                         <vbox flex="2">
1437                                 <spacer flex="1"/>
1438                                 <scrollbar id="scroller"
1439                                         align="center" orient="horizontal"
1440                                         oncommand="Presentation.showPage(parseInt(event.target.getAttribute('curpos')));"
1441                                         onclick="Presentation.showPage(parseInt(event.target.getAttribute('curpos')));"
1442                                         onmousedown="Presentation.onScrollerDragStart();"
1443                                         onmousemove="Presentation.onScrollerDragMove();"
1444                                         onmouseup="Presentation.onScrollerDragDrop();"/>
1445                                 <spacer flex="1"/>
1446                         </vbox>
1447                         <toolbarseparator/>
1448                         <toolbarbutton label="Pen"
1449                                 id="penButton"
1450                                 type="checkbox"
1451                                 autoCheck="false"
1452                                 oncommand="StrokablePresentationService.toggleCheck();"/>
1453                         <spacer flex="1"/>
1454                         <toolbarbutton id="func-menu-button"
1455                                 type="menu"
1456                                 label="Function">
1457                                 <menupopup
1458                                         onpopupshowing="if (event.target == this) { Presentation.preventToShowHideToolbar = true; Presentation.updateTimerItem(); }"
1459                                         onpopuphiding="if (event.target == this) Presentation.preventToShowHideToolbar = false;">
1460                                         <menuitem id="timerItem"
1461                                                 label="Set Timer"
1462                                                 label-normal="Set Timer"
1463                                                 label-active="Reset Timer (rest %smin)"
1464                                                 oncommand="Presentation.setTimer();" />
1465                                         <menuseparator/>
1466                                         <menuitem label="Start Auto-Cruise"
1467                                                 id="autoButton"
1468                                                 type="checkbox"
1469                                                 autoCheck="false"
1470                                                 oncommand="Presentation.toggleAutoCruiseMode();" />
1471                                         <menu id="auto-interval-button"
1472                                                 label="Change Interval">
1473                                                 <menupopup id="auto-interval-list"
1474                                                         onpopupshowing="(this.getElementsByAttribute('value', Presentation.autoCruiseInterval)[0] || this.lastChild).setAttribute('checked', true);"
1475                                                         oncommand="Presentation.changeAutoCruiseInterval(parseInt(event.target.value));">
1476                                                         <menuitem type="radio" radiogroup="autocruise-interval"
1477                                                                 label="1 sec" value="1000"/>
1478                                                         <menuitem type="radio" radiogroup="autocruise-interval"
1479                                                                 label="2 sec" value="2000"/>
1480                                                         <menuitem type="radio" radiogroup="autocruise-interval"
1481                                                                 label="3 sec" value="3000"/>
1482                                                         <menuitem type="radio" radiogroup="autocruise-interval"
1483                                                                 label="4 sec" value="4000"/>
1484                                                         <menuitem type="radio" radiogroup="autocruise-interval"
1485                                                                 label="5 sec" value="5000"/>
1486                                                         <menuseparator/>
1487                                                         <menuitem type="radio" radiogroup="autocruise-interval"
1488                                                                 label="1 min" value="60000"/>
1489                                                         <menuitem type="radio" radiogroup="autocruise-interval"
1490                                                                 label="2 min" value="120000"/>
1491                                                         <menuitem type="radio" radiogroup="autocruise-interval"
1492                                                                 label="3 min" value="180000"/>
1493                                                         <menuitem type="radio" radiogroup="autocruise-interval"
1494                                                                 label="4 min" value="240000"/>
1495                                                         <menuitem type="radio" radiogroup="autocruise-interval"
1496                                                                 label="5 min" value="300000"/>
1497                                                         <menuseparator/>
1498                                                         <menuitem type="radio" radiogroup="autocruise-interval"
1499                                                                 label="Custom"
1500                                                                 oncommand="
1501                                                                         var current = Presentation.autoCruiseInterval;
1502                                                                         var val = parseInt(prompt('input interval (sec)', parseInt(current/1000)));
1503                                                                         if (isNaN(val)) {
1504                                                                                 event.preventBubble();
1505                                                                                 return;
1506                                                                         }
1507                                                                         else
1508                                                                                 val = val * 1000;
1509                                                                         this.value = val;
1510                                                                 "/>
1511                                                 </menupopup>
1512                                         </menu>
1513                                         <menuseparator/>
1514                                         <menuitem oncommand="Presentation.print();" label="Print"/>
1515                                         <menu id="auto-interval-button"
1516                                                 label="Thumbnail Format">
1517                                                 <menupopup
1518                                                         onpopupshowing="(this.getElementsByAttribute('value', Presentation.imageType)[0] || this.firstChild).setAttribute('checked', true);"
1519                                                         oncommand="Presentation.imageType = event.target.value;">
1520                                                         <menuitem type="radio" radiogroup="print-image-type"
1521                                                                 label="PNG" value="png"/>
1522                                                         <menuitem type="radio" radiogroup="print-image-type"
1523                                                                 label="JPEG (50%)" value="jpeg"/>
1524                                                 </menupopup>
1525                                         </menu>
1526                                         <menuseparator/>
1527                                         <menuitem id="showPlainText" label="View Source"
1528                                                 key="key_plainText"
1529                                                 oncommand="Presentation.showPlainText();"/>
1530                                         <menuseparator/>
1531                                         <menuitem id="toggleEva" label="Eva Mode"
1532                                                 key="key_toggleEvaMode"
1533                                                 type="checkbox"
1534                                                 autoCheck="false"
1535                                                 oncommand="Presentation.toggleEvaMode();"/>
1536                                 </menupopup>
1537                         </toolbarbutton>
1538                         <toolbarseparator/>
1539                         <toolbarbutton label="Edit"
1540                                 oncommand="Presentation.toggleEditMode();"/>
1541                         <toolbarbutton oncommand="Presentation.reload();" label="Reload"/>
1542                 </toolbar>
1543         </toolbox>
1544         <vbox flex="1" id="canvas">
1545                 <stack flex="1">
1546                         <vbox flex="1">
1547                                 <hbox id="headerBox" flex="1">
1548                                         <label id="header"/>
1549                                         <spacer flex="1"/>
1550                                         <vbox>
1551                                                 <image id="logo"/>
1552                                                 <spacer flex="1"/>
1553                                         </vbox>
1554                                 </hbox>
1555                                 <spacer flex="19"/>
1556                                 <hbox id="footerBox" flex="1">
1557                                         <spacer flex="1"/>
1558                                         <label id="footer"/>
1559                                 </hbox>
1560                         </vbox>
1561                         <vbox id="canvasMain"
1562                                 flex="1"
1563                                 onmouseup="Presentation.handleEvent(event);"
1564                                 onmousedown="Presentation.handleEvent(event);"
1565                                 onmousemove="Presentation.handleEvent(event);"
1566                                 onclick="Presentation.onPresentationClick(event);">
1567                                 <spacer flex="1"/>
1568                                 <hbox flex="1" id="contentBox">
1569                                         <spacer flex="1"/>
1570                                         <vbox id="content"/>
1571                                         <spacer flex="1"/>
1572                                 </hbox>
1573                                 <spacer flex="1"/>
1574                         </vbox>
1575                         <vbox id="plainTextBox"
1576                                 hidden="true"
1577                                 flex="1"
1578                                 onclick="Presentation.hidePlainText();"
1579                                 align="center"
1580                                 pack="center">
1581                                 <textbox id="plainTextField" multiline="true"
1582                                         onkeypress="if (event.keyCode == event.DOM_VK_ESCAPE) Presentation.hidePlainText();"
1583                                         onclick="event.stopPropagation();"/>
1584                         </vbox>
1585                 </stack>
1586         </vbox>
1587         <hbox id="indicatorBar"
1588                 onclick="Presentation.onIndicatorBarClick(event);">
1589                 <stack flex="1">
1590                         <vbox>
1591                                 <progressmeter id="remainingPageIndicator"
1592                                         type="determined" value="0"
1593                                         flex="1"/>
1594                                 <progressmeter id="remainingTimeIndicator"
1595                                         type="determined" value="0"
1596                                         flex="1"
1597                                         collapsed="true"/>
1598                         </vbox>
1599                         <hbox flex="1">
1600                                 <label value="Next:"/>
1601                                 <description id="nextPage" flex="1" crop="end"/>
1602                         </hbox>
1603                 </stack>
1604         </hbox>
1605 </vbox>
1606  
1607 <vbox flex="1" id="edit"> 
1608         <toolbox>
1609                 <toolbar>
1610                         <menubar flex="1">
1611                                 <menu label="File">
1612                                         <menupopup>
1613                                                 <menuitem label="Save"
1614                                                         key="key_save"
1615                                                         oncommand="Presentation.output()"/>
1616                                                 <menuitem label="Reload"
1617                                                         key="key_reload"
1618                                                         oncommand="Presentation.reload()"/>
1619                                         </menupopup>
1620                                 </menu>
1621                                 <menu label="Insert">
1622                                         <menupopup>
1623                                                 <menuitem label="New Page"
1624                                                         key="key_insert_newpage"
1625                                                         oncommand="Presentation.insert('page')"/>
1626                                                 <menuseparator/>
1627                                                 <menuitem label="Header"
1628                                                         oncommand="Presentation.insert('header')"/>
1629                                                 <menuitem label="Footer"
1630                                                         oncommand="Presentation.insert('footer')"/>
1631                                                 <menuseparator/>
1632                                                 <menuitem label="Link"
1633                                                         oncommand="Presentation.insert('link')"/>
1634                                                 <menuitem label="Emphasis"
1635                                                         oncommand="Presentation.insert('em')"/>
1636                                                 <menuitem label="Preformatted"
1637                                                         oncommand="Presentation.insert('pre')"/>
1638                                                 <menuitem label="Monta"
1639                                                         oncommand="Presentation.insert('monta')"/>
1640                                                 <menuitem label="Image"
1641                                                         oncommand="Presentation.insert('img')"/>
1642                                         </menupopup>
1643                                 </menu>
1644                         </menubar>
1645                         <toolbarseparator/>
1646                         <toolbarbutton label="View"
1647                                 oncommand="Presentation.toggleEditMode();"/>
1648                         <toolbarbutton oncommand="Presentation.reload();" label="Reload"/>
1649                 </toolbar>
1650         </toolbox>
1651         <tabbox id="editTabBox" flex="1">
1652                 <tabs onselect="Presentation.toggleEditStyle(event);">
1653                         <tab label="Page Edit" id="editTab-pages"/>
1654                         <tab label="Source" id="editTab-source"/>
1655                 </tabs>
1656                 <tabpanels flex="1">
1657                         <scrollbox id="pageEditFields"
1658                                 orient="vertical"
1659                                 flex="1"/>
1660                         <textbox id="sourceEditField"
1661                                 flex="1"
1662                                 multiline="true"
1663                                 oninput="Presentation.onEditSource()"/>
1664                 </tabpanels>
1665         </tabbox>
1666 </vbox>
1667  
1668 </deck> 
1669   
1670 <data style="visibility: collapse;"> 
1671         <hbox id="pageEditBoxTemplate">
1672                 <textbox multiline="true"
1673                         class="page-edit-box-main"
1674                         flex="1"
1675                         oninput="Presentation.onEditPage(Number(this.parentNode.parentNode.id.match(/\d+/)))"/>
1676                 <vbox>
1677                         <toolbarbutton label="×"
1678                                 oncommand="Presentation.removePage(Number(this.parentNode.parentNode.id.match(/\d+/)));"/>
1679                 </vbox>
1680         </hbox>
1681 </data>
1682  
1683 <broadcasterset> 
1684         <broadcaster id="canBack"/>
1685         <broadcaster id="canForward"/>
1686 </broadcasterset>
1687  
1688 <commandset> 
1689         <command id="cmd_forward"
1690                 oncommand="if (Presentation.isPresentationMode) Presentation.forward();"/>
1691         <command id="cmd_forwardStep"
1692                 oncommand="if (Presentation.isPresentationMode) Presentation.forwardStep();"/>
1693         <command id="cmd_back"
1694                 oncommand="if (Presentation.isPresentationMode) Presentation.back();"/>
1695         <command id="cmd_home"
1696                 oncommand="if (Presentation.isPresentationMode) Presentation.home();"/>
1697         <command id="cmd_end"
1698                 oncommand="if (Presentation.isPresentationMode) Presentation.end();"/>
1699 </commandset>
1700  
1701 <keyset> 
1702         <key keycode="VK_ENTER"      command="cmd_forwardStep"/>
1703         <key keycode="VK_RETURN"     command="cmd_forwardStep"/>
1704         <key keycode="VK_PAGE_DOWN"  command="cmd_forwardStep"/>
1705         <key keycode="VK_RIGHT"      command="cmd_forwardStep"/>
1706         <key keycode="VK_DOWN"       command="cmd_forwardStep"/>
1707         <!--key keycode="VK_BACK_SPACE" command="cmd_back"/-->
1708         <key keycode="VK_PAGE_UP"    command="cmd_back"/>
1709         <key keycode="VK_UP"         command="cmd_back"/>
1710         <key keycode="VK_LEFT"       command="cmd_back"/>
1711         <key keycode="VK_HOME"       command="cmd_home"/>
1712         <key keycode="VK_END"        command="cmd_end"/>
1713
1714         <key id="key_insert_newpage"
1715                 key="n" modifiers="accel" oncommand="Presentation.insert('page');"/>
1716
1717         <key id="key_save"
1718                 key="s" modifiers="accel" oncommand="Presentation.output();"/>
1719         <key id="key_reload"
1720                 key="r" modifiers="accel" oncommand="Presentation.reload();"/>
1721         <key id="key_reload"
1722                 key="p" modifiers="accel" oncommand="Presentation.print();"/>
1723
1724         <key id="key_toggleEditMode"
1725                 key="e" modifiers="accel" oncommand="Presentation.toggleEditMode();"/>
1726         <key id="key_toggleEvaMode"
1727                 key="e" modifiers="accel,shift" oncommand="Presentation.toggleEvaMode();"/>
1728
1729         <key id="key_plainText"
1730                 key="u" modifiers="accel" oncommand="Presentation.showPlainText();"/>
1731         <key keycode="VK_ESCAPE"      oncommand="Presentation.hidePlainText();"/>
1732 </keyset>
1733   
1734 <!-- Implementation --> 
1735 <script type="application/x-javascript; e4x=1"><![CDATA[
1736          
1737 const XULNS   = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'; 
1738 const XHTMLNS = 'http://www.w3.org/1999/xhtml';
1739  
1740 var Presentation = { 
1741          
1742         baseSize        : 9, 
1743         montaLabelImage : 'monta-label.png',
1744
1745         //phraseOpenParen  : '[',
1746         //phraseCloseParen : ']',
1747         phraseOpenParen  : '%%',
1748         phraseCloseParen : '%%',
1749         makePhraseRegExp : function(aPattern, aFlags)
1750         {
1751                 return new RegExp(
1752                                 aPattern.replace(/%o(pen)?/gi, '\\'+this.phraseOpenParen)
1753                                         .replace(/%c(lose)?/gi, '\\'+this.phraseCloseParen),
1754                                 aFlags
1755                         );
1756         },
1757
1758         dragStartDelta : 8,
1759
1760         imageType : 'jpeg',
1761
1762         EDIT_BOX_ID_PREFIX : 'editPageBox-',
1763  
1764         initialized : false, 
1765
1766         preventToShowHideToolbar : false,
1767
1768         showMontaKeywordTimeout : 100,
1769         autoCruiseInterval      : 2000,
1770         timerUpdatingInterval   : 30000,
1771
1772         cachedContents : [],
1773         sourceData     : [],
1774  
1775         init : function(option){ 
1776                 if (this.initialized) {
1777                         this.startPresentation();
1778                         return;
1779                 }
1780                 this.initialized = true;
1781
1782
1783                 this._offset  = 0;
1784                 this.canvas     = document.getElementById('canvas');
1785                 this.canvasMain = document.getElementById('canvasMain');
1786                 this.content    = document.getElementById('content');
1787                 this.header     = document.getElementById('header');
1788                 this.footer     = document.getElementById('footer');
1789                 this.logo       = document.getElementById('logo');
1790                 this.next       = document.getElementById('nextPage');
1791
1792                 this.indicatorBar = document.getElementById('indicatorBar');
1793                 this.remainder    = document.getElementById('remainingPageIndicator');
1794                 this.timer        = document.getElementById('remainingTimeIndicator');
1795
1796                 this.list     = document.getElementById('pages-list');
1797                 this.source   = document.getElementById('sourceEditField');
1798                 this.pages    = document.getElementById('pageEditFields');
1799                 this.deck     = document.getElementById('deck');
1800                 this.scroller = document.getElementById('scroller');
1801
1802                 this.toolbar         = document.getElementById('canvasToolbar');
1803                 this.toolbarHeight   = this.toolbar.boxObject.height;
1804                 this.isToolbarHidden = true;
1805                 this.toolbar.setAttribute('style', 'margin-top:'+(0-this.toolbarHeight)+'px;margin-bottom:0px;');
1806
1807                 this.preloadImage(this.montaLabelImage);
1808
1809                 window.addEventListener('resize', this, false);
1810                 window.addEventListener('contextmenu', this, false);
1811                 window.addEventListener('CanvasContentAdded', this, false);
1812
1813                 this.canvasMain.addEventListener('DOMMouseScroll', this, false);
1814                 this.indicatorBar.addEventListener('DOMMouseScroll', this, false);
1815
1816                 if(option){
1817                         for(var i in option){this[i] = option[i]}
1818                 }
1819
1820                 this.cachedContents = [];
1821
1822                 if (this.readParameter()) {
1823                         this.startPresentation();
1824                 }
1825
1826                 document.documentElement.focus();
1827         },
1828  
1829 /* core */ 
1830         
1831         startPresentation : function() 
1832         {
1833                 if (this.data.length)
1834                         document.title = this.data[0].title || this.data[0].header || this.data[0].text.join(' ');
1835
1836                 this.takahashi();
1837         },
1838  
1839 /* rendering */ 
1840         
1841         takahashi : function() { 
1842                 this.isRendering = true;
1843
1844                 if (!this.data[this.offset]) {
1845                         this.offset = this.data.length-1;
1846                 }
1847                 document.getElementById("current_page").value = this.offset+1;
1848                 document.getElementById("max_page").value     = this.data.length;
1849
1850                 this.scroller.setAttribute('maxpos', this.data.length-1);
1851                 this.scroller.setAttribute('curpos', this.offset);
1852
1853                 var broadcaster = document.getElementById('canBack');
1854                 if (!this.offset)
1855                         broadcaster.setAttribute('disabled', true);
1856                 else
1857                         broadcaster.removeAttribute('disabled');
1858
1859                 var broadcaster = document.getElementById('canForward');
1860                 if (this.offset == this.data.length-1)
1861                         broadcaster.setAttribute('disabled', true);
1862                 else
1863                         broadcaster.removeAttribute('disabled');
1864
1865                 this.canvas.setAttribute('rendering', true);
1866
1867
1868                 this.header.removeAttribute('style');
1869                 this.footer.removeAttribute('style');
1870                 this.content.removeAttribute('style');
1871
1872                 this.clickableNodes = [];
1873
1874
1875                 if ('title' in this.data[this.offset])
1876                         document.title = this.data[this.offset].title;
1877
1878                 this.header.setAttribute('style', 'font-size:10px;');
1879                 this.header.value = this.data[this.offset].header;
1880                 this.footer.setAttribute('style', 'font-size:10px;');
1881                 this.footer.value = this.data[this.offset].footer;
1882
1883                 var page = this.content.getAttribute('page');
1884                 var range = document.createRange();
1885                 range.selectNodeContents(this.content);
1886                 this.cachedContents[page] = !this.content.hasChildNodes() ? null : {
1887                         fragment     : range.extractContents(),
1888                         offsetWidth  : parseInt(this.content.getAttribute('offsetWidth')),
1889                         offsetHeight : parseInt(this.content.getAttribute('offsetHeight'))
1890                 };
1891                 range.detach();
1892
1893
1894                 if (this.data[this.offset].load) {
1895                         this.isRendering = false;
1896                         location.replace(location.href.split('?')[0] + '?'+this.data[this.offset].load);
1897                         return;
1898                 }
1899
1900                 var content = (this.offset in this.cachedContents && this.cachedContents[this.offset]) ?
1901                                                         this.cachedContents[this.offset] :
1902                                                         this.createContent();
1903
1904                 this.content.setAttribute('style', 'font-size:10px;');
1905                 this.content.setAttribute('page',         this.offset);
1906                 this.content.setAttribute('offsetWidth',  content.offsetWidth);
1907                 this.content.setAttribute('offsetHeight', content.offsetHeight);
1908
1909                 this.content.appendChild(content.fragment);
1910
1911                 this.clickableNodes.push(this.content);
1912
1913
1914                 this.fitHeaderFooterToCanvas();
1915                 this.fitMainContentToCanvas();
1916
1917
1918                 try {
1919                         var checkedItems = this.list.getElementsByAttribute('checked', 'true');
1920                         max = checkedItems.length;
1921                         for (i = max-1; i > -1 ; i--)
1922                                 checkedItems[i].removeAttribute('checked');
1923                 }
1924                 catch(e) {
1925                 }
1926
1927                 this.list.getElementsByAttribute('value', this.offset)[0].setAttribute('checked', true);
1928
1929                 this.canvas.removeAttribute('rendering');
1930                 this.isRendering = false;
1931                 this.setHash('page', 'page'+(this.offset+1));
1932
1933                 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)' ;
1934                 this.remainder.setAttribute('value', this.offset == 0 ? 0 : parseInt(((this.offset)/(this.data.length-1))*100));
1935
1936                 var event = document.createEvent('Events');
1937                 event.initEvent('PresentationRedraw', false, true);
1938                 this.canvas.dispatchEvent(event);
1939         },
1940         
1941         createContent : function() 
1942         {
1943                 var retVal = {
1944                                 offsetWidth  : 0,
1945                                 offsetHeight : 0
1946                         };
1947                 var text = this.data[this.offset].text;
1948                 var line;
1949                 var newLine;
1950                 var uri;
1951                 var image_width;
1952                 var image_height;
1953                 var image_src;
1954
1955                 var labelId = 0;
1956                 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');
1957
1958                 var emRegExp         = this.makePhraseRegExp('^([^%O]+)?%O%Oem({([^}]*)})?:(.+?)()?%C%C', 'i');
1959                 var emStartRegExp    = this.makePhraseRegExp('^([^%O]+)?%O%Oem({([^}]*)})?:(.*)', 'i');
1960                 var emEndRegExp      = this.makePhraseRegExp('^(.*?)((:em)?%C%C)', 'i');
1961
1962                 var preRegExp        = this.makePhraseRegExp('^([^%O]+)?%O%Opre({([^}]*)})?:(.+?)(:pre)?%C%C', 'i');
1963                 var preStartRegExp   = this.makePhraseRegExp('^([^%O]+)?%O%Opre({([^}]*)})?:(.*)', 'i');
1964                 var preEndRegExp     = this.makePhraseRegExp('^(.*?)((:pre)?%C%C)', 'i');
1965
1966                 var rawRegExp        = this.makePhraseRegExp('^([^%O]+)?%O%O(raw|encoded)({([^}]*)})?:(.+?)(:raw|:encoded)?%C%C', 'i');
1967                 var rawStartRegExp   = this.makePhraseRegExp('^([^%O]+)?%O%O(raw|encoded)({([^}]*)})?:(.*)', 'i');
1968                 var rawEndRegExp     = this.makePhraseRegExp('^(.*?)((:raw|:encoded)?%C%C)', 'i');
1969
1970                 var styleRegExp      = this.makePhraseRegExp('^([^%O]+)?%O%O(#([^:{]*))?({([^}]*)})?:(.+?)%C%C', '');
1971                 var styleStartRegExp = this.makePhraseRegExp('^([^%O]+)?%O%O(#([^:{]*))?({([^}]*)})?:(.*)', '');
1972                 var styleEndRegExp   = this.makePhraseRegExp('^(.*?)(%C%C)', '');
1973
1974                 var imagesRegExp     = this.makePhraseRegExp('^([^%O]+)?%O%Oima?ge? +src="([^"]+)" +width="([0-9]+)" +height="([0-9]+)"[^%C]*%C%C', 'i');
1975
1976                 var linksRegExp      = this.makePhraseRegExp('^([^%O]+)?%O%O(([^|]+)?\\||)([^%C]+)%C%C', '');
1977
1978                 var montaRegExp      = this.makePhraseRegExp('^([^%O]+)?%O([^%C]+)%C', '');
1979
1980                 var inBlock       = false,
1981                         blockClass    = '',
1982                         blockStyle    = '',
1983                         blockContents = [];
1984
1985                 var inGrid       = false,
1986                         gridContents = null;
1987
1988                 var fragment = document.createDocumentFragment();
1989
1990                 var lineBox;
1991                 for (var i = 0, max = text.length; i < max; i++)
1992                 {
1993                         lineBox = document.createElement('hbox');
1994                         lineBox.setAttribute('align', 'center');
1995                         lineBox.setAttribute('pack', this.data[this.offset].align);
1996
1997                         line = text[i];
1998                         image_width  = 0;
1999                         image_height = 0;
2000                         if (!line) line = ' ';
2001
2002                         if (inBlock) {
2003                                 if (blockClass == 'raw' &&
2004                                         rawEndRegExp.test(line)) {
2005                                         inBlock = false;
2006                                         blockContents.push(RegExp.$1);
2007                                         line = line.substring((RegExp.$1+RegExp.$2).length);
2008
2009                                         eval('var xml = <hbox class="raw" style="blockStyle" onclick="event.stopPropagation();" onkeypress="event.stopPropagation();">'+blockContents.join('\n')+'</hbox>;');
2010                                         importNodeTreeWithDelay(importE4XNode(xml, document, XULNS), lineBox, XULNS);
2011
2012                                         blockClass    = '';
2013                                         blockStyle    = '';
2014                                         blockContents = [];
2015                                 }
2016                                 else if (blockClass == 'preformatted-text' &&
2017                                         preEndRegExp.test(line)) {
2018                                         inBlock = false;
2019                                         blockContents.push(RegExp.$1);
2020                                         line = line.substring((RegExp.$1+RegExp.$2).length);
2021
2022                                         lineBox.appendChild(document.createElement('description'));
2023                                         lineBox.lastChild.setAttribute('class', 'preformatted-text block');
2024                                         if (blockStyle)
2025                                                 lineBox.lastChild.setAttribute('style', blockStyle);
2026                                         lineBox.lastChild.appendChild(document.createTextNode(
2027                                                 blockContents.join('\n')
2028                                                         .replace(/^[\r\n\s]+/, '')
2029                                                         .replace(/[\r\n\s]+$/, '')
2030                                                         .replace(/&amp;/g, '&')
2031                                                         .replace(/&quot;/g, '"')
2032                                                         .replace(/&gt;/g, '>')
2033                                                         .replace(/&lt;/g, '<')
2034                                         ));
2035
2036                                         blockClass    = '';
2037                                         blockStyle    = '';
2038                                         blockContents = [];
2039                                 }
2040                                 else if (emEndRegExp.test(line) || styleEndRegExp.test(line)) {
2041                                         inBlock = false;
2042                                         blockContents.push(RegExp.$1);
2043                                         line = line.substring((RegExp.$1+RegExp.$2).length);
2044
2045                                         lineBox.appendChild(document.createElement('vbox'));
2046                                         lineBox.lastChild.setAttribute('class', blockClass+' block');
2047                                         if (blockStyle)
2048                                                 lineBox.lastChild.setAttribute('style', blockStyle);
2049                                         lineBox.lastChild.setAttribute('align', this.data[this.offset].align);
2050                                         blockContents = blockContents.join('\n')
2051                                                 .replace(/^[\r\n\s]+/, '')
2052                                                 .replace(/[\r\n\s]+$/, '')
2053                                                 .split('\n');
2054                                         for (var j = 0, jmax = blockContents.length; j < jmax; j++)
2055                                         {
2056                                                 lineBox.lastChild.appendChild(document.createElement('description'));
2057                                                 lineBox.lastChild.lastChild.setAttribute('value', blockContents[j]);
2058                                         }
2059
2060                                         blockClass    = '';
2061                                         blockStyle    = '';
2062                                         blockContents = [];
2063                                 }
2064                                 else {
2065                                         blockContents.push(line);
2066                                         continue;
2067                                 }
2068                         }
2069
2070                         if (line.indexOf('|') == 0) {
2071                                 fragment.appendChild(lineBox);
2072
2073                                 if (fragment.childNodes.length == 1 ||
2074                                         !fragment.childNodes[fragment.childNodes.length-2] ||
2075                                         !fragment.childNodes[fragment.childNodes.length-2].lastChild ||
2076                                         fragment.childNodes[fragment.childNodes.length-2].lastChild.localName != 'grid') {
2077                                         fragment.lastChild.appendChild(document.createElement('grid'));
2078                                         fragment.lastChild.lastChild.appendChild(document.createElement('columns'));
2079                                         fragment.lastChild.lastChild.appendChild(document.createElement('rows'));
2080                                 }
2081                                 else {
2082                                         fragment.removeChild(fragment.lastChild);
2083                                 }
2084                                 fragment.lastChild.lastChild.lastChild.appendChild(document.createElement('row'));
2085                                 fragment.lastChild.lastChild.lastChild.lastChild.setAttribute('flex', 1);
2086
2087                                 line = line.split('|');
2088                                 for (var j = 1, jmax = line.length; j < jmax; j++)
2089                                 {
2090                                         fragment.lastChild.lastChild.lastChild.lastChild.appendChild(document.createElement('vbox'));
2091                                         fragment.lastChild.lastChild.lastChild.lastChild.lastChild.setAttribute('align', this.data[this.offset].align);
2092                                         fragment.lastChild.lastChild.lastChild.lastChild.lastChild.setAttribute('pack', 'center');
2093                                         if (line[j].charAt(0) == '~') {
2094                                                 fragment.lastChild.lastChild.lastChild.lastChild.lastChild.setAttribute('class', 'special');
2095                                                 line[j] = line[j].substring(1);
2096                                         }
2097                                         line[j] = line[j].split(/<br\s*\/>/g);
2098                                         for (var k = 0, kmax = line[j].length; k < kmax; k++)
2099                                         {
2100                                                 fragment.lastChild.lastChild.lastChild.lastChild.lastChild.appendChild(document.createElement('description'));
2101                                                 fragment.lastChild.lastChild.lastChild.lastChild.lastChild.lastChild.appendChild(document.createTextNode(line[j][k].replace(/^\s+|\s+$/g, '')));
2102                                         }
2103                                         if (fragment.lastChild.lastChild.firstChild.childNodes.length < j) {
2104                                                 fragment.lastChild.lastChild.firstChild.appendChild(document.createElement('column'));
2105                                                 fragment.lastChild.lastChild.firstChild.lastChild.setAttribute('flex', 1);
2106                                         }
2107                                 }
2108                                 continue;
2109                         }
2110
2111
2112                         while (line.match(lineRegExp))
2113                         {
2114                                 if (RegExp.$1) {
2115                                         lineBox.appendChild(document.createElement('description'));
2116                                         lineBox.lastChild.setAttribute('value', RegExp.$1);
2117                                 }
2118                                 newLine = line.substring((RegExp.$1+RegExp.$2).length);
2119
2120                                 // Raw Codes: Parsed as XML
2121                                 if (rawRegExp.test(line)) {
2122                                         eval('var xml = <hbox class="raw" style="'+RegExp.$4+'" onclick="event.stopPropagation();" onkeypress="event.stopPropagation();">'+RegExp.$5+'</hbox>;');
2123                                         importNodeTreeWithDelay(importE4XNode(xml, document, XULNS), lineBox, XULNS);
2124                                 }
2125                                 else if (rawStartRegExp.test(line)) {
2126                                         inBlock       = true;
2127                                         blockClass    = 'raw';
2128                                         blockStyle    = RegExp.$4;
2129                                         blockContents = [RegExp.$5];
2130                                         newLine       = '';
2131                                 }
2132
2133                                 // Preformatted Text
2134                                 if (preRegExp.test(line)) {
2135                                         lineBox.appendChild(document.createElement('description'));
2136                                         if (RegExp.$3)
2137                                                 lineBox.lastChild.setAttribute('style', RegExp.$3);
2138                                         lineBox.lastChild.setAttribute('value', RegExp.$4);
2139                                         lineBox.lastChild.setAttribute('class', 'preformatted-text');
2140                                 }
2141                                 else if (preStartRegExp.test(line)) {
2142                                         inBlock       = true;
2143                                         blockClass    = 'preformatted-text';
2144                                         blockStyle    = RegExp.$3;
2145                                         blockContents = [RegExp.$4];
2146                                         newLine       = '';
2147                                 }
2148
2149                                 // Emphasis
2150                                 else if (emRegExp.test(line)) {
2151                                         lineBox.appendChild(document.createElement('description'));
2152                                         if (RegExp.$3)
2153                                                 lineBox.lastChild.setAttribute('style', RegExp.$3);
2154                                         lineBox.lastChild.setAttribute('value', RegExp.$4);
2155                                         lineBox.lastChild.setAttribute('class', 'em-text');
2156                                 }
2157                                 else if (emStartRegExp.test(line)) {
2158                                         inBlock       = true;
2159                                         blockClass    = 'em-text';
2160                                         blockStyle    = RegExp.$3;
2161                                         blockContents = [RegExp.$4];
2162                                         newLine       = '';
2163                                 }
2164
2165                                 // User-defined Styles
2166                                 else if (styleRegExp.test(line)) {
2167                                         lineBox.appendChild(document.createElement('description'));
2168                                         lineBox.lastChild.setAttribute('class', RegExp.$3);
2169                                         if (RegExp.$5)
2170                                                 lineBox.lastChild.setAttribute('style', RegExp.$5);
2171                                         lineBox.lastChild.setAttribute('value', RegExp.$6);
2172                                 }
2173                                 else if (styleStartRegExp.test(line)) {
2174                                         inBlock       = true;
2175                                         blockClass    = RegExp.$3;
2176                                         blockStyle    = RegExp.$5;
2177                                         blockContents = [RegExp.$6];
2178                                         newLine       = '';
2179                                 }
2180
2181                                 // Images
2182                                 else if (imagesRegExp.test(line)) {
2183                                         lineBox.appendChild(document.createElement('image'));
2184                                         image_src = RegExp.$2;
2185                                         if (image_src.indexOf('http://') < 0 &&
2186                                                 image_src.indexOf('https://') < 0 &&
2187                                                 image_src.indexOf('data:') < 0)
2188                                                 image_src = this.dataFolder+image_src;
2189                                         lineBox.lastChild.setAttribute('src', image_src);
2190                                         lineBox.lastChild.setAttribute('width', parseInt(RegExp.$3 || '0'));
2191                                         lineBox.lastChild.setAttribute('height', parseInt(RegExp.$4 || '0'));
2192                                         image_width  += parseInt(RegExp.$3 || '0');
2193                                         image_height = Math.max(image_height, parseInt(RegExp.$4 || '0'));
2194                                 }
2195
2196                                 // Links
2197                                 else if (linksRegExp.test(line)) {
2198                                         uri = RegExp.$4;
2199                                         if (uri.indexOf('://') < 0)
2200                                                 uri = this.dataFolder+uri;
2201                                         lineBox.appendChild(document.createElement('description'));
2202                                         lineBox.lastChild.setAttribute('value', RegExp.$3 || RegExp.$4);
2203                                         lineBox.lastChild.setAttribute('href', uri);
2204                                         lineBox.lastChild.setAttribute('tooltiptext', uri);
2205                                         lineBox.lastChild.setAttribute('statustext', uri);
2206                                         lineBox.lastChild.setAttribute('class', 'link-text');
2207
2208                                         this.clickableNodes.push(lineBox.lastChild);
2209                                 }
2210
2211                                 // Monta
2212                                 else if (montaRegExp.test(line)) {
2213                                         lineBox.appendChild(document.createElement('stack'));
2214
2215                                         lineBox.lastChild.appendChild(document.createElement('description'));
2216                                         lineBox.lastChild.lastChild.setAttribute('value', RegExp.$2);
2217                                         lineBox.lastChild.lastChild.setAttribute('class', 'monta-text');
2218
2219                                         lineBox.lastChild.appendChild(document.createElement('spacer'));
2220                                         lineBox.lastChild.lastChild.setAttribute('flex', 1);
2221                                         lineBox.lastChild.lastChild.setAttribute('class', 'monta-label');
2222
2223                                         lineBox.lastChild.lastChild.setAttribute('label-id', 'label-' + (++labelId));
2224
2225                                         lineBox.lastChild.lastChild.setAttribute('monta-hidden', 'true');
2226
2227                                         this.clickableNodes.push(lineBox.lastChild.lastChild);
2228                                 }
2229
2230                                 line = newLine;
2231                         }
2232
2233                         if (line) {
2234                                 lineBox.appendChild(document.createElement('description'));
2235                                 lineBox.lastChild.setAttribute('value', line);
2236                         }
2237
2238                         retVal.offsetWidth = Math.max(retVal.offsetWidth, image_width);
2239                         retVal.offsetHeight += image_height;
2240
2241                         if (lineBox.hasChildNodes())
2242                                 fragment.appendChild(lineBox);
2243                 }
2244
2245                 retVal.fragment = fragment;
2246                 return retVal;
2247         },
2248   
2249         fitToCanvas : function(aContent, aCanvas, aOffsetWidth, aOffsetHeight) 
2250         {
2251                 aContent.removeAttribute('style');
2252                 aContent.setAttribute('style', 'font-size:10px;');
2253
2254                 var grids      = aContent.getElementsByTagName('grid');
2255                 var gridsCount = grids.length;
2256
2257                 if (!aContent.boxObject.width) return;
2258
2259                 var canvas_w  = aCanvas.boxObject.width;
2260                 var canvas_h  = aCanvas.boxObject.height-aOffsetHeight;
2261
2262                 var content_w = aContent.boxObject.width;
2263                 var new_fs = Math.round((canvas_w/content_w) * this.data[this.offset].size);
2264                 aContent.setAttribute('style', 'font-size:'+ new_fs + "px");
2265
2266                 for (var i = 0; i < gridsCount; i++)
2267                 {
2268                         grids[i].firstChild.lastChild.removeAttribute('flex', 1);
2269                         grids[i].firstChild.lastChild.setAttribute('flex', 1);
2270                 }
2271
2272                 if (aContent.boxObject.width < aOffsetWidth) {
2273                         content_w = aOffsetWidth;
2274                         new_fs = Math.round((canvas_w/content_w) * this.data[this.offset].size);
2275                         aContent.setAttribute('style', 'font-size:'+ new_fs + "px");
2276
2277                         for (var i = 0; i < gridsCount; i++)
2278                         {
2279                                 grids[i].firstChild.lastChild.removeAttribute('flex', 1);
2280                                 grids[i].firstChild.lastChild.setAttribute('flex', 1);
2281                         }
2282                 }
2283
2284                 var content_h = aContent.boxObject.height;
2285                 if(content_h >= canvas_h){
2286                         state='height';
2287                         content_h = aContent.boxObject.height;
2288                         new_fs = Math.round((canvas_h/content_h) * new_fs);
2289                         aContent.setAttribute('style', 'font-size:'+ new_fs + "px");
2290
2291                         for (var i = 0; i < gridsCount; i++)
2292                         {
2293                                 grids[i].firstChild.lastChild.removeAttribute('flex', 1);
2294                                 grids[i].firstChild.lastChild.setAttribute('flex', 1);
2295                         }
2296                 }
2297         },
2298         
2299         fitMainContentToCanvas : function() 
2300         {
2301                 this.fitToCanvas(
2302                         this.content,
2303                         this.canvas,
2304                         parseInt(this.content.getAttribute('offsetWidth')),
2305                         parseInt(this.content.getAttribute('offsetHeight'))
2306                         +this.header.boxObject.height
2307                         +this.footer.boxObject.height
2308                 );
2309         },
2310  
2311         fitHeaderFooterToCanvas : function() 
2312         {
2313                 this.fitToCanvas(this.header, this.header.parentNode, 0, 0);
2314                 this.fitToCanvas(this.footer, this.footer.parentNode, 0, 0);
2315         },
2316    
2317         get offset(){ 
2318                 return this._offset;
2319         },
2320         set offset(aValue){
2321                 this._offset = parseInt(aValue || 0);
2322                 document.documentElement.setAttribute('lastoffset', this.offset);
2323                 return this.offset;
2324         },
2325   
2326 /* data I/O */ 
2327         
2328         setHash : function(aKey, aValue) 
2329         {
2330                 aKey = String(aKey).toLowerCase();
2331                 var hashArray = String(location.hash).replace(/^#/, '').toLowerCase().split(',');
2332
2333                 for (var i = hashArray.length-1; i > -1; i--)
2334                         if (!hashArray[i] || hashArray[i].indexOf(aKey) == 0)
2335                                 hashArray.splice(i, 1);
2336
2337                 if (aValue) hashArray.push(aValue);
2338                 hashArray.sort();
2339
2340                 location.replace(location.href.replace(/#.*$/, '') + (hashArray.length ? '#' + hashArray.join(',') : '' ));
2341         },
2342  
2343         get data(){ 
2344                 var codes = document.getElementById('builtinCode');
2345                 if (!this._data) {
2346                         // mozilla splits very long text node into multiple text nodes whose length is less than 4096 bytes.
2347                         // so, we must concat all the text nodes.
2348                         this.source.value = "";
2349                         this.sourceData   = [];
2350                         for (var i = 0; i < codes.childNodes.length; i++) {
2351                                 this.source.value += codes.childNodes[i].nodeValue;
2352                         }
2353
2354                         this._data = this.source.value.split(/----+/);
2355                         this.initData();
2356                 }
2357                 if (codes)
2358                         codes.parentNode.removeChild(codes);
2359
2360                 return this._data;
2361         },
2362         set data(aValue){
2363                 this._data = aValue.split(/----+/);
2364                 this.initData();
2365                 return this._data;
2366         },
2367         
2368         initData : function() 
2369         {
2370                 var range = document.createRange();
2371
2372                 range.selectNodeContents(this.list);
2373                 range.deleteContents();
2374
2375
2376                 this.sourceData = [];
2377
2378
2379                 var regexp = [
2380                                 /^[\r\n\s]+|[\r\n\s]+$/g,
2381                                 /(\r\n|[\r\n])/g
2382                         ];
2383
2384                 var title;
2385                 var titleRegExp   = /^(TITLE::)([^\n]*)\n?/im;
2386                 var header        = '';
2387                 var headerRegExp  = /^(HEADER::)([^\n]*)\n?/im;
2388                 var footer        = '';
2389                 var footerRegExp  = /^(FOOTER::)([^\n]*)\n?/im;
2390                 var chapter       = '';
2391                 var chapterRegExp = /^(CHAPTER::)([^\n]*)\n?/im;
2392                 var lastChapter;
2393                 var alignGlobal   = 'center';
2394                 var align;
2395                 var alignRegExp   = /^((GLOBAL-)?ALIGN::)(left|right|center|start|end)?\n?/im;
2396                 var size;
2397                 var sizeGlobal  = 9;
2398                 var sizeRegExp  = /^((GLOBAL-)?SIZE::)(\d+(\.\d+)?)\n?/im;
2399
2400                 var imageMatchResults;
2401                 var imagesRegExp  = this.makePhraseRegExp('%O%Oima?ge? +src="[^"]+" +width="[0-9]+" +height="[0-9]+"[^%C]*%C%C', 'gi');
2402                 var imagesRegExp2 = this.makePhraseRegExp('%O%Oima?ge? +src="([^"]+)"', 'i');
2403                 var image_src;
2404
2405                 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');
2406
2407                 var hiddenRegExp = /^(HIDDEN|IGNORE)::true\n?/im;
2408
2409                 var loadRegExp  = /^LOAD::(.+)\n?/im;
2410                 var timerRegExp = /^SET-TIMER::(\d+)(.*)\n?/im;
2411
2412                 var dataObj;
2413                 var i, j,
2414                         max = this._data.length;
2415                 var menuContents = document.createDocumentFragment();
2416                 var popup;
2417
2418                 var dataPath;
2419                 for (i = 0; i < max; i++)
2420                 {
2421                         image_src = null;
2422                         align     = null;
2423                         size     = null;
2424                         dataPath  = '';
2425
2426                         this._data[i] = this._data[i]
2427                                 .replace(regexp[0], '')
2428                                 .replace(regexp[1], '\n');
2429
2430                         this.sourceData[i] = this._data[i];
2431
2432                         if (loadRegExp.test(this._data[i])) {
2433                                 this._data[i] = this._data[i].replace(loadRegExp, '');
2434                                 dataPath = RegExp.$1;
2435                         }
2436
2437                         if (timerRegExp.test(this._data[i])) {
2438                                 this._data[i] = this._data[i].replace(timerRegExp, '');
2439                                 window.setTimeout(function(aSelf, aTime, aUnit) {
2440                                         if (!aSelf.timerTimer) {
2441                                                 switch(aUnit)
2442                                                 {
2443                                                         case 's':
2444                                                         case 'sec':
2445                                                         case 'sec.':
2446                                                         case 'second':
2447                                                         case 'seconds':
2448                                                                 aTime = aTime / 60;
2449                                                                 break;
2450
2451                                                         case 'h':
2452                                                         case 'hour':
2453                                                         case 'hours':
2454                                                                 aTime = aTime * 60;
2455                                                                 break;
2456
2457                                                         default:
2458                                                                 break;
2459                                                 }
2460                                                 aSelf.setTimer(aTime);
2461                                         }
2462                                 }, 100, this, parseInt(RegExp.$1), (RegExp.$2 || '').toLowerCase());
2463                         }
2464
2465                         if (hiddenRegExp.test(this._data[i])) {
2466                                 this._data.splice(i, 1);
2467                                 max--;
2468                                 i--;
2469                                 continue;
2470                         }
2471
2472                         while (titleRegExp.test(this._data[i])) {
2473                                 this._data[i] = this._data[i].replace(titleRegExp, '');
2474                                 if (String(RegExp.$1).toUpperCase() == 'TITLE::')
2475                                         title = RegExp.$2 || '' ;
2476                         }
2477
2478                         while (headerRegExp.test(this._data[i])) {
2479                                 this._data[i] = this._data[i].replace(headerRegExp, '');
2480                                 if (String(RegExp.$1).toUpperCase() == 'HEADER::')
2481                                         header = RegExp.$2 || '' ;
2482                         }
2483
2484                         while (footerRegExp.test(this._data[i])) {
2485                                 this._data[i] = this._data[i].replace(footerRegExp, '');
2486                                 if (String(RegExp.$1).toUpperCase() == 'FOOTER::')
2487                                         footer = RegExp.$2 || '' ;
2488                         }
2489
2490                         while (chapterRegExp.test(this._data[i])) {
2491                                 this._data[i] = this._data[i].replace(chapterRegExp, '');
2492                                 if (String(RegExp.$1).toUpperCase() == 'CHAPTER::')
2493                                         chapter = RegExp.$2 || '' ;
2494                         }
2495
2496                         while (alignRegExp.test(this._data[i])) {
2497                                 this._data[i] = this._data[i].replace(alignRegExp, '');
2498
2499                                 align = (RegExp.$3 || '').toLowerCase();
2500                                 if (align == 'left')
2501                                         align = 'start';
2502                                 else if (align == 'right')
2503                                         align = 'end';
2504
2505                                 if (String(RegExp.$1).toUpperCase() == 'GLOBAL-ALIGN::') {
2506                                         alignGlobal = align;
2507                                         align = null;
2508                                 }
2509                         }
2510
2511                         while (sizeRegExp.test(this._data[i])) {
2512                                 this._data[i] = this._data[i].replace(sizeRegExp, '');
2513                                 size = Math.max(0, Number(RegExp.$3 || this.baseSize));
2514                                 if (String(RegExp.$1).toUpperCase() == 'GLOBAL-SIZE::') {
2515                                         sizeGlobal = size;
2516                                         size = null;
2517                                 }
2518                         }
2519
2520                         imageMatchResults = this._data[i].match(imagesRegExp);
2521                         if (imageMatchResults) {
2522                                 for (j = imageMatchResults.length-1; j > -1; j--)
2523                                         image_src = this.preloadImage(imageMatchResults[j].match(imagesRegExp2)[1]);
2524                         }
2525
2526                         this._data[i] = {
2527                                 load   : dataPath,
2528                                 header : header,
2529                                 footer : footer,
2530                                 text   : this._data[i].split('\n'),
2531                                 image  : image_src,
2532                                 align  : align || alignGlobal,
2533                                 size   : size || sizeGlobal
2534                         };
2535                         this._data[i].plain = this._data[i].text
2536                                                         .join('\n')
2537                                                         .replace(plainTextRegExp, '$2$3$5$7$9')
2538                                                         .split('\n');
2539                         if (title !== void(0))
2540                                 this._data[i].title = title;
2541
2542                         this._data[i].chapter = chapter || title || '';
2543                         if (lastChapter === void(0) ||
2544                                 lastChapter != this._data[i].chapter) {
2545                                 lastChapter = this._data[i].chapter;
2546
2547                                 if (popup && popup.childNodes.length == 1) {
2548                                         menuContents.removeChild(menuContents.lastChild);
2549                                         menuContents.appendChild(popup.removeChild(popup.lastChild));
2550                                 }
2551
2552                                 popup = document.createElement('menupopup');
2553                                 menuContents.appendChild(document.createElement('menu'));
2554                                 menuContents.lastChild.setAttribute('label', this._data[i].chapter);
2555                                 menuContents.lastChild.appendChild(popup);
2556                         }
2557
2558                         popup.appendChild(document.createElement('menuitem'));
2559                         popup.lastChild.setAttribute('type', 'radio');
2560                         popup.lastChild.setAttribute('radiogroup', 'pages');
2561                         popup.lastChild.setAttribute('label', (i+1)+': '+(
2562                                 (this._data[i].plain.join('') || this._data[i].text.join(' ')).replace(/\s+/g, ' ')
2563                         ));
2564                         popup.lastChild.setAttribute('value', i);
2565
2566 //                      if (image_src) {
2567 //                              popup.lastChild.setAttribute('image', image_src);
2568 //                              popup.lastChild.setAttribute('class', 'menuitem-iconic');
2569 //                      }
2570                 }
2571
2572                 if (menuContents.childNodes.length == 1) {
2573                         range.selectNodeContents(menuContents.firstChild.firstChild);
2574                         menuContents = range.extractContents();
2575                 }
2576                 this.list.appendChild(menuContents);
2577
2578                 range.detach();
2579
2580
2581                 this.shownMontaLabels = [];
2582         },
2583   
2584         get dataPath(){ 
2585                 if (!this._dataPath)
2586                         this.dataPath = String(location.href).replace(/#.+$/, '');
2587                 return this._dataPath;
2588         },
2589         set dataPath(aValue){
2590                 var oldDataPath = this._dataPath;
2591                 this._dataPath = aValue;
2592                 if (oldDataPath != aValue) {
2593                         this._dataFolder = this._dataPath.split('?')[0].replace(/[^\/]+$/, '');
2594                 }
2595                 return this._dataPath;
2596         },
2597  
2598         get dataFolder(){ 
2599                 if (!this._dataFolder)
2600                         this.dataPath = this.dataPath;
2601                 return this._dataFolder;
2602         },
2603         set dataFolder(aValue){
2604                 this._dataFolder = aValue;
2605                 return this._dataFolder;
2606         },
2607  
2608         loadData : function(aPath) 
2609         {
2610                 this.dataPath = aPath;
2611                 var request = new XMLHttpRequest();
2612                 request.open('GET', aPath);
2613                 request.onload = function() {
2614                         Presentation.data = request.responseText;
2615                         Presentation.init();
2616                 };
2617                 request.send(null);
2618         },
2619  
2620         readParameter : function() { 
2621                 if (location.search || location.hash) {
2622                         var param = location.search.replace(/^\?/, '');
2623
2624                         if (location.hash.match(/page([0-9]+)/i) ||
2625                                 param.match(/page=([0-9]+)/i))
2626                                 this.offset = parseInt(RegExp.$1)-1;
2627
2628                         if (location.hash.match(/edit/i) ||
2629                                 param.match(/edit=(1|true|yes)/i)) {
2630                                 window.setTimeout('Presentation.toggleEditMode();', 0);
2631                         }
2632
2633                         if (location.hash.match(/eva/i) ||
2634                                 param.match(/eva=(1|true|yes)/i))
2635                                 this.toggleEvaMode();
2636
2637                         if (location.hash.match(/timer(\d+)\-(\d+)/i))
2638                                 this.setTimer(RegExp.$1, RegExp.$2);
2639
2640                         if (param.match(/(style|css)=([^&;]+)/i)) {
2641                                 var style = unescape(RegExp.$2);
2642                                 var pi = document.createProcessingInstruction('xml-stylesheet', 'href="'+style+'" type="text/css"');
2643                                 document.insertBefore(pi, document.documentElement);
2644                         }
2645
2646                         if (param.match(/data=([^&;]+)/i)) {
2647                                 this.loadData(RegExp.$1);
2648                                 return false;
2649                         }
2650                 }
2651                 return true;
2652         },
2653  
2654         preloadImage : function(aURI) 
2655         {
2656                 if (aURI in this.imageRequests) return;
2657
2658                 if (aURI.indexOf('http://') < 0 &&
2659                         aURI.indexOf('https://') < 0)
2660                         aURI = this.dataFolder+aURI;
2661
2662                 this.imageRequests[aURI] = new XMLHttpRequest();
2663                 try {
2664                         this.imageRequests[aURI].open('GET', aURI);
2665                         this.imageRequests[aURI].onload = function() {
2666                                 Presentation.imageRequests[aURI] = null;
2667                         };
2668                         this.imageRequests[aURI].send(null);
2669                 }
2670                 catch(e) {
2671                         this.imageRequests[aURI] = null;
2672                 }
2673                 return aURI;
2674         },
2675         imageRequests : {},
2676   
2677 /* commands */ 
2678         
2679         reload : function() { 
2680                 var file = String(location.href).replace(/#.+$/, '');
2681                 if (this.dataPath != file) {
2682                         var path = this.dataPath;
2683                         var request = new XMLHttpRequest();
2684                         request.open('GET', path);
2685                         request.onload = function() {
2686                                 Presentation.data = request.responseText;
2687                                 Presentation.init();
2688
2689                                 path = null;
2690                                 request = null;
2691                         };
2692                         request.send(null);
2693                 }
2694                 else
2695                         window.location.reload();
2696         },
2697  
2698         forward : function(){ 
2699                 if (!this.canForward) return;
2700                 this.offset++;
2701                 this.takahashi();
2702         },
2703         
2704         forwardStep : function(){ 
2705                 if (!this.canForward) return;
2706                 var monta = document.getElementsByAttribute('monta-hidden', 'true');
2707                 if (monta && monta.length) {
2708                         this.showMontaKeyword(monta[0]);
2709                 }
2710                 else
2711                         this.forward();
2712         },
2713   
2714         back : function(){ 
2715                 if (!this.canBack) return;
2716                 this.offset--;
2717                 if(this.offset < 0){this.offset = 0}
2718                 this.takahashi();
2719         },
2720  
2721         home : function(){ 
2722                 if (!this.canMove) return;
2723                 this.offset = 0;
2724                 this.takahashi();
2725         },
2726  
2727         end : function(){ 
2728                 if (!this.canMove) return;
2729                 this.offset = this.data.length-1;
2730                 this.takahashi();
2731         },
2732  
2733         showPage : function(aPageOffset){ 
2734                 if (!this.canMove) return;
2735                 this.offset = aPageOffset ? aPageOffset : 0 ;
2736                 this.takahashi();
2737         },
2738  
2739         toggleEditMode : function() 
2740         {
2741                 if (this.deck.selectedIndex == 1) {
2742                         if (document.getElementById('editTabBox').selectedTab.id == 'editTab-pages') {
2743                                 this.source.value = this.sourceData.join('\n----\n');
2744                         }
2745                         this.data = this.source.value;
2746                         this.cachedContents = [];
2747                         var range = document.createRange();
2748                         range.selectNodeContents(this.content);
2749                         range.deleteContents();
2750                         range.detach();
2751                         this.init();
2752                 }
2753                 else {
2754                         this.initEditPages();
2755                 }
2756
2757                 this.deck.selectedIndex = this.deck.selectedIndex == 0 ? 1 : 0 ;
2758                 this.setHash('edit', this.deck.selectedIndex == 0 ? '' : 'edit' );
2759         },
2760  
2761         toggleEvaMode : function() 
2762         {
2763                 var check = document.getElementById('toggleEva');
2764                 if (this.canvas.getAttribute('eva') == 'true') {
2765                         this.canvas.removeAttribute('eva');
2766                         this.logo.removeAttribute('eva');
2767                         check.checked = false;
2768                 }
2769                 else {
2770                         this.canvas.setAttribute('eva', true);
2771                         this.logo.setAttribute('eva',true);
2772                         check.checked = true;
2773                 }
2774                 this.setHash('eva', check.checked ? 'eva' : '' );
2775         },
2776   
2777 /* auto cruise */ 
2778         
2779         toggleAutoCruiseMode : function() 
2780         {
2781                 var autoCruise = document.getElementById('autoButton');
2782                 if(!autoCruise.checked)
2783                         this.startAutoCruise();
2784                 else
2785                         autoCruise.checked = false;
2786         },
2787  
2788         startAutoCruise : function() 
2789         {
2790                 var autoCruise = document.getElementById('autoButton');
2791                 autoCruise.checked = true;
2792
2793                 if (this.autoCruiseTimer) {
2794                         window.clearTimeout(this.autoCruiseTimer);
2795                 }
2796                 this.autoCruiseTimer = window.setTimeout(this.autoCruise, this.autoCruiseInterval);
2797         },
2798  
2799         changeAutoCruiseInterval : function(aInterval) 
2800         {
2801                 this.autoCruiseInterval = aInterval;
2802                 this.startAutoCruise();
2803         },
2804  
2805         autoCruise : function() 
2806         {
2807                 var autoCruise = document.getElementById('autoButton');
2808                 if (!autoCruise.checked) return;
2809
2810                 if (Presentation.offset == Presentation.data.length-1) {
2811                         if (Presentation.canMove)
2812                                 Presentation.home();
2813                 }
2814                 else {
2815                         if (Presentation.canForward)
2816                                 Presentation.forwardStep();
2817                 }
2818                 Presentation.autoCruiseTimer = window.setTimeout(arguments.callee, Presentation.autoCruiseInterval);
2819         },
2820         autoCruiseTimer : null,
2821   
2822 /* timer */ 
2823         
2824         resetTimer : function() 
2825         {
2826                 if (this.timerTimer) {
2827                         window.clearInterval(this.timerTimer);
2828                         this.timerTimer = null;
2829                 }
2830                 this.timer.setAttribute('value', 0);
2831                 this.timer.setAttribute('collapsed', true);
2832                 this.setHash('timer', '');
2833         },
2834  
2835         setTimer : function(aStart, aEnd) 
2836         {
2837                 var now = (new Date()).getTime();
2838                 if (aStart !== void(0) && aEnd === void(0)) {
2839                         var rest = Math.abs(aStart);
2840                         this.timerStart = now;
2841                         this.timerEnd   = this.timerStart + (rest * 60000);
2842                 }
2843                 else if (aStart === void(0) && aEnd === void(0)) {
2844                         var rest = prompt('Remaining Time (minits)');
2845                         if (rest == '') {
2846                                 this.resetTimer();
2847                                 return;
2848                         }
2849                         else {
2850                                 rest = Number(rest);
2851                                 if (!rest || isNaN(rest)) return;
2852                         }
2853
2854                         rest = Math.abs(rest);
2855                         this.timerStart = now;
2856                         this.timerEnd   = this.timerStart + (rest * 60000);
2857                 }
2858                 else {
2859                         aStart = Number(aStart);
2860                         aEnd   = Number(aEnd);
2861                         if (isNaN(aStart) || isNaN(aEnd)) return;
2862
2863                         this.timerStart = Math.min(aStart, aEnd);
2864                         this.timerEnd   = Math.max(aStart, aEnd);
2865
2866                         if (this.timerStart >= now || this.timerEnd <= now) return;
2867                 }
2868
2869                 this.resetTimer();
2870
2871                 this.timer.removeAttribute('collapsed');
2872                 this.setHash('timer', 'timer'+this.timerStart+'-'+this.timerEnd);
2873
2874                 if (now != this.timerStart)
2875                         this.updateTimer(this);
2876
2877                 this.timerTimer = window.setInterval(this.updateTimer, Math.min(this.timerUpdatingInterval, (this.timerEnd-this.timerStart)/(this.data.length*2)), this);
2878         },
2879         
2880         timerStart : 0, 
2881         timerEnd   : 0,
2882         timerTimer : null,
2883  
2884         updateTimer : function(aThis) 
2885         {
2886                 var now = (new Date()).getTime();
2887                 if (now >= aThis.timerEnd) {
2888                         aThis.resetTimer();
2889                         aThis.timer.setAttribute('value', 100);
2890                         aThis.timer.removeAttribute('collapsed');
2891                         aThis.setHash('timer', '');
2892                 }
2893                 else {
2894                         var value = parseInt(((now - aThis.timerStart) / (aThis.timerEnd - aThis.timerStart)) * 100);
2895                         aThis.timer.setAttribute('value', value);
2896                 }
2897         },
2898   
2899         updateTimerItem : function() 
2900         {
2901                 var item = document.getElementById('timerItem');
2902                 if (this.timerTimer) {
2903                         item.setAttribute('label', item.getAttribute('label-active').replace(/%s/gi, Math.round((this.timerEnd - (new Date()).getTime()) / 60000)));
2904                 }
2905                 else {
2906                         item.setAttribute('label', item.getAttribute('label-normal'));
2907                 }
2908         },
2909   
2910 /* print */ 
2911         
2912         print : function() 
2913         {
2914                 if (!this.canMove) {
2915                         alert('Please wait for a while, and retry later.');
2916                         return;
2917                 }
2918
2919                 this.stopPrint();
2920                 if (this.printWindow) {
2921                         this.printWindow.close();
2922                         this.printWindow = null;
2923                 }
2924
2925                 if (!this.isToolbarHidden)
2926                         this.showHideToolbar(true);
2927
2928                 this.printWindow = window.open('output.htm', 'PresentationPrint', 'dependent=yes,hotkeys=yes,location=yes,menubar=yes,personalbar=yes,scrollbars=yes,status=yes,toolbar=yes');
2929                 if (!this.printWindow) return;
2930
2931                 this.isPrinting = true;
2932
2933                 if (!this.printCanvas)
2934                         this.printCanvas = document.createElementNS(XHTMLNS, 'canvas');
2935
2936                 this.printWindow.document.write('<html><head><title>'+document.title+'</title></head><body></body></html>');
2937                 this.home();
2938                 this.printTimer = window.setInterval(this.printCallback, 0, this);
2939         },
2940          
2941         printCallback : function(aThis) 
2942         {
2943                 if (
2944                         !aThis.canMove
2945                         )
2946                         return;
2947
2948                 var monta = document.getElementsByAttribute('monta-hidden', 'true');
2949                 if (monta && monta.length) {
2950                         for (var i = monta.length-1; i > -1; i--)
2951                                 aThis.showMontaKeyword(monta[i], true);
2952                 }
2953
2954                 var doc  = aThis.printWindow.document;
2955                 var body = doc.getElementsByTagName('body')[0];
2956                 var img  = doc.createElement('img');
2957
2958                 if ((aThis.offset+1) % 2 == 1) {
2959                         body.appendChild(doc.createElement('div'));
2960 //                      body.lastChild.style.clear = 'both';
2961                 }
2962                 var box = doc.createElement('div');
2963                 box.appendChild(doc.createElement('div'));
2964                 box.lastChild.appendChild(document.createTextNode(aThis.offset+1));
2965                 body.lastChild.appendChild(box);
2966
2967                 var w = window.innerWidth;
2968                 var h = window.innerHeight;
2969                 var canvasW = parseInt(w * aThis.printSize);
2970                 var canvasH = parseInt(h * aThis.printSize);
2971
2972                 aThis.printCanvas.width  = canvasW;
2973                 aThis.printCanvas.height = canvasH;
2974                 aThis.printCanvas.style.border = 'black solid medium';
2975
2976                 img.style.border = 'black solid medium';
2977                 img.style.width  = canvasW+'px';
2978                 img.style.height = canvasH+'px';
2979
2980                 box.style.margin = '1em';
2981                 box.style.width  = parseInt(w * aThis.printSize)+'px';
2982                 box.style.cssFloat  = ((aThis.offset+1) % 2 == 1) ? 'left' : 'right' ;
2983
2984                 try {
2985                         netscape.security.PrivilegeManager.enablePrivilege('UniversalBrowserRead');
2986
2987                         var ctx = aThis.printCanvas.getContext('2d');
2988                         ctx.clearRect(0, 0, canvasW, canvasH);
2989                         ctx.save();
2990                         ctx.scale(aThis.printSize, aThis.printSize);
2991                         ctx.drawWindow(window, 0, 0, w, h, 'rgb(255,255,255)');
2992                         ctx.restore();
2993                         try {
2994                                 if (aThis.imageType == 'jpeg')
2995                                         img.src = aThis.printCanvas.toDataURL('image/jpeg', 'quality=50');
2996                                 else
2997                                         img.src = aThis.printCanvas.toDataURL('image/png', 'transparency=none');
2998
2999                                 box.appendChild(img);
3000                         }
3001                         catch(e) {
3002                                 box.appendChild(aThis.printCanvas.cloneNode(true));
3003                                 ctx = box.lastChild.getContext('2d');
3004                                 ctx.clearRect(0, 0, canvasW, canvasH);
3005                                 ctx.save();
3006                                 ctx.scale(aThis.printSize, aThis.printSize);
3007                                 ctx.drawWindow(window, 0, 0, w, h, 'rgb(255,255,255)');
3008                                 ctx.restore();
3009                         }
3010                 }
3011                 catch(e) {
3012                         alert('Error: Failed to create a document for printing.\n\n------\n'+e);
3013                         aThis.stopPrint();
3014                         return;
3015                 }
3016
3017                 if (aThis.offset == aThis.data.length-1) {
3018                         aThis.stopPrint();
3019                         aThis.printWindow.focus();
3020                 }
3021                 else {
3022                         aThis.forward();
3023                 }
3024         },
3025   
3026         stopPrint : function() 
3027         {
3028                 window.clearInterval(this.printTimer);
3029                 this.printTimer = null;
3030                 this.isPrinting = false;
3031         },
3032  
3033         printSize   : 0.4, 
3034         printTimer  : null,
3035         printWindow : null,
3036         printCanvas : null,
3037   
3038 /* view source */ 
3039         
3040         plainTextShown : false, 
3041  
3042         get plainTextBox() 
3043         {
3044                 return document.getElementById('plainTextBox');
3045         },
3046  
3047         get plainTextField() 
3048         {
3049                 return document.getElementById('plainTextField');
3050         },
3051  
3052         showPlainText : function() 
3053         {
3054                 if (this.plainTextShown) return;
3055                 this.plainTextShown = true;
3056                 this.plainTextBox.removeAttribute('hidden');
3057                 this.plainTextField.style.width = parseInt(this.canvas.boxObject.width * 0.8)+'px';
3058                 this.plainTextField.style.height = parseInt(this.canvas.boxObject.height * 0.8)+'px';
3059                 this.plainTextField.value = this.data[this.offset].text.join('\n').replace(/\u200b/g, '');
3060                 this.plainTextField.select();
3061                 this.plainTextField.focus();
3062         },
3063  
3064         hidePlainText : function() 
3065         {
3066                 if (!this.plainTextShown) return;
3067                 this.plainTextShown = false;
3068                 this.plainTextBox.setAttribute('hidden', true);
3069                 this.plainTextField.value = '';
3070                 this.plainTextField.blur();
3071         },
3072   
3073 /* state */ 
3074         
3075         get canMove() 
3076         {
3077                 return (
3078                                 this.isRendering ||
3079                                 importNodeTreeWithDelayTimers ||
3080                                 this.montaAnimating
3081                         ) ? false : true ;
3082         },
3083  
3084         get canBack() 
3085         {
3086                 return this.canMove;
3087         },
3088  
3089         get canForward() 
3090         {
3091                 return this.canMove;
3092         },
3093  
3094         get isPresentationMode(){ 
3095                 return (this.deck.selectedIndex == 0);
3096         },
3097   
3098 /* event handling */ 
3099         
3100         handleEvent : function(aEvent) 
3101         {
3102                 if (this.isPrinting) return;
3103
3104                 var node = aEvent.target;
3105                 var inRawContents = false;
3106                 do {
3107                         if (node.nodeType == Node.ELEMENT_NODE &&
3108                                 /\braw\b/i.test(node.getAttribute('class'))) {
3109                                 inRawContents = true;
3110                                 break;
3111                         }
3112
3113                         node = node.parentNode;
3114                 }
3115                 while (node.parentNode)
3116
3117
3118                 switch (aEvent.type)
3119                 {
3120                         default:
3121                                 break;
3122
3123                         case 'resize':
3124                                 this.takahashi(); // redrwa
3125                                 break;
3126
3127                         case 'contextmenu':
3128                                 aEvent.stopPropagation();
3129                                 aEvent.preventCapture();
3130                                 aEvent.preventDefault();
3131                                 aEvent.preventBubble();
3132                                 break;
3133
3134
3135                         case 'mouseup':
3136                                 if (inRawContents) return;
3137                                 this.dragStartX = -1;
3138                                 this.dragStartY = -1;
3139                                 if (this.indicatorBar.dragging)
3140                                         this.onIndicatorBarDragEnd(aEvent);
3141                                 break;
3142
3143                         case 'mousedown':
3144                                 if (inRawContents) return;
3145                                 if (this.dragStartX < 0) {
3146                                         this.dragStartX = aEvent.clientX;
3147                                         this.dragStartY = aEvent.clientY;
3148                                 }
3149                                 var box = this.indicatorBar.boxObject;
3150                                 if (!(aEvent.screenX < box.screenX ||
3151                                         aEvent.screenY < box.screenY ||
3152                                         aEvent.screenX > box.screenX+box.width ||
3153                                         aEvent.screenY > box.screenY+box.height))
3154                                         this.onIndicatorBarDragStart();
3155                                 break;
3156
3157                         case 'mousemove':
3158                                 if (inRawContents) return;
3159                                 this.checkShowHideToolbar(aEvent);
3160                                 if (this.indicatorBar.dragging) {
3161                                         this.onIndicatorBarDragMove(aEvent);
3162                                         return;
3163                                 }
3164                                 if (this.dragStartX > -1) {
3165                                         if (Math.abs(this.dragStartX-aEvent.clientX) > Math.abs(this.dragStartDelta) ||
3166                                                 Math.abs(this.dragStartY-aEvent.clientY) > Math.abs(this.dragStartDelta)) {
3167                                                 var event = document.createEvent('Events');
3168                                                 event.initEvent('StartDragOnCanvas', false, true);
3169                                                 this.canvas.dispatchEvent(event);
3170                                         }
3171                                 }
3172                                 break;
3173
3174                         case 'CanvasContentAdded':
3175                                 if (this.fitToCanvasTimer) {
3176                                         window.clearTimeout(this.fitToCanvasTimer);
3177                                         this.fitToCanvasTimer = null;
3178                                 }
3179                                 this.fitToCanvasTimer = window.setTimeout('Presentation.fitMainContentToCanvas()', 100);
3180                                 break;
3181
3182                         case 'DOMMouseScroll':
3183                                 if (
3184                                         (aEvent.detail > 0 && this.scrollCounter < 0) ||
3185                                         (aEvent.detail < 0 && this.scrollCounter > 0)
3186                                         )
3187                                         this.scrollCounter = 0;
3188
3189                                 this.scrollCounter += aEvent.detail;
3190                                 if (Math.abs(this.scrollCounter) >= this.scrollThreshold) {
3191                                         if (aEvent.detail > 0)
3192                                                 Presentation.forwardStep();
3193                                         else
3194                                                 Presentation.back();
3195
3196                                         this.scrollCounter = 0;
3197                                 }
3198                                 break;
3199                 }
3200         },
3201          
3202         dragStartX : -1, 
3203         dragStartY : -1,
3204         scrollCounter : 0,
3205         scrollThreshold : 10,
3206   
3207         onKeyPress : function(aEvent) { 
3208                 if (this.isPrinting) return;
3209
3210                 switch(aEvent.keyCode)
3211                 {
3212                         case aEvent.DOM_VK_BACK_SPACE:
3213                                 if (this.isPresentationMode) {
3214                                         aEvent.preventBubble();
3215                                         aEvent.preventDefault();
3216                                         Presentation.back();
3217                                 }
3218                                 break;
3219                         default:
3220                                 break;
3221                 }
3222         },
3223  
3224 /* actions on presentation */ 
3225          
3226         onPresentationClick : function(aEvent) 
3227         {
3228                 if (this.isPrinting) {
3229                         if (confirm('Do you want printing operation to be stopped?')) {
3230                                 this.stopPrint();
3231                         }
3232                         return;
3233                 }
3234
3235                 if (!this.isToolbarHidden)
3236                         this.showHideToolbar();
3237
3238                 switch(aEvent.button)
3239                 {
3240                         case 0:
3241                                 switch (aEvent.target.getAttribute('class'))
3242                                 {
3243                                         case 'link-text':
3244                                                 var uri = aEvent.target.getAttribute('href');
3245                                                 if (uri) {
3246                                                         window.open(uri);
3247                                                         return;
3248                                                 }
3249                                                 break;
3250
3251                                         case 'monta-label':
3252                                                 if (aEvent.target.getAttribute('monta-hidden') == 'true') {
3253                                                         this.showMontaKeyword(aEvent.target);
3254                                                         aEvent.preventBubble();
3255                                                         return;
3256                                                 }
3257
3258                                         default:
3259                                                 break;
3260                                 }
3261                                 this.forward();
3262                                 document.documentElement.focus();
3263                                 break;
3264                         case 2:
3265                                 this.back();
3266                                 document.documentElement.focus();
3267                                 break;
3268                         default:
3269                                 break;
3270                 }
3271         },
3272  
3273 /* scrollbar */ 
3274         
3275         onScrollerDragStart : function(){ 
3276                 if (this.isPrinting) return;
3277
3278                 this.scroller.dragging = true;
3279         },
3280  
3281         onScrollerDragMove : function(){ 
3282                 if (this.isPrinting) return;
3283
3284                 if (this.scroller.dragging)
3285                         this.showPage(parseInt(this.scroller.getAttribute('curpos')));
3286         },
3287  
3288         onScrollerDragDrop : function(){ 
3289                 if (this.isPrinting) return;
3290
3291                 this.onScrollerDragMove();
3292                 this.scroller.dragging = false;
3293         },
3294   
3295 /* indicator bar */ 
3296         
3297         onIndicatorBarClick : function(aEvent) 
3298         {
3299                 if (this.isPrinting) return;
3300
3301                 var bar = this.indicatorBar;
3302                 this.showPage(Math.round((aEvent.screenX - bar.boxObject.screenX) / bar.boxObject.width * this.data.length));
3303         },
3304  
3305         onIndicatorBarDragStart : function() 
3306         {
3307                 if (this.isPrinting) return;
3308
3309                 this.indicatorBar.dragging = true;
3310         },
3311  
3312         onIndicatorBarDragMove : function(aEvent) 
3313         {
3314                 if (this.isPrinting) return;
3315
3316                 var bar = this.indicatorBar;
3317                 this.showPage(Math.round((aEvent.screenX - bar.boxObject.screenX) / bar.boxObject.width * this.data.length));
3318         },
3319  
3320         onIndicatorBarDragEnd : function(aEvent) 
3321         {
3322                 if (this.isPrinting) return;
3323
3324                 this.onIndicatorBarDragMove(aEvent);
3325                 this.indicatorBar.dragging = false;
3326         },
3327   
3328         showMontaKeyword : function(aNode, aWithoutAnimation) { 
3329                 if (aNode.getAttribute('monta-hidden') != 'true') return;
3330
3331                 if (aWithoutAnimation) {
3332                         aNode.setAttribute('monta-hidden', 'false');
3333                         return;
3334                 }
3335
3336                 aNode.setAttribute('monta-hidden', 'progress');
3337
3338                 this.montaAnimating = true;
3339
3340                 window.setTimeout(this.showMontaKeywordCallback, 0, {
3341                         position : -100,
3342                         node     : aNode,
3343                         interval : this.showMontaKeywordTimeout/10
3344                 });
3345         },
3346         
3347         showMontaKeywordCallback : function(aInfo) { 
3348                 if (aInfo.position >= aInfo.node.boxObject.width) {
3349                         aInfo.node.setAttribute('monta-hidden', 'false');
3350                         Presentation.montaAnimating = false;
3351                         return;
3352                 }
3353
3354                 aInfo.position += (aInfo.node.boxObject.width/10);
3355                 aInfo.node.setAttribute('style', 'background-position: '+aInfo.position+'px 0 !important;');
3356                 window.setTimeout(arguments.callee, aInfo.interval, aInfo);
3357         },
3358         montaAnimating : false,
3359     
3360 /* toolbar animation */ 
3361         
3362         onToolbarArea   : false, 
3363         toolbarHeight   : 0,
3364         toolbarDelay    : 300,
3365         toolbarTimer    : null,
3366         isToolbarHidden : false,
3367  
3368         checkShowHideToolbar : function(aEvent) { 
3369                 if (!this.scroller || this.scroller.dragging || this.preventToShowHideToolbar) return;
3370
3371                 this.onToolbarArea = (aEvent.clientY < this.toolbarHeight);
3372
3373                 if (this.isToolbarHidden == this.onToolbarArea) {
3374                         if (this.toolbarTimer) window.clearTimeout(this.toolbarTimer);
3375                         this.toolbarTimer = window.setTimeout('Presentation.checkShowHideToolbarCallback()', this.toolbarDelay);
3376                 }
3377         },
3378         
3379         checkShowHideToolbarCallback : function() { 
3380                 if (this.isToolbarHidden == this.onToolbarArea)
3381                         this.showHideToolbar();
3382         },
3383   
3384         showHideToolbar : function(aWithoutAnimation) 
3385         {
3386                 if (this.isPrinting) return;
3387
3388                 if (this.toolbarAnimationTimer) window.clearTimeout(this.toolbarAnimationTimer);
3389
3390                 this.toolbarAnimationInfo = { count : 0 };
3391                 if (this.isToolbarHidden) {
3392                         this.toolbarAnimationInfo.start = 0;
3393                         this.toolbarAnimationInfo.end   = this.toolbarHeight;
3394                 }
3395                 else {
3396                         this.toolbarAnimationInfo.start = this.toolbarHeight;
3397                         this.toolbarAnimationInfo.end   = 0;
3398                 }
3399                 this.toolbarAnimationInfo.current = 0;
3400
3401                 this.toolbar.setAttribute('style', 'margin-top:'+(0-(this.toolbarHeight-this.toolbarAnimationInfo.start))+'px; margin-bottom:'+(0-this.toolbarAnimationInfo.start)+'px;');
3402
3403                 if (aWithoutAnimation) {
3404                         this.toolbarAnimationInfo.current = this.toolbarHeight;
3405                         Presentation.animateToolbar();
3406                 }
3407                 else {
3408                         this.toolbarAnimationTimer = window.setTimeout('Presentation.animateToolbar()', this.toolbarAnimationDelay/this.toolbarAnimationSteps);
3409                 }
3410         },
3411         
3412         animateToolbar : function() 
3413         {
3414                 this.toolbarAnimationInfo.current += parseInt(this.toolbarHeight/this.toolbarAnimationSteps);
3415
3416                 var top, bottom;
3417                 if (this.toolbarAnimationInfo.start < this.toolbarAnimationInfo.end) {
3418                         top    = this.toolbarHeight-this.toolbarAnimationInfo.current;
3419                         bottom = this.toolbarAnimationInfo.current;
3420                 }
3421                 else {
3422                         top    = this.toolbarAnimationInfo.current;
3423                         bottom = this.toolbarHeight-this.toolbarAnimationInfo.current;
3424                 }
3425
3426                 top    = Math.min(Math.max(top, 0), this.toolbarHeight);
3427                 bottom = Math.min(Math.max(bottom, 0), this.toolbarHeight);
3428
3429                 this.toolbar.setAttribute('style', 'margin-top:'+(0-top)+'px; margin-bottom:'+(0-bottom)+'px');
3430
3431                 if (this.toolbarAnimationInfo.count < this.toolbarAnimationSteps) {
3432                         this.toolbarAnimationInfo.count++;
3433                         this.toolbarAnimationTimer = window.setTimeout('Presentation.animateToolbar()', this.toolbarAnimationDelay/this.toolbarAnimationSteps);
3434                 }
3435                 else
3436                         this.isToolbarHidden = !this.isToolbarHidden;
3437         },
3438  
3439         toolbarAnimationDelay : 100, 
3440         toolbarAnimationSteps : 5,
3441         toolbarAnimationInfo  : null,
3442         toolbarAnimationTimer : null,
3443    
3444 /* edit mode */ 
3445          
3446         initEditPages : function() 
3447         {
3448                 var range = document.createRange();
3449                 range.selectNodeContents(this.pages);
3450                 range.deleteContents();
3451                 range.detach();
3452
3453                 var editBox  = document.getElementById('pageEditBoxTemplate');
3454                 var contents = document.createDocumentFragment();
3455                 for (var i = 0, maxi = this.sourceData.length; i < maxi; i++)
3456                 {
3457                         var newBox = editBox.cloneNode(true);
3458                         newBox.firstChild.setAttribute('value', this.sourceData[i])
3459                         newBox.setAttribute('id', this.EDIT_BOX_ID_PREFIX+i);
3460                         contents.appendChild(newBox);
3461                 }
3462
3463                 this.pages.appendChild(contents);
3464         },
3465  
3466         toggleEditStyle : function(aEvent) 
3467         {
3468                 var tabbox = document.getElementById('editTabBox');
3469                 var tab = tabbox.selectedTab;
3470                 if (tab.id == 'editTab-pages') {
3471                         this.sourceData = this.source.value.split(/\n?----+\n?/);
3472                         this.initEditPages();
3473                 }
3474                 else {
3475                         this.source.value = this.sourceData.join('\n----\n');
3476                 }
3477         },
3478  
3479         insert : function(aType) { 
3480                 switch (aType)
3481                 {
3482                         case 'page':
3483                                 this.insertTextFor('\n----\n', this.source, 6);
3484                                 break;
3485                         case 'header':
3486                                 this.insertTextFor('\nHEADER::\n', this.source, 9);
3487                                 break;
3488                         case 'footer':
3489                                 this.insertTextFor('\nFOOTER::\n', this.source, 9);
3490                                 break;
3491
3492                         case 'em':
3493                         case 'emphasis':
3494                                 this.insertTextFor(this.phraseOpenParen+this.phraseOpenParen+'EM:'+this.phraseCloseParen+this.phraseCloseParen, this.source, 5);
3495                                 break;
3496                         case 'pre':
3497                         case 'preformatted':
3498                                 this.insertTextFor(this.phraseOpenParen+this.phraseOpenParen+'PRE:'+this.phraseCloseParen+this.phraseCloseParen, this.source, 6);
3499                                 break;
3500                         case 'monta':
3501                                 this.insertTextFor(this.phraseOpenParen+this.phraseCloseParen, this.source, 1);
3502                                 break;
3503                         case 'link':
3504                                 this.insertTextFor(this.phraseOpenParen+this.phraseOpenParen+'|http://'+this.phraseCloseParen+this.phraseCloseParen, this.source, 2);
3505                                 break;
3506                         case 'img':
3507                         case 'image':
3508                                 this.insertTextFor(this.phraseOpenParen+this.phraseOpenParen+'image src="" width="" height=""'+this.phraseCloseParen+this.phraseCloseParen, this.source, 13);
3509                                 break;
3510
3511                         default:
3512                                 return;
3513                 }
3514                 this.onEditSource();
3515         },
3516         
3517         insertTextFor : function(aString, aNode, aPosOffset) 
3518         {
3519                 var pos = aNode.selectionStart;
3520                 var value = aNode.value;
3521                 aNode.value = [value.substring(0, pos), aString, value.substring(pos, value.length)].join('');
3522                 aNode.selectionEnd = aNode.selectionStart = pos + (aPosOffset || 0);
3523         },
3524   
3525         removePage : function(aPage) 
3526         {
3527                 if (aPage === void(0) || this.pages.childNodes.length == 1) return;
3528
3529                 var target = document.getElementById(this.EDIT_BOX_ID_PREFIX+aPage);
3530
3531                 var next = target;
3532                 while (next = next.nextSibling)
3533                 {
3534                         next.setAttribute('id', this.EDIT_BOX_ID_PREFIX + (Number(next.id.match(/\d+/)) - 1));
3535                 }
3536                 this.pages.removeChild(target);
3537
3538                 this.sourceData.splice(aPage, 1);
3539
3540                 var maxPage = this.pages.childNodes.length-1;
3541                 if (this.offset > maxPage) this.offset = maxPage;
3542
3543                 this.onEditPage();
3544         },
3545  
3546         onEditSource : function() { 
3547                 if (this.isPrinting) return;
3548         },
3549
3550  
3551         onEditPage : function(aPage) 
3552         {
3553                 if (aPage !== void(0)) {
3554                         var target = document.getElementById(this.EDIT_BOX_ID_PREFIX+aPage);
3555                         if (target) {
3556                                 target = getNodesByXPath('descendant::*[@class="page-edit-box-main"]', target).snapshotItem(0);
3557                                 this.sourceData[aPage] = target.value;
3558                         }
3559                 }
3560         },
3561  
3562         output : function() 
3563         {
3564                 location.href = 'data:application/octet-stream,'+encodeURIComponent(this.source.value);
3565         }
3566   
3567 }; 
3568         
3569 var StrokeService = { 
3570         
3571         className      : 'stroke-dot', 
3572         dragStartDelta : 10,
3573         lineColor      : 'red',
3574         lineWidth      : 3,
3575
3576         initialized : false,
3577
3578
3579         mode          : null,
3580         canvas        : null,
3581         canvasContext : null,
3582         startX        : -1,
3583         startY        : -1,
3584  
3585         init : function(aCanvas) 
3586         {
3587                 this.initialized = true;
3588
3589                 this.canvas = aCanvas;
3590                 this.canvasElement = aCanvas;
3591
3592                 var canvas = document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas');
3593                 canvas.width = this.canvas.boxObject.width;
3594                 canvas.height = this.canvas.boxObject.height;
3595                 this.canvas.appendChild(canvas);
3596                 if (!('getContext' in canvas) || !canvas.getContext) {
3597                         this.canvas.removeChild(canvas);
3598                         this.mode = 'box';
3599                 }
3600                 else {
3601                         this.canvasElement = canvas;
3602                         this.canvasContext = canvas.getContext('2d');
3603                         this.mode          = 'canvas';
3604                 }
3605
3606                 document.documentElement.addEventListener('PresentationRedraw', this, false);
3607                 window.addEventListener('resize', this, false);
3608                 this.canvasElement.addEventListener('mouseup',   this, false);
3609                 this.canvasElement.addEventListener('mousedown', this, false);
3610                 this.canvasElement.addEventListener('mousemove', this, false);
3611
3612                 this.canvasElement.addEventListener('click', this, false);
3613                 this.canvasElement.addEventListener('dblclick', this, false);
3614
3615                 this.clear();
3616         },
3617  
3618         destroy : function() 
3619         {
3620                 document.documentElement.removeEventListener('PresentationRedraw', this, false);
3621                 window.removeEventListener('resize', this, false);
3622                 if (this.canvasElement) {
3623                         this.canvasElement.removeEventListener('mouseup', this, false);
3624                         this.canvasElement.removeEventListener('mousedown', this, false);
3625                         this.canvasElement.removeEventListener('mousemove', this, false);
3626                         this.canvasElement.removeEventListener('click', this, false);
3627                 }
3628
3629                 this.cliclableNodesManager = null;
3630                 this.canvasElement = null;
3631                 this.canvas = null;
3632
3633                 this.initialized = false;
3634         },
3635  
3636         handleEvent : function(aEvent) 
3637         {
3638                 switch(aEvent.type)
3639                 {
3640                         default:
3641                                 break;
3642
3643                         case 'mouseup':
3644                                 this.finish(aEvent);
3645                                 this.startX = -1;
3646                                 this.startY = -1;
3647                                 window.setTimeout('StrokeService.preventToSendClickEvent = false', 10);
3648                                 break;
3649
3650                         case 'mousedown':
3651                                 if (this.startX < 0) {
3652                                         this.startX = aEvent.clientX;
3653                                         this.startY = aEvent.clientY;
3654                                 }
3655                                 break;
3656
3657                         case 'mousemove':
3658                                 if (this.startX > -1 && !this.active) {
3659                                         if (Math.abs(this.startX-aEvent.clientX) > Math.abs(this.dragStartDelta) ||
3660                                                 Math.abs(this.startY-aEvent.clientY) > Math.abs(this.dragStartDelta)) {
3661                                                 this.start(aEvent, this.startX, this.startY);
3662                                                 this.preventToSendClickEvent = true;
3663                                         }
3664                                 }
3665                                 else
3666                                         this.trace(aEvent);
3667
3668                                 break;
3669
3670                         case 'PresentationRedraw':
3671                         case 'resize':
3672                                 this.clear();
3673                                 break;
3674
3675                         case 'click':
3676                                 if (this.preventToSendClickEvent) {
3677                                         aEvent.stopPropagation();
3678                                         aEvent.preventCapture();
3679                                         aEvent.preventDefault();
3680                                         aEvent.preventBubble();
3681                                         this.preventToSendClickEvent = false;
3682                                 }
3683                                 else if (this.cliclableNodesManager && this.cliclableNodesManager.clickableNodes) {
3684                                         var nodes = this.cliclableNodesManager.clickableNodes;
3685                                         var max = nodes.length;
3686                                         var x, y, width, height
3687                                         for (var i = 0; i < max; i++)
3688                                         {
3689                                                 if (nodes[i].boxObject) {
3690                                                         x      = nodes[i].boxObject.x;
3691                                                         y      = nodes[i].boxObject.y;
3692                                                         width  = nodes[i].boxObject.width;
3693                                                         height = nodes[i].boxObject.height;
3694                                                 }
3695                                                 else {
3696                                                         x      = nodes[i].offsetLeft;
3697                                                         y      = nodes[i].offsetTop;
3698                                                         width  = nodes[i].offsetWidth;
3699                                                         height = nodes[i].offsetHeight;
3700                                                 }
3701                                                 if (aEvent.clientX < x ||
3702                                                         aEvent.clientX > x+width ||
3703                                                         aEvent.clientY < y ||
3704                                                         aEvent.clientY > y+height)
3705                                                         continue;
3706
3707                                                 var event = document.createEvent('MouseEvents');
3708                                                 event.initMouseEvent(
3709                                                         aEvent.type, aEvent.canBubble, aEvent.cancelable, aEvent.view,
3710                                                         aEvent.detail,
3711                                                         aEvent.screenX, aEvent.screenY, aEvent.clientX, aEvent.clientY,
3712                                                         aEvent.ctrlKey, aEvent.altKey, aEvent.shiftKey, aEvent.metaKey,
3713                                                         aEvent.button,
3714                                                         aEvent.relatedTarget
3715                                                 );
3716                                                 nodes[i].dispatchEvent(event);
3717                                                 break;
3718                                         }
3719                                 }
3720                                 break;
3721                 }
3722         },
3723         preventToSendClickEvent : false,
3724  
3725         start : function(aEvent, aX, aY) 
3726         {
3727                 this.active = true;
3728                 this.trace(aEvent, aX, aY);
3729         },
3730  
3731         finish : function(aEvent) 
3732         {
3733                 if (!this.active) return;
3734                 this.trace(aEvent);
3735                 this.finishStroke();
3736         },
3737  
3738         trace : function(aEvent, aX, aY) 
3739         {
3740                 if (!this.active) return;
3741                 this.addPoint((aX === void(0) ? aEvent.clientX : aX ), (aY === void(0) ? aEvent.clientY : aY ));
3742         },
3743  
3744         finishStroke : function() 
3745         {
3746                 this.active = false;
3747                 this.lastX = -1;
3748                 this.lastY = -1;
3749         },
3750  
3751         addPoint : function(aX, aY) 
3752         {
3753                 if (this.lastX != -1)
3754                         this.drawLine(this.lastX, this.lastY, aX, aY);
3755                 else
3756                         this.drawDot(aX, aY);
3757
3758                 this.lastX = aX;
3759                 this.lastY = aY;
3760         },
3761  
3762         clear : function() 
3763         {
3764                 this.active = false;
3765                 this.lastX = -1;
3766                 this.lastY = -1;
3767
3768                 if (this.mode == 'canvas') {
3769                         if (this.canvasElement.lastWindowWidth != window.innerWidth ||
3770                                 this.canvasElement.lastWindowHeight != window.innerHeight) {
3771                                 this.canvasElement.width  = this.canvasElement.parentNode.boxObject.width-2;
3772                                 this.canvasElement.height = this.canvasElement.parentNode.boxObject.height-2;
3773
3774                                 this.canvasElement.lastWindowWidth  = window.innerWidth;
3775                                 this.canvasElement.lastWindowHeight = window.innerHeight;
3776                         }
3777                         this.canvasContext.clearRect(0, 0, this.canvasElement.width, this.canvasElement.height);
3778                         this.canvasContext.strokeStyle = this.lineColor;
3779                         this.canvasContext.lineWidth   = this.lineWidth;
3780                 }
3781                 else {
3782                         var dotes = this.canvasElement.getElementsByAttribute('class', this.className);
3783                         if (!dotes.length) return;
3784
3785                         var range = document.createRange();
3786                         range.selectNodeContents(this.canvasElement);
3787                         range.setStartBefore(dotes[0]);
3788                         range.setEndAfter(dotes[dotes.length-1]);
3789                         range.deleteContents();
3790                         range.detach();
3791                 }
3792         },
3793  
3794         drawDot : function(aX, aY, aParent) 
3795         {
3796                 if (this.mode == 'canvas') {
3797                         this.canvasContext.strokeRect(aX, aY, 0, 0);
3798                         this.canvasContext.stroke();
3799                 }
3800                 else {
3801                         var dot = document.createElement('spacer');
3802                         dot.setAttribute('style', 'left:'+aX+'px; top:'+aY+'px');
3803                         dot.setAttribute('class', this.className);
3804                         (aParent || this.canvasElement).appendChild(dot);
3805                 }
3806         },
3807  
3808         drawLine : function(aX1, aY1, aX2, aY2) 
3809         {
3810                 if (aX1 == aX2 && aY1 == aY2) return;
3811
3812
3813                 if (this.mode == 'canvas') {
3814                         this.canvasContext.beginPath();
3815                         this.canvasContext.moveTo(aX1, aY1);
3816                         this.canvasContext.lineTo(aX2, aY2);
3817 /*
3818                         this.canvasContext.bezierCurveTo(
3819                                 parseInt(aX1+((aX2-this.lastX)*0.3)), parseInt(aY1+((aY2-this.lastY)*0.3)),
3820                                 parseInt(aX1+((aX2-this.lastX)*0.6)), parseInt(aY1+((aY2-this.lastY)*0.6)),
3821                                 aX2, aY2
3822                         );
3823 */
3824                         this.canvasContext.closePath();
3825                         this.canvasContext.stroke();
3826                 }
3827                 else {
3828                         var x_move = aX2 - aX1;
3829                         var y_move = aY2 - aY1;
3830                         var x_diff = x_move < 0 ? 1 : -1;
3831                         var y_diff = y_move < 0 ? 1 : -1;
3832
3833                         var fragment = document.createDocumentFragment();
3834                         if (Math.abs(x_move) >= Math.abs(y_move)) {
3835                                 for (var i = x_move; i != 0; i += x_diff)
3836                                         this.drawDot(aX2 - i, aY2 - Math.round(y_move * i / x_move), fragment);
3837                         }
3838                         else {
3839                                 for (var i = y_move; i != 0; i += y_diff)
3840                                         this.drawDot(aX2 - Math.round(x_move * i / y_move), aY2 - i, fragment);
3841                         }
3842                         this.canvasElement.appendChild(fragment);
3843                 }
3844         }
3845  
3846 }; 
3847   
3848 var StrokablePresentationService = { 
3849         
3850         id : 'stroke-canvas-box', 
3851
3852         strokeService         : null,
3853         cliclableNodesManager : null,
3854         canvasContainer       : null,
3855         canvas                : null,
3856
3857         autoStart : false,
3858  
3859         init : function(aPresentation, aStrokeService) 
3860         {
3861                 this.cliclableNodesManager = aPresentation;
3862                 this.strokeService         = aStrokeService;
3863                 this.canvasContainer       = document.getElementById('canvas').firstChild;
3864                 this.check = document.getElementById('penButton');
3865
3866                 document.documentElement.addEventListener('StartDragOnCanvas', this, false);
3867                 document.documentElement.addEventListener('PresentationRedraw', this, false);
3868         },
3869  
3870         toggle : function(aEnable) 
3871         {
3872                 if (aEnable)
3873                         this.start();
3874                 else
3875                         this.end();
3876         },
3877  
3878         start : function() 
3879         {
3880                 if (!this.strokeService || !this.canvasContainer) return;
3881
3882                 this.strokeService.cliclableNodesManager = this.cliclableNodesManager;
3883                 var box = document.createElement('vbox');
3884                 box.setAttribute('flex', 1);
3885                 box.setAttribute('id', this.id);
3886                 box.style.width = this.canvasContainer.boxObject.width+'px';
3887                 box.style.height = this.canvasContainer.boxObject.height+'px';
3888                 this.canvas = this.canvasContainer.appendChild(box);
3889                 this.strokeService.init(this.canvas);
3890
3891                 this.canvas.addEventListener('dblclick', this, false);
3892         },
3893  
3894         end : function() 
3895         {
3896                 this.strokeService.destroy();
3897                 if (this.canvas) {
3898                         this.canvas.removeEventListener('dblclick', this, false);
3899                         this.canvasContainer.removeChild(this.canvas);
3900                         this.canvas = null;
3901                 }
3902         },
3903  
3904         handleEvent : function(aEvent) 
3905         {
3906                 switch (aEvent.type)
3907                 {
3908                         default:
3909                                 break;
3910
3911                         case 'StartDragOnCanvas':
3912                                 if (!this.check.checked) {
3913                                         this.toggleCheck();
3914                                         this.strokeService.startX = Presentation.dragStartX;
3915                                         this.strokeService.startY = Presentation.dragStartY;
3916
3917                                         this.autoStart = true;
3918                                 }
3919                                 break;
3920
3921                         case 'PresentationRedraw':
3922                                 if (this.autoStart && this.check.checked) {
3923                                         this.autoStart = false;
3924                                         this.toggleCheck();
3925                                 }
3926                                 break;
3927
3928                         case 'dblclick':
3929                                 if (this.canvas)
3930                                         this.end();
3931                                 break;
3932                 }
3933         },
3934  
3935         toggleCheck : function() 
3936         {
3937                 var enable = !this.check.checked;
3938                 this.toggle(enable);
3939                 this.check.checked = enable;
3940
3941                 this.autoStart = false;
3942         }
3943  
3944 }; 
3945   
3946 function init() 
3947 {
3948         window.removeEventListener('load', init, false);
3949
3950         Presentation.init();
3951         StrokablePresentationService.init(Presentation, StrokeService);
3952 }
3953 window.addEventListener('load', init, false);
3954  
3955 function getNodesByXPath(aExpression, aContext, aLive) 
3956 {
3957         var d = aContext.ownerDocument || aContext;
3958         var type = aLive ? XPathResult.ORDERED_NODE_ITERATOR_TYPE : XPathResult.ORDERED_NODE_SNAPSHOT_TYPE ;
3959         var nodes;
3960         try {
3961                 nodes = d.evaluate(aExpression, aContext, null, type, null);
3962         }
3963         catch(e) {
3964                 nodes = document.evaluate(aExpression, aContext, null, type, null);
3965         }
3966         return nodes;
3967 }
3968  
3969 // Import Node from E4X to DOM 
3970 // http://ecmanaut.blogspot.com/2006/03/e4x-and-dom.html
3971
3972 function importE4XNode( e4x, doc, aDefaultNS )
3973 {
3974   aDefaultNS = aDefaultNS || XHTMLNS;
3975   var root, domTree, importMe;
3976   this.Const = this.Const || { mimeType: 'text/xml' };
3977   this.Static = this.Static || {};
3978   this.Static.parser = this.Static.parser || new DOMParser;
3979   eval('root = <testing xmlns="'+aDefaultNS+'" />;');
3980   root.test = e4x;
3981   domTree = this.Static.parser.parseFromString( root.toXMLString(),
3982            this.Const.mimeType );
3983   importMe = domTree.documentElement.firstChild;
3984   while( importMe && importMe.nodeType != 1 )
3985     importMe = importMe.nextSibling;
3986   if( !doc ) doc = document;
3987   return importMe ? doc.importNode( importMe, true ) : null;
3988 }
3989
3990 function appendE4XTo( e4x, node, doc, aDefaultNS )
3991 {
3992   return node.appendChild( importE4XNode( e4x, (doc || node.ownerDocument), aDefaultNS ) );
3993 }
3994
3995 function setE4XContent( e4x, node, aDefaultNS )
3996 {
3997   while( node.firstChild )
3998     node.removeChild( node.firstChild );
3999   appendE4XTo( e4x, node, aDefaultNS );
4000 }
4001
4002 // importE4XNodeで得たノードツリーを埋め込むと、XULでバインディングが適用されないことがある。
4003 // 遅延処理でこの問題を一部避けることができる(が、これでもまだダメな場合がある。menuとか。)
4004 // とりあえずXULとSVGとXHTMLはいけた。MathMLはダメだった。
4005 function importNodeTreeWithDelay(aNode, aParent, aDefaultNS, aFromTimeout)
4006 {
4007         if (aFromTimeout) {
4008                 importNodeTreeWithDelayTimers--;
4009         }
4010
4011         var node;
4012         var delay = 1;
4013         switch (aNode.nodeType)
4014         {
4015                 case Node.ELEMENT_NODE:
4016                         var ns = (aNode.namespaceURI || aDefaultNS);
4017                         node = document.createElementNS(ns, aNode.localName);
4018                         aParent.appendChild(node);
4019
4020                         var attr = aNode.attributes;
4021                         for (var i = 0, maxi = attr.length; i < maxi; i++)
4022                                 node.setAttribute(attr[i].name, attr[i].value);
4023
4024                         if (ns == XULNS) delay = 1; else delay = 0;
4025
4026                         var children = aNode.childNodes;
4027                         for (var i = 0, maxi = children.length; i < maxi; i++)
4028                                 if (delay) {
4029                                         importNodeTreeWithDelayTimers++;
4030                                         window.setTimeout(importNodeTreeWithDelay, delay, children[i], node, aDefaultNS, true);
4031                                 }
4032                                 else
4033                                         importNodeTreeWithDelay(children[i], node, aDefaultNS);
4034                         break;
4035
4036                 default:
4037                         if (
4038                                 aNode.nodeType == Node.TEXT_NODE &&
4039                                 /^\s*$/.test(aNode.nodeValue) &&
4040                                 (aNode.parentNode.namespaceURI || aDefaultNS) != XHTMLNS
4041                                 )
4042                                 return;
4043                         node = aParent.appendChild(aNode.cloneNode(true));
4044                         break;
4045         }
4046
4047         var event = document.createEvent('Events');
4048         event.initEvent('CanvasContentAdded', true, true);
4049         node.dispatchEvent(event);
4050
4051         return node;
4052 }
4053 var importNodeTreeWithDelayTimers = 0;
4054  
4055 ]]></script> 
4056   
4057 </page> 
4058