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