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