﻿/*global jQuery, window, console, document, alert */
/*jslint browser: true, unparam: true */
// (c) copyright 2011 carsales.com Ltd
// scripts for filter drop down

(function ($, csn) {
    var FilterDropDown = function () {
        this.container = null;
        this.activator = null;
        this.selectionText = null;
        this.menu = null;
        this.list = null;
        this.listAll = null;
        this.clearSelectionButton = null;
        this.checkboxes = null;
        this.maxValuesToDisplay = 0;
        this.disabled = false;
        this.isOpen = false;
        this.zIndex = 0;
        this._options = null;
    },

    uniqueTag = 0,
    openFilter = null,
    isKeyboardControl = false,
    timer,
    keyCode = {
        BACKSPACE: 8,
        DOWN: 40,
        ENTER: 13,
        ESCAPE: 27,
        SPACE: 32,
        TAB: 9,
        UP: 38
    },

    sortData = function (data, sortType) {
        switch (sortType) {
            case "alpha-column":
                data.sort(function (a, b) {
                    var c = String(a), d = String(b);
                    return c > d ? 1 : (c < d ? -1 : 0);
                });
                break;
            case "numeric-column":
                data.sort(function (a, b) {
                    return a - b; /* the good parts! */
                });
                break;
        }
    },
    init = function (target, settings) {
        var filter = new FilterDropDown(),
            sourceIsList = target.is("ol,ul"),
            data = settings.source,
            itemCount = sourceIsList ? target.children("li").length : data.length,
            label;

        uniqueTag += 1;
        label = target.siblings("label,.span-as-title");
        filter.container = $('<div class="csn-filter" style="position:relative"></div>').appendTo(target.parent());
        filter.maxValuesToDisplay = settings.maxValuesToDisplay;
        filter._options = settings;

        if (itemCount) {
            remainder = itemCount % settings.columns;
            if (target.is("a")) {
                filter.activator = target.addClass("filter-display").append('<span class="text">' + settings.allLabelText + '</span>').attr("href", "javascript:;");
            } else {
                filter.activator = $('<a href="javascript:;" class="filter-display"><span class="text">' + settings.allLabelText + '</span></a>');
            }
            filter.activator.wrapAll('<div class="filter-box"></div>').parent().css("z-index", "0").appendTo(filter.container);
            filter.selectionText = filter.activator.css("cursor", "pointer").find(".text");

            filter.menu = $('<div class="filter-menu"></div>')
                .css({ "position": "absolute", "display": "none" })
                .append('<ul><li><input type="checkbox" value="All" id="FilterListAll' + uniqueTag + '"/><label for="FilterListAll' + uniqueTag + '">' + settings.allLabelText + '</label></li></ul>')
                .appendTo(filter.container);

            filter.listAll = $('#FilterListAll' + uniqueTag)[0];
            filter.listAll.checked = true;

            filter.list = $('<div class="filter-list"></div>').appendTo(filter.menu);

            filter.clearSelectionButton = $("<a href='javascript:;' class='clear-filter'>Clear selection</a>").appendTo(filter.menu);

            if (target.hasClass("disabled")) {
                filter.container.addClass("disabled");
            }

            if (!sourceIsList && $.isArray(data) && data.length) {
                sortData(data, settings.sort);

                filter.load(data);

                if (target.children(".value,.text").length) {
                    filter.selectionText.text(target.children(".value,.text").text());
                }

            } else if (sourceIsList) {
                filter.list.append(target);
                // Make sure checkboxes and their attendent labels are unique!
                filter.checkboxes = filter.list.find(":checkbox").each(function () {
                    var id = this.id + "_" + uniqueTag;
                    this.id = id;
                    this.checked = false;
                    $(this).siblings("label").attr("for", id)[0];
                });
                bindEventsToCheckboxes(filter);
            }

            if (settings.useLabelAsPlaceholder) {
                label.prependTo(filter.activator);
            }

            filter.zIndex = parseInt(filter.container.css("zIndex"), 10) || 0;
            filter.menu.css("z-index", (filter.zIndex + 100));
        }

        return filter;
    },
    updateText = function (filter, labelText) {
        var selectionText = filter.selectionText;
        if (labelText.length > filter.maxValuesToDisplay) {
            selectionText.text(labelText.length + " selected");
        } else {
            selectionText.text(labelText.join(", "));
        }
        if (window.CSN && selectionText.css("overflow") === "hidden") {
            csn.addEllipsis(selectionText);
        }
    },
    handleChange = function (ev) {
        var filter = ev.data, values = [], text = [];

        if (ev.target !== filter.listAll) {
            if (!filter.checkboxes.filter(":checked").length) {
                filter.listAll.checked = true;
                values = ["All"];
                text = [filter._options.allLabelText];
            } else {
                filter.listAll.checked = false;
            }
            if (!filter.listAll.checked) {
                filter.checkboxes.filter(":checked").each(function () {
                    values.push(this.value);
                    text.push($(this).next("label").text());
                });
            }
            updateText(filter, text);
            filter.activator.triggerHandler("change.filterDropDown", [this.value, values]);
        } else {
            filter.reset();
        }
    },
    bindEventsToFilter = function (filter) {
        var toggleFilter = function () {
            if (!filter.isOpen) {
                filter.open();
            } else {
                filter.close();
            }
        },
        menuHandler = function (ev) {
            var key = ev.which, target = $(ev.target),
                targetIsInput = target.is("input"), targetIsLink = target.is("a");

            switch (key) {
                case keyCode.ESCAPE:
                case keyCode.ENTER:
                    isKeyboardControl = false;
                    toggleFilter();
                    ev.preventDefault();
                    filter.activator.focus();
                    break;
                case keyCode.SPACE:
                    if (targetIsLink) {
                        filter.reset();
                        ev.preventDefault();
                    }
                    break;
                case keyCode.TAB:
                    if (targetIsLink) {
                        filter.close();
                    }
                    break;
                case keyCode.UP:
                    if (targetIsInput) {
                        filter.activator.focus();
                    }
                    break;
            }
        },
        activatorHandler = function (ev) {
            var key = ev.which;
            switch (key) {
                case keyCode.DOWN:
                    if (!filter.isOpen) {
                        isKeyboardControl = true;
                        toggleFilter();
                    }
                    break;
                case keyCode.UP:
                    if (filter.isOpen) {
                        isKeyboardControl = false;
                        toggleFilter();
                    }
                    break;
            }
        };

        filter.activator.bind("keydown.filterDropDown", activatorHandler).parent().bind("click.filterDropDown", toggleFilter);
        filter.menu.bind("keydown", menuHandler);

        filter.container.mouseout(function (ev) {
            var relatedTarget = $(ev.relatedTarget),
                inMenu = relatedTarget.is(".csn-filter") || relatedTarget.parents(".csn-filter").length > 0;

            clearTimeout(timer);
            if (!inMenu && filter.container.hasClass("filter-open")) {
                // set timeout in case user has clicked something else and event bubbling was suppressed
                timer = setTimeout(toggleFilter, (!isKeyboardControl ? 100 : 2000));
            }
        });

        $("html").bind("click.filterDropDown", function (ev) {
            var t = $(ev.target), src = t.parents(".filter-open");
            if (!src.length && openFilter) {
                openFilter.close();
                isKeyboardControl = false;
            }
        });

        filter.clearSelectionButton.click(function () {
            filter.reset();
        });
    },

    bindEventsToCheckboxes = function (filter) {
        if ($.browser.msie) {
            $(filter.listAll).bind('click.filterDropDown', filter, handleChange);
            filter.checkboxes.bind('click.filterDropDown', filter, handleChange);
        } else {
            $(filter.listAll).bind('change.filterDropDown', filter, handleChange);
            filter.checkboxes.bind('change.filterDropDown', filter, handleChange);
        }
    };

    $.extend(FilterDropDown.prototype, {
        val: function (value) {
            var values = [], checkboxes = this.checkboxes, isAll = this.listAll, text = [];

            if (typeof value === "undefined") {
                checkboxes.each(function () {
                    if (this.checked || isAll.checked) {
                        values.push(this.value);
                    }
                });
                return values.join(",");
            } else if (typeof value === "string") {
                this.reset(true);
                if (value !== "All") {
                    this.listAll.checked = false;
                    checkboxes.each(function () {
                        if (value.indexOf(this.value) >= 0) {
                            this.checked = true;
                            text.push($(this).next("label").text());
                        }
                    });
                }
                // If value is "All", text.length is zero
                // Or if none of the specified values match the checkboxes, default to "All"
                if (!text.length || (checkboxes.length === text.length)) {
                    this.reset(true);
                } else if (text.length < checkboxes.length) {
                    isAll.checked = false;
                    updateText(this, text);
                }
            }
        },
        bind: function (eventName, eventHandler) {
            this.activator.bind("change.filterDropDown", eventHandler);
        },
        empty: function () {
            this.reset(true);
            this.list.empty();
        },
        load: function (data) {
            var settings = this._options, itemCount = data.length, prevMax = 0, remainder, max, html, i, j;
            sortData(data, settings.sort);

            if (!itemCount) {
                return;
            }

            uniqueTag += 1;
            html = [];
            // Organize columns
            for (i = 0; i < settings.columns; i++) {
                html.push('<ul>');

                if (i === settings.columns - 1) {
                    max = itemCount;
                } else {
                    max = Math.floor(prevMax + (itemCount / settings.columns));
                    if (i < remainder) {
                        max++;
                    }
                }
                for (j = prevMax; j < max; j++) {
                    html.push('<li><input id="FilterListBox' + uniqueTag + "_" + j + '" type="checkbox" value="' + data[j][0] + '"/><label for="FilterListBox' + uniqueTag + "_" + j + '">' + data[j][1] + '</label></li>');
                }

                prevMax = max;
                html.push('</ul>');
            }

            this.list.append(html.join(""));
            this.checkboxes = this.list.find(":checkbox");
            bindEventsToCheckboxes(this);
        },
        reset: function (preventEvent) {
            this.checkboxes.each(function () { this.checked = false; });
            this.listAll.checked = true;
            this.selectionText.text(this._options.allLabelText);
            if (!preventEvent) {
                this.activator.triggerHandler("change.filterDropDown", ["All", ["All"]]);
            }
        },
        disable: function () {
            this.disabled = true;
            this.container.addClass("disabled");
        },
        enable: function () {
            this.disabled = false;
            this.container.removeClass("disabled");
        },
        itemCount: function () {
            return this.list.children("li").length;
        },
        open: function () {
            this.container.addClass("filter-open");
            this.container.css("z-index", this.zIndex + 1);
            this.menu.show();
            this.isOpen = true;
            openFilter = this;
        },
        close: function () {
            this.container.removeClass("filter-open");
            this.container.css("z-index", this.zIndex);
            this.menu.hide();
            this.activator.focus();
            this.isOpen = false;
            openFilter = null;
        }
    });

    $.fn.filterDropDown = function (options) {
        var settings;

        if (!this.length) {
            return;
        }

        settings = $.extend({}, $.fn.filterDropDown.defaults, options);

        this.each(function () {
            var self = $(this), filter = init(self, settings);
            bindEventsToFilter(filter);

            if ($.isFunction(settings.onChange)) {
                filter.bind("change", settings.onChange);
            }
            self.data("filterDropDown", filter);
        });

        return this;
    };

    // 'source': 2-dimension array where [0] is value, and [1] is label
    $.fn.filterDropDown.defaults = {
        columns: 2,
        sort: 'alpha-column',
        source: [],
        onChange: null,
        maxValuesToDisplay: 1,
        useLabelAsPlaceholder: false,
        allLabelText: "All"
    };

} (jQuery, window.CSN));

