]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/xul/staff_client/server/serial/pattern_wizard.js
Serials: a regularity (i.e. 85X subfield $y) page for the caption/pattern wizard
[working/Evergreen.git] / Open-ILS / xul / staff_client / server / serial / pattern_wizard.js
1 /* The code in this file relies on common.js */
2
3 dojo.require("openils.Util");
4
5 var wizard;
6
7 function S(k) {
8     return dojo.byId("serialStrings").getString("pattern_wizard." + k).
9         replace("\\n", "\n");
10 }
11
12 var _chronstants = {    /* snicker */
13     "month": {
14         "values": [
15             "01", "02", "03", "04", "05", "06",
16             "07", "08", "09", "10", "11", "12"
17         ]
18     },
19     "weekday": {
20         "values": ["mo", "tu", "we", "th", "fr", "sa", "su"]
21     },
22     "week": {
23         "values": ["00", "01", "02", "03", "04", "05", "97", "98", "99"]
24     },
25     "season": {
26         "values": ["21", "22", "23", "24"]
27     }
28 };
29
30 function _menulist(values, labels, items_only) {
31     var menuitems = [];
32     for (var i = 0; i < values.length; i++) {
33         menuitems.push(
34             dojo.create(
35                 "menuitem", {"value": values[i], "label": labels[i]}
36             )
37         );
38     }
39     if (items_only) {
40         return menuitems;
41     } else {
42         var menupopup = dojo.create("menupopup");
43         menuitems.forEach(
44             function(menuitem) { dojo.place(menuitem, menupopup, "last"); }
45         );
46         var menulist = dojo.create("menulist");
47         dojo.place(menupopup, menulist, "only");
48         return menulist;
49     }
50 }
51
52 function _date_validate(date_val, month_val) {
53     /* general purpose date validation irrespective of year */
54     date_val = date_val.trim();
55
56     if (!date_val.match(/^[0123]?\d$/))
57         return false;
58
59     date_val = Number(date_val); /* do NOT use parseInt */
60     month_val = Number(month_val);
61
62     if (date_val < 1) {
63         return false;
64     } else if (month_val == 2) {
65         return date_val <= 29;
66     } else if ([1,3,5,7,8,10,12].indexOf(month_val) != -1) {
67         return date_val <= 31;
68     } else {
69         return date_val <= 30;
70     }
71 }
72
73 function CalendarChangeRow() {
74     var self = this;
75
76     this._init = function(template, id, manager) {
77         this.element = dojo.clone(template);
78
79         this.point_widgets = ["month", "season", "date"].map(
80             function(type) { return node_by_name(type, self.element); }
81         );
82
83         dojo.attr(
84             node_by_name("type", this.element), "oncommand", function(ev) {
85                 self.point_widgets.forEach(
86                     function(w) {
87                         var active = (dojo.attr(w, "name") == ev.target.value);
88                         (active ? show : hide)(w);
89                     }
90                 );
91             }
92         );
93
94         var date_month_selector = node_by_name("date_month", this.element);
95
96         dojo.attr(
97             node_by_name("date_day", this.element), "onchange", function(ev) {
98                 if (_date_validate(ev.target.value,date_month_selector.value)){
99                     return true;
100                 } else {
101                     alert(S("bad_date_value"));
102                     ev.target.focus();
103                     return false;
104                 }
105             }
106         );
107
108         this.remover = node_by_name("remover", this.element);
109         dojo.attr(
110             this.remover, "onclick", function() { manager.remove_row(id); }
111         );
112     };
113
114     this._compile_month = function() {
115         return node_by_name("month", this.element).value;
116     };
117
118     this._compile_season = function() {
119         return node_by_name("season", this.element).value;
120     };
121
122     this._compile_date = function() {
123         var n = Number(node_by_name("date_day", this.element).value);
124         if (n < 10)
125             n = "0" + String(n);
126         else
127             n = String(n);
128
129         return node_by_name("date_month", this.element).value + n;
130     };
131
132     this.compile = function() {
133         var type = node_by_name("type", this.element).value;
134
135         return type ? this["_compile_" + type]() : [];
136     };
137
138     this._init.apply(this, arguments);
139 };
140
141 function RegularityRow() {
142     var self = this;
143
144     this._init = function(template, id, manager) {
145         this.id = id;
146         this.manager = manager;
147         this.element = dojo.clone(template);
148
149         this.publication_code = null;
150         this.type_and_code_pattern = null;
151
152         this._prepare_event_handlers(id);
153     };
154
155     this._prepare_event_handlers = function(id) {
156         dojo.attr(
157             node_by_name("remove", this.element),
158             "oncommand",
159             function() { self.manager.remove_row(id); }
160         );
161         dojo.attr(
162             node_by_name("poc", this.element),
163             "oncommand",
164             function(ev) {
165                 self.publication_code = ev.target.value;
166                 self.update_chron_code_controls();
167             }
168         );
169         dojo.attr(
170             node_by_name("type_and_code_pattern", this.element),
171             "oncommand",
172             function(ev) {
173                 self.type_and_code_pattern = ev.target.value;
174                 self.update_chron_code_controls();
175             }
176         );
177
178         this.add_sub_row_btn = node_by_name("add_sub_row", this.element);
179         dojo.attr(
180             this.add_sub_row_btn, "oncommand", function(ev) {
181                 self.add_sub_row();
182             }
183         );
184     };
185
186     this.add_sub_row = function() {
187         var container = dojo.create(
188             "hbox", null, node_by_name("sub_rows_here", this.element), "last"
189         );
190
191         /* Break up our type and code pattern into parts that can be
192          * mapped to widgets. */
193         this.get_code_pattern().map(
194             function(pattern) {
195                 return self.manufacture_chron_code_control(pattern);
196             }
197         ).forEach(
198             function(control) {
199                 dojo.place(control, container, "last");
200             }
201         );
202
203         /* special case: add a label for clarity for MMWW */
204         if (this.type_and_code_pattern == "w:MMWW")
205             dojo.create("description", {"value": S("week")}, container, "last");
206
207         /* another special case: YYYY needs no add/remove subrow buttons */
208         if (this.type_and_code_pattern == "y:YYYY") {
209             this.add_sub_row_btn.disabled = true;
210         } else {
211             this.add_sub_row_btn.disabled = false;
212
213             dojo.create(
214                 "button", {
215                     "style": {
216                         "fontWeight": "bold", "color": "red"
217                     },
218                     "label": "X",
219                     "tooltiptext": S("remove_sub_row"),
220                     "oncommand": function() {
221                         hard_empty(container); dojo.destroy(container);
222                     }
223                 }, container, "last"
224             );
225         }
226     };
227
228     this.get_code_pattern = function() {
229         var code_pattern_str = this.type_and_code_pattern.split(":")[1];
230
231         /* A special case: if the strings is YYYY, return it whole. Otherwise
232          * break it up into two-char parts. These parts come from the
233          * "Possible Code Pattern" column of "Chronology Type and Code
234          * Patterns" of the subfield $y section of the document at
235          * http://www.loc.gov/marc/holdings/hd853855.html
236          *
237          * In retrospect, there was no reason to adopt these multi-char
238          * code patterns for this purpose. Something single-char and
239          * quicker to decode would have sufficed, but meh, this works.
240          */
241         if (code_pattern_str[0] == "Y")
242             return [code_pattern_str];
243
244         var code_pattern = [];
245         for (var i=0; code_pattern_str[i]; i+=2)
246             code_pattern.push(code_pattern_str.slice(i, i + 2));
247
248         return code_pattern;
249     };
250
251     this.allow_year_split = function(yes) {
252         dojo.attr(
253             dojo.query("[name='type_and_code_pattern'] [value='y:YYYY']")[0],
254             "disabled",
255             !yes
256         );
257     };
258
259     this.update_chron_code_controls = function() {
260         if (!this.type_and_code_pattern || !this.publication_code)
261             return;
262
263         this.allow_year_split(this.publication_code != "c");
264
265         var container = node_by_name("sub_rows_here", this.element);
266         /* for some reason, this repeitition is necessary with XUL documents
267          * and dojo */
268         for (var i = 0 ; i < 2; i++) {
269             hard_empty(container);
270             dojo.forEach(container.childNodes, dojo.destroy);
271         }
272
273         this.add_sub_row();
274     };
275
276     this.manufacture_chron_code_control = function(pattern) {
277         return {
278             "dd": function() {
279                 return _menulist(
280                     _chronstants.weekday.values, _chronstants.weekday.names
281                 );
282             },
283             "DD": function() {
284                 return dojo.create( /* XXX TODO change min/max based on month */
285                     "textbox", {
286                         "size": 3,
287                         "type": "number",
288                         "min": 1,
289                         "max": 31
290                     }
291                 );
292             },
293             "MM": function() {
294                 return _menulist(
295                     _chronstants.month.values, _chronstants.month.names
296                 );
297             },
298             "SS": function() {
299                 return _menulist(
300                     _chronstants.season.values, _chronstants.season.names
301                 );
302             },
303             "WW": function() {
304                 return _menulist(
305                     _chronstants.week.values, _chronstants.week.names
306                 );
307             },
308             "YYYY": function() {
309                 return dojo.create(
310                     "textbox", {
311                         "disabled": "true", "value": "yyy1/yyy2", "size": 9
312                     }
313                 );
314             }
315         }[pattern]();
316     };
317
318     this.compile = function() {
319         return this.publication_code +
320             this.type_and_code_pattern[0] +
321             dojo.query("hbox", node_by_name("sub_rows_here", this.element)).map(
322                 function(sub_row) {
323                     var t = "";
324                     dojo.filter(
325                         sub_row.childNodes, function(n) {
326                             return (
327                                 n.nodeName == "menulist" ||
328                                 n.nodeName == "textbox"
329                             );
330                         }
331                     ).forEach(
332                         function(control) {
333                             if (control.value.match(/^\d$/))
334                                 t += "0" + control.value;
335                             else
336                                 t += control.value;
337                         }
338                     );
339                     return t;
340                 }
341             ).join(this.publication_code == "c" ? "/" : ",");
342     };
343
344     this._init.apply(this, arguments);
345 }
346
347 function RegularityEditor() {
348     var self = this;
349
350     this._init = function() {
351         this.rows = {};
352         this.row_count = 0;
353
354         this._prepare_template();
355         this.add_row();
356     };
357
358     this._prepare_template = function() {
359         var tmpl = dojo.byId("regularity_template_y");
360         tmpl.parentNode.removeChild(tmpl);
361
362         this.template = tmpl;
363     };
364
365     this.toggle = function(ev) {
366         this.active = ev.target.checked;
367         (this.active ? show : hide)("regularity_editor_here");
368     };
369
370     this.add_row = function() {
371         var id = this.row_count++;
372
373         this.rows[id] = new RegularityRow(this.template, id, this);
374
375         dojo.place(this.rows[id].element, "y_row_before_this", "before");
376     };
377
378     this.remove_row = function(id) {
379         var row = this.rows[id];
380         hard_empty(row.element);
381         dojo.destroy(row.element);
382
383         delete this.rows[id];
384     };
385
386     this.compile = function() {
387         return openils.Util.objectProperties(this.rows).sort().reduce(
388             function(a, b) { return a.concat(["y", self.rows[b].compile()]); },
389             []
390         );
391     };
392
393     this._init.apply(this, arguments);
394 }
395
396 function CalendarChangeEditor() {
397     var self = this;
398
399     this._init = function() {
400         this.rows = {};
401         this.row_count = 0;
402
403         this._get_template();
404         this.add_row();
405     };
406
407     this._get_template = function() {
408         var temp_template = dojo.byId("calendar_row_template");
409         this.grid = temp_template.parentNode;
410         this.template = this.grid.removeChild(temp_template);
411         this.template.removeAttribute("id");
412
413         [
414             dojo.query("[name='month'] menupopup", this.template)[0],
415             dojo.query("[name='date_month'] menupopup", this.template)[0]
416         ].forEach(
417             function(menupopup) {
418                 _menulist(
419                     _chronstants.month.values,
420                     _chronstants.month.names,
421                     /* items_only */ true
422                 ).forEach(
423                     function(menuitem) {
424                         dojo.place(menuitem, menupopup, "last");
425                     }
426                 );
427             }
428         );
429     };
430
431     this.remove_row = function(id) {
432         hard_empty(this.rows[id].element);
433         dojo.destroy(this.rows[id].element);
434         delete this.rows[id];
435
436         dojo.byId("calendar_change_add_row").disabled = false;
437     };
438
439     this.add_row = function() {
440         var id = this.row_count++;
441
442         this.rows[id] =
443             new CalendarChangeRow(dojo.clone(this.template), id, this);
444         dojo.place(this.rows[id].element, this.grid, "last");
445     };
446
447     this.toggle = function(ev) {
448         this.active = ev.target.checked;
449         (this.active ? show : hide)("calendar_change_editor_here");
450     };
451
452     this.compile = function() {
453         if (!this.active) return [];
454
455         return [
456             "x",
457             openils.Util.objectProperties(this.rows).sort(num_sort).map(
458                 function(key) { return self.rows[key].compile(); }
459             ).join(",")
460         ];
461     };
462
463     this._init.apply(this, arguments);
464 }
465
466 function ChronRow() {
467     var self = this;
468
469     this._init = function(template, subfield, manager) {
470         this.subfield = subfield;
471         this.element = dojo.clone(template);
472
473         dojo.attr(
474             node_by_name("caption_label", this.element),
475             "value", S("chronology." + subfield) + ":"
476         );
477
478         this.fields = {};
479         ["caption", "display_in_holding"].forEach(
480             function(o) { self.fields[o] = node_by_name(o, self.element); }
481         );
482
483         this.remover = node_by_name("remover", this.element);
484         dojo.attr(
485             this.remover, "onclick", function(){ manager.remove_row(subfield); }
486         );
487     };
488
489     this._init.apply(this, arguments);
490 };
491
492 function ChronEditor() {
493     /* TODO make this enforce unique caption values for each row? */
494     var self = this;
495
496     this._init = function() {
497         this.rows = {};
498
499         this.subfields = ["i", "j", "k", "l", "m"];
500
501         this._get_template();
502         this.add_row();
503     };
504
505     this._get_template = function() {
506         var temp_template = dojo.byId("chron_row_template");
507         this.grid = temp_template.parentNode;
508         this.template = this.grid.removeChild(temp_template);
509         this.template.removeAttribute("id");
510     };
511
512     this._test_removability = function(subfield) {
513         var start = this.subfields.indexOf(subfield);
514
515         if (start < 0) {
516             /* no such field, not OK to remove */
517             return false;
518         } else if (!this.subfields[start]) {
519             /* field row not present, not OK to remove */
520             return false;
521         }
522
523         var next = this.subfields[start + 1];
524         if (typeof(next) == "undefined") { /* last in set, ok to remove */
525             return true;
526         } else {
527             if (this.rows[next]) { /* NOT last in set, not ok to remove */
528                 return false;
529             } else { /* last in set actually present, ok to remove */
530                 return true;
531             }
532         }
533     };
534
535     this.remove_row = function(subfield) {
536         if (this._test_removability(subfield)) {
537             hard_empty(this.rows[subfield].element);
538             dojo.destroy(this.rows[subfield].element);
539             delete this.rows[subfield];
540
541             dojo.byId("chron_add_row").disabled = false;
542         } else {
543             alert(S("not_removable_row"));
544         }
545     };
546
547     this.add_row = function() {
548         var available = this.subfields.filter(
549             function(subfield) { return !Boolean(self.rows[subfield]); }
550         );
551
552         if (available.length) {
553             var subfield = available.shift();
554             if (!available.length)
555                 dojo.byId("chron_add_row").disabled = true;
556         } else {
557             /* We shouldn't really be able to get here. */
558             return;
559         }
560
561         this.rows[subfield] =
562             new ChronRow(dojo.clone(this.template), subfield, this);
563
564         dojo.place(this.rows[subfield].element, this.grid, "last");
565     };
566
567     this.toggle = function(ev) {
568         this.active = ev.target.checked;
569         (this.active ? show : hide)("chron_editor_here");
570     };
571
572     this.compile = function() {
573         if (!this.active) return [];
574
575         return this.subfields.filter(
576             function(subfield) { return Boolean(self.rows[subfield]); }
577         ).reduce(
578             function(result, subfield) {
579                 var caption = self.rows[subfield].fields.caption.value;
580                 if (!self.rows[subfield].fields.display_in_holding.checked)
581                     caption = "(" + caption + ")";
582                 return result.concat([subfield, caption]);
583             }, []
584         );
585     };
586
587     this._init.apply(this, arguments);
588 }
589
590 function EnumRow() {
591     var self = this;
592
593     this._init = function(template, subfield, manager) {
594         this.subfield = subfield;
595         this.element = dojo.clone(template);
596
597         this.fields = {};
598         ["caption","units_per","units_per_number","continuity","remover"].
599             forEach(
600                 function(o) { self.fields[o] = node_by_name(o, self.element); }
601             );
602
603         if (subfield == "a" || subfield == "g") {
604             ["units_per", "continuity"].forEach(
605                 function(o) { soft_hide(node_by_name(o, self.element)); }
606             );
607         }
608
609         var caption_id = "enum_caption_" + subfield;
610         var caption_label = node_by_name("caption_label", this.element);
611         dojo.attr(this.fields.caption, "id", caption_id);
612         dojo.attr(caption_label, "control", caption_id);
613         dojo.attr(caption_label, "value", S("enumeration." + subfield) + ":");
614
615         this.remover = this.fields.remover;
616         dojo.attr(
617             this.remover, "onclick", function(){manager.remove_row(subfield);}
618         );
619     };
620
621     this._init.apply(this, arguments);
622 };
623
624 function EnumEditor() {
625     var self = this;
626
627     this._init = function() {
628         this.normal_rows = {};
629         this.alt_rows = {};
630
631         this.normal_subfields = ["a","b","c","d","e","f"];
632         this.alt_subfields = ["g","h"];
633
634         this._get_template();
635         this.add_normal_row();
636     };
637
638     this._get_template = function() {
639         var temp_template = dojo.byId("enum_row_template");
640         this.grid = temp_template.parentNode;
641         this.template = this.grid.removeChild(temp_template);
642         this.template.removeAttribute("id");
643     };
644
645     this.remove_row = function(subfield) {
646         if (this._test_removability(subfield)) {
647             var add_button = "enum_add_normal_row";
648             var set = this.normal_rows;
649             if (!set[subfield]) {
650                 set = this.alt_rows;
651                 add_button = "enum_add_alt_row";
652             }
653
654             hard_empty(set[subfield].element);
655             dojo.destroy(set[subfield].element);
656             delete set[subfield];
657             dojo.byId(add_button).disabled = false;
658         } else {
659             alert(S("not_removable_row"));
660         }
661     };
662
663     this._test_removability = function(id) {
664         var set = this.normal_subfields;
665         var rows = this.normal_rows;
666         var start = set.indexOf(id);
667
668         if (start == -1) {
669             set = this.alt_subfields;
670             rows = this.alt_rows;
671             start = set.indexOf(id);
672         }
673
674         if (start < 0) {
675             /* no such field, not OK to remove */
676             return false;
677         } else if (!set[start]) {
678             /* field row not present, not OK to remove */
679             return false;
680         }
681
682         var next = set[start + 1];
683         if (typeof(next) == "undefined") { /* last in set, ok to remove */
684             return true;
685         } else {
686             if (rows[next]) { /* NOT last in set, not ok to remove */
687                 return false;
688             } else { /* last in set actually present, ok to remove */
689                 return true;
690             }
691         }
692     };
693
694     this.add_normal_row = function() {
695         var available = this.normal_subfields.filter(
696             function(subfield) { return !Boolean(self.normal_rows[subfield]); }
697         );
698         if (available.length) {
699             var subfield = available.shift();
700             if (!available.length) {
701                 /* If that was the last available normal row, disable the
702                  * add rows button. */
703                 dojo.byId("enum_add_normal_row").disabled = true;
704             }
705         } else {
706             /* We shouldn't really be able to get here. */
707             return;
708         }
709
710         this.normal_rows[subfield] =
711             new EnumRow(dojo.clone(this.template), subfield, this);
712
713         dojo.place(this.normal_rows[subfield].element, this.grid, "last");
714     };
715
716     this.add_alt_row = function() {
717         var available = this.alt_subfields.filter(
718             function(subfield) { return !Boolean(self.alt_rows[subfield]); }
719         );
720         if (available.length) {
721             var subfield = available.shift();
722             if (!available.length) {
723                 /* If that was the last available normal row, disable the
724                  * add rows button. */
725                 dojo.byId("enum_add_alt_row").disabled = true;
726             }
727         } else {
728             /* We shouldn't really be able to get here. */
729             return;
730         }
731
732         this.alt_rows[subfield] =
733             new EnumRow(dojo.clone(this.template), subfield, this);
734
735         dojo.place(this.alt_rows[subfield].element, this.grid, "last");
736     };
737
738     this.toggle = function(ev) {
739         var func;
740         var use_calendar_change = dojo.byId("use_calendar_change");
741
742         this.active = ev.target.checked;
743
744         if (this.active) {
745             func = show;
746             use_calendar_change.disabled = false;
747         } else {
748             use_calendar_change.checked = false;
749             use_calendar_change.doCommand();
750             use_calendar_change.disabled = true;
751             func = hide;
752         }
753
754         func("enum_editor_here");
755     };
756
757     this.compile = function() {
758         if (!this.active) return [];
759
760         var rows = dojo.mixin({}, this.normal_rows, this.alt_rows);
761         var subfields = [].concat(this.normal_subfields, this.alt_subfields);
762
763         return subfields.filter(
764             function(subfield) { return Boolean(rows[subfield]); }
765         ).reduce(
766             function(result, subfield) {
767                 var fields = rows[subfield].fields;
768                 var pairs = [subfield, fields.caption.value];
769
770                 if (subfield != "a" && subfield != "g") {
771                     if (fields.units_per.value == "number") {
772                         if (fields.units_per_number.value) {
773                             pairs = pairs.concat([
774                                 "u", fields.units_per_number.value,
775                                 "v", fields.continuity.value
776                             ]);
777                         }
778                     } else {
779                         pairs = pairs.concat([
780                             "u", fields.units_per.value,
781                             "v", fields.continuity.value
782                         ]);
783                     }
784                 }
785
786                 return result.concat(pairs);
787             }, []
788         );
789     };
790
791     this._init.apply(this, arguments);
792 }
793
794 function Wizard() {
795     var self = this;
796
797     var _step_prefix = "wizard_step_";
798     var _step_regex = new RegExp("^" + _step_prefix + "(.+)$");
799
800     this._init = function(onsubmit) {
801         this._onsubmit = onsubmit;
802
803         this.load();
804         this.reset();
805     };
806
807     this.load = function() {
808         /* The Wizard object will handle simpler parts of the wizard (those
809          * parts with more-or-less static controls) itself, and will
810          * instantiate more specific objects to deal with more dynamic
811          * super-widgets (like the enum and chron editors).
812          */
813         this.steps = dojo.query("[id^='" + _step_prefix + "']").map(
814             function(o) {
815                 return dojo.attr(o, "id").match(_step_regex)[1];
816             }
817         );
818
819         this.enum_editor = new EnumEditor();
820         this.chron_editor = new ChronEditor();
821         this.calendar_change_editor = new CalendarChangeEditor();
822         this.regularity_editor = new RegularityEditor();
823
824         this.field_w = dojo.byId("hard_w");
825     };
826
827     this.reset = function() {
828         this.step = 0;
829         this.show_only_step(this.steps[0]);
830         this.step_bounds_check();
831     };
832
833     this.show_step = function(step) { show(_step_prefix + step); }
834     this.hide_step = function(step) { hide(_step_prefix + step); }
835
836     this.step_bounds_check = function() {
837         dojo.byId("wizard_previous_step").disabled = this.step < 1;
838         dojo.byId("wizard_next_step").disabled =
839             this.step >= this.steps.length -1;
840     };
841
842     this.show_only_step = function(to_keep) {
843         this.steps.forEach(
844             function(step) { if (step != to_keep) self.hide_step(step); }
845         );
846         this.show_step(to_keep);
847     };
848
849     this.previous_step = function() {
850         this.show_only_step(this.steps[--(this.step)]);
851         this.step_bounds_check();
852     };
853
854     /* Figure out the what step we're in, and proceed to the next */
855     this.next_step = function() {
856         this.show_only_step(this.steps[++(this.step)]);
857         this.step_bounds_check();
858     };
859
860     this.frequency_type_toggle = function(which) {
861         var other = which == "soft_w" ? "hard_w" : "soft_w";
862
863         dojo.byId(other).disabled = true;
864         dojo.byId(which).disabled = false;
865         dojo.byId(which).focus();
866
867         this.field_w = dojo.byId(which);
868     };
869
870     this.compile = function() {
871         var code = [
872             dojo.byId("ind1").value, dojo.byId("ind2").value,
873             "8", "1" /* TODO find out how to best deal with $8 */
874         ];
875
876         code = code.concat(this.enum_editor.compile());
877         code = code.concat(this.chron_editor.compile());
878
879         code = code.concat("w", this.field_w.value);
880
881         code = code.concat(this.calendar_change_editor.compile());
882         code = code.concat(this.regularity_editor.compile());
883
884         return code;
885     };
886
887     this.submit = function() {
888         this._onsubmit(js2JSON(this.compile()));
889         window.close();
890     };
891
892     this._init.apply(this, arguments);
893 }
894
895 function my_init() {
896     _chronstants.week.names = S("weeks").split(".");    /* ., sic */
897     _chronstants.weekday.names = S("weekdays").split(" ");
898     _chronstants.month.names = S("months").split(" ");
899     _chronstants.season.names = S("seasons").split(" ");
900
901     wizard = new Wizard(window.arguments[0]);
902 }