PageSourceSearch

https://dumpeedumpsters.com/cp/resources/js/general_functions.js?49

js dumpeedumpsters.com collected 2026-10-04 05:18:13 UTC 58,635 bytes, 2,400 lines download raw bytes

1// JavaScript Document
2
3//TRIM POLYFILL FOR OLDER BROWSERS//
4//	...eyes on you, IE...	 //
5if( ! "trim" in String.prototype )
6	String.prototype.trim = function() { this.replace(/^\s*/, '').replace(/\s*$/, ''); };
7
8String.prototype.ersPaddingLeft = function(paddingValue)
9{
10	return String(paddingValue + this).slice(-paddingValue.length);
11}
12
13loader_script_arr = new Array();
14function add_loader_script(str)
15{
16	loader_script_arr[loader_script_arr.length] = str;
17}
18
19function run_loader_scripts()
20{
21	for(n=0; n<loader_script_arr.length; n++)
22	{
23		var script_str = loader_script_arr[n];
24		eval(script_str);
25	}
26}
27
28window.onload = run_loader_scripts;
29
30function sort_js_array_by_key(jsarr,jskey) {
31	// note that keys must be unique and must exist in all rows for this function to work properly
32	var klist = new Array();
33	var arr_by_keys = new Array();
34	for(var k in jsarr) {
35		var keyval = jsarr[k][jskey];
36		arr_by_keys['K' + keyval] = jsarr[k];
37		klist.push(keyval);
38	}
39	klist.sort();
40	var newarr = new Array();
41	for(var k=0; k<klist.length; k++) {
42		var keyval = klist[k];
43		newarr.push(arr_by_keys['K' + keyval]);
44	}
45	return newarr;
46}
47
48function is_dev_site()
49{
50	var wnlc = "" + window.location + "";
51	if(wnlc.indexOf('_dev') > 1 || wnlc.indexOf('-dev') > 1)
52	{
53		return true;
54	}
55	else return false;
56}
57
58function append_html_to_id(areaid, newhtml, element_container)
59{
60	if(!element_container)
61		element_container = 'div';
62	newhtml = decode_escaped_string(newhtml);
63	var t = document.createElement(element_container);
64	t.innerHTML = newhtml;
65	document.getElementById(areaid).appendChild(t);
66	//document.getElementById(areaid).innerHTML += newhtml;
67	return t;
68}
69
70function decode_escaped_string(str)
71{
72	str = str.replace(/\[_quot_\]/ig,"\"");
73	str = str.replace(/\[_squot_\]/ig,"'");
74	str = str.replace(/\[_lt_\]/ig,"<");
75	str = str.replace(/\[_gt_\]/ig,">");
76	str = str.replace(/\[_ls_\]/ig,"\\");
77	str = str.replace(/\[_fs_\]/ig,"/");
78	str = str.replace(/\[_nl_\]/ig,"\n");
79	str = str.replace(/\[_qu_\]/ig,"?");
80	return str;
81}
82
83function getObjPosition(obj,type,check_scroll)
84{
85	if (!obj)
86	{
87		return [];
88	}
89
90	if(!check_scroll) check_scroll = false;
91	if(!type) type = "all";
92	var curwidth = obj.offsetWidth;
93	var curheight = obj.offsetHeight;
94	var curleft = obj.offsetLeft;
95	var curtop = obj.offsetTop;
96	var offleft = 0;
97	var offtop = 0;
98		if(check_scroll) {
99			if(obj.offsetParent != obj.parentNode) {
100				offleft -= obj.parentNode.scrollLeft;
101				offtop -= obj.parentNode.scrollTop;
102				//console.log(obj.parentNode.className + ":" + obj.parentNode.scrollTop);
103			}
104		}
105
106
107	while(obj.offsetParent)
108	{
109		obj = obj.offsetParent;
110		curleft += obj.offsetLeft;
111		curtop += obj.offsetTop;
112		offleft += obj.offsetLeft;
113		offtop += obj.offsetTop;
114		if(check_scroll) {
115			offleft -= obj.scrollLeft;
116			offtop -= obj.scrollTop;
117		}
118	}
119	var retval = new Array();
120	retval[0] = curleft;
121	retval[1] = curtop;
122	retval[2] = curwidth;
123	retval[3] = curheight;
124	retval['left'] = curleft;
125	retval['top'] = curtop;
126	retval['width'] = curwidth;
127	retval['height'] = curheight;
128	retval['offleft'] = offleft;
129	retval['offtop'] = offtop;
130	if(type=="left" || type=="top" || type=="width" || type=="height") return retval[type];
131	return retval;
132}
133
134function getElementByIdOrNode(node) {
135	if( node instanceof HTMLElement ) return node;
136	if( typeof node === "string" ) return document.getElementById(node);
137	return null;
138}
139
140/***
141 * elementDescendedFrom(element_id: string|Node, descendent_id: string|Node) -> success: bool
142 * @returns success: bool
143 *
144 * Returns whether or not element_id is an ancestor of descendent_id in the
145 * DOM tree.
146 * */
147function elementDescendedFrom(element_id,descendent_id)
148{
149	//JL: 2017.07.30
150	//Added if statement to allow DOM nodes as parameters.
151	//The old functionality is within the first `if`:
152	if( typeof element_id === "string" ) {
153
154		//begin old function//
155		var str = "searching for: " + descendent_id;
156		var obj = document.getElementById(element_id);
157		str += "\n" + obj.id;
158		if(obj.id==descendent_id) return true;
159		while(obj.offsetParent)
160		{
161			obj = obj.offsetParent;
162			str += "\n" + obj.id;
163			if(obj.id==descendent_id) return true;
164		}
165		return false;
166		//end old function//
167	}
168
169	if( element_id instanceof HTMLElement ) {
170
171		var descendent = getElementByIdOrNode(descendent_id);
172		if( obj === descendent ) return true;
173		while( obj.parentNode && obj.parentNode.tagName !== "BODY" ) {
174			obj = obj.parentNode;
175			if( obj === descendent ) return true;
176		}
177	}
178	return false;
179}
180
181function getElementPosition(element_id)
182{
183	var obj = document.getElementById(element_id);
184	return getObjPosition(obj);
185}
186
187function getViewPortSize()
188{
189	var viewportwidth;
190	var viewportheight;
191	// the more standards compliant browsers (mozilla/netscape/opera/IE7) use window.innerWidth and window.innerHeight
192	if (typeof window.innerWidth != 'undefined')
193	{
194		viewportwidth = window.innerWidth,
195		viewportheight = window.innerHeight
196	}
197	// IE6 in standards compliant mode (i.e. with a valid doctype as the first line in the document)
198	else if (typeof document.documentElement != 'undefined' && typeof document.documentElement.clientWidth != 'undefined' && document.documentElement.clientWidth != 0)
199	{
200		viewportwidth = document.documentElement.clientWidth,
201		viewportheight = document.documentElement.clientHeight
202	}
203	// older versions of IE
204	else
205	{
206		viewportwidth = document.getElementsByTagName('body')[0].clientWidth,
207		viewportheight = document.getElementsByTagName('body')[0].clientHeight
208	}
209	return new Array(viewportwidth, viewportheight);
210}
211
212function get_parent_element(parent_id)
213{
214	obj = document.getElementById("floating_window").parentNode;
215	while(obj.id.substring(0,14)=="container_area")
216		obj = obj.parentNode;
217	return obj;
218}
219
220function add_value_to_select(selectbox,setval_id,setval_name) // add value to select box but don't select it
221{
222	for ( var i = 0; i < selectbox.options.length; i++ )
223	{
224		if ( selectbox.options[i].value == setval_id )
225		{
226			//selectbox.options[i].selected = true;
227			return;
228		}
229	}
230	selectbox.options[selectbox.options.length] = new Option(setval_name, setval_id, false, false); // last arg is selected (t/f)
231}
232
233function set_select_value(selectbox,setval_id,setval_name) // add value to select box if doesn't exist and select it
234{
235	for ( var i = 0; i < selectbox.options.length; i++ )
236	{
237		if ( selectbox.options[i].value == setval_id )
238		{
239			selectbox.options[i].selected = true;
240			return;
241		}
242	}
243	selectbox.options[selectbox.options.length] = new Option(setval_name, setval_id, false, true); // last arg is selected (t/f)
244	//alert("value " + setval + " is not in the dropdown list yet");
245}
246
247function open_help_video(containerid,videoid)
248{
249	var setstr = "";
250	if(videoid.indexOf(',') > 0)
251	{
252		setstr += "<table cellspacing=8 cellpadding=2>";
253		videoid_list = videoid.split(',');
254		for(var vh=0; vh<videoid_list.length; vh++)
255		{
256			var videoid_parts = videoid_list[vh].split(':');
257			var current_videoid = videoid_parts[0];
258			var video_title = "Video #" + (vh + 1);
259			if(videoid_parts.length > 1)
260			{
261				video_title = videoid_parts[1];
262			}
263			setstr += "<tr>";
264			setstr += "<td style='border:solid 1px #777777; width:240px; cursor:pointer; font-size:14px; color:#004400' align='left' bgcolor='#ccccdd' onmouseover='this.bgColor = \"#eeeef8\"' onmouseout='this.bgColor = \"#ccccdd\"' onclick='open_help_video(\"" + containerid + "\",\"" + current_videoid + "\")'>";
265			//setstr += (vh + 1) + ")";
266			setstr += "<table><tr><td valign='middle'><img src='/cp/images/video_camera.png' border='0' /></td>";
267			setstr += "<td valign='middle' style='font-size:14px; color:#004400'>";
268			setstr += "&nbsp;&nbsp;Watch \"" + video_title + "\"";
269			setstr += "</td></tr></table>";
270
271			setstr += "</td>";
272			setstr += "</tr>";
273		}
274		setstr += "</table>";
275	}
276	else
277	{
278		setstr += "<table width='100%'><tr><td width='100%' align='center'><iframe allowfullscreen=\"\" frameborder=\"0\" height=\"315\" src=\"//www.youtube.com/embed/" + videoid + "\" width=\"560\"></iframe></td></tr></table>";
279	}
280	auto_reset_content_when_closed = true;
281	open_floating_window(" ",setstr,"main_area");
282	//document.getElementById(containerid).innerHTML = setstr;
283}
284
285
286/******* Help Stuff */
287
288function cp_page_help_edit(action)
289{
290	// Action can be:
291	// enable	- The edit button has been clicked, so display the edit controls and set up variables
292	// save		- submit the new cp-page-help string to the server for save
293	// cancel	- cancel
294
295	if (action=='init')
296	{
297		window.cp_page_help_edit_mode = false;
298	} else
299	if (action=='enable')
300	{
301		window.cp_page_help_edit_mode = true;
302		window.cp_page_help_edit_data = undefined;
303	} else
304	if (action=='save' && typeof window.page_area !== 'undefined')
305	{
306		new_data = cp_page_help_array_to_str(window.cp_page_help_edit_data);
307
308		var ajax_data = {
309			cmd:	"cp_page_help_update",
310			data:	new_data,
311			area:	window.page_area
312		};
313
314		$.ajax({
315			type: 'POST',
316			async: false,
317			url: '/ajax_query/',
318			data: ajax_data
319		})
320		.done(function(response)
321		{
322			// console.log("done");
323		})
324		.fail(function(error)
325		{
326			console.log('Failed: ' + error);
327		})
328		.always(function(message)
329		{
330		});
331
332	} else
333	if (action=='cancel')
334	{
335		window.cp_page_help_edit_mode = false;
336		window.cp_page_help_edit_data = [];
337	}
338}
339
340function cp_page_help_str_to_array(data)
341{
342	if (data == "")
343		return [];
344
345	var array_data	= [];
346	var item_count	= 0;
347	var split_items	= [];
348
349	if (data.indexOf(',') === -1)
350		split_items[0] = data;
351	else
352		split_items = data.split(',');
353
354
355	split_items.forEach(function(item){
356		item_parts = [];
357
358		if (item.indexOf(":") === -1)
359			item_parts[0] = item;
360		else
361			item_parts	= item.split(':');
362
363		item_data	 = item_parts[0];
364		item_data_raw = item_data;
365		if (item_parts.length > 1)
366			item_title = item_parts[1];
367		else
368			item_title = 'Item ' + ((item_count*1)+1);
369
370		if (item_data.match(/(kb_search_)/))
371		{
372			item_type = 'kb_search';
373			item_data = item_data.replace(/kb_search_/,"");
374		} else
375		if (item_data.match(/(kb_id_)/))
376		{
377			item_type = 'kb_id';
378			item_data = item_data.replace(/kb_id_/,"");
379		} else
380		if (item_data.match(/zendesk/))
381		{
382			item_type = "zendesk";
383			item_data = "https://dumpsterrentalsystems.zendesk.com/hc/en-us/";
384		} else
385		{
386			item_type = 'youtube';
387		}
388
389		array_data[item_count] = {title: item_title, data: item_data, data_raw: item_data_raw, type:item_type};
390		item_count++;
391	});
392
393	return array_data;
394}
395
396function cp_page_help_array_to_str(data)
397{
398	var str_data = "";
399	var total_count = data.length;
400	var count = 0;
401
402	data.forEach(function(data_item){
403		if (!data_item['data_raw'].match(/zendesk/))
404		{
405			str_data += `${data_item['data_raw']}:${data_item['title']}`;
406			if (count < (total_count-1))
407				str_data += ",";
408		}
409		count++;
410	});
411
412	return str_data;
413}
414
415function cp_page_help_row_action(action, id, args)
416{
417	// console.log(id+": "+action);
418
419	if (action=="up" && id > 0)
420	{
421		temp = window.cp_page_help_edit_data[id-1];
422		window.cp_page_help_edit_data[id-1] = window.cp_page_help_edit_data[id];
423		window.cp_page_help_edit_data[id] = temp;
424	} else
425	if (action=="down" && id < (window.cp_page_help_edit_data.length-1))
426	{
427		temp = window.cp_page_help_edit_data[id+1];
428		window.cp_page_help_edit_data[id+1] = window.cp_page_help_edit_data[id];
429		window.cp_page_help_edit_data[id] = temp;
430	} else
431	if (action=="trash")
432	{
433		if(confirm(`Are you sure you want to delete this item?\n"${window.cp_page_help_edit_data[id]['title']}?"\n(This won't be final until you click save)`))
434			window.cp_page_help_edit_data.splice(id,1);
435	} else
436	if (action=="add")
437	{
438		row_type		= $(`#new_row_type`).val();
439		row_title		= $(`#new_row_title`).val();
440		row_data		= $(`#new_row_data`).val();
441		row_industries	= $(`#new_row_industries`).val();
442
443		if (row_industries != '')
444		{
445			row_industries = row_industries.toLowerCase();
446			row_title += " (product " + row_industries.replace(',',' ') + ")";
447			console.log('present');
448		} else
449		{
450			console.log('empty');
451		}
452
453		if (row_type == 'kb_search')
454			raw_pre = "kb_search_";
455		else if (row_type == 'kb_id')
456			raw_pre = "kb_id_";
457		else
458			raw_pre = '';
459
460		row_data_raw = raw_pre+row_data;
461
462		window.cp_page_help_edit_data[(window.cp_page_help_edit_data.length)] = {
463			type:		row_type,
464			title:		row_title,
465			data:		row_data,
466			data_raw:	row_data_raw
467		};
468	} else
469	{
470		// console.log ('limit');
471		return;
472	}
473	// console.log("args:");
474	// console.log(args);
475	open_cp_page_help_content(args['containerid'],args['content_data'],args['header'],args['back'],args['show_edit']);
476}
477
478function open_cp_page_help_content(containerid,content_data,header,back="", show_edit="")
479{
480	header_display = header;
481
482	// console.log(content_data);
483
484	if (typeof window.cp_page_help_edit_mode === 'undefined')
485	{
486		cp_page_help_edit('init');
487		var cp_help_data = cp_page_help_str_to_array(content_data);
488	} else
489	if (window.cp_page_help_edit_mode === true)
490	{
491		if (typeof window.cp_page_help_edit_data === 'undefined' || window.cp_page_help_edit_data.length === '
491init')
492			window.cp_page_help_edit_data = cp_page_help_str_to_array(content_data);
493
494		var cp_help_data = window.cp_page_help_edit_data;
495	} else
496	{
497		var cp_help_data = cp_page_help_str_to_array(content_data);
498	}
499
500	// console.log('cp_help_data:', cp_help_data);
501
502	var output = "";
503
504	if (cp_help_data.length > 1 || cp_help_data.length == 0 || window.cp_page_help_edit_mode === true || content_data.match(/zendesk/))
505	{
506		// Multiple Items Listing
507		if (window.cp_page_help_edit_mode === true)
508		{
509			var cp_page_help_row_template = `<tr><td>ICON&nbsp;&nbsp;&nbsp;TITLE</td><td class='text-right'>UP DOWN TRASH</td></tr>`;
510			var cp_page_help_row_template_zendesk = cp_page_help_row_template;
511		} else
512		{
513				var cp_page_help_row_template_zendesk = `<tr><td class='zendesk_link'>ICON&nbsp;&nbsp;&nbsp;<a href='LINK' target='_blank'>TITLE</a></td><td>&nbsp;</td></tr>`;
514				var cp_page_help_row_template		 = `<tr><td LINK>ICON&nbsp;&nbsp;&nbsp;TITLE</td><td class='text-right'>UP DOWN TRASH</td></tr>`;
515				// var cp_page_help_row_template		= `<tr><td LINK>ICON&nbsp;&nbsp;&nbsp;TITLE</td></tr>`;
516		}
517		output += `<table class='table table-striped table-hover cp_page_help_table'>`;
518
519		cp_help_data.forEach(function(data_item, key){
520
521			if (!window.lv)
522				display_title = data_item['title'].replace(/\(products?\s*?([^\)]*)\)/,"");
523			else
524				display_title = data_item['title'];
525
526			// Don't display row if it's not the right industry (unless it's a level 12)
527			if (data_item['title'].match(/\(product/) && !window.lv)
528			{
529				row_industries = data_item['title'].match(/\(products?\s*?([^\)]*)\)/);
530				if (!row_industries[1].match(window.my_industry))
531					return;
532			}
533
534			if (data_item['type'].match(/kb/))
535				data_icon = `<i class='fal fa-fw fa-file-alt' style='font-size: 20px;'></i>`;
536			else
537			if (data_item['type'].match(/zendesk/))
538				data_icon = `<i class="fas fa-fw fa-globe-americas" style='font-size: 20px;'></i>`;
539			else
540				data_icon = `<i class='fal fa-fw fa-video' style='font-size: 20px;'></i>`;
541
542			if (window.cp_page_help_edit_mode === true && !data_item['type'].match(/zendesk/))
543			{
544				args			= `{containerid: "${containerid}", content_data: "${content_data}", header: "${header}", back: "${back}", show_edit: "${show_edit}"}`;
545				data_link		= "";
546				button_up		= `<button class='btn btn-xs btn-default cp_page_help_row_button' type='button' onclick='cp_page_help_row_action("up",${key}, ${args});'><i class="far fa-arrow-alt-up fa-fw"></i></button>`;
547				button_down		= `<button class='btn btn-xs btn-default cp_page_help_row_button' type='button' onclick='cp_page_help_row_action("down",${key}, ${args});'><i class="far fa-arrow-alt-down fa-fw"></i></button>`;
548				button_trash	= `<button class='btn btn-xs btn-default cp_page_help_row_button' type='button' onclick='cp_page_help_row_action("trash",${key}, ${args});'><i class="fal fa-trash-alt fa-fw"></i></button>`;
549				row_template	= cp_page_help_row_template;
550			} else
551			if (data_item['type'].match(/zendesk/))
552			{
553				data_link		= data_item['data'];
554				button_up		= "";
555				button_down		= "";
556				button_trash	= "";
557				row_template	= cp_page_help_row_template_zendesk;
558			} else
559			{
560				data_link		= `onclick='open_cp_page_help_content("${containerid}", "${data_item['data_raw']}:${display_title}", "${header}", "${content_data}", "${show_edit}")'`;
561				button_up		= "";
562				button_down		= "";
563				button_trash	= "";
564				row_template	= cp_page_help_row_template;
565			}
566
567			if (window.cp_page_help_edit_mode === false || (window.cp_page_help_edit_mode === true && !data_item['type'].match(/zendesk/)))
568				output += row_template.replace(/LINK/g,data_link).replace(/ICON/g,data_icon).replace(/TITLE/g,display_title).replace(/UP/g,button_up).replace(/DOWN/g,button_down).replace(/TRASH/g,button_trash);
569
570		});
571
572		if (window.cp_page_help_edit_mode === true)
573		{
574			args = `{containerid: "${containerid}", content_data: "${content_data}", header: "${header}", back: "${back}", show_edit: "${show_edit}"}`;
575			output += `<tr><td class='bg-info'>
576			<div class='form-group col-xs-12 col-md-3'>
577				<label for='type'>Content Type</label>
578				<select class='form-control' id="new_row_type" name="type">
579					<option value="video" selected>Youtube Video</option>
580					<option value="kb_search">Knowledge Base Search</option>
581					<option value="kb_id">Knowledge Base ID</option>
582				</select>
583			</div>
584			<div class='form-group col-xs-12 col-md-3'>
585				<label for='title'>Title</label>
586				<input class='form-control' type='text' id='new_row_title' name='title'>
587			</div>
588			<div class='form-group col-xs-12 col-md-3'>
589				<label for='data'>Data</label>
590				<input class='form-control' type='text' id='new_row_data' name='data'>
591			</div>
592			<div class='form-group col-xs-12 col-md-3'>
593				<label for='data'>Industries <button tabindex="-1" type="button" class="btn btn-default btn-xs" data-toggle="tooltip" data-placement="top" title="List one or more industries separated with spaces, or leave blank to be visible for all industries.">?</button></label>
594				<input class='form-control' type='text' id='new_row_industries' name='industries' placeholder='eg: "bounce dumpster fec"'>
595			</div>
596			</td><td class='text-right bg-info' style='vertical-align: middle;'><button style='margin-right: 5px;' class='btn btn-success' type='button' onclick='cp_page_help_row_action("add",0, ${args});'>Add</button></td>`;
597			window.setTimeout(function () {$('[data-toggle="tooltip"]').tooltip();});
598		}
599		output += `</table>`;
600	} else
601	{
602		// Single Content Item
603		if (back.indexOf(',') > 0)
604			back_button = `<h4><a href='#' onclick='open_cp_page_help_content("${containerid}","${back}","${header}","","${show_edit}");'><i class="far fa-backward"></i> Back</a></h4>`;
605		else
606			back_button = '';
607
608		if (cp_help_data[0]['type'].match(/(kb_(search|id))/))
609		{
610			var ajax_data = {
611				cmd:	"ers_help",
612				mode:	"search",
613				search:	cp_help_data[0]['data_raw'].replace(/kb_search_/,"").replace(/^kb_id_/,"id:")
614			};
615
616			$.ajax({
617				type: 'POST',
618				async: false,
619				url: '/ajax_query/',
620				data: ajax_data
621			})
622			.done(function(response)
623			{
624				adj_response = decodeQueryString(response);
625				output += back_button + "<div class='eh_sections_container'>" + adj_response['content'] + "</div>";
626			})
627			.fail(function(error)
628			{
629				console.log('Failed: ' + error);
630			})
631			.always(function(message)
632			{
633			});
634		} else
635		{
636			if (cp_help_data[0]['data'].match(/\?/))
637				d = "&";
638			else
639				d = "?";
640
641			data_link = "//www.youtube.com/embed/" + cp_help_data[0]['data'] + d + 'autoplay=1';
642			// data_link = "http://youtu.be/" + cp_help_data[0]['data'];
643
644			output += back_button + `<div class='cp_page_help_video_wrapper'><iframe allowfullscreen="" allow='autoplay' frameborder="0" class="cp_page_help_video_iframe" src="${data_link}"></iframe></div>`;
645		}
646
647		if (!window.lv)
648			display_title = cp_help_data[0]['title'].replace(/\(products?\s*?([^\)]*)\)/,"");
649		else
650			display_title = cp_help_data[0]['title'];
651
652		header_display = header + " - " + display_title;
653	}
654
655	edit_button = "";
656	if (show_edit == "yes" && back=="")
657	{
658		if (window.cp_page_help_edit_mode === true)
659		{
660			edit_button += `<button id='button_cp_help_Save' class='btn btn-sm btn-success' style='margin-left: 15px;' type='button' onclick='cp_page_help_edit("save"); location.reload();'>Save</button> `;
661			edit_button += `<button id='button_cp_help_Cancel' class='btn btn-sm btn-danger' style='margin-left: 15px;' type='button' onclick='cp_page_help_edit("cancel"); open_cp_page_help_content("${containerid}","${content_data}","${header}","","yes");'>Cancel</button>`;
662			edit_button += `<a target='_blank' class='btn btn-sm btn-default' style='margin-left:15px; type='button' href='${window.appConfig.filesURL}/cp/upload/default_dev/HelpHelp.mp4'>Help Help</a>`;
663		} else
664		{
665			edit_button = `<button id='button_cp_help_Edit' class='btn btn-sm btn-warning' style='margin-left: 15px;' type='button' onclick='cp_page_help_edit("enable"); open_cp_page_help_content("${containerid}","${content_data}","${header}","","yes");'>Edit</button>`;
666		}
667	}
668
669	auto_reset_content_when_closed = true;
670	open_floating_window("<h3>Control Panel Help: " + header_display + edit_button + "</h3>",output,"main_area",'90vw');
671}
672
673/******* End Help Stuff */
674
675function advance_time(t,add)
676{
677	var t_mins = t % 100;
678	var t_hours = t - t_mins;
679	var add_mins = add % 100;
680	var add_hours = add - add_mins;
681
682	var set_hours = t_hours + add_hours;
683	var set_mins = t_mins + add_mins;
684	if(set_mins >= 60)
685	{
686		set_hours += 100;
687		set_mins -= 60;
688	}
689
690	return (set_hours * 1 + set_mins * 1);
691}
692function display_time(t)
693{
694	if(typeof(t)=="String" && t.indexOf(':') > 0) {
695		t = t.replace(/\:/ig,'');
696	}
697
698	var t_mins = t % 100;
699	var t_hours = (t - t_mins) / 100;
700
701	if(t_mins < 10) t_mins = "0" + (t_mins * 1);
702	if(t_hours==0) { t_hours = 12; t_ampm = "am"; }
703	else if(t_hours==24) { t_hours = 12; t_ampm = "am"; }
704	else if(t_hours > 24) { t_hours -= 24; t_ampm = "am"; }
705	else if(t_hours==12) { t_ampm = "pm"; }
706	else if(t_hours > 12) { t_hours -= 12; t_ampm = "pm"; }
707	else { t_ampm = "am"; }
708
709	return t_hours + ":" + t_mins + t_ampm;
710}
711function unescape_decode(dstr)
712{
713	return unescape(dstr.replace(/\+/ig,' '));
714}
715function ersCurrentUnixTimestamp()
716{
717	return Math.floor(Date.now() / 1000);
718}
719function ersCurrentDateTime(joined)
720{
721	var d = new Date();
722
723	var year	= d.getFullYear();
724	var month	= (d.getMonth() + 1);
725	var day		= d.getDate();
726	var hour	= d.getHours();
727	var minute	= d.getMinutes();
728	var second	= d.getSeconds();
729
730	month	= month.toString().ersPaddingLeft("00");
731	day		= day.toString().ersPaddingLeft("00");
732	hour	= hour.toString().ersPaddingLeft("00");
733	minute	= minute.toString().ersPaddingLeft("00");
734	second	= second.toString().ersPaddingLeft("00");
735
736	if (joined)
737	{
738		return year + "" + month + "" + day + "" + hour + "" + minute + "" + second;
739	}
740	else
741	{
742		return year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second;
743	}
744}
745function ersDateStringFromDate(d)
746{
747	var year	= d.getFullYear();
748	var month	= (d.getMonth() + 1);
749	var day		= d.getDate();
750
751	month	= month.toString().ersPaddingLeft("00");
752	day		= day.toString().ersPaddingLeft("00");
753
754	return year + "-" + month + "-" + day;
755}
756function ersIsValidDate(date_str, boolean)
757{
758	var dashed	= ersVS(date_str).split("-");
759	var slashed	= ersVS(date_str).split("/");
760	var dparts	= [];
761	var sep		= "";
762
763	if (dashed.length >= 3)
764	{
765		sep = "-";
766		dparts = dashed;
767	}
768	else if (slashed.length >= 3)
769	{
770		sep = "/";
771		dparts = slashed;
772	}
773
774	if (dparts.length < 3)
775	{
776		return false;
777	}
778
779	var d0 = dparts[0];
780	var d1 = dparts[1];
781	var d2 = dparts[2];
782
783	if (isNaN(d0) || d0*1<1 || isNaN(d1) || d1*1<1 || isNaN(d2) || d2*1<1)
784	{
785		return false;
786	}
787
788	var l0 = d0.length;
789	var l1 = d1.length;
790	var l2 = d2.length;
791
792	var is_valid = false;
793
794	switch (sep)
795	{
796		case "-":
797
798			is_valid = (l0!=4 || l1!=2 || l1!=2)?false:true;
799			break;
800
801		case "/":
802
803			is_valid = (l0<1 || l0>2 || l1<1 || l1>2 || l2!=4)?false:true;
804			break;
805
806		default:
807			break;
808	}
809
810	if (boolean)
811	{
812		return is_valid;
813	}
814
815	return {
816		'parts':		dparts,
817		'seperator':	sep,
818		'valid':		is_valid
819	}
820}
821function ersTSFromDate(date_str)
822{
823	var is_valid = ersIsValidDate(date_str);
824
825	if (!is_valid || !is_valid.valid)
826	{
827		return 0;
828	}
829
830	var dparts = is_valid.parts;
831
832	var d = new Date();
833	d.setHours(0);
834	d.setMinutes(0);
835	d.setSeconds(0);
836	d.setMilliseconds(0);
837
838	switch (is_valid.seperator)
839	{
840		case "-":
841
842			d.setYear(dparts[0] * 1);
843			d.setMonth((dparts[1] * 1) - 1);
844			d.setDate(dparts[2] * 1);
845			break;
846
847		case "/":
848
849			d.setYear(dparts[2] * 1);
850			d.setMonth((dparts[0] * 1) - 1);
851			d.setDate((dparts[1] * 1));
852			break;
853
854		default:
855
856			return 0;
857	}
858
859	return d.valueOf();
860}
861
862function ersDurationFromSeconds(seconds, simplified)
863{
864	var sec				= ersVI(seconds);
865	var seconds_ts		= (sec % 60);
866	var total_mins_ts	= ((sec - seconds_ts) / 60);
867	var mins_ts			= (total_mins_ts % 60);
868	var hours_ts		= ((total_mins_ts - mins_ts) / 60);
869
870	var time_str = "";
871	if (hours_ts >= 1)
872	{
873		if (simplified)
874		{
875			if ((mins_ts >= 20) && (mins_ts <= 40))
876			{
877				hours_ts += 0.5;
878			}
879			else if (mins_ts > 40)
880			{
881				hours_ts += 1;
882			}
883		}
884
885		if (hours_ts > 1)
886		{
887			time_str += hours_ts + " hrs ";
888		}
889		else if (hours_ts == 1)
890		{
891			time_str += hours_ts + " hr ";
892		}
893
894		if (simplified)
895		{
896			return time_str.trim();
897		}
898	}
899
900	if (mins_ts >= 1)
901	{
902		if (simplified)
903		{
904			if ((seconds_ts >= 20) && (seconds_ts <= 40))
905			{
906				mins_ts += 0.5;
907			}
908			else if (seconds_ts > 40)
909			{
910				mins_ts += 1;
911			}
912		}
913
914		if (mins_ts > 1)
915		{
916			time_str += mins_ts + " mins ";
917		}
918		else if (mins_ts == 1)
919		{
920			time_str += mins_ts + " min ";
921		}
922
923		if (simplified)
924		{
925			return time_str.trim();
926		}
927	}
928
929	if (seconds_ts > 1)
930	{
931		time_str += seconds_ts + " secs";
932	}
933	else if (seconds_ts == 1)
934	{
935		time_str += seconds_ts + " sec";
936	}
937
938	return time_str;
939}
940
941function display_date(d)
942{
943	var d_parts = d.split("-");
944	if(d_parts.length > 2)
945	{
946		return d_parts[1] + "/" + d_parts[2] + "/" + d_parts[0];
947	}
948	else return d;
949}
950
951function change_date_by_days(dt,mod)
952{
953	var dt_parts = dt.split("-");
954	var actualDate = new Date(dt_parts[1] + "/" + dt_parts[2] + "/" + dt_parts[0]); // convert to actual date
955	var newDate = new Date(actualDate.getFullYear(), actualDate.getMonth(), actualDate.getDate()+mod); // create new increased date
956	var newYear = newDate.getFullYear();
957	var newMonth = (newDate.getMonth() + 1);
958	var newDate = newDate.getDate();
959	var datestr = newYear + "-";
960	if(newMonth < 10) datestr += "0" + newMonth; else datestr += newMonth;
961	datestr += "-";
962	if(newDate < 10) datestr += "0" + newDate; else datestr += newDate;
963
964	return datestr;
965}
966
967function advance_date_by_day(dt)
968{
969	return change_date_by_days(dt,1);
970}
971
972function reverse_date_by_day(dt)
973{
974	return change_date_by_days(dt,-1);
975}
976
977function enableSelection(element)
978{
979	element.onselectstart=null;
980	element.onmousedown=null;
981}
982
983function disableSelection(element)
984{
985	element.onselectstart=function(){return false};
986	element.onmousedown=function(){return false};
987}
988
989function element_exists(elementid)
990{
991	var element = document.getElementById(elementid);
992	return typeof element !== "undefined" && element !== null;
993}
994
995function noenter(e)
996{
997		var key;
998		if(window.event)
999			key = window.event.keyCode; //IE
1000		else
1001			key = e.which; //firefox
1002		if(key == 13)
1003			return false;
1004		else
1005			return true;
1006}
1007
1008function trim_string(str)
1009{
1010	if( String.prototype.trim ) return str.trim();
1011	return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
1012}
1013
1014//****** blurOnEnter *****************************************
1015// This function blurs a field on hitting enter.
1016//
1017// element -- is the element that needs to be blurred
1018// e -- is the event (sometimes e = element).
1019//
1020// Added by AMC October 7, 2014
1021//************************************************************
1022function blurOnEnter(element, e)
1023{
1024		var key;
1025		if(window.event)
1026			key = window.event.keyCode; //IE
1027		else
1028			key = e.which; //firefox
1029		if(key == 13)
1030		{
1031			element.blur();
1032		}
1033}
1034
1035//****** updateNumber ****************************************
1036// This function is used with a dropdown box that displays
1037//	a list of numbers for item quantity selection.
1038//	if "other" is chosen, it will convert the dropdown box to
1039//	a basic text input box.
1040//
1041// element -- typically "this"
1042// length -- how long the dropdown can be (e.g. 100)
1043// avail -- the maximum number of items available
1044//			(could be greater or less than length).
1045// selection -- sets the pre-selected value
1046// can_overbook -- a boolean, whether or not they have permission to overbook.
1047//
1048// Added by AMC October 2, 2014
1049//************************************************************
1050function updateNumber(element, length, avail, selection, can_overbook, update_onchange, qty_onchange)
1051{
1052	if (element.value=='other')
1053	{
1054		var newElement = document.createElement('input');
1055		newElement.name = element.name;
1056		newElement.id = element.id;
1057		newElement.type='number';
1058		newElement.value='';
1059		newElement.size='8';
1060		newElement.style.width='60px';
1061		newElement.min='0';
1062		newElement.style.display='block';
1063		newElement.placeholder= avail + ' avail'; //This will only work in HTML 5
1064		newElement.onblur = function(event){ updateSelect(this, length, avail, selection, can_overbook, update_onchange, qty_onchange); };
1065		newElement.onkeyup = function(event) { blurOnEnter(newElement, event); };
1066		newElement.onkeypress = function(event){ return noenter(event); };
1067
1068		//alert(element.parentNode.name);
1069		element.parentNode.replaceChild(newElement, element);
1070
1071		//Handle iOS differently.
1072		var deviceIsIOS = /iP(ad|hone|od)/.test(navigator.userAgent);
1073		if (deviceIsIOS && targetElement.setSelectionRange)	//Turns out there is an error (or feature) in how mobile safari
1074		{													// handles on focus events, and basically they can't be triggered
1075			length = newElement.value.length;				// programattically. So this kind of gets around that.
1076			newElement.setSelectionRange(length, length);	// It won't "lose" focus and revert to a list, but it doesn't get focus
1077		}
1078		else
1079		{
1080			newElement.parentNode.focus(); //This is only needed for firefox, which is dopey. AMC December 2, 2014.
1081			newElement.focus();
1082		}
1083
1084		//Create an "update" link (this a dummy, to encourage them to click).
1085		if (update_onchange)
1086		{
1087			var b = document.createElement('button');
1088			var t = document.createTextNode('update qty');
1089			b.appendChild(t);
1090			newElement.parentNode.appendChild(b);
1091		}
1092	}
1093	else
1094	{
1095		if (update_onchange)
1096			qty_onchange(element);
1097	}
1098}
1099
1100//****** updateSelect ****************************************
1101// This function is a complement to updateNumber. It will
1102//	revert an input text box back to a dropdown selection,
1103//	if the text box is empty and the focus shifts.
1104//
1105// element -- typically "this"
1106// length -- how long the dropdown can be (e.g. 100)
1107// avail -- the maximum number of items available
1108//			(could be greater or less than length).
1109// selection -- sets the pre-selected value
1110// can_overbook -- a boolean, whether or not they have permission to overbook.
1111//
1112// Added by AMC October 2, 2014
1113//************************************************************
1114function updateSelect(element, length, avail, selection, can_overbook, update_onchange, qty_onchange)
1115{
1116	//Here we do some validation:
1117	if (element.value != '')
1118	{
1119		v = element.value;
1120		if (v > avail)
1121		{
1122			if (can_overbook)
1123			{
1124				if(!confirm("This will cause an overbooking (max avail: "+avail+")\n Are you sure you wish to overbook?"))
1125					element.value = "";
1126			}
1127			else
1128			{
1129				alert ("Maximum quantity available is " + avail);
1130				element.value = "";
1131			}
1132		}
1133		if (update_onchange)
1134			qty_onchange(element);
1135	}
1136
1137
1138	if (element.value=='' && element.type=='number')
1139	{
1140		if (update_onchange)
1141			qty_onchange(element);
1142		var newElement = document.createElement('select');
1143		newElement.name = element.name;
1144		newElement.id = element.id;
1145		//newElement.type='select';
1146		newElement.onchange = function(event){ updateNumber(this, length, avail, selection, can_overbook, update_onchange, qty_onchange); };
1147
1148		for (var i=0; i<=length; i++)
1149		{
1150			var opt = document.createElement('option');
1151			opt.value = i;
1152			opt.text = i;
1153			if (i == selection)
1154				opt.selected = true;
1155			newElement.options.add(opt);
1156		}
1157
1158		var opt = document.createElement('option');
1159		opt.value = 'other';
1160		opt.text = '>'+length;
1161		newElement.options.add(opt);
1162
1163
1164		element.parentNode.replaceChild(newElement, element);
1165	}
1166}
1167
1168function decround(val, p)
1169{
1170	if(typeof p === 'undefined' || +p === 0) return Math.round(val);
1171	val = +val;
1172	p = +p;
1173	if (isNaN(val) || !(typeof p === 'number' && p % 1 === 0)) return NaN;
1174	val = val.toString().split('e');
1175	val = Math.round(+(val[0] + 'e' + (val[1] ? (+val[1] + p) : p)));
1176	val = val.toString().split('e');
1177	return +(val[0] + 'e' + (val[1] ? (+val[1] - p) : -p));
1178}
1179
1180function formatDollar(num) {
1181	var p = parseFloat(num).toFixed(2).split(".");
1182	return "$" + p[0].split("").reverse().reduce(function(acc, num, i, orig) {
1183		return num + (i && !(i % 3) ? "," : "") + acc;
1184	}, "") + "." + p[1];
1185}
1186
1187blue_editor_button_arr = new Array();
1188
1189(function() {
1190	"use strict";
1191
1192	//UTILITIES//
1193
1194	window._UTIL = {};
1195
1196	function base64_encode_safe(input) {
1197		/* Encodes input as base64 string in a URL-safe manner, e.g. void of "/", "+", and "=". */
1198		/* Decode with base64_decode_safe($str). */
1199		/* NOTE: since '=' padding is removed, it is not safe to append
1200		 * multiple encoded strings generated by this method (you won't be able
1201		 * to decode them). */
1202		return btoa(input)
1203			.replace(/=/g, '')
1204			.replace(/\+/g, '-')
1205			.replace(/\//g, '_');
1206	}
1207	_UTIL.base64_encode_safe = base64_encode_safe;
1208
1209	function base64_decode_safe(input) {
1210		/* Decodes input generated by "base64_encode_safe()". */
1211		return atob(input
1212			.replace(/=/g, '')
1213			.replace(/-/g, '+')
1214			.replace(/_/g, '/'));
1215	}
1216	_UTIL.base64_decode_safe = base64_decode_safe;
1217
1218	function object_to_query(object) {
1219		return "?"+Object.keys(object).map(function(key) {
1220			var safeKey = encodeURIComponent(key);
1221			var safeVal = encodeURIComponent(object[key]);
1222			return safeKey+"="+safeVal;
1223		}).join("&");
1224	}
1225	_UTIL.object_to_query = object_to_query;
1226
1227	function query_to_object(query) {
1228		if( query.startsWith("?") )
1229			query = query.slice(1);
1230		return query.split("&")
1231			.map(function(keyVal) {
1232				return keyVal.split("="); })
1233			.reduce(function(acc, element, index) {
1234				var key = decodeURIComponent(element.shift());
1235				var val = decodeURIComponent(element.shift());
1236				acc[key] = val;
1237				return acc; }, {});
1238	}
1239	_UTIL.query_to_object = query_to_object;
1240
1241	window.ERSPromise = (function() {
1242		//do done, fail, always
1243		function P() {
1244			this.doDone = function(){};
1245			this.doFail = function(){};
1246			this.doAlways = function(){};
1247		};
1248		P.prototype = {
1249			done : function(callback) {
1250				if( typeof callback !== "function" )
1251					throw new TypeError("ERSPromise.prototype.done expects function as 1st parameter, found \""+typeof callback+"\"");
1252				this.doDone = callback;
1253				this.runIfDone();
1254				return this;
1255			},
1256			fail : function(callback) {
1257				if( typeof callback !== "function" )
1258					throw new TypeError("ERSPromise.prototype.fail expects function as 1st parameter, found \""+typeof callback+"\"");
1259				this.doFail = callback;
1260				this.runIfDone();
1261				return this;
1262			},
1263			always : function(callback) {
1264				if( typeof callback !== "function" )
1265					throw new TypeError("ERSPromise.prototype.always expects function as 1st parameter, found \""+typeof callback+"\"");
1266				this.doAlways = callback;
1267				this.runIfDone();
1268				return this;
1269			},
1270			setResponse: function(data, success) {
1271				this.response = data;
1272				this.isDone = true;
1273				this.error = !success;
1274				this.runIfDone();
1275			},
1276			runIfDone: function() {
1277				if( this.isDone ) {
1278					this.doAlways(this.response);
1279					if( this.error ) {
1280						this.doFail(this.response);
1281					} else {
1282						this.doDone(this.response);
1283					}
1284				}
1285			}
1286
1287		};
1288		return P;
1289	})();
1290
1291	function safe_decode(input) {
1292		if( input )
1293			return JSON.parse(_UTIL.base64_decode_safe(input));
1294		return "";
1295	}
1296	_UTIL.safe_decode = safe_decode;
1297
1298	function safe_encode(input) {
1299		if( input )
1300			return _UTIL.base64_encode_safe(JSON.stringify(input));
1301		return "";
1302	}
1303	_UTIL.safe_encode = safe_encode;
1304
1305})();
1306
1307function decodeQueryString(str) {
1308	str = str.trim();
1309	return str.split("&").map(function(keyvalString) {
1310		if( keyvalString.trim() )
1311			return keyvalString.split("=");
1312		return false;
1313	}).filter(function(keyvalArray) {
1314		return keyvalArray !== false;
1315	}).reduce(function(acc,keyvalArray) {
1316		acc[unescape(keyvalArray[0])] = unescape(keyvalArray[1]);
1317		return acc;
1318	}, {});
1319}
1320
1321window.E = (function() {
1322
1323	"use strict";
1324
1325	function appendChildren(element, children) {
1326		(children||[]).forEach(function(child) {
1327			if( child ) element.appendChild(child);
1328		});
1329		return element;
1330	}
1331
1332	function setAttributes(element, obj) {
1333		for( var key in obj )
1334			if( typeof key === "string" && key !== "children" )
1335				element[key] = obj[key];
1336		return element;
1337	}
1338
1339	function newElement(tagname, attributes) {
1340		var elem = document.createElement(tagname);
1341		if( elem ) {
1342			setAttributes(elem, attributes);
1343			appendChildren(elem, attributes.children);
1344			return elem;
1345		}
1346		return null;
1347	};
1348
1349	return function validator(tagname, attributes) {
1350		if( typeof tagname !== "string" )
1351			throw new TypeError("newElement: 1st arg \"tagname\" should be \"string\", found \""+typeof tagname+"\".");
1352		if( ! attributes instanceof Object )
1353			throw new TypeError("newElement: 1st arg \"tagname\" should be instance of \"array\", found \""+typeof tagname+"\".");
1354		return newElement(tagname, attributes);
1355	}
1356
1357})();
1358
1359window.in_appcp = function(callback)
1360{
1361	var result = location.pathname.startsWith("/cp");
1362	if( callback instanceof Function ) callback( result );
1363	return result;
1364};
1365
1366function err(msg) { throw msg; }
1367
1368window.do_ajax_query = (function()
1369{
1370	function paramsToString(params)
1371	{
1372		return Object.keys(params).map(function(key) {
1373			return encodeURIComponent(key) + "=" + encodeURIComponent(params[key]);
1374		}).join("&");
1375	}
1376
1377	return function(params, callback, end_point)
1378	{
1379	    if (!end_point) end_point = "/ajax_query/";
1380
1381		var requestStr = paramsToString(params);
1382		var xhr = new XMLHttpRequest();
1383		xhr.open("POST", end_point, true);
1384		xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
1385		xhr.onload = function() {
1386			var response = decodeQueryString(xhr.responseText);
1387			callback(response);
1388		};
1389		xhr.send(requestStr);
1390	};
1391
1392}());
1393
1394(function() {
1395	var modal_container = null;
1396
1397	function closeModal()
1398	{
1399		if( modal_container && modal_container.parentNode )
1400			modal_container.parentNode.removeChild(modal_container);
1401	}
1402	window.closeModal = closeModal;
1403
1404	function createModal(contents, styles, can_clickout) {
1405		closeModal();
1406		can_clickout = can_clickout !== false;
1407		styles = styles || {};
1408
1409		var modal_content = document.createElement("span");
1410		modal_content.style.borderRadius = "5px";
1411		modal_content.style.backgroundColor = "#ddd";
1412		modal_content.style.verticalAlign = "middle";
1413		modal_content.style.display = "inline-block";
1414		modal_content.style.padding = "1em";
1415		modal_content.style.overflowX = "hidden";
1416		modal_content.style.overflowY = "hidden";
1417		modal_content.onclick = function(event) {
1418			event.stopPropagation();
1419		};
1420
1421		if( contents instanceof HTMLElement )
1422			modal_content.appendChild(contents);
1423		if( typeof contents === "string" )
1424			modal_content.appendChild(document.createTextNode(contents));
1425
1426		var modal_centerizer = document.createElement("span");
1427		modal_centerizer.style.verticalAlign = "middle";
1428		modal_centerizer.style.display = "inline-block";
1429		modal_centerizer.style.height = "100%";
1430		modal_centerizer.style.width = 0;
1431
1432		modal_container = document.createElement("div");
1433		modal_container.appendChild(modal_centerizer);
1434		modal_container.appendChild(modal_content);
1435		modal_container.style.backgroundColor = "rgba(0,0,0,0.9)";
1436		modal_container.style.textAlign = "center";
1437		modal_container.style.position = "fixed";
1438		modal_container.style.height = "100%";
1439		modal_container.style.zIndex = 12000;
1440		modal_container.style.width = "100%";
1441		modal_container.style.left = 0;
1442		modal_container.style.top = 0;
1443		modal_container.style.overflow = "scroll";
1444		modal_container.addEventListener("click", function(event) {
1445			event.stopPropagation();
1446			if( can_clickout ) {
1447				modal_container.parentNode.removeChild(modal_container);
1448			}
1449		});
1450		Object.keys(styles).forEach(function(style) {
1451			modal_content.style[style] = styles[style];
1452		});
1453		document.body.appendChild(modal_container);
1454		return modal_container;
1455	}
1456	window.createModal = createModal;
1457
1458	function modalPrompt(question, callback) {
1459		callback = callback instanceof Function ? callback : function(){};
1460
1461		var container_node = document.createElement("div");
1462
1463		var question_node = document.createElement("h4");
1464		question_node.innerHTML = question;
1465		container_node.appendChild(question_node);
1466
1467		var input_node = document.createElement("input");
1468		input_node.style.borderRadius = "5px";
1469		input_node.style.textIndent = "2em";
1470		input_node.style.border = "#ccc solid 1px";
1471		input_node.style.height = "3em";
1472		input_node.style.width = "90%";
1473		input_node.type = "text";
1474
1475		container_node.appendChild(document.createElement("br"));
1476		container_node.appendChild(input_node);
1477		container_node.appendChild(document.createElement("br"));
1478		container_node.appendChild(document.createElement("br"));
1479
1480		var button_no = document.createElement("button");
1481		button_no.className = "btn btn-info";
1482		button_no.innerHTML = "cancel";
1483		container_node.appendChild(button_no);
1484		button_no.onclick = function() {
1485			closeModal();
1486			callback(false);
1487		};
1488
1489		var button_ok = document.createElement("button");
1490		button_ok.className = "btn btn-success";
1491		button_ok.innerHTML = "OK";
1492		container_node.appendChild(button_ok);
1493		button_ok.onclick = function() {
1494			closeModal();
1495			callback(input_node.value);
1496		};
1497
1498		var modal_container = createModal(container_node, {'minWidth':'60%'}, true);
1499		modal_container.addEventListener('click', function(event) {
1500			event.stopPropagation();
1501			closeModal();
1502			callback(false);
1503		});
1504	}
1505	window.modalPrompt = modalPrompt;
1506
1507	function modalAlert(msg, callback) {
1508		callback = callback instanceof Function ? callback : function(){};
1509
1510		var container_node = document.createElement("div");
1511
1512		var msg_node = document.createElement("div");
1513		msg_node.innerHTML = msg;
1514		container_node.appendChild(msg_node);
1515
1516		var button_ok = document.createElement("button");
1517		button_ok.className = "btn btn-md";
1518		button_ok.innerHTML = "done";
1519		container_node.appendChild(button_ok);
1520		button_ok.onclick = function() {
1521			closeModal();
1522			callback();
1523		};
1524
1525		createModal(container_node, {'minWidth':'60%'}, true);
1526	}
1527	window.modalAlert = modalAlert;
1528
1529})();
1530
1531/**
1532 * For accordian icon toggling
1533 */
1534function toggleChevron(e)
1535{
1536	$(e.target)
1537		.prev('.panel-heading')
1538		.find("i.indicator")
1539		.toggleClass('glyphicon-chevron-down glyphicon-chevron-up');
1540}
1541
1542(function()
1543{
1544	var get_cache = null;
1545	var cookie_cache = null;
1546
1547	if ( ! ( "_GET" in window ) )
1548	{
1549		Object.defineProperties( window, {
1550			"_GET": {
1551				get : function() {
1552					if (get_cache === null) {
1553
1554						return ( get_cache = location.search
1555							.slice(1)
1556							.split("&")
1557							.map(function(e) {
1558								return e.split("=");
1559							})
1560							.reduce(function(acc,el) {
1561								acc[el[0]] = typeof el[1] !== "undefined" ? el[1] : null;
1562								return acc;
1563							}, {}));
1564
1565					}
1566					return get_cache;
1567
1568				}
1569			},
1570			"_COOKIE": {
1571
1572				get : function() {
1573					if (cookie_cache === null) {
1574
1575						return ( cookie_cache = document.cookie
1576							.slice(1)
1577							.split("; ")
1578							.map(function(e) {
1579								return e.split("=");
1580							})
1581							.reduce(function(acc,el) {
1582								acc[el[0]] = typeof el[1] !== "undefined" ? el[1] : null;
1583								return acc;
1584							}, {}));
1585					}
1586					return cookie_cache;
1587				}
1588			}
1589		});
1590	}
1591})();
1592
1593
1594function getvar( key, def )
1595{
1596	var val = _GET[key];
1597	if (typeof val === "undefined" || val === null)
1598	{
1599		return def;
1600	}
1601	return val;
1602}
1603
1604
1605function cookievar( key, def )
1606{
1607	var val = _COOKIE[key];
1608	if (typeof val === "undefined" || val === null)
1609	{
1610		return def;
1611	}
1612	return val;
1613}
1614
1615function ersCBC(eid) // checkbox checked
1616{
1617	var el = ersDE(eid);
1618	if (el && "checked" in el)
1619    {
1620        return el.checked;
1621    }
1622
1623	return false;
1624}
1625
1626function ersDE(eid) // document element
1627{
1628	return document.getElementById(eid);
1629}
1630
1631function ersEP(eid) // element parent
1632{
1633	var el = ersDE(eid);
1634	if (el)
1635	{
1636		return el.parentElement;
1637	}
1638
1639	return null;
1640}
1641
1642function ersDEBN(name, full_list) // document element(s) by name
1643{
1644	var els = document.getElementsByName(name);
1645
1646	if (NodeList.prototype.isPrototypeOf(els) && (els.length > 0))
1647	{
1648		if (full_list)
1649		{
1650			return els;
1651		}
1652
1653		return els[0];
1654	}
1655
1656	return null;
1657}
1658
1659function ersDEBC(cls) // document elements by class name
1660{
1661	return document.getElementsByClassName(cls);
1662}
1663
1664function ersElDisplay(el, display)
1665{
1666	if (el)
1667	{
1668		el.style.display = display;
1669	}
1670}
1671
1672function ersElHTML(el, html)
1673{
1674	if (el)
1675	{
1676		el.innerHTML = ersVS(html);
1677	}
1678}
1679function ersElOHTML(el, html)
1680{
1681	if (el)
1682	{
1683		el.outerHTML = ersVS(html);
1684	}
1685}
1686
1687function ersElOpac(el, opacity)
1688{
1689	if (el)
1690	{
1691		el.style.opacity = opacity;
1692	}
1693}
1694
1695function ersElPointerEvents(el, po)
1696{
1697	if (el)
1698	{
1699		el.style.pointerEvents = po;
1700	}
1701}
1702
1703function ersElValue(el, value)
1704{
1705	if (el)
1706	{
1707		el.value = value;
1708	}
1709}
1710
1711function ersElVisibility(el, vis)
1712{
1713	if (el)
1714	{
1715		el.style.visibility = vis;
1716	}
1717}
1718
1719function ersElsByClass(cl)
1720{
1721	return document.getElementsByClassName(cl);
1722}
1723
1724function ersEV(eid) // element value
1725{
1726	var el = ersDE(eid);
1727	if (el)
1728	{
1729		if ("value" in el)
1730		{
1731			return ersVS(el.value);
1732		}
1733
1734		if (el.dataset && el.dataset.value)
1735		{
1736			return ersVS(el.dataset.value);
1737		}
1738	}
1739
1740	return "";
1741}
1742
1743function ersFDE(iframe, eid) // iframe document element
1744{
1745	if (iframe && "contentWindow" in iframe)
1746	{
1747		return iframe.contentWindow.document.getElementById(eid);
1748	}
1749
1750	return null;
1751}
1752
1753function ersJSON(str)
1754{
1755	try
1756	{
1757		var o = JSON.parse(str);
1758		if (o && typeof o === "object" && o !== null)
1759		{
1760			return o;
1761		}
1762	}
1763	catch (e)
1764	{
1765		//console.log(e);
1766	}
1767
1768	return false;
1769}
1770
1771function ersObjectKeys(obj)
1772{
1773	var keys = [];
1774	if ((obj !== null) && (obj instanceof Object))
1775	{
1776		for (var k in obj)
1777		{
1778			keys.push(k);
1779		}
1780	}
1781
1782	return keys;
1783}
1784
1785function ersQSFO(obj) // query string from object
1786{
1787	var string = "";
1788
1789	if (obj instanceof Object)
1790	{
1791		arr = [];
1792		for (var k in obj)
1793		{
1794			arr.push( encodeURIComponent(k) + "=" + encodeURIComponent(obj[k]) );
1795		}
1796
1797		string = arr.join("&");
1798	}
1799
1800	return string;
1801}
1802
1803function ersSetSessvar(key, value)
1804{
1805	if (ersVS(key).length == 0)
1806	{
1807		return;
1808	}
1809
1810	var params = {
1811		cmd:	"set_sessvar",
1812		key:	key,
1813		value:	value
1814	};
1815
1816	do_ajax_query(params, function(response){});
1817}
1818
1819function ersVA(arr) // valid array
1820{
1821	if (arr === null) return [];
1822	if (typeof arr === "undefined") return [];
1823	if (arr.constructor !== Array) return [];
1824	return arr;
1825}
1826
1827function ersVAFA(arr, index) // valid array from array
1828{
1829	var array = ersVA(arr);
1830	if ((arr.length - 1) < ersVI(index)) return [];
1831	return ersVA(arr[index]);
1832}
1833
1834function ersVAFO(obj, key) // valid array from object
1835{
1836	if (typeof key !== "string") return [];
1837	var object = ersVO(obj);
1838	return ersVA(object[key]);
1839}
1840
1841function ersVI(integer, def) // valid integer
1842{
1843	if (!def)
1844	{
1845		def = 0;
1846	}
1847
1848	return parseInt(integer) || def;
1849}
1850
1851function ersVIFA(arr, index, def) // valid integer from array
1852{
1853	if (!def) def = 0;
1854	var array = ersVA(arr);
1855	if ((arr.length - 1) < ersVI(index)) return def;
1856	return ersVI(arr[index], def);
1857}
1858
1859function ersVIFO(obj, key, def) // valid integer from object
1860{
1861	if (!def) def = 0;
1862	if (typeof key !== "string") return def;
1863	var object = ersVO(obj);
1864	return ersVI(object[key], def);
1865}
1866
1867function ersVO(obj) // valid object
1868{
1869	if (obj === null) return {};
1870	if (typeof obj === "undefined") return {};
1871	if (obj.constructor !== Object) return {};
1872	return obj;
1873}
1874
1875function ersVOFA(arr, index) // valid object from array
1876{
1877	var array = ersVA(arr);
1878	if ((arr.length - 1) < ersVI(index)) return {};
1879	return ersVO(arr[index]);
1880}
1881
1882function ersVOFO(obj, key) // valid object from object
1883{
1884	if (typeof key !== "string") return {};
1885	var object = ersVO(obj);
1886	return ersVO(object[key]);
1887}
1888
1889function ersVS(str, def, convert) // valid string
1890{
1891	if (!def) def = "";
1892	if (str === null) return def;
1893	if (typeof str === "undefined") return def;
1894	if (convert) return str.toString();
1895	if (typeof str !== "string") return def;
1896	return str;
1897}
1898
1899function ersVSFA(arr, index, def) // valid string from array
1900{
1901	if (!def) def = "";
1902	var array = ersVA(arr);
1903	if ((arr.length - 1) < ersVI(index)) return def;
1904	return ersVS(arr[index]);
1905}
1906
1907function ersVSFO(obj, key, def) // valid string from object
1908{
1909	if (!def) def = "";
1910	if (typeof key !== "string") return def;
1911	var object = ersVO(obj);
1912	return ersVS(object[key], def, true);
1913}
1914
1915function fake_sleep(ms)
1916{
1917	var date = new Date();
1918	var curDate = null;
1919	do { curDate = new Date(); }
1920	while(curDate-date < ms);
1921}
1922
1923function ers_real_sleep(ms)
1924{
1925	return new Promise(resolve => setTimeout(resolve, ms));
1926}
1927
1928function ersAddClass(el, class_name, if_has_class) // if_has_class is optional
1929{
1930	if (!el)
1931	{
1932		return;
1933	}
1934
1935	var cl_na = ersVS(class_name);
1936	if (cl_na.length == 0)
1937	{
1938		return;
1939	}
1940
1941	var ihc = ersVS(if_has_class);
1942	if (ihc.length > 0)
1943	{
1944		if (!el.classList.contains(ihc))
1945		{
1946			return;
1947		}
1948	}
1949
1950	if (!el.classList.contains(cl_na))
1951	{
1952		el.classList.add(cl_na);
1953	}
1954}
1955
1956function ersRemoveClass(el, class_name)
1957{
1958	if (!el)
1959	{
1960		return;
1961	}
1962
1963	var cl_na = ersVS(class_name);
1964	if (cl_na.length == 0)
1965	{
1966		return;
1967	}
1968
1969	el.classList.remove(cl_na);
1970}
1971
1972function ersRemoveElement(el)
1973{
1974	if (!el)
1975	{
1976		return;
1977	}
1978
1979	var pn = el.parentNode;
1980	if (pn)
1981	{
1982		pn.removeChild(el);
1983	}
1984}
1985
1986function ersHasClass(el, cls)
1987{
1988	if (!el)
1989	{
1990		return false;
1991	}
1992
1993	return (" " + el.className + " ").indexOf(" " + cls + " ") > -1;
1994}
1995
1996function ersIsElement(obj)
1997{
1998	try
1999	{
2000		return obj instanceof HTMLElement;
2001	}
2002	catch(e)
2003	{
2004		return (typeof obj==="object")
2005		&& (obj.nodeType===1)
2006		&& (typeof obj.style === "object")
2007		&& (typeof obj.ownerDocument === "object");
2008	}
2009}
2010
2011function ersIsFunction(f)
2012{
2013	return (f && {}.toString.call(f) === "[object Function]");
2014}
2015
2016function ersRandom()
2017{
2018	return Math.floor((Math.random() * 1000000) + 1);
2019}
2020
2021var ersScheduledActions = [];
2022var ersScheduledActionTimer = null;
2023var ersRunningScheduledAction = false;
2024
2025function ersScheduleAction(action, delay)
2026{
2027	if (!delay)
2028	{
2029		delay = 0;
2030	}
2031
2032	if (delay == 0)
2033	{
2034		ersScheduledActions.push(action);
2035	}
2036	else
2037	{
2038		window.setTimeout(function(){ ersScheduledActions.push(action) }, delay);
2039	}
2040
2041	if (ersScheduledActionTimer == null)
2042	{
2043		ersScheduledActionTimer = setInterval(function() { ersRunNextScheduledAction() }, 99);
2044	}
2045}
2046
2047function ersRunNextScheduledAction()
2048{
2049	if (ersScheduledActions.length == 0)
2050	{
2051		return;
2052	}
2053
2054	if (ersRunningScheduledAction)
2055	{
2056		return;
2057	}
2058
2059	ersRunningScheduledAction = true;
2060
2061	var this_action = ersScheduledActions[0];
2062
2063	ersScheduledActions.splice(0, 1);
2064
2065	this_action();
2066
2067	ersRunningScheduledAction = false;
2068
2069	if (ersScheduledActions.length == 0)
2070	{
2071		clearInterval(ersScheduledActionTimer);
2072		ersScheduledActionTimer = null;
2073	}
2074}
2075
2076var ERSPreloadedImages = {};
2077
2078function ersPreloadImages(preload_list)
2079{
2080	if (preload_list == null)
2081	{
2082		return;
2083	}
2084
2085	if (preload_list instanceof Array)
2086	{
2087		for (p = 0; p < preload_list.length; p++)
2088		{
2089			ersPreloadImage(preload_list[p]);
2090		}
2091	}
2092}
2093
2094function ersPreloadImage(image_path)
2095{
2096	var check_path = ersVS(image_path);
2097	if (check_path.length == 0)
2098	{
2099		return;
2100	}
2101
2102	if (ERSPreloadedImages.hasOwnProperty(check_path))
2103	{
2104		return;
2105	}
2106
2107	var image = new Image();
2108	image.onload = function(){ ERSPreloadedImages[check_path] = this; };
2109	image.src = check_path;
2110}
2111
2112function ERSClickOrTouch()
2113{
2114	return CM_use_touch?"ontouchstart":"onclick";
2115}
2116
2117function html_to_dom(str) {
2118	const whole_doc = "<html><head></head><body>" + str + "</body></html>";
2119	parser = new DOMParser();
2120	doc = parser.parseFromString(whole_doc, "text/html");
2121	return doc.body.children[0];
2122}
2123
2124/*
2125function add_class(node, className) {
2126	const new_cnames = className.split(" ").filter(c=>c.trim()).filter(Boolean);
2127	const old_cnames = node.className.split(" ").filter(c=>c.trim()).filter(Boolean);
2128	for (const cname of new_cnames) {
2129		if (old_cnames.includes(cname)) {
2130			continue;
2131		}
2132		old_cnames.push(cname);
2133	}
2134	node.className = old_cnames.join(" ");
2135}
2136
2137function rm_class(node, className) {
2138	const new_cnames = className.split(" ").filter(c=>c.trim()).filter(Boolean);
2139	const old_cnames = node.className.split(" ").filter(c=>c.trim()).filter(Boolean);
2140	node.className = old_cnames.filter(cname => new_cnames.includes(cname) === false).join(" ");
2141}
2142
2143(function() {
2144
2145	function diff_dom(root, newstr) {
2146		const frag = html_to_dom(newstr);
2147		diff(root, frag);
2148	}
2149
2150	function diff(rootDOM, newDOM) {
2151		if (newDOM instanceof HTMLElement && rootDOM instanceof HTMLElement ||
2152			newDOM instanceof Text && rootDOM instanceof Text) {
2153
2154			// if same types, we can diff
2155			if (newDOM instanceof HTMLElement) {
2156				if (newDOM.tagName === rootDOM.tagName) {
2157					diff_html_node(rootDOM, newDOM);
2158				} else {
2159					rootDOM.parentNode.replaceChild(newDOM, rootDOM);
2160				}
2161			} else if (newDOM instanceof Text) {
2162				diff_text(rootDOM, newDOM);
2163			} else {
2164				throw new TypeError("No way to diff element: ", newDOM);
2165			}
2166		} else {
2167			// if they're different types, just replace whole branch
2168			rootDOM.parentNode.replaceChild(newDOM, rootDOM);
2169		}
2170	}
2171
2172	function diff_text(rootDOM, newDOM) {
2173		rootDOM.textContent = newDOM.textContent;
2174	}
2175
2176	function diff_html_node(rootDOM, newDOM) {
2177		diff_attributes(rootDOM, newDOM);
2178		diff_children(rootDOM, newDOM);
2179	}
2180
2181	function diff_attributes(rootDOM, newDOM) {
2182		// delete old attrs //
2183		for (const attr_name of rootDOM.getAttributeNames()) {
2184			if (!newDOM.hasAttribute(attr_name)) {
2185				rootDOM.removeAttribute(attr_name);
2186			}
2187		}
2188
2189		// add/set other attrs //
2190		for (const attr_name of newDOM.getAttributeNames()) {
2191			if (rootDOM[attr_name] !== newDOM[attr_name]) {
2192				if (attr_name.slice(0, 2) === "on") {
2193					rootDOM[attr_name] = new Function(newDOM.getAttribute(attr_name));
2194				} else {
2195					rootDOM[attr_name] = newDOM.getAttribute(attr_name);
2196				}
2197			}
2198		}
2199	}
2200
2201	function diff_children(rootDOM, newDOM) {
2202		const old_length = rootDOM.childNodes.length;
2203		const new_length = newDOM.childNodes.length;
2204		const least_length = Math.min(old_length, new_length);
2205		const greatest_length = Math.max(old_length, new_length);
2206		const deletables = [];
2207		const appendables = [];
2208
2209		for (let i = greatest_length-1; i >= 0; i--) {
2210			if (i < least_length) {
2211				// diff existing child
2212				const curr_child = rootDOM.childNodes[i];
2213				const next_child = newDOM.childNodes[i];
2214				diff(curr_child, next_child);
2215				//diff(rootDOM.childNodes[i], newDOM.childNodes[i]);
2216			} else {
2217				// remove / add excess child
2218				if (old_length > new_length) {
2219					deletables.push(rootDOM.childNodes[i]);
2220				} else {
2221					appendables.push(newDOM.childNodes[i]);
2222				}
2223			}
2224		}
2225
2226		// delete
2227		for (const node of deletables) {
2228			node.parentNode.removeChild(node);
2229		}
2230		// add
2231		for (const node of appendables) {
2232			rootDOM.appendChild(node);
2233		}
2234	}
2235
2236	window.diff_dom = diff_dom;
2237})();*/
2238
2239function strip_html_from(text)
2240{
2241	return text.replace(/(<([^>]+)>)/gi, "");
2242}
2243
2244function ampersand_decode(text)
2245{
2246	var ta = document.createElement("textarea");
2247	ta.innerHTML = text;
2248	return ta.value;
2249}
2250
2251function encode_notes_text(text)
2252{
2253	return strip_html_from(text);
2254}
2255
2256function decoded_notes_text(el)
2257{
2258	var html = ampersand_decode(el.innerHTML).replace("<br>", "");
2259
2260	if (strip_html_from(html).trim() == "")
2261	{
2262		return "";
2263	}
2264
2265	var regexp1 = new RegExp("</div><div>", "ig");
2266	var regexp2 = new RegExp("<div>", "ig");
2267
2268	html = html.replace(regexp1, "\r\n");
2269	if (html.substring(0, 5) == "<div>")
2270	{
2271		html = html.substring(5);
2272	}
2273	html = html.replace(regexp2, "\r\n");
2274
2275	return strip_html_from(html).trim();
2276}
2277
2278function ersDisplayMoreBelow(el)
2279{
2280	window.setTimeout(function()
2281	{
2282		ersElDisplay(el, "none");
2283		ersElDisplay(el.nextElementSibling, "block");
2284	}, 100);
2285}
2286
2287function ersDisplayLessBelow(el)
2288{
2289	window.setTimeout(function()
2290	{
2291		var hide_div = el.parentElement;
2292		if (hide_div)
2293		{
2294			ersElDisplay(hide_div, "none");
2295			ersElDisplay(hide_div.previousElementSibling, "inline-block");
2296		}
2297	}, 100);
2298}
2299
2300function ersValueTrue(val)
2301{
2302	var str = ((ersVS(val, "", true).toLowerCase()).trim()).substr(0, 1);
2303
2304	return (val===true || str=="y" || str=="t" || str=="1");
2305}
2306
2307function ersInlinePropStop()
2308{
2309	var event = arguments[0] || window.event;
2310	if (event)
2311	{
2312		if (event.stopPropagation)
2313		{
2314			event.stopPropagation();
2315		}
2316		else
2317		{
2318			event.cancelBubble = true;
2319		}
2320	}
2321}
2322
2323function ersTSVDownload(filename, tsv_text)
2324{
2325	var edll = ersDE("ERSExportDownloadLink");
2326	if (!edll)
2327	{
2328		// create on the fly?
2329	}
2330	if (edll)
2331	{
2332		edll.href = "data:text/tab-separated-values;charset=utf-8," + encodeURIComponent(tsv_text);
2333        // 2023-02-06 JML - changed encodeURI to encodeURIComponent to fix partial tsv export problem.
2334		edll.download = filename + ".tsv";
2335		edll.click();
2336	}
2337}
2338
2339function wm_title_to_id(tit)
2340{
2341	var replaces = { ' ': "_", '/': "-", ':': "_", "'": "", ',': "" };
2342
2343	return tit.replace(/ |\/|:|'|,/gi, function(matched) { return replaces[matched]; }).toLowerCase();
2344}
2345
2346function ersPostRequestToScript(req_obj, script, new_window)
2347{
2348	var form = document.createElement("form");
2349	form.setAttribute("method", "post");
2350	form.setAttribute("action", script);
2351	if (new_window)
2352	{
2353		form.setAttribute("target", "_blank");
2354	}
2355
2356	for (var key in req_obj)
2357	{
2358		if (req_obj.hasOwnProperty(key))
2359		{
2360			var value = req_obj[key];
2361			var value_string = "";
2362
2363			if ((value.constructor === Array) || (value.constructor === Object))
2364			{
2365				value_string = JSON.stringify(value);
2366			}
2367			else
2368			{
2369				value_string = value.toString();
2370			}
2371
2372			var hiddenField = document.createElement("input");
2373			hiddenField.setAttribute("type", "hidden");
2374			hiddenField.setAttribute("name", key);
2375			hiddenField.setAttribute("value", value_string);
2376
2377			form.appendChild(hiddenField);
2378		}
2379	}
2380
2381	document.body.appendChild(form);
2382	form.submit();
2383
2384	return form;
2385}
2386
2387function capitalizeWords(str) {
2388    return str
2389        .split(" ")
2390        .map(word => word.charAt(0).toUpperCase() + word.slice(1))
2391        .join(" ");
2392}
2393
2394function documentReady(callback) {
2395	if (document.readyState !== "loading") {
2396		callback();
2397	} else {
2398		document.addEventListener("DOMContentLoaded", callback);
2399	}
2400}

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.