PageSourceSearch

https://www.southwestphotobank.com/js/BS/1788533357/0.8.2/BS.js,BS.com.Prompt.js

js southwestphotobank.com collected 2026-09-28 02:02:42 UTC 66,680 bytes, 2,929 lines download raw bytes

1if (typeof(BS) == 'undefined') {
2	BS = {};
3	BS.com = {};
4	BS.utl = {};
5
6	function is_array(v)
7	{
8		if (!isset(v) || v === null) return false;
9		return v.constructor == Array;
10	}
11	function isset(v) { return (typeof v == 'undefined') ? false : true; }
12
13	function empty (obj)
14	{
15		if (obj === null) return true;
16
17		switch (typeof obj) {
18		case 'number': return (obj == 0);
19		case 'string': return (!obj || obj== '');
20		case 'boolean': return obj !== true;
21		case 'undefined': return true;
22		case 'object':
23			if (is_array(obj)) {
24				for (var i in obj) {
25					if (obj.hasOwnProperty(i)) return false;
26				}
27				return true;
28			}
29		case 'function':
30		default: return false;
31		}
32	}
33}
34
35/*!\brief	Callback class that represents a callback function, optional
36 *		data object, and optional bind to data scope.
37 * \param	f		function
38 * \param	bind		bind scope (optional)
39 * \param	d		data (optional)
40 */
41BS.com.Callback = function (f, bind, d)
42{
43	this.f = f;
44	this.bind = bind;
45	this.d = d;
46};
47
48BS.com.Browser = function ()
49{
50	this.agent = this._getAgent(this.dataBrowser) || 'unknown';
51	this.version = this._getVersion(navigator.userAgent)
52			|| this._getVersion(navigator.appVersion)
53			|| 'unknown';
54	this.OS = this._getAgent(this.dataOS) || 'an unknown OS';
55	
56	this.isIE = (this.agent == 'Explorer');
57	this.isSafari = (this.agent == 'Safari' || this.agent == 'Konqueror');
58	this.isOpera = (this.agent == 'Opera');
59	this.isChrome = (this.agent == 'Chrome');
60};
61
62BS.com.Browser.prototype = {
63	_getAgent: function (data)
64	{
65		for (var i=0; i<data.length; i++) {
66			var dataStr = data[i].str;
67			var dataProp = data[i].prop;
68			this.verSrchStr = data[i].verSrch || data[i].id;
69			if (dataStr) {
70				if (dataStr.indexOf(data[i].substr) != -1)
71					return data[i].id;
72			}
73			else if (dataProp) return data[i].id;
74		}
75	},
76
77	_getVersion: function (dataStr)
78	{
79		var index = dataStr.indexOf(this.verSrchStr);
80		if (index == -1) return;
81		return parseFloat(dataStr.substring(index+this.verSrchStr.length+1));
82	},
83
84	isValid: function (mode)
85	{
86		if (mode == 'Java' && this.OS == 'Mac') {
87			switch (this.agent) {
88			case 'Chrome': return true;
89			case 'Safari': return (this.version > 400);
90			case 'Firefox': return (this.version >= 1.5);
91			default: return false;
92			}
93		}
94
95		switch (this.agent) {
96		case 'Safari': return (this.version > 300);
97		case 'Chrome': return (this.version >= 0.2);
98		case 'Mozilla': return (this.version > 1.6);
99		case 'Firefox': return (this.version >= 1);
100		case 'Explorer': return (this.version > 5);
101		case 'Opera': return (this.version >= 9);
102		default: return false;
103		}
104	},
105
106	dataBrowser: [
107		{
108			str: navigator.vendor,
109			substr: 'Apple',
110			id: 'Safari'
111		},
112		{
113			str: navigator.vendor,
114			substr: 'Google',
115			id: 'Chrome'
116		},
117		{
118			prop: window.opera,
119			id: 'Opera'
120		},
121		{
122			str: navigator.vendor,
123			substr: 'iCab',
124			id: 'iCab'
125		},
126		{
127			str: navigator.vendor,
128			substr: 'KDE',
129			id: 'Konqueror'
130		},
131		{
132			str: navigator.userAgent,
133			substr: 'Firefox',
134			id: 'Firefox'
135		},
136		{	// for newer Netscapes (6+)
137			str: navigator.userAgent,
138			substr: 'Netscape',
139			id: 'Netscape'
140		},
141		{
142			str: navigator.userAgent,
143			substr: 'MSIE',
144			id: 'Explorer',
145			verSrch: 'MSIE'
146		},
147		{
148			str: navigator.userAgent,
149			substr: 'Gecko',
150			id: 'Mozilla',
151			verSrch: 'rv'
152		},
153		{ 	// for older Netscapes (4-)
154			str: navigator.userAgent,
155			substr: 'Mozilla',
156			id: 'Netscape',
157			verSrch: 'Mozilla'
158		}
159	],
160
161	dataOS : [
162		{
163			str: navigator.platform,
164			substr: 'Win',
165			id: 'Windows'
166		},
167		{
168			str: navigator.platform,
169			substr: 'Mac',
170			id: 'Mac'
171		},
172		{
173			str: navigator.platform,
174			substr: 'Linux',
175			id: 'Linux'
176		}
177	]
178};
179
180BS.com.Dom = function () 
181{
182	BS.utl.globalEvent.addCB('load', this._onLoad, this);
183};
184
185BS.com.Dom.mkidCnt = 0;
186
187BS.com.Dom.prototype = {
188	isLoaded:		false,
189
190	_onLoad: function () { this.isLoaded = true; },
191
192	mkid: function () { return 'BSDomID' + BS.com.Dom.mkidCnt++; },
193
194	get: function (id, doc)
195	{
196		if (id.nodeName) return id;
197
198		if (!doc) doc = document;
199
200		if (!doc.all && !document.getElementById)
201			throw 'AssertErr Dom.get';
202
203		if (doc.getElementById) return doc.getElementById(id);
204		else if (doc.all && doc.all[id]) return doc.all[id];
205		else return null;
206	},
207
208	textNode: function (obj, str)
209	{
210		var node = document.createTextNode(str);
211		obj.appendChild(node);
212		return node;
213	},
214
215	parentByNodeName: function (node, name)
216	{
217		name = name.toUpperCase();
218		for (; node && (node.nodeName != name); node=node.parentNode);
219		return node;
220	},
221
222	size: function (obj)
223	{
224		switch (this.getStyle(obj, 'overflow')) {
225		case 'scroll': return [obj.scrollWidth, obj.scrollHeight];
226		default: return [obj.offsetWidth, obj.offsetHeight];
227		}
228	},
229
230	//returns size, ignoring scroll attributes ... should probably
231	// be folded into size()
232	fullsize: function (obj)
233	{
234		return [obj.offsetWidth, obj.offsetHeight];
235	},
236
237	setSize: function (obj, dim)
238	{
239		_bsDom.setStyle(obj, 'width', dim[0] + 'px');
240		_bsDom.setStyle(obj, 'height', dim[1] + 'px');
241	},
242	
243	ofs: function (obj) { return [obj.offsetLeft, obj.offsetTop]; },
244
245	outerOfs: function (obj)
246	{
247		var			t = this.ofs(obj);
248		return BS.utl.xy.add(t, this.size(obj));
249	},
250
251	pos: function (obj)
252	{
253		var left = parseInt(this.getStyle(obj, 'left'));
254		var top = parseInt(this.getStyle(obj, 'top'));
255		var isRel = (this.getStyle(obj, 'position') == 'relative');
256
257		if (isNaN(left)) left = (isRel) ? 0 : obj.offsetLeft;
258		if (isNaN(top)) top = (isRel) ? 0 : obj.offsetTop;
259
260		var r = [left, top];
261
262		/*
263		if (BS.utl.browser.isIE &&
264		    this.getStyle(obj, 'position') == 'absolute')
265			BS.utl.xy.add(r, this.scrollOfs());
266		*/
267
268		return r;
269	},
270
271	outerPos: function (obj)
272	{
273		var			t = this.pos(obj);
274		return BS.utl.xy.add(t, this.size(obj));
275	},
276
277	objOfs: function (obj, top)
278	{
279		var			ofs = this.ofs(obj);
280		var			parent;
281
282		for (parent = obj.offsetParent; parent && parent != top;
283		     parent = parent.offsetParent)
284			BS.utl.xy.add(ofs, this.ofs(parent));
285
286		return ofs;
287	},
288
289	docOfs: function (obj, top)
290	{
291		var			ofs = this.ofs(obj);
292		var			parent;
293
294		for (parent = obj.offsetParent; parent && parent != top;
295		     parent = parent.offsetParent)
296			BS.utl.xy.add(ofs, this.ofs(parent));
297
298		return ofs;
299	},
300
301	outerDocOfs: function (obj)
302	{
303		var			t = this.pageOfs(obj);
304		return BS.utl.xy.add(t, this.size(obj));
305	},
306	
307	pageOfs: function (obj)
308	{
309		// must be part of document object
310		if (obj.parentNode === null ||
311		    this.getStyle(obj, 'display') == 'none')
312			return false;
313		
314		var		parent;
315		var		pos = [];
316		var		box;
317		
318		if (obj.getBoundingClientRect) {
319			// IE
320			box = obj.getBoundingClientRect();
321			pos = [box.left, box.top];
322			var scroll = [Math.max(document.documentElement.scrollLeft, document.body.scrollLeft),
323				      Math.max(document.documentElement.scrollTop, document.body.scrollTop)];
324			return BS.utl.xy.add(pos, scroll);
325		}
326		else if (document.getBoxObjectFor) {
327			// gecko
328			box = document.getBoxObjectFor(obj);
329			pos = [box.x, box.y];
330			var border = [parseInt(this.getStyle(obj, 'borderLeftWidth')),
331				      parseInt(this.getStyle(obj, 'borderTopWidth'))];
332			BS.utl.xy.add(pos, border);
333		}
334		else { 
335			// safari & opera
336			pos = [obj.offsetLeft, obj.offsetTop];
337			if ((parent = obj.offsetParent) != obj) {
338				for (; parent; parent = parent.offsetParent) {
339					pos[0] += parent.offsetLeft;
340					pos[1] += parent.offsetTop;
341				}
342			}
343			if (BS.utl.browser.isOpera || 
344			    (BS.utl.browser.isSafari &&
345			     this.getStyle(obj, 'position') == 'absolute')) {
346				pos[0] -= document.body.offsetLeft;
347				pos[1] -= document.body.offsetTop;
348			} 
349		}
350
351		for (parent = obj.parentNode;
352		     parent && parent.nodeName != 'BODY' &&
353			     parent.nodeName != 'HTML';
354		     parent = parent.parentNode) {
355			pos[0] -= parent.scrollLeft;
356			pos[1] -= parent.scrollTop;
357		}
358
359		return pos;
360	},
361
362	outerPageOfs: function (obj)
363	{
364		var			t = this.pageOfs(obj);
365		return BS.utl.xy.add(t, this.size(obj));
366	},
367
368	isInside: function (a, b, p, ofs)
369	{
370		var pt = (ofs) ? BS.utl.xy.add(p, ofs) : p;
371
372		if (pt[0] < a[0] || pt[1] < a[1]) return false;
373		if (pt[0] > b[0] || pt[1] > b[1]) return false;
374
375		return true;
376	},
377
378	scrollOfs: function ()
379	{
380		if (window.pageXOffset)
381			return [window.pageXOffset, window.pageYOffset];
382		else if (document.documentElement)
383			return [document.body.scrollLeft +
384				document.documentElement.scrollLeft, 
385				document.body.scrollTop +
386				document.documentElement.scrollTop];
387		else if (document.body.scrollLeft >= 0)
388			return [document.body.scrollLeft,
389				document.body.scrollTop];
390		else return [0, 0];
391	},
392
393	clientSize: function ()
394	{
395		if (window.innerHeight >= 0)
396			return [window.innerWidth, window.innerHeight];
397		else if (document.documentElement && document.documentElement.clientHeight)
398			return [document.documentElement.clientWidth,
399				document.documentElement.clientHeight];
400		else if (document.body.clientHeight >= 0)
401			return [document.body.clientWidth,
402				document.body.clientHeight];
403		else return [0, 0];
404	},
405
406	docSize: function ()
407	{
408		if (!document.body) return [0, 0];
409
410		var scrollH = -1, winH = -1, bodyH = -1;
411		var marginTop = parseInt(this.getStyle(document.body,
412						       'marginTop'), 10);
413		var marginBot = parseInt(this.getStyle(document.body,
414						       'marginBottom'), 10);
415		var docW = -1, bodyW = -1, winW = -1;
416		var marginR = parseInt(this.getStyle(document.body,
417						     'marginRight'), 10);
418		var marginL = parseInt(this.getStyle(document.body,
419						     'marginLeft'), 10);
420		
421		var mode = document.compatMode;
422
423		if ((mode || BS.utl.browser.isIE) && !BS.utl.browser.isOpera ) {
424			// (IE, Gecko)
425			switch (mode) {
426			case 'CSS1Compat':
427				// Standards mode
428				scrollH = ((window.innerHeight && window.scrollMaxY) ? window.innerHeight+window.scrollMaxY : -1);
429				winH = Math.max(document.documentElement.clientHeight, self.innerHeight||-1);
430				bodyH = document.body.offsetHeight + marginTop + marginBot;
431
432				docW = document.documentElement.clientWidth;
433				bodyW = document.body.offsetWidth + marginL + marginR;
434				winW = self.innerWidth || -1;
435				break;
436			
437			default:
438				// Quirks mode
439				scrollH = document.body.scrollHeight;
440				bodyH = document.body.clientHeight;
441
442				bodyW = document.body.clientWidth;
443				winWidth = document.body.scrollWidth;
444			}
445		}
446		else {
447			// Safari & Opera
448			scrollH = document.documentElement.scrollHeight;
449			winH = self.innerHeight;
450			bodyH = document.documentElement.clientHeight;
451			docW = document.documentElement.clientWidth;
452			bodyW = document.body.offsetWidth + marginL + marginR;
453			winW = self.innerWidth;
454		}
455	
456		return [Math.max(bodyW, docW, winW),
457			Math.max(bodyH, scrollH, winH)];
458	},
459
460	setPos: function (obj, pt)
461	{
462		this.setStyle(obj, 'left', pt[0] + 'px');
463		this.setStyle(obj, 'top', pt[1] + 'px');
464	},
465
466	clrPos: function (obj)
467	{
468		this.setStyle(obj, 'position', 'static');
469		this.setStyle(obj, 'left', 'auto');
470		this.setStyle(obj, 'top', 'auto');
471	},
472
473	setPageOfs: function (obj, pos, flag)
474	{
475		// change static to relative positioning
476		if (this.getStyle(obj, 'position') == 'static')
477			this.setStyle(obj, 'position', 'relative');
478		
479		var curPos = this.pageOfs(obj);
480		if (curPos === false) return false;
481		
482		var cssPos = this.pos(obj);
483
484		if (pos[0] !== null)
485			this.setStyle(obj, 'left',
486				      pos[0] - curPos[0] + cssPos[0] + 'px');
487		if (pos[1] !== null)
488			this.setStyle(obj, 'top',
489				      pos[1] - curPos[1] + cssPos[1] + 'px');
490	},
491	
492	clientCenter: function (obj, min, f_no_ofs)
493	{
494		min = (min) ? min : [10,10];
495		var size = this.size(obj);
496		var client = this.clientSize();
497		var scroll = this.scrollOfs();
498		var pos = [0, 0];
499
500		pos[0] = Math.round((client[0] - size[0])/2) + scroll[0];
501		pos[1] = Math.round((client[1] - size[1])/2);
502
503 		pos[1] += scroll[1];
504
505		if (!f_no_ofs) pos[1] -= Math.round(client[1]/20);
506
507		if (pos[0] < min[0]) pos[0] = min[0];
508		if (pos[1] < min[1]) pos[1] = min[1];
509
510		this.setPageOfs(obj, pos);
511	},
512
513	/*!\brief	Executes function on the argument.  Supports array of
514	 *		arguments or single argument.
515	 * \param	a		argument (can be array of arguments)
516	 * \param	f		function
517	 * \param	bind		bind scope (optional)
518	 */
519	_batch: function (a, f, bind)
520	{
521		if (is_array(a)) return BS.utl.array.foreach(a, f, bind);
522		else return f.call(bind, a);
523	},
524
525	/*!\brief	Get style.
526	 * \param	obj		object
527	 * \param	property	property
528	 */
529	getStyle: function (obj, property)
530	{
531		var		val = null;
532		var		dv = document.defaultView;
533	
534		if (property == 'opacity' && obj.filters) {
535			// IE opacity
536			val = 1;
537			try {
538				val = obj.filters.item('DXImageTransform.Microsoft.Alpha').opacity / 100;
539			}
540			catch(e) {
541				try { val = obj.filters.item('alpha').opacity / 100; }
542				catch(e) {}
543			}
544		}
545		else if (obj.style[property]) val = obj.style[property];
546		else if (obj.currentStyle && obj.currentStyle[property])
547			val = obj.currentStyle[property];
548		else if (dv && dv.getComputedStyle) {
549			// convert camelCase to hyphen-case
550			var str = '';
551			for (var i=0, l=property.length; i < l; i++) {
552				if (property.charAt(i) == property.charAt(i).toUpperCase()) 
553					str += '-' + property.charAt(i).toLowerCase();
554				else str += property.charAt(i);
555			}
556
557			if (dv.getComputedStyle(obj, '') &&
558			    dv.getComputedStyle(obj, '').getPropertyValue(str))
559				val = dv.getComputedStyle(obj, '').getPropertyValue(str);
560		}
561
562		return val;
563	},
564
565	/*!\brief	Set style.
566	 * \param	obj		object
567	 * \param	property	property
568	 * \param	val		value
569	 */
570	setStyle: function (obj, property, val)
571	{
572		if (!obj) throw 'BS.com.setStyle: null object';
573
574		switch (property) {
575		case 'opacity':
576			if (BS.utl.browser.isIE &&
577			    typeof obj.style.filter == 'string') {
578				obj.style.filter = 'alpha(opacity=' +
579					(val * 100) + ')';
580				if (!obj.currentStyle ||
581				    !obj.currentStyle.hasLayout)
582					obj.style.zoom = 1;
583			}
584			else {
585				obj.style.opacity = val;
586				obj.style['-moz-opacity'] = val;
587				obj.style['-khtml-opacity'] = val;
588			}
589			break;
590		// IE scroll compatibility
591		case 'position': if (BS.utl.browser.isIE) obj.style.zoom = 1;
592		default: try { obj.style[property] = val } catch (e) { };
593		}
594	},
595
596	setDisplay: function (obj, f)
597	{
598		BS.utl.dom.setStyle(obj, 'display', (f) ? 'block' : 'none');
599	},
600
601	setVis: function (obj, f)
602	{
603		BS.utl.dom.setStyle(obj, 'visibility', (f) ?
604				    'visible' : 'hidden');
605	},
606
607	toggleDisplay: function (obj)
608	{
609		obj = _bsDom.get(obj);
610		var f = (_bsDom.getStyle(obj, 'display') == 'none');
611		this.setDisplay(obj, f);
612	},
613
614	/*!\brief	Returns true if DOM object contains class name.
615	 * \param	obj		object
616	 * \param	cnm		class name
617	 * \return	boolean
618	 */
619	hasClass: function (obj, cnm)
620	{
621		var f = function (obj)
622		{
623			var re = new RegExp('(?:^|\\s+)' + cnm + '(?:\\s+|$)');
624			return re.test(obj['className']);
625		};
626
627		return this._batch(obj, f, this);
628	},
629   
630	/*!\brief	Adds class name to DOM object if it does not exist.
631	 * \param	obj		object
632	 * \param	cnm		class name
633	 */
634	addClass: function (obj, cnm)
635	{
636		var f = function (obj)
637		{
638			if (this.hasClass(obj, cnm)) return;
639			if (!empty(obj['className'])) cnm = ' ' + cnm;
640			obj['className'] += cnm;
641		};
642	 
643		this._batch(obj, f, this);
644	},
645
646	/*!\brief	Adds class name array to DOM object (wrapper).
647	 * \param	obj		object
648	 * \param	cnmA		class name array
649	 */
650	addClassArr: function (obj, cnmA)
651	{
652		for (var i=0, l=cnmA.length; i<l; i++)
653			this.addClass(obj, cnmA[i]);
654	},
655
656	/*!\brief	Delete class name from DOM object if it exists.
657	 * \param	obj		object
658	 * \param	cnm		class name
659	 */
660	delClass: function (obj, cnm)
661	{
662		var f = function (obj)
663		{
664			if (!this.hasClass(obj, cnm)) return;
665			var a = obj['className'].split(' ');
666			var str = '';
667			for (var i = 0; i < a.length; i++) {
668				if (a[i] == cnm) continue;
669				if (!empty(str)) str += ' ';
670				str += a[i];
671			}
672			obj['className'] = str;
673		};
674	 
675		this._batch(obj, f, this);
676	},
677
678	setClass: function (obj, cnm, f)
679	{
680		if (f) this.addClass(obj, cnm);
681		else this.delClass(obj, cnm);
682	},
683	
684	/*!\brief	Checks whether p is inside of the area of the node
685	 * \param	p		point
686	 * \param	node		dom object
687	 */
688	inNode: function (p, node)
689	{
690		var pos = this.pageOfs(node);
691		var size = this.size(node);
692		
693		return pos[0] <= p[0] && p[0] <= pos[0] + size[0] && 
694			pos[1] <= p[1] && p[1] <= pos[1] + size[1];
695	}
696};
697
698/*!\brief	Event class that provides a portability layer.
699 * \note	single instantiation only
700 */
701BS.com.Event = function ()
702{
703	// function array for attach/detach
704	this.fA = [];
705};
706
707BS.com.Event.prototype = {
708	/*!\brief	Destructor (pseudo) */
709	_delete: function ()
710	{
711		this.detachAll();
712		delete this.fA;
713	},
714
715	/*!\brief	Attach an event handler to an object.  A caching layer
716	 *		is in place for the detach method (to locate funcT).
717	 * \param	obj		DOM object
718	 * \param	type		event type
719	 * \param	func		function to attach
720	 * \param	bind		bind scope (optional)
721	 * \param	d		data (optional)
722	 */
723	attach: function (obj, type, func, bind, d)
724	{
725		var scope = bind ? bind : obj;
726		var funcT = function (e)
727		{
728			return func.call(scope, BS.com.Event.getEvent(e), d);
729		};
730
731		this.fA[this.fA.length] = [obj, type, func, funcT, scope, d];
732
733		// HACK: fix for stupid IE; there's probably a loop
734		// using the "for i in" syntax somewhere upstream
735		if (!obj) return;
736
737		if (obj.addEventListener)
738			obj.addEventListener(type, funcT, false);
739		else if (obj.attachEvent) obj.attachEvent('on' + type, funcT);
740		else throw 'AssertErr Event.attach';
741	},
742
743	/*!\brief	Detach event handler based on offset.
744	 * \param	i		offset
745	 */
746	_detach: function (i)
747	{
748		var			obj = this.fA[i][0];
749
750		// HACK: fix for stupid IE; there's probably a loop
751		// using the "for i in" syntax somewhere upstream
752		if (!obj) return;
753
754		if (obj.removeEventListener)
755			obj.removeEventListener(this.fA[i][1], this.fA[i][3],
756						false);
757		else if (obj.detachEvent)
758			obj.detachEvent('on' + this.fA[i][1], this.fA[i][3]);
759		else throw 'AssertErr Event.detach';
760
761		this.fA[i][0] = this.fA[i][3] = this.fA[i][4] = null;
762		this.fA.splice(i, 1);
763	},
764
765	/*!\brief	Detach an event handler from an object.
766	 * \param	obj		DOM object
767	 * \param	type		event type
768	 * \param	func		function to detach
769	 */
770	detach: function (obj, type, func)
771	{
772		var			t;
773		for (i=0, l=this.fA.length; i<l; i++) {
774			t = this.fA[i];
775			if (t[0] != obj || t[1] != type || t[2] != func)
776				continue;
777			break;
778		}
779
780		if (i == l) return false;
781
782		this._detach(i);
783
784		return true;
785	},
786
787	/*!\brief	Detach all event handlers. */
788	detachAll: function ()
789	{
790		var			t;
791		for (var i=0, l=this.fA.length; i<l; i++) {
792			t = this.fA[i];
793
794			// HACK: for stupid IE
795			if (!t[0]) continue;
796
797			if (t[0].removeEventListener)
798				t[0].removeEventListener(t[1], t[3], false);
799			else if (t[0].detachEvent)
800				t[0].detachEvent('on' + t[1], t[3]);
801			else throw 'AssertErr Event.detachAll';
802
803			delete this.fA[i][3];
804		}
805		delete this.fA;
806		this.fA = [];
807	},
808
809	/*!\brief	Detach multiple event handlers based on matching data.
810	 * \param	data		data
811	 */
812	detachData: function (data)
813	{
814		var			i, l, t = [];
815
816		for (i=0, l=this.fA.length; i<l; i++) {
817			if (this.fA[i][4] != data) continue;
818			t.push(i);
819		}
820
821		for (i=0, l=t.length; i<l; i++) this._detach(t[i] - i);
822	},
823
824	/*!\brief	Executes setInterval() with object support.
825	 * \param	ms		milliseconds
826	 * \param	func		function
827	 * \param	bind		bind scope (optional)
828	 * \param	d		data (optional)
829	 */
830	setInterval: function (ms, func, bind, d)
831	{
832		var t = function () { return func.call(bind, d); };
833		return setInterval(t, ms);
834	},
835	
836	/*!\brief	Executes setTimeout() with object support.
837	 * \param	ms		milliseconds
838	 * \param	func		function
839	 * \param	bind		bind scope (optional)
840	 * \param	d		data (optional)
841	 */
842	setTimeout: function (ms, func, bind, d)
843	{
844		var t = function () { return func.call(bind, d); };
845		return setTimeout(t, ms);
846	},
847	
848	/*!\brief	Returns KEY code for a key press event.
849	 * \param	e		event
850	 * \return	key code
851	 */
852	getKeyCode: function (e)
853	{
854		var code = e.keyCode || e.which;
855
856		// map unicode cursor codes in safari
857		if (e.type == 'keypress' && BS.utl.browser.isSafari) {
858			switch (code) {
859			case 63232:  return 38;
860			case 63233:  return 40;
861			case 63234:  return 37;
862			case 63235:  return 39;
863			}
864		}
865
866		// ignore function keys
867		if ((code >= 112 && code <= 123) && !e.charCode)
868			return null;
869
870		return code;
871	},
872
873	/*!\brief	Returns CHARACTER code for a key press event.
874	 * \param	e		event
875	 * \return	character code
876	 */
877	getCharCode: function (e)
878	{
879		return e.charCode ||
880		       ((e.type == 'keypress' || e.type == 'keydown' ||
881			 e.type == 'keyup') ? e.keyCode : 0) ||
882			e.which;
883	},
884
885	/*!\brief	Returns scroll position as an array.
886	 * \return	[ scrollLeft, scrollTop ]
887	 */
888	_getScroll: function ()
889	{
890		var dd = document.documentElement, db = document.body;
891		if (dd && dd.scrollTop) return [dd.scrollLeft, dd.scrollTop];
892		else if (db) return [db.scrollLeft, db.scrollTop];
893		else return [0, 0];
894	},
895
896	/*!\brief	Returns page X coordinate for event.
897	 * \param	e		event
898	 */
899	getX: function (e)
900	{
901		var			x = e.pageX;
902
903		if (!x && x !== 0) {
904			x = e.clientX || 0;
905		}
906
907		return x;
908	},
909
910	/*!\brief	Returns page Y coordinate for event.
911	 * \param	e		event
912	 */
913	getY: function (e)
914	{
915		var			y = e.pageY;
916
917		if (!y && y !== 0) {
918			y = e.clientY || 0;
919		}
920
921		return y;
922	},
923
924	/*!\brief	Return page X, Y cordinates for an event.
925	 * \param	e		event
926	 */
927	getXY: function (e)
928	{
929		return [this.getX(e), this.getY(e)];
930	},
931
932	/*!\brief	Returns event target.
933	 * \param	e		event
934	 * \param	textParent	parent if text (default false)
935	 * \return	element
936	 */
937	getTarget: function (e, textParent)
938	{
939		var			t = e.target || e.srcElement;
940		if (textParent && t && t.nodeName == '#text')
941			return t.parentNode;
942		return t;
943	},
944
945	/*!\brief	Return event related target.
946	 * \param	e		event
947	 * \return	element (related target)
948	 */
949	getRelatedTarget: function (e)
950	{
951		var			t = e.relatedTarget;
952
953		if (!t) {
954			if (e.type == 'mouseout') t = e.toElement;
955			else if (e.type == 'mouseover') t = e.fromElement;
956		}
957
958		return t;
959	},
960
961	/*!\brief	Prevent default action of browser for event.
962	 * \param	e		event
963	 */
964	preventDefault: function (e)
965	{
966		if (e.preventDefault) e.preventDefault();
967		else e.returnValue = false;
968	},
969
970	/*!\brief	Stop bubble phase for event.
971	 * \param	e		event
972	 */
973	stopPropagation: function (e)
974	{
975		if (e.stopPropagation) e.stopPropagation();
976		else e.cancelBubble = true;
977	},
978
979	disableSelect: function (obj)
980	{
981		if (BS.utl.browser.isIE || _bsBrowser.isSafari)
982			obj.onselectstart=function(){return false};
983		else if (BS.utl.browser.agent == 'Firefox') 
984			BS.utl.dom.setStyle(obj, 'MozUserSelect', 'none');
985		else 
986			obj.onmousedown=function(){return false};
987
988		BS.utl.dom.setStyle(obj, 'cursor', 'default');
989	}
990};
991
992/*!\brief	Returns event in a consistent manner.
993 * \param	e		event
994 * \return	event
995 */
996BS.com.Event.getEvent = function (e)
997{
998	if (!e) e = window.event;
999
1000	if (e.target) {
1001		if (e.target.nodeType == 3) e.target = e.target.parentNode;
1002	}
1003	else if (e.srcElement) e.target = e.srcElement;
1004
1005	return e;
1006};
1007
1008/*!\brief	CustomEvent class that represents a custom event type and can
1009 *		be associated with one or more Callback.
1010 * \param	type		event type
1011 * \param	bind		bind scope (default window)
1012 */
1013BS.com.CustomEvent = function (type, bind)
1014{
1015	this.type = type;
1016	this.scope = bind || window;
1017	this.cbA = [];
1018};
1019
1020BS.com.CustomEvent.prototype = {
1021	intvlID:		null,
1022
1023	/*!\brief	Add callback function.
1024	 * \param	f		function
1025	 * \param	bind		bind scope (optional)
1026	 * \param	d		data (optional)
1027	 */
1028	addCB: function (f, bind, d)
1029	{
1030		this.cbA.push(new BS.com.Callback(f, bind, d));
1031	},
1032
1033	/*!\brief	Delete callback function based on function and data.
1034	 * \param	f		function
1035	 * \param	bind		bind scope (optional)
1036	 * \param	d		data (optional)
1037	 */
1038	delCB: function (f, bind, d)
1039	{
1040		var			cb, found = false;
1041
1042		for (var i=0, l=this.cbA.length; i<l; i++) {
1043			cb = this.cbA[i];
1044			if (cb.f != f || cb.bind != bind || cb.d != d)
1045				continue;
1046
1047			cb.bind = cb.d = cb.f = null;
1048			this.cbA.splice(i, 1);
1049			found = true;
1050			i--;
1051			l--;
1052		}
1053
1054		return found;
1055	},
1056
1057	/*!\brief	Delete all callback functions. */
1058	delCBAll: function ()
1059	{
1060		BS.utl.event.detachData(this);
1061
1062		var			t;
1063		for (var i=0, l=this.cbA.length; i<l; i++) {
1064			t = this.cbA[i];
1065			t.bind = t.d = t.f = null;
1066			delete this.cbA[i];
1067		}
1068		this.cbA = [];
1069	},
1070
1071	/*!\brief	Destructor (pseudo) */
1072	_delete: function () { this.delCBAll(); },
1073
1074	/*!\brief	Execute callback functions.  The callback is executed
1075	 *		in the Callback::bind scope if not null otherwise, the
1076	 *		scope is CustomEvent::scope.  The callback function is
1077	 *		effectively called as follows:
1078	 *
1079	 *			cb(CustomEvent::type, Callback::d, arguments)
1080	 *			arguments[0]		target
1081	 *			arguments[1]		attach data (optional)
1082	 */
1083	exec: function ()
1084	{
1085		var			cb, r, scope;
1086		for (var i=0, l=this.cbA.length; i<l; i++) {
1087			cb = this.cbA[i];
1088			scope = cb.bind ? cb.bind : this.scope;
1089			r = cb.f.call(scope, this.type, cb.d, arguments);
1090		}
1091
1092		return r;
1093	},
1094
1095	/*!\brief	Attach to an event handler of an object.
1096	 * \param	obj		DOM object
1097	 * \param	type		event type
1098	 * \param	d		data (optional)
1099	 */
1100	attach: function (obj, type, d)
1101	{
1102		return BS.utl.event.attach(obj, type, this.exec, this, d);
1103	},
1104
1105	/*!\brief	Detach from an event handler of an object.
1106	 * \param	obj		DOM object
1107	 * \param	type		event type
1108	 */
1109	detach: function (obj, type)
1110	{
1111		return BS.utl.event.detach(obj, type, this.exec);
1112	},
1113
1114	/*!\brief	Attach to setInterval().
1115	 * \param	ms		milliseconds
1116	 * \param	d		data (optional)
1117	 */
1118	setInterval: function (ms, d)
1119	{
1120		this.clearInterval();
1121		this.intID = BS.utl.event.setInterval(ms, this.exec, this, d);
1122		return this.intID;
1123	},
1124
1125	/*!\brief	Attach to setTimeout().
1126	 * \param	ms		milliseconds
1127	 * \param	d		data (optional)
1128	 */
1129	setTimeout: function (ms, d)
1130	{
1131		return BS.utl.event.setTimeout(ms, this.exec, this, d);
1132	},
1133
1134	/*!\brief	Detach from setInterval(). */
1135	clearInterval: function () 
1136	{
1137		if (!this.intID) return;
1138		window.clearInterval(this.intID);
1139		this.intID = null;
1140	},
1141
1142	toString: function () { return 'CustomEvent: ' + this.type; }
1143};
1144
1145BS.com.Debug = function () {};
1146
1147BS.com.Debug.prototype = {
1148	_logOpt: function (opt, c)
1149	{
1150		if (!opt || typeof(opt) != 'string') return false;
1151		return (opt.indexOf(c) != -1)
1152	},
1153
1154	/*!\brief	Log message if textarea is set.
1155	 * \param	s		message
1156	 * \param	opt		option string
1157	 *				c: clear textarea
1158	 *				n: no newline
1159	 */
1160	log: function (s, opt)
1161	{
1162		if (!this._ta) this.setTextarea();
1163
1164		var			t;
1165
1166		if (s && typeof s == 'object') t = BS.utl.array.toString(s);
1167		else t = s;
1168
1169		if (this._logOpt(opt, 'c')) this._ta.value = '';
1170
1171		if (typeof(t) != 'undefined') {
1172			this._ta.value += t;
1173			if (!this._logOpt(opt, 'n')) this._ta.value += '\n';
1174		}
1175	},
1176
1177	/*!\brief	Set textarea.  If null, create textarea.
1178	 * \param	taID		textarea ID
1179	 * \note	requires document.body
1180	 */
1181	setTextarea: function (taID)
1182	{
1183		if (taID) this._ta = BS.utl.dom.get(taID);
1184		else {
1185			this._ta = BS.utl.mm.domNew(document.body, 'form');
1186			this._ta.innerHTML = '<input type=reset><br>' +
1187				'<textarea id=' +
1188				(this._ta = BS.utl.dom.mkid()) +
1189				' cols=80 rows=20></textarea>';
1190			this._ta = BS.utl.dom.get(this._ta);
1191		}
1192		this._ta.value = '';
1193	}
1194};
1195
1196/*!\brief	Memory management class that implements a pseudo destructor
1197 *		mechanism.  If the object is a DOM node, it is automatically
1198 *		removed from the parentNode.  Precedence is given to non DOM
1199 *		node objects in the delete phase.
1200 * \param	cnm		CSS class name
1201 */
1202BS.com.Delete = function (cnm)
1203{
1204	this._cnm = cnm;
1205	this._delA = [];
1206	this._param = [];
1207};
1208
1209/*!\brief	Hover compatibility callback. */
1210BS.com.Delete._hoverCB = function ()
1211{
1212	var			t = BS.utl.event.getTarget(arguments[0]);
1213	if (arguments[0].type == 'mouseover')
1214		BS.utl.dom.addClass(t, 'bsHover');
1215	else BS.utl.dom.delClass(t, 'bsHover');
1216};
1217
1218BS.com.Delete.prototype = {
1219	/*!\brief	Add item for later cleanup.
1220	 * \param	i		item
1221	 */
1222	add: function (i) { this._delA.push(i); },
1223
1224	_del: function (i)
1225	{
1226		if (!i || !i._delete) return;
1227
1228		i._delete();
1229		i._delete = null;
1230
1231		delete i;
1232	},
1233
1234	/*!\brief	Execute pseudo-destructor and delete for item.
1235	 * \param	obj		item
1236	 */
1237	del: function (obj)
1238	{
1239		var			i, l;
1240
1241		this._del(obj);
1242
1243		for (i=0, l=this._delA.length; i<l; i++) {
1244			if (this._delA[i] != obj) continue;
1245			this._delA.splice(i, 1);
1246			return;
1247		}
1248
1249		for (i=0, l=this._domA.length; i<l; i++) {
1250			if (this._domA[i] != obj) continue;
1251			this._domA.splice(i, 1);
1252			return;
1253		}
1254	},
1255
1256	/*!\brief	Execute pseudo-destructor and delete for
1257	 *		all items in the specified array.
1258	 * \param	iA		item array
1259	 */
1260	delArr: function (iA)
1261	{
1262                if (iA)
1263		    for (var i=0, l=iA.length; i<l; i++) this._del(iA[i]);
1264	             //for (var i in iA) this._del(iA[i]);
1265
1266		delete iA;
1267	},
1268
1269	/*!\brief	Returns object for specified object type (variable
1270	 *		arguments) and caches in this._delA.
1271	 * \param	obj		object constructor
1272	 * \return	object
1273	 */
1274	oNew: function (obj)
1275	{
1276		var t = [];
1277		for (var i=1, l=arguments.length; i<l; i++)
1278			t.push(arguments[i]);
1279		t = BS.utl.object.create(obj, t);
1280		this.add(t);
1281		return t;
1282	},
1283
1284	/*!\brief	Returns new CustomEvent and caches the object in
1285	 *		this._delA.
1286	 * \param	type		event type
1287	 * \param	oScope		object scope (default window)
1288	 */
1289	ceNew: function ()
1290	{
1291		var t = BS.utl.object.create(BS.com.CustomEvent, arguments);
1292		this.add(t);
1293		return t;
1294	},
1295
1296	/*!\brief	Returns DOM object for specified object id, sets
1297	 *		primary class name and appends optional class name.
1298	 * \param	e		object or object id
1299	 * \param	cnm		class name (optional)
1300	 * \return	DOM object
1301	 */
1302	domGet: function (e, cnm)
1303	{
1304		e = BS.utl.dom.get(e);
1305
1306		if (!e.nodeName) throw 'AssertErr BS.com.Delete.domGet';
1307
1308		if (this._cnm) BS.utl.dom.addClass(e, this._cnm);
1309
1310		if (this._param['className'])
1311			BS.utl.dom.addClass(e, this._param['className']);
1312
1313		if (cnm) BS.utl.dom.addClass(e, cnm);
1314
1315		return e;
1316	},
1317
1318	/*!\brief	Appends DOM object for specified node name, sets
1319	 *		primary class name and appends optional class name.
1320	 * \param	node		append node
1321	 * \param	name		node name
1322	 * \param	cnm		class name (optional)
1323	 * \return	DOM object
1324	 */
1325	domNew: function (node, name, cnm)
1326	{
1327		var e = document.createElement(name);
1328
1329		if (this._cnm) BS.utl.dom.addClass(e, this._cnm);
1330
1331		if (this._param['className'])
1332			BS.utl.dom.addClass(e, this._param['className']);
1333
1334		if (cnm) BS.utl.dom.addClass(e, cnm);
1335
1336		if (node) node.appendChild(e);
1337
1338		return e;
1339	},
1340
1341	/*!\brief	Hover wrapper for this.domNew(). */
1342	domNewHover: function (node, name, cnm)
1343	{
1344		var			e = this.domNew(node, name, cnm);
1345		BS.utl.event.attach(e, 'mouseover', BS.com.Delete._hoverCB);
1346		BS.utl.event.attach(e, 'mouseout', BS.com.Delete._hoverCB);
1347		return e;
1348	},
1349
1350	/*!\brief	Sets parameter value for specified name.
1351	 *		className	CSS class name
1352	 * \param	name		name
1353	 * \param	value		value
1354	 */
1355	setParam: function (name, value)
1356	{
1357		this._param[name] = value;
1358	},
1359
1360	/*!\brief	Pseudo destructor. */
1361	_delete: function ()
1362	{
1363		this._delA.reverse();
1364		this.delArr(this._delA);
1365		delete this._param;
1366	}
1367};
1368
1369/*!\brief	Global event handler that attaches to root-most elements.
1370 * \note	single instantiation only
1371 */
1372BS.com.GlobalEvent = function () { this.ceA = []; };
1373
1374BS.com.GlobalEvent.prototype = {
1375	_delete: function ()
1376	{
1377		//for (var i in this.ceA) {
1378		for (var i=0, l=this.ceA.length; i<l; i++) {
1379			if (this.ceA[i]._delete)
1380				this.ceA[i]._delete();
1381			delete this.ceA[i];
1382		}
1383	},
1384
1385	/*!\brief	Returns cached CustomEvent for specified type.
1386	 * \param	type		event type
1387	 * \return	CustomEvent
1388	 */
1389	_getCE: function (type)
1390	{
1391		if (!this.ceA[type]) {
1392			this.ceA[type] = new BS.com.CustomEvent(type + 'Evt');
1393
1394			var			obj;
1395			switch (type) {
1396			case 'load':
1397			case 'resize':
1398			case 'unload': obj = window; break;
1399			case 'keydown':
1400			case 'keyup':
1401			case 'keypress':
1402				obj = document; break;
1403			default: obj = document.body; break;
1404			}
1405
1406			this.ceA[type].attach(obj, type);
1407		}
1408
1409		return this.ceA[type];
1410	},
1411
1412	/*!\brief	Adds callback for specified type.
1413	 * \param	type		event type
1414	 * \param	f		function
1415	 * \param	bind		bind scope (optional)
1416	 * \param	data		data (optional)
1417	 */
1418	addCB: function (type, f, bind, d)
1419	{
1420		this._getCE(type).addCB(f, bind, d);
1421	},
1422
1423	/*!\brief	Deletes callback for specified type.
1424	 * \param	type		event type
1425	 * \param	f		function
1426	 * \param	data		data (optional)
1427	 */
1428	delCB: function (type, f, d)
1429	{
1430		this._getCE(type).delCB(f, d);
1431	}
1432};
1433
1434BS.com.Location = function () { this.location = document.location; };
1435
1436BS.com.Location.prototype = {
1437	/*!\brief	Returns array of the location path components. */
1438	getPathnameArr: function ()
1439	{
1440		if (!this.pathnameA) {
1441			this.pathnameA = this.location.pathname.split('/');
1442			this.pathnameA.shift();
1443		}
1444
1445		return this.pathnameA;
1446	},
1447
1448	/*!\brief	Returns the location search string. */
1449	getSearch: function ()
1450	{
1451		return (this.location.search) ?
1452			this.location.search.substr(1) : null;
1453	},
1454
1455	/*!\brief	Returns array of the location search string components
1456	 *		URL decoded.
1457	 * \note	no array support for multi form vars
1458	 */
1459	getSearchArr: function ()
1460	{
1461		var			s;
1462		if (!(s = this.getSearch())) return null;
1463		return BS.utl.array.urldecode(s);
1464	},
1465	
1466	getHash: function ()
1467	{
1468		return (this.location.hash) ?
1469			this.location.hash.substr(1) : null;
1470	},
1471	
1472	getHashArr: function ()
1473	{
1474		var			s;
1475		if (!(s = this.getHash())) return null;
1476		return BS.utl.array.urldecode(s);
1477	}
1478};
1479
1480BS.com.Window = function () {};
1481
1482BS.com.Window.prototype = {
1483	open: function (url, dim, attr, name)
1484	{
1485		var args = "width=" + dim[0] + ",height=" + dim[1];
1486
1487		if (attr && ((typeof attr) == "string")) {
1488			args += (attr.indexOf("menubar") != -1 ) ?
1489				",menubar=yes" : ",menubar=no";
1490			args += (attr.indexOf("noscroll") != -1 ) ?
1491				",scrollbars=no" : ",scrollbars=yes";
1492			args += (attr.indexOf("resize") != -1) ?
1493				",resizable=yes" : ",resizeable=no";
1494			args += (attr.indexOf("tool") != -1) ?
1495				",toolbar=yes" : ",toolbar=no";
1496			args += (attr.indexOf("loc") != -1) ?
1497				",location=yes" : ",location=no";
1498			args += (attr.indexOf("status") != -1) ?
1499				",status=yes" : ",status=no";
1500		}
1501		else args += ",menubar=no,scrollbars=yes,resizeable=no,toolbar=no,location=no,status=no";
1502
1503		if (name === true)
1504			name = 'Window' + Math.floor(100000*Math.random());
1505		else if (!name) name = 'Window';
1506
1507		var w =  window.open(url, name, args);
1508		w.focus();
1509		return w;
1510	},
1511	
1512	openLink: function(url, dim, attr, name)
1513	{
1514		this.open(url, dim, attr, name);
1515	}
1516};
1517
1518BS.com.Form = {};
1519
1520BS.com.Form.Select = function (obj, name, first, className)
1521{
1522	if (!obj) obj = null;
1523
1524	if (!className) className = null;
1525	
1526	this.mm = new BS.com.Delete();
1527	this.sel = this.mm.domNew(obj, 'select', className);
1528	this.sel.setAttribute('name', name);
1529
1530	if (first) {
1531		this.optMM = new BS.com.Delete();
1532		tmp  = this.optMM.domNew(this.sel, 'option');
1533		tmp.value = first[0]; tmp.text = first[1];
1534	}
1535};
1536
1537BS.com.Form.Select.prototype = {
1538	_delete: function () { this.mm._delete(); },
1539
1540	clear: function ()
1541	{
1542		if (this.optMM) {
1543			for (var t=this.sel; t.hasChildNodes();
1544			     t.removeChild(t.firstChild));
1545			this.optMM._delete();
1546			delete this.optMM;
1547		}
1548
1549		this.optMM = new BS.com.Delete();
1550	},
1551	
1552	load: function (arr, f_concat)
1553	{
1554		var tmp;
1555
1556		if (!this.optMM || !f_concat) {
1557			this.clear();
1558		}
1559
1560		for (var i = 0, l = arr.length; i<l; i++) {
1561			tmp = this.optMM.domNew(this.sel, 'option', (arr[i][2] ? arr[i][2] : null));
1562			tmp.value = arr[i][0];
1563			tmp.text = arr[i][1];
1564		}
1565
1566		this.sel.selectedIndex = 0;
1567	},
1568	
1569	setVal: function (val) { BS.utl.form.setValue(this.sel, val); },
1570
1571	getVal: function () { return BS.utl.form.getValue(this.sel); },
1572	getText: function ()
1573	{
1574	 	return this.sel.options[this.sel.selectedIndex].text;
1575	},
1576	
1577	setStyle: function (prop, val)
1578	{
1579		BS.utl.dom.setStyle(this.sel, prop, val);
1580	},
1581
1582	addClass: function (cn) { BS.utl.dom.addClass(this.sel, cn); },
1583
1584	disable: function (flag) { this.sel.disabled = flag; },
1585	
1586	reset: function () { this.sel.selectedIndex = 0; }
1587};
1588
1589BS.com.Button = function (rootObj, cls, txt)
1590{
1591	this.obj = _bsMm.domNew(null, "input", cls);
1592	this.obj.setAttribute('type', 'button');
1593	this.obj.setAttribute('value', txt);
1594	rootObj.appendChild(this.obj);
1595	this.ceA = [];
1596}
1597
1598BS.com.Button.prototype = {
1599	_delete: function ()
1600	{
1601		_bsMm.delArr(this.ceA);
1602	},
1603
1604	attach: function (type, func, scope, data)
1605	{
1606		var ce = _bsMm.ceNew();
1607		ce.addCB(func, scope, data);
1608		ce.attach(this.obj, type);
1609		this.ceA.push({ce:ce, type:type});
1610	},
1611
1612	disable: function (val)
1613	{
1614		if (val) this.obj.setAttribute('disabled', true);
1615		else this.obj.removeAttribute('disabled');
1616	},
1617
1618	setLabel: function (str)
1619	{
1620		this.obj.setAttribute('value', str);
1621	}
1622};
1623
1624BS.utl.gvA = [];
1625BS.utl.gvAi = 0;
1626BS.utl.mkGvId = function () { return BS.utl.gvAi++; };
1627
1628BS.utl.mkGvStr = function (obj)
1629{
1630	var gvid = BS.utl.mkGvId();
1631	BS.utl.gvA[gvid] = obj;
1632	return 'BS.utl.gvA[' + gvid + ']';
1633}
1634
1635BS.utl.object = {};
1636
1637BS.utl.object.clone = function (obj, noRecursion)
1638{
1639	if (!obj || typeof obj != 'object') return obj;
1640
1641	var newObj = (obj.constructor == Array) ? [] : {};
1642
1643	for (var i in obj)
1644		newObj[i] = noRecursion ? obj[i] : BS.utl.object.clone(obj[i]);
1645
1646	return newObj;
1647};
1648
1649/*!\brief	New object creation using argument array.
1650 * \param	obj		object
1651 * \param	argA		argument array
1652 */
1653BS.utl.object.create = function (obj, argA)
1654{
1655	var t = function (tA) { obj.apply(this, tA); };
1656	t.prototype = obj.prototype;
1657	return new t(argA);
1658};
1659
1660/*!\brief	Inherit class definition.  This function must be run after the
1661 *		definition of the parent/child constructors and prototypes.
1662 *		The child constructor uses the Function.call(this, ...)
1663 *		method to call the parent constructor.
1664 * \param	parent		parent class
1665 * \param	child		child class
1666 */
1667BS.utl.object.inherit = function (parent, child)
1668{
1669	if (!parent) throw 'BS.utl.object.inherit missing parent class';
1670	var tmp = function () {};
1671	tmp.prototype = parent.prototype;
1672	var old = function () {};
1673	old.prototype = child.prototype;
1674	child.prototype = new tmp;
1675	old.prototype = new old;
1676	for (var i in old.prototype) child.prototype[i] = old.prototype[i];
1677};
1678
1679BS.utl.object._cacheA = [];
1680BS.utl.object.cache = function (oName)
1681{
1682	var			a = BS.utl.object._cacheA;
1683
1684	if (!isset(a[oName])) 
1685		a[oName] = BS.utl.mm.oNew(eval(oName));
1686
1687	return a[oName];
1688}
1689
1690BS.utl.mm = new BS.com.Delete;
1691BS.utl.event = BS.utl.mm.oNew(BS.com.Event);
1692BS.utl.browser = BS.utl.mm.oNew(BS.com.Browser);
1693
1694BS.utl.array = {};
1695
1696/*!\brief	Copy array.
1697 * \param	a		Array
1698 */
1699BS.utl.array.copy = function (a)
1700{
1701	var			r = [];
1702
1703	if (a.constructor == arguments.constructor)
1704		a = Array.prototype.slice.call(a);
1705
1706	for (var i in a) r[i] = a[i];
1707
1708	return r;
1709};
1710
1711/*!\brief	Calls callback function for each member of the array with an
1712 *		optional data variable.  The callback is called with a single
1713 *		element of the array and the optional data.
1714 * \param	a		Array
1715 * \param	cb		callback
1716 * \param	d		data (optional)
1717 * \param	bind		bind scope (optional)
1718 */
1719BS.utl.array.foreach = function (a, cb, bind)
1720{
1721	if (bind) f = function (aT, dT) { cb.call(bind, aT, dT); };
1722	else f = cb;
1723
1724	for (var d=null, i=0, l=a.length; i<l; i++) d = f(a[i], d);
1725
1726	if (bind) delete f;
1727
1728	return d;
1729};
1730
1731/*!\brief	Returns length of associative array or object
1732 * \param	a		Array
1733 */
1734BS.utl.array.length = function (a)
1735{
1736	var n = 0;
1737	for (var i in a) n++;
1738	return n;
1739};
1740
1741/*!\brief	Reduces Array by removing [0] only elements.  Accepts an
1742 *		optional max depth parameter.
1743 * \param	arr		Array
1744 * \param	d		max depth (optional)
1745 * \return	Array
1746 */
1747BS.utl.array.reduce = function (arr, d)
1748{
1749	var			arF = BS.utl.array.reduce, i;
1750
1751	if (typeof d == 'undefined') d = -1;
1752
1753	if (!d) return arr;
1754	else if (d > 0) d--;
1755
1756	if (typeof arr != 'object') return arr;
1757	if (arr.length == 1) return arr = arF(arr[0], d);
1758
1759	for (i in arr) arr[i] = arF(arr[i], d);
1760
1761	return arr;
1762};
1763
1764/*!\brief	Calls BS.utl.array.reduce() for every member of array.
1765 * \param	arr		Array
1766 * \return	Array
1767 */
1768BS.utl.array.reduceArr = function (arr)
1769{
1770	for (var i in arr) arr[i] = BS.utl.array.reduce(arr[i]);
1771	return arr;
1772};
1773
1774/*!\brief	Converts Array to a String.
1775 * \param	arr		Array
1776 * \param	d		depth (internal)
1777 * \return	String
1778 */
1779BS.utl.array.toString = function (arr, d)
1780{
1781	var			i, j = 0, s = '', t;
1782
1783	if (typeof d == 'undefined') d = 0;
1784
1785	if (arr === null || arr.constructor == String)
1786		return (arr === null ? 'null' : '"' + arr + '"') + '\n';
1787
1788	for (i in arr) j++;
1789	if (!j && arr.length) arr = Array.prototype.slice.call(arr);
1790
1791	if (d && arr.constructor != Array) return arr.constructor.toString();
1792
1793	for (i in arr) {
1794		for (j=0; j<d; j++) s += '.       ';
1795		s += '[' + i + ']';
1796		t = arr[i];
1797		if (t !== null && typeof t == 'object')
1798			s += '\n' + BS.utl.array.toString(t, d+1);
1799		else {
1800			s += ' ';
1801			if (t === null) s += 'null\n';
1802			else if (typeof(t) == 'string') s += '"' + t + '"\n';
1803			else s += t + '\n';
1804		}
1805	}
1806
1807	return s;
1808};
1809
1810/*!\brief	Deletes empty members.
1811 * \param	a		Array
1812 */		
1813BS.utl.array.trim = function (a)
1814{
1815	for (var i in a) { if (empty(a[i])) delete a[i]; }
1816	return a;
1817};
1818
1819/*!\brief	Returns an array representation of the URL encoded string of
1820 *		name/value pairs.  The form variable array representation
1821 *		is based on the PHP interpretation.
1822 * \param	s		URL string
1823 * \return	array
1824 */
1825BS.utl.array.urldecode = function (s)
1826{
1827	var			a = s.split('&'), r = [], t = [];
1828	var			i, l, nA, nI, nL;
1829	var			de = BS.utl.string.urldecode;
1830	var			na = BS.utl.form._getNameArr;
1831
1832	for (i=0, l=a.length; i<l; i++) {
1833		t = a[i].split('=', 2);
1834		for (nA=na(de(t[0])), nI=0, nL=nA.length, nT=r; nI<nL-1;
1835		     nI++) {
1836			if (typeof(nT[nA[nI]]) == 'undefined' ||
1837			    nT[nA[nI]].constructor != Array)
1838				nT[nA[nI]] = [];
1839			nT = nT[nA[nI]];
1840		}
1841
1842		nT[nA[nI]] = de(t[1]);
1843	}
1844
1845	return r;
1846};
1847
1848/*!\brief	Returns a string representing the specified array as a form
1849 *		URL encoded string.  The form variable array representation
1850 *		is based on the PHP interpretation.
1851 * \param	arr		array
1852 * \param	name		name (optional for array recursion)
1853 * \return	URL string
1854 */
1855BS.utl.array.urlencode = function (arr, name)
1856{
1857	var			s = null, t;
1858	var			aeF = BS.utl.array.urlencode;
1859	var			seF = BS.utl.string.urlencode;
1860
1861	for (var i in arr) {
1862		if (s) s += '&';
1863		else s = '';
1864
1865		t = name ? (name + '[' + i + ']') : i;
1866
1867		if (typeof arr[i] == 'object')
1868			s += !empty(arr[i]) ? aeF(arr[i], t) : (seF(t) + '=');
1869		else s += (seF(t) + '=' + seF(arr[i]));
1870	}
1871
1872	return s;
1873};
1874
1875/*!\brief	Tests if array contains specified element
1876 * \param	array		array
1877 * \param	obj		element
1878 * \return	boolean
1879 */
1880BS.utl.array.inArray = function (arr, obj)
1881{
1882	if (empty(arr)) return false;
1883
1884	for (var i=0, l=arr.length; i < l; i++) if (arr[i] == obj) return true;
1885	return false;
1886};
1887
1888BS.utl.debug = new BS.com.Debug;
1889var log = function (a, b) { BS.utl.debug.log(a, b); };
1890
1891BS.utl.globalEvent = BS.utl.mm.oNew(BS.com.GlobalEvent);
1892BS.utl.globalEvent.addCB('unload', BS.utl.mm._delete, BS.utl.mm);
1893
1894BS.utl.dom = new BS.com.Dom;
1895
1896BS.utl.cookie = {};
1897
1898BS.utl.cookie.get = function (n)
1899{
1900	var			c = document.cookie, i, j;
1901	n = escape(n);
1902
1903	if ((i = c.indexOf(n + '=')) < 0) return null;
1904	
1905	i += n.length + 1;
1906
1907	if ((j = c.indexOf(';', i)) < 0) j = c.length;
1908
1909	return unescape(c.substring(i, j));
1910};
1911
1912BS.utl.form = {};
1913
1914/*!\brief	Return the form element of the specified name. Needed to get
1915 *		around a stupid IE problem with dynamically-generated form
1916 *		elements.
1917 * \param	fObj		form object
1918 * \param	n		element name
1919 */
1920BS.utl.form.getElem = function (fObj, n)
1921{
1922	if (BS.utl.browser.isIE) {
1923		var el = [];
1924		for (var e, i=0, l=fObj.elements.length; i<l; i++) {
1925			e = fObj.elements[i];
1926			if (e.name == n) el.push(e);
1927		}
1928		switch (el.length) {
1929		case 0: return null;
1930		case 1: return el[0];
1931		default: return el;
1932		}		
1933	}
1934	else {
1935		if (isset(fObj.elements[n])) return fObj.elements[n];
1936		return null;
1937	}
1938};
1939
1940/*!\brief	Return the value of the specified form element.
1941 * \param	fe		form element
1942 */
1943BS.utl.form.getValue = function (fe)
1944{
1945	switch (fe.type) {
1946	case 'hidden':
1947	case 'text':
1948	case 'password':
1949	case 'file':
1950	case 'textarea': return fe.value;
1951	case 'checkbox':
1952	case 'radio':
1953		if (fe.checked) return fe.value;
1954		break;
1955	case 'select-one':
1956		if (fe.selectedIndex < 0) fe.selectedIndex = 0;
1957		if (fe.length) return fe[fe.selectedIndex].value;
1958		break;
1959	case 'select-multiple':
1960		var			r = null;
1961		for (var i=0, l=fe.length; i<l; i++) {
1962			if (!fe[i].selected) continue;
1963			if (!r) r = [];
1964			r.push(fe[i].value);
1965		}
1966		return r;
1967	case 'button':
1968	case 'submit':
1969		break;
1970	default: throw 'AssertErr: unimplemented form element: ' + fe.type;
1971	}
1972
1973	return null;
1974};
1975
1976/*!\brief	Set the value of the specified form element.
1977 * \param	fe		form element
1978 * \param	v		value
1979 * \param	s		skip IE setTimeout() workaround (default false)
1980 */
1981BS.utl.form.setValue = function (fe, v, s)
1982{
1983	var			i, l, t;
1984
1985	try {
1986		t = fe.type;
1987		if (!isset(t) && isset(fe.length)) t = fe[0].type;
1988
1989		switch (t) {
1990		case 'checkbox':
1991			if (v.constructor == Array) {
1992				fe.checked = false;
1993				for (i in v) {
1994					if (fe.value == v[i]) {
1995						fe.checked = true;
1996						break;
1997					}
1998				}
1999			}
2000			else fe.checked = (fe.value == v);
2001			break;
2002		case 'hidden':
2003		case 'text':
2004		case 'textarea':
2005			fe.value = v;
2006			break;
2007		case 'radio':
2008			if (fe.length) {
2009				for (i=0, l=fe.length; i<l; i++)
2010					fe[i].checked = (fe[i].value == v);
2011			}
2012			else fe.checked = (fe.value == v);
2013			break;
2014		case 'select-one':
2015		case 'select-multiple':
2016			for (i=0, l=fe.length; i<l; i++) {
2017				if (fe[i].value != v) {
2018					if (fe.type ==  'select-one')
2019						fe[i].selected = false;
2020					continue;
2021				}
2022				fe[i].selected = true;
2023				break;
2024			}
2025			break;
2026		default: throw 'AssertErr: unimplemented form element';
2027		}
2028	}
2029	catch (e) {
2030		if (!s)	BS.utl.event.setTimeout(0,
2031			function () { BS.utl.form.setValue(fe, v, true); });
2032	}
2033};
2034
2035/*!\brief	Returns an array representing the array components in the
2036 *		string representation of an array.
2037 * \param	s		string array representation
2038 */
2039BS.utl.form._getNameArr = function (s)
2040{
2041	for (var a = s.split('['), i=0, l=a.length, n, t; i<l; i++) {
2042		n = a[i];
2043		t = n.indexOf(']');
2044
2045		if ((!i && (!n.length || t != -1)) ||
2046		    (i && (t == -1 || t != n.lastIndexOf(']'))))
2047			throw 'AssertErr: invalid name format';
2048
2049		if (i) a[i] = n.substr(0, n.length - 1);
2050	}
2051
2052	return a;
2053}
2054
2055/*!\brief	Sets form to match any existing members in the specified
2056 *		array.
2057 * \param	f		form object
2058 * \param	a		array
2059 * \param	s		skip IE setTimeout() workaround (default false)
2060 */
2061BS.utl.form.fromArray = function (f, a, s)
2062{
2063	var			sF = BS.utl.form.setValue;
2064	var			vF = BS.utl.form.fromArray._g;
2065
2066	for (var i=0, t=f.elements, l=t.length, v; i<l; i++) {
2067		if (!t[i].name) continue;
2068
2069		if ((v = vF(t[i].name, a)) === null) continue;
2070
2071		sF(t[i], v, s);
2072	}
2073}
2074
2075/*!\brief	Returns value from array based on string representation of the
2076 *		desired array value.
2077 * \param	s		string array representation
2078 * \param	dA		array
2079 */
2080BS.utl.form.fromArray._g = function (s, dA)
2081{
2082	var			a = BS.utl.form._getNameArr(s);
2083
2084	for (var i=0, l=a.length, n, tA = dA; i<l; i++) {
2085		n = a[i];
2086
2087		if (!isset(tA[n])) return null;
2088		else if (tA[n].constructor != Array &&
2089			 tA[n].constructor != Object) {
2090			if (i == l - 1) return tA[n];
2091			return null;
2092		}
2093		else if (i == l - 2 && a[i+1] === '' &&
2094			 (tA[n].constructor == Array ||
2095			  tA[n].constructor == Object))
2096			return tA[n];
2097
2098		tA = tA[n];
2099	}
2100	return null;
2101}
2102
2103/*!\brief	Returns an array representing the elements in the form object
2104 *		optionally skipping blank entries.
2105 * \param	f		form object
2106 * \param	s		skip blank (default false)
2107 * \return	array
2108 */
2109BS.utl.form.toArray = function (f, s)
2110{
2111	var			sF = BS.utl.form.toArray._s;
2112	var			vF = BS.utl.form.getValue;
2113
2114	for (var i=0, t=f.elements, l=t.length, rA=[], v; i<l; i++) {
2115		if (!t[i].name) continue;
2116
2117		if ((v = vF(t[i])) === null || (s && !v)) continue;
2118
2119		sF(t[i].name, v, rA);
2120	}
2121	return rA;
2122};
2123
2124/*!\brief	Sets value into array creating all necessary elements in the
2125 *		string representation of the destination in the array.
2126 * \param	s		string array represenation
2127 * \param	v		value
2128 * \param	rA		array (return)
2129 */
2130BS.utl.form.toArray._s = function (s, v, rA)
2131{
2132	var			a = BS.utl.form._getNameArr(s);
2133
2134	for (var i=0, l=a.length-1, n, t, tA=rA; i<l; i++) {
2135		n = a[i];
2136
2137		if (!n.length) {
2138			tA.push(t = []);
2139			tA = t;
2140			continue;
2141		}
2142
2143		if (!isset(tA[n]) || tA[n].constructor != Array) tA[n] = [];
2144
2145		tA = tA[n];
2146	}
2147
2148	n = a[a.length - 1];
2149
2150	if (!n.length) tA.push(v);
2151	else if (typeof(tA[n]) == 'undefined') tA[n] = v;
2152	else if (tA[n].constructor == Array) tA[n].push(v);
2153	else tA[n] = [tA[n], v];
2154
2155	return rA;
2156}
2157
2158BS.utl.form.post = function (d, action)
2159{
2160	var f = BS.utl.mm.domNew(null, 'form');
2161	f.setAttribute('method', 'post');
2162	if (action) f.setAttribute('action', action);
2163
2164	var str = '';
2165	for (var k in d) {
2166		str += '<input type="hidden" name="' + k + '" value="' + d[k] + '">';
2167	}
2168
2169	f.innerHTML = str;
2170	document.body.appendChild(f);
2171	f.submit();
2172}
2173
2174BS.utl.func = {};
2175
2176BS.utl.func.bind = function (f, scope)
2177{
2178	return function () { return f.apply(scope, arguments); };
2179};
2180
2181BS.utl.img = {};
2182
2183/*!\brief	Returns a single Image or an array of Images based on the
2184 *		number of image src strings supplied to the function.
2185 * \param	variable arguments	image src string(s)
2186 * \return	Image or array of Image
2187 */
2188BS.utl.img.create = function()
2189{
2190	var			r;
2191
2192	if (arguments.length == 1) {
2193		r = new Image;
2194		r.src = arguments[0];
2195		return r;
2196	}
2197	
2198	r = [];
2199	for (var i=0, l=arguments.length; i<l; i++) {
2200		r[i] = new Image;
2201		r[i].src = arguments[i];
2202	}
2203	
2204	return r;
2205};
2206
2207BS.utl.location = new BS.com.Location;
2208
2209BS.utl.mouse = {};
2210
2211/*!\brief	Return offset of mouse based on event.
2212 * \param	evt		event
2213 */
2214BS.utl.mouse.ofs = function (evt)
2215{
2216	var r = BS.utl.event.getXY(BS.com.Event.getEvent(evt));
2217	if (BS.utl.browser.isIE) BS.utl.xy.add(r, BS.utl.dom.scrollOfs());
2218	return r;
2219};
2220
2221/*!\brief	Return position of mouse based on event.
2222 * \param	evt		event
2223 */
2224BS.utl.mouse.pos = function (evt)
2225{
2226	evt = BS.com.Event.getEvent(evt);
2227	return [evt.clientX, evt.clientY];
2228};
2229
2230BS.utl.string = {};
2231
2232/*!\brief	Returns a URL decoded string based on the provided string.
2233 * \param	s		string
2234 * \return	URL encoded string
2235 */
2236BS.utl.string.urldecode = function (s) { return decodeURIComponent(s); };
2237
2238/*!\brief	Returns a URL encoded string based on the provided string.
2239 * \param	s		string
2240 * \return	URL encoded string
2241 */
2242BS.utl.string.urlencode = function (s) { return encodeURIComponent(s); };
2243
2244BS.utl.string.fmtBytes = function (bytes) 
2245{
2246	var			unit;
2247	var			n;
2248	
2249	bytes = parseInt(bytes);
2250	if (isNaN(bytes)) return '-';
2251	
2252	if (bytes/(1024) < 1) {
2253		unit = 'bytes';
2254		n = Math.floor(bytes);
2255	}
2256	else if (bytes/(1024*1024) < 1) {
2257		unit = 'KB';
2258		n = Math.floor(10*bytes/(1024))/10;
2259	}
2260	else if (bytes/(1024*1024*1024) < 1) {
2261		unit = 'MB';
2262		n = Math.floor(10*bytes/(1024*1024))/10;
2263	}
2264	else {
2265		unit = 'GB';
2266		n = Math.floor(10*bytes/(1024*1024*1024))/10;
2267	}
2268	
2269	n = n.toString();
2270	if (n.indexOf('.') < 0) n += '.0';
2271
2272	return n + unit;
2273};
2274
2275BS.utl.string.truncate = function (str, n)
2276{
2277	if (str.length >= n) return (str.substr(0, n-3) + '...');
2278	else return str;
2279};
2280
2281BS.utl.string.fmtMoney = function (mnt)
2282{
2283	mnt -= 0;
2284	mnt = (Math.round(mnt*100))/100;
2285	return (mnt == Math.floor(mnt)) ? mnt + '.00'
2286		: ( (mnt*10 == Math.floor(mnt*10)) ?
2287		mnt + '0' : mnt);
2288} 
2289
2290BS.utl.xy = {};
2291
2292/*!\brief	Add source XY to destination XY (destructive).
2293 * \param	d		destination XY
2294 * \param	s		source XY
2295 */
2296BS.utl.xy.add = function (d, s)
2297{
2298	d[0] += s[0];
2299	d[1] += s[1];
2300	return d;
2301};
2302
2303/*!\brief	Subtract source XY from destination XY (destructive).
2304 * \param	d		destination XY
2305 * \param	s		source XY
2306 */
2307BS.utl.xy.sub = function (d, s)
2308{
2309	d[0] -= s[0];
2310	d[1] -= s[1];
2311	return d;
2312};
2313
2314/*!\brief	Return distance between two XY points.
2315 * \param	p0		point XY
2316 * \param	p1		point XY
2317 */
2318BS.utl.xy.distance = function (p0, p1)
2319{
2320	return Math.sqrt(Math.pow(p0[0] - p1[0], 2) +
2321			 Math.pow(p0[1] - p1[1], 2));
2322};
2323
2324/*!\brief	Constrain point XY in bounding box.
2325 * \param	p		point XY
2326 * \param	b0		bounding box XY
2327 * \param	b1		bounding box XY
2328 */
2329BS.utl.xy.constrain = function (p, b0, b1)
2330{
2331	var			t;
2332
2333	if (p[0] > (t = Math.max(b0[0], b1[0]))) p[0] = t;
2334	else if (p[0] < (t = Math.min(b0[0], b1[0]))) p[0] = t;
2335
2336	if (p[1] > (t = Math.max(b0[1], b1[1]))) p[1] = t;
2337	else if (p[1] < (t = Math.min(b0[1], b1[1]))) p[1] = t;
2338
2339	return p;
2340};
2341
2342BS.utl.ieHoverNav = function (id) 
2343{
2344	if (!_bsBrowser.isIE) return;
2345	_bsMm.oNew(BS.com.HoverMenuUplifter, id);
2346};
2347
2348BS.com.HoverMenuUplifter = function (id)
2349{
2350	this.mm = new BS.com.Delete();
2351
2352	this.rootUL = _bsDom.get(id);
2353
2354	if (!this.rootUL) return;
2355
2356	this.mEvt = this.mm.ceNew('mouse', this);
2357	this.mEvt.addCB(this._mouseCB);
2358	this.uplift();
2359};
2360
2361BS.com.HoverMenuUplifter.prototype = {
2362	HOVER_CLASS: 'ieHover',
2363	
2364	_delete: function ()
2365	{
2366		this.rootUL = null;
2367		this.mm._delete();
2368	},
2369
2370	uplift: function ()
2371	{
2372		var el = this.rootUL.getElementsByTagName('li');
2373		delete this.cArr; this.cArr = [];
2374
2375		for (var i=0, l=el.length; i<l; i++) {
2376			this.cArr[i] = '';
2377			if (empty(el[i].className)) continue;
2378
2379			if (el[i].className != '')
2380				this.cArr[i] = '_' + el[i].className;
2381			this.mEvt.attach(el[i], 'mouseover', i);
2382			this.mEvt.attach(el[i], 'mouseout', i);
2383		}
2384	},
2385
2386	_mouseCB: function (type, data, args)
2387	{
2388		var e = args[0];
2389		var elindx = args[1];
2390		var type = e.type;
2391		var target = _bsEvt.getTarget(e);
2392		var hc = this.HOVER_CLASS;
2393
2394		var node = _bsDom.parentByNodeName(target, 'LI');
2395
2396		switch(type) {
2397		case 'mouseover':
2398			if (node.className == '' /*!_bsDom.hasClass(node, hc)*/) {
2399				var pNode = _bsDom.parentByNodeName(node.parentNode, 'LI');
2400				//log(node.className + ' ' + node.tagName);
2401				_bsDom.addClass(pNode, hc);
2402			} else
2403				_bsDom.addClass(node, hc + this.cArr[elindx]);
2404			_bsDom.addClass(node, hc);
2405			break;
2406		case 'mouseout':
2407			_bsDom.delClass(node, hc + this.cArr[elindx]);
2408			_bsDom.delClass(node, hc);
2409			if (node.className == '' /*!_bsDom.hasClass(node, hc)*/) {
2410				node = _bsDom.parentByNodeName(node.parentNode, 'LI');
2411				_bsDom.delClass(node,  hc);
2412			}
2413			break;		
2414		}
2415
2416		_bsEvt.stopPropagation(e);
2417	}
2418};
2419
2420BS.utl.window = new BS.com.Window;
2421
2422// shortcuts
2423_bsArr = BS.utl.array;
2424_bsBrowser = BS.utl.browser;
2425_bsCookie = BS.utl.cookie;
2426_bsDom = BS.utl.dom;
2427_bsEvt = BS.utl.event;
2428_bsForm = BS.utl.form;
2429_bsGlobalEvt = BS.utl.globalEvent;
2430_bsMm = BS.utl.mm;
2431_bsMouse = BS.utl.mouse;
2432_bsObj = BS.utl.object;
2433_bsStr = BS.utl.string;
2434_bsWin = BS.utl.window;
2435_bsXy = BS.utl.xy;
2436BS.com.Prompt = function (obj, parent, className, zindex)
2437{
2438	this.mm = new BS.com.Delete;
2439
2440	this.parent = (parent) ? parent : document.body;
2441	this.wobj = (obj) ?
2442		obj : this.mm.domNew(this.parent, 'div', 'BsPrompt');
2443
2444	this.obj =  this.mm.domNew(this.wobj, 'div', null);
2445
2446	if (className) _bsDom.addClass(this.wobj, className);
2447
2448	_bsDom.setStyle(this.wobj, 'position', 'absolute');
2449	_bsDom.setStyle(this.wobj, 'visibility', 'hidden');
2450	_bsDom.setStyle(this.wobj, 'zIndex', (zindex) ? zindex : '5000');
2451	_bsDom.setStyle(this.wobj, 'display', 'block');
2452	_bsDom.setStyle(this.obj, 'overflow', 'auto');
2453};
2454
2455BS.com.Prompt.prototype = {
2456	_delete: function () { this.mm._delete(); },
2457
2458	setPageOfs: function (pos)
2459	{
2460		_bsDom.setPageOfs(this.wobj, pos);
2461	},
2462
2463	setObjOfs: function (obj, ofs)
2464	{
2465		if (!obj) return;
2466		var pos = _bsDom.pageOfs(obj);
2467		_bsDom.setPageOfs(this.wobj, _bsXy.add(pos, ofs));
2468	},
2469
2470	clientCenter: function (min) { _bsDom.clientCenter(this.wobj, min); },
2471
2472	_resize: function () { this.clientCenter([25,25]); },
2473
2474	show: function (mode)
2475	{
2476		_bsDom.setVis(this.wobj, mode);
2477		_bsDom.setDisplay(this.wobj, mode);
2478		if (mode) {
2479			this._resize();
2480			_bsGlobalEvt.addCB('resize', this._resize, this);
2481		} else {
2482			_bsGlobalEvt.delCB('resize', this._resize, this);
2483		}
2484	}
2485};
2486
2487BS.com.BusyPrompt = function (title, msg, mask)
2488{
2489	BS.com.Prompt.call(this, null, null, 'BsBusyPrompt', '8000');
2490	this.mask = (mask) ? mask : this.mm.oNew(BS.com.Mask, null, 0.25, '7999');
2491
2492	this.titleH = this.mm.domNew(this.obj, 'h1');
2493	this.msgDIV = this.mm.domNew(this.obj, 'div', 'bsBpMsg');
2494	this.titleH.innerHTML = title;
2495	this.msgDIV.innerHTML = msg;
2496};
2497
2498BS.com.BusyPrompt.prototype = {
2499	_delete: function ()
2500	{
2501		BS.com.Prompt.prototype._delete.call(this);
2502		this.mask = null;
2503	},
2504
2505	show: function (mode)
2506	{
2507		this.mask.show(mode);
2508		BS.com.Prompt.prototype.show.call(this, mode);
2509
2510		if (mode) 
2511			_bsDom.clientCenter(this.wobj);
2512	}
2513};
2514
2515if (BS.com.Prompt) _bsObj.inherit(BS.com.Prompt, BS.com.BusyPrompt);
2516
2517BS.com.Balloon = function (delay, fade)
2518{
2519	this.mm = new BS.com.Delete('BsBalloon');
2520	this.delay = isset(delay) ? delay : 500;
2521
2522	this.f_fade = fade ? true : false;
2523
2524	this.obj = this.mm.domNew(document.body, 'div');
2525	with (_bsDom) {
2526		setPageOfs(this.obj, [0,0]);
2527		setStyle(this.obj, 'visibility', 'hidden');
2528	}
2529
2530	this.tailTopDIV = this.mm.domNew(this.obj, 'div');
2531	_bsDom.setStyle(this.tailTopDIV, 'display', 'none');
2532
2533	this.bodyDIV = this.mm.domNew(this.obj, 'div', 'bsbBody');
2534
2535	this.tailBottomDIV = this.mm.domNew(this.obj, 'div', 'downleft');
2536
2537	this.stayE = this.mm.ceNew('stay', this);
2538	this.stayE.addCB(this.hoverE);
2539	this.stayE.attach(this.obj, 'mouseover');
2540	this.stayE.attach(this.obj, 'mouseout');
2541
2542	this.hovE = this.mm.ceNew('hov', this);
2543	this.hovE.addCB(this.hoverE);
2544
2545	if (this.f_fade) {
2546		this.fxCE = this.mm.ceNew('animate', this);
2547		this.fxCE.addCB(this._setVis);
2548		
2549		this.fx = this.mm.oNew(BS.com.Animate, this.obj);
2550		this.fx.setCE(this.fxCE);
2551
2552		_bsDom.setStyle(this.obj, 'opacity', 0);
2553	}
2554
2555	this.htmlA = [];
2556	this.relObjA = [];
2557	this.widthA = [];
2558	this.objA = [];
2559};
2560
2561BS.com.Balloon.prototype = {
2562	f_show: 	false,
2563	f_stat:		false,
2564	f_fade:		false,
2565	f_enabled:	true,
2566
2567	_delete: function ()
2568	{
2569		this.stayE.detach(this.obj, 'mouseover');
2570		this.stayE.detach(this.obj, 'mouseout');
2571		
2572		for (var i in this.objA) {
2573			this.hovE.detach(this.objA[i], 'mouseover');
2574			this.hovE.detach(this.objA[i], 'mouseout');
2575			// this.hovE.detach(this.objA[i], 'mousedown');
2576		}
2577		
2578		delete this.relObjA;
2579		delete this.htmlA;
2580		delete this.widthA;
2581		delete this.objA;
2582		this.mm._delete();
2583	},
2584
2585	hoverE: function (type, data, args)
2586	{
2587		var evt = args[0];
2588
2589		switch (evt.type) {
2590		case 'mouseover':			
2591			if (!this.f_enabled) {
2592				if (this.f_show) {
2593					this.f_show = false;
2594					this._show();
2595				}
2596				return;
2597			}
2598			this.f_show = true;
2599			var caller = _bsEvt.getTarget(evt);
2600			
2601			if (type == 'hov' && 
2602			   (!this.lastPos || this.bodyDIV.innerHTML != this.htmlA[caller.id])) {
2603				if (this.lastPos) this.f_stat = false;
2604				_bsDom.setStyle(this.obj, 'width', this.widthA[caller.id] + 'px');
2605				this.bodyDIV.innerHTML = this.htmlA[caller.id];
2606				this.lastPos = this.relObjA[caller.id] ? 
2607				   _bsDom.pageOfs(caller) : BS.utl.mouse.ofs(evt);
2608
2609				var mult = this.relObjA[caller.id] ? .5 : 0;
2610
2611				this.lastPos[0] += mult*caller.offsetWidth;
2612
2613				this._setArrow('down');
2614				this._setArrow('left');
2615
2616				if (this.lastPos[1] - _bsDom.scrollOfs()[1]
2617					- this.obj.offsetHeight > 0) {
2618					this.lastPos[1] -= this.obj.offsetHeight;
2619				} else {
2620					if (this.relObjA[caller.id])
2621						this.lastPos[1] += caller.offsetHeight;
2622					this._setArrow('up');
2623				}				
2624
2625				if (_bsDom.clientSize()[0] +
2626				    _bsDom.scrollOfs()[0] - 
2627				    this.lastPos[0] - this.obj.offsetWidth < 0) {
2628					if (this.lastPos[0] - this.obj.offsetWidth >= 0) {
2629						this.lastPos[0] -= this.obj.offsetWidth;
2630						this._setArrow('right');
2631					}
2632				}				
2633			}
2634			_bsEvt.setTimeout(this.delay, this._show, this);
2635			break;
2636		case 'mouseout':
2637			this.f_show = false;
2638			_bsEvt.setTimeout(50, this._show, this);
2639			break;
2640		case 'mousedown':
2641			this.f_show = false;
2642			this._show();
2643			break;
2644		}
2645	},
2646
2647	attach: function (obj, html, width, f_relObj)
2648	{
2649		if (!obj) return;
2650		if (!obj.id) obj.id = _bsDom.mkid();
2651		
2652		this.relObjA[obj.id] = f_relObj; 
2653		this.htmlA[obj.id] = html;
2654		this.widthA[obj.id] = isset(width) ? width : 158;
2655		this.objA[obj.id] = obj;
2656		
2657		this.hovE.attach(obj, 'mouseover');
2658		this.hovE.attach(obj, 'mouseout');
2659		// this.hovE.attach(obj, 'mousedown');
2660	},
2661
2662	setEnabled: function (enabled) { this.f_enabled = enabled; },
2663
2664	setStyle: function (prop, val) { _bsDom.setStyle(this.obj, prop, val); },
2665
2666	setContent: function (html, id) { this.htmlA[id] = html; },
2667
2668	_setArrow: function (direction)
2669	{
2670		switch (direction) {
2671		case 'left':
2672			_bsDom.delClass(this.tailTopDIV, 'upright');
2673			_bsDom.delClass(this.tailBottomDIV, 'downright');
2674			_bsDom.addClass(this.tailTopDIV, 'upleft');
2675			_bsDom.addClass(this.tailBottomDIV, 'downleft');
2676			break;
2677		case 'right':
2678			_bsDom.delClass(this.tailTopDIV, 'upleft');
2679			_bsDom.delClass(this.tailBottomDIV, 'downleft');
2680			_bsDom.addClass(this.tailTopDIV, 'upright');
2681			_bsDom.addClass(this.tailBottomDIV, 'downright');
2682			break;
2683		case 'down':
2684			_bsDom.setStyle(this.tailTopDIV, 'display', 'none');
2685			_bsDom.setStyle(this.tailBottomDIV, 'display', 'block');
2686			break;
2687		case 'up':
2688			_bsDom.setStyle(this.tailBottomDIV, 'display', 'none');
2689			_bsDom.setStyle(this.tailTopDIV, 'display', 'block');
2690			break;
2691		}
2692	},
2693
2694	_show: function ()
2695	{
2696		if (this.f_show) {
2697			if (this.f_stat) return;
2698			_bsDom.setPageOfs(this.obj, this.lastPos);
2699			if (this.f_fade) this.fx.fadein(50);
2700			else this._setVis(null, null, ['start', 'fadein']);
2701			this.f_stat = true;
2702		} else {
2703			if (!this.f_stat) return;
2704			if (this.f_fade) this.fx.fadeout(200);
2705			else this._setVis(null, null, ['end', 'fadeout']);
2706			this.lastPos = null;
2707			this.orgiPos = null;
2708			this.f_stat = false;
2709		}
2710	},
2711
2712	_setVis: function (type, data, args)
2713	{
2714		var state = args[0];
2715		var effect = args[1];
2716		
2717		if (state == 'start' && effect == 'fadein')
2718			_bsDom.setStyle(this.obj, 'visibility', 'visible');
2719		else if (state == 'end' && effect == 'fadeout')
2720			_bsDom.setStyle(this.obj, 'visibility', 'hidden');
2721	}
2722};
2723
2724BS.com.Mask = function (parent, opacity, zindex, color)
2725{
2726	var		isIE = _bsBrowser.isIE;
2727
2728	if (!zindex) zindex = '1000';
2729	if (!color) color = '#000';
2730	if (!opacity) opacity = 0.4;
2731
2732	this.mm = new BS.com.Delete('Mask');
2733	this.parent = (parent) ? parent : document.body;
2734
2735	if (isIE) {
2736		this.obj = this.mm.domNew(null, 'iframe', 'BsMask');
2737		this.obj.src = '/com/mask?color=' + _bsStr.urlencode(color);
2738		document.body.appendChild(this.obj);
2739	} else this.obj = this.mm.domNew(document.body, 'div', 'BsMask');
2740
2741	_bsDom.setStyle(this.obj, 'position', 'absolute');
2742	_bsDom.setStyle(this.obj, 'visibility', 'hidden');
2743	_bsDom.setStyle(this.obj, 'zIndex', zindex);
2744	_bsDom.setStyle(this.obj, 'opacity', opacity);
2745
2746	if (!isIE) {
2747		_bsDom.setStyle(this.obj, 'backgroundColor', color);
2748		this.obj.innerHTML = '&nbsp;';
2749	}
2750
2751	// weird firefox bug! setting the 'height' style in _resize()
2752	// causes excessive CPU usage in firefox... should not call
2753	// _resize() in the constructor.
2754	// this._resize();
2755};
2756
2757BS.com.Mask.prototype = {
2758	f_show: 	false,	
2759
2760	_delete: function ()
2761	{
2762		this.mm._delete();
2763		this.parent = null;
2764	},
2765
2766	_resize: function ()
2767	{
2768		var		pos, size;
2769
2770		if (this.parent == document.body) {
2771			size = _bsDom.docSize();
2772			pos = [0,0];
2773		}
2774		else {
2775			size = _bsDom.size(this.parent);
2776			pos = _bsDom.pageOfs(this.parent);
2777			
2778		}
2779
2780		if (isNaN(size[0]) || isNaN(size[1]) || !pos) return;
2781
2782		_bsDom.setPageOfs(this.obj, pos);
2783		_bsDom.setSize(this.obj, size);
2784	},
2785
2786	show: function (mode)
2787	{
2788		if (mode == this.f_show) return;
2789		this.f_show = mode;
2790
2791		if (mode) {
2792			this._resize();
2793			_bsGlobalEvt.addCB('resize', this._resize, this,
2794						 true);
2795		}
2796		else {
2797			_bsDom.setStyle(this.obj, 'height', '1px');
2798			_bsGlobalEvt.delCB('resize', this._resize, this);
2799		}
2800
2801		_bsDom.setStyle(this.obj, 'visibility',
2802				    mode ? 'visible' : 'hidden');
2803	}
2804};
2805
2806BS.com.Alert = function ()
2807{
2808	BS.com.Prompt.call(this, null, null, 'BsAlert', '9999');
2809
2810	this.mask = _bsMm.oNew(BS.com.Mask, null, 0.3, '9998');
2811
2812	this.msgA = [];
2813
2814	this.titleH = this.mm.domNew(this.obj, 'h1');
2815	this.bodyDIV = this.mm.domNew(this.obj, 'div', 'body');
2816	this.footerDIV = this.mm.domNew(this.obj, 'div', 'footer')
2817	this.okINP = this.mm.domNew(null, 'input', 'button')
2818	this.okINP.setAttribute('type', 'button');
2819	this.okINP.value = "   OK   ";
2820	this.footerDIV.appendChild(this.okINP);
2821
2822	this.okE = _bsMm.ceNew('ok', this);
2823	this.okE.addCB(this._clear, this);
2824	this.okE.attach(this.okINP, 'click');
2825};
2826
2827BS.com.Alert.prototype = {
2828	f_show:		false,
2829	msgA:		null,
2830	ce:		null,
2831	DEBUG:		false,
2832
2833	_delete: function ()
2834	{
2835		BS.com.Prompt.prototype._delete.call(this);
2836		this.ce = null;
2837		delete this.msgA;
2838	},
2839
2840	_closeWin: function (type, data, args)
2841	{
2842		window.close();
2843	},
2844
2845	_clear: function (type, data, args)
2846	{
2847		if (!this.f_show) return;
2848		this.f_show = false;
2849		
2850		_bsGlobalEvt.delCB('resize', this._resize, this);
2851		this.bodyDIV.innerHTML = this.titleH.innerHTML = '';
2852		BS.com.Prompt.prototype.show.call(this, false);
2853		
2854		if (this.msgA.length) {
2855			_bsEvt.setTimeout(100, this._showA, this);
2856			return;
2857		}
2858
2859		if (this.ce)
2860			if (!this.ce.exec()) return;
2861
2862		this.ce = null;
2863		this.mask.show(false);
2864	},
2865
2866	_showA: function (type, data, args)
2867	{
2868		if (!this.msgA.length) return;
2869		var msg = this.msgA.shift();
2870		this.show(msg[0], msg[1], msg[2])
2871	},
2872
2873	_focus: function () { this.okINP.focus(); },
2874
2875	close: function (clearCe)
2876	{
2877		if (clearCe) this.ce = null;
2878		this._clear();
2879	},
2880
2881	setCloseWin: function (flag)
2882	{
2883		if (flag) this.okE.addCB(this._closeWin, this);
2884		else this.okE.delCB(this._closeWin);
2885	},
2886
2887	show: function (title, html, ce)
2888	{
2889		// check if a "false" error was thrown - 
2890		// e.g. used by PS.utl.responseErr()
2891		if (html === false) return;
2892
2893		if (this.f_show) {
2894			this.msgA.push([title, html, ce]);
2895			_bsEvt.setTimeout(0, this._focus, this);
2896			return;
2897		}
2898		this.f_show = true;
2899
2900		if (ce) this.ce = ce;
2901		this.mask.show(true);
2902		if (title) this.titleH.innerHTML = title;
2903		if (html) {
2904			if (typeof(html) == 'object' && html.length) {
2905				var str = '<ul>';
2906				for (var i = 0; i < html.length; i++)
2907					str += '<li>' + html[i] + '</li>';
2908				str += '</ul>';
2909				this.bodyDIV.innerHTML = str;
2910			
2911			}
2912			else this.bodyDIV.innerHTML = html;
2913
2914			if (this.DEBUG && html.stack)
2915				this.bodyDIV.innerHTML += '\n' + html.stack;
2916		}
2917
2918		BS.com.Prompt.prototype.show.call(this, true);
2919		_bsEvt.setTimeout(0, this._focus, this);
2920	}
2921};
2922
2923_bsObj.inherit(BS.com.Prompt, BS.com.Alert);
2924
2925_bsGlobalEvt.addCB('load',
2926	function () { _bsAlert = BS.utl.alert = _bsMm.oNew(BS.com.Alert); });
2927
2928// shortcuts
2929var _bsAlert;

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.