1 dojo.require("dijit.Tree");
2 dojo.require("dijit.form.Button");
3 dojo.require("dojo.data.ItemFileWriteStore");
4 dojo.require("dojo.dnd.Source");
5 dojo.require("openils.vandelay.TreeDndSource");
6 dojo.require("openils.vandelay.TreeStoreModel");
7 dojo.require("openils.CGI");
8 dojo.require("openils.User");
9 dojo.require("openils.Util");
10 dojo.require("openils.PermaCrud");
11 dojo.require("openils.widget.ProgressDialog");
13 var localeStrings, node_editor, _crads, CGI, tree, match_set;
15 function _find_crad_by_name(name) {
16 for (var i = 0; i < _crads.length; i++) {
17 if (_crads[i].name() == name)
23 function NodeEditor() {
26 var _svf_select_template = null;
27 var _factories_by_type = {
29 if (!_svf_select_template) {
30 _svf_select_template = dojo.create(
31 "select", {"fmfield": "svf"}
33 for (var i=0; i<_crads.length; i++) {
36 "value": _crads[i].name(),
37 "innerHTML": _crads[i].label()
38 }, _svf_select_template
43 var select = dojo.clone(_svf_select_template);
44 dojo.attr(select, "id", "svf-select");
45 var label = dojo.create(
47 "for": "svf-select", "innerHTML": "Single-Value-Field:"
51 var tr = dojo.create("tr");
52 dojo.place(label, dojo.create("td", null, tr));
53 dojo.place(select, dojo.create("td", null, tr));
58 var rows = [dojo.create("tr"), dojo.create("tr")];
61 "for": "tag-input", "innerHTML": "Tag:"
62 }, dojo.create("td", null, rows[0])
71 }, dojo.create("td", null, rows[0])
75 "for": "subfield-input", "innerHTML": "Subfield: \u2021"
76 }, dojo.create("td", null, rows[1])
80 "id": "subfield-input",
85 }, dojo.create("td", null, rows[1])
89 "bool_op": function() {
90 var tr = dojo.create("tr");
93 {"for": "operator-select", "innerHTML": "Operator:"},
94 dojo.create("td", null, tr)
96 var select = dojo.create(
97 "select", {"fmfield": "bool_op", "id": "operator-select"},
98 dojo.create("td", null, tr)
100 dojo.create("option", {"value": "AND", "innerHTML": "AND"}, select);
101 dojo.create("option", {"value": "OR", "innerHTML": "OR"}, select);
107 function _simple_value_getter(control) {
108 if (typeof control.selectedIndex != "undefined")
109 return control.options[control.selectedIndex].value;
110 else if (dojo.attr(control, "type") == "checkbox")
111 return control.checked;
113 return control.value;
116 this._init = function(dnd_source, node_editor_container) {
117 this.dnd_source = dnd_source;
118 this.node_editor_container = dojo.byId(node_editor_container);
121 this.clear = function() {
122 this.dnd_source.selectAll().deleteSelectedNodes();
124 "em", {"innerHTML": localeStrings.WORKING_MP_HERE},
125 this.node_editor_container, "only"
127 this.dnd_source._ready = false;
130 this.is_sensible = function(mp) {
132 ["tag", "svf", "bool_op"].forEach(
133 function(field) { if (mp[field]()) need_one++; }
137 alert(localeStrings.POINT_NEEDS_ONE);
143 !mp.tag().match(/^\d{3}$/) ||
144 mp.subfield().length != 1 ||
145 !mp.subfield().match(/\S/) ||
146 mp.subfield().charCodeAt(0) < 32
148 alert(localeStrings.FAULTY_MARC);
156 this.build_vmsp = function() {
157 var match_point = new vmsp();
158 var controls = dojo.query("[fmfield]", this.node_editor_container);
159 for (var i = 0; i < controls.length; i++) {
160 var field = dojo.attr(controls[i], "fmfield");
161 var value = _simple_value_getter(controls[i]);
162 match_point[field](value);
165 if (!this.is_sensible(match_point)) return null; /* will alert() */
166 else return match_point;
169 this.update_draggable = function(draggable) {
172 if (!(mp = this.build_vmsp())) return; /* will alert() */
174 draggable.match_point = mp;
175 dojo.attr(draggable, "innerHTML", render_vmsp_label(mp));
176 this.dnd_source._ready = true;
179 this._add_consistent_controls = function(tgt) {
180 if (!this._consistent_controls) {
181 var trs = dojo.query("[consistent-controls]");
182 this._consistent_controls = [];
183 for (var i = 0; i < trs.length; i++)
184 this._consistent_controls[i] = dojo.clone(trs[i]);
185 dojo.empty(trs[0].parentNode);
188 this._consistent_controls.forEach(
189 function(node) { dojo.place(dojo.clone(node), tgt); }
193 this.add = function(type) {
196 /* a representation, not the editing widgets, but will also carry
197 * the fieldmapper object when dragged to the tree */
198 var draggable = dojo.create(
199 "li", {"innerHTML": localeStrings.DEFINE_MP}
202 /* these are the editing widgets */
203 var table = dojo.create("table", {"className": "node-editor"});
205 var nodes = _factories_by_type[type]();
206 for (var i = 0; i < nodes.length; i++) dojo.place(nodes[i], table);
208 this._add_consistent_controls(table);
212 "type": "submit", "value": localeStrings.OK,
213 "onclick": function() { self.update_draggable(draggable); }
215 "td", {"colspan": 2, "align": "center"},
216 dojo.create("tr", null, table)
220 dojo.place(table, this.node_editor_container, "only");
222 this.dnd_source.insertNodes(false, [draggable]);
225 try { dojo.query("select, input", table)[0].focus(); }
226 catch(E) { console.log(String(E)); }
230 this._init.apply(this, arguments);
233 function render_vmsp_label(point, minimal) {
234 /* quick and dirty */
235 if (point.bool_op()) {
236 return (openils.Util.isTrue(point.negate()) ? "N" : "") +
238 } else if (point.svf()) {
239 return (openils.Util.isTrue(point.negate()) ? "NOT " : "") + (
240 minimal ? point.svf() :
241 (point.svf() + " / " + _find_crad_by_name(point.svf()).label())
244 return (openils.Util.isTrue(point.negate()) ? "NOT " : "") +
245 point.tag() + " \u2021" + point.subfield();
249 function replace_mode(explicit) {
250 if (typeof explicit == "undefined")
251 tree.model.replace_mode ^= 1;
253 tree.model.replace_mode = explicit;
256 "replacer", "innerHTML",
258 (tree.model.replace_mode ? "EXIT" : "ENTER") + "_REPLACE_MODE"
261 dojo[tree.model.replace_mode ? "addClass" : "removeClass"](
262 "replacer", "replace-mode"
266 function delete_selected_in_tree() {
267 /* relies on the fact that we only have one tree that would have
268 * registered a dnd controller. */
269 _tree_dnd_controllers[0].getSelectedItems().forEach(
271 if (item === tree.model.root)
272 alert(localeStrings.LEAVE_ROOT_ALONE);
274 tree.model.store.deleteItem(item);
279 function new_match_set_tree() {
280 var point = new vmsp();
281 point.bool_op("AND");
286 "name": render_vmsp_label(point),
292 /* dojoize_match_set_tree() takes an argument, "point", that is actually a
293 * vmsp fieldmapper object with descendants fleshed hierarchically. It turns
294 * that into a syntactically flat array but preserving the hierarchy
295 * semantically in the language used by dojo data stores, i.e.,
298 * {'id': 'root', children:[{'_reference': '0'}, {'_reference': '1'}]},
299 * {'id': '0', children:[]},
300 * {'id': '1', children:[]}
304 function dojoize_match_set_tree(point, refgen) {
308 return new_match_set_tree();
314 var bathwater = point.children();
317 "id": (root ? "root" : refgen),
318 "name": render_vmsp_label(point),
319 "match_point": point.clone(),
322 point.children(bathwater);
324 var results = [item];
326 if (point.children()) {
327 for (var i = 0; i < point.children().length; i++) {
328 var child = point.children()[i];
329 item.children.push({"_reference": ++refgen});
330 results = results.concat(
331 dojoize_match_set_tree(child, refgen)
339 function render_vms_metadata(match_set) {
340 dojo.byId("vms-name").innerHTML = match_set.name();
341 dojo.byId("vms-owner").innerHTML =
342 aou.findOrgUnit(match_set.owner()).name();
343 dojo.byId("vms-mtype").innerHTML = match_set.mtype();
346 function redraw_expression_preview() {
349 tree.model.get_simple_tree(
354 render_expression_preview(r)
362 function render_expression_preview(r) {
363 if (r.children().length) {
364 return "(" + r.children().map(render_expression_preview).join(
365 " " + render_vmsp_label(r) + " "
367 } else if (!r.bool_op()) {
368 return render_vmsp_label(r, true /* minimal */);
374 function save_tree() {
375 progress_dialog.show(true);
379 tree.model.get_simple_tree(
381 fieldmapper.standardRequest(
382 ["open-ils.vandelay",
383 "open-ils.vandelay.match_set.update"],/* XXX TODO */{
385 openils.User.authtoken, match_set.id(), r
388 "oncomplete": function(r) {
389 progress_dialog.hide();
390 /* catch exceptions */
391 r = openils.Util.readResponse(r);
393 location.href = location.href;
403 progress_dialog.show(true);
405 dojo.requireLocalization("openils.vandelay", "match_set");
406 localeStrings = dojo.i18n.getLocalization("openils.vandelay", "match_set");
408 pcrud = new openils.PermaCrud();
409 CGI = new openils.CGI();
411 if (!CGI.param("match_set")) {
412 alert(localeStrings.NO_CAN_DO);
413 progress_dialog.hide();
418 match_set = pcrud.retrieve("vms", CGI.param("match_set"))
421 /* No-one should have hundreds of these or anything, but theoretically
422 * this could be problematic with a big enough list of crad objects. */
423 _crads = pcrud.retrieveAll("crad", {"order_by": {"crad": "label"}});
425 var match_set_tree = fieldmapper.standardRequest(
426 ["open-ils.vandelay", "open-ils.vandelay.match_set.get_tree"],
427 [openils.User.authtoken, CGI.param("match_set")]
430 var store = new dojo.data.ItemFileWriteStore({
434 "items": dojoize_match_set_tree(match_set_tree)
438 var tree_model = new openils.vandelay.TreeStoreModel({
439 "store": store, "query": {"id": "root"}
442 var src = new dojo.dnd.Source("src-here");
443 tree = new dijit.Tree(
446 "dndController": openils.vandelay.TreeDndSource,
448 "betweenThreshold": 5,
453 node_editor = new NodeEditor(src, "node-editor-container");
458 src, "onDndDrop", null,
459 function(source, nodes, copy, target) {
460 /* Because of the... interesting... characteristics of DnD
461 * design in dojo/dijit (at least as of 1.3), this callback will
462 * fire both for our working node dndSource and for the tree!
465 node_editor.clear(); /* ... because otherwise this acts like a
466 copy operation no matter what the user
467 does, even though we really want a
472 redraw_expression_preview();
474 progress_dialog.hide();
477 openils.Util.addOnLoad(my_init);