PageSourceSearch

https://biblio-vidnoe.ru/bitrix/js/main/core/core.js?1326010829

js biblio-vidnoe.ru collected 2026-10-02 10:00:09 UTC 53,254 bytes, 2,583 lines download raw bytes

1/**********************************************************************/
2/*********** Bitrix JS Core library ver 0.9.0 beta ********************/
3/**********************************************************************/
4
5;(function(window){
6if (window.BX) return;
7
8
9var BX = function(node, bCache)
10{
11	if (BX.type.isNotEmptyString(node))
12	{
13		var ob;
14
15		if (!!bCache && null != NODECACHE[node]) ob = NODECACHE[node];
16		ob = ob || document.getElementById(node);
17		if (!!bCache) NODECACHE[node] = ob;
18
19		return ob;
20	}
21	else if (BX.type.isDomNode(node))
22		return node;
23	else if (BX.type.isFunction(node))
24		return BX.ready(node);
25
26	return null;
27},
28
29/* language messages */
30MESS = {},
31
32/* ready */
33__readyHandler = null,
34readyBound = false,
35readyList = [],
36
37/* list of registered proxy functions */
38proxyId = 1,
39proxyList = [],
40
41/* getElementById cache */
42NODECACHE = {},
43
44/* List of denied event handlers */
45deniedEvents = [],
46
47/* list of registered event handlers */
48eventsList = [],
49
50/* list of registered custom events */
51customEvents = {},
52
53/* list of external garbage collectors */
54garbageCollectors = [],
55
56/* browser detection */
57bOpera = navigator.userAgent.toLowerCase().indexOf('opera') != -1,
58bSafari = navigator.userAgent.toLowerCase().indexOf('webkit') != -1,
59bIE = document.attachEvent && !bOpera,
60
61/* regexps */
62r = {
63	script: /<script([^>]*)>/i,
64	script_src: /src=["\']([^"\']+)["\']/i,
65	space: /\s+/,
66	ltrim: /^[\s\r\n]+/g,
67	rtrim: /[\s\r\n]+$/g
68},
69
70eventTypes = {
71	click: 'MouseEvent',
72	dblclick: 'MouseEvent',
73	mousedown: 'MouseEvent',
74	mousemove: 'MouseEvent',
75	mouseout: 'MouseEvent',
76	mouseover: 'MouseEvent',
77	mouseup: 'MouseEvent'
78}
79
80lastWait = [];
81
82BX.ext = function(ob) {for (var i in ob) this[i] = ob[i];}
83
84/* OO emulation utility */
85BX.extend = function(child, parent)
86{
87	var f = function() {};
88	f.prototype = parent.prototype;
89	child.prototype = new f();
90	child.prototype.constructor = child;
91	child.superclass = parent.prototype;
92}
93
94BX.is_subclass_of = function(ob, parent_class)
95{
96	if (ob instanceof parent_class)
97		return true;
98
99	if (parent_class.superclass)
100		return BX.is_subclass_of(ob, parent_class.superclass);
101
102	return false;
103}
104
105// language utility
106BX.message = function(mess)
107{
108	if (BX.type.isString(mess))
109		return MESS[mess];
110	else
111	{
112		for (var i in mess)
113		{
114			MESS[i] = mess[i];
115		}
116		return true;
117	}
118}
119
120BX.bitrix_sessid = function() {return MESS.bitrix_sessid;}
121
122/* DOM manipulation */
123BX.create = function(tag, data, context)
124{
125	context = context || document;
126
127	if (null == data && typeof tag == 'object' && tag.constructor !== String)
128	{
129		data = tag; tag = tag.tag;
130	}
131
132	var elem;
133	if (BX.browser.IsIE() && !BX.browser.IsIE9() && null != data && null != data.props && (data.props.name || data.props.id))
134	{
135		elem = context.createElement('<' + tag + (data.props.name ? ' name="' + data.props.name + '"' : '') + (data.props.id ? ' id="' + data.props.id + '"' : '') + '>');
136	}
137	else
138	{
139		elem = context.createElement(tag);
140	}
141
142	return data ? BX.adjust(elem, data) : elem;
143}
144
145BX.adjust = function(elem, data)
146{
147	var j,len;
148
149	if (!elem.nodeType)
150		return null;
151
152	if (elem.nodeType == 9)
153		elem = elem.body;
154
155	if (data.attrs)
156	{
157		for (j in data.attrs)
158		{
159			if (j == 'class' || j == 'className')
160				elem.className = data.attrs[j];
161			else if(data.attrs[j] == "")
162				elem.removeAttribute(j);
163			else
164				elem.setAttribute(j, data.attrs[j]);
165		}
166	}
167
168	if (data.style)
169	{
170		for (j in data.style)
171			elem.style[j] = data.style[j];
172	}
173
174	if (data.props)
175	{
176		for (j in data.props)
177			elem[j] = data.props[j];
178	}
179
180	if (data.events)
181	{
182		for (j in data.events)
183			BX.bind(elem, j, data.events[j]);
184	}
185
186	if (data.children && data.children.length > 0)
187	{
188		for (j=0,len=data.children.length; j<len; j++)
189		{
190			if (BX.type.isNotEmptyString(data.children[j]))
191				elem.innerHTML += data.children[j];
192			else if (BX.type.isElementNode(data.children[j]))
193				elem.appendChild(data.children[j]);
194		}
195	}
196	else if (data.text)
197	{
198		BX.cleanNode(elem);
199		elem.appendChild((elem.ownerDocument || document).createTextNode(data.text));
200	}
201	else if (data.html)
202	{
203		elem.innerHTML = data.html;
204	}
205
206	return elem;
207}
208
209BX.remove = function(ob)
210{
211	if (null != ob.parentNode)
212		ob.parentNode.removeChild(ob);
213	ob = null;
214	return null;
215}
216
217BX.cleanNode = function(node, bSuicide)
218{
219	node = BX(node);
220	bSuicide = !!bSuicide;
221
222	if (node && node.childNodes)
223	{
224		while(node.childNodes.length > 0)
225			node.removeChild(node.firstChild);
226	}
227
228	if (node && bSuicide)
229	{
230		node = BX.remove(node);
231	}
232
233	return node;
234}
235
236BX.addClass = function(ob, value)
237{
238	var classNames;
239	ob = BX(ob)
240
241	if (ob)
242	{
243		if (!ob.className)
244		{
245			ob.className = value
246		}
247		else
248		{
249			classNames = (value || "").split(r.space);
250
251			var className = " " + ob.className + " ";
252			for (var j = 0, cl = classNames.length; j < cl; j++)
253			{
254				if (className.indexOf(" " + classNames[j] + " ") < 0)
255				{
256					ob.className += " " + classNames[j];
257				}
258			}
259		}
260	}
261
262	return ob;
263}
264
265BX.removeClass = function(ob, value)
266{
267	ob = BX(ob);
268	if (ob)
269	{
270		if (ob.className)
271		{
272			if (BX.type.isString(value))
273			{
274				var classNames = value.split(r.space), className = " " + ob.className + " ";
275				for (var j = 0, cl = classNames.length; j < cl; j++)
276				{
277					className = className.replace(" " + classNames[j] + " ", " ");
278				}
279
280				ob.className = BX.util.trim(className);
281			}
282			else
283			{
284				ob.className = "";
285			}
286		}
287	}
288
289	return ob;
290}
291
292BX.toggleClass = function(ob, value)
293{
294	var className;
295	if (BX.type.isArray(value))
296	{
297		className = ' ' + ob.className + ' ';
298		for (var j = 0, len = value.length; j < len; j++)
299		{
300			if (BX.hasClass(ob, value[j]))
301			{
302				className = (' ' + className + ' ').replace(' ' + value[j] + ' ', ' ');
303				className += ' ' + value[j >= len-1 ? 0 : j+1];
304
305				j--;
306				break;
307			}
308		}
309
310		if (j == len)
311			ob.className += ' ' + value[0];
312		else
313			ob.className = className;
314
315		ob.className = BX.util.trim(ob.className);
316	}
317	else if (BX.type.isNotEmptyString(value))
318	{
319		className = ob.className;
320		if (BX.hasClass(ob, value))
321		{
322			className = (' ' + className + ' ').replace(' ' + value + ' ', ' ');
323		}
324		else
325		{
326			className += ' ' + value;
327		}
328
329		ob.className = BX.util.trim(className);
330	}
331
332	return ob;
333}
334
335BX.hasClass = function(el, className)
336{
337	if (!el.className)
338		return false;
339	return ((" " + el.className + " ").indexOf(" " + className + " ")) >= 0;
340}
341
342BX.hoverEvents = function(el)
343{
344	if (el)
345		return BX.adjust(el, {events: BX.hoverEvents()});
346	else
347		return {mouseover: BX.hoverEventsHover, mouseout: BX.hoverEventsHout};
348}
349
350BX.hoverEventsHover = function(){BX.addClass(this,'bx-hover');this.BXHOVER=true;}
351BX.hoverEventsHout = function(){BX.removeClass(this,'bx-hover');this.BXHOVER=false;}
352
353BX.focusEvents = function(el)
354{
355	if (el)
356		return BX.adjust(el, {events: BX.focusEvents()});
357	else
358		return {mouseover: BX.focusEventsFocus, mouseout: BX.focusEventsBlur};
359}
360
361BX.focusEventsFocus = function(){BX.addClass(this,'bx-focus');this.BXFOCUS=true;}
362BX.focusEventsBlur = function(){BX.removeClass(this,'bx-focus');this.BXFOCUS=false;}
363
364BX.setUnselectable = function(node)
365{
366	BX.addClass(node, 'bx-unselectable');
367	node.setAttribute('unSelectable', 'on');
368}
369
370BX.setSelectable = function(node)
371{
372	BX.removeClass(node, 'bx-unselectable');
373	node.removeAttribute('unSelectable');
374}
375
376BX.styleIEPropertyName = function(name)
377{
378	if (name == 'float')
379		name = BX.browser.IsIE() ? 'styleFloat' : 'cssFloat';
380	else
381	{
382		var reg = /(\-([a-z]){1})/g;
383		if (reg.test(name))
384		{
385			name = name.replace(reg, function () {return arguments[2].toUpperCase();});
386		}
387	}
388	return name;
389}
390
391/* CSS-notation should be used here */
392BX.style = function(el, property, value)
393{
394	if (!BX.type.isElementNode(el))
395		return null;
396
397	if (value == null)
398	{
399		var res;
400		if(el.currentStyle)
401			res = el.currentStyle[BX.styleIEPropertyName(property)];
402		else if(window.getComputedStyle)
403			res = BX.GetContext(el).getComputedStyle(el, null).getPropertyValue(property);
404
405		if(!res)
406			res = '';
407		return res;
408	}
409	else
410	{
411		el.style[BX.styleIEPropertyName(property)] = value;
412		return el;
413	}
414}
415
416BX.focus = function(el)
417{
418	try
419	{
420		el.focus();
421		return true;
422	}
423	catch (e)
424	{
425		return false;
426	}
427}
428
429/*
430	params: {
431		tagName|tag : 'tagName',
432		className|class : 'className',
433		attribute : {attribute : value, attribute : value} | attribute | [attribute, attribute....],
434		property : {prop: value, prop: value} | prop | [prop, prop]
435	}
436
437	all values can be RegExps or strings
438*/
439BX.findChildren = function(obj, params, recursive)
440{
441	return BX.findChild(obj, params, recursive, true);
442}
443
444BX.findChild = function(obj, params, recursive, get_all)
445{
446	if(!obj || !obj.childNodes) return null;
447
448	recursive = !!recursive; get_all = !!get_all;
449
450	var n = obj.childNodes.length, result = [];
451
452	for (var j=0; j<n; j++)
453	{
454		var child = obj.childNodes[j];
455
456		if (_checkNode(child, params))
457		{
458			if (get_all)
459				result.push(child)
460			else
461				return child;
462		}
463
464		if(recursive == true)
465		{
466			var res = BX.findChild(child, params, recursive, get_all);
467			if (res)
468			{
469				if (get_all)
470					result = BX.util.array_merge(result, res);
471				else
472					return res;
473			}
474		}
475	}
476
477	if (get_all && result.length > 0)
478		return result;
479	else
480		return null;
481}
482
483BX.findParent = function(obj, params)
484{
485	if(!obj)
486		return null;
487
488	var o = obj;
489	while(o.parentNode)
490	{
491		var parent = o.parentNode;
492
493		if (_checkNode(parent, params))
494			return parent;
495
496		o = parent;
497	}
498	return null;
499}
500
501BX.findNextSibling = function(obj, params)
502{
503	if(!obj)
504		return null;
505	var o = obj;
506	while(o.nextSibling)
507	{
508		var sibling = o.nextSibling;
509		if (_checkNode(sibling, params))
510			return sibling;
511		o = sibling;
512	}
513	return null;
514}
515
516BX.findPreviousSibling = function(obj, params)
517{
518	if(!obj)
519		return null;
520
521	var o = obj;
522	while(o.previousSibling)
523	{
524		var sibling = o.previousSibling;
525		if(_checkNode(sibling, params))
526			return sibling;
527		o = sibling;
528	}
529	return null;
530}
531
532BX.clone = function(obj, bCopyObj)
533{
534	var _obj, i, l;
535	if (bCopyObj !== false)
536		bCopyObj = true;
537
538	if (obj === null)
539		return null;
540
541	if (BX.type.isDomNode(obj))
542	{
543		_obj = obj.cloneNode(bCopyObj);
544	}
545	else if (typeof obj == 'object')
546	{
547		if (BX.type.isArray(obj))
548		{
549			_obj = [];
550			for (i=0,l=obj.length;i<l;i++)
551			{
552				if (typeof obj[i] == "object" && bCopyObj)
553					_obj[i] = BX.clone(obj[i], bCopyObj);
554				else
555					_obj[i] = obj[i];
556			}
557		}
558		else
559		{
560			_obj =  {};
561			if (obj.constructor)
562			{
563				if (obj.constructor === Date)
564					_obj = new Date(obj);
565				else
566					_obj = new obj.constructor();
567			}
568
569			for (i in obj)
570			{
571				if (typeof obj[i] == "object" && bCopyObj)
572					_obj[i] = BX.clone(obj[i], bCopyObj);
573				else
574					_obj[i] = obj[i];
575			}
576		}
577
578	}
579	else
580	{
581		_obj = obj;
582	}
583
584	return _obj;
585}
586
587/* events */
588BX.bind = function(el, evname, func)
589{
590	if (!el)
591		return;
592
593	if (evname === 'mousewheel')
594		BX.bind(el, 'DOMMouseScroll', func);
595
596	if (el.addEventListener)
597		el.addEventListener(evname, func, false);
598	else if(el.attachEvent) // IE
599		el.attachEvent("on" + evname, BX.proxy(func, el));
600	else
601		el["on" + evname] = func;
602
603	eventsList[eventsList.length] = {'element': el, 'event': evname, 'fn': func};
604}
605
606BX.unbind = function(el, evname, func)
607{
608	if (!el)
609		return;
610
611	if (evname === 'mousewheel')
612		BX.unbind(el, 'DOMMouseScroll', func);
613
614	if(el.removeEventListener) // Gecko / W3C
615		el.removeEventListener(evname, func, false);
616	else if(el.detachEvent) // IE
617		el.detachEvent("on" + evname, BX.proxy(func, el));
618	else
619		el["on" + evname] = null;
620}
621
622BX.unbindAll = function(el)
623{
624	if (!el)
625		return;
626
627	for (var i=0,len=eventsList.length; i<len; i++)
628	{
629		try
630		{
631			if (eventsList[i] && (null==el || el==eventsList[i].element))
632			{
633				BX.unbind(eventsList[i].element, eventsList[i].event, eventsList[i].fn);
634				eventsList[i] = null;
635			}
636		}
637		catch(e){}
638	}
639
640	if (null==el)
641	{
642		eventsList = [];
643	}
644}
645
646var captured_events = null, _bind = null;
647BX.CaptureEvents = function(el_c, evname_c)
648{
649	if (_bind)
650		return false;
651
652	_bind = BX.bind; captured_events = [];
653
654	BX.bind = function(el, evname, func)
655	{
656		if (el === el_c && evname === evname_c)
657			captured_events.push(func);
658
659		_bind.apply(this, arguments);
660	}
661}
662
663BX.CaptureEventsGet = function()
664{
665	if (_bind)
666	{
667		BX.bind = _bind;
668
669		var captured = captured_events;
670
671		_bind = null; captured_events = null;
672		return captured;
673	}
674}
675
676BX.fireEvent = function(ob,ev)
677{
678	if (document.createEventObject)
679	{
680		// IE
681		var e = document.createEventObject();
682		ob.fireEvent('on' + ev, e)
683		if (ob[ev])
684			ob[ev]();
685	}
686	else
687	{
688		// non-IE
689		var e = null;
690
691		switch (eventTypes[ev])
692		{
693			case 'MouseEvent':
694				e = document.createEvent('MouseEvent');
695				e.initMouseEvent(ev, true, true, top, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, null);
696			break;
697			default:
698				e = document.createEvent('Event');
699				e.initEvent(ev, true, true);
700		}
701
702		ob.dispatchEvent(e);
703    }
704}
705
706BX.getWheelData = function(e)
707{
708	e = e || window.event;
709	return wheelData = e.detail ? e.detail * -1 : e.wheelDelta / 40;
710}
711
712BX.proxy_context = null;
713
714BX.delegate = function (func, thisObject)
715{
716	return function() {
717		var cur = BX.proxy_context;
718		BX.proxy_context = this;
719		var res = func.apply(thisObject, arguments);
720		BX.proxy_context = cur;
721		return res;
722	}
723}
724
725BX.delegateLater = function (func_name, thisObject, contextObject)
726{
727	return function() {
728		var cur = BX.proxy_context;
729		BX.proxy_context = this;
730		var res = thisObject[func_name].apply(contextObject||thisObject, arguments);
731		BX.proxy_context = cur;
732		return res;
733	}
734}
735
736BX.proxy = function(func, thisObject)
737{
738	if (null == thisObject.__proxy_id)
739	{
740		proxyList[thisObject.__proxy_id = proxyList.length] = {};
741	}
742
743	if (null == func.__proxy_id)
744		func.__proxy_id = proxyId++;
745
746	if (null == proxyList[thisObject.__proxy_id][func.__proxy_id])
747		proxyList[thisObject.__proxy_id][func.__proxy_id] = BX.delegate(func, thisObject);
748
749	return proxyList[thisObject.__proxy_id][func.__proxy_id];
750}
751
752BX.bindDelegate = function (elem, eventName, isTarget, handler)
753{
754	var h = BX.delegateEvent(elem, isTarget, handler);
755	BX.bind(elem, eventName, h);
756	return h;
757}
758
759BX.delegateEvent = function(el, isTarget, handler)
760{
761	return function(e)
762	{
763		e = e || window.event;
764		var target = e.target || e.srcElement;
765
766		while (target != this)
767		{
768			if (_checkNode(target, isTarget))
769			{
770				return handler.call(target, e);
771			}
772
773			target = target.parentNode;
774		}
775	}
776}
777
778BX.False = function() {return false;}
779BX.DoNothing = function() {}
780
781// TODO: also check event handlers set via BX.bind()
782BX.denyEvent = function(el, ev)
783{
784	deniedEvents.push([el, ev, el['on' + ev]]);
785	el['on' + ev] = BX.DoNothing;
786}
787
788BX.allowEvent = function(el, ev)
789{
790	for(var i=0, len=deniedEvents.length; i<len; i++)
791	{
792		if (deniedEvents[i][0] == el && deniedEvents[i][1] == ev)
793		{
794			el['on' + ev] = deniedEvents[i][2];
795			BX.util.deleteFromArray(deniedEvents, i);
796			return;
797		}
798	}
799}
800
801BX.fixEventPageXY = function(event)
802{
803	BX.fixEventPageX(event);
804	BX.fixEventPageY(event);
805	return event;
806};
807
808BX.fixEventPageX = function(event)
809{
810	if (event.pageX == null && event.clientX != null)
811	{
812    	event.pageX =
813			event.clientX +
814			(document.documentElement && document.documentElement.scrollLeft || document.body && document.body.scrollLeft || 0) -
815			(document.documentElement.clientLeft || 0);
816	}
817
818	return event;
819};
820
821BX.fixEventPageY = function(event)
822{
823	if (event.pageY == null && event.clientY != null)
824	{
825		event.pageY =
826			event.clientY +
827			(document.documentElement && document.documentElement.scrollTop || document.body && document.body.scrollTop || 0) -
828			(document.documentElement.clientTop || 0);
829	}
830
831	return event;
832};
833
834BX.PreventDefault = function(e)
835{
836	if(!e) e = window.event;
837	if(e.stopPropagation)
838	{
839		e.preventDefault();
840		e.stopPropagation();
841	}
842	else
843	{
844		e.cancelBubble = true;
845		e.returnValue = false;
846	}
847	return false;
848}
849
850BX.eventReturnFalse = function(e)
851{
852	e=e||window.event;
853	if (e && e.preventDefault) e.preventDefault();
854	else e.returnValue = false;
855	return false;
856}
857
858BX.eventCancelBubble = function(e)
859{
860	e=e||window.event;
861	if(e && e.stopPropagation)
862		e.stopPropagation();
863	else
864		e.cancelBubble = true;
865}
866
867/* custom events */
868/*
869	BX.addCustomEvent(eventObject, eventName, eventHandler) - set custom event handler for particular object
870	BX.addCustomEvent(eventName, eventHandler) - set custom event handler for all objects
871*/
872BX.addCustomEvent = function(eventObject, eventName, eventHandler)
873{
874	/* shift parameters for short version */
875	if (BX.type.isString(eventObject))
876	{
877		eventHandler = eventName;
878		eventName = eventObject;
879		eventObject = window;
880	}
881
882	eventName = eventName.toUpperCase();
883
884	if (!customEvents[eventName])
885		customEvents[eventName] = [];
886
887	customEvents[eventName].push(
888		{
889			handler: eventHandler,
890			obj: eventObject
891		}
892	);
893}
894
895BX.removeCustomEvent = function(eventObject, eventName, eventHandler)
896{
897	/* shift parameters for short version */
898	if (BX.type.isString(eventObject))
899	{
900		eventHandler = eventName;
901		eventName = eventObject;
902		eventObject = window;
903	}
904
905	eventName = eventName.toUpperCase();
906
907	if (!customEvents[eventName])
908		return;
909
910	for (var i = 0, l = customEvents[eventName].length; i < l; i++)
911	{
912		if (!customEvents[eventName][i])
913			continue;
914		if (customEvents[eventName][i].handler == eventHandler && customEvents[eventName][i].obj == eventObject)
915		{
916			delete customEvents[eventName][i];
917			return;
918		}
919	}
920}
921
922BX.onCustomEvent = function(eventObject, eventName, arEventParams)
923{
924	/* shift parameters for short version */
925	if (BX.type.isString(eventObject))
926	{
927		arEventParams = eventName;
928		eventName = eventObject;
929		eventObject = window;
930	}
931
932	eventName = eventName.toUpperCase();
933
934	if (!customEvents[eventName])
935		return;
936
937	if (!arEventParams)
938		arEventParams = [];
939
940	var h;
941	for (var i = 0, l = customEvents[eventName].length; i < l; i++)
942	{
943		h = customEvents[eventName][i];
944		if (!h || !h.handler)
945			continue;
946
947		if (h.obj == window || /*eventObject == window || */h.obj == eventObject) //- only global event handlers will be called
948		{
949			h.handler.apply(eventObject, arEventParams);
950		}
951	}
952}
953
954/* ready */
955BX.isReady = false;
956BX.ready = function(handler)
957{
958	bindReady();
959
960	if (BX.isReady)
961		handler.call(document);
962	else if (readyList)
963		readyList.push(handler);
964}
965
966BX.submit = function(obForm, action_name, action_value)
967{
968	action_name = action_name || 'save';
969	if (!obForm['BXFormSubmit_' + action_name])
970	{
971		obForm['BXFormSubmit_' + action_name] = obForm.appendChild(BX.create('INPUT', {
972			'props': {
973				'type': 'submit',
974				'name': action_name,
975				'value': action_value || 'Y'
976			},
977			'style': {
978				'display': 'none'
979			}
980		}));
981	}
982
983	BX.fireEvent(obForm['BXFormSubmit_' + action_name], 'click');
984}
985
986/* browser detection */
987BX.browser = {
988	IsIE: function()
989	{
990		return bIE;
991	},
992
993	IsIE6: function()
994	{
995		return (/MSIE 6/i.test(navigator.userAgent));
996	},
997
998	IsIE9: function()
999	{
1000		return !!document.documentMode && document.documentMode >= 9;
1001	},
1002
1003	IsOpera: function()
1004	{
1005		return bOpera;
1006	},
1007
1008	IsSafari: function()
1009	{
1010		return bSafari;
1011	},
1012
1013	IsDoctype: function(pDoc)
1014	{
1015		pDoc = pDoc || document;
1016
1017		if (pDoc.compatMode)
1018			return (pDoc.compatMode == "CSS1Compat");
1019
1020		if (pDoc.documentElement && pDoc.documentElement.clientHeight)
1021			return true;
1022
1023		return false;
1024	},
1025
1026	SupportLocalStorage: function() {
1027        try {
1028            return !!localStorage.getItem;
1029        } catch(e) {
1030            return false;
1031        }
1032    }
1033}
1034
1035/* low-level fx funcitons*/
1036BX.show = function(ob)
1037{
1038	if (ob.BXDISPLAY || !_checkDisplay(ob))
1039	{
1040		ob.style.display = ob.BXDISPLAY;
1041	}
1042}
1043
1044BX.hide = function(ob)
1045{
1046	if (!ob.BXDISPLAY)
1047		_checkDisplay(ob);
1048
1049	ob.style.display = 'none';
1050}
1051
1052BX.toggle = function(ob, values)
1053{
1054	if (!values && BX.type.isElementNode(ob))
1055	{
1056		var bShow = true;
1057		if (ob.BXDISPLAY)
1058			bShow = !_checkDisplay(ob);
1059		else
1060			bShow = ob.style.display == 'none';
1061
1062		if (bShow)
1063			BX.show(ob);
1064		else
1065			BX.hide(ob);
1066	}
1067	else if (BX.type.isArray(values))
1068	{
1069		for (var i=0,len=values.length; i<len; i++)
1070		{
1071			if (ob == values[i])
1072			{
1073				ob = values[i==len-1 ? 0 : i+1]
1074				break;
1075			}
1076		}
1077		if (i==len)
1078			ob = values[0];
1079	}
1080
1081	return ob;
1082}
1083
1084/* some useful util functions */
1085
1086BX.util = {
1087	array_values: function(ar)
1088	{
1089		if (!BX.type.isArray(ar))
1090			return BX.util._array_values_ob(ar);
1091		var arv = [];
1092		for(var i=0,l=ar.length;i<l;i++)
1093			if (ar[i] !== null && typeof ar[i] != 'undefined')
1094				arv.push(ar[i]);
1095		return arv;
1096	},
1097
1098	_array_values_ob: function(ar)
1099	{
1100		var arv = [];
1101		for(var i in ar)
1102			if (ar[i] !== null && typeof ar[i] != 'undefined')
1103				arv.push(ar[i]);
1104		return arv;
1105	},
1106
1107	array_keys: function(ar)
1108	{
1109		if (!BX.type.isArray(ar))
1110			return BX.util._array_keys_ob(ar);
1111		var arv = [];
1112		for(var i=0,l=ar.length;i<l;i++)
1113			if (ar[i] !== null && typeof ar[i] != 'undefined')
1114				arv.push(i);
1115		return arv;
1116	},
1117
1118	_array_keys_ob: function(ar)
1119	{
1120		var arv = [];
1121		for(var i in ar)
1122			if (ar[i] !== null && typeof ar[i] != 'undefined')
1123				arv.push(i);
1124		return arv;
1125	},
1126
1127	array_merge: function(first, second)
1128	{
1129		if (!BX.type.isArray(first)) first = [];
1130		if (!BX.type.isArray(second)) second = [];
1131
1132		var i = first.length, j = 0;
1133
1134		if (typeof second.length === "number")
1135		{
1136			for (var l = second.length; j < l; j++)
1137			{
1138				first[i++] = second[j];
1139			}
1140		}
1141		else
1142		{
1143			while (second[j] !== undefined)
1144			{
1145				first[i++] = second[j++];
1146			}
1147		}
1148
1149		first.length = i;
1150
1151		return first;
1152	},
1153
1154	array_unique: function(ar)
1155	{
1156		var i=0,j,len=ar.length;
1157		if(len<2) return ar;
1158
1159		for (; i<len-1;i++)
1160		{
1161			for (j=i+1; j<len;j++)
1162			{
1163				if (ar[i]==ar[j])
1164				{
1165					ar.splice(j--,1); len--;
1166				}
1167			}
1168		}
1169
1170		return ar;
1171	},
1172
1173	in_array: function(needle, haystack)
1174	{
1175		for(var i=0; i<haystack.length; i++)
1176		{
1177			if(haystack[i] == needle)
1178				return true;
1179		}
1180		return false;
1181	},
1182
1183	array_search: function(needle, haystack)
1184	{
1185		for(var i=0; i<haystack.length; i++)
1186		{
1187			if(haystack[i] == needle)
1188				return i;
1189		}
1190		return -1;
1191	},
1192
1193	trim: function(s)
1194	{
1195		if (BX.type.isString(s))
1196			return s.replace(r.ltrim, '').replace(r.rtrim, '');
1197		else
1198			return s;
1199	},
1200
1201	urlencode: function(s){return encodeURIComponent(s);},
1202
1203	// it may also be useful. via sVD.
1204	deleteFromArray: function(ar, ind) {return ar.slice(0, ind).concat(ar.slice(ind + 1));},
1205	insertIntoArray: function(ar, ind, el) {return ar.slice(0, ind).concat([el]).concat(ar.slice(ind));},
1206
1207	htmlspecialchars: function(str)
1208	{
1209		if(!str.replace) return str;
1210
1211		return str.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
1212	},
1213
1214	htmlspecialcharsback: function(str)
1215	{
1216		if(!str.replace) return str;
1217
1218		return str.replace(/\&quot;/g, '"').replace(/&#39;/g, "'").replace(/\&lt;/g, '<').replace(/\&gt;/g, '>').replace(/\&amp;/g, '&');
1219	},
1220
1221	// Quote regular expression characters plus an optional character
1222	preg_quote: function(str, delimiter)
1223	{
1224		if(!str.replace)
1225			return str;
1226		return str.replace(new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\' + (delimiter || '') + '-]', 'g'), '\\$&');
1227	},
1228
1229	jsencode: function(str)
1230	{
1231		if (!str || !str.replace)
1232			return str;
1233
1234		var escapes =
1235		[
1236			{ c: "\\\\", r: "\\\\" }, // should be first
1237			{ c: "\\t", r: "\\t" },
1238			{ c: "\\n", r: "\\n" },
1239			{ c: "\\r", r: "\\r" },
1240			{ c: "\"", r: "\\\"" },
1241			{ c: "'", r: "\\'" },
1242			{ c: "<", r: "\\x3C" },
1243			{ c: ">", r: "\\x3E" },
1244			{ c: "\\u2028", r: "\\u2028" },
1245			{ c: "\\u2029", r: "\\u2029" }
1246		];
1247		for (var i = 0; i < escapes.length; i++)
1248			str = str.replace(new RegExp(escapes[i].c, 'g'), escapes[i].r);
1249		return str;
1250	},
1251
1252	str_pad: function(input, pad_length, pad_string, pad_type)
1253	{
1254		pad_string = pad_string || ' ';
1255		pad_type = pad_type || 'right';
1256		input = input.toString();
1257
1258		if (pad_type == 'left')
1259			return BX.util.str_pad_left(input, pad_length, pad_string);
1260		else
1261			return BX.util.str_pad_right(input, pad_length, pad_string);
1262
1263	},
1264
1265	str_pad_left: function(input, pad_length, pad_string)
1266	{
1267		var i = input.length, q=pad_string.length;
1268		if (i >= pad_length) return input;
1269
1270		for(;i<pad_length;i+=q)
1271			input = pad_string + input;
1272
1273		return input;
1274	},
1275
1276	str_pad_right: function(input, pad_length, pad_string)
1277	{
1278		var i = input.length, q=pad_string.length;
1279		if (i >= pad_length) return input;
1280
1281		for(;i<pad_length;i+=q)
1282			input += pad_string;
1283
1284		return input;
1285	},
1286
1287	popup: function(url, width, height)
1288	{
1289		var w, h;
1290		if(BX.browser.IsOpera())
1291		{
1292			w = document.body.offsetWidth;
1293			h = document.body.offsetHeight;
1294		}
1295		else
1296		{
1297			w = screen.width;
1298			h = screen.height;
1299		}
1300		window.open(url, '', 'status=no,scrollbars=yes,resizable=yes,width='+width+',height='+height+',top='+Math.floor((h - height)/2-14)+',left='+Math.floor((w - width)/2-5));
1301	}
1302}
1303
1304BX.type = {
1305	isString: function(item) {
1306		return item === '' ? true : (item ? (typeof (item) == "string" || item instanceof String) : false);
1307	},
1308	isNotEmptyString: function(item) {
1309		return BX.type.isString(item) ? item.length > 0 : false;
1310	},
1311	isBoolean: function(item) {
1312		return item === false || item === false;
1313	},
1314	isNumber: function(item) {
1315		return item === 0 ? true : (item ? (typeof (item) == "number" || item instanceof Number) : false);
1316	},
1317	isFunction: function(item) {
1318		return item === null ? false : (typeof (item) == "function" || item instanceof Function);
1319	},
1320	isElementNode: function(item) {
1321		return item && typeof (item) == "object" && "nodeType" in item && item.nodeType == 1; //document.body.ELEMENT_NODE;
1322	},
1323	isDomNode: function(item) {
1324		return item && typeof (item) == "object" && "nodeType" in item;
1325	},
1326	isArray: function(item) {
1327		return item && Object.prototype.toString.call(item) == "[object Array]";
1328	},
1329	isDate : function(item) {
1330		return item && Object.prototype.toString.call(item) == "[object Date]";
1331	}
1332}
1333
1334BX.isNodeInDom = function(node)
1335{
1336	return node === document ? true :
1337		(node.parentNode ? BX.isNodeInDom(node.parentNode) : false);
1338}
1339
1340BX.evalPack = function(code)
1341{
1342	while (code.length > 0)
1343	{
1344		var c = code.shift();
1345
1346		if (c.TYPE == 'SCRIPT_EXT' || c.TYPE == 'SCRIPT_SRC')
1347		{
1348			BX.loadScript(c.DATA, function() {BX.evalPack(code)});
1349		}
1350		else if (c.TYPE == 'SCRIPT')
1351			BX.evalGlobal(c.DATA);
1352	}
1353}
1354
1355BX.evalGlobal = function(data)
1356{
1357	if (data)
1358	{
1359		var head = document.getElementsByTagName("head")[0] || document.documentElement,
1360			script = document.createElement("script");
1361
1362		script.type = "text/javascript";
1363
1364		if (!BX.browser.IsIE())
1365		{
1366			script.appendChild(document.createTextNode(data));
1367		}
1368		else
1369		{
1370			script.text = data;
1371		}
1372
1373		head.insertBefore(script, head.firstChild);
1374		head.removeChild(script);
1375	}
1376}
1377
1378BX.processHTML = function(HTML, scriptsRunFirst)
1379{
1380	var matchScript, scripts = [], data = HTML;
1381
1382	while ((matchScript = data.match(r.script)) !== null)
1383	{
1384		var end = data.search('<\/script>', 'i');
1385		if (end == -1)
1386			break;
1387
1388		var bRunFirst = scriptsRunFirst || (matchScript[1].indexOf('bxrunfirst') != '-1');
1389
1390		var matchSrc;
1391		if ((matchSrc = matchScript[1].match(r.script_src)) !== null)
1392			scripts.push({"bRunFirst": bRunFirst, "isInternal": false, "JS": matchSrc[1]});
1393		else
1394		{
1395			var start = matchScript.index + matchScript[0].length;
1396			var js = data.substr(start, end-start);
1397
1398			scripts.push({"bRunFirst": bRunFirst, "isInternal": true, "JS": js});
1399		}
1400
1401		data = data.substr(0, matchScript.index) + data.substr(end+9);
1402	}
1403
1404	return {'HTML': data, 'SCRIPT': scripts};
1405}
1406
1407BX.garbage = function(call, thisObject)
1408{
1409	garbageCollectors.push({callback: call, context: thisObject});
1410}
1411
1412/* window pos functions */
1413
1414BX.GetDocElement = function (pDoc)
1415{
1416	pDoc = pDoc || document;
1417	return (BX.browser.IsDoctype(pDoc) ? pDoc.documentElement : pDoc.body);
1418}
1419
1420BX.GetContext = function(node)
1421{
1422	if (BX.type.isElementNode(node))
1423		return node.ownerDocument.parentWindow || node.ownerDocument.defaultView || window;
1424	else if (BX.type.isDomNode(node))
1425		return node.parentWindow || node.defaultView || window;
1426	else
1427		return window;
1428}
1429
1430BX.GetWindowInnerSize = function(pDoc)
1431{
1432	var width, height;
1433
1434	pDoc = pDoc || document;
1435
1436	if (self.innerHeight) // all except Explorer
1437	{
1438		width = BX.GetContext(pDoc).innerWidth;
1439		height = BX.GetContext(pDoc).innerHeight;
1440	}
1441	else if (pDoc.documentElement && (pDoc.documentElement.clientHeight || pDoc.documentElement.clientWidth)) // Explorer 6 Strict Mode
1442	{
1443		width = pDoc.documentElement.clientWidth;
1444		height = pDoc.documentElement.clientHeight;
1445	}
1446	else if (pDoc.body) // other Explorers
1447	{
1448		width = pDoc.body.clientWidth;
1449		height = pDoc.body.clientHeight;
1450	}
1451	return {innerWidth : width, innerHeight : height};
1452}
1453
1454BX.GetWindowScrollPos = function(pDoc)
1455{
1456	var left, top;
1457
1458	pDoc = pDoc || document;
1459
1460	if (self.pageYOffset) // all except Explorer
1461	{
1462		left = BX.GetContext(pDoc).pageXOffset;
1463		top = BX.GetContext(pDoc).pageYOffset;
1464	}
1465	else if (pDoc.documentElement && (pDoc.documentElement.scrollTop || pDoc.documentElement.scrollLeft)) // Explorer 6 Strict
1466	{
1467		left = pDoc.documentElement.scrollLeft;
1468		top = pDoc.documentElement.scrollTop;
1469	}
1470	else if (pDoc.body) // all other Explorers
1471	{
1472		left = pDoc.body.scrollLeft;
1473		top = pDoc.body.scrollTop;
1474	}
1475	return {scrollLeft : left, scrollTop : top};
1476}
1477
1478BX.GetWindowScrollSize = function(pDoc)
1479{
1480	var width, height;
1481	if (!pDoc)
1482		pDoc = document;
1483
1484	if ( (pDoc.compatMode && pDoc.compatMode == "CSS1Compat"))
1485	{
1486		width = pDoc.documentElement.scrollWidth;
1487		height = pDoc.documentElement.scrollHeight;
1488	}
1489	else
1490	{
1491		if (pDoc.body.scrollHeight > pDoc.body.offsetHeight)
1492			height = pDoc.body.scrollHeight;
1493		else
1494			height = pDoc.body.offsetHeight;
1495
1496		if (pDoc.body.scrollWidth > pDoc.body.offsetWidth ||
1497			(pDoc.compatMode && pDoc.compatMode == "BackCompat") ||
1498			(pDoc.documentElement && !pDoc.documentElement.clientWidth)
1499		)
1500			width = pDoc.body.scrollWidth;
1501		else
1502			width = pDoc.body.offsetWidth;
1503	}
1504	return {scrollWidth : width, scrollHeight : height};
1505}
1506
1507BX.GetWindowSize = function(pDoc)
1508{
1509	var innerSize = this.GetWindowInnerSize(pDoc);
1510	var scrollPos = this.GetWindowScrollPos(pDoc);
1511	var scrollSize = this.GetWindowScrollSize(pDoc);
1512
1513	return  {
1514		innerWidth : innerSize.innerWidth, innerHeight : innerSize.innerHeight,
1515		scrollLeft : scrollPos.scrollLeft, scrollTop : scrollPos.scrollTop,
1516		scrollWidth : scrollSize.scrollWidth, scrollHeight : scrollSize.scrollHeight
1517	};
1518}
1519
1520BX.hide_object = function(ob)
1521{
1522	ob = BX(ob);
1523	ob.style.position = 'absolute';
1524	ob.style.top = '-1000px';
1525	ob.style.left = '-1000px';
1526	ob.style.height = '10px';
1527	ob.style.width = '10px';
1528};
1529
1530BX.is_relative = function(el)
1531{
1532	var p = BX.style(el, 'position');
1533	return p == 'relative' || p == 'absolute';
1534}
1535
1536BX.is_float = function(el)
1537{
1538	var p = BX.style(el, 'float');
1539	return p == 'right' || p == 'left';
1540}
1541
1542BX.pos = function(el, bRelative)
1543{
1544	var r = { top: 0, right: 0, bottom: 0, left: 0, width: 0, height: 0 };
1545	bRelative = !!bRelative;
1546	if (!el)
1547		return r;
1548	if (typeof (el.getBoundingClientRect) != "undefined" && el.ownerDocument == document && !bRelative)
1549	{
1550		var clientRect = el.getBoundingClientRect();
1551		var root = document.documentElement;
1552		var body = document.body;
1553
1554		r.top = clientRect.top + root.scrollTop + body.scrollTop;
1555		r.left = clientRect.left + root.scrollLeft + body.scrollLeft;
1556		r.width = clientRect.right - clientRect.left;
1557		r.height = clientRect.bottom - clientRect.top;
1558		r.right = clientRect.right + root.scrollLeft + body.scrollLeft;
1559		r.bottom = clientRect.bottom + root.scrollTop + body.scrollTop;
1560	}
1561	else
1562	{
1563		var x = 0, y = 0, w = el.offsetWidth, h = el.offsetHeight;
1564		var first = true;
1565		for (; el != null; el = el.offsetParent)
1566		{
1567			if (!first && bRelative && BX.is_relative(el))
1568				break;
1569
1570			x += el.offsetLeft;
1571			y += el.offsetTop;
1572			if (first)
1573			{
1574				first = false;
1575				continue;
1576			}
1577
1578			var elBorderLeftWidth = parseInt(BX.style(el, 'border-left-width')),
1579				elBorderTopWidth = parseInt(BX.style(el, 'border-top-width'));
1580
1581			if (!isNaN(elBorderLeftWidth) && elBorderLeftWidth > 0)
1582				x += elBorderLeftWidth;
1583			if (!isNaN(elBorderTopWidth) && elBorderTopWidth > 0)
1584				y += elBorderTopWidth;
1585		}
1586
1587		r.top = y;
1588		r.left = x;
1589		r.width = w;
1590		r.height = h;
1591		r.right = r.left + w;
1592		r.bottom = r.top + h;
1593	}
1594
1595	for (var i in r) r[i] = parseInt(r[i]);
1596
1597	return r;
1598}
1599
1600BX.align = function(pos, w, h)
1601{
1602	var pDoc = document;
1603	if (BX.type.isElementNode(pos))
1604	{
1605		pDoc = pos.ownerDocument;
1606		pos = BX.pos(pos);
1607	}
1608
1609	var x = pos["left"], y = pos["bottom"];
1610
1611	var scroll = BX.GetWindowScrollPos(pDoc);
1612	var size = BX.GetWindowInnerSize(pDoc);
1613
1614	if((size.innerWidth + scroll.scrollLeft) - (pos["left"] + w) < 0)
1615	{
1616		if(pos["right"] - w >= 0 )
1617			x = pos["right"] - w;
1618		else
1619			x = scroll.scrollLeft;
1620	}
1621
1622	if((size.innerHeight + scroll.scrollTop) - (pos["bottom"] + h) < 0)
1623	{
1624		if(pos["top"] - h >= 0)
1625			y = pos["top"] - h;
1626		else
1627			y = scroll.scrollTop;
1628	}
1629
1630	return {'left':x, 'top':y};
1631}
1632
1633BX.scrollToNode = function(node)
1634{
1635	var obNode = BX(node);
1636
1637	if (obNode.scrollIntoView)
1638		obNode.scrollIntoView(true);
1639	else
1640	{
1641		var arNodePos = BX.pos(obNode);
1642		window.scrollTo(arNodePos.left, arNodePos.top);
1643	}
1644}
1645
1646/* non-xhr loadings */
1647BX.showWait = function(node, msg)
1648{
1649	node = BX(node) || document.body || document.documentElement;
1650	msg = msg || BX.message('JS_CORE_LOADING');
1651
1652	var container_id = node.id || Math.random();
1653
1654	var obMsg = node.bxmsg = document.body.appendChild(BX.create('DIV', {
1655		props: {
1656			id: 'wait_' + container_id,
1657			className: 'bx-core-waitwindow'
1658		},
1659		text: msg
1660	}));
1661
1662	setTimeout(BX.delegate(_adjustWait, node), 10);
1663
1664	lastWait[lastWait.length] = obMsg;
1665	return obMsg;
1666}
1667
1668BX.closeWait = function(node, obMsg)
1669{
1670	obMsg = obMsg || node && (node.bxmsg || BX('wait_' + node.id)) || lastWait.pop();
1671	if (obMsg && obMsg.parentNode)
1672	{
1673		for (var i=0,len=lastWait.length;i<len;i++)
1674		{
1675			if (obMsg == lastWait[i])
1676			{
1677				lastWait = BX.util.deleteFromArray(lastWait, i);
1678				break;
1679			}
1680		}
1681
1682		obMsg.parentNode.removeChild(obMsg);
1683		if (node) node.bxmsg = null;
1684		BX.cleanNode(obMsg, true);
1685	}
1686}
1687
1688BX.loadScript = function(script, callback, doc)
1689{
1690	if (!BX.isReady)
1691	{
1692		var _args = arguments;
1693		BX.ready(function() {
1694			BX.loadScript.apply(this, _args);
1695		});
1696		return;
1697	}
1698
1699	doc = doc || document;
1700
1701	if (BX.type.isString(script))
1702		script = [script];
1703	var _callback = function()
1704	{
1705		return (callback && BX.type.isFunction(callback)) ? callback() : null
1706	}
1707	var load_js = function(ind)
1708	{
1709		if (ind >= script.length)
1710			return _callback();
1711
1712		var oHead = doc.getElementsByTagName("head")[0] || doc.documentElement;
1713		var oScript = doc.createElement('script');
1714		oScript.src = script[ind];
1715
1716		var bLoaded = false;
1717		oScript.onload = oScript.onreadystatechange = function()
1718		{
1719			if (!bLoaded && (!oScript.readyState || oScript.readyState == "loaded" || oScript.readyState == "complete"))
1720			{
1721				bLoaded = true;
1722				setTimeout(function (){load_js(++ind);}, 50);
1723
1724				oScript.onload = oScript.onreadystatechange = null;
1725				if (oHead && oScript.parentNode)
1726				{
1727					oHead.removeChild(oScript);
1728				}
1729			}
1730		}
1731
1732		return oHead.insertBefore(oScript, oHead.firstChild);
1733	}
1734
1735	load_js(0);
1736}
1737
1738BX.loadCSS = function(arCSS, doc, win)
1739{
1740	if (!BX.isReady)
1741	{
1742		var _args = arguments;
1743		BX.ready(function() {
1744			BX.loadCSS.apply(this, _args);
1745		});
1746		return null;
1747	}
1748
1749	if (BX.type.isString(arCSS))
1750	{
1751		var bSingle = true;
1752		arCSS = [arCSS];
1753	}
1754	var i, l = arCSS.length, pLnk = [];
1755
1756	if (l == 0)
1757		return null;
1758
1759	doc = doc || document;
1760	win = win ||window;
1761
1762	if (!win.bxhead)
1763	{
1764		var heads = doc.getElementsByTagName('HEAD');
1765		win.bxhead = heads[0];
1766	}
1767
1768	if (!win.bxhead)
1769		return null;
1770
1771	for (i = 0; i < l; i++)
1772	{
1773		var lnk = document.createElement('LINK');
1774		lnk.href = arCSS[i];
1775		lnk.rel = 'stylesheet';
1776		lnk.type = 'text/css';
1777		win.bxhead.appendChild(lnk);
1778		pLnk.push(lnk);
1779	}
1780
1781	if (bSingle)
1782		return lnk;
1783
1784	return pLnk;
1785}
1786
1787BX.reload = function(back_url, bAddClearCache)
1788{
1789	if (back_url === true)
1790	{
1791		bAddClearCache = true;
1792		back_url = null;
1793	}
1794
1795	var new_href = back_url || top.location.href;
1796
1797	var hashpos = new_href.indexOf('#'), hash = '';
1798
1799	if (hashpos != -1)
1800	{
1801		hash = new_href.substr(hashpos);
1802		new_href = new_href.substr(0, hashpos);
1803	}
1804
1805	if (bAddClearCache && new_href.indexOf('clear_cache=Y') < 0)
1806		new_href += (new_href.indexOf('?') == -1 ? '?' : '&') + 'clear_cache=Y';
1807
1808	if (hash)
1809	{
1810		// hack for clearing cache in ajax mode components with history emulation
1811		if (bAddClearCache && (hash.substr(0, 5) == 'view/' || hash.substr(0, 6) == '#view/') && hash.indexOf('clear_cache%3DY') < 0)
1812			hash += (hash.indexOf('%3F') == -1 ? '%3F' : '%26') + 'clear_cache%3DY'
1813
1814		new_href = new_href.replace(/(\?|\&)_r=[\d]*/, '');
1815		new_href += (new_href.indexOf('?') == -1 ? '?' : '&') + '_r='+Math.round(Math.random()*10000) + hash;
1816	}
1817
1818	top.location.href = new_href;
1819}
1820
1821BX.clearCache = function()
1822{
1823	BX.showWait();
1824	BX.reload(true);
1825}
1826
1827BX.formatDate = function(date, format)
1828{
1829	date = date || new Date();
1830
1831	var bTime = date.getHours() || date.getMinutes() || date.getSeconds(),
1832		str = !!format
1833			? format :
1834			(bTime ? BX.message('FORMAT_DATETIME') : BX.message('FORMAT_DATE')
1835		);
1836
1837	return str.replace(/YYYY/ig, date.getFullYear())
1838		.replace(/MM/ig, BX.util.str_pad_left((date.getMonth()+1).toString(), 2, '0'))
1839		.replace(/DD/ig, BX.util.str_pad_left(date.getDate().toString(), 2, '0'))
1840		.replace(/HH/ig, BX.util.str_pad_left(date.getHours().toString(), 2, '0'))
1841		.replace(/MI/ig, BX.util.str_pad_left(date.getMinutes().toString(), 2, '0'))
1842		.replace(/SS/ig, BX.util.str_pad_left(date.getSeconds().toString(), 2, '0'));
1843}
1844
1845BX.parseDate = function(str)
1846{
1847	var aDate = str.split(/\D/ig);
1848	var aFormat = BX.message('FORMAT_DATE').split(/\W/ig);
1849	if(aDate.length > aFormat.length)
1850		aFormat = BX.message('FORMAT_DATETIME').split(/\W/ig);
1851
1852	var i, cnt;
1853	var aDateArgs=[], aFormatArgs=[];
1854	for(i = 0, cnt = aDate.length; i < cnt; i++)
1855		if(BX.util.trim(aDate[i]) != '')
1856			aDateArgs[aDateArgs.length] = aDate[i];
1857	for(i = 0, cnt = aFormat.length; i < cnt; i++)
1858		if(BX.util.trim(aFormat[i]) != '')
1859			aFormatArgs[aFormatArgs.length] = aFormat[i];
1860
1861	var aResult={};
1862	for(i = 0, cnt = aFormatArgs.length; i < cnt; i++)
1863		aResult[aFormatArgs[i].toUpperCase()] = parseInt(aDateArgs[i], 10);
1864
1865	if(aResult['DD'] > 0 && aResult['MM'] > 0 && aResult['YYYY'] > 0)
1866	{
1867		var d = new Date();
1868		d.setDate(1);
1869		d.setFullYear(aResult['YYYY']);
1870		d.setMonth(aResult['MM']-1);
1871		d.setDate(aResult['DD']);
1872		d.setHours(0, 0, 0);
1873		if(!isNaN(aResult['HH']) && !isNaN(aResult['MI']) && !isNaN(aResult['SS']))
1874		{
1875			d.setHours(aResult['HH'], aResult['MI'], aResult['SS']);
1876		}
1877		return d;
1878	}
1879	return null;
1880}
1881
1882/******* HINT ***************/
1883// if function has 2 params - the 2nd one is hint html. otherwise hint_html is third and hint_title - 2nd;
1884// '<div onmouseover="BX.hint(this, 'This is &lt;b&gt;Hint&lt;/b&gt;')"'>;
1885// BX.hint(el, 'This is <b>Hint</b>') - this won't work, use constructor
1886BX.hint = function(el, hint_title, hint_html, hint_id)
1887{
1888	if (null == hint_html)
1889	{
1890		hint_html = hint_title;
1891		hint_title = '';
1892	}
1893
1894	if (null == el.BXHINT)
1895	{
1896		el.BXHINT = new BX.CHint({
1897			parent: el, hint: hint_html, title: hint_title, id: hint_id
1898		});
1899		el.BXHINT.Show();
1900	}
1901}
1902
1903BX.hint_replace = function(el, hint_title, hint_html)
1904{
1905	if (null == hint_html)
1906	{
1907		hint_html = hint_title;
1908		hint_title = '';
1909	}
1910
1911	if (!el || !el.parentNode || !hint_html)
1912			return null;
1913
1914	var obHint = new BX.CHint({
1915		hint: hint_html,
1916		title: hint_title
1917	});
1918
1919	obHint.CreateParent();
1920
1921	el.parentNode.insertBefore(obHint.PARENT, el);
1922	el.parentNode.removeChild(el);
1923
1924	obHint.PARENT.style.marginLeft = '5px';
1925
1926	return el;
1927}
1928
1929BX.CHint = function(params)
1930{
1931	this.PARENT = BX(params.parent);
1932
1933	this.HINT = params.hint;
1934	this.HINT_TITLE = params.title;
1935
1936	this.PARAMS = {}
1937	for (var i in this.defaultSettings)
1938	{
1939		if (null == params[i])
1940			this.PARAMS[i] = this.defaultSettings[i];
1941		else
1942			this.PARAMS[i] = params[i];
1943	}
1944
1945	if (null != params.id)
1946		this.ID = params.id;
1947
1948	this.timer = null;
1949	this.bInited = false;
1950	this.msover = true;
1951
1952	if (this.PARAMS.showOnce)
1953	{
1954		this.__show();
1955		this.msover = false;
1956		this.timer = setTimeout(BX.proxy(this.__hide, this), this.PARAMS.hide_timeout);
1957	}
1958	else if (this.PARENT)
1959	{
1960		BX.bind(this.PARENT, 'mouseover', BX.proxy(this.Show, this));
1961		BX.bind(this.PARENT, 'mouseout', BX.proxy(this.Hide, this));
1962	}
1963
1964	BX.addCustomEvent('onMenuOpen', BX.delegate(this.disable, this));
1965	BX.addCustomEvent('onMenuClose', BX.delegate(this.enable, this));
1966}
1967
1968BX.CHint.prototype.defaultSettings = {
1969	show_timeout: 1000,
1970	hide_timeout: 500,
1971	dx: 2,
1972	showOnce: false,
1973	preventHide: true,
1974	min_width: 250
1975}
1976
1977BX.CHint.prototype.CreateParent = function(element, params)
1978{
1979	if (this.PARENT)
1980	{
1981		BX.unbind(this.PARENT, 'mouseover', BX.proxy(this.Show, this));
1982		BX.unbind(this.PARENT, 'mouseout', BX.proxy(this.Hide, this));
1983	}
1984
1985	if (!params) params = {}
1986	var type = 'icon';
1987
1988	if (params.type && (params.type == "link" || params.type == "icon"))
1989		type = params.type;
1990
1991	if (element)
1992		type = "element";
1993
1994	if (type == "icon")
1995	{
1996		element = BX.create('IMG', {
1997			props: {
1998				src: params.iconSrc
1999					? params.iconSrc
2000					: "/bitrix/js/main/core/images/hint.gif"
2001			}
2002		});
2003	}
2004	else if (type == "link")
2005	{
2006		element = BX.create("A", {
2007			props: {href: 'javascript:void(0)'},
2008			html: '[?]'
2009		});
2010	}
2011
2012	this.PARENT = element;
2013
2014	BX.bind(this.PARENT, 'mouseover', BX.proxy(this.Show, this));
2015	BX.bind(this.PARENT, 'mouseout', BX.proxy(this.Hide, this));
2016
2017	return this.PARENT;
2018}
2019
2020BX.CHint.prototype.Show = function()
2021{
2022	this.msover = true;
2023
2024	if (null != this.timer)
2025		clearTimeout(this.timer);
2026
2027	this.timer = setTimeout(BX.proxy(this.__show, this), this.PARAMS.show_timeout);
2028}
2029
2030BX.CHint.prototype.Hide = function()
2031{
2032	this.msover = false;
2033
2034	if (null != this.timer)
2035		clearTimeout(this.timer);
2036
2037	this.timer = setTimeout(BX.proxy(this.__hide, this), this.PARAMS.hide_timeout);
2038}
2039
2040BX.CHint.prototype.__show = function()
2041{
2042	if (!this.msover || this.disabled) return;
2043	if (!this.bInited) this.Init();
2044
2045	this.prepareAdjustPos();
2046	this.DIV.style.display = 'block';
2047	this.adjustPos();
2048
2049	BX.bind(window, 'scroll', BX.proxy(this.__onscroll, this));
2050
2051	if (this.PARAMS.showOnce)
2052	{
2053		this.timer = setTimeout(BX.proxy(this.__hide, this), this.PARAMS.hide_timeout);
2054	}
2055}
2056
2057BX.CHint.prototype.__onscroll = function()
2058{
2059	if (!BX.admin || !BX.admin.panel || !BX.admin.panel.isFixed()) return;
2060
2061	if (this.scrollTimer) clearTimeout(this.scrollTimer);
2062
2063	this.DIV.style.display = 'none';
2064	this.scrollTimer = setTimeout(BX.proxy(this.Reopen, this), this.PARAMS.show_timeout);
2065}
2066
2067BX.CHint.prototype.Reopen = function()
2068{
2069	if (null != this.timer) clearTimeout(this.timer);
2070	this.timer = setTimeout(BX.proxy(this.__show, this), 50);
2071}
2072
2073BX.CHint.prototype.__hide = function()
2074{
2075	if (this.msover) return;
2076	if (!this.bInited) return;
2077
2078	BX.unbind(window, 'scroll', BX.proxy(this.Reopen, this));
2079
2080	if (this.PARAMS.showOnce)
2081	{
2082		this.Destroy();
2083	}
2084	else
2085	{
2086		this.DIV.style.display = 'none';
2087	}
2088}
2089
2090BX.CHint.prototype.__hide_immediately = function()
2091{
2092	this.msover = false;
2093	this.__hide();
2094}
2095
2096BX.CHint.prototype.Init = function()
2097{
2098	this.DIV = document.body.appendChild(BX.create('DIV', {
2099		props: {className: 'bx-panel-tooltip'},
2100		style: {display: 'none'},
2101		children: [
2102			BX.create('DIV', {
2103				props: {className: 'bx-panel-tooltip-top-border'},
2104				html: '<div class="bx-panel-tooltip-corner bx-panel-tooltip-left-corner"></div><div class="bx-panel-tooltip-border"></div><div class="bx-panel-tooltip-corner bx-panel-tooltip-right-corner"></div>'
2105			}),
2106			(this.CONTENT = BX.create('DIV', {
2107				props: {className: 'bx-panel-tooltip-content'},
2108				children: [
2109					BX.create('DIV', {
2110						props: {className: 'bx-panel-tooltip-underlay'},
2111						children: [
2112							BX.create('DIV', {props: {className: 'bx-panel-tooltip-underlay-bg'}})
2113						]
2114					})
2115				]
2116			})),
2117
2118			BX.create('DIV', {
2119				props: {className: 'bx-panel-tooltip-bottom-border'},
2120				html: '<div class="bx-panel-tooltip-corner bx-panel-tooltip-left-corner"></div><div class="bx-panel-tooltip-border"></div><div class="bx-panel-tooltip-corner bx-panel-tooltip-right-corner"></div>'
2121			})
2122		]
2123	}));
2124
2125	if (this.ID)
2126	{
2127		this.CONTENT.insertBefore(BX.create('A', {
2128			attrs: {href: 'javascript:void(0)'},
2129			props: {className: 'bx-panel-tooltip-close'},
2130			events: {click: BX.delegate(this.Close, this)}
2131		}), this.CONTENT.firstChild)
2132	}
2133
2134	if (this.HINT_TITLE)
2135	{
2136		this.CONTENT.appendChild(
2137			BX.create('DIV', {
2138				props: {className: 'bx-panel-tooltip-title'},
2139				text: this.HINT_TITLE
2140			})
2141		)
2142	}
2143
2144	if (this.HINT)
2145	{
2146		this.CONTENT_TEXT = this.CONTENT.appendChild(BX.create('DIV', {props: {className: 'bx-panel-tooltip-text'}})).appendChild(BX.create('SPAN', {html: this.HINT}));
2147	}
2148
2149	if (this.PARAMS.preventHide)
2150	{
2151		BX.bind(this.DIV, 'mouseout', BX.proxy(this.Hide, this));
2152		BX.bind(this.DIV, 'mouseover', BX.proxy(this.Show, this));
2153	}
2154
2155	this.bInited = true;
2156}
2157
2158BX.CHint.prototype.setContent = function(content)
2159{
2160	this.HINT = content;
2161
2162	if (this.CONTENT_TEXT)
2163		this.CONTENT_TEXT.innerHTML = this.HINT;
2164	else
2165		this.CONTENT_TEXT = this.CONTENT.appendChild(BX.create('DIV', {props: {className: 'bx-panel-tooltip-text'}})).appendChild(BX.create('SPAN', {html: this.HINT}));
2166}
2167
2168BX.CHint.prototype.prepareAdjustPos = function()
2169{
2170	this._wnd = {scrollPos: BX.GetWindowScrollPos(),scrollSize:BX.GetWindowScrollSize()};
2171}
2172
2173BX.CHint.prototype.getAdjustPos = function()
2174{
2175	var res = {}, pos = BX.pos(this.PARENT);
2176
2177	res.top = pos.bottom + this.PARAMS.dx;
2178
2179	if (BX.admin && BX.admin.panel.DIV)
2180	{
2181		var min_top = BX.admin.panel.DIV.offsetHeight + this.PARAMS.dx;
2182
2183		if (BX.admin.panel.isFixed())
2184		{
2185			min_top += this._wnd.scrollPos.scrollTop;
2186		}
2187	}
2188
2189	if (res.top < min_top)
2190		res.top = min_top;
2191	else
2192	{
2193		if (res.top + this.DIV.offsetHeight > this._wnd.scrollSize.scrollHeight)
2194			res.top = pos.top - this.PARAMS.dx - this.DIV.offsetHeight;
2195	}
2196
2197	res.left = pos.left;
2198	if (pos.left < this.PARAMS.dx) pos.left = this.PARAMS.dx;
2199	else
2200	{
2201		floatWidth = this.DIV.offsetWidth;
2202
2203		var max_left = this._wnd.scrollSize.scrollWidth - floatWidth - this.PARAMS.dx;
2204
2205		if (res.left > max_left)
2206			res.left = max_left;
2207	}
2208
2209	return res;
2210}
2211
2212BX.CHint.prototype.adjustWidth = function()
2213{
2214	if (this.bWidthAdjusted) return;
2215
2216	var w = this.DIV.offsetWidth, h = this.DIV.offsetHeight;
2217
2218	if (w > this.PARAMS.min_width)
2219		w = Math.round(Math.sqrt(1.618*w*h));
2220
2221	if (w < this.PARAMS.min_width)
2222		w = this.PARAMS.min_width;
2223
2224	this.DIV.style.width = w + "px";
2225
2226	if (this._adjustWidthInt)
2227		clearInterval(this._adjustWidthInt);
2228	this._adjustWidthInt = setInterval(BX.delegate(this._adjustWidthInterval, this), 5);
2229
2230	this.bWidthAdjusted = true;
2231}
2232
2233BX.CHint.prototype._adjustWidthInterval = function()
2234{
2235	if (!this.DIV || this.DIV.style.display == 'none')
2236		clearInterval(this._adjustWidthInt);
2237
2238	var
2239		dW = 20,
2240		maxWidth = 1500,
2241		w = this.DIV.offsetWidth,
2242		w1 = this.CONTENT_TEXT.offsetWidth;
2243
2244	if (w > 0 && w1 > 0 && w - w1 < dW && w < maxWidth)
2245	{
2246		this.DIV.style.width = (w + dW) + "px";
2247		return;
2248	}
2249
2250	clearInterval(this._adjustWidthInt);
2251}
2252
2253BX.CHint.prototype.adjustPos = function()
2254{
2255	this.adjustWidth();
2256
2257	var pos = this.getAdjustPos();
2258
2259	this.DIV.style.top = pos.top + 'px';
2260	this.DIV.style.left = pos.left + 'px';
2261}
2262
2263BX.CHint.prototype.Close = function()
2264{
2265	if (this.ID && BX.WindowManager)
2266		BX.WindowManager.saveWindowOptions(this.ID, {display: 'off'});
2267	this.__hide_immediately();
2268	this.Destroy();
2269}
2270
2271BX.CHint.prototype.Destroy = function()
2272{
2273	if (this.PARENT)
2274	{
2275		BX.unbind(this.PARENT, 'mouseover', BX.proxy(this.Show, this));
2276		BX.unbind(this.PARENT, 'mouseout', BX.proxy(this.Hide, this));
2277	}
2278
2279	if (this.DIV)
2280	{
2281		BX.unbind(this.DIV, 'mouseover', BX.proxy(this.Show, this));
2282		BX.unbind(this.DIV, 'mouseout', BX.proxy(this.Hide, this));
2283
2284		BX.cleanNode(this.DIV, true);
2285	}
2286}
2287
2288BX.CHint.prototype.enable = function(){this.disabled = false;}
2289BX.CHint.prototype.disable = function(){this.__hide_immediately(); this.disabled = true;}
2290
2291/* ready */
2292if (document.addEventListener)
2293{
2294	__readyHandler = function()
2295	{
2296		document.removeEventListener("DOMContentLoaded", __readyHandler, false);
2297		runReady();
2298	}
2299}
2300else if (document.attachEvent)
2301{
2302	__readyHandler = function()
2303	{
2304		if (document.readyState === "complete")
2305		{
2306			document.detachEvent("onreadystatechange", __readyHandler);
2307			runReady();
2308		}
2309	}
2310}
2311
2312function bindReady()
2313{
2314	if (!readyBound)
2315	{
2316		readyBound = true;
2317
2318		if (document.readyState === "complete")
2319		{
2320			return runReady();
2321		}
2322
2323		if (document.addEventListener)
2324		{
2325			document.addEventListener("DOMContentLoaded", __readyHandler, false);
2326			window.addEventListener("load", runReady, false);
2327		}
2328		else if (document.attachEvent) // IE
2329		{
2330			document.attachEvent("onreadystatechange", __readyHandler);
2331			window.attachEvent("onload", runReady);
2332
2333			var toplevel = false;
2334			try {toplevel = (window.frameElement == null);} catch(e) {}
2335
2336			if (document.documentElement.doScroll && toplevel)
2337				doScrollCheck();
2338		}
2339	}
2340
2341	return null;
2342}
2343
2344
2345function runReady()
2346{
2347	if (!BX.isReady)
2348	{
2349		if (!document.body)
2350			return setTimeout(runReady, 15);
2351
2352		BX.isReady = true;
2353
2354
2355		if (readyList && readyList.length > 0)
2356		{
2357			var fn, i = 0;
2358			while (readyList && (fn = readyList[i++]))
2359			{
2360				fn.call(document);
2361			}
2362
2363			readyList = null;
2364		}
2365		// TODO: check ready handlers binded some other way;
2366	}
2367	return null;
2368}
2369
2370// hack for IE
2371function doScrollCheck()
2372{
2373	if (BX.isReady)
2374		return;
2375
2376	try {document.documentElement.doScroll("left");} catch( error ) {setTimeout(doScrollCheck, 1); return;}
2377
2378	runReady();
2379}
2380/* \ready */
2381
2382/* garbage collector */
2383function Trash()
2384{
2385	var i,len;
2386
2387	for (i = 0, len = garbageCollectors.length; i<len; i++)
2388	{
2389		garbageCollectors[i].callback.apply(garbageCollectors[i].context || window);
2390
2391		try {
2392			delete garbageCollectors[i];
2393			garbageCollectors[i] = null;
2394		} catch (e) {}
2395	}
2396
2397	for (i = 0, len = proxyList.length; i < len; i++)
2398	{
2399		try {
2400			delete proxyList[i];
2401			proxyList[i] = null;
2402		} catch (e) {}
2403	}
2404
2405	try {BX.unbindAll();} catch(e) {}
2406
2407	NODECACHE = null;
2408	garbageCollectors = null;
2409	proxyList = null;
2410	eventsList = null;
2411	readyList = null;
2412	deniedEvents = null;
2413	customEvents = null;
2414	__readyHandler = null;
2415}
2416
2417function _adjustWait()
2418{
2419	if (!this.bxmsg) return;
2420
2421	var arContainerPos = BX.pos(this),
2422		div_top = arContainerPos.top;
2423
2424	if (div_top < BX.GetDocElement().scrollTop)
2425		div_top = BX.GetDocElement().scrollTop + 5;
2426
2427	this.bxmsg.style.top = (div_top + 5) + 'px';
2428
2429	if (this == BX.GetDocElement())
2430	{
2431		this.bxmsg.style.right = '5px';
2432	}
2433	else
2434	{
2435		this.bxmsg.style.left = (arContainerPos.right - this.bxmsg.offsetWidth - 5) + 'px';
2436	}
2437}
2438
2439function _checkDisplay(ob)
2440{
2441	var d = ob.style.display || BX.style(ob, 'display');
2442	if (d != 'none')
2443	{
2444		ob.BXDISPLAY = d;
2445		return true;
2446	}
2447	else
2448	{
2449		ob.BXDISPLAY = 'block';
2450		return false;
2451	}
2452}
2453
2454function _checkNode(obj, params)
2455{
2456	params = params || {};
2457
2458	if (BX.type.isFunction(params))
2459		return params.call(window, obj);
2460
2461	if (!params.allowTextNodes && !BX.type.isElementNode(obj))
2462		return false;
2463	var i,j,len;
2464	for (i in params)
2465	{
2466		switch(i)
2467		{
2468			case 'tag':
2469			case 'tagName':
2470				if (BX.type.isString(params[i]))
2471				{
2472					if (obj.tagName.toUpperCase() != params[i].toUpperCase())
2473						return false;
2474				}
2475				else if (params[i] instanceof RegExp)
2476				{
2477					if (!params[i].test(obj.tagName))
2478						return false;
2479				}
2480			break;
2481
2482			case 'class':
2483			case 'className':
2484				if (BX.type.isString(params[i]))
2485				{
2486					if (!BX.hasClass(obj, params[i]))
2487						return false;
2488				}
2489				else if (params[i] instanceof RegExp)
2490				{
2491					if (!BX.type.isString(obj.className) || !params[i].test(obj.className))
2492						return false;
2493				}
2494			break;
2495
2496			case 'attr':
2497			case 'attribute':
2498				if (BX.type.isString(params[i]))
2499				{
2500					if (!obj.getAttribute(params[i]))
2501						return false;
2502				}
2503				else if (BX.type.isArray(params[i]))
2504				{
2505					for (j = 0, len = params[i].length; j < len; j++)
2506					{
2507						if (params[i] && !obj.getAttribute(params[i]))
2508							return false;
2509					}
2510				}
2511				else
2512				{
2513					for (j in params[i])
2514					{
2515						var q = obj.getAttribute(j);
2516						if (BX.type.isString(params[i][j]))
2517						{
2518							if (q != params[i][j])
2519								return false;
2520						}
2521						else if (params[i][j] instanceof RegExp)
2522						{
2523							if (!BX.type.isString(q) || !params[i][j].test(q))
2524								return false;
2525						}
2526					}
2527				}
2528			break;
2529
2530			case 'property':
2531				if (BX.type.isString(params[i]))
2532				{
2533					if (!obj[params[i]])
2534						return false;
2535				}
2536				else if (BX.type.isArray(params[i]))
2537				{
2538					for (j = 0, len = params[i].length; j < len; j++)
2539					{
2540						if (params[i] && !obj[params[i]])
2541							return false;
2542					}
2543				}
2544				else
2545				{
2546					for (j in params[i])
2547					{
2548						if (BX.type.isString(params[i][j]))
2549						{
2550							if (obj[j] != params[i][j])
2551								return false;
2552						}
2553						else if (params[i][j] instanceof RegExp)
2554						{
2555							if (!BX.type.isString(obj[j]) || !params[i][j].test(obj[j]))
2556								return false;
2557						}
2558					}
2559				}
2560			break;
2561
2562			case 'callback':
2563				return params[i](obj);
2564			break;
2565		}
2566	}
2567
2568	return true;
2569}
2570
2571if(window.attachEvent) // IE
2572	window.attachEvent("onunload", Trash);
2573else if(window.addEventListener) // Gecko / W3C
2574	window.addEventListener('unload', Trash, false);
2575else
2576	window.onunload = Trash;
2577/* \garbage collector */
2578
2579// set empty ready handler
2580BX(BX.DoNothing);
2581
2582window.BX = BX;
2583})(window)

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.