]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/web/js/dojo/openils/Util.js
Serials: provide a way for users to create and modify serial items
[Evergreen.git] / Open-ILS / web / js / dojo / openils / Util.js
1 /* ---------------------------------------------------------------------------
2  * Copyright (C) 2008  Georgia Public Library Service
3  * Bill Erickson <erickson@esilibrary.com>
4  *
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.
9  *
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  * ---------------------------------------------------------------------------
15  */
16
17
18 /**
19  * General purpose, static utility functions
20  */
21
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, {});
29
30
31     openils.Util.timeStampRegexp =
32         /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[\+-]\d{2})(\d{2})$/;
33
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")
38         );
39     }
40
41     /**
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()
47      */
48     openils.Util.timeStamp = function(s, opts) {
49         if (typeof(opts) == "undefined") opts = {};
50
51         return dojo.date.locale.format(
52             openils.Util.timeStampAsDateObj(s), opts
53         );
54     };
55
56     openils.Util._userFullNameFields = [
57         "prefix", "first_given_name", "second_given_name",
58         "family_name", "suffix", "alias", "usrname"
59     ];
60
61     /**
62      * Return an array of all the name-related attributes, with nulls replaced
63      * by empty strings, from a given actor.usr fieldmapper object, to be used
64      * as the arguments to any string formatting function that wants them.
65      * Code to do this is duplicated all over the place and should be
66      * simplified as we go.
67      */
68     openils.Util.userFullName = function(user) {
69         return dojo.map(
70             openils.Util._userFullNameFields,
71             function(a) { return user[a]() || ""; }
72         );
73     };
74
75     /**
76      * Same as openils.Util.userFullName, but with a hash of results instead
77      * of an array (dojo.string.substitute(), for example, can use this too).
78      */
79     openils.Util.userFullNameHash = function(user) {
80         var hash = {};
81         dojo.forEach(
82             openils.Util._userFullNameFields,
83             function(a) { hash[a] = user[a]() || ""; }
84         );
85         return hash;
86     };
87
88     /**
89      * Wrapper for dojo.addOnLoad that verifies a valid login session is active
90      * before adding the function to the onload set
91      */
92     openils.Util.addOnLoad = function(func, noSes) {
93         if(func) {
94             if(!noSes) {
95                 dojo.require('openils.User');
96                 if(!openils.User.authtoken) 
97                     return;
98             }
99             console.log("adding onload " + func.name);
100             dojo.addOnLoad(func);
101         }
102     };
103
104     /**
105      * Returns true if the provided array contains the specified value
106      */
107     openils.Util.arrayContains = function(arr, val) {
108         for(var i = 0; arr && i < arr.length; i++) {
109             if(arr[i] == val)
110                 return true;
111         }
112         return false;
113     };
114
115     /**
116      * Given a HTML select object, returns the currently selected value
117      */
118     openils.Util.selectorValue = function(sel) {
119         if(!sel) return null;
120         var idx = sel.selectedIndex;
121         if(idx < 0) return null;
122         var o = sel.options[idx];
123         var v = o.value; 
124         if(v == null) v = o.innerHTML;
125         return v;
126     }
127
128     /**
129      * Returns the character code of the provided (or current window) event
130      */
131     openils.Util.getCharCode = function(evt) {
132         evt = (evt) ? evt : ((window.event) ? event : null); 
133         if(evt) {
134             return (evt.charCode ? evt.charCode : 
135                 ((evt.which) ? evt.which : evt.keyCode ));
136         } else { return -1; }
137     }
138
139
140     /**
141      * Registers a handler for when the Enter key is pressed while 
142      * the provided DOM node has focus.
143      */
144     openils.Util.registerEnterHandler = function(domNode, func) {
145             if(!(domNode && func)) return;
146             domNode.onkeydown = function(evt) {
147             var code = openils.Util.getCharCode(evt);
148             if(code == 13 || code == 3) 
149                 func();
150         }
151         }
152
153
154     /**
155      * Parses opensrf response objects to see if they contain 
156      * data and/or an ILS event.  This only calls request.recv()
157      * once, so in a streaming context, it's necessary to loop on
158      * this method. 
159      * @param r The OpenSRF Request object
160      * @param eventOK If true, any found events will be returned as responses.  
161      * If false, they will be treated as error conditions and their content will
162      * be alerted if openils.Util.alertEvent is set to true.  Also, if eventOk is
163      * false, the response content will be null when an event is encountered.
164      * @param isList If true, assume the response will be a list of data and
165      * check the 1st item in the list for event-ness instead of the list itself.
166      */
167     openils.Util.alertEvent = true;
168     openils.Util.readResponse = function(r, eventOk, isList) {
169         var msg = r.recv();
170         if(msg == null) return msg;
171         var val = msg.content();
172         var testval = val;
173         if(isList && dojo.isArray(val))
174             testval = val[0];
175         if(e = openils.Event.parse(testval)) {
176             if(eventOk) return e;
177             console.log(e.toString());
178
179             // session timed out.  Stop propagation of requests queued by Util.onload 
180             // and launch the XUL login dialog if possible
181             var retryLogin = false;
182             if(e.textcode == 'NO_SESSION') {
183                 openils.User.authtoken = null; 
184                 if(openils.XUL.isXUL()) {
185                     retryLogin = true;
186                     openils.XUL.getNewSession( function() { location.href = location.href } );
187                 } else {
188                     // TODO: make the oilsLoginDialog templated via dojo so it can be 
189                     // used as a standalone widget
190                 }
191             }
192
193             if(openils.Util.alertEvent && !retryLogin)
194                 alert(e);
195             return null;
196         }
197         return val;
198     };
199
200
201     /**
202      * Given a DOM node, adds the provided class to the node 
203      */
204     openils.Util.addCSSClass = function(node, cls) {
205         if(!(node && cls)) return; 
206         var className = node.className;
207
208         if(!className) {
209             node.className = cls;
210             return;
211         }
212
213         var classList = className.split(/\s+/);
214         var newName = '';
215             
216         for (var i = 0; i < classList.length; i++) {
217             if(classList[i] == cls) return;
218             if(classList[i] != null)
219                 newName += classList[i] + " ";
220         }
221
222         newName += cls;
223         node.className = newName;
224     },
225
226     /**
227      * Given a DOM node, removes the provided class from the CSS class 
228      * name list.
229      */
230     openils.Util.removeCSSClass = function(node, cls) {
231         if(!(node && cls && node.className)) return;
232         var classList = node.className.split(/\s+/);
233         var className = '';
234         for(var i = 0; i < classList.length; i++) {
235             if (typeof(cls) == "object") { /* assume regex */
236                 if (!cls.test(classList[i])) {
237                     if(i == 0)
238                         className = classList[i];
239                     else
240                         className += ' ' + classList[i];
241                 }
242             } else {
243                 if (classList[i] != cls) {
244                     if(i == 0)
245                         className = classList[i];
246                     else
247                         className += ' ' + classList[i];
248                 }
249             }
250         }
251         node.className = className;
252     }
253
254     openils.Util.objectSort = function(list, field) {
255         if(dojo.isArray(list)) {
256             if(!field) field = 'id';
257             return list.sort(
258                 function(a, b) {
259                     if(a[field]() > b[field]()) return 1;
260                     return -1;
261                 }
262             );
263         }
264         return [];
265     };
266
267     openils.Util.isTrue = function(val) {
268         return (val && val != '0' && !(val+'').match(/^f$/i));
269     };
270
271     /**
272      * Turns a list into a mapped object.
273      * @param list The list
274      * @param pkey The field to use as the map key 
275      * @param isFunc If true, the map key field is an accessor function 
276      * that will return the value of the map key
277      */
278     openils.Util.mapList = function(list, pkey, isFunc) {
279         if(!(list && pkey)) 
280             return null;
281         var map = {};
282         for(var i in list) {
283             if(isFunc)
284                 map[list[i][pkey]()] = list[i];
285             else
286                 map[list[i][pkey]] = list[i];
287         }
288         return map;
289     };
290
291     /**
292      * Assume a space-separated interval string, with optional comma
293      * E.g. "1 year, 2 days"  "3 days 6 hours"
294      */
295     openils.Util.intervalToSeconds = function(interval) {
296         var d = new Date();
297         var start = d.getTime();
298         var parts = interval.split(' ');
299         for(var i = 0; i < parts.length; i += 2)  {
300             var type = parts[i+1].replace(/s?,?$/,'');
301             switch(type) {
302                 case 'mon': // postgres
303                     type = 'month'; // dojo
304                     break;
305                 // add more as necessary
306             }
307
308             d = dojo.date.add(d, type, Number(parts[i]));
309         }
310         return Number((d.getTime() - start) / 1000);
311     };
312
313     openils.Util.hide = function(node) {
314         if(typeof node == 'string')
315             node = dojo.byId(node);
316         dojo.style(node, 'display', 'none');
317         dojo.style(node, 'visibility', 'hidden');
318     };
319
320     openils.Util.show = function(node, displayType) {
321         if(typeof node == 'string')
322             node = dojo.byId(node);
323         displayType = displayType || 'block';
324         dojo.style(node, 'display', displayType);
325         dojo.style(node, 'visibility', 'visible');
326     };
327
328     /** Toggles the display using show/hide, depending on the current value for CSS 'display' */
329     openils.Util.toggle = function(node, displayType) {
330         if(typeof node == 'string')
331             node = dojo.byId(node);
332         if(dojo.style(node, 'display') == 'none')
333             openils.Util.show(node, displayType);
334         else
335             openils.Util.hide(node);
336     };
337
338     openils.Util.appendClear = function(node, child) {
339         if(typeof node == 'string')
340             node = dojo.byId(node);
341         while(node.childNodes[0])
342             node.removeChild(node.childNodes[0]);
343         node.appendChild(child);
344     };
345
346     /**
347      * Plays a sound file via URL.  Only works with browsers
348      * that support HTML 5 <audio> element.  E.g. Firefox 3.5
349      */
350     openils.Util.playAudioUrl = function(urlString) {
351         if(!urlString) return;
352         var audio = document.createElement('audio');
353         audio.setAttribute('src', urlString);
354         audio.setAttribute('autoplay', 'true');
355         document.body.appendChild(audio);
356         document.body.removeChild(audio);
357     }
358
359     /**
360      * Return the properties of an object as a list. Saves typing.
361      */
362     openils.Util.objectProperties = function(obj) {
363         var K = [];
364         for (var k in obj) K.push(k);
365         return K;
366     }
367
368     openils.Util.uniqueElements = function(L) {
369         var o = {};
370         for (var k in L) o[L[k]] = true;
371         return openils.Util.objectProperties(o);
372     }
373
374     openils.Util.uniqueObjects = function(list, field) {
375         var sorted = openils.Util.objectSort(list, field);
376         var results = [];
377         for (var i = 0; i < sorted.length; i++) {
378             if (!i || (sorted[i][field]() != sorted[i-1][field]()))
379                 results.push(sorted[i]);
380         }
381         return results;
382     };
383
384     /**
385      * Highlight instances of each pattern in the given DOM node
386      * Inspired by the jquery plugin
387      * http://johannburkard.de/blog/programming/javascript/highlight-javascript-text-higlighting-jquery-plugin.html
388      */
389     openils.Util.hilightNode = function(node, patterns, args) {
390
391         args = args ||{};
392         var hclass = args.classname || 'oils-highlight';
393
394         function _hilightNode(node, pat) {
395
396             if(node.nodeType == 3) { 
397
398                 pat = pat.toUpperCase();
399                 var text = node.data.toUpperCase();
400                 var pos = -1;
401
402                 // find each instance of pat in the current node
403                 while( (pos =  text.indexOf(pat, pos + 1)) >= 0 ) {
404
405                     var wrapper = dojo.create('span', {className : hclass});
406                     var midnode = node.splitText(pos);
407                     midnode.splitText(pat.length);
408                     wrapper.appendChild(midnode.cloneNode(true));
409                     midnode.parentNode.replaceChild(wrapper, midnode);
410                 }
411
412             } else if(node.nodeType == 1 && node.childNodes[0]) {
413
414                 // not a text node?  have you checked the children?
415                 dojo.forEach(
416                     node.childNodes,
417                     function(child) { _hilightNode(child, pat); }
418                 );
419             }
420         }
421
422         // descend the tree for each pattern, since nodes are changed during highlighting
423         dojo.forEach(patterns, function(pat) { _hilightNode(node, pat); });
424     };
425
426     openils.Util._legacyModulePaths = {};
427     /*****
428      * Take the URL of a JS file and magically turn it into something that
429      * dojo.require can load by registering a module path for it ... and load it.
430      *****/
431     openils.Util.requireLegacy = function(url) {
432         var bURL = url.replace(/\/[^\/]+$/,'');
433         var file = url.replace(/^.*\/([^\/]+)$/,'$1');
434         var libname = url.replace(/^.*?\/(.+)\/[^\/]+$/,'$1').replace(/[^a-z]/ig,'_');
435         var modname = libname + '.' + file.replace(/\.js$/,'');
436
437         if (!openils.Util._legacyModulePaths[libname]) {
438             dojo.registerModulePath(libname,bURL);
439             openils.Util._legacyModulePaths[libname] = {};
440         }
441
442         if (!openils.Util._legacyModulePaths[libname][modname]) {
443             dojo.require(modname, true);
444             openils.Util._legacyModulePaths[libname][modname] = true;
445         }
446
447         return openils.Util._legacyModulePaths[libname][modname];
448     };
449
450     /**
451      * Takes a chunk of HTML, inserts it into a new window, prints the window, 
452      * then closes the windw.  To provide ample printer queueing time, automatically
453      * wait a short time before closing the window after calling .print().  The amount
454      * of time to wait is based on the size of the data to be printed.
455      * @param html The HTML string
456      * @param callback Optional post-printing callback
457      */
458     openils.Util.printHtmlString = function(html, callback) {
459
460         var win = window.open('', 'Print Window', 'resizable,width=800,height=600,scrollbars=1'); 
461
462         // force the new window to the background
463         win.blur(); 
464         window.focus(); 
465
466         win.document.body.innerHTML = html;
467         win.print();
468
469         setTimeout(
470             function() { 
471                 win.close();
472                 if(callback)
473                     callback();
474             },
475             // 1k == 1 second pause, max 10 seconds
476             Math.min(html.length, 10000)
477         );
478     };
479
480 }
481