1 /* ---------------------------------------------------------------------------
2 * Copyright (C) 2008 Georgia Public Library Service
3 * Bill Erickson <erickson@esilibrary.com>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 * ---------------------------------------------------------------------------
19 * General purpose, static utility functions
22 if(!dojo._hasResource["openils.Util"]) {
23 dojo._hasResource["openils.Util"] = true;
24 dojo.provide("openils.Util");
25 dojo.require("dojo.date.locale");
26 dojo.require("dojo.date.stamp");
27 dojo.require('openils.Event');
28 dojo.declare('openils.Util', null, {});
31 openils.Util.timeStampRegexp =
32 /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[\+-]\d{2})(\d{2})$/;
34 openils.Util.timeStampAsDateObj = function(s) {
35 if (s.constructor.name == "Date") return s;
36 return dojo.date.stamp.fromISOString(
37 s.replace(openils.Util.timeStampRegexp, "$1:$2")
42 * Returns a locale-appropriate representation of a timestamp when the
43 * timestamp (first argument) is actually a string as provided by
44 * fieldmapper objects.
45 * The second argument is an optional argument that will be provided
46 * as the second argument to dojo.date.locale.format()
48 openils.Util.timeStamp = function(s, opts) {
49 if (typeof(opts) == "undefined") opts = {};
51 return dojo.date.locale.format(
52 openils.Util.timeStampAsDateObj(s), opts
57 * Wrapper for dojo.addOnLoad that verifies a valid login session is active
58 * before adding the function to the onload set
60 openils.Util.addOnLoad = function(func, noSes) {
63 dojo.require('openils.User');
64 if(!openils.User.authtoken)
67 console.log("adding onload " + func.name);
73 * Returns true if the provided array contains the specified value
75 openils.Util.arrayContains = function(arr, val) {
76 for(var i = 0; arr && i < arr.length; i++) {
84 * Given a HTML select object, returns the currently selected value
86 openils.Util.selectorValue = function(sel) {
88 var idx = sel.selectedIndex;
89 if(idx < 0) return null;
90 var o = sel.options[idx];
92 if(v == null) v = o.innerHTML;
97 * Returns the character code of the provided (or current window) event
99 openils.Util.getCharCode = function(evt) {
100 evt = (evt) ? evt : ((window.event) ? event : null);
102 return (evt.charCode ? evt.charCode :
103 ((evt.which) ? evt.which : evt.keyCode ));
104 } else { return -1; }
109 * Registers a handler for when the Enter key is pressed while
110 * the provided DOM node has focus.
112 openils.Util.registerEnterHandler = function(domNode, func) {
113 if(!(domNode && func)) return;
114 domNode.onkeydown = function(evt) {
115 var code = openils.Util.getCharCode(evt);
116 if(code == 13 || code == 3)
123 * Parses opensrf response objects to see if they contain
124 * data and/or an ILS event. This only calls request.recv()
125 * once, so in a streaming context, it's necessary to loop on
127 * @param r The OpenSRF Request object
128 * @param eventOK If true, any found events will be returned as responses.
129 * If false, they will be treated as error conditions and their content will
130 * be alerted if openils.Util.alertEvent is set to true. Also, if eventOk is
131 * false, the response content will be null when an event is encountered.
132 * @param isList If true, assume the response will be a list of data and
133 * check the 1st item in the list for event-ness instead of the list itself.
135 openils.Util.alertEvent = true;
136 openils.Util.readResponse = function(r, eventOk, isList) {
138 if(msg == null) return msg;
139 var val = msg.content();
141 if(isList && dojo.isArray(val))
143 if(e = openils.Event.parse(testval)) {
144 if(eventOk) return e;
145 console.log(e.toString());
147 // session timed out. Stop propagation of requests queued by Util.onload
148 // and launch the XUL login dialog if possible
149 var retryLogin = false;
150 if(e.textcode == 'NO_SESSION') {
151 openils.User.authtoken = null;
152 if(openils.XUL.isXUL()) {
154 openils.XUL.getNewSession( function() { location.href = location.href } );
156 // TODO: make the oilsLoginDialog templated via dojo so it can be
157 // used as a standalone widget
161 if(openils.Util.alertEvent && !retryLogin)
170 * Given a DOM node, adds the provided class to the node
172 openils.Util.addCSSClass = function(node, cls) {
173 if(!(node && cls)) return;
174 var className = node.className;
177 node.className = cls;
181 var classList = className.split(/\s+/);
184 for (var i = 0; i < classList.length; i++) {
185 if(classList[i] == cls) return;
186 if(classList[i] != null)
187 newName += classList[i] + " ";
191 node.className = newName;
195 * Given a DOM node, removes the provided class from the CSS class
198 openils.Util.removeCSSClass = function(node, cls) {
199 if(!(node && cls && node.className)) return;
200 var classList = node.className.split(/\s+/);
202 for(var i = 0; i < classList.length; i++) {
203 if (typeof(cls) == "object") { /* assume regex */
204 if (!cls.test(classList[i])) {
206 className = classList[i];
208 className += ' ' + classList[i];
211 if (classList[i] != cls) {
213 className = classList[i];
215 className += ' ' + classList[i];
219 node.className = className;
222 openils.Util.objectSort = function(list, field) {
223 if(dojo.isArray(list)) {
224 if(!field) field = 'id';
227 if(a[field]() > b[field]()) return 1;
235 openils.Util.isTrue = function(val) {
236 return (val && val != '0' && !(val+'').match(/^f$/i));
240 * Turns a list into a mapped object.
241 * @param list The list
242 * @param pkey The field to use as the map key
243 * @param isFunc If true, the map key field is an accessor function
244 * that will return the value of the map key
246 openils.Util.mapList = function(list, pkey, isFunc) {
252 map[list[i][pkey]()] = list[i];
254 map[list[i][pkey]] = list[i];
260 * Assume a space-separated interval string, with optional comma
261 * E.g. "1 year, 2 days" "3 days 6 hours"
263 openils.Util.intervalToSeconds = function(interval) {
265 var start = d.getTime();
266 var parts = interval.split(' ');
267 for(var i = 0; i < parts.length; i += 2) {
268 var type = parts[i+1].replace(/s?,?$/,'');
270 case 'mon': // postgres
271 type = 'month'; // dojo
273 // add more as necessary
276 d = dojo.date.add(d, type, Number(parts[i]));
278 return Number((d.getTime() - start) / 1000);
281 openils.Util.hide = function(node) {
282 if(typeof node == 'string')
283 node = dojo.byId(node);
284 dojo.style(node, 'display', 'none');
285 dojo.style(node, 'visibility', 'hidden');
288 openils.Util.show = function(node, displayType) {
289 if(typeof node == 'string')
290 node = dojo.byId(node);
291 displayType = displayType || 'block';
292 dojo.style(node, 'display', displayType);
293 dojo.style(node, 'visibility', 'visible');
296 /** Toggles the display using show/hide, depending on the current value for CSS 'display' */
297 openils.Util.toggle = function(node, displayType) {
298 if(typeof node == 'string')
299 node = dojo.byId(node);
300 if(dojo.style(node, 'display') == 'none')
301 openils.Util.show(node, displayType);
303 openils.Util.hide(node);
306 openils.Util.appendClear = function(node, child) {
307 if(typeof node == 'string')
308 node = dojo.byId(node);
309 while(node.childNodes[0])
310 node.removeChild(node.childNodes[0]);
311 node.appendChild(child);
315 * Plays a sound file via URL. Only works with browsers
316 * that support HTML 5 <audio> element. E.g. Firefox 3.5
318 openils.Util.playAudioUrl = function(urlString) {
319 if(!urlString) return;
320 var audio = document.createElement('audio');
321 audio.setAttribute('src', urlString);
322 audio.setAttribute('autoplay', 'true');
323 document.body.appendChild(audio);
324 document.body.removeChild(audio);
328 * Return the properties of an object as a list. Saves typing.
330 openils.Util.objectProperties = function(obj) {
332 for (var k in obj) K.push(k);
336 openils.Util.uniqueElements = function(L) {
338 for (var k in L) o[L[k]] = true;
339 return openils.Util.objectProperties(o);
342 openils.Util.uniqueObjects = function(list, field) {
343 var sorted = openils.Util.objectSort(list, field);
345 for (var i = 0; i < sorted.length; i++) {
346 if (!i || (sorted[i][field]() != sorted[i-1][field]()))
347 results.push(sorted[i]);
353 * Highlight instances of each pattern in the given DOM node
354 * Inspired by the jquery plugin
355 * http://johannburkard.de/blog/programming/javascript/highlight-javascript-text-higlighting-jquery-plugin.html
357 openils.Util.hilightNode = function(node, patterns, args) {
360 var hclass = args.classname || 'oils-highlight';
362 function _hilightNode(node, pat) {
364 if(node.nodeType == 3) {
366 pat = pat.toUpperCase();
367 var text = node.data.toUpperCase();
370 // find each instance of pat in the current node
371 while( (pos = text.indexOf(pat, pos + 1)) >= 0 ) {
373 var wrapper = dojo.create('span', {className : hclass});
374 var midnode = node.splitText(pos);
375 midnode.splitText(pat.length);
376 wrapper.appendChild(midnode.cloneNode(true));
377 midnode.parentNode.replaceChild(wrapper, midnode);
380 } else if(node.nodeType == 1 && node.childNodes[0]) {
382 // not a text node? have you checked the children?
385 function(child) { _hilightNode(child, pat); }
390 // descend the tree for each pattern, since nodes are changed during highlighting
391 dojo.forEach(patterns, function(pat) { _hilightNode(node, pat); });
394 openils.Util._legacyModulePaths = {};
396 * Take the URL of a JS file and magically turn it into something that
397 * dojo.require can load by registering a module path for it ... and load it.
399 openils.Util.requireLegacy = function(url) {
400 var bURL = url.replace(/\/[^\/]+$/,'');
401 var file = url.replace(/^.*\/([^\/]+)$/,'$1');
402 var libname = url.replace(/^.*?\/(.+)\/[^\/]+$/,'$1').replace(/[^a-z]/ig,'_');
403 var modname = libname + '.' + file.replace(/\.js$/,'');
405 if (!openils.Util._legacyModulePaths[libname]) {
406 dojo.registerModulePath(libname,bURL);
407 openils.Util._legacyModulePaths[libname] = {};
410 if (!openils.Util._legacyModulePaths[libname][modname]) {
411 dojo.require(modname, true);
412 openils.Util._legacyModulePaths[libname][modname] = true;
415 return openils.Util._legacyModulePaths[libname][modname];
419 * Takes a chunk of HTML, inserts it into a new window, prints the window,
420 * then closes the windw. To provide ample printer queueing time, automatically
421 * wait a short time before closing the window after calling .print(). The amount
422 * of time to wait is based on the size of the data to be printed.
423 * @param html The HTML string
424 * @param callback Optional post-printing callback
426 openils.Util.printHtmlString = function(html, callback) {
428 var win = window.open('', 'Print Window', 'resizable,width=800,height=600,scrollbars=1');
430 // force the new window to the background
434 win.document.body.innerHTML = html;
443 // 1k == 1 second pause, max 10 seconds
444 Math.min(html.length, 10000)