<commit_before>function addZero(num) {
    return (parseInt(num, 10) < 10) ? "0" + num : num;
}

var calendar1 = $("#calendar-1").calendar(),
    showEvent = jasmine.createSpy('showEvent'),
    hideEvent = jasmine.createSpy('hideEvent'),
    readyEvent = jasmine.createSpy('readyEvent'),
    selectEvent = jasmine.createSpy('selectEvent'),
    nextEvent = jasmine.createSpy('nextEvent'),
    prevEvent = jasmine.createSpy('prevEvent'),
    DATE = (function(){
        var date = new Date(),
            TODAY =  {
                'day': addZero(date.getDate()),
                'month': addZero((date.getMonth() + 1)),
                'year': date.getFullYear()
            },
            FORMAT = {
                'ddmmyyyy': [TODAY.day, TODAY.month, TODAY.year].join('/'),
                'yyyymmdd': [TODAY.year, TODAY.month, TODAY.day].join('/')
            };
        return {
            'TODAY': TODAY,
            'FORMAT': FORMAT
        }
    })();

describe('Calendar', function () {
    calendar1
        .on('ready', function () { readyEvent(); })
        .on('select', function () { selectEvent(); });

    it('should be defined on ch object', function () {
        expect(ch.hasOwnProperty('Calendar')).toBeTruthy();
        expect(typeof ch.Calendar).toEqual('function');
    });

    it('should be defined on $ object', function () {
        expect($.fn.hasOwnProperty('calendar')).toBeTruthy();
        expect(typeof $.fn.calendar).toEqual('function');
    });

    it('should be return a new instance', function () {
        expect(calendar1 instanceof ch.Calendar).toBeTruthy();
    });

    it('should emit the "ready" event when it\'s created', function () {
        waits(50);
        runs(function () {
            expect(readyEvent).toHaveBeenCalled();
        });
    });

    describe('should use the following abilities:', function () {
        it('EventEmitter', function () {
            expect(calendar1.on).not.toEqual(undefined);
            expect(typeof calendar1.on).toEqual('function');
        });
    });
});

describe('It should have the following public properties:', function () {

    it('.el', function () {
        expect(calendar1.el).not.toEqual(undefined);
        expect(calendar1.el.nodeType).toEqual(1);
    });

    it('.$el', function () {
        expect(calendar1.$el).not.toEqual(undefined);
        expect(calendar1.$el instanceof $).toBeTruthy();
    });

    it('.name', function () {
        expect(calendar1.name).not.toEqual(undefined);
        expect(typeof calendar1.name).toEqual('string');
        expect(calendar1.name).toEqual('calendar');
    });

    it('.constructor', function () {
        expect(calendar1.constructor).not.toEqual(undefined);
        expect(typeof calendar1.constructor).toEqual('function');
    });

    it('.uid', function () {
        expect(calendar1.uid).not.toEqual(undefined);
        expect(typeof calendar1.uid).toEqual('number');
    });

});

describe('It should have the following public methods:', function () {
    var methods = ['init', 'destroy', 'from', 'to', 'next', 'prev', 'select', 'today', 'enable', 'disable'],
        i = 0,
        len = methods.length;

    for (i; i < len; i += 1) {
        (function (i){
            it('.' + methods[i] + '()', function () {
                expect(calendar1[methods[i]]).not.toEqual(undefined);
                expect(typeof calendar1[methods[i]]).toEqual('function');
            });
        }(i));
    }
});

describe('It should have an element/container and', function () {
    var $el = calendar1.$el;

    it('should exist', function () {
        expect($el).not.toEqual(undefined);
        expect($el[0].nodeType).toEqual(1);
        expect($el instanceof $).toBeTruthy();
    });

    describe('should have the following class names:', function () {

        it('.ch-calenaar', function () {
            expect($el.hasClass('ch-calendar')).toBeTruthy();
        });

        it('.ch-user-no-select', function () {
            expect($el.hasClass('ch-user-no-select')).toBeTruthy();
        });
    });

    describe('should have got a next button', function () {
        var $month = $el.find('.ch-calendar-next');

        it('it should have got the ".ch-calendar-next" class name', function () {
            expect($month.length).toEqual(1);
        });

        it('it should have got the WAI-ARIA" role "button"', function () {
            expect($month.attr('role')).toEqual('button');
        });

        it('should have got the WAI-ARIA attribute "aria-hidden" in "false"', function () {
           expect($month.attr('aria-hidden')).toEqual('false');
        });

        it('should have got the WAI-ARIA attribute "aria-controls" in "ch-calendar-grid-1"', function () {
           expect($month.attr('aria-controls')).toEqual('ch-calendar-grid-1');
        });
    });

    describe('should have got a prev button', function () {
        var $month = $el.find('.ch-calendar-prev');

        it('it should have got the ".ch-calendar-prev" class name', function () {
            expect($month.length).toEqual(1);
        });

        it('it should have got the WAI-ARIA" role "button"', function () {
            expect($month.attr('role')).toEqual('button');
        });

        it('it should have got the WAI-ARIA attribute "aria-hidden" in "false"', function () {
           expect($month.attr('aria-hidden')).toEqual('false');
        });

        it('it should have got the WAI-ARIA attribute "aria-controls" in "ch-calendar-grid-1"', function () {
           expect($month.attr('aria-controls')).toEqual('ch-calendar-grid-1');
        });
    });

    describe('should have got a calendar table', function () {

        var $table = $el.find('.ch-calendar-month');

        it('it should have got the ".ch-calendar-prev" class name', function () {
            expect($table.length).toEqual(1);
        });

        it('it should have got the WAI-ARIA" role "grid"', function () {
            expect($table.attr('role')).toEqual('grid');
        });

        it('it should have got the weeks of the month', function () {
            var weeks = $table.find('tbody tr');
            weeks.each(function(i, week){
                expect( $(week).hasClass('ch-calendar-week') ).toBeTruthy();
            });
        });

        it('it should have got the days', function () {
            var days = $el.find('tbody td');
            days.each(function(i, day){
                var $day = $(day);
                var dayNum = parseInt($day.text());
                if( !isNaN(dayNum) && (dayNum >= 1 || dayNum <= 31)) {
                    expect($day.hasClass('ch-calendar-day')).toBeTruthy();
                }
            });
        });

        it('it should have got the day of today', function () {
            var date = new Date(),
                $todayElement = $('.ch-calendar-today', $el);
            expect($todayElement.length ).toEqual(1);
            expect(parseInt($todayElement.text())).toEqual(date.getDate());
        });

    });

});

describe('Its select() method', function () {

    it('should emit the "select" event', function () {
        calendar1.select();
        expect(selectEvent).toHaveBeenCalled();
    });

    it('should return "undefined" when it hasn\'t got a date selected', function () {
        expect(calendar1.select()).not.toBeDefined();
    });

    it('should set a date as calendar.select(\'2013/01/07\')', function () {
        calendar1.select(DATE.FORMAT['yyyymmdd']);
        expect(calendar1.select()).toEqual(DATE.FORMAT['ddmmyyyy']);
    });
});

describe('Its next() method', function(){
    var current = calendar1.$el.find('caption').text(),
        next;

    it('should show the next month', function () {
        calendar1.next('month');

        next = calendar1.$el.find('caption').text();
        expect(current).not.toEqual(next);
    });

    it('should show the next year', function () {

        calendar1.next('year');

        next = calendar1.$el.find('caption').text();
        expect(current).not.toEqual(next);
    });
});

describe('Its prev() method', function(){
    var current = calendar1.$el.find('caption').text(),
        prev;

    it('should show the previous month', function () {
        calendar1.prev('month');

        prev = calendar1.$el.find('caption').text();
        expect(current).not.toEqual(prev);
    });

    it('should show the previous year', function () {
        calendar1.prev('year');

        prev = calendar1.$el.find('caption').text();
        expect(current).toEqual(prev);
    });
});

describe('Its from() method', function () {
    it('should do dates unselectable with class "ch-calendar-disabled"', function () {
        calendar1.select('today');
        calendar1.from(DATE.FORMAT['yyyymmdd']);
        // This test fails because the is not refreshing the table after the from method is executed

        var days = calendar1.$el.find('tbody td');

        days.each(function (i, day) {
            var $day = $(day),
                dayNum = parseInt($day.text());

            if (dayNum < DATE.TODAY.day) {
                expect($day.hasClass('ch-calendar-disabled')).toBeTruthy();
            }
        });
    });

    it('should throw an error when date is set as a invalid format calendar.from(\'22/10/2012\').', function () {
        expect(function(){ calendar4.from('20/10/2012'); }).toThrow();
    });
});

describe('Its to() method', function(){
    it('should do dates unselectable with class "ch-calendar-disabled".', function () {

        // select the day of tody today
        calendar1.select('today');

        calendar1.to(DATE.TODAY.year + 1 + '/' + DATE.TODAY.month + '/' + DATE.TODAY.day);
        calendar1.next('year');
        // This test fails because the is not refreshing the table after the to method is executed

        var days = calendar1.$el.find('tbody td');

        days.each(function(i, day){
            var $day = $(day),
                dayNum = parseInt($day.text());

            if (dayNum > DATE.TODAY.day) {
                expect($day.hasClass('ch-calendar-disabled')).toBeTruthy();
            }

        });
    });

    it('should throw an error when date is set as a invalid format calendar.to(\'22/10/2012\').', function () {
        expect(function(){ calendar4.to('25/10/2012'); }).toThrow();
    });
});

describe('Its today() method', function(){
    it('should return the current date in the pre configured format DD/MM/YYYY or YYYY/MM/DD', function () {
        expect(calendar1.today()).toEqual(DATE.FORMAT.ddmmyyyy);
    });
});<commit_msg>#987 Calendar: Update tests.<commit_after>function addZero(num) {
    return (parseInt(num, 10) < 10) ? "0" + num : num;
}

var calendar1 = $("#calendar-1").calendar(),
    readyEvent = jasmine.createSpy('readyEvent'),
    selectEvent = jasmine.createSpy('selectEvent'),
    nextmonthEvent = jasmine.createSpy('nextmonthEvent'),
    prevmonthEvent = jasmine.createSpy('prevmonthEvent'),
    nextyearEvent = jasmine.createSpy('nextYearEvent'),
    prevyearEvent = jasmine.createSpy('prevYearEvent'),
    DATE = (function(){
        var date = new Date(),
            TODAY =  {
                'day': addZero(date.getDate()),
                'month': addZero((date.getMonth() + 1)),
                'year': date.getFullYear()
            },
            FORMAT = {
                'ddmmyyyy': [TODAY.day, TODAY.month, TODAY.year].join('/'),
                'yyyymmdd': [TODAY.year, TODAY.month, TODAY.day].join('/')
            };
        return {
            'TODAY': TODAY,
            'FORMAT': FORMAT
        }
    })();

describe('Calendar', function () {
    calendar1
        .on('ready', function () { readyEvent(); })
        .on('select', function () { selectEvent(); })
        .on('nextmonth', function () { nextmonthEvent(); })
        .on('prevmonth', function () { prevmonthEvent(); })
        .on('nextyear', function () { nextyearEvent(); })
        .on('prevyear', function () { prevyearEvent(); });

    it('should be defined on ch object', function () {
        expect(ch.hasOwnProperty('Calendar')).toBeTruthy();
        expect(typeof ch.Calendar).toEqual('function');
    });

    it('should be defined on $ object', function () {
        expect($.fn.hasOwnProperty('calendar')).toBeTruthy();
        expect(typeof $.fn.calendar).toEqual('function');
    });

    it('should be return a new instance', function () {
        expect(calendar1 instanceof ch.Calendar).toBeTruthy();
    });

    it('should emit the "ready" event when it\'s created', function () {
        waits(50);
        runs(function () {
            expect(readyEvent).toHaveBeenCalled();
        });
    });

    describe('should use the following abilities:', function () {
        it('EventEmitter', function () {
            expect(calendar1.on).not.toEqual(undefined);
            expect(typeof calendar1.on).toEqual('function');
        });
    });
});

describe('It should have the following public properties:', function () {

    it('.el', function () {
        expect(calendar1.el).not.toEqual(undefined);
        expect(calendar1.el.nodeType).toEqual(1);
    });

    it('.$el', function () {
        expect(calendar1.$el).not.toEqual(undefined);
        expect(calendar1.$el instanceof $).toBeTruthy();
    });

    it('.name', function () {
        expect(calendar1.name).not.toEqual(undefined);
        expect(typeof calendar1.name).toEqual('string');
        expect(calendar1.name).toEqual('calendar');
    });

    it('.constructor', function () {
        expect(calendar1.constructor).not.toEqual(undefined);
        expect(typeof calendar1.constructor).toEqual('function');
    });

    it('.uid', function () {
        expect(calendar1.uid).not.toEqual(undefined);
        expect(typeof calendar1.uid).toEqual('number');
    });

});

describe('It should have the following public methods:', function () {
    var methods = ['init', 'destroy', 'setFrom', 'setTo', 'nextMonth', 'nextYear', 'prevMonth', 'prevYear', 'select', 'getToday', 'enable', 'disable'],
        i = 0,
        len = methods.length;

    for (i; i < len; i += 1) {
        (function (i){
            it('.' + methods[i] + '()', function () {
                expect(calendar1[methods[i]]).not.toEqual(undefined);
                expect(typeof calendar1[methods[i]]).toEqual('function');
            });
        }(i));
    }
});

describe('It should have an element/container and', function () {
    var $el = calendar1.$el;

    it('should exist', function () {
        expect($el).not.toEqual(undefined);
        expect($el[0].nodeType).toEqual(1);
        expect($el instanceof $).toBeTruthy();
    });

    describe('should have the following class names:', function () {

        it('.ch-calenaar', function () {
            expect($el.hasClass('ch-calendar')).toBeTruthy();
        });

        it('.ch-user-no-select', function () {
            expect($el.hasClass('ch-user-no-select')).toBeTruthy();
        });
    });

    describe('should have got a next button', function () {
        var $month = $el.find('.ch-calendar-next');

        it('it should have got the ".ch-calendar-next" class name', function () {
            expect($month.length).toEqual(1);
        });

        it('it should have got the WAI-ARIA" role "button"', function () {
            expect($month.attr('role')).toEqual('button');
        });

        it('should have got the WAI-ARIA attribute "aria-hidden" in "false"', function () {
           expect($month.attr('aria-hidden')).toEqual('false');
        });

        it('should have got the WAI-ARIA attribute "aria-controls" in "ch-calendar-grid-1"', function () {
           expect($month.attr('aria-controls')).toEqual('ch-calendar-grid-1');
        });
    });

    describe('should have got a prev button', function () {
        var $month = $el.find('.ch-calendar-prev');

        it('it should have got the ".ch-calendar-prev" class name', function () {
            expect($month.length).toEqual(1);
        });

        it('it should have got the WAI-ARIA" role "button"', function () {
            expect($month.attr('role')).toEqual('button');
        });

        it('it should have got the WAI-ARIA attribute "aria-hidden" in "false"', function () {
           expect($month.attr('aria-hidden')).toEqual('false');
        });

        it('it should have got the WAI-ARIA attribute "aria-controls" in "ch-calendar-grid-1"', function () {
           expect($month.attr('aria-controls')).toEqual('ch-calendar-grid-1');
        });
    });

    describe('should have got a calendar table', function () {

        var $table = $el.find('.ch-calendar-month');

        it('it should have got the ".ch-calendar-prev" class name', function () {
            expect($table.length).toEqual(1);
        });

        it('it should have got the WAI-ARIA" role "grid"', function () {
            expect($table.attr('role')).toEqual('grid');
        });

        it('it should have got the weeks of the month', function () {
            var weeks = $table.find('tbody tr');
            weeks.each(function(i, week){
                expect( $(week).hasClass('ch-calendar-week') ).toBeTruthy();
            });
        });

        it('it should have got the days', function () {
            var days = $el.find('tbody td');
            days.each(function(i, day){
                var $day = $(day);
                var dayNum = parseInt($day.text());
                if( !isNaN(dayNum) && (dayNum >= 1 || dayNum <= 31)) {
                    expect($day.hasClass('ch-calendar-day')).toBeTruthy();
                }
            });
        });

        it('it should have got the day of today', function () {
            var date = new Date(),
                $todayElement = $('.ch-calendar-today', $el);
            expect($todayElement.length ).toEqual(1);
            expect(parseInt($todayElement.text())).toEqual(date.getDate());
        });

    });

});

describe('Its select() method', function () {
    it('should return "undefined" when it hasn\'t got a date selected', function () {
        expect(calendar1.select()).not.toBeDefined();
    });

    it('should set a date as calendar.select(\'yyyy/mm/dd\')', function () {
        calendar1.select(DATE.FORMAT['yyyymmdd']);
        expect(calendar1.select()).toEqual(DATE.FORMAT['ddmmyyyy']);
    });

    it('should emit the "select" event', function () {
        calendar1.select();
        expect(selectEvent).toHaveBeenCalled();
    });
});

describe('Its nextMonth() method', function(){
    var instance;

    it('should show the next month', function () {
        var month = calendar1._dates.current.month,
            next;

        instance = calendar1.nextMonth();
        next = calendar1._dates.current.month;

        expect(month + 1).toEqual(next);
    });

    it('should emit the "nextmonth" event', function () {
        expect(nextmonthEvent).toHaveBeenCalled();
    });

    it('should return the same instance than initialized widget', function () {
        expect(instance).toEqual(calendar1);
    });
});

describe('Its prevMonth() method', function(){
    var instance;

    it('should show the previous month', function () {
        var month = calendar1._dates.current.month,
            prev;

        instance = calendar1.prevMonth();
        prev = calendar1._dates.current.month;

        expect(month - 1).toEqual(prev);
    });

    it('should emit the "prevmonth" event', function () {
        expect(prevmonthEvent).toHaveBeenCalled();
    });

    it('should return the same instance than initialized widget', function () {
        expect(instance).toEqual(calendar1);
    });
});

describe('Its nextYear() method', function(){
    var instance;

    it('should show the next year', function () {
        var current = calendar1._dates.current.year,
            next;

        instance = calendar1.nextYear();
        next = calendar1._dates.current.year;

        expect(current + 1).toEqual(next);
    });

    it('should emit the "nextyear" event', function () {
        expect(nextyearEvent).toHaveBeenCalled();
    });

    it('should return the same instance than initialized widget', function () {
        expect(instance).toEqual(calendar1);
    });
});

describe('Its prevYear() method', function(){
    var instance;

    it('should show the previous month', function () {
        var current = calendar1._dates.current.year,
            prev;

        instance = calendar1.prevYear();
        prev = calendar1._dates.current.year;

        expect(current - 1).toEqual(prev);
    });

    it('should emit the "prevyear" event', function () {
        expect(prevyearEvent).toHaveBeenCalled();
    });

    it('should return the same instance than initialized widget', function () {
        expect(instance).toEqual(calendar1);
    });
});

describe('Its setFrom() method', function () {
    var instance;

    it('should do dates unselectable with class "ch-calendar-disabled"', function () {

        calendar1.select('today');

        instance = calendar1.setFrom(DATE.FORMAT['yyyymmdd']);

        // This test fails because the is not refreshing the table after the from method is executed
        var days = calendar1.$el.find('tbody td');

        days.each(function (i, day) {
            var $day = $(day),
                dayNum = parseInt($day.text());

            if (dayNum < DATE.TODAY.day) {
                expect($day.hasClass('ch-calendar-disabled')).toBeTruthy();
            }
        });
    });

    it('should remove the "from" date if receive \'reset\' as parameter', function () {
        calendar1.setFrom('reset');
        expect(calendar1._hasPrevMonth()).toBeTruthy();
    });

    it('should throw an error when date is set as a invalid format calendar.from(\'DD/MM/YYYY\').', function () {
        expect(function(){ calendar4.from('20/10/2012'); }).toThrow();
    });

    it('should return the same instance than initialized widget', function () {
        expect(instance).toEqual(calendar1);
    });
});

describe('Its setTo() method', function(){
    var instance;

    it('should do dates unselectable with class "ch-calendar-disabled".', function () {

        // select the day of tody today
        calendar1.select('today');

        instance = calendar1.setTo(DATE.TODAY.year + 1 + '/' + DATE.TODAY.month + '/' + DATE.TODAY.day);

        calendar1.nextYear();
        // This test fails because the is not refreshing the table after the to method is executed

        var days = calendar1.$el.find('tbody td');

        days.each(function(i, day){
            var $day = $(day),
                dayNum = parseInt($day.text());

            if (dayNum > DATE.TODAY.day) {
                expect($day.hasClass('ch-calendar-disabled')).toBeTruthy();
            }

        });
    });

    it('should remove the "to" date if receive \'reset\' as parameter', function () {
        calendar1.setTo('reset');
        expect(calendar1._hasNextMonth()).toBeTruthy();
    });

    it('should throw an error when date is set as a invalid format calendar.to(\'DD/MM/YYYY\').', function () {
        expect(function(){ calendar4.to('25/10/2012'); }).toThrow();
    });

    it('should return the same instance than initialized widget', function () {
        expect(instance).toEqual(calendar1);
    });
});

describe('Its getToday() method', function(){
    it('should return the current date in the pre configured format DD/MM/YYYY or YYYY/MM/DD', function () {
        expect(calendar1.getToday()).toEqual(DATE.FORMAT.ddmmyyyy);
    });
});<|endoftext|>


<commit_before>// Window state (size and position) preservation between app launches.
(function () {
    'use strict';

    var gui = require('nw.gui');
    var win = gui.Window.get();
    var state;
    var currMode = 'normal';
    var maximizationJustHappened = false;
    var saveTimeout;

    var init = function () {
        try {
            state = JSON.parse(localStorage.windowState);

            // Make sure the window is in-bounds of the screen.
            if (state.x < 0 ||
                state.y < 0 ||
                state.width > screen.width ||
                state.height > screen.height) {
                throw "Window out of bounds.";
            }

            // Restore saved window state.
            currMode = state.mode;
            win.resizeTo(state.width, state.height);
            win.moveTo(state.x, state.y);
            if (currMode === 'maximized') {
                // Have to delay maximization a little to let NW set
                // the normal x,y,width,height of the window,
                // so we can unmaximize to that state later on.
                setTimeout(function () {
                    win.maximize();
                }, 100);
            }
        } catch (err) {
            // There was no data, or data has been corrupted.
            // Start from scratch with safe defaults.
            state = {
                mode: currMode,
                x: win.x,
                y: win.y,
                width: win.width,
                height: win.height
            };
        }

        win.show();
    };

    // We are delaying save for one second to be sure window state
    // has "stabilized" (order of events is sometimes unreliable,
    // and we can save some junk otherwise).
    var scheduleSave = function () {
        clearTimeout(saveTimeout);
        saveTimeout = setTimeout(save, 1000);
    };

    var save = function () {
        if (currMode === 'minimized') {
            // Don't save minimized state.
            return;
        }
        if (currMode === 'normal') {
            // Update window dimensions only if in normal mode.
            state.x = win.x;
            state.y = win.y;
            state.width = win.width;
            state.height = win.height;
        }
        state.mode = currMode;
        localStorage.windowState = JSON.stringify(state);
    };

    init();

    win.on('maximize', function () {
        maximizationJustHappened = true;
        setTimeout(function () {
            // Switch it back after 0.5 sec.
            maximizationJustHappened = false;
        }, 500);
        currMode = 'maximized';
        scheduleSave();
    });

    win.on('unmaximize', function () {
        currMode = 'normal';
        scheduleSave();
    });

    win.on('minimize', function () {
        currMode = 'minimized';
        // Don't save minimized state.
    });

    win.on('restore', function () {
        currMode = 'normal';
        scheduleSave();
    });

    win.on('resize', function () {
        if (!maximizationJustHappened && currMode === 'maximized') {
            // On OSX you can resize maximized window, so it is no longer maximized.
            currMode = 'normal';
        }
        scheduleSave();
    });

    win.on('move', function () {
        scheduleSave();
    });

}());<commit_msg>Added info about bugfix<commit_after>// Window state (size and position) preservation between app launches.
(function () {
    'use strict';

    var gui = require('nw.gui');
    var win = gui.Window.get();
    var state;
    var currMode = 'normal';
    var maximizationJustHappened = false;
    var saveTimeout;

    var init = function () {
        try {
            state = JSON.parse(localStorage.windowState);

            // Make sure the window is in-bounds of the screen.
            if (state.x < 0 ||
                state.y < 0 ||
                state.width > screen.width ||
                state.height > screen.height) {
                throw "Window out of bounds.";
            }

            // Restore saved window state.
            currMode = state.mode;
            win.resizeTo(state.width, state.height);
            win.moveTo(state.x, state.y);
            if (currMode === 'maximized') {
                // Have to delay maximization a little to let NW set
                // the normal x,y,width,height of the window,
                // so we can unmaximize to that state later on.
                // It also prevents bug https://github.com/rogerwang/node-webkit/issues/1105
                setTimeout(function () {
                    win.maximize();
                }, 100);
            }
        } catch (err) {
            // There was no data, or data has been corrupted.
            // Start from scratch with safe defaults.
            state = {
                mode: currMode,
                x: win.x,
                y: win.y,
                width: win.width,
                height: win.height
            };
        }

        win.show();
    };

    // We are delaying save for one second to be sure window state
    // has "stabilized" (order of events is sometimes unreliable,
    // and we can save some junk otherwise).
    var scheduleSave = function () {
        clearTimeout(saveTimeout);
        saveTimeout = setTimeout(save, 1000);
    };

    var save = function () {
        if (currMode === 'minimized') {
            // Don't save minimized state.
            return;
        }
        if (currMode === 'normal') {
            // Update window dimensions only if in normal mode.
            state.x = win.x;
            state.y = win.y;
            state.width = win.width;
            state.height = win.height;
        }
        state.mode = currMode;
        localStorage.windowState = JSON.stringify(state);
    };

    init();

    win.on('maximize', function () {
        maximizationJustHappened = true;
        setTimeout(function () {
            // Switch it back after 0.5 sec.
            maximizationJustHappened = false;
        }, 500);
        currMode = 'maximized';
        scheduleSave();
    });

    win.on('unmaximize', function () {
        currMode = 'normal';
        scheduleSave();
    });

    win.on('minimize', function () {
        currMode = 'minimized';
        // Don't save minimized state.
    });

    win.on('restore', function () {
        currMode = 'normal';
        scheduleSave();
    });

    win.on('resize', function () {
        if (!maximizationJustHappened && currMode === 'maximized') {
            // On OSX you can resize maximized window, so it is no longer maximized.
            currMode = 'normal';
        }
        scheduleSave();
    });

    win.on('move', function () {
        scheduleSave();
    });

}());<|endoftext|>


<commit_before>import RESTAdapter from '@ember-data/adapter/rest';

export default RESTAdapter.extend({
  namespace: 'api/v1',
});
<commit_msg>Auto merge of #2048 - Turbo87:fastboot-ua, r=locks<commit_after>import RESTAdapter from '@ember-data/adapter/rest';
import { inject as service } from '@ember/service';
import { computed } from '@ember/object';

export default RESTAdapter.extend({
  fastboot: service(),

  namespace: 'api/v1',

  headers: computed('fastboot.{isFastBoot,request.headers}', function() {
    if (this.fastboot.isFastBoot) {
      return { 'User-Agent': this.fastboot.request.headers.get('User-Agent') };
    }

    return {};
  }),
});
<|endoftext|>


<commit_before>define([],
  function() {

    function toType(obj) {
      // summary:
      //   A better type then Object.toString() or typeof.
      // decription:
      //      toType(undefined); //"undefined"
      //      toType(new); //"null"
      //      toType({a: 4}); //"object"
      //      toType([1, 2, 3]); //"array"
      //      (function() {console.log(toType(arguments))})(); //arguments
      //      toType(new ReferenceError); //"error"
      //      toType(new Date); //"date"
      //      toType(/a-z/); //"regexp"
      //      toType(Math); //"math"
      //      toType(JSON); //"json"
      //      toType(new Number(4)); //"number"
      //      toType(new String("abc")); //"string"
      //      toType(new Boolean(true)); //"boolean"

      /* based on
       http://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/
       */

      return Object.prototype.toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase();  // return String
    }

    return toType;
  }
);
<commit_msg>global is an object too<commit_after>define([],
  function() {

    function toType(obj) {
      // summary:
      //   A better type then Object.toString() or typeof.
      // description:
      //      toType(undefined); //"undefined"
      //      toType(new); //"null"
      //      toType({a: 4}); //"object"
      //      toType([1, 2, 3]); //"array"
      //      (function() {console.log(toType(arguments))})(); //arguments
      //      toType(new ReferenceError); //"error"
      //      toType(new Date); //"date"
      //      toType(/a-z/); //"regexp"
      //      toType(Math); //"math"
      //      toType(JSON); //"json"
      //      toType(new Number(4)); //"number"
      //      toType(new String("abc")); //"string"
      //      toType(new Boolean(true)); //"boolean"

      /* based on
       http://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/
       */

      var result = Object.prototype.toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase();
      // on some browsers, the main window returns as "global", but this is an object too
      if (result === "global") {
        result = "object";
      }
      return result; // return String
    }

    return toType;
  }
);
<|endoftext|>


<commit_before>var path = require('path');
var templateData = require(path.resolve(__dirname, '..', '_config', 'templateData.json'));
var templateHelpers = require(path.resolve(__dirname, '..', '_config', 'templateHelpers.js'))();

var WebsiteController = function (website) {
	// Public functions
	var website = website;
	this.get = function(request, response) {
		if (!request.body) return response.sendStatus(400);
		var url = parseUrl(request.params.loc);

		response.render(url, createModel());
	};

	function createModel(params){
		var model = {
			layout: false,
			data: templateData,
			helpers: templateHelpers
		}

		return model;
	}

	function parseUrl(url){
		if(url === '/' || url === '' || url === undefined || url === 'favicon.ico'){
			// change to index or homepage when ready to work on site pages
			url = 'styleguide'
		}
		return url;
	}

};

module.exports = function(website) {
	var controller = new WebsiteController(website);
	website.get(['/*','/:loc'], controller.get);

};<commit_msg>fix: Not being able to access pages other than index locally - routing issue<commit_after>var path = require('path');
var templateData = require(path.resolve(__dirname, '..', '_config', 'templateData.json'));
var templateHelpers = require(path.resolve(__dirname, '..', '_config', 'templateHelpers.js'))();

var WebsiteController = function (website) {
	// Public functions
	var website = website;
	this.get = function(request, response) {
		if (!request.body) return response.sendStatus(400);
		var url = parseUrl(request.params[0]);

		response.render(url, createModel());
	};

	function createModel(params){
		var model = {
			layout: false,
			data: templateData,
			helpers: templateHelpers
		}

		return model;
	}

	function parseUrl(url){
		if(url === '/' || url === '' || url === undefined || url === 'favicon.ico'){
			// change to index or homepage when ready to work on site pages
			url = 'styleguide'
		}
		return url;
	}

};

module.exports = function(website) {
	var controller = new WebsiteController(website);
	website.get(['/*','/:loc'], controller.get);

};<|endoftext|>


<commit_before>/* eslint-env mocha */
// The following two rules are disabled so that we can use anonymous functions with mocha
// This allows the test instance to be properly referenced with `this`
/* eslint prefer-arrow-callback: "off", func-names: "off" */
const Vue = require('vue');
const Vuex = require('vuex');
const assert = require('assert');
const _ = require('lodash');

const { store, mutations, constants } = require('../src/vuex/store.js');
const Management = require('../src/main.vue');
const fixture1 = require('./fixtures/fixture1.js');

describe('The management module', () => {
  it('defines a Management vue', () => {
    // A sanity check
    assert(Management !== undefined);
  });

  describe('has the following components:', function () {
    before(function () {
      const container = new Vue({
        template: '
<management v-ref:main></management>
',
        components: { Management },
        store,
      }).$mount();
      this.vm = container.$refs.main;
    });

    after(function () {
      this.vm.$destroy();
    });

    it('a classroom selector', function (done) {
      Vue.nextTick(() => {
        const child = this.vm.$refs.classroomSelector;
        assert.notStrictEqual(child, undefined);
        done();
      });
    });

    it('a learner group selector', function (done) {
      Vue.nextTick(() => {
        const child = this.vm.$refs.learnerGroupSelector;
        assert.notStrictEqual(child, undefined);
        done();
      });
    });

    it('a learner roster', function (done) {
      Vue.nextTick(() => {
        const child = this.vm.$refs.learnerRoster;
        assert.notStrictEqual(child, undefined);
        done();
      });
    });
  });

  describe('changes the list of students in the roster when you select a classroom.', function () {
    beforeEach(function () {
      const testStore = new Vuex.Store({
        state: fixture1,
        mutations,
      });
      const container = new Vue({
        template: '
<management v-ref:main></management>
',
        components: { Management },
        store: testStore,
      }).$mount();
      this.vm = container.$refs.main;
      this.store = testStore;
    });

    afterEach(function () {
      this.vm.$destroy();
    });

    it('The roster shows only John Duck when you select "Classroom C".', function (done) {
      // Look at the fixture file for the magic numbers here.
      this.store.dispatch('SET_SELECTED_CLASSROOM_ID', 3);
      Vue.nextTick(() => {
        assert(_.isEqual(this.vm.$refs.learnerRoster.learners, [{
          id: 2,
          first_name: 'John',
          last_name: 'Duck',
          username: 'jduck',
        }]));
        done();
      });
    });

    it('The roster shows all learners when you select "All classrooms".', function (done) {
      this.store.dispatch('SET_SELECTED_CLASSROOM_ID', constants.ALL_CLASSROOMS_ID);
      Vue.nextTick(() => {
        assert(_.isEqual(this.vm.$refs.learnerRoster.learners, fixture1.learners));
        done();
      });
    });
  });
});
<commit_msg>Add some more tests<commit_after>/* eslint-env mocha */
// The following two rules are disabled so that we can use anonymous functions with mocha
// This allows the test instance to be properly referenced with `this`
/* eslint prefer-arrow-callback: "off", func-names: "off" */
const Vue = require('vue');
const Vuex = require('vuex');
const assert = require('assert');
const _ = require('lodash');

const { store, mutations, constants } = require('../src/vuex/store.js');
const Management = require('../src/main.vue');
const fixture1 = require('./fixtures/fixture1.js');

describe('The management module', () => {
  it('defines a Management vue', () => {
    // A sanity check
    assert(Management !== undefined);
  });

  describe('has the following components:', function () {
    before(function () {
      const container = new Vue({
        template: '
<management v-ref:main></management>
',
        components: { Management },
        store,
      }).$mount();
      this.vm = container.$refs.main;
    });

    after(function () {
      this.vm.$destroy();
    });

    it('a classroom selector', function (done) {
      Vue.nextTick(() => {
        const child = this.vm.$refs.classroomSelector;
        assert.notStrictEqual(child, undefined);
        done();
      });
    });

    it('a learner group selector', function (done) {
      Vue.nextTick(() => {
        const child = this.vm.$refs.learnerGroupSelector;
        assert.notStrictEqual(child, undefined);
        done();
      });
    });

    it('a learner roster', function (done) {
      Vue.nextTick(() => {
        const child = this.vm.$refs.learnerRoster;
        assert.notStrictEqual(child, undefined);
        done();
      });
    });
  });

  describe('changes the list of students in the roster when you select a classroom.', function () {
    beforeEach(function () {
      const testStore = new Vuex.Store({
        state: fixture1,
        mutations,
      });
      const container = new Vue({
        template: '
<management v-ref:main></management>
',
        components: { Management },
        store: testStore,
      }).$mount();
      this.vm = container.$refs.main;
      this.store = testStore;
    });

    afterEach(function () {
      this.vm.$destroy();
    });

    it('The roster shows only John Duck when you select "Classroom C".', function (done) {
      // Look at the fixture file for the magic numbers here.
      this.store.dispatch('SET_SELECTED_CLASSROOM_ID', 3);
      Vue.nextTick(() => {
        assert(_.isEqual(this.vm.$refs.learnerRoster.learners, [{
          id: 2,
          first_name: 'John',
          last_name: 'Duck',
          username: 'jduck',
        }]));
        done();
      });
    });

    it('The roster shows all learners when you select "All classrooms".', function (done) {
      this.store.dispatch('SET_SELECTED_CLASSROOM_ID', constants.ALL_CLASSROOMS_ID);
      Vue.nextTick(() => {
        assert.deepStrictEqual(this.vm.$refs.learnerRoster.learners, fixture1.learners);
        done();
      });
    });

    it('The roster shows two students when you select "Classroom A" and "Group 1".', function (done) {  // eslint-disable-line max-len
      this.store.dispatch('SET_SELECTED_CLASSROOM_ID', 1);
      Vue.nextTick(() => {
        this.store.dispatch('SET_SELECTED_GROUP_ID', 1);
        Vue.nextTick(() => {
          const expectedIds = [1, 2];
          assert.deepStrictEqual(this.vm.$refs.learnerRoster.learners.map(learner =>
            learner.id
          ), expectedIds);
          done();
        });
      });
    });

    it('The roster shows no students when you select "Classroom B".', function (done) {
      this.store.dispatch('SET_SELECTED_CLASSROOM_ID', 2);
      Vue.nextTick(() => {
        assert(_.isEqual(this.vm.$refs.learnerRoster.learners, []));
        done();
      });
    });
  });
});
<|endoftext|>


<commit_before>/**
 * MBP - Mobile boilerplate helper functions
 */

(function(document) {

    window.MBP = window.MBP || {};

    /**
     * Fix for iPhone viewport scale bug
     * http://www.blog.highub.com/mobile-2/a-fix-for-iphone-viewport-scale-bug/
     */

    MBP.viewportmeta = document.querySelector && document.querySelector('meta[name="viewport"]');
    MBP.ua = navigator.userAgent;

    MBP.scaleFix = function() {
        if (MBP.viewportmeta && /iPhone|iPad|iPod/.test(MBP.ua) && !/Opera Mini/.test(MBP.ua)) {
            MBP.viewportmeta.content = 'width=device-width, minimum-scale=1.0, maximum-scale=1.0';
            document.addEventListener('gesturestart', MBP.gestureStart, false);
        }
    };

    MBP.gestureStart = function() {
        MBP.viewportmeta.content = 'width=device-width, minimum-scale=0.25, maximum-scale=1.6';
    };

    /**
     * Normalized hide address bar for iOS & Android
     * (c) Scott Jehl, scottjehl.com
     * MIT License
     */

    // If we split this up into two functions we can reuse
    // this function if we aren't doing full page reloads.

    // If we cache this we don't need to re-calibrate everytime we call
    // the hide url bar
    MBP.BODY_SCROLL_TOP = false;

    // So we don't redefine this function everytime we
    // we call hideUrlBar
    MBP.getScrollTop = function() {
        var win = window;
        var doc = document;

        return win.pageYOffset || doc.compatMode === 'CSS1Compat' && doc.documentElement.scrollTop || doc.body.scrollTop || 0;
    };

    // It should be up to the mobile
    MBP.hideUrlBar = function() {
        var win = window;

        // if there is a hash, or MBP.BODY_SCROLL_TOP hasn't been set yet, wait till that happens
        if (!location.hash && MBP.BODY_SCROLL_TOP !== false) {
            win.scrollTo( 0, MBP.BODY_SCROLL_TOP === 1 ? 0 : 1 );
        }
    };

    MBP.hideUrlBarOnLoad = function() {
        var win = window;
        var doc = win.document;
        var bodycheck;

        // If there's a hash, or addEventListener is undefined, stop here
        if ( !win.navigator.standalone && !location.hash && win.addEventListener ) {

            // scroll to 1
            window.scrollTo( 0, 1 );
            MBP.BODY_SCROLL_TOP = 1;

            // reset to 0 on bodyready, if needed
            bodycheck = setInterval(function() {
                if ( doc.body ) {
                    clearInterval( bodycheck );
                    MBP.BODY_SCROLL_TOP = MBP.getScrollTop();
                    MBP.hideUrlBar();
                }
            }, 15 );

            win.addEventListener('load', function() {
                setTimeout(function() {
                    // at load, if user hasn't scrolled more than 20 or so...
                    if (MBP.getScrollTop() < 20) {
                        // reset to hide addr bar at onload
                        MBP.hideUrlBar();
                    }
                }, 0);
            });
        }
    };

    /**
     * Fast Buttons - read wiki below before using
     * https://github.com/h5bp/mobile-boilerplate/wiki/JavaScript-Helper
     */

    MBP.fastButton = function(element, handler, pressedClass) {
        this.handler = handler;
        // styling of .pressed is defined in the project's CSS files
        this.pressedClass = typeof pressedClass === 'undefined' ? 'pressed' : pressedClass;

        MBP.listenForGhostClicks();

        if (element.length && element.length > 1) {
            for (var singleElIdx in element) {
                this.addClickEvent(element[singleElIdx]);
            }
        } else {
            this.addClickEvent(element);
        }
    };

    MBP.fastButton.prototype.handleEvent = function(event) {
        event = event || window.event;

        switch (event.type) {
            case 'touchstart': this.onTouchStart(event); break;
            case 'touchmove': this.onTouchMove(event); break;
            case 'touchend': this.onClick(event); break;
            case 'click': this.onClick(event); break;
        }
    };

    MBP.fastButton.prototype.onTouchStart = function(event) {
        var element = event.target || event.srcElement;
        event.stopPropagation();
        element.addEventListener('touchend', this, false);
        document.body.addEventListener('touchmove', this, false);
        this.startX = event.touches[0].clientX;
        this.startY = event.touches[0].clientY;

        element.className+= ' ' + this.pressedClass;
    };

    MBP.fastButton.prototype.onTouchMove = function(event) {
        if (Math.abs(event.touches[0].clientX - this.startX) > 10 ||
            Math.abs(event.touches[0].clientY - this.startY) > 10) {
            this.reset(event);
        }
    };

    MBP.fastButton.prototype.onClick = function(event) {
        event = event || window.event;
        var element = event.target || event.srcElement;
        if (event.stopPropagation) {
            event.stopPropagation();
        }
        this.reset(event);
        this.handler.apply(event.currentTarget, [event]);
        if (event.type == 'touchend') {
            MBP.preventGhostClick(this.startX, this.startY);
        }
        var pattern = new RegExp(' ?' + this.pressedClass, 'gi');
        element.className = element.className.replace(pattern, '');
    };

    MBP.fastButton.prototype.reset = function(event) {
        var element = event.target || event.srcElement;
        rmEvt(element, 'touchend', this, false);
        rmEvt(document.body, 'touchmove', this, false);

        var pattern = new RegExp(' ?' + this.pressedClass, 'gi');
        element.className = element.className.replace(pattern, '');
    };

    MBP.fastButton.prototype.addClickEvent = function(element) {
        addEvt(element, 'touchstart', this, false);
        addEvt(element, 'click', this, false);
    };

    MBP.preventGhostClick = function(x, y) {
        MBP.coords.push(x, y);
        window.setTimeout(function() {
            MBP.coords.splice(0, 2);
        }, 2500);
    };

    MBP.ghostClickHandler = function(event) {
        if (!MBP.hadTouchEvent && MBP.dodgyAndroid) {
            // This is a bit of fun for Android 2.3...
            // If you change window.location via fastButton, a click event will fire
            // on the new page, as if the events are continuing from the previous page.
            // We pick that event up here, but MBP.coords is empty, because it's a new page,
            // so we don't prevent it. Here's we're assuming that click events on touch devices
            // that occur without a preceding touchStart are to be ignored.
            event.stopPropagation();
            event.preventDefault();
            return;
        }
        for (var i = 0, len = MBP.coords.length; i < len; i += 2) {
            var x = MBP.coords[i];
            var y = MBP.coords[i + 1];
            if (Math.abs(event.clientX - x) < 25 && Math.abs(event.clientY - y) < 25) {
                event.stopPropagation();
                event.preventDefault();
            }
        }
    };

    // This bug only affects touch Android 2.3 devices, but a simple ontouchstart test creates a false positive on
    // some Blackberry devices. https://github.com/Modernizr/Modernizr/issues/372
    // The browser sniffing is to avoid the Blackberry case. Bah
    MBP.dodgyAndroid = ('ontouchstart' in window) && (navigator.userAgent.indexOf('Android 2.3') != -1);

    MBP.listenForGhostClicks = (function() {
        var alreadyRan = false;

        return function() {
            if(alreadyRan) {
                return;
            }

            if (document.addEventListener) {
                document.addEventListener('click', MBP.ghostClickHandler, true);
            }
            addEvt(document.documentElement, 'touchstart', function() {
                MBP.hadTouchEvent = true;
            }, false);

            alreadyRan = true;
        };
    })();

    MBP.coords = [];

    // fn arg can be an object or a function, thanks to handleEvent
    // read more about the explanation at: http://www.thecssninja.com/javascript/handleevent
    function addEvt(el, evt, fn, bubble) {
        if ('addEventListener' in el) {
            // BBOS6 doesn't support handleEvent, catch and polyfill
            try {
                el.addEventListener(evt, fn, bubble);
            } catch(e) {
                if (typeof fn == 'object' && fn.handleEvent) {
                    el.addEventListener(evt, function(e){
                        // Bind fn as this and set first arg as event object
                        fn.handleEvent.call(fn,e);
                    }, bubble);
                } else {
                    throw e;
                }
            }
        } else if ('attachEvent' in el) {
            // check if the callback is an object and contains handleEvent
            if (typeof fn == 'object' && fn.handleEvent) {
                el.attachEvent('on' + evt, function(){
                    // Bind fn as this
                    fn.handleEvent.call(fn);
                });
            } else {
                el.attachEvent('on' + evt, fn);
            }
        }
    }

    function rmEvt(el, evt, fn, bubble) {
        if ('removeEventListener' in el) {
            // BBOS6 doesn't support handleEvent, catch and polyfill
            try {
                el.removeEventListener(evt, fn, bubble);
            } catch(e) {
                if (typeof fn == 'object' && fn.handleEvent) {
                    el.removeEventListener(evt, function(e){
                        // Bind fn as this and set first arg as event object
                        fn.handleEvent.call(fn,e);
                    }, bubble);
                } else {
                    throw e;
                }
            }
        } else if ('detachEvent' in el) {
            // check if the callback is an object and contains handleEvent
            if (typeof fn == 'object' && fn.handleEvent) {
                el.detachEvent("on" + evt, function() {
                    // Bind fn as this
                    fn.handleEvent.call(fn);
                });
            } else {
                el.detachEvent('on' + evt, fn);
            }
        }
    }

    /**
     * Autogrow
     * http://googlecode.blogspot.com/2009/07/gmail-for-mobile-html5-series.html
     */

    MBP.autogrow = function(element, lh) {
        function handler(e) {
            var newHeight = this.scrollHeight;
            var currentHeight = this.clientHeight;
            if (newHeight > currentHeight) {
                this.style.height = newHeight + 3 * textLineHeight + 'px';
            }
        }

        var setLineHeight = (lh) ? lh : 12;
        var textLineHeight = element.currentStyle ? element.currentStyle.lineHeight : getComputedStyle(element, null).lineHeight;

        textLineHeight = (textLineHeight.indexOf('px') == -1) ? setLineHeight : parseInt(textLineHeight, 10);

        element.style.overflow = 'hidden';
        element.addEventListener ? element.addEventListener('input', handler, false) : element.attachEvent('onpropertychange', handler);
    };

    /**
     * Enable CSS active pseudo styles in Mobile Safari
     * http://alxgbsn.co.uk/2011/10/17/enable-css-active-pseudo-styles-in-mobile-safari/
     */

    MBP.enableActive = function() {
        document.addEventListener('touchstart', function() {}, false);
    };

    /**
     * Prevent default scrolling on document window
     */

    MBP.preventScrolling = function() {
        document.addEventListener('touchmove', function(e) {
            if (e.target.type === 'range') { return; }
            e.preventDefault();
        }, false);
    };

    /**
     * Prevent iOS from zooming onfocus
     * https://github.com/h5bp/mobile-boilerplate/pull/108
     * Adapted from original jQuery code here: http://nerd.vasilis.nl/prevent-ios-from-zooming-onfocus/
     */

    MBP.preventZoom = function() {
        if (MBP.viewportmeta && navigator.platform.match(/iPad|iPhone|iPod/i)) {
            var formFields = document.querySelectorAll('input, select, textarea');
            var contentString = 'width=device-width,initial-scale=1,maximum-scale=';
            var i = 0;
            var fieldLength = formFields.length;

            var setViewportOnFocus = function() {
                MBP.viewportmeta.content = contentString + '1';
            };

            var setViewportOnBlur = function() {
                MBP.viewportmeta.content = contentString + '10';
            };

            for (; i < fieldLength; i++) {
                formFields[i].onfocus = setViewportOnFocus;
                formFields[i].onblur = setViewportOnBlur;
            }
        }
    };

    /**
     * iOS Startup Image helper
     */

    MBP.startupImage = function() {
        var portrait;
        var landscape;
        var pixelRatio;
        var head;
        var link1;
        var link2;

        pixelRatio = window.devicePixelRatio;
        head = document.getElementsByTagName('head')[0];

        if (navigator.platform === 'iPad') {
            portrait = pixelRatio === 2 ? 'img/startup/startup-tablet-portrait-retina.png' : 'img/startup/startup-tablet-portrait.png';
            landscape = pixelRatio === 2 ? 'img/startup/startup-tablet-landscape-retina.png' : 'img/startup/startup-tablet-landscape.png';

            link1 = document.createElement('link');
            link1.setAttribute('rel', 'apple-touch-startup-image');
            link1.setAttribute('media', 'screen and (orientation: portrait)');
            link1.setAttribute('href', portrait);
            head.appendChild(link1);

            link2 = document.createElement('link');
            link2.setAttribute('rel', 'apple-touch-startup-image');
            link2.setAttribute('media', 'screen and (orientation: landscape)');
            link2.setAttribute('href', landscape);
            head.appendChild(link2);
        } else {
            portrait = pixelRatio === 2 ? "img/startup/startup-retina.png" : "img/startup/startup.png";
            portrait = screen.height === 568 ? "img/startup/startup-retina-4in.png" : portrait;
            link1 = document.createElement('link');
            link1.setAttribute('rel', 'apple-touch-startup-image');
            link1.setAttribute('href', portrait);
            head.appendChild(link1);
        }

        //hack to fix letterboxed full screen web apps on 4" iPhone / iPod with iOS 6
        if (navigator.platform.match(/iPhone|iPod/i) && (screen.height === 568) && navigator.userAgent.match(/\bOS 6_/)) {
            if (MBP.viewportmeta) {
                MBP.viewportmeta.content = MBP.viewportmeta.content
                    .replace(/\bwidth\s*=\s*320\b/, 'width=320.1')
                    .replace(/\bwidth\s*=\s*device-width\b/, '');
            }
        }
    };

})(document);
<commit_msg> fix hideUrlBarOnLoad load event listener for compatibility with older browsers (closes #198)<commit_after>/**
 * MBP - Mobile boilerplate helper functions
 */

(function(document) {

    window.MBP = window.MBP || {};

    /**
     * Fix for iPhone viewport scale bug
     * http://www.blog.highub.com/mobile-2/a-fix-for-iphone-viewport-scale-bug/
     */

    MBP.viewportmeta = document.querySelector && document.querySelector('meta[name="viewport"]');
    MBP.ua = navigator.userAgent;

    MBP.scaleFix = function() {
        if (MBP.viewportmeta && /iPhone|iPad|iPod/.test(MBP.ua) && !/Opera Mini/.test(MBP.ua)) {
            MBP.viewportmeta.content = 'width=device-width, minimum-scale=1.0, maximum-scale=1.0';
            document.addEventListener('gesturestart', MBP.gestureStart, false);
        }
    };

    MBP.gestureStart = function() {
        MBP.viewportmeta.content = 'width=device-width, minimum-scale=0.25, maximum-scale=1.6';
    };

    /**
     * Normalized hide address bar for iOS & Android
     * (c) Scott Jehl, scottjehl.com
     * MIT License
     */

    // If we split this up into two functions we can reuse
    // this function if we aren't doing full page reloads.

    // If we cache this we don't need to re-calibrate everytime we call
    // the hide url bar
    MBP.BODY_SCROLL_TOP = false;

    // So we don't redefine this function everytime we
    // we call hideUrlBar
    MBP.getScrollTop = function() {
        var win = window;
        var doc = document;

        return win.pageYOffset || doc.compatMode === 'CSS1Compat' && doc.documentElement.scrollTop || doc.body.scrollTop || 0;
    };

    // It should be up to the mobile
    MBP.hideUrlBar = function() {
        var win = window;

        // if there is a hash, or MBP.BODY_SCROLL_TOP hasn't been set yet, wait till that happens
        if (!location.hash && MBP.BODY_SCROLL_TOP !== false) {
            win.scrollTo( 0, MBP.BODY_SCROLL_TOP === 1 ? 0 : 1 );
        }
    };

    MBP.hideUrlBarOnLoad = function() {
        var win = window;
        var doc = win.document;
        var bodycheck;

        // If there's a hash, or addEventListener is undefined, stop here
        if ( !win.navigator.standalone && !location.hash && win.addEventListener ) {

            // scroll to 1
            window.scrollTo( 0, 1 );
            MBP.BODY_SCROLL_TOP = 1;

            // reset to 0 on bodyready, if needed
            bodycheck = setInterval(function() {
                if ( doc.body ) {
                    clearInterval( bodycheck );
                    MBP.BODY_SCROLL_TOP = MBP.getScrollTop();
                    MBP.hideUrlBar();
                }
            }, 15 );

            win.addEventListener('load', function() {
                setTimeout(function() {
                    // at load, if user hasn't scrolled more than 20 or so...
                    if (MBP.getScrollTop() < 20) {
                        // reset to hide addr bar at onload
                        MBP.hideUrlBar();
                    }
                }, 0);
            }, false);
        }
    };

    /**
     * Fast Buttons - read wiki below before using
     * https://github.com/h5bp/mobile-boilerplate/wiki/JavaScript-Helper
     */

    MBP.fastButton = function(element, handler, pressedClass) {
        this.handler = handler;
        // styling of .pressed is defined in the project's CSS files
        this.pressedClass = typeof pressedClass === 'undefined' ? 'pressed' : pressedClass;

        MBP.listenForGhostClicks();

        if (element.length && element.length > 1) {
            for (var singleElIdx in element) {
                this.addClickEvent(element[singleElIdx]);
            }
        } else {
            this.addClickEvent(element);
        }
    };

    MBP.fastButton.prototype.handleEvent = function(event) {
        event = event || window.event;

        switch (event.type) {
            case 'touchstart': this.onTouchStart(event); break;
            case 'touchmove': this.onTouchMove(event); break;
            case 'touchend': this.onClick(event); break;
            case 'click': this.onClick(event); break;
        }
    };

    MBP.fastButton.prototype.onTouchStart = function(event) {
        var element = event.target || event.srcElement;
        event.stopPropagation();
        element.addEventListener('touchend', this, false);
        document.body.addEventListener('touchmove', this, false);
        this.startX = event.touches[0].clientX;
        this.startY = event.touches[0].clientY;

        element.className+= ' ' + this.pressedClass;
    };

    MBP.fastButton.prototype.onTouchMove = function(event) {
        if (Math.abs(event.touches[0].clientX - this.startX) > 10 ||
            Math.abs(event.touches[0].clientY - this.startY) > 10) {
            this.reset(event);
        }
    };

    MBP.fastButton.prototype.onClick = function(event) {
        event = event || window.event;
        var element = event.target || event.srcElement;
        if (event.stopPropagation) {
            event.stopPropagation();
        }
        this.reset(event);
        this.handler.apply(event.currentTarget, [event]);
        if (event.type == 'touchend') {
            MBP.preventGhostClick(this.startX, this.startY);
        }
        var pattern = new RegExp(' ?' + this.pressedClass, 'gi');
        element.className = element.className.replace(pattern, '');
    };

    MBP.fastButton.prototype.reset = function(event) {
        var element = event.target || event.srcElement;
        rmEvt(element, 'touchend', this, false);
        rmEvt(document.body, 'touchmove', this, false);

        var pattern = new RegExp(' ?' + this.pressedClass, 'gi');
        element.className = element.className.replace(pattern, '');
    };

    MBP.fastButton.prototype.addClickEvent = function(element) {
        addEvt(element, 'touchstart', this, false);
        addEvt(element, 'click', this, false);
    };

    MBP.preventGhostClick = function(x, y) {
        MBP.coords.push(x, y);
        window.setTimeout(function() {
            MBP.coords.splice(0, 2);
        }, 2500);
    };

    MBP.ghostClickHandler = function(event) {
        if (!MBP.hadTouchEvent && MBP.dodgyAndroid) {
            // This is a bit of fun for Android 2.3...
            // If you change window.location via fastButton, a click event will fire
            // on the new page, as if the events are continuing from the previous page.
            // We pick that event up here, but MBP.coords is empty, because it's a new page,
            // so we don't prevent it. Here's we're assuming that click events on touch devices
            // that occur without a preceding touchStart are to be ignored.
            event.stopPropagation();
            event.preventDefault();
            return;
        }
        for (var i = 0, len = MBP.coords.length; i < len; i += 2) {
            var x = MBP.coords[i];
            var y = MBP.coords[i + 1];
            if (Math.abs(event.clientX - x) < 25 && Math.abs(event.clientY - y) < 25) {
                event.stopPropagation();
                event.preventDefault();
            }
        }
    };

    // This bug only affects touch Android 2.3 devices, but a simple ontouchstart test creates a false positive on
    // some Blackberry devices. https://github.com/Modernizr/Modernizr/issues/372
    // The browser sniffing is to avoid the Blackberry case. Bah
    MBP.dodgyAndroid = ('ontouchstart' in window) && (navigator.userAgent.indexOf('Android 2.3') != -1);

    MBP.listenForGhostClicks = (function() {
        var alreadyRan = false;

        return function() {
            if(alreadyRan) {
                return;
            }

            if (document.addEventListener) {
                document.addEventListener('click', MBP.ghostClickHandler, true);
            }
            addEvt(document.documentElement, 'touchstart', function() {
                MBP.hadTouchEvent = true;
            }, false);

            alreadyRan = true;
        };
    })();

    MBP.coords = [];

    // fn arg can be an object or a function, thanks to handleEvent
    // read more about the explanation at: http://www.thecssninja.com/javascript/handleevent
    function addEvt(el, evt, fn, bubble) {
        if ('addEventListener' in el) {
            // BBOS6 doesn't support handleEvent, catch and polyfill
            try {
                el.addEventListener(evt, fn, bubble);
            } catch(e) {
                if (typeof fn == 'object' && fn.handleEvent) {
                    el.addEventListener(evt, function(e){
                        // Bind fn as this and set first arg as event object
                        fn.handleEvent.call(fn,e);
                    }, bubble);
                } else {
                    throw e;
                }
            }
        } else if ('attachEvent' in el) {
            // check if the callback is an object and contains handleEvent
            if (typeof fn == 'object' && fn.handleEvent) {
                el.attachEvent('on' + evt, function(){
                    // Bind fn as this
                    fn.handleEvent.call(fn);
                });
            } else {
                el.attachEvent('on' + evt, fn);
            }
        }
    }

    function rmEvt(el, evt, fn, bubble) {
        if ('removeEventListener' in el) {
            // BBOS6 doesn't support handleEvent, catch and polyfill
            try {
                el.removeEventListener(evt, fn, bubble);
            } catch(e) {
                if (typeof fn == 'object' && fn.handleEvent) {
                    el.removeEventListener(evt, function(e){
                        // Bind fn as this and set first arg as event object
                        fn.handleEvent.call(fn,e);
                    }, bubble);
                } else {
                    throw e;
                }
            }
        } else if ('detachEvent' in el) {
            // check if the callback is an object and contains handleEvent
            if (typeof fn == 'object' && fn.handleEvent) {
                el.detachEvent("on" + evt, function() {
                    // Bind fn as this
                    fn.handleEvent.call(fn);
                });
            } else {
                el.detachEvent('on' + evt, fn);
            }
        }
    }

    /**
     * Autogrow
     * http://googlecode.blogspot.com/2009/07/gmail-for-mobile-html5-series.html
     */

    MBP.autogrow = function(element, lh) {
        function handler(e) {
            var newHeight = this.scrollHeight;
            var currentHeight = this.clientHeight;
            if (newHeight > currentHeight) {
                this.style.height = newHeight + 3 * textLineHeight + 'px';
            }
        }

        var setLineHeight = (lh) ? lh : 12;
        var textLineHeight = element.currentStyle ? element.currentStyle.lineHeight : getComputedStyle(element, null).lineHeight;

        textLineHeight = (textLineHeight.indexOf('px') == -1) ? setLineHeight : parseInt(textLineHeight, 10);

        element.style.overflow = 'hidden';
        element.addEventListener ? element.addEventListener('input', handler, false) : element.attachEvent('onpropertychange', handler);
    };

    /**
     * Enable CSS active pseudo styles in Mobile Safari
     * http://alxgbsn.co.uk/2011/10/17/enable-css-active-pseudo-styles-in-mobile-safari/
     */

    MBP.enableActive = function() {
        document.addEventListener('touchstart', function() {}, false);
    };

    /**
     * Prevent default scrolling on document window
     */

    MBP.preventScrolling = function() {
        document.addEventListener('touchmove', function(e) {
            if (e.target.type === 'range') { return; }
            e.preventDefault();
        }, false);
    };

    /**
     * Prevent iOS from zooming onfocus
     * https://github.com/h5bp/mobile-boilerplate/pull/108
     * Adapted from original jQuery code here: http://nerd.vasilis.nl/prevent-ios-from-zooming-onfocus/
     */

    MBP.preventZoom = function() {
        if (MBP.viewportmeta && navigator.platform.match(/iPad|iPhone|iPod/i)) {
            var formFields = document.querySelectorAll('input, select, textarea');
            var contentString = 'width=device-width,initial-scale=1,maximum-scale=';
            var i = 0;
            var fieldLength = formFields.length;

            var setViewportOnFocus = function() {
                MBP.viewportmeta.content = contentString + '1';
            };

            var setViewportOnBlur = function() {
                MBP.viewportmeta.content = contentString + '10';
            };

            for (; i < fieldLength; i++) {
                formFields[i].onfocus = setViewportOnFocus;
                formFields[i].onblur = setViewportOnBlur;
            }
        }
    };

    /**
     * iOS Startup Image helper
     */

    MBP.startupImage = function() {
        var portrait;
        var landscape;
        var pixelRatio;
        var head;
        var link1;
        var link2;

        pixelRatio = window.devicePixelRatio;
        head = document.getElementsByTagName('head')[0];

        if (navigator.platform === 'iPad') {
            portrait = pixelRatio === 2 ? 'img/startup/startup-tablet-portrait-retina.png' : 'img/startup/startup-tablet-portrait.png';
            landscape = pixelRatio === 2 ? 'img/startup/startup-tablet-landscape-retina.png' : 'img/startup/startup-tablet-landscape.png';

            link1 = document.createElement('link');
            link1.setAttribute('rel', 'apple-touch-startup-image');
            link1.setAttribute('media', 'screen and (orientation: portrait)');
            link1.setAttribute('href', portrait);
            head.appendChild(link1);

            link2 = document.createElement('link');
            link2.setAttribute('rel', 'apple-touch-startup-image');
            link2.setAttribute('media', 'screen and (orientation: landscape)');
            link2.setAttribute('href', landscape);
            head.appendChild(link2);
        } else {
            portrait = pixelRatio === 2 ? "img/startup/startup-retina.png" : "img/startup/startup.png";
            portrait = screen.height === 568 ? "img/startup/startup-retina-4in.png" : portrait;
            link1 = document.createElement('link');
            link1.setAttribute('rel', 'apple-touch-startup-image');
            link1.setAttribute('href', portrait);
            head.appendChild(link1);
        }

        //hack to fix letterboxed full screen web apps on 4" iPhone / iPod with iOS 6
        if (navigator.platform.match(/iPhone|iPod/i) && (screen.height === 568) && navigator.userAgent.match(/\bOS 6_/)) {
            if (MBP.viewportmeta) {
                MBP.viewportmeta.content = MBP.viewportmeta.content
                    .replace(/\bwidth\s*=\s*320\b/, 'width=320.1')
                    .replace(/\bwidth\s*=\s*device-width\b/, '');
            }
        }
    };

})(document);
<|endoftext|>


<commit_before>var path = require('path'),
    util = require('util'),
    gulp = require('gulp'),
    browsersync = require('browser-sync'),
    $ = require('gulp-load-plugins')(),
    package = require('./package.json');

/* Configurations. Note that most of the configuration is stored in
the task context. These are mainly for repeating configuration items */
var config = {
  version: package.version,
  debug: Boolean($.util.env.debug),
  browsersync: Boolean($.util.env.browsersync),
  appwallurl: 'http://appwall.sc5.io/',
};

// Package management
/* Install & update Bower dependencies */
gulp.task('install', function() {
  // FIXME specifying the component directory broken in gulp
  // For now, use .bowerrc; No need for piping, either
  $.bower();
  // Downloads the Selenium webdriver
  $.protractor.webdriver_update(function() {});
});

/* Bump version number for package.json & bower.json */
// TODO Provide means for appending a patch id based on git commit id or md5 hash
gulp.task('bump', function() {
  // Fetch whether we're bumping major, minor or patch; default to minor
  var env = $.util.env,
      type = (env.major) ? 'major' : (env.patch) ? 'patch' : 'minor';

  gulp.src(['./bower.json', './package.json'])
    .pipe($.bump({ type: type }))
    .pipe(gulp.dest('./'));
});

// Cleanup
gulp.task('clean', function() {
  gulp.src('dist', { read: false })
    .pipe($.clean());
});

/* Serve the web site */
gulp.task('serve', $.serve({
  root: 'dist',
  port: 8080
}));

gulp.task('preprocess', function() {
  gulp.src('src/app/**/*.js')
    .pipe($.jshint())
    .pipe($.jshint.reporter('default'));
});

gulp.task('javascript', ['preprocess'], function() {

  // The non-MD5fied prefix, so that we know which version we are actually
  // referring to in case of fixing bugs
  var bundleName = util.format('bundle-%s.js', config.version),
      componentsPath = 'src/components',
      browserifyConfig = {
        debug: config.debug,
        shim: {
          jquery: {
            path: path.join(componentsPath, 'jquery/dist/jquery.js'),
            exports: 'jQuery'
          },
          moment: {
            path: path.join(componentsPath, 'moment/moment.js'),
            exports: 'moment'
          }
        }
      };

  return gulp.src('src/app/main.js', { read: false })
    // Compile
    // Unit test
    // Integrate (link, package, concatenate)
    .pipe($.plumber())
    .pipe($.browserify(browserifyConfig))
    .pipe($.concat(bundleName))
    .pipe($.if(config.debug, $.uglify()))
    // Integration test
    .pipe(gulp.dest('dist'));
});

gulp.task('stylesheets', function() {
  // The non-MD5fied prefix, so that we know which version we are actually
  // referring to in case of fixing bugs
  var bundleName = util.format('styles-%s.css', config.version);

  return gulp.src('src/css/styles.scss')
    .pipe($.plumber())
    // Compile
    .pipe($.compass({
        project: path.join(__dirname, 'src'),
        sass: 'css',
        css: '../temp/css'
    }))
    // Unit test
    // Integrate (link, package, concatenate)
    .pipe($.concat(bundleName))
    // Integrate
    .pipe(gulp.dest('dist/css'))
    // Integration test
    .pipe($.csslint())
    .pipe($.csslint.reporter());
});

gulp.task('assets', function() {
  return gulp.src('src/assets/**')
    .pipe(gulp.dest('dist/assets'));
    // Integration test
});

gulp.task('clean', function() {
  gulp.src(['dist', 'temp'], { read: false })
    .pipe($.clean());
});

gulp.task('integrate', ['javascript', 'stylesheets', 'assets'], function() {
  return gulp.src(['dist/*.js', 'dist/css/*.css'])
    .pipe($.inject('src/index.html', { ignorePath: ['/dist/'], addRootSlash: false }))
    .pipe(gulp.dest('./dist'));
});

var dependencies = (config.browsersync) ? ['integrate', 'test', 'browsersync'] : ['integrate', 'test', 'serve', 'livereload'];

gulp.task('watch', dependencies, function() {

  // Watch the actual resources; Currently trigger a full rebuild
  gulp.watch([
    'src/css/**/*.scss',
    'src/app/**/*.js',
    'src/app/**/*.hbs',
    'src/*.html',
    'tests/**/*.js'
  ], ['integrate', 'test']);

});

gulp.task('livereload', function() {

  // Only livereload if the HTML (or other static assets) are changed, because
  // the HTML will change for any JS or CSS change
  gulp.src('dist/**', { read: false })
    .pipe($.watch())
    .pipe($.livereload());

});

/* Watch, live reload and serve the web site with browsersync */
gulp.task('browsersync', function() {

    var bs = browsersync.init('dist/*', {
      server: {
        baseDir: 'dist'
      }
    });

  bs.events.on('init', function(api) {

      var request = require('request');

      request(
        config.appwallurl + 'add',
        { method: 'POST', form: { address: api.options.url + '/' } },
        function(error, response, body) {
          console.log(body);
        }
      );

  });

});

gulp.task('webdriver', function(cb) {

  if (config.debug) {
    cb();
  } else {

    var phantom = require('phantomjs-server'),
        webdriver = require('selenium-webdriver');

    // Start PhantomJS
    phantom.start().done(function() {
      var driver = new webdriver.Builder()
        .usingServer(phantom.address())
        .build();
      cb();
    });

  }

});

gulp.task('test', ['webdriver'], function() {

  if (config.debug) {
    // Find the selenium server standalone jar file. Version number in the file name
    // is due to change
    var find = require('find'),
        paths = find.fileSync(/selenium-server-standalone.*\.jar/, 'node_modules/protractor/selenium'),
        args = ['--seleniumServerJar', paths[0]];
  } else {
    var args = ['--seleniumAddress', 'http://localhost:4444/'];
  }

  // Set baseUrl
  args = args.concat(['--baseUrl', 'http://localhost:' + ((config.browsersync) ? 3002 : 8080) + '/']);

  // Run tests
  gulp.src('tests/**/*.js')
    .pipe($.protractor.protractor({
      configFile: 'protractor.config.js',
      args: args
    }))
    .on('error', function(e) { throw e; });

});

gulp.task('default', ['integrate', 'test']);
<commit_msg>Bettered<commit_after>var path = require('path'),
    util = require('util'),
    gulp = require('gulp'),
    browsersync = require('browser-sync'),
    $ = require('gulp-load-plugins')(),
    package = require('./package.json');

/* Configurations. Note that most of the configuration is stored in
the task context. These are mainly for repeating configuration items */
var config = {
  version: package.version,
  debug: Boolean($.util.env.debug),
  browsersync: Boolean($.util.env.browsersync)
};

// Package management
/* Install & update Bower dependencies */
gulp.task('install', function() {
  // FIXME specifying the component directory broken in gulp
  // For now, use .bowerrc; No need for piping, either
  $.bower();
  // Downloads the Selenium webdriver
  $.protractor.webdriver_update(function() {});
});

/* Bump version number for package.json & bower.json */
// TODO Provide means for appending a patch id based on git commit id or md5 hash
gulp.task('bump', function() {
  // Fetch whether we're bumping major, minor or patch; default to minor
  var env = $.util.env,
      type = (env.major) ? 'major' : (env.patch) ? 'patch' : 'minor';

  gulp.src(['./bower.json', './package.json'])
    .pipe($.bump({ type: type }))
    .pipe(gulp.dest('./'));
});

// Cleanup
gulp.task('clean', function() {
  gulp.src('dist', { read: false })
    .pipe($.clean());
});

/* Serve the web site */
gulp.task('serve', $.serve({
  root: 'dist',
  port: 8080
}));

gulp.task('preprocess', function() {
  gulp.src('src/app/**/*.js')
    .pipe($.jshint())
    .pipe($.jshint.reporter('default'));
});

gulp.task('javascript', ['preprocess'], function() {

  // The non-MD5fied prefix, so that we know which version we are actually
  // referring to in case of fixing bugs
  var bundleName = util.format('bundle-%s.js', config.version),
      componentsPath = 'src/components',
      browserifyConfig = {
        debug: config.debug,
        shim: {
          jquery: {
            path: path.join(componentsPath, 'jquery/dist/jquery.js'),
            exports: 'jQuery'
          },
          moment: {
            path: path.join(componentsPath, 'moment/moment.js'),
            exports: 'moment'
          }
        }
      };

  return gulp.src('src/app/main.js', { read: false })
    // Compile
    // Unit test
    // Integrate (link, package, concatenate)
    .pipe($.plumber())
    .pipe($.browserify(browserifyConfig))
    .pipe($.concat(bundleName))
    .pipe($.if(config.debug, $.uglify()))
    // Integration test
    .pipe(gulp.dest('dist'));
});

gulp.task('stylesheets', function() {
  // The non-MD5fied prefix, so that we know which version we are actually
  // referring to in case of fixing bugs
  var bundleName = util.format('styles-%s.css', config.version);

  return gulp.src('src/css/styles.scss')
    .pipe($.plumber())
    // Compile
    .pipe($.compass({
        project: path.join(__dirname, 'src'),
        sass: 'css',
        css: '../temp/css'
    }))
    // Unit test
    // Integrate (link, package, concatenate)
    .pipe($.concat(bundleName))
    // Integrate
    .pipe(gulp.dest('dist/css'))
    // Integration test
    .pipe($.csslint())
    .pipe($.csslint.reporter());
});

gulp.task('assets', function() {
  return gulp.src('src/assets/**')
    .pipe(gulp.dest('dist/assets'));
    // Integration test
});

gulp.task('clean', function() {
  gulp.src(['dist', 'temp'], { read: false })
    .pipe($.clean());
});

gulp.task('integrate', ['javascript', 'stylesheets', 'assets'], function() {
  return gulp.src(['dist/*.js', 'dist/css/*.css'])
    .pipe($.inject('src/index.html', { ignorePath: ['/dist/'], addRootSlash: false }))
    .pipe(gulp.dest('./dist'));
});

var dependencies = (config.browsersync) ? ['integrate', 'test', 'browsersync'] : ['integrate', 'test', 'serve', 'livereload'];

gulp.task('watch', dependencies, function() {

  // Watch the actual resources; Currently trigger a full rebuild
  gulp.watch([
    'src/css/**/*.scss',
    'src/app/**/*.js',
    'src/app/**/*.hbs',
    'src/*.html',
    'tests/**/*.js'
  ], ['integrate', 'test']);

});

gulp.task('livereload', function() {

  // Only livereload if the HTML (or other static assets) are changed, because
  // the HTML will change for any JS or CSS change
  gulp.src('dist/**', { read: false })
    .pipe($.watch())
    .pipe($.livereload());

});

/* Watch, live reload and serve the web site with browsersync */
gulp.task('browsersync', function() {

  var bs = browsersync.init('dist/*', {
    server: {
      baseDir: 'dist'
    }
  });

});

gulp.task('webdriver', function(cb) {

  if (config.debug) {
    cb();
  } else {

    var phantom = require('phantomjs-server'),
        webdriver = require('selenium-webdriver');

    // Start PhantomJS
    phantom.start().done(function() {
      var driver = new webdriver.Builder()
        .usingServer(phantom.address())
        .build();
      cb();
    });

  }

});

gulp.task('test', ['webdriver'], function() {

  if (config.debug) {
    // Find the selenium server standalone jar file. Version number in the file name
    // is due to change
    var find = require('find'),
        paths = find.fileSync(/selenium-server-standalone.*\.jar/, 'node_modules/protractor/selenium'),
        args = ['--seleniumServerJar', paths[0]];
  } else {
    var args = ['--seleniumAddress', 'http://localhost:4444/'];
  }

  // Set baseUrl
  args = args.concat(['--baseUrl', 'http://localhost:' + ((config.browsersync) ? 3002 : 8080) + '/']);

  // Run tests
  gulp.src('tests/**/*.js')
    .pipe($.protractor.protractor({
      configFile: 'protractor.config.js',
      args: args
    }))
    .on('error', function(e) { throw e; });

});

gulp.task('default', ['integrate', 'test']);
<|endoftext|>