<commit_before>var expect = chai.expect,
    should = chai.should(),
    debug = interact.debug();

function blank () {}

function mockEvent (options, target, currentTarget) {
    'use strict';

    options.target = options.target || target;
    options.currentTarget = options.currentTarget || currentTarget;

    return {
        target: options.target,
        currentTarget: options.current || options.target,
        type: options.type,
        pageX: options.x,
        pageY: options.y,
        clientX: options.x - (options.scrollX|0),
        clientY: options.y - (options.scrollY|0),
        touches: options.touches && options.touches.map(mockEvent),
        changedTouches: options.changed && options.changed.map(mockEvent),

        preventDefault: blank,
        stopPropagation: blank,
        stopImmediatePropagation: blank
    };
}

describe('interact', function () {
    'use strict';

    describe('when called as a function', function () {
        var validSelector = 'svg .draggable, body button';

        it('should return an Interactable when given an Element', function () {
            var bod = interact(document.body);

            expect(bod).to.be.an.instanceof(debug.Interactable);

            bod.element().should.equal(document.body);
        });

        it('should return an Interactable when given a valid CSS selector string', function () {
            var interactable = interact(validSelector);

            expect(interactable).to.be.an.instanceof(debug.Interactable);
        });

        it('should throw an error when given a string that is not a valid selector', function () {
            var error = 0;

            try {
                interact('<< invalid selector >>');
            }
            catch (e) {
                error = e;
            }

            error.should.be.instanceof(DOMException);
        });

        it('should return the same value from a given parameter unless returned Interactable is unset', function () { var iBody = interact(document.body),
                iSelector = interact(validSelector);

            interact(document.body).should.equal(iBody);
            interact(validSelector).should.equal(iSelector);

            iBody.unset();
            iSelector.unset();

            interact(document.body).should.not.equal(iBody);
            interact(validSelector).should.not.equal(iSelector);
        });
    });

    describe('gobal options', function () {

    });
});

describe('Interactable', function () {
    'use strict';

    var defaults = debug.defaultOptions,
        iable = interact(document.createElement('div')),
        simpleOptions = {
            draggable    : 'draggable',
            dropzone     : 'dropzone',
            resizable   : 'resizable',
            squareResize : 'squareResize',
            gesturable  : 'gesturable',
            styleCursor  : 'styleCursor',
            restrict     : 'restrictions',
            origin       : 'origin',
            deltaSource  : 'deltaSource'
        },
        enableOptions = [
            'snap',
            'autoScroll'
        ],
        checkerOptions = {
            actionChecker: 'getAction',
            rectChecker  : 'getRect'
        };

    describe('options', function () {
        it('should return the default setting if they were never previously set', function () {
            var option, i;

            for (option in simpleOptions) {
                iable[option]().should.equal(defaults[simpleOptions[option]]);
            }

            for (i = 0; option = enableOptions[i], i < enableOptions.length; i++) {
                if (iable[option]()) {
                    iable[option]().should.equal(defaults[option]);
                }
                else {
                    iable[option]().should.equal(defaults[option + 'Enabled']);
                }
            }

            for (option in checkerOptions) {
                iable[option]().should.equal(debug.Interactable.prototype[checkerOptions[option]]);
            }
        });
    });

    describe('#element', function () {
        it('should return the element if this is not a selector Interactable', function () {
            var p = document.createElement('p');

            interact(p).element().should.equal(p);
        });
    });

    describe('#actionChecker', function () {
        var div = document.createElement('div'),
            iDiv = interact(div)
                .draggable(true)
                .resizable(true)
                .gesturable(true),
            i,
            action,
            actions = ['drag', 'resizexy', 'resizex', 'resizey', 'gesture'],
            returnActionI = function alwaysResize () { return actions[i]; };

        it('should set set the function used to determine actions on pointer down events', function () {
            iDiv.actionChecker(returnActionI);
            iDiv.getAction.should.equal(returnActionI);

            for (i = 0; action = actions[i], i < actions.length; i++) {
                debug.pointerDown.call(div, mockEvent({
                    target: div,
                    x: -123,
                    y: 209
                }));

                interact.debug().prepared.should.equal(action);

                interact.stop();
            }
        });
    });
});

describe('Events', function () {
    'use strict';

    interact.stop();

    var dragElement = document.createElement('div'),
        draggable = interact(dragElement),
        events = [],
        mockEvents = data.downMove2Up.map(function (e) {
            return mockEvent(e, dragElement);
        }),
        pushEvent = function (event) {
            events.push(event);
        };

    describe('drag sequence', function () {
        draggable.draggable({
                onstart: pushEvent,
                onmove: pushEvent,
                onend: pushEvent
        }).actionChecker(function () {
            return 'drag';
        });

        debug.pointerDown(mockEvents[0]);
        debug.pointerMove(mockEvents[1]);
        debug.pointerMove(mockEvents[2]);
        debug.pointerUp  (mockEvents[3]);

        it('should be triggered by mousedown -> mousemove -> mouseup sequence', function () {
            events.length.should.equal(4);

            events[0].type.should.equal('dragstart');
            events[1].type.should.equal('dragmove');
            events[2].type.should.equal('dragmove');
            events[3].type.should.equal('dragend');
        });

        it('should have the same coordinates as the original events', function () {
            var event, mock, i;

            for (i = 0; event = events[i], mock = mockEvents[i], i < events.length; i++) {
                event.pageX.should.equal(mock.pageX);
                event.pageY.should.equal(mock.pageY);
                event.clientX.should.equal(mock.clientX);
                event.clientY.should.equal(mock.clientY);
            }
        });

        it('should have the same x0/y0 and clientX0/Y0 as the start event of the sequence', function () {
            var startEvent = mockEvents[0],
                event, i;

            for (i = 0; event = events[i], i < events.length; i++) {
                event.x0.should.equal(startEvent.pageX);
                event.y0.should.equal(startEvent.pageY);
                event.clientX0.should.equal(startEvent.clientX);
                event.clientY0.should.equal(startEvent.clientY);
            }
        });

        it('should keep the same target', function () {
            var event, i;

            for (i = 0; event = events[i], i < events.length; i++) {
                event.target.should.equal(dragElement);
            }
        });

        describe('dragstart', function () {
            var downEvent = mockEvents[0],
                startEvent = events[0];

            it('should have dy/dx of 0', function () {
                startEvent.dx.should.equal(0);
                startEvent.dy.should.equal(0);
            });
        });

        describe('dragmove', function () {

            it('should have dy/dx of this event\'s coordinates - the previous event\'s', function () {
                events[1].dx.should.equal(events[1].pageX - events[0].pageX);
                events[1].dy.should.equal(events[1].pageY - events[0].pageY);

                events[2].dx.should.equal(events[2].pageX - events[1].pageX);
                events[2].dy.should.equal(events[2].pageY - events[1].pageY);
            });
        });

        describe('dragend', function () {
            it('should have dy/dx of the end event\'s coordinates - the start event\'s', function () {
                events[3].dx.should.equal(events[2].pageX - events[0].pageX);
                events[3].dy.should.equal(events[2].pageY - events[0].pageY);
            });
        });
    });

    describe('Gesture sequence', function () {
        interact.stop();

        var pushEvent = function (event) {
                gestureEvents.push(event);
            },
            element = document.createElement('button'),
            iElement = interact(element).gesturable({
                onstart: pushEvent,
                onmove: pushEvent,
                onend: pushEvent
            }).actionChecker(function () { return 'gesture'; }),
            mockEvents = data.touch2Move2End2.map(function (e) {
                return mockEvent(e, element);
            }),
            gestureEvents = [],
            eventMap = [1, 2, 3, 4];

        debug.pointerDown(mockEvents[0]);
        debug.pointerDown(mockEvents[1]);
        debug.pointerMove(mockEvents[2]);
        debug.pointerMove(mockEvents[3]);
        debug.pointerUp(mockEvents[4]);
        debug.pointerUp(mockEvents[5]);

        it('should be started by 2 touches starting and moving and end when there are fewer than two active touches', function () {
            gestureEvents.length.should.equal(4);

            gestureEvents[0].type.should.equal('gesturestart');
            gestureEvents[1].type.should.equal('gesturemove');
            gestureEvents[2].type.should.equal('gesturemove');
            gestureEvents[3].type.should.equal('gestureend');
        });

        describe('touches', function () {

            it('should be the original list of touches in the correspoinding touch event', function () {
                for (var i = 0, gEvent, mEvent;
                     mEvent = mockEvents[eventMap[i]], gEvent = gestureEvents[i], i < gestureEvents.length;
                     i++) {
                         gEvent.touches.should.equal(mEvent.touches);
                     }
            });
        });

        describe('coordinates', function () {
            it('should be the averages of touches', function () {
                for (var i = 0, gEvent, mEvent;
                     mEvent = mockEvents[eventMap[i]], gEvent = gestureEvents[i], i < gestureEvents.length;
                     i++) {

                    var average = interact.getTouchAverage(mEvent);

                    gEvent.pageX.should.equal(average.pageX);
                    gEvent.pageY.should.equal(average.pageY);
                    gEvent.clientX.should.equal(average.clientX);
                    gEvent.clientY.should.equal(average.clientY);
                }
            });
        });

        describe('angle', function () {
            it('should be the angle of the line joining the first two touches of the correspoinding touch event', function () {
                for (var i = 0, gEvent, mEvent;
                     mEvent = mockEvents[eventMap[i]], gEvent = gestureEvents[i], i < gestureEvents.length;
                     i++) {

                    gEvent.angle.should.equal(interact.getTouchAngle(mEvent));
                }
            });
        });

        describe('da', function () {
            describe('in a gesturestart', function () {
                it('should be 0', function () {
                    gestureEvents[0].da.should.equal(0);
                });
            });

            describe('in a gesturemove', function () {
                it('should be (thisEvent.angle - previousEvent.angle)', function () {
                    gestureEvents[1].da.should.equal(gestureEvents[1].angle - gestureEvents[0].angle);
                    gestureEvents[2].da.should.equal(gestureEvents[2].angle - gestureEvents[1].angle);
                });
            });

            describe('in a gestureend', function () {
                it('should be (lastMoveEvent.angle - startEvent.angle)', function () {
                    gestureEvents[3].da.should.equal(gestureEvents[2].angle - gestureEvents[0].angle);
                });
            });
        });

        describe('distance', function () {
            it('should be the distance between the first two touches of the correspoinding touch event', function () {
                for (var i = 0, gEvent, mEvent;
                     mEvent = mockEvents[eventMap[i]], gEvent = gestureEvents[i], i < gestureEvents.length;
                     i++) {

                    gEvent.distance.should.equal(interact.getTouchDistance(mEvent));
                    gEvent.distance.should.equal(interact.getTouchDistance(mEvent));
                }
            });
        });

        describe('scale', function () {
            describe('in a gesturestart', function () {
                it('should be 1', function () {
                    gestureEvents[0].scale.should.equal(1);
                });
            });

            describe('in a gesturemove', function () {
                it('should be the (thisevent.distance / startevent.distance)', function () {
                    gestureEvents[1].scale.should.equal(gestureEvents[1].distance / gestureEvents[0].distance);
                    gestureEvents[2].scale.should.equal(gestureEvents[2].distance / gestureEvents[0].distance);
                });
            });

            describe('in a gestureend', function () {
                it('should be the scale of the last gesturemove event', function () {
                    gestureEvents[3].scale.should.equal(gestureEvents[2].scale);
                });
            });
        });

        describe('ds', function () {
            describe('in a gesturestart', function () {
                it('should be 0', function () {
                    gestureEvents[0].ds.should.equal(0);
                });
            });

            describe('in a gesturemove', function () {
                it('should be the difference between this event\'s scale and that of the previous event', function () {
                    gestureEvents[1].ds.should.equal(gestureEvents[1].scale - gestureEvents[0].scale);
                    gestureEvents[2].ds.should.equal(gestureEvents[2].scale - gestureEvents[1].scale);
                });
            });

            describe('in a gestureend', function () {
                it('should be (lastMoveEvent.scale - startEvent.scale)', function () {
                    gestureEvents[3].ds.should.equal(gestureEvents[2].scale - gestureEvents[0].scale);
                });
            });
        });
    });
});
<commit_msg>Fix test for restrict options<commit_after>var expect = chai.expect,
    should = chai.should(),
    debug = interact.debug();

function blank () {}

function mockEvent (options, target, currentTarget) {
    'use strict';

    options.target = options.target || target;
    options.currentTarget = options.currentTarget || currentTarget;

    return {
        target: options.target,
        currentTarget: options.current || options.target,
        type: options.type,
        pageX: options.x,
        pageY: options.y,
        clientX: options.x - (options.scrollX|0),
        clientY: options.y - (options.scrollY|0),
        touches: options.touches && options.touches.map(mockEvent),
        changedTouches: options.changed && options.changed.map(mockEvent),

        preventDefault: blank,
        stopPropagation: blank,
        stopImmediatePropagation: blank
    };
}

describe('interact', function () {
    'use strict';

    describe('when called as a function', function () {
        var validSelector = 'svg .draggable, body button';

        it('should return an Interactable when given an Element', function () {
            var bod = interact(document.body);

            expect(bod).to.be.an.instanceof(debug.Interactable);

            bod.element().should.equal(document.body);
        });

        it('should return an Interactable when given a valid CSS selector string', function () {
            var interactable = interact(validSelector);

            expect(interactable).to.be.an.instanceof(debug.Interactable);
        });

        it('should throw an error when given a string that is not a valid selector', function () {
            var error = 0;

            try {
                interact('<< invalid selector >>');
            }
            catch (e) {
                error = e;
            }

            error.should.be.instanceof(DOMException);
        });

        it('should return the same value from a given parameter unless returned Interactable is unset', function () { var iBody = interact(document.body),
                iSelector = interact(validSelector);

            interact(document.body).should.equal(iBody);
            interact(validSelector).should.equal(iSelector);

            iBody.unset();
            iSelector.unset();

            interact(document.body).should.not.equal(iBody);
            interact(validSelector).should.not.equal(iSelector);
        });
    });

    describe('gobal options', function () {

    });
});

describe('Interactable', function () {
    'use strict';

    var defaults = debug.defaultOptions,
        iable = interact(document.createElement('div')),
        simpleOptions = {
            draggable    : 'draggable',
            dropzone     : 'dropzone',
            resizable   : 'resizable',
            squareResize : 'squareResize',
            gesturable  : 'gesturable',
            styleCursor  : 'styleCursor',
            origin       : 'origin',
            deltaSource  : 'deltaSource'
        },
        enableOptions = [
            'snap',
            'autoScroll',
            'restrict'
        ],
        checkerOptions = {
            actionChecker: 'getAction',
            rectChecker  : 'getRect'
        };

    describe('options', function () {
        it('should return the default setting if they were never previously set', function () {
            var option, i;

            for (option in simpleOptions) {
                iable[option]().should.equal(defaults[simpleOptions[option]]);
            }

            for (i = 0; option = enableOptions[i], i < enableOptions.length; i++) {
                if (iable[option]()) {
                    iable[option]().should.equal(defaults[option]);
                }
                else {
                    iable[option]().should.equal(defaults[option + 'Enabled']);
                }
            }

            for (option in checkerOptions) {
                iable[option]().should.equal(debug.Interactable.prototype[checkerOptions[option]]);
            }
        });
    });

    describe('#element', function () {
        it('should return the element if this is not a selector Interactable', function () {
            var p = document.createElement('p');

            interact(p).element().should.equal(p);
        });
    });

    describe('#actionChecker', function () {
        var div = document.createElement('div'),
            iDiv = interact(div)
                .draggable(true)
                .resizable(true)
                .gesturable(true),
            i,
            action,
            actions = ['drag', 'resizexy', 'resizex', 'resizey', 'gesture'],
            returnActionI = function alwaysResize () { return actions[i]; };

        it('should set set the function used to determine actions on pointer down events', function () {
            iDiv.actionChecker(returnActionI);
            iDiv.getAction.should.equal(returnActionI);

            for (i = 0; action = actions[i], i < actions.length; i++) {
                debug.pointerDown.call(div, mockEvent({
                    target: div,
                    x: -123,
                    y: 209
                }));

                interact.debug().prepared.should.equal(action);

                interact.stop();
            }
        });
    });
});

describe('Events', function () {
    'use strict';

    interact.stop();

    var dragElement = document.createElement('div'),
        draggable = interact(dragElement),
        events = [],
        mockEvents = data.downMove2Up.map(function (e) {
            return mockEvent(e, dragElement);
        }),
        pushEvent = function (event) {
            events.push(event);
        };

    describe('drag sequence', function () {
        draggable.draggable({
                onstart: pushEvent,
                onmove: pushEvent,
                onend: pushEvent
        }).actionChecker(function () {
            return 'drag';
        });

        debug.pointerDown(mockEvents[0]);
        debug.pointerMove(mockEvents[1]);
        debug.pointerMove(mockEvents[2]);
        debug.pointerUp  (mockEvents[3]);

        it('should be triggered by mousedown -> mousemove -> mouseup sequence', function () {
            events.length.should.equal(4);

            events[0].type.should.equal('dragstart');
            events[1].type.should.equal('dragmove');
            events[2].type.should.equal('dragmove');
            events[3].type.should.equal('dragend');
        });

        it('should have the same coordinates as the original events', function () {
            var event, mock, i;

            for (i = 0; event = events[i], mock = mockEvents[i], i < events.length; i++) {
                event.pageX.should.equal(mock.pageX);
                event.pageY.should.equal(mock.pageY);
                event.clientX.should.equal(mock.clientX);
                event.clientY.should.equal(mock.clientY);
            }
        });

        it('should have the same x0/y0 and clientX0/Y0 as the start event of the sequence', function () {
            var startEvent = mockEvents[0],
                event, i;

            for (i = 0; event = events[i], i < events.length; i++) {
                event.x0.should.equal(startEvent.pageX);
                event.y0.should.equal(startEvent.pageY);
                event.clientX0.should.equal(startEvent.clientX);
                event.clientY0.should.equal(startEvent.clientY);
            }
        });

        it('should keep the same target', function () {
            var event, i;

            for (i = 0; event = events[i], i < events.length; i++) {
                event.target.should.equal(dragElement);
            }
        });

        describe('dragstart', function () {
            var downEvent = mockEvents[0],
                startEvent = events[0];

            it('should have dy/dx of 0', function () {
                startEvent.dx.should.equal(0);
                startEvent.dy.should.equal(0);
            });
        });

        describe('dragmove', function () {

            it('should have dy/dx of this event\'s coordinates - the previous event\'s', function () {
                events[1].dx.should.equal(events[1].pageX - events[0].pageX);
                events[1].dy.should.equal(events[1].pageY - events[0].pageY);

                events[2].dx.should.equal(events[2].pageX - events[1].pageX);
                events[2].dy.should.equal(events[2].pageY - events[1].pageY);
            });
        });

        describe('dragend', function () {
            it('should have dy/dx of the end event\'s coordinates - the start event\'s', function () {
                events[3].dx.should.equal(events[2].pageX - events[0].pageX);
                events[3].dy.should.equal(events[2].pageY - events[0].pageY);
            });
        });
    });

    describe('Gesture sequence', function () {
        interact.stop();

        var pushEvent = function (event) {
                gestureEvents.push(event);
            },
            element = document.createElement('button'),
            iElement = interact(element).gesturable({
                onstart: pushEvent,
                onmove: pushEvent,
                onend: pushEvent
            }).actionChecker(function () { return 'gesture'; }),
            mockEvents = data.touch2Move2End2.map(function (e) {
                return mockEvent(e, element);
            }),
            gestureEvents = [],
            eventMap = [1, 2, 3, 4];

        debug.pointerDown(mockEvents[0]);
        debug.pointerDown(mockEvents[1]);
        debug.pointerMove(mockEvents[2]);
        debug.pointerMove(mockEvents[3]);
        debug.pointerUp(mockEvents[4]);
        debug.pointerUp(mockEvents[5]);

        it('should be started by 2 touches starting and moving and end when there are fewer than two active touches', function () {
            gestureEvents.length.should.equal(4);

            gestureEvents[0].type.should.equal('gesturestart');
            gestureEvents[1].type.should.equal('gesturemove');
            gestureEvents[2].type.should.equal('gesturemove');
            gestureEvents[3].type.should.equal('gestureend');
        });

        describe('touches', function () {

            it('should be the original list of touches in the correspoinding touch event', function () {
                for (var i = 0, gEvent, mEvent;
                     mEvent = mockEvents[eventMap[i]], gEvent = gestureEvents[i], i < gestureEvents.length;
                     i++) {
                         gEvent.touches.should.equal(mEvent.touches);
                     }
            });
        });

        describe('coordinates', function () {
            it('should be the averages of touches', function () {
                for (var i = 0, gEvent, mEvent;
                     mEvent = mockEvents[eventMap[i]], gEvent = gestureEvents[i], i < gestureEvents.length;
                     i++) {

                    var average = interact.getTouchAverage(mEvent);

                    gEvent.pageX.should.equal(average.pageX);
                    gEvent.pageY.should.equal(average.pageY);
                    gEvent.clientX.should.equal(average.clientX);
                    gEvent.clientY.should.equal(average.clientY);
                }
            });
        });

        describe('angle', function () {
            it('should be the angle of the line joining the first two touches of the correspoinding touch event', function () {
                for (var i = 0, gEvent, mEvent;
                     mEvent = mockEvents[eventMap[i]], gEvent = gestureEvents[i], i < gestureEvents.length;
                     i++) {

                    gEvent.angle.should.equal(interact.getTouchAngle(mEvent));
                }
            });
        });

        describe('da', function () {
            describe('in a gesturestart', function () {
                it('should be 0', function () {
                    gestureEvents[0].da.should.equal(0);
                });
            });

            describe('in a gesturemove', function () {
                it('should be (thisEvent.angle - previousEvent.angle)', function () {
                    gestureEvents[1].da.should.equal(gestureEvents[1].angle - gestureEvents[0].angle);
                    gestureEvents[2].da.should.equal(gestureEvents[2].angle - gestureEvents[1].angle);
                });
            });

            describe('in a gestureend', function () {
                it('should be (lastMoveEvent.angle - startEvent.angle)', function () {
                    gestureEvents[3].da.should.equal(gestureEvents[2].angle - gestureEvents[0].angle);
                });
            });
        });

        describe('distance', function () {
            it('should be the distance between the first two touches of the correspoinding touch event', function () {
                for (var i = 0, gEvent, mEvent;
                     mEvent = mockEvents[eventMap[i]], gEvent = gestureEvents[i], i < gestureEvents.length;
                     i++) {

                    gEvent.distance.should.equal(interact.getTouchDistance(mEvent));
                    gEvent.distance.should.equal(interact.getTouchDistance(mEvent));
                }
            });
        });

        describe('scale', function () {
            describe('in a gesturestart', function () {
                it('should be 1', function () {
                    gestureEvents[0].scale.should.equal(1);
                });
            });

            describe('in a gesturemove', function () {
                it('should be the (thisevent.distance / startevent.distance)', function () {
                    gestureEvents[1].scale.should.equal(gestureEvents[1].distance / gestureEvents[0].distance);
                    gestureEvents[2].scale.should.equal(gestureEvents[2].distance / gestureEvents[0].distance);
                });
            });

            describe('in a gestureend', function () {
                it('should be the scale of the last gesturemove event', function () {
                    gestureEvents[3].scale.should.equal(gestureEvents[2].scale);
                });
            });
        });

        describe('ds', function () {
            describe('in a gesturestart', function () {
                it('should be 0', function () {
                    gestureEvents[0].ds.should.equal(0);
                });
            });

            describe('in a gesturemove', function () {
                it('should be the difference between this event\'s scale and that of the previous event', function () {
                    gestureEvents[1].ds.should.equal(gestureEvents[1].scale - gestureEvents[0].scale);
                    gestureEvents[2].ds.should.equal(gestureEvents[2].scale - gestureEvents[1].scale);
                });
            });

            describe('in a gestureend', function () {
                it('should be (lastMoveEvent.scale - startEvent.scale)', function () {
                    gestureEvents[3].ds.should.equal(gestureEvents[2].scale - gestureEvents[0].scale);
                });
            });
        });
    });
});
<|endoftext|>


<commit_before>var $username = $('#username'),
    $password = $('#password'),
    $email = $('#email');

var $loginForm = $('form.login').submit(function (event) {
  event.preventDefault();

  var form = $(this),
      name = form.find('input[name=username]').val(),
      key = form.find('input[name=password]').val(),
      email = form.find('input[name=email]').val(),
      $loginFeedback = form.find('.loginFeedback');

  // jsbin.settings.home = name; // will save later
  $loginFeedback.show().text('Checking...');

  $.ajax({
    url: form.attr('action'),
    data: { name: name, key: key, email: email },
    type: 'post',
    dataType: 'json',
    complete: function (jqXHR) {
      var data = $.parseJSON(jqXHR.responseText) || {};
      // cookie is required to share with the server so we can do a redirect on new bin
      if (data.ok) {
        if (data.avatar) {
          $('#avatar').find('img').remove().end().prepend('');
        }
        if (data.message) {
          $loginFeedback.text(data.message);
        } else {
          window.location = window.location.pathname + window.location.search;
        }
      } else {
        analytics.login(false);
        $loginFeedback.text(data.message || ('"' + name + '" has already been taken. Please either double check the password, or choose another username.'));
      }
    }
  });
});

// if ($('#homebtn').length) {
//   jsbin.settings.home = document.cookie.split('home=')[1].split(';')[0];
//   document.title = jsbin.settings.home + '@' + document.title;
// }<commit_msg>Changign user to username in json sent to /login<commit_after>var $username = $('#username'),
    $password = $('#password'),
    $email = $('#email');

var $loginForm = $('form.login').submit(function (event) {
  event.preventDefault();

  var form = $(this),
      name = form.find('input[name=username]').val(),
      key = form.find('input[name=password]').val(),
      email = form.find('input[name=email]').val(),
      $loginFeedback = form.find('.loginFeedback');

  // jsbin.settings.home = name; // will save later
  $loginFeedback.show().text('Checking...');

  $.ajax({
    url: 'http://' + window.location.host + form.attr('action'),
    data: { username: name, key: key, email: email },
    type: 'POST',
    dataType: 'json',
    complete: function (jqXHR) {
      var data = $.parseJSON(jqXHR.responseText) || {};
      // cookie is required to share with the server so we can do a redirect on new bin
      if (data.ok) {
        if (data.avatar) {
          $('#avatar').find('img').remove().end().prepend('');
        }
        if (data.message) {
          $loginFeedback.text(data.message);
        } else {
          window.location = window.location.pathname + window.location.search;
        }
      } else {
        analytics.login(false);
        $loginFeedback.text(data.message || ('"' + name + '" has already been taken. Please either double check the password, or choose another username.'));
      }
    }
  });
});

// if ($('#homebtn').length) {
//   jsbin.settings.home = document.cookie.split('home=')[1].split(';')[0];
//   document.title = jsbin.settings.home + '@' + document.title;
// }<|endoftext|>


<commit_before>/**
 * Tests.
 */
require('dotenv').config();
var assert = require('assert');
var should = require('chai').should();
var rewire = require('rewire');
var parser = rewire("../server/log_management/parser");
var users = rewire("../server/routes/users");

/*
describe('Default', function() {
    describe('#indexOf()', function() {
        it('should return -1 when the value is not present', function() {
            assert.equal(-1, [1,2,3].indexOf(4));
        });
    });
});*/

describe('Parser', function () {
    describe('#parseLine()', function () {
        var line = '195.113.20.155 - - [08/Mar/2016:14:48:43 +0100] "GET /services/test/nesmysly"';
        var parseLine = parser.__get__('parseLine');
        var parsed = parseLine(line);

        it('ip is valid', function () {
            (parsed.ip).should.be.a('string');
            //(parsed.ip).should.be.equal.to('195.113.20.155');
        })

        it('datetime is valid', function () {
            //TODO dk: look up how to check if it's a valid timestamp
            //(parsed.datetime).should.be.a('time');
        })

        it('service is valid', function () {
            (parsed.service).should.be.a('boolean');
        })

        it('request is valid', function () {
            (parsed.request).should.be.a('string');
            //(parsed.ip).should.be.equal.to('/services/test/nesmysly');
        })
    })
})

describe('Others', function () {
    describe('#validateIPaddress @routes/users', function () {
        var validate = users.__get__('validateIPaddress');
        it('valid ip should be true', function() {
            validate('127.0.0.1').should.be.true;
        });
        it('invalid ip should be false', function() {
            validate('127.0.0.300').should.be.false;
            validate('127.0.0.30.0').should.be.false;
        });
    })
})
<commit_msg>minor changes<commit_after>/**
 * Tests.
 */
require('dotenv').config();
var assert = require('assert');
var should = require('chai').should();
var rewire = require('rewire');
var parser = rewire("../server/log_management/parser");
var users = rewire("../server/routes/users");

/*
describe('Default', function() {
    describe('#indexOf()', function() {
        it('should return -1 when the value is not present', function() {
            assert.equal(-1, [1,2,3].indexOf(4));
        });
    });
});*/

describe('Parser', function () {
    describe('#parseLine()', function () {
        var line = '195.113.20.155 - - [08/Mar/2016:14:48:43 +0100] "GET /services/test/nesmysly"';
        var parseLine = parser.__get__('parseLine');
        var parsed = parseLine(line);

        it('ip is valid', function () {
            (parsed.ip).should.be.a('string');
            //(parsed.ip).should.be.equal.to('195.113.20.155');
        })

        it('datetime is valid', function () {
            //TODO dk: look up how to check if it's a valid timestamp
            // ... that is not really the point of it, is it?
        })

        it('service is valid', function () {
            (parsed.service).should.be.a('boolean');
        })

        it('request is valid', function () {
            (parsed.request).should.be.a('string');
            //(parsed.ip).should.be.equal.to('/services/test/nesmysly');
        })
    })
})

describe('Others', function () {
    describe('#validateIPaddress @routes/users', function () {
        var validate = users.__get__('validateIPaddress');
        it('valid ip should be true', function() {
            validate('127.0.0.1').should.be.true;
        });
        it('invalid ip should be false', function() {
            validate('127.0.0.300').should.be.false;
            validate('127.0.0.30.0').should.be.false;
        });
    })
})
<|endoftext|>


<commit_before>(function(){

    var irailapp = angular.module('irailapp', ['ui.bootstrap']);

    irailapp.controller('StationListCtrl', function ($scope, $http, $filter) {

        // Init departure and destination as undefined
        $scope.departure = undefined;
        $scope.destination = undefined;

        // Fetch stations via HTTP GET request
        $http.get('data/stations.json').success(function(data) {
            $scope.stations = data;
            console.log($scope.stations);
        });

        // Check if we can dump the data
        $scope.saveDataCheck = function(){
            if('id' in $scope.departure && 'id' in $scope.destination){
                $scope.data = {
                    "departure" : $scope.departure,
                    "destination" : $scope.destination,
                    "date" : $filter('date')($scope.mydate, 'shortDate'),
                    "time" : $filter('date')($scope.mytime, 'HH:mm'),
                    "timeoption" : $scope.timeoption
                }
            }
        };

        $scope.save = function(){
            // Check if departure and destination are bound
            try{
                $scope.saveDataCheck();
            }
            // If not bound, try to bind data (1 attempt)
            catch(ex){
                    for (var i = 0, len = $scope.stations.stations.length; i < len; i++) {
                        try{
                            if (($scope.stations.stations[i].name.toLowerCase()).indexOf($scope.departure.toLowerCase()) != -1){
                                arr = $scope.stations.stations;
                                $scope.departure = arr[i];
                            }
                        }
                        catch(ex){
                            alert('We could not find that station based on your input. Sorry.');
                        }
                        try{
                            if(($scope.stations.stations[i].name.toLowerCase()).indexOf($scope.destination.toLowerCase()) != -1){
                                arr = $scope.stations.stations;
                                $scope.destination = arr[i];
                            }
                        }
                        catch(ex){
                            alert('We could not find that station based on your input. Sorry.');
                        }
                    }
                    try{
                        $scope.saveDataCheck();
                    }catch(ex){
                        alert('We could not find that station based on your input. Sorry.');
                    }
                }
            // Check if time and date are set
            // Check if select box has been set
        };

    });

}());<commit_msg>Fix infinite alert loop :)<commit_after>(function(){

    var irailapp = angular.module('irailapp', ['ui.bootstrap']);

    irailapp.controller('StationListCtrl', function ($scope, $http, $filter) {

        // Init departure and destination as undefined
        $scope.departure = undefined;
        $scope.destination = undefined;

        // Fetch stations via HTTP GET request
        $http.get('data/stations.json').success(function(data) {
            $scope.stations = data;
            console.log($scope.stations);
        });

        // Check if we can dump the data
        $scope.saveDataCheck = function(){
            if('id' in $scope.departure && 'id' in $scope.destination){
                $scope.data = {
                    "departure" : $scope.departure,
                    "destination" : $scope.destination,
                    "date" : $filter('date')($scope.mydate, 'shortDate'),
                    "time" : $filter('date')($scope.mytime, 'HH:mm'),
                    "timeoption" : $scope.timeoption
                }
            }
        };

        $scope.save = function(){
            // Check if departure and destination are bound
            try{
                $scope.saveDataCheck();
            }
            // If not bound, try to bind data (1 attempt)
            catch(ex){
                    for (var i = 0, len = $scope.stations.stations.length; i < len; i++) {
                        try{
                            if (($scope.stations.stations[i].name.toLowerCase()).indexOf($scope.departure.toLowerCase()) != -1){
                                arr = $scope.stations.stations;
                                $scope.departure = arr[i];
                            }
                        }
                        catch(ex){

                        }
                        try{
                            if(($scope.stations.stations[i].name.toLowerCase()).indexOf($scope.destination.toLowerCase()) != -1){
                                arr = $scope.stations.stations;
                                $scope.destination = arr[i];
                            }
                        }
                        catch(ex){

                        }
                    }
                    try{
                        $scope.saveDataCheck();
                    }catch(ex){
                        alert('We could not find that station based on your input. Sorry.');
                    }
                }
            // Check if time and date are set
            // Check if select box has been set
        };

    });

}());<|endoftext|>


<commit_before>'use strict';

var typeEnv;
if (!chai) {
  var chai = require('chai');
  var chaiImmutable = require('../chai-immutable');
  var Immutable = require('immutable');

  chai.use(chaiImmutable);
  typeEnv = 'Node.js';
}
else typeEnv = 'PhantomJS';

var assert = chai.assert;
var expect = chai.expect;
var List = Immutable.List;
var Map = Immutable.Map;
var Set = Immutable.Set;
var Stack = Immutable.Stack;

describe('chai-immutable (' + typeEnv + ')', function () {
  var list3 = List.of(1, 2, 3);

  describe('BDD interface', function () {
    describe('empty property', function () {
      it('should be true when given an empty collection', function () {
        expect(new List()).to.be.empty;
      });

      it('should be false when given a non-empty collection', function () {
        expect(list3).to.not.be.empty;
      });

      it('should not affect the original assertions', function () {
        expect([]).to.be.empty;
        expect('').to.be.empty;
        expect({}).to.be.empty;
      });
    });

    describe('equal method', function () {
      it(
        'should fail when only the "expected" value is an Immutable collection',
        function () {
          var fn = function () { expect([]).to.equal(List()); };
          expect(fn).to.throw(Error);
        }
      );

      it('should be true when compared structure is equal', function () {
        expect(list3).to.equal(List.of(1, 2, 3));
      });

      it('should be false when compared structure not equal', function () {
        expect(list3).to.not.equal(new List());
      });

      it('aliases of equal should also work', function () {
        expect(list3).to.equal(List.of(1, 2, 3));
        expect(list3).to.not.equal(new List());
        expect(list3).to.equals(List.of(1, 2, 3));
        expect(list3).to.not.equals(new List());
        expect(list3).to.eq(List.of(1, 2, 3));
        expect(list3).to.not.eq(new List());
        expect(list3).to.eql(List.of(1, 2, 3));
        expect(list3).to.not.eql(new List());
        expect(list3).to.deep.equal(List.of(1, 2, 3));
        expect(list3).to.not.deep.equal(new List());
      });

      it('should not affect the original assertions', function () {
        expect('hello').to.equal('hello');
        expect(42).to.equal(42);
        expect(1).to.not.equal(true);
        expect({ foo: 'bar' }).to.not.equal({ foo: 'bar' });
        expect({ foo: 'bar' }).to.deep.equal({ foo: 'bar' });
      });
    });

    describe('include method', function () {
      it('should be true with an existing value', function () {
        expect(new List([1, 2, 3])).to.include(2);
      });

      it('should be false with an inexisting value', function () {
        expect(new List([1, 2, 3])).to.not.include(42);
      });

      it('should chain and be true with existing keys', function () {
        expect(new Map({
          foo: 'bar',
          hello: 'universe'
        })).to.include.keys('foo');
      });

      it('should chain and be false with inexisting keys', function () {
        expect(new Map({
          foo: 'bar',
          hello: 'universe'
        })).to.not.include.keys('not-foo');
      });

      it('aliases of include should also work', function () {
        expect(new List([1, 2, 3])).contain(2);
        expect(new List([1, 2, 3])).not.contain(42);
        expect(new List([1, 2, 3])).contains(2);
        expect(new List([1, 2, 3])).not.contains(42);
        expect(new List([1, 2, 3])).includes(2);
        expect(new List([1, 2, 3])).not.includes(42);
      });

      it('should not affect the original assertions', function () {
        expect([1, 2, 3]).to.include(2);
        expect('foobar').to.contain('foo');
        expect({ foo: 'bar', hello: 'universe' }).to.include.keys('foo');
      });
    });

    describe('keys method', function () {
      var map = new Map({ x: 1, y: 2 });
      var obj = { x: 1, y: 2 };

      it('should be true when given an existing key', function () {
        expect(new Map({ x: 1 })).to.have.key('x');
        expect({ x: 1 }).to.have.key('x');
      });

      it('should be false when given a non existing key', function () {
        expect(map).to.not.have.key('z');
        expect(obj).to.not.have.key('z');
      });

      it('should be true when given multiple existing keys', function () {
        expect(map).to.have.keys('x', 'y');
        expect(obj).to.have.keys('x', 'y');
      });

      it('should be false when given multiple non existing keys', function () {
        expect(map).to.not.have.keys('z1', 'z2');
        expect(obj).to.not.have.keys('z1', 'z2');
      });

      it('should accept an Array of keys to check against', function () {
        expect(map).to.have.keys(['x', 'y']);
        expect(obj).to.have.keys(['x', 'y']);
      });

      it('should accept an List of keys to check against', function () {
        expect(map).to.have.keys(new List(['x', 'y']));
      });

      it('should accept an Set of keys to check against', function () {
        expect(map).to.have.keys(new Set(['x', 'y']));
      });

      it('should accept an Stack of keys to check against', function () {
        expect(map).to.have.keys(new Stack(['x', 'y']));
      });

      it('should accept an Object to check against', function () {
        expect(map).to.have.keys({ 'x': 6, 'y': 7 });
        expect(obj).to.have.keys({ 'x': 6, 'y': 7 });
      });

      it('should accept a Map to check against', function () {
        expect(map).to.have.keys(new Map({ 'x': 6, 'y': 7 }));
      });

      it('should be true when used with any and an existing key', function () {
        expect(map).to.have.any.keys('x', 'z');
        expect(obj).to.have.any.keys('x', 'z');
      });

      it('should be false when used with any and inexisting keys', function () {
        expect(map).to.not.have.any.keys('z1', 'z2');
        expect(obj).to.not.have.any.keys('z1', 'z2');
      });

      it('should be true when used with all and existing keys', function () {
        expect(map).to.have.all.keys('x', 'y');
        expect(obj).to.have.all.keys('x', 'y');
      });

      it('should be false when used with all and inexisting keys', function () {
        expect(map).to.not.have.all.keys('z1', 'y');
        expect(obj).to.not.have.all.keys('z1', 'y');
      });

      it('should be true when used with contain and an existing key', function () {
        expect(map).to.contain.key('x');
        expect(obj).to.contain.key('x');
      });

      it('should not affect the original assertions', function () {
        expect({ x: 1, y: 2 }).to.have.any.keys('x', 'z');
        expect({ x: 1, y: 2 }).to.have.any.keys('x');
        expect({ x: 1, y: 2 }).to.contain.any.keys('y', 'z');
        expect({ x: 1, y: 2 }).to.contain.any.keys(['x']);
        expect({ x: 1, y: 2 }).to.contain.any.keys({ 'x': 6 });
        expect({ x: 1, y: 2 }).to.have.all.keys(['x', 'y']);
        expect({ x: 1, y: 2 }).to.have.all.keys({ 'x': 6, 'y': 7 });
        expect({ x: 1, y: 2, z: 3 }).to.contain.all.keys(['x', 'y']);
        expect({ x: 1, y: 2, z: 3 }).to.contain.all.keys({ 'x': 6 });
      });
    });

    describe('size method', function () {
      it('should be true when given the right size', function () {
        expect(list3).to.have.size(3);
      });

      it('should be false when given the wrong size', function () {
        expect(list3).to.not.have.size(42);
      });

      it('should also work with alias sizeOf', function () {
        expect(list3).to.have.sizeOf(3);
        expect(list3).to.not.have.sizeOf(42);
      });
    });

    describe('size property', function () {
      it('above should be true when given a good min size', function () {
        expect(list3).to.have.size.above(2);
      });

      it('above should be false when given a bad min size', function () {
        expect(list3).to.not.have.size.above(42);
      });

      it('aliases of above should also work', function () {
        expect(list3).to.have.size.gt(2);
        expect(list3).to.have.size.greaterThan(2);
        expect(list3).to.not.have.size.gt(42);
        expect(list3).to.not.have.size.greaterThan(42);
      });

      it('should not affect the original assertions of above', function () {
        expect('foo').to.have.length.above(2);
        expect([1, 2, 3]).to.have.length.above(2);
      });

      it('below should be true when given a good max size', function () {
        expect(list3).to.have.size.below(42);
      });

      it('below should be false when given a bad max size', function () {
        expect(list3).to.not.have.size.below(1);
      });

      it('aliases of below should also work', function () {
        expect(list3).to.have.size.lt(4);
        expect(list3).to.have.size.lessThan(4);
        expect(list3).to.not.have.size.lt(1);
        expect(list3).to.not.have.size.lessThan(1);
      });

      it('should not affect the original assertions of below', function () {
        expect('foo').to.have.length.below(4);
        expect([1, 2, 3]).to.have.length.below(4);
      });

      it('within should be true when given a good range', function () {
        expect(list3).to.have.size.within(2, 42);
      });

      it('within should be false when given a bad range', function () {
        expect(list3).to.not.have.size.within(10, 20);
      });

      it('should not affect the original assertions of within', function () {
        expect('foo').to.have.length.within(2, 4);
        expect([1, 2, 3]).to.have.length.within(2, 4);
      });

      it('least should be true when given a good min size', function () {
        expect(list3).to.have.size.of.at.least(2);
      });

      it('least should be false when given a bad min size', function () {
        expect(list3).to.not.have.size.of.at.least(42);
      });

      it('aliases of least should also work', function () {
        expect(list3).to.have.size.gte(2);
        expect(list3).to.not.have.size.gte(42);
      });

      it('should not affect the original assertions of least', function () {
        expect('foo').to.have.length.of.at.least(2);
        expect([1, 2, 3]).to.have.length.of.at.least(3);
      });

      it('most should be true when given a good max size', function () {
        expect(list3).to.have.size.of.at.most(42);
      });

      it('most should be false when given a bad max size', function () {
        expect(list3).to.not.have.size.of.at.most(2);
      });

      it('aliases of most should also work', function () {
        expect(list3).to.have.size.lte(42);
        expect(list3).to.not.have.size.lte(2);
      });

      it('should not affect the original assertions of most', function () {
        expect('foo').to.have.length.of.at.most(4);
        expect([1, 2, 3]).to.have.length.of.at.most(3);
      });
    });
  });

  describe('TDD interface', function () {
    describe('equal assertion', function () {
      it(
        'should fail when only the "expected" value is an Immutable collection',
        function () {
          var fn = function () { assert.equal([], List()); };
          assert.throw(fn);
        }
      );

      it('should be true when compared structure is equal', function () {
        assert.equal(list3, List.of(1, 2, 3));
      });

      it('should be false when compared structure not equal', function () {
        assert.notEqual(list3, new List());
      });

      it('should not affect the original assertion', function () {
        assert.equal(42, 42);
        assert.equal(3, '3');
        assert.notEqual('oui', 'non');
        assert.notEqual({ foo: 'bar' }, { foo: 'bar' });
      });
    });

    describe('unoverridden strictEqual and deepEqual assertions', function () {
      it('should be true when compared structure is equal', function () {
        assert.strictEqual(list3, List.of(1, 2, 3));
        assert.deepEqual(list3, List.of(1, 2, 3));
      });

      it('should be false when compared structure not equal', function () {
        assert.notStrictEqual(list3, new List());
        assert.notDeepEqual(list3, new List());
      });
    });

    describe('sizeOf assertion', function () {
      it('should be true when given the right size', function () {
        assert.sizeOf(List.of(1, 2, 3), 3);
      });

      it('should work with empty collections', function () {
        assert.sizeOf(new List(), 0);
      });
    });
  });
});
<commit_msg>Add tests for deep equal (in)equality<commit_after>'use strict';

var typeEnv;
if (!chai) {
  var chai = require('chai');
  var chaiImmutable = require('../chai-immutable');
  var Immutable = require('immutable');

  chai.use(chaiImmutable);
  typeEnv = 'Node.js';
}
else typeEnv = 'PhantomJS';

var assert = chai.assert;
var expect = chai.expect;
var List = Immutable.List;
var Map = Immutable.Map;
var Set = Immutable.Set;
var Stack = Immutable.Stack;

describe('chai-immutable (' + typeEnv + ')', function () {
  var list3 = List.of(1, 2, 3);
  var deepMap = new Map({
    foo: 'bar',
    list: List.of(1, 2, 3)
  });

  describe('BDD interface', function () {
    describe('empty property', function () {
      it('should be true when given an empty collection', function () {
        expect(new List()).to.be.empty;
      });

      it('should be false when given a non-empty collection', function () {
        expect(list3).to.not.be.empty;
      });

      it('should not affect the original assertions', function () {
        expect([]).to.be.empty;
        expect('').to.be.empty;
        expect({}).to.be.empty;
      });
    });

    describe('equal method', function () {
      it(
        'should fail when only the "expected" value is an Immutable collection',
        function () {
          var fn = function () { expect([]).to.equal(List()); };
          expect(fn).to.throw(Error);
        }
      );

      it('should be true when compared structure is equal', function () {
        expect(list3).to.equal(List.of(1, 2, 3));
      });

      it('should be false when compared structure not equal', function () {
        expect(list3).to.not.equal(new List());
      });

      it('aliases of equal should also work', function () {
        expect(list3).to.equal(List.of(1, 2, 3));
        expect(list3).to.not.equal(new List());
        expect(list3).to.equals(List.of(1, 2, 3));
        expect(list3).to.not.equals(new List());
        expect(list3).to.eq(List.of(1, 2, 3));
        expect(list3).to.not.eq(new List());
        expect(list3).to.eql(List.of(1, 2, 3));
        expect(list3).to.not.eql(new List());
        expect(list3).to.deep.equal(List.of(1, 2, 3));
        expect(list3).to.not.deep.equal(new List());
      });

      it('should not affect the original assertions', function () {
        expect('hello').to.equal('hello');
        expect(42).to.equal(42);
        expect(1).to.not.equal(true);
        expect({ foo: 'bar' }).to.not.equal({ foo: 'bar' });
        expect({ foo: 'bar' }).to.deep.equal({ foo: 'bar' });
      });

      it('should work on deep structures that are equal', function () {
        var sameDeepMap = new Map({
          foo: 'bar',
          list: List.of(1, 2, 3)
        });

        expect(deepMap).to.equal(sameDeepMap);
        expect(deepMap).to.equals(sameDeepMap);
        expect(deepMap).to.eq(sameDeepMap);
        expect(deepMap).to.eql(sameDeepMap);
        expect(deepMap).to.deep.equal(sameDeepMap);
      });

      it('should work on deep structures that are not equal', function () {
        var differentDeepMap = new Map({
          foo: 'bar',
          list: List.of(42)
        });

        expect(deepMap).to.not.equal(differentDeepMap);
        expect(deepMap).to.not.equals(differentDeepMap);
        expect(deepMap).to.not.eq(differentDeepMap);
        expect(deepMap).to.not.eql(differentDeepMap);
        expect(deepMap).to.not.deep.equal(differentDeepMap);
      });
    });

    describe('include method', function () {
      it('should be true with an existing value', function () {
        expect(new List([1, 2, 3])).to.include(2);
      });

      it('should be false with an inexisting value', function () {
        expect(new List([1, 2, 3])).to.not.include(42);
      });

      it('should chain and be true with existing keys', function () {
        expect(new Map({
          foo: 'bar',
          hello: 'universe'
        })).to.include.keys('foo');
      });

      it('should chain and be false with inexisting keys', function () {
        expect(new Map({
          foo: 'bar',
          hello: 'universe'
        })).to.not.include.keys('not-foo');
      });

      it('aliases of include should also work', function () {
        expect(new List([1, 2, 3])).contain(2);
        expect(new List([1, 2, 3])).not.contain(42);
        expect(new List([1, 2, 3])).contains(2);
        expect(new List([1, 2, 3])).not.contains(42);
        expect(new List([1, 2, 3])).includes(2);
        expect(new List([1, 2, 3])).not.includes(42);
      });

      it('should not affect the original assertions', function () {
        expect([1, 2, 3]).to.include(2);
        expect('foobar').to.contain('foo');
        expect({ foo: 'bar', hello: 'universe' }).to.include.keys('foo');
      });
    });

    describe('keys method', function () {
      var map = new Map({ x: 1, y: 2 });
      var obj = { x: 1, y: 2 };

      it('should be true when given an existing key', function () {
        expect(new Map({ x: 1 })).to.have.key('x');
        expect({ x: 1 }).to.have.key('x');
      });

      it('should be false when given a non existing key', function () {
        expect(map).to.not.have.key('z');
        expect(obj).to.not.have.key('z');
      });

      it('should be true when given multiple existing keys', function () {
        expect(map).to.have.keys('x', 'y');
        expect(obj).to.have.keys('x', 'y');
      });

      it('should be false when given multiple non existing keys', function () {
        expect(map).to.not.have.keys('z1', 'z2');
        expect(obj).to.not.have.keys('z1', 'z2');
      });

      it('should accept an Array of keys to check against', function () {
        expect(map).to.have.keys(['x', 'y']);
        expect(obj).to.have.keys(['x', 'y']);
      });

      it('should accept an List of keys to check against', function () {
        expect(map).to.have.keys(new List(['x', 'y']));
      });

      it('should accept an Set of keys to check against', function () {
        expect(map).to.have.keys(new Set(['x', 'y']));
      });

      it('should accept an Stack of keys to check against', function () {
        expect(map).to.have.keys(new Stack(['x', 'y']));
      });

      it('should accept an Object to check against', function () {
        expect(map).to.have.keys({ 'x': 6, 'y': 7 });
        expect(obj).to.have.keys({ 'x': 6, 'y': 7 });
      });

      it('should accept a Map to check against', function () {
        expect(map).to.have.keys(new Map({ 'x': 6, 'y': 7 }));
      });

      it('should be true when used with any and an existing key', function () {
        expect(map).to.have.any.keys('x', 'z');
        expect(obj).to.have.any.keys('x', 'z');
      });

      it('should be false when used with any and inexisting keys', function () {
        expect(map).to.not.have.any.keys('z1', 'z2');
        expect(obj).to.not.have.any.keys('z1', 'z2');
      });

      it('should be true when used with all and existing keys', function () {
        expect(map).to.have.all.keys('x', 'y');
        expect(obj).to.have.all.keys('x', 'y');
      });

      it('should be false when used with all and inexisting keys', function () {
        expect(map).to.not.have.all.keys('z1', 'y');
        expect(obj).to.not.have.all.keys('z1', 'y');
      });

      it('should be true when used with contain and an existing key', function () {
        expect(map).to.contain.key('x');
        expect(obj).to.contain.key('x');
      });

      it('should not affect the original assertions', function () {
        expect({ x: 1, y: 2 }).to.have.any.keys('x', 'z');
        expect({ x: 1, y: 2 }).to.have.any.keys('x');
        expect({ x: 1, y: 2 }).to.contain.any.keys('y', 'z');
        expect({ x: 1, y: 2 }).to.contain.any.keys(['x']);
        expect({ x: 1, y: 2 }).to.contain.any.keys({ 'x': 6 });
        expect({ x: 1, y: 2 }).to.have.all.keys(['x', 'y']);
        expect({ x: 1, y: 2 }).to.have.all.keys({ 'x': 6, 'y': 7 });
        expect({ x: 1, y: 2, z: 3 }).to.contain.all.keys(['x', 'y']);
        expect({ x: 1, y: 2, z: 3 }).to.contain.all.keys({ 'x': 6 });
      });
    });

    describe('size method', function () {
      it('should be true when given the right size', function () {
        expect(list3).to.have.size(3);
      });

      it('should be false when given the wrong size', function () {
        expect(list3).to.not.have.size(42);
      });

      it('should also work with alias sizeOf', function () {
        expect(list3).to.have.sizeOf(3);
        expect(list3).to.not.have.sizeOf(42);
      });
    });

    describe('size property', function () {
      it('above should be true when given a good min size', function () {
        expect(list3).to.have.size.above(2);
      });

      it('above should be false when given a bad min size', function () {
        expect(list3).to.not.have.size.above(42);
      });

      it('aliases of above should also work', function () {
        expect(list3).to.have.size.gt(2);
        expect(list3).to.have.size.greaterThan(2);
        expect(list3).to.not.have.size.gt(42);
        expect(list3).to.not.have.size.greaterThan(42);
      });

      it('should not affect the original assertions of above', function () {
        expect('foo').to.have.length.above(2);
        expect([1, 2, 3]).to.have.length.above(2);
      });

      it('below should be true when given a good max size', function () {
        expect(list3).to.have.size.below(42);
      });

      it('below should be false when given a bad max size', function () {
        expect(list3).to.not.have.size.below(1);
      });

      it('aliases of below should also work', function () {
        expect(list3).to.have.size.lt(4);
        expect(list3).to.have.size.lessThan(4);
        expect(list3).to.not.have.size.lt(1);
        expect(list3).to.not.have.size.lessThan(1);
      });

      it('should not affect the original assertions of below', function () {
        expect('foo').to.have.length.below(4);
        expect([1, 2, 3]).to.have.length.below(4);
      });

      it('within should be true when given a good range', function () {
        expect(list3).to.have.size.within(2, 42);
      });

      it('within should be false when given a bad range', function () {
        expect(list3).to.not.have.size.within(10, 20);
      });

      it('should not affect the original assertions of within', function () {
        expect('foo').to.have.length.within(2, 4);
        expect([1, 2, 3]).to.have.length.within(2, 4);
      });

      it('least should be true when given a good min size', function () {
        expect(list3).to.have.size.of.at.least(2);
      });

      it('least should be false when given a bad min size', function () {
        expect(list3).to.not.have.size.of.at.least(42);
      });

      it('aliases of least should also work', function () {
        expect(list3).to.have.size.gte(2);
        expect(list3).to.not.have.size.gte(42);
      });

      it('should not affect the original assertions of least', function () {
        expect('foo').to.have.length.of.at.least(2);
        expect([1, 2, 3]).to.have.length.of.at.least(3);
      });

      it('most should be true when given a good max size', function () {
        expect(list3).to.have.size.of.at.most(42);
      });

      it('most should be false when given a bad max size', function () {
        expect(list3).to.not.have.size.of.at.most(2);
      });

      it('aliases of most should also work', function () {
        expect(list3).to.have.size.lte(42);
        expect(list3).to.not.have.size.lte(2);
      });

      it('should not affect the original assertions of most', function () {
        expect('foo').to.have.length.of.at.most(4);
        expect([1, 2, 3]).to.have.length.of.at.most(3);
      });
    });
  });

  describe('TDD interface', function () {
    describe('equal assertion', function () {
      it(
        'should fail when only the "expected" value is an Immutable collection',
        function () {
          var fn = function () { assert.equal([], List()); };
          assert.throw(fn);
        }
      );

      it('should be true when compared structure is equal', function () {
        assert.equal(list3, List.of(1, 2, 3));
      });

      it('should be false when compared structure not equal', function () {
        assert.notEqual(list3, new List());
      });

      it('should not affect the original assertion', function () {
        assert.equal(42, 42);
        assert.equal(3, '3');
        assert.notEqual('oui', 'non');
        assert.notEqual({ foo: 'bar' }, { foo: 'bar' });
      });
    });

    describe('unoverridden strictEqual and deepEqual assertions', function () {
      it('should be true when compared structure is equal', function () {
        assert.strictEqual(list3, List.of(1, 2, 3));
        assert.deepEqual(list3, List.of(1, 2, 3));
      });

      it('should be false when compared structure not equal', function () {
        assert.notStrictEqual(list3, new List());
        assert.notDeepEqual(list3, new List());
      });
    });

    it('should work on deep structures that are equal', function () {
      var sameDeepMap = new Map({
        foo: 'bar',
        list: List.of(1, 2, 3)
      });

      assert.equal(deepMap, sameDeepMap);
      assert.strictEqual(deepMap, sameDeepMap);
      assert.deepEqual(deepMap, sameDeepMap);
    });

    it('should work on deep structures that are not equal', function () {
      var differentDeepMap = new Map({
        foo: 'bar',
        list: List.of(42)
      });

      assert.notEqual(deepMap, differentDeepMap);
      assert.notStrictEqual(deepMap, differentDeepMap);
      assert.notDeepEqual(deepMap, differentDeepMap);
    });

    describe('sizeOf assertion', function () {
      it('should be true when given the right size', function () {
        assert.sizeOf(List.of(1, 2, 3), 3);
      });

      it('should work with empty collections', function () {
        assert.sizeOf(new List(), 0);
      });
    });
  });
});
<|endoftext|>


<commit_before>openerp.web.data_import = function(openerp) {
var QWeb = openerp.web.qweb,
    _t = openerp.web._t;
/**
 * Safari does not deal well at all with raw JSON data being returned. As a
 * result, we're going to cheat by using a pseudo-jsonp: instead of getting
 * JSON data in the iframe, we're getting a ``script`` tag which consists of a
 * function call and the returned data (the json dump).
 *
 * The function is an auto-generated name bound to ``window``, which calls
 * back into the callback provided here.
 *
 * @param {Object} form the form element (DOM or jQuery) to use in the call
 * @param {Object} attributes jquery.form attributes object
 * @param {Function} callback function to call with the returned data
 */
function jsonp(form, attributes, callback) {
    attributes = attributes || {};
    var options = {jsonp: _.uniqueId('import_callback_')};
    window[options.jsonp] = function () {
        delete window[options.jsonp];
        callback.apply(null, arguments);
    };
    if ('data' in attributes) {
        _.extend(attributes.data, options);
    } else {
        _.extend(attributes, {data: options});
    }
    $(form).ajaxSubmit(attributes);
}

openerp.web.DataImport = openerp.web.Dialog.extend({
    template: 'ImportDataView',
    dialog_title: {toString: function () { return _t("Import Data"); }},
    init: function(parent, dataset){
        var self = this;
        this._super(parent, {});
        this.model = parent.model;
        this.fields = [];
        this.all_fields = [];
        this.fields_with_defaults = [];
        this.required_fields = null;

        var convert_fields = function (root, prefix) {
            prefix = prefix || '';
            _(root.fields).each(function (f) {
                self.all_fields.push(prefix + f.name);
                if (f.fields) {
                    convert_fields(f, prefix + f.id + '/');
                }
            });
        };
        this.ready  = $.Deferred.queue().then(function () {
            self.required_fields = _(self.fields).chain()
                .filter(function (field) {
                    return field.required &&
                           !_.include(self.fields_with_defaults, field.id); })
                .pluck('name')
                .value();
            convert_fields(self);
            self.all_fields.sort();
        });
    },
    start: function() {
        var self = this;
        this._super();
        this.open({
            buttons: [
                {text: _t("Close"), click: function() { self.destroy(); }},
                {text: _t("Import File"), click: function() { self.do_import(); }, 'class': 'oe-dialog-import-button'}
            ],
            close: function(event, ui) {
                self.destroy();
            }
        });
        this.toggle_import_button(false);
        this.$element.find('#csvfile').change(this.on_autodetect_data);
        this.$element.find('fieldset').change(this.on_autodetect_data);
        this.$element.delegate('fieldset legend', 'click', function() {
            $(this).parent().toggleClass('oe-closed');
        });
        this.ready.push(new openerp.web.DataSet(this, this.model).call(
            'fields_get', [], function (fields) {
                self.graft_fields(fields);
                self.ready.push(new openerp.web.DataSet(self, self.model)
                        .default_get(_.pluck(self.fields, 'id')).then(function (fields) {
                    _.each(fields, function(val, key) {
                        if (val) {
                            self.fields_with_defaults.push(key);
                        }
                    });
                })
            )
        }));
    },
    graft_fields: function (fields, parent, level) {
        parent = parent || this;
        level = level || 0;

        var self = this;
        if (level === 0) {
            parent.fields.push({
                id: 'id',
                name: 'id',
                string: _t('External ID'),
                required: false
            });
        }
        _(fields).each(function (field, field_name) {
            // Ignore spec for id field
            // Don't import function fields (function and related)
            if (field_name === 'id') {
                return;
            }
            // Skip if there's no state which could disable @readonly,
            // if a field is ever always readonly we can't import it
            if (field.readonly) {
                // no states at all
                if (_.isEmpty(field.states)) { return; }
                // no state altering @readonly
                if (!_.any(field.states, function (modifiers) {
                    return _(modifiers).chain().pluck(0).contains('readonly').value();
                })) { return; }
            }

            var f = {
                id: field_name,
                name: field_name,
                string: field.string,
                required: field.required
            };

            switch (field.type) {
            case 'many2many':
            case 'many2one':
                f.name += '/id';
                break;
            case 'one2many':
                f.name += '/id';
                f.fields = [];
                // only fetch sub-fields to a depth of 2 levels
                if (level < 2) {
                    self.ready.push(new openerp.web.DataSet(self, field.relation).call(
                        'fields_get', [], function (fields) {
                            self.graft_fields(fields, f, level+1);
                    }));
                }
                break;
            }
            parent.fields.push(f);
        });
    },
    toggle_import_button: function (newstate) {
    	openerp.web.dialog(this.$element, 'widget')
                .find('.oe-dialog-import-button')
                .button('option', 'disabled', !newstate);
    },
    do_import: function() {
        if(!this.$element.find('#csvfile').val()) { return; }
        var lines_to_skip = parseInt(this.$element.find('#csv_skip').val(), 10);
        var with_headers = this.$element.find('#file_has_headers').prop('checked');
        if (!lines_to_skip && with_headers) {
            lines_to_skip = 1;
        }
        var indices = [], fields = [];
        this.$element.find(".sel_fields").each(function(index, element) {
            var val = element.value;
            if (!val) {
                return;
            }
            indices.push(index);
            fields.push(val);
        });

        jsonp(this.$element.find('#import_data'), {
            url: '/web/import/import_data',
            data: {
                model: this.model,
                meta: JSON.stringify({
                    skip: lines_to_skip,
                    indices: indices,
                    fields: fields
                })
            }
        }, this.on_import_results);
    },
    on_autodetect_data: function() {
        if(!this.$element.find('#csvfile').val()) { return; }
        jsonp(this.$element.find('#import_data'), {
            url: '/web/import/detect_data'
        }, this.on_import_results);
    },
    on_import_results: function(results) {
        this.$element.find('#result').empty();
        var headers, result_node = this.$element.find("#result");

        if (results['error']) {
            result_node.append(QWeb.render('ImportView.error', {
                'error': results['error']}));
            this.$element.find('fieldset').removeClass('oe-closed');
            return;
        }
        if (results['success']) {
            if (this.getParent().getParent().active_view == "list") {
                this.getParent().reload_content();
            }
            this.destroy();
            return;
        }

        if (results['records']) {
            var lines_to_skip = parseInt(this.$element.find('#csv_skip').val(), 10),
                with_headers = this.$element.find('#file_has_headers').prop('checked');
            headers = with_headers ? results.records[0] : null;

            result_node.append(QWeb.render('ImportView.result', {
                'headers': headers,
                'records': lines_to_skip ? results.records.slice(lines_to_skip)
                          : with_headers ? results.records.slice(1)
                          : results.records
            }));
            this.$element.find('fieldset').addClass('oe-closed');
        }
        this.$element.find('form').removeClass('oe-import-no-result');

        this.$element.delegate('.oe-m2o-drop-down-button', 'click', function () {
            $(this).prev('input').focus();
        });

        var self = this;
        this.ready.then(function () {
            var $fields = self.$element.find('.sel_fields').bind('blur', function () {
                if (this.value && !_(self.all_fields).contains(this.value)) {
                    this.value = '';
                }
            }).autocomplete({
                minLength: 0,
                source: self.all_fields,
                change: self.on_check_field_values
            }).focus(function () {
                $(this).autocomplete('search');
            });
            // Column auto-detection
            _(headers).each(function (header, index) {
                var field_name = self.match_column_to_field(header);
                if (field_name) {
                    $fields.eq(index).val(field_name);
                }
            });
            self.on_check_field_values();
        });
    },
    /**
     * Returns the name of the field (nested) matching the provided column name
     *
     * @param {String} name column name to look for
     * @param {Array} [fields] fields to look into for the provided name
     * @returns {String|undefined}
     */
    match_column_to_field: function (name, fields) {
        fields = fields || this.fields;
        var f;
        f = _(fields).detect(function (field) {
            // TODO: levenshtein between header and field.string
            return field.name === name
                || field.string.toLowerCase() === name.toLowerCase();
        });
        if (f) { return f.name; }

        // if ``name`` is a path (o2m), we need to recurse through its .fields
        var index = name.indexOf('/');
        if (index === -1) { return undefined; }
        // Get the first path section, try to find the matching field
        var column_name = name.substring(0, index);
        f = _(fields).detect(function (field) {
            // field.name for o2m is $foo/id, so we want to match on id
            return field.id === column_name
                || field.string.toLowerCase() === column_name.toLowerCase()
        });
        if (!f) { return undefined; }

        // if we found a matching field for the first path section, recurse in
        // its own .fields to try and get the rest of the path matched
        var rest = this.match_column_to_field(
                name.substring(index+1), f.fields);
        if (!rest) { return undefined; }
        return f.id + '/' + rest;
    },
    /**
     * Looks through all the field selections, and tries to find if two
     * (or more) columns were matched to the same model field.
     *
     * Returns a map of the multiply-mapped fields to an array of offending
     * columns (not actually columns, but the inputs containing the same field
     * names).
     *
     * Also has the side-effect of marking the discovered inputs with the class
     * ``duplicate_fld``.
     *
     * @returns {Object<String, Array<String>>} map of duplicate field matches to same-valued inputs
     */
    find_duplicate_fields: function() {
        // Maps values to DOM nodes, in order to discover duplicates
        var values = {}, duplicates = {};
        this.$element.find(".sel_fields").each(function(index, element) {
            var value = element.value;
            var $element = $(element).removeClass('duplicate_fld');
            if (!value) { return; }

            if (!(value in values)) {
                values[value] = element;
            } else {
                var same_valued_field = values[value];
                if (value in duplicates) {
                    duplicates[value].push(element);
                } else {
                    duplicates[value] = [same_valued_field, element];
                }
                $element.add(same_valued_field).addClass('duplicate_fld');
            }
        });
        return duplicates;
    },
    on_check_field_values: function () {
        this.$element.find("#message, #msg").remove();

        var required_valid = this.check_required();

        var duplicates = this.find_duplicate_fields();
        if (_.isEmpty(duplicates)) {
            this.toggle_import_button(required_valid);
        } else {
            var $err = $('
Destination fields should only be selected once, some fields are selected more than once:
').insertBefore(this.$element.find('#result'));
            var $dupes = $('
').appendTo($err);
            _(duplicates).each(function(elements, value) {
                $('
').text(value).appendTo($dupes);
                _(elements).each(function(element) {
                    var cell = $(element).closest('td');
                    $('
').text(cell.parent().children().index(cell)).appendTo($dupes);
                });
            });
            this.toggle_import_button(false);
        }

    },
    check_required: function() {
        if (!this.required_fields.length) { return true; }

        var selected_fields = _(this.$element.find('.sel_fields').get()).chain()
            .pluck('value')
            .compact()
            .value();

        var missing_fields = _.difference(this.required_fields, selected_fields);
        if (missing_fields.length) {
            this.$element.find("#result").before('
*Required Fields are not selected : ' + missing_fields + '.
');
            return false;
        }
        return true;
    },
    destroy: function() {
        this.$element.remove();
        this._super();
    }
});
};
<commit_msg>[imp] refactored data_import.js<commit_after>openerp.web.data_import = function(instance) {
var QWeb = instance.web.qweb,
    _t = instance.web._t;
/**
 * Safari does not deal well at all with raw JSON data being returned. As a
 * result, we're going to cheat by using a pseudo-jsonp: instead of getting
 * JSON data in the iframe, we're getting a ``script`` tag which consists of a
 * function call and the returned data (the json dump).
 *
 * The function is an auto-generated name bound to ``window``, which calls
 * back into the callback provided here.
 *
 * @param {Object} form the form element (DOM or jQuery) to use in the call
 * @param {Object} attributes jquery.form attributes object
 * @param {Function} callback function to call with the returned data
 */
function jsonp(form, attributes, callback) {
    attributes = attributes || {};
    var options = {jsonp: _.uniqueId('import_callback_')};
    window[options.jsonp] = function () {
        delete window[options.jsonp];
        callback.apply(null, arguments);
    };
    if ('data' in attributes) {
        _.extend(attributes.data, options);
    } else {
        _.extend(attributes, {data: options});
    }
    $(form).ajaxSubmit(attributes);
}

instance.web.DataImport = instance.web.Dialog.extend({
    template: 'ImportDataView',
    dialog_title: {toString: function () { return _t("Import Data"); }},
    init: function(parent, dataset){
        var self = this;
        this._super(parent, {});
        this.model = parent.model;
        this.fields = [];
        this.all_fields = [];
        this.fields_with_defaults = [];
        this.required_fields = null;

        var convert_fields = function (root, prefix) {
            prefix = prefix || '';
            _(root.fields).each(function (f) {
                self.all_fields.push(prefix + f.name);
                if (f.fields) {
                    convert_fields(f, prefix + f.id + '/');
                }
            });
        };
        this.ready  = $.Deferred.queue().then(function () {
            self.required_fields = _(self.fields).chain()
                .filter(function (field) {
                    return field.required &&
                           !_.include(self.fields_with_defaults, field.id); })
                .pluck('name')
                .value();
            convert_fields(self);
            self.all_fields.sort();
        });
    },
    start: function() {
        var self = this;
        this._super();
        this.open({
            buttons: [
                {text: _t("Close"), click: function() { self.destroy(); }},
                {text: _t("Import File"), click: function() { self.do_import(); }, 'class': 'oe-dialog-import-button'}
            ],
            close: function(event, ui) {
                self.destroy();
            }
        });
        this.toggle_import_button(false);
        this.$element.find('#csvfile').change(this.on_autodetect_data);
        this.$element.find('fieldset').change(this.on_autodetect_data);
        this.$element.delegate('fieldset legend', 'click', function() {
            $(this).parent().toggleClass('oe-closed');
        });
        this.ready.push(new instance.web.DataSet(this, this.model).call(
            'fields_get', [], function (fields) {
                self.graft_fields(fields);
                self.ready.push(new instance.web.DataSet(self, self.model)
                        .default_get(_.pluck(self.fields, 'id')).then(function (fields) {
                    _.each(fields, function(val, key) {
                        if (val) {
                            self.fields_with_defaults.push(key);
                        }
                    });
                })
            )
        }));
    },
    graft_fields: function (fields, parent, level) {
        parent = parent || this;
        level = level || 0;

        var self = this;
        if (level === 0) {
            parent.fields.push({
                id: 'id',
                name: 'id',
                string: _t('External ID'),
                required: false
            });
        }
        _(fields).each(function (field, field_name) {
            // Ignore spec for id field
            // Don't import function fields (function and related)
            if (field_name === 'id') {
                return;
            }
            // Skip if there's no state which could disable @readonly,
            // if a field is ever always readonly we can't import it
            if (field.readonly) {
                // no states at all
                if (_.isEmpty(field.states)) { return; }
                // no state altering @readonly
                if (!_.any(field.states, function (modifiers) {
                    return _(modifiers).chain().pluck(0).contains('readonly').value();
                })) { return; }
            }

            var f = {
                id: field_name,
                name: field_name,
                string: field.string,
                required: field.required
            };

            switch (field.type) {
            case 'many2many':
            case 'many2one':
                f.name += '/id';
                break;
            case 'one2many':
                f.name += '/id';
                f.fields = [];
                // only fetch sub-fields to a depth of 2 levels
                if (level < 2) {
                    self.ready.push(new instance.web.DataSet(self, field.relation).call(
                        'fields_get', [], function (fields) {
                            self.graft_fields(fields, f, level+1);
                    }));
                }
                break;
            }
            parent.fields.push(f);
        });
    },
    toggle_import_button: function (newstate) {
    	instance.web.dialog(this.$element, 'widget')
                .find('.oe-dialog-import-button')
                .button('option', 'disabled', !newstate);
    },
    do_import: function() {
        if(!this.$element.find('#csvfile').val()) { return; }
        var lines_to_skip = parseInt(this.$element.find('#csv_skip').val(), 10);
        var with_headers = this.$element.find('#file_has_headers').prop('checked');
        if (!lines_to_skip && with_headers) {
            lines_to_skip = 1;
        }
        var indices = [], fields = [];
        this.$element.find(".sel_fields").each(function(index, element) {
            var val = element.value;
            if (!val) {
                return;
            }
            indices.push(index);
            fields.push(val);
        });

        jsonp(this.$element.find('#import_data'), {
            url: '/web/import/import_data',
            data: {
                model: this.model,
                meta: JSON.stringify({
                    skip: lines_to_skip,
                    indices: indices,
                    fields: fields
                })
            }
        }, this.on_import_results);
    },
    on_autodetect_data: function() {
        if(!this.$element.find('#csvfile').val()) { return; }
        jsonp(this.$element.find('#import_data'), {
            url: '/web/import/detect_data'
        }, this.on_import_results);
    },
    on_import_results: function(results) {
        this.$element.find('#result').empty();
        var headers, result_node = this.$element.find("#result");

        if (results['error']) {
            result_node.append(QWeb.render('ImportView.error', {
                'error': results['error']}));
            this.$element.find('fieldset').removeClass('oe-closed');
            return;
        }
        if (results['success']) {
            if (this.getParent().getParent().active_view == "list") {
                this.getParent().reload_content();
            }
            this.destroy();
            return;
        }

        if (results['records']) {
            var lines_to_skip = parseInt(this.$element.find('#csv_skip').val(), 10),
                with_headers = this.$element.find('#file_has_headers').prop('checked');
            headers = with_headers ? results.records[0] : null;

            result_node.append(QWeb.render('ImportView.result', {
                'headers': headers,
                'records': lines_to_skip ? results.records.slice(lines_to_skip)
                          : with_headers ? results.records.slice(1)
                          : results.records
            }));
            this.$element.find('fieldset').addClass('oe-closed');
        }
        this.$element.find('form').removeClass('oe-import-no-result');

        this.$element.delegate('.oe-m2o-drop-down-button', 'click', function () {
            $(this).prev('input').focus();
        });

        var self = this;
        this.ready.then(function () {
            var $fields = self.$element.find('.sel_fields').bind('blur', function () {
                if (this.value && !_(self.all_fields).contains(this.value)) {
                    this.value = '';
                }
            }).autocomplete({
                minLength: 0,
                source: self.all_fields,
                change: self.on_check_field_values
            }).focus(function () {
                $(this).autocomplete('search');
            });
            // Column auto-detection
            _(headers).each(function (header, index) {
                var field_name = self.match_column_to_field(header);
                if (field_name) {
                    $fields.eq(index).val(field_name);
                }
            });
            self.on_check_field_values();
        });
    },
    /**
     * Returns the name of the field (nested) matching the provided column name
     *
     * @param {String} name column name to look for
     * @param {Array} [fields] fields to look into for the provided name
     * @returns {String|undefined}
     */
    match_column_to_field: function (name, fields) {
        fields = fields || this.fields;
        var f;
        f = _(fields).detect(function (field) {
            // TODO: levenshtein between header and field.string
            return field.name === name
                || field.string.toLowerCase() === name.toLowerCase();
        });
        if (f) { return f.name; }

        // if ``name`` is a path (o2m), we need to recurse through its .fields
        var index = name.indexOf('/');
        if (index === -1) { return undefined; }
        // Get the first path section, try to find the matching field
        var column_name = name.substring(0, index);
        f = _(fields).detect(function (field) {
            // field.name for o2m is $foo/id, so we want to match on id
            return field.id === column_name
                || field.string.toLowerCase() === column_name.toLowerCase()
        });
        if (!f) { return undefined; }

        // if we found a matching field for the first path section, recurse in
        // its own .fields to try and get the rest of the path matched
        var rest = this.match_column_to_field(
                name.substring(index+1), f.fields);
        if (!rest) { return undefined; }
        return f.id + '/' + rest;
    },
    /**
     * Looks through all the field selections, and tries to find if two
     * (or more) columns were matched to the same model field.
     *
     * Returns a map of the multiply-mapped fields to an array of offending
     * columns (not actually columns, but the inputs containing the same field
     * names).
     *
     * Also has the side-effect of marking the discovered inputs with the class
     * ``duplicate_fld``.
     *
     * @returns {Object<String, Array<String>>} map of duplicate field matches to same-valued inputs
     */
    find_duplicate_fields: function() {
        // Maps values to DOM nodes, in order to discover duplicates
        var values = {}, duplicates = {};
        this.$element.find(".sel_fields").each(function(index, element) {
            var value = element.value;
            var $element = $(element).removeClass('duplicate_fld');
            if (!value) { return; }

            if (!(value in values)) {
                values[value] = element;
            } else {
                var same_valued_field = values[value];
                if (value in duplicates) {
                    duplicates[value].push(element);
                } else {
                    duplicates[value] = [same_valued_field, element];
                }
                $element.add(same_valued_field).addClass('duplicate_fld');
            }
        });
        return duplicates;
    },
    on_check_field_values: function () {
        this.$element.find("#message, #msg").remove();

        var required_valid = this.check_required();

        var duplicates = this.find_duplicate_fields();
        if (_.isEmpty(duplicates)) {
            this.toggle_import_button(required_valid);
        } else {
            var $err = $('
Destination fields should only be selected once, some fields are selected more than once:
').insertBefore(this.$element.find('#result'));
            var $dupes = $('
').appendTo($err);
            _(duplicates).each(function(elements, value) {
                $('
').text(value).appendTo($dupes);
                _(elements).each(function(element) {
                    var cell = $(element).closest('td');
                    $('
').text(cell.parent().children().index(cell)).appendTo($dupes);
                });
            });
            this.toggle_import_button(false);
        }

    },
    check_required: function() {
        if (!this.required_fields.length) { return true; }

        var selected_fields = _(this.$element.find('.sel_fields').get()).chain()
            .pluck('value')
            .compact()
            .value();

        var missing_fields = _.difference(this.required_fields, selected_fields);
        if (missing_fields.length) {
            this.$element.find("#result").before('
*Required Fields are not selected : ' + missing_fields + '.
');
            return false;
        }
        return true;
    },
    destroy: function() {
        this.$element.remove();
        this._super();
    }
});
};
<|endoftext|>