PageSourceSearch

https://marblehillpublishers.co.uk/lib/acs/js/acs.tools.js

js marblehillpublishers.co.uk collected 2026-09-26 09:24:27 UTC 54,136 bytes, 1,703 lines download raw bytes

1/*
2	CMS Javascript Editing Tools [2018-10-10]
3	- the intention is to consolidate CMS Javascript files as follows:
4	- Any variables you need from $ACS[] or $params to be set in page using set_php_vars();
5	- N.B. to set HTMLEditor to show by default see showEditorFields below
6	- N.B. to configure HTMLEditor toolbar see ckCMS.js
7	- N.B. See [https://vm7.ehaus2.co.uk/ifrro/page/member-details/] for working example
8	
9*/
10/* ******************************************************************************/
11// /<![CDATA[
12	$(document).ready(function(){
13		
14	// POPOVER (tooltips)
15		$(".icon-info-sign,.fa-info-circle").popover({
16			placement:'top',
17			trigger: 'hover',
18			html: true
19		});	
20		
21	/* DATEPICKERS */
22		$(".datepicker").each(function(){
23			var $this = $(this);
24			//console.log($this.data('date-format'));
25			$this.datepicker({
26			   format: ($this.data('date-format')) ? $this.data('date-format') : 'dd MM yyyy'
27			});
28		});
29	
30	// TOGGLE TABS	
31		if($('#common').length){
32		
33			var dbm = $("body").data("dbm") ;
34			var ref_no = $("body").data("user-ref") ;
35			
36			$('a[data-toggle="tab"]').on('show.bs.tab', function(event)
37			{
38				//console.log(event);
39				var _tab_id		=	event.relatedTarget.href.split('#')[1];
40				var _form_id	=	$("#"+_tab_id).children("form").attr('id');
41
42				if(_form_id	!== undefined)
43				{
44					if(!validate(_form_id))
45					{
46						event.preventDefault();
47					}
48					else
49					{
50						$("#idx").val('N');
51						var _pairs	=	$("#common").serialize();
52						PrepareSave(_form_id);
53						//destroyEditor();
54						form_submit('#'+_form_id, _pairs);
55						get_data(ref_no, dbm);
56					}
57				}
58				
59			});
60		}
61		
62	// ACS forms feedback
63		$('.invalid-feedback').each(function() {
64		
65			$feedback = $(this);
66			//console.log($feedback.html());
67			if ($feedback.html().match(/[a-z]/i)) {
68				//$feedback.siblings('input').addClass("is-invalid");
69			}		
70		
71		});
72	// ====
73	
74	// ACS Login
75		$('#btn-login').on('click',function() 
76		{
77			var _form_id = $(this).closest('form').attr('id');
78			$('#'+_form_id).submit(); return false;
79			
80		});
81	// ====
82	
83	// ACS Login
84		$('#btn-register').on('click',function() 
85		{
86			var _form_id = $(this).closest('form').attr('id');
87			$('#'+_form_id).submit(); return false;
88			
89		});
90	// ====
91	
92	// show/hide password
93		$('.fa-password').on('click',function() {
94			$(this).toggleClass("fa-eye-slash");
95			var x = $(this).parents('.input-group').find('input');
96			if (x.attr('type') === "password") {
97				x.attr('type', 'text');
98			} else {
99				x.attr('type', 'password');
100			}
101		});
102	
103	// REQUEST NEW PASSWORD
104		$('.new-password').on('click', function(event)
105		{
106			event.preventDefault();
107			var _form_id = $(this).closest('form').attr('id');
108			$('#'+_form_id).submit(); return false;
109
110		});
111		
112	// CHANGE PASSWORD BUTTON
113		$('.change-password').on('click', function(event)
114		{
115			event.preventDefault();
116			if ($(this).hasClass('disabled'))	{
117				return true;
118			} 
119			
120			var data = get_form( ['#password-form'] );
121			//console.log(data);
122			
123			$('#responsemsg').html('').hide();
124			$('#errormsg').html('').hide();
125			$('#loading').show();
126		
127			var callback = function(response){
128				form_submit_results(response);	
129			};
130			var url = "lib/acs/acs-proxy.php";
131			ajax_search(url, data, callback);  
132			
133
134		});
135		
136	// FORGOT PASSWORD
137		$('.forgot').on('click', function(event)
138		{
139			event.preventDefault();
140			var eml = '?eml=' + $('#login-username').val();
141			var rurl = give_me_a_baseref() + 'page/acs-new-password/' + eml;
142			window.location = rurl;
143			return false;
144
145		});
146
147		if (document.URL.includes("acs-new-password") ) {
148			
149			$('#login-username').val( getParameterByName('eml') );
150		}
151
152		
153		/* DROPDWON SELECT BOXES
154			<input type="hidden" data-select="field" name="fv_<?php echo $field;?>" id="fv_<?php echo $field;?>" value="">
155			<input type="hidden" data-select="code" name="fv_<?php echo $field;?>_code" id="fv_<?php echo $field;?>_code" value="" >
156			<select class="md-form form-control auto-select"  data-select-id="mdb">
157			  <?php require('include/Options_Country.inc'); ?>
158			</select>
159		*/
160		$('.auto-select').on('click', function(event)
161		{
162			
163			var code = $(this).val();
164			var name = $(this).find('option:selected').attr('id');
165			$(this).siblings('input:hidden').each(function(){
166				
167				if( $(this).data('select') == 'field' ){
168					$(this).val(name);
169				}else if( $(this).data('select') == 'code' ){
170					$(this).val(code);
171				}
172				
173			});
174			
175		});
176		
177	}); // END Document Ready
178
179// ]]>
180	
181	
182/* ******************************************************************************/	
183	function form_submit(frm, pairs, response_handler, redirect_url){
184	
185		//console.log('form_submit:: frm:[' + frm + '] ' + 'pairs:[' + pairs + '] ' + 'redirect_url:[' + redirect_url + '] ' + 'response_handler:[' + response_handler + '] ');
186		var params = "";
187		
188		if (frm.indexOf("#") < 0) { frm = '#' + frm; }
189		if(frm != '' && !empty($(frm))){
190		
191			$(':input', frm).each(function(){
192				//console.log('id: [' + $(this).attr('id') + '] val: [' + $(this).val() + ']' + '] name: [' + $(this).attr('name') + ']');
193				var inputID = $(this).attr('id');
194				var inputName = $(this).attr('name');
195				var inputRel = $(this).attr('rel');
196				var inputVal = $(this).val();
197				
198				if(!empty(inputName)){
199					
200					inputName = inputName.toLowerCase();
201					if(!empty(inputRel)){
202
203						params = params + inputName + "|" + inputRel + "=" + encodeURIComponent(inputVal) + "&" ;
204						
205					} else {
206					//we have no rel meaning its a normal text field
207						params = params + inputName + "=" + encodeURIComponent(inputVal) + "&" ;
208					}
209					//console.log('form_submit: inputID:[ ' + inputID + '] inputName:[' + inputName + '] inputRel:[ ' + inputRel + ' ] inputVal:[ ' + inputVal + ' ]');
210		
211				}else{
212					//console.log('form_submit: inputName: [' + typeof inputName + ']');
213					//console.log('form_submit: inputID:[ ' + inputID + '] inputName:[' + inputName + '] inputRel:[ ' + inputRel + ' ] inputVal:[ ' + inputVal + ' ]');
214						
215				}
216				
217			});
218		
219			//remove the trailing &
220			params = params.substring(0,params.length -1);
221			
222			if(pairs != "")	{
223				params	=	params + "&" + pairs;
224			}
225			
226		}else{
227			//console.log('form_submit:: pairs: [' + pairs + ']');
228			params = pairs;
229		}
230		
231		//console.log('form_submit:: params: [' + params + ']');
232		if(!params){
233			alert('You need to submit some params!');
234			return false;
235		}
236		
237		
238		//console.log('form_submit:: params: [' + params + ']');
239		
240		$('#messages').addClass('alert alert-info').show();
241		$('#responsemsg').html('').hide();
242		$('#errormsg').html('').hide();
243		$('#loading').show();
244		$('#message').html('<img src="' + give_me_a_baseref() + 'img/nicerloading.gif" id="loading" /> Please wait').show();
245		
246		if(empty(response_handler)){
247			response_handler = form_submit_results;
248		}
249		
250		var callback = function(response){
251			response_handler(response, redirect_url, params);	
252		};
253		
254		ajax_search('', params, callback);  
255			
256		
257	}
258	
259/* ******************************************************************************/	
260	function form_submit_results(json, redirect_url){
261		
262		$('#loading').hide();
263		//console.log('form_submit_results: ', json.response);
264		
265		var $logfile = !empty(json.response.logfile) ? json.response.logfile : '';
266		
267		if(json.response.error_code == 0 || json.response.error_code == ''){
268			
269			//console.log("submitForm: success:: " + json.response.error_code);
270			//$('#'+json.response.div).html(json.response.text).fadeIn("slow");
271			$('#messages').removeClass().addClass('alert alert-success');
272			$('#message').html('<span class="icon-ok"></span> ' + json.response.error_msg);
273			$('html, body').animate({scrollTop: 0}, 900, 'swing');
274			
275			var $div = (json.response.div) ? json.response.div : 'responsemsg' ;
276			if(json.response.text != "" ){
277				
278				$('#'+$div).html(json.response.text).fadeIn("slow");
279				
280			} 
281			
282			if(!empty(redirect_url)){
283				
284				//console.log('redirect new:' + redirect_url);
285				 setTimeout(function(){ redirect(redirect_url) }, 1000);
286
287			}
288			if( !empty($logfile) ) {
289				
290				$('#responsemsg').append(' <a href="#" onclick="openWindow(\'../logprocess.php?log='  + $logfile + '\', \'Log process\', 675, 700);">View log</a>');
291				$('.subnav-container').css('background-color', '#fbeed5');
292				
293			}
294
295		}else{
296			
297			//alert('something is wrong!');
298			$('#messages').removeClass().addClass('alert alert-danger');
299			$('#message').html('<span class="icon-remove"></span> Process was not completed.');
300			$('#errormsg').html(json.response.error_msg).fadeIn("slow");	
301			
302		}
303		
304	}
305
306/*******************************************************************************/	
307/* 
308	DEFAULTS: POST to "lib/acs/acs-proxy.php" expecting 'xml' response
309	
310	@load_url: e.g. 'include/xmla-elastic.php' - give_me_a_baseref() will add root
311	@params: e.g. &name=value pairs
312	@callback: name of function to handle the Ajax success / fail response
313	@data_type: [xml|json] defaults to 'xml' for [db.server|db.client|xml.proxy.php] else 'json'
314	@action: [POST|GET] defaults to POST
315	
316	e.g. 	function form_submit(frm, data, query){
317			
318			// handle the frm/data/query stuff
319				
320			// set up the response handler
321				var callback = function(response){
322					form_submit_results(response);	
323				};
324				
325			// call the ajax_search function[  ajax_search(url["lib/acs/acs-proxy.php"], $data, callback);  ], sends response to callback function 
326				ajax_search(url, data, callback);  
327			
328			}
329
330*/
331	function ajax_search(load_url, params, callback, data_type, action){
332	// assume params already serialized [&name=value etc.]
333	// requires jQuery Ajax Queue v0.1.1 | (c) 2013 Corey Frang | Licensed MIT */ see next
334
335		//console.log('ajax_search', data_type );
336		/**/
337		var $dtype = data_type || '';	// N.B. try to make callbacks XML/JSON agnostic see form_subscribe_response() below
338		var $ax = action || 'POST';			// N.B. xmla-elastic.php doesn't accept POSTs - only GET
339		//console.log("ajax_search:" + $dtype + " - ax: " + $ax);
340		
341		if( load_url.match('db.server') || load_url.match('db.client') || load_url.match('xml.proxy') ){
342			
343			if(empty($dtype)){	// i.e. if not set in function call params
344				$dtype = 'xml';
345			}
346			
347		} else {
348			
349			if(empty($dtype)){	// i.e. if not set in function call params
350				$dtype = 'json';
351			}
352		}
353
354		$.ajaxQueue({
355			type: $ax,	// POST by default unless set via function call params
356			url: give_me_a_baseref() + ((load_url) ? load_url : "lib/acs/acs-proxy.php"), 
357			dataType: $dtype,
358			data: params,
359			success: function(response){
360				callback(response);
361			},
362			error:  function(xhr, opts, err) { 
363				console.log(err); 
364				console.log(xhr); 
365				$('#messages').removeClass().addClass('alert alert-error').show();
366				$('#message').html('<span class="icon-remove"></span> Error processing AJAX request (Switch off debug and check console log)').show();
367			}
368				
369
370		});
371	}
372/* ******************************************************************************/	
373/*! jQuery Ajax Queue v0.1.1 | (c) 2013 Corey Frang | Licensed MIT */
374(function(e){var r=e({});e.ajaxQueue=function(n){function t(r){u=e.ajax(n).done(a.resolve).fail(a.reject).then(r,r)}var u,a=e.Deferred(),i=a.promise();
374return r.queue(t),i.abort=function(o){if(u)return u.abort(o);var c=r.queue(),f=e.inArray(t,c);return f>-1&&c.splice(f,1),a.rejectWith(n.context||n,[i,o,""]),i},i}})(jQuery);
375/* ******************************************************************************/
376/* 
377	Call one of the new PHP scripts directly - e.g. elastic-indexer.php
378	
379*/ 
380	function runScript(script_path, params, rid){	
381
382		$('#messages').addClass('alert alert-info').show();
383		$('#responsemsg').html('').hide();
384		$('#errormsg').html('').hide();
385		$('#loading').show();
386		$('#message').html('<img src="../img/nicerloading.gif" id="loading" /> Please wait').show();
387	
388		//alert(setflag);	
389		$.ajaxQueue({
390		   type: "POST",
391		   url: script_path, 
392		   data: params,
393		   success: function(xml){     
394			
395				$('#loading').hide();
396			
397				var errCode = $("errorcode", xml).text();
398				var errMsg = $("errormsg", xml).text().replace(/~~/g,' ');
399				var response = $("responsetext", xml).text().replace(/~~/g,' ');
400				var refno = $("ref_no", xml).text();
401				var rID = $("rid", xml).text();
402				var title = $("ctitle",xml).text();
403				var logfile = $("logfile",xml).text();
404				var msg = $("msg",xml).text();
405			   
406				
407			   if(errCode == '0'){
408					$('#messages').removeClass().addClass('alert alert-success');
409					$('#message').html('<span class="icon-ok"></span> Process complete. ');
410					$('#responsemsg').html(response).fadeIn("slow");
411					
412					if(response != "" && rID == "")	{
413						$('#responsemsg').html(response).fadeIn("slow");
414					} else {
415						$("#"+rID).html(response);
416					}
417					if(logfile != '') {
418						$('#responsemsg').append(' <a href="#" onclick="openWindow(\'../logprocess.php?log='  + logfile + '\', \'Log process\', 675, 700);">View log</a>');
419						$('.subnav-container').css('background-color', '#fbeed5');
420					}
421					
422				}else{
423					
424					$('#messages').removeClass().addClass('alert alert-danger');
425					$('#message').html('<span class="icon-remove"></span> Process was not completed.');
426					$('#errormsg').html(errMsg).fadeIn("slow");	
427					$('#responsemsg').html(msg).fadeIn("slow");	
428					
429					if(logfile != '') {
430						$('#responsemsg').append(' <a href="#" onclick="openWindow(\'../logprocess.php?log='  + logfile + '\', \'Log process\', 675, 700);">View log</a>');
431						$('.subnav-container').css('background-color', '#fbeed5');
432					}
433				
434				}
435				
436			}   
437		});
438	}    	
439/* ******************************************************************************/	
440	function get_data(refno, dbm, qs){
441		
442		if(typeof qs == 'undefined'){ qs = '';}
443		var params = "ax=getdata&k=" + refno + "&rid=return&dbm=" + dbm + qs;
444		
445		$.ajaxQueue({
446			type: "POST",
447			url: give_me_a_baseref() + "/lib/acs/acs-proxy.php", 
448			dataType: "json",
449			data: params,
450			success: function(json){
451			
452				$('#loading').hide();
453							
454				var errCode = json.response.error_code;
455				var errMsg = json.response.error_msg;
456				var response = json.response.text;
457				var rID = json.response.div;
458				var $data = json.response.data;
459				//console.log('get_data', $data);
460				
461				if( typeof $data != 'undefined' && typeof json.response.data.record.acs_address != 'undefined'){
462				// prefer to keep ACS address field as textarea except for SmartCart Checkout.
463					var lines = (json.response.data.record.acs_address['@cdata']).split("\n");
464					json.response.data.record.acs_address_line1 = lines.join(", ");
465						
466				}
467				
468				//console.log(json.response.data);
469				
470				if(errCode == 0){
471					
472					//console.log($data);
473					PopulateXML($data);
474					
475				} else {
476					
477					$('#messages').removeClass().addClass('alert alert-danger').show();
478					$('#message').html('<span class="icon-remove"></span> Error retrieving data').show();
479					$('#errormsg').html(errMsg).show();
480					
481				}
482			},
483			error: function($data){
484				
485				console.log($data);
486				console.error('Error processing ajax request. getData() function.');
487				$('#messages').removeClass().addClass('alert alert-danger').show();
488				$('#message').html('<span class="icon-remove"></span> Error processing AJAX request').show();
489				
490			}
491		});
492	}	
493/* ******************************************************************************/	
494	function PrepareSave(_form_id){
495
496		//console.log('PrepareSave: ', _form_id);
497		
498		$(':input',"#"+_form_id).each(function(){
499			var input_id = $(this).attr('id');
500			var input_val = $(this).val();
501			//console.log('PrepareSave:' + input_id);
502			//console.log('before: ', input_val);
503		// Save funny characters as numeric character entities (msp:)
504			input_val = htmlEntity(input_val);
505			//console.log('after: ', input_val);
506			$(this).val(input_val);
507			
508		// Save ckEditor content back into textareas
509			var ckeditor = $('#cke_'+input_id);
510			//console.log(ckeditor)
511			if(window.CKEDITOR){
512				if(CKEDITOR.instances[input_id]){
513					//alert("i have an instance");
514					var ckinstance = true;
515				}
516				if(ckinstance){
517					var data = CKEDITOR.instances[input_id].getData();
518					var ckdisplay = ckeditor.css('display');
519					if(ckdisplay != 'none'){
520				//when we click hide editor it does this all for us already no need to repeat it.
521						data = data.replace('<br type="_moz" />', '');
522						data = data.replace('[[','<');
523						data = data.replace(']]','>');
524						$('#'+input_id).val(data);
525						//console.log('PrepareSave:' + input_id);
526						//console.log('writing: [' + data +  '] to ' + input_id );
527					}else{
528						data = data.replace('[[','<');
529						data = data.replace(']]','>');
530						$('#'+input_id).val(data);
531					}
532				}
533			}
534		});
535		
536		if( typeof CKEDITOR !== 'undefined') { destroyEditor(); }
537		
538	}
539	
540/* ****************************************************************************************/
541/*
542	This function will now populate any form input tag with values from the XML supplied
543	e.g. <input type="text" id="fv_grandtotal" value="" rel="node:content">
544	use the rel="content/items" to specify path to XML element
545	use the id="fv_os_order_item_status_2" to specify XML tag name and index if the xml repeats
546	_sel and _dta boxes must refer to hidden inputs [ see working examples ]
547	.. and be amazed at how neat it is :)!!
548
549*/
550	function PopulateXML(data) {
551		
552		//console.log('PopulateXML');
553		//console.log(data);
554		//console.log( "PopulateXML(): data: " + data.record.acs_session_id );
555		
556		var fv					=	{};
557		var showEditorFields	=	new Array('fv_message_body_dta', 'fv_cms_text_dta', 'fv_cms_image_thumb_dta','fv_cms_image_full_dta', 'fv_cms_stay_informed_dta', 'fv_cms_notes_dta');
558		var modalFields			=	new Array('fv_contributors');
559			
560		// Now go thru the form elements that will need populating
561		$('.acs-form input, .acs-form select, .acs-form textarea').each(function(){
562			
563			var _id		= $(this).attr('id');
564			var _name	= $(this).attr('name');
565			var _rel	= $(this).attr('rel');
566			
567			//console.log(_id + '/' + _name + '/' + _rel + '/' );
568			
569			if(_id !== undefined)
570			{
571				_id	 = _id.toLowerCase();
572				//console.log('populate:id: ' + _id + ': [' + getElementValue(_id, data, _rel) + ']');
573				if(_id.indexOf('_sel') != -1){
574					//console.log(_id);
575					if(getElementValue(_id, data, _rel)){
576						$(this).attr('data', getElementValue(_id, data, _rel));
577					}
578				}
579				if(_id.indexOf('_dta') != -1){
580					//console.log('dta:'+_id);
581					//data = _id.substr(0,_id.indexOf('_dta'));
582					if(getElementValue(_id, data, _rel)){
583						//console.log('array:'+fv[data]);
584						//console.log(getElementValue(_id, data, _rel));
585						$(this).html(getElementValue(_id, data, _rel));
586					}
587				}
588				// end of the drop down
589				if(getElementValue(_id, data, _rel)){	
590				
591					
592					if( $.inArray(_id, modalFields) !== -1 ) {
593						var $field = _id.replace(/^fv_/, '',  _id);
594						//console.log('populate:field: ' + $field + ' id:' + _id);
595						$c = $(data).find($field).html();
596						$(this).val($c);
597						populate_modal($(data).find($field));
598						
599					}else{
600						//console.log('populate:id: ' + _id + ': [' + getElementValue(_id, data, _rel) + ']');
601						//console.log( $(this) );
602						$(this).val(getElementValue(_id, data, _rel));
603						$(this).prev().addClass('active');
604						//console.log( 'eek:' +  $(this).val() );
605					}
606					
607
608				}else if(_id.indexOf("_flag") != -1){
609					
610					//console.log("PopulateXML:" + _id);
611					//console.log(data);
612					$(this).val("N");
613					
614				}
615				// ToggleEditor on: consult list of Fields that require to be always showing.
616				if( $.inArray(_id, showEditorFields) !== -1 ) {
617					id = _id.replace(/_dta$/, '');
618					//console.log('Show:' + id);
619					showEditor(id);
620				}
621			}
622		});
623
624		activateSelectTags();
625
626	}
627	
628/* ****************************************************************************************/
629/*
630	Handles _sel and _dta form tags which modify hidden inputs of the same name (minus the tail _sel/_dta)
631	- e.g. 	<input type="hidden" name="fv_os_item_status_1" id="fv_os_item_status_1" value="Cancelled" rel="node:content/items">		
632			<input type="hidden" name="fv_os_item_status_code_1" id="fv_os_item_status_code_1" value="CANCELLED" rel="node:content/items">		
633			<select class="autoSelector" data="CANCELLED" id="fv_os_item_status_code_1_sel" name="fv_os_item_status_code_1_sel" rel="fv_os_item_status_1">
634			..
635			</select>	
636			
637	Also handles ordinary fv_field_name form elements
638	And repeating elements e.g. item_1 / item_2 / etc.
639
640*/
641	function getElementValue(id, data, rel){
642			
643	// Set up the keys we need
644		var fv_name = id;
645		
646		//console.log( 'oof: ' + arr_get(data, 'record.acs_address.@cdata') );
647		//console.log('getEV' + id + rel);
648		
649	// remove _sel and _dta from textareas and drop-down boxes
650		if(id.indexOf('_sel') != -1){	
651			fv_name = id.replace(/_sel$/, '', id);
652		}
653		if(id.indexOf('_dta') != -1){	
654			fv_name = id.replace(/_dta$/, '', id);
655		}
656	
657	// remove prefix fv_ + [ AND NO _sel / _dta ]
658		field_name = fv_name.replace(/^fv_/, '',  fv_name);
659		
660	// test for _1 / _2 / _3 etc and set idx number (for child arrays)
661		var reg = new RegExp(/_\d{1,2}/);
662		var $matches = (fv_name.match(reg)) ? fv_name.match(reg) : '';
663		if ( $matches.length > 0 ){
664			var rep = $matches[0];
665			var idx = $matches[0].replace(/[_]+/, '') - 1;
666			var field_name = field_name.replace(rep, '');
667		} else {
668			var idx = '';
669		}
670	
671	
672	// try to find the rel attr values which contain node info e.g rel=node:content/items@t
672ype:html
673		var nodes = '';
674		var info = {};
675		if(empty(rel)){
676			rel = $("input,textarea[name='"+fv_name+"']").attr('rel');
677		}
678
679		//console.log(rel);
680		//console.log('idx [' + idx + '] ' + 'fv_name [' + fv_name + '] ' + 'field_name [' + field_name + '] ' + 'rel [' + rel + '] ');
681		//console.log(data);
682		
683		if(!empty(rel)){
684			var parts = rel.split(/@/);
685			$.each(parts, function(index, item) {
686				info[ item.split(/:/)[0] ] = item.split(/:/)[1]
687			});			
688		}
689		//console.log(info);
690		var ret_str = '';
691		var path = '';
692		
693		if( !empty(info['node']) ){
694			path = 'record.' + info['node'] + '.' + idx + '.' + field_name + '.' + info['type'];
695		} else {
696			path = 'record.' + '.' + field_name + '.' + idx + '.' + info['type'];
697		}
698		path = path.replace(/undefined/, '').replace(/\//, '.').replace(/[\.]+/g, '.').replace(/html/, '@cdata').replace(/\.$/, '');
699		//console.log('path:' + path);
700		
701		ret_str = arr_get(data, path);
702		
703		var html = ret_str;
704		
705		
706		if(!empty(html)){
707		/* 	EEK! jquery shortens '<p></p>' to '<p/>' - CKEditor disapproves of this and fucks up the HTML! ACTUALLY ALL EMPTY TAGS e.g. <cmsection></cmsection> => <cmsection/>
708			See: http://stackoverflow.com/questions/348736/is-writing-self-closing-tags-for-elements-not-traditionally-empty-bad-practice
709		*/
710		
711		// pipe separated field data will come back as an array! 
712			if ( Object.toType(html) == 'array' ){
713				
714				html = html.join('|');
715				//console.log(path);console.log(html);
716				
717			}
718			
719		// @cdata nodes come back as an object for some reason!
720			if( Object.toType(html) == 'object' ) {
721				
722				html = arr_get(html, '@cdata');
723				//console.log(path);console.log(html);
724
725			}
726			
727		//console.log("getElementValue(): html: " + fv_name + " [" + html + "] - " + Object.toType(html));
728		
729		// treat everything as text [amazingly elastic interpreted acs_last_logged_out as long which carried over into javascript - breaking string handling!]
730			if( Object.toType(html) == 'number' ) {
731				
732				html = html.toString();
733				
734			}
735		
736		// do some tidying - mainly for ckEditor - N.B. .replace only works on Typeof = string
737			if( Object.toType(html) == 'string' ) {
738							
739				html = html.replace(/<p\/>/g, '<p></p>');
740				html = html.replace(/<i\/>/g, '<i></i>');			
741				html = html.replace(/<i ([\s\S]+?)\/>/g, "<i $1><\/i>");
742				html = html.replace(/<p ([\s\S]+?)\/>/g, "<p $1><\/p>");
743				html = html.replace(/<span ([\s\S]+?)\/>/g, "<span $1><\/span>");
744				html = html.replace(/<textarea ([\s\S]+?)\/>/g, "<textarea $1><\/textarea>");
745			
746			// carriage returns stored in elastic as \n but browsers use \r\n :)
747				html = html.replace(/\r?\n/g, "\r\n");
748				
749		//console.log('eek ['+html+"]\r\n");
750			/* EEK! am i the only one? - a lone '%' will - of course - break decodeURIComponent!!! */
751				html = html.replace(/%(?![0-9a-fA-F]{2})/, '%25');
752				//console.log('['+id+']'+' val: [' + html +']');
753			}
754			
755		// OK this converts numerical char entities back into user friendly stuff. See PrepareSave() (msp:)
756			html = decodeHtml(html);
757		}
758		
759		if (html){
760			
761			return html;
762			
763			/* 	NOTE!!
764			
765				We should move all CMS save_data and get_data to use admin.tools and acs-proxy.php
766				- this uses JSON as a transport method :)
767				- funny characters will be converted on save [See: PrepareSave.htmlEntity()] and stored in XML as numeric character entities
768				- acs-proxy.php update_record uses xml2array() class which escapes all ampersands to &amp;
769				- elasticSearchCGI.php converts &amp; to & [see: output_XML2()]
770				- PopulateXML() also converts numeric entities to ordinary characters [see: getElementValue.decodeHtml()]
771				
772				N.B. Data is stored directly into the elastic index, in most cases without using the DOM, so no &amp;'s
773				
774				So, we should NOT need to use  decodeURIComponent(ret_str); anywhere!
775				
776			*/
777			
778
779		} else {
780			return false;
781		}
782		
783	}	
784	
785// *****************************************************************************/
786	function get_form(frms){
787
788		var id, i, len = frms.length;
789		var data = {};
790		var form_id = frms.attr('id');
791		//console.log('get_form: ', form_id);
792		
793		PrepareSave(form_id);
794		
795		for (i=0; i<len; ++i) {
796			
797			id = frms[i];
798			$(':input', id).each(function(){
799				//console.log('id: [' + $(this).attr('id') + '] val: [' + $(this).val() + ']' + '] name: [' + $(this).attr('name') + ']');
800				var input_id	= $(this).attr('id');
801				var input_name 	= $(this).attr('name');
802				var input_rel 	= $(this).attr('rel');
803				var input_val 	= $(this).val();
804				var input_key = '';
805				
806				if(!empty(input_rel)){
807
808					input_key = input_name + "|" + input_rel;
809					
810				} else {
811				
812					input_key = input_name;
813				}
814				
815				data[input_key] = input_val;
816				
817			});
818
819		}
820		
821		return data;
822		
823	} 
824/* ******************************************************************************/	
825	Object.toType = function(obj) {
826		return ({}).toString.call(obj).match(/\s([a-z|A-Z]+)/)[1].toLowerCase();
827	}
828/* ******************************************************************************/		
829/* ******************************************************************************/		
830/* ******************************************************************************/
831	function MakeExact(str){
832		
833		var ret = '';
834		var nreg = /(&.{1,5};|@#.{1,5};|\W|\s+)/ig;
835															  
836		
837		if(!empty(str)){
838			ret = str.replace(nreg, '').substr(0,120).toUpperCase();
839		}
840		
841		return ret;
842		
843	}
844	
845/* ******************************************************************************/
846	function isNumber(n) {
847	//should return true if n is a number
848	  return !isNaN(parseFloat(n)) && isFinite(n);
849	}
850	
851/* ******************************************************************************/
852	function toTitleCase(str){
853		return str.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
854	}
855	
856/* ******************************************************************************/
857	function roundNumber(num, dec) {
858		var result = Math.round(num*Math.pow(10,dec))/Math.pow(10,dec);
859		result = result.toFixed(dec);
860		return result;
861	}
862	
863/* ******************************************************************************/
864	function decodeHtml(html) {
865		var txt = document.createElement("textarea");
866		txt.innerHTML = html;
867		return txt.value;
868	}
869	
870/* ******************************************************************************/		
871	function redirect(url){
872		
873		//console.log('redirecting:' + url);
874		window.location = url ;
875		return false;
876	
877	}
878	
879/* ******************************************************************************/	
880	function give_me_a_baseref() {
881		
882		//Is the location path a script?
883		var _isScript = (location.pathname.split('/')[1]).match(/\./);
884			_isScript = !(_isScript == null);
885		var _barePath = (location.pathname.toString() == "/");
886		var _protocol = location.protocol;
887		
888		return ((location.pathname.split('/')[1] == 'pid')||(location.pathname.split('/')[1] == 'page')||(location.pathname.split('/')[1] == 'book')||(_isScript)||(_barePath) ? '//' + location.hostname + '/' :'//' + location.hostname +'/' + location.pathname.split('/')[1] + '/');
889
890	}
891	
892/* ******************************************************************************/
893	function getParameterByName(name) {
894		
895		name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
896		var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
897			results = regex.exec(location.search);
898		return results == null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
899		
900	}
901/* ******************************************************************************/
902	function parse_str(str) {
903		
904		var query = {};
905		var pairs = (str[0] === '?' ? str.substr(1) : str).split('&');
906		for (var i = 0; i < pairs.length; i++) {
907			var pair = pairs[i].split('=');
908			query[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1] || '');
909		}
910		return query;
911	}
912/* ****************************************************************************************/
913// Magic returns values from the XML Obj from a string e.g. arr_get(data, 'CONTENT.ITEMS.OS_ITEM_STATUS_CODE[2]')
914	arr_get = function(o, s) {
915		
916		s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
917		s = s.replace(/^\./, '');           // strip a leading dot
918		var a = s.split('.');
919		for (var i = 0, n = a.length; i < n; ++i) {
920			var k = a[i];
921			//console.log( "k: " + Object.toType(k) + "  o: " + Object.toType(o) );
922			if( Object.toType(o) == 'string' ){
923				console.log('arr_get: Type error. [e.g. Form specifies type:HTML, but data is missing @cdata node]');
924				console.log(s);
925				console.log(o);
926				return;
927			}
928			if (k in o) {
929				o = o[k];
930			} else {
931				return;
932			}
933		}
934		return o;
935	}
936/* *****************************************************************************/
937	function htmlEntity(txt, tags) { 
938	// Set tags to 1 for '&lt;' instead of '<' and &amp; instead of '&'
939	// Set tags to 2 for for '&lt;' instead of '<' 
940		if(!tags){ tags == 0};
941		var aa = txt;
942		//console.log('htmlEntity: [' + typeof txt + ']');
943		if(typeof txt !== 'object'){
944			var bb = '';
945			for (var i = 0; i < aa.length; i++) {
946				bb += hEnt(aa.charAt(i),tags);
947			}
948			//console.log('bb1: ' + bb);
949			bb = bb.replace(/&amp;/ig, '&#38;');
950			bb = bb.replace(/(&)(\w)/ig, '&#38;$2');
951			bb = bb.replace(/(&)(\s)/, '&#38;$2');
952			bb = bb.replace(/(&$)/, '&#38;');
953			//console.log('bb2: ' + bb);
954			return bb;
955		}
956	}
957  
958	function hEnt(d, tags) {
959
960		//* HTML entity */
961		if (tags == 2) {
962			if (d == '<'){return '&lt;'};
963			if (d == '>'){return '&gt;'};	
964		}
965		if (tags == 1) {
966			if (d == '<'){	return '&lt;'} ;
967			if (d == '>'){return '&gt;'};	
968			if (d == '&'){return '&#38;'};
969		}
970		if (d == '\''){	return '&#39;'} ;
971		if (d.charCodeAt(0) > 127) {
972			return '&#' + d.charCodeAt(0) + ';';
973		}
974	   return d;
975
976	}
977
978/* ******************************************************************************/
979	function empty( val ) {
980
981    // test results
982    //---------------
983    // []        true, empty array
984    // {}        true, empty object
985    // null      true
986    // undefined true
987    // ""        true, empty string
988    // ''        true, empty string
989    // 0         false, number
990    // true      false, boolean
991    // false     false, boolean
992    // Date      false
993    // function  false
994
995        if (val === undefined )
996			return true;
997
998		if (typeof (val) == 'function' || typeof (val) == 'number' || typeof (val) == 'boolean' || Object.prototype.toString.call(val) === '[object Date]')
999			return false;
1000
1001		if (val == null || val.length === 0)        // null or 0 length array
1002			return true;
1003
1004		if (typeof (val) == "object") {
1005			// empty object
1006
1007			var r = true;
1008
1009			for (var f in val)
1010				r = false;
1011
1012			return r;
1013		}
1014
1015		return false;
1016	}
1017
1018/* ******************************************************************************/
1019	function padz(n, totalDigits) {
1020		n = n.toString(); 
1021		var pd = ''; 
1022		if (totalDigits > n.length) 
1023		{ 
1024			for (i=0; i < (totalDigits-n.length); i++) 
1025			{ 
1026				pd += '0'; 
1027			} 
1028		} 
1029		return pd + n.toString(); 
1030	}
1031	
1032/* ******************************************************************************/
1033	function trunc(str){
1034		var n = "";
1035		
1036		if(typeof str != 'undefined'){
1037		
1038			n = str.indexOf("|")
1039			if(n > 0) {
1040				str = str.slice(0, n);
1041			}
1042			n = str.indexOf("(")
1043			if(n > 0) {
1044				str = str.slice(0, n);
1045			}
1046			n = str.indexOf("[")
1047			if(n > 0) {
1048				str = str.slice(0, n);
1049			}
1050		
1051		} else {
1052		
1053			str = '';
1054			
1055		}
1056		
1057		return str;
1058	}
1059
1060/******************************************************************************/
1061	function trim(str){
1062	
1063		//str = str.replace(/^\s+|\s+$/g, "");	// whitespace
1064		//console.log("trim in  ["+ str+ "]");
1065		str = str.replace(/(^[\s,:;\._-]+)|([\s,:;\._-]+$)/g, "");		// commas etc
1066		//console.log("trim out ["+ str+ "]");
1067		return str
1068		
1069	}	
1070/******************************************************************************/
1071/*
1072*/
1073	function validate_form(frm){
1074		
1075		//console.log('validate_form: ', frm );
1076		
1077		var valid_form = true;
1078		
1079		$('.validate').each(function(){
1080				
1081			var input			=	$(this);
1082			var input_id		=	input.attr('id') || 'undefined';
1083			var input_name 		=	input.attr('name');
1084			var input_type		=	input.attr('type');
1085			var input_val		=	input.val();
1086			
1087			/*
1088			console.log(
1089				"input_id: ["+input_id+"] " + 
1090				"input_name: ["+input_name+"] " + 
1091				"input_type: ["+input_type+"] " + 
1092				"input_val: ["+input_val+"] "
1093			); 
1094			*/
1095			
1096			valid_form = validate_this(input);
1097			
1098			if(valid_form === false){
1099				
1100				$('#'+input_id).focus();
1101				return valid_form;
1102				
1103			}
1104				
1105
1106		
1107		});
1108		
1109		return valid_form;
1110		
1111	}
1112
1113/******************************************************************************/
1114/*
1115	
1116	@input [class="validate" type="" value="" name="" id="" data-required="[true|false]" data-valid="[Y|N]" etc.
1117	Typical HTML: Please set type / name=fv_ / id
1118	
1119	<?php $field_label = "Title"; $field_name = "ctitle"; $placeholder="placeholder_text" ?>
1120	<div class="md-form">
1121		<label for="<?php echo $field_name; ?>"><span class="fas fa-info-circle" data-content=""  title="<?php echo $field_label; ?>"></span><?php echo $field_label; ?>:</label> 
1122		<input class="form-control validate mb-0" type="text" name="fv_<?php echo $field_name; ?>" id="<?php echo $field_name; ?>" placeholder="<?php echo $placeholder;?>" value="" data-required="true" data-valid="N" data-maxlength="" data-pattern="">
1123		<div class="invalid-feedback"></div>
1124	</div>
1125	
1126	N.B. if data-required="true" then set data-valid="N" to initialise the test. 
1127	N.B. if data-required="false" then set data-valid="" to skip the test. 
1128							
1129*/
1130	function validate_this(input){
1131		
1132		//console.log('acs.tools.js::validate_this');
1133		//console.log('validate_this:', input );
1134		
1135		var $data = {};		
1136		var input_id		=	input.attr('id') || 'undefined';
1137		var input_name 		=	input.attr('name');
1138		var input_type		=	input.attr('type');
1139		var input_val		=	input.val();
1140		var valid_input		= 	true;
1141		
1142		if(typeof input_name !== 'undefined'){
1143			
1144			/* 
1145			console.log(
1146				"input_id: ["+input_id+"] " + 
1147				"input_name: ["+input_name+"] " + 
1148				"input_type: ["+input_type+"] " + 
1149				"input_val: ["+input_val+"] "
1150			); 
1151			*/
1152			 
1153			var key	= input_name.toLowerCase();
1154			var checked = input.is(":checked");
1155			$data[key] = {
1156				
1157				'type':		input.attr('type'),
1158				'name':		input.attr('name'),
1159				'id': 		input.attr('id'),
1160				'checked':	checked,
1161				'val':		input.val()
1162			
1163			};
1164
1165		// PATTERN
1166			if( $data[key]['type'] == 'text' && !empty(input.data('pattern')) ){
1167			
1168			// You have to specify type & name in the form otherwise they are undefined
1169			/* N.B. 'g' flag can cause odd results so best specify full ^pattern$ */
1170				//console.log('pattern: ', input.data('pattern'));
1171				$ptrn = new RegExp("" + input.data('pattern') + "", ""); 
1172				$txt = $data[key]['val'];
1173				//console.log('testing: ',  $ptrn.test($txt));
1174					
1175				if( $ptrn.test($txt) !== true){
1176				// negative pattern - we've matched an unwanted character;
1177					//console.log('invalid reason: [pattern]', $ptrn);
1178					input.removeClass('is-valid valid').addClass('is-invalid');
1179					$("#"+input_id).attr('data-valid', 'N');
1180					valid_input = false;
1181					return valid_input;
1182					
1183				} else {
1184					//console.log('yay: ', $ptrn);
1185					input.removeClass('is-invalid').addClass('is-valid');
1186					$("#"+input_id).attr('data-valid', 'Y');
1187					
1188				}
1189			
1190			}
1191					
1192		
1193		// Check for data-MAXLENGTH:
1194			if( !empty(input.data('maxlength')) && input_val.length > input.data('maxlength') ){
1195			
1196				if(!empty(input.data('maxlength'))){
1197					
1198					if( input_val.length > input.data('maxlength') ){
1199						
1200						//console.log('invalid reason: [maxlength]', input_val.length + ' / ' + input.data('maxlength'));
1201						input.removeClass('is-valid valid').addClass('is-invalid');
1202						//input.siblings('div').html('Maximum number of characters is ' + input.data('maxlength'));
1203						$("#"+input_id).attr('data-valid', 'N');
1204						valid_input = false;
1205						return valid_input;
1206						
1207					} else {
1208						
1209						input.removeClass('is-invalid').addClass('is-valid');
1210						$("#"+input_id).attr('data-valid', 'Y');
1211						
1212					}
1213				}
1214			
1215			}
1216			
1217		// SPECIAL FIELD TYPE:TEXT - ISBN
1218			if( $data[key]['type'] == 'text' && input_id.match(/barcode/gi ) ){
1219			
1220				if( !valid_isbn(input_val) ){
1221										
1222					console.log('invalid reason: [ISBN] [' + input_val + ']' + '[' + input_id + ']');
1223					input.removeClass('is-valid valid').addClass('is-invalid');
1224					$("#"+input_id).siblings('div').html('Not a valid ISBN.');
1225					$("#"+input_id).attr('data-valid', 'N');
1226					valid_input = false;
1227					return valid_input;
1228		
1229				}else{
1230		
1231					//console.log('good email.');
1232					input.removeClass('is-invalid').addClass('is-valid');
1233					$("#"+input_id).attr('data-valid', 'Y');
1234
1235				}
1236		
1237			} 
1238			
1239		// EMAILS:
1240			if( $data[key]['type'] == 'email' ){
1241			
1242				if( !valid_email(input_val) ){
1243										
1244					//console.log('invalid reason: [email] [' + input_val + ']' + '[' + input_id + ']');
1245					input.removeClass('is-valid valid').addClass('is-invalid');
1246					$("#"+input_id).siblings('div').html('You need to enter a valid email address so we can contact you.');
1247					$("#"+input_id).attr('data-valid', 'N');
1248					valid_input = false;
1249					return valid_input;
1250		
1251				}else{
1252		
1253					//console.log('good email.');
1254					input.removeClass('is-invalid').addClass('is-valid');
1255					$("#"+input_id).attr('data-valid', 'Y');
1256
1257				}
1258		
1259			} 
1260		
1261		// CHECKBOXES:
1262			if( $data[key]['type'] == 'checkbox' ){
1263			
1264				$x = ($data[key]['checked']) ? 'Y' : 'N';
1265				//console.log('checkbox: [' + input_id + '] ' + $x);
1266				input.val($x);
1267				
1268			}
1269			
1270		// SELECT / OPTION DROPDOWNS +/- REQUIRED
1271			if( $data[key]['type'] === 'select' && input.data('required') == true ){
1272				
1273				//console.log('select: ', input_id);
1274			// You have to specify type & name in the form otherwise they are undefined
1275				if( empty($data[key]['val']) ){
1276
1277					//console.log('invalid [' + input_id + '] reason: [select required]');
1278					input.removeClass('is-valid valid').addClass('is-invalid');
1279					$("#"+input_id).attr('data-valid', 'N');
1280					valid_input = false;
1281					return valid_input;
1282					
1283				} else {
1284
1285					input.removeClass('is-invalid').addClass('is-valid');
1286					$("#"+input_id).attr('data-valid', 'Y');
1287					
1288				}
1289				
1290			}
1291			
1292				
1293		// NUMERIC (e.g. Telephones) 
1294			if( $data[key]['type'] == 'tel' ){
1295				
1296				// See https://regexlib.com:: it is not perfect...
1297				$ptrn = new RegExp(/((\+?44\s?\(0\)\s?\d{2,4})|(\+44\s?(01|02|03|07|08)\d{2,3})|(\+44\s?(1|2|3|7|8)\d{2,3})|(\(\+44\)\s?\d{3,4})|(\(\d{5}\))|((01|02|03|07|08)\d{1,3})|(\d{5}))(\s|-|.)(((\d{3,4})(\s|-)(\d{3,4}))|((\d{6,7})))/);
1298				if( $ptrn.test($data[key]['val']) === true){
1299					
1300					//console.log('tel:valid');
1301					input.removeClass('is-invalid').addClass('is-valid');
1302					$("#"+input_id).attr('data-valid', 'Y');
1303					
1304				}else{
1305					
1306					if( empty($data[key]['val']) && input.data('required') == false ){
1307						
1308						//console.log('tel:valid');
1309						input.removeClass('is-invalid').addClass('is-valid');
1310						$("#"+input_id).attr('data-valid', 'Y');
1311						
1312					}else{
1313					
1314						console.log('tel:invalid');
1315						input.removeClass('is-valid valid').addClass('is-invalid');
1316						if( empty($("#"+input_id).siblings('div').html()) ){
1317							$("#"+input_id).siblings('div').html('You need to enter a valid telephone number so we can contact you.');
1318						}
1319						$("#"+input_id).attr('data-valid', 'N');
1320						valid_input = false;
1321						return valid_input;
1322						
1323					}
1324					
1325				}
1326					
1327				
1328			}
1329		
1330		// REQUIRED Fields Check for any field with data attribute='REQUIRED' that is empty 
1331			if( input.data('required') == true ){
1332				
1333				//console.log('required: ', input_id + ": " + input.data('required') );
1334				
1335				if( empty($data[key]['val']) ){
1336				
1337					//console.log('invalid reason: [required field] ' + key);
1338					$("#"+input_id).removeClass('is-valid valid').addClass('is-invalid');
1339					$("#"+input_id).siblings('div').html('Field cannot be empty');
1340					$("#"+input_id).attr('data-valid', 'N');
1341					valid_input = false;
1342					return valid_input;
1343					
1344				} else {
1345				
1346					$("#"+input_id).removeClass('is-invalid').addClass('is-valid');
1347					$("#"+input_id).attr('data-valid', 'Y');
1348				
1349				}
1350				
1351			}else{
1352				
1353				$("#"+input_id).removeClass('is-invalid').addClass('is-valid');
1354				$("#"+input_id).attr('data-valid', 'Y');
1355					
1356			}
1357			
1358		// HIDDEN INPUTS	
1359			if( $data[key]['type'] != 'hidden' ){
1360			// purpose is to 'refresh' input boxes after autofill (Chrome) & is quite absurd!	
1361				$("#"+input_id).val( $("#"+input_id).val() + '' );
1362			}
1363		
1364			
1365			$('textarea').each(function(index){  
1366				$(this).addClass( 'chrome-crap');
1367		
1368			});
1369	
1370		}
1371
1372		//console.log('validate_this:' + input_name + '[' + input_id +']')
1373		return valid_input;
1374		
1375	}
1376/******************************************************************************/
1377	function valid_email(email) { 
1378		var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
1379		return re.test(email);
1380	} 
1381/*******************************************************************************/
1382	var valid_isbn = function(str) 
1383	{
1384
1385		var sum,
1386			weight,
1387			digit,
1388			check,
1389			i;
1390
1391		str = str.replace(/[^0-9X]/gi, '');
1392
1393		if (str.length != 10 && str.length != 13) {
1394			return false;
1395		}
1396
1397		if (str.length == 13) {
1398			sum = 0;
1399			for (i = 0; i < 12; i++) {
1400				digit = parseInt(str[i]);
1401				if (i % 2 == 1) {
1402					sum += 3*digit;
1403				} else {
1404					sum += digit;
1405				}
1406			}
1407			check = (10 - (sum % 10)) % 10;
1408			return (check == str[str.length-1]);
1409		}
1410
1411		if (str.length == 10) {
1412			weight = 10;
1413			sum = 0;
1414			for (i = 0; i < 9; i++) {
1415				digit = parseInt(str[i]);
1416				sum += weight*digit;
1417				weight--;
1418			}
1419			check = (11 - (sum % 11)) % 11
1420			if (check == 10) {
1421				check = 'X';
1422			}
1423			return (check == str[str.length-1].toUpperCase());
1424		}
1425	}
1426/******************************************************************************/
1427	function valid_date(d) {
1428		
1429		$dte = new Date(d);
1430		//console.log('dte: ' + $dte);
1431		return $dte instanceof Date && !isNaN($dte);
1432		
1433	}
1434/*******************************************************************************/
1435/*			COOKIES															   */
1436/*******************************************************************************/
1437/* 
1438	e.g. Cookie.set('test', 'Jim', {path: '/', days: 10});
1439	e.g. Cookie.get('test'):
1440	e.g. Cookie.delete('test');
1441	
1442	domain left blank (which = current host without subdomain).
1443	
1444	Also:
1445	Cookie.getJSON = name => JSON.parse(Cookie.get(name))
1446	Cookie.setJSON = (name, value, opts) => Cookie.set(name, JSON.stringify(value), opts);
1447
1448*/
1449if( typeof Cookie === 'undefined' ){	// see ehaus.functions.js
1450
1451	const Cookie = {
1452		get: name => {
1453			let c = document.cookie.match(`(?:(?:^|.*; *)${name} *= *([^;]*).*$)|^.*$`)[1]
1454			console.log('acs.tools:Cookie.get::', name + '[' + c + ']');
1455			if (c) return decodeURIComponent(c)
1456		},
1457		set: (name, value, opts = {}) => {
1458
1459		console.log('acs.tools:Cookie.set::', name + '[' + value + ']');
1460		/*If options contains days then we're configuring max-age*/
1461			if(!opts.days){ opts['days'] = 7; }
1461	// or leave days blank to set Expires:Session
1462			if (opts.days) {
1463				opts['max-age'] = opts.days * 60 * 60 * 24;
1464				delete opts.days 
1465			}
1466			
1467		/* Set some other defaults */
1468			if(!opts.path){ opts['path'] = '/'; }
1469			if(!opts.SameSite){ opts['SameSite'] = 'Strict'; }
1470			if(!opts.Secure){ opts['Secure'] = 1; }
1471			
1472		/*Configuring options to cookie standard by reducing each property*/
1473			opts = Object.entries(opts).reduce(
1474				(accumulatedStr, [k, v]) => `${accumulatedStr}; ${k}=${v}`, ''
1475			)
1476
1477		/*Finally, creating the key*/
1478			document.cookie = name + '=' + encodeURIComponent(value) + opts
1479		},
1480		delete: (name, opts) => Cookie.set(name, '', {'days': -1}) 
1481		// path & domain must match cookie being deleted 
1482        }
1483}		
1484/******************************************************************************/
1485	function display_date_time(action, mod){
1486
1487	// Set up a dateArray()
1488		var months = "Zero,Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec"
1489		var dateArray = new Array();
1490		dateArray = months.split(",");
1491		var dateHash = new Array();
1492		for (var n=0; n<dateArray.length; n++) {
1493			dateHash[padz(n, 2)] = dateArray[n];
1494		}
1495	
1496		//console.log(dateHash);
1497		
1498		var date	= new Date();
1499		
1500		if(!empty(mod)){
1501			date = date.addDays(mod);
1502		}
1503		var day 	= date.getDate();
1504		var month 	= date.getMonth() + 1;
1505		var year 	= date.getFullYear()
1506		var hh 		= date.getHours();
1507		var mm 		= date.getMinutes();
1508		var ss 		= date.getSeconds();
1509		
1510		//alert(month);
1511		
1512		if(action == 'pretty') {
1513			
1514			return padz(day, 2) + '/' + padz(month, 2) + '/' + year;
1515			
1516		} else if (action == 'vpretty')  {
1517			
1518			return padz(day, 2) + ' ' + dateHash[padz(month, 2)] + ' ' + year;
1519			
1520		} else if (action == 'time')  {
1521			
1522			return padz(hh, 2) + ':' + padz(mm, 2) + ':' + padz(ss, 2);
1523			
1524		} else if (action == 'date')  {
1525			
1526			return year + '-' + month + '-' + padz(day, 2) ;
1527			
1528		} else if (action == 'datetime')  {
1529			
1530			return year + '-' + month + '-' + padz(day, 2) + 'T' + padz(hh, 2) + ':' + padz(mm, 2) + ':' + padz(ss, 2);
1531		
1532		} else if (action == 'yyyymmdd')  {
1533			
1534			return year + padz(month, 2) + padz(day, 2);
1535			
1536		} else {
1537			
1538			return year + padz(month, 2) + padz(day, 2);
1539			
1540		}
1541
1542	}
1543	
1544/* ******************************************************************************/
1545	
1546	Date.prototype.addDays = function(days) {
1547		var date = new Date(this.valueOf());
1548		date.setDate(date.getDate() + days);
1549		return date;
1550	}
1551
1552/* ******************************************************************************/
1553	function ResetFlag(refno, field, node){	
1554
1555		/*var setflag = val;   // better to get it from the image then you can change it back :)
1556			e.g. for an ehaus node set field = 'ehaus.fv_field_name' OR set node = 'ehaus' otherwise set it to '' 
1557			see admin.acs.edit.js for a cooler way to do this :)
1558		
1559		*/
1560		if( field.indexOf('.') ){
1561			// this results in undefined!! ToDO: fix!
1562			//node = field.split('.', 2)[0];
1563			//field= field.split('.', 2)[1];
1564		}
1565			
1566		node = (node) ? ('|node:' + node) : '';
1567		
1568		var setflag = $('#' + field + '_' + refno).attr('src');
1569		setflag = setflag.replace('../images/','');
1570		setflag = setflag.replace('.png','');
1571		setflag = (setflag == 'Y')?'N':'Y';// alert('['+setflag+']');
1572		
1573		var dbm = get_dbm();
1574		
1575		//alert(setflag);	
1576		$.ajaxQueue({
1577		   type: "POST",
1578		   url: give_me_a_baseref() + "lib/acs/acs-proxy.php", 
1579		   dataType: "json",
1580		   data: "dbm=" + dbm  + "&ax=update&xml=save&k="+refno +"&fv_"+field+node+"="+setflag+"",
1581		   success: function(json){       
1582			   
1583			   console.log(json.response);
1584				var errCode = json.response.error_code;
1585				var errMsg = json.response.error_msg;  
1586				//var fValue = $('responsetext', xml).text();   //same thing..
1587			   if(errCode == '0'){
1588					$('#' + field + '_' + refno).attr('src', '../images/'+setflag+'.png');
1589					
1590				}else{
1591					console.log('eeror: ResetFlag')
1592					//alert(errMsg);
1593				}
1594				
1595			}   
1596		});
1597	} 
1598
1599/* ******************************************************************************/
1600	function OrderStatusFlag(refno, field, node){	
1601
1602		//var setflag = val;   // better to get it from the image then you can change it back :)
1603		var setflag = $('#' + field + '_' + refno).attr('src');
1604		setflag = setflag.replace('../images/','');
1605		setflag = setflag.replace('.png','');
1606		setflag = (setflag == 'Y')?'N':'Y';// alert('['+setflag+']');
1607		
1608		//alert(setflag);	
1609		$.ajaxQueue({
1610		   type: "POST",
1611		   url: "../include/db.server.php", 
1612		   data: "admdb=" + admdb_dbm + "&ax=update-xml&fv_ref_no="+refno +"&fv_"+field+"|node:ehaus"+"="+setflag+"&idx=Y",
1613		   success: function(xml){       
1614			   
1615				var errCode = $("errorcode", xml).text();
1616				var errMsg = $("errormsg", xml).text();  
1617				//var fValue = $('responsetext', xml).text();   //same thing..
1618			   if(errCode == '0'){
1619					$('#' + field + '_' + refno).attr('src', '../images/'+setflag+'.png');
1620					
1621				}else{
1622					console.log('error: OrderStatusFlag');
1623					//alert(errMsg);
1624				}
1625				
1626			}   
1627		});
1628	}    
1629	
1630/* ******************************************************************************/
1631/* DROPDWON SELECT BOXES
1632		<input type="hidden" class="" data-select="field" name="fv_<?php echo $field;?>" id="<?php echo $field;?>" value=""  >
1633		<input type="hidden" data-select="code" name="fv_<?php echo $field;?>_code" id="<?php echo $field;?>_code" value="" >
1634		<select class="md-form md-form-compact form-control auto-select validate" type="select" name="" id="<?php echo $field;?>_code_sel" rel="fv_<?php echo $field;?>" data-valid='N' data-required=true data-maxlength=''>
1635		  <?php require('include/Options_Country_Codes.inc'); ?>
1636		</select>
1637				
1638	<!-- Form: CHECKBOX -  -->		
1639	<div class="form-check form-check-plain my-2">
1640		<?php $field = "ship_and_bill_flag"; $label = "Billing address is same as shipping";?>
1641		<input class="autoCheck form-check-input" type="checkbox" checked="checked" id="<?php echo $field; ?>" name="fv_<?php echo $field; ?>" value="Y" />		
1642		<label class="" for="<?php echo $field; ?>" id="<?php echo $field;?>_label"><?php echo $label;?>:</label>
1643		<div class="d-none control-messages"><div class="alert alert-error hidden"></div></div>
1644	</div>	
1645				
1646	*/
1647	function activateSelectTags() 
1648	{
1649																	   
1650				
1651	//	DROPDWON SELECT BOXES
1652		$('.auto-select').on('click keyup', function(event)
1653				{
1654				
1655			var code = $(this).val();
1656			var name = $(this).find('option:selected').attr('id') || $(this).find('option:selected').text();
1657			//console.log($(this).attr('id'), 'code:' + code + " name: " + name);
1658			$(this).siblings('input:hidden').each(function(){
1659				//console.log( $(this).attr('id'), $(this).data('select') + '[' + code + ']' );
1660				if( $(this).data('select') == 'field' ){
1661					$(this).val(name);
1662				}else if( $(this).data('select') == 'code' ){
1663					$(this).val(code);
1664				}
1665				
1666			});
1667					
1668					});
1669					
1670				
1671		
1672	// CHECKBOXES	
1673		$('input.autoCheck').unbind('click');
1674		
1675		$('input.autoCheck').each(function () {
1676			var idCheck = $(this);
1677			var idValue = idCheck.attr("id").replace(/_chk$/,"");
1678			//console.log('autoCheck:' + idValue + '[' + idCheck.val() + ']');
1679			//console.log('autoCheck:x:' + idCheck.prop('checked') + '[' + idCheck.attr('checked') + ']');
1680			
1681			if($('#' + idValue).val() == "Y"){
1682				idCheck.prop('checked', true);
1683			}else{
1684				idCheck.prop('checked', false);
1685				$('#' + idValue).val('N');
1686			}
1687		})
1688		
1689		$('input.autoCheck').click(function () {
1690			
1691			//console.log('who clicked me?');
1692			var idCheck = $(this);
1693			var idValue = idCheck.attr("id").replace(/_chk/,"");
1694			if(idCheck.prop('checked')){
1695				$('#' + idValue).val('Y');
1696			}else{
1697				$('#' + idValue).val('N');
1698			}
1699		})
1700		
1701	}
1702	
1703/* ******************************************************************************/

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.