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;
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();
123 dojo.empty(this.node_editor_container);
124 this.dnd_source._ready = false;
127 this.is_sensible = function(mp) {
129 ["tag", "svf", "bool_op"].forEach(
130 function(field) { if (mp[field]()) need_one++; }
134 alert(localeStrings.POINT_NEEDS_ONE);
140 !mp.tag().match(/^\d{3}$/) ||
141 mp.subfield().length != 1 ||
142 !mp.subfield().match(/\S/) ||
143 mp.subfield().charCodeAt(0) < 32
145 alert(localeStrings.FAULTY_MARC);
153 this.build_vmsp = function() {
154 var match_point = new vmsp();
155 var controls = dojo.query("[fmfield]", this.node_editor_container);
156 for (var i = 0; i < controls.length; i++) {
157 var field = dojo.attr(controls[i], "fmfield");
158 var value = _simple_value_getter(controls[i]);
159 match_point[field](value);
162 if (!this.is_sensible(match_point)) return null; /* will alert() */
163 else return match_point;
166 this.update_draggable = function(draggable) {
169 if (!(mp = this.build_vmsp())) return; /* will alert() */
171 draggable.match_point = mp;
172 dojo.attr(draggable, "innerHTML", render_vmsp_label(mp));
173 this.dnd_source._ready = true;
176 this._add_consistent_controls = function(tgt) {
177 if (!this._consistent_controls) {
178 var trs = dojo.query("[consistent-controls]");
179 this._consistent_controls = [];
180 for (var i = 0; i < trs.length; i++)
181 this._consistent_controls[i] = dojo.clone(trs[i]);
182 dojo.empty(trs[0].parentNode);
185 this._consistent_controls.forEach(
186 function(node) { dojo.place(dojo.clone(node), tgt); }
190 this.add = function(type) {
193 /* a representation, not the editing widgets, but will also carry
194 * the fieldmapper object when dragged to the tree */
195 var draggable = dojo.create(
196 "li", {"innerHTML": localeStrings.DEFINE_MP}
199 /* these are the editing widgets */
200 var table = dojo.create("table", {"className": "node-editor"});
202 var nodes = _factories_by_type[type]();
203 for (var i = 0; i < nodes.length; i++) dojo.place(nodes[i], table);
205 this._add_consistent_controls(table);
209 "type": "submit", "value": localeStrings.OK,
210 "onclick": function() { self.update_draggable(draggable); }
212 "td", {"colspan": 2, "align": "center"},
213 dojo.create("tr", null, table)
217 dojo.place(table, this.node_editor_container, "only");
218 /* XXX around here attach other data structures to the node */
219 this.dnd_source.insertNodes(false, [draggable]);
222 this._init.apply(this, arguments);
225 function render_vmsp_label(point) {
226 /* quick and dirty */
227 if (point.bool_op()) {
228 return (openils.Util.isTrue(point.negate()) ? "N" : "") +
230 } else if (point.svf()) {
231 return (openils.Util.isTrue(point.negate()) ? "NOT " : "") +
232 point.svf() + " / " + _find_crad_by_name(point.svf()).label();
234 return (openils.Util.isTrue(point.negate()) ? "NOT " : "") +
235 point.tag() + " \u2021" + point.subfield();
239 function replace_mode(explicit) {
240 if (typeof explicit == "undefined")
241 tree.model.replace_mode ^= 1;
243 tree.model.replace_mode = explicit;
246 "replacer", "innerHTML",
248 (tree.model.replace_mode ? "EXIT" : "ENTER") + "_REPLACE_MODE"
251 dojo[tree.model.replace_mode ? "addClass" : "removeClass"](
252 "replacer", "replace-mode"
256 function delete_selected_in_tree() {
257 /* relies on the fact that we only have one tree that would have
258 * registered a dnd controller. */
259 _tree_dnd_controllers[0].getSelectedItems().forEach(
261 if (item === tree.model.root)
262 alert(localeStrings.LEAVE_ROOT_ALONE);
264 tree.model.store.deleteItem(item);
269 function new_match_set_tree() {
270 var point = new vmsp();
271 point.bool_op("AND");
276 "name": render_vmsp_label(point),
282 /* dojoize_match_set_tree() takes an argument, "point", that is actually a
283 * vmsp fieldmapper object with descendants fleshed hierarchically. It turns
284 * that into a syntactically flat array but preserving the hierarchy
285 * semantically in the language used by dojo data stores, i.e.,
288 * {'id': 'root', children:[{'_reference': '0'}, {'_reference': '1'}]},
289 * {'id': '0', children:[]},
290 * {'id': '1', children:[]}
294 function dojoize_match_set_tree(point, refgen) {
298 return new_match_set_tree();
304 var bathwater = point.children();
307 "id": (root ? "root" : refgen),
308 "name": render_vmsp_label(point),
309 "match_point": point.clone(),
312 point.children(bathwater);
314 var results = [item];
316 if (point.children()) {
317 for (var i = 0; i < point.children().length; i++) {
318 var child = point.children()[i];
319 item.children.push({"_reference": ++refgen});
320 results = results.concat(
321 dojoize_match_set_tree(child, refgen)
329 function render_vms_metadata(match_set) {
330 dojo.byId("vms-name").innerHTML = match_set.name();
331 dojo.byId("vms-owner").innerHTML =
332 aou.findOrgUnit(match_set.owner()).name();
333 dojo.byId("vms-mtype").innerHTML = match_set.mtype();
337 progress_dialog.show(true);
339 dojo.requireLocalization("openils.vandelay", "match_set");
340 localeStrings = dojo.i18n.getLocalization("openils.vandelay", "match_set");
342 pcrud = new openils.PermaCrud();
343 CGI = new openils.CGI();
345 if (!CGI.param("match_set")) {
346 alert(localeStrings.NO_CAN_DO);
347 progress_dialog.hide();
351 render_vms_metadata(pcrud.retrieve("vms", CGI.param("match_set")));
353 /* No-one should have hundreds of these or anything, but theoretically
354 * this could be problematic with a big enough list of crad objects. */
355 _crads = pcrud.retrieveAll("crad", {"order_by": {"crad": "label"}});
357 var match_set_tree = fieldmapper.standardRequest(
358 ["open-ils.vandelay", "open-ils.vandelay.match_set.get_tree"],
359 [openils.User.authtoken, CGI.param("match_set")]
362 var store = new dojo.data.ItemFileWriteStore({
366 "items": dojoize_match_set_tree(match_set_tree)
370 var tree_model = new openils.vandelay.TreeStoreModel({
371 "store": store, "query": {"id": "root"}
374 var src = new dojo.dnd.Source("src-here");
375 tree = new dijit.Tree(
378 "dndController": openils.vandelay.TreeDndSource,
380 "betweenThreshold": 5,
385 node_editor = new NodeEditor(src, "node-editor-container");
390 src, "onDndDrop", null,
391 function(source, nodes, copy, target) {
392 /* Because of the... interesting... characteristics of DnD
393 * design in dojo/dijit (at least as of 1.3), this callback will
394 * fire both for our working node dndSource and for the tree!
397 node_editor.clear(); /* ... because otherwise this acts like a
398 copy operation no matter what the user
399 does, even though we really want a
403 progress_dialog.hide();
406 openils.Util.addOnLoad(my_init);