PageSourceSearch

https://www.ghbc.org/includes/eventbox/eventbox.js

js ghbc.org collected 2026-09-26 15:02:24 UTC 46,641 bytes, 1,382 lines download raw bytes

1
2// Create namespace ------------------------------------------------------------
3if (!perpetuacms) {
4	var perpetuacms = {};
5}
6
7// Eventbox class ------------------------------------------------------------
8perpetuacms.eventbox = Class.create({
9	initialize: function(element, settings) {
10		var elementIsID = 1; // mark that we're passing an ID of an element
11		this.isPaused = false;
12		this.navigationIsPaused = false;
13		this.isTransitioning = false;
14		this.videoIsPlaying = false;
15		this.flowplayerID = false;
16
17		if (Object.isElement(element)) {
18			elementIsID = 0; // mark that we're passing an actual element and not an ID
19		}
20
21		element = $(element);
22		this.eventbox_container = element;
23		this.eventbox_container.store("eventbox", this);
24
25		// defaults
26		var DefaultSettings = {
27			delay: 3000,
28			order: "sequential",
29			type: "fade",
30			duration: 0.50,
31			showEmptyDescriptions: false,
32			pauseOnMouseOver: true,
33			effectDelay: 0,
34			setAsBackground: false,
35			transitionDescriptions: false,
36			startPaused: false,
37			touch: false,
38			firstSlide: 1,
39			slider: {
40				active: false,
41				button_next: "", // class of the navigation's next button
42				button_previous: "", // class of the navigation's previous button
43				container: "", // class of the navigation's (ul's) parent
44				orientation: "horizontal", // orientation of the slider [horizontal or vertical]
45				step: 4,
46				view: 4
47			},
48			video: {
49				hideOverlay: false,
50				popupSettings: {
51					top: 50,
52					padding: 10,
53					backgroundColor: "#000",
54					className: ""
55				}
56			},
57			flowplayer_settings: {
58				swf: "/includes/videoplayers/flowplayer/flowplayer.swf",
59				key: null,
60				logo: null
61			}
62		};
63
64		if (!settings) { 
65			settings = {};
66		}
67
68		this.settings = Object.extend(Object.extend({ }, DefaultSettings), settings);
69		this.settings.slider = Object.extend(Object.extend({ }, DefaultSettings.slider), settings.slider);
70		this.settings.video = Object.extend(Object.extend({ }, DefaultSettings.video), settings.video);
71		this.settings.flowplayer_settings = Object.extend(Object.extend({ }, DefaultSettings.flowplayer_settings), settings.flowplayer_settings);
72/*
73		this.settings.video.popupSettings = Object.extend(Object.extend({ }, DefaultSettings.video.popupSettings), settings.video.popupSettings);
74*/
75
76
77		/* setup some call backs */
78		this.onBeforeLoaded = this.settings.onBeforeLoaded || function(){};
79		this.onLoaded = this.settings.onLoaded || function(){};
80		this.onBeforePause = this.settings.onBeforePause || function(){};
81		this.onPause = this.settings.onPause || function(){};
82		this.onBeforeResume = this.settings.onBeforeResume || function(){};
83		this.onResume = this.settings.onResume || function(){};
84		this.onBeforeTransition = this.settings.onBeforeTransition || function(){};
85		this.onTransition = this.settings.onTransition || function(){};
86		
87		this.onBeforeLoaded();
88		
89		// convert delay from seconds to milliseconds
90		if (this.settings.delay < 100) {
91			this.settings.delay = this.settings.delay / 0.001;
92		}
93		
94		// load all the images into an object (makes manipulating the images easier)
95		// if an ID of an element was passed
96		if (elementIsID == 1) {
97		    this.objWindowContainer = $(this.eventbox_container.identify() + "Window");
98		    this.objWindow = this.objWindowContainer.select("> div");
99
100		    this.objNavigationContainer = $(this.eventbox_container.identify() + "Navigation");
101		    this.objNavigation = this.objNavigationContainer.select("> li");
102
103		    this.objDescriptionsContainer = $(this.eventbox_container.identify() + "Descriptions");
104		    this.objDescriptions = this.objDescriptionsContainer.select("> div");
105		    
106		    if ($(this.eventbox_container.identify() + "Container")) {
107		    	this.objContainer = $(this.eventbox_container.identify() + "Container");
108		    }
109		    
110		    if ($(this.eventbox_container.identify() + "Overlay")) {
111		    	this.objOverlay = $(this.eventbox_container.identify() + "Overlay");
112		    }
113		    
114		    if ($(this.eventbox_container.identify() + "Anchor")) {
115		    	this.objAnchor = $(this.eventbox_container.identify() + "Anchor");
116		    }
117
118			// get the dimensions of the window (for use later in setting the height of the images)
119			this.windowDimensions = $(this.eventbox_container.identify() + "Window").getDimensions();
120
121		// if an actual element was passed
122		} else {
123			this.base_class = $w(this.eventbox_container.className).first();
124
125		    this.objWindowContainer = this.eventbox_container.down("." + this.base_class + "Window");
126		    this.objWindow = this.objWindowContainer.select(">div");
127
128		    this.objNavigationContainer = this.eventbox_container.down("." + this.base_class + "Navigation");
129		    this.objNavigation = this.objNavigationContainer.select("li");
130
131		    this.objDescriptionsContainer = this.eventbox_container.down("." + this.base_class + "Descriptions");
132		    this.objDescriptions = this.objDescriptionsContainer.select(">div");
133
134		    if (this.eventbox_container.down("." + this.base_class + "Container")) {
135		    	this.objContainer = this.eventbox_container.down("." + this.base_class + "Container");
136		    }
137
138		    if (this.eventbox_container.down("." + this.base_class + "Overlay")) {
139		    	this.objOverlay = this.eventbox_container.down("." + this.base_class + "Overlay");
140		    }
141
142		    if (this.eventbox_container.down("." + this.base_class + "Anchor")) {
143		    	this.objAnchor = this.eventbox_container.down("." + this.base_class + "Anchor");
144		    }
145
146		    if (this.eventbox_container.down("." + this.base_class + "Play")) {
147		    	this.objPlay = this.eventbox_container.down("." + this.base_class + "Play");
148		    }
149
150		    if (this.eventbox_container.down("." + this.base_class + "Stop")) {
151		    	this.objStop = this.eventbox_container.down("." + this.base_class + "Stop");
152		    }
153
154		    if (this.eventbox_container.down("." + this.base_class + "Pause")) {
155		    	this.objPause = this.eventbox_container.down("." + this.base_class + "Pause");
156		    }
157
158		    if (this.eventbox_container.down("." + this.base_class + "ControlNext")) {
159		    	this.objNext = this.eventbox_container.down("." + this.base_class + "ControlNext");
160		    }
161
162		    if (this.eventbox_container.down("." + this.base_class + "ControlPrevious")) {
163		    	this.objPrev = this.eventbox_container.down("." + this.base_class + "ControlPrevious");
164		    }
165
166			// get the dimensions of the window (for use later in setting the height of the images)
167			this.windowDimensions = this.eventbox_container.down("." + this.base_class + "Window").getDimensions();
168		}
169
170		// content references
171		this.previous_content = 0;		/* we use this to store the previous content we were on */
172		this.active_content = -1;
173		this.active_content = this.getNextImage(!isNaN(this.settings.firstSlide) ? parseInt(this.settings.firstSlide) - 1 : this.objWindow.size());		/* get the first (or random) content */
174		
175		// activate the slider
176		if (this.settings.slider.active) {
177			this._sliderInit();
178		}
179
180		/* loop through images in the window:
181			turn off the image
182			set an absolute position
183			position image top/left
184			set width and height to the window size
185		*/
186		
187		if (this.settings['type'] == "horizontal_continuous") {
188			var first_image = this.objWindow.first();
189			var last_image = this.objWindow.last();
190			var first_navigation = this.objNavigation.first();
191			var last_navigation = this.objNavigation.last();
192			var first_description = this.objDescriptions.first();
193			var last_description = this.objDescriptions.last();
194			this.image_width = first_image.getWidth(),
195			this.image_offset = 60, /* without this, the current image is flush left */
196			this.current_image = 2, /* defaults the rotator to be the 3rd image (since there are two cloned images in front) */
197
198			/* clone first and second image to append to end */
199			this.objWindowContainer.insert(first_image.clone(true).addClassName("cloned-image"));
200			this.objWindowContainer.insert(first_image.next().clone(true).addClassName("cloned-image"));
201			this.objNavigationContainer.insert(first_navigation.clone(true).addClassName("cloned-image"));
202			this.objNavigationContainer.insert(first_navigation.next().clone(true).addClassName("cloned-image"));
203			this.objDescriptionsContainer.insert(first_description.clone(true).addClassName("cloned-image"));
204			this.objDescriptionsContainer.insert(first_description.next().clone(true).addClassName("cloned-image"));
205	
206			/* clone last and second-to-last to append to beginning */
207			this.objWindowContainer.insert({top: last_image.clone(true).addClassName("cloned-image")});
208			this.objWindowContainer.insert({top: last_image.previous().clone(true).addClassName("cloned-image")});
209			this.objNavigationContainer.insert({top: last_navigation.clone(true).addClassName("cloned-image")});
210			this.objNavigationContainer.insert({top: last_navigation.previous().clone(true).addClassName("cloned-image")});
211			this.objDescriptionsContainer.insert({top: last_description.clone(true).addClassName("cloned-image")});
212			this.objDescriptionsContainer.insert({top: last_description.previous().clone(true).addClassName("cloned-image")});
213			
214			this.objWindow = this.objWindowContainer.select(">div");
215			this.objNavigation = this.objNavigationContainer.select("li");
216		    this.objDescriptions = this.objDescriptionsContainer.select(">div");
217
218			/* set the width of the rotator container */
219			this.objWindowContainer.setStyle({
220				width: this.image_width * this.objWindow.size() + "px"
221			});
222			
223			/* move container to actual first image (requires -pixel margin or left since there are now two images in front */
224			this.objWindowContainer.setStyle({
225				left: -((this.current_image * this.image_width) - this.image_offset) + "px"
226			});
227
228		} else {
229			this.objWindow.invoke("hide").invoke("absolutize").invoke("setStyle", {
230				top: 0,
231				left: 0,
232				width: this.windowDimensions.width + "px",
233				height: this.windowDimensions.height + "px"
234			});
235
236			/* loop through the descriptions
237				turn off the description
238				position description top/left
239			*/
240/*
241			this.objDescriptions.invoke("hide").invoke("setStyle", {
242				top: 0,
243				left: 0
244			});
245*/
246			this.objDescriptions.invoke("hide");
247		}
248
249		if (this.settings.transitionDescriptions) {
250			var description_container = this.objDescriptions.first().up("div");
251			var description_width = description_container.getWidth() - parseInt(this.objDescriptions.first().getStyle("padding-left")) - parseInt(this.objDescriptions.first().getStyle("padding-right")) + "px";
252			var description_height = description_container.getHeight() - parseInt(this.objDescriptions.first().getStyle("padding-top")) - parseInt(this.objDescriptions.first().getStyle("padding-bottom")) + "px";
253			
254/*
255			this.objDescriptions.invoke("absolutize").invoke("setStyle", {
256				width: description_width,
257				height: description_height
258			});
259*/
260			this.objDescriptions.invoke("setStyle", {
261				position: "absolute",
262				width: description_width,
263				height: description_height
264			});
265		}
266
267		/* loop through the navigation
268			make sure none of the navigation buttons appear active
269			turn all the onClicks on
270		*/
271		this.objNavigation.each(function(n, index) {
272			n.removeClassName("on");
273			this.toggleNavigation(index, 'on');
274		}.bind(this));
275
276		this.objWindow[this.active_content].show();					/* turn on the active image */
277		this.objNavigation[this.active_content].addClassName("on");	/* turn on the navigation class */
278
279		if (!this.objDescriptions[this.active_content].empty() || this.settings['showEmptyDescriptions']) {
280			this.objDescriptions[this.active_content].show();				/* turn on the active description if applicable */
281		}
282
283		// update anchor href
284		if (this.objAnchor) {
285			this.updateAnchor(this.active_content);
286		}
287
288		// set the image as the background
289		if (this.settings['setAsBackground']) {
290			this.objWindow[this.active_content].setStyle({
291				backgroundImage: "url(" + this.objWindow[this.active_content].down("img").hide().readAttribute("src") + ")"
292			});
293
294			if (this.objAnchor) {
295				this.objAnchor.setStyle({
296					width: this.windowDimensions.width + "px",
297					height: this.windowDimensions.height + "px"
298				});
299			}
300			
301			Event.observe(window, "resize", function(event) {
302				this.windowDimensions = this.eventbox_container.down("." + this.base_class + "Window").getDimensions();
303
304				this.objWindow.invoke("setStyle", {
305					width: this.windowDimensions.width + "px",
306					height: this.windowDimensions.height + "px"
307				});
308				
309				if (this.objAnchor) {
310					this.objAnchor.setStyle({
311						width: this.windowDimensions.width + "px",
312						height: this.windowDimensions.height + "px"
313					});
314				}
315			}.bind(this));
316		}
317
318		this.toggleNavigation(this.active_content, 'off');				/* deactivate the onClick for the active image */
319			
320		// set a timer to slide the images
321		if (this.settings['type'] != '' && this.settings['type'] != 'static' && !this.isPaused && !this.isTransitioning) {
322			this.interval = setTimeout(this.doTransition.bind(this, -1), this.settings['delay']);
323		}
324		
325		// setup an event to pause the transition on mouse over on resume on mouse out
326		if (this.settings.pauseOnMouseOver && this.interval) {
327			this.addObservers();
328		}
329		
330		// pause the rotation on startup
331		if (this.settings.startPaused) {
332			if (this.objPause) {
333				this.objPause.fire("rotator:pause");
334			} else {
335				this.pause();
336			}
337		}
338		
339		// setup play, pause and stop buttons
340		if (this.objPlay) {
341			this.objPlay.observe("click", function(event) {
342				event.stop();
343				this.resume();
344			}.bind(this));
345		}
346		if (this.objStop) {
347			this.objStop.observe("click", function(event) {
348				event.stop();
349				this.pause();
350			}.bind(this));
351		}
352		if (this.objPause) {
353			this.objPause.observe("click", function(event) {
354				event.stop();
355				this.objPause.fire("rotator:pause");
356			}.bind(this));
357		}
358
359		// setup next and previous buttons
360		if (this.objNext) {
361			this.objNext.observe("click", function(event) {
362				event.stop();
363				this.doTransition(-1);
364
365				if (this.objPause && !this.isPaused) {
366					this.objPause.fire("rotator:pause");
367				}
368			}.bind(this));
369		}
370		if (this.objPrev) {
371			this.objPrev.observe("click", function(event) {
372				event.stop();
373				this.doTransition((this.active_content - 1 < 0) ? this.objWindow.size() - 1 : this.active_content - 1);
374
375				if (this.objPause && !this.isPaused) {
376					this.objPause.fire("rotator:pause");
377				}
378			}.bind(this));
379		}
380		
381		// setup swipe left/right support
382		if (this.settings['touch']) {
383			var slide_hammer = new Hammer(this.eventbox_container, {domEvents:true, velocity: 0.65});
384
385			slide_hammer.on("panleft panright swipeleft swiperight", function(event) {
386				if (!this.isTransitioning) {
387					if (event.type == "swiperight" || event.type == "panright") {
388						this.doTransition((this.active_content - 1 < 0) ? this.objWindow.size() - 1 : this.active_content - 1);
389		
390						if (this.objPause && !this.isPaused) {
391							this.objPause.fire("rotator:pause");
392						}
393					}
394					
395					if (event.type == "swipeleft" || event.type == "panleft") {
396						this.doTransition(-1);
397		
398						if (this.objPause && !this.isPaused) {
399							this.objPause.fire("rotator:pause");
400						}
401					}
402				}
403			}.bind(this));
404
405		}
406
407		this.onLoaded(this);
408	},
409	
410	addObservers: function() {
411		// if we have a Container div, set the mouse event on it
412		var mouse_event_element = this.objContainer || this.eventbox_container;
413
414		this.boundPause = this._pause.bind(this);
415		this.boundResume = this._resume.bind(this);
416
417		mouse_event_element.observe("mouseover", this.boundPause);
418		mouse_event_element.observe("mouseout", this.boundResume);
419
420		if (this.objPause) {
421			this.objPause.stopObserving("rotator:pause").observe("rotator:pause", function(event) {
422				if (this.isPaused) {
423					this.resume();
424					this.objPause.removeClassName("stopped").addClassName("playing");
425					this.objPause.down("span").update("Pause");
426				} else {
427					this.pause();
428					this.objPause.removeClassName("playing").addClassName("stopped");
429					this.objPause.down("span").update("Resume");
430				}
431			}.bind(this));
432		}
433	},
434	
435	cancelObservers: function() {
436		// if we have a Container div, set the mouse event on it
437		var mouse_event_element = this.objContainer || this.eventbox_container;
438
439		mouse_event_element.stopObserving("mouseover", this.boundPause);
440		mouse_event_element.stopObserving("mouseout", this.boundResume);
441	},
442	
443	afterTransition: function() {
444		// mark that we're no longer transitioning
445		this.isTransitioning = false;
446		
447		// turn on the navigation
448		this.toggleNavigation('all', 'on');
449		
450		// turn off the previous content
451		if (this.objWindow[this.previous_content] && this.objWindow[this.previous_content] != this.objWindow[this.active_content]) {
452			this.objWindow[this.previous_content].hide();
453			this.objDescriptions[this.previous_content].hide();
454		}
455
456		// set the timeout to transition the images
457		if (this.settings['type'] != '' && this.settings['type'] != 'static' && !this.isPaused) {
458			this.interval = setTimeout(this.doTransition.bind(this, -1), this.settings['delay']);
459		}
460
461		this.onTransition(this);
462	},
463	
464	//
465	// slide left and right
466	//
467	doTransition: function(nextImage) {
468		this.onBeforeTransition(this);
469
470		// clear the timeout in case of manual click
471		clearTimeout(this.interval);
472		
473		this.isTransitioning = true;
474		
475		// turn off the onClicks for the navigation while doing transition
476		this.toggleNavigation('all', 'off');
477		
478		// get the next image
479		nextImage = this.getNextImage(nextImage);
480
481		// turn off the previous image
482		if (this.previous_content != this.active_content && this.previous_content != nextImage) {
483			this.objWindow[this.previous_content].hide();					/* turn off the previous image */
484			this.objWindow[this.previous_content].setStyle({
485				left: 0													/* position the image all the way to the left */
486			});
487			this.objDescriptions[this.previous_content].hide();			/* turn off the previous description */
488		}
489		
490		this.objWindow[nextImage].show();	/* turn on the next image */
491
492		// turn on the description coming into focus
493		if (!this.objDescriptions[nextImage].empty() || this.settings['showEmptyDescriptions']) {
494			this.objDescriptions[nextImage].show();
495		}
496	
497		if (!this.settings.transitionDescriptions) {
498			this.objDescriptions[this.active_content].hide();			/* turn off the description */
499		}
500
501		if (this.objOverlay && !this.objOverlay.visible()) {
502			this.objOverlay.show();
503		}
504
505		// update anchor href
506		if (this.objAnchor) {
507			if (!this.objAnchor.visible()) {
508				this.objAnchor.show();
509			}
510			this.updateAnchor(nextImage);
511		}
512
513		// set the image as the background
514		if (this.settings['setAsBackground']) {
515			this.objWindow[nextImage].setStyle({
516				backgroundImage: "url(" + this.objWindow[nextImage].down("img").hide().readAttribute("src") + ")"
517			});
518		}
519			
520		// call the transition function
521		switch(this.settings['type']) {
522			case 'slide_horizontal':
523				this.doTransition_slideHorizontal(nextImage);
524				break;
525
526			case 'slide_vertical':
527				this.doTransition_slideVertical(nextImage);
528				break;
529
530			case 'fade':
531				this.doTransition_fade(nextImage);
532				break;
533
534			case 'fade_delay':
535				this.doTransition_fadeDelay(nextImage);
536				break;
537
538			case 'horizontal_continuous':
539				this.doTransition_slideHorizontal_continuous(nextImage);
540				break;
541
542			default:	/* does not do a transition */
543				this.objWindow[this.active_content].hide();				
544				this.objDescriptions[this.active_content].hide();
545				this.afterTransition();
546				break;
547		}
548		
549		this.objNavigation[this.active_content].removeClassName("on");		/* remove the 'on' class for the navigation button */
550		this.objNavigation[nextImage].addClassName("on");				/* add the 'on' class for the navigation button */
551		
552		// update the this.active_content and this.previous_content variables
553		this.previous_content = this.active_content
554		this.active_content = nextImage;
555
556		
557		if (this.settings.slider.active && !this.navigationIsPaused) {
558			this._sliderMove();
559		}
560	},
561
562	doTransition_slideHorizontal: function(nextImage) {
563
564		// slide the images to the left
565		if (nextImage > this.active_content) {
566			this.objWindow[nextImage].setStyle({
567				left: (this.windowDimensions.width - 2) + "px"			/* position the image all the way to the left */
568			});
569			
570			new Effect.Move(this.objWindow[nextImage], {
571				x: 0, 
572				y: 0, 
573				duration: this.settings['duration'], 
574				mode: 'absolute', 
575				afterFinish: this.afterTransition.bind(this)
576			});
577			new Effect.Move(this.objWindow[this.active_content], {
578				x: -this.windowDimensions.width,
579				y: 0, 
580				duration: this.settings['duration'],
581				mode: "absolute"
582			});
583
584			// if we're transitioning descriptions
585			if (this.settings.transitionDescriptions) {
586				// if the active slide has a description or we're displaying empty descriptions
587				if (!this.objDescriptions[this.active_content].empty() || this.settings['showEmptyDescriptions']) {
588					new Effect.Move(this.objDescriptions[this.active_content], {
589						x: -this.windowDimensions.width, 
590						y: 0, 
591						duration: this.settings['duration'], 
592						mode: "absolute"
593					});
594				}
595
596				// if the next description is not empty or we're showing empty descriptions
597				if (!this.objDescriptions[nextImage].empty() || this.settings['showEmptyDescriptions']) {
598					this.objDescriptions[nextImage].setStyle({
599						left: (this.windowDimensions.width - 2) + "px"			/* position the description all the way to the left */
600					});
601
602					new Effect.Move(this.objDescriptions[nextImage], {
603						x: 0, 
604						y: 0, 
605						duration: this.settings['duration'], 
606						mode: 'absolute'
607					});
608				}
609			}
610
611		// slide the images to the right
612		} else {
613			this.objWindow[nextImage].setStyle({
614				left: (-this.windowDimensions.width) + 'px'				/* position the image all the way to the left */
615			});
616
617			new Effect.Move(this.objWindow[nextImage], {
618				x: 0, 
619				y: 0, 
620				duration: this.settings['duration'],
621				mode: "absolute", 
622				afterFinish: this.afterTransition.bind(this)
623			});
624
625			new Effect.Move(this.objWindow[this.active_content], {
626				x: this.windowDimensions.width,
627				y: 0, 
628				duration: this.settings['duration'], 
629				mode: "absolute"
630			});
631
632			// if we're transitioning descriptions
633			if (this.settings.transitionDescriptions) {
634				// if the active slide has a description or we're displaying empty descriptions
635				if (!this.objDescriptions[this.active_content].empty() || this.settings['showEmptyDescriptions']) {
636					new Effect.Move(this.objDescriptions[this.active_content], {x: this.windowDimensions.width, y: 0, duration: this.settings['duration'], mode: "absolute"});
637				}
638
639				// if the next description is not empty or we're showing empty descriptions
640				if (!this.objDescriptions[nextImage].empty() || this.settings['showEmptyDescriptions']) {
641					this.objDescriptions[nextImage].setStyle({
642						left: (-this.windowDimensions.width) + 'px'				/* position the description all the way to the left */
643					});
644
645					new Effect.Move(this.objDescriptions[nextImage], {x: 0, y: 0, duration: this.settings['duration'], mode: "absolute"});
646				}
647			}
648		}
649
650	},
651
652	doTransition_slideVertical: function(nextImage) {
653
654		// slide the images up
655		if (nextImage > this.active_content) {
656			this.objWindow[nextImage].setStyle({
657				top: (this.windowDimensions.height) + "px"				/* position the image all the way to the left */
658			});
659
660			new Effect.Move(this.objWindow[nextImage], {
661				x: 0, 
662				y: 0, 
663				duration: this.settings['duration'],
664				mode: "absolute", 
665				afterFinish: this.afterTransition.bind(this)
666			});
667			
668			new Effect.Move(this.objWindow[this.active_content], {x: 0, y: -this.windowDimensions.height, duration: this.settings['duration'], mode: "absolute"});
669
670			// if we're transitioning descriptions
671			if (this.settings.transitionDescriptions) {
672				// if the active slide has a description or we're displaying empty descriptions
673				if (!this.objDescriptions[this.active_content].empty() || this.settings['showEmptyDescriptions']) {
674					new Effect.Move(this.objDescriptions[this.active_content], {x: 0, y: -this.windowDimensions.height, duration: this.settings['duration'], mode: "absolute"});
675				}
676
677				// if the next description is not empty or we're showing empty descriptions
678				if (!this.objDescriptions[nextImage].empty() || this.settings['showEmptyDescriptions']) {
679					this.objDescriptions[nextImage].setStyle({
680						top: (this.windowDimensions.height) + "px"				/* position the image all the way to the left */
681					});
682
683					new Effect.Move(this.objDescriptions[nextImage], {x: 0, y: 0, duration: this.settings['duration'], mode: "absolute"});
684				}
685			}
686
687		// slide the images down
688		} else {
689			this.objWindow[nextImage].setStyle({
690				top: -(this.windowDimensions.height) + 'px'			/* position the image all the way to the left */
691			});
692
693			new Effect.Move(this.objWindow[nextImage], {
694				x: 0,
695				y: 0, 
696				duration: this.settings['duration'], 
697				mode: "absolute", 
698				afterFinish: this.afterTransition.bind(this)
699			});
700			
701			new Effect.Move(this.objWindow[this.active_content], {x: 0, y: this.windowDimensions.height, duration: this.settings['duration'], mode: "absolute"});
702
703			// if we're transitioning descriptions and the active slide has a description or we're displaying empty descriptions
704			if (this.settings.transitionDescriptions) {
705				// if the active slide has a description or we're displaying empty descriptions
706				if (!this.objDescriptions[this.active_content].empty() || this.settings['showEmptyDescriptions']) {
707					new Effect.Move(this.objDescriptions[this.active_content], {x: 0, y: this.windowDimensions.height, duration: this.settings['duration'], mode: "absolute"});
708				}
709
710				// if the next description is not empty or we're showing empty descriptions
711				if (!this.objDescriptions[nextImage].empty() || this.settings['showEmptyDescriptions']) {
712					this.objDescriptions[nextImage].setStyle({
713						top: -(this.windowDimensions.height) + 'px'			/* position the image all the way to the left */
714					});
715
716					new Effect.Move(this.objDescriptions[nextImage], {x: 0, y: 0, duration: this.settings['duration'], mode: "absolute"});
717				}
718			}
719		}
720
721	},
722
723	doTransition_fade: function(nextImage) {
724
725		var fading_slide = this.objWindow[this.active_content];
726		var appearing_slide = this.objWindow[nextImage];
727
728		// turn off nextImage
729		appearing_slide.hide();
730
731		// adjust zIndexes to ensure the nextImage is on top of the activeImage
732		appearing_slide.style.zIndex = 1;
733		fading_slide.style.zIndex = 0;
734
735		
736		// do effect
737//		console.log("start effect");
738		new Effect.Appear(appearing_slide, {
739			duration: this.settings['duration'],
740			afterFinish: function() {
741//				console.log("end effect");
742				this.afterTransition(this);
743//				console.log("effect finished");
744			}.bind(this)
745		});
746
747		new Effect.Fade(fading_slide, {
748			duration: this.settings['duration'],
749			afterFinish: function() {
750			}.bind(this)
751		});
752
753		// if we're transitioning descriptions
754		if (this.settings.transitionDescriptions) {
755			
756			// if the active slide has a description or we're displaying empty descriptions
757			if (!this.objDescriptions[this.active_content].empty() || this.settings['showEmptyDescriptions']) {
758				var description_opacity = this.objDescriptions[this.active_content].getStyle("opacity");
759	
760				this.objDescriptions[this.active_content].style.zIndex = 0;
761		
762				// do effect
763				this.objDescriptions[this.active_content].fade({
764					duration: this.settings['duration'],
765					from: description_opacity
766				});
767			}
768
769			// if the next description is not empty or we're displaying empty descriptions
770			if (!this.objDescriptions[nextImage].empty() || this.settings['showEmptyDescriptions']) {
771				var description_opacity = this.objDescriptions[nextImage].get
771Style("opacity");
772	
773				this.objDescriptions[nextImage].style.zIndex = 1;
774		
775				// turn off next description
776				this.objDescriptions[nextImage].hide();
777				
778				// do effect
779				new Effect.Appear(this.objDescriptions[nextImage], {
780					duration: this.settings['duration'],
781					to: description_opacity
782				});
783			}
784		}
785
786	},
787
788	doTransition_fadeDelay: function(nextImage) {
789
790		// adjust zIndexes to ensure the nextImage is on top of the activeImage
791		this.objWindow[nextImage].style.zIndex = 1;
792		this.objWindow[this.active_content].style.zIndex = 0;
793
794		// turn off nextImage
795		this.objWindow[nextImage].hide();
796		
797		// do effect
798		this.objWindow[this.active_content].fade({
799			duration: this.settings['duration'],
800			afterFinish: function() {
801				if (!this.isPaused) {
802					this.cancelObservers();
803				}
804				new PeriodicalExecuter(function(pe) {
805					new Effect.Appear(this.objWindow[nextImage], {
806						duration: this.settings['duration'],
807						afterFinish: function() {
808							this.afterTransition();
809							if (!this.isPaused) {
810								this.addObservers();
811							}
812						}.bind(this)
813					});
814					
815					pe.stop();
816				}.bind(this), this.settings['effectDelay']);
817			}.bind(this)
818		});
819		
820		// transition the description
821		if (this.settings.transitionDescriptions) {
822			var description_opacity = 0.8;
823
824			if (!this.objDescriptions[this.active_content].empty() || this.settings['showEmptyDescriptions']) {
825				description_opacity = this.objDescriptions[this.active_content].getStyle("opacity");
826
827				this.objDescriptions[this.active_content].fade({
828					duration: this.settings['duration'],
829					from: description_opacity,
830					to: 0,
831					afterFinish: function() {
832						this.objDescriptions[this.active_content].hide().setStyle({
833							opacity: description_opacity
834						});
835					}.bind(this)
836				});
837			}
838
839			if (!this.objDescriptions[nextImage].empty() || this.settings['showEmptyDescriptions']) {
840				description_opacity = this.objDescriptions[nextImage].getStyle("opacity");
841				this.objDescriptions[nextImage].hide();
842	
843				new PeriodicalExecuter(function(pe) {
844					new Effect.Appear(this.objDescriptions[nextImage], {
845						duration: this.settings['duration'],
846						from: 0,
847						to: description_opacity
848					});
849					
850					pe.stop();
851				}.bind(this), this.settings['effectDelay'] + this.settings['duration']);
852			}
853		}
854	},
855	
856	doTransition_slideHorizontal_continuous: function(nextImage) {
857		console.log(this.active_content, nextImage);
858		var cycle = false;
859		//var current_series = rotator_container.down("li.item-active");
860		var dir = (nextImage > this.active_content) ? 1 : -1;
861		var dir_amt = nextImage - this.active_content;
862
863
864		//rotator_navigation.stop();
865
866		this.current_image += dir_amt;
867		cycle = !!(this.current_image === 1 || this.current_image > this.objWindow.size() + 1);
868		
869/*
870		current_series.removeClassName("item-active");
871		rotator_container.select("li")[this.current_image].addClassName("item-active");
872*/
873
874		this.objWindowContainer.morph("left: " + (parseInt(this.objWindowContainer.getStyle("left")) + (-this.image_width * dir * dir_amt)) + "px;", {
875			afterFinsh: function() {
876				if (cycle) {
877					this.current_image = (this.current_image === 1) ? this.objWindow.size() + 1 : 2;
878
879					this.objWindowContainer.setStyle({
880						left: -((this.current_image * this.image_width) - this.image_offset) + "px"
881					});
882	
883/*
884					rotator_container.down("li.item-active").removeClassName("item-active");
885					rotator_container.select("div")[this.current_image].addClassName("item-active");
886*/
887				}
888
889				// mark that we're no longer transitioning
890				this.isTransitioning = false;
891				
892				// turn on the navigation
893				this.toggleNavigation('all', 'on');
894				
895				// turn off the previous content
896/*
897				if (this.objWindow[this.previous_content] && this.objWindow[this.previous_content] != this.objWindow[this.active_content]) {
898					this.objWindow[this.previous_content].hide();
899					this.objDescriptions[this.previous_content].hide();
900				}
901*/
902		
903				// set the timeout to transition the images
904				if (this.settings['type'] != '' && this.settings['type'] != 'static' && !this.isPaused) {
905					this.interval = setTimeout(this.doTransition.bind(this, -1), this.settings['delay']);
906				}
907		
908				this.onTransition(this);
909			}.bind(this)
910		});
911	},
912	
913	getNextImage: function(nextImage) {
914
915		// get the next image based off of the "order" value
916		switch(this.settings['order']) {
917			case 'random':
918				nextImage = this.getRandom();
919				break;
920
921			default:
922				// if the nextImage is a negative number, then increment from the active image (the timer passes a -1)
923				if (nextImage < 0) { nextImage = this.active_content + 1; }
924		
925				// if the 'nextImage' is greater than the amount of images we have, reset to 0
926				if ((nextImage + 1) > this.objWindow.size()) { nextImage = 0; }
927		};
928
929		return nextImage;
930	},
931	
932	getRandom: function() {
933		var random_number = Math.floor(Math.random() * this.objWindow.size());
934
935		// if it's the same as the active image, try again
936		if (random_number == this.active_content) {
937			return this.getRandom();
938		} else {
939			return random_number;
940		}
941	},
942	
943	_pause: function() {
944		clearTimeout(this.interval);
945		this.isPaused = true;
946	},
947	
948	_resume: function() {
949		clearTimeout(this.interval);
950		if (!this.isTransitioning) {
951			this.interval = setTimeout(this.doTransition.bind(this, -1), this.settings['delay']);
952		}
953		this.isPaused = false;
954	},
955	
956	pause: function() {
957		this.onBeforePause();
958		this._pause();
959		this.cancelObservers();
960		this.onPause();
961	},
962	
963	resume: function() {
964		this.onBeforeResume();
965		this._resume();
966		if (typeof $f == "function") {
967			$f("*").each(function() {
968				this.unload();
969			});
970		}
971		this.addObservers();
972		this.onResume();
973	},
974	
975	flowplayer_init: function(player) {
976		player.onBegin(function() {
977			this.pause();
978			this.objDescriptionsContainer.fade({duration: 0.5});
979		}.bind(this));
980		player.onFinish(function() {
981			this.resume();
982		}.bind(this));
983		player.onUnload(function() {
984			this.objDescriptionsContainer.appear({duration: 0.5});
985		}.bind(this));
986	},
987	
988	_flowplayer_play: function() {
989		var popup_player = $(this.flowplayerID).cloneNode(true).writeAttribute("id", "");
990		popup_player.identify();
991		
992		var orig_player = flowplayer(this.flowplayerID);
993
994		this.pause();
995		this.viewerCreate();
996		
997		this.viewer_container.setStyle({
998			width: $(this.flowplayerID).getStyle("width"),
999			height: $(this.flowplayerID).getStyle("height")
1000		});
1001		this.viewerShow();
1002		this.viewer_content.insert(
1003			popup_player.update()
1004		);
1005		
1006		var player_config = orig_player.getConfig(true);
1007		player_config.key = this.settings.flowplayer_settings.key;
1008		player_config.logo = this.settings.flowplayer_settings.logo;
1009
1010		flowplayer(popup_player.identify(), {src: this.settings.flowplayer_settings.swf, wmode: "opaque"}, player_config).ipad();
1011		flowplayer(popup_player.identify()).onFinish(function() {
1012			/* flowplayer(popup_player.identify()).unload(); */
1013			this.videoIsPlaying = false;
1014			this.resume();
1015			this.viewerDestroy();
1016		}.bind(this));
1017
1018		this.videoIsPlaying = true;
1019	},
1020	
1021	_flowplayer_stop: function() {
1022		if (this.objAnchor) {
1023			this.objAnchor.stopObserving("click");
1024			this.objAnchor.observe("click", function(event) {
1025				event.stop();
1026
1027				this._flowplayer_play();
1028			}.bind(this));
1029		}
1030
1031		this.eventbox_container.select("a[rel*='flowplayer[']").each(function(f) {
1032			var flowplayer_rel = f.readAttribute("rel");
1033			var flowplayer_match = flowplayer_rel.match(/flowplayer\[(.*)\]/i);
1034
1035			if (Object.isArray(flowplayer_match) && flowplayer_match.size() > 1) {
1036				var flowplayer_id = flowplayer_match.last();
1037
1038				if (flowplayer(flowplayer_id).isLoaded()) {
1039					flowplayer(flowplayer_id).stop();
1040					flowplayer(flowplayer_id).unload();
1041					this.videoIsPlaying = false;
1042					this.resume();
1043				}
1044			}
1045		}.bind(this));
1046	},
1047
1048	//
1049	// toggle on and off navigation (used during the transition)
1050	//
1051	toggleNavigation: function(buttonNumber, state) {
1052		// set defaults
1053		if (state == undefined) { state = 'on'; }
1054		if (buttonNumber == undefined) { buttonNumber = 'all'; }
1055		
1056		if (this.isPaused && !!this.flowplayerID) { this._flowplayer_stop(); }
1057	
1058		// change the navigation
1059		if (buttonNumber == 'all') {
1060	
1061			// loop through all the navigation buttons
1062			this.objWindow.each(function(w, index) {
1063
1064				// don't turn on the button for the active image
1065				if (index != this.active_content) {
1066					this.toggleNavigation(index, state);
1067				}
1068			}.bind(this));
1069
1070		} else {
1071			if (state == 'off') {
1072				this.objNavigation[buttonNumber].setStyle({
1073					cursor: "default"
1074				}).stopObserving("click");
1075			
1076			} else {
1077				this.objNavigation[buttonNumber].setStyle({
1078					cursor: "pointer"
1079				}).stopObserving("click").observe('click', function(event) {
1080					this.doTransition(buttonNumber);
1081				}.bind(this));
1082			}
1083		}
1084	},
1085	
1086	updateAnchor: function(content) {
1087		this.objAnchor.stopObserving("click");
1088		this.flowplayerID = false;
1089
1090		if (this.objWindow[content].down("a")) {
1091			var anchor_rel = this.objWindow[content].down("a").readAttribute("rel");
1092			var anchor_target = this.objWindow[content].down("a").readAttribute("target") || "_self";
1093			var flowplayer_match = "";
1094			
1095			if (anchor_rel) {
1096				flowplayer_match = anchor_rel.match(/flowplayer\[(.*)\]/i);
1097			}
1098
1099			if (anchor_rel != "no_follow") {
1100				this.objAnchor.show().writeAttribute("href", this.objWindow[content].down("a").readAttribute("href")).writeAttribute("target", anchor_target);
1101			} else {
1102				this.objAnchor.hide().writeAttribute("href", "");
1103			}
1104				
1105			// found a flowplayer object
1106			if (Object.isArray(flowplayer_match) && flowplayer_match.size() > 1) {
1107
1108				this.flowplayerID = flowplayer_match[1];
1109
1110				// if we're hiding the overlay
1111				if (this.settings.video.hideOverlay) {
1112					this.objAnchor.hide();
1113					if (this.objOverlay) {
1114						this.objOverlay.hide();
1115					}
1116
1117				// otherwise, show an inline popup with the video
1118				} else {
1119
1120					this.objAnchor.observe("click", function(event) {
1121						event.stop();
1122
1123						this._flowplayer_play();
1124					}.bind(this));
1125				}
1126			}
1127
1128		} else {
1129			this.objAnchor.hide().writeAttribute("href", "");
1130		}
1131	},
1132	
1133	_sliderInit: function() {
1134		this.slider_container = this.objNavigationContainer.up("." + this.settings.slider.container);
1135		this.slider_button_next = this.eventbox_container.down("." + this.settings.slider.button_next);
1136		this.slider_button_previous = this.eventbox_container.down("." + this.settings.slider.button_previous);
1137		this.slider_navigation_timeout = "";
1138		this.slider_handle = new Element("span", {style: "display: none;"});
1139		this.slider_container.insert(this.slider_handle);
1140
1141		var additional_height_props = ["margin-top", "margin-bottom"];
1142		var additional_width_props = ["margin-left", "margin-right"];
1143		var additional_props = (this.settings.slider.orientation == "vertical") ? additional_height_props : additional_width_props;
1144		var slider_container_dimension = (this.settings.slider.orientation == "vertical") ? this.slider_container.getHeight() : this.slider_container.getWidth();
1145		var slider_elements_total_dimension = 0;
1146
1147		this.objNavigation.each(function(ci) {
1148			slider_elements_total_dimension += (this.settings.slider.orientation == "vertical") ? ci.getHeight() : ci.getWidth();
1149			additional_props.each(function(p) {
1150				prop_value = parseInt(ci.getStyle(p));
1151				if (!isNaN(prop_value)) {
1152					slider_elements_total_dimension += prop_value;
1153				}
1154			});
1155
1156		}.bind(this));
1157		
1158		if (this.settings.slider.orientation == "vertical") {
1159			this.objNavigationContainer.setStyle({height: slider_elements_total_dimension + "px"});
1160		} else {
1161			this.objNavigationContainer.setStyle({width: slider_elements_total_dimension + "px"});
1162		}
1163		
1164		this.settings.slider.item_dimension = slider_elements_total_dimension / this.objNavigation.size();
1165		this.settings.slider.max_range = slider_elements_total_dimension - slider_container_dimension + 1;
1166		this.settings.slider.current_value = 0; // update to account for "this.settings.firstSlide"
1167		
1168		this.slider_values = $R(0,this.settings.slider.max_range).partition(function(n) {
1169			return 0 == n % (this.settings.slider.item_dimension * this.settings.slider.step);
1170		}.bind(this)).first();
1171		
1172		(this.slider_values.last() != this.settings.slider.max_range) ? this.slider_values.push(this.settings.slider.max_range) : Prototype.emptyFunction ;
1173		
1174		this.slider_control = new Control.Slider(this.slider_handle, this.slider_container, {
1175			axis: this.settings.slider.orientation,
1176			range: $R(0,this.settings.slider.max_range),
1177			increment: this.settings.slider.item_dimension,
1178			values: this.slider_values,
1179			onChange: function(v) {
1180				var move_property = (this.settings.slider.orientation == "vertical") ? "top" : "left";
1181				new Effect.Morph(this.objNavigationContainer, {
1182					style: move_property + ": " + v * -1 + "px",
1183					duration: this.settings.duration
1184				});
1185			}.bind(this)
1186		});
1187
1188		// setup slider navigation handlers
1189		if (this.slider_button_next) {
1190
1191			if (this.objNavigation.size() > this.settings.slider.view) {
1192				this.slider_button_next.observe("click", function(event) {
1193					event.stop();
1194
1195					this.settings.slider.current_value += this.settings.slider.item_dimension * this.settings.slider.step;
1196
1197					if (this.settings.slider.current_value > this.settings.slider.max_range) {
1198						this.settings.slider.current_value = this.settings.slider.max_range;
1199					}
1200
1201					this.slider_control.setValue(this.settings.slider.current_value);
1202				}.bind(this));
1203			}
1204		}
1205		
1206		if (this.slider_button_previous) {
1207			if (this.objNavigation.size() > this.settings.slider.view) {
1208				this.slider_button_previous.observe("click", function(event) {
1209					event.stop();
1210
1211					this.settings.slider.current_value -= this.settings.slider.item_dimension * this.settings.slider.step;
1212
1213					if (this.settings.slider.current_value < 0) {
1214						this.settings.slider.current_value = 0;
1215					} else if (this.slider_values.indexOf(this.settings.slider.current_value) < 0) {
1216						this.settings.slider.current_value = this.slider_values.detect(function(n) {
1217							return n > this.settings.slider.current_value;
1218						}.bind(this));
1219					}
1220
1221					this.slider_control.setValue(this.settings.slider.current_value);
1222				}.bind(this));
1223			}
1224		}
1225		
1226		this._sliderMove();
1227	},
1228	
1229	_sliderMove: function() {
1230		var active_content_position = this.active_content * this.settings.slider.item_dimension;
1231		var slider_viewport_dimension = this.settings.slider.item_dimension * this.settings.slider.view;
1232
1233		// see if we're moving to the next
1234		if (active_content_position - this.settings.slider.current_value >= slider_viewport_dimension) {
1235			this.settings.slider.current_value += this.settings.slider.item_dimension * this.settings.slider.step;
1236
1237			if (this.settings.slider.current_value > this.settings.slider.max_range) {
1238				this.settings.slider.current_value = this.settings.slider.max_range;
1239			}
1240			
1241			if (active_content_position - this.settings.slider.current_value >= slider_viewport_dimension) {
1242				this._sliderMove();
1243			} else {
1244				this.slider_control.setValue(this.settings.slider.current_value);
1245			}
1246
1247
1248		// see if we're moving to the previous
1249		} else if (active_content_position < this.settings.slider.current_value) {
1250			this.settings.slider.current_value -= this.settings.slider.item_dimension * this.settings.slider.step;
1251			
1252			if (this.settings.slider.current_value < 0) {
1253				this.settings.slider.current_value = 0;
1254			}
1255
1256			if (active_content_position < this.settings.slider.current_value) {
1257				this._sliderMove();
1258			} else {
1259
1260				if (this.slider_values.indexOf(this.settings.slider.current_value) < 0) {
1261					this.settings.slider.current_value = this.slider_values.reverse(false).detect(function(n) {
1262						return n <= active_content_position;
1263					}.bind(this));
1264				}
1265
1266				this.slider_control.setValue(this.settings.slider.current_value);
1267			}
1268		}
1269	},
1270	
1271	_detectMacXFF: function() {
1272		var userAgent = navigator.userAgent.toLowerCase();
1273		if (userAgent.indexOf('mac') != -1 && userAgent.indexOf('firefox')!=-1) {
1274			return true;
1275		}
1276	},
1277	
1278	viewerCreate: function() {
1279		this.viewer_parent = $(document.getElementsByTagName("body").item(0));
1280		this.viewer_backdrop = new Element("div", {style: "display: none;"});
1281		this.viewer_container = new Element("div", {style: "display: none;"});
1282		this.viewer_window = new Element("div");
1283		this.viewer_content = new Element("div");
1284		this.viewer_close = new Element("a", {href: "#"}).insert(
1285			new Element("img", {src: "/includes/eventbox/images/viewer_close.png"})
1286		);
1287		
1288		if (!this.settings.video.popupSettings.className.blank()) {
1289			this.viewer_container.addClassName(this.settings.video.popupSettings.className);
1290		}
1291		
1292		var d = this._detectMacXFF();
1293		if (d) {
1294			//osx ff css opacity + flash wmode transparent doesn't work
1295			this.viewer_backdrop.setStyle({
1296				backgroundImage: "url(/includes/login/trans.png)",
1297				backgroundRepeat: "repeat",
1298				backgroundColor: ""
1299			});
1300			
1301		} else {
1302			this.viewer_backdrop.setStyle({
1303				opacity: 0.7,
1304				backgroundColor: "#000"
1305			});
1306		}
1307
1308		this.viewer_backdrop.setStyle({
1309			position: "fixed",
1310			top: 0,
1311			left: 0,
1312			width: "100%",
1313			height: "100%",
1314			zIndex: 9000
1315		});
1316
1317		this.viewer_container.setStyle({
1318			position: "absolute",
1319			top: parseInt(this.settings.video.popupSettings.top) + "px",
1320			left: "100px",
1321			width: "500px",
1322			height: "500px",
1323			padding: parseInt(this.settings.video.popupSettings.padding) + "px",
1324			backgroundColor: this.settings.video.popupSettings.backgroundColor,
1325			zIndex: 9010
1326		});
1327		
1328		this.viewer_close.setStyle({
1329			position: "absolute",
1330			top: "-15px",
1331			right: "-15px",
1332			opacity: 0.8,
1333			zIndex: 9015
1334		});
1335
1336		this.viewer_parent.insert({top: this.viewer_backdrop});
1337		this.viewer_backdrop.insert({after: this.viewer_container});
1338		this.viewer_container.insert(this.viewer_window);
1339		this.viewer_window.insert(this.viewer_close);
1340		this.viewer_window.insert(this.viewer_content);
1341		
1342		[this.viewer_backdrop, this.viewer_close].each(function(c) {
1343			c.observe("click", function(event) {
1344				event.stop();
1345				this.videoIsPlaying = false;
1346				this.resume();
1347				this.viewerDestroy();
1348			}.bind(this));
1349		}.bind(this));
1350	},
1351	
1352	viewerDestroy: function() {
1353		this.viewer_backdrop.fade({
1354			duration: 0.5,
1355			afterFinish: function() {
1356				this.viewer_backdrop.remove();
1357			}.bind(this)
1358		});
1359		this.viewer_container.fade({
1360			duration: 0.5,
1361			afterFinish: function() {
1362				this.viewer_container.remove();
1363			}.bind(this)
1364		});
1365	},
1366	
1367	_viewerMove: function() {
1368		var offset = document.viewport.getScrollOffsets();
1369	
1370		this.viewer_container.setStyle({
1371			top: offset.top + parseInt(this.settings.video.popupSettings.top) + "px",
1372			left: ((this.viewer_backdrop.getWidth() - this.viewer_container.getWidth()) / 2) + "px"
1373		});
1374	},
1375
1376	viewerShow: function() {
1377		[this.viewer_backdrop, this.viewer_container].invoke("show");
1378		this._viewerMove();
1379	}
1380	
1381});
1382

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.