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