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 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 (ev.target.checked ? show : hide)("calendar_change_editor_here");
175 this.compile = function() {
178 openils.Util.objectProperties(this.rows).sort(num_sort).map(
179 function(key) { return self.rows[key].compile(); }
184 this._init.apply(this, arguments);
187 function ChronRow() {
190 this._init = function(template, subfield, manager) {
191 this.subfield = subfield;
192 this.element = dojo.clone(template);
195 node_by_name("caption_label", this.element),
196 "value", S("chronology." + subfield) + ":"
200 ["caption", "display_in_holding"].forEach(
201 function(o) { self.fields[o] = node_by_name(o, self.element); }
204 this.remover = node_by_name("remover", this.element);
206 this.remover, "onclick", function(){ manager.remove_row(subfield); }
210 this._init.apply(this, arguments);
213 function ChronEditor() {
214 /* TODO make this enforce unique caption values for each row? */
217 this._init = function() {
220 this.subfields = ["i", "j", "k", "l", "m"];
222 this._get_template();
226 this._get_template = function() {
227 var temp_template = dojo.byId("chron_row_template");
228 this.grid = temp_template.parentNode;
229 this.template = this.grid.removeChild(temp_template);
230 this.template.removeAttribute("id");
233 this._test_removability = function(subfield) {
234 var start = this.subfields.indexOf(subfield);
237 /* no such field, not OK to remove */
239 } else if (!this.subfields[start]) {
240 /* field row not present, not OK to remove */
244 var next = this.subfields[start + 1];
245 if (typeof(next) == "undefined") { /* last in set, ok to remove */
248 if (this.rows[next]) { /* NOT last in set, not ok to remove */
250 } else { /* last in set actually present, ok to remove */
256 this.remove_row = function(subfield) {
257 if (this._test_removability(subfield)) {
258 hard_empty(this.rows[subfield].element);
259 dojo.destroy(this.rows[subfield].element);
260 delete this.rows[subfield];
262 dojo.byId("chron_add_row").disabled = false;
264 alert(S("not_removable_row"));
268 this.add_row = function() {
269 var available = this.subfields.filter(
270 function(subfield) { return !Boolean(self.rows[subfield]); }
273 if (available.length) {
274 var subfield = available.shift();
275 if (!available.length)
276 dojo.byId("chron_add_row").disabled = true;
278 /* We shouldn't really be able to get here. */
282 this.rows[subfield] =
283 new ChronRow(dojo.clone(this.template), subfield, this);
285 dojo.place(this.rows[subfield].element, this.grid, "last");
288 this.toggle = function(ev) {
289 (ev.target.checked ? show : hide)("chron_editor_here");
292 this.compile = function() {
293 return this.subfields.filter(
294 function(subfield) { return Boolean(self.rows[subfield]); }
296 function(result, subfield) {
297 var caption = self.rows[subfield].fields.caption.value;
298 if (!self.rows[subfield].fields.display_in_holding.checked)
299 caption = "(" + caption + ")";
300 return result.concat([subfield, caption]);
305 this._init.apply(this, arguments);
311 this._init = function(template, subfield, manager) {
312 this.subfield = subfield;
313 this.element = dojo.clone(template);
316 ["caption","units_per","units_per_number","continuity","remover"].
318 function(o) { self.fields[o] = node_by_name(o, self.element); }
321 if (subfield == "a" || subfield == "g") {
322 ["units_per", "continuity"].forEach(
323 function(o) { soft_hide(node_by_name(o, self.element)); }
327 var caption_id = "enum_caption_" + subfield;
328 var caption_label = node_by_name("caption_label", this.element);
329 dojo.attr(this.fields.caption, "id", caption_id);
330 dojo.attr(caption_label, "control", caption_id);
331 dojo.attr(caption_label, "value", S("enumeration." + subfield) + ":");
333 this.remover = this.fields.remover;
335 this.remover, "onclick", function(){manager.remove_row(subfield);}
339 this._init.apply(this, arguments);
342 function EnumEditor() {
345 this._init = function() {
346 this.normal_rows = {};
349 this.normal_subfields = ["a","b","c","d","e","f"];
350 this.alt_subfields = ["g","h"];
352 this._get_template();
353 this.add_normal_row();
356 this._get_template = function() {
357 var temp_template = dojo.byId("enum_row_template");
358 this.grid = temp_template.parentNode;
359 this.template = this.grid.removeChild(temp_template);
360 this.template.removeAttribute("id");
363 this.remove_row = function(subfield) {
364 if (this._test_removability(subfield)) {
365 var add_button = "enum_add_normal_row";
366 var set = this.normal_rows;
367 if (!set[subfield]) {
369 add_button = "enum_add_alt_row";
372 hard_empty(set[subfield].element);
373 dojo.destroy(set[subfield].element);
374 delete set[subfield];
375 dojo.byId(add_button).disabled = false;
377 alert(S("not_removable_row"));
381 this._test_removability = function(id) {
382 var set = this.normal_subfields;
383 var rows = this.normal_rows;
384 var start = set.indexOf(id);
387 set = this.alt_subfields;
388 rows = this.alt_rows;
389 start = set.indexOf(id);
393 /* no such field, not OK to remove */
395 } else if (!set[start]) {
396 /* field row not present, not OK to remove */
400 var next = set[start + 1];
401 if (typeof(next) == "undefined") { /* last in set, ok to remove */
404 if (rows[next]) { /* NOT last in set, not ok to remove */
406 } else { /* last in set actually present, ok to remove */
412 this.add_normal_row = function() {
413 var available = this.normal_subfields.filter(
414 function(subfield) { return !Boolean(self.normal_rows[subfield]); }
416 if (available.length) {
417 var subfield = available.shift();
418 if (!available.length) {
419 /* If that was the last available normal row, disable the
420 * add rows button. */
421 dojo.byId("enum_add_normal_row").disabled = true;
424 /* We shouldn't really be able to get here. */
428 this.normal_rows[subfield] =
429 new EnumRow(dojo.clone(this.template), subfield, this);
431 dojo.place(this.normal_rows[subfield].element, this.grid, "last");
434 this.add_alt_row = function() {
435 var available = this.alt_subfields.filter(
436 function(subfield) { return !Boolean(self.alt_rows[subfield]); }
438 if (available.length) {
439 var subfield = available.shift();
440 if (!available.length) {
441 /* If that was the last available normal row, disable the
442 * add rows button. */
443 dojo.byId("enum_add_alt_row").disabled = true;
446 /* We shouldn't really be able to get here. */
450 this.alt_rows[subfield] =
451 new EnumRow(dojo.clone(this.template), subfield, this);
453 dojo.place(this.alt_rows[subfield].element, this.grid, "last");
456 this.toggle = function(ev) {
458 var use_calendar_change = dojo.byId("use_calendar_change");
460 if (ev.target.checked) {
462 use_calendar_change.disabled = false;
464 use_calendar_change.checked = false;
465 use_calendar_change.doCommand();
466 use_calendar_change.disabled = true;
470 func("enum_editor_here");
473 this.compile = function() {
474 var rows = dojo.mixin({}, this.normal_rows, this.alt_rows);
475 var subfields = [].concat(this.normal_subfields, this.alt_subfields);
477 return subfields.filter(
478 function(subfield) { return Boolean(rows[subfield]); }
480 function(result, subfield) {
481 var fields = rows[subfield].fields;
482 var pairs = [subfield, fields.caption.value];
484 if (subfield != "a" && subfield != "g") {
485 if (fields.units_per.value == "number") {
486 if (fields.units_per_number.value) {
487 pairs = pairs.concat([
488 "u", fields.units_per_number.value,
489 "v", fields.continuity.value
493 pairs = pairs.concat([
494 "u", fields.units_per.value,
495 "v", fields.continuity.value
500 return result.concat(pairs);
505 this._init.apply(this, arguments);
511 var _step_prefix = "wizard_step_";
512 var _step_regex = new RegExp("^" + _step_prefix + "(.+)$");
514 this._init = function(onsubmit) {
515 this._onsubmit = onsubmit;
521 this.load = function() {
522 /* The Wizard object will handle simpler parts of the wizard (those
523 * parts with more-or-less static controls) itself, and will
524 * instantiate more specific objects to deal with more dynamic
525 * super-widgets (like the enum and chron editors).
527 this.steps = dojo.query("[id^='" + _step_prefix + "']").map(
529 return dojo.attr(o, "id").match(_step_regex)[1];
533 this.enum_editor = new EnumEditor();
534 this.chron_editor = new ChronEditor();
535 this.calendar_change_editor = new CalendarChangeEditor();
537 this.field_w = dojo.byId("hard_w");
540 this.reset = function() {
542 this.show_only_step(this.steps[0]);
543 this.step_bounds_check();
546 this.show_step = function(step) { show(_step_prefix + step); }
547 this.hide_step = function(step) { hide(_step_prefix + step); }
549 this.step_bounds_check = function() {
550 dojo.byId("wizard_previous_step").disabled = this.step < 1;
551 dojo.byId("wizard_next_step").disabled =
552 this.step >= this.steps.length -1;
555 this.show_only_step = function(to_keep) {
557 function(step) { if (step != to_keep) self.hide_step(step); }
559 this.show_step(to_keep);
562 this.previous_step = function() {
563 this.show_only_step(this.steps[--(this.step)]);
564 this.step_bounds_check();
567 /* Figure out the what step we're in, and proceed to the next */
568 this.next_step = function() {
569 this.show_only_step(this.steps[++(this.step)]);
570 this.step_bounds_check();
573 this.frequency_type_toggle = function(which) {
574 var other = which == "soft_w" ? "hard_w" : "soft_w";
576 dojo.byId(other).disabled = true;
577 dojo.byId(which).disabled = false;
578 dojo.byId(which).focus();
580 this.field_w = dojo.byId(which);
583 this.compile = function() {
585 dojo.byId("ind1").value, dojo.byId("ind2").value,
586 "8", "1" /* TODO find out how to best deal with $8 */
589 code = code.concat(this.enum_editor.compile());
590 code = code.concat(this.chron_editor.compile());
592 code = code.concat("w", this.field_w.value);
594 code = code.concat(this.calendar_change_editor.compile());
599 this.submit = function() {
600 this._onsubmit(js2JSON(this.compile()));
604 this._init.apply(this, arguments);
608 wizard = new Wizard(window.arguments[0]);