PageSourceSearch

https://www.prairiemoon.com/mm5/clientside.mvc?T=9f09a8e2&Filename=runtime_ui.js

js prairiemoon.com collected 2026-10-01 10:30:32 UTC 77,842 bytes, 2,673 lines download raw bytes

1// Miva Merchant
2//
3// This file and the source codes contained herein are the property of
4// Miva, Inc.  Use of this file is restricted to the specific terms and
5// conditions in the License Agreement associated with this file.  Distribution
6// of this file or portions of this file for uses not covered by the License
7// Agreement is not allowed without a written agreement signed by an officer of
8// Miva, Inc.
9//
10// Copyright 1998-2026 Miva, Inc.  All rights reserved.
11// http://www.miva.com
12//
13
14function DeriveFrom( base_class, child_class )
15{
16	var placeholder;
17
18	placeholder							= function() { ; }
19	placeholder.prototype				= base_class.prototype;
20	child_class.prototype				= new placeholder();
21	child_class.prototype.constructor	= child_class;
22}
23
24function newElement( type, attributes, styles, parent )
25{
26	var element;
27
28	element = document.createElement( type );
29
30	setElementAttributes( element, attributes );
31	setElementStyles( element, styles );
32
33	if ( parent )
34	{
35		parent.appendChild( element );
36	}
37
38	return element;
39}
40
41function newTextNode( text, parent )
42{
43	var element;
44
45	element	= document.createTextNode( text );
46
47	if ( parent )
48	{
49		parent.appendChild( element );
50	}
51
52	return element;
53}
54
55function newTextNode_EmptyParent( text, parent )
56{
57	if ( parent )
58	{
59		parent.innerHTML = '';
60	}
61
62	return newTextNode( text, parent );
63}
64
65function setElementAttributes( element, attributes )
66{
67	var attribute;
68
69	for ( attribute in attributes )
70	{
71		if ( !attributes.hasOwnProperty( attribute ) )
72		{
73			continue;
74		}
75
76		if ( attribute == 'name' )			element.name		= attributes[ attribute ];
77		else if ( attribute == 'class' )	element.className	= attributes[ attribute ];
78		else								element.setAttribute( attribute, attributes[ attribute ] );
79	}
80}
81
82function setElementStyles( element, styles )
83{
84	var style;
85
86	for ( style in styles )
87	{
88		if ( styles.hasOwnProperty( style ) )
89		{
90			element.style[ style ] = styles[ style ];
91		}
92	}
93}
94
95function classNameContains( element, classname, allow_regex_in_classname )
96{
97	var i, i_len, classlist;
98
99	classlist		= element.className.split( /\s/ );
100
101	for ( i = 0, i_len = classlist.length; i < i_len; i++ )
102	{
103		if ( classlist[ i ] == '' )																						continue;
104		else if ( !allow_regex_in_classname && classlist[ i ] == classname )											return true;
105		else if ( allow_regex_in_classname && classlist[ i ].match( new RegExp( '(?:^)' + classname + '(?:$)' ) ) )		return true;
106	}
107
108	return false;
109}
110
111function classNameReplace( element, classname, replacement_classname, allow_regex_in_classname )
112{
113	var i, i_len, classlist, new_classlist;
114
115	new_classlist	= new Array();
116	classlist		= element.className.split( /\s/ );
117
118	for ( i = 0, i_len = classlist.length; i < i_len; i++ )
119	{
120		if ( classlist[ i ] == '' )																						continue;
121		else if ( !allow_regex_in_classname && classlist[ i ] == classname )											continue;
122		else if ( allow_regex_in_classname && classlist[ i ].match( new RegExp( '(?:^)' + classname + '(?:$)' ) ) )		continue;
123
124		new_classlist.push( classlist[ i ] );
125	}
126
127	new_classlist.push( replacement_classname );
128
129	return new_classlist.join( ' ' );
130}
131
132function classNameReplaceIfAltered( element, classname, replacement_classname, allow_regex_in_classname )
133{
134	var new_classlist, original_classlist;
135
136	original_classlist		= element.className.split( /\s/ ).map( function( filter_classname ) { return trim( filter_classname ); } );
137	original_classlist		= original_classlist.filter( function( filter_classname ) { return filter_classname !== ''; } );
138	new_classlist			= original_classlist.filter( function( filter_classname )
139	{
140		if ( filter_classname === replacement_classname )
141		{
142			return false;
143		}
144
145		if ( !allow_regex_in_classname )
146		{
147			return filter_classname !== classname;
148		}
149
150		return !filter_classname.match( new RegExp( '(?:^)' + classname + '(?:$)' ) );
151	} );
152
153	new_classlist.push( replacement_classname );
154
155	new_classlist.sort();
156	original_classlist.sort();
157
158	if ( !compareObjects( original_classlist, new_classlist ) )
159	{
160		element.className = new_classlist.join( ' ' );
161	}
162}
163
164function classNameAdd( element, classname )
165{
166	var new_classlist, original_classlist;
167
168	original_classlist		= element.className.split( /\s/ ).map( function( filter_classname ) { return trim( filter_classname ); } );
169	new_classlist			= original_classlist.filter( function( filter_classname ) { return ( filter_classname !== '' && filter_classname !== classname ); } );
170
171	new_classlist.push( classname );
172
173	return new_classlist.join( ' ' );
174}
175
176function classNameAddIfMissing( element, classname )
177{
178	var new_classlist, original_classlist;
179
180	if ( Array.isArray( classname ) )
181	{
182		return classNameAddListIfMissing( element, classname );
183	}
184
185	original_classlist		= element.className.split( /\s/ ).map( function( filter_classname ) { return trim( filter_classname ); } );
186	original_classlist		= original_classlist.filter( function( filter_classname ) { return filter_classname !== ''; } );
187	new_classlist			= original_classlist.filter( function( filter_classname ) { return filter_classname !== classname; } );
188
189	new_classlist.push( classname );
190
191	new_classlist.sort();
192	original_classlist.sort();
193
194	if ( !compareObjects( original_classlist, new_classlist ) )
195	{
196		element.className = new_classlist.join( ' ' );
197	}
198}
199
200function classNameAddList( element, classnamelist )
201{
202	var new_classlist, original_classlist;
203
204	original_classlist	= element.className.split( /\s/ ).map( function( filter_classname ) { return trim( filter_classname ); } );
205	new_classlist		= original_classlist.filter( function( filter_classname ) { return filter_classname !== '' && classnamelist.indexOf( filter_classname ) === -1; } );
206	new_classlist		= new_classlist.concat( classnamelist );
207
208	return new_classlist.join( ' ' );
209}
210
211function classNameAddListIfMissing( element, classnamelist )
212{
213	var new_classlist, original_classlist;
214
215	original_classlist		= element.className.split( /\s/ ).map( function( filter_classname ) { return trim( filter_classname ); } );
216	original_classlist		= original_classlist.filter( function( filter_classname ) { return filter_classname !== ''; } );
217	new_classlist			= original_classlist.filter( function( filter_classname ) { return classnamelist.indexOf( filter_classname ) === -1; } );
218	new_classlist			= new_classlist.concat( classnamelist );
219
220	new_classlist.sort();
221	original_classlist.sort();
222
223	if ( !compareObjects( original_classlist, new_classlist ) )
224	{
225		element.className = new_classlist.join( ' ' );
226	}
227}
228
229function classNameRemove( element, classname, allow_regex_in_classname )
230{
231	var new_classlist, original_classlist;
232
233	original_classlist		= element.className.split( /\s/ ).map( function( filter_classname ) { return trim( filter_classname ); } );
234	new_classlist			= original_classlist.filter( function( filter_classname )
235	{
236		if ( filter_classname === '' )			return false;
237		else if ( !allow_regex_in_classname )	return filter_classname !== classname;
238
239		return !filter_classname.match( new RegExp( '(?:^)' + classname + '(?:$)' ) );
240	} );
241
242	return new_classlist.join( ' ' );
243}
244
245function classNameRemoveIfPresent( element, classname, allow_regex_in_classname )
246{
247	var new_classlist, original_classlist;
248
249	if ( Array.isArray( classname ) )
250	{
251		return classNameRemoveListIfPresent( element, classname, allow_regex_in_classname );
252	}
253
254	original_classlist		= element.className.split( /\s/ ).map( function( filter_classname ) { return trim( filter_classname ); } );
255	original_classlist		= original_classlist.filter( function( filter_classname ) { return filter_classname !== ''; } );
256	new_classlist			= original_classlist.filter( function( filter_classname )
257	{
258		if ( !allow_regex_in_classname )
259		{
260			return filter_classname !== classname;
261		}
262
263		return !filter_classname.match( new RegExp( '(?:^)' + classname + '(?:$)' ) );
264	} );
265
266	new_classlist.sort();
267	original_classlist.sort();
268
269	if ( !compareObjects( original_classlist, new_classlist ) )
270	{
271		element.className = new_classlist.join( ' ' );
272	}
273}
274
275function classNameRemoveList( element, classnamelist, allow_regex_in_classname )
276{
277	var new_classlist, original_classlist;
278
279	original_classlist		= element.className.split( /\s/ ).map( function( filter_classname ) { return trim( filter_classname ); } );
280	new_classlist			= original_classlist.filter( function( filter_classname )
281	{
282		var i, i_len;
283
284		if ( filter_classname === '' )			return false;
285		else if ( !allow_regex_in_classname )	return classnamelist.indexOf( filter_classname ) === -1;
286
287		for ( i = 0, i_len = classnamelist.length; i < i_len; i++ )
288		{
289			if ( filter_classname.match( new RegExp( '(?:^)' + classnamelist[ i ] + '(?:$)' ) ) )
290			{
291				return false;
292			}
293		}
294
295		return true;
296	} );
297
298	return new_classlist.join( ' ' );
299}
300
301function classNameRemoveListIfPresent( element, classnamelist, allow_regex_in_classname )
302{
303	var new_classlist, original_classlist;
304
305	original_classlist		= element.className.split( /\s/ ).map( function( filter_classname ) { return trim( filter_classname ); } );
306	original_classlist		= original_classlist.filter( function( filter_classname ) { return filter_classname !== ''; } );
307	new_classlist			= original_classlist.filter( function( filter_classname )
308	{
309		var i, i_len;
310
311		if ( !allow_regex_in_classname )
312		{
313			return classnamelist.indexOf( filter_classname ) === -1;
314		}
315
316		for ( i = 0, i_len = classnamelist.length; i < i_len; i++ )
317		{
318			if ( filter_classname.match( new RegExp( '(?:^)' + classnamelist[ i ] + '(?:$)' ) ) )
319			{
320				return false;
321			}
322		}
323
324		return true;
325	} );
326
327	new_classlist.sort();
328	original_classlist.sort();
329
330	if ( !compareObjects( original_classlist, new_classlist ) )
331	{
332		element.className = new_classlist.join( ' ' );
333	}
334}
335
336function computedStyleValue( element, property )
337{
338	if ( element instanceof Element )
339	{
340		return window.getComputedStyle( element, null ).getPropertyValue( property )
341	}
342	else if ( element.ownerDocument && element.ownerDocument.defaultView && element instanceof element.ownerDocument.defaultView.Element )
343	{
344		return element.ownerDocument.defaultView.getComputedStyle( element, null ).getPropertyValue( property );
345	}
346
347	return returnUndefined()
348}
349
350function elementExistsInNodeType( element, type )
351{
352	while ( element )
353	{
354		if ( element.nodeName.toLowerCase() == type.toLowerCase() )
355		{
356			return true;
357		}
358
359		element = element.parentNode;
360	}
361
362	return false;
363}
364
365function getNearestElementAncestorNodeTypeOrNull( element, type /* can be comma separated string */ )
366{
367	var typelist = type.toLowerCase().split( ',' );
368
369	while ( element )
370	{
371		if ( typelist.indexOf( element.nodeName.toLowerCase() ) != -1 )
372		{
373			return element;
374		}
375
376		element = element.parentNode;
377	}
378
379	return null;
380}
381
382function getNearestElementAncestorWithStyleValue( element, property, value /* can be comma separated string */ )
383{
384	var style, valuelist;
385
386	valuelist = value.toLowerCase().split( ',' );
387
388	while ( element )
389	{
390		style = computedStyleValue( element, property );
391
392		if ( typeof style === 'string' && valuelist.indexOf( style.toLowerCase() ) !== -1 )
393		{
394			return element;
395		}
396
397		element = element.parentNode;
398	}
399
400	return null;
401}
402
403function getPreviousTreeNode( start_node, root_node )
404{
405	if ( start_node === root_node )
406	{
407		return null;
408	}
409	else if ( start_node.previousSibling )
410	{
411		if ( start_node.previousSibling.hasChildNodes() )
412		{
413			return start_node.previousSibling.childNodes[ start_node.previousSibling.childNodes.length - 1 ];
414		}
415
416		return start_node.previousSibling;
417	}
418	else if ( start_node.parentNode && start_node.parentNode !== root_node )
419	{
420		return start_node.parentNode;
421	}
422
423	return null;
424}
425
426function getNextTreeNode( start_node, root_node )
427{
428	var current_node;
429
430	if ( start_node.hasChildNodes() )
431	{
432		return start_node.childNodes[ 0 ];
433	}
434	else if ( start_node !== root_node )
435	{
436		if ( start_node.nextSibling )
437		{
438			return start_node.nextSibling;
439		}
440		else
441		{
442			current_node = start_node;
443
444			while ( current_node = current_node.parentNode )
445			{
446				if ( current_node === root_node )
447				{
448					return null;
449				}
450				else if ( current_node.nextSibling )
451				{
452					return current_node.nextSibling;
453				}
454			}
455		}
456	}
457
458	return null;
459}
460
461function getScopedElementsByClassName( className, scope )
462{
463    var regex_match, all_elements, results, i;
464    
465    regex_match     = new RegExp( "(?:^|\\s)" + className + "(?:$|\\s)" );
466    all_elements    = scope.getElementsByTagName( '*' );
467    results         = [];
468    
469    for ( i = 0; all_elements[ i ] != null; i++ )
470    {
471        if ( typeof all_elements[ i ].className === 'string' && ( all_elements[ i ].className.indexOf( className ) != -1 ) && regex_match.test( all_elements[ i ].className ) )
472        {
473            results.push( all_elements[ i ] );
474        }
475    }
476    
477    return results;
478}
479
480function getScopedElementByName( name, parent_element )
481{
482	var i, i_len, children;
483
484	children = parent_element.getElementsByTagName( '*' );
485
486	if ( children )
487	{
488		for ( i = 0, i_len = children.length; i < i_len; i++ )
489		{
490			if ( children[ i ].name == name )
491			{
492				return children[ i ];
493			}
494		}
495	}
496
497	return null;
498}
499
500function containsChild( parent_elem, element )
501{
502	if ( element )
503	{
504		while ( ( element = element.parentNode ) != null )
505		{
506			if ( element === parent_elem )
507			{
508				return true;
509			}
510		}
511	}
512
513	return false;
514}
515
516function getScrollOffset( e )
517{
518	if ( e.type == 'wheel' )									return { x: -e.deltaX, y: -e.deltaY };
519	else if ( ( e.deltaX || e.deltaY ) && e.deltaMode == 0 )	return { x: ( e.deltaX ? -e.deltaX : 0 ), y: ( e.deltaY ? -e.deltaY : 0 ) };
520	else if ( ( e.deltaX || e.deltaY ) && e.deltaMode != 0 )	return { x: ( e.deltaX ? -e.deltaX * 18 : 0 ), y: ( e.deltaY ? -e.deltaY * 18 : 0 ) };
521	else if ( e.wheelDeltaX || e.wheelDeltaY )					return { x: ( originalEvent.wheelDeltaX ? ( originalEvent.wheelDeltaX / 2 ) : 0 ), y: ( originalEvent.wheelDeltaY ? ( originalEvent.wheelDeltaY / 2 ) : ( -originalEvent.wheelDelta / 2 ) ) };
522	else if ( e.detail )										return { x: 0, y: -originalEvent.detail * 18 };
523	else														return { x: 0, y: ( e.wheelDelta ? e.wheelDelta / 2 : 0 ) };
524}
525
526function offsetLeft( element )
527{
528	var offset = 0;
529
530	while ( element )
531	{
532		offset += element.offsetLeft;
533		element = element.offsetParent;
534	}
535
536	return offset;
537}
538
539function offsetTop( element )
540{
541	var offset = 0;
542
543	while ( element )
544	{
545		offset += element.offsetTop;
546		element = element.offsetParent;
547	}
548
549	return offset;
550}
551
552function testBoxModel()
553{
554	var div;
555
556	div							= document.createElement( 'div' );
557	div.style.width				= '1px';
558	div.style.paddingLeft		= '1px';
559
560	document.body.appendChild( div );
561
562	window.boxModelSupported	= div.offsetWidth === 2;
563	window.boxModelTested		= true;
564
565	document.body.removeChild( div ).style.display = 'none';
566}
567
568function getScrollTop()
569{
570	if ( !window.boxModelTested ) // box model support must be done after document.body loads
571	{
572		testBoxModel();
573	}
574
575	if ( 'pageYOffset' in window )			return window.pageYOffset;
576	else if ( window.boxModelSupported )	return document.documentElement.scrollTop;
577	else									return document.body.scrollTop;
578}
579
580function getScrollLeft()
581{
582	if ( !window.boxModelTested ) // box model support must be done after document.body loads
583	{
584		testBoxModel();
585	}
586
587	if ( 'pageXOffset' in window )			return window.pageXOffset;
588	else if ( window.boxModelSupported )	return document.documentElement.scrollLeft;
589	else									return document.body.scrollLeft;
590}
591
592function cursorToEnd( element )
593{
594	var range;
595
596	element.focus();
597
598    if ( typeof element.selectionStart == 'number' )
599	{
600        element.selectionStart	= element.value.length;
601		element.selectionEnd	= element.value.length;
602    }
603	else if ( typeof element.createTextRange != 'undefined' )
604	{
605        range = element.createTextRange();
606
607        range.collapse( false );
608        range.select();
609    }
610}
611
612function clearTextSelection()
613{
614	var range;
615
616	if ( window.getSelection )
617	{
618		range = window.getSelection();
619		range.removeAllRanges();
620	}
621	else if ( document.selection && document.body.createTextRange )
622	{
623		range = document.body.createTextRange();
624		range.collapse( true );
625		range.select();
626	}
627}
628
629function keySupportsMultiSelect( e )
630{
631	if ( e.ctrlKey || e.metaKey ) // In general, e.metaKey is the Command key on Mac and the Windows key on Windows. 
632	{
633		return true;
634	}
635
636	return false;
637}
638
639function eventStopPropagation( event )
640{
641	if ( event.stopPropagation )
642	{
643		return event.stopPropagation();
644	}
645
646	event.cancelBubble = true;
647}
648
649function eventPreventDefault( event )
650{
651	if ( event.preventDefault )
652	{
653		return event.preventDefault();
654	}
655
656	event.returnValue = false;
657
658	return false;
659}
660
661function returnUndefined()
662{
663	return;
664}
665
666function AddEvent( obj, eventType, fn )
667{
668	try
669	{
670		obj.addEventListener( eventType, fn, false );
671		return true;
672	}
673	catch ( e )
674	{
675		try
676		{
677			return obj.attachEvent( 'on' + eventType, fn );
678		}
679		catch ( e )
680		{
681			return false;
682		}
683	}
684}
685
686function RemoveEvent( obj, eventType, fn )
687{
688	try
689	{
690		obj.removeEventListener( eventType, fn, false );
691		return true;
692	}
693	catch ( e )
694	{
695		try
696		{
697			return obj.detachEvent( 'on' + eventType, fn );
698		}
699		catch ( e )
700		{
701			return false;
702		}
703	}
704}
705
706function AddScrollEvent( element, original_callback )
707{
708	var event_type, callback;
709
710	if ( 'onwheel' in document.createElement( 'div' ) )	event_type = 'wheel';			// IE 9+, FF 17+
711	else if ( document.onmousewheel !== undefined )		event_type = 'mousewheel';		// IE 8-, Chrome, Safari
712	else												event_type = 'DOMMouseScroll';	// FF 16-
713
714	callback = function( originalEvent )
715	{
716		var event, mousepos;
717
718		mousepos						= captureMousePosition( originalEvent );
719		event							= new Object();
720		event.target					= originalEvent.target || originalEvent.srcElement;
721		event.timeStamp					= originalEvent.timeStamp ? originalEvent.timeStamp : new Date();
722		event.eventPhase				= originalEvent.eventPhase;
723		event.defaultPrevented			= originalEvent.defaultPrevented;
724		event.currentTarget				= originalEvent.currentTarget;
725		event.cancelable				= originalEvent.cancelable;
726		event.bubbles					= originalEvent.bubbles;
727		event.type						= 'wheel';
728		event.pageX						= mousepos.x;
729		event.pageY						= mousepos.y;
730		event.screenX					= originalEvent.screenX;
731		event.screenY					= originalEvent.screenY;
732		event.clientX					= originalEvent.clientX;
733		event.clientY					= originalEvent.clientY;
734		event.ctrlKey					= originalEvent.ctrlKey;
735		event.altKey					= originalEvent.altKey;
736		event.shiftKey					= originalEvent.shiftKey;
737		event.metaKey					= originalEvent.metaKey;
738		event.button					= originalEvent.button;
739		event.relatedTarget				= originalEvent.relatedTarget;
740		event.deltaMode					= 0;
741		event.deltaZ					= originalEvent.deltaZ || 0;
742		event.preventDefault			= function() { originalEvent.preventDefault ? originalEvent.preventDefault() : originalEvent.returnValue = false; };
743		event.stopPropagation			= function() { originalEvent.stopPropagation ? originalEvent.stopPropagation() : originalEvent.cancelBubble = true; };
744		event.stopImmediatePropagation	= originalEvent.stopImmediatePropagation;
745            
746		// calculate deltaY (and deltaX) according to the event
747		if ( event_type == 'wheel' )
748		{
749			event.deltaY				= ( originalEvent.deltaMode == 0 ? originalEvent.deltaY : ( originalEvent.deltaMode == 1 ? originalEvent.deltaY * 18 : 0 ) );
750			event.deltaX				= ( originalEvent.deltaMode == 0 ? originalEvent.deltaX : ( originalEvent.deltaMode == 1 ? originalEvent.deltaX * 18 : 0 ) );
751		}
752		else if ( event_type == 'mousewheel' )
753		{
754			event.deltaY				= ( originalEvent.wheelDeltaY ? ( -originalEvent.wheelDeltaY / 2 ) : ( -originalEvent.wheelDelta / 2 ) );
755			event.deltaX				= ( originalEvent.wheelDeltaX ? ( -originalEvent.wheelDeltaX / 2 ) : 0 );
756		}
757		else
758		{
759			event.deltaY				= originalEvent.detail * 18;
760			event.deltaX				= 0;
761		}
762
763		return original_callback( event );
764	};
765	original_callback.callback			= callback;
766
767	AddEvent( element, event_type, callback );
768}
769
770function RemoveScrollEvent( element, original_callback )
771{
772	var event_type;
773
774	if ( 'onwheel' in document.createElement( 'div' ) )	event_type = 'wheel';			// IE 9+, FF 17+
775	else if ( document.onmousewheel !== undefined )		event_type = 'mousewheel';		// IE 8-, Chrome, Safari
776	else												event_type = 'DOMMouseScroll';	// FF 16-
777
778	RemoveEvent( element, event_type, original_callback.callback );
779	original_callback.callback = null;
780}
781
782function AddClickEvent( element, onclick )
783{
784	element.mm_event_mousedown		= function( event ) { return element.Event_OnMouseDown( event ? event : window.event ); };
785	element.mm_event_mouseup		= function( event ) { return element.Event_OnMouseUp( event ? event : window.event ); };
786
787	element.Event_OnMouseDown = function( e )
788	{
789		AddEvent( document,	'mouseup',	element.mm_event_mouseup );
790		AddEvent( window,	'blur',		element.mm_event_mouseup );
791
792		eventStopPropagation( e );
793		return true;
794	}
795
796	element.Event_OnMouseUp = function( e )
797	{
798		var target = e.target;
799
800		RemoveEvent( document,	'mouseup',	element.mm_event_mouseup );
801		RemoveEvent( window,	'blur',		element.mm_event_mouseup );
802
803		if ( mouseClickType( e ) == 'RIGHT' )
804		{
805			return true;
806		}
807
808		if ( target !== element && !containsChild( element, target ) )
809		{
810			return true;
811		}
812
813		if ( typeof onclick === 'function' )
814		{
815			onclick( e );
816		}
817
818		eventStopPropagation( e );
819		return eventPreventDefault( e );
820	}
821
822	AddEvent( element, 'mousedown',	element.mm_event_mousedown );
823}
824
825function RemoveClickEvent( element, onclick )
826{
827	RemoveEvent( element,	'mousedown',	element.mm_event_mousedown );
828	RemoveEvent( document,	'mouseup',		element.mm_event_mouseup );
829	RemoveEvent( window,	'blur',			element.mm_event_mouseup );
830}
831
832function regexEscape( text )
833{
834	return text.replace( new RegExp( '[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\-]', 'g' ), '\\$&' ); // Compare to PHP's preg_quote function
835}
836
837function sortAlphaNumeric( a, b, sort_asc )
838{
839	var regex_alpha, regex_numeric, replaced_a_alpha, replaced_b_alpha, replaced_a_numeric, replaced_b_numeric;
840
841	regex_alpha				= new RegExp( '[^a-zA-Z]', 'g' );
842	regex_numeric			= new RegExp( '[^0-9]', 'g' );
843
844	replaced_a_alpha		= a.replace( regex_alpha, '' );
845	replaced_b_alpha		= b.replace( regex_alpha, '' );
846
847	if ( replaced_a_alpha === replaced_b_alpha )
848	{
849		replaced_a_numeric	= stoi( a.replace( regex_numeric, '' ) );
850		replaced_b_numeric	= stoi( b.replace( regex_numeric, '' ) );
851
852		return ( replaced_a_numeric === replaced_b_numeric ) ? 0 : ( ( replaced_a_numeric > replaced_b_numeric ) ? ( sort_asc ? 1 : -1 ) : ( sort_asc ? -1 : 1 ) );
853	}
854
855	return ( replaced_a_alpha > replaced_b_alpha ) ? ( sort_asc ? 1 : -1 ) : ( sort_asc ? -1 : 1 );
856}
857
858function stringIntersect( string_a, string_b )
859{
860	var i, j, i_len, j_len, grid, match, temp_string, longest_common_substring;
861
862	match						= '';
863	longest_common_substring	= 0;
864
865	if ( string_b.length > string_a.length )
866	{
867		temp_string				= string_b;
868		string_b				= string_a;
869		string_a				= temp_string;
870	}
871
872    grid = new Array( string_a.length );
873
874	for ( i = 0, i_len = string_a.length; i < i_len; i++ )
875	{
876		grid[ i ] = new Array( string_b.length );
877
878		for ( j = 0, j_len = string_b.length; j < j_len; j++ )
879		{
880			grid[ i ][ j ] = 0;
881		}
882	}
883
884	for ( i = 0, i_len = string_a.length; i < i_len; i++ )
885	{
886		for ( j = 0, j_len = string_b.length; j < j_len; j++ )
887		{
888			if ( string_a.charAt( i ) == string_b.charAt( j ) )
889			{
890				if ( i == 0 || j == 0 )		grid[ i ][ j ] = 1;
891				else						grid[ i ][ j ] = grid[ i - 1 ][ j - 1 ] + 1;
892
893				if ( grid[ i ][ j ] > longest_common_substring )
894				{
895					longest_common_substring	= grid[ i ][ j ];
896					match						= '';
897				}
898
899				if ( grid[ i ][ j ] == longest_common_substring )
900				{
901					match = string_a.substring( i - longest_common_substring + 1, i + 1 );
902				}
903			}
904		}
905	}
906
907	if ( ( ( string_b.indexOf( match ) == 0 ) && ( string_a.indexOf( match ) == ( string_a.length - match.length ) ) ) ||
908		 ( ( string_a.indexOf( match ) == 0 ) && ( string_b.indexOf( match ) == ( string_b.length - match.length ) ) ) )
909	{
910		return match;
911	}
912
913    return '';
914}
915
916function textContent( element )
917{
918	if ( typeof( element.textContent ) != 'undefined' )	return element.textContent;
919	if ( typeof( element.innerText ) != 'undefined' )	return element.innerText;
920
921	return '';
922}
923
924function captureMousePosition( e ) 
925{
926	var x, y;
927	
928	if ( e.pageX || e.pageY ) 
929	{
930		x = e.pageX;
931		y = e.pageY;
932	}
933	else if ( e.clientX || e.clientY ) 
934	{
935		x = e.clientX + getScrollLeft();
936		y = e.clientY + getScrollTop();
937	}
938
939	return { x: x, y: y };
940}
941
942function getQueryStringAsArray()
943{
944	var result, results
945	var querystring, regex, element;
946
947	results				= new Array();
948	querystring			= document.location.search.substring( 1 );
949	regex				= /([^&=]+)=([^&]*)/g;
950
951	while ( ( element = regex.exec( querystring ) ) != null )
952	{
953		result			= new Object();
954		result.name		= element[ 1 ];
955		result.value	= element[ 2 ];
956
957		results.push( result );
958	}
959
960	return results;
961}
962
963function arrayIndexOf( array, element )
964{
965	var i;
966
967	if ( array.indexOf )
968	{
969		return array.indexOf( element );
970	}
971
972	for ( i = 0; i < array.length; i++ )
973	{
974		if ( array[ i ] === element )
975		{
976			return i;
977		}
978	}
979
980	return -1;
981}
982
983function arrayMove( arr, old_index, new_index )
984{
985	var k;
986
987    while ( old_index < 0 ) old_index += arr.length;
988    while ( new_index < 0 ) new_index += arr.length;
989
990    if ( new_index >= arr.length )
991    {
992        k = new_index - arr.length;
993        while ( ( k-- ) + 1 )
994        {
995            arr.push( undefined );
996        }
997    }
998
999    arr.splice( new_index, 0, arr.splice( old_index, 1 )[ 0 ] );
1000}
1001
1002function arrayFilter( array, func, passed_this )
1003{
1004	var i, i_len, value, result;
1005
1006	if ( array.filter )
1007	{
1008		return array.filter( func, passed_this );
1009	}
1010
1011	if ( array == null )					throw new TypeError();
1012	else if ( typeof func !== 'function' )	throw new TypeError();
1013
1014	result		= new Array();
1015
1016	for ( i = 0, i_len = array.length; i < i_len; i++ )
1017	{
1018		value = array[ i ];
1019		if ( func.call( passed_this, value, i, array ) )
1020		{
1021			result.push( value );
1022		}
1023	}
1024
1025	return result;
1026}
1027
1028function arrayFind( array, func, passed_this )
1029{
1030	var i, i_len, value;
1031
1032	if ( array.find )
1033	{
1034		return array.find( func, passed_this );
1035	}
1036
1037	if ( array == null )					throw new TypeError();
1038	else if ( typeof func !== 'function' )	throw new TypeError();
1039
1040	for ( i = 0, i_len = array.length; i < i_len; i++ )
1041	{
1042		value = array[ i ];
1043		if ( func.call( passed_this, value, i, array ) )
1044		{
1045			return value;
1046		}
1047	}
1048
1049	return returnUndefined();
1050}
1051
1052function arrayUniquify( array )
1053{
1054	return [ ...new Set( [ ...array ] ) ];
1055}
1056
1057function compareObjects( obj1, obj2 )
1058{
1059	var i, i_len, attribute, obj1_attribute_count, obj2_attribute_count;
1060
1061	if ( obj1 === obj2 )
1062	{
1063		return true;
1064	}
1065
1066	if ( obj1 instanceof Array && obj2 instanceof Array )
1067	{
1068		if ( obj1.length != obj2.length )
1069		{
1070			return false;
1071		}
1072
1073		for ( i = 0, i_len = obj1.length; i < i_len; i++ )
1074		{
1075			if ( !compareObjects( obj1[ i ], obj2[ i ] ) )
1076			{
1077				return false;
1078			}
1079		}
1080
1081		return true;
1082	}
1083	else if ( obj1 instanceof Object && obj2 instanceof Object )
1084	{
1085		obj1_attribute_count = 0;
1086		obj2_attribute_count = 0;
1087
1088		for ( attribute in obj1 )
1089		{
1090			if ( obj1.hasOwnProperty( attribute ) )
1091			{
1092				if ( !obj2.hasOwnProperty( attribute ) )
1093				{
1094					return false;
1095				}
1096
1097				obj1_attribute_count++;
1098			}
1099		}
1100
1101		for ( attribute in obj2 )
1102		{
1103			if ( obj2.hasOwnProperty( attribute ) )
1104			{
1105				if ( !obj1.hasOwnProperty( attribute ) )
1106				{
1107					return false;
1108				}
1109
1110				obj2_attribute_count++;
1111			}
1112		}
1113
1114		if ( obj1_attribute_count != obj2_attribute_count )
1115		{
1116			return false;
1117		}
1118
1119		for ( attribute in obj1 )
1120		{
1121			if ( obj1.hasOwnProperty( attribute ) )
1122			{
1123				if ( !compareObjects( obj1[ attribute ], obj2[ attribute ] ) )
1124				{
1125					return false;
1126				}
1127			}
1128		}
1129
1130		return true;
1131	}
1132
1133	return false;
1134}
1135
1136function cloneObject( obj )
1137{
1138	var obj_copy;
1139	var i, i_len, attribute;
1140
1141	if ( ( obj == null ) ||
1142		 ( typeof obj != 'object' ) )
1143	{
1144		return obj;
1145	}
1146
1147	if ( obj instanceof Array )
1148	{
1149		obj_copy = new Array();
1150
1151		for ( i = 0, i_len = obj.length; i < i_len; i++ )
1152		{
1153			obj_copy[ i ] = cloneObject( obj[ i ] );
1154		}
1155
1156		return obj_copy;
1157	}
1158	else if ( obj instanceof Object )
1159	{
1160		obj_copy = new Object();
1161
1162		for ( attribute in obj )
1163		{
1164			if ( obj.hasOwnProperty( attribute ) )
1165			{
1166				obj_copy[ attribute ] = cloneObject( obj[ attribute ] );
1167			}
1168		}
1169
1170		return obj_copy;
1171	}
1172
1173	return obj;
1174}
1175
1176function objectDeepAssign( target, ...sources )
1177{
1178	if ( typeof target !== 'object' )
1179	{
1180		target = new Object();
1181	}
1182
1183	for ( const source of sources )
1184	{
1185		for ( const key in source )
1186		{
1187			if ( typeof source[ key ] !== 'object' || Array.isArray( source[ key ] ) || !target.hasOwnProperty( key ) )	Object.assign( target, { [ key ]: source[ key ] } );
1188			else																										target[ key ] = objectDeepAssign( target[ key ], source[ key ] );
1189		}
1190	}
1191
1192	return target;
1193}
1194
1195function encodeentities( input )
1196{
1197	var result;
1198
1199	result	= new String( input );
1200	result	= result.replace(	/&/g,	'&amp;' );
1201	result	= result.replace(	/"/g,	'&quot;' );
1202	result	= result.replace(	/</g,	'&lt;' );
1203	result	= result.replace(	/>/g,	'&gt;' );
1204	result	= result.replace(	/\(/g,	'&#40;' );
1205	result	= result.replace(	/\)/g,	'&#41;' );
1206
1207	return result;
1208}
1209
1210function encodeattribute( text )
1211{
1212	var i, i_len, character, character_code, data;
1213
1214	if ( typeof text !== 'string' )
1215	{
1216		return text;
1217	}
1218
1219	data = '';
1220
1221	for ( i = 0, i_len = text.length; i < i_len; i++ )
1222	{
1223		character		= text.charAt( i );
1224		character_code	= text.charCodeAt( i );
1225
1226		switch ( character )
1227		{
1228			case ' '	:
1229			{
1230				data += '+';
1231
1232				break; 
1233			}
1234			case '~'	:
1235			case '`'	:
1236			case '!'	:
1237			case '#'	:
1238			case '$'	:
1239			case '%'	:
1240			case '^'	:
1241			case '&'	:
1242			case '('	:
1243			case ')'	:
1244			case '+'	:
1245			case '='	:
1246			case '{'	:
1247			case '}'	:
1248			case '['	:
1249			case ']'	:
1250			case '|'	:
1251			case '\\'	:
1252			case ':'	:
1253			case ';'	:
1254			case '"'	:
1255			case '\''	:
1256			case '<'	:
1257			case '>'	:
1258			case ','	:
1259			case '?'	:
1260			case '/'	:
1261			{
1262				data += '%' + padl( character_code.toString( 16 ).toUpperCase(), 2, '0' );
1263
1264				break;
1265			}
1266			default		:
1267			{
1268				if ( character_code > 0x20 && character_code < 0x7F )	data += character;
1269				else													data += '%' + padl( character_code.toString( 16 ).toUpperCase(), 2, '0' );
1270
1271				break;
1272			}
1273		}
1274	}
1275
1276	return data;
1277}
1278
1279function decodeattribute( text )
1280{
1281	if ( typeof text !== 'string' )
1282	{
1283		return text;
1284	}
1285	
1286	return decodeURIComponent( text.replace( new RegExp( regexEscape( '+' ), 'g' ), ' ' ) );
1287}
1288
1289function trim( value )
1290{
1291	if ( typeof value !== 'string' )	return value;
1292	else								return value.replace( /^\s+|\s+$/g, '' );
1293}
1294
1295function GetNormalizedValue( value )
1296{
1297	// Normalize line breaks in value string
1298
1299	if ( typeof value !== 'string' )
1300	{
1301		return '';
1302	}
1303
1304	value = value.replace( /\r\n/gm,	'\n' );
1305	value = value.replace( /\r/gm,		'\n' );
1306	value = value.replace( /\n/gm,		'\r\n' );
1307
1308	return value;
1309}
1310
1311function RFC3339DateString( date, timezone )
1312{
1313	var fixed_timezone, rfc3339_timezone;
1314
1315	timezone = stod( timezone );
1316
1317	if ( timezone === 0 )
1318	{
1319		rfc3339_timezone	= 'Z';
1320	}
1321	else
1322	{
1323		fixed_timezone		= Math.abs( timezone ).toFixed( 2 );
1324		rfc3339_timezone	= ( ( timezone > 0 ) ? '+' : '-' ) + padl( fixed_timezone.split( '.' )[ 0 ], 2, '0' ) + ':' + padl( Math.floor( ( fixed_timezone.split( '.' )[ 1 ] * 60 ) / 100 ), 2, '0' );
1325	}
1326
1327	return ( date.getFullYear() + '-' + padl( date.getMonth() + 1, 2, '0' ) + '-' + padl( date.getDate(), 2, '0' ) + 'T' + padl( date.getHours(), 2, '0' ) + ':' + padl( date.getMinutes(), 2, '0' ) + ':' + padl( date.getSeconds(), 2, '0' ) + rfc3339_timezone );
1328}
1329
1330function RFC3339DateStringNoTimeZone( date )
1331{
1332	return ( date.getFullYear() + '-' + padl( date.getMonth() + 1, 2, '0' ) + '-' + padl( date.getDate(), 2, '0' ) + 'T' + padl( date.getHours(), 2, '0' ) + ':' + padl( date.getMinutes(), 2, '0' ) + ':' + padl( date.getSeconds(), 2, '0' ) + '-00:00' );
1333}
1334
1335function padl( string, length, character )
1336{
1337	var pad;
1338
1339	length		= stoi_def( length, 0 );
1340	pad			= new Array( Math.ceil( length / character.length ) + 1 ).join( character ); // More efficient for longer strings than a while loop
1341
1342	if ( typeof string !== 'string' )
1343	{
1344		string	= "" + string;
1345	}
1346
1347	if ( string.length >= length )
1348	{
1349		return string;
1350	}
1351
1352	return pad.substring( 0, length - string.length ) + string;
1353}
1354
1355function padr( string, length, character )
1356{
1357	var pad;
1358
1359	length		= stoi_def( length, 0 );
1360	pad			= new Array( Math.ceil( length / character.length ) + 1 ).join( character ); // More efficient for longer strings than a while loop
1361
1362	if ( typeof string !== 'string' )
1363	{
1364		string	= "" + string;
1365	}
1366
1367	if ( string.length >= length )
1368	{
1369		return string;
1370	}
1371
1372	return string + pad.substring( 0, length - string.length );
1373}
1374
1375function stob( value )
1376{
1377	if ( typeof value !== 'string' )
1378	{
1379		return value ? true : false;
1380	}
1381
1382	value = value.toLowerCase();
1383
1384	return ( value === '' || value === '0' || value === 'no' || value === 'false' ) ? false : true;
1385}
1386
1387function stoi( value )
1388{
1389	return parseInt( value, 10 );
1390}
1391
1392function stoi_def( value, default_value )
1393{
1394	value = stoi( value );
1395
1396	return ( ( isNaN( value ) || value == Number.POSITIVE_INFINITY || value == Number.NEGATIVE_INFINITY ) ? default_value : value );
1397}
1398
1399function stoi_def_nonneg( value, default_value )
1400{
1401	value = stoi( value );
1402
1403	return ( ( isNaN( value ) || ( value < 0 ) || value == Number.POSITIVE_INFINITY || value == Number.NEGATIVE_INFINITY ) ? default_value : value );
1404}
1405
1406function stoi_min( value, min_value )
1407{
1408	value = stoi( value );
1409
1410	return ( ( isNaN( value ) || ( value < min_value ) || value == Number.POSITIVE_INFINITY || value == Number.NEGATIVE_INFINITY ) ? min_value : value );
1411}
1412
1413function stoi_max( value, max_value )
1414{
1415	value = stoi( value );
1416
1417	return ( ( isNaN( value ) || ( value > max_value ) || value == Number.POSITIVE_INFINITY || value == Number.NEGATIVE_INFINITY ) ? max_value : value );
1418}
1419
1420function stoi_range( value, min_value, max_value, default_value )
1421{
1422	return stoi_min( stoi_max( stoi_def( value, default_value ), max_value ), min_value );
1423}
1424
1425function stod( value )
1426{
1427	return parseFloat( value );
1428}
1429
1430function stod_def( value, default_value )
1431{
1432	value = stod( value );
1433
1434	return ( ( isNaN( value ) || value == Number.POSITIVE_INFINITY || value == Number.NEGATIVE_INFINITY ) ? default_value : value );
1435}
1436
1437function stod_def_nonneg( value, default_value )
1438{
1439	value = stod( value );
1440
1441	return ( ( isNaN( value ) || ( value < 0 ) || value == Number.POSITIVE_INFINITY || value == Number.NEGATIVE_INFINITY ) ? default_value : value );
1442}
1443
1444function stod_min( value, min_value )
1445{
1446	value = stod( value );
1447
1448	return ( ( isNaN( value ) || ( value < min_value ) || value == Number.POSITIVE_INFINITY || value == Number.NEGATIVE_INFINITY ) ? min_value : value );
1449}
1450
1451function stod_max( value, max_value )
1452{
1453	value = stod( value );
1454
1455	return ( ( isNaN( value ) || ( value > max_value ) || value == Number.POSITIVE_INFINITY || value == Number.NEGATIVE_INFINITY ) ? max_value : value );
1456}
1457
1458function stod_range( value, min_value, max_value, default_value )
1459{
1460	return stod_min( stod_max( stod_def( value, default_value ), max_value ), min_value );
1461}
1462
1463function ValueIsEmpty( value )
1464{
1465	if ( value === null )										return true;
1466	else if ( typeof value === 'object' )						return Object.keys( value ).length === 0 && value.constructor === Object;
1467	else if ( typeof value === 'undefined' )					return true;
1468	else if ( typeof value === 'string' && value.length == 0 )	return true;
1469
1470	// type is boolean, number, function, non-zero length string, etc
1471	return false;
1472}
1473
1474function ValueIsNonEmptyString( value )
1475{
1476	if ( typeof value !== 'string' )		return false;
1477	else if ( value.trim().length === 0 )	return false;
1478
1479	return true;
1480}
1481
1482function ValueIsNonEmptyArray( value )
1483{
1484	if ( !Array.isArray( value ) )			return false;
1485	else if ( value.length === 0 )			return false;
1486
1487	return true;
1488}
1489
1490function getMonthName_Abbreviated( date )
1491{
1492	switch ( date.getMonth() )
1493	{
1494		case 0:		return 'Jan';
1495		case 1:		return 'Feb';
1496		case 2:		return 'Mar';
1497		case 3:		return 'Apr';
1498		case 4:		return 'May';
1499		case 5:		return 'Jun';
1500		case 6:		return 'Jul';
1501		case 7:		return 'Aug';
1502		case 8:		return 'Sep';
1503		case 9:		return 'Oct';
1504		case 10:	return 'Nov';
1505		case 11:	return 'Dec';
1506	}
1507}
1508
1509/*
1510 * Note: This function is also present (with a different name) in ajax.js and v55_ui.js.
1511 *		 Modifications here should be made in those other locations as well.
1512 */
1513
1514function isUnicode()
1515{
1516	return ( document.characterSet || document.charset || '' ).search( 'UTF' ) == 0 ? true : false;
1517}
1518
1519/*
1520 * Note: This function is also present (with a different name) in ajax.js and v55_ui.js.
1521 *		 Modifications here should be made in those other locations as well.
1522 */
1523
1524function CharsetEncodeAttribute( instring )
1525{
1526	var encoded;
1527
1528	if ( isUnicode() )
1529	{
1530		return encodeURIComponent( instring );
1531	}
1532	else
1533	{
1534		if ( typeof escape === 'function' )
1535		{
1536			encoded = escape( instring );
1537			encoded = encoded.replace( '+', '%2B' );
1538			encoded = encoded.replace( '/', '%2F' );
1539			encoded = encoded.replace( '@', '%40' );
1540
1541			return encoded;
1542		}
1543		else
1544		{
1545			return instring;
1546		}
1547	}
1548}
1549
1550function CharsetDecodeAttribute( instring )
1551{
1552	if ( isUnicode() )
1553	{
1554		return decodeattribute( instring );
1555	}
1556	else
1557	{
1558		if ( typeof unescape === 'function' )	return unescape( instring );
1559		else									return instring;
1560	}
1561}
1562
1563function MMStripHTML( string )
1564{
1565	var html_stripper = new DOMParser().parseFromString( string, 'text/html' );
1566	
1567	return html_stripper.body.textContent;
1568}
1569
1570function Price_Pad( price )
1571{
1572	return Decimal_Pad( price, 2 );
1573}
1574
1575function Decimal_Pad( value, digits )
1576{
1577	return stod_def( value, 0.0 ).toLocaleString( 'en-US', { minimumFractionDigits: digits, maximumFractionDigits: 8, useGrouping: false } );
1578}
1579
1580( function( global )
1581{
1582	global.getVariableType = function( variable )
1583	{
1584		// Return a more accurate representation of a variable's type based on the
1585		// passed in [[Class]], when possible. If variable is null, return 'null'.
1586		// If variable is undefined, return 'undefined'. If variable is global (such
1587		// as the window), pass back global, otherwise, pass back the parsed toString
1588		// value from Object.prototype.toString, which gives us a more detailed
1589		// object type [object Array], [object Date], etc. While not a perfect solution
1590		// for every case, this will give us a much more accurate value than
1591		// JavaScript's builtin "typeof" operator. The anonymous wrapper function
1592		// ensures we receive the "global" context for later comparison
1593
1594		if ( variable === null )			return 'null';		// true null
1595		else if ( variable == null )		return 'undefined';	// Undefined variable
1596		else if ( variable === global )		return 'global';	// global object, such as window
1597		else 								return Object.prototype.toString.call( variable ).match( /\s([a-zA-Z]+)/)[ 1 ].toLowerCase();
1598	}
1599}( this ) );
1600// Miva Merchant
1601//
1602// This file and the source codes contained herein are the property of
1603// Miva, Inc.  Use of this file is restricted to the specific terms and
1604// conditions in the License Agreement associated with this file.  Distribution
1605// of this file or portions of this file for uses not covered by the License
1606// Agreement is not allowed without a written agreement signed by an officer of
1607// Miva, Inc.
1608//
1609// Copyright 1998-2024 Miva, Inc.  All rights reserved.
1610// http://www.miva.com
1611//
1612
1613// Misc HTML/JavaScript functions
1614////////////////////////////////////////////////////
1615
1616function MMProdList_FormElement_Value( element )
1617{
1618	var node, type;
1619
1620	if ( element && element.nodeType === 1 )
1621	{
1622		node = element.nodeName.toLowerCase();
1623
1624		if ( node == 'textarea' )
1625		{
1626			return element.value;
1627		}
1628		else if ( node == 'select' )
1629		{
1630			return ( element.selectedIndex != -1 && element.options[ element.selectedIndex ] ? element.options[ element.selectedIndex ].value : '' );
1631		}
1632		else if ( node == 'input' )
1633		{
1634			type = element.type.toLowerCase();
1635
1636			if ( type == 'text' || type == 'hidden' )
1637			{
1638				return element.value;
1639			}
1640			else if ( type == 'radio' && element.checked )
1641			{
1642				return element.value;
1643			}
1644			else if ( type == 'checkbox' )
1645			{
1646				return element.checked ? element.value : '';
1647			}
1648		}
1649	}
1650
1651	return '';
1652}
1653
1654function MMProdList_AddNestedFacetValue( element, code, value, submit_delay )
1655{
1656	var form, entry;
1657
1658	if ( element.form )																	form = element.form;
1659	else if ( !( form = getNearestElementAncestorNodeTypeOrNull( element, 'FORM' ) ) )	return;
1660
1661	if ( !form.mmprodlist_updatequery_form_data )
1662	{
1663		form.mmprodlist_updatequery_form_data = new Array();
1664	}
1665
1666	entry							= new Object();
1667	entry.element					= element;
1668	entry.onParametersGenerated		= function( form_params_struct, url_params_struct )
1669	{
1670		if ( form_params_struct[ code ] )
1671		{
1672			form_params_struct[ code ].values.push( value );
1673		}
1674		else if ( url_params_struct[ code ] )
1675		{
1676			url_params_struct[ code ].values.push( value );
1677		}
1678		else
1679		{
1680			form_params_struct[ code ]			= new Object();
1681			form_params_struct[ code ].name		= code;
1682			form_params_struct[ code ].values	= new Array();
1683			form_params_struct[ code ].values.push( value );
1684		}
1685	};
1686
1687	form.mmprodlist_updatequery_form_data.push( entry );
1688	MMProdList_UpdateQuery_SubmitParameters_Timeout( form, submit_delay );
1689}
1690
1691function MMProdList_RemoveNestedFacetValue( element, code, value, submit_delay )
1692{
1693	var form, entry;
1694
1695	if ( element.form )																	form = element.form;
1696	else if ( !( form = getNearestElementAncestorNodeTypeOrNull( element, 'FORM' ) ) )	return;
1697
1698	if ( !form.mmprodlist_updatequery_form_data )
1699	{
1700		form.mmprodlist_updatequery_form_data = new Array();
1701	}
1702
1703	entry							= new Object();
1704	entry.element					= element;
1705	entry.onParametersGenerated		= function( form_params_struct, url_params_struct )
1706	{
1707		var i, i_len;
1708
1709		if ( form_params_struct[ code ] )
1710		{
1711			for ( i = 0, i_len = form_params_struct[ code ].values.length; i < i_len; i++ )
1712			{
1713				if ( form_params_struct[ code ].values[ i ] == value )
1714				{
1715					form_params_struct[ code ].values.splice( i, form_params_struct[ code ].values.length - i );
1716
1717					if ( form_params_struct[ code ].values.length == 0 )
1718					{
1719						if ( form_params_struct[ code + '_reset' ] )
1720						{
1721							form_params_struct[ code + '_reset' ].values	= [ '1' ];
1722						}
1723						else
1724						{
1725							form_params_struct[ code + '_reset' ]			= new Object();
1726							form_params_struct[ code + '_reset' ].name		= code + '_reset';
1727							form_params_struct[ code + '_reset' ].values	= [ '1' ];
1728						}
1729					}
1730
1731					break;
1732				}
1733			}
1734		}
1735
1736		if ( url_params_struct[ code ] )
1737		{
1738			for ( i = 0, i_len = url_params_struct[ code ].values.length; i < i_len; i++ )
1739			{
1740				if ( url_params_struct[ code ].values[ i ] == value )
1741				{
1742					url_params_struct[ code ].values.splice( i, url_params_struct[ code ].values.length - i );
1743
1744					if ( url_params_struct[ code ].values.length == 0 )
1745					{
1746						if ( url_params_struct[ code + '_reset' ] )
1747						{
1748							url_params_struct[ code + '_reset' ].values	= [ '1' ];
1749						}
1750						else
1751						{
1752							url_params_struct[ code + '_reset' ]		= new Object();
1753							url_params_struct[ code + '_reset' ].name	= code + '_reset';
1754							url_params_struct[ code + '_reset' ].values	= [ '1' ];
1755						}
1756					}
1757
1758					break;
1759				}
1760			}
1761		}
1762	};
1763
1764	form.mmprodlist_updatequery_form_data.push( entry );
1765	MMProdList_UpdateQuery_SubmitParameters_Timeout( form, submit_delay );
1766}
1767
1768function MMProdList_UpdateQuery( element, submit_delay )
1769{
1770	var form, entry;
1771
1772	if ( element.form )																	form = element.form;
1773	else if ( !( form = getNearestElementAncestorNodeTypeOrNull( element, 'FORM' ) ) )	return;
1774
1775	if ( !form.mmprodlist_updatequery_form_data )
1776	{
1777		form.mmprodlist_updatequery_form_data = new Array();
1778	}
1779
1780	entry							= new Object();
1781	entry.element					= element;
1782	entry.onParametersGenerated		= null;
1783
1784	form.mmprodlist_updatequery_form_data.push( entry );
1785	MMProdList_UpdateQuery_SubmitParameters_Timeout( form, submit_delay );
1786}
1787
1788function MMProdList_UpdateQueryBuildParameters( form )
1789{
1790	var i, j, name, query, regex, match, value, i_len, j_len, params, member;
1791	var element, url_param, url_params_struct, form_params_struct, merged_params_struct;
1792
1793	params				= new Array();
1794	url_params_struct	= new Object();
1795	form_params_struct	= new Object();
1796
1797	query				= window.location.search.substring( 1 );
1798	regex				= /([^=&?]+)=([^&]+)/g;
1799
1800	while ( ( match = regex.exec( query ) ) !== null )
1801	{
1802		name	= CharsetDecodeAttribute( match[ 1 ] );
1803		value	= CharsetDecodeAttribute( match[ 2 ] );
1804
1805		if ( name == 'Offset'			||
1806			 name == 'SearchOffset'		||
1807			 name == 'RelatedOffset' 	||
1808			 name == 'CatListingOffset' ||
1809			 name == 'AllOffset' )
1810		{
1811			continue;
1812		}
1813
1814		// Handle single-value inputs that are disabled by excluding them from url_params.  Handles rangesliders that
1815		// wouldn't be covered in the code below because the values don't match.
1816
1817		if ( !form.elements[ name ] || ( typeof form.elements[ name ] !== 'object' ) || !form.elements[ name ].disabled )
1818		{
1819			if ( !url_params_struct[ name ] )
1820			{
1821				url_params_struct[ name ]			= new Object();
1822				url_params_struct[ name ].name		= name;
1823				url_params_struct[ name ].values	= new Array();
1824			}
1825
1826			url_params_struct[ name ].values.push( value );
1827		}
1828	}
1829
1830	for ( i = 0, i_len = form.elements.length; i < i_len; i++ )
1831	{
1832		if ( form.elements[ i ].disabled )
1833		{
1834			// Remove this particular value from the url_params, if it is present, and do
1835			// not add this value to the form_params.
1836
1837			url_param = url_params_struct[ form.elements[ i ].name ];
1838			if ( url_param )
1839			{
1840				for ( j = 0, j_len = url_param.values.length; j < j_len; j++ )
1841				{
1842					if ( url_param.values[ j ] == form.elements[ i ].value )
1843					{
1844						url_param.values.splice( j, 1 );
1845						break;
1846					}
1847				}
1848			}
1849
1850			continue;
1851		}
1852
1853		if ( ( typeof form.elements[ i ].name === 'string' ) && form.elements[ i ].name.length )
1854		{
1855			if ( !form_params_struct[ form.elements[ i ].name ] )
1856			{
1857				form_params_struct[ form.elements[ i ].name ]			= new Object();
1858				form_params_struct[ form.elements[ i ].name ].name		= form.elements[ i ].name;
1859				form_params_struct[ form.elements[ i ].name ].values	= new Array();
1860			}
1861
1862			form_params_struct[ form.elements[ i ].name ].values.push( MMProdList_FormElement_Value( form.elements[ i ] ) );
1863		}
1864	}
1865
1866	if ( form.mmprodlist_updatequery_form_data )
1867	{
1868		for ( i = 0, i_len = form.mmprodlist_updatequery_form_data.length; i < i_len; i++ )
1869		{
1870			element = form.mmprodlist_updatequery_form_data[ i ].element;
1871
1872			if ( !element.disabled && ( typeof element.name === 'string' ) && element.name.length && !form_params_struct[ element.name ] )
1873			{
1874				form_params_struct[ element.name ]			= new Object();
1875				form_params_struct[ element.name ].name		= element.name;
1876				form_params_struct[ element.name ].values	= new Array();
1877
1878				form_params_struct[ element.name ].values.push( MMProdList_FormElement_Value( element ) );
1879			}
1880
1881			if ( typeof form.mmprodlist_updatequery_form_data[ i ].onParametersGenerated === 'function' )
1882			{
1883				form.mmprodlist_updatequery_form_data[ i ].onParametersGenerated( form_params_struct, url_params_struct )
1884			}
1885		}
1886	}
1887
1888	merged_params_struct = url_params_struct;
1889
1890	for ( member in form_params_struct )
1891	{
1892		if ( form_params_struct.hasOwnProperty( member ) )
1893		{
1894			merged_params_struct[ member ] = form_params_struct[ member ];
1895		}
1896	}
1897
1898	for ( member in merged_params_struct )
1899	{
1900		if ( merged_params_struct.hasOwnProperty( member ) )
1901		{
1902			params.push( merged_params_struct[ member ] );
1903		}
1904	}
1905
1906	return params;
1907}
1908
1909function MMProdList_UpdateQuery_SubmitParameters( form, params )
1910{
1911	var i, j, i_len, j_len, elements, params_output_array;
1912
1913	if ( window.mmprodlist_updatequery_submitparameters_submitted )
1914	{
1915		return;
1916	}
1917
1918	window.mmprodlist_updatequery_submitparameters_submitted = true;
1919	params_output_array										 = new Array();
1920
1921	params.sort( function( a, b ) { return sortAlphaNumeric( a.name, b.name, true ); } );
1922
1923	for ( i = 0, i_len = params.length; i < i_len; i++ )
1924	{
1925		for ( j = 0, j_len = params[ i ].values.length; j < j_len; j++ )
1926		{
1927			if ( params[ i ].values[ j ].length )
1928			{
1929				params_output_array.push( encodeURIComponent( params[ i ].name ) + '=' + encodeURIComponent( params[ i ].values[ j ] ) );
1930			}
1931		}
1932	}
1933
1934	if ( window.mmprodlist_updatequery_submitparameters_timeout )
1935	{
1936		clearTimeout( window.mmprodlist_updatequery_submitparameters_timeout );
1937		delete window.mmprodlist_updatequery_submitparameters_timeout;
1938	}
1939
1940	if ( window.mmprodlist_updatequery_submitparameters_timeout_event_attached )
1941	{
1942		elements = form.getElementsByTagName( '*' );
1943
1944		for ( i = 0, i_len = elements.length; i < i_len; i++ )
1945		{
1946			RemoveEvent( elements[ i ], 'mousedown',	window.mmprodlist_updatequery_submitparameters_timeout_event_function );
1947			RemoveEvent( elements[ i ], 'mousemove',	window.mmprodlist_updatequery_submitparameters_timeout_event_function );
1948			RemoveEvent( elements[ i ], 'mouseup',		window.mmprodlist_updatequery_submitparameters_timeout_event_function );
1949		}
1950	}
1951
1952	window.location.href = [ window.location.protocol, '//', window.location.host, window.location.pathname ].join( '' ) + ( params_output_array.length ? ( '?' + params_output_array.join( '&' ) ) : '' );
1953}
1954
1955// Overridable: If different timeout functionality is desired, override
1956// the following function "MMProdList_UpdateQuery_SubmitParameters_Timeout"
1957
1958function MMProdList_UpdateQuery_SubmitParameters_Timeout( form, submit_delay )
1959{
1960	var i, i_len, elements;
1961
1962	submit_delay = stoi_def_nonneg( submit_delay, 0 )
1963
1964	if ( window.mmprodlist_updatequery_submitparameters_timeout )
1965	{
1966		clearTimeout( window.mmprodlist_updatequery_submitparameters_timeout );
1967		delete window.mmprodlist_updatequery_submitparameters_timeout;
1968	}
1969
1970	if ( submit_delay === 0 )
1971	{
1972		return MMProdList_UpdateQuery_SubmitParameters_LowLevel( form );
1973	}
1974
1975	window.mmprodlist_updatequery_submitparameters_timeout = setTimeout( function() { MMProdList_UpdateQuery_SubmitParameters_LowLevel( form ); }, submit_delay );
1976
1977	if ( !window.mmprodlist_updatequery_submitparameters_timeout_event_attached )
1978	{
1979		window.mmprodlist_updatequery_submitparameters_timeout_event_attached = true;
1980		window.mmprodlist_updatequery_submitparameters_timeout_event_function = function( event ) { MMProdList_UpdateQuery_SubmitParameters_Timeout( form, submit_delay ); };
1981
1982		elements = form.getElementsByTagName( '*' );
1983
1984		for ( i = 0, i_len = elements.length; i < i_len; i++ )
1985		{
1986			AddEvent( elements[ i ], 'mousedown',	window.mmprodlist_updatequery_submitparameters_timeout_event_function );
1987			AddEvent( elements[ i ], 'mousemove',	window.mmprodlist_updatequery_submitparameters_timeout_event_function );
1988			AddEvent( elements[ i ], 'mouseup',		window.mmprodlist_updatequery_submitparameters_timeout_event_function );
1989		}
1990	}
1991}
1992
1993function MMProdList_UpdateQuery_SubmitParameters_LowLevel( form ) 
1994{
1995	var params = MMProdList_UpdateQueryBuildParameters( form );
1996
1997	MMProdList_UpdateQuery_Notify_Updating( form );
1998	MMProdList_UpdateQuery_SubmitParameters( form, params );
1999	MMProdList_UpdateQuery_Notify_Updated( form );
2000}
2001
2002// Overridable: If different notification functionality is desired, override
2003// the following function "MMProdList_UpdateQuery_Notify_Updating"
2004
2005function MMProdList_UpdateQuery_Notify_Updating( form )
2006{
2007	var i, i_len;
2008
2009	/**
2010	 * Disable all inputs while keeping track of what is already disabled
2011	 */
2012
2013	for ( i = 0, i_len = form.elements.length; i < i_len; i++ )
2014	{
2015		form.elements[ i ].mm_original_disabled = form.elements[ i ].disabled;
2016		form.elements[ i ].setAttribute( 'disabled', 'disabled' );
2017	}
2018}
2019
2020// Overridable: If different updated functionality is desired, override
2021// the following function "MMProdList_UpdateQuery_Notify_Updated"
2022
2023function MMProdList_UpdateQuery_Notify_Updated( form )
2024{
2025	var i, i_len;
2026
2027	/**
2028	 * Enable any inputs which were not already disabled and setup the form for resubmission
2029	 */
2030
2031	for ( i = 0, i_len = form.elements.length; i < i_len; i++ )
2032	{
2033		if ( form.elements[ i ].hasOwnProperty( 'mm_original_disabled' ) )
2034		{
2035			if ( !form.elements[ i ].mm_original_disabled )
2036			{
2037				form.elements[ i ].removeAttribute( 'disabled' );
2038			}
2039
2040			delete form.elements[ i ].mm_original_disabled;
2041		}
2042	}
2043
2044	window.mmprodlist_updatequery_submitparameters_submitted = false;
2045}
2046
2047function MMFacet_RangeSlider_Initialize()
2048{
2049	var i, name, i_len, module_code, facet_code, elementlist, range_low, range_high, allow_value_input, selected_range_low, selected_range_high;
2050
2051	elementlist = document.querySelectorAll( '[data-mm-facet-rangeslider-name]' );
2052
2053	for ( i = 0, i_len = elementlist.length; i < i_len; i++ )
2054	{
2055		module_code				= elementlist[ i ].getAttribute( 'data-mm-facet-module-code' );
2056		facet_code				= elementlist[ i ].getAttribute( 'data-mm-facet-code' );
2057		name					= elementlist[ i ].getAttribute( 'data-mm-facet-rangeslider-name' );
2058		range_low				= stod_def( elementlist[ i ].getAttribute( 'data-mm-facet-rangeslider-range-low' ), 0 );
2059		range_high				= stod_def( elementlist[ i ].getAttribute( 'data-mm-facet-rangeslider-range-high' ), 0 );
2060		selected_range_low		= stod_def( elementlist[ i ].getAttribute( 'data-mm-facet-rangeslider-selected-range-low' ), 0 );
2061		selected_range_high		= stod_def( elementlist[ i ].getAttribute( 'data-mm-facet-rangeslider-selected-range-high' ), range_high );
2062
2063		if ( elementlist[ i ].hasAttribute( 'data-mm-facet-rangeslider-allow-value-input' ) )	allow_value_input = elementlist[ i ].getAttribute( 'data-mm-facet-rangeslider-allow-value-input' ).toLowerCase();
2064		else																					allow_value_input = 'No';
2065
2066		elementlist[ i ].mm_facet_rangeslider = new MMFacet_RangeSlider( elementlist[ i ], module_code, facet_code, name, range_low, range_high, selected_range_low, selected_range_high, allow_value_input === 'yes' || allow_value_input === '1' || allow_value_input === 'true' );
2067	}
2068}
2069
2070// MMFacet_RangeSlider
2071////////////////////////////////////////////////////
2072
2073function MMFacet_RangeSlider( element_parent, module_code, facet_code, name, range_low, range_high, selected_range_low, selected_range_high, allow_value_input )
2074{
2075	var self = this;
2076
2077	this.element_parent								= element_parent;
2078	this.module_code								= module_code;
2079	this.facet_code									= facet_code;
2080	this.name										= name;
2081	this.range_low									= range_low;
2082	this.range_high									= range_high;
2083	this.range_span									= range_high - range_low;
2084	this.selected_range_low							= selected_range_low;
2085	this.selected_range_high						= selected_range_high;
2086	this.allow_value_input							= allow_value_input;
2087
2088	this.shouldrender								= false;
2089	this.slider_position_low						= 0;
2090	this.slider_position_high						= 0;
2091	this.original_value								= this.selected_range_low + '-' + this.selected_range_high;
2092
2093	this.element_parent.innerHTML					= '';
2094
2095	this.element_value								= newElement( 'input',	{ 'type': 'hidden', 'name': name },							null, this.element_parent );
2096	this.element_input_container_low				= newElement( 'span',	{ 'class': 'mm_facet_rangeslider_input_container_low' },	null, this.element_parent );
2097	this.element_track_container					= newElement( 'span',	{ 'class': 'mm_facet_rangeslider_track_container' },		null, this.element_parent );
2098	this.element_track								= newElement( 'span',	{ 'class': 'mm_facet_rangeslider_track' },					null, this.element_track_container );
2099	this.element_track_background					= newElement( 'span',	{ 'class': 'mm_facet_rangeslider_track_background' },		null, this.element_track );
2100	this.element_track_selection					= newElement( 'span',	{ 'class': 'mm_facet_rangeslider_track_selection' },		null, this.element_track );
2101	this.element_track_handle_low					= newElement( 'span',	{ 'class': 'mm_facet_rangeslider_track_handle_low' },		null, this.element_track );
2102	this.element_track_handle_high					= newElement( 'span',	{ 'class': 'mm_facet_rangeslider_track_handle_high' },		null, this.element_track );
2103	this.element_input_container_high				= newElement( 'span',	{ 'class': 'mm_facet_rangeslider_input_container_high' },	null, this.element_parent );
2104
2105	if ( !this.allow_value_input )
2106	{
2107		this.element_label_low						= newElement( 'span',	{ 'class': 'mm_facet_rangeslider_input' },					null, this.element_input_container_low );
2108		this.element_label_high						= newElement( 'span',	{ 'class': 'mm_facet_rangeslider_input' },					null, this.element_input_container_high );
2109	}
2110	else
2111	{
2112		this.element_input_low						= newElement( 'input',	{ 'type': 'text', 'class': 'mm_facet_rangeslider_input' },	null, this.element_input_container_low );
2113		this.element_input_high						= newElement( 'input',	{ 'type': 'text', 'class': 'mm_facet_rangeslider_input' },	null, this.element_input_container_high );
2114
2115		this.event_keydown_input					= function( event ) { return self.Event_KeyDown_Input( event ? event : window.event ); };
2116		this.event_focus_input_low					= function( event ) { return self.Event_Focus_Input_Low( event ? event : window.event ); };
2117		this.event_blur_input_low					= function( event ) { return self.Event_Blur_Input_Low( event ? event : window.event ); };
2118		this.event_focus_input_high					= function( event ) { return self.Event_Focus_Input_High( event ? event : window.event ); };
2119		this.event_blur_input_high					= function( event ) { return self.Event_Blur_Input_High( event ? event : window.event ); };
2120		this.event_click_input_container_low		= function( event ) { return self.Event_Click_Input_Container_Low( event ? event : window.event ); };
2121		this.event_click_input_container_high		= function( event ) { return self.Event_Click_Input_Container_High( event ? event : window.event ); };
2122
2123		AddEvent( this.element_input_low,				'keydown',		this.event_keydown_input );
2124		AddEvent( this.element_input_low,				'focus',		this.event_focus_input_low );
2125		AddEvent( this.element_input_low,				'blur',			this.event_blur_input_low );
2126		AddEvent( this.element_input_high,				'keydown',		this.event_keydown_input );
2127		AddEvent( this.element_input_high,				'focus',		this.event_focus_input_high );
2128		AddEvent( this.element_input_high,				'blur',			this.event_blur_input_high );
2129		AddEvent( this.element_input_container_low,		'click',		this.event_click_input_container_low );
2130		AddEvent( this.element_input_container_high,	'click',		this.event_click_input_container_high );
2131	}
2132
2133	this.event_render								= function() { self.Render(); };
2134	this.event_returnfalse							= function() { return false; };
2135
2136	this.event_mousedown_handle_low					= function( event ) { return self.Event_MouseDown_Handle_Low( event ? event : window.event ); };
2137	this.event_mousedown_handle_high				= function( event ) { return self.Event_MouseDown_Handle_High( event ? event : window.event ); };
2138	this.event_mousemove_handle_low					= function( event ) { return self.Event_MouseMove_Handle_Low( event ? event : window.event ); };
2139	this.event_mousemove_handle_high				= function( event ) { return self.Event_MouseMove_Handle_High( event ? event : window.event ); };
2140	this.event_mouseup_handle_low					= function( event ) { return self.Event_MouseUp_Handle_Low( event ? event : window.event ); };
2141	this.event_mouseup_handle_high					= function( event ) { return self.Event_MouseUp_Handle_High( event ? event : window.event ); };
2142
2143	this.event_touchstart_handle_low				= function( event ) { return self.Event_TouchStart_Handle_Low( event ? event : window.event ); };
2144	this.event_touchstart_handle_high				= function( event ) { return self.Event_TouchStart_Handle_High( event ? event : window.event ); };
2145	this.event_touchmove_handle_low					= function( event ) { return self.Event_TouchMove_Handle_Low( event ? event : window.event ); };
2146	this.event_touchmove_handle_high				= function( event ) { return self.Event_TouchMove_Handle_High( event ? event : window.event ); };
2147	this.event_touchend_handle_low					= function( event ) { return self.Event_TouchEnd_Handle_Low( event ? event : window.event ); };
2148	this.event_touchend_handle_high					= function( event ) { return self.Event_TouchEnd_Handle_High( event ? event : window.event ); };
2149
2150	AddEvent( this.element_track_handle_low,			'mousedown',	this.event_mousedown_handle_low );
2151	AddEvent( this.element_track_handle_high,			'mousedown',	this.event_mousedown_handle_high );
2152
2153	AddEvent( this.element_track_handle_low,			'touchstart',	this.event_touchstart_handle_low );
2154	AddEvent( this.element_track_handle_high,			'touchstart',	this.event_touchstart_handle_high );
2155
2156	this.SetSelectedRange( this.selected_range_low, this.selected_range_high );
2157	this.RequestRender();
2158}
2159
2160MMFacet_RangeSlider.prototype.FormatValues = function( low, high )
2161{
2162	var value_low, value_high, slider_rect, handle_rect, slider_length, available_handle_length;
2163
2164	slider_rect						= this.element_track.getBoundingClientRect();
2165	handle_rect						= this.element_track_handle_low.getBoundingClientRect();
2166	slider_length					= ( slider_rect.right - slider_rect.left );
2167	available_handle_length			= slider_length - ( handle_rect.right - handle_rect.left + 2 );
2168
2169	value_low						= stoi_def( Math.round( this.range_low + stod_def( stod_range( ( ( low / available_handle_length ) * slider_length ) / slider_length, 0, 1 ) * this.range_span, 0 ) ), 0 );
2170	value_high						= stoi_def( Math.round( this.range_low + stod_def( stod_range( ( ( high / available_handle_length ) * slider_length ) / slider_length, 0, 1 ) * this.range_span, 0 ) ), 0 );
2171
2172	this.FormatValues_LowLevel( value_low, value_high );
2173}
2174
2175MMFacet_RangeSlider.prototype.FormatValues_LowLevel = function( value_low, value_high )
2176{
2177	var tmp_low;
2178
2179	if ( value_low > value_high )
2180	{
2181		tmp_low								= value_low;
2182		value_low							= value_high;
2183		value_high							= tmp_low;
2184	}
2185
2186	this.element_value.value				= value_low + '-' + value_high;
2187	this.element_value.disabled				= ( value_low == this.range_low && value_high == this.range_high ) ? true : false;
2188
2189	if ( this.allow_value_input )
2190	{
2191		this.element_input_low.value		= MMFacet_RangeSlider_FormatValue( this.module_code, this.facet_code, value_low );
2192		this.element_input_high.value		= MMFacet_RangeSlider_FormatValue( this.module_code, this.facet_code, value_high );
2193	}
2194	else
2195	{
2196		this.element_label_low.innerHTML	= MMFacet_RangeSlider_FormatValue( this.module_code, this.facet_code, value_low );
2197		this.element_label_high.innerHTML	= MMFacet_RangeSlider_FormatValue( this.module_code, this.facet_code, value_high );
2198	}
2199}
2200
2201MMFacet_RangeSlider.prototype.SetSelectedRange = function( low, high )
2202{
2203	var slider_rect, handle_rect, slider_length, available_handle_length;
2204
2205	slider_rect					= this.element_track.getBoundingClientRect();
2206	handle_rect					= this.element_track_handle_low.getBoundingClientRect();
2207	slider_length				= ( slider_rect.right - slider_rect.left );
2208	available_handle_length		= slider_length - ( handle_rect.right - handle_rect.left + 2 );
2209
2210	this.slider_position_low	= stod_def( ( ( ( ( low - this.range_low ) / this.range_span ) * slider_length ) / slider_length ) * available_handle_length, 0 );
2211	this.slider_position_high	= stod_def( ( ( ( ( high - this.range_low ) / this.range_span ) * slider_length ) / slider_length ) * available_handle_length, 0 );
2212
2213	this.FormatValues_LowLevel( low, high );
2214}
2215
2216MMFacet_RangeSlider.prototype.Event_Click_Input_Container_Low = function( e )
2217{
2218	var rect, mousepos, scrollfromtop, scrollfromleft;
2219
2220	rect			= this.element_input_low.getBoundingClientRect();
2221	mousepos		= captureMousePosition( e );
2222	scrollfromtop	= getScrollTop();
2223	scrollfromleft	= getScrollLeft();
2224
2225	if ( mousepos.y < ( rect.top + scrollfromtop ) 								||
2226		 mousepos.y > ( rect.top + ( rect.bottom - rect.top ) + scrollfromtop )	||
2227		 mousepos.x < ( rect.left + scrollfromleft ) 							||
2228		 mousepos.x > ( rect.left + ( rect.right - rect.left ) + scrollfromleft ) )
2229	{
2230		this.element_input_low.focus();
2231	}
2232}
2233
2234MMFacet_RangeSlider.prototype.Event_Click_Input_Container_High = function( e )
2235{
2236	var rect, mousepos, scrollfromtop, scrollfromleft;
2237
2238	rect			= this.element_input_high.getBoundingClientRect();
2239	mousepos		= captureMousePosition( e );
2240	scrollfromtop	= getScrollTop();
2241	scrollfromleft	= getScrollLeft();
2242
2243	if ( mousepos.y < ( rect.top + scrollfromtop ) 								||
2244		 mousepos.y > ( rect.top + ( rect.bottom - rect.top ) + scrollfromtop )	||
2245		 mousepos.x < ( rect.left + scrollfromleft ) 							||
2246		 mousepos.x > ( rect.left + ( rect.right - rect.left ) + scrollfromleft ) )
2247	{
2248		this.element_input_high.focus();
2249	}
2250}
2251
2252MMFacet_RangeSlider.prototype.Event_Focus_Input_Low = function( e )
2253{
2254	this.element_input_container_low.className = classNameAdd( this.element_input_container_low, 'mm_facet_rangeslider_input_active' );
2255}
2256
2257MMFacet_RangeSlider.prototype.Event_Blur_Input_Low = function( e )
2258{
2259	this.element_input_container_low.className = classNameRemove( this.element_input_container_low, 'mm_facet_rangeslider_input_active' );
2260	this.SetSelectedRange( stod_def( this.element_input_low.value.replace( /[^\d.-]/g, '' ), 0 ), stod_def( this.element_input_high.value.replace( /[^\d.-]/g, '' ), 0 ) );
2261	this.RequestRender();
2262	this.Submit();
2263}
2264
2265MMFacet_RangeSlider.prototype.Event_Focus_Input_High = function( e )
2266{
2267	this.element_input_container_high.className = classNameAdd( this.element_input_container_high, 'mm_facet_rangeslider_input_active' );
2268}
2269
2270MMFacet_RangeSlider.prototype.Event_Blur_Input_High = function( e )
2271{
2272	this.element_input_container_high.className = classNameRemove( this.element_input_container_high, 'mm_facet_rangeslider_input_active' );
2273	this.SetSelectedRange( stod_def( this.element_input_low.value.replace( /[^\d.-]/g, '' ), 0 ), stod_def( this.element_input_high.value.replace( /[^\d.-]/g, '' ), 0 ) );
2274	this.RequestRender();
2275	this.Submit();
2276}
2277
2278MMFacet_RangeSlider.prototype.Event_KeyDown_Input = function( e )
2279{
2280	var keycode = e.keyCode || e.which;
2281
2282	if ( keycode == 13 )
2283	{
2284		this.SetSelectedRange( stod_def( this.element_input_low.value.replace( /[^\d.-]/g, '' ), 0 ), stod_def( this.element_input_high.value.replace( /[^\d.-]/g, '' ), 0 ) );
2285		this.RequestRender();
2286		this.Submit();
2287	}
2288
2289	return true;
2290}
2291
2292MMFacet_RangeSlider.prototype.Event_MouseDown_Handle_Low = function( e )
2293{
2294	var mousepos;
2295
2296	AddEvent( window, 'mousemove', this.event_mousemove_handle_low );
2297	AddEvent( window, 'mouseup', this.event_mouseup_handle_low );
2298	AddEvent( window, 'blur', this.event_mouseup_handle_low );
2299
2300	mousepos							= captureMousePosition( e );
2301	this.slider_started					= true;
2302	this.slider_target					= e.target ? e.target : e.srcElement;
2303	this.slider_startpos_low			= mousepos.x;
2304	this.slider_originalposition_low	= this.element_track_handle_low.getBoundingClientRect().left - this.element_track.getBoundingClientRect().left;
2305
2306	clearTextSelection();
2307	document.body.focus();
2308	document.body.unselectable			= 'on';
2309	document.onselectstart				= this.event_returnfalse;
2310	this.slider_target.ondragstart		= this.event_returnfalse;
2311
2312	if ( this.slider_target.setCapture )
2313	{
2314		this.slider_target.setCapture();
2315	}
2316
2317	eventStopPropagation( e );
2318	return eventPreventDefault( e );
2319}
2320
2321MMFacet_RangeSlider.prototype.Event_MouseMove_Handle_Low = function( e )
2322{
2323	var mousepos, slider_rect, handle_rect, slider_length, available_handle_length;
2324
2325	if ( this.slider_started )
2326	{
2327		eventPreventDefault( e );
2328		this.slider_started		= false;
2329	}
2330
2331	mousepos					= captureMousePosition( e );
2332	slider_rect					= this.element_track.getBoundingClientRect();
2333	handle_rect					= this.element_track_handle_low.getBoundingClientRect();
2334	slider_length				= ( slider_rect.right - slider_rect.left );
2335	available_handle_length		= slider_length - ( handle_rect.right - handle_rect.left + 2 );
2336	this.slider_position_low	= stod_range( this.slider_originalposition_low + ( mousepos.x - this.slider_startpos_low ), 0, available_handle_length );
2337
2338	clearTextSelection();
2339	this.RequestRender();
2340
2341	eventStopPropagation( e );
2342	return eventPreventDefault( e );
2343}
2344
2345MMFacet_RangeSlider.prototype.Event_MouseUp_Handle_Low = function( e )
2346{
2347	RemoveEvent( window, 'mousemove', this.event_mousemove_handle_low );
2348	RemoveEvent( window, 'mouseup', this.event_mouseup_handle_low );
2349	RemoveEvent( window, 'blur', this.event_mouseup_handle_low );
2350
2351	this.slider_started				= false;
2352	document.body.unselectable		= null;
2353	document.onselectstart			= null;
2354	this.slider_target.ondragstart	= null;
2355
2356	if ( this.slider_target.releaseCapture )
2357	{
2358		this.slider_target.releaseCapture();
2359	}
2360
2361	this.RequestRender();
2362	this.Submit();
2363
2364	eventStopPropagation( e );
2365	return eventPreventDefault( e );
2366}
2367
2368MMFacet_RangeSlider.prototype.Event_MouseDown_Handle_High = function( e )
2369{
2370	var mousepos;
2371
2372	AddEvent( window, 'mousemove', this.event_mousemove_handle_high );
2373	AddEvent( window, 'mouseup', this.event_mouseup_handle_high );
2374	AddEvent( window, 'blur', this.event_mouseup_handle_high );
2375
2376	mousepos							= captureMousePosition( e );
2377	this.slider_started					= true;
2378	this.slider_target					= e.target ? e.target : e.srcElement;
2379	this.slider_startpos_high			= mousepos.x;
2380	this.slider_originalposition_high	= this.element_track_handle_high.getBoundingClientRect().left - this.element_track.getBoundingClientRect().left;
2381
2382	clearTextSelection();
2383	document.body.focus();
2384	document.body.unselectable			= 'on';
2385	document.onselectstart				= this.event_returnfalse;
2386	this.slider_target.ondragstart		= this.event_returnfalse;
2387
2388	if ( this.slider_target.setCapture )
2389	{
2390		this.slider_target.setCapture();
2391	}
2392
2393	eventStopPropagation( e );
2394	return eventPreventDefault( e );
2395}
2396
2397MMFacet_RangeSlider.prototype.Event_MouseMove_Handle_High = function( e )
2398{
2399	var mousepos, slider_rect, handle_rect, slider_length, available_handle_length;
2400
2401	if ( this.slider_started )
2402	{
2403		eventPreventDefault( e );
2404		this.slider_started		= false;
2405	}
2406
2407	mousepos					= captureMousePosition( e );
2408	slider_rect					= this.element_track.getBoundingClientRect();
2409	handle_rect					= this.element_track_handle_high.getBoundingClientRect();
2410	slider_length				= ( slider_rect.right - slider_rect.left );
2411	available_handle_length		= slider_length - ( handle_rect.right - handle_rect.left + 2 );
2412	this.slider_position_high	= stod_range( this.slider_originalposition_high + ( mousepos.x - this.slider_startpos_high ), 0, available_handle_length );
2413
2414	clearTextSelection();
2415	this.RequestRender();
2416
2417	eventStopPropagation( e );
2418	return eventPreventDefault( e );
2419}
2420
2421MMFacet_RangeSlider.prototype.Event_MouseUp_Handle_High = function( e )
2422{
2423	RemoveEvent( window, 'mousemove', this.event_mousemove_handle_high );
2424	RemoveEvent( window, 'mouseup', this.event_mouseup_handle_high );
2425	RemoveEvent( window, 'blur', this.event_mouseup_handle_high );
2426
2427	this.slider_started				= false;
2428	document.body.unselectable		= null;
2429	document.onselectstart			= null;
2430	this.slider_target.ondragstart	= null;
2431
2432	if ( this.slider_target.releaseCapture )
2433	{
2434		this.slider_target.releaseCapture();
2435	}
2436
2437	this.RequestRender();
2438	this.Submit();
2439
2440	eventStopPropagation( e );
2441	return eventPreventDefault( e );
2442}
2443
2444MMFacet_RangeSlider.prototype.Event_TouchStart_Handle_Low = function( e )
2445{
2446	if ( e.touches && e.touches.length > 1 )
2447	{
2448		if ( this.touchevents_applied )
2449		{
2450			RemoveEvent( this.touch_target, 'touchmove',	this.event_touchmove_handle_low );
2451			RemoveEvent( this.touch_target, 'touchend',		this.event_touchend_handle_low );
2452			RemoveEvent( this.touch_target, 'touchcancel',	this.event_touchend_handle_low );
2453
2454			this.touchevents_applied	= false;
2455		}
2456
2457		return;
2458	}
2459
2460	this.touches						= e.touches;
2461	this.touch_target					= e.target;
2462
2463	this.slider_started					= true;
2464	this.slider_startpos_low			= this.touches[ 0 ].pageX;
2465	this.slider_originalposition_low	= this.element_track_handle_low.getBoundingClientRect().left - this.element_track.getBoundingClientRect().left;
2466
2467	if ( !this.touchevents_applied )
2468	{
2469		this.touchevents_applied		= true;
2470
2471		AddEvent( e.target, 'touchmove',	this.event_touchmove_handle_low );
2472		AddEvent( e.target, 'touchend',		this.event_touchend_handle_low );
2473		AddEvent( e.target, 'touchcancel',	this.event_touchend_handle_low );
2474	}
2475
2476	eventStopPropagation( e );
2477	return eventPreventDefault( e );
2478}
2479
2480MMFacet_RangeSlider.prototype.Event_TouchMove_Handle_Low = function( e )
2481{
2482	var slider_rect, handle_rect, slider_length, available_handle_length;
2483
2484	if ( this.slider_started )
2485	{
2486		eventPreventDefault( e );
2487		this.slider_started		= false;
2488	}
2489
2490	this.touches				= e.touches;
2491
2492	slider_rect					= this.element_track.getBoundingClientRect();
2493	handle_rect					= this.element_track_handle_low.getBoundingClientRect();
2494	slider_length				= ( slider_rect.right - slider_rect.left );
2495	available_handle_length		= slider_length - ( handle_rect.right - handle_rect.left + 2 );
2496	this.slider_position_low	= stod_range( this.slider_originalposition_low + ( this.touches[ 0 ].pageX - this.slider_startpos_low ), 0, available_handle_length );
2497
2498	this.RequestRender();
2499}
2500
2501MMFacet_RangeSlider.prototype.Event_TouchEnd_Handle_Low = function( e )
2502{
2503	RemoveEvent( this.touch_target, 'touchmove',	this.event_touchmove_handle_low );
2504	RemoveEvent( this.touch_target, 'touchend',		this.event_touchend_handle_low );
2505	RemoveEvent( this.touch_target, 'touchcancel',	this.event_touchend_handle_low );
2506
2507	this.touchevents_applied	= false;
2508	this.slider_started			= false;
2509
2510	this.RequestRender();
2511	this.Submit();
2512
2513	eventStopPropagation( e );
2514	return eventPreventDefault( e );
2515}
2516
2517MMFacet_RangeSlider.prototype.Event_TouchStart_Handle_High = function( e )
2518{
2519	if ( e.touches && e.touches.length > 1 )
2520	{
2521		if ( this.touchevents_applied )
2522		{
2523			RemoveEvent( this.touch_target, 'touchmove',	this.event_touchmove_handle_high );
2524			RemoveEvent( this.touch_target, 'touchend',		this.event_touchend_handle_high );
2525			RemoveEvent( this.touch_target, 'touchcancel',	this.event_touchend_handle_high );
2526
2527			this.touchevents_applied	= false;
2528		}
2529
2530		return;
2531	}
2532
2533	this.touches						= e.touches;
2534	this.touch_target					= e.target;
2535
2536	this.slider_started					= true;
2537	this.slider_startpos_high			= this.touches[ 0 ].pageX;
2538	this.slider_originalposition_high	= this.element_track_handle_high.getBoundingClientRect().left - this.element_track.getBoundingClientRect().left;
2539
2540	if ( !this.touchevents_applied )
2541	{
2542		this.touchevents_applied		= true;
2543
2544		AddEvent( e.target, 'touchmove',	this.event_touchmove_handle_high );
2545		AddEvent( e.target, 'touchend',		this.event_touchend_handle_high );
2546		AddEvent( e.target, 'touchcancel',	this.event_touchend_handle_high );
2547	}
2548
2549	eventStopPropagation( e );
2550	return eventPreventDefault( e );
2551}
2552
2553MMFacet_RangeSlider.prototype.Event_TouchMove_Handle_High = function( e )
2554{
2555	var slider_rect, handle_rect, slider_length, available_handle_length;
2556
2557	if ( this.slider_started )
2558	{
2559		eventPreventDefault( e );
2560		this.slider_started		= false;
2561	}
2562
2563	this.touches				= e.touches;
2564
2565	slider_rect					= this.element_track.getBoundingClientRect();
2566	handle_rect					= this.element_track_handle_high.getBoundingClientRect();
2567	slider_length				= ( slider_rect.right - slider_rect.left );
2568	available_handle_length		= slider_length - ( handle_rect.right - handle_rect.left + 2 );
2569	this.slider_position_high	= stod_range( this.slider_originalposition_high + ( this.touches[ 0 ].pageX - this.slider_startpos_high ), 0, available_handle_length );
2570
2571	this.RequestRender();
2572}
2573
2574MMFacet_RangeSlider.prototype.Event_TouchEnd_Handle_High = function( e )
2575{
2576	RemoveEvent( this.touch_target, 'touchmove',	this.event_touchmove_handle_high );
2577	RemoveEvent( this.touch_target, 'touchend',		this.event_touchend_handle_high );
2578	RemoveEvent( this.touch_target, 'touchcancel',	this.event_touchend_handle_high );
2579
2580	this.touchevents_applied	= false;
2581	this.slider_started			= false;
2582
2583	this.RequestRender();
2584	this.Submit();
2585
2586	eventStopPropagation( e );
2587	return eventPreventDefault( e );
2588}
2589
2590MMFacet_RangeSlider.prototype.Submit = function()
2591{
2592	if ( this.original_value !== this.element_value.value )
2593	{
2594		MMProdList_UpdateQuery( this.element_value );
2595	}
2596}
2597
2598// Rendering
2599
2600MMFacet_RangeSlider.prototype.RequestRender = function()
2601{
2602	if ( !this.shouldrender )
2603	{
2604		this.shouldrender = true;
2605		window.requestAnimationFrame( this.event_render );
2606	}
2607}
2608
2609MMFacet_RangeSlider.prototype.Render = function()
2610{
2611	if ( this.shouldrender )
2612	{
2613		this.shouldrender = false;
2614		this.Redraw();
2615	}
2616}
2617
2618MMFacet_RangeSlider.prototype.Redraw = function()
2619{
2620	var low, high, tmp_low, slider_rect, handle_rect, slider_length, available_handle_length;
2621
2622	slider_rect						= this.element_track.getBoundingClientRect();
2623	handle_rect						= this.element_track_handle_high.getBoundingClientRect();
2624	slider_length					= ( slider_rect.right - slider_rect.left );
2625	available_handle_length			= slider_length - ( handle_rect.right - handle_rect.left + 2 );
2626
2627	this.slider_position_low		= stod_range( this.slider_position_low, 0, available_handle_length );
2628	this.slider_position_high		= stod_range( this.slider_position_high, 0, available_handle_length );
2629
2630	low								= this.slider_position_low;
2631	high							= this.slider_position_high;
2632
2633	if ( low > high )
2634	{
2635		tmp_low	= low;
2636		low		= high;
2637		high	= tmp_low;
2638	}
2639
2640	this.element_track_handle_low.style.left	= this.slider_position_low + 'px';
2641	this.element_track_handle_high.style.left	= this.slider_position_high + 'px';
2642	this.element_track_selection.style.left		= ( low + ( this.element_track_handle_low.offsetWidth / 2 ) ) + 'px';
2643	this.element_track_selection.style.right	= ( this.element_track.offsetWidth - ( high + ( this.element_track_handle_low.offsetWidth / 2 ) ) ) + 'px';
2644
2645	this.FormatValues( low, high );
2646}
2647
2648// Miscellaneous
2649////////////////////////////////////////////////////
2650
2651function MMDynamic_Form_Submit( action, hidden_input_fields, form_attributes )
2652{
2653	var key, form;
2654
2655	form = newElement( 'form', { 'action': action, 'method': 'POST', 'taget': '_self' }, null, null );
2656
2657	if ( form_attributes && ( typeof form_attributes === 'object' ) )
2658	{
2659		for ( key in form_attributes )
2660		{
2661			form.setAttribute( key, form_attributes[ key ] );
2662		}
2663	}
2664
2665	for ( key in hidden_input_fields )
2666	{
2667		newElement( 'input', { 'type': 'hidden', 'name': key, 'value': hidden_input_fields[ key ] }, null, form );
2668	}
2669
2670	document.body.appendChild( form );
2671	form.submit();
2672	document.body.removeChild( form );
2673}

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.