1 dojo.require("dojo.date.stamp");
2 dojo.require("dojox.encoding.base64");
3 dojo.require("openils.widget.AutoGrid");
4 dojo.require("openils.widget.AutoWidget");
5 dojo.require("openils.widget.XULTermLoader");
6 dojo.require("openils.PermaCrud");
7 dojo.require('dijit.layout.TabContainer');
9 if (!localeStrings) { /* we can do this because javascript doesn't have block
11 dojo.requireLocalization('openils.acq', 'acq');
12 var localeStrings = dojo.i18n.getLocalization('openils.acq', 'acq');
15 var termSelectorFactory;
19 var pcrud = new openils.PermaCrud();
20 var cgi = new openils.CGI();
22 /* typing save: add {get,set}Value() to all HTML <select> elements */
23 HTMLSelectElement.prototype.getValue = function() {
24 return this.options[this.selectedIndex].value;
27 /* only sets the selected value if such an option is actually available */
28 HTMLSelectElement.prototype.setValue = function(s) {
29 for (var i = 0; i < this.options.length; i++) {
30 if (s == this.options[i].value) {
31 this.selectedIndex = i;
37 /* minor formatting function used by autogrids in unified.tt2 */
38 function getName(rowIndex, item) {
41 "name": this.grid.store.getValue(item, "name") ||
42 localeStrings.UNNAMED,
43 "id": this.grid.store.getValue(item, "id")
48 /* quickly find elements by the value of a "name" attribute */
49 function nodeByName(name, root) {
50 return dojo.query("[name='" + name + "']", root)[0];
54 openils.Util.hide("acq-unified-hide-form");
55 openils.Util.show("acq-unified-reveal-form", "inline");
56 openils.Util.hide("acq-unified-form");
59 function revealForm() {
60 openils.Util.hide("acq-unified-reveal-form");
61 openils.Util.show("acq-unified-hide-form", "inline");
62 openils.Util.show("acq-unified-form");
65 /* The TermSelectorFactory will be instantiated by the TermManager. It
66 * provides HTML select controls whose options are all the searchable
67 * fields. Selecting a field from one of these controls will create the
68 * appopriate type of corresponding widget for the user to enter a search
69 * term against the selected field.
71 function TermSelectorFactory(terms) {
74 this.onlyBibFriendly = false;
76 this.template = dojo.create("select");
77 this.template.appendChild(
78 dojo.create("option", {
79 "disabled": "disabled",
80 "selected": "selected",
82 "innerHTML": localeStrings.SELECT_SEARCH_FIELD
86 /* Create abbreviations for class names to make field categories
87 * more readable in field selector control. */
88 this._abbreviate = function(s) {
90 for (var i = 0; i < s.length; i++) {
92 if (!i) result = s[i];
93 else if (last == " ") result += s[i];
100 var selectorMethods = {
101 /* Important: within the following functions, "this" refers to one
102 * HTMLSelect object, and "self" refers to the TermSelectorFactory. */
103 "getTerm": function() { return this.valueToTerm(this.getValue()); },
104 "valueToTerm": function(value) {
105 var parts = value.split(":");
106 if (!parts || parts.length != 2) return null;
108 self.terms[parts[0]][parts[1]],
109 {"hint": parts[0], "field": parts[1]}
112 "onlyBibFriendly": function(yes) {
114 for (var i = 0; i < this.options.length; i++) {
115 if (this.options[i].value) {
116 var term = this.valueToTerm(this.options[i].value);
117 this.options[i].disabled = !term.bib_friendly;
121 for (var i = 0; i < this.options.length; i++) {
122 if (this.options[i].value)
123 this.options[i].disabled = false;
127 "makeWidget": function(
128 parentNode, wStore, matchHow, value, noFocus, callback
130 var term = this.getTerm();
131 var widgetKey = this.uniq;
134 target = termManager.getLinkTarget(term);
136 void(0); /* ok for this to fail (it doesn't handle acqlia right,
137 but we don't need it to in this case). */
140 if (matchHow.getValue() == "__in") {
141 new openils.widget.XULTermLoader({
142 "parentNode": parentNode
145 wStore[widgetKey] = w;
146 if (typeof(callback) == "function")
147 callback(term, widgetKey);
148 if (typeof(value) != "undefined")
149 w.attr("value", value);
150 /* I would love for the following call not to be
151 * necessary, so that updating the value of the dijit
152 * would lead to this automatically, but I can't yet
153 * figure out the correct way to do this in Dojo.
158 } else if (term.hint == "acqlia" ||
159 (term.hint == "jub" && term.field == "eg_bib_id") ||
160 term.datatype == "org_unit" ||
161 (term.datatype == "link" && target == "au")) {
162 /* The test for jub.eg_bib_id is a special case to prevent
163 * AutoFieldWidget from trying to render a ridiculous dropdown
164 * of every bib record ID in the system. */
165 wStore[widgetKey] = dojo.create(
166 "input", {"type": "text"}, parentNode, "only"
168 if (typeof(value) != "undefined")
169 wStore[widgetKey].value = value;
171 wStore[widgetKey].focus();
172 if (typeof(callback) == "function")
173 callback(term, widgetKey);
176 dojo.connect(wStore[widgetKey], 'onkeyup',
178 if(e.keyCode == dojo.keys.ENTER) {
179 resultManager.go(termManager.buildSearchObject());
185 new openils.widget.AutoFieldWidget({
186 "fmClass": term.hint,
187 "fmField": term.field,
188 "noDisablePkey": true,
189 "parentNode": dojo.create("span", null, parentNode, "only")
192 wStore[widgetKey] = w;
193 if (typeof(value) != "undefined") {
194 if (w.declaredClass.match(/Check/))
195 w.attr("checked", value == "t");
197 w.attr("value", value);
201 if (typeof(callback) == "function")
202 callback(term, widgetKey);
205 dojo.connect(w.domNode, 'onkeyup',
207 if(e.keyCode == dojo.keys.ENTER) {
208 resultManager.go(termManager.buildSearchObject());
218 for (var hint in this.terms) {
219 var optgroup = dojo.create(
220 "optgroup", {"value": "", "label": this.terms[hint].__label}
222 var prefix = this._abbreviate(this.terms[hint].__label);
224 for (var field in this.terms[hint]) {
225 if (!/^__/.test(field)) {
226 optgroup.appendChild(
227 dojo.create("option", {
228 "class": "acq-unified-option-regular",
229 "value": hint + ":" + field,
230 "innerHTML": prefix + " - " +
231 this.terms[hint][field].label
237 this.template.appendChild(optgroup);
240 this.make = function(n) {
241 var node = dojo.clone(this.template);
243 dojo.attr(node, "id", "term-" + n);
244 for (var name in selectorMethods)
245 node[name] = selectorMethods[name];
246 if (this.onlyBibFriendly)
247 node.onlyBibFriendly(true);
252 /* The term manager retrieves information from the IDL about all the fields
253 * in the classes that we consider searchable for our purpose. It maintains
254 * a dynamic HTML table of search terms, using the TermSelectorFactory
255 * to generate search field selectors, which in turn provide appropriate
256 * widgets for entering search terms. The TermManager provides search term
257 * modifiers (fuzzy searching, not searching). The TermManager also handles
258 * adding and removing rows of search terms, as well as building the search
259 * query to pass to the middle layer from the search term widgets.
261 function TermManager() {
264 /* All the keys in this object are bib-search-friendly attributes, but the
265 * boolean values indicate whether they should be searched by their
266 * field name as such, or simply mapped to "keyword". */
267 this.bibFriendlyAttrNames = {
268 "author": true, "title": true,
269 "isbn": false, "issn": false, "upc": false
273 ["jub", "acqpl", "acqpo", "acqinv", "acqlid"].forEach(
276 o.__label = fieldmapper.IDL.fmclasses[hint].label;
277 fieldmapper.IDL.fmclasses[hint].fields.forEach(
279 if (!field.virtual) {
281 "label": field.label, "datatype": field.datatype
286 self.terms[hint] = o;
290 this.terms.acqlia = {"__label": fieldmapper.IDL.fmclasses.acqlia.label};
291 pcrud.retrieveAll("acqliad", {"order_by": {"acqliad": "id"}}).forEach(
293 self.terms.acqlia[def.id()] = {
294 "label": def.description(),
297 (typeof(self.bibFriendlyAttrNames[def.code()]) !=
300 self.bibFriendlyAttrNames[def.code()] ?
301 def.code() : "keyword"
306 this.selectorFactory = new TermSelectorFactory(this.terms);
307 this.template = dojo.byId("acq-unified-terms-tbody").
308 removeChild(dojo.byId("acq-unified-terms-row-tmpl"));
309 dojo.attr(this.template, "id");
311 this.lastResultType = null;
316 dojo.byId("acq-unified-result-type").onchange = function() {
317 self.resultTypeChange(this.getValue());
320 this.allRowIds = function() {
321 return dojo.query("tr[id^='term-row-']", "acq-unified-terms-tbody").
322 map(function(o) { return o.id.match(/^term-row-(\d+)$/)[1]; });
325 this._row = function(id) { return dojo.byId("term-row-" + id); };
326 this._selector = function(id) { return dojo.byId("term-" + id); };
327 this._match_how = function(id) { return dojo.byId("term-match-" + id); };
329 this._updateMatchHowForField = function(term, key) {
330 /* NOTE important to use self, not this, in this function.
332 * Based on the selected field (its datatype and the kind of widget
333 * that AutoFieldWidget provides for it) we update the possible
334 * choices in the mach_how selector.
336 var w = self.widgets[key];
337 var can_do_fuzzy, can_do_in;
338 if (term.datatype == "id") {
339 can_do_fuzzy = false;
341 } else if (term.datatype == "link") {
342 var target = self.getLinkTarget(term);
343 can_do_fuzzy = (target == "au");
344 can_do_in = (target == "bre"); /* XXX might revise later */
345 } else if (typeof(w.declaredClass) != "undefined") {
346 can_do_fuzzy = can_do_in =
347 Boolean(w.declaredClass.match(/form\.Text|XULT/));
349 var type = dojo.attr(w, "type");
351 can_do_fuzzy = can_do_in = (type == "text");
353 can_do_fuzzy = can_do_in = false;
356 self.matchHowAllow(key, "__fuzzy", can_do_fuzzy);
357 self.matchHowAllow(key, "__in", can_do_in);
359 var inequalities = (term.datatype == "timestamp");
360 self.matchHowAllow(key, "__gte", inequalities);
361 self.matchHowAllow(key, "__lte", inequalities);
364 this.removerButton = function(n) {
365 return dojo.create("button", {
367 "class": "acq-unified-remover",
368 "onclick": function() { self.removeRow(n); }
372 this.matchHowAllow = function(where, what, which, exact) {
374 "option[value" + (exact ? "" : "*") + "='" + what + "']",
375 typeof(where) == "object" ? where : this._match_how(where)
376 ).forEach(function(o) { o.disabled = !which; });
379 this.getLinkTarget = function(term) {
380 return fieldmapper.IDL.fmclasses[term.hint].
381 field_map[term.field]["class"];
384 this.updateRowWidget = function(id, value, noFocus) {
385 var where = nodeByName("widget", this._row(id));
387 delete this.widgets[id];
390 this._selector(id).makeWidget(
391 where, this.widgets, this._match_how(id), value, noFocus,
392 this._updateMatchHowForField
396 this.resultTypeChange = function(resultType) {
398 this.lastResultType == "lineitem_and_bib" &&
399 resultType != "lineitem_and_bib"
401 /* Re-enable all non-bib-friendly fields in all search term
402 * field selectors. */
403 this.allRowIds().forEach(
405 self._selector(id).onlyBibFriendly(false);
406 self.matchHowAllow(id, "", true, /* exact */ true);
407 self.matchHowAllow(id, "__not", true, /* exact */ true);
410 /* Tell the selector factory to create new search term field
411 * selectors with all fields, not just bib-friendly ones. */
412 this.selectorFactory.onlyBibFriendly = false;
414 this.lastResultType != "lineitem_and_bib" &&
415 resultType == "lineitem_and_bib"
417 /* Remove all search term rows set to non-bib-friendly fields. */
418 this.allRowIds().forEach(
420 var term = self._selector(id).getTerm();
422 !self.terms[term.hint][term.field].bib_friendly) {
427 /* Disable all non-bib-friendly fields in all remaining search term
428 * field selectors. */
429 this.allRowIds().forEach(
431 self._selector(id).onlyBibFriendly(true);
432 self.matchHowAllow(id, "", false, /* exact */ true);
433 self.matchHowAllow(id, "__not", false, /* exact */ true);
436 /* Tell the selector factory to create new search term field
437 * selectors with only bib friendly options. */
438 this.selectorFactory.onlyBibFriendly = true;
440 this.lastResultType = resultType;
443 /* this method is particularly kludgy... puts back together a string
444 * based on object properties that might arrive in indeterminate order. */
445 this._term_reverse_match_how = function(term) {
446 /* only two-key combination we use */
447 if (term.__not && term.__fuzzy)
448 return "__not,__fuzzy";
450 /* only other possibilities are single-key or no key */
451 for (var key in term) {
460 this._term_reverse_selector_field = function(term) {
461 for (var key in term) {
462 if (!/^__/.test(key))
468 this._term_reverse_selector_value = function(term) {
469 for (var key in term) {
470 if (!/^__/.test(key))
476 this.addRow = function(term, hint) {
477 var uniq = (this.rowId)++;
479 var row = dojo.clone(this.template);
480 dojo.attr(row, "id", "term-row-" + uniq);
482 var selector = this.selectorFactory.make(uniq);
484 selector, "onchange", function() { self.updateRowWidget(uniq); }
487 var match_how = dojo.query("select", nodeByName("match", row))[0];
488 dojo.attr(match_how, "id", "term-match-" + uniq);
489 dojo.attr(match_how, "selectedIndex", 0);
491 match_how, "onchange",
493 if (this.getValue() == "__in") {
494 self.updateRowWidget(uniq);
496 } else if (this.was_in) {
497 self.updateRowWidget(uniq);
500 if (self.widgets[uniq]) self.widgets[uniq].focus();
504 /* Kind of inelegant; could be improved: this section turns off
505 * match-type options that don't apply to bib searching. */
508 !this.selectorFactory.onlyBibFriendly, /* exact */ true
512 !this.selectorFactory.onlyBibFriendly, /* exact */ true
514 if (this.selectorFactory.onlyBibFriendly)
515 match_how.setValue("__fuzzy");
517 nodeByName("selector", row).appendChild(selector);
518 nodeByName("remove", row).appendChild(this.removerButton(uniq));
520 dojo.place(row, "acq-unified-terms-tbody", "last");
523 var attr = this._term_reverse_selector_field(term);
524 var field = hint + ":" + attr;
525 selector.setValue(field);
527 var match_how_value = this._term_reverse_match_how(term);
529 match_how.setValue(match_how_value);
531 var value = this._term_reverse_selector_value(term);
532 if (this.terms[hint][attr].datatype == "timestamp")
533 value = dojo.date.stamp.fromISOString(value);
534 this.updateRowWidget(uniq, value, /* noFocus */ true);
539 this.removeRow = function(id) {
540 delete this.widgets[id];
541 dojo.destroy(this._row(id));
544 this.reflect = function(search_object) {
545 for (var hint in search_object) {
546 search_object[hint].forEach(
547 function(term) { self.addRow(term, hint); }
552 this.buildSearchObject = function() {
555 for (var id in this.widgets) {
556 var kvlist = this._selector(id).getValue().split(":");
557 var hint = kvlist[0];
558 var attr = kvlist[1];
559 if (!(hint && attr)) continue;
562 this._match_how(id).getValue().split(",").filter(Boolean);
565 if (typeof(this.widgets[id].declaredClass) != "undefined") {
566 if (this.widgets[id].declaredClass.match(/Date/)) {
568 dojo.date.stamp.toISOString(this.widgets[id].value).
571 value = this.widgets[id].attr("value");
572 if (this.widgets[id].declaredClass.match(/Check/))
573 value = (value == "on") ? "t" : "f";
576 value = this.widgets[id].value;
584 match_how.forEach(function(key) { unit[key] = true; });
585 if (this.terms[hint][attr].datatype == "timestamp")
586 unit.__castdate = true;
593 this.buildBibSearchString = function() {
594 var conj = {"and": " ", "or": " || "}[
595 dojo.byId("acq-unified-conjunction").getValue()
599 /* Notice that below we use conj in two places and a constant " || "
600 * in one. That constant " || " is applied for the "file of terms"
601 * search term type, which is in itself always an or search. */
602 for (var id in this.widgets) {
603 var term = this._selector(id).getTerm();
604 var attr = term.bib_attr_name;
605 var match_how = this._match_how(id).getValue();
606 var widget = this.widgets[id];
608 if (!sso[attr]) sso[attr] = [];
610 typeof(widget.attr) == "function" ?
611 widget.attr("value") : widget.value
613 if (typeof(value) != "string")
614 value = value.join(" || ");
616 (match_how.indexOf("__not") == -1 ? "" : "-") + value
620 for (var attr in sso)
621 ssa.push(attr + ": " + sso[attr].join(conj));
622 return "(" + ssa.join(conj) + ")";
626 /* The result manager is used primarily when the users submits a search. It
627 * consults the termManager to get the search query to send to the middl
628 * layer, and it chooses which ML method to call as well as what widgets to use
629 * to display the results.
631 function ResultManager(liPager, poGrid, plGrid, invGrid) {
634 this.liPager = liPager;
636 this.poGrid = poGrid;
637 this.plGrid = plGrid;
638 this.invGrid = invGrid;
643 if (window.unifiedSearchExternalMode) {
645 // external user will define result types and handlers
649 this.result_types = {
653 "flesh_cancel_reason": true,
656 "revealer": function() {
658 progressDialog.show(true);
660 "finisher": function() {
661 self.liPager.batch_length = self.count_results;
662 self.liPager.relabelControls();
663 self.liPager.enableControls(true);
664 progressDialog.hide();
666 "adder": function(li) {
667 self.liPager.liTable.addLineitem(li);
669 "interface": self.liPager
673 "no_flesh_cancel_reason": true
675 "revealer": function() {
676 self.poGrid.resetStore();
677 self.poGrid.showLoadProgressIndicator();
680 "finisher": function() {
681 self.poGrid.hideLoadProgressIndicator();
683 "adder": function(po) {
684 self.poCache[po.id()] = po;
685 self.poGrid.store.newItem(acqpo.toStoreItem(po));
687 "interface": self.poGrid
691 "flesh_lineitem_count": true,
694 "revealer": function() {
695 self.plGrid.resetStore();
696 self.plGrid.showLoadProgressIndicator();
699 "finisher": function() {
700 self.plGrid.hideLoadProgressIndicator();
702 "adder": function(pl) {
703 self.plCache[pl.id()] = pl;
704 self.plGrid.store.newItem(acqpl.toStoreItem(pl));
706 "interface": self.plGrid
710 "no_flesh_misc": true
712 "finisher": function() {
713 self.invGrid.hideLoadProgressIndicator();
715 "revealer": function() {
716 self.invGrid.resetStore();
719 "adder": function(inv) {
720 self.invCache[inv.id()] = inv;
721 self.invGrid.store.newItem(acqinv.toStoreItem(inv));
723 "interface": self.invGrid
726 "revealer": function() { alert(localeStrings.NO_RESULTS); }
731 this._dataLoader = function(opts) {
732 /* This function must contain references to "self" only, not "this." */
733 var grid = self.result_types[self.result_type].interface;
738 self.count_results = 0;
740 var use_params = dojo.clone(self.params); /* need copy, not ref */
742 if (!opts.skip_paging) {
743 use_params[4].offset = grid.displayOffset;
744 use_params[4].limit = grid.displayLimit;
747 var method = self.method_name;
752 use_params[4].id_list = true;
754 var request_options = {
755 "params": use_params,
759 if (typeof opts.onresponse != "undefined") {
760 request_options.onresponse = opts.onresponse;
762 /* normal onresponse handler for most times we call this method */
763 request_options.onresponse = function(r) {
764 if (r = openils.Util.readResponse(r)) {
765 if (!self.count_results++)
766 self.show(self.result_type);
767 self.add(self.result_type, r);
772 if (typeof opts.oncomplete != "undefined") {
773 request_options.oncomplete = opts.oncomplete;
775 /* normal oncomplete handler for most times we call this method */
776 request_options.oncomplete = function() { self.resultsComplete(); };
779 fieldmapper.standardRequest(["open-ils.acq", method], request_options);
782 this.add = function(which, what) {
783 var f = this.result_types[which].adder;
787 this.finish = function(which) {
788 var f = this.result_types[which].finisher;
792 this.show = function(which) {
793 openils.Util.objectProperties(this.result_types).forEach(
795 openils.Util[rt == which ? "show" : "hide"](
796 "acq-unified-results-" + rt
800 this.result_types[which].revealer();
803 this.resultsComplete = function() {
805 // now that the records are loaded, we need to do the actual focusing
806 if (this.result_type == 'lineitem') {
808 this.liPager.focusLi();
811 if (!this.count_results)
812 this.show("no_results");
813 else this.finish(this.result_type);
816 this.go = function(search_object) {
818 if (window.unifiedSearchExternalMode) {
819 // assume for now that external mode implies inline results display
821 uriManager = uriManager || new URIManager();
822 uriManager.search_object = search_object;
823 uriManager.result_type = dojo.byId("acq-unified-result-type").getValue();
824 uriManager.conjunction = dojo.byId("acq-unified-conjunction").getValue();
825 this.search(uriManager, termManager);
829 location.href = oilsBasePath + "/acq/search/unified?" +
830 "so=" + base64Encode(search_object) +
831 "&rt=" + dojo.byId("acq-unified-result-type").getValue() +
832 "&c=" + dojo.byId("acq-unified-conjunction").getValue();
836 this.search = function(uriManager, termManager) {
837 var bib_search_string = null;
838 this.count_results = 0;
839 this.result_type = dojo.byId("acq-unified-result-type").getValue();
841 /* lineitem_and_bib: a special case */
842 if (this.result_type == "lineitem_and_bib") {
843 this.result_type = "lineitem";
844 bib_search_string = termManager.buildBibSearchString();
847 this.method_name = "open-ils.acq." + this.result_type +
849 /* Except for building the API method name that we want to call,
850 * we want to treat lineitem_and_bib the same way as lineitem from
854 openils.User.authtoken,
856 this.result_types[this.result_type].search_options
860 dojo.byId("acq-unified-conjunction").getValue() == "and" ? 1 : 2
861 ] = uriManager.search_object;
862 if (uriManager.order_by)
863 this.params[4].order_by = uriManager.order_by;
865 var interface = this.result_types[this.result_type].interface;
866 interface.dataLoader = this._dataLoader;
868 if (bib_search_string) {
869 /* Have the ML do the bib search first, which incidentally has the
870 * side effect of creating line items that will show up when
871 * we do the LI part of the search (so we don't actually want
872 * to display these results directly). */
873 fieldmapper.standardRequest(
874 ["open-ils.acq", "open-ils.acq.biblio.wrapped_search.atomic"], {
876 openils.User.authtoken, bib_search_string, {
880 "onresponse": function(r) {
881 r = openils.Util.readResponse(r, false, true);
887 // if the caller has requested we focus on a specific
888 // lineitem, allow the pager to find the lineitem
889 // and load the results directly.
890 if (this.result_type == 'lineitem') {
891 if (this.liPager && this.liPager.loadFocusLi()) {
896 interface.dataLoader();
900 function URIManager() {
902 this.cannedSearches = {
906 {"ordering_agency": openils.User.user.ws_ou()},
907 {"state": "on-order"}
911 "result_type": "purchase_order",
912 "conjunction": "and",
914 {"class": "acqpo", "field": "edit_time", "direction": "desc"}
920 {"owner": openils.User.user.usrname()}
923 "result_type": "picklist",
924 "conjunction": "and",
926 {"class": "acqpl", "field": "edit_time", "direction": "desc"}
933 {"receiver": openils.User.user.ws_ou()}
937 "result_type": "invoice",
938 "conjunction": "and",
940 {"class": "acqinv", "field": "recv_date", "direction": "desc"}
945 if (this.canned = cgi.param("ca")) { /* assignment */
946 dojo.mixin(this, this.cannedSearches[this.canned]);
947 dojo.byId("acq-unified-result-type").setValue(this.result_type);
948 dojo.byId("acq-unified-result-type").onchange();
949 dojo.byId("acq-unified-conjunction").setValue(this.conjunction);
951 this.search_object = cgi.param("so");
952 if (this.search_object)
953 this.search_object = base64Decode(this.search_object);
955 this.result_type = cgi.param("rt");
956 if (this.result_type) {
957 dojo.byId("acq-unified-result-type").setValue(this.result_type);
958 dojo.byId("acq-unified-result-type").onchange();
961 this.conjunction = cgi.param("c");
962 if (this.conjunction)
963 dojo.byId("acq-unified-conjunction").setValue(this.conjunction);
968 openils.Util.addOnLoad(
971 // onload handled by external user
972 if (window.unifiedSearchExternalMode) return;
974 termManager = new TermManager();
976 resultManager = new ResultManager(
977 new LiTablePager(null, new AcqLiTable()),
978 dijit.byId("acq-unified-po-grid"),
979 dijit.byId("acq-unified-pl-grid"),
980 dijit.byId("acq-unified-inv-grid")
983 uriManager = new URIManager();
984 if (uriManager.search_object) {
985 if (!uriManager.half_search)
987 openils.Util.show("acq-unified-body");
988 termManager.reflect(uriManager.search_object);
990 if (!uriManager.half_search)
991 resultManager.search(uriManager, termManager);
993 termManager.addRow();
994 openils.Util.show("acq-unified-body");