(function(jQuery) {
    function stripFiletype(ref) {
        var x = ref.replace('.html', '');
        return x.replace('#', '');
    }
    function initOrigin(l) {
        if (l.xorigin == 'left') {
            l.xorigin = 0;
        } else if (l.xorigin == 'middle' || l.xorigin == 'centre' || l.xorigin == 'center') {
            l.xorigin = 0.5;
        } else if (l.xorigin == 'right') {
            l.xorigin = 1;
        }
        if (l.yorigin == 'top') {
            l.yorigin = 0;
        } else if (l.yorigin == 'middle' || l.yorigin == 'centre' || l.yorigin == 'center') {
            l.yorigin = 0.5;
        } else if (l.yorigin == 'bottom') {
            l.yorigin = 1;
        }
    }
    function positionMouse(mouseport, localmouse, virtualmouse) {
        var difference = {
            x: 0,
            y: 0,
            sum: 0
        };
        if (!mouseport.ontarget) {
            difference.x = virtualmouse.x - localmouse.x;
            difference.y = virtualmouse.y - localmouse.y;
            difference.sum = Math.sqrt(difference.x * difference.x + difference.y * difference.y);
            virtualmouse.x = localmouse.x + difference.x * mouseport.takeoverFactor;
            virtualmouse.y = localmouse.y + difference.y * mouseport.takeoverFactor;
            if (difference.sum < mouseport.takeoverThresh && difference.sum > mouseport.takeoverThresh * -1) {
                mouseport.ontarget = true;
            }
        } else {
            virtualmouse.x = localmouse.x;
            virtualmouse.y = localmouse.y;
        }
    }
    function setupPorts(viewport, mouseport) {
        var offset = mouseport.element.offset();
        jQuery.extend(viewport, {
            width: viewport.element.width(),
            height: viewport.element.height()
            });
        jQuery.extend(mouseport, {
            width: mouseport.element.width(),
            height: mouseport.element.height(),
            top: offset.top,
            left: offset.left
        });
    }
    function parseTravel(travel, origin, dimension) {
        var offset;
        var cssPos;
        if (typeof(travel) === 'string') {
            if (travel.search(/^\d+\s?px$/) != -1) {
                travel = travel.replace('px', '');
                travel = parseInt(travel, 10);
                offset = origin * (dimension - travel);
                cssPos = origin * 100 + '%';
                return {
                    travel: travel,
                    travelpx: true,
                    offset: offset,
                    cssPos: cssPos
                };
            } else if (travel.search(/^\d+\s?%$/) != -1) {
                travel.replace('%', '');
                travel = parseInt(travel, 10) / 100;
            } else {
                travel = 1;
            }
        }
        offset = origin * (1 - travel);
        return {
            travel: travel,
            travelpx: false,
            offset: offset
        }
    }
    function setupLayer(layer, i, mouseport) {
        var xStuff;
        var yStuff;
        var cssObject = {};
        layer[i] = jQuery.extend({}, {
            width: layer[i].element.width(),
            height: layer[i].element.height()
            }, layer[i]);
        xStuff = parseTravel(layer[i].xtravel, layer[i].xorigin, layer[i].width);
        yStuff = parseTravel(layer[i].ytravel, layer[i].yorigin, layer[i].height);
        jQuery.extend(layer[i], {
            diffxrat: mouseport.width / (layer[i].width - mouseport.width),
            diffyrat: mouseport.height / (layer[i].height - mouseport.height),
            xtravel: xStuff.travel,
            ytravel: yStuff.travel,
            xtravelpx: xStuff.travelpx,
            ytravelpx: yStuff.travelpx,
            xoffset: xStuff.offset,
            yoffset: yStuff.offset
        });
        if (xStuff.travelpx) {
            cssObject.left = xStuff.cssPos;
        }
        if (yStuff.travelpx) {
            cssObject.top = yStuff.cssPos;
        }
        if (xStuff.travelpx || yStuff.travelpx) {
            layer[i].element.css(cssObject);
        }
    }
    function setupLayerContents(layer, i, viewportOffset) {
        var contentOffset;
        jQuery.extend(layer[i], {
            content: []
            });
        for (var n = 0; n < layer[i].element.children().length; n++) {
            if (!layer[i].content[n])
                layer[i].content[n] = {};
            if (!layer[i].content[n].element)
                layer[i].content[n]['element'] = layer[i].element.children().eq(n);
            if (!layer[i].content[n].anchor && layer[i].content[n].element.children('a').attr('name')) {
                layer[i].content[n]['anchor'] = layer[i].content[n].element.children('a').attr('name');
            }
            if (layer[i].content[n].anchor) {
                contentOffset = layer[i].content[n].element.offset();
                jQuery.extend(layer[i].content[n], {
                    width: layer[i].content[n].element.width(),
                    height: layer[i].content[n].element.height(),
                    x: contentOffset.left - viewportOffset.left,
                    y: contentOffset.top - viewportOffset.top
                });
                jQuery.extend(layer[i].content[n], {
                    posxrat: (layer[i].content[n].x + layer[i].content[n].width / 2) / layer[i].width,
                    posyrat: (layer[i].content[n].y + layer[i].content[n].height / 2) / layer[i].height
                });
            }
        }
    }
    function moveLayers(layer, xratio, yratio) {
        var xpos;
        var ypos;
        var cssObject;
        for (var i = 0; i < layer.length; i++) {
            xpos = layer[i].xtravel * xratio + layer[i].xoffset;
            ypos = layer[i].ytravel * yratio + layer[i].yoffset;
            cssObject = {};
            if (layer[i].xparallax) {
                if (layer[i].xtravelpx) {
                    cssObject.marginLeft = xpos * -1 + 'px';
                } else {
                    cssObject.left = xpos * 100 + '%';
                    cssObject.marginLeft = xpos * layer[i].width * -1 + 'px';
                }
            }
            if (layer[i].yparallax) {
                if (layer[i].ytravelpx) {
                    cssObject.marginTop = ypos * -1 + 'px';
                } else {
                    cssObject.top = ypos * 100 + '%';
                    cssObject.marginTop = ypos * layer[i].height * -1 + 'px';
                }
            }
            layer[i].element.css(cssObject);
        }
    }
    jQuery.fn.jparallax = function(options) {
        var settings = jQuery().extend({}, jQuery.fn.jparallax.settings, options);
        var settingsLayer = {
            xparallax: settings.xparallax,
            yparallax: settings.yparallax,
            xorigin: settings.xorigin,
            yorigin: settings.yorigin,
            xtravel: settings.xtravel,
            ytravel: settings.ytravel
        };
        var settingsMouseport = {
            element: settings.mouseport,
            takeoverFactor: settings.takeoverFactor,
            takeoverThresh: settings.takeoverThresh
        };
        if (settings.mouseport)
            settingsMouseport['element'] = settings.mouseport;
        var layersettings = [];
        for (var a = 1; a < arguments.length; a++) {
            layersettings.push(jQuery.extend({}, settingsLayer, arguments[a]));
        }
        return this.each(function() {
            var localmouse = {
                x: 0.5,
                y: 0.5
            };
            var virtualmouse = {
                x: 0.5,
                y: 0.5
            };
            var timer = {
                running: false,
                frame: settings.frameDuration,
                fire: function(x, y) {
                    positionMouse(mouseport, localmouse, virtualmouse);
                    moveLayers(layer, virtualmouse.x, virtualmouse.y);
                    this.running = setTimeout(function() {
                        if (localmouse.x != x || localmouse.y != y || !mouseport.ontarget) {
                            timer.fire(localmouse.x, localmouse.y);
                        } else if (timer.running) {
                            timer.running = false;
                        }
                    }, timer.frame);
                }
            };
            var viewport = {
                element: jQuery(this)
                };
            var mouseport = jQuery.extend({}, {
                element: viewport.element
            }, settingsMouseport, {
                xinside: false,
                yinside: false,
                active: false,
                ontarget: false
            });
            var layer = [];
            function matrixSearch(layer, ref, callback) {
                for (var i = 0; i < layer.length; i++) {
                    var gotcha = false;
                    for (var n = 0; n < layer[i].content.length; n++) {
                        if (layer[i].content[n].anchor == ref) {
                            callback(i, n);
                            return [i, n];
                        }
                    }
                }
                return false;
            }
            setupPorts(viewport, mouseport);
            for (var i = 0; i < viewport.element.children().length; i++) {
                layer[i] = jQuery.extend({}, settingsLayer, layersettings[i], {
                    element: viewport.element.children('*:eq(' + i + ')')
                    });
                setupLayer(layer, i, mouseport);
                if (settings.triggerResponse) {
                    setupLayerContents(layer, i, viewport.element.offset());
                }
            }
            viewport.element.children().css('position', 'absolute');
            moveLayers(layer, 0.5, 0.5);

            if (settings.mouseResponse) {
                jQuery().mousemove(function(mouse) {
                    mouseport.xinside = (mouse.pageX >= mouseport.left && mouse.pageX < mouseport.width + mouseport.left) ? true: false;
                    mouseport.yinside = (mouse.pageY >= mouseport.top && mouse.pageY < mouseport.height + mouseport.top) ? true: false;
                    if (mouseport.xinside && mouseport.yinside && !mouseport.active) {
                        mouseport.ontarget = false;
                        mouseport.active = true;
                    }
                    if (mouseport.active) {
                        if (mouseport.xinside) {
                            localmouse.x = (mouse.pageX - mouseport.left) / mouseport.width;
                        } else {
                            localmouse.x = (mouse.pageX < mouseport.left) ? 0: 1;
                        }
                        if (mouseport.yinside) {
                            localmouse.y = (mouse.pageY - mouseport.top) / mouseport.height;
                        } else {
                            localmouse.y = (mouse.pageY < mouseport.top) ? 0: 1;
                        }
                    }
                    if (mouseport.xinside && mouseport.yinside) {
                        if (!timer.running)
                            timer.fire(localmouse.x, localmouse.y);
                    } else if (mouseport.active) {
                        mouseport.active = false;
                    }
                });
            }
            if (settings.triggerResponse) {
                viewport.element.bind("jparallax", function(event, ref) {
                    ref = stripFiletype(ref);
                    matrixSearch(layer, ref, function(i, n) {
                        localmouse.x = layer[i].content[n].posxrat * (layer[i].diffxrat + 1) - (0.5 * layer[i].diffxrat);
                        localmouse.y = layer[i].content[n].posyrat * (layer[i].diffyrat + 1) - (0.5 * layer[i].diffyrat);
                        if (!settings.triggerExposesEdges) {
                            if (localmouse.x < 0)
                                localmouse.x = 0;
                            if (localmouse.x > 1)
                                localmouse.x = 1;
                            if (localmouse.y < 0)
                                localmouse.y = 0;
                            if (localmouse.y > 1)
                                localmouse.y = 1;
                        }
                        mouseport.ontarget = false;
                        if (!timer.running)
                            timer.fire(localmouse.x, localmouse.y);
                    });
                });
            }
            /*jQuery(window).resize(function() {
                setupPorts(viewport, mouseport);
                for (var i = 0; i < layer.length; i++) {
                    setupLayer(layer, i, mouseport);
                }
            });*/
        });
    };
    jQuery.fn.jparallax.settings = {
        mouseResponse: true,
        mouseActiveOutside: false,
        triggerResponse: true,
        triggerExposesEdges: false,
        xparallax: true,
        yparallax: true,
        xorigin: 0.5,
        yorigin: 0.5,
        xtravel: 1,
        ytravel: 1,
        takeoverFactor: 0.65,
        takeoverThresh: 0.002,
        frameDuration: 25
    };
    initOrigin(jQuery.fn.jparallax.settings);
    jQuery(function() {});
})(jQuery);
