<commit_before>var common = require("../common.js");
var EC = protractor.ExpectedConditions;

fdescribe('resources', function() {

    beforeEach(function(){
        browser.get('/#/resources');
    });

    it('should find it by its identifier', function() {
        common.createDoc("12")
            .then(typeInIdentifierInSearchField)
            .then(function(){
                expect(element(by.css('#objectList .list-group-item .identifier')).getText()).toEqual("12");
            });
    });

    it ('should show only resources of the selected type', function() {
        common.createDoc("1", 0)
            .then(common.createDoc("2", 1))
            .then(setTypeFilter(2))
            .then(setTypeFilter(1))
            .then(function() {
                browser.wait(EC.presenceOf(element(by.id('resource-2'))), 1000);
                expect(element(by.id('resource-1')).isPresent()).toBe(false);
            })
            .then(setTypeFilter(0))
            .then(function() {
                browser.wait(EC.presenceOf(element(by.id('resource-1'))), 1000);
                expect(element(by.id('resource-2')).isPresent()).toBe(false);
            })
            .then(setTypeFilter('all'))
            .then(function() {
                browser.wait(EC.presenceOf(element(by.id('resource-1'))), 1000);
                browser.wait(EC.presenceOf(element(by.id('resource-2'))), 1000);
            });
    });

    it ('should reflect changes in overview in realtime', function() {
        common.createDoc("1a")
            .then(common.createDoc("2"))
            .then(common.selectObject(1))
            .then(common.switchToEditMode)
            .then(common.typeInIdentifier("1b"))
            .then(function(){
                expect(element.all(by.css('#objectList .list-group-item .identifier')).get(1).getText()).toEqual("1b");
            });
    });

    /**
     * There was a bug which caused that a freshly created object
     * was not the same instance in the document edit and the overview component anymore
     * so that changes made to one would not be reflected in the other.
     *
     * This however did not happen with an object already saved.
     */
    it ('should reflect changes in overview after creating object', function() {
        common.createDoc("12")
            .then(common.typeInIdentifier("34"))
            .then(function(){
                expect(element(by.css('#objectList .list-group-item .identifier')).getText()).toEqual("34");
            });
    });

    /**
     * There has been a bug where this was not possible.
     * The attempt to do so got rejected with the duplicate identifier message.
     */
    it ('should save a new object and then save it again', function() {
        common.createDoc("1")
            .then(common.saveObject)
            .then(function(){
                expect(element(by.id('message-0')).getText())
                    .toContain("erfolgreich");
            });
    });

    /**
     * There has been a bug where clicking the new button without doing anything
     * led to leftovers of "Neues Objekt" for every time the button was pressed.
     */
    it("should remove a new object from the list if it hasn't been saved", function() {
        common.createDoc("1")
            .then(common.clickCreateObjectButton)
            .then(common.selectType)
            .then(common.chooseGeometry)
            .then(common.clickCreateObjectButton)
            .then(common.selectType)
            .then(common.chooseGeometry)
            .then(function(){
                expect(element(by.css('#objectList .list-group-item .new')).getText()).toEqual("Neues Objekt");
            })
            .then(common.scrollUp)
            .then(common.selectObject(1))
            .then(function(){
                expect(element(by.css('#objectList .list-group-item .identifier')).getText()).toEqual("1");
            })
    });

    it ("should change the selection to new when saving via modal", function() {
        common.createDoc("1")
            .then(common.selectObject(0))
            .then(common.switchToEditMode())
            .then(common.typeInIdentifier("2"))
            .then(common.clickCreateObjectButton())
            .then(common.selectType())
            .then(common.chooseGeometry())
            .then(common.scrollUp)
            .then(common.clickSaveInModal)
            .then(common.scrollUp)
            .then(function(){
                expect(element(by.css('#objectList .list-group-item .new')).getText()).toEqual("Neues Objekt");
            })
    });

    it ("should change the selection to existing when saving via modal", function() {
        common.createDoc("1")
            .then(common.createDoc("2"))
            .then(common.selectObject(0))
            .then(common.switchToEditMode())
            .then(common.typeInIdentifier("2a"))
            .then(common.selectObject(1))
            .then(common.scrollUp)
            .then(common.clickSaveInModal)
            .then(common.scrollUp)
            .then(function(){
                expect(element.all(by.css('#objectList .list-group-item')).get(1)
                    .getAttribute('class')).toContain('selected')
            })
    });

    it ("should not change the selection to existing when cancelling in modal", function() {
        common.createDoc("1")
            .then(common.createDoc("2"))
            .then(common.selectObject(0))
            .then(common.switchToEditMode())
            .then(common.typeInIdentifier("2a"))
            .then(common.selectObject(1))
            .then(common.scrollUp)
            .then(common.clickCancelInModal)
            .then(common.scrollUp)
            .then(function(){
                expect(element.all(by.css('#objectList .list-group-item')).get(0)
                    .getAttribute('class')).toContain('selected')
            })
    });

    function typeInIdentifierInSearchField() {
        return common.typeIn(element(by.id('object-search')), "12");
    }

    function setTypeFilter(typeIndex) {
        return element(by.id('searchfilter')).click()
            .then(function() { return element(by.id('choose-type-filter-option-' + typeIndex)).click() });
    }

});
<commit_msg>Remove f.<commit_after>var common = require("../common.js");
var EC = protractor.ExpectedConditions;

describe('resources', function() {

    beforeEach(function(){
        browser.get('/#/resources');
    });

    it('should find it by its identifier', function() {
        common.createDoc("12")
            .then(typeInIdentifierInSearchField)
            .then(function(){
                expect(element(by.css('#objectList .list-group-item .identifier')).getText()).toEqual("12");
            });
    });

    it ('should show only resources of the selected type', function() {
        common.createDoc("1", 0)
            .then(common.createDoc("2", 1))
            .then(setTypeFilter(2))
            .then(setTypeFilter(1))
            .then(function() {
                browser.wait(EC.presenceOf(element(by.id('resource-2'))), 1000);
                expect(element(by.id('resource-1')).isPresent()).toBe(false);
            })
            .then(setTypeFilter(0))
            .then(function() {
                browser.wait(EC.presenceOf(element(by.id('resource-1'))), 1000);
                expect(element(by.id('resource-2')).isPresent()).toBe(false);
            })
            .then(setTypeFilter('all'))
            .then(function() {
                browser.wait(EC.presenceOf(element(by.id('resource-1'))), 1000);
                browser.wait(EC.presenceOf(element(by.id('resource-2'))), 1000);
            });
    });

    it ('should reflect changes in overview in realtime', function() {
        common.createDoc("1a")
            .then(common.createDoc("2"))
            .then(common.selectObject(1))
            .then(common.switchToEditMode)
            .then(common.typeInIdentifier("1b"))
            .then(function(){
                expect(element.all(by.css('#objectList .list-group-item .identifier')).get(1).getText()).toEqual("1b");
            });
    });

    /**
     * There was a bug which caused that a freshly created object
     * was not the same instance in the document edit and the overview component anymore
     * so that changes made to one would not be reflected in the other.
     *
     * This however did not happen with an object already saved.
     */
    it ('should reflect changes in overview after creating object', function() {
        common.createDoc("12")
            .then(common.typeInIdentifier("34"))
            .then(function(){
                expect(element(by.css('#objectList .list-group-item .identifier')).getText()).toEqual("34");
            });
    });

    /**
     * There has been a bug where this was not possible.
     * The attempt to do so got rejected with the duplicate identifier message.
     */
    it ('should save a new object and then save it again', function() {
        common.createDoc("1")
            .then(common.saveObject)
            .then(function(){
                expect(element(by.id('message-0')).getText())
                    .toContain("erfolgreich");
            });
    });

    /**
     * There has been a bug where clicking the new button without doing anything
     * led to leftovers of "Neues Objekt" for every time the button was pressed.
     */
    it("should remove a new object from the list if it hasn't been saved", function() {
        common.createDoc("1")
            .then(common.clickCreateObjectButton)
            .then(common.selectType)
            .then(common.chooseGeometry)
            .then(common.clickCreateObjectButton)
            .then(common.selectType)
            .then(common.chooseGeometry)
            .then(function(){
                expect(element(by.css('#objectList .list-group-item .new')).getText()).toEqual("Neues Objekt");
            })
            .then(common.scrollUp)
            .then(common.selectObject(1))
            .then(function(){
                expect(element(by.css('#objectList .list-group-item .identifier')).getText()).toEqual("1");
            })
    });

    it ("should change the selection to new when saving via modal", function() {
        common.createDoc("1")
            .then(common.selectObject(0))
            .then(common.switchToEditMode())
            .then(common.typeInIdentifier("2"))
            .then(common.clickCreateObjectButton())
            .then(common.selectType())
            .then(common.chooseGeometry())
            .then(common.scrollUp)
            .then(common.clickSaveInModal)
            .then(common.scrollUp)
            .then(function(){
                expect(element(by.css('#objectList .list-group-item .new')).getText()).toEqual("Neues Objekt");
            })
    });

    it ("should change the selection to existing when saving via modal", function() {
        common.createDoc("1")
            .then(common.createDoc("2"))
            .then(common.selectObject(0))
            .then(common.switchToEditMode())
            .then(common.typeInIdentifier("2a"))
            .then(common.selectObject(1))
            .then(common.scrollUp)
            .then(common.clickSaveInModal)
            .then(common.scrollUp)
            .then(function(){
                expect(element.all(by.css('#objectList .list-group-item')).get(1)
                    .getAttribute('class')).toContain('selected')
            })
    });

    it ("should not change the selection to existing when cancelling in modal", function() {
        common.createDoc("1")
            .then(common.createDoc("2"))
            .then(common.selectObject(0))
            .then(common.switchToEditMode())
            .then(common.typeInIdentifier("2a"))
            .then(common.selectObject(1))
            .then(common.scrollUp)
            .then(common.clickCancelInModal)
            .then(common.scrollUp)
            .then(function(){
                expect(element.all(by.css('#objectList .list-group-item')).get(0)
                    .getAttribute('class')).toContain('selected')
            })
    });

    function typeInIdentifierInSearchField() {
        return common.typeIn(element(by.id('object-search')), "12");
    }

    function setTypeFilter(typeIndex) {
        return element(by.id('searchfilter')).click()
            .then(function() { return element(by.id('choose-type-filter-option-' + typeIndex)).click() });
    }

});
<|endoftext|>


<commit_before>/* global describe:false, it:false, beforeEach:false, expect:false, elementResizeDetectorMaker:false, _:false, $:false, jasmine:false */

"use strict";

//This messed with tests in IE8.
//jasmine.getFixtures().fixturesPath = "/base/test/";

function ensureMapEqual(before, after, ignore) {
    var beforeKeys = _.keys(before);
    var afterKeys = _.keys(after);

    var unionKeys = _.union(beforeKeys, afterKeys);

    var diffValueKeys = _.filter(unionKeys, function(key) {
        var beforeValue = before[key];
        var afterValue = after[key];
        return !ignore(key, beforeValue, afterValue) && beforeValue !== afterValue;
    });

    if(diffValueKeys.length) {
        var beforeDiffObject = {};
        var afterDiffObject = {};

        _.forEach(diffValueKeys, function(key) {
            beforeDiffObject[key] = before[key];
            afterDiffObject[key] = after[key];
        });

        expect(afterDiffObject).toEqual(beforeDiffObject);
    }
}

function getStyle(element) {
    function clone(styleObject) {
        var clonedTarget = {};
        _.forEach(styleObject.cssText.split(";").slice(0, -1), function(declaration){
            var colonPos = declaration.indexOf(":");
            var attr = declaration.slice(0, colonPos).trim();
            if(attr.indexOf("-") === -1){ // Remove attributes like "background-image", leaving "backgroundImage"
                clonedTarget[attr] = declaration.slice(colonPos+2);
            }
        });
        return clonedTarget;
    }

    var style = getComputedStyle(element);
    return clone(style);
}

var ensureStyle = ensureMapEqual;

function positonFromStaticToRelative(key, before, after) {
    return key === "position" && before === "static" && after === "relative";
}

function getAttributes(element) {
    var attrs = {};
    _.forEach(element.attributes, function(attr) {
        attrs[attr.nodeName] = attr.nodeValue;
    });
    return attrs;
}

var ensureAttributes = ensureMapEqual;

var reporter = {
    log: function() {
        throw new Error("Reporter.log should not be called");
    },
    warn: function() {
        throw new Error("Reporter.warn should not be called");
    },
    error: function() {
        throw new Error("Reporter.error should not be called");
    }
};

$("body").prepend("

");

function listenToTest(strategy) {
    describe("listenTo (" + strategy + ")", function() {
        it("should be able to attach a listener to an element", function(done) {
            var erd = elementResizeDetectorMaker({
                callOnAdd: false,
                reporter: reporter,
                strategy: strategy
            });

            var listener = jasmine.createSpy("listener");

            erd.listenTo($("#test")[0], listener);

            setTimeout(function() {
                $("#test").width(300);
            }, 100);

            setTimeout(function() {
                expect(listener).toHaveBeenCalledWith($("#test")[0]);
                done();
            }, 400);
        });

        it("should throw on invalid parameters", function() {
            var erd = elementResizeDetectorMaker({
                callOnAdd: false,
                reporter: reporter,
                strategy: strategy
            });

            expect(erd.listenTo).toThrow();

            expect(_.partial(erd.listenTo, $("#test")[0])).toThrow();
        });

        describe("option.onReady", function() {
            it("should be called when installing a listener to an element", function(done) {
                var erd = elementResizeDetectorMaker({
                    callOnAdd: false,
                    reporter: reporter,
                    strategy: strategy
                });

                var listener = jasmine.createSpy("listener");

                erd.listenTo({
                    onReady: function() {
                        $("#test").width(200);
                        setTimeout(function() {
                            expect(listener).toHaveBeenCalledWith($("#test")[0]);
                            done();
                        }, 100);
                    }
                }, $("#test")[0], listener);
            });

            it("should be called when all elements are ready", function(done) {
                var erd = elementResizeDetectorMaker({
                    callOnAdd: false,
                    reporter: reporter,
                    strategy: strategy
                });

                var listener = jasmine.createSpy("listener");

                erd.listenTo({
                    onReady: function() {
                        $("#test").width(200);
                        $("#test2").width(300);
                        setTimeout(function() {
                            expect(listener).toHaveBeenCalledWith($("#test")[0]);
                            expect(listener).toHaveBeenCalledWith($("#test2")[0]);
                            done();
                        }, 100);
                    }
                }, $("#test, #test2"), listener);
            });

            it("should be able to handle listeners for the same element but different calls", function(done) {
                var erd = elementResizeDetectorMaker({
                    callOnAdd: false,
                    reporter: reporter,
                    strategy: strategy
                });

                var onReady1 = jasmine.createSpy("listener");
                var onReady2 = jasmine.createSpy("listener");

                erd.listenTo({
                    onReady: onReady1
                }, $("#test"), function noop() {});
                erd.listenTo({
                    onReady: onReady2
                }, $("#test"), function noop() {});

                setTimeout(function() {
                    expect(onReady1.calls.count()).toBe(1);
                    expect(onReady2.calls.count()).toBe(1);
                    done();
                }, 300);
            });

            it("should be able to handle when elements occur multiple times in the same call (and other calls)", function(done) {
                var erd = elementResizeDetectorMaker({
                    callOnAdd: false,
                    reporter: reporter,
                    strategy: strategy
                });

                var onReady1 = jasmine.createSpy("listener");
                var onReady2 = jasmine.createSpy("listener");

                erd.listenTo({
                    onReady: onReady1
                }, [$("#test")[0], $("#test")[0]], function noop() {});
                erd.listenTo({
                    onReady: onReady2
                }, $("#test"), function noop() {});

                setTimeout(function() {
                    expect(onReady1.calls.count()).toBe(1);
                    expect(onReady2.calls.count()).toBe(1);
                    done();
                }, 300);
            });
        });

        it("should be able to attach multiple listeners to an element", function(done) {
            var erd = elementResizeDetectorMaker({
                callOnAdd: false,
                reporter: reporter,
                strategy: strategy
            });

            var listener1 = jasmine.createSpy("listener1");
            var listener2 = jasmine.createSpy("listener2");

            erd.listenTo($("#test")[0], listener1);
            erd.listenTo($("#test")[0], listener2);

            setTimeout(function() {
                $("#test").width(300);
            }, 200);

            setTimeout(function() {
                expect(listener1).toHaveBeenCalledWith($("#test")[0]);
                expect(listener2).toHaveBeenCalledWith($("#test")[0]);
                done();
            }, 400);
        });

        it("should be able to attach a listener to an element multiple times within the same call", function(done) {
            var erd = elementResizeDetectorMaker({
                callOnAdd: false,
                reporter: reporter,
                strategy: strategy
            });

            var listener1 = jasmine.createSpy("listener1");

            erd.listenTo([$("#test")[0], $("#test")[0]], listener1);

            setTimeout(function() {
                $("#test").width(300);
            }, 200);

            setTimeout(function() {
                expect(listener1).toHaveBeenCalledWith($("#test")[0]);
                expect(listener1.calls.count()).toBe(2);
                done();
            }, 400);
        });

        it("should be able to attach listeners to multiple elements", function(done) {
            var erd = elementResizeDetectorMaker({
                callOnAdd: false,
                reporter: reporter,
                strategy: strategy
            });

            var listener1 = jasmine.createSpy("listener1");

            erd.listenTo($("#test, #test2"), listener1);

            setTimeout(function() {
                $("#test").width(200);
            }, 100);

            setTimeout(function() {
                expect(listener1).toHaveBeenCalledWith($("#test")[0]);
            }, 300);

            setTimeout(function() {
                $("#test2").width(500);
            }, 400);

            setTimeout(function() {
               expect(listener1).toHaveBeenCalledWith($("#test2")[0]);
               done();
            }, 800);
        });

        //Only run this test if the browser actually is able to get the computed style of an element.
        //Only IE8 is lacking the getComputedStyle method.
        if(window.getComputedStyle) {
            it("should keep the style of the element intact", function(done) {
                var erd = elementResizeDetectorMaker({
                    callOnAdd: false,
                    reporter: reporter,
                    strategy: strategy
                });

                var before = getStyle($("#test")[0]);
                erd.listenTo($("#test")[0], _.noop);
                var after = getStyle($("#test")[0]);
                ensureStyle(before, after, positonFromStaticToRelative);

                //Test styles async since making an element listenable is async.
                setTimeout(function() {
                    var afterAsync = getStyle($("#test")[0]);
                    ensureStyle(before, afterAsync, positonFromStaticToRelative);
                    done();
                }, 100);
            });
        }

        it("should call listener if the element is changed synchronously after listenTo", function(done) {
            var erd = elementResizeDetectorMaker({
                callOnAdd: true,
                reporter: reporter,
                strategy: strategy
            });

            var listener1 = jasmine.createSpy("listener1");
            erd.listenTo($("#test, #test2"), listener1);

            setTimeout(function() {
                expect(listener1).toHaveBeenCalledWith($("#test")[0]);
                expect(listener1).toHaveBeenCalledWith($("#test2")[0]);
                done();
            }, 100);
        });

        it("should use the option.idHandler if present", function(done) {
            var ID_ATTR = "some-fancy-id-attr";

            var idHandler = {
                get: function(element) {
                    if(element[ID_ATTR] === undefined) {
                        var id;

                        if($(element).attr("id") === "test") {
                            id = "test+1";
                        } else if($(element).attr("id") === "test2") {
                            id = "test2+2";
                        }

                        $(element).attr(ID_ATTR, id);
                    }

                    return $(element).attr(ID_ATTR);
                }
            };

            var erd = elementResizeDetectorMaker({
                idHandler: idHandler,
                callOnAdd: false,
                reporter: reporter,
                strategy: strategy
            });

            var listener1 = jasmine.createSpy("listener1");
            var listener2 = jasmine.createSpy("listener1");

            var attrsBeforeTest = getAttributes($("#test")[0]);
            var attrsBeforeTest2 = getAttributes($("#test2")[0]);

            erd.listenTo($("#test"), listener1);
            erd.listenTo($("#test, #test2"), listener2);

            var attrsAfterTest = getAttributes($("#test")[0]);
            var attrsAfterTest2 = getAttributes($("#test2")[0]);

            var ignoreValidIdAttrAndStyle = function(key) {
                return key === ID_ATTR || key === "style";
            };

            ensureAttributes(attrsBeforeTest, attrsAfterTest, ignoreValidIdAttrAndStyle);
            ensureAttributes(attrsBeforeTest2, attrsAfterTest2, ignoreValidIdAttrAndStyle);

            expect($("#test").attr(ID_ATTR)).toEqual("test+1");
            expect($("#test2").attr(ID_ATTR)).toEqual("test2+2");

            setTimeout(function() {
                $("#test").width(300);
                $("#test2").width(500);
            }, 100);

            setTimeout(function() {
               expect(listener1).toHaveBeenCalledWith($("#test")[0]);
               expect(listener2).toHaveBeenCalledWith($("#test")[0]);
               expect(listener2).toHaveBeenCalledWith($("#test2")[0]);
               done();
            }, 600);
        });
    });
}

describe("element-resize-detector", function() {
    beforeEach(function() {
        //This messed with tests in IE8.
        //TODO: Investigate why, because it would be nice to have instead of the current solution.
        //loadFixtures("element-resize-detector_fixture.html");
        $("#fixtures").html("

");
    });

    describe("elementResizeDetectorMaker", function() {
        it("should be globally defined", function() {
            expect(elementResizeDetectorMaker).toBeFunction();
        });

        it("should create an element-resize-detector instance", function() {
            var erd = elementResizeDetectorMaker();

            expect(erd).toBeObject();
            expect(erd).toHaveMethod("listenTo");
        });
    });

    describe("options.callOnAdd", function() {
        it("should be true default and call all functions when listenTo succeeds", function(done) {
            var erd = elementResizeDetectorMaker({
                reporter: reporter
            });

            var listener = jasmine.createSpy("listener");
            var listener2 = jasmine.createSpy("listener2");

            erd.listenTo($("#test")[0], listener);
            erd.listenTo($("#test")[0], listener2);

            setTimeout(function() {
                expect(listener).toHaveBeenCalledWith($("#test")[0]);
                expect(listener2).toHaveBeenCalledWith($("#test")[0]);
                listener.calls.reset();
                listener2.calls.reset();
                $("#test").width(300);
            }, 100);

            setTimeout(function() {
                expect(listener).toHaveBeenCalledWith($("#test")[0]);
                expect(listener2).toHaveBeenCalledWith($("#test")[0]);
                done();
            }, 400);
        });
    });

    describe("resizeDetector.removeListener", function() {
        it("should remove listener from element", function(done) {
            var erd = elementResizeDetectorMaker({
                callOnAdd: false
            });

            var $testElem = $("#test");

            var listenerCall    = jasmine.createSpy("listener");
            var listenerNotCall = jasmine.createSpy("listener");

            erd.listenTo($testElem[0], listenerCall);
            erd.listenTo($testElem[0], listenerNotCall);

            setTimeout(function() {
                erd.removeListener($testElem[0], listenerNotCall);
                $testElem.width(300);
            }, 100);

            setTimeout(function() {
                expect(listenerCall).toHaveBeenCalled();
                expect(listenerNotCall).not.toHaveBeenCalled();
                done();
            }, 400);
        });
    });

    describe("resizeDetector.removeAllListeners", function() {
        it("should remove all listeners from element", function(done) {
            var erd = elementResizeDetectorMaker({
                callOnAdd: false
            });

            var $testElem = $("#test");

            var listener1 = jasmine.createSpy("listener");
            var listener2 = jasmine.createSpy("listener");

            erd.listenTo($testElem[0], listener1);
            erd.listenTo($testElem[0], listener2);

            setTimeout(function() {
                erd.removeAllListeners($testElem[0]);
                $testElem.width(300);
            }, 100);

            setTimeout(function() {
                expect(listener1).not.toHaveBeenCalled();
                expect(listener2).not.toHaveBeenCalled();
                done();
            }, 400);
        });
    });

    describe("resizeDetector.uninstall - object strategy", function() {
        it("should completely remove detector from element", function(done) {
            var erd = elementResizeDetectorMaker({
                callOnAdd: false,
                strategy: "object"
            });

            var $testElem = $("#test");

            var listener = jasmine.createSpy("listener");

            erd.listenTo($testElem[0], listener);

            setTimeout(function() {
                erd.uninstall($testElem[0]);
                // detector element should be removed
                expect($testElem[0].childNodes.length).toBe(0);
                $testElem.width(300);
            }, 100);

            setTimeout(function() {
                expect(listener).not.toHaveBeenCalled();
                done();
            }, 400);
        });
    });

    describe("resizeDetector.uninstall - scroll strategy", function() {
        it("should completely remove detector from element", function(done) {
            var erd = elementResizeDetectorMaker({
                callOnAdd: false,
                strategy: "scroll"
            });

            var $testElem = $("#test");

            var listener = jasmine.createSpy("listener");

            erd.listenTo($testElem[0], listener);

            setTimeout(function() {
                erd.uninstall($testElem[0]);
                // detector element should be removed
                expect($testElem[0].childNodes.length).toBe(0);
                $testElem.width(300);
            }, 100);

            setTimeout(function() {
                expect(listener).not.toHaveBeenCalled();
                done();
            }, 400);
        });
    });

    listenToTest("object");

    //Scroll only supported on non-opera browsers.
    if(!window.opera) {
        listenToTest("scroll");
    }
});
<commit_msg>test that removeAllListeners works on elements without detector<commit_after>/* global describe:false, it:false, beforeEach:false, expect:false, elementResizeDetectorMaker:false, _:false, $:false, jasmine:false */

"use strict";

//This messed with tests in IE8.
//jasmine.getFixtures().fixturesPath = "/base/test/";

function ensureMapEqual(before, after, ignore) {
    var beforeKeys = _.keys(before);
    var afterKeys = _.keys(after);

    var unionKeys = _.union(beforeKeys, afterKeys);

    var diffValueKeys = _.filter(unionKeys, function(key) {
        var beforeValue = before[key];
        var afterValue = after[key];
        return !ignore(key, beforeValue, afterValue) && beforeValue !== afterValue;
    });

    if(diffValueKeys.length) {
        var beforeDiffObject = {};
        var afterDiffObject = {};

        _.forEach(diffValueKeys, function(key) {
            beforeDiffObject[key] = before[key];
            afterDiffObject[key] = after[key];
        });

        expect(afterDiffObject).toEqual(beforeDiffObject);
    }
}

function getStyle(element) {
    function clone(styleObject) {
        var clonedTarget = {};
        _.forEach(styleObject.cssText.split(";").slice(0, -1), function(declaration){
            var colonPos = declaration.indexOf(":");
            var attr = declaration.slice(0, colonPos).trim();
            if(attr.indexOf("-") === -1){ // Remove attributes like "background-image", leaving "backgroundImage"
                clonedTarget[attr] = declaration.slice(colonPos+2);
            }
        });
        return clonedTarget;
    }

    var style = getComputedStyle(element);
    return clone(style);
}

var ensureStyle = ensureMapEqual;

function positonFromStaticToRelative(key, before, after) {
    return key === "position" && before === "static" && after === "relative";
}

function getAttributes(element) {
    var attrs = {};
    _.forEach(element.attributes, function(attr) {
        attrs[attr.nodeName] = attr.nodeValue;
    });
    return attrs;
}

var ensureAttributes = ensureMapEqual;

var reporter = {
    log: function() {
        throw new Error("Reporter.log should not be called");
    },
    warn: function() {
        throw new Error("Reporter.warn should not be called");
    },
    error: function() {
        throw new Error("Reporter.error should not be called");
    }
};

$("body").prepend("

");

function listenToTest(strategy) {
    describe("listenTo (" + strategy + ")", function() {
        it("should be able to attach a listener to an element", function(done) {
            var erd = elementResizeDetectorMaker({
                callOnAdd: false,
                reporter: reporter,
                strategy: strategy
            });

            var listener = jasmine.createSpy("listener");

            erd.listenTo($("#test")[0], listener);

            setTimeout(function() {
                $("#test").width(300);
            }, 100);

            setTimeout(function() {
                expect(listener).toHaveBeenCalledWith($("#test")[0]);
                done();
            }, 400);
        });

        it("should throw on invalid parameters", function() {
            var erd = elementResizeDetectorMaker({
                callOnAdd: false,
                reporter: reporter,
                strategy: strategy
            });

            expect(erd.listenTo).toThrow();

            expect(_.partial(erd.listenTo, $("#test")[0])).toThrow();
        });

        describe("option.onReady", function() {
            it("should be called when installing a listener to an element", function(done) {
                var erd = elementResizeDetectorMaker({
                    callOnAdd: false,
                    reporter: reporter,
                    strategy: strategy
                });

                var listener = jasmine.createSpy("listener");

                erd.listenTo({
                    onReady: function() {
                        $("#test").width(200);
                        setTimeout(function() {
                            expect(listener).toHaveBeenCalledWith($("#test")[0]);
                            done();
                        }, 100);
                    }
                }, $("#test")[0], listener);
            });

            it("should be called when all elements are ready", function(done) {
                var erd = elementResizeDetectorMaker({
                    callOnAdd: false,
                    reporter: reporter,
                    strategy: strategy
                });

                var listener = jasmine.createSpy("listener");

                erd.listenTo({
                    onReady: function() {
                        $("#test").width(200);
                        $("#test2").width(300);
                        setTimeout(function() {
                            expect(listener).toHaveBeenCalledWith($("#test")[0]);
                            expect(listener).toHaveBeenCalledWith($("#test2")[0]);
                            done();
                        }, 100);
                    }
                }, $("#test, #test2"), listener);
            });

            it("should be able to handle listeners for the same element but different calls", function(done) {
                var erd = elementResizeDetectorMaker({
                    callOnAdd: false,
                    reporter: reporter,
                    strategy: strategy
                });

                var onReady1 = jasmine.createSpy("listener");
                var onReady2 = jasmine.createSpy("listener");

                erd.listenTo({
                    onReady: onReady1
                }, $("#test"), function noop() {});
                erd.listenTo({
                    onReady: onReady2
                }, $("#test"), function noop() {});

                setTimeout(function() {
                    expect(onReady1.calls.count()).toBe(1);
                    expect(onReady2.calls.count()).toBe(1);
                    done();
                }, 300);
            });

            it("should be able to handle when elements occur multiple times in the same call (and other calls)", function(done) {
                var erd = elementResizeDetectorMaker({
                    callOnAdd: false,
                    reporter: reporter,
                    strategy: strategy
                });

                var onReady1 = jasmine.createSpy("listener");
                var onReady2 = jasmine.createSpy("listener");

                erd.listenTo({
                    onReady: onReady1
                }, [$("#test")[0], $("#test")[0]], function noop() {});
                erd.listenTo({
                    onReady: onReady2
                }, $("#test"), function noop() {});

                setTimeout(function() {
                    expect(onReady1.calls.count()).toBe(1);
                    expect(onReady2.calls.count()).toBe(1);
                    done();
                }, 300);
            });
        });

        it("should be able to attach multiple listeners to an element", function(done) {
            var erd = elementResizeDetectorMaker({
                callOnAdd: false,
                reporter: reporter,
                strategy: strategy
            });

            var listener1 = jasmine.createSpy("listener1");
            var listener2 = jasmine.createSpy("listener2");

            erd.listenTo($("#test")[0], listener1);
            erd.listenTo($("#test")[0], listener2);

            setTimeout(function() {
                $("#test").width(300);
            }, 200);

            setTimeout(function() {
                expect(listener1).toHaveBeenCalledWith($("#test")[0]);
                expect(listener2).toHaveBeenCalledWith($("#test")[0]);
                done();
            }, 400);
        });

        it("should be able to attach a listener to an element multiple times within the same call", function(done) {
            var erd = elementResizeDetectorMaker({
                callOnAdd: false,
                reporter: reporter,
                strategy: strategy
            });

            var listener1 = jasmine.createSpy("listener1");

            erd.listenTo([$("#test")[0], $("#test")[0]], listener1);

            setTimeout(function() {
                $("#test").width(300);
            }, 200);

            setTimeout(function() {
                expect(listener1).toHaveBeenCalledWith($("#test")[0]);
                expect(listener1.calls.count()).toBe(2);
                done();
            }, 400);
        });

        it("should be able to attach listeners to multiple elements", function(done) {
            var erd = elementResizeDetectorMaker({
                callOnAdd: false,
                reporter: reporter,
                strategy: strategy
            });

            var listener1 = jasmine.createSpy("listener1");

            erd.listenTo($("#test, #test2"), listener1);

            setTimeout(function() {
                $("#test").width(200);
            }, 100);

            setTimeout(function() {
                expect(listener1).toHaveBeenCalledWith($("#test")[0]);
            }, 300);

            setTimeout(function() {
                $("#test2").width(500);
            }, 400);

            setTimeout(function() {
               expect(listener1).toHaveBeenCalledWith($("#test2")[0]);
               done();
            }, 800);
        });

        //Only run this test if the browser actually is able to get the computed style of an element.
        //Only IE8 is lacking the getComputedStyle method.
        if(window.getComputedStyle) {
            it("should keep the style of the element intact", function(done) {
                var erd = elementResizeDetectorMaker({
                    callOnAdd: false,
                    reporter: reporter,
                    strategy: strategy
                });

                var before = getStyle($("#test")[0]);
                erd.listenTo($("#test")[0], _.noop);
                var after = getStyle($("#test")[0]);
                ensureStyle(before, after, positonFromStaticToRelative);

                //Test styles async since making an element listenable is async.
                setTimeout(function() {
                    var afterAsync = getStyle($("#test")[0]);
                    ensureStyle(before, afterAsync, positonFromStaticToRelative);
                    done();
                }, 100);
            });
        }

        it("should call listener if the element is changed synchronously after listenTo", function(done) {
            var erd = elementResizeDetectorMaker({
                callOnAdd: true,
                reporter: reporter,
                strategy: strategy
            });

            var listener1 = jasmine.createSpy("listener1");
            erd.listenTo($("#test, #test2"), listener1);

            setTimeout(function() {
                expect(listener1).toHaveBeenCalledWith($("#test")[0]);
                expect(listener1).toHaveBeenCalledWith($("#test2")[0]);
                done();
            }, 100);
        });

        it("should use the option.idHandler if present", function(done) {
            var ID_ATTR = "some-fancy-id-attr";

            var idHandler = {
                get: function(element) {
                    if(element[ID_ATTR] === undefined) {
                        var id;

                        if($(element).attr("id") === "test") {
                            id = "test+1";
                        } else if($(element).attr("id") === "test2") {
                            id = "test2+2";
                        }

                        $(element).attr(ID_ATTR, id);
                    }

                    return $(element).attr(ID_ATTR);
                }
            };

            var erd = elementResizeDetectorMaker({
                idHandler: idHandler,
                callOnAdd: false,
                reporter: reporter,
                strategy: strategy
            });

            var listener1 = jasmine.createSpy("listener1");
            var listener2 = jasmine.createSpy("listener1");

            var attrsBeforeTest = getAttributes($("#test")[0]);
            var attrsBeforeTest2 = getAttributes($("#test2")[0]);

            erd.listenTo($("#test"), listener1);
            erd.listenTo($("#test, #test2"), listener2);

            var attrsAfterTest = getAttributes($("#test")[0]);
            var attrsAfterTest2 = getAttributes($("#test2")[0]);

            var ignoreValidIdAttrAndStyle = function(key) {
                return key === ID_ATTR || key === "style";
            };

            ensureAttributes(attrsBeforeTest, attrsAfterTest, ignoreValidIdAttrAndStyle);
            ensureAttributes(attrsBeforeTest2, attrsAfterTest2, ignoreValidIdAttrAndStyle);

            expect($("#test").attr(ID_ATTR)).toEqual("test+1");
            expect($("#test2").attr(ID_ATTR)).toEqual("test2+2");

            setTimeout(function() {
                $("#test").width(300);
                $("#test2").width(500);
            }, 100);

            setTimeout(function() {
               expect(listener1).toHaveBeenCalledWith($("#test")[0]);
               expect(listener2).toHaveBeenCalledWith($("#test")[0]);
               expect(listener2).toHaveBeenCalledWith($("#test2")[0]);
               done();
            }, 600);
        });
    });
}

describe("element-resize-detector", function() {
    beforeEach(function() {
        //This messed with tests in IE8.
        //TODO: Investigate why, because it would be nice to have instead of the current solution.
        //loadFixtures("element-resize-detector_fixture.html");
        $("#fixtures").html("

");
    });

    describe("elementResizeDetectorMaker", function() {
        it("should be globally defined", function() {
            expect(elementResizeDetectorMaker).toBeFunction();
        });

        it("should create an element-resize-detector instance", function() {
            var erd = elementResizeDetectorMaker();

            expect(erd).toBeObject();
            expect(erd).toHaveMethod("listenTo");
        });
    });

    describe("options.callOnAdd", function() {
        it("should be true default and call all functions when listenTo succeeds", function(done) {
            var erd = elementResizeDetectorMaker({
                reporter: reporter
            });

            var listener = jasmine.createSpy("listener");
            var listener2 = jasmine.createSpy("listener2");

            erd.listenTo($("#test")[0], listener);
            erd.listenTo($("#test")[0], listener2);

            setTimeout(function() {
                expect(listener).toHaveBeenCalledWith($("#test")[0]);
                expect(listener2).toHaveBeenCalledWith($("#test")[0]);
                listener.calls.reset();
                listener2.calls.reset();
                $("#test").width(300);
            }, 100);

            setTimeout(function() {
                expect(listener).toHaveBeenCalledWith($("#test")[0]);
                expect(listener2).toHaveBeenCalledWith($("#test")[0]);
                done();
            }, 400);
        });
    });

    describe("resizeDetector.removeListener", function() {
        it("should remove listener from element", function(done) {
            var erd = elementResizeDetectorMaker({
                callOnAdd: false
            });

            var $testElem = $("#test");

            var listenerCall    = jasmine.createSpy("listener");
            var listenerNotCall = jasmine.createSpy("listener");

            erd.listenTo($testElem[0], listenerCall);
            erd.listenTo($testElem[0], listenerNotCall);

            setTimeout(function() {
                erd.removeListener($testElem[0], listenerNotCall);
                $testElem.width(300);
            }, 100);

            setTimeout(function() {
                expect(listenerCall).toHaveBeenCalled();
                expect(listenerNotCall).not.toHaveBeenCalled();
                done();
            }, 400);
        });
    });

    describe("resizeDetector.removeAllListeners", function() {
        it("should remove all listeners from element", function(done) {
            var erd = elementResizeDetectorMaker({
                callOnAdd: false
            });

            var $testElem = $("#test");

            var listener1 = jasmine.createSpy("listener");
            var listener2 = jasmine.createSpy("listener");

            erd.listenTo($testElem[0], listener1);
            erd.listenTo($testElem[0], listener2);

            setTimeout(function() {
                erd.removeAllListeners($testElem[0]);
                $testElem.width(300);
            }, 100);

            setTimeout(function() {
                expect(listener1).not.toHaveBeenCalled();
                expect(listener2).not.toHaveBeenCalled();
                done();
            }, 400);
        });

        it("should work for elements that don't have the detector installed", function() {
            var erd = elementResizeDetectorMaker();
            var $testElem = $("#test");
            expect(erd.removeAllListeners.bind(erd, $testElem[0])).not.toThrow();
        });
    });

    describe("resizeDetector.uninstall - object strategy", function() {
        it("should completely remove detector from element", function(done) {
            var erd = elementResizeDetectorMaker({
                callOnAdd: false,
                strategy: "object"
            });

            var $testElem = $("#test");

            var listener = jasmine.createSpy("listener");

            erd.listenTo($testElem[0], listener);

            setTimeout(function() {
                erd.uninstall($testElem[0]);
                // detector element should be removed
                expect($testElem[0].childNodes.length).toBe(0);
                $testElem.width(300);
            }, 100);

            setTimeout(function() {
                expect(listener).not.toHaveBeenCalled();
                done();
            }, 400);
        });
    });

    describe("resizeDetector.uninstall - scroll strategy", function() {
        it("should completely remove detector from element", function(done) {
            var erd = elementResizeDetectorMaker({
                callOnAdd: false,
                strategy: "scroll"
            });

            var $testElem = $("#test");

            var listener = jasmine.createSpy("listener");

            erd.listenTo($testElem[0], listener);

            setTimeout(function() {
                erd.uninstall($testElem[0]);
                // detector element should be removed
                expect($testElem[0].childNodes.length).toBe(0);
                $testElem.width(300);
            }, 100);

            setTimeout(function() {
                expect(listener).not.toHaveBeenCalled();
                done();
            }, 400);
        });
    });

    listenToTest("object");

    //Scroll only supported on non-opera browsers.
    if(!window.opera) {
        listenToTest("scroll");
    }
});
<|endoftext|>


<commit_before>var assert = require('assert'),
    math = require('../../../index.js'),
    max = math.max;

describe('max', function() {

  it('should return the max between several numbers', function() {
    assert.equal(max(5), 5);
    assert.equal(max(3,1), 3);
    assert.equal(max(1,3), 3);
    assert.equal(max(1,3,5,2,-5), 5);
    assert.equal(max(0,0,0,0), 0);
  });

  it('should return the max string following lexical order', function() {
    assert.equal(max('A', 'C', 'D', 'B'), 'D');
  });

  it('should return the max element from a vector', function() {
    assert.equal(max(math.matrix([1,3,5,2,-5])), 5);
  });

  it('should return the max element from a 2d matrix', function() {
    assert.deepEqual(max([
      [ 1, 4,  7],
      [ 3, 0,  5],
      [-1, 11, 9]
    ]), 11);
    assert.deepEqual(max(math.matrix([
      [ 1, 4,  7],
      [ 3, 0,  5],
      [-1, 11, 9]
    ])), 11);
  });

  it('should return a reduced n-1 matrix from a n matrix', function() {
	  assert.deepEqual(max([
	   [ 1, 2, 3],
	   [ 4, 5, 6],
	   [ 7, 8, 9]], 1), [7, 8, 9]);
	  assert.deepEqual(max([
	   [ 1, 2, 3],
	   [ 4, 5, 6],
	   [ 7, 8, 9]], 2), [3, 6, 9]);
  });

  it('should throw an error when called with complex numbers', function() {
    assert.throws(function () {max(math.complex(2,3), math.complex(2,1))}, TypeError);
    assert.throws(function () {max(math.complex(2,3), math.complex(2,5))}, TypeError);

    assert.throws(function () {max(math.complex(3,4), 4)}, TypeError);
    assert.throws(function () {max(math.complex(3,4), 5)}, TypeError);
    assert.throws(function () {max(5, math.complex(3,4))}, TypeError);
    assert.throws(function () {max(math.complex(3,4), 6)}, TypeError);
  });

  it('should throw an error if called with invalid number of arguments', function() {
    assert.throws(function() {max()});
  });

  it('should return undefined if called with an empty array', function() {
    assert.throws(function() {max([])});
  });

});
<commit_msg>Added another multidimensional test for max<commit_after>var assert = require('assert'),
    math = require('../../../index.js'),
    max = math.max;

describe('max', function() {

  it('should return the max between several numbers', function() {
    assert.equal(max(5), 5);
    assert.equal(max(3,1), 3);
    assert.equal(max(1,3), 3);
    assert.equal(max(1,3,5,2,-5), 5);
    assert.equal(max(0,0,0,0), 0);
  });

  it('should return the max string following lexical order', function() {
    assert.equal(max('A', 'C', 'D', 'B'), 'D');
  });

  it('should return the max element from a vector', function() {
    assert.equal(max(math.matrix([1,3,5,2,-5])), 5);
  });

  it('should return the max element from a 2d matrix', function() {
    assert.deepEqual(max([
      [ 1, 4,  7],
      [ 3, 0,  5],
      [-1, 11, 9]
    ]), 11);
    assert.deepEqual(max(math.matrix([
      [ 1, 4,  7],
      [ 3, 0,  5],
      [-1, 11, 9]
    ])), 11);
  });

  it('should return a reduced n-1 matrix from a n matrix', function() {
	  assert.deepEqual(max([
	   [ 1, 2, 3],
	   [ 4, 5, 6],
	   [ 7, 8, 9]], 1), [7, 8, 9]);
	  assert.deepEqual(max([
	   [ 1, 2, 3],
	   [ 4, 5, 6],
	   [ 7, 8, 9]], 2), [3, 6, 9]);
	  assert.deepEqual(max([
       [ [1,2], [3,4], [5,6]],
       [ [6,7], [8,9], [10,11]]],3),
		  [[2, 4, 6], [7, 9, 11]]);
  });

  it('should throw an error when called with complex numbers', function() {
    assert.throws(function () {max(math.complex(2,3), math.complex(2,1))}, TypeError);
    assert.throws(function () {max(math.complex(2,3), math.complex(2,5))}, TypeError);

    assert.throws(function () {max(math.complex(3,4), 4)}, TypeError);
    assert.throws(function () {max(math.complex(3,4), 5)}, TypeError);
    assert.throws(function () {max(5, math.complex(3,4))}, TypeError);
    assert.throws(function () {max(math.complex(3,4), 6)}, TypeError);
  });

  it('should throw an error if called with invalid number of arguments', function() {
    assert.throws(function() {max()});
  });

  it('should return undefined if called with an empty array', function() {
    assert.throws(function() {max([])});
  });

});
<|endoftext|>


<commit_before>/**
 * Email Auto-complete Library written in pure Vanilla JS
 *
 * Author: Sugato Sengupta <thcdesigning@gmail.com>
 *
 * Special Thanks to Low Yong Zhen <yz@stargate.io> for the jQuery version of this library.

 License Type: MIT License

 Copyright (c) 2016 Sugato Sengupta

 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.

 **/
var EmailAutoComplete = function(elem, options) {

    var defaults = {
        suggClass: "eac-sugg",
        domains: ["yahoo.com", "hotmail.com", "gmail.com", "me.com", "aol.com", "mac.com", "live.com", "comcast.net", "googlemail.com", "msn.com", "hotmail.co.uk", "yahoo.co.uk", "facebook.com", "verizon.net", "sbcglobal.net", "att.net", "gmx.com", "outlook.com", "icloud.com"]
    }
    var $suggOverlay, $wrap, $cval, $field, $originalField, suggestion, fieldLeftOffset;
    var classSelectorRE = /^\.([\w-]+)$/;
    var idSelectorRE = /^#([\w-]+)$/;
    var tagSelectorRE = /^[\w-]+$/;
    var extend = function() {
        var extended = {};
        var deep = false;
        var i = 0;
        var length = arguments.length;
        if (Object.prototype.toString.call(arguments[0]) === '[object Boolean]') {
            deep = arguments[0];
            i++;
        }
        var merge = function(obj) {
            for (var prop in obj) {
                if (Object.prototype.hasOwnProperty.call(obj, prop)) {
                    if (deep && Object.prototype.toString.call(obj[prop]) === '[object Object]') {
                        extended[prop] = extend(true, extended[prop], obj[prop]);
                    } else {
                        extended[prop] = obj[prop];
                    }
                }
            }
        }
        for (; i < length; i++) {
            var obj = arguments[i];
            merge(obj);
        }
        return extended;
    }
    options = extend({}, defaults, options);
    var $$ = function(element, selector) {
        var found;
        return (element === document && idSelectorRE.test(selector)) ?
            ((found = element.getElementById(RegExp.$1)) ? [found] : []) :
            Array.prototype.slice.call(
                classSelectorRE.test(selector) ? element.getElementsByClassName(RegExp.$1) :
                tagSelectorRE.test(selector) ? element.getElementsByTagName(selector) :
                element.querySelectorAll(selector)
            );
    }

    function outerWidth(el) {
        var width = el.offsetWidth;
        var style = getComputedStyle(el);
        width += parseInt(style.marginLeft) + parseInt(style.marginRight);
        return width;
    }

    function hasClass(target, className) {
        return new RegExp('(\\s|^)' + className + '(\\s|$)').test(target.className);
    }

    function insertAfter(referenceNode, newNode) {
        referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
    }

    function wrap(toWrap, wrapper) {
        wrapper = wrapper || document.createElement('div');
        if (toWrap.nextSibling) {
            toWrap.parentNode.insertBefore(wrapper, toWrap.nextSibling);
        } else {
            toWrap.parentNode.appendChild(wrapper);
        }
        return wrapper.appendChild(toWrap);
    }

    if (!Array.prototype.indexOf) {
        Array.prototype.indexOf = function(obj, start) {
            for (var i = (start || 0), j = this.length; i < j; i++) {
                if (this[i] === obj) {
                    return i;
                }
            }
            return -1;
        }
    }

    function addEvent(elem, event, fn) {
        function listenHandler(e) {
            var ret = fn.apply(this, arguments);
            if (ret === false) {
                e.stopPropagation();
                e.preventDefault();
            }
            return (ret);
        }

        function attachHandler() {
            var ret = fn.call(elem, window.event);
            if (ret === false) {
                window.event.returnValue = false;
                window.event.cancelBubble = true;
            }
            return (ret);
        }
        if (elem.addEventListener) {
            elem.addEventListener(event, listenHandler, false);
            return {
                elem: elem,
                handler: listenHandler,
                event: event
            };
        } else {
            elem.attachEvent("on" + event, attachHandler);
            return {
                elem: elem,
                handler: attachHandler,
                event: event
            };
        }
    }

    function removeEvent(token) {
        if (token.elem.removeEventListener) {
            token.elem.removeEventListener(token.event, token.handler);
        } else {
            token.elem.detachEvent("on" + token.event, token.handler);
        }
    }

    function autocomplete() {
        if ("undefined" == typeof suggestion || suggestion.length < 1) {
            return false;
        } else {
            $field.value = val + suggestion;
        }
        $cval.innerHTML = "";
        $suggOverlay.innerHTML = "";
        return true;
    }

    function getDisplayType(element) {
        var cStyle = element.currentStyle || window.getComputedStyle(element, "");
        return cStyle.display;
    }

    function displaySuggestion(e) {
        val = $field.value;
        suggestion = suggest(val);
        suggestion.length ? e.preventDefault() : $suggOverlay.innerHTML = "";
        $suggOverlay.innerHTML = suggestion;
        $cval.innerHTML = val;
        null === fieldLeftOffset && (fieldLeftOffset = (outerWidth($field) - $field.offsetWidth) / 2);
        var b = $cval.offsetWidth;
        outerWidth($field) > b && ($suggOverlay.style.left = fieldLeftOffset + b + "px");
    }

    function suggest(a) {
        var b = a.split("@");
        if (!(b.length > 1))
            return "";
        if (a = b.pop(), !a.length)
            return "";
        var c = options.domains.filter(function(b) {
            return 0 === b.indexOf(a)
        }).shift() || "";
        return c.replace(a, "")
    }

    function outerHght(elem) {
        var curStyle = elem.currentStyle || window.getComputedStyle(elem);
        OH = elem.offsetHeight;
        if (!isNaN(parseInt(curStyle.marginTop))) {
            OH += parseInt(curStyle.marginTop);
        }
        if (!isNaN(parseInt(curStyle.marginBottom))) {
            OH += parseInt(curStyle.marginBottom)
        }
        return OH;
    }

    function getHTML(node) {
        if (!node || !node.tagName) return '';
        if (node.outerHTML) return node.outerHTML;
        var wrapper = document.createElement('div');
        wrapper.appendChild(node.cloneNode(true));
        return wrapper.innerHTML;
    }

    function init() {

        fieldLeftOffset = null;
        var val = null;
        suggestion = null;
        $field = elem;
        if ($field.length < 1) {
            console.log('field cannot be blank.');
            return false;
        }
        if (!hasClass($field.parentNode, 'eac-input-wrap')) {
            $wrap = document.createElement("div");
            document.body.setAttribute("tabindex", 1);
            $wrap.classList.add('eac-input-wrap');
            $wrap.style.display = getDisplayType($field);
            $wrap.style.position = getComputedStyle($field).position === 'static' ? 'relative' : getComputedStyle($field).position;
            $wrap.style.fontSize = getComputedStyle($field).fontSize;
            $wrap.style.width = getComputedStyle($field).width;
            $wrap.style.height = getComputedStyle($field).height;
            wrap($field, $wrap);
        } else {
            $wrap = $field.parentNode;
            $field.parentNode;
            var $c = $$($wrap, '.eac-cval');
            if ($c.length) {
                $c[0].parentNode.removeChild($c[0]);
            }
            var $s = $$($wrap, '.' + options.suggClass);
            if ($s.length) {
                $s[0].parentNode.removeChild($s[0]);
            }
        }
        $cval = document.createElement("span");
        $cval.classList.add('eac-cval');
        $field.style.position = "absolute";
        $field.style.left = "0px";
        $cval.style.visibility = 'hidden';
        $cval.style.position = 'absolute';
        $cval.style.display = 'inline-block';
        $cval.style.fontFamily = getComputedStyle($field).fontFamily;
        $cval.style.fontWeight = getComputedStyle($field).fontWeight;
        $cval.style.letterSpacing = getComputedStyle($field).letterSpacing;
        insertAfter($field, $cval);
        $cval.style.fontSize = getComputedStyle($field).fontSize;
        $cval.style.paddingLeft = getComputedStyle($field).paddingLeft;
        var c = (parseFloat(outerHght($field)) - parseFloat(getComputedStyle($field).height)) / 2;
        if (isNaN(c)) {
            return false;
        }
        $suggOverlay = document.createElement("span");
        var slh = parseFloat(getComputedStyle($wrap).height);
        $suggOverlay.classList.add(options.suggClass);
        $suggOverlay.style.display = 'block';
        $suggOverlay.style.boxSizing = 'content-box';
        $suggOverlay.style.lineHeight = slh + "px";
        $suggOverlay.style.fontFamily = getComputedStyle($field).fontFamily;
        $suggOverlay.style.fontWeight = getComputedStyle($field).fontWeight;
        $suggOverlay.style.letterSpacing = getComputedStyle($field).letterSpacing;
        $suggOverlay.style.position = 'absolute';
        $suggOverlay.style.opacity = '0.4';
        $suggOverlay.style.top = 0;
        $suggOverlay.style.left = 0;
        $suggOverlay.style.zIndex = 99999;
        insertAfter($field, $suggOverlay);

        addEvent($field, 'keyup', function(e) {
            displaySuggestion(e);
        });
        addEvent($field, 'keydown', function(a) {
            if (39 === a.which || 9 === a.which) {
                a.preventDefault();
                autocomplete($field);
            }
        });
        addEvent(document, 'click', function(a) {
            if (a.target.className === options.suggClass) {
                $field.value += $suggOverlay.innerHTML;
                $suggOverlay.innerHTML = "";
            }
        });
        addEvent($field, 'blur', function(a) {
            setTimeout(function() {
                $suggOverlay.innerHTML = "";
            }, 150);
        });
    }
    init();
}<commit_msg>don't know<commit_after>/**
 * Email Auto-complete Library written in pure Vanilla JS
 *
 * Author: Sugato Sengupta <thcdesigning@gmail.com>
 *
 * Special Thanks to Low Yong Zhen <yz@stargate.io> for the jQuery version of this library.

 License Type: MIT License

 Copyright (c) 2016 Sugato Sengupta

 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.

 **/
var EmailAutoComplete = function(elem, options) {

    var defaults = {
        suggClass: "eac-sugg",
        domains: ["yahoo.com", "hotmail.com", "gmail.com", "me.com", "aol.com", "mac.com", "live.com", "comcast.net", "googlemail.com", "msn.com", "hotmail.co.uk", "yahoo.co.uk", "facebook.com", "verizon.net", "sbcglobal.net", "att.net", "gmx.com", "outlook.com", "icloud.com"]
    }
    var $suggOverlay, $wrap, $cval, $field, $originalField, suggestion, fieldLeftOffset;
    var classSelectorRE = /^\.([\w-]+)$/;
    var idSelectorRE = /^#([\w-]+)$/;
    var tagSelectorRE = /^[\w-]+$/;
    var extend = function() {
        var extended = {};
        var deep = false;
        var i = 0;
        var length = arguments.length;
        if (Object.prototype.toString.call(arguments[0]) === '[object Boolean]') {
            deep = arguments[0];
            i++;
        }
        var merge = function(obj) {
            for (var prop in obj) {
                if (Object.prototype.hasOwnProperty.call(obj, prop)) {
                    if (deep && Object.prototype.toString.call(obj[prop]) === '[object Object]') {
                        extended[prop] = extend(true, extended[prop], obj[prop]);
                    } else {
                        extended[prop] = obj[prop];
                    }
                }
            }
        }
        for (; i < length; i++) {
            var obj = arguments[i];
            merge(obj);
        }
        return extended;
    }
    options = extend({}, defaults, options);
    var $$ = function(element, selector) {
        var found;
        return (element === document && idSelectorRE.test(selector)) ?
            ((found = element.getElementById(RegExp.$1)) ? [found] : []) :
            Array.prototype.slice.call(
                classSelectorRE.test(selector) ? element.getElementsByClassName(RegExp.$1) :
                tagSelectorRE.test(selector) ? element.getElementsByTagName(selector) :
                element.querySelectorAll(selector)
            );
    }

    function outerWidth(el) {
        var width = el.offsetWidth;
        var style = getComputedStyle(el);
        width += parseInt(style.marginLeft) + parseInt(style.marginRight);
        return width;
    }

    function hasClass(target, className) {
        return new RegExp('(\\s|^)' + className + '(\\s|$)').test(target.className);
    }

    function insertAfter(referenceNode, newNode) {
        referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
    }

    function wrap(toWrap, wrapper) {
        wrapper = wrapper || document.createElement('div');
        if (toWrap.nextSibling) {
            toWrap.parentNode.insertBefore(wrapper, toWrap.nextSibling);
        } else {
            toWrap.parentNode.appendChild(wrapper);
        }
        return wrapper.appendChild(toWrap);
    }

    if (!Array.prototype.indexOf) {
        Array.prototype.indexOf = function(obj, start) {
            for (var i = (start || 0), j = this.length; i < j; i++) {
                if (this[i] === obj) {
                    return i;
                }
            }
            return -1;
        }
    }

    function addEvent(elem, event, fn) {
        function listenHandler(e) {
            var ret = fn.apply(this, arguments);
            if (ret === false) {
                e.stopPropagation();
                e.preventDefault();
            }
            return (ret);
        }

        function attachHandler() {
            var ret = fn.call(elem, window.event);
            if (ret === false) {
                window.event.returnValue = false;
                window.event.cancelBubble = true;
            }
            return (ret);
        }
        if (elem.addEventListener) {
            elem.addEventListener(event, listenHandler, false);
            return {
                elem: elem,
                handler: listenHandler,
                event: event
            };
        } else {
            elem.attachEvent("on" + event, attachHandler);
            return {
                elem: elem,
                handler: attachHandler,
                event: event
            };
        }
    }

    function removeEvent(token) {
        if (token.elem.removeEventListener) {
            token.elem.removeEventListener(token.event, token.handler);
        } else {
            token.elem.detachEvent("on" + token.event, token.handler);
        }
    }

    function autocomplete() {
        if ("undefined" == typeof suggestion || suggestion.length < 1) {
            return false;
        } else {
            $field.value = val + suggestion;
        }
        $cval.innerHTML = "";
        $suggOverlay.innerHTML = "";
        return true;
    }

    function getDisplayType(element) {
        var cStyle = element.currentStyle || window.getComputedStyle(element, "");
        return cStyle.display;
    }

    function displaySuggestion(e) {
        val = $field.value;
        suggestion = suggest(val);
        suggestion.length ? e.preventDefault() : $suggOverlay.innerHTML = "";
        $suggOverlay.innerHTML = suggestion;
        $cval.innerHTML = val;
        null === fieldLeftOffset && (fieldLeftOffset = (outerWidth($field) - $field.offsetWidth) / 2);
        var b = $cval.offsetWidth;
        outerWidth($field) > b && ($suggOverlay.style.left = fieldLeftOffset + b + "px");
    }

    function suggest(a) {
        var b = a.split("@");
        if (!(b.length > 1))
            return "";
        if (a = b.pop(), !a.length)
            return "";
        var c = options.domains.filter(function(b) {
            return 0 === b.indexOf(a)
        }).shift() || "";
        return c.replace(a, "")
    }

    function outerHght(elem) {
        var curStyle = elem.currentStyle || window.getComputedStyle(elem);
        OH = elem.offsetHeight;
        if (!isNaN(parseInt(curStyle.marginTop))) {
            OH += parseInt(curStyle.marginTop);
        }
        if (!isNaN(parseInt(curStyle.marginBottom))) {
            OH += parseInt(curStyle.marginBottom)
        }
        return OH;
    }

    function getHTML(node) {
        if (!node || !node.tagName) return '';
        if (node.outerHTML) return node.outerHTML;
        var wrapper = document.createElement('div');
        wrapper.appendChild(node.cloneNode(true));
        return wrapper.innerHTML;
    }

    function init() {

        fieldLeftOffset = null;
        var val = null;
        suggestion = null;
        $field = elem;
        if ($field.length < 1) {
            console.log('field cannot be blank.');
            return false;
        }
        if (!hasClass($field.parentNode, 'eac-input-wrap')) {
            $wrap = document.createElement("div");
            document.body.setAttribute("tabindex", 1);
            $wrap.classList.add('eac-input-wrap');
            $wrap.style.display = getDisplayType($field);
            $wrap.style.position = getComputedStyle($field).position === 'static' ? 'relative' : getComputedStyle($field).position;
            $wrap.style.fontSize = getComputedStyle($field).fontSize;
            $wrap.style.width = getComputedStyle($field).width;
            $wrap.style.height = getComputedStyle($field).height;
            wrap($field, $wrap);
        } else {
            $wrap = $field.parentNode;
            $field.parentNode;
            var $c = $$($wrap, '.eac-cval');
            if ($c.length) {
                $c[0].parentNode.removeChild($c[0]);
            }
            var $s = $$($wrap, '.' + options.suggClass);
            if ($s.length) {
                $s[0].parentNode.removeChild($s[0]);
            }
        }
        $cval = document.createElement("span");
        $cval.classList.add('eac-cval');
        $field.style.position = "absolute";
        $field.style.left = "0px";
        $cval.style.visibility = 'hidden';
        $cval.style.position = 'absolute';
        $cval.style.display = 'inline-block';
        $cval.style.fontFamily = getComputedStyle($field).fontFamily;
        $cval.style.fontWeight = getComputedStyle($field).fontWeight;
        $cval.style.letterSpacing = getComputedStyle($field).letterSpacing;
        insertAfter($field, $cval);
        $cval.style.fontSize = getComputedStyle($field).fontSize;
        $cval.style.paddingLeft = getComputedStyle($field).paddingLeft;
		$cval.style.lineHeight = getComputedStyle($field).lineHeight;
        var c = (parseFloat(outerHght($field)) - parseFloat(getComputedStyle($field).height)) / 2;
        if (isNaN(c)) {
            return false;
        }
        $suggOverlay = document.createElement("span");
        var slh = parseFloat(getComputedStyle($wrap).height);
        $suggOverlay.classList.add(options.suggClass);
        $suggOverlay.style.display = 'block';
        $suggOverlay.style.boxSizing = 'content-box';
        $suggOverlay.style.lineHeight = slh + "px";
        $suggOverlay.style.fontFamily = getComputedStyle($field).fontFamily;
        $suggOverlay.style.fontWeight = getComputedStyle($field).fontWeight;
        $suggOverlay.style.letterSpacing = getComputedStyle($field).letterSpacing;
        $suggOverlay.style.position = 'absolute';
        $suggOverlay.style.opacity = '0.4';
        $suggOverlay.style.top = 0;
        $suggOverlay.style.left = 0;
        $suggOverlay.style.zIndex = 99999;
        insertAfter($field, $suggOverlay);

        addEvent($field, 'keyup', function(e) {
            displaySuggestion(e);
        });
        addEvent($field, 'keydown', function(a) {
            if (39 === a.which || 9 === a.which) {
                a.preventDefault();
                autocomplete($field);
            }
        });
        addEvent(document, 'click', function(a) {
            if (a.target.className === options.suggClass) {
                $field.value += $suggOverlay.innerHTML;
                $suggOverlay.innerHTML = "";
            }
        });
        addEvent($field, 'blur', function(a) {
            setTimeout(function() {
                $suggOverlay.innerHTML = "";
            }, 150);
        });
    }
    init();
}<|endoftext|>


<commit_before>/**!
 * MIT License
 * Copyright (c) 2015 Thomas Scheinecker
 *
 * 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.
 */
/**
 * @ngdoc directive
 * @name tsSelect2
 * @element select
 *
 * @param {expression} [tsSelect2] the config object used for initializing select2
 *
 * @description
 * A wrapper directive for select2 version 4
 */
(function(angular) {
    'use strict';

    var TS_SELECT2_DEFAULTS = {
        // copy all classes on the original element upon initialization
        containerCssClass: function(element) {
            return element.attr('class');
        }
    };

    function tsSelect2Directive($http, $q, $animate) {
        var PRISTINE_CLASS = 'ng-pristine';
        var DIRTY_CLASS = 'ng-dirty';
        var TOUCHED_CLASS = 'ng-touched';
        var UNTOUCHED_CLASS = 'ng-untouched';
        var VALID_CLASS = 'ng-valid';
        var INVALID_CLASS = 'ng-invalid';

        // we override the default transport function to use the $http service instead of $.ajax
        function tsSelect2DirectiveTransport(params, success, failure) {
            var timeout = $q.defer();

            angular.extend({
                timeout: timeout
            }, params);
            $http(params).then(function(response) {
                success(response.data);
            }, failure);

            return {
                abort: timeout.resolve
            };
        }

        function tsSelect2DirectiveLink(scope, element, attrs, ngModelCtrl) {
            element = $(element[0]);

            var select2Options = {};
            angular.merge(select2Options, TS_SELECT2_DEFAULTS);
            angular.merge(select2Options, scope.$eval(attrs.tsSelect2));

            element.hide();

            /**
             * Adds a seleted option element for the given value to the dom
             * @param {*} value the value used for rendering the option element
             */
            // todo use format functions / sub directives
            function appendOption(value) {
                if (angular.isObject(value)) {
                    element.append('<option value="' + value.id + '" selected>' + value.text + '</option>');
                } else {
                    element.append('<option value="' + value + '" selected>' + value + '</option>');
                }
            }

            function initInitialSelectionOptions() {
                element.empty();

                var modelValue = angular.copy(ngModelCtrl.$modelValue);

                if (typeof modelValue === 'undefined' || modelValue === null) {
                    return;
                }

                if (!angular.isArray(modelValue)) {
                    modelValue = [modelValue];
                } else if (modelValue.length === 0) {
                    return;
                }

                angular.forEach(modelValue, appendOption);
            }

            /**
             * Links into the ngModel controller and correctly sets the classes to the provided element
             * @param element the dom element to apply the css class changes to
             */
            function propagateCssStateChanges(element) {
                function handleChange(property, addClass, removeClass) {
                    var original = ngModelCtrl[property];
                    ngModelCtrl[property] = function() {
                        original();
                        $animate.setClass(element, addClass, removeClass);
                    };
                }

                handleChange('$setDirty', DIRTY_CLASS, PRISTINE_CLASS);
                handleChange('$setPristine', PRISTINE_CLASS, DIRTY_CLASS);

                handleChange('$setValid', VALID_CLASS, INVALID_CLASS);
                handleChange('$setInvalid', INVALID_CLASS, VALID_CLASS);

                handleChange('$setTouched', TOUCHED_CLASS, UNTOUCHED_CLASS);
                handleChange('$setUntouched', UNTOUCHED_CLASS, TOUCHED_CLASS);
            }

            /**
             * Provides the input element used for filtering the select2 options
             * @returns {*} the dom element used by select2 for allowing user input
             */
            function getInput() {
                return element.data().select2.dropdown.$search || element.data().select2.selection.$search;
            }

            function initSelect2() {
                if ( !! select2Options.ajax && !select2Options.ajax.transport) {
                    select2Options.ajax.transport = tsSelect2DirectiveTransport;
                }

                attrs.textField = scope.$eval(attrs.textField);
                attrs.textFn = scope.$eval(attrs.textFn);

                if (!attrs.ngOptions && !! select2Options.ajax) {

                    initInitialSelectionOptions();

                    ngModelCtrl.$parsers.push(function(id) {
                        var dat = element.select2('data');

                        if (angular.isArray(id)) {
                            var values = [];
                            var idx = 0;
                            angular.forEach(id, function(singleId) {
                                values.push(
                                    singleId && {
                                        id: singleId,
                                        text: attrs.textFn ? attrs.textFn(dat[idx]) : dat[idx][attrs.textField || 'text']
                                    });
                                idx++;
                            });
                            return values;
                        }

                        return id && {
                            id: id,
                            text: attrs.textFn ? attrs.textFn(dat[0]) : dat[0][attrs.textField || 'text']
                        };
                    });

                    ngModelCtrl.$parsers.push(function(id) {
                        return id === null ? undefined : id;
                    });
                }

                element.select2(select2Options);
                var selection = element.data().select2.$selection;
                propagateCssStateChanges(selection);

                element.on('select2:select', function(e) {
                    ngModelCtrl.$setViewValue(element.val().replace(/^string\:/g, ''), e);
                });

                getInput().on('blur.select2', function() {
                    element.trigger('blur');
                });
            }

            // defer initialization to execute after select and ngOptions initialization
            // we don't need to use $window as this is a visual change only - nothing changes in regard to angular
            // we could also just trigger a 'change' event on the element but this would result in
            // the control getting marked as dirty by angular
            // TODO - is there a better way to defer until the ngModel / ngOptions is initialized?
            window.setTimeout(initSelect2);

            // destroy the select2 on scope destruction
            scope.$on('$destroy', function() {
                getInput().off('.select2');
                element.select2('destroy');
            });
        }

        return {
            restrict: 'A',
            require: '^ngModel',
            priority: 1,
            link: tsSelect2DirectiveLink
        };
    }

    angular.module('tsSelect2', [])
        .constant('TS_SELECT2_DEFAULTS', TS_SELECT2_DEFAULTS)
        .directive('tsSelect2', ['$http', '$q', '$animate', tsSelect2Directive]);

})(angular);
<commit_msg>Fixing my hack so it works with non-strings.<commit_after>/**!
 * MIT License
 * Copyright (c) 2015 Thomas Scheinecker
 *
 * 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.
 */
/**
 * @ngdoc directive
 * @name tsSelect2
 * @element select
 *
 * @param {expression} [tsSelect2] the config object used for initializing select2
 *
 * @description
 * A wrapper directive for select2 version 4
 */
(function(angular) {
    'use strict';

    var TS_SELECT2_DEFAULTS = {
        // copy all classes on the original element upon initialization
        containerCssClass: function(element) {
            return element.attr('class');
        }
    };

    function tsSelect2Directive($http, $q, $animate) {
        var PRISTINE_CLASS = 'ng-pristine';
        var DIRTY_CLASS = 'ng-dirty';
        var TOUCHED_CLASS = 'ng-touched';
        var UNTOUCHED_CLASS = 'ng-untouched';
        var VALID_CLASS = 'ng-valid';
        var INVALID_CLASS = 'ng-invalid';

        // we override the default transport function to use the $http service instead of $.ajax
        function tsSelect2DirectiveTransport(params, success, failure) {
            var timeout = $q.defer();

            angular.extend({
                timeout: timeout
            }, params);
            $http(params).then(function(response) {
                success(response.data);
            }, failure);

            return {
                abort: timeout.resolve
            };
        }

        function tsSelect2DirectiveLink(scope, element, attrs, ngModelCtrl) {
            element = $(element[0]);

            var select2Options = {};
            angular.merge(select2Options, TS_SELECT2_DEFAULTS);
            angular.merge(select2Options, scope.$eval(attrs.tsSelect2));

            element.hide();

            /**
             * Adds a seleted option element for the given value to the dom
             * @param {*} value the value used for rendering the option element
             */
            // todo use format functions / sub directives
            function appendOption(value) {
                if (angular.isObject(value)) {
                    element.append('<option value="' + value.id + '" selected>' + value.text + '</option>');
                } else {
                    element.append('<option value="' + value + '" selected>' + value + '</option>');
                }
            }

            function initInitialSelectionOptions() {
                element.empty();

                var modelValue = angular.copy(ngModelCtrl.$modelValue);

                if (typeof modelValue === 'undefined' || modelValue === null) {
                    return;
                }

                if (!angular.isArray(modelValue)) {
                    modelValue = [modelValue];
                } else if (modelValue.length === 0) {
                    return;
                }

                angular.forEach(modelValue, appendOption);
            }

            /**
             * Links into the ngModel controller and correctly sets the classes to the provided element
             * @param element the dom element to apply the css class changes to
             */
            function propagateCssStateChanges(element) {
                function handleChange(property, addClass, removeClass) {
                    var original = ngModelCtrl[property];
                    ngModelCtrl[property] = function() {
                        original();
                        $animate.setClass(element, addClass, removeClass);
                    };
                }

                handleChange('$setDirty', DIRTY_CLASS, PRISTINE_CLASS);
                handleChange('$setPristine', PRISTINE_CLASS, DIRTY_CLASS);

                handleChange('$setValid', VALID_CLASS, INVALID_CLASS);
                handleChange('$setInvalid', INVALID_CLASS, VALID_CLASS);

                handleChange('$setTouched', TOUCHED_CLASS, UNTOUCHED_CLASS);
                handleChange('$setUntouched', UNTOUCHED_CLASS, TOUCHED_CLASS);
            }

            /**
             * Provides the input element used for filtering the select2 options
             * @returns {*} the dom element used by select2 for allowing user input
             */
            function getInput() {
                return element.data().select2.dropdown.$search || element.data().select2.selection.$search;
            }

            function initSelect2() {
                if ( !! select2Options.ajax && !select2Options.ajax.transport) {
                    select2Options.ajax.transport = tsSelect2DirectiveTransport;
                }

                attrs.textField = scope.$eval(attrs.textField);
                attrs.textFn = scope.$eval(attrs.textFn);

                if (!attrs.ngOptions && !! select2Options.ajax) {

                    initInitialSelectionOptions();

                    ngModelCtrl.$parsers.push(function(id) {
                        var dat = element.select2('data');

                        if (angular.isArray(id)) {
                            var values = [];
                            var idx = 0;
                            angular.forEach(id, function(singleId) {
                                values.push(
                                    singleId && {
                                        id: singleId,
                                        text: attrs.textFn ? attrs.textFn(dat[idx]) : dat[idx][attrs.textField || 'text']
                                    });
                                idx++;
                            });
                            return values;
                        }

                        return id && {
                            id: id,
                            text: attrs.textFn ? attrs.textFn(dat[0]) : dat[0][attrs.textField || 'text']
                        };
                    });

                    ngModelCtrl.$parsers.push(function(id) {
                        return id === null ? undefined : id;
                    });
                }

                element.select2(select2Options);
                var selection = element.data().select2.$selection;
                propagateCssStateChanges(selection);

                element.on('select2:select', function(e) {
                    var val = element.val();
                    if (typeof val === 'string') {
                        val = val.replace(/^string\:/g, '');
                    }
                    ngModelCtrl.$setViewValue(val, e);
                });

                getInput().on('blur.select2', function() {
                    element.trigger('blur');
                });
            }

            // defer initialization to execute after select and ngOptions initialization
            // we don't need to use $window as this is a visual change only - nothing changes in regard to angular
            // we could also just trigger a 'change' event on the element but this would result in
            // the control getting marked as dirty by angular
            // TODO - is there a better way to defer until the ngModel / ngOptions is initialized?
            window.setTimeout(initSelect2);

            // destroy the select2 on scope destruction
            scope.$on('$destroy', function() {
                getInput().off('.select2');
                element.select2('destroy');
            });
        }

        return {
            restrict: 'A',
            require: '^ngModel',
            priority: 1,
            link: tsSelect2DirectiveLink
        };
    }

    angular.module('tsSelect2', [])
        .constant('TS_SELECT2_DEFAULTS', TS_SELECT2_DEFAULTS)
        .directive('tsSelect2', ['$http', '$q', '$animate', tsSelect2Directive]);

})(angular);
<|endoftext|>


<commit_before>var webpack = require('webpack');
var path = require('path');

module.exports = {
  entry : {
    index : path.join(__dirname, './js/index')
  },
  output : {
    path : path.join(__dirname, '../js'),
    filename : '[name].js'
  },
  module : {
    loaders : [ {
      test : /\.js$/,
      exclude : /node_modules/,
      loader : 'babel-loader'
    }, {
      test : /\.css$/,
      loader : 'style-loader!css-loader'
    }, {
      test : /\.(png|woff|woff2|eot|ttf|svg)$/,
      loader : 'url-loader?limit=1000000'
    } ]
  },
  plugins : [
    new webpack.ProvidePlugin({
      React: 'react',
      ReactDOM: 'react-dom'
    }),
    new webpack.DefinePlugin({
      'process.env': {
        'NODE_ENV': '"production"'
      }
    }),
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        warnings: false
      }
    })
  ]
};
<commit_msg>refactor: code<commit_after>var webpack = require('webpack');
var path = require('path');

module.exports = {
  entry : {
    index : path.join(__dirname, './js/index')
  },
  output : {
    path : path.join(__dirname, '../js'),
    filename : '[name].js'
  },
  module : {
    loaders : [ {
      test : /\.js$/,
      exclude : /node_modules/,
      loader : 'babel-loader'
    }, {
      test : /\.css$/,
      loader : 'style-loader!css-loader'
    }, {
      test : /\.(png|woff|woff2|eot|ttf|svg)$/,
      loader : 'url-loader?limit=1000000'
    } ]
  },
  plugins : [
    new webpack.DefinePlugin({
      'process.env': {
        'NODE_ENV': '"production"'
      }
    }),
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        warnings: false
      }
    })
  ]
};
<|endoftext|>