]> git.evergreen-ils.org Git - Evergreen.git/blob - Evergreen/xul/staff_client/server/patron/ue_config.js
added some more debugging
[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                                 }
416                         }
417                 },
418                 {
419                         required : false,
420                         object  : patron,
421                         key             : 'net_access_level',
422                         widget  : {
423                                 id              : 'ue_net_level',
424                                 type    : 'select'
425                         }
426                 },
427                 {
428                         required : false,
429                         object  : patron,
430                         key             : 'master_account',
431                         widget  : {
432                                 id                      : 'ue_group_lead',
433                                 type            : 'checkbox',
434                         }
435                 },
436                 {
437                         required : true,
438                         object  : patron,
439                         key             : 'claims_returned_count',
440                         widget  : {
441                                 id                      : 'ue_claims_returned',
442                                 type            : 'input',
443                                 regex           : numRegex,
444                                 disabled : true
445                         }
446                 },
447                 {
448                         required : false,
449                         object  : patron,
450                         key             : 'alert_message',
451                         widget  : {
452                                 id                      : 'ue_alert_message',
453                                 type            : 'input',
454                         }
455                 }
456         ];
457
458         for( var f in fields ) 
459                 dataFields.push(fields[f]);
460
461         uEditBuildAddrs(patron);
462         uEditBuildPatronSCM(patron);
463 }
464
465 var uEditOldFirstName;
466 var uEditOldMiddleName; /* future */
467 var uEditOldLastName;
468 function uEditCheckNamesDup(type, field) {
469         var newval = uEditNodeVal(field);
470         if(!newval) return;
471
472         var dosearch = false;
473
474         if(type =='first') {
475                 if( newval != uEditOldFirstName )
476                         dosearch = true;
477                 uEditOldFirstName = newval;
478         }
479
480         if(type =='last') {
481                 if( newval != uEditOldLastName )
482                         dosearch = true;
483                 uEditOldLastName = newval;
484         }
485
486         if( dosearch && uEditOldFirstName && uEditOldLastName ) {
487                 var search_hash = {};
488                 search_hash['first_given_name'] = { value : uEditOldFirstName, group : 0 };
489                 search_hash['family_name'] = { value : uEditOldLastName, group : 0 };
490                 uEditRunDupeSearch('names', search_hash);
491         }
492 }
493
494 var uEditOldIdentValue;
495 function uEditCheckIdentDup(field) {
496         var newval = uEditNodeVal(field);
497         if( newval && newval != uEditOldIdentValue ) {
498                 /* searches all ident_value fields */
499                 var search_hash  = { ident : { value : newval, group : 2 } };
500                 uEditRunDupeSearch('ident', search_hash);
501                 uEditOldIdentValue = newval;
502         }
503 }
504
505
506 /* Adds all of the addresses attached to the patron object
507         to the fields array */
508 var uEditAddrTemplate;
509 function uEditBuildAddrs(patron) {
510         var tbody = $('ue_address_tbody');
511         if(!uEditAddrTemplate)
512                 uEditAddrTemplate = tbody.removeChild($('ue_address_template'));
513         for( var a in patron.addresses() ) 
514                 uEditBuildAddrFields( patron, patron.addresses()[a]);
515 }
516
517
518 function uEditDeleteAddr( tbody, row, address, detach ) {
519         if(!confirm($('ue_delete_addr_warn').innerHTML)) return;
520         if(address.isnew()) { 
521                 patron.addresses(
522                         grep( patron.addresses(), 
523                                 function(i) {
524                                         return (i.id() != address.id());
525                                 }
526                         )
527                 );
528
529                 /* XXX */
530                 for( var f in dataFields ) {
531                         if( dataFields[f].object == address ) {
532                                 dataFields[f] = null;
533                         }
534                 }
535
536                 dataFields = compactArray(dataFields);
537
538         } else {
539
540                 if( detach ) { /* remove the offending address from the list */
541                         patron.addresses(
542                                 grep( 
543                                         patron.addresses(), 
544                                         function(i) {
545                                                 return (i.id() != address.id());
546                                         }
547                                 )
548                         );
549
550                 } else {
551                         address.isdeleted(1);
552                 }
553         }
554
555         tbody.removeChild(row);
556
557         var bid = patron.billing_address();
558         bid = (typeof bid == 'object') ? bid.id() : bid;
559
560         var mid = patron.mailing_address();
561         mid = (typeof mid == 'object') ? mid.id() : mid;
562
563
564         /* -----------------------------------------------------------------------
565                 if we're deleting a billing or mailing address 
566                 make sure some other address is automatically
567                 assigned as the billing or mailng address 
568                 ----------------------------------------------------------------------- */
569
570         if( bid == address.id() ) {
571                 for( var a in patron.addresses() ) {
572                         var addr = patron.addresses()[a];
573                         if(!addr.isdeleted() && addr.id() != address.id()) {
574                                 var node = uEditFindAddrInput('billing', addr.id());
575                                 node.checked = true;
576                                 uEditAddrTypeClick(node, 'billing');
577                                 break;
578                         }
579                 }
580         }
581
582         if( mid == address.id() ) {
583                 for( var a in patron.addresses() ) {
584                         var addr = patron.addresses()[a];
585                         if(!addr.isdeleted() && addr.id() != address.id()) {
586                                 var node = uEditFindAddrInput('mailing', addr.id());
587                                 node.checked = true;
588                                 uEditAddrTypeClick(node, 'mailing');
589                                 break;
590                         }
591                 }
592         }
593
594 }
595
596
597 function uEditFindAddrInput(type, id) {
598         var tbody = $('ue_address_tbody');
599         var rows = tbody.getElementsByTagName('tr');
600         for( var r in rows ) {
601                 var row = rows[r];
602                 if(row.parentNode != tbody) continue;
603                 var node = $n(row, 'ue_addr_'+type+'_yes');
604                 if( node.getAttribute('address') == id )
605                         return node;
606         }
607 }
608
609
610 function uEditAddrTypeClick(input, type) {
611         var tbody = $('ue_address_tbody');
612         var rows = tbody.getElementsByTagName('tr');
613         for( var r in rows ) {
614                 var row = rows[r];
615                 if(row.parentNode != tbody) continue;
616                 var node = $n(row, 'ue_addr_'+type+'_yes');
617                 removeCSSClass(node.parentNode,'addr_info_checked');
618         }
619
620         addCSSClass(input.parentNode,'addr_info_checked');
621         patron[type+'_address'](input.getAttribute('address'));
622         patron.ischanged(1);
623 }
624
625
626
627
628 /* Creates the field entries for an address object. */
629 function uEditBuildAddrFields(patron, address) {
630
631         var tbody = $('ue_address_tbody');
632
633         var row = tbody.appendChild(
634                 uEditAddrTemplate.cloneNode(true));
635
636         uEditCheckSharedAddr(patron, address, tbody, row);
637
638         $n(row, 'ue_addr_delete').onclick = 
639                 function() { uEditDeleteAddr(tbody, row, address); }
640
641         if( patron.billing_address() &&
642                         address.id() == patron.billing_address().id() ) 
643                 $n(row, 'ue_addr_billing_yes').checked = true;
644
645         if( patron.mailing_address() &&
646                         address.id() == patron.mailing_address().id() ) 
647                 $n(row, 'ue_addr_mailing_yes').checked = true;
648
649         $n(row, 'ue_addr_billing_yes').setAttribute('address', address.id());
650         $n(row, 'ue_addr_mailing_yes').setAttribute('address', address.id());
651
652         /* currently, non-owners cannot edit an address */
653         var disabled = (address.usr() != patron.id())
654
655         var fields = [
656                 { 
657                         required : false,
658                         object  : address, 
659                         key             : 'address_type', 
660                         widget  : {
661                                 base    : row,
662                                 name    : 'ue_addr_label',
663                                 type    : 'input',
664                                 disabled : disabled,
665                         }
666                 },
667                 { 
668                         required : true,
669                         object  : address, 
670                         key             : 'street1', 
671                         errkey  : 'ue_bad_addr_street',
672                         widget  : {
673                                 base    : row,
674                                 name    : 'ue_addr_street1',
675                                 type    : 'input',
676                                 disabled : disabled,
677                         }
678                 },
679                 { 
680                         required : false,
681                         object  : address, 
682                         key             : 'street2', 
683                         errkey  : 'ue_bad_addr_street',
684                         widget  : {
685                                 base    : row,
686                                 name    : 'ue_addr_street2',
687                                 type    : 'input',
688                                 disabled : disabled,
689                         }
690                 },
691                 { 
692                         required : true,
693                         object  : address, 
694                         key             : 'city', 
695                         errkey  : 'ue_bad_addr_city',
696                         widget  : {
697                                 base    : row,
698                                 name    : 'ue_addr_city',
699                                 type    : 'input',
700                                 disabled : disabled,
701                         }
702                 },
703                 { 
704                         required : false,
705                         object  : address, 
706                         key             : 'county', 
707                         widget  : {
708                                 base    : row,
709                                 name    : 'ue_addr_county',
710                                 type    : 'input',
711                                 disabled : disabled,
712                         }
713                 },
714                 { 
715                         required : true,
716                         object  : address, 
717                         key             : 'state', 
718                         errkey  : 'ue_bad_addr_state',
719                         widget  : {
720                                 base    : row,
721                                 name    : 'ue_addr_state',
722                                 type    : 'input',
723                                 disabled : disabled,
724                         }
725                 },
726                 { 
727                         required : true,
728                         object  : address, 
729                         key             : 'country', 
730                         errkey  : 'ue_bad_addr_country',
731                         widget  : {
732                                 base    : row,
733                                 name    : 'ue_addr_country',
734                                 type    : 'input',
735                                 disabled : disabled,
736                         }
737                 },
738                 { 
739                         required : true,
740                         object  : address, 
741                         key             : 'post_code',
742                         errkey  : 'ue_bad_addr_zip',
743                         widget  : {
744                                 base    : row,
745                                 name    : 'ue_addr_zip',
746                                 type    : 'input',
747                                 disabled : disabled,
748                                 regex   : zipRegex,
749                                 onblur : function(f) {
750                                         var v = uEditNodeVal(f);
751                                         var req = new Request(ZIP_SEARCH, v);
752                                         req.callback( 
753                                                 function(r) {
754                                                         var info = r.getResultObject();
755                                                         if(!info) return;
756                                                         var state = $n(f.widget.base, 'ue_addr_state');
757                                                         var county = $n(f.widget.base, 'ue_addr_county');
758                                                         var city = $n(f.widget.base, 'ue_addr_city');
759                                                         if(!state.value) {
760                                                                 state.value = info.state;
761                                                                 state.onchange();
762                                                         }
763                                                         if(!county.value) {
764                                                                 county.value = info.county;
765                                                                 county.onchange();
766                                                         }
767                                                         if(!city.value) {
768                                                                 city.value = info.city;
769                                                                 city.onchange();
770                                                         }
771                                                 }
772                                         );
773                                         req.send();
774                                 }
775                         }
776                 },
777                 { 
778                         required : false,
779                         object  : address, 
780                         key             : 'within_city_limits',
781                         widget  : {
782                                 base    : row,
783                                 name    : 'ue_addr_inc_yes',
784                                 type    : 'checkbox',
785                                 disabled : disabled,
786                         }
787                 },
788                 { 
789                         required : false,
790                         object  : address, 
791                         key             : 'valid',
792                         widget  : {
793                                 base    : row,
794                                 name    : 'ue_addr_valid_yes',
795                                 type    : 'checkbox',
796                                 disabled : disabled,
797                         }
798                 }
799         ];
800
801         for( var f in fields ) {
802                 dataFields.push(fields[f]);
803                 uEditActivateField(fields[f]);
804         }
805 }
806
807 function uEditBuildPatronSCM(patron) {
808         /* get the list of pre-defined maps */
809         var fields = uEditFindFieldsByKey('stat_cat_entry');
810         var map;
811         var newmaps = [];
812
813         /* for each user stat cat, pop it off the list,
814         updated the existing stat map field to match
815         the popped map and shove the existing stat
816         map field onto the user's list of stat maps */
817         while( (map = patron.stat_cat_entries().pop()) ) {
818
819                 var field = grep(fields, 
820                         function(item) {
821                                 return (item.object.stat_cat() == map.stat_cat());
822                         }
823                 );
824
825                 if(field) {
826                         var val = map.stat_cat_entry();
827                         field = field[0];
828                         $n(field.widget.base, field.widget.name).value = val;
829                         setSelector($n(field.widget.base, 'ue_stat_cat_selector'), val );
830                         field.object.stat_cat_entry(val);
831                         field.object.id(map.id());
832                         newmaps.push(field.object);
833                 }
834         }
835
836         for( var m in newmaps ) 
837                 patron.stat_cat_entries().push(newmaps[m]);
838 }
839
840
841 function uEditBuildSCMField(statcat, row) {
842
843         var map = new actscecm();
844         map.stat_cat(statcat.id());
845         map.target_usr(patron.id());
846
847         var field = {
848                 required : false,
849                 object  : map,
850                 key             : 'stat_cat_entry',
851                 widget  : {
852                         base    : row,
853                         name    : 'ue_stat_cat_newval',
854                         type    : 'input',
855
856                         onpostchange : function( field, newval ) {
857
858                                 /* see if the current map already resides in 
859                                         the patron entry list */
860                                 var exists = grep( patron.stat_cat_entries(),
861                                         function(item) {
862                                                 return (item.stat_cat() == statcat.id()); 
863                                         }
864                                 );
865
866                                 if(newval) {
867                                         map.isdeleted(0);
868                                         setSelector($n(row, 'ue_stat_cat_selector'), newval);
869                                 }
870
871                                 if(exists) {
872                                         if(!newval) {
873
874                                                 /* if the map is new but currently contains no value
875                                                         remove it from the set of new maps */
876                                                 if(map.isnew()) {
877                                                         patron.stat_cat_entries(
878                                                                 grep( patron.stat_cat_entries(),
879                                                                         function(item) {
880                                                                                 return (item.stat_cat() != map.stat_cat());
881                                                                         }
882                                                                 )
883                                                         );
884
885                                                 } else {
886                                                         map.isdeleted(1);
887                                                         map.ischanged(0);
888                                                 }
889                                         } 
890
891                                 } else {
892
893                                         /* map does not exist in the map array but now has data */
894                                         if(newval) { 
895                                                 map.isnew(1);
896                                                 if(!patron.stat_cat_entries())
897                                                         patron.stat_cat_entries([]);
898                                                 patron.stat_cat_entries().push(map);
899                                         }
900                                 }
901                         }
902                 }
903         }
904
905         dataFields.push(field);
906 }
907
908
909
910 /** Run this after a new ident type is selected */
911 function _uEditIdentPostchange(type, field, newval) {
912
913         if(!newval) return;
914
915         /* When the ident type is changed, we change the
916         regex on the ident_value to match the selected type */
917         var vfname = 'ident_value';
918         if(type == 'secondary') vfname = 'ident_value2';
919         var vfield = uEditFindFieldByKey(vfname);
920         var name = identTypesCache[uEditNodeVal(field)].name();
921
922         hideMe($(type+'_ident_ssn_help'));
923         hideMe($(type+'_ident_dl_help'));
924
925         if(name.match(/ssn/i)) {
926                 vfield.widget.regex = ssnRegex;
927                 vfield.errkey = 'ue_bad_ident_ssn';
928                 unHideMe($(type+'_ident_ssn_help'));
929
930         } else {
931
932                 if(name.match(/driver/i)) {
933                         vfield.widget.regex = dlRegex;
934                         vfield.errkey = 'ue_bad_ident_dl';
935                         unHideMe($(type+'_ident_dl_help'));
936                         if(!uEditNodeVal(vfield))
937                                 vfield.widget.node.value = defaultState + '-';
938
939                 } else {
940                         vfield.widget.regex = null;
941                         vfield.errkey = null;
942                 }
943         }
944
945         /* focus then valdate the value field */
946         vfield.widget.node.onchange();
947         vfield.widget.node.focus();
948 }
949
950
951 /* checks to see if the given address is shared by others.
952  * if so, the address row is styled and ...
953  */
954
955 function uEditCheckSharedAddr(patron, address, tbody, row) {
956
957         if( address.isnew() || (patron.isnew() && !clone) ) return;
958
959         var req = new Request(FETCH_ADDR_MEMS, SESSION, address.id());
960         req.callback( 
961                 function(r) {
962
963                         var members = r.getResultObject();
964                         var shared = false;
965
966                         for( var m in members ) {
967                                 var id = members[m];
968
969                                 if( id != patron.id() ) {
970
971                                         addCSSClass(row.getElementsByTagName('table')[0], 'shared_address');
972                                         unHideMe($n(row, 'shared_row'));
973                                         $n(row, 'ue_addr_delete').disabled = true;
974
975                                         if( address.usr() != patron.id() ) {
976                                                 var button = $n(row, 'ue_addr_detach');
977                                                 unHideMe(button);
978                                                 button.onclick = 
979                                                         function() { uEditDeleteAddr( tbody, row, address, true ); }
980                                         }
981
982                                         shared = true;
983                                         break;
984                                 }
985                         }
986
987                         if( shared ) {
988
989                                 /* if this is a shared address, set the owner field and 
990                                         give the staff a chance to edit the owner if it's not this user */
991
992                                 var nnode = $n(row, 'addr_owner_name');
993                                 var link = $n(row, 'addr_owner');
994                                 var id = address.usr();
995                         
996                                 if( id == patron.id() ) {
997                         
998                                         nnode.appendChild(text(
999                                                 patron.first_given_name() + ' ' + patron.family_name()));
1000                                         hideMe($n(row, 'owner_link_div'));
1001                         
1002                                 } else {
1003                         
1004                                         link.onclick = 
1005                                                 function() { window.xulG.spawn_editor({ses:cgi.param('ses'),usr:id}) };
1006                                 
1007                                         if( userCache[id] ) {
1008                                                 nnode.appendChild(text(
1009                                                         usr.first_given_name() + ' ' +  usr.family_name()));
1010                                 
1011                                         } else {
1012                                 
1013                                                 fetchFleshedUser( id, 
1014                                                         function(usr) {
1015                                                                 userCache[usr.id()] = usr;
1016                                                                 nnode.appendChild(text(
1017                                                                         usr.first_given_name() + ' ' + usr.family_name()));
1018                                                         }
1019                                                 );
1020                                         }
1021                                 }
1022                         }
1023                 }
1024         );
1025
1026         req.send();
1027 }
1028
1029
1030
1031
1032 var __lastdob;
1033 function uEditCheckDOB(field) {
1034
1035         var dob = uEditNodeVal(field);
1036
1037         /* don't bother if the data isn't valid */
1038         if(!dob || !dob.match(field.widget.regex)) 
1039                 return;
1040
1041         if( dob == __lastdob ) return;
1042
1043         __lastdob = dob;
1044
1045         var parts = dob.split(/-/);
1046         parts[2] = parts[2].replace(/[T ].*/,'');
1047         dob = buildDate( parts[0], parts[1], parts[2] );
1048
1049         var today = new Date();
1050
1051         if(!dob || dob > today) {
1052                 addCSSClass(field.widget.node, CSS_INVALID_DATA);
1053                 alertId('ue_bad_date');
1054                 return;
1055         }
1056
1057         var base = new Date();
1058         base.setYear( today.getYear() + 1900 - ADULT_AGE );
1059
1060         /* patron is at least 18 */
1061
1062         var f = uEditFindFieldByKey('ident_value2');
1063
1064         if( dob < base ) { /* patron is of age */
1065                 f.required = false;
1066                 hideMe(f.widget.node.parentNode.parentNode.parentNode);
1067                 return;
1068         }
1069
1070         unHideMe(f.widget.node.parentNode.parentNode.parentNode);
1071         f.required = true;
1072         uEditCheckErrors();
1073 }
1074
1075
1076