/[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.20 - (hide annotations) (download) (as text)
Sat Jan 31 11:00:11 2009 UTC (17 years, 5 months ago) by wakaba
Branch: MAIN
Changes since 1.19: +11 -6 lines
File MIME type: application/javascript
Don't show back/prev command if any <c:command/> element is there; <c:command name/> renamed as <c:command type/>; Added |close| command

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24