PageSourceSearch

https://www.fernstudi.net/media/com_hikashop/js/hikashop.js?v=6602609231355

js fernstudi.net collected 2026-10-02 06:36:51 UTC 81,987 bytes, 2,504 lines download raw bytes

1/**
2 * @package    HikaShop
3 * @version    6.6.0
4 * @author     hikashop.com
5 * @copyright  (C) 2010-2026 HIKARI SOFTWARE. All rights reserved.
6 * @license    GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
7 */
8(function() {
9
10function preventDefault() { this.returnValue = false; }
11function stopPropagation() { this.cancelBubble = true; }
12
13var Oby = {
14	version: 20171104,
15	ajaxEvents : {},
16
17	hasClass: function(o,n) {
18		if(o.classList && o.classList.contains)
19			return o.classList.contains(n);
20		if(o.className == '' ) return false;
21		var reg = new RegExp("(^|\\s+)"+n+"(\\s+|$)");
22		return reg.test(o.className);
23	},
24	addClass: function(o,n) {
25		if(o.classList && o.classList.add)
26			return o.classList.add(n);
27		if( !this.hasClass(o,n) ) {
28			if( o.className == '' ) {
29				o.className = n;
30			} else {
31				o.className += ' '+n;
32			}
33		}
34	},
35	trim: function(s) {
36		if(s.trim) return s.trim();
37		return (s ? '' + s : '').replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
38	},
39	removeClass: function(e, c) {
40		if(e.classList && e.classList.remove)
41			return e.classList.remove(c);
42		var t = this;
43		if( t.hasClass(e,c) ) {
44			var cn = ' ' + e.className + ' ';
45			e.className = t.trim(cn.replace(' '+c+' ',' '));
46		}
47	},
48	toggleClass: function(e,c) {
49		if(e.classList && e.classList.toggle)
50			return e.classList.toggle(c);
51		var t = this;
52		if( t.hasClass(e,c) ) {
53			return t.removeClass(e,c);
54		}
55		return t.addClass(e,c);
56	},
57	addEvent: function(d,e,f) {
58		if( d.attachEvent )
59			d.attachEvent('on' + e, f);
60		else if (d.addEventListener)
61			d.addEventListener(e, f, false);
62		else
63			d['on' + e] = f;
64		return f;
65	},
66	removeEvent: function(d,e,f) {
67		try {
68			if( d.detachEvent )
69				d.detachEvent('on' + e, f);
70			else if( d.removeEventListener)
71				d.removeEventListener(e, f, false);
72			else
73				d['on' + e] = null;
74		} catch(e) {}
75	},
76	cancelEvent: function(e) {
77		if( !e ) {
78			e = window.event;
79			if( !e )
80				return false;
81		}
82		if(e.stopPropagation)
83			e.stopPropagation();
84		else
85			 e.cancelBubble = true;
86		if( e.preventDefault )
87			e.preventDefault();
88		else
89			e.returnValue = false;
90		return false;
91	},
92	fireEvent: function(obj,e,data) {
93		var d = document, evt = null;
94		if(document.createEvent) {
95			evt = d.createEvent('HTMLEvents');
96			evt.initEvent(e, false, true);
97			if(data) evt.data = data;
98			obj.dispatchEvent(evt);
99			return;
100		}
101		if(data && d.createEventObject) {
102			evt = d.createEventObject();
103			evt.data = data;
104			obj.fireEvent('on'+e, evt);
105			return;
106		}
107		obj.fireEvent('on'+e);
108	},
109	fireAjax: function(name,params) {
110		var t = this, ev, r = null, ret = [];
111		if( t.ajaxEvents[name] === undefined )
112			return false;
113		for(var e in t.ajaxEvents[name]) {
114			if( e == '_id' )
115				continue;
116			ev = t.ajaxEvents[name][e];
117			if(!ev || typeof(ev) != 'function')
118				continue;
119			try {
120				r = ev(params);
121				if(r !== undefined)
122					ret.push(r);
123			}catch(e){}
124		}
125		return ret;
126	},
127	registerAjax: function(name, fct) {
128		var t = this;
129		if(typeof(name) == 'object') {
130			var r = [];
131			for(var k = name.length - 1; k >= 0; k--) {
132				r[r.length] = t.registerAjax(name[k], fct);
133			}
134			return r;
135		}
136		if( t.ajaxEvents[name] === undefined )
137			t.ajaxEvents[name] = {'_id':0};
138		var id = t.ajaxEvents[name]['_id'];
139		t.ajaxEvents[name]['_id'] += 1;
140		t.ajaxEvents[name][id] = fct;
141		return id;
142	},
143	unregisterAjax: function(name, id) {
144		if( t.ajaxEvents[name] === undefined || t.ajaxEvents[name][id] === undefined)
145			return false;
146		t.ajaxEvents[name][id] = null;
147		return true;
148	},
149	ready: function(fct) {
150		var w = window, d = document, t = this;
151		if(d.readyState === "complete") {
152			fct();
153			return;
154		}
155		var done = false, top = true, root = d.documentElement,
156			init = function(e) {
157				if(e.type == 'readystatechange' && d.readyState != 'complete') return;
158				t.removeEvent((e.type == 'load' ? w : d), e.type, init);
159				if(!done && (done = true))
160					fct();
161			},
162			poll = function() {
163				try{ root.doScroll('left'); } catch(e){ setTimeout(poll, 50); return; }
164				init('poll');
165			};
166		if(d.createEventObject && root.doScroll) {
167			try{ top = !w.frameElement; } catch(e){}
168			if(top) poll();
169		}
170		t.addEvent(d,'DOMContentLoaded',init);
171		t.addEvent(d,'readystatechange',init);
172		t.addEvent(w,'load',init);
173	},
174	evalJSON: function(text, secure) {
175		if( typeof(text) != "string" || !text.length) return null;
176		if(JSON !== undefined && typeof(JSON.parse) == 'function') {
177			try { var ret = JSON.parse(text); return ret; } catch(e) {
178				return this.extractJSON(text);
179			}
180		}
181		if(secure && !(/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(text.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''))) return null;
182		try { var ret = eval('(' + text + ')'); return ret; } catch(e) { }
183		return null;
184	},
185	extractJSON: function(str) {
186		var firstOpen, firstClose, candidate;
187		firstOpen = str.indexOf('{', firstOpen + 1);
188		do {
189			firstClose = str.lastIndexOf('}');
190			if(firstClose <= firstOpen) {
191				return null;
192			}
193			do {
194				candidate = str.substring(firstOpen, firstClose + 1);
195				try {
196					var res = JSON.parse(candidate);
197					return res;
198				}
199				catch(e) {
200					console.log('failed parsing JSON in string below:');
201					console.log(candidate);
202				}
203				firstClose = str.substr(0, firstClose).lastIndexOf('}');
204			} while(firstClose > firstOpen);
205			firstOpen = str.indexOf('{', firstOpen + 1);
206		} while(firstOpen != -1);
207		return null;
208	},
209	getXHR: function() {
210		var xhr = null, w = window;
211		if(w.XMLHttpRequest || w.ActiveXObject) {
212			if(w.ActiveXObject) {
213				try {
214					xhr = new ActiveXObject("Microsoft.XMLHTTP");
215				} catch(e) {}
216			} else
217				xhr = new w.XMLHttpRequest();
218		}
219		return xhr;
220	},
221	xRequest: function(url, options, cb, cbError) {
222		var t = this, xhr = t.getXHR();
223		if(!options) options = {};
224		if(!cb) cb = function(){};
225		options.mode = options.mode || 'GET';
226		options.update = options.update || false;
227		xhr.onreadystatechange = function() {
228			if(xhr.readyState != 4)
229				return;
230			if( xhr.status == 200 || (xhr.status == 0 && xhr.responseText > 0) || !cbError ) {
231				if(cb)
232					cb(xhr,options.params);
233				if(options.update)
234					t.updateElem(options.update, xhr.responseText);
235			} else {
236				cbError(xhr,options.params);
237			}
238		};
239		xhr.open(options.mode, url, true);
240		if(options.mode.toUpperCase() == 'POST' && typeof(options.data) == 'string') {
241			xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
242		}
243		if(typeof Joomla !== 'undefined' && Joomla && Joomla.getOptions) {
244			var csrf = Joomla.getOptions("csrf.token", "");
245			if(csrf) {
246				xhr.setRequestHeader("X-CSRF-Token", csrf);
247			}
248		}
249		xhr.send( options.data );
250	},
251	getFormData: function(target, encoded) {
252		var d = document, ret = {};
253		if(encoded === undefined) encoded = true;
254		if( typeof(target) == 'string' )
255			target = d.getElementById(target);
256		if( target === undefined )
257			target = d;
258		var typelist = ['input','select','textarea'];
259		for(var t in typelist) {
260			if(!typelist.hasOwnProperty(t))
261				continue;
262			t = typelist[t];
263			var inputs = target.getElementsByTagName(t);
264			for(var i = 0; i < inputs.length; i++) {
265				if( !inputs[i].name || inputs[i].disabled )
266					continue;
267				var evalue = inputs[i].value, n = inputs[i].name, etype = '';
268				if( t == 'input' )
269					etype = inputs[i].type.toLowerCase();
270				if( (etype == 'radio' || etype == 'checkbox') && !inputs[i].checked )
271					evalue = null;
272				if(t == 'select' && inputs[i].multiple) {
273					for(var k = inputs[i].options.length - 1; k >= 0; k--) {
274						if(!inputs[i].options[k].selected)
275							continue;
276						//if( ret != '' ) ret += '&';
277						//ret += encodeURI(inputs[i].name) + '=' + encodeURIComponent(inputs[i].options[k].value);
278						if(ret.hasOwnProperty(n)) {
279							if(typeof(ret[n]) != 'object')
280								ret[n] = [ ret[n] ];
281							ret[n][ ret[n].length ] = inputs[i].options[k].value;
282						} else
283							ret[ n ] = inputs[i].options[k].value;
284						evalue = null;
285					}
286				}
287				if( (etype != 'file' && etype != 'submit') && evalue != null ) {
288					//if( ret != '' ) ret += '&';
289					//ret += encodeURI(inputs[i].name) + '=' + encodeURIComponent(evalue);
290
291					if(ret.hasOwnProperty(n)) {
292						if(typeof(ret[n]) != 'object')
293							ret[n] = [ ret[n] ];
294						ret[n][ ret[n].length ] = evalue;
295					} else
296						ret[ n ] = evalue;
297				}
298			}
299		}
300		// Collect orphaned inputs associated with the form via the HTML form attribute
301		// (these are outside the form element in the DOM but linked to it by form="formId")
302		if(target && target.id) {
303			var scope = target.closest('.hikashop_product_page') || target.parentNode;
304			var orphanSelector = 'input[form="'+target.id+'"], select[form="'+target.id+'"], textarea[form="'+target.id+'"]';
305			var orphans = scope.querySelectorAll(orphanSelector);
306			for(var oi = 0; oi < orphans.length; oi++) {
307				if(!orphans[oi].name || orphans[oi].disabled)
308					continue;
309				var n = orphans[oi].name;
310				if(ret.hasOwnProperty(n))
311					continue;
312				var etype = orphans[oi].tagName.toLowerCase() == 'input' ? orphans[oi].type.toLowerCase() : '';
313				if(etype == 'file' || etype == 'submit')
314					continue;
315				if((etype == 'radio' || etype == 'checkbox') && !orphans[oi].checked)
316					continue;
317				ret[n] = orphans[oi].value;
318			}
319		}
320		if(encoded)
321			return this.encodeFormData(ret);
322		return ret;
323	},
324	encodeFormData: function(data) {
325		var ret = '', v = null;
326		if(typeof(data) == "string")
327			return data;
328		for(var k in data) {
329			if(!data.hasOwnProperty(k))
330				continue;
331			v = data[k];
332			if(typeof(v) == 'object') {
333				for(var i in v) {
334					if(!v.hasOwnProperty(i))
335						continue;
336					if( ret != '' ) ret += '&';
337					ret += encodeURI(k) + '=' + encodeURIComponent(v[i]);
338				}
339			} else {
340				if( ret != '' ) ret += '&';
341				ret += encodeURI(k) + '=' + encodeURIComponent(v);
342			}
343		}
344		return ret;
345	},
346	updateElem: function(elem, data) {
347		var d = document, scripts = [];
348		if( typeof(elem) == 'string' )
349			elem = d.getElementById(elem);
350		var text = data.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function(all, code){
351			if(all.indexOf('type="application/json"')>=0 || all.indexOf('type="application/ld+json"')>=0)
352				return all;
353			scripts.push(code);
354			return '';
355		});
356		elem.innerHTML = text;
357		if( scripts.length ) {
358			scripts.forEach(function(code){
359				var script = d.createElement('script');
360				script.setAttribute('type', 'text/javascript');
361				script.text = code;
362				try {
363					d.head.appendChild(script);
364					d.head.removeChild(script);
365				} catch(e) {}
366			});
367		}
368	},
369	ease: function(v) {
370		return 1+Math.pow(v-1,3);
371	},
372	easeInOut: function(t,s,dt,du) {
373		return dt/2 * (1 - Math.cos(Math.PI*t/du)) + s;
374	},
375	scrollTo: function(name, anim, visible, margin) {
376		var t = this, d = document, w = window,
377			elem = d.getElementById(name);
378		if(!elem)
379			return;
380		if(margin === undefined) margin = 0;
381		if(!anim) {
382			window.scrollTo(0, elem.offsetTop - margin);
383			return;
384		}
385		if( t.anim && t.anim.timer )
386			clearInterval( t.anim.timer );
387		t.anim = { timer:null, s:null, dt:0, du:150, t:0, inc:10 };
388		if( w.scrollY )
389			t.anim.s = w.scrollY;
390		else if( d.documentElement.scrollTop )
391			t.anim.s = d.documentElement.scrollTop;
392		else
393			t.anim.s = d.body.scrollTop;
394		if(visible) {
395			if( d.documentElement.scrollTop )
396				h = d.documentElement.clientHeight;
397			else
398				h = d.body.clientHeight;
399			if(t.anim.s <= elem.offsetTop && (t.anim.s + h - 150) > elem.offsetTop)
400				return;
401		}
402		t.anim.dt = elem.offsetTop - t.anim.s - margin;
403		var o = this;
404		t.anim.timer = setInterval( function() {
405			var a = o.anim;
406			if( !a || !a.timer )
407				return;
408			a.t += a.inc;
409			if( a.t < a.du ) {
410				window.scrollTo(0, o.easeInOut(a.t, a.s, a.dt, a.du), false, margin);
411			} else {
412				window.scrollTo(0, a.s + a.dt, false, margin);
413				clearInterval(a.timer);
414				a.timer = null;
415			}
416		}, t.anim.inc );
417	}
418};
419if((typeof(window.Oby) == 'undefined') || window.Oby.version < Oby.version) {
420	window.Oby = Oby;
421	window.obscurelighty = Oby;
422}
423
424var oldHikaShop = window.hikashop || hikashop;
425
426var hikashop = {
427	translations: {},
428	translations_url: null,
429	submitFct: null,
430	filterRefreshTimer: false,
431
432	// Picks the form of a count known only in the browser, from the object hikashop_pluralForms()
433	// builds. The CLDR categories come from the browser, which always has them.
434	plural: function(forms, count) {
435		var category = 'other';
436		try {
437			category = new Intl.PluralRules(forms.lang || 'en').select(count);
438		} catch(e) {}
439		var text = (forms[category] !== undefined) ? forms[category] : forms.other;
440		return String(text).replace('%s', count).replace('%d', count);
441	},
442
443	setCookie: function (name,value,delay) {
444		document.cookie = name + "=" + (value || "")  +  "; expires=" + delay + "; path=/";
445   	},
446	submitBox: function(data) {
447		var t = this, d = document, w = window;
448		if( t.submitFct ) {
449			try {
450				t.submitFct(data);
451			} catch(err) {}
452		}
453		t.closeBox();
454	},
455	deleteId: function(id) {
456		var t = this, d = document, el = id;
457		if( typeof(id) == "string") {
458			el = d.getElementById(id);
459		}
460		if(!el)
461			return;
462		el.parentNode.removeChild(el);
463	},
464	dup: function(tplName, htmlblocks, id, extraData, appendTo) {
465		var d = document, tplElem = d.getElementById(tplName);
466		if(!tplElem) return;
467		var container = tplElem.parentNode;
468		elem = tplElem.cloneNode(true);
469		if(!appendTo) {
470			container.insertBefore(elem, tplElem);
471		} else {
472			if(typeof(appendTo) == "string")
473				appendTo = d.getElementById(appendTo);
474			appendTo.appendChild(elem);
475		}
476		elem.style.display = "";
477		elem.id = '';
478		if(id)
479			elem.id = id;
480		for(var k in htmlblocks) {
481			elem.innerHTML = elem.innerHTML.replace(new RegExp("{"+k+"}","g"), htmlblocks[k]);
482			elem.innerHTML = elem.innerHTML.replace(new RegExp("%7B"+k+"%7D","g"), htmlblocks[k]);
483		}
484		if(extraData) {
485			for(var k in extraData) {
486				elem.innerHTML = elem.innerHTML.replace(new RegExp('{'+k+'}','g'), extraData[k]);
487				elem.innerHTML = elem.innerHTML.replace(new RegExp('%7B'+k+'%7D','g'), extraData[k]);
488			}
489		}
490		return elem;
491	},
492	deleteRow: function(id) {
493		var t = this, d = document, el = id;
494		if( typeof(id) == "string") {
495			el = d.getElementById(id);
496		} else {
497			while(el != null && el.tagName.toLowerCase() != 'tr') {
498				el = el.parentNode;
499			}
500		}
501		if(!el)
502			return;
503		var table = el.parentNode;
504		table.removeChild(el);
505		if( table.tagName.toLowerCase() == 'tbody' )
506			table = table.parentNode;
507		t.cleanTableRows(table);
508		return;
509	},
510	dupRow: function(tplName, htmlblocks, id, extraData) {
511		var d = document, tplLine = d.getElementById(tplName),
512				tableUser = tplLine.parentNode;
513		if(!tplLine) return;
514		trLine = tplLine.cloneNode(true);
515		tableUser.insertBefore(trLine, tplLine);
516		trLine.style.display = "";
517		trLine.id = "";
518		if(id)
519			trLine.id = id;
520		for(var i = tplLine.cells.length - 1; i >= 0; i--) {
521			if(trLine.cells[i]) {
522				for(var k in htmlblocks) {
523					if(!htmlblocks.hasOwnProperty(k))
524						continue;
525					trLine.cells[i].innerHTML = trLine.cells[i].innerHTML.replace(new RegExp("{"+k+"}","g"), htmlblocks[k]);
526					trLine.cells[i].innerHTML = trLine.cells[i].innerHTML.replace(new RegExp("%7B"+k+"%7D","g"), htmlblocks[k]);
527				}
528				if(extraData) {
529					for(var k in extraData) {
530						if(!extraData.hasOwnProperty(k))
531							continue;
532						trLine.cells[i].innerHTML = trLine.cells[i].innerHTML.replace(new RegExp('{'+k+'}','g'), extraData[k]);
533						trLine.cells[i].innerHTML = trLine.cells[i].innerHTML.replace(new RegExp('%7B'+k+'%7D','g'), extraData[k]);
534					}
535				}
536			}
537		}
538		if(tplLine.className == "row0") tplLine.className = "row1";
539		else if(tplLine.className == "row1") tplLine.className = "row0";
540		return trLine;
541	},
542	cleanTableRows: function(id) {
543		var d = document, el = id;
544		if(typeof(id) == "string")
545			el = d.getElementById(id);
546		if(el == null || el.tagName.toLowerCase() != 'table')
547			return;
548
549		var k = 0, c = '', line = null, lines = el.getElementsByTagName('tr');
550		for(var i = 0; i < lines.length; i++) {
551			line = lines[i];
552			if( line.style.display != "none") {
553				c = ' '+line.className+' ';
554				if( c.indexOf(' row0 ') >= 0 || c.indexOf(' row1 ') >= 0 ) {
555					line.className = c.replace(' row'+(1-k)+' ', ' row'+k+' ').replace(/^\s*|\s*$/g, '');
556					k = 1 - k;
557				}
558			}
559		}
560	},
561	checkRow: function(id) {
562		var t = this, d = document, el = id;
563		if(typeof(id) == "string")
564			el = d.getElementById(id);
565		if(el == null || el.tagName.toLowerCase() != 'input')
566			return;
567		if(this.clicked) {
568			this.clicked = null;
569			t.isChecked(el);
570			return;
571		}
572		el.checked = !el.checked;
573		t.isChecked(el);
574	},
575	isChecked: function(id,cancel) {
576		var d = document, el = id;
577		if(typeof(id) == "string")
578			el = d.getElementById(id);
579		if(el == null || el.tagName.toLowerCase() != 'input')
580			return;
581		if(el.form.boxchecked) {
582			if(el.checked)
583				el.form.boxchecked.value++;
584			else
585				el.form.boxchecked.value--;
586		}
587	},
588	checkAll: function(checkbox, stub) {
589		stub = stub || 'cb';
590		if(!checkbox.form)
591			return false;
592		var o = window.Oby, cb = checkbox.form, c = 0;
593		for(var i = 0, n = cb.elements.length; i < n; i++) {
594			var e = cb.elements[i];
595			if (e != checkbox && e.type == checkbox.type && ((stub && e.id.indexOf(stub) == 0) || !stub)) {
596				e.checked = checkbox.checked;
597				o.fireEvent(e, 'change');
598				o.fireEvent(e, 'click');
599				c += (e.checked == true ? 1 : 0);
600			}
601		}
602		if (cb.boxchecked) {
603			cb.boxchecked.value = c;
604		}
605		return true;
606	},
607	submitform: function(task, form, extra) {
608		var d = document;
609		if(typeof form == 'string') {
610			var f = d.getElementById(form);
611			if(!f)
612				f = d.forms[form];
613			if(!f)
614				return true;
615			form = f;
616		}
617		if(task) {
618			form.task.value = task;
619		}
620		if(typeof form.onsubmit == 'function')
621			form.onsubmit();
622		form.submit();
623		return false;
624	},
625	get: function(elem, target) {
626		window.hikashop.xRequest(elem.getAttribute('href'), {update: target});
627		return false;
628	},
629	form: function(elem, target) {
630		var data = window.Oby.getFormData(target);
631		window.hikashop.xRequest(elem.getAttribute('href'), {update: target, mode: 'POST', data: data});
632		return false;
633	},
634	openBox: function(elem, url, jqmodal) {
635		var w = window;
636		if(typeof(elem) == "string")
637			elem = document.getElementById(elem);
638		if(!elem)
639			return false;
640		try {
641			var hkpopup = elem.getAttribute('data-hk-popup');
642			if(jqmodal === undefined) {
643				jqmodal = false;
644				var test_rel = elem.getAttribute('rel');
645				if(test_rel == null && hkpopup == null && typeof(jQuery) != "undefined")
646					jqmodal = true;
647			}
648			if(hkpopup) {
649				var fct = this['openBox_' + hkpopup.toLowerCase()];
650				if(fct) {
651					var ret = fct(elem, url);
652					if(ret == true)
653						return false;
654				}
655			}
656			if(!jqmodal && this.openBox_squeezebox(elem, url))
657				return false;
658			if(this.openBox_bootstrap(elem, url))
659				return false;
660			console.log('no popup system found');
661		} catch(e) { console.log(e); }
662		return false;
663	},
664	openBox_squeezebox: function(elem, url) {
665		if(window.SqueezeBox === undefined)
666			return false;
667		if(url !== undefined && url !== null)
668			elem.href = url;
669		if(!elem.rel && elem.getAttribute('data-hk-popup') == 'squeezebox')
670			elem.rel = elem.getAttribute('data-squeezebox');
671		if(window.SqueezeBox.open !== undefined)
672			SqueezeBox.open(elem, {parse: 'rel'});
673		else if(window.SqueezeBox.fromElement !== undefined)
674			SqueezeBox.fromElement(elem);
675		setTimeout(function(){
676			jQuery('#sbox-content').find('iframe').attr('name', 'hikashop_popup_iframe');
677		},500);
678		return true;
679	},
680	openBox_bootstrap: function(elem, url) {
681		if(typeof(jQuery) == "undefined")
682			return false;
683		var id = elem.getAttribute('id');
684		jQuery('#modal-' + id).modal('show');
685		if(!url)
686			return true;
687		if(document.getElementById('modal-' + id + '-container'))
688			jQuery('#modal-' + id + '-container').find('iframe').attr('src', url);
689		else
690			jQuery('#modal-' + id).find('iframe').attr('src', url);
691		jQuery('#modal-' + id).find('iframe').attr('name', 'hikashop_popup_iframe');
692		return true;
693	},
694	openBox_vex: function(elem, url) {
695		if(typeof(vex) == "undefined")
696			return false;
697		var href = elem.href || null;
698		if(url !== undefined && url !== null)
699			href = url;
700		if(!href) href = elem.getAttribute('href');
701		settings = window.Oby.evalJSON(elem.getAttribute('data-vex'));
702		var triggerEl = elem;
703		if(settings.x && settings.y && href) {
704			settings.content = '<iframe style="border:0;margin:0;padding:0;" name="hikashop_popup_iframe" width="'+settings.x+'px" height="'+settings.y+'px" src="'+href+'"></iframe>';
705			var origAfterOpen = settings.afterOpen || null;
706			settings.afterOpen = function(context) {
707				if(origAfterOpen) origAfterOpen(context);
708				context.width(settings.x + 'px');
709				hikashop.vexA11y(context, triggerEl);
710			};
711		}
712		settings.beforeClose = function() {
713			if(triggerEl && triggerEl.focus) triggerEl.focus();
714		};
715		vex.defaultOptions.className = 'vex-theme-default';
716		vex.open( settings );
717
718		// Fix for CSS filter on body (e.g. Joomla a11y_contrast) breaking position:fixed.
719		// When a CSS filter is set on the body, position:fixed elements are positioned
720		// relative to the body instead of the viewport. We compensate by converting
721		// the popup to position:absolute and setting its top to the current scroll position.
722		var bodyStyle = window.getComputedStyle(document.body);
723		if(bodyStyle.filter && bodyStyle.filter !== 'none') {
724			var vexEls = document.querySelectorAll('.vex');
725			for(var vi = 0; vi < vexEls.length; vi++) {
726				vexEls[vi].style.position = 'absolute';
727				vexEls[vi].style.top = window.scrollY + 'px';
728				vexEls[vi].style.height = window.innerHeight + 'px';
729				// Also fix the overlay inside
730				var overlay = vexEls[vi].querySelector('.vex-overlay');
731				if(overlay) {
732					overlay.style.position = 'absolute';
733					overlay.style.top = '0';
734					overlay.style.height = '100%';
735				}
736			}
737		}
738
739		return true;
740	},
741	vexA11y: function(context, triggerEl) {
742		var el = context.get(0) || context[0];
743		if(!el) return;
744		var content = el.querySelector('.vex-content');
745		if(!content) return;
746		// ARIA attributes
747		content.setAttribute('role', 'dialog');
748		content.setAttribute('aria-modal', 'true');
749		var title = triggerEl ? (triggerEl.getAttribute('title') || triggerEl.textContent || '') : '';
750		if(title) content.setAttribute('aria-label', title.trim().substring(0, 100));
751		// Make close button accessible
752		var closeBtn = content.querySelector('.vex-close');
753		if(closeBtn) {
754			closeBtn.setAttribute('role', 'button');
755			closeBtn.setAttribute('tabindex', '0');
756			closeBtn.setAttribute('aria-label', window.hikashop.translations['HIKA_CLOSE'] || 'Close');
757			closeBtn.addEventListener('keydown', function(e) {
758				if(e.keyCode === 13 || e.keyCode === 32) { e.preventDefault(); this.click(); }
759			});
760		}
761		// Focus trap
762		content.addEventListener('keydown', function(e) {
763			if(e.keyCode !== 9) return;
764			var focusable = content.querySelectorAll('a[href], button, textarea, input, select, iframe, [tabindex]:not
764([tabindex="-1"])');
765			if(!focusable.length) return;
766			var first = focusable[0], last = focusable[focusable.length - 1];
767			if(e.shiftKey) {
768				if(document.activeElement === first || document.activeElement === content) { e.preventDefault(); last.focus(); }
769			} else {
770				if(document.activeElement === last) { e.preventDefault(); first.focus(); }
771			}
772		});
773		// Focus the first focusable element
774		var firstFocusable = content.querySelector('iframe, button, [tabindex]:not([tabindex="-1"]), a[href], input, select, textarea');
775		if(firstFocusable) firstFocusable.focus();
776	},
777	closeBox: function(parent) {
778		var d = document, w = window;
779		if(parent) {
780			d = window.parent.document;
781			w = window.parent;
782		}
783		try {
784			var e = d.getElementById('sbox-window');
785			if(e && typeof(e.close) != "undefined") {
786				e.close();
787			} else if(typeof(w.jQuery) != "undefined" && w.jQuery('div.modal.in') && w.jQuery('div.modal.in').hasClass('in')) {
788				w.jQuery('div.modal.in').modal('hide');
789			} else if(typeof(vex) != 'undefined' && vex.close && vex.close() === true) {
790				return;
791			} else if(w.SqueezeBox !== undefined) {
792				w.SqueezeBox.close();
793			}
794		} catch(err) {}
795	},
796	submitPopup: function(id, task, form) {
797		var d = document, t = this, el = d.getElementById('modal-'+id+'-iframe');
798		if(!el) {
799			if(document.getElementById('modal-' + id + '-container'))
800				el = jQuery('#modal-' + id + '-container').find('iframe').get(0);
801			else
802				el = jQuery('#modal-' + id).find('iframe').get(0);
803		}
804		if(el && el.contentWindow.hikashop) {
805			if(task === undefined) task = null;
806			if(form === undefined) form = 'adminForm';
807			el.contentWindow.hikashop.submitform(task, form);
808		}
809		return false;
810	},
811	tabSelect: function(m,c,id) {
812		var d = document, sub = null;
813		if(typeof m == 'string')
814			m = d.getElementById(m);
815		if(!m) return;
816		if(typeof id == 'string')
817			id = d.getElementById(id);
818		sub = m.getElementsByTagName('div');
819		if(sub) {
820			for(var i = sub.length - 1; i >= 0; i--) {
821				if(sub[i].getAttribute('class') == c) {
822					sub[i].style.display = 'none';
823				}
824			}
825		}
826		if(id) id.style.display = '';
827	},
828	changeState: function(el, id, url) {
829		var d = document;
830		if(!d.getElementById(id + '_container'))
831			return false;
832		var mainDiv = el.closest('.hikashop_checkout_address');
833		if(mainDiv)
834			window.checkout.setLoading(mainDiv, true, true);
835		window.Oby.xRequest(url, null, function(xhr){
836			var w = window;
837			w.hikashop.updateElem(id + '_container', xhr.responseText);
838			var defaultVal = '', defaultValInput = d.getElementById(id + '_default_value'), stateSelect = d.getElementById(id);
839			if(defaultValInput) { defaultVal = defaultValInput.value; }
840			if(stateSelect && w.hikashop.optionValueIndexOf(stateSelect.options, defaultVal) >= 0)
841				stateSelect.value = defaultVal;
842			if(typeof(jQuery) != "undefined" && jQuery().chosen) { jQuery('#'+id).chosen(); }
843			if(mainDiv)
844				window.checkout.setLoading(mainDiv, false);
845			w.Oby.fireAjax('hikashop.stateupdated', {id: id, elem: stateSelect});
846		});
847	},
848	optionValueIndexOf: function(options, value) {
849		for(var i = options.length - 1; i >= 0; i--) {
850			if(options[i].value == value)
851				return i;
852		}
853		return -1;
854	},
855	getOffset: function(el) {
856		var x = 0, y = 0;
857		while(el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop )) {
858			x += el.offsetLeft - el.scrollLeft;
859			y += el.offsetTop - el.scrollTop;
860			el = el.offsetParent;
861		}
862		return { top: y, left: x };
863	},
864	dataStore: function(name, value) {
865		if(localStorage) {
866			localStorage.setItem(name, value);
867		} else {
868			var expire = new Date(); expire.setDate(expire.getDate() + 5);
869			document.cookie = name+"="+value+"; expires="+expire;
870		}
871	},
872	dataGet: function(name) {
873		if(localStorage) {
874			return localStorage.getItem(name);
875		}
876		if(document.cookie.length > 0 && document.cookie.indexOf(name+"=") != -1) {
877			var s = name+"=", o = document.cookie.indexOf(s) + s.length, e = document.cookie.indexOf(";",o);
878			if(e == -1) e = document.cookie.length;
879			return unescape(document.cookie.substring(o, e));
880		}
881		return null;
882	},
883	setArrayDisplay: function(fields, displayValue) {
884		var d = document, e = null;
885		if(displayValue === true) displayValue = '';
886		if(displayValue === false) displayValue = 'none';
887		for(var i = 0; i < fields.length; i++) {
888			e = d.getElementById(fields[i]);
889			if(e) e.style.display = displayValue;
890		}
891	},
892	ready: function(fct) {
893		var w = window, d = w.document;
894		if(d.readyState === "complete") {
895			fct();
896			return;
897		}
898		if(w.jQuery !== undefined) {
899			jQuery(fct);
900		} else if(window.addEvent) {
901			w.addEvent("domready", fct);
902		} else
903			w.Oby.ready(fct);
904	},
905	noChzn: function() {
906		if(!window.jQuery)
907			return false;
908		jQuery('.no-chzn').each(function(i,el) {
909			var id = el.getAttribute('id'), chzn = null;
910			if(id) {
911				id = id.replace('{','_').replace('}','_');
912				chzn = jQuery('#'+id+'_chzn');
913			} else {
914				chzn = el.nextSibling;
915			}
916			if(chzn) chzn.remove();
917			jQuery(el).removeClass('chzn-done').show();
918		});
919		return true;
920	},
921	switchTab: function(el) {
922		if(!el || !el.parentNode || !el.parentNode.parentNode) return false;
923		var d = document, w = window, o = w.Oby,
924			c = el.parentNode.parentNode,
925			r = c.getAttribute('rel'),
926			current = el.getAttribute('rel'),
927			dest = null;
928		if(!r || r.substring(0,5) != 'tabs:') return false;
929		if(current.substring(0,4) != 'tab:') return false;
930		var id = r.substring(5),
931			tabs = c.childNodes;
932		current = current.substring(4);
933		dest = d.getElementById(id + current);
934		if(!dest) return false;
935		for(var k = 0; k < tabs.length; k++) {
936			if(!tabs[k] || tabs[k].nodeName.toLowerCase() != 'li') continue;
937			var i = 0, l = tabs[k].childNodes[i], lr = null;
938			while(l.nodeName.toLowerCase() != 'a' && i < tabs[k].childNodes.length)
939				l = tabs[k].childNodes[++i];
940			if(l.nodeName.toLowerCase() == 'a')
941				lr = l.getAttribute('rel');
942			if(!lr || lr.substring(0,4) != 'tab:')
943				continue;
944			var lid = lr.substring(4);
945			if(lid == current) continue;
946			o.removeClass(tabs[k], 'active');
947			var ld = d.getElementById(id + lid);
948			if(ld) ld.style.display = 'none';
949		}
950		dest.style.display = '';
951		o.addClass(el.parentNode, 'active');
952		el.blur();
953		return false;
954	},
955	dlTitle: function(parent) {
956		var t = this, d = document, w = window;
957		if(parent && typeof(parent) == 'string')
958			parent = d.getElementById(parent);
959		if(!parent)
960			parent = d;
961		var dt = parent.getElementsByTagName('dt'), val = null,
962			hkTip = (typeof(hkjQuery) != "undefined" && hkjQuery().hktooltip);
963		for(var i = 0; i < dt.length; i++) {
964			if(dt[i].offsetWidth === 0) {
965				dt[i].dlTitleFct = function(evt){
966					t.dlTitle(this.parentNode);
967					if(hkTip)
968						hkjQuery(this).hktooltip('show');
969					this.removeEventListener('mouseover', this.dlTitleFct);
970					this.dlTitleFct = null;
971				};
972				dt[i].addEventListener('mouseover', dt[i].dlTitleFct);
973			}
974			if(dt[i].offsetWidth < dt[i].scrollWidth && !dt[i].getAttribute('title')) {
975				val = (dt[i].innerText !== undefined) ? dt[i].innerText : dt[i].textContent;
976
977				if(hkTip) {
978					dt[i].setAttribute('data-title', val);
979					hkjQuery(dt[i]).hktooltip({"html": true,"container": "body"});
980				} else
981					dt[i].setAttribute('title', val);
982			}
983		}
984	},
985	checkConsistency: function() {
986		if(!document.querySelectorAll)
987			return;
988		var s = null, elems = null,
989			parents = document.querySelectorAll('[data-consistencyheight]');
990		if(!parents || !parents.length)
991			return;
992		for(var i = parents.length - 1; i >= 0; i--) {
993			s = parents[i].getAttribute('data-consistencyheight');
994			if(s == '' || s == 'true')
995				continue;
996			var reg = new RegExp('^\.[-_a-z0-9]+$', 'i');
997			if(reg.test(s) && document.getElementsByClassName)
998				elems = parents[i].getElementsByClassName(s.substring(1));
999			else
1000				elems = parents[i].querySelectorAll(s);
1001			if(!elems || !elems.length)
1002				continue;
1003			if(this.setConsistencyHeight(elems, 'min') === false)
1004				continue;
1005			parents[i].setAttribute('data-consistencyheight-done', s);
1006			parents[i].removeAttribute('data-consistencyheight');
1007		}
1008	},
1009	setConsistencyHeight: function(elems, mode) {
1010		if(!elems || !elems.length || elems.length == 0)
1011			return;
1012		var maxHeight = 0, cpt = 0, h = 0, w = window;
1013		for(var i = elems.length - 1; i >= 0; i--) {
1014			if(!elems[i].offsetParent)
1015				continue;
1016			try {
1017				h = parseFloat( w.getComputedStyle(elems[i], '').getPropertyValue('height') );
1018				h = Math.ceil(h);
1019			} catch(e) {
1020				h = NaN;
1021			}
1022			if(isNaN(h))
1023				h = (elems[i].currentStyle ? elems[i].currentStyle.height : elems[i].clientHeight);
1024
1025			if(maxHeight > 0 && h < maxHeight) {
1026				cpt++;
1027			} else if(h > maxHeight) {
1028				maxHeight = h;
1029				cpt++;
1030			}
1031		}
1032		if(maxHeight <= 0)
1033			return false;
1034
1035		if(cpt <= 1)
1036			return;
1037		for(var i = elems.length - 1; i >= 0; i--) {
1038			if(mode !== undefined && mode == 'min')
1039				elems[i].style.minHeight = maxHeight + 'px';
1040			else
1041				elems[i].style.height = maxHeight + 'px';
1042		}
1043	},
1044	// A plain return URL is inlined in the path by the SEF router, where its own
1045	// query string leaks into the request and overwrites the filter values.
1046	base64Url: function (str) {
1047		"use strict";
1048		var encoded = window.btoa(unescape(encodeURIComponent(str)));
1049		return encoded.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
1050	},
1051	refreshFilters: function (el, skipSelf) {
1052		"use strict";
1053		var d = document, t = this, o = window.Oby,
1054		container = null, data = null, containerName = el.getAttribute('data-container-div');
1055
1056		if(containerName)
1057			container = d.forms[containerName];
1058
1059		if(!container)
1060			return false;
1061
1062		var url = container.getAttribute('action');
1063		var scrollToTop = container.getAttribute('data-scroll');
1064
1065		// delay timer to avoid too many ajax calls
1066		if(t.filterRefreshTimer !== false) clearTimeout(t.filterRefreshTimer);
1067		t.filterRefreshTimer = setTimeout(function() {
1068
1069			data = o.getFormData(container);
1070			data += '&tmpl=raw';
1071			o.xRequest(url, {mode:'POST', data: data}, function(xhr) {
1072				var resp = o.evalJSON(xhr.responseText);
1073
1074				if(resp.newURL) {
1075					var urlInHistory = resp.newURL.replace('tmpl=raw&', '', 'g').replace('tmpl=component&', '', 'g').replace('filter=1&', '', 'g').replace('&tmpl=raw', '', 'g').replace('&tmpl=component', '', 'g').replace('&filter=1', '', 'g');
1076					window.history.pushState(data, d.title, urlInHistory);
1077
1078					window.addEventListener('popstate', function(e) {
1079						if(window.location.href.includes('hikashop_url_reload=1')) {
1080							window.location.href.replace('&hikashop_url_reload=1','').reload();
1081						}
1082					});
1083				}
1084
1085				var refreshAreas = document.querySelectorAll('.filter_refresh_div');
1086
1087				var triggers = o.fireAjax('filters.update', {el: el, refreshAreas : refreshAreas, resp: resp});
1088				if(triggers !== false && triggers.length > 0)
1089					return true;
1090
1091				var refreshUrl = null;
1092				t.refreshCounter = 0;
1093				for(let i = 0; i < refreshAreas.length; i++) {
1094					var currentArea = refreshAreas[i];
1095					if(skipSelf && currentArea.querySelector('#'+el.id))
1096						continue;
1097
1098					if(resp.newURL && currentArea.getAttribute('data-use-url')) {
1099						refreshUrl = resp.newURL;
1100					} else {
1101						refreshUrl = currentArea.getAttribute('data-refresh-url');
1102						if(refreshUrl && resp.params) {
1103							refreshUrl += '&' + resp.params + '&return_url_b64=' + t.base64Url(window.location.href);
1104						}
1105					}
1106					if(!refreshUrl)
1107						continue;
1108					t.refreshCounter++;
1109					var className = currentArea.getAttribute('data-refresh-class');
1110					if(className) o.addClass(currentArea, className);
1111					t.refreshOneArea(refreshUrl, currentArea, el, refreshAreas, resp);
1112				}
1113
1114				if(scrollToTop) {
1115					window.hikashop.smoothScroll();
1116				}
1117			});
1118			t.filterRefreshTimer = false;
1119		}, 300);
1120		return false;
1121	},
1122	smoothScroll: function(target) {
1123		var target = document.querySelector('div[id^="hikashop_category_information_menu_"]');
1124		if(!target)
1125			return;
1126		var currentScroll = document.documentElement.scrollTop || document.body.scrollTop;
1127		if (currentScroll > target.offsetTop) {
1128			window.requestAnimationFrame(window.hikashop.smoothScroll);
1129			window.scrollTo (target.offsetTop, currentScroll - (currentScroll/5));
1130		}
1131	},
1132	refreshOneArea: function(refreshUrl, currentArea, el, refreshAreas, resp) {
1133		var d = document, t = this, o = window.Oby;
1134		o.xRequest(refreshUrl, {mode:'GET'}, function (xhr2) {
1135			var div = d.createElement('div');
1136			var scripts = [];
1137			var response = xhr2.responseText;
1138			// When SEF or a proxy strips tmpl=raw, the response is the whole page with
1139			// every module instead of the raw block. Detect that so we inject only the
1140			// refreshed block and run only its own scripts: re-running the whole page's
1141			// template and third-party scripts after load blanks the page.
1142			var isFullPage = /<!DOCTYPE|<html[\s>]|<body[\s>]/i.test(response);
1143
1144			var body = /<body.*?>([\s\S]*)<\/body>/i.exec(response);
1145			body = body ? body[1] : response;
1146
1147			if(isFullPage) {
1148				// Parse detached (innerHTML never executes scripts), then keep only the
1149				// scripts that belong to the block being refreshed.
1150				div.innerHTML = body;
1151				var block = (currentArea && currentArea.id) ? div.querySelector('#' + currentArea.id) : null;
1152				if(!block)
1153					block = div.querySelector('.filter_refresh_div');
1154				if(block) {
1155					var blockScripts = block.querySelectorAll('script');
1156					for(var s = 0; s < blockScripts.length; s++) {
1157						var sn = blockScripts[s], stype = sn.getAttribute('type') || '';
1158						if(!sn.getAttribute('src') && stype.indexOf('application/json') == -1 && stype.indexOf('application/ld+json') == -1)
1159							scripts.push(sn.textContent);
1160						sn.parentNode.removeChild(sn);
1161					}
1162				}
1163			} else {
1164				// Raw block: strip and run every script it carries, as before.
1165				var text = body.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function(all, code){
1166					if(all.indexOf('type="application/json"') != -1)
1167						return '';
1168					if(all.indexOf('type="application/ld+json"') != -1)
1169						return '';
1170					scripts.push(code);
1171					return '';
1172				});
1173				window.hikashop.updateElem(div, text);
1174			}
1175
1176			var newElem = (currentArea && currentArea.id) ? div.querySelector('#' + currentArea.id) : null;
1177			if(!newElem)
1178				newElem = div.querySelector('.filter_refresh_div');
1179
1180			// to avoid scroll in chrome
1181			setTimeout(function(){
1182				if(!currentArea) {
1183					t.refreshCounter--;
1184					return;
1185				}
1186				var className = currentArea.getAttribute('data-refresh-class');
1187				if(className) o.removeClass(currentArea, className);
1188				if(!newElem) {
1189					t.refreshCounter--;
1190					return;
1191				}
1192				var parentNode = currentArea.parentNode;
1193				if(!parentNode) {
1194					t.refreshCounter--;
1195					return;
1196				}
1197				parentNode.replaceChild(newElem, currentArea);
1198				if( scripts.length ) {
1199					scripts.forEach(function(code){
1200						var injectedCode = code.replaceAll('let jch_', 'jch_').replaceAll('const jch', 'window.hikashop.jch');
1201						injectedCode = injectedCode.replace(/(^|[;{}\n)])([ \t]*)(const|let|class)([ \t]+)([A-Za-z_$][\w$]*)/g, function(m, pre, ws, kw, sp, name) {
1202							var declared = false;
1203							try { (0, eval)(name); declared = true; }
1204							catch (err) { declared = !(err instanceof ReferenceError); }
1205							if(!declared)
1206								return m;
1207							return pre + ws + 'window.hikashop.' + name + (kw === 'class' ? ' = class' : '');
1208						});
1209						// Run each inline script on its own so one that throws (a third-party
1210						// or template script) cannot abort the ones after it, which used to
1211						// leave the price cursor and other widgets un-initialised.
1212						var script = d.createElement('script');
1213						script.setAttribute('type', 'text/javascript');
1214						script.text = injectedCode;
1215						// A runtime error in an injected inline script is reported to window.onerror
1216						// rather than thrown back to appendChild, so catch it there and log it
1217						// cleanly instead of as an uncaught error. The listener is active only for
1218						// this single script's synchronous execution.
1219						var onError = function(event){
1220							event.preventDefault();
1221							console.warn('Could not process JavaScript code:\n', event.error, '\n', injectedCode);
1222						};
1223						window.addEventListener('error', onError);
1224						try {
1225							d.head.appendChild(script);
1226							d.head.removeChild(script);
1227						} catch (e) {
1228							console.warn('Could not process JavaScript code:\n', e);
1229						}
1230						window.removeEventListener('error', onError);
1231					});
1232				}
1233
1234				if(!window.localPage) window.localPage = {};
1235				window.localPage.infiniteScrollPage = 1;
1236
1237				setTimeout(function(){
1238					var needHeightConsistency = currentArea.querySelector('[data-consistencyheight]');
1239					if(needHeightConsistency) {
1240						var elems = parentNode.querySelectorAll('.hikashop_subcontainer');
1241						if(elems && elems.length)
1242							window.hikashop.setConsistencyHeight(elems, 'min');
1243					}
1244
1245					if(window.hikaVotes)
1246						initVote(currentArea);
1247					if(hkjQuery && hkjQuery.hktooltip)
1248						hkjQuery('[data-toggle="hk-tooltip"]').hktooltip({"html": true,"container": "body"});
1249
1250					t.refreshCounter--;
1251					if(t.refreshCounter == 0) {
1252						o.fireAjax('filters.updated', {el: el, refreshAreas : refreshAreas, resp: resp});
1253					}
1254				}, 200);
1255			}, 0);
1256		});
1257	},
1258	addToCart: function(el, type, container, data) {
1259
1260		var d = document, t = this, o = window.Oby,
1261			product_id = 0,
1262			url = el.getAttribute('href'),
1263			cart_type = ((type !== 'wishlist') ? 'cart' : 'wishlist'),
1264			containerName = el.getAttribute('data-addTo-div'),
1265			extraContainer = el.getAttribute('data-addTo-extra'),
1266			dest_id = el.getAttribute('data-addTo-cartid');
1267
1268		product_id = (cart_type == 'cart') ? el.getAttribute('data-addToCart') : el.getAttribute('data-addToWishlist');
1269		dest_id = (dest_id ? parseInt(dest_id) : 0);
1270		if(!url)
1271			url = el.getAttribute('data-href');
1272
1273		// Avoid bots and crawlers to add products in the cart
1274		var r = /bot|googlebot|crawler|spider|robot|crawling/i;
1275		if(navigator && navigator.userAgent && r.test(navigator.userAgent))
1276			return false;
1277
1278		var process = true;
1279		window.Oby.fireAjax(cart_type+'-button.clicked', {el: el, product_id: product_id, type: cart_type, cart_id: dest_id, process: process});
1280		if(!process)
1281			return true;
1282
1283		// No product ID - fallback mode
1284		if(!product_id || !url) {
1285			if(containerName && d.forms[containerName]) {
1286				d.forms[containerName].submit();
1287				return false;
1288			}
1289			return true;
1290		}
1291
1292		if(typeof container !== 'undefined') {
1293			// container is provided, just use it
1294		}else if(containerName && product_id) {
1295			// search for the container on the page
1296			container = d.forms['hikashop_product_form_' + product_id + '_' + containerName] || d.forms[containerName];
1297		}
1298
1299		url += (url.indexOf('?') >= 0 ? '&' : '?') + 'tmpl=raw';
1300		if(typeof data !== 'undefined') {
1301		} else if(container) {
1302			if(window.FormData)
1303				data = new FormData(container);
1304			else
1305				data = o.getFormData(container);
1306			// Collect orphaned inputs associated with the form via the form attribute
1307			// (browsers may not include dynamically inserted inputs with form= in FormData)
1308			if(window.FormData && data && container.id) {
1309				var scope = container.closest('.hikashop_product_page') || container.parentNode;
1310				var orphans = scope.querySelectorAll('input[form="'+container.id+'"], select[form="'+container.id+'"], textarea[form="'+container.id+'"]');
1311				for(var oi = 0; oi < orphans.length; oi++) {
1312					var orphan = orphans[oi];
1313					if(!orphan.name || orphan.disabled || data.has(orphan.name))
1314						continue;
1315					var etype = orphan.tagName.toLowerCase() == 'input' ? orphan.type.toLowerCase() : '';
1316					if(etype == 'file' || etype == 'submit')
1317						continue;
1318					if((etype == 'radio' || etype == 'checkbox') && !orphan.checked)
1319						continue;
1320					data.append(orphan.name, orphan.value);
1321				}
1322			}
1323			if(extraContainer) {
1324				extraContainer = d.forms[extraContainer] || d.getElementById(extraContainer);
1325				if(window.FormData) {
1326					extra = o.getFormData(extraContainer, false);
1327					for(var k in extra) {
1328						if(!extra.hasOwnProperty(k))
1329							continue;
1330						if(k == 'product_id')
1331							extra[k] = product_id;
1332						if(typeof(extra[k]) == 'object') {
1333							for(var i in extra[k]) {
1334								data.append(k, extra[k][i]);
1335							}
1336						} else
1337							data.append(k, extra[k]);
1338					}
1339				} else {
1340					var extra = o.getFormData(extraContainer);
1341					if(extra)
1342						data += '&' + extra;
1343					data += '&product_id='+product_id;
1344				}
1345			}
1346			if(window.FormData) {
1347				data.append('cart_type', cart_type);
1348				if(dest_id)
1349					data.append('cart_id', dest_id);
1350			} else {
1351				data += '&cart_type=' + cart_type;
1352				if(dest_id)
1353					data += '&cart_id+' + dest_id;
1354			}
1355		} else {
1356			data = 'cart_type=' + cart_type;
1357			if(dest_id)
1358				data += '&cart_id+' + dest_id;
1359		}
1360
1361		var className = el.getAttribute('data-addTo-class');
1362		if(className) o.addClass(el, className);
1363
1364		// if we are in a popup we want the add to cart to happen in the parent frame.
1365		// however, this can cause issues if the website is inside an iframe from another domain so we add a try/catch so that the add to cart can work
1366		if(window.self !== window.top) {
1367			try {
1368				if(window.top.hikashop)
1369					return window.top.hikashop.addToCart(el, type, container, data);
1370			} catch(e) {}
1371		}
1372
1373		o.xRequest(url, {mode:'POST', data: data}, function(xhr) {
1374			var className = el.getAttribute('data-addTo-class');
1375			if(className) o.removeClass(el, className);
1376
1377			var resp = Oby.evalJSON(xhr.responseText);
1378			var cart_id = (resp && (resp.ret || resp.ret === 0)) ? resp.ret : parseInt(xhr.responseText);
1379			var failed = isNaN(cart_id);
1380			if(failed) {
1381				// the server didn't answer with a cart id: report it like a refused add instead of doing nothing
1382				console.log('cart_id was not returned in addToCart AJAX call');
1383				console.log(resp);
1384				resp = {ret: 0};
1385				cart_id = 0;
1386			}
1387
1388			var triggers = window.Oby.fireAjax(cart_type+'.updated', {id: cart_id, el: el, product_id: product_id, type: cart_type, resp: resp});
1389			if(triggers !== false && triggers.length > 0)
1390				return true;
1391			if(failed) {
1392				// nothing displayed the error and the product is not in the cart, so no redirect either
1393				t.displayAddToCartError(el, cart_type);
1394				return false;
1395			}
1396			if(window.localPage && cart_type == 'cart' && window.localPage.cartRedirect && typeof(window.localPage.cartRedirect) == 'function')
1397				return window.localPage.cartRedirect(cart_id, product_id, resp);
1398			if(window.localPage && cart_type == 'wishlist' && window.localPage.wishlistRedirect && typeof(window.localPage.wishlistRedirect) == 'function')
1399				return window.localPage.wishlistRedirect(cart_id, product_id, resp);
1400		});
1401		return false;
1402	},
1403	// fallback when no notification plugin is there to display the failed add to cart
1404	displayAddToCartError: function(el, cart_type) {
1405		var t = this, wishlist = (cart_type == 'wishlist');
1406		t.translate([wishlist ? 'PRODUCT_NOT_ADDED_TO_WISHLIST' : 'PRODUCT_NOT_ADDED_TO_CART'], function(trans) {
1407			if(!trans[0])
1408				return;
1409			if(window.hkjQuery && hkjQuery.notify) {
1410				hkjQuery(el).notify({title: trans[0], text: ''}, {style: "metro-lite", className: "error", arrowShow: true, position: "top left"});
1411				return;
1412			}
1413			alert(trans[0]);
1414		});
1415	},
1416	addToWishlist: function(el) {
1417		return this.addToCart(el, 'wishlist');
1418	},
1419	checkQuantity: function(el) {
1420		var value = parseInt(el.value), old = el.getAttribute('data-hk-qty-old'),
1421			min = parseInt(el.getAttribute('data-hk-qty-min')),
1422			max = parseInt(el.getAttribute('data-hk-qty-max')),
1423			allowZero = el.getAttribute('data-hk-allow-zero') == 'true';
1424		if(old)
1425			old = parseInt(old);
1426		// No values - return
1427		if(isNaN(value)) {
1428			el.value = old || (isNaN(min) ? 1 : min);
1429			return false;
1430		}
1431		if(''+value != el.value)
1432			el.value = value;
1433		if(isNaN(min) || isNaN(max))
1434			return false;
1435		var triggers = window.Oby.fireAjax("quantity.checked", {el:el, value:value, max:max, min:min});
1436		if(
1437			(triggers !== false && triggers.length > 0) ||
1438			(value == 0 && allowZero) ||
1439			((value <= max || max == 0) && value >= min)
1440		) {
1441			// quantity change is ok
1442
1443			// trigger add to cart if the quantity input is synchronized with the cart
1444			var isSynch = document.getElementById(el.id + '_synch');
1445			if(isSynch) {
1446				isSynch.value = value;
1447				var addToCartButton = document.getElementById(el.id + '_add_to_cart_button');
1448				if(addToCartButton) {
1449					addToCartButton.onclick();
1450				}
1451			}
1452			return true;
1453		}
1454		if(max > 0 && value > max) {
1455			el.value = max;
1456			if(hkjQuery.notify) {
1457				this.translate(['QUANTITY_CHANGE_IMPOSSIBLE', 'MAXIMUM_FOR_PRODUCT_IS_X'], function(trans){
1458					hkjQuery(el).notify({title:trans[0],text:trans[1].replace('%s', max), image:'<i class="fa fa-3x fa-exclamation-circle"></i>'},{style:"metro",className:"warning",arrowShow:true,position:"top left"});
1459				});
1460			}
1461		} else if(value < min) {
1462			el.value = min;
1463			if(hkjQuery.notify) {
1464				this.translate(['QUANTITY_CHANGE_IMPOSSIBLE', 'MINIMUM_FOR_PRODUCT_IS_X'], function(trans){
1465					hkjQuery(el).notify({title:trans[0],text:trans[1].replace('%s', min), image:'<i class="fa fa-3x fa-exclamation-circle"></i>'},{style:"metro",className:"warning",arrowShow:true,position:"top left"});
1466				});
1467			}
1468		}
1469		// should not happen ?
1470		return true;
1471	},
1472	translate: function(keys, callback) {
1473		var t = this, trans = {}, missingKeys = [], o = window.Oby;
1474
1475		for(var c = 0; c < keys.length; c++) {
1476			var key = keys[c];
1477			if(!t.translations[key]) {
1478				missingKeys.push(key);
1479			} else {
1480				trans[c] = t.translations[key];
1481			}
1482		}
1483
1484		if(!missingKeys.length) {
1485			callback(trans);
1486			return;
1487		}
1488
1489		if(!t.translations_url) {
1490			console.log('missing translations URL');
1491			return;
1492		}
1493		o.xRequest(t.translations_url, {mode:'POST', data: 'translations=' + missingKeys.join(',')}, function(xhr) {
1494			var resp = o.evalJSON(xhr.responseText);
1495			foundKeys = Object.getOwnPropertyNames(resp);
1496			for(var c = 0; c < foundKeys.length; c++) {
1497				var key = foundKeys[c];
1498				trans[keys.indexOf(key)] = resp[key];
1499				t.translations[key] = resp[key];
1500			}
1501			callback(trans);
1502		});
1503	},
1504	addTrans: function(data) {
1505		for(var k in data) {
1506			if(!data.hasOwnProperty(k)) continue;
1507			this.translations[k] = data[k];
1508		}
1509	},
1510	updateQuantity: function(el, dataInput, mod) {
1511		var d = document, input = el;
1512		if(!el)
1513			return false;
1514		if(dataInput === undefined || !dataInput)
1515			dataInput = el.getAttribute('data-hk-qty-input');
1516		if(d.getElementById(dataInput))
1517			input = d.getElementById(dataInput);
1518		if(mod === undefined || !mod)
1519			mod =  parseInt(el.getAttribute('data-hk-qty-mod'));
1520		if(isNaN(mod) || mod == 0)
1521			mod = 1;
1522		var value = parseInt(input.value);
1523		if(isNaN(value))
1524			value = 0;
1525		input.value = (value + mod);
1526		this.checkQuantity(input);
1527		if(el.tagName.toLowerCase() == 'a')
1528			el.blur();
1529		return false;
1530	},
1531	deleteFromCart: function(el, cart_type, container) {
1532		if(el.processing)
1533			return false;
1534
1535		var d = document, t = this, o = window.Oby,
1536			url = el.getAttribute('href');
1537		if(!cart_type || cart_type === undefined)
1538			cart_type = el.getAttribute('data-cart-type');
1539		if(!cart_type || cart_type == '')
1540			return true;
1541
1542		url += (url.indexOf('?') >= 0 ? '&' : '?') + 'tmpl=raw';
1543		var cart_id = parseInt(el.getAttribute('data-cart-id')),
1544			cart_product_id = parseInt(el.getAttribute('data-cart-product-id'));
1545		if(cart_id === NaN || cart_product_id === NaN)
1546			return true;
1547
1548		if(container && typeof(container) == 'string')
1549			container = d.getElementById(container);
1550
1551		el.processing = true;
1552		if(container)
1553			o.addClass(container, "hikashop_checkout_loading");
1554		var data = 'cart_type=' + cart_type + '&cart_id=' + cart_id + '&cart_product_id=' + cart_product_id;
1555
1556		o.xRequest(url, {mode:'POST', data: data}, function(xhr) {
1557			el.processing = false;
1558			if(container)
1559				o.removeClass(container, "hikashop_checkout_loading");
1560
1561			var resp = Oby.evalJSON(xhr.responseText);
1562			cart_id = (resp && resp.ret) ? resp.ret : ((resp && resp.empty && resp.empty == 'true') ? cart_id : parseInt(xhr.responseText));
1563			if(cart_id === NaN)
1564				return;
1565			resp.cart_product_id = cart_product_id;
1566			resp.quantity = 0;
1567			var params = {id: cart_id, type: cart_type, resp: resp, notify: false};
1568			window.Oby.fireAjax(cart_type+'.updated', params);
1569		});
1570		return false;
1571	},
1572	submitCartModule: function(form, container, cart_type) {
1573		this.formAjaxSubmit(form, container, function(data, params) {
1574			var resp = window.Oby.evalJSON(data);
1575			var cart_id = (resp && resp.ret) ? resp.ret : parseInt(data);
1576			if(cart_id === NaN)
1577				return;
1578			var updatedElements = [];
1579			for (var key of params.keys()) {
1580				if(key.startsWith('item[') && key.endsWith('][cart_product_quantity]')) {
1581					updatedElements.push({ cart_product_id: key.replace('item[', '').replace('][cart_product_quantity]', ''), quantity_requested: params.get(key)});
1582				}
1583			}
1584			window.Oby.fireAjax(cart_type+'.updated', {id: cart_id, type: cart_type, resp: resp, updated_elements: updatedElements, notify: false});
1585		});
1586		return false;
1587	},
1588	formAjaxSubmit: function(form, container, cb) {
1589		var d = document, o = window.Oby,
1590			url = form.action;
1591		if(form.processing)
1592			return false;
1593		if(container && typeof(container) == 'string')
1594			container = d.getElementById(container);
1595		if(window.FormData) {
1596			data = new FormData(form);
1597			data.append('tmpl', 'raw');
1598		} else {
1599			data = o.getFormData(form);
1600			data += '&tmpl=raw';
1601		}
1602		form.processing = true;
1603		if(container)
1604			o.addClass(container, "hikashop_checkout_loading");
1605		o.xRequest(url, {mode:'POST', data: data}, function(xhr) {
1606			form.processing = false;
1607			if(container)
1608				o.removeClass(container, "hikashop_checkout_loading");
1609			if(!cb)
1610				o.updateElem(container, xhr.responseText);
1611			cb(xhr.responseText, data);
1612		});
1613		return false;
1614	},
1615	syncInit : function() {
1616		window.hikashop.noChzn();
1617		window.Oby.registerAjax(['cart.updated'], function(params){
1618			window.hikashop.refreshSync(params);
1619		});
1620		window.Oby.registerAjax(['hkContentChanged'], function(params){
1621			// on a one page product the options drive the cart, so write instead of only reading
1622			if(window.hikashopOnepageSync) {
1623				window.hikashop.syncCartNow();
1624				return;
1625			}
1626			window.Oby.xRequest(window.hikashop.cartInfoUrl, {}, function(xhr) {
1627				var resp = Oby.evalJSON(xhr.responseText);
1628				var params = {resp: resp};
1629				window.hikashop.refreshSync(params);
1630			});
1631		});
1632
1633		window.Oby.registerAjax(['hkCustomFieldChanged'], function(params){
1634			if(params.field_type != 'item')
1635				return;
1636			if(window.hikashopOnepageSync) {
1637				window.hikashop.syncCartNow();
1638				return;
1639			}
1640			window.Oby.xRequest(window.hikashop.cartInfoUrl, {}, function(xhr) {
1641				var resp = Oby.evalJSON(xhr.responseText);
1642				var params = {resp: resp};
1643				window.hikashop.refreshSync(params);
1644			});
1645		});
1646
1647		if(window.hikashop.cartInfo) {
1648			var params = {resp: window.hikashop.cartInfo};
1649			window.hikashop.refreshSync(params);
1650		}
1651	},
1652	xRequest: function(url, options, cb, cbError) {
1653		var t = this, xhr = window.Oby.getXHR();
1654		if(!options) options = {};
1655		if(!cb) cb = function(){};
1656		options.mode = options.mode || 'GET';
1657		options.update = options.update || false;
1658		options.replace = options.replace === undefined || options.replace;
1659		xhr.onreadystatechange = function() {
1660			if(xhr.readyState != 4)
1661				return;
1662			if( xhr.status == 200 || (xhr.status == 0 && xhr.responseText > 0) || !cbError ) {
1663				if(cb)
1664					cb(xhr,options.params);
1665				if(options.update)
1666					t.updateElem(options.update, xhr.responseText, options.replace);
1667			} else {
1668				cbError(xhr,options.params);
1669			}
1670		};
1671		xhr.open(options.mode, url, true);
1672		if(options.mode.toUpperCase() == 'POST' && typeof(options.data) == 'string') {
1673			xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
1674		}
1675		if(typeof Joomla !== 'undefined' && Joomla && Joomla.getOptions) {
1676			var csrf = Joomla.getOptions("csrf.token", "");
1677			if(csrf) {
1678				xhr.setRequestHeader("X-CSRF-Token", csrf);
1679			}
1680		}
1681		xhr.send( options.data );
1682	},
1683	updateElem: function(elem, data, replace = true) {
1684		var d = document, scripts = [], json = '';
1685		if( typeof(elem) == 'string' )
1686			elem = d.getElementById(elem);
1687
1688		// extract the javascript files and scripts from the HTML
1689		var files = [];
1690		var scriptsAlreadyLoaded = document.getElementsByTagName("script");
1691		var text = data.replace(/<script([^>]*)>([\s\S]*?)<\/script>/gi, function(all, attributes, code){
1692			if(all.indexOf('type="application/json"') != -1) {
1693				json += code + '\n';
1694				return '';
1695			}
1696			if(all.indexOf('type="application/ld+json"') != -1)
1697				return '';
1698
1699			if(all.indexOf('type="importmap"') != -1)
1700				return '';
1701			regex = RegExp('src="([^"]+)"', 'gi');
1702			result = regex.exec(attributes);
1703			if(result) {
1704				result[1] = result[1].replace(/\.js\?[0-9a-f\.]{1,128}/gi, '.js').replace(/&amp;/g, '&');
1705				for(var i = 0; i < scriptsAlreadyLoaded.length; i++) {
1706					var src = scriptsAlreadyLoaded[i].getAttribute('src');
1707					if(src && src.includes(result[1]))
1708						return '';
1709				}
1710				files.push(result[1]);
1711				return '';
1712			}
1713			scripts.push(code.replace('document.addEventListener(\'DOMContentLoaded\',', 'window.hikashop.ready(').replace('document.addEventListener("DOMContentLoaded",', 'window.hikashop.ready(').replace('$(document).ready(', 'window.hikashop.ready(').replace( 'find("select").chosen(', 'find("select").filter(":visible").chosen('));
1714			return '';
1715		});
1716
1717		// add the HTML without the javascript elements to the element on the page
1718		if(text.indexOf('id="hikashop_main_content"')) {
1719			var wrapper = document.createElement('div');
1720			wrapper.innerHTML = text;
1721			var mainArea = wrapper.querySelector('#hikashop_main_content');
1722			if(mainArea) {
1723				if(replace)
1724					elem.innerHTML = '';
1725				elem.appendChild(mainArea);
1726				text = false;
1727			}
1728		}
1729		if(text !== false) {
1730			if(!replace)
1731				elem.insertAdjacentHTML('beforeend',text);
1732			else
1733				elem.innerHTML = text;
1734		}
1735
1736		// add the javascript elements and run them
1737		if( json != '' && Joomla) {
1738			var option = JSON.parse(json);
1739			if (option) {
1740				Joomla.loadOptions(option);
1741			}
1742		}
1743
1744		// Joomla scripts (password eye, showon...) rebind on joomla:updated
1745		var updated = function() {
1746			window.hikashop.addScript(scripts);
1747			elem.dispatchEvent(new CustomEvent('joomla:updated', {bubbles: true, cancelable: true}));
1748		};
1749		if(files.length) {
1750			this.loadScripts(files, updated);
1751		} else {
1752			updated();
1753		}
1754	},
1755	addScript: function (code, url) {
1756		if(!code.length)
1757			return;
1758		for(var k=0; k < code.length; k++) {
1759			try {
1760				var oNew = document.createElement("script");
1761				oNew.type = "text/javascript";
1762				oNew.textContent = code[k];
1763				document.getElementsByTagName("head")[0].appendChild(oNew);
1764			} catch (e) {
1765				if (e instanceof SyntaxError) {
1766					console.log(e.message + ' for ' + url);
1767					console.log(code);
1768				}
1769			}
1770		}
1771	},
1772	loadScripts: function(scripts, complete) {
1773		var xhr = window.Oby.getXHR();
1774		var loadScript = function( src ) {
1775			xhr.open("GET", src , true);
1776			xhr.send();
1777		};
1778		xhr.onreadystatechange = function() {
1779			if (xhr.readyState != 4)
1780				return;
1781			if (xhr.status == 200) {
1782				try {
1783					window.hikashop.addScript([xhr.responseText], xhr.responseURL);
1784				} catch (e) {
1785					if (e instanceof SyntaxError) {
1786						console.log(e.message + ' for ' + xhr.responseURL);
1787						console.log(xhr.responseText);
1788					}
1789				}
1790			}
1791			var next = scripts.shift();
1792			if ( next ) {
1793				loadScript(next);
1794			} else if ( typeof complete == 'function' ) {
1795				complete();
1796			}
1797		}
1798		loadScript( scripts.shift() );
1799	},
1800	refreshSync : function(params) {
1801		// the cart is empty so reset all the synched elements and refresh them
1802		if(params.resp.empty) {
1803			synchronizedEls = document.querySelectorAll('.synchronized_add_to_cart');
1804			for(var i=0; i < synchronizedEls.length; i++) {
1805				var el = synchronizedEls[i];
1806				el.value = 0;
1807				el.setAttribute('data-cart-product-id', 0);
1808				var id = el.getAttribute('data-id');
1809				window.hikashop.syncQuantity(id, el.value, el.value);
1810			}
1811		}
1812		// the cart is updated from the cart module
1813		else if(params.resp.products) {
1814			// loop on all the products on the page
1815			var els = document.querySelectorAll('.synchronized_add_to_cart');
1816
1817			if(!els.length)
1818				return;
1819			for(var k=0; k < els.length; k++) {
1820				var el = els[k];
1821				var id = el.getAttribute('data-id');
1822				var productId = el.getAttribute('data-product-id');
1823				var options = [];
1824				if(el.form) {
1825					var optionsTable = el.form.querySelector('.hikashop_product_options_table');
1826					if(optionsTable) {
1827						options = hikaProductOptions.getOptions();
1828					}
1829				}
1830				var found = false;
1831				// see if we have the product in the cart
1832				for(var i=0; i < params.resp.products.length; i++) {
1833					var result = params.resp.products[i];
1834					// get the current quantity from the response of the server and update the quantity for the quantity input of the current loop
1835					if(productId == result.product_id) {
1836						if(options.length || (result.options && result.options.length)) {
1837							// no options selected on the product page or no options selected in the product in the cart -> products are different
1838							if(!options.length || !result.options || !result.options.length)
1839								continue;
1840							var match = true;
1841							// check that both arrays contain the same elements
1842							for(var j=0; j < options.length; j++) {
1843								if(!result.options.includes(options[j])) {
1844									match = false;
1845									break;
1846								}
1847							}
1848							for(var j=0; j < result.options.length; j++) {
1849								if(!options.includes(result.options[j])) {
1850									match = false;
1851									break;
1852								}
1853							}
1854							if(!match)
1855								continue;
1856						}
1857						found = true;
1858						el.value = result.quantity;
1859					}
1860				}
1861				// TODO: add custom item fields
1862				if(!found) {
1863					el.value = 0;
1864					el.setAttribute('data-cart-product-id', 0);
1865				}
1866				window.hikashop.syncQuantity(id, el.value, el.value);
1867			}
1868		}
1869	},
1870	// Pushes the product form into the cart, debounced because options and custom
1871	// fields can fire several changes in a row. One page product pages only.
1872	syncCartNow : function() {
1873		var t = window.hikashop;
1874		if(t.onepageSyncTimer)
1875			clearTimeout(t.onepageSyncTimer);
1876		t.onepageSyncTimer = setTimeout(function() {
1877			var el = document.querySelector('.synchronized_add_to_cart');
1878			if(!el)
1879				return;
1880			var btn = document.getElementById(el.getAttribute('data-id') + '_add_to_cart_button');
1881			if(btn && btn.onclick)
1882				btn.onclick();
1883		}, 300);
1884	},
1885	syncQuantity : function(id, qtyInCart, qtyInInput) {
1886		// refresh the quantity in the input field
1887		if(typeof qtyInInput !== 'undefined') {
1888
1889			var inputField = document.getElementById(id+'_select');
1890			if(!inputField)
1891				inputField = document.getElementById(id);
1892			if(inputField) {
1893				if(qtyInInput == 0) {
1894					var minQty = inputField.getAttribute('data-hk-qty-min');
1895					if(minQty == 0)
1896						qtyInInput = 1;
1897					else
1898						qtyInInput = minQty;
1899				}
1900				inputField.value = qtyInInput;
1901			}
1902		}
1903
1904		// update the display of the quantity input field and the buttons
1905		var quantityArea = document.getElementById(id + '_area');
1906		var cartButtonArea = document.getElementById(id + '_add_to_cart_button');
1907		var wishlistButtonArea = document.getElementById(id + '_add_to_wishlist_button');
1908		if(!quantityArea || !cartButtonArea)
1909			return;
1910		// a one page product has no add to cart step: the quantity is always the control
1911		if(qtyInCart > 0 || window.hikashopOnepageSync) {
1912			quantityArea.style.display = '';
1913			cartButtonArea.style.display = 'none';
1914			if(wishlistButtonArea)
1915				wishlistButtonArea.style.display = 'none';
1916		} else {
1917			quantityArea.style.display = 'none';
1918			cartButtonArea.style.display = '';
1919			if(wishlistButtonArea)
1920				wishlistButtonArea.style.display = '';
1921		}
1922	},
1923	toggleOverlayBlock: function(el, type, state) {
1924		var t = this, d = document, w = window, o = w.Oby;
1925		if(typeof(el) == 'string')
1926			el = d.getElementById(el);
1927		if(!el)
1928			return false;
1929		var open = !!el.toggleOpen; // (el.style.display != 'none');
1930		if(type != 'hover' && type != 'toggle' && type != 'drawer')
1931			type = 'click';
1932		if(type == 'drawer') {
1933			var target = d.getElementById(el.getAttribute('aria-controls'));
1934			if(!target) return false;
1935			if(target.classList.contains(hkDrawer.settings['activeClass']))
1936				hkDrawer.closeDrawer(target);
1937			else
1938				hkDrawer.openDrawer(el);
1939			return true;
1940		}
1941		if(type == 'hover' && (!state && open) || (state && !open))
1942			return;
1943
1944		if(window.jQuery) {
1945			jQuery(el).slideToggle('fast');
1946		} else {
1947			el.style.display = (el.style.display == 'none')?'block':'none';
1948		}
1949
1950		el.toggleOpen = !el.toggleOpen;
1951
1952		if(open) {
1953			if(type == 'hover') {
1954				o.removeEvent(el, "mouseout", el.toggleFunctionHover);
1955				el.toggleFunctionHover = null;
1956			}
1957			if(el.toggleFunction)
1958				o.removeEvent(document, "click", el.toggleFunction);
1959			el.toggleFunction = null;
1960			return true;
1961		}
1962		if(type == 'hover') {
1963			el.toggleFunctionHover = function(event) {
1964				if(event.target && this != event.target)
1965					return false;
1966				window.hikashop.toggleOverlayBlock(el, 'hover', true);
1967			};
1968			if(window.jQuery) {
1969				jQuery(el).on('mouseleave', el.toggleFunctionHover);
1970			} else {
1971				o.addEvent(el, "mouseout", el.toggleFunctionHover);
1972			}
1973		}
1974		var f = function(evt) {
1975			if (!evt) var evt = window.event;
1976			var trg = (window.event) ? evt.srcElement : evt.target;
1977			while(trg != null) {
1978				if(trg == el)
1979					return;
1980				trg = trg.parentNode;
1981			}
1982			t.toggleOverlayBlock(el);
1983			o.removeEvent(document, "click", f);
1984			el.toggleFunction = null;
1985		};
1986		el.toggleFunction = f;
1987		setTimeout(function(){ o.addEvent(document, "click", f); }, 100);
1988		return true;
1989	},
1990	addToCompare: function(el) {
1991		var t = this, d = document, w = window, o = w.Oby;
1992		if(!t.compare_list)
1993			t.compare_list = {};
1994
1995		if(el.disabled)
1996			return false;
1997
1998		var product_id = parseInt(el.getAttribute('data-addToCompare')),
1999			product_name = el.getAttribute('data-product-name'),
2000			css = el.getAttribute('data-addTo-class');
2001
2002		if(isNaN(product_id) || product_id <= 0)
2003			return false;
2004		if(!css || css == '')
2005			css = 'hika-compare';
2006
2007		var adding = !t.compare_list.hasOwnProperty(product_id);
2008		if(adding)
2009			t.compare_list[product_id] = product_name;
2010		else
2011			delete t.compare_list[product_id];
2012
2013		var elems = d.querySelectorAll('[data-addToCompare="'+product_id+'"]');
2014		if(elems && elems.forEach) {
2015			elems.forEach(function(e){
2016				if(e.nodeName.toLowerCase() == 'input' && e.type.toLowerCase() == 'checkbox')
2017					e.checked = adding;
2018				if(adding)
2019					o.addClass(e, css);
2020				else
2021					o.removeClass(e, css);
2022			});
2023		}
2024		var size = 0;
2025		if(Object.keys) {
2026			size = Object.keys(t.compare_list).length;
2027		} else {
2028			for(var k in t.compare_list) {
2029				if(compare_list.hasOwnProperty(k))
2030					size++;
2031			}
2032		}
2033		var triggers = window.Oby.fireAjax('compare.updated', {el: el, product_id: product_id, added: adding, list: t.compare_list, size: size});
2034		if(triggers !== false && triggers.length > 0)
2035			return false;
2036		return false;
2037	},
2038	toggleOptions: function() {
2039		var d = document, btnText = d.getElementById('openSearch_btn'),
2040		tagsDiv = d.getElementById('hikashop_listing_filters_id');
2041		if(tagsDiv.classList.contains("hidden-features") ) {
2042			tagsDiv.classList.remove("hidden-features");
2043			tagsDiv.classList.add("show-features");
2044			btnText.innerHTML = "<i class='fas fa-chevron-up'></i>";
2045		} else {
2046			tagsDiv.classList.remove("show-features");
2047			tagsDiv.classList.add("hidden-features");
2048			btnText.innerHTML = "<i class='fas fa-chevron-down'></i>";
2049		}
2050		return false;
2051	},
2052	clearOptions: function(options, defaults) {
2053		var d = document, btnText = d.getElementById('openSearch_btn'),
2054		tagsDiv = d.getElementById('hikashop_listing_filters_id');
2055		if(!options)
2056			return false;
2057		for(var i = options.length - 1; i >= 0; i--) {
2058			var name = 'filter_' + options[i];
2059			var el = d.getElementById(name);
2060			if(!el) {
2061				console.log('Filter option '+name+' not found');
2062				continue;
2063			}
2064			el.value = defaults[i];
2065		}
2066		var	clearEl = document.getElementById('hkclear');
2067		if(clearEl) {
2068			clearEl.value = 1;
2069		}
2070		return true;
2071	},
2072	clearSearch: function(el, id, all) {
2073		if(el.form.limitstart)
2074			el.form.limitstart.value = 0;
2075		var search = document.getElementById(id);
2076		if(search)
2077			search.value = '';
2078		if(all) {
2079			var v, els = el.form.querySelectorAll('[data-search-clear]');
2080			for(var i = els.length - 1; i >= 0; i--) {
2081				v = els[i].getAttribute('data-search-clear');
2082				els[i].value = v;
2083			}
2084		} else
2085			all = false;
2086		var triggers = window.Oby.fireAjax('search.cleared', {el: el, id: id, all: all});
2087		if(triggers !== false && triggers.length > 0)
2088			return false;
2089		el.form.submit();
2090		return true;
2091	},
2092	compareProducts: function(el, elems) {
2093		var t = this, params = '',
2094			url = el.getAttribute('data-compare-href');
2095		if(!url)
2096			return false;
2097		if(!elems)
2098			elems = t.compare_list;
2099		if(!elems)
2100			return false;
2101		for(var k in elems) {
2102			if(!elems.hasOwnProperty)
2103				continue;
2104			if(params != '') params += '&';
2105			params += 'cid[]=' + k;
2106		}
2107		el.href = url + ((url.indexOf('?') >= 0) ? '&' : '?') + params;
2108		return true;
2109	},
2110	toggleField: function(new_value, namekey, field_type, id, prefix, type) {
2111		var d = document, checked = 0, size = 0, obj = null, specialField = false,
2112			checkedGood = [], count = [], el = null,
2113			arr = d.getElementsByName('data['+field_type+']['+namekey+'][]');
2114
2115		// handle when this is passed in new_value
2116		// it allows for better handling of radios and checkboxes
2117		if(new_value instanceof Element) {
2118			if(new_value.type == 'radio' || new_value.type == 'checkbox') {
2119				elements = d.getElementsByName(new_value.name);
2120				for(cpt = 0; cpt < elements.length; cpt++) {
2121					if(elements[cpt].checked) {
2122						new_value = elements[cpt].value;
2123					}
2124				}
2125				if(new_value instanceof Element) {
2126					new_value = '';
2127				}
2128			} else {
2129				new_value = new_value.value;
2130			}
2131		} 
2132		if(!arr)
2133			return false;
2134
2135		if( new_value === null) {
2136			if(d.getElementById(type + namekey))
2137				new_value = d.getElementById(type + namekey).value;
2138			else {
2139				inputs = d.getElementsByName('data['+field_type+']['+namekey+']');
2140				for(var i = inputs.length - 1; i >= 0; i--) {
2141					if(inputs[i].checked)
2142						new_value = inputs[i].value;
2143				}
2144			}
2145		}
2146
2147		if(!this.fields_data && window.hikashopFieldsJs) {
2148			this.fields_data = window.hikashopFieldsJs;
2149		} else {
2150			for(var n in window.hikashopFieldsJs) {
2151				if(!window.hikashopFieldsJs.hasOwnProperty(n)) continue;
2152				if(this.fields_data[n]) continue;
2153				this.fields_data[n] = window.hikashopFieldsJs[n];
2154			}
2155		}
2156		if(this.fields_data === undefined || this.fields_data[field_type] === undefined) {
2157
2158		} else {
2159			size = (arr[0] && arr[0].length !== undefined) ? arr[0].length : arr.length;
2160
2161			if(prefix === undefined || !prefix || prefix.length == 0 || prefix.substr(-1) != '_')
2162				prefix = 'hikashop_';
2163
2164			var elementName = prefix + field_type + '_' + namekey;
2165			if(id)
2166				elementName = elementName + '_' + id;
2167			el = document.getElementById(elementName);
2168			var parentHidden = (el && el.style.display && el.style.display == 'none');
2169
2170			for(var c = 0; c < size; c++) {
2171				if(arr && arr[0] != undefined && arr[0].length != undefined)
2172					obj = d.getElementsByName('data['+field_type+']['+namekey+'][]').item(0).item(c);
2173				else
2174					obj = d.getElementsByName('data['+field_type+']['+namekey+'][]').item(c);
2175
2176				if(obj.checked || obj.selected)
2177					checked++;
2178
2179				if((obj.type && obj.type == 'checkbox') || obj.selected)
2180					specialField = true;
2181			}
2182			var data = this.fields_data[field_type][namekey];
2183
2184			for(var k in data) {
2185				if(typeof data[k] != 'object')
2186					continue;
2187
2188				for(var l in data[k]) {
2189					if(typeof data[k][l] != 'string')
2190						continue;
2191
2192					if (typeof count[k] == 'undefined') {
2193						count[k] = 0;
2194						checkedGood[k] = 0;
2195					}
2196					count[k]++;
2197					newEl = d.getElementById(namekey + '_' + k);
2198					if(newEl && (newEl.checked || newEl.selected)) {
2199						checkedGood[k]++;
2200						break;
2201					}
2202				}
2203			}
2204
2205			specialField = specialField || (arr[0] && arr[0].length && count.length > 1);
2206
2207			for(var j in data) {
2208				if(typeof data[j] != 'object')
2209					continue;
2210				for(var i in data[j]) {
2211					if(typeof data[j][i] != 'string')
2212						continue;
2213
2214					var elementName = prefix + field_type + '_' + data[j][i];
2215					if(id)
2216						elementName = elementName + '_' + id;
2217					el = document.getElementById(elementName);
2218					if(!el)
2219						continue;
2220					var condition = 'IS';
2221					if(data[j]['condition'] && data[j]['condition'][i])
2222						condition = data[j]['condition'][i];
2223					if( condition != 'IS NOT') {
2224						var display = 'none';
2225						var value = '';
2226					} else {
2227						var display = '';
2228						var value = el.value;
2229					}
2230					if(!parentHidden &&
2231						(
2232							(specialField && checkedGood[j] == count[j] && new_value != '')
2233							||
2234							(!specialField &&
2235								(
2236									j == new_value
2237									||
2238									(checkedGood[j] && count[j] && checkedGood[j] == count[j])
2239								)
2240							)
2241						)
2242					) {
2243						if(condition != 'IS NOT') {
2244							display = '';
2245							value = el.value;
2246						} else {
2247							display = 'none';
2248							value = '';
2249						}
2250					}
2251
2252					// set and unset the required attribute so that HTML5 validation works properly with hidden fields
2253					var input = document.getElementById(data[j][i]);
2254					if(input && input.hasAttribute('aria-required')) {
2255						if(display == 'none') {
2256							input.removeAttribute('required');
2257							input.classList.remove('required', 'invalid', 'form-control-danger');
2258							input.setAttribute('aria-invalid', 'false');
2259							var label = document.querySelector("label[for='"+data[j][i]+"']");
2260							if (label) {
2261								label.classList.remove("invalid");
2262								var fb = label.querySelector('.form-control-feedback');
2263								if (fb) {
2264									fb.remove();
2265								}
2266							}
2267						} else {
2268							input.setAttribute('required', 'required');
2269							input.classList.add('required');
2270						}
2271					}
2272
2273					el.style.display = display;
2274					this.toggleField(value, data[j][i], field_type, id, prefix);
2275				}
2276			}
2277		}
2278		if(window.Oby && window.Oby.fireAjax)
2279			window.Oby.fireAjax("hkCustomFieldChanged", {field_type: field_type, namekey: namekey});
2280		return false;
2281	},
2282	updateFieldError: function(el) {
2283		if(!el || !el.id) return;
2284		var errorSpan = document.getElementById(el.id + '_error');
2285		if(!errorSpan) return;
2286		var isInvalid = el.classList.contains('invalid') || (el.validity && !el.validity.valid);
2287		if(isInvalid) {
2288			var msg = el.getAttribute('data-validation-text') || errorSpan.getAttribute('data-validation-text') || '';
2289			errorSpan.textContent = msg;
2290			errorSpan.style.display = '';
2291			el.setAttribute('aria-invalid', 'true');
2292		} else {
2293			errorSpan.textContent = '';
2294			errorSpan.style.display = 'none';
2295			el.removeAttribute('aria-invalid');
2296		}
2297	}
2298};
2299window.hikashop = hikashop;
2300
2301if(oldHikaShop && oldHikaShop instanceof Object) {
2302	for (var attr in oldHikaShop) {
2303		if (oldHikaShop.hasOwnProperty(attr) && !window.hikashop.hasOwnProperty(attr))
2304			window.hikashop[attr] = oldHikaShop[attr];
2305	}
2306}
2307})();
2308
2309function tableOrdering(order, dir, task) {
2310	var form = document.adminForm;
2311	form.filter_order.value = order;
2312	form.filter_order_Dir.value	= dir;
2313	submitform(task);
2314}
2315
2316function submitform(pressbutton) {
2317	var d = document;
2318	if(!d.adminForm)
2319		return false;
2320	if(pressbutton)
2321		d.adminForm.task.value = pressbutton;
2322
2323	// old codeMirror code
2324	if(typeof(CodeMirror) == 'function') {
2325		for(x in CodeMirror.instances) {
2326			d.getElementById(x).value = CodeMirror.instances[x].getCode();
2327		}
2328	}
2329	// Creating the event
2330	var event = new Event('submit', {
2331		'bubbles'    : true, // Whether the event will bubble up through the DOM or not
2332		'cancelable' : true  // Whether the event may be canceled or not
2333	});
2334	if(d.adminForm.dispatchEvent( event )) {
2335		d.adminForm.submit();
2336	}
2337	return false;
2338}
2339
2340if(!window.submitbutton) {
2341	window.submitbutton = function(name) { submitform(name); };
2342}
2343
2344function hikashopCheckChangeForm(type, form) {
2345	if(!form)
2346		return true;
2347	var varform = document[form];
2348
2349	if(typeof(hikashopFieldsJs) == 'undefined' || typeof(hikashopFieldsJs['reqFieldsComp']) == 'undefined' || typeof(hikashopFieldsJs['reqFieldsComp'][type]) == 'undefined' || hikashopFieldsJs['reqFieldsComp'][type].length <= 0)
2350		return true;
2351
2352	var d = document;
2353	for(var i = 0; i < hikashopFieldsJs['reqFieldsComp'][type].length; i++) {
2354		elementName = 'data['+type+']['+hikashopFieldsJs['reqFieldsComp'][type][i]+']';
2355		if(typeof(varform.elements[elementName]) == 'undefined')
2356			elementName = type+'_'+hikashopFieldsJs['reqFieldsComp'][type][i];
2357
2358		elementToCheck = varform.elements[elementName];
2359		elementId = 'hikashop_'+type+'_'+ hikashopFieldsJs['reqFieldsComp'][type][i];
2360		el = d.getElementById(elementId);
2361
2362		if(elementToCheck && (typeof el == 'undefined' || el == null || typeof el.style == 'undefined' || el.style.display!='none') && !hikashopCheckField(elementToCheck,type,i,elementName,varform.elements)) {
2363			if(typeof(hikashopFieldsJs['entry_id']) == 'undefined')
2364				return false;
2365
2366			for(var j = 1; j <= hikashop['entry_id']; j++) {
2367				elementName = 'data['+type+'][entry_'+j+']['+hikashopFieldsJs['reqFieldsComp'][type][i]+']';
2368				elementToCheck = varform.elements[elementName];
2369				elementId = 'hikashop_'+type+'_'+ hikashopFieldsJs['reqFieldsComp'][type][i] + '_' + j;
2370				el = d.getElementById(elementId);
2371				if(elementToCheck && (typeof el == 'undefined' || el == null || typeof el.style == 'undefined' || el.style.display != 'none') && !hikashopCheckField(elementToCheck,type,i,elementName,varform.elements)) {
2372					return false;
2373				}
2374			}
2375		}
2376	}
2377
2378	if(type == 'register') {
2379		// check the password confirmation field only if we are in selector registration and that the user selected "registration" or "simplified registration", or that the registration is on "all in one page" and that the password confirmation field is there
2380		var register = d.getElementById('data_register_registration_method0');
2381		if(!register)
2382			register = d.getElementById('data[register][registration_method]0');
2383
2384		var simplified_pwd = d.getElementById('data_register_registration_method3');
2385		if(!simplified_pwd)
2386			simplified_pwd = d.getElementById('data[register][registration_method]3');
2387
2388		if((simplified_pwd && simplified_pwd.checked) || (register && register.checked) || (!simplified_pwd && !register)) {
2389			// check password
2390			if(typeof(varform.elements['data[register][password]']) != 'undefined' && typeof(varform.elements['data[register][password2]']) != 'undefined') {
2391				passwd = varform.elements['data[register][password]'];
2392				passwd2 = varform.elements['data[register][password2]'];
2393				if(passwd.value != passwd2.value) {
2394					alert(hikashopFieldsJs['password_different']);
2395					return false;
2396				}
2397			}
2398		}
2399
2400		//check email
2401		var emailField = varform.elements['data[register][email]'];
2402		emailField.value = emailField.value.replace(/ /g,"");
2403		var filter = /^([a-z0-9_'&\.\-\+])+\@(([a-z0-9\-])+\.)+([a-z0-9]{2,14})+$/i;
2404		if(!emailField || !filter.test(emailField.value)) {
2405			alert(hikashopFieldsJs['valid_email']);
2406			return false;
2407		}
2408	} else if(type == 'address' && typeof(varform.elements['data[address][address_telephone]']) != 'undefined') {
2409		var phoneField = varform.elements['data[address][address_telephone]'], filter = /[0-9]+/i;
2410		if(phoneField) {
2411			phoneField.value = phoneField.value.replace(/ /g,"");
2412			if(phoneField.value.length > 0 && !filter.test(phoneField.value)) {
2413				alert(hikashopFieldsJs['valid_phone']);
2414				return false;
2415			}
2416		}
2417	}
2418
2419	return true;
2420}
2421
2422function hikashopCheckField(elementToCheck, type, i, elementName, form) {
2423	if(!elementToCheck)
2424		return true;
2425
2426	var d = document, isValid = false;
2427	if(typeof(elementToCheck.value) != 'undefined') {
2428		if(elementToCheck.value == ' ' && typeof(form[elementName+'[]']) != 'undefined') {
2429			if(form[elementName+'[]'].checked) {
2430				isValid = true;
2431			} else {
2432				for(var a = form[elementName+'[]'].length - 1; a >= 0; a--) {
2433					if(form[elementName+'[]'][a].checked && form[elementName+'[]'][a].value.length > 0)
2434						isValid = true;
2435				}
2436			}
2437		} else if(elementToCheck.value.length > 0){
2438			var found = false;
2439			for(var j in hikashopFieldsJs['regexFieldsComp'][type]) {
2440				if(hikashopFieldsJs['regexFieldsComp'][type][j] == hikashopFieldsJs['reqFieldsComp'][type][i]) found = j;
2441			}
2442			if(typeof(hikashopFieldsJs['regexFieldsComp']) != 'undefined' && typeof(hikashopFieldsJs['regexFieldsComp'][type]) != 'undefined' && found){
2443				myregexp = new RegExp(hikashopFieldsJs['regexValueFieldsComp'][type][found]);
2444				if(myregexp.test(elementToCheck.value)){
2445					isValid = true;
2446				}
2447			}else{
2448				isValid = true;
2449			}
2450		}
2451	} else {
2452		for(var a = elementToCheck.length - 1; a >= 0; a--) {
2453			 if(elementToCheck[a].checked && elementToCheck[a].value.length > 0)
2454			 	isValid = true;
2455		}
2456	}
2457
2458	// Case for the switcher display, ignore check according to the method selected
2459	// joomla 3 ids are differents than joomla 1.5...
2460	var simplified_pwd = d.getElementById('data_register_registration_method3');
2461	if(!simplified_pwd) simplified_pwd = d.getElementById('data[register][registration_method]3');
2462
2463	var simplified = d.getElementById('data_register_registration_method1');
2464	if(!simplified) simplified = d.getElementById('data[register][registration_method]1');
2465
2466	var guest = d.getElementById('data_register_registration_method2');
2467	if(!guest) guest = d.getElementById('data[register][registration_method]2');
2468
2469	if(!isValid && ((simplified && simplified.checked) || (guest && guest.checked) ) && (elementName == 'data[register][password]' || elementName == 'data[register][password2]')){
2470		window.Oby.addClass(elementToCheck, 'invalid');
2471		return true;
2472	}
2473
2474	if(!isValid && ( (simplified && simplified.checked) || (guest && guest.checked) || (simplified_pwd && simplified_pwd.checked) ) && (elementName == 'data[register][name]' || elementName == 'data[register][username]')) {
2475		window.Oby.addClass(elementToCheck, 'invalid');
2476		return true;
2477	}
2478	if(!isValid) {
2479		window.Oby.addClass(elementToCheck, 'invalid');
2480		alert(hikashopFieldsJs['validFieldsComp'][type][i]);
2481		return false;
2482	} else {
2483		window.Oby.removeClass(elementToCheck, 'invalid');
2484	}
2485	return true;
2486}
2487
2488window.hikashop.ready(function(){
2489	if(window.jQuery && typeof(jQuery.noConflict) == "function" && !window.hkjQuery) {
2490		window.hkjQuery = jQuery.noConflict();
2491	}
2492	if(window.hikaVotes && typeof(initVote) == 'function')
2493		initVote();
2494	// timeout to make sure that everything is rendered before triggering the height calculations
2495	window.setTimeout(
2496		function(){
2497			window.hikashop.checkConsistency();
2498		},
2499		50
2500	);
2501});
2502if(window.jQuery && typeof(jQuery.noConflict) == "function" && !window.hkjQuery) {
2503	window.hkjQuery = jQuery.noConflict();
2504}

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.