/[suikacvs]/webroot/www/js/jste/tutorial.js
Suika

Contents of /webroot/www/js/jste/tutorial.js

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.22 - (hide annotations) (download) (as text)
Sun Feb 1 06:40:10 2009 UTC (16 years, 6 months ago) by wakaba
Branch: MAIN
Changes since 1.21: +84 -8 lines
File MIME type: application/javascript
Implemented the save-state feature

1 wakaba 1.1
2     if (typeof (JSTE) === "undefined") var JSTE = {};
3    
4     JSTE.WATNS = 'http://suika.fam.cx/ns/wat';
5     JSTE.SpaceChars = /[\x09\x0A\x0C\x0D\x20]+/;
6    
7     JSTE.Class = function (constructor, prototype) {
8     return JSTE.Subclass (constructor, JSTE.EventTarget, prototype);
9     }; // Class
10    
11     JSTE.Class.addClassMethods = function (classObject, methods) {
12     new JSTE.Hash (methods).forEach (function (n, v) {
13     if (!classObject[n]) {
14     classObject[n] = v;
15     }
16     });
17     }; // addClassMethods
18    
19     JSTE.Subclass = function (constructor, superclass, prototype) {
20     constructor.prototype = new superclass;
21     for (var n in prototype) {
22     constructor.prototype[n] = prototype[n];
23     }
24     constructor.prototype.constructor = constructor;
25     constructor.prototype._super = superclass;
26     return constructor;
27     }; // Subclass
28    
29     JSTE.EventTarget = new JSTE.Subclass (function () {
30    
31     }, function () {}, {
32     addEventListener: function (eventType, handler, useCapture) {
33     if (useCapture) return;
34     if (!this.eventListeners) this.eventListeners = {};
35     if (!this.eventListeners[eventType]) {
36     this.eventListeners[eventType] = new JSTE.List;
37     }
38     this.eventListeners[eventType].push (handler);
39     }, // addEventListener
40     removeEventListener: function (eventType, handler, useCapture) {
41     if (useCapture) return;
42     if (!this.eventListeners) return;
43     if (!this.eventListeners[eventType]) return;
44     this.eventListeners[eventType].remove (handler);
45     }, // removeEventListener
46     dispatchEvent: function (e) {
47     if (!this.eventListeners) return;
48     var handlers = this.eventListeners[e.type];
49     if (!handlers) return;
50     e.currentTarget = this;
51     e.target = this;
52     var preventDefault;
53     handlers.forEach (function (handler) {
54     if (handler.apply (this, [e])) {
55     preventDefault = true;
56     }
57     });
58     return preventDefault || e.isDefaultPrevented ();
59     } // dispatchEvent
60     }); // EventTarget
61    
62     JSTE.Event = new JSTE.Class (function (eventType, canBubble, cancelable) {
63     this.type = eventType;
64     this.bubbles = canBubble;
65     this.cancelable = cancelable;
66     }, {
67     preventDefault: function () {
68     this.defaultPrevented = true;
69     }, // preventDefault
70     isDefaultPrevented: function () {
71     return this.defaultPrevented;
72     } // isDefaultPrevented
73     });
74    
75     JSTE.Observer = new JSTE.Class (function (eventType, target, onevent) {
76     this.eventType = eventType;
77 wakaba 1.10 this.target = target;
78 wakaba 1.1 if (target.addEventListener) {
79     this.code = onevent;
80     } else if (target.attachEvent) {
81     this.code = function () {
82     onevent (event);
83     };
84 wakaba 1.10 } else {
85     this.code = onevent;
86 wakaba 1.1 }
87 wakaba 1.10 this.disabled = true;
88     this.start ();
89 wakaba 1.1 }, {
90 wakaba 1.10 start: function () {
91     if (!this.disabled) return;
92     if (this.target.addEventListener) {
93     this.target.addEventListener (this.eventType, this.code, false);
94     this.disabled = false;
95     } else if (this.target.attachEvent) {
96     this.target.attachEvent ("on" + this.eventType, this.code);
97     this.disabled = false;
98     }
99     }, // start
100 wakaba 1.1 stop: function () {
101 wakaba 1.10 if (this.disabled) return;
102 wakaba 1.1 if (this.target.removeEventListener) {
103     this.target.removeEventListener (this.eventType, this.code, false);
104 wakaba 1.10 this.disabled = true;
105 wakaba 1.11 } else if (this.target.detachEvent) {
106 wakaba 1.1 this.target.detachEvent ("on" + this.eventType, this.code);
107 wakaba 1.10 this.disabled = true;
108 wakaba 1.1 }
109     } // stop
110     }); // Observer
111    
112 wakaba 1.5 new JSTE.Observer ('load', window, function () {
113     JSTE.windowLoaded = true;
114     });
115    
116 wakaba 1.10
117     JSTE.Hash = new JSTE.Class (function (hash) {
118     this.hash = hash || {};
119     }, {
120     forEach: function (code) {
121     for (var n in this.hash) {
122     var r = code (n, this.hash[n]);
123     if (r && r.stop) break;
124     }
125     }, // forEach
126     clone: function (code) {
127     var newHash = {};
128     this.forEach (function (n, v) {
129     newHash[n] = v;
130     });
131     return new this.constructor (newHash);
132     }, // clone
133    
134     getNamedItem: function (n) {
135     return this.hash[n];
136     }, // getNamedItem
137     setNamedItem: function (n, v) {
138     return this.hash[n] = v;
139 wakaba 1.22 }, // setNamedItem
140    
141     getByNames: function (names) {
142     var self = this;
143     return names.forEach (function (name) {
144     var value = self.getNamedItem (name);
145     if (value != null) {
146     return new JSTE.List.Return (value);
147     } else {
148     return null;
149     }
150     });
151     } // getByNames
152     }); // Hash
153 wakaba 1.10
154    
155 wakaba 1.1 JSTE.List = new JSTE.Class (function (arrayLike) {
156     this.list = arrayLike || [];
157     }, {
158     getLast: function () {
159     if (this.list.length) {
160     return this.list[this.list.length - 1];
161     } else {
162     return null;
163     }
164     }, // getLast
165    
166     forEach: function (code) {
167     var length = this.list.length;
168     for (var i = 0; i < length; i++) {
169     var r = code (this.list[i]);
170     if (r && r.stop) return r.returnValue;
171     }
172     return null;
173     }, // forEach
174 wakaba 1.4
175     numberToInteger: function () {
176     var newList = [];
177     this.forEach (function (item) {
178     if (typeof item === "number") {
179     newList.push (Math.floor (item));
180     } else {
181     newList.push (item);
182     }
183     });
184     return new this.constructor (newList);
185     }, // numberToInteger
186 wakaba 1.1
187     clone: function () {
188     var newList = [];
189     this.forEach (function (item) {
190     newList.push (item);
191     });
192     return new this.constructor (newList);
193     }, // clone
194    
195     grep: function (code) {
196     var newList = [];
197     this.forEach (function (item) {
198     if (code (item)) {
199     newList.push (item);
200     }
201     });
202     return new this.constructor (newList);
203     }, // grep
204     onlyNonNull: function () {
205     return this.grep (function (item) {
206     return item != null; /* Intentionally "!=" */
207     });
208     }, // onlyNonNull
209 wakaba 1.11
210     uniq: function (eq) {
211     if (!eq) eq = function (i1, i2) { return i1 === i2 };
212     var prevItems = [];
213     return this.grep (function (item) {
214     for (var i = 0; i < prevItems.length; i++) {
215     if (eq (item, prevItems[i])) {
216     return false;
217     }
218     }
219     prevItems.push (item);
220     return true;
221     });
222     }, // uniq
223 wakaba 1.1
224     getFirstMatch: function (code) {
225     return this.forEach (function (item) {
226     if (code (item)) {
227     return new JSTE.List.Return (item);
228     }
229     });
230     }, // getFirstMatch
231    
232     switchByElementType: function () {
233     var cases = new JSTE.List (arguments);
234     this.forEach (function (n) {
235     cases.forEach (function (c) {
236     if (c.namespaceURI == n.namespaceURI) {
237     return new JSTE.List.Return (c.execute (n));
238     }
239     });
240     });
241     }, // switchByElementType
242    
243 wakaba 1.10 // destructive
244 wakaba 1.1 push: function (item) {
245     this.list.push (item);
246     }, // push
247 wakaba 1.10
248     // destructive
249 wakaba 1.1 pushCloneOfLast: function () {
250     this.list.push (this.getLast ().clone ());
251     }, // pushCloneOfLast
252 wakaba 1.10
253     // destructive
254 wakaba 1.1 append: function (list) {
255     var self = this;
256     list.forEach (function (n) {
257     self.list.push (n);
258     });
259 wakaba 1.10 return this;
260 wakaba 1.1 }, // append
261    
262 wakaba 1.10 // destructive
263 wakaba 1.1 pop: function () {
264     return this.list.pop ();
265     }, // pop
266 wakaba 1.10
267     // destructive
268 wakaba 1.1 remove: function (removedItem) {
269     var length = this.list.length;
270     for (var i = length - 1; i >= 0; --i) {
271     var item = this.list[i];
272     if (item == removedItem) { // Intentionally "=="
273     this.list.splice (i, 1);
274     }
275     }
276     }, // remove
277 wakaba 1.10
278     // destructive
279 wakaba 1.1 clear: function () {
280     this.list.splice (0, this.list.length);
281     } // clear
282    
283     }); // List
284    
285 wakaba 1.10 JSTE.Class.addClassMethods (JSTE.List, {
286     spaceSeparated: function (v) {
287     return new JSTE.List ((v || '').split (JSTE.SpaceChars)).grep (function (v) {
288     return v.length;
289     });
290     }, // spaceSeparated
291    
292     getCommonItems: function (l1, l2, cb, eq) {
293 wakaba 1.11 if (!eq) eq = function (i1, i2) { return i1 === i2 };
294 wakaba 1.10
295     var common = new JSTE.List;
296    
297     l1 = l1.grep (function (i1) {
298     var hasI1;
299     l2 = l2.grep (function (i2) {
300     if (eq (i1, i2)) {
301     common.push (i1);
302     hasI1 = true;
303     return false;
304     } else {
305     return true;
306     }
307     });
308     return !hasI1;
309     });
310    
311     cb (common, l1, l2);
312     } // getCommonItems
313     }); // List class methods
314    
315 wakaba 1.1 JSTE.List.Return = new JSTE.Class (function (rv) {
316     this.stop = true;
317     this.returnValue = rv;
318     }, {
319    
320     }); // Return
321    
322     JSTE.List.SwitchByLocalName = new JSTE.Class (function (ns, cases, ow) {
323     this.namespaceURI = ns;
324     this.cases = cases;
325     this.otherwise = ow || function (n) { };
326     }, {
327     execute: function (n) {
328     for (var ln in this.cases) {
329     if (JSTE.Element.matchLocalName (n, ln)) {
330     return this.cases[ln] (n);
331     }
332     }
333     return this.otherwise (n);
334     }
335     });
336    
337    
338     if (!JSTE.Node) JSTE.Node = {};
339    
340     JSTE.Class.addClassMethods (JSTE.Node, {
341     querySelector: function (node, selectors) {
342     if (node.querySelector) {
343     var el;
344     try {
345     el = node.querySelector (selectors);
346     } catch (e) {
347     el = null;
348     }
349     return el;
350     } else if (window.uu && uu.css) {
351     if (selectors != "") {
352     /* NOTE: uu.css return all elements for "" or ",xxx" */
353     return uu.css (selectors, node)[0];
354     } else {
355     return null;
356     }
357     } else if (window.Ten && Ten.DOM && Ten.DOM.getElementsBySelector) {
358     return Ten.DOM.getElementsBySelector (selectors)[0];
359     } else {
360     return null;
361     }
362     }, // querySelector
363     querySelectorAll: function (node, selectors) {
364     if (node.querySelectorAll) {
365     var nl;
366     try {
367     nl = node.querySelectorAll (selectors);
368     } catch (e) {
369     nl = null;
370     }
371     return new JSTE.List (nl);
372     } else if (window.uu && uu.css) {
373     if (selectors != "") {
374 wakaba 1.11 /* NOTE: uu.css return all elements for "" or ",xxx". */
375 wakaba 1.1 return new JSTE.List (uu.css (selectors, node));
376     } else {
377     return new JSTE.List;
378     }
379     } else if (window.Ten && Ten.DOM && Ten.DOM.getElementsBySelector) {
380     return new JSTE.List (Ten.DOM.getElementsBySelector (selectors));
381     } else {
382     return new JSTE.List;
383     }
384     } // querySelectorAll
385     });
386    
387     if (!JSTE.Element) JSTE.Element = {};
388    
389     JSTE.Class.addClassMethods (JSTE.Element, {
390     getLocalName: function (el) {
391     var localName = el.localName;
392     if (!localName) {
393     localName = el.nodeName;
394     if (el.prefix) {
395     localName = localName.substring (el.prefix.length + 1);
396     }
397     }
398     return localName;
399     }, // getLocalName
400    
401     match: function (el, ns, ln) {
402     if (el.nodeType !== 1) return false;
403     if (el.namespaceURI !== ns) return false;
404     return JSTE.Element.matchLocalName (el, ln);
405     }, // match
406     matchLocalName: function (el, ln) {
407     var localName = JSTE.Element.getLocalName (el);
408     if (ln instanceof RegExp) {
409     if (!localName.match (ln)) return false;
410     } else {
411     if (localName !== ln) return false;
412     }
413     return true;
414     }, // matchLocalName
415    
416     getChildElement: function (el, ns, ln) {
417     return new JSTE.List (el.childNodes).getFirstMatch (function (item) {
418     return JSTE.Element.match (item, ns, ln);
419     });
420     }, // getChildElement
421     getChildElements: function (el, ns, ln) {
422     return new JSTE.List (el.childNodes).grep (function (item) {
423     return JSTE.Element.match (item, ns, ln);
424     });
425     }, // getChildElements
426 wakaba 1.17 getChildrenClassifiedByType: function (el) {
427     var r = new JSTE.ElementHash;
428     new JSTE.List (el.childNodes).forEach (function (n) {
429     if (n.nodeType == 1) {
430     r.getOrCreate (n.namespaceURI, JSTE.Element.getLocalName (n)).push (n);
431     } else {
432     r.getOrCreate (null, n.nodeType).push (n);
433     }
434     });
435     return r;
436     }, // getChildrenClassifiedByType
437 wakaba 1.18
438     isEmpty: function (el) {
439     // HTML5 definition of "empty"
440     return !new JSTE.List (el.childNodes).forEach (function (n) {
441     var nt = n.nodeType;
442     if (nt == 1) {
443     return new JSTE.List.Return (true /* not empty */);
444     } else if (nt == 3 || nt == 4) {
445     if (/[^\u0009\u000A\u000C\u000D\u0020]/.test (n.data)) {
446     return new JSTE.List.Return (true /* not empty */);
447     }
448     } else if (nt == 7 || nt == 8) { // comment/pi
449     // does not affect emptyness
450     } else {
451     // We don't support EntityReference.
452     return new JSTE.List.Return (true /* not empty */);
453     }
454     });
455     }, // isEmpty
456 wakaba 1.1
457     appendText: function (el, s) {
458     return el.appendChild (el.ownerDocument.createTextNode (s));
459     }, // appendText
460    
461     createTemplate: function (doc, node) {
462     var df = doc.createDocumentFragment ();
463     new JSTE.List (node.childNodes).forEach (function (n) {
464     if (n.nodeType == 1) {
465     var c = doc.createElement (JSTE.Element.getLocalName (n));
466 wakaba 1.15 new JSTE.List (n.attributes).forEach (function (n) {
467 wakaba 1.1 c.setAttribute (n.name, n.value);
468     });
469     c.appendChild (JSTE.Element.createTemplate (doc, n));
470     df.appendChild (c);
471     } else if (n.nodeType == 3 || n.nodeType == 4) {
472     df.appendChild (doc.createTextNode (n.data));
473     }
474     });
475     return df;
476     }, // createTemplate
477    
478     hasAttribute: function (el, localName) {
479     if (el.hasAttribute) {
480     return el.hasAttribute (localName);
481     } else {
482     return el.getAttribute (localName) != null;
483     }
484     }, // hasAttribute
485    
486     getClassNames: function (el) {
487     return new JSTE.List (el.className.split (JSTE.SpaceChars));
488     }, // getClassNames
489     addClassName: function (el, newClassName) {
490     el.className = el.className + ' ' + newClassName;
491     }, // deleteClassName
492     deleteClassName: function (el, oldClassName) {
493     var classNames = el.className.split (JSTE.SpaceChars);
494     var newClasses = [];
495     for (var n in classNames) {
496     if (classNames[n] != oldClassName) {
497     newClasses.push (classNames[n]);
498     }
499     }
500     el.className = newClasses.join (' ');
501     }, // deleteClassName
502     replaceClassName: function (el, oldClassName, newClassName) {
503     var classNames = el.className.split (JSTE.SpaceChars);
504     var newClasses = [newClassName];
505     for (var n in classNames) {
506     if (classNames[n] != oldClassName) {
507     newClasses.push (classNames[n]);
508     }
509     }
510     el.className = newClasses.join (' ');
511     }, // replaceClassName
512    
513     getIds: function (el) {
514     return new JSTE.List (el.id != "" ? [el.id] : []);
515 wakaba 1.5 }, // getIds
516    
517     /*
518     NR.js <http://suika.fam.cx/www/css/noderect/NodeRect.js> must be loaded
519     before the invocation.
520     */
521     scroll: function (elements) {
522     if (!JSTE.windowLoaded) {
523     new JSTE.Observer ('load', window, function () {
524     JSTE.Element.scroll (elements);
525     });
526     return;
527     }
528    
529     var top = Infinity;
530     var left = Infinity;
531     var topEl;
532     var leftEl;
533     elements.forEach (function (el) {
534     var rect = NR.Element.getRects (el, window).borderBox;
535     if (rect.top < top) {
536     top = rect.top;
537     topEl = el;
538     }
539     if (rect.left < left) {
540     left = rect.left;
541     leftEl = el;
542     }
543     });
544    
545     if (!leftEl && !topEl) {
546     return;
547     }
548    
549     var doc = (leftEl || topEl).ownerDocument;
550 wakaba 1.1
551 wakaba 1.5 var rect = NR.View.getViewportRects (window, doc).contentBox;
552     if (rect.top <= top && top <= rect.bottom) {
553     top = rect.top;
554     }
555     if (rect.left <= left && left <= rect.right) {
556     left = rect.left;
557     }
558    
559     /*
560     Set scroll* of both <html> and <body> elements, to support all of
561     four browsers and two (or three) rendering modes. This might result
562     in confusing the user if both <html> and <body> elements have their
563     'overflow' properties specified to 'scroll'.
564    
565     Note that this code does not do a good job if the |el| is within an
566     |overflow: scroll| element.
567     */
568     doc.body.scrollTop = top;
569     doc.body.scrollLeft = left;
570     doc.documentElement.scrollTop = top;
571     doc.documentElement.scrollLeft = left;
572     } // scroll
573 wakaba 1.18 }); // Element
574 wakaba 1.1
575 wakaba 1.17 JSTE.ElementHash = new JSTE.Class (function () {
576     this.items = [];
577     }, {
578     get: function (ns, ln) {
579     ns = ns || '';
580     if (this.items[ns]) {
581     return this.items[ns].getNamedItem (ln) || new JSTE.List;
582     } else {
583     return new JSTE.List;
584     }
585     }, // get
586     getOrCreate: function (ns, ln) {
587     ns = ns || '';
588     if (this.items[ns]) {
589     var l = this.items[ns].getNamedItem (ln);
590     if (!l) this.items[ns].setNamedItem (ln, l = new JSTE.List);
591     return l;
592     } else {
593     var l;
594     this.items[ns] = new JSTE.Hash;
595     this.items[ns].setNamedItem (ln, l = new JSTE.List);
596     return l;
597     }
598     } // getOrCreate
599     }); // ElementHash
600    
601 wakaba 1.9 JSTE.XHR = new JSTE.Class (function (url, onsuccess, onerror) {
602     try {
603     this._xhr = new XMLHttpRequest ();
604     } catch (e) {
605     try {
606     this._xhr = new ActiveXObject ('Msxml2.XMLHTTP');
607     } catch (e) {
608     try {
609     this._xhr = new ActiveXObject ('Microsoft.XMLHTTP');
610     } catch (e) {
611     try {
612     this._xhr = new ActiveXObject ('Msxml2.XMLHTTP.4.0');
613     } catch (e) {
614     this._xhr = null;
615     }
616     }
617     }
618     }
619    
620     this._url = url;
621     this._onsuccess = onsuccess || function () { };
622     this._onerror = onerror || function () { };
623     }, {
624     get: function () {
625     if (!this._xhr) return;
626    
627     var self = this;
628     this._xhr.open ('GET', this._url, true);
629     this._xhr.onreadystatechange = function () {
630     self._onreadystatechange ();
631     }; // onreadystatechange
632     this._xhr.send (null);
633     }, // get
634    
635     _onreadystatechange: function () {
636     if (this._xhr.readyState == 4) {
637     if (this.succeeded ()) {
638     this._onsuccess ();
639     } else {
640     this._onerror ();
641     }
642     }
643     }, // _onreadystatechange
644    
645     succeeded: function () {
646     return (this._xhr.status < 400);
647     }, // succeeded
648    
649     getDocument: function () {
650     return this._xhr.responseXML;
651     } // getDocument
652     }); // XHR
653    
654 wakaba 1.22 // An abstract class
655     JSTE.Storage = new JSTE.Class (function () {
656    
657     }, {
658    
659     }); // Storage
660    
661     JSTE.Storage.PageLocal = new JSTE.Subclass (function () {
662    
663     }, JSTE.Storage, {
664     get: function (name) {
665     return this['value-' + name];
666     }, // get
667     set: function (name, value) {
668     this['value-' + name] = value;
669     }, // set
670    
671     getNames: function () {
672     var names = new JSTE.List;
673     for (var n in this) {
674     if (/^value-/.test (n)) {
675     names.push (n.substring (6));
676     }
677     }
678     return names;
679     } // getNames
680     });
681    
682 wakaba 1.9
683 wakaba 1.1 /* Events: load, close, shown, hidden */
684 wakaba 1.18 JSTE.Message = new JSTE.Class (function (doc, template, commandTarget, availCommands) {
685 wakaba 1.1 if (!doc) return;
686     this._targetDocument = doc;
687     this._template = template || doc.createDocumentFragment ();
688 wakaba 1.8
689 wakaba 1.1 this._commandTarget = commandTarget;
690 wakaba 1.18 this._availCommands = availCommands || new JSTE.List;
691 wakaba 1.8
692 wakaba 1.1 this.hidden = true;
693     this.select = "";
694    
695     var e = new JSTE.Event ('load');
696     this.dispatchEvent (e);
697     }, {
698     render: function () {
699     var self = this;
700     var doc = this._targetDocument;
701    
702     var msgContainer = doc.createElement ('section');
703     msgContainer.appendChild (this._template);
704 wakaba 1.20
705     if (!this._availCommands.list.length) {
706 wakaba 1.8 this._availCommands.push ({name: 'back'});
707     this._availCommands.push ({name: 'next'});
708 wakaba 1.7 }
709 wakaba 1.20
710     this._availCommands = this._availCommands.grep(function (item) {
711     return self._commandTarget.canExecuteCommand (item.name, item.args);
712     });
713 wakaba 1.1
714 wakaba 1.8 this._outermostElement = this._render (msgContainer);
715 wakaba 1.1
716     this.show ();
717     }, // render
718     _render: function (msgContainer, buttonContainer) {
719     var doc = this._targetDocument;
720    
721     var container = doc.createElement ('article');
722    
723     container.appendChild (msgContainer);
724 wakaba 1.8
725     var buttonContainer = this.createCommandButtons ();
726 wakaba 1.1 container.appendChild (buttonContainer);
727 wakaba 1.8
728 wakaba 1.1 doc.documentElement.appendChild (container);
729    
730     return container;
731     }, // _render
732 wakaba 1.8 createCommandButtons: function () {
733     var self = this;
734 wakaba 1.18 var doc = this._targetDocument;
735     var buttonContainer = doc.createElement ('menu');
736 wakaba 1.8 this._availCommands.forEach (function (cmd) {
737 wakaba 1.18 var label = cmd.name;
738     if (cmd.labelTemplate) {
739     label = JSTE.Element.createTemplate (doc, cmd.labelTemplate);
740     }
741    
742 wakaba 1.8 var button = new JSTE.Message.Button
743 wakaba 1.18 (label, self._commandTarget, cmd.name, cmd.args);
744 wakaba 1.8 buttonContainer.appendChild (button.element);
745     });
746     return buttonContainer;
747     }, // createCommandButtons
748    
749 wakaba 1.1 remove: function () {
750     this.hide ();
751    
752     this._remove ();
753    
754     if (this._outermostElement && this._outermostElement.parentNode) {
755     this._outermostElement.parentNode.removeChild (this._outermostElement);
756     }
757    
758     var e = new JSTE.Event ("close");
759     this.dispatchEvent (e);
760     }, // remove
761     _remove: function () {
762    
763     }, // remove
764    
765     show: function () {
766     if (!this.hidden) return;
767     this.hidden = false;
768     if (this._outermostElement) {
769     JSTE.Element.replaceClassName
770     (this._outermostElement, "jste-hidden", "jste-shown");
771     }
772    
773     var e = new JSTE.Event ("shown");
774     this.dispatchEvent (e);
775     }, // show
776     hide: function () {
777     if (this.hidden) return;
778     this.hidden = true;
779     if (this._outermostElement) {
780     JSTE.Element.replaceClassName
781     (this._outermostElement, "jste-shown", "jste-hidden");
782     }
783    
784     var e = new JSTE.Event ("hidden");
785     this.dispatchEvent (e);
786     }, // hide
787    
788     setTimeout: function () {
789     /* TODO: ... */
790    
791     }
792    
793     }); // Message
794    
795 wakaba 1.7 /* TODO: button label text should refer message catalog */
796    
797 wakaba 1.1 JSTE.Message.Button =
798 wakaba 1.18 new JSTE.Class (function (label, commandTarget, commandName, commandArgs) {
799     this._label = label != null ? label : "";
800 wakaba 1.6
801 wakaba 1.1 if (commandTarget && commandTarget instanceof Function) {
802     this._command = commandTarget;
803 wakaba 1.6 this._classNames = new JSTE.List;
804 wakaba 1.1 } else if (commandTarget) {
805     this._command = function () {
806     return commandTarget.executeCommand.apply
807     (commandTarget, [commandName, commandArgs]);
808     };
809 wakaba 1.6 this._classNames = new JSTE.List (['jste-command-' + commandName]);
810 wakaba 1.1 } else {
811     this._command = function () { };
812 wakaba 1.6 this._classNames = new JSTE.List;
813 wakaba 1.1 }
814    
815 wakaba 1.6 this._createElement ();
816 wakaba 1.1 }, {
817 wakaba 1.6 _createElement: function () {
818     try {
819     this.element = document.createElement ('button');
820     this.element.setAttribute ('type', 'button');
821     } catch (e) {
822     this.element = document.createElement ('<button type=button>');
823     }
824 wakaba 1.18 if (this._label.nodeType) {
825     this.element.appendChild (this._label);
826     } else {
827     JSTE.Element.appendText (this.element, this._label);
828     }
829 wakaba 1.6 this.element.className = this._classNames.list.join (' ');
830 wakaba 1.1
831 wakaba 1.6 var self = this;
832     new JSTE.Observer ("click", this.element, function (e) {
833     self._command (e);
834     });
835     } // _createElement
836 wakaba 1.1 }); // Button
837    
838     JSTE.Course = new JSTE.Class (function (doc) {
839     this._targetDocument = doc;
840    
841 wakaba 1.22 this._entryPointsByStateName = new JSTE.Hash;
842     this._entryPointsByStateName.setNamedItem ('done', '');
843    
844 wakaba 1.1 this._entryPointsByURL = {};
845     this._entryPointsById = {};
846     this._entryPointsByClassName = {};
847    
848     this._stepsState = new JSTE.List ([new JSTE.Hash]);
849     this._steps = new JSTE.Hash;
850    
851     var nullState = new JSTE.Step;
852     nullState.uid = "";
853     this._steps.setNamedItem (nullState.uid, nullState);
854     this._initialStepUid = nullState.uid;
855     }, {
856 wakaba 1.10 _processStepsContent: function (el, parentSteps) {
857 wakaba 1.1 var self = this;
858     new JSTE.List (el.childNodes).switchByElementType (
859     new JSTE.List.SwitchByLocalName (JSTE.WATNS, {
860 wakaba 1.10 steps: function (n) { self._processStepsElement (n, parentSteps) },
861     step: function (n) { self._processStepElement (n, parentSteps) },
862     jump: function (n) { self._processJumpElement (n, parentSteps) },
863 wakaba 1.14 entryPoint: function (n) { self._processEntryPointElement (n, parentSteps) }
864 wakaba 1.1 })
865     );
866     }, // _processStepsContent
867 wakaba 1.10 _processStepsElement: function (e, parentSteps) {
868     var steps = new JSTE.Steps ();
869     steps.parentSteps = parentSteps;
870 wakaba 1.1 this._stepsState.pushCloneOfLast ();
871     this._stepsState.getLast ().prevStep = null;
872 wakaba 1.10 this._processStepsContent (e, steps);
873 wakaba 1.1 this._stepsState.pop ();
874     }, // _processStepsElement
875    
876 wakaba 1.14 _processEntryPointElement: function (e, parentSteps) {
877 wakaba 1.22 if (JSTE.Element.hasAttribute (e, 'state')) {
878     this.setEntryPointByStateName
879     (e.getAttribute ('state'), e.getAttribute ('step'));
880     } else if (JSTE.Element.hasAttribute (e, 'url')) {
881 wakaba 1.1 this.setEntryPointByURL
882     (e.getAttribute ('url'), e.getAttribute ('step'));
883     } else if (JSTE.Element.hasAttribute (e, 'root-id')) {
884     this.setEntryPointById
885     (e.getAttribute ('root-id'), e.getAttribute ('step'));
886     } else if (JSTE.Element.hasAttribute (e, 'root-class')) {
887     this.setEntryPointByClassName
888     (e.getAttribute ('root-class'), e.getAttribute ('step'));
889     }
890 wakaba 1.14 }, // _processEntryPointElement
891 wakaba 1.22 setEntryPointByStateName: function (stateName, stepName) {
892     this._entryPointsByStateName.setNamedItem (stateName, stepName || '');
893     }, // setEntryPointByStateName
894 wakaba 1.1 setEntryPointByURL: function (url, stepName) {
895     this._entryPointsByURL[url] = stepName || '';
896     }, // setEntryPointByURL
897     setEntryPointById: function (id, stepName) {
898     this._entryPointsById[id] = stepName || '';
899     }, // setEntryPointById
900     setEntryPointByClassName: function (className, stepName) {
901     this._entryPointsByClassName[className] = stepName || '';
902     }, // setEntryPointByClassName
903 wakaba 1.22 findEntryPoint: function (doc, states) {
904 wakaba 1.1 var self = this;
905     var td = this._targetDocument;
906     var stepName;
907 wakaba 1.22
908     if (states) {
909     stepName = self._entryPointsByStateName.getByNames (states.getNames ());
910     if (stepName) return stepName;
911     }
912 wakaba 1.1
913     var url = doc.URL;
914     if (url) {
915     stepName = self._entryPointsByURL[url];
916     if (stepName) return 'id-' + stepName;
917     }
918    
919     var docEl = td.documentElement;
920     if (docEl) {
921     var docElId = JSTE.Element.getIds (docEl).forEach (function (i) {
922     stepName = self._entryPointsById[i];
923     if (stepName) return new JSTE.List.Return (stepName);
924     });
925     if (stepName) return 'id-' + stepName;
926    
927     stepName = JSTE.Element.getClassNames (docEl).forEach (function (c) {
928     stepName = self._entryPointsByClassName[c];
929     if (stepName) return new JSTE.List.Return (stepName);
930     });
931     if (stepName) return 'id-' + stepName;
932     }
933    
934     var bodyEl = td.body;
935     if (bodyEl) {
936     var bodyElId = JSTE.Element.getIds (bodyEl).forEach (function (i) {
937     stepName = self._entryPointsById[i];
938     if (stepName) return new JSTE.List.Return (stepName);
939     });
940     if (stepName) return 'id-' + stepName;
941    
942     stepName = JSTE.Element.getClassNames (bodyEl).forEach (function (c) {
943     stepName = self._entryPointsByClassName[c];
944     if (stepName) return new JSTE.List.Return (stepName);
945     });
946     if (stepName) return 'id-' + stepName;
947     }
948    
949     return this._initialStepUid;
950     }, // findEntryPoint
951    
952 wakaba 1.10 _processStepElement: function (e, parentSteps) {
953 wakaba 1.18 var self = this;
954    
955 wakaba 1.1 var step = new JSTE.Step (e.getAttribute ('id'));
956 wakaba 1.10 step.parentSteps = parentSteps;
957 wakaba 1.1 step.setPreviousStep (this._stepsState.getLast ().prevStep);
958     step.select = e.getAttribute ('select') || "";
959     step.nextEvents.append
960 wakaba 1.10 (JSTE.List.spaceSeparated (e.getAttribute ('next-event')));
961 wakaba 1.17
962     var cs = JSTE.Element.getChildrenClassifiedByType (e);
963    
964     var msgEl = cs.get (JSTE.WATNS, 'message').list[0];
965 wakaba 1.1 if (msgEl) {
966     var msg = JSTE.Element.createTemplate (this._targetDocument, msgEl);
967     step.setMessageTemplate (msg);
968     }
969 wakaba 1.17
970     var nextEls = cs.get (JSTE.WATNS, 'next-step');
971 wakaba 1.16 if (nextEls.list.length) {
972 wakaba 1.1 nextEls.forEach (function (nextEl) {
973     step.addNextStep
974     (nextEl.getAttribute ('if'), nextEl.getAttribute ('step'));
975     });
976     this._stepsState.getLast ().prevStep = null;
977     } else {
978     this._stepsState.getLast ().prevStep = step;
979     }
980 wakaba 1.17
981 wakaba 1.19 cs.get (JSTE.WATNS, 'command').forEach (function (bEl) {
982 wakaba 1.18 var cmd = {
983 wakaba 1.20 name: bEl.getAttribute ('type') || 'gotoStep'
984 wakaba 1.18 };
985     if (cmd.name == 'gotoStep') {
986     cmd.args = ['id-' + bEl.getAttribute ('step')];
987     }
988     if (!JSTE.Element.isEmpty (bEl)) {
989     cmd.labelTemplate = JSTE.Element.createTemplate (self._targetDocument, bEl);
990     }
991     step.availCommands.push (cmd);
992 wakaba 1.22 }); // wat:command
993 wakaba 1.18
994 wakaba 1.22 cs.get (JSTE.WATNS, 'save-state').forEach (function (bEl) {
995     var ss = new JSTE.SaveState
996     (bEl.getAttribute ('name'), bEl.getAttribute ('value'));
997     step.saveStates.push (ss);
998     }); // wat:save-state
999 wakaba 1.13
1000     var evs = JSTE.List.spaceSeparated (e.getAttribute ('entry-event'));
1001     if (evs.list.length) {
1002     var jump = new JSTE.Jump (step.select, evs, step.uid);
1003     if (parentSteps) parentSteps._jumps.push (jump);
1004     }
1005 wakaba 1.1
1006     this._steps.setNamedItem (step.uid, step);
1007     if (!this._initialStepUid) {
1008     this._initialStepUid = step.uid;
1009     }
1010     }, // _processStepElement
1011    
1012 wakaba 1.10 _processJumpElement: function (e, parentSteps) {
1013     var target = e.getAttribute ('target') || '';
1014     var evs = JSTE.List.spaceSeparated (e.getAttribute ('event'));
1015     var stepName = e.getAttribute ('step') || '';
1016    
1017     var jump = new JSTE.Jump (target, evs, 'id-' + stepName);
1018     if (parentSteps) parentSteps._jumps.push (jump);
1019 wakaba 1.1 }, // _processJumpElement
1020    
1021     getStep: function (uid) {
1022     return this._steps.getNamedItem (uid);
1023     } // getStep
1024     }); // Course
1025    
1026 wakaba 1.9 JSTE.Class.addClassMethods (JSTE.Course, {
1027     createFromDocument: function (doc, targetDoc) {
1028     var course = new JSTE.Course (targetDoc);
1029     var docEl = doc.documentElement;
1030     if (!docEl) return course;
1031     if (!JSTE.Element.match (docEl, JSTE.WATNS, 'course')) return course;
1032 wakaba 1.10 course._processStepsContent (docEl, null);
1033 wakaba 1.9 return course;
1034     }, // createFromDocument
1035     createFromURL: function (url, targetDoc, onload, onerror) {
1036     new JSTE.XHR (url, function () {
1037     var course = JSTE.Course.createFromDocument
1038     (this.getDocument (), targetDoc);
1039     if (onload) onload (course);
1040     }, onerror).get ();
1041     } // creatFromURL
1042     }); // Course class methods
1043 wakaba 1.1
1044 wakaba 1.10 JSTE.Jump = new JSTE.Class (function (selectors, eventNames, stepUid) {
1045     this.selectors = selectors;
1046     this.eventNames = eventNames;
1047     this.stepUid = stepUid;
1048     // this.parentSteps
1049     }, {
1050     startObserver: function (doc, commandTarget) {
1051     var self = this;
1052     var observers = new JSTE.List;
1053    
1054     var onev = function () {
1055     commandTarget.gotoStep (self.stepUid);
1056     };
1057    
1058     JSTE.Node.querySelectorAll (doc, this.selectors).forEach
1059     (function (el) {
1060     self.eventNames.forEach (function (evName) {
1061     var ob = new JSTE.Observer (evName, el, onev);
1062     ob._stepUid = self.stepUid;
1063     observers.push (ob);
1064     });
1065     });
1066    
1067     return observers;
1068     } // startObserver
1069     }); // Jump
1070    
1071     JSTE.Steps = new JSTE.Class (function () {
1072     this._jumps = new JSTE.List;
1073     this._jumpHandlers = new JSTE.List;
1074     }, {
1075     setCurrentStepByUid: function (uid) {
1076     this._jumpHandlers.forEach (function (jh) {
1077     if (jh._stepUid != uid && jh.disabled) {
1078     jh.start ();
1079     } else if (jh._stepUid == uid && !jh.disabled) {
1080     jh.stop ();
1081     }
1082     });
1083     }, // setCurrentStepByUid
1084    
1085     installJumps: function (doc, commandTarget) {
1086     if (this._jumpHandlers.list.length) return;
1087     var self = this;
1088     this._jumps.forEach (function (j) {
1089     self._jumpHandlers.append (j.startObserver (doc, commandTarget));
1090     });
1091     }, // installJumps
1092    
1093     uninstallJumps: function () {
1094     this._jumpHandlers.forEach (function (jh) {
1095     jh.stop ();
1096     });
1097     this._jumpHandlers.clear ();
1098     } // uninstallJumps
1099     }); // Steps
1100    
1101 wakaba 1.1 JSTE.Step = new JSTE.Class (function (id) {
1102     if (id != null && id != '') {
1103     this.uid = 'id-' + id;
1104     } else {
1105     this.uid = 'rand-' + Math.random ();
1106     }
1107     this._nextSteps = new JSTE.List;
1108     this.nextEvents = new JSTE.List;
1109 wakaba 1.18 this.availCommands = new JSTE.List;
1110 wakaba 1.22 this.saveStates = new JSTE.List;
1111 wakaba 1.1 this.select = "";
1112     }, {
1113     setMessageTemplate: function (msg) {
1114     this._messageTemplate = msg;
1115     }, // setMessageTemplate
1116     hasMessage: function () {
1117     return this._messageTemplate ? true : false;
1118     }, // hasMessage
1119     createMessage: function (msg, doc, commandTarget) {
1120     var msg;
1121     if (this._messageTemplate) {
1122     var clone = JSTE.Element.createTemplate (doc, this._messageTemplate);
1123 wakaba 1.18 msg = new msg (doc, clone, commandTarget, this.availCommands.clone ());
1124 wakaba 1.1 } else {
1125     msg = new msg (doc, null, commandTarget);
1126     }
1127     msg.select = this.select;
1128     return msg;
1129     }, // createMessage
1130    
1131     addNextStep: function (condition, stepId) {
1132 wakaba 1.16 if (stepId != null) this._nextSteps.push ([condition, stepId]);
1133 wakaba 1.1 }, // addNextStep
1134     setPreviousStep: function (prevStep) {
1135     if (!prevStep) return;
1136     if (prevStep._defaultNextStepUid) return;
1137     prevStep._defaultNextStepUid = this.uid;
1138     }, // setPreviousStep
1139    
1140     getNextStepUid: function (doc) {
1141     var m = this._nextSteps.getFirstMatch (function (item) {
1142     var condition = item[0];
1143     if (condition) {
1144     return JSTE.Node.querySelector (doc, condition) != null;
1145     } else {
1146     return true;
1147     }
1148     });
1149     if (m) {
1150     return 'id-' + m[1];
1151     } else if (this._defaultNextStepUid) {
1152     return this._defaultNextStepUid;
1153     } else {
1154     return null;
1155     }
1156 wakaba 1.10 }, // getNextStepUid
1157    
1158     getAncestorStepsObjects: function () {
1159     var steps = new JSTE.List;
1160     var s = this.parentSteps;
1161     while (s != null) {
1162     steps.push (s);
1163     s = s.parentSteps;
1164     }
1165     return steps;
1166     } // getAncestorStepsObjects
1167 wakaba 1.1 }); // Step
1168    
1169 wakaba 1.22 JSTE.SaveState = new JSTE.Class (function (name, value) {
1170     this.name = name || '';
1171     this.value = value || '';
1172     }, {
1173     save: function (states) {
1174     states.set (this.name, this.value);
1175     } // save
1176     }); // SaveState
1177    
1178     /* Events: load, error, cssomready, close */
1179 wakaba 1.9 JSTE.Tutorial = new JSTE.Class (function (course, doc, args) {
1180 wakaba 1.1 this._course = course;
1181     this._targetDocument = doc;
1182     this._messageClass = JSTE.Message;
1183     if (args) {
1184     if (args.messageClass) this._messageClass = args.messageClass;
1185 wakaba 1.22 if (args.states) this._states = args.states;
1186 wakaba 1.1 }
1187 wakaba 1.22 if (!this._states) this._states = new JSTE.Storage.PageLocal;
1188 wakaba 1.1
1189     this._currentMessages = new JSTE.List;
1190     this._currentObservers = new JSTE.List;
1191     this._prevStepUids = new JSTE.List;
1192 wakaba 1.10 this._currentStepsObjects = new JSTE.List;
1193 wakaba 1.1
1194 wakaba 1.22 var stepUid = this._course.findEntryPoint (document, this._states);
1195 wakaba 1.1 this._currentStep = this._getStepOrError (stepUid);
1196     if (this._currentStep) {
1197     var e = new JSTE.Event ('load');
1198     this.dispatchEvent (e);
1199    
1200 wakaba 1.3 var self = this;
1201     new JSTE.Observer ('cssomready', this, function () {
1202     self._renderCurrentStep ();
1203     });
1204     this._dispatchCSSOMReadyEvent ();
1205 wakaba 1.1 return this;
1206     } else {
1207     return {};
1208     }
1209     }, {
1210     _getStepOrError: function (stepUid) {
1211     var step = this._course.getStep (stepUid);
1212     if (step) {
1213     return step;
1214 wakaba 1.22 } else if (stepUid == 'special-none') {
1215     return null;
1216 wakaba 1.1 } else {
1217     var e = new JSTE.Event ('error');
1218     e.errorMessage = 'Step not found';
1219     e.errorArguments = [this._currentStepUid];
1220     this.dispatchEvent (e);
1221     return null;
1222     }
1223     }, // _getStepOrError
1224    
1225     _renderCurrentStep: function () {
1226     var self = this;
1227     var step = this._currentStep;
1228 wakaba 1.22
1229     step.saveStates.forEach (function (ss) { ss.save (self._states) });
1230 wakaba 1.1
1231     /* Message */
1232     var msg = step.createMessage
1233     (this._messageClass, this._targetDocument, this);
1234     msg.render ();
1235     this._currentMessages.push (msg);
1236    
1237     /* Next-events */
1238     var selectedNodes = JSTE.Node.querySelectorAll
1239     (this._targetDocument, step.select);
1240     var handler = function () {
1241     self.executeCommand ("next");
1242     };
1243     selectedNodes.forEach (function (node) {
1244     step.nextEvents.forEach (function (eventType) {
1245     self._currentObservers.push
1246     (new JSTE.Observer (eventType, node, handler));
1247     });
1248     });
1249 wakaba 1.10
1250     JSTE.List.getCommonItems (this._currentStepsObjects,
1251     step.getAncestorStepsObjects (),
1252     function (common, onlyInOld, onlyInNew) {
1253     common.forEach (function (item) {
1254     item.setCurrentStepByUid (step.uid);
1255     });
1256     onlyInOld.forEach (function (item) {
1257     item.uninstallJumps ();
1258     });
1259     onlyInNew.forEach (function (item) {
1260     item.installJumps (self._targetDocument, self);
1261     });
1262     self._currentStepsObjects = common.append (onlyInNew);
1263     });
1264 wakaba 1.1 }, // _renderCurrentStep
1265     clearMessages: function () {
1266     this._currentMessages.forEach (function (msg) {
1267     msg.remove ();
1268     });
1269     this._currentMessages.clear ();
1270    
1271     this._currentObservers.forEach (function (ob) {
1272     ob.stop ();
1273     });
1274     this._currentObservers.clear ();
1275     }, // clearMessages
1276 wakaba 1.10 clearStepsHandlers: function () {
1277     this._currentStepsObjects.forEach (function (item) {
1278     item.uninstallJumps ();
1279     });
1280     this._currentStepsObjects.clear ();
1281     }, // clearStepsHandlers
1282 wakaba 1.1
1283     executeCommand: function (commandName, commandArgs) {
1284     if (this[commandName]) {
1285     return this[commandName].apply (this, commandArgs || []);
1286     } else {
1287     var e = new JSTE.Event ('error');
1288     e.errorMessage = 'Command not found';
1289     e.errorArguments = [commandName];
1290     return null;
1291     }
1292     }, // executeCommand
1293 wakaba 1.7 canExecuteCommand: function (commandName, commandArgs) {
1294     if (this[commandName]) {
1295     var can = this['can' + commandName.substring (0, 1).toUpperCase ()
1296     + commandName.substring (1)];
1297     if (can) {
1298     return can.apply (this, arguments);
1299     } else {
1300     return true;
1301     }
1302     } else {
1303     return false;
1304     }
1305     }, // canExecuteCommand
1306 wakaba 1.1
1307     back: function () {
1308     var prevStepUid = this._prevStepUids.pop ();
1309     var prevStep = this._getStepOrError (prevStepUid);
1310     if (prevStep) {
1311     this.clearMessages ();
1312     this._currentStep = prevStep;
1313     this._renderCurrentStep ();
1314     }
1315     }, // back
1316 wakaba 1.7 canBack: function () {
1317     return this._prevStepUids.list.length > 0;
1318     }, // canBack
1319 wakaba 1.1 next: function () {
1320     var nextStepUid = this._currentStep.getNextStepUid (this._targetDocument);
1321     var nextStep = this._getStepOrError (nextStepUid);
1322     if (nextStep) {
1323     this._prevStepUids.push (this._currentStep.uid);
1324     this.clearMessages ();
1325     this._currentStep = nextStep;
1326     this._renderCurrentStep ();
1327     }
1328 wakaba 1.3 }, // next
1329 wakaba 1.7 canNext: function () {
1330     return this._currentStep.getNextStepUid (this._targetDocument) != null;
1331     }, // canNext
1332 wakaba 1.10 gotoStep: function (uid) {
1333     var nextStep = this._getStepOrError (uid);
1334     if (nextStep) {
1335     this._prevStepUids.push (this._currentStep.uid);
1336     this.clearMessages ();
1337     this._currentStep = nextStep;
1338     this._renderCurrentStep ();
1339     }
1340     }, // gotoStep
1341 wakaba 1.20
1342     close: function () {
1343     this.clearMessages ();
1344 wakaba 1.22 var e = new JSTE.Event ('closed');
1345     this.dispatchEvent (e);
1346 wakaba 1.20 }, // close
1347 wakaba 1.3
1348     // <http://twitter.com/waka/status/1129513097>
1349     _dispatchCSSOMReadyEvent: function () {
1350     var self = this;
1351     var e = new JSTE.Event ('cssomready');
1352     if (window.opera && document.readyState != 'complete') {
1353     new JSTE.Observer ('readystatechange', document, function () {
1354     if (document.readyState == 'complete') {
1355     self.dispatchEvent (e);
1356     }
1357     });
1358     } else {
1359     this.dispatchEvent (e);
1360     }
1361     } // dispatchCSSOMReadyEvent
1362    
1363 wakaba 1.1 }); // Tutorial
1364 wakaba 1.9
1365     JSTE.Class.addClassMethods (JSTE.Tutorial, {
1366 wakaba 1.12 createFromURL: function (url, doc, args, onload) {
1367 wakaba 1.9 JSTE.Course.createFromURL (url, doc, function (course) {
1368 wakaba 1.12 var tutorial = new JSTE.Tutorial (course, doc, args);
1369     if (onload) onload (tutorial);
1370 wakaba 1.9 });
1371     } // createFromURL
1372     }); // Tutorial class methods
1373    
1374    
1375 wakaba 1.5
1376     if (JSTE.onLoadFunctions) {
1377     new JSTE.List (JSTE.onLoadFunctions).forEach (function (code) {
1378     code ();
1379     });
1380     }
1381    
1382     if (JSTE.isDynamicallyLoaded) {
1383     JSTE.windowLoaded = true;
1384     }
1385 wakaba 1.2
1386     /* ***** BEGIN LICENSE BLOCK *****
1387     * Copyright 2008-2009 Wakaba <w@suika.fam.cx>. All rights reserved.
1388     *
1389     * This program is free software; you can redistribute it and/or
1390     * modify it under the same terms as Perl itself.
1391     *
1392     * Alternatively, the contents of this file may be used
1393     * under the following terms (the "MPL/GPL/LGPL"),
1394     * in which case the provisions of the MPL/GPL/LGPL are applicable instead
1395     * of those above. If you wish to allow use of your version of this file only
1396     * under the terms of the MPL/GPL/LGPL, and not to allow others to
1397     * use your version of this file under the terms of the Perl, indicate your
1398     * decision by deleting the provisions above and replace them with the notice
1399     * and other provisions required by the MPL/GPL/LGPL. If you do not delete
1400     * the provisions above, a recipient may use your version of this file under
1401     * the terms of any one of the Perl or the MPL/GPL/LGPL.
1402     *
1403     * "MPL/GPL/LGPL":
1404     *
1405     * Version: MPL 1.1/GPL 2.0/LGPL 2.1
1406     *
1407     * The contents of this file are subject to the Mozilla Public License Version
1408     * 1.1 (the "License"); you may not use this file except in compliance with
1409     * the License. You may obtain a copy of the License at
1410     * <http://www.mozilla.org/MPL/>
1411     *
1412     * Software distributed under the License is distributed on an "AS IS" basis,
1413     * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
1414     * for the specific language governing rights and limitations under the
1415     * License.
1416     *
1417     * The Original Code is JSTE code.
1418     *
1419     * The Initial Developer of the Original Code is Wakaba.
1420     * Portions created by the Initial Developer are Copyright (C) 2008
1421     * the Initial Developer. All Rights Reserved.
1422     *
1423     * Contributor(s):
1424     * Wakaba <w@suika.fam.cx>
1425     *
1426     * Alternatively, the contents of this file may be used under the terms of
1427     * either the GNU General Public License Version 2 or later (the "GPL"), or
1428     * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
1429     * in which case the provisions of the GPL or the LGPL are applicable instead
1430     * of those above. If you wish to allow use of your version of this file only
1431     * under the terms of either the GPL or the LGPL, and not to allow others to
1432     * use your version of this file under the terms of the MPL, indicate your
1433     * decision by deleting the provisions above and replace them with the notice
1434     * and other provisions required by the LGPL or the GPL. If you do not delete
1435     * the provisions above, a recipient may use your version of this file under
1436     * the terms of any one of the MPL, the GPL or the LGPL.
1437     *
1438     * ***** END LICENSE BLOCK ***** */

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24