var nanofadeProto = {

    fade_container: null,
    anchor: null,
    pic_data: null,
    /* controling variables */
    options : {
        zIndex : 10,
        duration : 1500,
        slidetime : 6000,
        imageClass : 'fade_img',
        aClass : 'fade_a',
        auto_goto_next: true,
        align: {h: 'top', v: 'left'}
    },
    anim_f: 'fade',
    auto_goto_next: true,
    duration : null,
    slidetime : null,
    fadeTimer : null,
    /* animation variables */
    animating: true, // true to prevent calles to animation before it's ready
    transProp : null,
    transEndEvent : null,
    filter : 'DXImageTransform.Microsoft.Fade',
    transType : null,
    el1 : null,
    el2 : null,
    currentEl : null,
    nextEl : null,
    nextEl_i: 0, // general picture iteration counter.
    current_i : 0,
    next_i: 0,

	getTransitionType : function(el) {
		var props = {
			'WebkitTransition': 'webkitTransitionEnd',
			'MozTransition': 'transitionend',
			'Transition': 'transitionend'
		};
		for (var p in props) {
			if (el.style[p] !== undefined) {
				this.transProp = p;
				this.transEndEvent = props[p];
			}
		}
		if (this.transProp !== null) {
			return 'css';
		}
		else if (el.filters !== undefined) {
			return 'filter';
		}
		return 'anim';
	},
	stack : function() {
		this.currentEl.style.zIndex = this.options.zIndex + 1;
        this.next_i = this.get_next_pic_i();
        this.nextEl_i ++;
		this.nextEl = this['el'+(this.nextEl_i%2)];// (this.currentEl == this.el0) ? this.el1 : this.el0;
		this.nextEl.style[this.transProp] = '';
		this.nextEl.style.zIndex = this.options.zIndex;
		this.nextEl.style.opacity = 0;
        this.nextEl.src = this.pic_data.pic_list[this.next_i].src;
        this.checkLink(this.current_i);
		/*this.show(this.nextEl, this.pic_data.pic_list[this.next_i].src);
        this.schedule(this.checkLink, this.duration / 2, [this.current_i]);*/
	},
    /* fade effect functions */
    animFade : function(el, from, to, dur, after){
        var start = +new Date, finish = start+dur;
        var self = this;
        var interval = setInterval(function(){
            var time = +new Date, pos = time>finish ? 1 : (time-start)/dur;
            //pos = ((-Math.cos(pos*Math.PI)/2) + 0.5) + to;    //easing
            el.style['opacity'] = ((from+(to-from)*pos).toFixed(3));
            if(time>finish) {
                clearInterval(interval);
                after && self.schedule(after, 1);
            }
        },10);
    },
    cssFade : function(el, from, to, dur) {
        el.style[this.transProp] = 'opacity '+(dur/1000)+'s linear';
        el.style.opacity = to;
    },
    filterFade : function(el, dur) {
        el.style.filter = 'progid:'+this.filter+'(duration='+(dur/1000)+')';
        el.filters.item(this.filter).apply();
        //this.next_i = this.get_next_pic_i();
        this.do_pic_align(el);
        el.src = this.pic_data.pic_list[this.next_i].src;
        this.schedule(this.checkLink, this.duration / 2, [this.next_i]);
        el.filters.item(this.filter).play();
    },
    checkLink: function($i){
        if (this.pic_data.pic_list[$i].href != ''){
            this.anchor.href = this.pic_data.pic_list[$i].href[$i].href;
            this.anchor.target = this.pic_data.pic_list[$i].href[$i].target;
        } else {
            this.anchor.removeAttribute('href');
        }
    },
  	fade : function() {
        //var _next_i = (this.current < this.pic_data.pics.length - 1) ? this.current + 1 : 0;
		if (!this.nextEl.complete || this.animating == true) {
			this.schedule(this.fade, 500);
			return false;
		}
        this.animating = true;
        this.fadeTimer = 0;
        this.nextEl.style.opacity = 1;
        this.onAnimStart(this.current_i, this.next_i);
		if (this.transType == 'css') {
            this.do_pic_align(this.nextEl);
            this.nextEl.src = this.pic_data.pic_list[this.next_i].src;
			this.cssFade(this.currentEl, 1, 0, this.duration);
            this.currentEl = this.nextEl;
		}
		else if (this.transType == 'filter') {
			this.filterFade(this.el0, this.duration);1
		}
		else {
            this.do_pic_align(this.nextEl);
            this.nextEl.src = this.pic_data.pic_list[this.next_i].src;
			this.animFade(this.currentEl, 1, 0, this.duration, this.stack);
            this.currentEl = this.nextEl;
		}

		this.current_i = this.next_i;
        //this.goto_pic();//this.schedule(this.fade, this.slidetime);
	},
    jump_to_picture: function($pic_i){
        this.next_i = $pic_i;
        try {this.currentEl.style.filter = '';} catch(e){};
        this.currentEl.src = this.pic_data.pic_list[this.next_i].src;
        //this.currentEl.filters.item(this.filter).play();
        //console.log(this.pic_data.pic_list[this.next_i].src);
        this.current_i = this.next_i;
        this.checkLink(this.current_i);
        this.onAnimEnd(this.current_i);
    },
    /* aligning function */
    do_pic_align: function($el){
        var _pic_size = {w: this.pic_data.pic_list[this.next_i].width, h: this.pic_data.pic_list[this.next_i].height};
        var _cont_size = {w: this.fade_container.offsetWidth, h: this.fade_container.offsetHeight};
        if (this.options.align.v == 'center') $el.style.top = Math.round((_cont_size.h - _pic_size.h)/2) + 'px';
        if (this.options.align.v == 'bottom') $el.style.top = Math.round(_cont_size.h - _pic_size.h) + 'px';
        if (this.options.align.v == 'top') $el.style.top = '0px';
        if (this.options.align.h == 'center') $el.style.left = Math.round((_cont_size.w - _pic_size.w)/2) + 'px';
        if (this.options.align.h == 'right') $el.style.left = Math.round((_cont_size.w - _pic_size.w)) + 'px';
        if (this.options.align.h == 'left') $el.style.left = '0px';
    },
    /* & fade effect functions */
    initialize : function(el, data, options) {
        this.pic_data = data;
        options = options || {};
        this.options = $merge(this.options, options);

        this.duration = this.options.duration;
        this.slidetime = this.options.slidetime;
        this.current_i = this.options.start_pic_i;
        this.auto_goto_next = this.options.auto_goto_next;
        this.prepare_markup(el);
        //this.pic_data.links = data.links;
        this.currentEl = this.el0;
        this.do_pic_align(this.currentEl);

        this.attach_event(this.el0);
        this.attach_event(this.el1);
        this.stack();
        if (this.transType == 'filter') this.nextEl.src = 'http://www.tibe.no/molde/site/pages/shim.gif'; // some IE bug, doesn't work if no src is set
        this.animating = false;
        if (this.auto_goto_next == true) this.goto_pic();
        this.init_gallery_dom();
    },

    attach_event: function(anim_el){
        var self = this;
        anim_el.style.cssText = 'position:absolute;top:0;left:0;';
        if (this.transType == 'css') { // FF, GChrome, Safari ....
            anim_el.addEventListener(this.transEndEvent, function() {
                self.stack();
                self.animating = false;
                self.goto_pic();
                self.onAnimEnd(self.current_i);
            });
        }
        if (this.transType == 'filter'){ // IE
            anim_el.onfilterchange = function(){
                self.animating = false;
                self.goto_pic();
                self.next_i = self.get_next_pic_i();
                self.onAnimEnd(self.current_i);
            };
        }
    },

    prepare_markup: function(el){
        this.el0 = (typeof el == 'string' ? document.getElementById(el) : el);
        this.el1 = document.createElement('img');
        this.el0.className = this.el1.className = (this.options.imageClass);
        this.transType = this.getTransitionType(this.el0);
        var wrapper = document.createElement('div');
        wrapper.style.position = 'relative';
        wrapper.style.overflow = 'hidden';
        if (this.options.height) {
            wrapper.style.height = this.options.height + 'px';
        }
        if (this.options.width) {
            wrapper.style.width = this.options.width + 'px';
        }
        this.el0.parentNode.replaceChild(wrapper, this.el0);
        wrapper.appendChild(this.el0);
        wrapper.appendChild(this.el1);
        this.anchor = document.createElement('a');
        var self = this;
        this.anchor.onclick = function(){
            self.onClick(self.current_i);
        };
        wrapper.parentNode.replaceChild(this.anchor, wrapper);
        this.fade_container = wrapper;
        this.anchor.appendChild(wrapper);
        this.anchor.style.border = '0';
        this.anchor.className = this.options.aClass;
    },

	schedule : function(fn, time, args) {
		var self = this;
        args = args || [];
		this.fadeTimer = setTimeout(function() {
			fn.apply(self, args);
		}, time);
	},
    get_next_pic_i: function(){
        return (this.current_i < this.pic_data.pic_list.length - 1) ? this.current_i + 1 : 0;
    },
    get_previous_pic_i: function(){
        return (this.current_i > 0 ? this.current_i - 1 : this.pic_data.pic_list.length - 1);
    },
    /*
            ment for public access
    */
    goto_pic: function($pic_i){
        ///console.log(this.animating, this.pic_data.pics.length, this.auto_goto_next);
        if (this.animating == true) return false;
        if (this.pic_data.pic_list.length > 0){
            clearTimeout(this.fadeTimer);
            if (!isNaN($pic_i) && +$pic_i != this.current_i) {
                this.next_i = $pic_i;
                this[this.anim_f]();
            }
            if (this.auto_goto_next == true && !$pic_i) {
                this.schedule(this[this.anim_f], this.slidetime - this.duration);
            }
        }
        return true;
    },
    jump_to_pic: function($pic_i){
        if (this.animating == true) return false;
        if (this.pic_data.pic_list.length > 0){
            clearTimeout(this.fadeTimer);
            this.jump_to_picture($pic_i);
            if (this.auto_goto_next == true) {
                this.schedule(this[this.anim_f], this.slidetime - this.duration);
            }
            return true;
        }
        return false;
    },
    goto_previous_img: function(){
        var _i = this.get_previous_pic_i();
        return (this.goto_pic(_i) == true) ? _i : this.current_i;
    },
    goto_next_img: function(){
        var _i = this.get_next_pic_i();
        return (this.goto_pic(_i) == true) ? _i : this.current_i;
    },
    get_pic_info: function($pic_i){
        return this.pic_data.pic_list[$pic_i];
    },
    set_anim_status: function($status){
        if (this.auto_goto_next == false && $status == true) this.goto_pic();
        if (this.auto_goto_next == true && $status == false) clearTimeout(this.fadeTimer);
        this.auto_goto_next = $status;
    },
    /** Function ment to be called when element is visible so that aligning of pic elements could be done.
    */
    init_gallery_dom: function(){
        this.do_pic_align(this.currentEl);
    },
    onAnimStart: function($current_pic_i, $next_i){
        //console.log('anim_start', this.animating, $current_pic_i, $next_i);
    },
    onAnimEnd: function($current_pic_i){
        //console.log('anim_end', this.animating, $current_pic_i);
    },
    onClick: function($current_pic_i){
    }
}

function nanofade() {
	var instance = new function() {};
	for (var a in nanofadeProto) {
		instance[a] = nanofadeProto[a];
	}
	nanofadeProto.initialize.apply(instance, arguments);
	return instance;
}

