LP1251424 - Fix for submit button when placing holds in staff client
[working/Evergreen.git] / Open-ILS / web / js / dojo / openils / widget / HoldingCode.js
1 if (!dojo._hasResource["openils.widget.HoldingCode"]) {
2     dojo.provide("openils.widget.HoldingCode");
3     dojo.require("dijit.layout.ContentPane");
4     dojo.require("dijit.form.DropDownButton");
5     dojo.require("dijit.form.TextBox");
6
7     /* XXX These variables and functions preceding the call to dojo.declar()
8      * all pollute the window namespace.  They're not written as methods for
9      * the openils.widget.HoldingCode "class," but they should probably move
10      * into there anyway.
11      */
12
13     var _needed_fields = "abcdefghijklm";
14     var _season_store = new dojo.data.ItemFileReadStore({
15         "data": {
16             "identifier": "code",
17             "label": "label",
18             "items": [
19                 {"code": 21, "label": "Spring"},
20                 {"code": 22, "label": "Summer"},
21                 {"code": 23, "label": "Fall"},
22                 {"code": 24, "label": "Winter"}
23             ]
24         }
25     }); /* XXX i18n the above seasons. Also maybe don't
26          hardcode MFHD seasons here? */
27
28
29     function _prepare_ttip_dialog(div, wizard) {
30         dojo.empty(div);
31
32         var selector = wizard.args.scap_selector;
33         var caption_and_pattern = new scap().fromStoreItem(selector.item);
34
35         /* we're going to look for subfields a-h and i-m now. There may already
36          * be JS libs available to make this easier, but for now I'm doing this
37          * the fastest way I know. */
38
39         var pattern_code;
40         try {
41             pattern_code = JSON2js(caption_and_pattern.pattern_code());
42         } catch (E) {
43             /* no-op */;
44         }
45
46         if (!dojo.isArray(pattern_code)) {
47             div.innerHTML =
48                 "Have you selected a valid caption and pattern?";/* XXX i18n */
49             return;
50         }
51
52         var fields = [];
53         for (var i = 0; i < pattern_code.length; i += 2) {
54             var subfield = pattern_code[i];
55             var value = pattern_code[i + 1];
56
57             if (_needed_fields.indexOf(subfield) != -1)
58                 fields.push({"subfield": subfield, "caption": value, "pattern_value": value});
59         }
60
61         if (!fields.length) {
62             div.innerHTML = /* XXX i18n (below) */
63                 "No caption subfields in seleted caption and pattern";
64             return;
65         }
66
67         _prepare_ttip_dialog_fields(div, fields, wizard);
68     }
69
70     function _generate_dijit_for_field(field, tr) {
71         dojo.create("td", {"innerHTML": field.caption}, tr);
72
73         /* Any more special cases than this and we should switch to a dispatch
74          * table or something. */
75         var input;
76         if (field.pattern_value.match(/season/)) {
77             input = new dijit.form.FilteringSelect(
78                 {
79                     "name": field.subfield,
80                     "store": _season_store,
81                     "searchAttr": "label",
82                     "scrollOnFocus": false
83                 }, dojo.create("td", null, tr)
84             );
85         } else {
86             input = new dijit.form.TextBox(
87                 {"name": field.subfield, "scrollOnFocus": false},
88                 dojo.create("td", null, tr)
89             );
90         }
91         input.startup();
92
93         return input;
94     }
95
96     function _prepare_ttip_dialog_fields(div, fields, wizard) {
97         /* XXX TODO Don't assume these defaults for the indicators and $8, and
98          * provide reasonable control over them. */
99         var holding_code = ["4", "1", "8", "1"];
100         var inputs = [];
101
102         wizard.wizard_button.attr("disabled", true);
103         var table = dojo.create("table", {"className": "serial-holding-code"});
104         fields.forEach(
105             function(field) {
106                 var tr = dojo.create("tr", null, table);
107
108                 field.caption = field.caption.replace(/^\(?([^\)]+)\)?$/, "$1");
109                 if (field.subfield > "h") {
110                     field.caption = field.caption.slice(0,1).toUpperCase() +
111                         field.caption.slice(1);
112                 }
113
114                 var input = _generate_dijit_for_field(field, tr);
115                 wizard.preset_input_by_date(input, field.caption.toLowerCase());
116                 inputs.push({"subfield": field.subfield, "input": input});
117             }
118         );
119
120         new dijit.form.Button(
121             {
122                 "label": "Create Holding Code",
123                 "onClick": function() {
124                     inputs.forEach(
125                         function(input) {
126                             var value = input.input.attr("value");
127                             if (value === null || value === "") {
128                                 /* XXX i18n */
129                                 alert("A valid holding code cannot be " +
130                                     "produced with any blank fields.");
131                             }
132                             holding_code.push(input.subfield);
133                             holding_code.push(value);
134                         }
135                     );
136                     wizard.code_text_box.attr("value", js2JSON(holding_code));
137                     wizard.wizard_button.attr("disabled", false);
138                     dojo.empty(div);
139                 },
140                 "scrollOnFocus": false
141             }, dojo.create(
142                 "span", null, dojo.create(
143                     "td", {"colspan": 2},
144                     dojo.create("tr", null, table)
145                 )
146             )
147         );
148         dojo.place(table, div, "only");
149     }
150
151     /* Approximate a season value given a date using the same logic as
152      * OpenILS::Utils::MFHD::Holding::chron_to_date().
153      */
154     function _loose_season(D) {
155         var m = D.getMonth() + 1;
156         var d = D.getDate();
157
158         if (
159             (m == 1 || m == 2) || (m == 12 && d >= 21) || (m == 3 && d < 20)
160         ) {
161             return 24;  /* MFHD winter */
162         } else if (
163             (m == 4 || m == 5) || (m == 3 && d >= 20) || (m == 6 && d < 21)
164         ) {
165             return 21;  /* spring */
166         } else if (
167             (m == 7 || m == 8) || (m == 6 && d >= 21) || (m == 9 && d < 22)
168         ) {
169             return 22;  /* summer */
170         } else {
171             return 23;  /* autumn */
172         }
173     }
174
175     dojo.declare(
176         "openils.widget.HoldingCode", dijit.layout.ContentPane, {
177             "constructor": function(args) {
178                 this.args = args || {};
179             },
180
181             "startup": function() {
182                 var self = this;
183                 this.inherited(arguments);
184
185                 var dialog_div = dojo.create(
186                     "div", {
187                         "style": "padding:1em;margin:0;text-align:center;"
188                     }, this.domNode
189                 );
190                 var target_div = dojo.create("div", null, this.domNode);
191                 dojo.create("br", null, this.domNode);
192
193                 this.wizard_button = new dijit.form.Button(
194                     {
195                         "label": "Wizard" /* XXX i18n */,
196                         "onClick": function() {
197                             _prepare_ttip_dialog(target_div, self);
198                         }
199                     },
200                     dojo.create("span", null, dialog_div)
201                 );
202
203                 this.code_text_box = new dijit.form.ValidationTextBox(
204                     {}, dojo.create("div", null, this.domNode)
205                 );
206
207                 /* This by no means will fully validate plausible holding codes,
208                  * but it will perhaps help users who experiment with typing
209                  * the holding code in here freehand (a little). */
210                 this.code_text_box.validator = function(value) {
211                     try {
212                         return dojo.isArray(dojo.fromJson(value));
213                     } catch(E) {
214                         return false;
215                     }
216                 };
217
218                 this.code_text_box.startup();
219             },
220
221             "attr": function(name, value) {
222                 if (name == "value") {
223                     /* XXX can this get called before any subdijits are
224                      * built (before startup() is run)? */
225                     if (value) {
226                         this.code_text_box.attr(name, value);
227                     }
228                     return this.code_text_box.attr(name);
229                 } else {
230                     return this.inherited(arguments);
231                 }
232             },
233
234             "update_scap_selector": function(selector) {
235                 this.args.scap_selector = selector;
236                 this.attr("value", "");
237             },
238
239             "preset_input_by_date": function(input, chron_part) {
240                 try {
241                     input.attr("value", {
242                             /* NOTE: week is specifically not covered. I'm
243                              * not sure there's an acceptably standard way
244                              * to number the weeks in a year.  Do we count
245                              * from the week of January 1? Or the first week
246                              * with a day of the week matching our example
247                              * date?  Do weeks run Mon-Sun or Sun-Sat?
248                              */
249                             "year": function(d) { return d.getFullYear(); },
250                             "season": function(d) { return _loose_season(d); },
251                             "month": function(d) { return d.getMonth() + 1; },
252                             "day": function(d) { return d.getDate(); },
253                             "hour": function(d) { return d.getHours(); },
254                         }[chron_part](this.date_widget.attr("value"))
255                     );
256                 } catch (E) {
257                     ; /* Oh well; can't win them all. */
258                 }
259             },
260
261             "date_widget": null
262         }
263     );
264 }