/**

 * ReMooz - Zoomer

 *

 *

 * Inspired by so many boxes and zooms

 *

 * @version		1.0

 *

 * @license		MIT-style license

 * @author		Harald Kirschner <mail [at] digitarald.de>

 * @copyright	Author

 */

var ReMooz = new Class({



	Implements: [Events, Options, Chain],



	options: {

		source: null,

		type: 'image',

		container: null,

		className: null,

		centered: false,

		dragging: true,

		closeOnClick: true,

		shadow: 'onOpen',

		resize: true,

		margin: 20,

		resizeFactor: 0.8,

		resizeLimit: false, // {x: 640, y: 640}

		fixedSize: false,

		hideSource: true,

		addClick: true,

		resizeOpacity: 1,

		resizeOptions: {},

		fxOptions: {},

		closer: true,

		destroy: false,

		onBuild: null,

		onLoad: null,

		onOpen: null,

		onOpenEnd: null,

		onClose: null,

		onCloseEnd: null,

		generateTitle: function(el) {

			var text = el.getProperty('title');

			if (!text) return false;

			var title = text.split(' :: ');

			var head = new Element('h6', {'html': title[0]});

			return (title[1]) ? [head, new Element('p', {'html': title[1]})] : head;

		}

	},



	initialize: function(element, options) {

		this.element = $(element);

		this.setOptions(options);

		this.options.source = this.options.source || this.element.getProperty('href') || this.element.getProperty('src');

		this.container = $(this.options.container) || this.element.ownerDocument;

		this.bound = {

			'click': function(e) {

				this.open.delay(1, this);

				return false;

			}.bind(this),

			'close': this.close.bind(this)

		};

		if (this.options.addClick) this.bindToElement();

	},



	destroy: function(unload) {

		if (this.box && !unload) this.box.destroy();

		this.box = this.tweens = this.body = this.content = null;

		return null;

	},



	bindToElement: function(el) {

		($(el) || this.element).addClass('remooz-element').addEvent('click', this.bound.click);

		return this;

	},



	getSourceCoordinates: function() {

		var coords = this.element.getCoordinates();

		delete coords.right;

		delete coords.bottom;

		return coords;

	},



	open: function(e) {

		if (this.opened) return (e) ? this.close() : this;

		this.opened = true;

		if (!this.box) this.build();

		this.coords = this.getSourceCoordinates();

		this.coords.opacity = 0.7;

		this.coords.display = '';

		this.tweens.box.set(this.coords);

		this.box.addClass('remooz-loading').setStyle('display', '');

		this.drag = (this.drag) ? this.drag.attach() : new Drag.Move(this.box, { // inits here because of safari

			'snap': 15,

			'onBeforeStart': function() {

				if (!this.focussed && !this.loading) ReMooz.focus(this);

				else if (this.loading || this.options.closeOnClick) this.box.addEvent('mouseup', this.bound.close);

			}.bind(this),

			'onSnap': function() {

				this.box.removeEvent('mouseup', this.bound.close);

				if (!this.options.dragging) this.drag.stop();

				else this.box.addClass('remooz-box-dragging');

			}.bind(this),

			'onComplete': function() {

				this.box.removeClass('remooz-box-dragging');

			}.bind(this)

		});

		this.loading = true;

		this.fireEvent('onLoad');

		this['open' + this.options.type.capitalize()]();

		return this;

	},



	finishOpen: function() {

		this.tweens.fade.start(0, 1);

		this.fireEvent('onOpenEnd').callChain();

	},



	close: function() {

		if (!this.opened) return this;

		this.opened = false;

		ReMooz.close(this.fireEvent('onClose'));

		if (this.loading) {

			this.box.hide();

			return this;

		}

		this.drag.detach();

		this.tweens.fade.cancel().set(0).fireEvent('onComplete');

		if (this.tweens.box.timer) this.tweens.box.clearChain();

		var vars = this.getSourceCoordinates();

		if (this.options.resizeOpacity != 1) vars.opacity = this.options.resizeOpacity;

		this.tweens.box.start(vars).chain(this.closeEnd.bind(this));

		return this;

	},



	closeEnd: function() {

		this.element.setStyle('visibility', 'visible');

		this.box.hide();

		this.fireEvent('onCloseEnd').callChain();

		if (this.options.destroy) this.destroy();

	},



	openImage: function() {

		var tmp = new Image();

		tmp.onload = tmp.onabort = tmp.onerror = function(fast) {

			this.loading = tmp.onload = tmp.onabort = tmp.onerror = null;

			if (!tmp.width || !this.opened) {

				this.fireEvent('onError').close();

				return;

			}

			var to = {x: tmp.width, y: tmp.height};

			if (!this.content) this.content = $(tmp).inject(this.body);

			else tmp = null;

			this[(this.options.resize) ? 'zoomRelativeTo' : 'zoomTo'].create({

				'delay': (tmp && fast !== true) ? 1 : null,

				'arguments': [to],

				'bind': this

			})();

		}.bind(this);

		tmp.src = this.options.source;

		if (tmp && tmp.complete && tmp.onload) tmp.onload(true);

	},



	/**

	 * @todo Test implementation

	 */

	openElement: function() {

		this.content = this.content || $(this.options.source) || $E(this.options.source);

		if (!this.content) {

			this.fireEvent('onError').close();

			return;

		}

		this.content.inject(this.body);

		this.zoomTo({x: this.content.scrollWidth, y: this.content.scrollHeight});

	},



	zoomRelativeTo: function(to) {

		var scale = this.options.resizeLimit;

		if (!scale) {

			scale = this.container.getSize();

			scale.x *= this.options.resizeFactor;

			scale.y *= this.options.resizeFactor;

		}

		for (var i = 2; i--;) {

			if (to.x > scale.x) {

				to.y *= scale.x / to.x;

				to.x = scale.x;

			} else if (to.y > scale.y) {

				to.x *= scale.y / to.y;

				to.y = scale.y;

			}

		}

		return this.zoomTo({x: to.x.toInt(), y: to.y.toInt()});

	},



	zoomTo: function(to) {

		to = this.options.fixedSize || to;

		var box = this.container.getSize(), scroll = this.container.getScroll();

		var pos = (!this.options.centered) ? {

			x: (this.coords.left + (this.coords.width / 2) - to.x / 2).toInt()

				.limit(scroll.x + this.options.margin, scroll.x + box.x - this.options.margin - to.x),

			y: (this.coords.top + (this.coords.height / 2) - to.y / 2).toInt()

				.limit(scroll.y + this.options.margin, scroll.y + box.y - this.options.margin - to.y)

		} :  {

			x: scroll.x + ((box.x - to.x) / 2).toInt(),

			y: scroll.y + ((box.y - to.y) / 2).toInt()

		};

		if (this.options.hideSource) this.element.setStyle('visibility', 'hidden');

		this.box.removeClass('remooz-loading');

		var vars = {left: pos.x, top: pos.y, width: to.x, height: to.y};

		if (this.options.resizeOpacity != 1) vars.opacity = [this.options.resizeOpacity, 1];

		else this.box.set('opacity', 1);

		ReMooz.open(this.fireEvent('onOpen'));

		this.tweens.box.start(vars).chain(this.finishOpen.bind(this));

	},



	build: function() {

		this.addEvent('onBlur', function() {

			this.focussed = false;

			this.box.removeClass('remooz-box-focus').setStyle('z-index', ReMooz.options.zIndex);

		}, true);

		this.addEvent('onFocus', function() {

			this.focussed = true;

			this.box.addClass('remooz-box-focus').setStyle('z-index', ReMooz.options.zIndexFocus);

		}, true);

		this.element.addEvent('trash', this.destroy.bind(this));



		var classes = ['remooz-box', 'remooz-type-' + this.options.type, 'remooz-engine-' + Browser.Engine.name + Browser.Engine.version];

		if (this.options.className) classes.push(this.options.className);

		this.box = new Element('div', {

			'class': classes.join(' '),

			'styles': {'display': 'none', 'zIndex': ReMooz.options.zIndex}

		});



		this.tweens = {

			'box': new Fx.Morph(this.box, $merge({

					'duration': 400,

					'unit': 'px',

					'transition': Fx.Transitions.Quart.easeOut,

					'chain': 'cancel'

				}, this.options.resizeOptions)

			),

			'fade': new Fx.Tween(null, 'opacity', $merge({

					'duration': 300,

					'chain': 'cancel'

				}, this.options.fxOptions)).addEvents({

					'onComplete': function() {

						if (!this.element.get('opacity')) this.element.setStyle('display', 'none');

					},

					'onStart': function() {

						if (!this.element.get('opacity')) this.element.setStyle('display', '');

					}

				}

			)

		};

		this.tweens.fade.element = $$();



		if (this.options.shadow && !Browser.Engine.trident4) {

			var shadow = new Element('div', {'class': 'remooz-bg-wrap'}).inject(this.box);

			['n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw'].each(function(dir) {

				new Element('div', {'class': 'remooz-bg remooz-bg-' + dir}).inject(shadow);

			});

			this.tweens.bg = new Fx.Tween(shadow, 'opacity', {'chain': 'cancel'}).set(0);

			this.addEvent(this.options.shadow, this.tweens.bg.set.bind(this.tweens.bg, 1), true);

			this.addEvent('onClose', this.tweens.bg.set.bind(this.tweens.bg, 0), true);

		}



		if (this.options.closer) {

			var closer = new Element('a', {

				'class': 'remooz-btn-close',

				'events': {'click': this.bound.close}

			}).inject(this.box);

			this.tweens.fade.element.push(closer);

		}

		this.body = new Element('div', {'class': 'remooz-body'}).inject(this.box);



		var title = this.options.title || this.options.generateTitle.call(this, this.element);

		if (title) {

			this.tweens.fade.element.push(new Element('div', {'class': 'remooz-title'}).adopt(

				new Element('div', {'class': 'remooz-title-bg', 'opacity': 0.75}),

				new Element('div', {'class': 'remooz-title-content'}).adopt(title)

			).inject(this.box));

		}

		this.tweens.fade.set(0).fireEvent('onComplete');

		this.fireEvent('onBuild', this.box, this.element);

		this.box.inject(this.element.getDocument().body);

	}



});



ReMooz.factory = function(extended) {

	return $extend(this, extended);

};



ReMooz.factory({



	options: {

		zIndex: 41,

		zIndexFocus: 42,

		query: 'a.remooz',

		optionsField: 'rel',

		classOptions: {}

	},



	initialize: function(elements, options) {

		this.setOptions(options);

		return $$(elements || this.options.query).map(function(el) {

			var obj = el.getProperty(this.options.optionsField);

			if (obj && (obj = Json.decode(obj, true))) obj = $merge(obj, this.options.classOptions);

			return new ReMooz(el, obj || this.options.classOptions);

		}, this);

	},



	stack: [],



	open: function(obj) {

		this.focus(obj);

	},



	close: function(obj) {

		var last = this.stack.length - 1;

		if (this.stack.indexOf(obj) == last) this.focus(this.stack[last - 1]);

		this.stack.remove(obj);

	},



	focus: function(obj) {

		var last = this.stack.getLast();

		if (!obj || last == obj) return;

		if (last) last.fireEvent('onBlur', [last], 10);

		obj.fireEvent('onFocus', [obj], 10);

		this.stack.remove(obj).push(obj);

	}



});



ReMooz.factory(new Options);

