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