PageSourceSearch

http://free.naplesplus.us/shared/yacs.js

js naplesplus.us collected 2026-10-02 12:05:56 UTC 36,361 bytes, 1,283 lines download raw bytes

1/**
2 * The YACS AJAX shared library.
3 *
4 * This file extends prototype, etc., to enhance interactions with the end-user
5 *
6 * @author Bernard Paques [email][email protected][/email]
7 * @reference
8 * @license http://www.gnu.org/copyleft/lesser.txt GNU Lesser General Public License
9 */
10
11var startWatch = new Date();
12
13var Yacs = {
14
15	/**
16	 * alert the surfer
17	 *
18	 * @param message to display
19	 * @param function to be called on click, if any
20	 */
21	alert: function(message, callBack) {
22
23		Yacs.displayModalBox({ body: '<div style="text-align: left">'+message.replace(/\n/gi, '<br />')+'</div>',
24			button_TRUE: "OK" }, callBack);
25
26	},
27
28	/**
29	 * reflect last sort in table
30	 *
31	 * @param target table
32	 * @param sorted column
33	 */
34	beautifyTable: function(tblEl, col) {
35
36		// look for class names in styles
37		var rowTest = new RegExp("odd", "gi");
38		var colTest = new RegExp("sorted", "gi");
39
40		// alternate row appearance
41		var i, j;
42		var rowEl, cellEl;
43
44		for (i = 0; i < tblEl.rows.length; i++) {
45			rowEl = tblEl.rows[i];
46			Element.removeClassName(rowEl, 'odd');
47			if((i % 2) !== 0) {
48				Element.addClassName(rowEl, 'odd');
49			}
50
51			// set style classes on each column
52			for (j = 2; j < tblEl.rows[i].cells.length; j++) {
53				cellEl = rowEl.cells[j];
54				Element.removeClassName(cellEl, 'sorted');
55				// highlight the one that was sorted
56				if (j == col) {
57					Element.addClassName(cellEl, 'sorted');
58				}
59			}
60		}
61
62		// find the table header and highlight the column that was sorted
63		var el = tblEl.parentNode.tHead;
64		rowEl = el.rows[el.rows.length - 1];
65
66		// set style classes for each column
67		for (i = 0; i < rowEl.cells.length; i++) {
68			cellEl = rowEl.cells[i];
69			Element.removeClassName(cellEl, 'sorted');
70
71			// highlight the header of the sorted column
72			if (i == col) {
73				Element.addClassName(cellEl, 'sorted');
74			}
75		}
76	},
77
78	/**
79	 * remote procedure call based on JSON-RPC
80	 *
81	 */
82	call: function(parameters, callBack) {
83
84		// hash encoding to JSON
85		parameters = Object.toJSON(parameters);
86		
87		// start an ajax transaction
88		var handle = new Ajax.Request(url_to_root + 'services/json_rpc.php', {
89			method: 'post',
90			parameters: parameters,
91			requestHeaders: {Accept: 'application/json'},
92			onSuccess: function(transport) {
93				var response = transport.responseText.evalJSON(true);
94				if(response.error) {
95					callBack(FALSE);
96				} else if(response.result) {
97					callBack(response.result);
98				} else {
99					callBack(FALSE);
100				}
101				response = null; // no memory leak
102			},
103			onFailure: function(transport) {
104				callBack(FALSE);
105			}
106		});
107	},
108
109	/**
110	 * clear one cookie
111	 *
112	 * @param cookie name
113	 */
114	clearCookie: function(name) {
115		if(Yacs.getCookie(name)) {
116			document.cookie = name + "=; expires=Thu, 01-Jan-70 00:00:01 GMT";
117		}
118	},
119
120	/**
121	 * trigger the show
122	 */
123	clickImage: function() {
124		Yacs.showImage(this);
125		return false; 
126	},
127	
128	/**
129	 * close the modal box
130	 */
131	closeModalBox: function() {
132
133		var handle = new Effect.Opacity('modal_content', {duration:0.3, from:1.0, to:0.3, queue: 'end',
134			afterFinish: function(target) {
135
136				// clear the content
137				Element.update('modal_content', '');
138
139				// mask the modal box
140				Element.setStyle('modal_panel', { display: 'none' });
141
142			} });
143	},
144
145	/**
146	 * ask for confirmation
147	 *
148	 * @param message to display
149	 * @param callBack the function to call when the OK button is cliqued
150	 */
151	confirm: function(message, callBack) {
152
153		Yacs.displayModalBox({ body: '<div style="text-align: left">
153'+message.replace(/\n/gi, '<br />')+'</div>',
154			button_TRUE: "OK", button_FALSE: "Cancel" }, callBack);
155
156	},
157
158	/**
159	 * compare two values
160	 *
161	 * @param first value
162	 * @param second value
163	 * @return comparison value
164	 */
165	compareValues: function(v1, v2) {
166
167		// if the values are numeric, convert them to floats
168		var f1 = parseFloat(v1);
169		var f2 = parseFloat(v2);
170		if(!isNaN(f1) && !isNaN(f2)) {
171			v1 = f1;
172			v2 = f2;
173		}
174
175		// compare the two values
176		if(v1 == v2) {
177			return 0;
178		}
179		if(v1 > v2) {
180			return 1;
181		}
182		return -1;
183	},
184
185	/**
186	 * detect Flash on client-side
187	 *
188	 * The script used to detect Flash under Firefox, etc. This is complemented
189	 * by server-side detection for IE.
190	 *
191	 * This function also save findings in a cookie for transmission to server.
192	 *
193	 * @return 'yes' or 'no'
194	 */
195	detectFlash: function () {
196
197		var detected = Yacs.getCookie("FlashIsAvailable");
198
199		// cookie has already been set
200		if(detected) {
201			return detected;
202		}
203
204		// detect in plugins
205		if((navigator.plugins !== null) && (navigator.plugins.length > 0)) {
206			if(navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]) {
207				detected = 'yes';
208			}
209		} else if(navigator.userAgent.toLowerCase().indexOf("webtv") != -1) {
210				detected = 'yes';
211		}
212
213		// remember this in a cookie
214		Yacs.setCookie("FlashIsAvailable", detected);
215
216		// return the result, if useful
217		return detected;
218
219	},
220
221	/**
222	 * compute and record surfer time zone
223	 *
224	 * This function computes the surfer time zone, and save it in a cookie to
225	 * share this with the back-end
226	 *
227	 * @return the time zone
228	 */
229	detectTimeZone: function () {
230
231		var timeZone = Yacs.getCookie("TimeZone");
232
233		// cookie has already been set
234		if(timeZone) {
235			return timeZone;
236		}
237
238		// compute time shift
239		var now = new Date();
240		timeZone = (-now.getTimezoneOffset() /60);
241
242		// remember this in a cookie
243		Yacs.setCookie("TimeZone", timeZone);
244
245		// return the result, if useful
246		return timeZone;
247
248	},
249
250	/**
251	 * display a modal box
252	 *
253	 * @param string XHTML content of the box
254	 * @param function to be called on OK or Cancel
255	 *
256	 * @see tools/ajax.php
257	 */
258	displayModalBox: function(content, callBack) {
259
260		// prepare box actual content
261		var boxContent = '';
262
263		if(content.title) {
264			boxContent += '<h2 class="boxTitle">'+content.title+'</h2>';
265		}
266		if(content.body) {
267			boxContent += '<div class="boxBody">'+content.body+'</div>';
268		}
269
270		// handle key press
271		var keyCodes = [];
272
273		// all buttons
274		boxContent += '<div class="boxButtons">';
275		if(content.button_PREVIOUS) {
276			if(typeof Yacs.modalCallPrevious == "function") {
277				boxContent += '<span class="button"><button type="button" onclick="(Yacs.modalCallPrevious)();"><div style="padding: 0 1em">'+content.button_PREVIOUS+'</div></button></span> ';
278			} else {
279				boxContent += '<span class="button"><button type="button" disabled="disabled"><div style="padding: 0 1em">'+content.button_PREVIOUS+'</div></button></span> ';
280			}
281		}
282		if(content.button_TRUE) {
283			boxContent += '<span class="button"><button type="button" onclick="if(typeof Yacs.modalCallBack == &quot;function&quot;) { (Yacs.modalCallBack)(true) }; Yacs.closeModalBox();"><div style="padding: 0 1em">'+content.button_TRUE+'</div></button></span> ';
284		}
285		if(content.button_FALSE) {
286			boxContent += ' &nbsp; <span class="button"><button type="button" onclick="if(typeof Yacs.modalCallBack == &quot;function&quot;) { (Yacs.modalCallBack)(false) }; Yacs.closeModalBox();">'+content.button_FALSE+'</button></span> ';
287		}
288		if(content.button_NEXT) {
289			if(typeof Yacs.modalCallNext == "function") {
290				boxContent += '<span class="button"><button type="button" onclick="(Yacs.modalCallNext)();"><div style="padding: 0 1em">'+content.button_NEXT+'</div></button></span>';
291			} else {
292				boxContent += '<span class="button"><button type="button" disabled="disabled"><div style="padding: 0 1em">'+content.button_NEXT+'</div></button></span>';
293			}
294		}
295		boxContent += '</div>';
296
297		// wait for the update
298		if(typeof callBack == "function") {
299			Yacs.modalCallBack = callBack;
300		} else {
301			Yacs.modalCallBack = null;
302		}
303
304		// insert some html at the bottom of the page that looks similar to this:
305		//
306		// <div id="modal_panel">
307		//	<div id="modal_centered">
308		//		<div id="modal_content">
309		//			content, passed as parameter, goes here
310		//		</div>
311		//	</div>
312		// </div>
313
314		// extend the DOM tree
315		if(!Yacs.modalOverlay) {
316
317			var objContent = document.createElement("div");
318			objContent.setAttribute('id','modal_content');
319			Element.update(objContent, '<img src="'+Yacs.spinningImage.src+'" />');
320
321			var objCentered = document.createElement("div");
322			objCentered.setAttribute('id','modal_centered');
323//			objCentered.setStyle({ visibility: 'hidden' });
324			objCentered.appendChild(objContent);
325
326			Yacs.modalOverlay = document.createElement("div");
327			Yacs.modalOverlay.setAttribute('id','modal_panel');
328			Yacs.modalOverlay.onclick = function() {};	// you have to click in the box to close the modal box
329			Yacs.modalOverlay.appendChild(objCentered);
330
331			var objBody = document.getElementsByTagName("body").item(0);
332			objBody.appendChild(Yacs.modalOverlay);
333
334			// just to fix a bug on first image rendering for Internet Explorer 7...
335			var fix = new Effect.MoveBy('modal_centered', 0, 0, {duration: 0.0});
336
337		// ensure containers are visible to compute box size
338		} else {
339			Element.setStyle('modal_panel', { display: 'block' });
340		}
341
342		// paint or repaint box content
343		var handle = new Effect.Opacity('modal_content', {duration:0.1, from:1.0, to:0.3, queue: 'end',
344			afterFinish: function(target) {
345
346				// update the content
347				Yacs.updateModalBox(boxContent);
348
349				// display the updated box
350				Element.setStyle('modal_content', { visibility: 'visible' });
351				var opacity = new Effect.Opacity('modal_content', {duration:0.3, from:0.5, to:1.0, queue: 'end'});
352
353			} });
354
355
356
357	},
358
359	/**
360	 * get the value of one cookie
361	 *
362	 * @param cookie name
363	 * @return cookie value
364	 */
365	getCookie: function(name) {
366
367		var prefix = name + "=";
368		var begin = document.cookie.indexOf(prefix);
369		if(begin == -1) { return null; }
370		var end = document.cookie.indexOf(";", begin);
371		if(end == -1) { end = document.cookie.length; }
372		return unescape(document.cookie.substring(begin + prefix.length, end));
373
374	},
375
376	/**
377	 * get a sortable string
378	 *
379	 * This function finds and concatenate the values of all text nodes
380	 *
381	 * @param the target DOM element
382	 * @return a sortable string
383	 */
384	getTextValue: function(el) {
385
386		// the returned string
387		var s = "";
388
389		// look at every child node
390		var i;
391		for(i = 0; i < el.childNodes.length; i++) {
392
393			// a text node
394			if(el.childNodes[i].nodeType == 3) {
395				s += el.childNodes[i].nodeValue;
396
397			// an element node which is a break
398			} else if((el.childNodes[i].nodeType == 1) && (el.childNodes[i].tagName == "br")) {
399				s += " ";
400
401			// use recursion to get text within sub-elements
402			} else {
403				s += Yacs.getTextValue(el.childNodes[i]);
404			}
405
406		}
407
408		return s;
409	},
410
411	/**
412	 * we have received an 'alert' notification
413	 *
414	 * @see users/notifications.php
415	 */
416	handleAlertNotification: function(response) {
417
418		// build a message in English if no localized message has been provided by the server
419		if(typeof response.dialog_text != 'string') {
420			response.dialog_text = 'New at ' + response.title + "\nby: " + response.nick_name + "\n" + 'Would you like to browse this page?';
421		}
422
423		// switch to the offered address, if accepted by surfer
424		if(typeof response.address == 'string') {
425			Yacs.confirm(response.dialog_text, function(choice) { if(choice) {window.open(response.address);} });
426		}
427	},
428
429	/**
430	 * we have received a 'browse' notification
431	 *
432	 * @see users/notifications.php
433	 */
434	handleBrowseNotification: function(response) {
435
436		// build a message in English if no localized message has been provided by the server
437		if(typeof response.dialog_text != 'string') {
438			response.dialog_text = 'From ' + response.nick_name + ":\n" + response.message + "\n" + 'Would you like to browse the provided link?';
439		}
440
441		// switch to the offered address, if already in tracker, or if accepted by surfer
442		if(typeof response.address == 'string') {
443			if((window.name == 'yacs_tracker')) {
444				window.open(response.address, 'yacs_tracker');
445			} else {
446				Yacs.confirm(response.dialog_text, function(choice) { if(choice) {window.open(response.address, 'yacs_tracker');} });
447			}
448		}
449	},
450
451	/**
452	 * we have received a 'hello' notification
453	 *
454	 * @see users/notifications.php
455	 */
456	handleHelloNotification: function(response) {
457
458		// build a message in English if no localized message has been provided by the server
459		if(typeof response.dialog_text != 'string') {
460			response.dialog_text = 'From ' + response.nick_name + ":\n" + response.message;
461
462			if(typeof response.address == 'string') {
463				response.dialog_text += "\n" + 'Would you like to chat with this person?';
464			}
465		}
466
467		// only show the message to the end-user
468		if(typeof response.address != 'string') {
469			Yacs.alert(response.dialog_text);
470
471		// else switch to the offered address, if accepted by surfer
472		} else {
473			Yacs.confirm(response.dialog_text, function(choice) { if(choice) {window.open(response.address);} });
474		}
475	},
476
477	/**
478	 * prepare for mouse hovering
479	 */
480	addOnDemandTools: function(handle, option) {
481		handle = $(handle);
482
483		var prefix = '';
484		if(handle.hasClassName('sortable')) {
485			prefix += '<span class="onHoverLeft drag_handle"><img src="'+url_to_root+'skins/_reference/on_demand_handle.png" width="16" height="16" alt="Drag" /></span>';
486		}
487		var suffix = '<span class="onHoverRight">';
488		if(handle.hasClassName('mutable')) {
489			suffix += '<a href="#" onclick="Yacs.toggleProperties(\''+handle.identify()+'\'); return false;"><img src="'+url_to_root+'skins/_reference/on_demand_properties.png" width="16" height="16" alt="Properties" /></a>';
490		}
491		suffix += '<a href="#" onclick="Element.remove(\''+handle.identify()+'\'); return false;"><img src="'+url_to_root+'skins/_reference/on_demand_delete.png" width="16" height="16" alt="Delete" /></a></span>';
492		var here = new Element.insert(handle, { top: suffix + prefix });
493
494		handle.onmouseout = function () { Yacs.mouseOut(this); return false; };
495		handle.onmouseover = function () { Yacs.mouseOver(this); return false; };
496
497		handle = null; // no memory leak
498
499	},
500
501	/**
502	 * mouse is moving elsewhere
503	 */
504	mouseOut: function(handle) {
505		var nodes = handle.select('span.onHoverLeft', 'span.onHoverRight');
506		nodes.each(function (node) { Element.setStyle(node, { visibility: 'hidden' }); });
507
508		nodes = null; // no memory leak
509	},
510
511	/**
512	 * mouse is coming on top of some element
513	 */
514	mouseOver: function(handle) {
515		var nodes = handle.select('span.onHoverLeft', 'span.onHoverRight');
516		nodes.each(function (node) { Element.setStyle(node, { visibility: 'visible' }); });
517
518		nodes = null; // no memory leak
519	},
520
521	toggleProperties: function(handle) {
522		var nodes = $(handle).select('.properties');
523		nodes.each(function (node) { var handle = new Effect.toggle(node, 'slide'); });
524
525		nodes = null; // no memory leak
526	},
527
528	/**
529	 * general initialization
530	 */
531	onWindowLoad: function() {
532
533		// resize according to surfer preferences
534		Yacs.textSize();
535
536		// this window has the focus
537		Yacs.hasFocus = true;
538
539		// compute and record surfer time zone
540		Yacs.detectTimeZone();
541
542		// detect Flash on client side
543		Yacs.detectFlash();
544
545		// pre-load the spinning image used during ajax updates
546		Yacs.spinningImage = new Image();
547		Yacs.spinningImage.src = url_to_root + 'skins/_reference/ajax_spinner.gif';
548
549		// pre-load the image used at the working overlay
550		Yacs.workingImage = new Image();
551		Yacs.workingImage.src = url_to_root + 'skins/_reference/ajax_working.gif';
552
553		// change the behavior of buttons used for data submission, except those with style 'no_spin_on_click'
554		var buttons = $$('button');
555		for(var index = 0; index < buttons.length; index++) {
556			var button = buttons[index];
557			var buttonType = String(button.getAttribute('type'));
558			if(buttonType.toLowerCase().match('submit') && !Element.hasClassName(button, 'no_spin_on_click')) {
559				button.onclick = Yacs.startWorking;
560			}
561
562			button = null; // no memory leak
563		}
564
565		// on-demand headers
566		var nodes = $$('.onDemandTools');
567		for(index = 0; index < nodes.length; index++) {
568			var node = nodes[index];
569			Yacs.addOnDemandTools(node, { });
570		}
571
572		// prepare for a nice slideshow
573		var anchors = $$('a.image_show');
574		for(index = 0; index < anchors.length; index++) {
575			var anchor = anchors[index];
576			anchor.onclick = Yacs.clickImage;
577			if(index > 0) {
578				anchor.previousAnchor = anchors[index - 1];
579			}
580			if(index+1 < anchors.length) {
581				anchor.nextAnchor = anchors[index + 1];
582			}
583
584			anchor = null; // no memory leak
585		}
586
587		// slow down notifications on window blur
588		Event.observe(window, 'blur', Yacs.looseFocus);
589
590		// back to normal rate on focus
591		Event.observe(window, 'focus', Yacs.getFocus);
592
593		// check for asynchronous notifications
594		setTimeout(Yacs.subscribe, 40000);
595	},
596
597	getFocus: function() {
598		Yacs.hasFocus = true;
599	},
600
601	looseFocus: function() {
602		Yacs.hasFocus = false;
603	},
604
605	/**
606	 * open a popup window
607	 */
608	popup: function(options) {
609
610		// default options
611		this.options = {
612			url: '#',
613			width: 600,
614			height: 500,
615			name:"_blank",
616			location:"no",
617			menubar:"no",
618			toolbar:"no",
619			status:"yes",
620			scrollbars:"yes",
621			resizable:"yes",
622			left:"",
623			top:"",
624			normal:false
625		};
626
627		// use provided options, if any
628		Object.extend(this.options, options || {});
629
630		// sanity check
631		if(this.options.normal) {
632			this.options.menubar = "yes";
633			this.options.status = "yes";
634			this.options.toolbar = "yes";
635			this.options.location = "yes";
636		}
637
638		// some computations
639		this.options.width = (this.options.width < screen.availWidth?this.options.width:screen.availWidth);
640		this.options.height= (this.options.height < screen.availHeight?this.options.height:screen.availHeight);
641		var openoptions = 'width='+this.options.width+',height='+this.options.height+',location='+this.options.location+',menubar='+this.options.menubar+',toolbar='+this.options.toolbar+',scrollbars='+this.options.scrollbars+',resizable='+this.options.resizable+',status='+this.options.status;
642		if(this.options.top !== '') { openoptions+=",top="+this.options.top; }
643		if(this.options.left !== '') { openoptions+=",left="+this.options.left; }
644
645		// open the popup
646		var window_handle;
647		window_handle = window.open(this.options.url, this.options.name, openoptions);
648
649		// add content, if option allows to do so
650		if(window_handle && this.options.content && (this.options.url == '#')) {
651			window_handle.document.write(this.options.content);
652			window_handle.document.close();
653		}
654
655		// focus on the new window
656		if(window_handle && window_handle.focus) {
657			window_handle.focus();
658		}
659
660		// allow for additional handling of this window
661		return window_handle;
662	},
663
664	/**
665	 * set a new cookie
666	 *
667	 * @param cookie name
668	 * @param cookie value
669	 * @param days before expiration (optional)
670	 */
671	setCookie: function(name, value) {
672
673		var argv = arguments;
674		var argc = arguments.length;
675		var expires = (argc > 2) ? argv[2] : null;
676		if(expires > 0) {
677			var today = new Date();
678			var future = today.getTime() + 3600000*24*expires;
679			expires = new Date();
680			expires.setTime(future);
681		}
682		var path = (argc > 3) ? argv[3] : '/';
683		var domain = (argc > 4) ? argv[4] : null;
684		var secure = (argc > 5) ? argv[5] : false;
685		document.cookie = name + "=" + escape (value) +
686			((expires === null) ? "" : ("; expires=" + expires.toGMTString())) +
687			((path === null) ? "" : ("; path=" + path)) +
688			((domain === null) ? "" : ("; domain=" + domain)) +
689			((secure === true) ? "; secure" : "");
690
691	},
692
693	/**
694	 * similar to lightbox, integrated into yacs
695	 */
696	showImage: function(anchor) {
697
698		// load the image in the background
699		var loader = new Image();
700		loader.onload=function(){
701
702			// adjust image size to viewport dimensions
703			var scale = 1.0;
704			if((loader.width > 1) && (loader.width + 30 > document.viewport.getWidth())) {
705				scale = (document.viewport.getWidth() - 30) / loader.width;
706				loader.height*= scale;
707				loader.width *= scale;
708			}
709			if((loader.height > 1) && (loader.height + 110 > document.viewport.getHeight())) { // take title and buttons into account
710				scale = (document.viewport.getHeight() - 110) / loader.height;
711				loader.height*= scale;
712				loader.width *= scale;
713			}
714
715			// difference with previous image
716			var yDelta = 0;
717			if(typeof Yacs.previousImageHeight == 'number') {
718				yDelta = loader.height - Yacs.previousImageHeight;
719			}
720			Yacs.previousImageHeight = loader.height;
721			var xDelta = 0;
722			if(typeof Yacs.previousImageWidth == 'number') {
723				xDelta = loader.width - Yacs.previousImageWidth;
724			}
725			Yacs.previousImageWidth = loader.width;
726
727			// rescale on size change
728			if((yDelta !== 0) && (xDelta !== 0) && $('modal_image_panel')) {
729
730				// previous image -- <div id="modal_image_panel"><img ...
731				var previousImage = $('modal_image_panel').down();
732
733				// current height and width
734				var currentHeight = Element.getHeight(previousImage);
735				var currentWidth = Element.getWidth(previousImage);
736
737				// compute scaling factors
738				var yScale = ((currentHeight + yDelta) / currentHeight) * 100;
739				var xScale = ((currentWidth + xDelta) / currentWidth) * 100;
740
741				// scaling previous image makes ugly things
742				var opacity = new Effect.Opacity('modal_content', {duration:0.1, from:1.0, to:0.0});
743				Element.setStyle('modal_content', { visibility: 'hidden' });
744
745				// adjust the overall size
746				if(yDelta !== 0) {
747					var effect = new Effect.Scale(previousImage, yScale, {scaleX: false, duration: 0.4, queue: 'end'});
748				}
749				if(xDelta !== 0) {
750					var effect2 = new Effect.Scale(previousImage, xScale, {scaleY: false, duration: 0.4, queue: 'end'});
751				}
752			}
753
754			// image title -- <a><span><img title="" ...
755			var imageTitle = Element.firstDescendant(Element.firstDescendant(anchor)).getAttribute('title');
756//			if(!imageTitle)
757//				imageTitle = Element.firstDescendant(Element.firstDescendant(anchor)).getAttribute('alt');
758
759			// image href
760			var imageReference = '<div id="modal_image_panel"><img src="'+anchor.getAttribute('href')+'" width="'+loader.width+'" height="'+loader.height+'" /></div>';
761
762			// a neighbour on the left
763			if(anchor.previousAnchor) {
764				Yacs.modalCallPrevious = function() { Yacs.showImage(anchor.previousAnchor); };
765			} else {
766				Yacs.modalCallPrevious = null;
767			}
768
769			// a neighbour on the right
770			if(anchor.nextAnchor) {
771				Yacs.modalCallNext = function() { Yacs.showImage(anchor.nextAnchor); };
772
773				// do not wait for user click to load the image
774				var nextLoader = new Image();
775				nextLoader.src = anchor.nextAnchor.getAttribute('href');
776
777			} else {
778				Yacs.modalCallNext = null;
779			}
780
781			// display the image on screen
782			Yacs.displayModalBox({ title: imageTitle,
783				body: imageReference,
784				button_PREVIOUS: '<<',
785				button_TRUE: 'X',
786				button_NEXT: '>>' });
787
788			//	clear onLoad, IE behaves erratically with animated gifs otherwise
789			loader.onload = function(){};
790		};
791
792		// actual pre-load
793		loader.src = anchor.getAttribute('href');
794
795	},
796
797	/**
798	 * load some opaque overlay during back-end processing
799	 */
800	startWorking: function() {
801
802		if(Yacs.workingOverlay) {
803			Element.setStyle(Yacs.workingOverlay, { display: 'block' });
804			return true;
805		}
806
807		// insert some html at the bottom of the page that looks similar to this:
808		//
809		// <div id="yacsWorkingOverlay">
810		//	<div>
811		//		<img src="/yacs/skins/_reference/ajax_working.gif"/>
812		//	</div>
813		// </div>
814
815		var objWorkingImage = document.createElement("img");
816		objWorkingImage.setAttribute('src', url_to_root + 'skins/_reference/ajax_working.gif');
817
818		var objCentered = document.createElement("div");
819		Element.setStyle(objCentered, { position: 'absolute', top: '30%', left: '0%', height: '25%', width: '100%', textAlign: 'center', lineHeight: '0' });
820		objCentered.appendChild(objWorkingImage);
821
822		Yacs.workingOverlay = document.createElement("div");
823		Yacs.workingOverlay.setAttribute('id','yacsWorkingOverlay');
824		Element.setStyle(Yacs.workingOverlay, { position: 'fixed', top: '0', left: '0', zIndex: '1000', width: '100%', height: '100%', minHeight: '100%', backgroundColor: '#000', filter: 'alpha(opacity=20)', opacity: '0.2', display: 'block' });
825		Yacs.workingOverlay.onclick = function() { Element.setStyle(Yacs.workingOverlay, { display: 'none' });};
826		Yacs.workingOverlay.appendChild(objCentered);
827
828		var objBody = document.getElementsByTagName("body").item(0);
829		objBody.appendChild(Yacs.workingOverlay);
830
831		return true;
832	},
833
834	/**
835	 * hide the working overlay
836	 */
837	stopWorking: function() {
838
839		var handle = $('yacsWorkingOverlay');
840		if(handle) {
841			Element.setStyle(handle, { display: 'none' });
842		}
843
844	},
845
846	/**
847	 * subscribe to notifications sent by the back-end asynchronously
848	 *
849	 * @see users/heartbit.php
850	 */
851	subscribe: function() {
852
853		// not less than 3 seconds between two successive calls
854		var now = new Date();
855                if((typeof Yacs.subscribeStamp == "object") && (now.getTime() < (Yacs.subscribeStamp.getTime() + 60000))) {
856			return;
857		}
858		Yacs.subscribeStamp = now;
859
860		// a transaction is taking place
861		if(Yacs.subscribeAjax) {
862			return;
863		}
864
865		// clear on-going action, if any
866		if((typeof Yacs.subscribeTimer == "number") && (Yacs.subscribeTimer > 0)) {
867			clearTimeout(Yacs.subscribeTimer);
868			Yacs.subscribeTimer = 0;
869		}
870
871		// start an ajax transaction
872		Yacs.subscribeAjax = new Ajax.Request(url_to_root + 'users/heartbit.php', {
873			method: 'get',
874			parameters: { },
875			requestHeaders: {Accept: 'application/json'},
876			onSuccess: Yacs.subscribeSuccess,
877			onFailure: Yacs.subscribeFailure
878		});
879	},
880
881	subscribeFailure: function(transport) {
882
883		if(Yacs.hasFocus) { // regular idle cycle
884			Yacs.subscribeTimer = setTimeout(Yacs.subscribe, 40000);
885		} else { // don't stress the server when we don't have the focus
886			Yacs.subscribeTimer = setTimeout(Yacs.subscribe, 120000);
887		}
888		Yacs.subscribeAjax = null;
889
890	},
891
892	subscribeSuccess: function(transport) {
893
894		// dispatch received notification
895		var response = transport.responseText.evalJSON(true);
896		switch(response.type) {
897		case 'alert':
898			Yacs.handleAlertNotification(response);
899			break;
900		case 'browse':
901			Yacs.handleBrowseNotification(response);
902			break;
903		case 'hello':
904			Yacs.handleHelloNotification(response);
905			break;
906		}
907
908		// minimum time between two successive notifications
909		Yacs.subscribeTimer = setTimeout(Yacs.subscribe, 20000);
910		Yacs.subscribeAjax = null;
911
912	},
913
914	// remember polling rate
915	subscribeRate: "fast",
916
917	// on-going timer, if any
918	subscribeTimer: 0,
919
920	/**
921	 * sort a table
922	 *
923	 * @param id of the table, tbody or tfoot element to be sorted
924	 * @param index of the column to sort, starting at 0
925	 * @param if true, sort in reverse order
926	 */
927	sortTable: function(id, column, rev) {
928
929		// get the table or table section to sort
930		var handle = document.getElementById(id);
931
932		// on first sort set up an array of reverse sort flags
933		if(!handle.reverseSort || (handle.reverseSort === null)) {
934			handle.reverseSort = [];
935		}
936
937		// set the initial sort direction
938		if(!handle.reverseSort[column] || (handle.reverseSort[column] === null)) {
939			handle.reverseSort[column] = rev;
940		}
941
942		// if this column was the last one sorted, reverse its sort direction
943		if(column == handle.lastColumn) {
944			handle.reverseSort[column] = !handle.reverseSort[column];
945		}
946
947		// remember this column as the last one sorted
948		handle.lastColumn = column;
949
950		// hide the table during operations
951		var oldDsply = handle.style.display;
952		handle.style.display = "none";
953
954		// use a selection sort algorithm
955		var tmpEl;
956		var i, j;
957		var minVal, minIdx;
958		var testVal;
959		var cmp;
960		for(i = 0; i < handle.rows.length - 1; i++) {
961
962			// assume the current row has the minimum value
963			minIdx = i;
964			minVal = Yacs.getTextValue(handle.rows[i].cells[column]);
965
966			// look for a smaller values in following rows
967			for(j = i + 1; j < handle.rows.length; j++) {
968				testVal = Yacs.getTextValue(handle.rows[j].cells[column]);
969				cmp = Yacs.compareValues(minVal, testVal);
970
971				// the case of reverse ordering
972				if(handle.reverseSort[column]) {
973					cmp = -cmp;
974				}
975
976				// sort by the first column if those values are equal
977				if(cmp === 0 && column !== 0) {
978					cmp = Yacs.compareValues(Yacs.getTextValue(handle.rows[minIdx].cells[0]), Yacs.getTextValue(handle.rows[j].cells[0]));
979				}
980
981				// this is the new minimum
982				if (cmp > 0) {
983					minIdx = j;
984					minVal = testVal;
985				}
986			}
987
988			// move the minimum row just below the current row
989			if (minIdx > i) {
990				tmpEl = handle.removeChild(handle.rows[minIdx]);
991				handle.insertBefore(tmpEl, handle.rows[i]);
992			}
993		}
994
995		// beautify the table
996		Yacs.beautifyTable(handle, column);
997
998		// Set team rankings.
999		//	setRanks(handle, column, rev);
1000
1001		// show the table again
1002		handle.style.display = oldDsply;
1003
1004		return false;
1005	},
1006
1007	/**
1008	 * @link http://ajaxcookbook.org/
1009	 */
1010	syslog: function(message) {
1011
1012		if (!Yacs.window_ || Yacs.window_.closed) {
1013			var win = window.open("", null, "width=400,height=200," +
1014								  "scrollbars=yes,resizable=yes,status=no," +
1015								  "location=no,menubar=no,toolbar=no");
1016			if (!win) { return; }
1017			var doc = win.document;
1018			doc.write("<html><head><title>Debug Log</title></head>" +
1019				  "<body></body></html>");
1020			doc.close();
1021			Yacs.window_ = win;
1022		}
1023		var logLine = Yacs.window_.document.createElement("div");
1024		logLine.appendChild(Yacs.window_.document.createTextNode('=> ' + message));
1025		Yacs.window_.document.body.appendChild(logLine);
1026
1027	},
1028
1029	/**
1030	 * This handler adds code to tabbing items, to process further clicks.
1031	 *
1032	 * It has to be called once, after the construction of proper DOM elements.
1033	 * You can either build the DOM manually, then place a call to this function,
1034	 * or, alternatively, just call Skin::build_tabs() from within you PHP code
1035	 * to have everything done automatically.
1036	 *
1037	 * @param tabs A list of tabs related to panels and URLs
1038	 * @param args This corresponds to the Ajax options supported in prototype.js
1039	 *
1040	 * @see users/view.php
1041	 * @see skins/skin_skeleton.php
1042	 *
1043	 * @link http://actsasflinn.com/Ajax_Tabs/index.html AJAX Tabs (Rails redux)
1044	 * @link http://20bits.com/2007/05/23/dynamic-ajax-tabs-in-20-lines/
1045	 */
1046	tabs: function(tabs, args) {
1047
1048		Yacs.tabs_list = tabs;
1049		Yacs.tabs_args = args;
1050
1051		// react to clicks
1052		var id;
1053		for(id in tabs) {
1054			if(tabs.hasOwnProperty(id)) {
1055				Event.observe($(id), 'click', Yacs.tabs_event);
1056			}
1057		}
1058	},
1059
1060	/**
1061	 * click on a tab
1062	 */
1063	tabs_event: function(e) {
1064
1065		// target the clicked tab
1066		var clicked = Event.element(e);
1067
1068		// if we click on a link, move upwards to list item -- 'a' is for XHTML strict, 'A' for other cases
1069		if((clicked.tagName == 'a') || (clicked.tagName == 'A')) {
1070			clicked = clicked.parentNode;
1071		}
1072
1073		// trigger custom behavior, if any
1074		if(typeof Yacs.tabs_args.onClick == 'function') {
1075			Yacs.tabs_args.onClick(clicked);
1076		}
1077
1078		// activate the clicked tab -- see skins/_reference/ajax.css
1079		var iterator;
1080		for(iterator in Yacs.tabs_list) {
1081			if(clicked.id == $(iterator).id) {
1082				$(iterator).className = 'tab-foreground';
1083			} else {
1084				$(iterator).className = 'tab-background';
1085			}
1086		}
1087
1088		// activate the related panel -- see skins/_reference/ajax.css
1089		for(iterator in Yacs.tabs_list) {
1090			if(clicked.id == $(iterator).id) {
1091				$(Yacs.tabs_list[iterator][0]).className = 'panel-foreground';
1092
1093				// load panel content, if any
1094				if(Yacs.tabs_list[iterator].length > 1) {
1095					Yacs.updateOnce(Yacs.tabs_list[iterator][0], Yacs.tabs_list[iterator][1], Yacs.tabs_args);
1096				}
1097
1098			} else {
1099				$(Yacs.tabs_list[iterator][0]).className = 'panel-background';
1100			}
1101		}
1102
1103		// do not propagate event
1104		Event.stop(e);
1105	},
1106
1107	/**
1108	 * change text size
1109	 *
1110	 * Use this function in links, to allow surfer to adjust text size.
1111	 *
1112	 * @param target container in page
1113	 * @param size increment or decrement
1114	 */
1115	textResize: function(handle, increment) {
1116
1117		// get current size
1118		var current = Yacs.getCookie('TextSize');
1119		var currentSize = 2;
1120		if(current !== null) {
1121			eval("current = "+current);
1122			currentSize = current.size;
1123		}
1124
1125		// change it
1126		currentSize += increment;
1127		if(currentSize < 0 ) { currentSize = 0; }
1128		if(currentSize > 6 ) { currentSize = 6; }
1129
1130		// save it for 6 months = 6 * 30 = 180 days
1131		Yacs.setCookie('TextSize', '{ handle: "' + handle + '", size: ' + currentSize + ' }', 180);
1132
1133		// actual style update
1134		Yacs.textSize();
1135	},
1136
1137	/**
1138	 * enforce current text settings
1139	 *
1140	 * This is invoked each time a page is loaded
1141	 */
1142	textSize: function() {
1143
1144		// use data from cookie
1145		var current = Yacs.getCookie('TextSize');
1146		if(current === null) {
1147			return;
1148		}
1149		eval('current = ' + current);
1150
1151		// do nothing on standard size
1152		if(current == 2) {
1153			return;
1154		}
1155
1156		// get actual style
1157		var allSizes = [ 'xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large' ];
1158		var currentSize = allSizes[ current.size ];
1159
1160		// resize the target container
1161		$(current.handle).style.fontsize = currentSize;
1162
1163		// also resize poorly inherited items
1164		allTags = $(current.handle).getElementsByTagName('div');
1165		for(var index = 0; index < allTags.length; index++ ) { allTags[index].style.fontSize = currentSize; }
1166		allTags = $(current.handle).getElementsByTagName('td');
1167		for(index = 0; index < allTags.length; index++ ) { allTags[index].style.fontSize = currentSize; }
1168		allTags = $(current.handle).getElementsByTagName('tr');
1169		for(index = 0; index < allTags.length; index++ ) { allTags[index].style.fontSize = currentSize; }
1170	},
1171
1172	/**
1173	 * toggle a folded box
1174	 *
1175	 * @param the box
1176	 * @param string URL of the extending icon
1177	 * @param string URL of the collapsing icon
1178	 */
1179	toggle_folder: function(node, plus_href, minus_href) {
1180
1181		// unfold the branch if it is not visible
1182		if(node.nextSibling.style.display == 'none') {
1183			node.nextSibling.style.display = 'block';
1184
1185			// change the image (if there is an image)
1186			if(node.childNodes.length > 0) {
1187				if(node.childNodes.item(0).nodeName == "IMG") {
1188					node.childNodes.item(0).src = minus_href;
1189				}
1190			}
1191
1192		// collapse the branch if it is visible
1193		} else {
1194			node.nextSibling.style.display = 'none';
1195
1196			// change the image (if there is an image)
1197			if(node.childNodes.length > 0) {
1198				if(node.childNodes.item(0).nodeName == "IMG") {
1199					node.childNodes.item(0).src = plus_href;
1200				}
1201			}
1202
1203		}
1204
1205	},
1206
1207	/**
1208	 * update content asynchronously
1209	 *
1210	 * This function displays a nice spinning image while loading the page.
1211	 *
1212	 * @param string id of the target CSS container
1213	 * @param string web address to fetch new snippet
1214	 * @param mixed additional parameters to transmit to Ajax
1215	 *
1216	 */
1217	update: function(panel, address, args) {
1218
1219		$(panel).innerHTML = '<img alt="*" src="' + Yacs.spinningImage.src + '" style="vertical-align:-3px" />';
1220
1221		var updater = new Ajax.Updater(panel, address, $H({
1222			asynchronous: true,
1223			method: 'get',
1224			evalScripts: true
1225			}).merge(args)
1226		);
1227
1228	},
1229
1230	/**
1231	 * update a modal box
1232	 *
1233	 * This is called internally by Yacs.displayModalBox()
1234	 *
1235	 * @param string XHTML content of the box
1236	 */
1237	updateModalBox: function(content) {
1238
1239		// update box content
1240		Element.update('modal_content', content);
1241
1242		// adjust box size
1243		Element.setStyle('modal_content', { width: 'auto', height: 'auto' });
1244
1245		// center the box
1246		var yShift, xShift;
1247		yShift = Math.floor(((document.viewport.getHeight() - $('modal_centered').offsetHeight) / 2) - $('modal_centered').offsetTop);
1248		xShift = Math.floor(((document.viewport.getWidth() - $('modal_centered').offsetWidth) / 2) - $('modal_centered').offsetLeft);
1249
1250		// update box position
1251		if((Math.abs(yShift) > 1) || (Math.abs(xShift) > 1)) {
1252			var effect = new Effect.MoveBy('modal_centered', yShift, xShift, {duration: 0.2, queue: 'end'});
1253		}
1254
1255	},
1256
1257	/**
1258	 * set content only once
1259	 *
1260	 * This function is similar to Yacs.update(), except it does nothing if the target
1261	 * item already contains something.
1262	 *
1263	 * Use this function for example to populate tabbed panels of a complex page.
1264	 * Look at Yacs.tabs() above for a practical example of use
1265	 *
1266	 * @param string id of the target CSS container
1267	 * @param string web address to fetch new snippet
1268	 * @param mixed additional parameters to transmit to Ajax
1269	 *
1270	 */
1271	updateOnce: function(panel, address, args) {
1272
1273		// do nothing if the panel contains something
1274		if(!$(panel).innerHTML || ($(panel).innerHTML === '') || ($(panel).innerHTML == '<img alt="*" src="' + Yacs.spinningImage.src + '" style="vertical-align:-3px" />')) {
1275			Yacs.update(panel, address, args);
1276		}
1277
1278	}
1279
1280};
1281
1282// initialize yacs
1283Event.observe(window, "load", Yacs.onWindowLoad);

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.