/*  Noopty Slideshow & Noopty Image Scaler version 1.0
 *  (c) 2010 Outpost Design
 *
 *  SVN FILE: $Id: noopty.js 1171 2011-11-17 19:31:43Z mklauss $
 *--------------------------------------------------------------------------*/

var Noopty = Class.create({
	container:      null,
	slides:         [],
	currentSlide:   0,
	nextSlide:      0,
	initialize: function (args) {
		// update default options
		'use strict';
		var i, h = $H(args);
		h.each(function (item) {
			if (typeof (this.options[item.key]) !== 'undefined') {
				this.options[item.key] = item.value;
			}
		}.bind(this));
		// set slide container element
		if (this.options.containerId !== null) {
			this.container = $(this.options.containerId);
		}
		// select slide elements
		if (this.options.slide !== null) {
			this.slides = this.container.select(this.options.slide);
		} else {
			this.slides = this.container.childElements();
		}
		this.slides.each(function (s) { s.hide(); }); // hide them by default
		if (this.slides.length < 2) {
			return;
		}
		if (this.options.shuffle) { this.slides.shuffle(); } // mix them up
		// set a primary image as the default image (maybe something with a logo)
		// we swap the src to show a different version of the image
		// only when it's the very first image on display
		// after it's been seen, we roll it back to it's default value
		if (this.options.primary === true) {
			for (i = 0; i < this.slides.length; i++) {
				if (this.slides[i].readAttribute('primary') === 'true') {
					this.currentSlide = i;
					this.setprimary(this.slides[i]);
					break;
				}
			}
		}
		if (this.options.before !== null) {
			this.options.before(this.slides[this.currentSlide]);
		}
		this.slides[this.currentSlide].show();

		// attach UI events
		this.attachui();
	},
	attachui: function () {
		'use strict';
		if (this.options.hover) {
			this.slides.each(function (item) {
				if (this.options.mouseenter !== null) {
					$(item).observe('mouseenter', function (evt) {
						this.stop();
						this.options.mouseenter(evt);
					}.bindAsEventListener(this));
				}
				if (this.options.mouseleave !== null) {
					$(item).observe('mouseleave', function (evt) {
						this.options.mouseleave(evt);
						this.start();
					}.bindAsEventListener(this));
				}
			}.bind(this));
		}
		['next', 'back', 'stop', 'start'].each(function (item) {
			var action = function (evt) {
				evt.stop();
				this[item]();
			}.bindAsEventListener(this);
			$$(this.options[item]).each(function (item) {
				item.observe('click', action);
			});
		}.bind(this));
	},
	start: function () {
		'use strict';
		this.stop();
		if (this.options.timeout > 0) {
			this.peHandle = new PeriodicalExecuter(function () { this.next(); }.bind(this), (this.options.delay));
			this.options.delay = this.options.timeout; // delay used on initial start only
		}
	},
	stop: function () {
		'use strict';
		if (this.peHandle) {
			this.peHandle.stop();
		}
	},
	back: function () {
		'use strict';
		this.move('back');
	},
	next: function () {
		'use strict';
		this.move('next');
	},
	move: function (pos) {
		'use strict';
		if (this.slides.length < 2) {
			this.stop();
			return;
		}
		if (pos === 'next') {
			this.nextSlide = this.currentSlide + 1;
		} else if (pos === 'back') {
			this.nextSlide = this.currentSlide - 1;
		} else {
			this.nextSlide = pos;
		}
		if (this.nextSlide < 0) {
			this.nextSlide = this.slides.length - 1;
		}
		if (this.nextSlide >= this.slides.length) {
			this.nextSlide = 0;
		}
		this.transition();
	},
	transition: function () {
		'use strict';
		if (this.inprogress !== true) {
			// Prevent new transition while one is in progress
			this.inprogress = true;
			// Run before callback
			if (this.options.before !== null) {
				this.options.before(this.slides[this.nextSlide]);
			}
			// Fade out current slide - Fade in next slide
			this.slides[this.currentSlide].fade({
				duration: this.options.duration
			});
			this.slides[this.nextSlide].appear({
				duration: this.options.duration,
				afterFinish: function () {
					this.release();
				}.bind(this)
			});
		}
	},
	release: function () {
		'use strict';
		this.inprogress = false;
		// run after callback
		if (this.options.after !== null) {
			this.options.after(this.slides[this.currentSlide]);
		}
		if (this.options.primary === true) {
			this.removeprimary();
		}
		// Update current - next slide references
		this.currentSlide = this.nextSlide;
		this.nextSlide    = null;
		// Reset PeriodicalExecuter
		this.start();
	},
	setprimary: function (slide) {
		'use strict';
		if (this.options.primary_sufx === null) { return; }
		// find the <img> and update the src to pull the primary image
		var pos, img = (slide.tageName === 'IMG') ? slide : slide.select('img')[0];
		pos = img.src.lastIndexOf(".");
		img.src = img.src.substring(0, pos) + this.options.primary_sufx + img.src.substring(pos);
	},
	removeprimary: function () {
		'use strict';
		if (this.options.primary_sufx === null) { return; }
		this.slides.each(function (slide) {
			var img = (slide.tageName === 'IMG') ? slide : slide.select('img')[0];
			if (img.src.match(this.options.primary_sufx + '.')) {
				img.src = img.src.replace((this.options.primary_sufx + '.'), '.');
			}
		}.bind(this));
	},
	options: {
		containerId:   null,  // id of slide container element
		slide:         null,  // expression for selecting slides (if something other than all children is required)
		delay:         6.0,   // additional delay (in seconds) for first transition
		timeout:       6.0,   // seconds between slide transitions (0 to disable auto advance)
		duration:      1.0,   // speed of the transition in seconds
		next:          '[class="nooptyNext"]',  // expression for selecting elements to use as click trigger for next slide
		back:          '[class="nooptyBack"]',  // expression for selecting elements to use as click trigger for back slide
		stop:          '[class="nooptyStop"]',  // expression for selecting elements to use as click trigger for stop transitions
		start:         '[class="nooptyStart"]', // expression for selecting elements to use as click trigger for start transitions
		before:        null,  // transition callback (scope set to element to be shown)
		after:         null,  // transition callback (scope set to element that was shown)
		mouseenter:    null,  // mouseenter callback
		mouseleave:    null,  // mouseleave callback
		hover:         0,     // true to enable "pause on hover"
		shuffle:       0,     // randomize our array of slides
		primary:       0,     // load slides with html attribute "primary"="true" by default
		primary_sufx:  '-primary' // append a suffix to the default primary image to show an alternate version (with logo for example)
	},
	version: function () {
		'use strict';
		return '1.0';
	}
});

var NooptyScaler = Class.create({
	vp: null,
	imgs: [],
	initialize: function (args) {
		'use strict';
		// update default options
		var images, h = $H(args);
		h.each(function (item) {
			if (typeof (this.options[item.key]) !== 'undefined') {
				this.options[item.key] = item.value;
			}
		}.bind(this));
		// set slide container element
		if (this.options.containerId !== null && this.options.image !== null) {
			images = $(this.options.containerId).select(this.options.image);
		} else {
			images = $$(this.options.image);
		}
		images.each(function (image, idx) {
			this.imgs.push({
				ele: image,
				width: image.getWidth(),
				height: image.getHeight(),
				ratio: image.getWidth() / image.getHeight()
			});
			if (!this.options.rightclick) {
				image.oncontextmenu = function () { return false; };
			}
		}.bind(this));
		if (this.options.coverId !== null && !this.options.rightclick) {
			$(this.options.coverId).oncontextmenu = function () { return false; };
		}
		this.update();
	},
	update_vp: function () {
		'use strict';
		this.vp = $(this.options.containerId).getDimensions();
		this.vp.ratio = this.vp.width / this.vp.height;
	},
	update: function () {
		'use strict';
		this.update_vp();
		this.imgs.each(function (obj) {
			this.resize(obj);
		}.bind(this));
	},
	resize: function (obj) {
		'use strict';
		var w, h, t = 0, l = 0;
		if (this.vp.ratio === obj.ratio) {
			w = this.vp.width;
			h = this.vp.height;
		} else if (this.vp.ratio > obj.ratio) { // window more horizontal than image
			w = this.vp.width;
			h = Math.abs((w * obj.height) / obj.width);
			t = Math.abs((h - this.vp.height) / 2);
		} else { //  this.vp.ratio < obj.ratio // window more vertical than image
			h = this.vp.height;
			w = Math.abs((h * obj.width) / obj.height);
			l = Math.abs((w - this.vp.width) / 2);
		}
		obj.ele.setStyle({
			'width': w + 'px',
			'height': h + 'px',
			'left': -l + 'px',
			'top': -t + 'px'
		});
	},
	options: {
		containerId:   null,  // id of slide container element
		coverId:       null,  // id of slide cover element
		image:         null,  // expression for selecting slides (if something other than all children is required)
		rightclick:    true  // disable right click on image
	},
	version: function () {
		'use strict';
		return '1.0';
	}
});

Array.prototype.shuffle = function (deep) {
	'use strict';
	var i = this.length, j, t;
	while (i) {
		j = Math.floor((i--) * Math.random());
		t = (deep && typeof this[i].shuffle !== 'undefined') ? this[i].shuffle() : this[i];
		this[i] = this[j];
		this[j] = t;
	}
	return this;
};
