]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/web/js/dojo/openils/Util.js
Provide a mechanism to load any random JS file via dojo.require()-ish syntax.
[working/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     /**
57      * Wrapper for dojo.addOnLoad that verifies a valid login session is active
58      * before adding the function to the onload set
59      */
60     openils.Util.addOnLoad = function(func, noSes) {
61         if(func) {
62             if(!noSes) {
63                 dojo.require('openils.User');
64                 if(!openils.User.authtoken) 
65                     return;
66             }
67             console.log("adding onload " + func.name);
68             dojo.addOnLoad(func);
69         }
70     };
71
72     /**
73      * Returns true if the provided array contains the specified value
74      */
75     openils.Util.arrayContains = function(arr, val) {
76         for(var i = 0; arr && i < arr.length; i++) {
77             if(arr[i] == val)
78                 return true;
79         }
80         return false;
81     };
82
83     /**
84      * Given a HTML select object, returns the currently selected value
85      */
86     openils.Util.selectorValue = function(sel) {
87         if(!sel) return null;
88         var idx = sel.selectedIndex;
89         if(idx < 0) return null;
90         var o = sel.options[idx];
91         var v = o.value; 
92         if(v == null) v = o.innerHTML;
93         return v;
94     }
95
96     /**
97      * Returns the character code of the provided (or current window) event
98      */
99     openils.Util.getCharCode = function(evt) {
100         evt = (evt) ? evt : ((window.event) ? event : null); 
101         if(evt) {
102             return (evt.charCode ? evt.charCode : 
103                 ((evt.which) ? evt.which : evt.keyCode ));
104         } else { return -1; }
105     }
106
107
108     /**
109      * Registers a handler for when the Enter key is pressed while 
110      * the provided DOM node has focus.
111      */
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) 
117                 func();
118         }
119         }
120
121
122     /**
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
126      * this method. 
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.
134      */
135     openils.Util.alertEvent = true;
136     openils.Util.readResponse = function(r, eventOk, isList) {
137         var msg = r.recv();
138         if(msg == null) return msg;
139         var val = msg.content();
140         var testval = val;
141         if(isList && dojo.isArray(val))
142             testval = val[0];
143         if(e = openils.Event.parse(testval)) {
144             if(eventOk) return e;
145             console.log(e.toString());
146
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()) {
153                     retryLogin = true;
154                     openils.XUL.getNewSession( function() { location.href = location.href } );
155                 } else {
156                     // TODO: make the oilsLoginDialog templated via dojo so it can be 
157                     // used as a standalone widget
158                 }
159             }
160
161             if(openils.Util.alertEvent && !retryLogin)
162                 alert(e);
163             return null;
164         }
165         return val;
166     };
167
168
169     /**
170      * Given a DOM node, adds the provided class to the node 
171      */
172     openils.Util.addCSSClass = function(node, cls) {
173         if(!(node && cls)) return; 
174         var className = node.className;
175
176         if(!className) {
177             node.className = cls;
178             return;
179         }
180
181         var classList = className.split(/\s+/);
182         var newName = '';
183             
184         for (var i = 0; i < classList.length; i++) {
185             if(classList[i] == cls) return;
186             if(classList[i] != null)
187                 newName += classList[i] + " ";
188         }
189
190         newName += cls;
191         node.className = newName;
192     },
193
194     /**
195      * Given a DOM node, removes the provided class from the CSS class 
196      * name list.
197      */
198     openils.Util.removeCSSClass = function(node, cls) {
199         if(!(node && cls && node.className)) return;
200         var classList = node.className.split(/\s+/);
201         var className = '';
202         for(var i = 0; i < classList.length; i++) {
203             if (typeof(cls) == "object") { /* assume regex */
204                 if (!cls.test(classList[i])) {
205                     if(i == 0)
206                         className = classList[i];
207                     else
208                         className += ' ' + classList[i];
209                 }
210             } else {
211                 if (classList[i] != cls) {
212                     if(i == 0)
213                         className = classList[i];
214                     else
215                         className += ' ' + classList[i];
216                 }
217             }
218         }
219         node.className = className;
220     }
221
222     openils.Util.objectSort = function(list, field) {
223         if(dojo.isArray(list)) {
224             if(!field) field = 'id';
225             return list.sort(
226                 function(a, b) {
227                     if(a[field]() > b[field]()) return 1;
228                     return -1;
229                 }
230             );
231         }
232         return [];
233     };
234
235     openils.Util.isTrue = function(val) {
236         return (val && val != '0' && !(val+'').match(/^f$/i));
237     };
238
239     /**
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
245      */
246     openils.Util.mapList = function(list, pkey, isFunc) {
247         if(!(list && pkey)) 
248             return null;
249         var map = {};
250         for(var i in list) {
251             if(isFunc)
252                 map[list[i][pkey]()] = list[i];
253             else
254                 map[list[i][pkey]] = list[i];
255         }
256         return map;
257     };
258
259     /**
260      * Assume a space-separated interval string, with optional comma
261      * E.g. "1 year, 2 days"  "3 days 6 hours"
262      */
263     openils.Util.intervalToSeconds = function(interval) {
264         var d = new Date();
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?,?$/,'');
269             switch(type) {
270                 case 'mon': // postgres
271                     type = 'month'; // dojo
272                     break;
273                 // add more as necessary
274             }
275
276             d = dojo.date.add(d, type, Number(parts[i]));
277         }
278         return Number((d.getTime() - start) / 1000);
279     };
280
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');
286     };
287
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');
294     };
295
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);
302         else
303             openils.Util.hide(node);
304     };
305
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);
312     };
313
314     /**
315      * Plays a sound file via URL.  Only works with browsers
316      * that support HTML 5 <audio> element.  E.g. Firefox 3.5
317      */
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);
325     }
326
327     /**
328      * Return the properties of an object as a list. Saves typing.
329      */
330     openils.Util.objectProperties = function(obj) {
331         var K = [];
332         for (var k in obj) K.push(k);
333         return K;
334     }
335
336     openils.Util.uniqueElements = function(L) {
337         var o = {};
338         for (var k in L) o[L[k]] = true;
339         return openils.Util.objectProperties(o);
340     }
341
342     openils.Util.uniqueObjects = function(list, field) {
343         var sorted = openils.Util.objectSort(list, field);
344         var results = [];
345         for (var i = 0; i < sorted.length; i++) {
346             if (!i || (sorted[i][field]() != sorted[i-1][field]()))
347                 results.push(sorted[i]);
348         }
349         return results;
350     };
351
352     /**
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
356      */
357     openils.Util.hilightNode = function(node, patterns, args) {
358
359         args = args ||{};
360         var hclass = args.classname || 'oils-highlight';
361
362         function _hilightNode(node, pat) {
363
364             if(node.nodeType == 3) { 
365
366                 pat = pat.toUpperCase();
367                 var text = node.data.toUpperCase();
368                 var pos = -1;
369
370                 // find each instance of pat in the current node
371                 while( (pos =  text.indexOf(pat, pos + 1)) >= 0 ) {
372
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);
378                 }
379
380             } else if(node.nodeType == 1 && node.childNodes[0]) {
381
382                 // not a text node?  have you checked the children?
383                 dojo.forEach(
384                     node.childNodes,
385                     function(child) { _hilightNode(child, pat); }
386                 );
387             }
388         }
389
390         // descend the tree for each pattern, since nodes are changed during highlighting
391         dojo.forEach(patterns, function(pat) { _hilightNode(node, pat); });
392     };
393
394     openils.Util._legacyModulePaths = {};
395     /*****
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.
398      *****/
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$/,'');
404
405         if (!openils.Util._legacyModulePaths[libname]) {
406             dojo.registerModulePath(libname,bURL);
407             openils.Util._legacyModulePaths[libname] = {};
408         }
409
410         if (!openils.Util._legacyModulePaths[libname][modname]) {
411             dojo.require(modname, true);
412             openils.Util._legacyModulePaths[libname][modname] = true;
413         }
414
415         return openils.Util._legacyModulePaths[libname][modname];
416     };
417
418     /**
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
425      */
426     openils.Util.printHtmlString = function(html, callback) {
427
428         var win = window.open('', 'Print Window', 'resizable,width=800,height=600,scrollbars=1'); 
429
430         // force the new window to the background
431         win.blur(); 
432         window.focus(); 
433
434         win.document.body.innerHTML = html;
435         win.print();
436
437         setTimeout(
438             function() { 
439                 win.close();
440                 if(callback)
441                     callback();
442             },
443             // 1k == 1 second pause, max 10 seconds
444             Math.min(html.length, 10000)
445         );
446     };
447
448 }
449