1 /* -----------------------------------------------------------------
2 * Copyright (C) 2008 Equinox Software, Inc.
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.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 ----------------------------------------------------------------- */
21 var STAFF_SES_PARAM = 'ses';
22 var PATRON_BARCODE_COOKIE = 'pbcc';
24 var itemBarcode = null;
25 var itemsOutTemplate = null;
26 var isRenewal = false;
27 var pendingXact = false;
28 var patronTimeout = 600000; /* 10 minutes */
32 var successfulItems = {};
33 var scanTimeout = 800;
35 var patronBarcodeRegex;
41 function selfckInit() {
43 var staff = grabUser(cookieManager.read(STAFF_SES_PARAM) || cgi.param(STAFF_SES_PARAM));
47 orgUnit = findOrgUnitSN(cgi.param('l')) || globalOrgTree;
48 selfckFetchOrgDetails();
50 var t = fetchOrgSettingDefault(orgUnit.id(), 'circ.selfcheck.patron_login_timeout');
51 patronTimeout = (t) ? parseInt(t) * 1000 : patronTimeout;
53 var reg = fetchOrgSettingDefault(orgUnit.id(), 'opac.barcode_regex');
54 if(reg) patronBarcodeRegex = new RegExp(reg);
57 // should not happen when behind the proxy
58 return alert('Staff must login');
61 unHideMe($('selfck-patron-login-container'));
62 $('selfck-patron-login-input').focus();
64 $('selfck-patron-login-input').onkeypress = function(evt) {
65 if(userPressedEnter(evt))
69 $('selfck-item-barcode-input').onkeypress = selfckItemBarcodeKeypress;
71 // for debugging, allow passing the user barcode via param
72 var urlbc = new CGI().param('patron');
74 selfckPatronLogin(urlbc);
78 printWrapper = $('selfck-print-items-list');
79 printTemplate = printWrapper.removeChild($n(printWrapper, 'selfck-print-items-template'));
80 itemsOutTemplate = $('selfck-items-out-tbody').removeChild($('selfck-items-out-row'));
82 selfckTryPatronCookie();
84 // selfckMkDummyCirc(); // testing only
88 function selfckFetchOrgDetails() {
89 var hreq = new Request('open-ils.actor:open-ils.actor.org_unit.hours_of_operation.retrieve', G.user.session, orgUnit.id());
90 hreq.callback(function(r) { orgUnitHours = r.getResultObject(); });
93 var areq = new Request('open-ils.actor:open-ils.actor.org_unit.address.retrieve', orgUnit.mailing_address());
94 areq.callback(function(r) { orgUnitAddress = r.getResultObject(); });
98 function selfckSetupPrinter() {
100 netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
101 netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
102 netscape.security.PrivilegeManager.enablePrivilege('UniversalPreferencesRead');
103 netscape.security.PrivilegeManager.enablePrivilege('UniversalPreferencesWrite');
104 var pref = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
106 pref.setBoolPref('print.always_print_silent', true);
112 function selfckTryPatronCookie() {
113 var pb = cookieManager.read(PATRON_BARCODE_COOKIE);
115 cookieManager.write(PATRON_BARCODE_COOKIE, '');
116 $('selfck-patron-login-input').value = pb;
122 function selfckItemBarcodeKeypress(evt) {
123 if(userPressedEnter(evt)) {
124 clearTimeout(scanTimeoutId);
127 /* If scanTimeout milliseconds have passed and there is
128 still data in the input box, it's a likely indication
129 of a partial scan. Select the text so the next scan
130 will replace the value */
131 clearTimeout(scanTimeoutId);
132 scanTimeoutId = setTimeout(
134 if($('selfck-item-barcode-input').value) {
135 $('selfck-item-barcode-input').select();
144 * Start the logout timer
146 function selfckStartTimer() {
147 timerId = setTimeout(
149 selfckLogoutPatron();
157 * Reset the logout timer
159 function selfckResetTimer() {
160 clearTimeout(timerId);
165 * Clears fields and "logs out" the patron by reloading the page
167 function selfckLogoutPatron() {
168 $('selfck-item-barcode-input').value = ''; // prevent browser caching
169 $('selfck-patron-login-input').value = '';
173 function() { location.href = location.href; },
180 * Fetches the user by barcode and displays the item barcode input
183 function selfckPatronLogin(barcode) {
184 barcode = barcode || $('selfck-patron-login-input').value;
187 var bcReq = new Request(
188 'open-ils.actor:open-ils.actor.user.fleshed.retrieve_by_barcode',
189 G.user.session, barcode);
191 bcReq.request.alertEvent = false;
193 bcReq.callback(function(r) {
194 patron = r.getResultObject();
195 if(checkILSEvent(patron)) {
196 if(patron.textcode == 'ACTOR_USER_NOT_FOUND') {
197 unHideMe($('selfck-patron-not-found'));
198 $('selfck-patron-login-input').select();
202 if(patron.textcode == 'NO_SESSION')
203 return selfckLogoutStaff();
205 return alert(patron.textcode);
207 $('selfck-patron-login-input').value = ''; // reset the input
208 hideMe($('selfck-patron-login-container'));
209 unHideMe($('selfck-patron-checkout-container'));
210 $('selfck-patron-name-span').appendChild(text(patron.usrname()));
211 $('selfck-item-barcode-input').focus();
217 function selfckLogoutStaff() {
218 cookieManager.remove(STAFF_SES_PARAM);
219 location.reload(true);
223 * If a user barcode was scanned into the item barcode
224 * input, log out the current user and log in the new user
226 function selfckCheckPatronBarcode(itemBc) {
227 if(patronBarcodeRegex) {
228 if(itemBc.match(patronBarcodeRegex)) {
229 cookieManager.write(PATRON_BARCODE_COOKIE, itemBc, -1);
230 selfckLogoutPatron();
238 * Sends the checkout request
240 function selfckCheckout() {
241 if(pendingXact) return;
246 removeChildren($('selfck-event-time'));
247 removeChildren($('selfck-event-span'));
249 itemBarcode = $('selfck-item-barcode-input').value;
250 if(!itemBarcode) return;
252 if(selfckCheckPatronBarcode(itemBarcode))
255 if (itemBarcode in successfulItems) {
256 selfckShowMsgNode({textcode:'dupe-barcode'});
257 $('selfck-item-barcode-input').select();
262 var coReq = new Request(
263 'open-ils.circ:open-ils.circ.checkout.full',
264 G.user.session, {patron_id:patron.id(), copy_barcode:itemBarcode});
266 coReq.request.alertEvent = false;
267 coReq.callback(selfckHandleCoResult);
272 * Handles the resulting event. If the item is already checked out,
273 * attempts renewal. Any other events will display a message
275 function selfckHandleCoResult(r) {
279 evt = r.getResultObject();
282 selfckShowMsgNode({textcode:'UNKNOWN'});
283 appendClear($('selfck-errors'), text(E.toString()));
287 if(evt.textcode == 'SUCCESS') {
288 selfckDislplayCheckout(evt);
289 selfckShowMsgNode(evt);
290 successfulItems[itemBarcode] = 1;
292 } else if(evt.textcode == 'OPEN_CIRCULATION_EXISTS') {
295 } else if(evt.textcode == 'NO_SESSION') {
297 return selfckLogoutStaff();
301 selfckShowMsgNode(evt);
302 $('selfck-item-barcode-input').select();
307 * Display event text in the messages block
309 function selfckShowMsgNode(evt) {
310 var code = evt.textcode;
311 var msgspan = $('selfck-event-span');
313 // if we're not explicitly handling the event, just say "copy cannot circ"
314 if(!$('selfck-event-' + code))
315 code = 'COPY_CIRC_NOT_ALLOWED';
317 appendClear($('selfck-event-time'), text(new Date().toLocaleString()));
318 appendClear($('selfck-event-span'), text($('selfck-event-' + code).innerHTML));
322 * Renders a row in the checkouts table for the current circulation
324 function selfckDislplayCheckout(evt) {
325 unHideMe($('selfck-items-out-table-wrapper'));
327 var template = itemsOutTemplate.cloneNode(true);
328 var copy = evt.payload.copy;
329 var record = evt.payload.record;
330 var circ = evt.payload.circ;
333 var pic = $n(template, 'selfck.jacket');
334 pic.setAttribute('src', '../ac/jacket/small/'+cleanISBN(record.isbn()));
336 $n(template, 'selfck.barcode').appendChild(text(copy.barcode()));
337 $n(template, 'selfck.title').appendChild(text(record.title()));
338 $n(template, 'selfck.author').appendChild(text(record.author()));
339 $n(template, 'selfck.due_date').appendChild(text(circ.due_date().replace(/T.*/,'')));
340 $n(template, 'selfck.remaining').appendChild(text(circ.renewal_remaining()));
342 hideMe($n(template, 'selfck.cotype_co'));
343 unHideMe($n(template, 'selfck.cotype_rn'));
346 $('selfck-items-out-tbody').appendChild(template);
347 $('selfck-item-barcode-input').value = '';
350 // flesh out the printable version of the page as well
351 var ptemplate = printTemplate.cloneNode(true);
352 $n(ptemplate, 'title').appendChild(text(record.title()));
353 $n(ptemplate, 'barcode').appendChild(text(copy.barcode()));
354 $n(ptemplate, 'due_date').appendChild(text(circ.due_date().replace(/T.*/,'')));
355 printWrapper.appendChild(ptemplate);
361 * Checks to see if this item is checked out to the current patron. If so,
362 * this sends the renewal request.
364 function selfckRenew() {
366 // first, make sure the item is checked out to this patron
367 var detailReq = new Request(
368 'open-ils.circ:open-ils.circ.copy_details.retrieve.barcode',
369 G.user.session, itemBarcode);
373 var details = r.getResultObject();
374 if(details.circ.usr() == patron.id()) {
375 // OK, this is our item, renew it
377 var rnReq = new Request(
378 'open-ils.circ:open-ils.circ.renew',
379 G.user.session, {copy_barcode:itemBarcode});
380 rnReq.request.alertEvent = false;
381 rnReq.callback(selfckHandleCoResult);
391 * Sets the print date and prints the page
393 function selfckPrint() {
394 for(var x in successfulItems) { // make sure we've checked out at least one item
395 appendClear($('selfck-print-date'), text(new Date().toLocaleString()));
396 appendClear($('selfck-print-lib-name'), text(orgUnit.name()));
398 appendClear($('selfck-print-lib-addr-street'), text(orgUnitAddress.street1()+' '+orgUnitAddress.street2()));
399 appendClear($('selfck-print-lib-addr-city'), text(orgUnitAddress.city()));
400 appendClear($('selfck-print-lib-addr-state'), text(orgUnitAddress.state()));
401 appendClear($('selfck-print-lib-addr-post-code'), text(orgUnitAddress.post_code()));
403 appendClear($('selfck-print-lname'), text(patron.family_name()));
404 appendClear($('selfck-print-fname'), text(patron.first_given_name()));
405 appendClear($('selfck-print-lib-phone'), text(orgUnit.phone()));
407 for(var i in [0, 1, 2, 3, 4, 5, 6]) {
408 appendClear($('selfck-print-dow_'+i+'_open'), text(orgUnitHours['dow_'+i+'_open']()));
409 appendClear($('selfck-print-dow_'+i+'_close'), text(orgUnitHours['dow_'+i+'_close']()));
419 * Test method for generating dummy data in the checkout tables
421 function selfckMkDummyCirc() {
422 unHideMe($('selfck-items-out-table'));
424 var template = itemsOutTemplate.cloneNode(true);
425 $n(template, 'selfck.barcode').appendChild(text('123456789'));
426 $n(template, 'selfck.title').appendChild(text('Test Title'));
427 $n(template, 'selfck.author').appendChild(text('Test Author'));
428 $n(template, 'selfck.due_date').appendChild(text('2008-08-01'));
429 $n(template, 'selfck.remaining').appendChild(text('1'));
430 $('selfck-items-out-tbody').appendChild(template);
432 // flesh out the printable version of the page as well
433 var ptemplate = printTemplate.cloneNode(true);
434 $n(ptemplate, 'title').appendChild(text('Test Title'));
435 $n(ptemplate, 'barcode').appendChild(text('123456789'));
436 $n(ptemplate, 'due_date').appendChild(text('2008-08-01'));
437 printWrapper.appendChild(ptemplate);