]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/web/js/ui/default/actor/user/register.js
make new addresses valid by default. make the 1st address on a new patron the billin...
[working/Evergreen.git] / Open-ILS / web / js / ui / default / actor / user / register.js
1 dojo.require('dojo.data.ItemFileReadStore');
2 dojo.require('dijit.form.Textarea');
3 dojo.require('dijit.form.FilteringSelect');
4 dojo.require('dijit.form.ComboBox');
5 dojo.require('dijit.form.NumberSpinner');
6 dojo.require('fieldmapper.IDL');
7 dojo.require('openils.PermaCrud');
8 dojo.require('openils.widget.AutoGrid');
9 dojo.require('openils.widget.AutoFieldWidget');
10 dojo.require('dijit.form.CheckBox');
11 dojo.require('dijit.form.Button');
12 dojo.require('dojo.date');
13 dojo.require('openils.CGI');
14 dojo.require('openils.XUL');
15 dojo.require('openils.Util');
16 dojo.require('openils.Event');
17
18 dojo.requireLocalization('openils.actor', 'register');
19 var localeStrings = dojo.i18n.getLocalization('openils.actor', 'register');
20
21
22 var pcrud;
23 var fmClasses = ['au', 'ac', 'aua', 'actsc', 'asv', 'asvq', 'asva'];
24 var fieldDoc = {};
25 var statCats;
26 var statCatTempate;
27 var surveys;
28 var staff;
29 var patron;
30 var uEditUsePhonePw = false;
31 var widgetPile = [];
32 var uEditCardVirtId = -1;
33 var uEditAddrVirtId = -1;
34 var orgSettings = {};
35 var userSettings = {};
36 var userSettingsToUpdate = {};
37 var tbody;
38 var addrTemplateRows;
39 var cgi;
40 var cloneUser;
41 var claimReturnedPermList;
42
43
44 if(!window.xulG) var xulG = null;
45
46
47 function load() {
48     staff = new openils.User().user;
49     pcrud = new openils.PermaCrud();
50     cgi = new openils.CGI();
51     cloneUser = cgi.param('clone');
52     var userId = cgi.param('usr');
53
54     if(xulG) {
55             if(xulG.ses) openils.User.authtoken = xulG.ses;
56             if(xulG.clone !== null) cloneUser = xulG.clone;
57         if(xulG.usr !== null) userId = xulG.usr
58         if(xulG.params) {
59             var parms = xulG.params;
60                 if(parms.ses) 
61                 openils.User.authtoken = parms.ses;
62                 if(parms.clone) 
63                 cloneUser = parms.clone;
64             if(parms.usr !== null)
65                 userId = parms.usr
66         }
67     }
68
69     orgSettings = fieldmapper.aou.fetchOrgSettingBatch(staff.ws_ou(), [
70         'global.juvenile_age_threshold',
71         'patron.password.use_phone',
72         'ui.patron.default_inet_access_level',
73         'circ.holds.behind_desk_pickup_supported'
74     ]);
75     for(k in orgSettings)
76         if(orgSettings[k])
77             orgSettings[k] = orgSettings[k].value;
78
79     uEditFetchUserSettings(userId);
80     uEditLoadUser(userId);
81
82     var list = pcrud.search('fdoc', {fm_class:fmClasses});
83     for(var i in list) {
84         var doc = list[i];
85         if(!fieldDoc[doc.fm_class()])
86             fieldDoc[doc.fm_class()] = {};
87         fieldDoc[doc.fm_class()][doc.field()] = doc;
88     }
89
90     tbody = dojo.byId('uedit-tbody');
91
92     addrTemplateRows = dojo.query('tr[type=addr-template]', tbody);
93     dojo.forEach(addrTemplateRows, function(row) { row.parentNode.removeChild(row); } );
94     statCatTemplate = tbody.removeChild(dojo.byId('stat-cat-row-template'));
95     surveyTemplate = tbody.removeChild(dojo.byId('survey-row-template'));
96     surveyQuestionTemplate = tbody.removeChild(dojo.byId('survey-question-row-template'));
97
98     loadStaticFields();
99     if(patron.isnew()) 
100         uEditNewAddr(null, uEditAddrVirtId, true);
101     else loadAllAddrs();
102     loadStatCats();
103     loadSurveys();
104     checkClaimsReturnCountPerm();
105 }
106
107 function uEditFetchUserSettings(userId) {
108     userSettings = fieldmapper.standardRequest(
109         ['open-ils.actor', 'open-ils.actor.patron.settings.retrieve'],
110         {params : [openils.User.authtoken, userId, ['circ.holds_behind_desk']]});
111 }
112
113
114 function uEditLoadUser(userId) {
115     if(!userId) return uEditNewPatron();
116     patron = fieldmapper.standardRequest(
117         ['open-ils.actor', 'open-ils.actor.user.fleshed.retrieve'],
118         {params : [openils.User.authtoken, userId]}
119     );
120     openils.Event.parse_and_raise(patron);
121 }
122
123 function loadStaticFields() {
124     for(var idx = 0; tbody.childNodes[idx]; idx++) {
125         var row = tbody.childNodes[idx];
126         if(row.nodeType != row.ELEMENT_NODE) continue;
127         var fmcls = row.getAttribute('fmclass');
128         if(fmcls) {
129             fleshFMRow(row, fmcls);
130         } else {
131             if(row.getAttribute('user_setting'))
132                 fleshUserSettingRow(row, row.getAttribute('user_setting'))
133         }
134     }
135 }
136
137 function fleshUserSettingRow(row, userSetting) {
138     switch(userSetting) {
139         case 'circ.holds_behind_desk':
140             if(orgSettings['circ.holds.behind_desk_pickup_supported']) {
141                 openils.Util.show('uedit-settings-divider', 'table-row');
142                 openils.Util.show(row, 'table-row');
143                 if(userSettings[userSetting]) 
144                     holdsBehindShelfBox.attr('checked', true);
145
146                 // if the setting changes, add it to the list of settings that need updating
147                 dojo.connect(
148                     holdsBehindShelfBox, 
149                     'onChange', 
150                     function(newVal) { userSettingsToUpdate['circ.holds_behind_desk'] = newVal; }
151                 );
152             } 
153     }
154 }
155
156 function uEditUpdateUserSettings(userId) {
157     return fieldmapper.standardRequest(
158         ['open-ils.actor', 'open-ils.actor.patron.settings.update'],
159         {params : [openils.User.authtoken, userId, userSettingsToUpdate]});
160 }
161
162 function loadAllAddrs() {
163     dojo.forEach(patron.addresses(),
164         function(addr) {
165             uEditNewAddr(null, addr.id());
166         }
167     );
168 }
169
170 function loadStatCats() {
171
172     statCats = fieldmapper.standardRequest(
173         ['open-ils.circ', 'open-ils.circ.stat_cat.actor.retrieve.all'],
174         {params : [openils.User.authtoken, staff.ws_ou()]}
175     );
176
177     // draw stat cats
178     for(var idx in statCats) {
179         var stat = statCats[idx];
180         var row = statCatTemplate.cloneNode(true);
181         row.id = 'stat-cat-row-' + idx;
182         tbody.appendChild(row);
183         getByName(row, 'name').innerHTML = stat.name();
184         var valtd = getByName(row, 'widget');
185         var span = valtd.appendChild(document.createElement('span'));
186         var store = new dojo.data.ItemFileReadStore(
187                 {data:fieldmapper.actsc.toStoreData(stat.entries())});
188         var comboBox = new dijit.form.ComboBox({store:store}, span);
189         comboBox.labelAttr = 'value';
190         comboBox.searchAttr = 'value';
191
192         comboBox._wtype = 'statcat';
193         comboBox._statcat = stat.id();
194         widgetPile.push(comboBox); 
195
196         // populate existing cats
197         var map = patron.stat_cat_entries().filter(
198             function(mp) { return (mp.stat_cat() == stat.id()) })[0];
199         if(map) comboBox.attr('value', map.stat_cat_entry()); 
200
201     }
202 }
203
204 function loadSurveys() {
205
206     surveys = fieldmapper.standardRequest(
207         ['open-ils.circ', 'open-ils.circ.survey.retrieve.all'],
208         {params : [openils.User.authtoken]}
209     );
210
211     // draw surveys
212     for(var idx in surveys) {
213         var survey = surveys[idx];
214         var srow = surveyTemplate.cloneNode(true);
215         tbody.appendChild(srow);
216         getByName(srow, 'name').innerHTML = survey.name();
217
218         for(var q in survey.questions()) {
219             var quest = survey.questions()[q];
220             var qrow = surveyQuestionTemplate.cloneNode(true);
221             tbody.appendChild(qrow);
222             getByName(qrow, 'question').innerHTML = quest.question();
223
224             var span = getByName(qrow, 'answers').appendChild(document.createElement('span'));
225             var store = new dojo.data.ItemFileReadStore(
226                 {data:fieldmapper.asva.toStoreData(quest.answers())});
227             var select = new dijit.form.FilteringSelect({store:store}, span);
228             select.labelAttr = 'answer';
229             select.searchAttr = 'answer';
230
231             select._wtype = 'survey';
232             select._survey = survey.id();
233             select._question = quest.id();
234             widgetPile.push(select); 
235         }
236     }
237 }
238
239
240 function fleshFMRow(row, fmcls, args) {
241     var fmfield = row.getAttribute('fmfield');
242     var wclass = row.getAttribute('wclass');
243     var wstyle = row.getAttribute('wstyle');
244     var wconstraints = row.getAttribute('wconstraints');
245     var fieldIdl = fieldmapper.IDL.fmclasses[fmcls].field_map[fmfield];
246     if(!args) args = {};
247
248     var existing = dojo.query('td', row);
249     var htd = existing[0] || row.appendChild(document.createElement('td'));
250     var ltd = existing[1] || row.appendChild(document.createElement('td'));
251     var wtd = existing[2] || row.appendChild(document.createElement('td'));
252
253     openils.Util.addCSSClass(htd, 'uedit-help');
254     if(fieldDoc[fmcls] && fieldDoc[fmcls][fmfield]) {
255         var link = dojo.byId('uedit-help-template').cloneNode(true);
256         link.id = '';
257         link.onclick = function() { ueLoadContextHelp(fmcls, fmfield) };
258         openils.Util.removeCSSClass(link, 'hidden');
259         htd.appendChild(link);
260     }
261
262     if(!ltd.textContent) {
263         var span = document.createElement('span');
264         ltd.appendChild(document.createTextNode(fieldIdl.label));
265     }
266
267     span = document.createElement('span');
268     wtd.appendChild(span);
269
270     var fmObject = null;
271     switch(fmcls) {
272         case 'au' : fmObject = patron; break;
273         case 'ac' : fmObject = patron.card(); break;
274         case 'aua' : 
275             fmObject = patron.addresses().filter(
276                 function(i) { return (i.id() == args.addr) })[0];
277             break;
278     }
279
280     var dijitArgs = {
281         style: wstyle, 
282         required : required,
283         constraints : (wconstraints) ? eval('('+wconstraints+')') : {} // the ()'s prevent Invalid Label errors with eval
284     };
285
286     var value = row.getAttribute('wvalue');
287     if(value !== null)
288         dijitArgs.value = value;
289
290     var required = row.getAttribute('required') == 'required';
291     var widget = new openils.widget.AutoFieldWidget({
292         idlField : fieldIdl,
293         fmObject : fmObject,
294         fmClass : fmcls,
295         parentNode : span,
296         widgetClass : wclass,
297         dijitArgs : dijitArgs,
298         orgLimitPerms : ['UPDATE_USER'],
299     });
300
301     widget.build();
302
303     widget._wtype = fmcls;
304     widget._fmfield = fmfield;
305     widget._addr = args.addr;
306     widgetPile.push(widget);
307     attachWidgetEvents(fmcls, fmfield, widget);
308     return widget;
309 }
310
311 function findWidget(wtype, fmfield, callback) {
312     return widgetPile.filter(
313         function(i){
314             if(i._wtype == wtype && i._fmfield == fmfield) {
315                 if(callback) return callback(i);
316                 return true;
317             }
318         }
319     ).pop();
320 }
321
322 /**
323  * if the user does not have the UPDATE_PATRON_CLAIM_RETURN_COUNT, 
324  * they are not allowed to directly alter the claim return count. 
325  * This function checks the perm and disable/enables the widget.
326  */
327 function checkClaimsReturnCountPerm() {
328     new openils.User().getPermOrgList(
329         'UPDATE_PATRON_CLAIM_RETURN_COUNT',
330         function(orgList) { 
331             var cr = findWidget('au', 'claims_returned_count');
332             if(orgList.indexOf(patron.home_ou()) == -1) 
333                 cr.widget.attr('disabled', true);
334             else
335                 cr.widget.attr('disabled', false);
336         },
337         true, 
338         true
339     );
340 }
341
342 function attachWidgetEvents(fmcls, fmfield, widget) {
343
344     if(fmcls == 'ac') {
345         if(fmfield == 'barcode') {
346             dojo.connect(widget.widget, 'onChange',
347                 function() {
348                     var un = findWidget('au', 'usrname');
349                     if(!un.widget.attr('value'))
350                         un.widget.attr('value', this.attr('value'));
351                 }
352             );
353             return;
354         }
355     }
356
357     if(fmcls == 'au') {
358         switch(fmfield) {
359
360             case 'profile': // when the profile changes, update the expire date
361                 dojo.connect(widget.widget, 'onChange', 
362                     function() {
363                         var self = this;
364                         var expireWidget = findWidget('au', 'expire_date');
365                         function found(items) {
366                             if(items.length == 0) return;
367                             var item = items[0];
368                             var interval = self.store.getValue(item, 'perm_interval');
369                             expireWidget.widget.attr('value', dojo.date.add(new Date(), 
370                                 'second', openils.Util.intervalToSeconds(interval)));
371                         }
372                         this.store.fetch({onComplete:found, query:{id:this.attr('value')}});
373                     }
374                 );
375                 return;
376
377             case 'dob':
378                 dojo.connect(widget.widget, 'onChange',
379                     function(newDob) {
380                         if(!newDob) return;
381                         var oldDob = patron.dob();
382                         if(dojo.date.stamp.fromISOString(oldDob) == newDob) return;
383
384                         var juvInterval = orgSettings['global.juvenile_age_threshold'] || '18 years';
385                         var juvWidget = findWidget('au', 'juvenile');
386                         var base = new Date();
387                         base.setTime(base.getTime() - Number(openils.Util.intervalToSeconds(juvInterval) + '000'));
388
389                         if(newDob <= base) // older than global.juvenile_age_threshold
390                             juvWidget.widget.attr('value', false);
391                         else
392                             juvWidget.widget.attr('value', true);
393                     }
394                 );
395                 return;
396
397             case 'first_given_name':
398             case 'family_name':
399                 dojo.connect(widget.widget, 'onChange',
400                     function(newVal) { uEditDupeSearch('name', newVal); });
401                 return;
402
403             case 'email':
404                 dojo.connect(widget.widget, 'onChange',
405                     function(newVal) { uEditDupeSearch('email', newVal); });
406                 return;
407
408             case 'ident_value':
409             case 'ident_value2':
410                 dojo.connect(widget.widget, 'onChange',
411                     function(newVal) { uEditDupeSearch('ident', newVal); });
412                 return;
413
414             case 'day_phone':
415             case 'evening_phone':
416             case 'other_phone':
417                 dojo.connect(widget.widget, 'onChange',
418                     function(newVal) { uEditDupeSearch('phone', newVal); });
419                 return;
420
421             case 'home_ou':
422                 dojo.connect(widget.widget, 'onChange',
423                     function(newVal) { checkClaimsReturnCountPerm(); });
424                 return;
425
426         }
427     }
428
429     if(fmclass = 'aua') {
430         switch(fmfield) {
431             case 'post_code':
432                 dojo.connect(widget.widget, 'onChange',
433                     function(e) { 
434                         fieldmapper.standardRequest(
435                             ['open-ils.search', 'open-ils.search.zip'],
436                             {   async: true,
437                                 params: [e],
438                                 oncomplete : function(r) {
439                                     var res = openils.Util.readResponse(r);
440                                     if(!res) return;
441                                     var callback = function(w) { return w._addr == widget._addr; };
442                                     if(res.city) findWidget('aua', 'city', callback).widget.attr('value', res.city);
443                                     if(res.state) findWidget('aua', 'state', callback).widget.attr('value', res.state);
444                                     if(res.county) findWidget('aua', 'county', callback).widget.attr('value', res.county);
445                                     if(res.alert) alert(res.alert);
446                                 }
447                             }
448                         );
449                     }
450                 );
451                 return;
452
453             case 'street1':
454             case 'street2':
455             case 'city':
456                 dojo.connect(widget.widget, 'onChange',
457                     function(e) {
458                         var callback = function(w) { return w._addr == widget._addr; };
459                         var args = {
460                             street1 : findWidget('aua', 'street1', callback).widget.attr('value'),
461                             street2 : findWidget('aua', 'street2', callback).widget.attr('value'),
462                             city : findWidget('aua', 'city', callback).widget.attr('value'),
463                             post_code : findWidget('aua', 'post_code', callback).widget.attr('value')
464                         };
465                         if(args.street1 && args.city && args.post_code)
466                             uEditDupeSearch('address', args); 
467                     }
468                 );
469                 return;
470         }
471     }
472 }
473
474 function uEditDupeSearch(type, value) {
475     var search;
476     switch(type) {
477
478         case 'name':
479             openils.Util.hide('uedit-dupe-names-link');
480             var fname = findWidget('au', 'first_given_name').widget.attr('value');
481             var lname = findWidget('au', 'family_name').widget.attr('value');
482             if( !(fname && lname) ) return;
483             search = {
484                 first_given_name : {value : fname, group : 0},
485                 family_name : {value : lname, group : 0},
486             };
487             break;
488
489         case 'email':
490             openils.Util.hide('uedit-dupe-email-link');
491             search = {email : {value : value, group : 0}};
492             break;
493
494         case 'ident':
495             openils.Util.hide('uedit-dupe-ident-link');
496             search = {ident : {value : value, group : 2}};
497             break;
498
499         case 'phone':
500             openils.Util.hide('uedit-dupe-phone-link');
501             search = {phone : {value : value, group : 2}};
502             break;
503
504         case 'address':
505             openils.Util.hide('uedit-dupe-address-link');
506             search = {};
507             dojo.forEach(['street1', 'street2', 'city', 'post_code'],
508                 function(field) {
509                     if(value[field])
510                         search[field] = {value : value[field], group: 1};
511                 }
512             );
513             break;
514     }
515
516     // find possible duplicate patrons
517     fieldmapper.standardRequest(
518         ['open-ils.actor', 'open-ils.actor.patron.search.advanced'],
519         {   async: true,
520             params: [openils.User.authtoken, search],
521             oncomplete : function(r) {
522                 var resp = openils.Util.readResponse(r);
523                 resp = resp.filter(function(id) { return (id != patron.id()); });
524
525                 if(resp && resp.length > 0) {
526
527                     openils.Util.hide('uedit-help-div');
528                     openils.Util.show('uedit-dupe-div');
529                     var link;
530
531                     switch(type) {
532                         case 'name':
533                             link = dojo.byId('uedit-dupe-names-link');
534                             link.innerHTML = dojo.string.substitute(localeStrings.DUPE_PATRON_NAME, [resp.length]);
535                             break;
536                         case 'email':
537                             link = dojo.byId('uedit-dupe-email-link');
538                             link.innerHTML = dojo.string.substitute(localeStrings.DUPE_PATRON_EMAIL, [resp.length]);
539                             break;
540                         case 'ident':
541                             link = dojo.byId('uedit-dupe-ident-link');
542                             link.innerHTML = dojo.string.substitute(localeStrings.DUPE_PATRON_IDENT, [resp.length]);
543                             break;
544                         case 'phone':
545                             link = dojo.byId('uedit-dupe-phone-link');
546                             link.innerHTML = dojo.string.substitute(localeStrings.DUPE_PATRON_PHONE, [resp.length]);
547                             break;
548                         case 'address':
549                             link = dojo.byId('uedit-dupe-address-link');
550                             link.innerHTML = dojo.string.substitute(localeStrings.DUPE_PATRON_ADDR, [resp.length]);
551                             break;
552                     }
553
554                     openils.Util.show(link);
555                     link.onclick = function() {
556                         search.search_sort = js2JSON(["penalties", "family_name", "first_given_name"]);
557                         if(window.xulG)
558                             window.xulG.spawn_search(search);
559                         else
560                             console.log("running XUL patron search " + js2JSON(search));
561                     }
562                 }
563             }
564         }
565     );
566 }
567
568 function getByName(node, name) {
569     return dojo.query('[name='+name+']', node)[0];
570 }
571
572
573 function ueLoadContextHelp(fmcls, fmfield) {
574     openils.Util.hide('uedit-dupe-div');
575     openils.Util.show('uedit-help-div');
576     dojo.byId('uedit-help-field').innerHTML = fieldmapper.IDL.fmclasses[fmcls].field_map[fmfield].label;
577     dojo.byId('uedit-help-text').innerHTML = fieldDoc[fmcls][fmfield].string();
578 }
579
580
581 /* creates a new patron object with card attached */
582 function uEditNewPatron() {
583     patron = new au();
584     patron.isnew(1);
585     patron.id(-1);
586     card = new ac();
587     card.id(uEditCardVirtId);
588     card.isnew(1);
589     patron.active(1);
590     patron.card(card);
591     patron.cards([card]);
592     patron.net_access_level(orgSettings['ui.patron.default_inet_access_level'] || 1);
593     patron.stat_cat_entries([]);
594     patron.survey_responses([]);
595     patron.addresses([]);
596     uEditMakeRandomPw(patron);
597 }
598
599 function uEditMakeRandomPw(patron) {
600     if(uEditUsePhonePw) return;
601     var rand  = Math.random();
602     rand = parseInt(rand * 10000) + '';
603     while(rand.length < 4) rand += '0';
604 /*
605     appendClear($('ue_password_plain'),text(rand));
606     unHideMe($('ue_password_gen'));
607 */
608     patron.passwd(rand);
609     return rand;
610 }
611
612 function uEditWidgetVal(w) {
613     var val = (w.getFormattedValue) ? w.getFormattedValue() : w.attr('value');
614     if(val === '') val = null;
615     return val;
616 }
617
618 function uEditSave() { _uEditSave(); }
619 function uEditSaveClone() { _uEditSave(true); }
620
621 function _uEditSave(doClone) {
622
623     for(var idx in widgetPile) {
624         var w = widgetPile[idx];
625         var val = uEditWidgetVal(w);
626
627         switch(w._wtype) {
628             case 'au':
629                 patron[w._fmfield](val);
630                 break;
631
632             case 'ac':
633                 patron.card()[w._fmfield](val);
634                 break;
635
636             case 'aua':
637                 var addr = patron.addresses().filter(function(i){return (i.id() == w._addr)})[0];
638                 if(!addr) {
639                     addr = new fieldmapper.aua();
640                     addr.id(w._addr);
641                     addr.isnew(1);
642                     addr.usr(patron.id());
643                     patron.addresses().push(addr);
644                 } else {
645                     if(addr[w._fmfield]() != val)
646                         addr.ischanged(1);
647                 }
648                 addr[w._fmfield](val);
649
650                 if(dojo.byId('uedit-billing-address-' + addr.id()).checked) 
651                     patron.billing_address(addr.id());
652
653                 if(dojo.byId('uedit-mailing-address-' + addr.id()).checked)
654                     patron.mailing_address(addr.id());
655
656                 break;
657
658             case 'survey':
659                 if(val == null) break;
660                 var resp = new fieldmapper.asvr();
661                 resp.isnew(1);
662                 resp.survey(w._survey)
663                 resp.usr(patron.id());
664                 resp.question(w._question)
665                 resp.answer(val);
666                 patron.survey_responses().push(resp);
667                 break;
668
669             case 'statcat':
670                 if(val == null) break;
671
672                 var map = patron.stat_cat_entries().filter(
673                     function(m){
674                         return (m.stat_cat() == w._statcat) })[0];
675
676                 if(map) {
677                     if(map.stat_cat_entry() == val) 
678                         break;
679                     map.ischanged(1);
680                 } else {
681                     map = new fieldmapper.actscecm();
682                     map.isnew(1);
683                 }
684
685                 map.stat_cat(w._statcat);
686                 map.stat_cat_entry(val);
687                 map.target_usr(patron.id());
688                 patron.stat_cat_entries().push(map);
689                 break;
690         }
691     }
692
693     patron.ischanged(1);
694     fieldmapper.standardRequest(
695         ['open-ils.actor', 'open-ils.actor.patron.update'],
696         {   async: true,
697             params: [openils.User.authtoken, patron],
698             oncomplete: function(r) {
699                 newPatron = openils.Util.readResponse(r);
700                 if(newPatron) {
701                     uEditUpdateUserSettings(newPatron.id());
702                     uEditFinishSave(newPatron, doClone);
703                 }
704             }
705         }
706     );
707 }
708
709 function uEditFinishSave(newPatron, doClone) {
710
711     if(doClone &&cloneUser == null)
712         cloneUser = newPatron.id();
713
714         if( doClone ) {
715
716                 if(xulG && typeof xulG.spawn_editor == 'function' && !patron.isnew() ) {
717             window.xulG.spawn_editor({ses:openils.User.authtoken,clone:cloneUser});
718             uEditRefresh();
719
720                 } else {
721                         location.href = href.replace(/\?.*/, '') + '?clone=' + cloneUser;
722                 }
723
724         } else {
725
726                 uEditRefresh();
727         }
728
729         uEditRefreshXUL(newPatron);
730 }
731
732 function uEditRefresh() {
733     var usr = cgi.param('usr');
734     var href = location.href.replace(/\?.*/, '');
735     href += ((usr) ? '?usr=' + usr : '');
736     location.href = href;
737 }
738
739 function uEditRefreshXUL(newuser) {
740         if (window.xulG && typeof window.xulG.on_save == 'function') 
741                 window.xulG.on_save(newuser);
742 }
743
744
745 /**
746  * Create a new address and insert it into the DOM
747  * @param evt ignored
748  * @param id The address id
749  * @param mkLinks If true, set the new address as the 
750  *  mailing/billing address for the user
751  */
752 function uEditNewAddr(evt, id, mkLinks) {
753
754     if(id == null) 
755         id = --uEditAddrVirtId; // new address
756
757     dojo.forEach(addrTemplateRows, 
758         function(row) {
759
760             row = tbody.insertBefore(row.cloneNode(true), dojo.byId('new-addr-row'));
761             row.setAttribute('type', '');
762             row.setAttribute('addr', id+'');
763
764             if(row.getAttribute('fmclass')) {
765                 var widget = fleshFMRow(row, 'aua', {addr:id});
766
767                 // make new addresses valid by default
768                 if(id < 0 && row.getAttribute('fmfield') == 'valid') 
769                     widget.widget.attr('value', true); 
770
771             } else if(row.getAttribute('name') == 'uedit-addr-pending-row') {
772
773                 var addr =  patron.addresses().filter(
774                     function(i) { return (i.id() == id) })[0];
775                 
776                 // if it's a pending address, show the 'approve' button
777                 if(addr && openils.Util.isTrue(addr.pending())) {
778                     openils.Util.show(row, 'table-row');
779                     dojo.query('[name=approve-button]', row)[0].onclick = 
780                         function() { uEditApproveAddress(addr); };
781
782                     if(addr.replaces()) {
783                         var div = dojo.query('[name=replaced-addr]', row)[0]
784                         div.innerHTML = addr.replaces();
785                     } else {
786                         openils.Util.hide(dojo.query('[name=replaced-addr-div]', row)[0]);
787                     }
788                 }
789
790             } else if(row.getAttribute('name') == 'uedit-addr-divider') {
791                 // link up the billing/mailing address and give the inputs IDs so we can acces the later
792                 
793                 // billing address
794                 var ba = getByName(row, 'billing_address');
795                 ba.id = 'uedit-billing-address-' + id;
796                 if(mkLinks || (patron.billing_address() && patron.billing_address().id() == id))
797                     ba.checked = true;
798
799                 // mailing address
800                 var ma = getByName(row, 'mailing_address');
801                 ma.id = 'uedit-mailing-address-' + id;
802                 if(mkLinks || (patron.mailing_address() && patron.mailing_address().id() == id))
803                     ma.checked = true;
804
805             } else {
806                 var btn = dojo.query('[name=delete-button]', row)[0];
807                 if(btn) btn.onclick = function(){ uEditDeleteAddr(id) };
808             }
809         }
810     );
811 }
812
813 function uEditApproveAddress(addr) {
814     fieldmapper.standardRequest(
815         ['open-ils.actor', 'open-ils.actor.user.pending_address.approve'],
816         {   async: true,
817             params:  [openils.User.authtoken, addr],
818
819             oncomplete : function(r) {
820                 var oldId = openils.Util.readResponse(r);
821                     
822                 // remove addrs from UI
823                 dojo.forEach(
824                     patron.addresses(), 
825                     function(addr) { uEditDeleteAddr(addr.id(), true); }
826                 );
827
828                 if(oldId != null) {
829                     
830                     // remove the replaced address 
831                     if(oldId != addr.id()) {
832                                 patron.addresses(
833                             patron.addresses().filter(
834                                                 function(i) { return (i.id() != oldId); }
835                                         )
836                                 );
837                     }
838                     
839                     // fix the the new address
840                     addr.id(oldId);
841                     addr.replaces(null);
842                     addr.pending('f');
843
844                 }
845
846                 // redraw addrs
847                 loadAllAddrs();
848             }
849         }
850     );
851 }
852
853
854 function uEditDeleteAddr(id, noAlert) {
855     if(!noAlert) {
856         if(!confirm('Delete address ' + id)) return; /* XXX i18n */
857     }
858     var rows = dojo.query('tr[addr='+id+']', tbody);
859     for(var i = 0; i < rows.length; i++)
860         rows[i].parentNode.removeChild(rows[i]);
861     widgetPile = widgetPile.filter(function(w){return (w._addr != id)});
862 }
863
864 function uEditToggleRequired() {
865     if((tbody.className +'').match(/hide-non-required/)) 
866         openils.Util.removeCSSClass(tbody, 'hide-non-required');
867     else
868         openils.Util.addCSSClass(tbody, 'hide-non-required');
869     openils.Util.toggle('uedit-show-required');
870     openils.Util.toggle('uedit-show-all');
871 }
872
873
874
875 openils.Util.addOnLoad(load);