fixed bug which caused the user editor to alert errors
[Evergreen.git] / Evergreen / xul / staff_client / server / patron / ue_config.js
1 /* -----------------------------------------------------------------------
2         ----------------------------------------------------------------------- */
3 const SC_FETCH_ALL              = 'open-ils.circ:open-ils.circ.stat_cat.actor.retrieve.all';
4 const SC_CREATE_MAP             = 'open-ils.circ:open-ils.circ.stat_cat.actor.user_map.create';
5 const SV_FETCH_ALL              = 'open-ils.circ:open-ils.circ.survey.retrieve.all';
6 const FETCH_ID_TYPES            = 'open-ils.actor:open-ils.actor.user.ident_types.retrieve';
7 const FETCH_GROUPS              = 'open-ils.actor:open-ils.actor.groups.tree.retrieve';
8 const FETCH_NET_LEVELS  = 'open-ils.actor:open-ils.actor.net_access_level.retrieve.all';
9 const UPDATE_PATRON             = 'open-ils.actor:open-ils.actor.patron.update';
10 const PATRON_SEARCH             = 'open-ils.actor:open-ils.actor.patron.search.advanced';
11 const ZIP_SEARCH                        = 'open-ils.search:open-ils.search.zip';
12 const FETCH_ADDR_MEMS   = 'open-ils.actor:open-ils.actor.address.members';
13 const FETCH_GRP_MEMS            = 'open-ils.actor:open-ils.actor.usergroup.members.retrieve';
14 const CREATE_USER_NOTE  = 'open-ils.actor:open-ils.actor.note.create';
15 const CHECK_BARCODE             = 'open-ils.actor:open-ils.actor.barcode.exists';
16 const defaultState              = 'GA';
17 const defaultCountry            = 'USA';
18 const defaultNetAccess  = 'None';
19 const defaultNetLevel   = 1;
20 const CSS_INVALID_DATA  = 'invalid_value';
21 const ADULT_AGE                 = 18;
22 //const GUARDIAN_NOTE           = 'SYSTEM: Parent/Guardian';
23
24 /* if they don't have these perms, they shouldn't be here */
25 var myPerms = [ 
26         'BAR_PATRON',
27         'group_application.user',
28         'group_application.user.patron',
29         'group_application.user.staff',
30         'group_application.user.staff.circ',
31         'group_application.user.staff.cat',
32         'group_application.user.staff.admin.global_admin',
33         'group_application.user.staff.admin.local_admin',
34         'group_application.user.staff.admin.lib_manager',
35         'group_application.user.staff.cat.cat1',
36         'group_application.user.staff.supercat',
37         'group_application.user.sip_client',
38         'group_application.user.vendor'
39         ];
40
41 var dataFields;
42 const numRegex          = /^\d+$/;
43 const wordRegex = /^[\w-]+$/;
44 const unameRegex        = /^\w[\.\w\@-]*$/;
45 const ssnRegex          = /^\d{3}-\d{2}-\d{4}$/;
46 const dlRegex           = /^[a-zA-Z]{2}-\w+/; /* driver's license */
47 const phoneRegex        = /^\d{3}-\d{3}-\d{4}(| ex\d+)$/i;
48 const nonumRegex        = /^[a-zA-Z]\D*$/; /* no numbers, no beginning whitespace */
49 const dateRegex = /^\d{4}-\d{2}-\d{2}/;
50 const zipRegex          = /^\d{5}(-\d{4}|-?$)/; /* 12345 or 12345-6789 */
51
52 var barredAlerted = false;
53
54
55 function uEditUsrnameBlur(field) {
56         var usrname = uEditNodeVal(field);
57         if(!usrname) return;
58         var req = new Request(CHECK_USERNAME, SESSION, usrname);
59         req.callback( 
60                 function(r) {
61                         var res = r.getResultObject();
62                         if( res && res != patron.id() ) {
63                                 field.widget.onblur = null; /* prevent alert storm */
64                                 alertId('ue_dup_username');
65                                 field.widget.onblur = uEditUsrnameBlur;
66                                 setTimeout( 
67                                         function() {
68                                                 field.widget.node.focus();
69                                                 field.widget.node.select();
70                                         }, 10 
71                                 );
72                         }
73                 }
74         );
75         req.send();
76 }
77
78
79 function uEditBarcodeBlur(field) {
80         var barcode = uEditNodeVal(field);
81         if(!barcode) return;
82         _debug("blurring card with new value " + barcode);
83         var req = new Request(CHECK_BARCODE, SESSION, barcode);
84         req.callback( 
85                 function(r) {
86                         var res = r.getResultObject();
87                         if( res && res != patron.id() ) {
88                                 field.widget.onblur = null; /* prevent alert storm */
89                                 alertId('ue_dup_barcode');
90                                 field.widget.onblur = uEditBarcodeBlur;
91                                 setTimeout( 
92                                         function() {
93                                                 field.widget.node.focus();
94                                                 field.widget.node.select();
95                                         }, 10 
96                                 );
97                         } else {
98                                 var node = uEditFindFieldByWId("ue_username");
99                                 if(!node.widget.node.value) {
100                                         node.widget.node.value = barcode;
101                                         node.widget.node.onchange();
102                                 }
103                         }
104                 }
105         );
106         req.send();
107 }
108
109
110 function uEditDefineData(patron) {
111
112         var fields = [
113                 {
114                         required : true,
115                         object  : patron.card(),
116                         key             : 'barcode',
117                         errkey  : 'ue_bad_barcode',
118                         widget  : {
119                                 id              : 'ue_barcode',
120                                 regex   : wordRegex,
121                                 type    : 'input',
122                                 onblur : uEditBarcodeBlur
123                         }
124                 },
125                 {
126                         required : true,
127                         object  : patron,
128                         key             : 'usrname',
129                         errkey  : 'ue_bad_username',
130                         widget  : {
131                                 id              : 'ue_username',
132                                 regex   : unameRegex,
133                                 type    : 'input',
134                                 onblur : uEditUsrnameBlur
135                         }
136                 },
137                 {
138                         required : (patron.isnew()) ? true : false,
139                         object  : patron,
140                         key             : 'passwd',
141                         errkey  : 'ue_bad_password',
142                         widget  : {
143                                 id              : 'ue_password1',
144                                 type    : 'input',
145                                 onpostchange : function(field, newval) {
146                                         var pw2 = uEditFindFieldByWId('ue_password2');
147                                         /* tell the second passsword input to re-validate */
148                                         pw2.widget.node.onchange();
149                                 }
150
151                         }
152                 },
153                 {
154                         required : (patron.isnew()) ? true : false,
155                         object  : patron,
156                         key             : 'passwd',
157                         errkey  : 'ue_bad_password',
158                         widget  : {
159                                 id              : 'ue_password2',
160                                 type    : 'input',
161                                 onpostchange : function(field, newval) {
162                                         var pw1f = uEditFindFieldByWId('ue_password1');
163                                         var pw1 = uEditNodeVal(pw1f);
164                                         field.widget.regex = new RegExp('^'+pw1+'$');
165                                         if( pw1 ) field.required = true;
166                                         else {
167                                                 if(!patron.isnew())
168                                                         field.required = false;
169                                         }
170                                 }
171                         }
172                 },
173                 {
174                         required : true,
175                         object  : patron,
176                         key             : 'first_given_name',
177                         errkey  : 'ue_bad_firstname',
178                         widget  : {
179                                 id              : 'ue_firstname',
180                                 regex   : nonumRegex,
181                                 type    : 'input',
182                                 onblur : function(field) {
183                                         uEditCheckNamesDup('first', field );
184                                 }
185                         }
186                 },
187
188                 {
189                         required : false,
190                         object  : patron,
191                         key             : 'second_given_name',
192                         errkey  : 'ue_bad_middlename',
193                         widget  : {
194                                 id              : 'ue_middlename',
195                                 regex   : nonumRegex,
196                                 type    : 'input'
197                         }
198                 },
199                 {
200                         required : true,
201                         object  : patron,
202                         key             : 'family_name',
203                         errkey  : 'ue_bad_lastname',
204                         widget  : {
205                                 id              : 'ue_lastname',
206                                 regex   : nonumRegex,
207                                 type    : 'input',
208                                 onblur : function(field) {
209                                         uEditCheckNamesDup('last', field );
210                                 }
211                         }
212                 },
213                 {
214                         required : false,
215                         object  : patron,
216                         key             : 'suffix',
217                         widget  : {
218                                 id                      : 'ue_suffix',
219                                 type            : 'input',
220                                 onload  : function(val) {
221                                         setSelector($('ue_suffix_selector'), val);
222                                         $('ue_suffix_selector').onchange = function() {
223                                                 uEditFindFieldByKey('suffix').widget.node.onchange();
224                                         }
225                                 },
226                         }
227                 },
228                 {
229                         required : false,
230                         object  : patron,
231                         key             : 'dob',
232                         errkey  : 'ue_bad_dob',
233                         widget  : {
234                                 id                      : 'ue_dob',
235                                 regex           : dateRegex,
236                                 type            : 'input',
237                                 onpostchange    : function(field) { uEditCheckDOB(field); },
238                                 onblur  : function(field) { uEditCheckDOB(field); }
239                         }
240                 },
241                 {
242                         required : true,
243                         object  : patron,
244                         key             : 'ident_type',
245                         errkey  : 'ue_no_ident',
246                         widget  : {
247                                 id              : 'ue_primary_ident_type',
248                                 regex   : numRegex,
249                                 type    : 'select',
250                                 onpostchange : function(field, newval) 
251                                         { _uEditIdentPostchange('primary', field, newval); }
252                         }
253                 },
254                 {
255                         required : false,
256                         object  : patron,
257                         key             : 'ident_value',
258                         widget  : {
259                                 id                      : 'ue_primary_ident',
260                                 type            : 'input',
261                                 onblur : function(field) {
262                                         uEditCheckIdentDup(field);
263                                 }
264                         }
265                 },
266                 {
267                         required : false,
268                         object  : patron,
269                         key             : 'ident_value2',
270                         widget  : {
271                                 id                      : 'ue_secondary_ident',
272                                 type            : 'input'
273                         }
274                 },
275                 {
276                         required : false,
277                         object  : patron,
278                         key             : 'email',
279                         errkey  : 'ue_bad_email',
280                         widget  : {
281                                 id                      : 'ue_email',
282                                 type            : 'input',
283                                 regex           :  /.+\@.+\..+/,  /* make me better */
284                                 onblur  : function(field) {
285                                         var val = uEditNodeVal(field);
286                                         if( val && val != field.oldemail ) {
287                                                 uEditRunDupeSearch('email',
288                                                         { email : { value : val, group : 0 } });
289                                                 field.oldemail = val;
290                                         }
291                                 }
292                         }
293                 },
294                 {
295                         required : false,
296                         object  : patron,
297                         key             : 'day_phone',
298                         errkey  : 'ue_bad_phone',
299                         widget  : {
300                                 id                      : 'ue_day_phone',
301                                 type            : 'input',
302                                 regex           :  phoneRegex,
303                         }
304                 },
305                 {
306                         required : false,
307                         object  : patron,
308                         key             : 'evening_phone',
309                         errkey  : 'ue_bad_phone',
310                         widget  : {
311                                 id                      : 'ue_night_phone',
312                                 type            : 'input',
313                                 regex           :  phoneRegex,
314                         }
315                 },
316                 {
317                         required : false,
318                         object  : patron,
319                         key             : 'other_phone',
320                         errkey  : 'ue_bad_phone',
321                         widget  : {
322                                 id                      : 'ue_other_phone',
323                                 type            : 'input',
324                                 regex           :  phoneRegex,
325                         }
326                 },
327                 {
328                         required : true,
329                         object  : patron,
330                         key             : 'home_ou',
331                         widget  : {
332                                 id                      : 'ue_org_selector',
333                                 type            : 'select',
334                                 regex           :  numRegex,
335                         }
336                 },
337                 {
338                         required : true,
339                         object  : patron,
340                         key             : 'expire_date',
341                         errkey  : 'ue_bad_expire',
342                         widget  : {
343                                 id                      : 'ue_expire',
344                                 type            : 'input',
345                                 regex           :  dateRegex,
346                         }
347                 },
348                 {
349                         required : false,
350                         object  : patron,
351                         key             : 'active',
352                         widget  : {
353                                 id                      : 'ue_active',
354                                 type            : 'checkbox',
355                         }
356                 },
357                 {
358                         required : false,
359                         object  : patron,
360                         key             : 'barred',
361                         widget  : {
362                                 id                      : 'ue_barred',
363                                 type            : 'checkbox',
364                                 onpostchange : function(field, val) {
365                                         var afield = uEditFindFieldByKey('alert_message');
366                                         if( val ) {
367                                                 if( !barredAlerted ) {
368                                                         barredAlerted = true;
369                                                         alertId('ue_made_barred');
370                                                 }
371                                                 afield.required = true; 
372                                         } else {
373                                                 afield.required = false;
374                                         }
375                                 }
376                         }
377                 },
378                 {
379                         required : true,
380                         object  : patron,
381                         key             : 'profile',
382                         errkey  : 'ue_no_profile',
383                         widget  : {
384                                 id                      : 'ue_profile',
385                                 type            : 'select',
386                                 regex           : numRegex,
387                                 onpostchange : function(field, value) {
388                                         var type                        = groupsCache[value];
389                                         if(!type) return;
390                                         var interval    = type.perm_interval();
391
392                                         /* interval_to_seconds expects 'M' for months, 'm' for minutes */
393                                         interval                        = interval.replace(/mon/, 'Mon'); 
394                                         var intsecs             = parseInt(interval_to_seconds(interval));
395
396                                         var expdate             = new Date();
397                                         var exptime             = expdate.getTime();
398                                         exptime                 += intsecs * 1000;
399                                         expdate.setTime(exptime);
400
401                                         _debug("profile change (interval= '"+interval+"', seconds="+intsecs+")\n\tgenerated a date of " + expdate);
402
403                                         var year                        = expdate.getYear() + 1900;
404                                         var month               = (expdate.getMonth() + 1) + '';
405                                         var day                 = (expdate.getDate()) + '';
406
407                                         if(!month.match(/\d{2}/)) month = '0' + month;
408                                         if(!day.match(/\d{2}/)) day = '0' + day;
409
410
411                                         var node = $('ue_expire');
412                                         node.value = year+'-'+month+'-'+day;
413
414                                         _debug("profile change formatted date to "+ node.value);
415                                         node.onchange();
416                                 }
417                         }
418                 },
419                 {
420                         required : false,
421                         object  : patron,
422                         key             : 'net_access_level',
423                         widget  : {
424                                 id              : 'ue_net_level',
425                                 type    : 'select'
426                         }
427                 },
428                 {
429                         required : false,
430                         object  : patron,
431                         key             : 'master_account',
432                         widget  : {
433                                 id                      : 'ue_group_lead',
434                                 type            : 'checkbox',
435                         }
436                 },
437                 {
438                         required : true,
439                         object  : patron,
440                         key             : 'claims_returned_count',
441                         widget  : {
442                                 id                      : 'ue_claims_returned',
443                                 type            : 'input',
444                                 regex           : numRegex,
445                                 disabled : true
446                         }
447                 },
448                 {
449                         required : false,
450                         object  : patron,
451                         key             : 'alert_message',
452                         widget  : {
453                                 id                      : 'ue_alert_message',
454                                 type            : 'input',
455                         }
456                 }
457         ];
458
459         for( var f in fields ) 
460                 dataFields.push(fields[f]);
461
462         uEditBuildAddrs(patron);
463         uEditBuildPatronSCM(patron);
464 }
465
466 var uEditOldFirstName;
467 var uEditOldMiddleName; /* future */
468 var uEditOldLastName;
469 function uEditCheckNamesDup(type, field) {
470         var newval = uEditNodeVal(field);
471         if(!newval) return;
472
473         var dosearch = false;
474
475         if(type =='first') {
476                 if( newval != uEditOldFirstName )
477                         dosearch = true;
478                 uEditOldFirstName = newval;
479         }
480
481         if(type =='last') {
482                 if( newval != uEditOldLastName )
483                         dosearch = true;
484                 uEditOldLastName = newval;
485         }
486
487         if( dosearch && uEditOldFirstName && uEditOldLastName ) {
488                 var search_hash = {};
489                 search_hash['first_given_name'] = { value : uEditOldFirstName, group : 0 };
490                 search_hash['family_name'] = { value : uEditOldLastName, group : 0 };
491                 uEditRunDupeSearch('names', search_hash);
492         }
493 }
494
495 var uEditOldIdentValue;
496 function uEditCheckIdentDup(field) {
497         var newval = uEditNodeVal(field);
498         if( newval && newval != uEditOldIdentValue ) {
499                 /* searches all ident_value fields */
500                 var search_hash  = { ident : { value : newval, group : 2 } };
501                 uEditRunDupeSearch('ident', search_hash);
502                 uEditOldIdentValue = newval;
503         }
504 }
505
506
507 /* Adds all of the addresses attached to the patron object
508         to the fields array */
509 var uEditAddrTemplate;
510 function uEditBuildAddrs(patron) {
511         var tbody = $('ue_address_tbody');
512         if(!uEditAddrTemplate)
513                 uEditAddrTemplate = tbody.removeChild($('ue_address_template'));
514         for( var a in patron.addresses() ) 
515                 uEditBuildAddrFields( patron, patron.addresses()[a]);
516 }
517
518
519 function uEditDeleteAddr( tbody, row, address, detach ) {
520         if(!confirm($('ue_delete_addr_warn').innerHTML)) return;
521         if(address.isnew()) { 
522                 patron.addresses(
523                         grep( patron.addresses(), 
524                                 function(i) {
525                                         return (i.id() != address.id());
526                                 }
527                         )
528                 );
529
530                 /* XXX */
531                 for( var f in dataFields ) {
532                         if( dataFields[f].object == address ) {
533                                 dataFields[f] = null;
534                         }
535                 }
536
537                 dataFields = compactArray(dataFields);
538
539         } else {
540
541                 if( detach ) { /* remove the offending address from the list */
542                         patron.addresses(
543                                 grep( 
544                                         patron.addresses(), 
545                                         function(i) {
546                                                 return (i.id() != address.id());
547                                         }
548                                 )
549                         );
550
551                 } else {
552                         address.isdeleted(1);
553                 }
554         }
555
556         tbody.removeChild(row);
557
558         var bid = patron.billing_address();
559         bid = (typeof bid == 'object') ? bid.id() : bid;
560
561         var mid = patron.mailing_address();
562         mid = (typeof mid == 'object') ? mid.id() : mid;
563
564
565         /* -----------------------------------------------------------------------
566                 if we're deleting a billing or mailing address 
567                 make sure some other address is automatically
568                 assigned as the billing or mailng address 
569                 ----------------------------------------------------------------------- */
570
571         if( bid == address.id() ) {
572                 for( var a in patron.addresses() ) {
573                         var addr = patron.addresses()[a];
574                         if(!addr.isdeleted() && addr.id() != address.id()) {
575                                 var node = uEditFindAddrInput('billing', addr.id());
576                                 node.checked = true;
577                                 uEditAddrTypeClick(node, 'billing');
578                                 break;
579                         }
580                 }
581         }
582
583         if( mid == address.id() ) {
584                 for( var a in patron.addresses() ) {
585                         var addr = patron.addresses()[a];
586                         if(!addr.isdeleted() && addr.id() != address.id()) {
587                                 var node = uEditFindAddrInput('mailing', addr.id());
588                                 node.checked = true;
589                                 uEditAddrTypeClick(node, 'mailing');
590                                 break;
591                         }
592                 }
593         }
594
595 }
596
597
598 function uEditFindAddrInput(type, id) {
599         var tbody = $('ue_address_tbody');
600         var rows = tbody.getElementsByTagName('tr');
601         for( var r in rows ) {
602                 var row = rows[r];
603                 if(row.parentNode != tbody) continue;
604                 var node = $n(row, 'ue_addr_'+type+'_yes');
605                 if( node.getAttribute('address') == id )
606                         return node;
607         }
608 }
609
610
611 function uEditAddrTypeClick(input, type) {
612         var tbody = $('ue_address_tbody');
613         var rows = tbody.getElementsByTagName('tr');
614         for( var r in rows ) {
615                 var row = rows[r];
616                 if(row.parentNode != tbody) continue;
617                 var node = $n(row, 'ue_addr_'+type+'_yes');
618                 removeCSSClass(node.parentNode,'addr_info_checked');
619         }
620
621         addCSSClass(input.parentNode,'addr_info_checked');
622         patron[type+'_address'](input.getAttribute('address'));
623         patron.ischanged(1);
624 }
625
626
627
628
629 /* Creates the field entries for an address object. */
630 function uEditBuildAddrFields(patron, address) {
631
632         var tbody = $('ue_address_tbody');
633
634         var row = tbody.appendChild(
635                 uEditAddrTemplate.cloneNode(true));
636
637         uEditCheckSharedAddr(patron, address, tbody, row);
638
639         $n(row, 'ue_addr_delete').onclick = 
640                 function() { 
641                         uEditDeleteAddr(tbody, row, address); 
642                         uEditCheckErrors();
643                 };
644
645         if( patron.billing_address() &&
646                         address.id() == patron.billing_address().id() ) 
647                 $n(row, 'ue_addr_billing_yes').checked = true;
648
649         if( patron.mailing_address() &&
650                         address.id() == patron.mailing_address().id() ) 
651                 $n(row, 'ue_addr_mailing_yes').checked = true;
652
653         $n(row, 'ue_addr_billing_yes').setAttribute('address', address.id());
654         $n(row, 'ue_addr_mailing_yes').setAttribute('address', address.id());
655
656         /* currently, non-owners cannot edit an address */
657         var disabled = (address.usr() != patron.id())
658
659         var fields = [
660                 { 
661                         required : false,
662                         object  : address, 
663                         key             : 'address_type', 
664                         widget  : {
665                                 base    : row,
666                                 name    : 'ue_addr_label',
667                                 type    : 'input',
668                                 disabled : disabled,
669                         }
670                 },
671                 { 
672                         required : true,
673                         object  : address, 
674                         key             : 'street1', 
675                         errkey  : 'ue_bad_addr_street',
676                         widget  : {
677                                 base    : row,
678                                 name    : 'ue_addr_street1',
679                                 type    : 'input',
680                                 disabled : disabled,
681                         }
682                 },
683                 { 
684                         required : false,
685                         object  : address, 
686                         key             : 'street2', 
687                         errkey  : 'ue_bad_addr_street',
688                         widget  : {
689                                 base    : row,
690                                 name    : 'ue_addr_street2',
691                                 type    : 'input',
692                                 disabled : disabled,
693                         }
694                 },
695                 { 
696                         required : true,
697                         object  : address, 
698                         key             : 'city', 
699                         errkey  : 'ue_bad_addr_city',
700                         widget  : {
701                                 base    : row,
702                                 name    : 'ue_addr_city',
703                                 type    : 'input',
704                                 disabled : disabled,
705                         }
706                 },
707                 { 
708                         required : false,
709                         object  : address, 
710                         key             : 'county', 
711                         widget  : {
712                                 base    : row,
713                                 name    : 'ue_addr_county',
714                                 type    : 'input',
715                                 disabled : disabled,
716                         }
717                 },
718                 { 
719                         required : true,
720                         object  : address, 
721                         key             : 'state', 
722                         errkey  : 'ue_bad_addr_state',
723                         widget  : {
724                                 base    : row,
725                                 name    : 'ue_addr_state',
726                                 type    : 'input',
727                                 disabled : disabled,
728                         }
729                 },
730                 { 
731                         required : true,
732                         object  : address, 
733                         key             : 'country', 
734                         errkey  : 'ue_bad_addr_country',
735                         widget  : {
736                                 base    : row,
737                                 name    : 'ue_addr_country',
738                                 type    : 'input',
739                                 disabled : disabled,
740                         }
741                 },
742                 { 
743                         required : true,
744                         object  : address, 
745                         key             : 'post_code',
746                         errkey  : 'ue_bad_addr_zip',
747                         widget  : {
748                                 base    : row,
749                                 name    : 'ue_addr_zip',
750                                 type    : 'input',
751                                 disabled : disabled,
752                                 regex   : zipRegex,
753                                 onblur : function(f) {
754                                         var v = uEditNodeVal(f);
755                                         var req = new Request(ZIP_SEARCH, v);
756                                         req.callback( 
757                                                 function(r) {
758                                                         var info = r.getResultObject();
759                                                         if(!info) return;
760                                                         var state = $n(f.widget.base, 'ue_addr_state');
761                                                         var county = $n(f.widget.base, 'ue_addr_county');
762                                                         var city = $n(f.widget.base, 'ue_addr_city');
763                                                         if(!state.value) {
764                                                                 state.value = info.state;
765                                                                 state.onchange();
766                                                         }
767                                                         if(!county.value) {
768                                                                 county.value = info.county;
769                                                                 county.onchange();
770                                                         }
771                                                         if(!city.value) {
772                                                                 city.value = info.city;
773                                                                 city.onchange();
774                                                         }
775                                                 }
776                                         );
777                                         req.send();
778                                 }
779                         }
780                 },
781                 { 
782                         required : false,
783                         object  : address, 
784                         key             : 'within_city_limits',
785                         widget  : {
786                                 base    : row,
787                                 name    : 'ue_addr_inc_yes',
788                                 type    : 'checkbox',
789                                 disabled : disabled,
790                         }
791                 },
792                 { 
793                         required : false,
794                         object  : address, 
795                         key             : 'valid',
796                         widget  : {
797                                 base    : row,
798                                 name    : 'ue_addr_valid_yes',
799                                 type    : 'checkbox',
800                                 disabled : disabled,
801                         }
802                 }
803         ];
804
805         for( var f in fields ) {
806                 dataFields.push(fields[f]);
807                 uEditActivateField(fields[f]);
808         }
809 }
810
811 function uEditBuildPatronSCM(patron) {
812         /* get the list of pre-defined maps */
813         var fields = uEditFindFieldsByKey('stat_cat_entry');
814         var map;
815         var newmaps = [];
816
817         /* for each user stat cat, pop it off the list,
818         updated the existing stat map field to match
819         the popped map and shove the existing stat
820         map field onto the user's list of stat maps */
821         while( (map = patron.stat_cat_entries().pop()) ) {
822
823                 var field = grep(fields, 
824                         function(item) {
825                                 return (item.object.stat_cat() == map.stat_cat());
826                         }
827                 );
828
829                 if(field) {
830                         var val = map.stat_cat_entry();
831                         field = field[0];
832                         $n(field.widget.base, field.widget.name).value = val;
833                         setSelector($n(field.widget.base, 'ue_stat_cat_selector'), val );
834                         field.object.stat_cat_entry(val);
835                         field.object.id(map.id());
836                         newmaps.push(field.object);
837                 }
838         }
839
840         for( var m in newmaps ) 
841                 patron.stat_cat_entries().push(newmaps[m]);
842 }
843
844
845 function uEditBuildSCMField(statcat, row) {
846
847         var map = new actscecm();
848         map.stat_cat(statcat.id());
849         map.target_usr(patron.id());
850
851         var field = {
852                 required : false,
853                 object  : map,
854                 key             : 'stat_cat_entry',
855                 widget  : {
856                         base    : row,
857                         name    : 'ue_stat_cat_newval',
858                         type    : 'input',
859
860                         onpostchange : function( field, newval ) {
861
862                                 /* see if the current map already resides in 
863                                         the patron entry list */
864                                 var exists = grep( patron.stat_cat_entries(),
865                                         function(item) {
866                                                 return (item.stat_cat() == statcat.id()); 
867                                         }
868                                 );
869
870                                 if(newval) {
871                                         map.isdeleted(0);
872                                         setSelector($n(row, 'ue_stat_cat_selector'), newval);
873                                 }
874
875                                 if(exists) {
876                                         if(!newval) {
877
878                                                 /* if the map is new but currently contains no value
879                                                         remove it from the set of new maps */
880                                                 if(map.isnew()) {
881                                                         patron.stat_cat_entries(
882                                                                 grep( patron.stat_cat_entries(),
883                                                                         function(item) {
884                                                                                 return (item.stat_cat() != map.stat_cat());
885                                                                         }
886                                                                 )
887                                                         );
888
889                                                 } else {
890                                                         map.isdeleted(1);
891                                                         map.ischanged(0);
892                                                 }
893                                         } 
894
895                                 } else {
896
897                                         /* map does not exist in the map array but now has data */
898                                         if(newval) { 
899                                                 map.isnew(1);
900                                                 if(!patron.stat_cat_entries())
901                                                         patron.stat_cat_entries([]);
902                                                 patron.stat_cat_entries().push(map);
903                                         }
904                                 }
905                         }
906                 }
907         }
908
909         dataFields.push(field);
910 }
911
912
913
914 /** Run this after a new ident type is selected */
915 function _uEditIdentPostchange(type, field, newval) {
916
917         if(!newval) return;
918
919         /* When the ident type is changed, we change the
920         regex on the ident_value to match the selected type */
921         var vfname = 'ident_value';
922         if(type == 'secondary') vfname = 'ident_value2';
923         var vfield = uEditFindFieldByKey(vfname);
924         var name = identTypesCache[uEditNodeVal(field)].name();
925
926         hideMe($(type+'_ident_ssn_help'));
927         hideMe($(type+'_ident_dl_help'));
928
929         if(name.match(/ssn/i)) {
930                 vfield.widget.regex = ssnRegex;
931                 vfield.errkey = 'ue_bad_ident_ssn';
932                 unHideMe($(type+'_ident_ssn_help'));
933
934         } else {
935
936                 if(name.match(/driver/i)) {
937                         vfield.widget.regex = dlRegex;
938                         vfield.errkey = 'ue_bad_ident_dl';
939                         unHideMe($(type+'_ident_dl_help'));
940                         if(!uEditNodeVal(vfield))
941                                 vfield.widget.node.value = defaultState + '-';
942
943                 } else {
944                         vfield.widget.regex = null;
945                         vfield.errkey = null;
946                 }
947         }
948
949         /* focus then valdate the value field */
950         vfield.widget.node.onchange();
951         vfield.widget.node.focus();
952 }
953
954
955 /* checks to see if the given address is shared by others.
956  * if so, the address row is styled and ...
957  */
958
959 function uEditCheckSharedAddr(patron, address, tbody, row) {
960
961         if( address.isnew() || (patron.isnew() && !clone) ) return;
962
963         var req = new Request(FETCH_ADDR_MEMS, SESSION, address.id());
964         req.callback( 
965                 function(r) {
966
967                         var members = r.getResultObject();
968                         var shared = false;
969
970                         for( var m in members ) {
971                                 var id = members[m];
972
973                                 if( id != patron.id() ) {
974
975                                         addCSSClass(row.getElementsByTagName('table')[0], 'shared_address');
976                                         unHideMe($n(row, 'shared_row'));
977                                         $n(row, 'ue_addr_delete').disabled = true;
978
979                                         if( address.usr() != patron.id() ) {
980                                                 var button = $n(row, 'ue_addr_detach');
981                                                 unHideMe(button);
982                                                 button.onclick = 
983                                                         function() { uEditDeleteAddr( tbody, row, address, true ); }
984                                         }
985
986                                         shared = true;
987                                         break;
988                                 }
989                         }
990
991                         if( shared ) {
992
993                                 /* if this is a shared address, set the owner field and 
994                                         give the staff a chance to edit the owner if it's not this user */
995
996                                 var nnode = $n(row, 'addr_owner_name');
997                                 var link = $n(row, 'addr_owner');
998                                 var id = address.usr();
999                         
1000                                 if( id == patron.id() ) {
1001                         
1002                                         nnode.appendChild(text(
1003                                                 patron.first_given_name() + ' ' + patron.family_name()));
1004                                         hideMe($n(row, 'owner_link_div'));
1005                         
1006                                 } else {
1007                         
1008                                         link.onclick = 
1009                                                 function() { window.xulG.spawn_editor({ses:cgi.param('ses'),usr:id}) };
1010                                 
1011                                         if( userCache[id] ) {
1012                                                 nnode.appendChild(text(
1013                                                         usr.first_given_name() + ' ' +  usr.family_name()));
1014                                 
1015                                         } else {
1016                                 
1017                                                 fetchFleshedUser( id, 
1018                                                         function(usr) {
1019                                                                 userCache[usr.id()] = usr;
1020                                                                 nnode.appendChild(text(
1021                                                                         usr.first_given_name() + ' ' + usr.family_name()));
1022                                                         }
1023                                                 );
1024                                         }
1025                                 }
1026                         }
1027                 }
1028         );
1029
1030         req.send();
1031 }
1032
1033
1034
1035
1036 var __lastdob;
1037 function uEditCheckDOB(field) {
1038
1039         var dob = uEditNodeVal(field);
1040
1041         /* don't bother if the data isn't valid */
1042         if(!dob || !dob.match(field.widget.regex)) 
1043                 return;
1044
1045         if( dob == __lastdob ) return;
1046
1047         __lastdob = dob;
1048
1049         var parts = dob.split(/-/);
1050         parts[2] = parts[2].replace(/[T ].*/,'');
1051         dob = buildDate( parts[0], parts[1], parts[2] );
1052
1053         var today = new Date();
1054
1055         if(!dob || dob > today) {
1056                 addCSSClass(field.widget.node, CSS_INVALID_DATA);
1057                 alertId('ue_bad_date');
1058                 return;
1059         }
1060
1061         var base = new Date();
1062         base.setYear( today.getYear() + 1900 - ADULT_AGE );
1063
1064         /* patron is at least 18 */
1065
1066         var f = uEditFindFieldByKey('ident_value2');
1067
1068         if( dob < base ) { /* patron is of age */
1069                 f.required = false;
1070                 hideMe(f.widget.node.parentNode.parentNode.parentNode);
1071                 return;
1072         }
1073
1074         unHideMe(f.widget.node.parentNode.parentNode.parentNode);
1075         f.required = true;
1076         uEditCheckErrors();
1077 }
1078
1079
1080