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