// source --> https://www.lifestyleelectronics.ie/wp-content/themes/archi/js/maplace.js?ver=6.0.11 
/**
* Maplace.js
*
* Copyright (c) 2013 Daniele Moraschi
* Licensed under the MIT license
* For all details and documentation:
* http://maplacejs.com
*
* @version  0.2.7
* @preserve
*/

;(function(root, factory) {
    if (typeof define === 'function' && define.amd) {
        define(['jquery'], factory);
    } else if (typeof exports === 'object') {
        module.exports = factory(require('jquery'));
    } else {
        root.Maplace = factory(root.jQuery);
    }

}(this, function($) {
    'use strict';

    var html_dropdown,
        html_ullist;

    //dropdown menu type
    html_dropdown = {
        activateCurrent: function(index) {
            this.html_element.find('select').val(index);
        },

        getHtml: function() {
            var self = this,
                html = '',
                title,
                a;

            if (this.ln > 1) {
                html += '<select class="dropdown controls ' + this.o.controls_cssclass + '">';

                if (this.ShowOnMenu(this.view_all_key)) {
                    html += '<option value="' + this.view_all_key + '">' + this.o.view_all_text + '</option>';
                }

                for (a = 0; a < this.ln; a += 1) {
                    if (this.ShowOnMenu(a)) {
                        html += '<option value="' + (a + 1) + '">' + (this.o.locations[a].title || ('#' + (a + 1))) + '</option>';
                    }
                }
                html += '</select>';

                html = $(html).bind('change', function() {
                    self.ViewOnMap(this.value);
                });
            }

            title = this.o.controls_title;
            if (this.o.controls_title) {
                title = $('<div class="controls_title"></div>').css(this.o.controls_applycss ? {
                    fontWeight: 'bold',
                    fontSize: this.o.controls_on_map ? '12px' : 'inherit',
                    padding: '3px 10px 5px 0'
                } : {}).append(this.o.controls_title);
            }

            this.html_element = $('<div class="wrap_controls"></div>').append(title).append(html);

            return this.html_element;
        }
    };


    //ul list menu type
    html_ullist = {
        html_a: function(i, hash, ttl) {
            var self = this,
                index = hash || (i + 1),
                title = ttl || this.o.locations[i].title,
                el_a = $('<a data-load="' + index + '" id="ullist_a_' + index + '" href="#' + index + '" title="' + title + '">' + (title || ('#' + (i + 1))) + '</a>');

            el_a.css(this.o.controls_applycss ? {
                
            } : {});

            el_a.on('click', function(e) {
                e.preventDefault();
                var i = $(this).attr('data-load');
                self.ViewOnMap(i);
            });

            return el_a;
        },

        activateCurrent: function(index) {
            this.html_element.find('li').removeClass('active');
            this.html_element.find('#ullist_a_' + index).parent().addClass('active');
        },

        getHtml: function() {
            var html = $('<ul class=\'sidebar-style-2' + '\'></ul>'),
                title, a;

            if (this.ShowOnMenu(this.view_all_key)) {
                html.append($('<li></li>').append(html_ullist.html_a.call(this, false, this.view_all_key, this.o.view_all_text)));
            }

            for (a = 0; a < this.ln; a++) {
                if (this.ShowOnMenu(a)) {
                    html.append($('<li></li>').append(html_ullist.html_a.call(this, a)));
                }
            }

            title = this.o.controls_title;
            if (this.o.controls_title) {
                title = $('<div class="controls_title"></div>').css(this.o.controls_applycss ? {
                    fontWeight: 'bold',
                    padding: '3px 10px 5px 0',
                    fontSize: this.o.controls_on_map ? '12px' : 'inherit'
                } : {}).append(this.o.controls_title);
            }

            this.html_element = $('<div class="wrap_controls"></div>').append(title).append(html);

            return this.html_element;
        }
    };


    /**
    * Create a new instance
    * @class Maplace
    * @constructor
    */
    function Maplace(args) {
        this.VERSION = '0.2.7';
        this.loaded = false;
        this.markers = [];
        this.circles = [];
        this.oMap = false;
        this.view_all_key = 'all';

        this.infowindow = null;
        this.maxZIndex = 0;
        this.ln = 0;
        this.oMap = false;
        this.oBounds = null;
        this.map_div = null;
        this.canvas_map = null;
        this.controls_wrapper = null;
        this.current_control = null;
        this.current_index = null;
        this.Polyline = null;
        this.Polygon = null;
        this.Fusion = null;
        this.directionsService = null;
        this.directionsDisplay = null;

        //default options
        this.o = {
            debug: false,
            map_div: '#gmap',
            controls_div: '#controls',
            generate_controls: true,
            controls_type: 'dropdown',
            controls_cssclass: '',
            controls_title: '',
            controls_on_map: true,
            controls_applycss: true,
            controls_position: google.maps.ControlPosition.RIGHT_TOP,
            type: 'marker',
            view_all: true,
            view_all_text: 'View All',
            pan_on_click: true,
            start: 0,
            locations: [],
            shared: {},
            map_options: {
                mapTypeId: google.maps.MapTypeId.ROADMAP
            },
            stroke_options: {
                strokeColor: '#0000FF',
                strokeOpacity: 0.8,
                strokeWeight: 2,
                fillColor: '#0000FF',
                fillOpacity: 0.4
            },
            directions_options: {
                travelMode: google.maps.TravelMode.DRIVING,
                unitSystem: google.maps.UnitSystem.METRIC,
                optimizeWaypoints: false,
                provideRouteAlternatives: false,
                avoidHighways: false,
                avoidTolls: false
            },
            circle_options: {
                radius: 100,
                visible: true
            },
            styles: {},
            fusion_options: {},
            directions_panel: null,
            draggable: false,
            editable: false,
            show_infowindows: true,
            show_markers: true,
            infowindow_type: 'bubble',
            listeners: {},

            //events
            beforeViewAll: function() {},
            afterViewAll: function() {},
            beforeShow: function(index, location, marker) {},
            afterShow: function(index, location, marker) {},
            afterCreateMarker: function(index, location, marker) {},

            beforeCloseInfowindow: function(index, location) {},
            afterCloseInfowindow: function(index, location) {},
            beforeOpenInfowindow: function(index, location, marker) {},
            afterOpenInfowindow: function(index, location, marker) {},

            afterRoute: function(distance, status, result) {},
            onPolylineClick: function(obj) {},
            onPolygonClick: function(obj) {},

            circleRadiusChanged: function(index, circle, marker) {},
            circleCenterChanged: function(index, circle, marker) {},

            drag: function(index, location, marker) {},
            dragEnd: function(index, location, marker) {},
            dragStart: function(index, location, marker) {}
        };

        //default menu types
        this.AddControl('dropdown', html_dropdown);
        this.AddControl('list', html_ullist);

        if (args && args.type === 'directions') {
            !args.show_markers && (args.show_markers = false);
            !args.show_infowindows && (args.show_infowindows = false);
        }

        //init
        $.extend(true, this.o, args);
    }

    //where to store the menu types
    Maplace.prototype.controls = {};

    //initialize google map object
    Maplace.prototype.create_objMap = function() {
        var self = this,
            count = 0,
            i;

        //if styled
        for (i in this.o.styles) {
            if (this.o.styles.hasOwnProperty(i)) {
                if (count === 0) {
                    this.o.map_options.mapTypeControlOptions = {
                        mapTypeIds: [google.maps.MapTypeId.ROADMAP]
                    };
                }
                count++;
                this.o.map_options.mapTypeControlOptions.mapTypeIds.push('map_style_' + count);
            }
        }

        //if init
        if (!this.loaded) {
            try {
                this.map_div.css({
                    position: 'relative',
                    overflow: 'hidden'
                });

                //create the container div into map_div
                this.canvas_map = $('<div>').addClass('canvas_map').css({
                    width: '100%',
                    height: '100%'
                }).appendTo(this.map_div);

                this.oMap = new google.maps.Map(this.canvas_map.get(0), this.o.map_options);

            } catch (err) {
                this.debug('create_objMap::' + this.map_div.selector, err.toString());
            }

        //else loads the new optionsl
        } else {
            self.oMap.setOptions(this.o.map_options);
        }

        //if styled
        count = 0;
        for (i in this.o.styles) {
            if (this.o.styles.hasOwnProperty(i)) {
                count++;
                this.oMap.mapTypes.set('map_style_' + count, new google.maps.StyledMapType(this.o.styles[i], {
                    name: i
                }));
                this.oMap.setMapTypeId('map_style_' + count);
            }
        }
    };

    //adds markers to the map
    Maplace.prototype.add_markers_to_objMap = function() {
        var a,
            point,
            type = this.o.type || 'marker';

        //switch how to display the locations
        switch (type) {
            case 'marker':
                for (a = 0; a < this.ln; a++) {
                    point = this.create_objPoint(a);
                    this.create.marker.call(this, a, point);
                }
                break;
            default:
                this.create[type].apply(this);
                break;
        }
    };

    //create the main object point
    Maplace.prototype.create_objPoint = function(index) {
        var point = $.extend({}, this.o.locations[index]),
            visibility = point.visible === undefined ? undefined : point.visible;

        !point.type && (point.type = this.o.type);

        //set obj map
        point.map = this.oMap;
        point.position = new google.maps.LatLng(point.lat, point.lon);
        point.zIndex = point.zIndex === undefined ? 10000 : (point.zIndex + 100);
        point.visible = visibility === undefined  ? this.o.show_markers : visibility;

        this.o.maxZIndex = point.zIndex > this.maxZIndex ? point.zIndex : this.maxZIndex;

        if (point.image) {
            point.icon = new google.maps.MarkerImage(
                point.image,
                new google.maps.Size(point.image_w || 32, point.image_h || 32),
                new google.maps.Point(0, 0),
                new google.maps.Point((point.image_w || 32) / 2, (point.image_h || 32)  / 2)
            );
        }

        return point;
    };

    //create the main object circle
    Maplace.prototype.create_objCircle = function(point) {
        var def_stroke_opz,
            def_circle_opz,
            circle;

        circle = $.extend({}, point);
        def_stroke_opz = $.extend({}, this.o.stroke_options);
        def_circle_opz = $.extend({}, this.o.circle_options);

        $.extend(def_stroke_opz, point.stroke_options || {});
        $.extend(circle, def_stroke_opz);

        $.extend(def_circle_opz, point.circle_options || {});
        $.extend(circle, def_circle_opz);

        circle.center = point.position;
        circle.draggable = false;
        circle.zIndex = point.zIndex > 0 ? point.zIndex - 10 : 1;

        return circle;
    };

    //create the main object point
    Maplace.prototype.add_markerEv = function(index, point, marker) {
        var self = this;

        google.maps.event.addListener(marker, 'click', function(ev) {
            self.o.beforeShow(index, point, marker);

            //show infowindow?
            if (self.o.show_infowindows && (point.show_infowindow === false ? false : true)) {
                self.open_infowindow(index, marker, ev);
            }

            //pan and zoom the map
            if (self.o.pan_on_click && (point.pan_on_click === false ? false : true)) {
                self.oMap.panTo(point.position);
                point.zoom && self.oMap.setZoom(point.zoom);
            }

            //activate related menu link
            if (self.current_control && self.o.generate_controls && self.current_control.activateCurrent) {
                self.current_control.activateCurrent.call(self, index + 1);
            }

            //update current location index
            self.current_index = index;

            self.o.afterShow(index, point, marker);
        });

        if (point.draggable) {
            this.add_dragEv(index, point, marker);
        }
    };

    //add events to circles objs
    Maplace.prototype.add_circleEv = function(index, circle, marker) {
        var self = this;

        google.maps.event.addListener(marker, 'click', function() {
            self.ViewOnMap(index + 1);
        });

        google.maps.event.addListener(marker, 'center_changed', function() {
            self.o.circleCenterChanged(index, circle, marker);
        });

        google.maps.event.addListener(marker, 'radius_changed', function() {
            self.o.circleRadiusChanged(index, circle, marker);
        });

        if (circle.draggable) {
            this.add_dragEv(index, circle, marker);
        }
    };

    //add drag events
    Maplace.prototype.add_dragEv = function(index, obj, marker) {
        var self = this;

        google.maps.event.addListener(marker, 'drag', function(ev) {
            var pos,
                extraType;

            if (marker.getPosition) {
                pos = marker.getPosition();
            } else if (marker.getCenter) {
                pos = marker.getCenter();
            } else {
                return;
            }

            //update circle position
            if (self.circles[index]) {
                self.circles[index].setCenter(pos);
            }

            //update polygon or polyline if defined
            if (self.Polyline) {
                extraType = 'Polyline';
            } else if (self.Polygon) {
                extraType = 'Polygon';
            }

            if (extraType) {
                var path = self[extraType].getPath(),
                    pathArray = path.getArray(),
                    arr = [],
                    i = 0;

                for (; i < pathArray.length; ++i) {
                    arr[i] = (index === i) ?
                        new google.maps.LatLng(pos.lat(), pos.lng())
                        : new google.maps.LatLng(pathArray[i].lat(), pathArray[i].lng());
                }

                self[extraType].setPath(new google.maps.MVCArray(arr));
                self.add_polyEv(extraType);
            }

            //fire drag event
            self.o.drag(index, obj, marker);
        });

        google.maps.event.addListener(marker, 'dragend', function() {
            self.o.dragEnd(index, obj, marker);
        });

        google.maps.event.addListener(marker, 'dragstart', function() {
            self.o.dragStart(index, obj, marker);
        });

        google.maps.event.addListener(marker, 'center_changed', function() {
            //update marker position
            if (self.markers[index] && marker.getCenter) {
                self.markers[index].setPosition(marker.getCenter());
            }

            self.o.drag(index, obj, marker);
        });
    };

    //add events to poly objs
    Maplace.prototype.add_polyEv = function(typeName) {
        var self = this;

        google.maps.event.addListener(this[typeName].getPath(), 'set_at', function(index, obj) {
            var item = self[typeName].getPath().getAt(index),
                newPos = new google.maps.LatLng(item.lat(), item.lng());

            self.markers[index] && self.markers[index].setPosition(newPos);
            self.circles[index] && self.circles[index].setCenter(newPos);

            self.o['on' + typeName + 'Changed'](index, obj, self[typeName].getPath().getArray());
        });
    };

    //wrapper for the map types
    Maplace.prototype.create = {

        //single marker
        marker: function(index, point, marker) {
            var self = this,
                circle;

            //allow mix circles with markers
            if (point.type === 'circle' && !marker) {
                circle = this.create_objCircle(point);

                if (!point.visible) {
                    circle.draggable = point.draggable;
                }

                marker = new google.maps.Circle(circle);
                this.add_circleEv(index, circle, marker);

                //store the new circle
                this.circles[index] = marker;
            }

            point.type = 'marker';

            //create the marker and add click event
            marker = new google.maps.Marker(point);
            this.add_markerEv(index, point, marker);

            //extends bounds with this location
            this.oBounds.extend(point.position);

            //store the new marker
            this.markers[index] = marker;

            this.o.afterCreateMarker(index, point, marker);

            return marker;
        },


        //circle mode
        circle: function() {
            var self = this,
                a,
                point,
                circle,
                marker;

            for (a = 0; a < this.ln; a++) {
                point = this.create_objPoint(a);

                //allow mix markers with circles
                if (point.type === 'circle') {
                    circle = this.create_objCircle(point);

                    if (!point.visible) {
                        circle.draggable = point.draggable;
                    }

                    marker = new google.maps.Circle(circle);
                    this.add_circleEv(a, circle, marker);

                    //store the new circle
                    this.circles[a] = marker;
                }

                point.type = 'marker';
                this.create.marker.call(this, a, point, marker);
            }
        },


        //polyline mode
        polyline: function() {
            var self = this,
                a,
                point,
                stroke = $.extend({}, this.o.stroke_options);

            stroke.path = [];
            stroke.draggable = this.o.draggable;
            stroke.editable = this.o.editable;
            stroke.map = this.oMap;
            stroke.zIndex = this.o.maxZIndex + 100;

            //create the path and location marker
            for (a = 0; a < this.ln; a++) {
                point = this.create_objPoint(a);
                this.create.marker.call(this, a, point);

                stroke.path.push(point.position);
            }

            this.Polyline ?
                this.Polyline.setOptions(stroke)
                : this.Polyline = new google.maps.Polyline(stroke);

            this.add_polyEv('Polyline');
        },


        //polygon mode
        polygon: function() {
            var self = this,
                a,
                point,
                stroke = $.extend({}, this.o.stroke_options);

            stroke.path = [];
            stroke.draggable = this.o.draggable;
            stroke.editable = this.o.editable;
            stroke.map = this.oMap;
            stroke.zIndex = this.o.maxZIndex + 100;

            //create the path and location marker
            for (a = 0; a < this.ln; a++) {
                point = this.create_objPoint(a);
                this.create.marker.call(this, a, point);

                stroke.path.push(point.position);
            }

            this.Polygon ?
                this.Polygon.setOptions(stroke)
                : this.Polygon = new google.maps.Polygon(stroke);

            google.maps.event.addListener(this.Polygon, 'click', function(obj) {
                self.o.onPolygonClick(obj);
            });

            this.add_polyEv('Polygon');
        },


        //fusion tables
        fusion: function() {
            this.o.fusion_options.styles = [this.o.stroke_options];
            this.o.fusion_options.map = this.oMap;

            this.Fusion ?
                this.Fusion.setOptions(this.o.fusion_options)
                : this.Fusion = new google.maps.FusionTablesLayer(this.o.fusion_options);
        },


        //directions mode
        directions: function() {
            var self = this,
                a,
                point,
                stopover,
                origin,
                destination,
                waypoints = [],
                distance = 0;

            //create the waypoints and location marker
            for (a = 0; a < this.ln; a++) {
                point = this.create_objPoint(a);

                //first location start point
                if (a === 0) {
                    origin = point.position;

                //last location end point
                } else if (a === (this.ln - 1)) {
                    destination = point.position;

                //waypoints in the middle
                } else {
                    stopover = this.o.locations[a].stopover === true ? true : false;
                    waypoints.push({
                        location: point.position,
                        stopover: stopover
                    });
                }

                this.create.marker.call(this, a, point);
            }

            this.o.directions_options.origin = origin;
            this.o.directions_options.destination = destination;
            this.o.directions_options.waypoints = waypoints;

            this.directionsService || (this.directionsService = new google.maps.DirectionsService());
            this.directionsDisplay ?
                this.directionsDisplay.setOptions({draggable: this.o.draggable})
                : this.directionsDisplay = new google.maps.DirectionsRenderer({draggable: this.o.draggable});

            this.directionsDisplay.setMap(this.oMap);

            //show the directions panel
            if (this.o.directions_panel) {
                this.o.directions_panel = $(this.o.directions_panel);
                this.directionsDisplay.setPanel(this.o.directions_panel.get(0));
            }

            if (this.o.draggable) {
                google.maps.event.addListener(this.directionsDisplay, 'directions_changed', function() {
                    distance = self.compute_distance(self.directionsDisplay.directions);
                    self.o.afterRoute(distance);
                });
            }

            this.directionsService.route(this.o.directions_options, function(result, status) {
                //directions found
                if (status === google.maps.DirectionsStatus.OK) {
                    distance = self.compute_distance(result);
                    self.directionsDisplay.setDirections(result);
                }
                self.o.afterRoute(distance, status, result);
            });
        }
    };

    //route distance
    Maplace.prototype.compute_distance = function(result) {
        var total = 0,
            i,
            myroute = result.routes[0],
            rlen = myroute.legs.length;

        for (i = 0; i < rlen; i++) {
            total += myroute.legs[i].distance.value;
        }

        return total;
    };

    //wrapper for the infowindow types
    Maplace.prototype.type_to_open = {
        //google default infowindow
        bubble: function(location) {
            this.infowindow = new google.maps.InfoWindow({
                content: location.html || ''
            });
        }
    };

    //open the infowindow
    Maplace.prototype.open_infowindow = function(index, marker, ev) {
        //close if any open
        this.CloseInfoWindow();
        var point = this.o.locations[index],
            type = this.o.infowindow_type;

        //show if content and valid infowindow type provided
        if (point.html && this.type_to_open[type]) {
            this.o.beforeOpenInfowindow(index, point, marker);
            this.type_to_open[type].call(this, point);
            this.infowindow.open(this.oMap, marker);
            this.o.afterOpenInfowindow(index, point, marker);
        }
    };

    //gets the html for the menu
    Maplace.prototype.get_html_controls = function() {
        if (this.controls[this.o.controls_type] && this.controls[this.o.controls_type].getHtml) {
            this.current_control = this.controls[this.o.controls_type];

            return this.current_control.getHtml.apply(this);
        }
        return '';
    };

    //creates the controls menu
    Maplace.prototype.generate_controls = function() {
        //append menu on the div container
        if (!this.o.controls_on_map) {
            this.controls_wrapper.empty();
            this.controls_wrapper.append(this.get_html_controls());
            return;
        }

        //else
        //controls in map
        var cntr = $('<div class="on_gmap ' + this.o.controls_type + ' gmap_controls"></div>')
            .css(this.o.controls_applycss ? {margin: '5px'} : {}),

            inner = $(this.get_html_controls()).css(this.o.controls_applycss ? {
                background: '#fff',
                padding: '5px',
                border: '1px solid #eee',
                boxShadow: 'rgba(0, 0, 0, 0.298039) 0px 1px 4px -1px',
                maxHeight: this.map_div.find('.canvas_map').outerHeight() - 80,
                minWidth: 100,
                overflowY: 'auto',
                overflowX: 'hidden'
            } : {});

        cntr.append(inner);

        //attach controls
        this.oMap.controls[this.o.controls_position].push(cntr.get(0));
    };

    //resets obj map, markers, bounds, listeners, controllers
    Maplace.prototype.init_map = function() {
        var self = this;

        this.Polyline && this.Polyline.setMap(null);
        this.Polygon && this.Polygon.setMap(null);
        this.Fusion && this.Fusion.setMap(null);
        this.directionsDisplay && this.directionsDisplay.setMap(null);

        for (var i = this.markers.length - 1; i >= 0; i -= 1) {
            try {
                this.markers[i] && this.markers[i].setMap(null);
            } catch (err) {
                self.debug('init_map::markers::setMap', err.stack);
            }
        }

        this.markers.length = 0;
        this.markers = [];

        for (var e = this.circles.length - 1; e >= 0; e -= 1) {
            try {
                this.circles[e] && this.circles[e].setMap(null);
            } catch (err) {
                self.debug('init_map::circles::setMap', err.stack);
            }
        }

        this.circles.length = 0;
        this.circles = [];

        if (this.o.controls_on_map && this.oMap.controls) {
            this.oMap.controls[this.o.controls_position].forEach(function(element, index) {
                try {
                    self.oMap.controls[this.o.controls_position].removeAt(index);
                } catch (err) {
                    self.debug('init_map::removeAt', err.stack);
                }
            });
        }

        this.oBounds = new google.maps.LatLngBounds();
    };

    //perform the first view of the map
    Maplace.prototype.perform_load = function() {
        //one location
        if (this.ln === 1) {
            if (this.o.map_options.set_center) {
                this.oMap.setCenter(new google.maps.LatLng(this.o.map_options.set_center[0], this.o.map_options.set_center[1]));

            } else {
                this.oMap.fitBounds(this.oBounds);
                this.ViewOnMap(1);
            }

            this.o.map_options.zoom && this.oMap.setZoom(this.o.map_options.zoom);

        //no locations
        } else if (this.ln === 0) {
            if (this.o.map_options.set_center) {
                this.oMap.setCenter(new google.maps.LatLng(this.o.map_options.set_center[0], this.o.map_options.set_center[1]));

            } else {
                this.oMap.fitBounds(this.oBounds);
            }

            this.oMap.setZoom(this.o.map_options.zoom || 1);

        //n+ locations
        } else {
            this.oMap.fitBounds(this.oBounds);

            //check the start option
            if (typeof (this.o.start - 0) === 'number' && this.o.start > 0 && this.o.start <= this.ln) {
                this.ViewOnMap(this.o.start);

            //check if set_center exists
            } else if (this.o.map_options.set_center) {
                this.oMap.setCenter(new google.maps.LatLng(this.o.map_options.set_center[0], this.o.map_options.set_center[1]));

            //view all
            } else {
                this.ViewOnMap(this.view_all_key);
            }

            this.o.map_options.zoom && this.oMap.setZoom(this.o.map_options.zoom);
        }
    };

    Maplace.prototype.debug = function(code, msg) {
        this.o.debug && console.log(code, msg);
        return this;
    };


    /////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////


    //adds a custom menu to the class
    Maplace.prototype.AddControl = function(name, func) {
        if (!name || !func) {
            self.debug('AddControl', 'Missing "name" and "func" callback.');
            return false;
        }
        this.controls[name] = func;
        return this;
    };

    //close the infowindow
    Maplace.prototype.CloseInfoWindow = function() {
        if (this.infowindow && (this.current_index || this.current_index === 0)) {
            this.o.beforeCloseInfowindow(this.current_index, this.o.locations[this.current_index]);
            this.infowindow.close();
            this.infowindow = null;
            this.o.afterCloseInfowindow(this.current_index, this.o.locations[this.current_index]);
        }
        return this;
    };

    //checks if a location has to be in menu
    Maplace.prototype.ShowOnMenu = function(index) {
        if (index === this.view_all_key && this.o.view_all && this.ln > 1) {
            return true;
        }

        index = parseInt(index, 10);
        if (typeof (index - 0) === 'number' && index >= 0 && index < this.ln) {
            var on_menu = this.o.locations[index].on_menu === false ? false : true;
            if (on_menu) {
                return true;
            }
        }

        return false;
    };

    //triggers to show a location in map
    Maplace.prototype.ViewOnMap = function(index) {
        //view all
        if (index === this.view_all_key) {
            this.o.beforeViewAll();
            this.current_index = index;
            if (this.o.locations.length > 0 && this.o.generate_controls && this.current_control && this.current_control.activateCurrent) {
                this.current_control.activateCurrent.apply(this, [index]);
            }
            this.oMap.fitBounds(this.oBounds);
            this.CloseInfoWindow();
            this.o.afterViewAll();

        //specific location
        } else {
            index = parseInt(index, 10);
            if (typeof (index - 0) === 'number' && index > 0 && index <= this.ln) {
                try {
                    google.maps.event.trigger(this.markers[index - 1], 'click');
                } catch (err) {
                    this.debug('ViewOnMap::trigger', err.stack);
                }
            }
        }
        return this;
    };

    //replace current locations
    Maplace.prototype.SetLocations = function(locs, reload) {
        this.o.locations = locs;
        reload && this.Load();
        return this;
    };

    //adds one or more locations to the end of the array
    Maplace.prototype.AddLocations = function(locs, reload) {
        var self = this;

        if ($.isArray(locs)) {
            $.each(locs, function(index, value) {
                self.o.locations.push(value);
            });
        }

        if ($.isPlainObject(locs)) {
            this.o.locations.push(locs);
        }

        reload && this.Load();
        return this;
    };

    //adds a location at the specific index
    Maplace.prototype.AddLocation = function(location, index, reload) {
        var self = this;

        if ($.isPlainObject(location)) {
            this.o.locations.splice(index, 0, location);
        }

        reload && this.Load();
        return this;
    };

    //remove one or more locations
    Maplace.prototype.RemoveLocations = function(locs, reload) {
        var self = this,
            k = 0;

        if ($.isArray(locs)) {
            $.each(locs, function(index, value) {
                if ((value - k) < self.ln) {
                    self.o.locations.splice(value - k, 1);
                }
                k++;
            });
        } else {
            if (locs < this.ln) {
                this.o.locations.splice(locs, 1);
            }
        }

        reload && this.Load();
        return this;
    };

    //check if already initialized with a Load()
    Maplace.prototype.Loaded = function() {
        return this.loaded;
    };

    //loads the options
    Maplace.prototype._init = function() {
        //store the locations length
        this.ln = this.o.locations.length;

        //update all locations with shared
        for (var i = 0; i < this.ln; i++) {
            var common = $.extend({}, this.o.shared);
            this.o.locations[i] = $.extend(common, this.o.locations[i]);
            if (this.o.locations[i].html) {
                this.o.locations[i].html = this.o.locations[i].html.replace('%index', i + 1);
                this.o.locations[i].html = this.o.locations[i].html.replace('%title', (this.o.locations[i].title || ''));
            }
        }

        //store dom references
        this.map_div = $(this.o.map_div);
        this.controls_wrapper = $(this.o.controls_div);
        return this;
    };

    //creates the map and menu
    Maplace.prototype.Load = function(args) {
        $.extend(true, this.o, args);
        args && args.locations && (this.o.locations = args.locations);
        this._init();

        //reset/init google map objects
        this.o.visualRefresh === false ? (google.maps.visualRefresh = false) : (google.maps.visualRefresh = true);
        this.init_map();
        this.create_objMap();

        //add markers
        this.add_markers_to_objMap();

        //generate controls
        if ((this.ln > 1 && this.o.generate_controls) || this.o.force_generate_controls)  {
            this.o.generate_controls = true;
            this.generate_controls();
        } else {
            this.o.generate_controls = false;
        }

        var self = this;

        //first call
        if (!this.loaded) {
            google.maps.event.addListenerOnce(this.oMap, 'idle', function() {
                self.perform_load();
            });

            //add custom listeners
            for (var i in this.o.listeners) {
                if (this.o.listeners.hasOwnProperty(i)) {
                    google.maps.event.addListener(this.oMap, i, this.o.listeners[i]);
                }
            }

        //all other calls
        } else {
            this.perform_load();
        }

        this.loaded = true;

        return this;
    };

    return Maplace;
}));
// source --> https://www.lifestyleelectronics.ie/wp-content/themes/archi/js/jquery.malihu.PageScroll2id.js?ver=6.0.11 
/*
== Page scroll to id == 
Version: 1.5.2 
Plugin URI: http://manos.malihu.gr/page-scroll-to-id/
Author: malihu
Author URI: http://manos.malihu.gr
License: MIT License (MIT)
*/

/*
Copyright 2013  malihu  (email: manos@malihu.gr)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

;(function($,window,document,undefined){
	
	/* plugin namespace, prefix, default selector(s) */
	
	var pluginNS="mPageScroll2id",
		pluginPfx="mPS2id",
		defaultSelector=".m_PageScroll2id,a[rel~='m_PageScroll2id'],.page-scroll-to-id,a[rel~='page-scroll-to-id']",
	
	/* default options */
	
		defaults={
			/* scroll animation speed in milliseconds: Integer */
			scrollSpeed:1300,
			/* auto-adjust animation speed (according to target element position and window scroll): Boolean */
			autoScrollSpeed:true,
			/* scroll animation easing when page is idle: String */
			scrollEasing:"easeInOutExpo",
			/* scroll animation easing while page is scrolling: String */
			scrollingEasing:"easeInOutCirc",
			/* end of page "smooth scrolling" (auto-adjust the scroll-to position when bottom elements are too short): Boolean */
			pageEndSmoothScroll:true,
			/* 
			page layout defines scrolling direction: String 
			values: "vertical", "horizontal", "auto" 
			*/
			layout:"vertical",
			/* extra space in pixels for the target element position: Integer */
			offset:0,
			/* highlight the main/default selectors or insert a different set: Boolean, String */
			highlightSelector:false,
			/* class of the clicked element: String */
			clickedClass:pluginPfx+"-clicked",
			/* class of the current target element: String */
			targetClass:pluginPfx+"-target",
			/* class of the highlighted element: String */
			highlightClass:pluginPfx+"-highlight",
			/* force a single highlighted element each time: Boolean */
			forceSingleHighlight:false,
			/* keep element highlighted until next (one element always stays highlighted): boolean */
			keepHighlightUntilNext:false,
			/* disable plugin below [x,y] screen size: boolean, integer, array ([x,y]) */
			disablePluginBelow:false,
			/* enable/disable click events for all selectors */
			clickEvents:true,
			/* user callback functions: fn */
			onStart:function(){},
			onComplete:function(){},
			/* enable/disable the default selector: Boolean */
			defaultSelector:false
		},
	
	/* vars, constants */
	
		selector,opt,_init,_trigger,_clicked,_target,_to,_axis,_offset,_dataOffset,
	
	/* 
	---------------
	methods 
	---------------
	*/
	
		methods={
			
			/* plugin initialization method */
			
			init:function(options){
				
				/* extend options, store each option in jquery data */
				
				var options=$.extend(true,{},defaults,options);
				
				$(document).data(pluginPfx,options);
				opt=$(document).data(pluginPfx);
				
				/* set default selector */
						
				selector=(!selector) ? this.selector : selector+","+this.selector;
				
				if(opt.defaultSelector){
					if(typeof $(selector)!=="object" || $(selector).length===0){
						selector=defaultSelector;
					}
				}
				
				/* plugin events */
				
				if(opt.clickEvents){
					$(document)
					
					.undelegate("."+pluginPfx)
					
					.delegate(selector,"click."+pluginPfx,function(e){
						if(functions._isDisabled.call(null)){
							functions._removeClasses.call(null);
							return;
						}
						var $this=$(this),
							href=$this.attr("href"),
							hrefProp=$this.prop("href");
						if(href && href.indexOf("#/")!==-1){
							return;
						}
						functions._reset.call(null);
						_dataOffset=$this.data("ps2id-offset") || 0;
						if(functions._isValid.call(null,href,hrefProp) && functions._findTarget.call(null,href)){
							e.preventDefault();
							_trigger="selector";
							_clicked=$this;
							functions._setClasses.call(null,true);
							functions._scrollTo.call(null);
						}
					});
				}
				
				$(window)
				
				.unbind("."+pluginPfx)
				
				.bind("scroll."+pluginPfx+" resize."+pluginPfx,function(){
					if(functions._isDisabled.call(null)){
						functions._removeClasses.call(null);
						return;
					}
					var targets=$("._"+pluginPfx+"-t");
					targets.each(function(i){
						var t=$(this),id=t.attr("id"),
							h=functions._findHighlight.call(null,id);
						functions._setClasses.call(null,false,t,h);
						if(i==targets.length-1){functions._extendClasses.call(null);}
					});
				});
				
				/* plugin has initialized */
				
				_init=true;
				
				/* setup selectors, target elements, basic plugin classes etc. */
				
				functions._setup.call(null);
			},
			
			/* scrollTo method */
			
			scrollTo:function(id,options){
				if(functions._isDisabled.call(null)){
					functions._removeClasses.call(null);
					return;
				}
				if(id && typeof id!=="undefined"){
					functions._isInit.call(null);
					var defaults={
							layout:opt.layout,
							offset:opt.offset,
							clicked:false
						},
						options=$.extend(true,{},defaults,options);
					functions._reset.call(null);
					_axis=options.layout;
					_offset=options.offset;
					id=(id.indexOf("#")!==-1) ? id : "#"+id;
					if(functions._isValid.call(null,id) && functions._findTarget.call(null,id)){
						_trigger="scrollTo";
						_clicked=options.clicked;
						if(_clicked){
							functions._setClasses.call(null,true);
						}
						functions._scrollTo.call(null);
					}
				}
			},
			
			/* destroy method */
			
			destroy:function(){
				$(window).unbind("."+pluginPfx);
				$(document).undelegate("."+pluginPfx).removeData(pluginPfx);
				$("._"+pluginPfx+"-t").removeData(pluginPfx);
				functions._removeClasses.call(null,true);
			}
		},
	
	/* 
	---------------
	functions
	---------------
	*/
	
		functions={
			
			/* checks if screen size ([x,y]) is below the value(s) set in disablePluginBelow option */
			
			_isDisabled:function(){
				var e=window,a="inner",
					val=opt.disablePluginBelow instanceof Array ? [opt.disablePluginBelow[0] || 0,opt.disablePluginBelow[1] || 0] : [opt.disablePluginBelow || 0,0];
				if(!("innerWidth" in window )){
					a="client";
					e=document.documentElement || document.body;
				}
				return e[a+"Width"]<=val[0] || e[a+"Height"]<=val[1];
			},
			
			/* checks if href attribute is valid */
			
			_isValid:function(href,hrefProp){
				if(!href){
					return;
				}
				hrefProp=(!hrefProp) ? href : hrefProp;
				var str=(hrefProp.indexOf("#/")!==-1) ? hrefProp.split("#/")[0] : hrefProp.split("#")[0],
					loc=window.location.toString().split("#")[0];
				return href!=="#" && href.indexOf("#")!==-1 && (str==="" || str===loc);
			},
			
			/* setup selectors, target elements, basic plugin classes etc. */
			
			_setup:function(){
				var el=(opt.highlightSelector && opt.highlightSelector!=="") ? opt.highlightSelector : selector,i=1;
				return $(el).each(function(){
					var $this=$(this),href=$this.attr("href"),hrefProp=$this.prop("href");
					if(functions._isValid.call(null,href,hrefProp)){
						var id=(href.indexOf("#/")!==-1) ? href.split("#/")[1] : href.split("#")[1],t=$("#"+id); 
						if(t.length>0){
							if(!t.hasClass("_"+pluginPfx+"-t")){
								t.addClass("_"+pluginPfx+"-t").data(pluginPfx,{i:i});
							}
							if(!$this.hasClass("_"+pluginPfx+"-h")){
								$this.addClass("_"+pluginPfx+"-h");
							}
							var h=functions._findHighlight.call(null,id);
							functions._setClasses.call(null,false,t,h);
							i++
							if(i==$(el).length){functions._extendClasses.call(null);}
						}
					}
				});
			},
			
			/* finds the target element */
			
			_findTarget:function(str){
				var val=(str.indexOf("#/")!==-1) ? str.split("#/")[1] : str.split("#")[1], 
					el=$("#"+val);
				if(el.length<1 || el.css("position")==="fixed"){
					if(val==="top"){
						el=$("body");
					}else{
						return;
					}
				}
				_target=el;
				if(!_axis){
					_axis=opt.layout;
				}
				_offset=functions._setOffset.call(null);
				_to=[(el.offset().top-_offset[0]).toString(),(el.offset().left-_offset[1]).toString()]; 
				_to[0]=(_to[0]<0) ? 0 : _to[0];
				_to[1]=(_to[1]<0) ? 0 : _to[1];
				return _to;
			},
			
			/* sets the offset value (pixels, objects etc.) */
			
			_setOffset:function(){
				if(!_offset){
					_offset=(opt.offset) ? opt.offset : 0;
				}
				if(_dataOffset){
					_offset=_dataOffset;
				}
				var val,obj,y,x;
				switch(typeof _offset){
					case "object":
					case "string":
						val=[(_offset["y"]) ? _offset["y"] : _offset,(_offset["x"]) ? _offset["x"] : _offset];
						obj=[(val[0] instanceof jQuery) ? val[0] : $(val[0]),(val[1] instanceof jQuery) ? val[1] : $(val[1])];
						if(obj[0].length>0){ // js/jquery object
							y=obj[0].height();
							if(obj[0].css("position")==="fixed"){ // include position for fixed elements
								y+=obj[0][0].offsetTop;
							}
						}else if(!isNaN(parseFloat(val[0])) && isFinite(val[0])){ // numeric string
							y=parseInt(val[0]);
						}else{
							y=0; // non-existing value
						}
						if(obj[1].length>0){ // js/jquery object
							x=obj[1].width();
							if(obj[1].css("position")==="fixed"){ // include position for fixed elements
								x+=obj[1][0].offsetLeft;
							}
						}else if(!isNaN(parseFloat(val[1])) && isFinite(val[1])){ // numeric string
							x=parseInt(val[1]);
						}else{
							x=0; // non-existing value
						}
						break;
					case "function":
						val=_offset.call(null); // function (single value or array)
						if(val instanceof Array){
							y=val[0];
							x=val[1];
						}else{
							y=x=val;
						}
						break;
					default:
						y=x=parseInt(_offset); // number
				}
				return [y,x];
			},
			
			/* finds the element that should be highlighted */
			
			_findHighlight:function(id){
				var loc=window.location.toString().split("#")[0],
					hHash=$("._"+pluginPfx+"-h[href='#"+id+"']"),
					lhHash=$("._"+pluginPfx+"-h[href='"+loc+"#"+id+"']"),
					hHashSlash=$("._"+pluginPfx+"-h[href='#/"+id+"']"),
					lhHashSlash=$("._"+pluginPfx+"-h[href='"+loc+"#/"+id+"']");
				hHash=(hHash.length>0) ? hHash : lhHash;
				hHashSlash=(hHashSlash.length>0) ? hHashSlash : lhHashSlash;
				return (hHashSlash.length>0) ? hHashSlash : hHash;
			},
			
			/* sets plugin classes */
			
			_setClasses:function(c,t,h){
				var cc=opt.clickedClass,tc=opt.targetClass,hc=opt.highlightClass;
				if(c && cc && cc!==""){
					$("."+cc).removeClass(cc);
					_clicked.addClass(cc);
				}else if(t && tc && tc!=="" && h && hc && hc!==""){
					if(functions._currentTarget.call(null,t)){
						t.addClass(tc);
						h.addClass(hc);
					}else{
						if(!opt.keepHighlightUntilNext || $("."+hc).length>1){
							t.removeClass(tc);
							h.removeClass(hc);
						}
					}
				}
			},
			
			/* extends plugin classes */
			
			_extendClasses:function(){
				var tc=opt.targetClass,hc=opt.highlightClass,
					$tc=$("."+tc),$hc=$("."+hc),ftc=tc+"-first",ltc=tc+"-last",fhc=hc+"-first",lhc=hc+"-last";
				$("._"+pluginPfx+"-t").removeClass(ftc+" "+ltc);
				$("._"+pluginPfx+"-h").removeClass(fhc+" "+lhc);
				if(!opt.forceSingleHighlight){
					$tc.slice(0,1).addClass(ftc).end().slice(-1).addClass(ltc);
					$hc.slice(0,1).addClass(fhc).end().slice(-1).addClass(lhc);
				}else{
					if(opt.keepHighlightUntilNext && $tc.length>1){
						$tc.slice(0,1).removeClass(tc); $hc.slice(0,1).removeClass(hc);
					}else{
						$tc.slice(1).removeClass(tc); $hc.slice(1).removeClass(hc);
					}
				}
			},
			
			/* removes plugin classes */
			
			_removeClasses:function(destroy){
				$("."+opt.clickedClass).removeClass(opt.clickedClass);
				$("."+opt.targetClass).removeClass(opt.targetClass+" "+opt.targetClass+"-first "+opt.targetClass+"-last");
				$("."+opt.highlightClass).removeClass(opt.highlightClass+" "+opt.highlightClass+"-first "+opt.highlightClass+"-last");
				if(destroy){
					$("._"+pluginPfx+"-t").removeClass("_"+pluginPfx+"-t");
					$("._"+pluginPfx+"-h").removeClass("_"+pluginPfx+"-h");
				}
			},
			
			/* checks if target element is in viewport */
			
			_currentTarget:function(t){
				var o=opt["target_"+t.data(pluginPfx).i],
					rect=t[0].getBoundingClientRect();
				if(typeof o!=="undefined"){
					var y=t.offset().top,x=t.offset().left,
						from=(o.from) ? o.from+y : y,to=(o.to) ? o.to+y : y,
						fromX=(o.fromX) ? o.fromX+x : x,toX=(o.toX) ? o.toX+x : x;
					return(
						rect.top >= to && rect.top <= from && 
						rect.left >= toX && rect.left <= fromX
					);
				}else{
					var wh=$(window).height(),ww=$(window).width(),
						th=t.height(),tw=t.width(),
						base=1+(th/wh),
						top=base,bottom=(th<wh) ? base*(wh/th) : base,
						baseX=1+(tw/ww),
						left=baseX,right=(tw<ww) ? baseX*(ww/tw) : baseX;
					return(
						rect.top <= wh/top && rect.bottom >= wh/bottom && 
						rect.left <= ww/left && rect.right >= ww/right
					);
				}
			},
			
			/* scrolls the page */
			
			_scrollTo:function(){
				opt.scrollSpeed=parseInt(opt.scrollSpeed);
				_to=(opt.pageEndSmoothScroll) ? functions._pageEndSmoothScroll.call(null) : _to;
				var el=$("html,body"),
					speed=(opt.autoScrollSpeed) ? functions._autoScrollSpeed.call(null) : opt.scrollSpeed,
					easing=(el.is(":animated")) ? opt.scrollingEasing : opt.scrollEasing,
					_t=$(window).scrollTop(),_l=$(window).scrollLeft();
				switch(_axis){
					case "horizontal":
						if(_l!=_to[1]){
							functions._callbacks.call(null,"onStart");
							el.stop().animate({scrollLeft:_to[1]},speed,easing).promise().then(function(){
								functions._callbacks.call(null,"onComplete");
							});
						}
						break;
					case "auto":
						if(_t!=_to[0] || _l!=_to[1]){
							functions._callbacks.call(null,"onStart");
							if(navigator.userAgent.match(/(iPod|iPhone|iPad|Android)/)){ // mobile fix
								var left;
								el.stop().animate({pageYOffset:_to[0],pageXOffset:_to[1]},{
								    duration:speed,
								    easing:easing,
								    step:function(now,fx){
								        if(fx.prop=='pageXOffset'){
								            left=now;
								        }else if(fx.prop=='pageYOffset'){
								            window.scrollTo(left,now);
								        }
								    }
								}).promise().then(function(){
									functions._callbacks.call(null,"onComplete");
								});
							}else{
								el.stop().animate({scrollTop:_to[0],scrollLeft:_to[1]},speed,easing).promise().then(function(){
									functions._callbacks.call(null,"onComplete");
								});
							}
						}
						break;
					default:
						if(_t!=_to[0]){
							functions._callbacks.call(null,"onStart");
							el.stop().animate({scrollTop:_to[0]},speed,easing).promise().then(function(){
								functions._callbacks.call(null,"onComplete");
							});
						}
				}
			},
			
			/* sets end of page "smooth scrolling" position */
			
			_pageEndSmoothScroll:function(){
				var _dh=$(document).height(),_dw=$(document).width(),
					_wh=$(window).height(),_ww=$(window).width();
				return [((_dh-_to[0])<_wh) ? _dh-_wh : _to[0],((_dw-_to[1])<_ww) ? _dw-_ww : _to[1]];
			},
			
			/* sets the auto-adjusted animation speed */
			
			_autoScrollSpeed:function(){
				var _t=$(window).scrollTop(),_l=$(window).scrollLeft(),
					_h=$(document).height(),_w=$(document).width(),
					val=[
						opt.scrollSpeed+((opt.scrollSpeed*(Math.floor((Math.abs(_to[0]-_t)/_h)*100)))/100),
						opt.scrollSpeed+((opt.scrollSpeed*(Math.floor((Math.abs(_to[1]-_l)/_w)*100)))/100)
					];
				return Math.max.apply(Math,val);
			},
			
			/* user callback functions */
			
			_callbacks:function(c){
				if(!opt){
					return;
				}
				this[pluginPfx]={
					trigger:_trigger,clicked:_clicked,target:_target,scrollTo:{y:_to[0],x:_to[1]}
				};
				switch(c){
					case "onStart":
						opt.onStart.call(null,this[pluginPfx]);
						break;
					case "onComplete":
						opt.onComplete.call(null,this[pluginPfx]);
						break;
				}
			},
			
			/* resets/clears vars and constants */
			
			_reset:function(){
				_axis=_offset=_dataOffset=false;
			},
			
			/* checks if plugin has initialized */
			
			_isInit:function(){
				if(!_init){
					methods.init.apply(this);
				}
			},
			
			/* extends jquery with custom easings (as jquery ui) */
			
			_easing:function(){
				$.easing.easeInQuad=$.easing.easeInQuad || 
					function(x,t,b,c,d){return c*(t/=d)*t + b;};	
				$.easing.easeOutQuad=$.easing.easeOutQuad || 
					function(x,t,b,c,d){return -c *(t/=d)*(t-2) + b;};
				$.easing.easeInOutQuad=$.easing.easeInOutQuad || 
					function(x,t,b,c,d){
						if ((t/=d/2) < 1) return c/2*t*t + b;
						return -c/2 * ((--t)*(t-2) - 1) + b;
					};
				$.easing.easeInCubic=$.easing.easeInCubic || 
					function(x,t,b,c,d){return c*(t/=d)*t*t + b;};
				$.easing.easeOutCubic=$.easing.easeOutCubic || 
					function(x,t,b,c,d){return c*((t=t/d-1)*t*t + 1) + b;};
				$.easing.easeInOutCubic=$.easing.easeInOutCubic || 
					function(x,t,b,c,d){
						if ((t/=d/2) < 1) return c/2*t*t*t + b;
						return c/2*((t-=2)*t*t + 2) + b;
					};
				$.easing.easeInQuart=$.easing.easeInQuart || 
					function(x,t,b,c,d){return c*(t/=d)*t*t*t + b;};
				$.easing.easeOutQuart=$.easing.easeOutQuart || 
					function(x,t,b,c,d){return -c * ((t=t/d-1)*t*t*t - 1) + b;};
				$.easing.easeInOutQuart=$.easing.easeInOutQuart || 
					function(x,t,b,c,d){
						if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
						return -c/2 * ((t-=2)*t*t*t - 2) + b;
					};
				$.easing.easeInQuint=$.easing.easeInQuint || 
					function(x,t,b,c,d){return c*(t/=d)*t*t*t*t + b;};
				$.easing.easeOutQuint=$.easing.easeOutQuint || 
					function(x,t,b,c,d){return c*((t=t/d-1)*t*t*t*t + 1) + b;};
				$.easing.easeInOutQuint=$.easing.easeInOutQuint || 
					function(x,t,b,c,d){
						if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
						return c/2*((t-=2)*t*t*t*t + 2) + b;
					};
				$.easing.easeInExpo=$.easing.easeInExpo || 
					function(x,t,b,c,d){return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;};
				$.easing.easeOutExpo=$.easing.easeOutExpo || 
					function(x,t,b,c,d){return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;};
				$.easing.easeInOutExpo=$.easing.easeInOutExpo || 
					function(x,t,b,c,d){
						if (t==0) return b;
						if (t==d) return b+c;
						if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
						return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
					};
				$.easing.easeInSine=$.easing.easeInSine || 
					function(x,t,b,c,d){return -c * Math.cos(t/d * (Math.PI/2)) + c + b;};
				$.easing.easeOutSine=$.easing.easeOutSine || 
					function(x,t,b,c,d){return c * Math.sin(t/d * (Math.PI/2)) + b;};
				$.easing.easeInOutSine=$.easing.easeInOutSine || 
					function(x,t,b,c,d){return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;};
				$.easing.easeInCirc=$.easing.easeInCirc || 
					function(x,t,b,c,d){return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;};
				$.easing.easeOutCirc=$.easing.easeOutCirc || 
					function(x,t,b,c,d){return c * Math.sqrt(1 - (t=t/d-1)*t) + b;};
				$.easing.easeInOutCirc=$.easing.easeInOutCirc || 
					function(x,t,b,c,d){
						if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
						return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
					};
				$.easing.easeInElastic=$.easing.easeInElastic || 
					function(x,t,b,c,d){
						var s=1.70158;var p=0;var a=c;
						if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
						if (a < Math.abs(c)) { a=c; var s=p/4; }
						else var s = p/(2*Math.PI) * Math.asin (c/a);
						return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
					};
				$.easing.easeOutElastic=$.easing.easeOutElastic || 
					function(x,t,b,c,d){
						var s=1.70158;var p=0;var a=c;
						if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
						if (a < Math.abs(c)) { a=c; var s=p/4; }
						else var s = p/(2*Math.PI) * Math.asin (c/a);
						return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
					};
				$.easing.easeInOutElastic=$.easing.easeInOutElastic || 
					function(x,t,b,c,d){
						var s=1.70158;var p=0;var a=c;
						if (t==0) return b;  if ((t/=d/2)==2) return b+c;  if (!p) p=d*(.3*1.5);
						if (a < Math.abs(c)) { a=c; var s=p/4; }
						else var s = p/(2*Math.PI) * Math.asin (c/a);
						if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
						return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
					};
				$.easing.easeInBack=$.easing.easeInBack || 
					function(x,t,b,c,d,s){
						if (s == undefined) s = 1.70158;
						return c*(t/=d)*t*((s+1)*t - s) + b;
					};
				$.easing.easeOutBack=$.easing.easeOutBack || 
					function(x,t,b,c,d,s){
						if (s == undefined) s = 1.70158;
						return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
					};
				$.easing.easeInOutBack=$.easing.easeInOutBack || 
					function(x,t,b,c,d,s){
						if (s == undefined) s = 1.70158;
						if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
						return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
					};
				$.easing.easeInBounce=$.easing.easeInBounce || 
					function(x,t,b,c,d){return c - $.easing.easeOutBounce (x, d-t, 0, c, d) + b;};
				$.easing.easeOutBounce=$.easing.easeOutBounce || 
					function(x,t,b,c,d){
						if ((t/=d) < (1/2.75)) {return c*(7.5625*t*t) + b;} 
						else if (t < (2/2.75)) {return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;} 
						else if (t < (2.5/2.75)) {return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;} 
						else {return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;}
					};
				$.easing.easeInOutBounce=$.easing.easeInOutBounce || 
					function(x,t,b,c,d){
						if (t < d/2) return $.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b;
						return $.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b;
					};
			}
		}
		
	/* 
	---------------
	plugin setup 
	---------------
	*/
	
	/* extend jquery with custom easings */
	
	functions._easing.call();
	
	/* plugin constructor functions */
	
	$.fn[pluginNS]=function(method){
		if(methods[method]){
			return methods[method].apply(this,Array.prototype.slice.call(arguments,1));
		}else if(typeof method==="object" || !method){
			return methods.init.apply(this,arguments);
		}else{
			$.error("Method "+method+" does not exist");
		}
	};
	$[pluginNS]=function(method){
		if(methods[method]){
			return methods[method].apply(this,Array.prototype.slice.call(arguments,1));
		}else if(typeof method==="object" || !method){
			return methods.init.apply(this,arguments);
		}else{
			$.error("Method "+method+" does not exist");
		}
	};
	
	/* 
	allow setting plugin default options. 
	example: $.plugin_name.defaults.option_name="option_value"; 
	*/
	
	$[pluginNS].defaults=defaults;
	
})(jQuery,window,document);