1 /* The code in this file relies on common.js */
3 dojo.require("openils.Util");
8 return dojo.byId("serialStrings").getString("pattern_wizard." + k).
12 function _month_menuitems() {
13 /* XXX i18n, and also this is just pathetic in general, but a datepicker
14 * seemed wrong since we don't want a year. */
30 return dojo.create("menuitem", {"value": t[0], "label": t[1]});
35 function _date_validate(date_val, month_val) {
36 /* general purpose date validation irrespective of year */
37 date_val = date_val.trim();
39 if (!date_val.match(/^[0123]?\d$/))
42 date_val = Number(date_val); /* do NOT use parseInt */
43 month_val = Number(month_val);
47 } else if (month_val == 2) {
48 return date_val <= 29;
49 } else if ([1,3,5,7,8,10,12].indexOf(month_val) != -1) {
50 return date_val <= 31;
52 return date_val <= 30;
56 function CalendarChangeRow() {
59 this._init = function(template, id, manager) {
60 this.element = dojo.clone(template);
62 this.point_widgets = ["month", "season", "date"].map(
63 function(type) { return node_by_name(type, self.element); }
67 node_by_name("type", this.element), "oncommand", function(ev) {
68 self.point_widgets.forEach(
70 var active = (dojo.attr(w, "name") == ev.target.value);
71 (active ? show : hide)(w);
77 var date_month_selector = node_by_name("date_month", this.element);
80 node_by_name("date_day", this.element), "onchange", function(ev) {
81 if (_date_validate(ev.target.value,date_month_selector.value)){
84 alert(S("bad_date_value"));
91 this.remover = node_by_name("remover", this.element);
93 this.remover, "onclick", function() { manager.remove_row(id); }
97 this._compile_month = function() {
98 return node_by_name("month", this.element).value;
101 this._compile_season = function() {
102 return node_by_name("season", this.element).value;
105 this._compile_date = function() {
106 var n = Number(node_by_name("date_day", this.element).value);
112 return node_by_name("date_month", this.element).value + n;
115 this.compile = function() {
116 var type = node_by_name("type", this.element).value;
118 return type ? this["_compile_" + type]() : [];
121 this._init.apply(this, arguments);
124 function CalendarChangeEditor() {
127 this._init = function() {
131 this._get_template();
135 this._get_template = function() {
136 var temp_template = dojo.byId("calendar_row_template");
137 this.grid = temp_template.parentNode;
138 this.template = this.grid.removeChild(temp_template);
139 this.template.removeAttribute("id");
142 dojo.query("[name='month'] menupopup", this.template)[0],
143 dojo.query("[name='date_month'] menupopup", this.template)[0]
145 function(menupopup) {
146 _month_menuitems().forEach(
148 dojo.place(menuitem, menupopup, "last");
155 this.remove_row = function(id) {
156 hard_empty(this.rows[id].element);
157 dojo.destroy(this.rows[id].element);
158 delete this.rows[id];
160 dojo.byId("calendar_change_add_row").disabled = false;
163 this.add_row = function() {
164 var id = this.row_count++;
167 new CalendarChangeRow(dojo.clone(this.template), id, this);
168 dojo.place(this.rows[id].element, this.grid, "last");
171 this.toggle = function(ev) {
172 this.active = ev.target.checked;
173 (this.active ? show : hide)("calendar_change_editor_here");
176 this.compile = function() {
177 if (!this.active) return [];
181 openils.Util.objectProperties(this.rows).sort(num_sort).map(
182 function(key) { return self.rows[key].compile(); }
187 this._init.apply(this, arguments);
190 function ChronRow() {
193 this._init = function(template, subfield, manager) {
194 this.subfield = subfield;
195 this.element = dojo.clone(template);
198 node_by_name("caption_label", this.element),
199 "value", S("chronology." + subfield) + ":"
203 ["caption", "display_in_holding"].forEach(
204 function(o) { self.fields[o] = node_by_name(o, self.element); }
207 this.remover = node_by_name("remover", this.element);
209 this.remover, "onclick", function(){ manager.remove_row(subfield); }
213 this._init.apply(this, arguments);
216 function ChronEditor() {
217 /* TODO make this enforce unique caption values for each row? */
220 this._init = function() {
223 this.subfields = ["i", "j", "k", "l", "m"];
225 this._get_template();
229 this._get_template = function() {
230 var temp_template = dojo.byId("chron_row_template");
231 this.grid = temp_template.parentNode;
232 this.template = this.grid.removeChild(temp_template);
233 this.template.removeAttribute("id");
236 this._test_removability = function(subfield) {
237 var start = this.subfields.indexOf(subfield);
240 /* no such field, not OK to remove */
242 } else if (!this.subfields[start]) {
243 /* field row not present, not OK to remove */
247 var next = this.subfields[start + 1];
248 if (typeof(next) == "undefined") { /* last in set, ok to remove */
251 if (this.rows[next]) { /* NOT last in set, not ok to remove */
253 } else { /* last in set actually present, ok to remove */
259 this.remove_row = function(subfield) {
260 if (this._test_removability(subfield)) {
261 hard_empty(this.rows[subfield].element);
262 dojo.destroy(this.rows[subfield].element);
263 delete this.rows[subfield];
265 dojo.byId("chron_add_row").disabled = false;
267 alert(S("not_removable_row"));
271 this.add_row = function() {
272 var available = this.subfields.filter(
273 function(subfield) { return !Boolean(self.rows[subfield]); }
276 if (available.length) {
277 var subfield = available.shift();
278 if (!available.length)
279 dojo.byId("chron_add_row").disabled = true;
281 /* We shouldn't really be able to get here. */
285 this.rows[subfield] =
286 new ChronRow(dojo.clone(this.template), subfield, this);
288 dojo.place(this.rows[subfield].element, this.grid, "last");
291 this.toggle = function(ev) {
292 this.active = ev.target.checked;
293 (this.active ? show : hide)("chron_editor_here");
296 this.compile = function() {
297 if (!this.active) return [];
299 return this.subfields.filter(
300 function(subfield) { return Boolean(self.rows[subfield]); }
302 function(result, subfield) {
303 var caption = self.rows[subfield].fields.caption.value;
304 if (!self.rows[subfield].fields.display_in_holding.checked)
305 caption = "(" + caption + ")";
306 return result.concat([subfield, caption]);
311 this._init.apply(this, arguments);
317 this._init = function(template, subfield, manager) {
318 this.subfield = subfield;
319 this.element = dojo.clone(template);
322 ["caption","units_per","units_per_number","continuity","remover"].
324 function(o) { self.fields[o] = node_by_name(o, self.element); }
327 if (subfield == "a" || subfield == "g") {
328 ["units_per", "continuity"].forEach(
329 function(o) { soft_hide(node_by_name(o, self.element)); }
333 var caption_id = "enum_caption_" + subfield;
334 var caption_label = node_by_name("caption_label", this.element);
335 dojo.attr(this.fields.caption, "id", caption_id);
336 dojo.attr(caption_label, "control", caption_id);
337 dojo.attr(caption_label, "value", S("enumeration." + subfield) + ":");
339 this.remover = this.fields.remover;
341 this.remover, "onclick", function(){manager.remove_row(subfield);}
345 this._init.apply(this, arguments);
348 function EnumEditor() {
351 this._init = function() {
352 this.normal_rows = {};
355 this.normal_subfields = ["a","b","c","d","e","f"];
356 this.alt_subfields = ["g","h"];
358 this._get_template();
359 this.add_normal_row();
362 this._get_template = function() {
363 var temp_template = dojo.byId("enum_row_template");
364 this.grid = temp_template.parentNode;
365 this.template = this.grid.removeChild(temp_template);
366 this.template.removeAttribute("id");
369 this.remove_row = function(subfield) {
370 if (this._test_removability(subfield)) {
371 var add_button = "enum_add_normal_row";
372 var set = this.normal_rows;
373 if (!set[subfield]) {
375 add_button = "enum_add_alt_row";
378 hard_empty(set[subfield].element);
379 dojo.destroy(set[subfield].element);
380 delete set[subfield];
381 dojo.byId(add_button).disabled = false;
383 alert(S("not_removable_row"));
387 this._test_removability = function(id) {
388 var set = this.normal_subfields;
389 var rows = this.normal_rows;
390 var start = set.indexOf(id);
393 set = this.alt_subfields;
394 rows = this.alt_rows;
395 start = set.indexOf(id);
399 /* no such field, not OK to remove */
401 } else if (!set[start]) {
402 /* field row not present, not OK to remove */
406 var next = set[start + 1];
407 if (typeof(next) == "undefined") { /* last in set, ok to remove */
410 if (rows[next]) { /* NOT last in set, not ok to remove */
412 } else { /* last in set actually present, ok to remove */
418 this.add_normal_row = function() {
419 var available = this.normal_subfields.filter(
420 function(subfield) { return !Boolean(self.normal_rows[subfield]); }
422 if (available.length) {
423 var subfield = available.shift();
424 if (!available.length) {
425 /* If that was the last available normal row, disable the
426 * add rows button. */
427 dojo.byId("enum_add_normal_row").disabled = true;
430 /* We shouldn't really be able to get here. */
434 this.normal_rows[subfield] =
435 new EnumRow(dojo.clone(this.template), subfield, this);
437 dojo.place(this.normal_rows[subfield].element, this.grid, "last");
440 this.add_alt_row = function() {
441 var available = this.alt_subfields.filter(
442 function(subfield) { return !Boolean(self.alt_rows[subfield]); }
444 if (available.length) {
445 var subfield = available.shift();
446 if (!available.length) {
447 /* If that was the last available normal row, disable the
448 * add rows button. */
449 dojo.byId("enum_add_alt_row").disabled = true;
452 /* We shouldn't really be able to get here. */
456 this.alt_rows[subfield] =
457 new EnumRow(dojo.clone(this.template), subfield, this);
459 dojo.place(this.alt_rows[subfield].element, this.grid, "last");
462 this.toggle = function(ev) {
464 var use_calendar_change = dojo.byId("use_calendar_change");
466 this.active = ev.target.checked;
470 use_calendar_change.disabled = false;
472 use_calendar_change.checked = false;
473 use_calendar_change.doCommand();
474 use_calendar_change.disabled = true;
478 func("enum_editor_here");
481 this.compile = function() {
482 if (!this.active) return [];
484 var rows = dojo.mixin({}, this.normal_rows, this.alt_rows);
485 var subfields = [].concat(this.normal_subfields, this.alt_subfields);
487 return subfields.filter(
488 function(subfield) { return Boolean(rows[subfield]); }
490 function(result, subfield) {
491 var fields = rows[subfield].fields;
492 var pairs = [subfield, fields.caption.value];
494 if (subfield != "a" && subfield != "g") {
495 if (fields.units_per.value == "number") {
496 if (fields.units_per_number.value) {
497 pairs = pairs.concat([
498 "u", fields.units_per_number.value,
499 "v", fields.continuity.value
503 pairs = pairs.concat([
504 "u", fields.units_per.value,
505 "v", fields.continuity.value
510 return result.concat(pairs);
515 this._init.apply(this, arguments);
521 var _step_prefix = "wizard_step_";
522 var _step_regex = new RegExp("^" + _step_prefix + "(.+)$");
524 this._init = function(onsubmit) {
525 this._onsubmit = onsubmit;
531 this.load = function() {
532 /* The Wizard object will handle simpler parts of the wizard (those
533 * parts with more-or-less static controls) itself, and will
534 * instantiate more specific objects to deal with more dynamic
535 * super-widgets (like the enum and chron editors).
537 this.steps = dojo.query("[id^='" + _step_prefix + "']").map(
539 return dojo.attr(o, "id").match(_step_regex)[1];
543 this.enum_editor = new EnumEditor();
544 this.chron_editor = new ChronEditor();
545 this.calendar_change_editor = new CalendarChangeEditor();
547 this.field_w = dojo.byId("hard_w");
550 this.reset = function() {
552 this.show_only_step(this.steps[0]);
553 this.step_bounds_check();
556 this.show_step = function(step) { show(_step_prefix + step); }
557 this.hide_step = function(step) { hide(_step_prefix + step); }
559 this.step_bounds_check = function() {
560 dojo.byId("wizard_previous_step").disabled = this.step < 1;
561 dojo.byId("wizard_next_step").disabled =
562 this.step >= this.steps.length -1;
565 this.show_only_step = function(to_keep) {
567 function(step) { if (step != to_keep) self.hide_step(step); }
569 this.show_step(to_keep);
572 this.previous_step = function() {
573 this.show_only_step(this.steps[--(this.step)]);
574 this.step_bounds_check();
577 /* Figure out the what step we're in, and proceed to the next */
578 this.next_step = function() {
579 this.show_only_step(this.steps[++(this.step)]);
580 this.step_bounds_check();
583 this.frequency_type_toggle = function(which) {
584 var other = which == "soft_w" ? "hard_w" : "soft_w";
586 dojo.byId(other).disabled = true;
587 dojo.byId(which).disabled = false;
588 dojo.byId(which).focus();
590 this.field_w = dojo.byId(which);
593 this.compile = function() {
595 dojo.byId("ind1").value, dojo.byId("ind2").value,
596 "8", "1" /* TODO find out how to best deal with $8 */
599 code = code.concat(this.enum_editor.compile());
600 code = code.concat(this.chron_editor.compile());
602 code = code.concat("w", this.field_w.value);
604 code = code.concat(this.calendar_change_editor.compile());
609 this.submit = function() {
610 this._onsubmit(js2JSON(this.compile()));
614 this._init.apply(this, arguments);
618 wizard = new Wizard(window.arguments[0]);