/**
 * @package        PickMeUp - jQuery datepicker plugin
 * @author        Nazar Mokrynskyi <nazar@mokrynskyi.com>
 * @author        Stefan Petre <www.eyecon.ro>
 * @copyright    Copyright (c) 2013-2015, Nazar Mokrynskyi
 * @copyright    Copyright (c) 2008-2009, Stefan Petre
 * @license        MIT License, see license.txt
 */

(function (d) {
    function getMaxDays() {
        var tmpDate = new Date(this.toString()),
            d = 28,
            m = tmpDate.getMonth();
        while (tmpDate.getMonth() == m) {
            ++d;
            tmpDate.setDate(d);
        }
        return d - 1;
    }

    d.addDays = function (n) {
        this.setDate(this.getDate() + n);
    };
    d.addMonths = function (n) {
        var day = this.getDate();
        this.setDate(1);
        this.setMonth(this.getMonth() + n);
        this.setDate(Math.min(day, getMaxDays.apply(this)));
    };
    d.addYears = function (n) {
        var day = this.getDate();
        this.setDate(1);
        this.setFullYear(this.getFullYear() + n);
        this.setDate(Math.min(day, getMaxDays.apply(this)));
    };
    d.getDayOfYear = function () {
        var now = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
        var then = new Date(this.getFullYear(), 0, 0, 0, 0, 0);
        var time = now - then;
        return Math.floor(time / 24 * 60 * 60 * 1000);
    };
})(Date.prototype);

(function (factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD
        define(['jquery'], factory);
    } else if (typeof exports === 'object') {
        // CommonJS
        factory(require('jquery'));
    } else {
        // Browser globals
        factory(jQuery);
    }
}(function ($) {
    var instances_count = 0;
    $.pickmeup = $.extend($.pickmeup || {}, {
        date: new Date,
        default_date: new Date,
        flat: false,
        first_day: 1,
        prev: '&#9664;',
        next: '&#9654;',
        mode: 'single',
        select_year: true,
        select_month: true,
        select_day: true,
        view: 'days',
        calendars: 1,
        format: 'd-m-Y',
        position: 'bottom',
        trigger_event: 'click touchstart',
        class_name: '',
        separator: ' - ',
        hide_on_select: false,
        min: null,
        max: null,
        render: function () {
        },
        change: function () {
            return true;
        },
        before_show: function () {
            return true;
        },
        show: function () {
            return true;
        },
        hide: function () {
            return true;
        },
        fill: function () {
            return true;
        },
        locale: {
            days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'],
            daysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
            daysMin: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'],
            months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
            monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
        }
    });
    var views = {
            years: 'pmu-view-years',
            months: 'pmu-view-months',
            days: 'pmu-view-days'
        },
        tpl = {
            wrapper: '<div class="pickmeup" />',
            head: function (d) {
                var result = '';
                for (var i = 0; i < 7; ++i) {
                    result += '<div>' + d.day[i] + '</div>'
                }
                return '<div class="pmu-instance">' +
                    '<nav>' +
                    '<div class="pmu-prev pmu-button">' + d.prev + '</div>' +
                    '<div class="pmu-month pmu-button" />' +
                    '<div class="pmu-next pmu-button">' + d.next + '</div>' +
                    '</nav>' +
                    '<nav class="pmu-day-of-week">' + result + '</nav>' +
                    '</div>';
            },
            body: function (elements, container_class_name) {
                var result = '';
                for (var i = 0; i < elements.length; ++i) {
                    result += '<div class="' + elements[i].class_name + ' pmu-button">' + elements[i].text + '</div>'
                }
                return '<div class="' + container_class_name + '">' + result + '</div>';
            }
        };

    function fill() {
        var options = $(this).data('pickmeup-options'),
            pickmeup = this.pickmeup,
            current_cal = Math.floor(options.calendars / 2),
            actual_date = options.date,
            current_date = options.current,
            min_date = options.min ? new Date(options.min) : null,
            max_date = options.max ? new Date(options.max) : null,
            local_date,
            header,
            html,
            instance,
            today = (new Date).setHours(0, 0, 0, 0).valueOf(),
            shown_date_from,
            shown_date_to,
            tmp_date;
        if (min_date) {
            min_date.setDate(1);
            min_date.addMonths(1);
            min_date.addDays(-1);
        }
        if (max_date) {
            max_date.setDate(1);
            max_date.addMonths(1);
            max_date.addDays(-1);
        }
        /**
         * Remove old content except header navigation
         */
        pickmeup.find('.pmu-instance > :not(nav)').remove();
        /**
         * If several calendars should be shown
         */
        for (var i = 0; i < options.calendars; i++) {
            local_date = new Date(current_date);
            instance = pickmeup.find('.pmu-instance').eq(i);
            if (pickmeup.hasClass('pmu-view-years')) {
                local_date.addYears((i - current_cal) * 12);
                header = (local_date.getFullYear() - 6) + ' - ' + (local_date.getFullYear() + 5);
            } else if (pickmeup.hasClass('pmu-view-months')) {
                local_date.addYears(i - current_cal);
                header = local_date.getFullYear();
            } else if (pickmeup.hasClass('pmu-view-days')) {
                local_date.addMonths(i - current_cal);
                header = formatDate(local_date, 'B, Y', options.locale);
            }
            if (!shown_date_to) {
                if (max_date) {
                    // If all dates in this month (months in year or years in years block) are after max option - set next month as current
                    // in order not to show calendar with all disabled dates
                    tmp_date = new Date(local_date);
                    if (options.select_day) {
                        tmp_date.addMonths(options.calendars - 1);
                    } else if (options.select_month) {
                        tmp_date.addYears(options.calendars - 1);
                    } else {
                        tmp_date.addYears((options.calendars - 1) * 12);
                    }
                    if (tmp_date > max_date) {
                        --i;
                        current_date.addMonths(-1);
                        shown_date_to = undefined;
                        continue;
                    }
                }
            }
            shown_date_to = new Date(local_date);
            if (!shown_date_from) {
                shown_date_from = new Date(local_date);
                // If all dates in this month are before min option - set next month as current in order not to show calendar with all disabled dates
                shown_date_from.setDate(1);
                shown_date_from.addMonths(1);
                shown_date_from.addDays(-1);
                if (min_date && min_date > shown_date_from) {
                    --i;
                    current_date.addMonths(1);
                    shown_date_from = undefined;
                    continue;
                }
            }
            instance
                .find('.pmu-month')
                .text(header);
            html = '';
            var is_year_selected = function (year) {
                return (
                        options.mode == 'range' &&
                        year >= new Date(actual_date[0]).getFullYear() &&
                        year <= new Date(actual_date[1]).getFullYear()
                    ) ||
                    (
                        options.mode == 'multiple' &&
                        actual_date.reduce(function (prev, current) {
                            prev.push(new Date(current).getFullYear());
                            return prev;
                        }, []).indexOf(year) !== -1
                    ) ||
                    new Date(actual_date).getFullYear() == year;
            };
            var is_months_selected = function (year, month) {
                var first_year = new Date(actual_date[0]).getFullYear(),
                    lastyear = new Date(actual_date[1]).getFullYear(),
                    first_month = new Date(actual_date[0]).getMonth(),
                    last_month = new Date(actual_date[1]).getMonth();
                return (
                        options.mode == 'range' &&
                        year > first_year &&
                        year < lastyear
                    ) ||
                    (
                        options.mode == 'range' &&
                        year == first_year &&
                        year < lastyear &&
                        month >= first_month
                    ) ||
                    (
                        options.mode == 'range' &&
                        year > first_year &&
                        year == lastyear &&
                        month <= last_month
                    ) ||
                    (
                        options.mode == 'range' &&
                        year == first_year &&
                        year == lastyear &&
                        month >= first_month &&
                        month <= last_month
                    ) ||
                    (
                        options.mode == 'multiple' &&
                        actual_date.reduce(function (prev, current) {
                            current = new Date(current);
                            prev.push(current.getFullYear() + '-' + current.getMonth());
                            return prev;
                        }, []).indexOf(year + '-' + month) !== -1
                    ) ||
                    (
                        new Date(actual_date).getFullYear() == year &&
                        new Date(actual_date).getMonth() == month
                    )
            };
            (function () {
                var years = [],
                    start_from_year = local_date.getFullYear() - 6,
                    min_year = new Date(options.min).getFullYear(),
                    max_year = new Date(options.max).getFullYear(),
                    year;
                for (var j = 0; j < 12; ++j) {
                    year = {
                        text: start_from_year + j,
                        class_name: []
                    };
                    if (
                        (
                            options.min && year.text < min_year
                        ) ||
                        (
                            options.max && year.text > max_year
                        )
                    ) {
                        year.class_name.push('pmu-disabled');
                    } else if (is_year_selected(year.text)) {
                        year.class_name.push('pmu-selected');
                    }
                    year.class_name = year.class_name.join(' ');
                    years.push(year);
                }
                html += tpl.body(years, 'pmu-years');
            })();
            (function () {
                var months = [],
                    current_year = local_date.getFullYear(),
                    min_year = new Date(options.min).getFullYear(),
                    min_month = new Date(options.min).getMonth(),
                    max_year = new Date(options.max).getFullYear(),
                    max_month = new Date(options.max).getMonth(),
                    month;
                for (var j = 0; j < 12; ++j) {
                    month = {
                        text: options.locale.monthsShort[j],
                        class_name: []
                    };
                    if (
                        (
                            options.min &&
                            (
                                current_year < min_year ||
                                (
                                    j < min_month && current_year == min_year
                                )
                            )
                        ) ||
                        (
                            options.max &&
                            (
                                current_year > max_year ||
                                (
                                    j > max_month && current_year >= max_year
                                )
                            )
                        )
                    ) {
                        month.class_name.push('pmu-disabled');
                    } else if (is_months_selected(current_year, j)) {
                        month.class_name.push('pmu-selected');
                    }
                    month.class_name = month.class_name.join(' ');
                    months.push(month);
                }
                html += tpl.body(months, 'pmu-months');
            })();
            (function () {
                var days = [],
                    current_month = local_date.getMonth(),
                    day;
                // Correct first day in calendar taking into account first day of week (Sunday or Monday)
                (function () {
                    local_date.setDate(1);
                    var day = (local_date.getDay() - options.first_day) % 7;
                    local_date.addDays(-(day + (day < 0 ? 7 : 0)));
                })();
                var indexSelected = 0;
                for (var j = 0; j < 42; ++j) {
                    day = {
                        text: local_date.getDate(),
                        class_name: []
                    };
                    if (current_month != local_date.getMonth()) {
                        day.class_name.push('pmu-not-in-month');
                    }
                    if (local_date.getDay() == 0) {
                        day.class_name.push('pmu-sunday');
                    } else if (local_date.getDay() == 6) {
                        day.class_name.push('pmu-saturday');
                    }
                    var from_user = options.render(new Date(local_date)) || {},
                        val = local_date.valueOf(),
                        disabled = (options.min && options.min > local_date) || (options.max && options.max < local_date);
                    if (from_user.disabled || disabled) {
                        day.class_name.push('pmu-disabled');
                    } else if (
                        from_user.selected ||
                        options.date == val ||
                        $.inArray(val, options.date) !== -1 ||
                        (
                            options.mode == 'range' && val >= options.date[0] && val <= options.date[1]
                        )
                    ) {
                        //HACK
                        if (indexSelected === 0) {
                            day.class_name.push('pmu-selected-first');
                        }
                        // /HACK
                        day.class_name.push('pmu-selected');
                        indexSelected++;
                    }
                    //HACK
                    else {
                        if (indexSelected >= 2) {
                            days[j - 1].class_name += ' pmu-selected-last';
                            indexSelected = 0;
                        }
                    }
                    // /HACK
                    if (val == today) {
                        day.class_name.push('pmu-today');
                    }
                    if (from_user.class_name) {
                        day.class_name.push(from_user.class_name);
                    }
                    day.class_name = day.class_name.join(' ');
                    days.push(day);
                    // Move to next day
                    local_date.addDays(1);
                }
                html += tpl.body(days, 'pmu-days');
            })();
            instance.append(html);
            // HACK
            $(this).data('$pmuelem', instance);
            // /HACK
        }
        shown_date_from.setDate(1);
        shown_date_to.setDate(1);
        shown_date_to.addMonths(1);
        shown_date_to.addDays(-1);
        pickmeup.find('.pmu-prev').css(
            'visibility',
            options.min && options.min >= shown_date_from ? 'hidden' : 'visible'
        );
        pickmeup.find('.pmu-next').css(
            'visibility',
            options.max && options.max <= shown_date_to ? 'hidden' : 'visible'
        );
        options.fill.apply(this);
    }

    function parseDate(date, format, separator, locale) {
        if (date.constructor == Date) {
            return date;
        } else if (!date) {
            return new Date;
        }
        var splitted_date = date.split(separator);
        if (splitted_date.length > 1) {
            splitted_date.forEach(function (element, index, array) {
                array[index] = parseDate($.trim(element), format, separator, locale);
            });
            return splitted_date;
        }
        var months_text = locale.monthsShort.join(')(') + ')(' + locale.months.join(')('),
            separator = new RegExp('[^0-9a-zA-Z(' + months_text + ')]+'),
            parts = date.split(separator),
            against = format.split(separator),
            d,
            m,
            y,
            h,
            min,
            now = new Date();
        for (var i = 0; i < parts.length; i++) {
            switch (against[i]) {
                case 'b':
                    m = locale.monthsShort.indexOf(parts[i]);
                    break;
                case 'B':
                    m = locale.months.indexOf(parts[i]);
                    break;
                case 'd':
                case 'e':
                    d = parseInt(parts[i], 10);
                    break;
                case 'm':
                    m = parseInt(parts[i], 10) - 1;
                    break;
                case 'Y':
                case 'y':
                    y = parseInt(parts[i], 10);
                    y += y > 100 ? 0 : (y < 29 ? 2000 : 1900);
                    break;
                case 'H':
                case 'I':
                case 'k':
                case 'l':
                    h = parseInt(parts[i], 10);
                    break;
                case 'P':
                case 'p':
                    if (/pm/i.test(parts[i]) && h < 12) {
                        h += 12;
                    } else if (/am/i.test(parts[i]) && h >= 12) {
                        h -= 12;
                    }
                    break;
                case 'M':
                    min = parseInt(parts[i], 10);
                    break;
            }
        }
        var parsed_date = new Date(
            y === undefined ? now.getFullYear() : y,
            m === undefined ? now.getMonth() : m,
            d === undefined ? now.getDate() : d,
            h === undefined ? now.getHours() : h,
            min === undefined ? now.getMinutes() : min,
            0
        );
        if (isNaN(parsed_date * 1)) {
            parsed_date = new Date;
        }
        return parsed_date;
    }

    function formatDate(date, format, locale) {
        var m = date.getMonth();
        var d = date.getDate();
        var y = date.getFullYear();
        var w = date.getDay();
        var s = {};
        var hr = date.getHours();
        var pm = (hr >= 12);
        var ir = (pm) ? (hr - 12) : hr;
        var dy = date.getDayOfYear();
        if (ir == 0) {
            ir = 12;
        }
        var min = date.getMinutes();
        var sec = date.getSeconds();
        var parts = format.split(''), part;
        for (var i = 0; i < parts.length; i++) {
            part = parts[i];
            switch (part) {
                case 'a':
                    part = locale.daysShort[w];
                    break;
                case 'A':
                    part = locale.days[w];
                    break;
                case 'b':
                    part = locale.monthsShort[m];
                    break;
                case 'B':
                    part = locale.months[m];
                    break;
                case 'C':
                    part = 1 + Math.floor(y / 100);
                    break;
                case 'd':
                    part = (d < 10) ? ("0" + d) : d;
                    break;
                case 'e':
                    part = d;
                    break;
                case 'H':
                    part = (hr < 10) ? ("0" + hr) : hr;
                    break;
                case 'I':
                    part = (ir < 10) ? ("0" + ir) : ir;
                    break;
                case 'j':
                    part = (dy < 100) ? ((dy < 10) ? ("00" + dy) : ("0" + dy)) : dy;
                    break;
                case 'k':
                    part = hr;
                    break;
                case 'l':
                    part = ir;
                    break;
                case 'm':
                    part = (m < 9) ? ("0" + (1 + m)) : (1 + m);
                    break;
                case 'M':
                    part = (min < 10) ? ("0" + min) : min;
                    break;
                case 'p':
                case 'P':
                    part = pm ? "PM" : "AM";
                    break;
                case 's':
                    part = Math.floor(date.getTime() / 1000);
                    break;
                case 'S':
                    part = (sec < 10) ? ("0" + sec) : sec;
                    break;
                case 'u':
                    part = w + 1;
                    break;
                case 'w':
                    part = w;
                    break;
                case 'y':
                    part = ('' + y).substr(2, 2);
                    break;
                case 'Y':
                    part = y;
                    break;
            }
            parts[i] = part;
        }
        return parts.join('');
    }

    function update_date() {
        var $this = $(this),
            options = $this.data('pickmeup-options'),
            current_date = options.current,
            new_value;
        switch (options.mode) {
            case 'multiple':
                new_value = current_date.setHours(0, 0, 0, 0).valueOf();
                if ($.inArray(new_value, options.date) !== -1) {
                    $.each(options.date, function (index, value) {
                        if (value == new_value) {
                            options.date.splice(index, 1);
                            return false;
                        }
                        return true;
                    });
                } else {
                    options.date.push(new_value);
                }
                break;
            case 'range':
                if (!options.lastSel) {
                    options.date[0] = current_date.setHours(0, 0, 0, 0).valueOf();
                }
                new_value = current_date.setHours(0, 0, 0, 0).valueOf();
                if (new_value <= options.date[0]) {
                    options.date[1] = options.date[0];
                    options.date[0] = new_value;
                } else {
                    options.date[1] = new_value;
                }
                options.lastSel = !options.lastSel;
                break;
            default:
                options.date = current_date.valueOf();
                break;
        }
        var prepared_date = prepareDate(options);
        if ($this.is('input')) {
            $this.val(options.mode == 'single' ? prepared_date[0] : prepared_date[0].join(options.separator)).trigger('timechange');
        }
        else {
            $this.trigger('timechange');//HACKED
        }
        options.change.apply(this, prepared_date);
        if (
            !options.flat &&
            options.hide_on_select &&
            (
                options.mode != 'range' || !options.lastSel
            )
        ) {
            options.binded.hide();
            return false;
        }
    }

    function click(e) {
        var el = $(e.target);
        if (!el.hasClass('pmu-button')) {
            el = el.closest('.pmu-button');
        }
        if (el.length) {
            if (el.hasClass('pmu-disabled')) {
                return false;
            }
            var $this = $(this),
                options = $this.data('pickmeup-options'),
                instance = el.parents('.pmu-instance').eq(0),
                root = instance.parent(),
                instance_index = $('.pmu-instance', root).index(instance);
            if (el.parent().is('nav')) {
                if (el.hasClass('pmu-month')) {
                    options.current.addMonths(instance_index - Math.floor(options.calendars / 2));
                    if (root.hasClass('pmu-view-years')) {
                        // Shift back to current date, otherwise with min value specified may jump on few (tens) years forward
                        if (options.mode != 'single') {
                            options.current = new Date(options.date[options.date.length - 1]);
                        } else {
                            options.current = new Date(options.date);
                        }
                        if (options.select_day) {
                            root.removeClass('pmu-view-years').addClass('pmu-view-days');
                        } else if (options.select_month) {
                            root.removeClass('pmu-view-years').addClass('pmu-view-months');
                        }
                    } else if (root.hasClass('pmu-view-months')) {
                        if (options.select_year) {
                            root.removeClass('pmu-view-months').addClass('pmu-view-years');
                        } else if (options.select_day) {
                            root.removeClass('pmu-view-months').addClass('pmu-view-days');
                        }
                    } else if (root.hasClass('pmu-view-days')) {
                        if (options.select_month) {
                            root.removeClass('pmu-view-days').addClass('pmu-view-months');
                        } else if (options.select_year) {
                            root.removeClass('pmu-view-days').addClass('pmu-view-years');
                        }
                    }
                } else {
                    if (el.hasClass('pmu-prev')) {
                        options.binded.prev(false);
                    } else {
                        options.binded.next(false);
                    }
                }
            } else if (!el.hasClass('pmu-disabled')) {
                if (root.hasClass('pmu-view-years')) {
                    options.current.setFullYear(parseInt(el.text(), 10));
                    if (options.select_month) {
                        root.removeClass('pmu-view-years').addClass('pmu-view-months');
                    } else if (options.select_day) {
                        root.removeClass('pmu-view-years').addClass('pmu-view-days');
                    } else {
                        options.binded.update_date();
                    }
                } else if (root.hasClass('pmu-view-months')) {
                    options.current.setMonth(instance.find('.pmu-months .pmu-button').index(el));
                    options.current.setFullYear(parseInt(instance.find('.pmu-month').text(), 10));
                    if (options.select_day) {
                        root.removeClass('pmu-view-months').addClass('pmu-view-days');
                    } else {
                        options.binded.update_date();
                    }
                    // Move current month to the first place
                    options.current.addMonths(Math.floor(options.calendars / 2) - instance_index);
                } else {
                    var val = parseInt(el.text(), 10);
                    options.current.addMonths(instance_index - Math.floor(options.calendars / 2));
                    if (el.hasClass('pmu-not-in-month')) {
                        options.current.addMonths(val > 15 ? -1 : 1);
                    }
                    options.current.setDate(val);
                    options.binded.update_date();
                }
            }
            options.binded.fill();
        }
        return false;
    }

    function prepareDate(options) {
        var result;
        if (options.mode == 'single') {
            result = new Date(options.date);
            return [formatDate(result, options.format, options.locale), result];
        } else {
            result = [[], []];
            $.each(options.date, function (nr, val) {
                var date = new Date(val);
                result[0].push(formatDate(date, options.format, options.locale));
                result[1].push(date);
            });
            return result;
        }
    }

    function show(force) {
        var pickmeup = this.pickmeup;
        if (force || !pickmeup.is(':visible')) {
            var $this = $(this),
                options = $this.data('pickmeup-options'),
                pos = $this.offset(),
                viewport = {
                    l: document.documentElement.scrollLeft,
                    t: document.documentElement.scrollTop,
                    w: document.documentElement.clientWidth,
                    h: document.documentElement.clientHeight
                },
                top = pos.top,
                left = pos.left;
            options.binded.fill();
            if ($this.is('input')) {
                $this
                    .pickmeup('set_date', parseDate($this.val() ? $this.val() : options.default_date, options.format, options.separator, options.locale))
                    .keydown(function (e) {
                        if (e.which == 9) {
                            $this.pickmeup('hide');
                        }
                    });
                options.lastSel = false;
            }
            options.before_show();
            if (options.show() == false) {
                return;
            }
            if (!options.flat) {
                switch (options.position) {
                    case 'top':
                        top -= pickmeup.outerHeight();
                        break;
                    case 'left':
                        left -= pickmeup.outerWidth();
                        break;
                    case 'right':
                        left += this.offsetWidth;
                        break;
                    case 'bottom':
                        top += this.offsetHeight;
                        break;
                }
                if (top + pickmeup.offsetHeight > viewport.t + viewport.h) {
                    top = pos.top - pickmeup.offsetHeight;
                }
                if (top < viewport.t) {
                    top = pos.top + this.offsetHeight + pickmeup.offsetHeight;
                }
                if (left + pickmeup.offsetWidth > viewport.l + viewport.w) {
                    left = pos.left - pickmeup.offsetWidth;
                }
                if (left < viewport.l) {
                    left = pos.left + this.offsetWidth
                }
                pickmeup.css({
                    display: 'inline-block',
                    top: top + 'px',
                    left: left + 'px'
                });
                $(document)
                    .on(
                        'mousedown' + options.events_namespace,// HACKED remove touchstart
                        options.binded.hide
                    )
                    .on(
                        'resize' + options.events_namespace,
                        [
                            true
                        ],
                        options.binded.forced_show
                    );
            }
        }
    }

    function forced_show() {
        show.call(this, true);
    }

    function hide(e) {
        if (
            !e || !e.target ||														//Called directly
            (
                e.target != this &&												//Clicked not on element itself
                !(this.pickmeup.get(0).compareDocumentPosition(e.target) & 16)	//And not o its children
            )
        ) {
            var pickmeup = this.pickmeup,
                options = $(this).data('pickmeup-options');
            if (options.hide() != false) {
                pickmeup.hide();
                $(document)
                    .off('click', options.binded.hide)// HACKED remove touchstart
                    .off('resize', options.binded.forced_show);
                options.lastSel = false;
            }
        }
    }

    function update() {
        var options = $(this).data('pickmeup-options');
        $(document)
            .off('mousedown', options.binded.hide)
            .off('resize', options.binded.forced_show);
        options.binded.forced_show();
    }

    function clear() {
        var options = $(this).data('pickmeup-options');
        if (options.mode != 'single') {
            options.date = [];
            options.lastSel = false;
            options.binded.fill();
        }
    }

    function prev(fill) {
        if (typeof fill == 'undefined') {
            fill = true;
        }
        var root = this.pickmeup;
        var options = $(this).data('pickmeup-options');
        if (root.hasClass('pmu-view-years')) {
            options.current.addYears(-12);
        } else if (root.hasClass('pmu-view-months')) {
            options.current.addYears(-1);
        } else if (root.hasClass('pmu-view-days')) {
            options.current.addMonths(-1);
        }
        if (fill) {
            options.binded.fill();
        }
    }

    function next(fill) {
        if (typeof fill == 'undefined') {
            fill = true;
        }
        var root = this.pickmeup;
        var options = $(this).data('pickmeup-options');
        if (root.hasClass('pmu-view-years')) {
            options.current.addYears(12);
        } else if (root.hasClass('pmu-view-months')) {
            options.current.addYears(1);
        } else if (root.hasClass('pmu-view-days')) {
            options.current.addMonths(1);
        }
        if (fill) {
            options.binded.fill();
        }
    }

    function get_date(formatted) {
        var options = $(this).data('pickmeup-options'),
            prepared_date = prepareDate(options);
        if (typeof formatted === 'string') {
            var date = prepared_date[1];
            if (date.constructor == Date) {
                return formatDate(date, formatted, options.locale)
            } else {
                return date.map(function (value) {
                    return formatDate(value, formatted, options.locale);
                });
            }
        } else {
            return prepared_date[formatted ? 0 : 1];
        }
    }

    function set_date(date) {
        var $this = $(this),
            options = $this.data('pickmeup-options');
        options.date = date;
        if (typeof options.date === 'string') {
            options.date = parseDate(options.date, options.format, options.separator, options.locale).setHours(0, 0, 0, 0);
        } else if (options.date.constructor == Date) {
            options.date.setHours(0, 0, 0, 0);
        }
        if (!options.date) {
            options.date = new Date;
            options.date.setHours(0, 0, 0, 0);
        }
        if (options.mode != 'single') {
            if (options.date.constructor != Array) {
                options.date = [options.date.valueOf()];
                if (options.mode == 'range') {
                    options.date.push(((new Date(options.date[0])).setHours(0, 0, 0, 0)).valueOf());
                }
            } else {
                for (var i = 0; i < options.date.length; i++) {
                    options.date[i] = (parseDate(options.date[i], options.format, options.separator, options.locale).setHours(0, 0, 0, 0)).valueOf();
                }
                if (options.mode == 'range') {
                    options.date[1] = ((new Date(options.date[1])).setHours(0, 0, 0, 0)).valueOf();
                }
            }
        } else {
            if ($this.val() || options.default_date !== false) {
                options.date = options.date.constructor == Array ? options.date[0].valueOf() : options.date.valueOf();
            }
        }
        options.current = new Date(options.mode != 'single' ? options.date[0] : options.date);
        options.binded.fill();
        if ($this.is('input')) {
            var prepared_date = prepareDate(options);
            $this.val(
                options.mode == 'single'
                    ? (options.default_date === false ? $this.val() : prepared_date[0])
                    : prepared_date[0].join(options.separator)
            );
        }
    }

    function destroy() {
        var $this = $(this),
            options = $this.data('pickmeup-options');
        $this.removeData('pickmeup-options');
        $this.off(options.events_namespace);
        $(document).off(options.events_namespace);
        $(this.pickmeup).remove();
    }

    $.fn.pickmeup = function (initial_options) {
        if (typeof initial_options === 'string') {
            var data,
                parameters = Array.prototype.slice.call(arguments, 1);
            switch (initial_options) {
                case 'hide':
                case 'show':
                case 'clear':
                case 'update':
                case 'prev':
                case 'next':
                case 'destroy':
                    this.each(function () {
                        data = $(this).data('pickmeup-options');
                        if (data) {
                            data.binded[initial_options]();
                        }
                    });
                    break;
                case 'get_date':
                    data = this.data('pickmeup-options');
                    if (data) {
                        return data.binded.get_date(parameters[0]);
                    } else {
                        return null;
                    }
                    break;
                case 'set_date':
                    this.each(function () {
                        data = $(this).data('pickmeup-options');
                        if (data) {
                            data.binded[initial_options].apply(this, parameters);
                        }
                    });
            }
            return this;
        }
        return this.each(function () {
            var $this = $(this);
            if ($this.data('pickmeup-options')) {
                return;
            }
            var i,
                option,
                options = $.extend({}, $.pickmeup, initial_options || {});
            for (i in options) {
                option = $this.data('pmu-' + i);
                if (typeof option !== 'undefined') {
                    options[i] = option;
                }
            }
            // 4 conditional statements in order to account all cases
            if (options.view == 'days' && !options.select_day) {
                options.view = 'months';
            }
            if (options.view == 'months' && !options.select_month) {
                options.view = 'years';
            }
            if (options.view == 'years' && !options.select_year) {
                options.view = 'days';
            }
            if (options.view == 'days' && !options.select_day) {
                options.view = 'months';
            }
            options.calendars = Math.max(1, parseInt(options.calendars, 10) || 1);
            options.mode = /single|multiple|range/.test(options.mode) ? options.mode : 'single';
            if (typeof options.min === 'string') {
                options.min = parseDate(options.min, options.format, options.separator, options.locale).setHours(0, 0, 0, 0);
            } else if (options.min && options.min.constructor == Date) {
                options.min.setHours(0, 0, 0, 0);
            }
            if (typeof options.max === 'string') {
                options.max = parseDate(options.max, options.format, options.separator, options.locale).setHours(0, 0, 0, 0);
            } else if (options.max && options.max.constructor == Date) {
                options.max.setHours(0, 0, 0, 0);
            }
            if (!options.select_day) {
                if (options.min) {
                    options.min = new Date(options.min);
                    options.min.setDate(1);
                    options.min = options.min.valueOf();
                }
                if (options.max) {
                    options.max = new Date(options.max);
                    options.max.setDate(1);
                    options.max = options.max.valueOf();
                }
            }
            if (typeof options.date === 'string') {
                options.date = parseDate(options.date, options.format, options.separator, options.locale).setHours(0, 0, 0, 0);
            } else if (options.date.constructor == Date) {
                options.date.setHours(0, 0, 0, 0);
            }
            if (!options.date) {
                options.date = new Date;
                options.date.setHours(0, 0, 0, 0);
            }
            if (options.mode != 'single') {
                if (options.date.constructor != Array) {
                    options.date = [options.date.valueOf()];
                    if (options.mode == 'range') {
                        options.date.push(((new Date(options.date[0])).setHours(0, 0, 0, 0)).valueOf());
                    }
                } else {
                    for (i = 0; i < options.date.length; i++) {
                        options.date[i] = (parseDate(options.date[i], options.format, options.separator, options.locale).setHours(0, 0, 0, 0)).valueOf();
                    }
                    if (options.mode == 'range') {
                        options.date[1] = ((new Date(options.date[1])).setHours(0, 0, 0, 0)).valueOf();
                    }
                }
                options.current = new Date(options.date[0]);
                // Set days to 1 in order to handle them consistently
                if (!options.select_day) {
                    for (i = 0; i < options.date.length; ++i) {
                        options.date[i] = new Date(options.date[i]);
                        options.date[i].setDate(1);
                        options.date[i] = options.date[i].valueOf();
                        // Remove duplicates
                        if (
                            options.mode != 'range' &&
                            options.date.indexOf(options.date[i]) !== i
                        ) {
                            delete options.date.splice(i, 1);
                            --i;
                        }
                    }
                }
            } else {
                options.date = options.date.valueOf();
                options.current = new Date(options.date);
                if (!options.select_day) {
                    options.date = new Date(options.date);
                    options.date.setDate(1);
                    options.date = options.date.valueOf();
                }
            }
            options.current.setDate(1);
            options.current.setHours(0, 0, 0, 0);
            var cnt,
                pickmeup = $(tpl.wrapper);
            this.pickmeup = pickmeup;
            if (options.class_name) {
                pickmeup.addClass(options.class_name);
            }
            var html = '';
            for (i = 0; i < options.calendars; i++) {
                cnt = options.first_day;
                html += tpl.head({
                    prev: options.prev,
                    next: options.next,
                    day: [
                        options.locale.daysMin[(cnt++) % 7],
                        options.locale.daysMin[(cnt++) % 7],
                        options.locale.daysMin[(cnt++) % 7],
                        options.locale.daysMin[(cnt++) % 7],
                        options.locale.daysMin[(cnt++) % 7],
                        options.locale.daysMin[(cnt++) % 7],
                        options.locale.daysMin[(cnt++) % 7]
                    ]
                });
            }
            $this.data('pickmeup-options', options);
            for (i in options) {
                if (['render', 'change', 'before_show', 'show', 'hide'].indexOf(i) != -1) {
                    options[i] = options[i].bind(this);
                }
            }
            options.binded = {
                fill: fill.bind(this),
                update_date: update_date.bind(this),
                click: click.bind(this),
                show: show.bind(this),
                forced_show: forced_show.bind(this),
                hide: hide.bind(this),
                update: update.bind(this),
                clear: clear.bind(this),
                prev: prev.bind(this),
                next: next.bind(this),
                get_date: get_date.bind(this),
                set_date: set_date.bind(this),
                destroy: destroy.bind(this)
            };
            options.events_namespace = '.pickmeup-' + (++instances_count);
            pickmeup
                .on('click', options.binded.click)// HACKED remove touchstart
                .addClass(views[options.view])
                .append(html)
                .on(
                    $.support.selectstart ? 'selectstart' : 'mousedown',
                    function (e) {
                        e.preventDefault();
                    }
                );
            options.binded.fill();
            if (options.flat) {
                pickmeup.appendTo(this).css({
                    position: 'relative',
                    display: 'inline-block'
                });
            } else {
                pickmeup.appendTo(document.body);
                // Multiple events support
                var trigger_event = options.trigger_event.split(' ');
                for (i = 0; i < trigger_event.length; ++i) {
                    trigger_event[i] += options.events_namespace;
                }
                trigger_event = trigger_event.join(' ');
                $this.on(trigger_event, options.binded.show);
            }
        });
    };
}));
