/[suikacvs]/test/suikawebwww/www/js/jste/tutorial.js
Suika

Contents of /test/suikawebwww/www/js/jste/tutorial.js

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.6 - (hide annotations) (download) (as text)
Sun Jan 25 12:32:37 2009 UTC (17 years, 5 months ago) by wakaba
Branch: MAIN
Changes since 1.5: +20 -16 lines
File MIME type: application/javascript
Make Button customizable

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     if (target.addEventListener) {
78     this.target = target;
79     this.code = onevent;
80     target.addEventListener (eventType, this.code, false);
81     } else if (target.attachEvent) {
82     this.code = function () {
83     onevent (event);
84     };
85     this.target = target;
86     target.attachEvent ("on" + eventType, this.code);
87     }
88     }, {
89     stop: function () {
90     if (this.target.removeEventListener) {
91     this.target.removeEventListener (this.eventType, this.code, false);
92     } else if (this.detachEvent) {
93     this.target.detachEvent ("on" + this.eventType, this.code);
94     }
95     } // stop
96     }); // Observer
97    
98 wakaba 1.5 new JSTE.Observer ('load', window, function () {
99     JSTE.windowLoaded = true;
100     });
101    
102 wakaba 1.1 JSTE.List = new JSTE.Class (function (arrayLike) {
103     this.list = arrayLike || [];
104     }, {
105     getLast: function () {
106     if (this.list.length) {
107     return this.list[this.list.length - 1];
108     } else {
109     return null;
110     }
111     }, // getLast
112    
113     forEach: function (code) {
114     var length = this.list.length;
115     for (var i = 0; i < length; i++) {
116     var r = code (this.list[i]);
117     if (r && r.stop) return r.returnValue;
118     }
119     return null;
120     }, // forEach
121 wakaba 1.4
122     numberToInteger: function () {
123     var newList = [];
124     this.forEach (function (item) {
125     if (typeof item === "number") {
126     newList.push (Math.floor (item));
127     } else {
128     newList.push (item);
129     }
130     });
131     return new this.constructor (newList);
132     }, // numberToInteger
133 wakaba 1.1
134     clone: function () {
135     var newList = [];
136     this.forEach (function (item) {
137     newList.push (item);
138     });
139     return new this.constructor (newList);
140     }, // clone
141    
142     grep: function (code) {
143     var newList = [];
144     this.forEach (function (item) {
145     if (code (item)) {
146     newList.push (item);
147     }
148     });
149     return new this.constructor (newList);
150     }, // grep
151     onlyNonNull: function () {
152     return this.grep (function (item) {
153     return item != null; /* Intentionally "!=" */
154     });
155     }, // onlyNonNull
156    
157     getFirstMatch: function (code) {
158     return this.forEach (function (item) {
159     if (code (item)) {
160     return new JSTE.List.Return (item);
161     }
162     });
163     }, // getFirstMatch
164    
165     switchByElementType: function () {
166     var cases = new JSTE.List (arguments);
167     this.forEach (function (n) {
168     cases.forEach (function (c) {
169     if (c.namespaceURI == n.namespaceURI) {
170     return new JSTE.List.Return (c.execute (n));
171     }
172     });
173     });
174     }, // switchByElementType
175    
176     push: function (item) {
177     this.list.push (item);
178     }, // push
179     pushCloneOfLast: function () {
180     this.list.push (this.getLast ().clone ());
181     }, // pushCloneOfLast
182     append: function (list) {
183     var self = this;
184     list.forEach (function (n) {
185     self.list.push (n);
186     });
187     }, // append
188    
189     pop: function () {
190     return this.list.pop ();
191     }, // pop
192     remove: function (removedItem) {
193     var length = this.list.length;
194     for (var i = length - 1; i >= 0; --i) {
195     var item = this.list[i];
196     if (item == removedItem) { // Intentionally "=="
197     this.list.splice (i, 1);
198     }
199     }
200     }, // remove
201     clear: function () {
202     this.list.splice (0, this.list.length);
203     } // clear
204    
205     }); // List
206    
207     JSTE.List.Return = new JSTE.Class (function (rv) {
208     this.stop = true;
209     this.returnValue = rv;
210     }, {
211    
212     }); // Return
213    
214     JSTE.List.SpaceSeparated = function (v) {
215     return new JSTE.List ((v || '').split (JSTE.SpaceChars)).grep (function (v) {
216     return v.length;
217     });
218     }; // SpaceSeparated
219    
220     JSTE.List.SwitchByLocalName = new JSTE.Class (function (ns, cases, ow) {
221     this.namespaceURI = ns;
222     this.cases = cases;
223     this.otherwise = ow || function (n) { };
224     }, {
225     execute: function (n) {
226     for (var ln in this.cases) {
227     if (JSTE.Element.matchLocalName (n, ln)) {
228     return this.cases[ln] (n);
229     }
230     }
231     return this.otherwise (n);
232     }
233     });
234    
235     JSTE.Hash = new JSTE.Class (function (hash) {
236     this.hash = hash || {};
237     }, {
238     forEach: function (code) {
239     for (var n in this.hash) {
240     var r = code (n, this.hash[n]);
241     if (r && r.stop) break;
242     }
243     }, // forEach
244     clone: function (code) {
245     var newHash = {};
246     this.forEach (function (n, v) {
247     newHash[n] = v;
248     });
249     return new this.constructor (newHash);
250     }, // clone
251    
252     getNamedItem: function (n) {
253     return this.hash[n];
254     }, // getNamedItem
255     setNamedItem: function (n, v) {
256     return this.hash[n] = v;
257     } // setNamedItem
258     });
259    
260     if (!JSTE.Node) JSTE.Node = {};
261    
262     JSTE.Class.addClassMethods (JSTE.Node, {
263     querySelector: function (node, selectors) {
264     if (node.querySelector) {
265     var el;
266     try {
267     el = node.querySelector (selectors);
268     } catch (e) {
269     el = null;
270     }
271     return el;
272     } else if (window.uu && uu.css) {
273     if (selectors != "") {
274     /* NOTE: uu.css return all elements for "" or ",xxx" */
275     return uu.css (selectors, node)[0];
276     } else {
277     return null;
278     }
279     } else if (window.Ten && Ten.DOM && Ten.DOM.getElementsBySelector) {
280     return Ten.DOM.getElementsBySelector (selectors)[0];
281     } else {
282     return null;
283     }
284     }, // querySelector
285     querySelectorAll: function (node, selectors) {
286     if (node.querySelectorAll) {
287     var nl;
288     try {
289     nl = node.querySelectorAll (selectors);
290     } catch (e) {
291     nl = null;
292     }
293     return new JSTE.List (nl);
294     } else if (window.uu && uu.css) {
295     if (selectors != "") {
296     /* NOTE: uu.css return all elements for "" or ",xxx" */
297     return new JSTE.List (uu.css (selectors, node));
298     } else {
299     return new JSTE.List;
300     }
301     } else if (window.Ten && Ten.DOM && Ten.DOM.getElementsBySelector) {
302     return new JSTE.List (Ten.DOM.getElementsBySelector (selectors));
303     } else {
304     return new JSTE.List;
305     }
306     } // querySelectorAll
307     });
308    
309     if (!JSTE.Element) JSTE.Element = {};
310    
311     JSTE.Class.addClassMethods (JSTE.Element, {
312     getLocalName: function (el) {
313     var localName = el.localName;
314     if (!localName) {
315     localName = el.nodeName;
316     if (el.prefix) {
317     localName = localName.substring (el.prefix.length + 1);
318     }
319     }
320     return localName;
321     }, // getLocalName
322    
323     match: function (el, ns, ln) {
324     if (el.nodeType !== 1) return false;
325     if (el.namespaceURI !== ns) return false;
326     return JSTE.Element.matchLocalName (el, ln);
327     }, // match
328     matchLocalName: function (el, ln) {
329     var localName = JSTE.Element.getLocalName (el);
330     if (ln instanceof RegExp) {
331     if (!localName.match (ln)) return false;
332     } else {
333     if (localName !== ln) return false;
334     }
335     return true;
336     }, // matchLocalName
337    
338     getChildElement: function (el, ns, ln) {
339     return new JSTE.List (el.childNodes).getFirstMatch (function (item) {
340     return JSTE.Element.match (item, ns, ln);
341     });
342     }, // getChildElement
343     getChildElements: function (el, ns, ln) {
344     return new JSTE.List (el.childNodes).grep (function (item) {
345     return JSTE.Element.match (item, ns, ln);
346     });
347     }, // getChildElements
348    
349     appendText: function (el, s) {
350     return el.appendChild (el.ownerDocument.createTextNode (s));
351     }, // appendText
352    
353     createTemplate: function (doc, node) {
354     var df = doc.createDocumentFragment ();
355     new JSTE.List (node.childNodes).forEach (function (n) {
356     if (n.nodeType == 1) {
357     var c = doc.createElement (JSTE.Element.getLocalName (n));
358     new JSTE.List (c.attributes).forEach (function (n) {
359     c.setAttribute (n.name, n.value);
360     });
361     c.appendChild (JSTE.Element.createTemplate (doc, n));
362     df.appendChild (c);
363     } else if (n.nodeType == 3 || n.nodeType == 4) {
364     df.appendChild (doc.createTextNode (n.data));
365     }
366     });
367     return df;
368     }, // createTemplate
369    
370     hasAttribute: function (el, localName) {
371     if (el.hasAttribute) {
372     return el.hasAttribute (localName);
373     } else {
374     return el.getAttribute (localName) != null;
375     }
376     }, // hasAttribute
377    
378     getClassNames: function (el) {
379     return new JSTE.List (el.className.split (JSTE.SpaceChars));
380     }, // getClassNames
381     addClassName: function (el, newClassName) {
382     el.className = el.className + ' ' + newClassName;
383     }, // deleteClassName
384     deleteClassName: function (el, oldClassName) {
385     var classNames = el.className.split (JSTE.SpaceChars);
386     var newClasses = [];
387     for (var n in classNames) {
388     if (classNames[n] != oldClassName) {
389     newClasses.push (classNames[n]);
390     }
391     }
392     el.className = newClasses.join (' ');
393     }, // deleteClassName
394     replaceClassName: function (el, oldClassName, newClassName) {
395     var classNames = el.className.split (JSTE.SpaceChars);
396     var newClasses = [newClassName];
397     for (var n in classNames) {
398     if (classNames[n] != oldClassName) {
399     newClasses.push (classNames[n]);
400     }
401     }
402     el.className = newClasses.join (' ');
403     }, // replaceClassName
404    
405     getIds: function (el) {
406     return new JSTE.List (el.id != "" ? [el.id] : []);
407 wakaba 1.5 }, // getIds
408    
409     /*
410     NR.js <http://suika.fam.cx/www/css/noderect/NodeRect.js> must be loaded
411     before the invocation.
412     */
413     scroll: function (elements) {
414     if (!JSTE.windowLoaded) {
415     new JSTE.Observer ('load', window, function () {
416     JSTE.Element.scroll (elements);
417     });
418     return;
419     }
420    
421     var top = Infinity;
422     var left = Infinity;
423     var topEl;
424     var leftEl;
425     elements.forEach (function (el) {
426     var rect = NR.Element.getRects (el, window).borderBox;
427     if (rect.top < top) {
428     top = rect.top;
429     topEl = el;
430     }
431     if (rect.left < left) {
432     left = rect.left;
433     leftEl = el;
434     }
435     });
436    
437     if (!leftEl && !topEl) {
438     return;
439     }
440    
441     var doc = (leftEl || topEl).ownerDocument;
442 wakaba 1.1
443 wakaba 1.5 var rect = NR.View.getViewportRects (window, doc).contentBox;
444     if (rect.top <= top && top <= rect.bottom) {
445     top = rect.top;
446     }
447     if (rect.left <= left && left <= rect.right) {
448     left = rect.left;
449     }
450    
451     /*
452     Set scroll* of both <html> and <body> elements, to support all of
453     four browsers and two (or three) rendering modes. This might result
454     in confusing the user if both <html> and <body> elements have their
455     'overflow' properties specified to 'scroll'.
456    
457     Note that this code does not do a good job if the |el| is within an
458     |overflow: scroll| element.
459     */
460     doc.body.scrollTop = top;
461     doc.body.scrollLeft = left;
462     doc.documentElement.scrollTop = top;
463     doc.documentElement.scrollLeft = left;
464     } // scroll
465    
466 wakaba 1.1 }); // JSTE.Element
467    
468     /* Events: load, close, shown, hidden */
469     JSTE.Message = new JSTE.Class (function (doc, template, commandTarget) {
470     if (!doc) return;
471     this._targetDocument = doc;
472     this._template = template || doc.createDocumentFragment ();
473     this._commandTarget = commandTarget;
474     this.hidden = true;
475     this.select = "";
476    
477     var e = new JSTE.Event ('load');
478     this.dispatchEvent (e);
479     }, {
480     render: function () {
481     var self = this;
482     var doc = this._targetDocument;
483    
484     var msgContainer = doc.createElement ('section');
485     msgContainer.appendChild (this._template);
486    
487     var buttonContainer = doc.createElement ('menu');
488    
489     var backButton = new JSTE.Message.Button
490     ("Back", this._commandTarget, "back");
491     buttonContainer.appendChild (backButton.element);
492    
493     var nextButton = new JSTE.Message.Button
494     ("Next", this._commandTarget, "next");
495     buttonContainer.appendChild (nextButton.element);
496    
497     this._outermostElement = this._render (msgContainer, buttonContainer);
498    
499     this.show ();
500     }, // render
501     _render: function (msgContainer, buttonContainer) {
502     var doc = this._targetDocument;
503    
504     var container = doc.createElement ('article');
505     var style = doc.createElement ('style');
506     style.innerHTML = this.select + ' { outline: red 2px solid }';
507     container.appendChild (style);
508    
509     container.appendChild (msgContainer);
510     container.appendChild (buttonContainer);
511     doc.documentElement.appendChild (container);
512    
513     return container;
514     }, // _render
515     remove: function () {
516     this.hide ();
517    
518     this._remove ();
519    
520     if (this._outermostElement && this._outermostElement.parentNode) {
521     this._outermostElement.parentNode.removeChild (this._outermostElement);
522     }
523    
524     var e = new JSTE.Event ("close");
525     this.dispatchEvent (e);
526     }, // remove
527     _remove: function () {
528    
529     }, // remove
530    
531     show: function () {
532     if (!this.hidden) return;
533     this.hidden = false;
534     if (this._outermostElement) {
535     JSTE.Element.replaceClassName
536     (this._outermostElement, "jste-hidden", "jste-shown");
537     }
538    
539     var e = new JSTE.Event ("shown");
540     this.dispatchEvent (e);
541     }, // show
542     hide: function () {
543     if (this.hidden) return;
544     this.hidden = true;
545     if (this._outermostElement) {
546     JSTE.Element.replaceClassName
547     (this._outermostElement, "jste-shown", "jste-hidden");
548     }
549    
550     var e = new JSTE.Event ("hidden");
551     this.dispatchEvent (e);
552     }, // hide
553    
554     setTimeout: function () {
555     /* TODO: ... */
556    
557     }
558    
559     }); // Message
560    
561     JSTE.Message.Button =
562     new JSTE.Class (function (labelText, commandTarget, commandName, commandArgs) {
563 wakaba 1.6 this._labelText = labelText != null ? labelText : "";
564    
565 wakaba 1.1 if (commandTarget && commandTarget instanceof Function) {
566     this._command = commandTarget;
567 wakaba 1.6 this._classNames = new JSTE.List;
568 wakaba 1.1 } else if (commandTarget) {
569     this._command = function () {
570     return commandTarget.executeCommand.apply
571     (commandTarget, [commandName, commandArgs]);
572     };
573 wakaba 1.6 this._classNames = new JSTE.List (['jste-command-' + commandName]);
574 wakaba 1.1 } else {
575     this._command = function () { };
576 wakaba 1.6 this._classNames = new JSTE.List;
577 wakaba 1.1 }
578    
579 wakaba 1.6 this._createElement ();
580 wakaba 1.1 }, {
581 wakaba 1.6 _createElement: function () {
582     try {
583     this.element = document.createElement ('button');
584     this.element.setAttribute ('type', 'button');
585     } catch (e) {
586     this.element = document.createElement ('<button type=button>');
587     }
588     JSTE.Element.appendText (this.element, this._labelText);
589     this.element.className = this._classNames.list.join (' ');
590 wakaba 1.1
591 wakaba 1.6 var self = this;
592     new JSTE.Observer ("click", this.element, function (e) {
593     self._command (e);
594     });
595     } // _createElement
596 wakaba 1.1 }); // Button
597    
598     JSTE.Course = new JSTE.Class (function (doc) {
599     this._targetDocument = doc;
600    
601     this._entryPointsByURL = {};
602     this._entryPointsById = {};
603     this._entryPointsByClassName = {};
604    
605     this._stepsState = new JSTE.List ([new JSTE.Hash]);
606     this._steps = new JSTE.Hash;
607    
608     var nullState = new JSTE.Step;
609     nullState.uid = "";
610     this._steps.setNamedItem (nullState.uid, nullState);
611     this._initialStepUid = nullState.uid;
612     }, {
613     _processStepsContent: function (el) {
614     var self = this;
615     new JSTE.List (el.childNodes).switchByElementType (
616     new JSTE.List.SwitchByLocalName (JSTE.WATNS, {
617     steps: function (n) { self._processStepsElement (n) },
618     step: function (n) { self._processStepElement (n) },
619     jump: function (n) { self._processJumpElement (n) },
620     entry: function (n) { self._processEntryElement (n) }
621     })
622     );
623     }, // _processStepsContent
624     _processStepsElement: function (e) {
625     this._stepsState.pushCloneOfLast ();
626     this._stepsState.getLast ().prevStep = null;
627     this._processStepsContent (e);
628     this._stepsState.pop ();
629     }, // _processStepsElement
630    
631     _processEntryElement: function (e) {
632     if (JSTE.Element.hasAttribute (e, 'url')) {
633     this.setEntryPointByURL
634     (e.getAttribute ('url'), e.getAttribute ('step'));
635     } else if (JSTE.Element.hasAttribute (e, 'root-id')) {
636     this.setEntryPointById
637     (e.getAttribute ('root-id'), e.getAttribute ('step'));
638     } else if (JSTE.Element.hasAttribute (e, 'root-class')) {
639     this.setEntryPointByClassName
640     (e.getAttribute ('root-class'), e.getAttribute ('step'));
641     }
642     }, // _processEntryElement
643     setEntryPointByURL: function (url, stepName) {
644     this._entryPointsByURL[url] = stepName || '';
645     }, // setEntryPointByURL
646     setEntryPointById: function (id, stepName) {
647     this._entryPointsById[id] = stepName || '';
648     }, // setEntryPointById
649     setEntryPointByClassName: function (className, stepName) {
650     this._entryPointsByClassName[className] = stepName || '';
651     }, // setEntryPointByClassName
652     findEntryPoint: function (doc) {
653     var self = this;
654     var td = this._targetDocument;
655     var stepName;
656    
657     var url = doc.URL;
658     if (url) {
659     stepName = self._entryPointsByURL[url];
660     if (stepName) return 'id-' + stepName;
661     }
662    
663     var docEl = td.documentElement;
664     if (docEl) {
665     var docElId = JSTE.Element.getIds (docEl).forEach (function (i) {
666     stepName = self._entryPointsById[i];
667     if (stepName) return new JSTE.List.Return (stepName);
668     });
669     if (stepName) return 'id-' + stepName;
670    
671     stepName = JSTE.Element.getClassNames (docEl).forEach (function (c) {
672     stepName = self._entryPointsByClassName[c];
673     if (stepName) return new JSTE.List.Return (stepName);
674     });
675     if (stepName) return 'id-' + stepName;
676     }
677    
678     var bodyEl = td.body;
679     if (bodyEl) {
680     var bodyElId = JSTE.Element.getIds (bodyEl).forEach (function (i) {
681     stepName = self._entryPointsById[i];
682     if (stepName) return new JSTE.List.Return (stepName);
683     });
684     if (stepName) return 'id-' + stepName;
685    
686     stepName = JSTE.Element.getClassNames (bodyEl).forEach (function (c) {
687     stepName = self._entryPointsByClassName[c];
688     if (stepName) return new JSTE.List.Return (stepName);
689     });
690     if (stepName) return 'id-' + stepName;
691     }
692    
693     return this._initialStepUid;
694     }, // findEntryPoint
695    
696     _processStepElement: function (e) {
697     var step = new JSTE.Step (e.getAttribute ('id'));
698     step.setPreviousStep (this._stepsState.getLast ().prevStep);
699     step.select = e.getAttribute ('select') || "";
700     step.nextEvents.append
701     (JSTE.List.SpaceSeparated (e.getAttribute ('next-event')));
702     var msgEl = JSTE.Element.getChildElement (e, JSTE.WATNS, 'message');
703     if (msgEl) {
704     var msg = JSTE.Element.createTemplate (this._targetDocument, msgEl);
705     step.setMessageTemplate (msg);
706     }
707     var nextEls = JSTE.Element.getChildElements (e, JSTE.WATNS, 'next-step');
708     if (nextEls.length) {
709     nextEls.forEach (function (nextEl) {
710     step.addNextStep
711     (nextEl.getAttribute ('if'), nextEl.getAttribute ('step'));
712     });
713     this._stepsState.getLast ().prevStep = null;
714     } else {
715     this._stepsState.getLast ().prevStep = step;
716     }
717     /* TODO: @save */
718    
719     this._steps.setNamedItem (step.uid, step);
720     if (!this._initialStepUid) {
721     this._initialStepUid = step.uid;
722     }
723     }, // _processStepElement
724    
725     _processJumpElement: function (e) {
726    
727     }, // _processJumpElement
728    
729     getStep: function (uid) {
730     return this._steps.getNamedItem (uid);
731     } // getStep
732     }); // Course
733    
734     JSTE.Course.createFromDocument = function (doc, targetDoc) {
735     var course = new JSTE.Course (targetDoc);
736     var docEl = doc.documentElement;
737     if (!docEl) return course;
738     if (!JSTE.Element.match (docEl, JSTE.WATNS, 'course')) return course;
739     course._processStepsContent (docEl);
740     return course;
741     }; // createFromDocument
742    
743     JSTE.Step = new JSTE.Class (function (id) {
744     if (id != null && id != '') {
745     this.uid = 'id-' + id;
746     } else {
747     this.uid = 'rand-' + Math.random ();
748     }
749     this._nextSteps = new JSTE.List;
750     this.nextEvents = new JSTE.List;
751     this.select = "";
752     }, {
753     setMessageTemplate: function (msg) {
754     this._messageTemplate = msg;
755     }, // setMessageTemplate
756     hasMessage: function () {
757     return this._messageTemplate ? true : false;
758     }, // hasMessage
759     createMessage: function (msg, doc, commandTarget) {
760     var msg;
761     if (this._messageTemplate) {
762     var clone = JSTE.Element.createTemplate (doc, this._messageTemplate);
763     msg = new msg (doc, clone, commandTarget);
764     } else {
765     msg = new msg (doc, null, commandTarget);
766     }
767     msg.select = this.select;
768     return msg;
769     }, // createMessage
770    
771     addNextStep: function (condition, stepId) {
772     this._nextSteps.push ([condition, stepId]);
773     }, // addNextStep
774     setPreviousStep: function (prevStep) {
775     if (!prevStep) return;
776     if (prevStep._defaultNextStepUid) return;
777     prevStep._defaultNextStepUid = this.uid;
778     }, // setPreviousStep
779    
780     getNextStepUid: function (doc) {
781     var m = this._nextSteps.getFirstMatch (function (item) {
782     var condition = item[0];
783     if (condition) {
784     return JSTE.Node.querySelector (doc, condition) != null;
785     } else {
786     return true;
787     }
788     });
789     if (m) {
790     return 'id-' + m[1];
791     } else if (this._defaultNextStepUid) {
792     return this._defaultNextStepUid;
793     } else {
794     return null;
795     }
796     } // getNextStepUid
797    
798     }); // Step
799    
800 wakaba 1.3 /* Events: load, error, cssomready */
801 wakaba 1.1 JSTE.Tutorial = new JSTE.Class (function (doc, course, args) {
802     this._course = course;
803     this._targetDocument = doc;
804     this._messageClass = JSTE.Message;
805     if (args) {
806     if (args.messageClass) this._messageClass = args.messageClass;
807     }
808    
809     this._currentMessages = new JSTE.List;
810     this._currentObservers = new JSTE.List;
811     this._prevStepUids = new JSTE.List;
812    
813     var stepUid = this._course.findEntryPoint (document);
814     this._currentStep = this._getStepOrError (stepUid);
815     if (this._currentStep) {
816     var e = new JSTE.Event ('load');
817     this.dispatchEvent (e);
818    
819 wakaba 1.3 var self = this;
820     new JSTE.Observer ('cssomready', this, function () {
821     self._renderCurrentStep ();
822     });
823     this._dispatchCSSOMReadyEvent ();
824 wakaba 1.1 return this;
825     } else {
826     return {};
827     }
828     }, {
829     _getStepOrError: function (stepUid) {
830     var step = this._course.getStep (stepUid);
831     if (step) {
832     return step;
833     } else {
834     var e = new JSTE.Event ('error');
835     e.errorMessage = 'Step not found';
836     e.errorArguments = [this._currentStepUid];
837     this.dispatchEvent (e);
838     return null;
839     }
840     }, // _getStepOrError
841    
842     _renderCurrentStep: function () {
843     var self = this;
844     var step = this._currentStep;
845    
846     /* Message */
847     var msg = step.createMessage
848     (this._messageClass, this._targetDocument, this);
849     msg.render ();
850     this._currentMessages.push (msg);
851    
852     /* Next-events */
853     var selectedNodes = JSTE.Node.querySelectorAll
854     (this._targetDocument, step.select);
855     var handler = function () {
856     self.executeCommand ("next");
857     };
858     selectedNodes.forEach (function (node) {
859     step.nextEvents.forEach (function (eventType) {
860     self._currentObservers.push
861     (new JSTE.Observer (eventType, node, handler));
862     });
863     });
864     }, // _renderCurrentStep
865     clearMessages: function () {
866     this._currentMessages.forEach (function (msg) {
867     msg.remove ();
868     });
869     this._currentMessages.clear ();
870    
871     this._currentObservers.forEach (function (ob) {
872     ob.stop ();
873     });
874     this._currentObservers.clear ();
875     }, // clearMessages
876    
877     executeCommand: function (commandName, commandArgs) {
878     if (this[commandName]) {
879     return this[commandName].apply (this, commandArgs || []);
880     } else {
881     var e = new JSTE.Event ('error');
882     e.errorMessage = 'Command not found';
883     e.errorArguments = [commandName];
884     return null;
885     }
886     }, // executeCommand
887    
888     startTutorial: function () {
889     this.resetVisited ();
890    
891     }, // startTutorial
892     continueTutorial: function () {
893    
894     }, // continueTutorial
895    
896     saveVisited: function () {
897    
898     }, // saveVisited
899     resetVisited: function () {
900    
901     }, // resetVisited
902    
903     back: function () {
904     var prevStepUid = this._prevStepUids.pop ();
905     var prevStep = this._getStepOrError (prevStepUid);
906     if (prevStep) {
907     this.clearMessages ();
908     this._currentStep = prevStep;
909     this._renderCurrentStep ();
910     }
911     }, // back
912     next: function () {
913     var nextStepUid = this._currentStep.getNextStepUid (this._targetDocument);
914     var nextStep = this._getStepOrError (nextStepUid);
915     if (nextStep) {
916     this._prevStepUids.push (this._currentStep.uid);
917     this.clearMessages ();
918     this._currentStep = nextStep;
919     this._renderCurrentStep ();
920     }
921 wakaba 1.3 }, // next
922    
923     // <http://twitter.com/waka/status/1129513097>
924     _dispatchCSSOMReadyEvent: function () {
925     var self = this;
926     var e = new JSTE.Event ('cssomready');
927     if (window.opera && document.readyState != 'complete') {
928     new JSTE.Observer ('readystatechange', document, function () {
929     if (document.readyState == 'complete') {
930     self.dispatchEvent (e);
931     }
932     });
933     } else {
934     this.dispatchEvent (e);
935     }
936     } // dispatchCSSOMReadyEvent
937    
938 wakaba 1.1 }); // Tutorial
939 wakaba 1.5
940     if (JSTE.onLoadFunctions) {
941     new JSTE.List (JSTE.onLoadFunctions).forEach (function (code) {
942     code ();
943     });
944     }
945    
946     if (JSTE.isDynamicallyLoaded) {
947     JSTE.windowLoaded = true;
948     }
949 wakaba 1.2
950     /* ***** BEGIN LICENSE BLOCK *****
951     * Copyright 2008-2009 Wakaba <[email protected]>. All rights reserved.
952     *
953     * This program is free software; you can redistribute it and/or
954     * modify it under the same terms as Perl itself.
955     *
956     * Alternatively, the contents of this file may be used
957     * under the following terms (the "MPL/GPL/LGPL"),
958     * in which case the provisions of the MPL/GPL/LGPL are applicable instead
959     * of those above. If you wish to allow use of your version of this file only
960     * under the terms of the MPL/GPL/LGPL, and not to allow others to
961     * use your version of this file under the terms of the Perl, indicate your
962     * decision by deleting the provisions above and replace them with the notice
963     * and other provisions required by the MPL/GPL/LGPL. If you do not delete
964     * the provisions above, a recipient may use your version of this file under
965     * the terms of any one of the Perl or the MPL/GPL/LGPL.
966     *
967     * "MPL/GPL/LGPL":
968     *
969     * Version: MPL 1.1/GPL 2.0/LGPL 2.1
970     *
971     * The contents of this file are subject to the Mozilla Public License Version
972     * 1.1 (the "License"); you may not use this file except in compliance with
973     * the License. You may obtain a copy of the License at
974     * <http://www.mozilla.org/MPL/>
975     *
976     * Software distributed under the License is distributed on an "AS IS" basis,
977     * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
978     * for the specific language governing rights and limitations under the
979     * License.
980     *
981     * The Original Code is JSTE code.
982     *
983     * The Initial Developer of the Original Code is Wakaba.
984     * Portions created by the Initial Developer are Copyright (C) 2008
985     * the Initial Developer. All Rights Reserved.
986     *
987     * Contributor(s):
988     * Wakaba <[email protected]>
989     *
990     * Alternatively, the contents of this file may be used under the terms of
991     * either the GNU General Public License Version 2 or later (the "GPL"), or
992     * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
993     * in which case the provisions of the GPL or the LGPL are applicable instead
994     * of those above. If you wish to allow use of your version of this file only
995     * under the terms of either the GPL or the LGPL, and not to allow others to
996     * use your version of this file under the terms of the MPL, indicate your
997     * decision by deleting the provisions above and replace them with the notice
998     * and other provisions required by the LGPL or the GPL. If you do not delete
999     * the provisions above, a recipient may use your version of this file under
1000     * the terms of any one of the MPL, the GPL or the LGPL.
1001     *
1002     * ***** END LICENSE BLOCK ***** */

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24