<commit_before>(function($) {
    var defaults = {
        fetchUrl: '',
        moveUrl: '',
        addUrl: '',
        editUrl: '',
        deleteUrl: '',
        passengerTypeaheadUrl: '',
        editable: false,
        deletable: false,
        form: null,
        monthNames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
        monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
        dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
        dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
        buttonText: {},
        editable: false,
        loadError: function () {},
        removeError: function () {},
        removeSuccess: function () {},
        addError: function () {},
        addSuccess: function () {},
        updateError: function () {},
        updateSuccess: function () {},
        hideErrors: function () {},
        tNewReservation: 'New Reservation',
        tEditReservation: 'Edit Reservation',
        tStartDate: 'Start Date',
        tEndDate: 'End Date',
        tLoad: 'Load',
        tAdditionalInformation: 'Additional Information',
        tDriver: 'Driver',
        tPassenger: 'Passenger',
        tDelete: 'Delete',
        tEdit: 'Edit',
    };

    var methods = {
        init: function (options) {
            var settings = $.extend(defaults, options);

            $(this).data('logisticsCalendar', settings);
            _init($(this));

            return this;
        },
        gotoDate: function (year, month, day) {
            $(this).fullCalendar('gotoDate', year, month-1, day);
            return this;
        }
    };

    $.fn.logisticsCalendar = function (method) {
        if (methods[method]) {
            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if (typeof method === 'object' || ! method) {
            return methods.init.apply(this, arguments);
        } else {
            $.error('Method ' +  method + ' does not exist on $.logisticsCalendar');
        }
    }

    function _init($this) {
        var settings = $this.data('logisticsCalendar');

        $this.fullCalendar({
            header: {
                left: 'prev,next today',
                center: 'title',
                right: 'month,agendaWeek,agendaDay'
            },
            editable: settings.editable,
            disableResizing: false,
            disableDragging: false,
            slotMinutes: 15,
            allDaySlot: false,
            allDayDefault: false,
            firstHour: 7,
            firstDay: 1,
            lazyFetching: false,
            defaultView: 'agendaWeek',
            timeFormat: {
                agenda: 'H:mm{ - H:mm}',
                '': 'H:mm'
            },
            columnFormat: {
                month: 'ddd',    // Mon
                week: 'ddd d/M', // Mon 9/7
                day: 'dddd d/M'  // Monday 9/7
            },
            axisFormat: 'H:mm',
            monthNames: settings.monthNames,
            monthNamesShort: settings.monthNamesShort,
            dayNames: settings.dayNames,
            dayNamesShort: settings.dayNamesShort,
            buttonText: settings.buttonText,

            eventSources: [
                {
                    events: function (start, end, callback) {
                        _getEvents($this, start, end, callback);
                    }
                }
            ],

            loading: function(isLoading, view) {
                if (isLoading) {
                    $this.addClass('loading');
                } else {
                    $this.removeClass('loading');
                }
            },

            selectable: settings.editable,
            select: function (startDate, endDate, allDay, jsEvent, view) {
                if (!settings.editable)
                    return;
                _addEvent($this, startDate, endDate, allDay, jsEvent, view);
            },
            eventDrop: function (event, dayDelta, minuteDelta, allDay, revertFunc, jsEvent, ui, view) {
                if (!settings.editable)
                    return;
                _movedEvent($this, event, dayDelta, minuteDelta, allDay, revertFunc, jsEvent, ui, view);
            },
            eventResize: function (event, dayDelta, minuteDelta, revertFunc, jsEvent, ui, view) {
                if (!settings.editable)
                    return;
                _resizedEvent($this, event, dayDelta, minuteDelta, revertFunc, jsEvent, ui, view);
            },
            eventClick: function (event, jsEvent, view) {
                _clickedEvent($this, event, jsEvent, view);
            }
        });
    }

    function _getEvents($this, start, end, callback) {
        var settings = $this.data('logisticsCalendar');
        settings.hideErrors();

        start = Math.round(start.getTime() / 1000);
        end = Math.round(end.getTime() / 1000);

        $.post(settings.fetchUrl + start + '/' + end, function (data) {
            if (data && data.status == 'success') {
                reservations = data.reservations;

                var events = [];
                var firstHour = 24;

                for (index in reservations) {
                    var reservation = reservations[index];
                    firstHour = Math.min(firstHour, new Date(reservation.start*1000).getHours());

                    events.push({
                        title: reservation.reason,
                        start: reservation.start,
                        end: reservation.end,
                        color: reservation.driver.color,

                        driver: reservation.driver.name,
                        driverId: reservation.driver.id,
                        passenger: reservation.passenger,
                        passengerId: reservation.passengerId,
                        load: reservation.load,
                        additional: reservation.additionalInfo,
                        dbid: reservation.id
                    });
                }

                callback(events);

                $this.fullCalendar('option', 'firstHour', firstHour);
            } else {
                settings.loadError();
            }
        }, 'json').error(settings.loadError);
    }

    function _addEvent($this, startDate, endDate, allDay, jsEvent, view) {
        var settings = $this.data('logisticsCalendar');

        if ($this.data('currentPopover'))
            $this.data('currentPopover').popover('destroy');
        $(jsEvent.target).popover({
            placement: _getPopoverPlacement(startDate, view),
            title: $('<div>').append(
                $('<b>', {'class': 'reason'}).html(settings.tNewReservation),
                $('<div>', {'class': 'pull-right'}).append(
                    $('<a>', {'class': 'close'}).html('×').click(function () {$(jsEvent.target).popover('destroy')})
                )
            ),
            content: settings.form.html(),
            trigger: 'manual',
            html: true,
            container: 'body'
        });
        $(jsEvent.target).popover('show');
        $this.data('currentPopover', $(jsEvent.target));

        $('.popover .start').val(_formatDate(startDate));
        $('.popover .end').val(_formatDate(endDate));
        $('.popover .reservation_edit').hide();

        $('.popover #passengerSearch').typeaheadRemote(
            {
                source: settings.passengerTypeaheadUrl,
            }
        ).change(function(e) {
            if ($(this).data('value')) {
                $('.popover #passengerId').val($(this).data('value').id);
            } else {
                $('.popover #passengerId').val('');
            }
        });

        $('.popover form').ajaxForm({
            url: settings.addUrl,
            success: function (data) {
                if (data && data.status == 'success') {
                    settings.addSuccess();
                    if ($this.data('currentPopover'))
                        $this.data('currentPopover').popover('destroy');
                    $('.typeahead').remove();

                    $this.fullCalendar('renderEvent', {
                        title: data.reservation.reason,
                        start: data.reservation.start,
                        end: data.reservation.end,
                        color: data.reservation.driver.color,

                        driver: data.reservation.driver.name,
                        driverId: data.reservation.driver.id,
                        passenger: data.reservation.passenger,
                        passengerId: data.reservation.passengerId,
                        load: data.reservation.load,
                        additional: data.reservation.additionalInfo,
                        dbid: data.reservation.id
                    });
                } else {
                    settings.addError();
                    $('.popover form').find('ul.errors').remove();
                    if (data && data.errors) {
                        for(element in data.errors) {
                            var list = $('<ul>', {'class': 'errors'});
                            for (error in data.errors[element])
                                list.append($('<li>').html(data.errors[element][error]));
                            var div = $('<div>', {'class': 'help-inline'}).append(list);
                            $('.popover form').find('#' + element).closest('.control-group').addClass('error').find('.controls').append(div);
                        }
                    }
                }
            },
            error: function(a, b, c) {
                settings.addError();
            },
            dataType: 'json'
        });
    }

    function _movedEvent($this, event, dayDelta, minuteDelta, allDay, revertFunc, jsEvent, ui, view) {
        var settings = $this.data('logisticsCalendar');
        $.post(settings.moveUrl + event.dbid, {start: Math.round(event.start.getTime() / 1000), end: Math.round(event.end.getTime() / 1000)}, function (data) {
            if (data && data.status == 'success') {
                settings.updateSuccess();
            } else {
                settings.updateError();
            }
        }, 'json').error(settings.updateError);
    }

    function _resizedEvent($this, event, dayDelta, minuteDelta, revertFunc, jsEvent, ui, view) {
        var settings = $this.data('logisticsCalendar');
        $.post(settings.moveUrl + event.dbid, {start: Math.round(event.start.getTime() / 1000), end: Math.round(event.end.getTime() / 1000)}, function (data) {
            if (data && data.status == 'success') {
                settings.updateSuccess();
            } else {
                settings.updateError();
            }
        }, 'json').error(settings.updateError);
    }

    function _clickedEvent($this, event, jsEvent, view) {
        var settings = $this.data('logisticsCalendar');

        var content = $('<div>').append(
            $('<dl>', {'class': 'dl-horizontal'}).append(
                $('<dt>').html(settings.tStartDate),
                $('<dd>').html(_formatDate(event.start)),
                $('<dt>').html(settings.tEndDate),
                $('<dd>').html(_formatDate(event.end))
            )
        );

        if (event.load) {
            content.find('dl').append(
                $('<dt>').html(settings.tLoad),
                $('<dd>').html(event.load)
            )
        }

        if (event.additional) {
            content.find('dl').append(
                $('<dt>').html(settings.tAdditionalInformation),
                $('<dd>').html(event.additional)
            )
        }

        if (event.passenger) {
            content.find('dl').append(
                $('<dt>').html(settings.tPassenger),
                $('<dd>').html(event.passenger)
            )
        }

        if (event.driver) {
            content.find('dl').append(
                $('<dt>').html(settings.tDriver),
                $('<dd>').html(event.driver)
            )
        }

        if (settings.editable) {
            content.append(
                '<hr>'
            )

            if (settings.deletable) {
                content.append(
                    $('<a>', {'class': 'delete btn btn-danger pull-right'}).html(settings.tDelete).css('margin-right', '10px')
                );
            }

            content.append(
                $('<a>', {'class': 'edit btn btn-primary pull-right'}).html(settings.tEdit).css('margin-right', '10px')
            );
        }

        if ($this.data('currentPopover'))
            $this.data('currentPopover').popover('destroy');
        $(jsEvent.target).popover({
            placement: _getPopoverPlacement(event.start, view),
            title: $('<div>').append(
                $('<b>', {'class': 'reason'}).html(event.title),
                $('<div>', {'class': 'pull-right'}).append(
                    $('<a>', {'class': 'close'}).html('×').click(function () {$(jsEvent.target).popover('destroy')})
                )
            ),
            content: content.html(),
            trigger: 'manual',
            html: true,
            container: 'body'
        });
        $(jsEvent.target).popover('show');
        $this.data('currentPopover', $(jsEvent.target));
        $('.popover .delete').click(function () {
            _deleteEvent($this, event, jsEvent, view);
        });
        $('.popover .edit').click(function () {
            _editEvent($this, event, jsEvent, view);
        });
    }

    function _deleteEvent($this, event, jsEvent, view) {
        var settings = $this.data('logisticsCalendar');

        $.post(settings.deleteUrl + event.dbid, function (data) {
            if (data && data.status == 'success') {
                settings.removeSuccess();

                if ($this.data('currentPopover'))
                    $this.data('currentPopover').popover('destroy');

                $this.fullCalendar('removeEvents', event._id);
            } else {
                settings.removeError();
            }
        }, 'json').error(settings.removeError);
    }

    function _editEvent($this, event, jsEvent, view) {
        var settings = $this.data('logisticsCalendar');

        if ($this.data('currentPopover'))
            $this.data('currentPopover').popover('destroy');

        $(jsEvent.target).popover({
            placement: _getPopoverPlacement(event.start, view),
            title: $('<div>').append(
                $('<b>', {'class': 'reason'}).html(settings.tEditReservation),
                $('<div>', {'class': 'pull-right'}).append(
                    $('<a>', {'class': 'close'}).html('×').click(function () {$(jsEvent.target).popover('destroy')})
                )
            ),
            content: settings.form.html(),
            trigger: 'manual',
            html: true,
            container: 'body'
        });
        $(jsEvent.target).popover('show');
        $this.data('currentPopover', $(jsEvent.target));

        $('.popover .start').val(_formatDate(event.start));
        $('.popover .end').val(_formatDate(event.end));
        $('.popover .reason').val(event.title);
        $('.popover .load').val(event.load);
        $('.popover .additional').html(event.additional);
        $('.popover .driver').val(event.driverId);
        $('.popover #passengerId').val(event.passengerId);
        $('.popover .passenger').val(event.passenger);
        $('.popover .reservation_add').hide();

        $('.popover #passengerSearch').typeaheadRemote(
            {
                source: settings.passengerTypeaheadUrl,
            }
        ).change(function(e) {
            if ($(this).data('value')) {
                $('.popover #passengerId').val($(this).data('value').id);
            } else {
                $('.popover #passengerId').val('');
            }
        });

        $('.popover form').ajaxForm({
            url: settings.editUrl + event.dbid,
            success: function (data) {
                if (data && data.status == 'success') {
                    settings.updateSuccess();
                    if ($this.data('currentPopover'))
                        $this.data('currentPopover').popover('destroy');
                    $('.typeahead').remove();

                    $this.fullCalendar('removeEvents', event._id);
                    $this.fullCalendar('renderEvent', {
                        title: data.reservation.reason,
                        start: data.reservation.start,
                        end: data.reservation.end,
                        color: data.reservation.driver.color,

                        driver: data.reservation.driver.name,
                        driverId: data.reservation.driver.id,
                        passenger: data.reservation.passenger,
                        passengerId: data.reservation.passengerId,
                        load: data.reservation.load,
                        additional: data.reservation.additionalInfo,
                        dbid: data.reservation.id
                    });
                } else {
                    settings.updateError();
                    $('.popover form').find('ul.errors').remove();
                    if (data && data.errors) {
                        for(element in data.errors) {
                            var list = $('<ul>', {'class': 'errors'});
                            for (error in data.errors[element])
                                list.append($('<li>').html(data.errors[element][error]));
                            var div = $('<div>', {'class': 'help-inline'}).append(list);
                            $('.popover form').find('#' + element).closest('.control-group').addClass('error').find('.controls').append(div);
                        }
                    }
                }
            },
            error: function(a, b, c) {
                settings.updateError();
            },
            dataType: 'json'
        });
    }

    function _formatDate(date) {
        return $.fullCalendar.formatDate(date, 'dd/MM/yyyy H:mm')
    }

    function _getPopoverPlacement(startDay, view) {
        var placement = 'rigth';
        if (view.name == 'month') {
            placement = startDay.getDay() < 4 && startDay.getDay() > 0 ? 'right' : 'left';
        } else if (view.name == 'agendaWeek') {
            placement = startDay.getDay() < 4 && startDay.getDay() > 0 ? 'right' : 'left';
        } else if (view.name == 'agendaDay') {
            placement = 'right';
        }
        return placement;
    }
}) (jQuery);<commit_msg>Small workflow improvement<commit_after>(function($) {
    var defaults = {
        fetchUrl: '',
        moveUrl: '',
        addUrl: '',
        editUrl: '',
        deleteUrl: '',
        passengerTypeaheadUrl: '',
        editable: false,
        deletable: false,
        form: null,
        monthNames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
        monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
        dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
        dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
        buttonText: {},
        editable: false,
        loadError: function () {},
        removeError: function () {},
        removeSuccess: function () {},
        addError: function () {},
        addSuccess: function () {},
        updateError: function () {},
        updateSuccess: function () {},
        hideErrors: function () {},
        tNewReservation: 'New Reservation',
        tEditReservation: 'Edit Reservation',
        tStartDate: 'Start Date',
        tEndDate: 'End Date',
        tLoad: 'Load',
        tAdditionalInformation: 'Additional Information',
        tDriver: 'Driver',
        tPassenger: 'Passenger',
        tDelete: 'Delete',
        tEdit: 'Edit',
    };

    var methods = {
        init: function (options) {
            var settings = $.extend(defaults, options);

            $(this).data('logisticsCalendar', settings);
            _init($(this));

            return this;
        },
        gotoDate: function (year, month, day) {
            $(this).fullCalendar('gotoDate', year, month-1, day);
            return this;
        }
    };

    $.fn.logisticsCalendar = function (method) {
        if (methods[method]) {
            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if (typeof method === 'object' || ! method) {
            return methods.init.apply(this, arguments);
        } else {
            $.error('Method ' +  method + ' does not exist on $.logisticsCalendar');
        }
    }

    function _init($this) {
        var settings = $this.data('logisticsCalendar');

        $this.fullCalendar({
            header: {
                left: 'prev,next today',
                center: 'title',
                right: 'month,agendaWeek,agendaDay'
            },
            editable: settings.editable,
            disableResizing: false,
            disableDragging: false,
            slotMinutes: 15,
            allDaySlot: false,
            allDayDefault: false,
            firstHour: 7,
            firstDay: 1,
            lazyFetching: false,
            unselectAuto: false,
            defaultView: 'agendaWeek',
            timeFormat: {
                agenda: 'H:mm{ - H:mm}',
                '': 'H:mm'
            },
            columnFormat: {
                month: 'ddd',    // Mon
                week: 'ddd d/M', // Mon 9/7
                day: 'dddd d/M'  // Monday 9/7
            },
            axisFormat: 'H:mm',
            monthNames: settings.monthNames,
            monthNamesShort: settings.monthNamesShort,
            dayNames: settings.dayNames,
            dayNamesShort: settings.dayNamesShort,
            buttonText: settings.buttonText,

            eventSources: [
                {
                    events: function (start, end, callback) {
                        _getEvents($this, start, end, callback);
                    }
                }
            ],

            loading: function(isLoading, view) {
                if (isLoading) {
                    $this.addClass('loading');
                } else {
                    $this.removeClass('loading');
                }
            },

            selectable: settings.editable,
            select: function (startDate, endDate, allDay, jsEvent, view) {
                if (!settings.editable)
                    return;
                _addEvent($this, startDate, endDate, allDay, jsEvent, view);
            },
            eventDrop: function (event, dayDelta, minuteDelta, allDay, revertFunc, jsEvent, ui, view) {
                if (!settings.editable)
                    return;
                _movedEvent($this, event, dayDelta, minuteDelta, allDay, revertFunc, jsEvent, ui, view);
            },
            eventResize: function (event, dayDelta, minuteDelta, revertFunc, jsEvent, ui, view) {
                if (!settings.editable)
                    return;
                _resizedEvent($this, event, dayDelta, minuteDelta, revertFunc, jsEvent, ui, view);
            },
            eventClick: function (event, jsEvent, view) {
                _clickedEvent($this, event, jsEvent, view);
            }
        });
    }

    function _getEvents($this, start, end, callback) {
        var settings = $this.data('logisticsCalendar');
        settings.hideErrors();

        start = Math.round(start.getTime() / 1000);
        end = Math.round(end.getTime() / 1000);

        $.post(settings.fetchUrl + start + '/' + end, function (data) {
            if (data && data.status == 'success') {
                reservations = data.reservations;

                var events = [];
                var firstHour = 24;

                for (index in reservations) {
                    var reservation = reservations[index];
                    firstHour = Math.min(firstHour, new Date(reservation.start*1000).getHours());

                    events.push({
                        title: reservation.reason,
                        start: reservation.start,
                        end: reservation.end,
                        color: reservation.driver.color,

                        driver: reservation.driver.name,
                        driverId: reservation.driver.id,
                        passenger: reservation.passenger,
                        passengerId: reservation.passengerId,
                        load: reservation.load,
                        additional: reservation.additionalInfo,
                        dbid: reservation.id
                    });
                }

                callback(events);

                $this.fullCalendar('option', 'firstHour', firstHour);
            } else {
                settings.loadError();
            }
        }, 'json').error(settings.loadError);
    }

    function _addEvent($this, startDate, endDate, allDay, jsEvent, view) {
        var settings = $this.data('logisticsCalendar');

        if ($this.data('currentPopover'))
            $this.data('currentPopover').popover('destroy');
        $(jsEvent.target).popover({
            placement: _getPopoverPlacement(startDate, view),
            title: $('<div>').append(
                $('<b>', {'class': 'reason'}).html(settings.tNewReservation),
                $('<div>', {'class': 'pull-right'}).append(
                    $('<a>', {'class': 'close'}).html('×').click(function () {
                        $(jsEvent.target).popover('destroy');
                        $this.fullCalendar('unselect');
                    })
                )
            ),
            content: settings.form.html(),
            trigger: 'manual',
            html: true,
            container: 'body'
        });
        $(jsEvent.target).popover('show');
        $this.data('currentPopover', $(jsEvent.target));

        $('.popover .start').val(_formatDate(startDate));
        $('.popover .end').val(_formatDate(endDate));
        $('.popover .reservation_edit').hide();

        $('.popover #passengerSearch').typeaheadRemote(
            {
                source: settings.passengerTypeaheadUrl,
            }
        ).change(function(e) {
            if ($(this).data('value')) {
                $('.popover #passengerId').val($(this).data('value').id);
            } else {
                $('.popover #passengerId').val('');
            }
        });

        $('.popover form').ajaxForm({
            url: settings.addUrl,
            success: function (data) {
                if (data && data.status == 'success') {
                    $this.fullCalendar('unselect');
                    settings.addSuccess();
                    if ($this.data('currentPopover'))
                        $this.data('currentPopover').popover('destroy');
                    $('.typeahead').remove();

                    $this.fullCalendar('renderEvent', {
                        title: data.reservation.reason,
                        start: data.reservation.start,
                        end: data.reservation.end,
                        color: data.reservation.driver.color,

                        driver: data.reservation.driver.name,
                        driverId: data.reservation.driver.id,
                        passenger: data.reservation.passenger,
                        passengerId: data.reservation.passengerId,
                        load: data.reservation.load,
                        additional: data.reservation.additionalInfo,
                        dbid: data.reservation.id
                    });
                } else {
                    settings.addError();
                    $('.popover form').find('ul.errors').remove();
                    if (data && data.errors) {
                        for(element in data.errors) {
                            var list = $('<ul>', {'class': 'errors'});
                            for (error in data.errors[element])
                                list.append($('<li>').html(data.errors[element][error]));
                            var div = $('<div>', {'class': 'help-inline'}).append(list);
                            $('.popover form').find('#' + element).closest('.control-group').addClass('error').find('.controls').append(div);
                        }
                    }
                }
            },
            error: function(a, b, c) {
                settings.addError();
            },
            dataType: 'json'
        });
    }

    function _movedEvent($this, event, dayDelta, minuteDelta, allDay, revertFunc, jsEvent, ui, view) {
        var settings = $this.data('logisticsCalendar');
        $.post(settings.moveUrl + event.dbid, {start: Math.round(event.start.getTime() / 1000), end: Math.round(event.end.getTime() / 1000)}, function (data) {
            if (data && data.status == 'success') {
                settings.updateSuccess();
            } else {
                settings.updateError();
            }
        }, 'json').error(settings.updateError);
    }

    function _resizedEvent($this, event, dayDelta, minuteDelta, revertFunc, jsEvent, ui, view) {
        var settings = $this.data('logisticsCalendar');
        $.post(settings.moveUrl + event.dbid, {start: Math.round(event.start.getTime() / 1000), end: Math.round(event.end.getTime() / 1000)}, function (data) {
            if (data && data.status == 'success') {
                settings.updateSuccess();
            } else {
                settings.updateError();
            }
        }, 'json').error(settings.updateError);
    }

    function _clickedEvent($this, event, jsEvent, view) {
        var settings = $this.data('logisticsCalendar');

        var content = $('<div>').append(
            $('<dl>', {'class': 'dl-horizontal'}).append(
                $('<dt>').html(settings.tStartDate),
                $('<dd>').html(_formatDate(event.start)),
                $('<dt>').html(settings.tEndDate),
                $('<dd>').html(_formatDate(event.end))
            )
        );

        if (event.load) {
            content.find('dl').append(
                $('<dt>').html(settings.tLoad),
                $('<dd>').html(event.load)
            )
        }

        if (event.additional) {
            content.find('dl').append(
                $('<dt>').html(settings.tAdditionalInformation),
                $('<dd>').html(event.additional)
            )
        }

        if (event.passenger) {
            content.find('dl').append(
                $('<dt>').html(settings.tPassenger),
                $('<dd>').html(event.passenger)
            )
        }

        if (event.driver) {
            content.find('dl').append(
                $('<dt>').html(settings.tDriver),
                $('<dd>').html(event.driver)
            )
        }

        if (settings.editable) {
            content.append(
                '<hr>'
            )

            if (settings.deletable) {
                content.append(
                    $('<a>', {'class': 'delete btn btn-danger pull-right'}).html(settings.tDelete).css('margin-right', '10px')
                );
            }

            content.append(
                $('<a>', {'class': 'edit btn btn-primary pull-right'}).html(settings.tEdit).css('margin-right', '10px')
            );
        }

        if ($this.data('currentPopover'))
            $this.data('currentPopover').popover('destroy');
        $(jsEvent.target).popover({
            placement: _getPopoverPlacement(event.start, view),
            title: $('<div>').append(
                $('<b>', {'class': 'reason'}).html(event.title),
                $('<div>', {'class': 'pull-right'}).append(
                    $('<a>', {'class': 'close'}).html('×').click(function () {$(jsEvent.target).popover('destroy')})
                )
            ),
            content: content.html(),
            trigger: 'manual',
            html: true,
            container: 'body'
        });
        $(jsEvent.target).popover('show');
        $this.data('currentPopover', $(jsEvent.target));
        $('.popover .delete').click(function () {
            _deleteEvent($this, event, jsEvent, view);
        });
        $('.popover .edit').click(function () {
            _editEvent($this, event, jsEvent, view);
        });
    }

    function _deleteEvent($this, event, jsEvent, view) {
        var settings = $this.data('logisticsCalendar');

        $.post(settings.deleteUrl + event.dbid, function (data) {
            if (data && data.status == 'success') {
                settings.removeSuccess();

                if ($this.data('currentPopover'))
                    $this.data('currentPopover').popover('destroy');

                $this.fullCalendar('removeEvents', event._id);
            } else {
                settings.removeError();
            }
        }, 'json').error(settings.removeError);
    }

    function _editEvent($this, event, jsEvent, view) {
        var settings = $this.data('logisticsCalendar');

        if ($this.data('currentPopover'))
            $this.data('currentPopover').popover('destroy');

        $(jsEvent.target).popover({
            placement: _getPopoverPlacement(event.start, view),
            title: $('<div>').append(
                $('<b>', {'class': 'reason'}).html(settings.tEditReservation),
                $('<div>', {'class': 'pull-right'}).append(
                    $('<a>', {'class': 'close'}).html('×').click(function () {$(jsEvent.target).popover('destroy')})
                )
            ),
            content: settings.form.html(),
            trigger: 'manual',
            html: true,
            container: 'body'
        });
        $(jsEvent.target).popover('show');
        $this.data('currentPopover', $(jsEvent.target));

        $('.popover .start').val(_formatDate(event.start));
        $('.popover .end').val(_formatDate(event.end));
        $('.popover .reason').val(event.title);
        $('.popover .load').val(event.load);
        $('.popover .additional').html(event.additional);
        $('.popover .driver').val(event.driverId);
        $('.popover #passengerId').val(event.passengerId);
        $('.popover .passenger').val(event.passenger);
        $('.popover .reservation_add').hide();

        $('.popover #passengerSearch').typeaheadRemote(
            {
                source: settings.passengerTypeaheadUrl,
            }
        ).change(function(e) {
            if ($(this).data('value')) {
                $('.popover #passengerId').val($(this).data('value').id);
            } else {
                $('.popover #passengerId').val('');
            }
        });

        $('.popover form').ajaxForm({
            url: settings.editUrl + event.dbid,
            success: function (data) {
                if (data && data.status == 'success') {
                    settings.updateSuccess();
                    if ($this.data('currentPopover'))
                        $this.data('currentPopover').popover('destroy');
                    $('.typeahead').remove();

                    $this.fullCalendar('removeEvents', event._id);
                    $this.fullCalendar('renderEvent', {
                        title: data.reservation.reason,
                        start: data.reservation.start,
                        end: data.reservation.end,
                        color: data.reservation.driver.color,

                        driver: data.reservation.driver.name,
                        driverId: data.reservation.driver.id,
                        passenger: data.reservation.passenger,
                        passengerId: data.reservation.passengerId,
                        load: data.reservation.load,
                        additional: data.reservation.additionalInfo,
                        dbid: data.reservation.id
                    });
                } else {
                    settings.updateError();
                    $('.popover form').find('ul.errors').remove();
                    if (data && data.errors) {
                        for(element in data.errors) {
                            var list = $('<ul>', {'class': 'errors'});
                            for (error in data.errors[element])
                                list.append($('<li>').html(data.errors[element][error]));
                            var div = $('<div>', {'class': 'help-inline'}).append(list);
                            $('.popover form').find('#' + element).closest('.control-group').addClass('error').find('.controls').append(div);
                        }
                    }
                }
            },
            error: function(a, b, c) {
                settings.updateError();
            },
            dataType: 'json'
        });
    }

    function _formatDate(date) {
        return $.fullCalendar.formatDate(date, 'dd/MM/yyyy H:mm')
    }

    function _getPopoverPlacement(startDay, view) {
        var placement = 'rigth';
        if (view.name == 'month') {
            placement = startDay.getDay() < 4 && startDay.getDay() > 0 ? 'right' : 'left';
        } else if (view.name == 'agendaWeek') {
            placement = startDay.getDay() < 4 && startDay.getDay() > 0 ? 'right' : 'left';
        } else if (view.name == 'agendaDay') {
            placement = 'right';
        }
        return placement;
    }
}) (jQuery);<|endoftext|>


<commit_before>/**
 * @class Oskari.paikkatietoikkuna.Main
 *
 * Launcher class for a paikkatietoikkuna.fi map window
 */
Oskari.clazz.define('Oskari.paikkatietoikkuna.Published', function() {

    this.args = null;
    this.styleBndl = null;
}, {

    /**
     * @method processArgs
     *
     * applies page args to this instance
     */
    processArgs : function(args) {
        this.args = args;
        this.styleBndl = args.style;
    },
    /**
     * @method start
     *
     * starts the application with bundle definitions declared
     * in property appSetup.startupSequence
     */
    start : function(cb) {

        var me = this;

        var appSetup = this.appSetup;
        var appConfig = this.appConfig;
        var app = Oskari.app;

        /* me.applyStyle(appSetup,'ui'); */

        app.setApplicationSetup(appSetup);
        app.setConfiguration(appConfig);
        app.startApplication(function(startupInfos) {
            me.instance = startupInfos.bundlesInstanceInfos['mapfull'].bundleInstance;
            if(cb) {
                cb(me.instance);
            }
        });
    },
    /**
     * @static
     * @property appConfig
     */
    appConfig : {
        // bundle id
        'mapfull' : {
            // properties that will be made available before bundle start()
            // 'key' : 'value'
            // can be accessed in mapfull.start() like: alert('This should return
            // "value" :' + this.key);
        }
    },

    /**
     * @static
     * @property appSetup.startupSequence
     */
    appSetup : {

        startupSequence : [
        // openlayers
        {
            // style selection may be done with CSS Links also - just for demo
            title : 'OpenLayers',
            fi : 'OpenLayers',
            sv : '?',
            en : 'OpenLayers',
            bundlename : 'openlayers-default-theme',
            bundleinstancename : 'openlayers-default-theme',
            metadata : {
                "Import-Bundle" : {
                    "openlayers-single-full" : {
                        bundlePath : '/Oskari/packages/openlayers/bundle/'
                    },
                    "openlayers-default-theme" : {
                        bundlePath : '/Oskari/packages/openlayers/bundle/'
                    }
                },
                "Require-Bundle-Instance" : []
            },
            instanceProps : {}
        },
        {
            title : 'OskariUI',
            fi : 'OskariUI',
            sv : 'OskariUI',
            en : 'OskariUI',
            bundlename : 'oskariui',
            bundleinstancename : 'oskariui',
            metadata : {
                "Import-Bundle" : {
                    "oskariui" : {
                        bundlePath : '/Oskari/packages/framework/bundle/'
                    }
                },
                "Require-Bundle-Instance" : []
            },
            instanceProps : {}
        },

        // main app
        {
            title : 'Map',
            fi : 'Map',
            sv : '?',
            en : 'Map',
            bundlename : 'mapfull',
            bundleinstancename : 'mapfull',
            metadata : {
               "Import-Bundle":{
                  "mapwmts":{
                     "bundlePath":"/Oskari/packages/framework/bundle/"
                  },
                  "mapwfs":{
                     "bundlePath":"/Oskari/packages/framework/bundle/"
                  },
                  "service-base":{
                     "bundlePath":"/Oskari/packages/framework/bundle/"
                  },
                  "event-map-layer":{
                     "bundlePath":"/Oskari/packages/framework/bundle/"
                  },
                  "request-map-layer":{
                     "bundlePath":"/Oskari/packages/framework/bundle/"
                  },
                  "mapmodule-plugin":{
                     "bundlePath":"/Oskari/packages/framework/bundle/"
                  },
                  "event-base":{
                     "bundlePath":"/Oskari/packages/framework/bundle/"
                  },
                  "mapfull":{
                     "bundlePath":"/Oskari/packages/framework/bundle/"
                  },
                  "core-base":{
                     "bundlePath":"/Oskari/packages/framework/bundle/"
                  },
                  "request-base":{
                     "bundlePath":"/Oskari/packages/framework/bundle/"
                  },
                  "domain":{
                     "bundlePath":"/Oskari/packages/framework/bundle/"
                  },
                  "core-map":{
                     "bundlePath":"/Oskari/packages/framework/bundle/"
                  },
                  "request-map":{
                     "bundlePath":"/Oskari/packages/framework/bundle/"
                  },
                  "sandbox-base":{
                     "bundlePath":"/Oskari/packages/framework/bundle/"
                  },
                  "service-map":{
                     "bundlePath":"/Oskari/packages/framework/bundle/"
                  },
                  "sandbox-map":{
                     "bundlePath":"/Oskari/packages/framework/bundle/"
                  },
                  "event-map":{
                     "bundlePath":"/Oskari/packages/framework/bundle/"
                  }
               },
               "Require-Bundle-Instance" : []

            },
            instanceProps : {}
        }, {
            title : 'Info Box',
            fi : 'infobox',
            sv : '?',
            en : '?',
            bundlename : 'infobox',
            bundleinstancename : 'infobox',
            metadata : {
                "Import-Bundle" : {
                    "infobox" : {
                        bundlePath : '/Oskari/packages/framework/bundle/'
                    }
                },
                "Require-Bundle-Instance" : []
            },
            instanceProps : {}
        },{
            title : 'PostProcessor',
            fi : 'PostProcessor',
            sv : 'PostProcessor',
            en : 'PostProcessor',
            bundlename : 'postprocessor',
            bundleinstancename : 'postprocessor',
            metadata : {
                "Import-Bundle" : {
                    "postprocessor" : {
                        bundlePath : '/Oskari/packages/framework/bundle/'
                    }
                },
                "Require-Bundle-Instance" : []
            },
            instanceProps : {}
        }]
    }
});

/**
 * Start when dom ready
 */
jQuery(document).ready(function() {

    function getURLParameter(name) {
    	var value = (RegExp(name + '=' + '(.+?)(&|$)').exec(location.search)||[,null])[1];
    	if(value) {
        	return decodeURI(value);
    	}
    }
	// returns empty string if parameter doesn't exist
	// otherwise returns '<param>=<param value>&'
    function getAdditionalParam(param) {
        var value = getURLParameter(param);
        if (value) {
            return param + '=' + value + '&';
        }
        return '';
    }

    var args = {
        oskariLoaderMode : 'yui',
        style : 'style1'
    };
    if(!ajaxUrl) {
        jQuery('#mapdiv').append('Unable to start');
    	return;
    }
    if(!language) {
    	// default to finnish
    	language = 'fi';
    }
    // populate url with possible control parameters
    ajaxUrl += getAdditionalParam('zoomLevel');
    ajaxUrl += getAdditionalParam('coord');
    ajaxUrl += getAdditionalParam('mapLayers');
    ajaxUrl += getAdditionalParam('oldId');
    ajaxUrl += getAdditionalParam('viewId');

    ajaxUrl += getAdditionalParam('isCenterMarker');
    ajaxUrl += getAdditionalParam('address')
    ajaxUrl += getAdditionalParam('showGetFeatureInfo');
    ajaxUrl += getAdditionalParam('nationalCadastralReference');

    ajaxUrl += getAdditionalParam('nationalCadastralReferenceHighlight');
    ajaxUrl += getAdditionalParam('wfsFeature');
    ajaxUrl += getAdditionalParam('wfsHighlightLayer');

    Oskari.setLang(language);

    Oskari.setLoaderMode('dev');
    Oskari.setPreloaded(false);

    if (args.oskariLoaderAsync && args.oskariLoaderAsync == 'on') {
        Oskari.setSupportBundleAsync(true);
    }

    var main = Oskari.clazz.create('Oskari.paikkatietoikkuna.Published');
    main.processArgs(args);

    if (ajaxUrl.indexOf('http') == 0) {
        var hostIdx = ajaxUrl.indexOf('://') + 3;
        var pathIdx = ajaxUrl.indexOf('/', hostIdx);
        ajaxUrl = ajaxUrl.substring(pathIdx);
    }

    var gfiParamHandler = function(sandbox) {
        if(getURLParameter('showGetFeatureInfo') != 'true') {
            return;
        }
        var lon  = sandbox.getMap().getX();
        var lat  = sandbox.getMap().getY();
        var mapModule = sandbox.findRegisteredModuleInstance('MainMapModule');
        var px = mapModule.getMap().getViewPortPxFromLonLat({lon : lon, lat: lat});
        sandbox.postRequestByName('MapModulePlugin.GetFeatureInfoRequest', [lon, lat, px.x, px.y]);
    }

    jQuery.ajax({
        type : 'GET',
        dataType : 'json',
        beforeSend : function(x) {
            if (x && x.overrideMimeType) {
                x.overrideMimeType("application/j-son;charset=UTF-8");
            }
        },
        url : ajaxUrl + 'action_route=GetAppSetup&noSavedState=true',
        success : function(appSetup) {
            if (appSetup.startupSequence && appSetup.configuration) {
                main.appSetup.startupSequence = appSetup.startupSequence;
                main.appConfig = appSetup.configuration;
                main.start(function(instance) {
                    var sb = instance.getSandbox();
                    gfiParamHandler(sb);
                });
            }
        },
        error : function(jqXHR, textStatus) {
            if (jqXHR.status != 0) {
                jQuery('#mapdiv').append('Unable to start');
            }
        }
    });

}); <commit_msg>[AL-703] index.js expects a variable viewId to be present instead of parsing queryUrl<commit_after>/**
 * @class Oskari.paikkatietoikkuna.Main
 *
 * Launcher class for a paikkatietoikkuna.fi map window
 */
Oskari.clazz.define('Oskari.paikkatietoikkuna.Published', function() {

    this.args = null;
    this.styleBndl = null;
}, {

    /**
     * @method processArgs
     *
     * applies page args to this instance
     */
    processArgs : function(args) {
        this.args = args;
        this.styleBndl = args.style;
    },
    /**
     * @method start
     *
     * starts the application with bundle definitions declared
     * in property appSetup.startupSequence
     */
    start : function(cb) {

        var me = this;

        var appSetup = this.appSetup;
        var appConfig = this.appConfig;
        var app = Oskari.app;

        /* me.applyStyle(appSetup,'ui'); */

        app.setApplicationSetup(appSetup);
        app.setConfiguration(appConfig);
        app.startApplication(function(startupInfos) {
            me.instance = startupInfos.bundlesInstanceInfos['mapfull'].bundleInstance;
            if(cb) {
                cb(me.instance);
            }
        });
    },
    /**
     * @static
     * @property appConfig
     */
    appConfig : {
        // bundle id
        'mapfull' : {
            // properties that will be made available before bundle start()
            // 'key' : 'value'
            // can be accessed in mapfull.start() like: alert('This should return
            // "value" :' + this.key);
        }
    },

    /**
     * @static
     * @property appSetup.startupSequence
     */
    appSetup : {

        startupSequence : [
        // openlayers
        {
            // style selection may be done with CSS Links also - just for demo
            title : 'OpenLayers',
            fi : 'OpenLayers',
            sv : '?',
            en : 'OpenLayers',
            bundlename : 'openlayers-default-theme',
            bundleinstancename : 'openlayers-default-theme',
            metadata : {
                "Import-Bundle" : {
                    "openlayers-single-full" : {
                        bundlePath : '/Oskari/packages/openlayers/bundle/'
                    },
                    "openlayers-default-theme" : {
                        bundlePath : '/Oskari/packages/openlayers/bundle/'
                    }
                },
                "Require-Bundle-Instance" : []
            },
            instanceProps : {}
        },
        {
            title : 'OskariUI',
            fi : 'OskariUI',
            sv : 'OskariUI',
            en : 'OskariUI',
            bundlename : 'oskariui',
            bundleinstancename : 'oskariui',
            metadata : {
                "Import-Bundle" : {
                    "oskariui" : {
                        bundlePath : '/Oskari/packages/framework/bundle/'
                    }
                },
                "Require-Bundle-Instance" : []
            },
            instanceProps : {}
        },

        // main app
        {
            title : 'Map',
            fi : 'Map',
            sv : '?',
            en : 'Map',
            bundlename : 'mapfull',
            bundleinstancename : 'mapfull',
            metadata : {
               "Import-Bundle":{
                  "mapwmts":{
                     "bundlePath":"/Oskari/packages/framework/bundle/"
                  },
                  "mapwfs":{
                     "bundlePath":"/Oskari/packages/framework/bundle/"
                  },
                  "service-base":{
                     "bundlePath":"/Oskari/packages/framework/bundle/"
                  },
                  "event-map-layer":{
                     "bundlePath":"/Oskari/packages/framework/bundle/"
                  },
                  "request-map-layer":{
                     "bundlePath":"/Oskari/packages/framework/bundle/"
                  },
                  "mapmodule-plugin":{
                     "bundlePath":"/Oskari/packages/framework/bundle/"
                  },
                  "event-base":{
                     "bundlePath":"/Oskari/packages/framework/bundle/"
                  },
                  "mapfull":{
                     "bundlePath":"/Oskari/packages/framework/bundle/"
                  },
                  "core-base":{
                     "bundlePath":"/Oskari/packages/framework/bundle/"
                  },
                  "request-base":{
                     "bundlePath":"/Oskari/packages/framework/bundle/"
                  },
                  "domain":{
                     "bundlePath":"/Oskari/packages/framework/bundle/"
                  },
                  "core-map":{
                     "bundlePath":"/Oskari/packages/framework/bundle/"
                  },
                  "request-map":{
                     "bundlePath":"/Oskari/packages/framework/bundle/"
                  },
                  "sandbox-base":{
                     "bundlePath":"/Oskari/packages/framework/bundle/"
                  },
                  "service-map":{
                     "bundlePath":"/Oskari/packages/framework/bundle/"
                  },
                  "sandbox-map":{
                     "bundlePath":"/Oskari/packages/framework/bundle/"
                  },
                  "event-map":{
                     "bundlePath":"/Oskari/packages/framework/bundle/"
                  }
               },
               "Require-Bundle-Instance" : []

            },
            instanceProps : {}
        }, {
            title : 'Info Box',
            fi : 'infobox',
            sv : '?',
            en : '?',
            bundlename : 'infobox',
            bundleinstancename : 'infobox',
            metadata : {
                "Import-Bundle" : {
                    "infobox" : {
                        bundlePath : '/Oskari/packages/framework/bundle/'
                    }
                },
                "Require-Bundle-Instance" : []
            },
            instanceProps : {}
        },{
            title : 'PostProcessor',
            fi : 'PostProcessor',
            sv : 'PostProcessor',
            en : 'PostProcessor',
            bundlename : 'postprocessor',
            bundleinstancename : 'postprocessor',
            metadata : {
                "Import-Bundle" : {
                    "postprocessor" : {
                        bundlePath : '/Oskari/packages/framework/bundle/'
                    }
                },
                "Require-Bundle-Instance" : []
            },
            instanceProps : {}
        }]
    }
});

/**
 * Start when dom ready
 */
jQuery(document).ready(function() {

    function getURLParameter(name) {
    	var value = (RegExp(name + '=' + '(.+?)(&|$)').exec(location.search)||[,null])[1];
    	if(value) {
        	return decodeURI(value);
    	}
    }
	// returns empty string if parameter doesn't exist
	// otherwise returns '<param>=<param value>&'
    function getAdditionalParam(param) {
        var value = getURLParameter(param);
        if (value) {
            return param + '=' + value + '&';
        }
        return '';
    }

    var args = {
        oskariLoaderMode : 'yui',
        style : 'style1'
    };
    if(!ajaxUrl) {
        jQuery('#mapdiv').append('Unable to start');
    	return;
    }
    if(!language) {
    	// default to finnish
    	language = 'fi';
    }
    // populate url with possible control parameters
    ajaxUrl += getAdditionalParam('zoomLevel');
    ajaxUrl += getAdditionalParam('coord');
    ajaxUrl += getAdditionalParam('mapLayers');
    ajaxUrl += getAdditionalParam('oldId');
    ajaxUrl += "viewId=" + viewId + "&"; //getAdditionalParam('viewId');

    ajaxUrl += getAdditionalParam('isCenterMarker');
    ajaxUrl += getAdditionalParam('address')
    ajaxUrl += getAdditionalParam('showGetFeatureInfo');
    ajaxUrl += getAdditionalParam('nationalCadastralReference');

    ajaxUrl += getAdditionalParam('nationalCadastralReferenceHighlight');
    ajaxUrl += getAdditionalParam('wfsFeature');
    ajaxUrl += getAdditionalParam('wfsHighlightLayer');

    Oskari.setLang(language);

    Oskari.setLoaderMode('dev');
    Oskari.setPreloaded(false);

    if (args.oskariLoaderAsync && args.oskariLoaderAsync == 'on') {
        Oskari.setSupportBundleAsync(true);
    }

    var main = Oskari.clazz.create('Oskari.paikkatietoikkuna.Published');
    main.processArgs(args);

    if (ajaxUrl.indexOf('http') == 0) {
        var hostIdx = ajaxUrl.indexOf('://') + 3;
        var pathIdx = ajaxUrl.indexOf('/', hostIdx);
        ajaxUrl = ajaxUrl.substring(pathIdx);
    }

    var gfiParamHandler = function(sandbox) {
        if(getURLParameter('showGetFeatureInfo') != 'true') {
            return;
        }
        var lon  = sandbox.getMap().getX();
        var lat  = sandbox.getMap().getY();
        var mapModule = sandbox.findRegisteredModuleInstance('MainMapModule');
        var px = mapModule.getMap().getViewPortPxFromLonLat({lon : lon, lat: lat});
        sandbox.postRequestByName('MapModulePlugin.GetFeatureInfoRequest', [lon, lat, px.x, px.y]);
    }

    jQuery.ajax({
        type : 'GET',
        dataType : 'json',
        beforeSend : function(x) {
            if (x && x.overrideMimeType) {
                x.overrideMimeType("application/j-son;charset=UTF-8");
            }
        },
        url : ajaxUrl + 'action_route=GetAppSetup&noSavedState=true',
        success : function(appSetup) {
            if (appSetup.startupSequence && appSetup.configuration) {
                main.appSetup.startupSequence = appSetup.startupSequence;
                main.appConfig = appSetup.configuration;
                main.start(function(instance) {
                    var sb = instance.getSandbox();
                    gfiParamHandler(sb);
                });
            }
        },
        error : function(jqXHR, textStatus) {
            if (jqXHR.status != 0) {
                jQuery('#mapdiv').append('Unable to start');
            }
        }
    });

}); <|endoftext|>


<commit_before>/*
	Note:
		Whenever we support a new PB adapter, we need to check if it needs actual sizes to be passed,
			if so we will need to add special handling

	TODO:
		PubMatic special handliing
		We are not doing mandatory param check as PB does it
			does PB logs if mandatory param is missing
*/
var CONFIG = require("../config.js");
var CONSTANTS = require("../constants.js");
var BID = require("../bid.js");
var util = require("../util.js");
var bidManager = require("../bidManager.js");
var adapterManager = require("../adapterManager.js");

var parentAdapterID = "prebid";

/* start-test-block */
exports.parentAdapterID = parentAdapterID;
/* end-test-block */
var kgpvMap = {};

/* start-test-block */
exports.kgpvMap = kgpvMap;
/* end-test-block */

var refThis = this;

function handleBidResponses(bidResponses){
	for(var responseID in bidResponses){
		if(util.isOwnProperty(bidResponses, responseID) && util.isOwnProperty(kgpvMap, responseID)){
			var bidObject = bidResponses[responseID];
			var bids = bidObject.bids || [];

			for(var i = 0; i<bids.length; i++){
				var bid = bids[i];
				if(bid.bidderCode){

					var theBid = BID.createBid(bid.bidderCode, kgpvMap[responseID].kgpv);
					theBid.
						setGrossEcpm(bid.cpm).
						setDealID(bid.dealId).
						setDealChannel(bid.dealChannel).
						setAdHtml(bid.ad || "").
						setWidth(bid.width).
						setHeight(bid.height).
						setReceivedTime(bid.responseTimestamp);
					//todo: does any PB partner passes URL as creative ?
					util.forEachOnObject(bid.adserverTargeting, function(key, value){
						theBid.setKeyValuePair(key, value);
					});
					bidManager.setBidFromBidder(kgpvMap[responseID].divID, theBid);
				}
			}
		}
	}
}

/* start-test-block */
exports.handleBidResponses = handleBidResponses;
/* end-test-block */

function MyFunction(adapterID, adUnits, adapterConfig, impressionID, generatedKey, kgpConsistsWidthAndHeight, currentSlot, keyConfig, currentWidth, currentHeight){

	var code, sizes, divID = currentSlot.getDivID();

	if(kgpConsistsWidthAndHeight){
		code = divID + "@" + adapterID + "@" + currentWidth + "X" + currentHeight;
		sizes = [[currentWidth, currentHeight]];
	}else{
		code = divID;
		sizes = currentSlot.getSizes();
	}

	refThis.kgpvMap [ code ] = {
		kgpv: generatedKey,
		divID: divID
	};

	if(!util.isOwnProperty(adUnits, code)){
		adUnits[ code ] = {
			code: code,
			sizes: sizes,
			bids: []
		};
	}

	var slotParams = {};
	util.forEachOnObject(keyConfig, function(key, value){
		slotParams[key] = value;
	});

	// processing for each partner
	switch(adapterID){
		case "pubmatic":
			slotParams["publisherId"] = adapterConfig["publisherId"];
			slotParams["adSlot"] = generatedKey;
			slotParams["wiid"] = impressionID;
			slotParams["profId"] = CONFIG.getProfileID();
			if(window.PWT.udpv){
				slotParams["verId"] = CONFIG.getProfileDisplayVersionID();
			}
			adUnits[ code ].bids.push({	bidder: adapterID, params: slotParams });
			break;

		case "pulsepoint":
			util.forEachOnArray(sizes, function(index, size){
				slotParams["cf"] = size[0] + "x" + size[1];
				adUnits[ code ].bids.push({	bidder: adapterID, params: slotParams });
			});
			break;

		default:
			adUnits[ code ].bids.push({	bidder: adapterID, params: slotParams });
			break;
	}
};

/* start-test-block */
exports.MyFunction = MyFunction;
/* end-test-block */

function generatePbConf(adapterID, adapterConfig, activeSlots, adUnits, impressionID){
	util.log(adapterID+CONSTANTS.MESSAGES.M1);

	if(!adapterConfig){
		return;
	}

	util.forEachGeneratedKey(
		adapterID,
		adUnits,
		adapterConfig,
		impressionID,
		[],
		activeSlots,
		adapterConfig[CONSTANTS.CONFIG.KEY_GENERATION_PATTERN],
		adapterConfig[CONSTANTS.CONFIG.KEY_LOOKUP_MAP] || null,
		refThis.MyFunction,
		true
	);
}

/* start-test-block */
exports.generatePbConf = generatePbConf;
/* end-test-block */

function fetchBids(activeSlots, impressionID){
	// console.log(window.pbjs);
	if(! window.pbjs){
		util.log("PreBid js is not loaded");
		return;
	}

	var adUnits = {};// create ad-units for prebid
	var randomNumberBelow100 = adapterManager.getRandomNumberBelow100();

	CONFIG.forEachAdapter(function(adapterID, adapterConfig){
		if(adapterID !== refThis.parentAdapterID){
			if(adapterManager.throttleAdapter(randomNumberBelow100, adapterID) == false){
				adapterManager.setInitTimeForSlotsForAdapter(activeSlots, adapterID);
				refThis.generatePbConf(adapterID, adapterConfig, activeSlots, adUnits, impressionID);
			}else{
				util.log(adapterID+CONSTANTS.MESSAGES.M2);
			}
		}
	});

	// adUnits is object create array from it
	var adUnitsArray = [];
	for(var code in adUnits){
		if(util.isOwnProperty(adUnits, code)){
			adUnitsArray.push(adUnits[code]);
		}
	}

	if(adUnitsArray.length > 0 && window.pbjs){

		if(util.isFunction(window.pbjs.setBidderSequence)){
			window.pbjs.setBidderSequence("random");
		}

		if(util.isFunction(window.pbjs.requestBids)){
			window.pbjs.logging = true;//todo: enable optionally
			window.pbjs.requestBids({
				adUnits: adUnitsArray,
				bidsBackHandler: function(bidResponses) {
					refThis.handleBidResponses(bidResponses);
				},
				timeout: CONFIG.getTimeout()-50 //todo is it higher ?: major pre and post processing time and then
			});
		}
	}
}

/* start-test-block */
exports.fetchBids = fetchBids;
/* end-test-block */

function getParenteAdapterID() {
	return refThis.parentAdapterID;
}

/* start-test-block */
exports.getParenteAdapterID = getParenteAdapterID;
/* end-test-block */

exports.register = function(){
	return {
		fB: refThis.fetchBids,
		ID: refThis.getParenteAdapterID
	};
};<commit_msg>added an assumption<commit_after>/*
	Note:
		Whenever we support a new PB adapter, we need to check if it needs actual sizes to be passed,
			if so we will need to add special handling

	TODO:
		PubMatic special handliing
		We are not doing mandatory param check as PB does it
			does PB logs if mandatory param is missing
*/
var CONFIG = require("../config.js");
var CONSTANTS = require("../constants.js");
var BID = require("../bid.js");
var util = require("../util.js");
var bidManager = require("../bidManager.js");
var adapterManager = require("../adapterManager.js");

var parentAdapterID = "prebid";

/* start-test-block */
exports.parentAdapterID = parentAdapterID;
/* end-test-block */
var kgpvMap = {};

/* start-test-block */
exports.kgpvMap = kgpvMap;
/* end-test-block */

var refThis = this;

function handleBidResponses(bidResponses){
	for(var responseID in bidResponses){
		if(util.isOwnProperty(bidResponses, responseID) && util.isOwnProperty(kgpvMap, responseID)){
			var bidObject = bidResponses[responseID];
			var bids = bidObject.bids || [];

			for(var i = 0; i<bids.length; i++){
				var bid = bids[i];
				if(bid.bidderCode){

					var theBid = BID.createBid(bid.bidderCode, kgpvMap[responseID].kgpv);
					theBid.
						setGrossEcpm(bid.cpm).
						setDealID(bid.dealId).
						setDealChannel(bid.dealChannel).
						setAdHtml(bid.ad || "").
						setWidth(bid.width).
						setHeight(bid.height).
						setReceivedTime(bid.responseTimestamp);
					//todo: does any PB partner passes URL as creative ?
					util.forEachOnObject(bid.adserverTargeting, function(key, value){
						theBid.setKeyValuePair(key, value);
					});
					bidManager.setBidFromBidder(kgpvMap[responseID].divID, theBid);
				}
			}
		}
	}
}

/* start-test-block */
exports.handleBidResponses = handleBidResponses;
/* end-test-block */

function MyFunction(adapterID, adUnits, adapterConfig, impressionID, generatedKey, kgpConsistsWidthAndHeight, currentSlot, keyConfig, currentWidth, currentHeight){

	var code, sizes, divID = currentSlot.getDivID();

	if(kgpConsistsWidthAndHeight){
		code = divID + "@" + adapterID + "@" + currentWidth + "X" + currentHeight;
		sizes = [[currentWidth, currentHeight]];
	}else{
		code = divID;
		sizes = currentSlot.getSizes();
	}

	refThis.kgpvMap [ code ] = {
		kgpv: generatedKey,
		divID: divID
	};

	if(!util.isOwnProperty(adUnits, code)){
		adUnits[ code ] = {
			code: code,
			sizes: sizes,
			bids: []
		};
	}

	var slotParams = {};
	util.forEachOnObject(keyConfig, function(key, value){
		slotParams[key] = value;
	});

	// processing for each partner
	switch(adapterID){
		case "pubmatic":
			slotParams["publisherId"] = adapterConfig["publisherId"];
			slotParams["adSlot"] = generatedKey;
			slotParams["wiid"] = impressionID;
			slotParams["profId"] = CONFIG.getProfileID();
			if(window.PWT.udpv){
				slotParams["verId"] = CONFIG.getProfileDisplayVersionID();
			}
			adUnits[ code ].bids.push({	bidder: adapterID, params: slotParams });
			break;

		case "pulsepoint":
			util.forEachOnArray(sizes, function(index, size){
				slotParams["cf"] = size[0] + "x" + size[1];
				adUnits[ code ].bids.push({	bidder: adapterID, params: slotParams });
			});
			break;

		default:
			adUnits[ code ].bids.push({	bidder: adapterID, params: slotParams });
			break;
	}
};

/* start-test-block */
exports.MyFunction = MyFunction;
/* end-test-block */

function generatePbConf(adapterID, adapterConfig, activeSlots, adUnits, impressionID){
	util.log(adapterID+CONSTANTS.MESSAGES.M1);

	if(!adapterConfig){
		return;
	}

	util.forEachGeneratedKey(
		adapterID,
		adUnits,
		adapterConfig,
		impressionID,
		[],
		activeSlots,
		adapterConfig[CONSTANTS.CONFIG.KEY_GENERATION_PATTERN],
		adapterConfig[CONSTANTS.CONFIG.KEY_LOOKUP_MAP] || null,
		refThis.MyFunction,
		true
	);
}

/* start-test-block */
exports.generatePbConf = generatePbConf;
/* end-test-block */

function fetchBids(activeSlots, impressionID){
	// console.log(window.pbjs);
	if(! window.pbjs){
		util.log("PreBid js is not loaded");
		return;
	}

	var adUnits = {};// create ad-units for prebid
	var randomNumberBelow100 = adapterManager.getRandomNumberBelow100();

	CONFIG.forEachAdapter(function(adapterID, adapterConfig){
		// Assumption: all partners are running through PreBid,
		//				if we add any new parent-adapter, then code changes will be required
		if(adapterID !== refThis.parentAdapterID){
			if(adapterManager.throttleAdapter(randomNumberBelow100, adapterID) == false){
				adapterManager.setInitTimeForSlotsForAdapter(activeSlots, adapterID);
				refThis.generatePbConf(adapterID, adapterConfig, activeSlots, adUnits, impressionID);
			}else{
				util.log(adapterID+CONSTANTS.MESSAGES.M2);
			}
		}
	});

	// adUnits is object create array from it
	var adUnitsArray = [];
	for(var code in adUnits){
		if(util.isOwnProperty(adUnits, code)){
			adUnitsArray.push(adUnits[code]);
		}
	}

	if(adUnitsArray.length > 0 && window.pbjs){

		if(util.isFunction(window.pbjs.setBidderSequence)){
			window.pbjs.setBidderSequence("random");
		}

		if(util.isFunction(window.pbjs.requestBids)){
			window.pbjs.logging = true;//todo: enable optionally
			window.pbjs.requestBids({
				adUnits: adUnitsArray,
				bidsBackHandler: function(bidResponses) {
					refThis.handleBidResponses(bidResponses);
				},
				timeout: CONFIG.getTimeout()-50 //todo is it higher ?: major pre and post processing time and then
			});
		}
	}
}

/* start-test-block */
exports.fetchBids = fetchBids;
/* end-test-block */

function getParenteAdapterID() {
	return refThis.parentAdapterID;
}

/* start-test-block */
exports.getParenteAdapterID = getParenteAdapterID;
/* end-test-block */

exports.register = function(){
	return {
		fB: refThis.fetchBids,
		ID: refThis.getParenteAdapterID
	};
};<|endoftext|>


<commit_before>import React from 'react';
import Analyzer from 'Parser/Core/Analyzer';
import Combatants from 'Parser/Core/Modules/Combatants';
import SPELLS from 'common/SPELLS';
import SpellIcon from 'common/SpellIcon';
import { formatNumber, formatPercentage } from 'common/format';
import StatisticBox, { STATISTIC_ORDER } from 'Main/StatisticBox';
import StatTracker from 'Parser/Core/Modules/StatTracker';

class BoneShield extends Analyzer {

  static dependencies = {
    combatants: Combatants,
    statTracker: StatTracker,
  };

  SS_DR = 0.08;
  BONE_SHIELD_DR = 0.16;

  EXCLUDES_ABSORBS = [
    SPELLS.BLOOD_SHIELD.id,
    SPELLS.BLOOD_MIRROR_TALENT.id,
  ];

  totalDamageTaken = 0;
  excludedAbsorbs = 0;

  hasSS = false;
  hasSD = false;

  totalCrit = 0;
  totalCritChecks = 0;

  boneShieldDR = 0.16;
  ssDR = 0;

  on_initialized() {
    this.hasSS = this.combatants.selected.traitsBySpellId[SPELLS.SKELETAL_SHATTERING_TRAIT.id];
    this.hasSD = this.combatants.selected.hasTalent(SPELLS.SPECTRAL_DEFLECTION_TALENT.id);
  }

  get BoneShieldAbsorbTooltip() {
    if (this.hasSS) {
      const avgCrit = this.totalCrit / this.totalCritChecks;
      this.ssDR = avgCrit * this.SS_DR;
      this.boneShieldDR = this.BONE_SHIELD_DR + this.ssDR;
    }

    return formatNumber((this.totalDamageTaken - this.excludedAbsorbs) * this.boneShieldDR) + " Bone Shield Absorb
";
  }

  get SkeletalShatteringTooltip() {
    if (this.hasSS) {
      return "~" + formatNumber((this.totalDamageTaken - this.excludedAbsorbs) * this.ssDR) + " of which was done by the Skeletal Shattering Trait.
";
    } else {
      return "";
    }
  }

  on_toPlayer_damage(event) {

    if (!this.combatants.selected.hasBuff(SPELLS.BONE_SHIELD.id)) {
      return;
    }

    this.totalDamageTaken += event.amount + event.absorbed;

    this.totalCrit += this.statTracker.currentCritPercentage;
    this.totalCritChecks += 1;
  }

  on_byPlayer_absorbed(event) {
    if(!this.combatants.selected.hasBuff(SPELLS.BONE_SHIELD.id)) {
      return;
    }

    this.EXCLUDES_ABSORBS.forEach(elem => {
      if (event.ability.guid === elem) {
        this.excludedAbsorbs += event.amount;
      }
    });
  }

  get uptime() {
    return this.combatants.selected.getBuffUptime(SPELLS.BONE_SHIELD.id) / this.owner.fightDuration;
  }

  get uptimeSuggestionThresholds() {
    return {
      actual: this.uptime,
      isLessThan: {
        minor: 0.95,
        average: 0.9,
        major: .8,
      },
      style: 'percentage',
    };
  }

  suggestions(when) {
    when(this.uptimeSuggestionThresholds)
        .addSuggestion((suggest, actual, recommended) => {
          return suggest('Your Bone Shield uptime can be improved. Try to keep it up at all times.')
            .icon(SPELLS.BONE_SHIELD.icon)
            .actual(`${formatPercentage(actual)}% Bone Shield uptime`)
            .recommended(`>${formatPercentage(recommended)}% is recommended`);
        });
  }

  statistic() {

    if (this.hasSD) {

      return (
        <StatisticBox
          icon={<SpellIcon id={SPELLS.BONE_SHIELD.id} />}
          value={`${formatPercentage(this.uptime)} %`}
          label="Bone Shield Uptime"
        />

      );

    } else {

      return (
        <StatisticBox
          icon={<SpellIcon id={SPELLS.BONE_SHIELD.id} />}
          value={`${formatPercentage(this.uptime)} %`}
          label="Bone Shield Uptime"
          tooltip={`~${ this.BoneShieldAbsorbTooltip }
          ${ this.SkeletalShatteringTooltip }
          Those numbers are estimates as it's not possible to track the actual Bone Shield absorb.`}
        />

      );

    }
  }
  statisticOrder = STATISTIC_ORDER.CORE(2);
}

export default BoneShield;
<commit_msg>code refactoring<commit_after>import React from 'react';
import Analyzer from 'Parser/Core/Analyzer';
import Combatants from 'Parser/Core/Modules/Combatants';
import SPELLS from 'common/SPELLS';
import SpellIcon from 'common/SpellIcon';
import { formatNumber, formatPercentage } from 'common/format';
import StatisticBox, { STATISTIC_ORDER } from 'Main/StatisticBox';
import StatTracker from 'Parser/Core/Modules/StatTracker';

class BoneShield extends Analyzer {

  static dependencies = {
    combatants: Combatants,
    statTracker: StatTracker,
  };

  SS_DR = 0.08;
  BONE_SHIELD_DR = 0.16;

  EXCLUDES_ABSORBS = [
    SPELLS.BLOOD_SHIELD.id,
    SPELLS.BLOOD_MIRROR_TALENT.id,
  ];

  totalDamageTaken = 0;
  excludedAbsorbs = 0;

  hasSS = false;
  hasSD = false;

  totalCrit = 0;
  totalCritChecks = 0;

  boneShieldDR = 0.16;
  ssDR = 0;

  on_initialized() {
    this.hasSS = this.combatants.selected.traitsBySpellId[SPELLS.SKELETAL_SHATTERING_TRAIT.id];
    this.hasSD = this.combatants.selected.hasTalent(SPELLS.SPECTRAL_DEFLECTION_TALENT.id);
  }

  get boneShieldAbsorbTooltip() {
    if (this.hasSS) {
      const avgCrit = this.totalCrit / this.totalCritChecks;
      this.ssDR = avgCrit * this.SS_DR;
      this.boneShieldDR = this.BONE_SHIELD_DR + this.ssDR;
    }

    return formatNumber((this.totalDamageTaken - this.excludedAbsorbs) * this.boneShieldDR) + " Bone Shield Absorb
";
  }

  get skeletalShatteringTooltip() {
    if (this.hasSS) {
      return "~" + formatNumber((this.totalDamageTaken - this.excludedAbsorbs) * this.ssDR) + " of which was done by the Skeletal Shattering Trait.
";
    } else {
      return "";
    }
  }

  on_toPlayer_damage(event) {

    if (!this.combatants.selected.hasBuff(SPELLS.BONE_SHIELD.id)) {
      return;
    }

    this.totalDamageTaken += event.amount + event.absorbed;

    this.totalCrit += this.statTracker.currentCritPercentage;
    this.totalCritChecks += 1;
  }

  on_byPlayer_absorbed(event) {
    if(!this.combatants.selected.hasBuff(SPELLS.BONE_SHIELD.id)) {
      return;
    }

    this.EXCLUDES_ABSORBS.forEach(elem => {
      if (event.ability.guid === elem) {
        this.excludedAbsorbs += event.amount;
      }
    });
  }

  get uptime() {
    return this.combatants.selected.getBuffUptime(SPELLS.BONE_SHIELD.id) / this.owner.fightDuration;
  }

  get uptimeSuggestionThresholds() {
    return {
      actual: this.uptime,
      isLessThan: {
        minor: 0.95,
        average: 0.9,
        major: .8,
      },
      style: 'percentage',
    };
  }

  suggestions(when) {
    when(this.uptimeSuggestionThresholds)
        .addSuggestion((suggest, actual, recommended) => {
          return suggest('Your Bone Shield uptime can be improved. Try to keep it up at all times.')
            .icon(SPELLS.BONE_SHIELD.icon)
            .actual(`${formatPercentage(actual)}% Bone Shield uptime`)
            .recommended(`>${formatPercentage(recommended)}% is recommended`);
        });
  }

  statistic() {

    if (this.hasSD) {

      return (
        <StatisticBox
          icon={<SpellIcon id={SPELLS.BONE_SHIELD.id} />}
          value={`${formatPercentage(this.uptime)} %`}
          label="Bone Shield Uptime"
        />

      );

    } else {

      return (
        <StatisticBox
          icon={<SpellIcon id={SPELLS.BONE_SHIELD.id} />}
          value={`${formatPercentage(this.uptime)} %`}
          label="Bone Shield Uptime"
          tooltip={`~${ this.boneShieldAbsorbTooltip }
          ${ this.skeletalShatteringTooltip }
          Those numbers are estimates as it's not possible to track the actual Bone Shield absorb.`}
        />

      );

    }
  }
  statisticOrder = STATISTIC_ORDER.CORE(2);
}

export default BoneShield;
<|endoftext|>


<commit_before>"use strict";

// Require codemirror extra JS bits and bobs so they're included
// since codemirror isn't commonjs
require("codemirror/mode/javascript/javascript");
require("codemirror/addon/edit/matchbrackets");
require("codemirror/addon/selection/active-line");
require("codemirror/addon/comment/continuecomment");

var m        = require("mithril"),
    debounce = require("lodash.debounce"),
    Editor   = require("codemirror"),

    children = require("../types/children"),
    db       = require("../lib/firebase"),

    css    = require("./schemas-edit.css");

module.exports = {
    controller : function() {
        var ctrl = this,
            id   = m.route.param("id"),
            ref  = db.child("schemas/" + id),
            // Weird path is because this isn't browserified
            save = new Worker("/workers/save-schema.js");

        ctrl.schema = null;
        ctrl.recent = null;

        // Listen for updates from Firebase
        ref.on("value", function(snap) {
            ctrl.schema = snap.val();

            m.redraw();
        });

        // Ensure the updated timestamp is always accurate-ish
        ref.on("child_changed", function(snap) {
            if(snap.key() === "updated") {
                return;
            }

            ref.child("updated").set(db.TIMESTAMP);
        });

        // get recent entries using this schema to display
        db.child("content").orderByChild("_schema").equalTo(id).limitToLast(5).on("value", function(snap) {
            ctrl.recent = snap.val();

            m.redraw();
        });

        // Take processed code from worker, save it to firebase
        save.addEventListener("message", function(e) {
            ref.child("fields").set(e.data);
        });

        // Set up codemirror
        ctrl.editorSetup = function(el, init) {
            if(init) {
                return;
            }

            ctrl.editor = Editor.fromTextArea(el, {
                mode : "application/javascript",
                lint : true,

                indentUnit   : 4,
                smartIndent  : false,
                lineNumbers  : true,
                lineWrapping : true,

                // Plugin options
                styleActiveLine  : true,
                continueComments : true,

                autoCloseBrackets : true,
                matchBrackets     : true,

                extraKeys : {
                    Tab : function(cm) {
                        if(cm.somethingSelected()) {
                            return cm.indentSelection("add");
                        }

                        cm.execCommand(cm.options.indentWithTabs ? "insertTab" : "insertSoftTab");
                    },
                    "Shift-Tab": function (cm) {
                        cm.indentSelection("subtract");
                    }
                }
            });

            // Respond to editor changes, but debounced.
            ctrl.editor.on("changes", debounce(ctrl.editorChanged, 100, { maxWait : 10000 }));
        };

        // Handle codemirror change events
        ctrl.editorChanged = function() {
            var text = ctrl.editor.doc.getValue();

            ref.child("source").set(text);
            save.postMessage(text);
        };
    },

    view : function(ctrl) {
        if(!ctrl.schema) {
            return m("h1", "Loading...");
        }

        return m("div", { class : css.page.join(" ") },
            m("div", { class : css.meta.join(" ") },
                m("h1", ctrl.schema.name),
                m("h2", "Recent Entries"),
                m("ul",
                    Object.keys(ctrl.recent || {}).map(function(key) {
                        return m("li",
                            m("a", { href : "/content/" + key, config : m.route }, ctrl.recent[key]._name)
                        );
                    })
                ),
                m("hr")
            ),
            m("div", { class : css.contents.join(" ") },
                m("div", { class : css.editor.join(" ") },
                    m("textarea", { config : ctrl.editorSetup, },
                        ctrl.schema.source || "{}"
                    )
                ),

                m("div", { class : css.fields.join(" ") },
                    m.component(children, {
                        details : ctrl.schema.fields
                    })
                )
            )
        );
    }
};
<commit_msg>Fix links on schema editing page<commit_after>"use strict";

// Require codemirror extra JS bits and bobs so they're included
// since codemirror isn't commonjs
require("codemirror/mode/javascript/javascript");
require("codemirror/addon/edit/matchbrackets");
require("codemirror/addon/selection/active-line");
require("codemirror/addon/comment/continuecomment");

var m        = require("mithril"),
    debounce = require("lodash.debounce"),
    Editor   = require("codemirror"),

    children = require("../types/children"),
    db       = require("../lib/firebase"),

    css    = require("./schemas-edit.css");

module.exports = {
    controller : function() {
        var ctrl = this,
            id   = m.route.param("id"),
            ref  = db.child("schemas/" + id),
            // Weird path is because this isn't browserified
            save = new Worker("/workers/save-schema.js");

        ctrl.schema = null;
        ctrl.recent = null;

        // Listen for updates from Firebase
        ref.on("value", function(snap) {
            ctrl.schema = snap.val();

            m.redraw();
        });

        // Ensure the updated timestamp is always accurate-ish
        ref.on("child_changed", function(snap) {
            if(snap.key() === "updated") {
                return;
            }

            ref.child("updated").set(db.TIMESTAMP);
        });

        // get recent entries using this schema to display
        db.child("content").orderByChild("_schema").equalTo(id).limitToLast(5).on("value", function(snap) {
            ctrl.recent = snap.val();

            m.redraw();
        });

        // Take processed code from worker, save it to firebase
        save.addEventListener("message", function(e) {
            ref.child("fields").set(e.data);
        });

        // Set up codemirror
        ctrl.editorSetup = function(el, init) {
            if(init) {
                return;
            }

            ctrl.editor = Editor.fromTextArea(el, {
                mode : "application/javascript",
                lint : true,

                indentUnit   : 4,
                smartIndent  : false,
                lineNumbers  : true,
                lineWrapping : true,

                // Plugin options
                styleActiveLine  : true,
                continueComments : true,

                autoCloseBrackets : true,
                matchBrackets     : true,

                extraKeys : {
                    Tab : function(cm) {
                        if(cm.somethingSelected()) {
                            return cm.indentSelection("add");
                        }

                        cm.execCommand(cm.options.indentWithTabs ? "insertTab" : "insertSoftTab");
                    },
                    "Shift-Tab": function (cm) {
                        cm.indentSelection("subtract");
                    }
                }
            });

            // Respond to editor changes, but debounced.
            ctrl.editor.on("changes", debounce(ctrl.editorChanged, 100, { maxWait : 10000 }));
        };

        // Handle codemirror change events
        ctrl.editorChanged = function() {
            var text = ctrl.editor.doc.getValue();

            ref.child("source").set(text);
            save.postMessage(text);
        };
    },

    view : function(ctrl) {
        if(!ctrl.schema) {
            return m("h1", "Loading...");
        }

        return m("div", { class : css.page.join(" ") },
            m("div", { class : css.meta.join(" ") },
                m("h1", ctrl.schema.name),
                m("h2", "Recent Entries"),
                m("ul",
                    Object.keys(ctrl.recent || {}).map(function(key) {
                        var content = ctrl.recent[key];

                        return m("li",
                            m("a", { href : "/content/" + content._schema + "/" + key, config : m.route }, content._name)
                        );
                    })
                ),
                m("hr")
            ),
            m("div", { class : css.contents.join(" ") },
                m("div", { class : css.editor.join(" ") },
                    m("textarea", { config : ctrl.editorSetup, },
                        ctrl.schema.source || "{}"
                    )
                ),

                m("div", { class : css.fields.join(" ") },
                    m.component(children, {
                        details : ctrl.schema.fields
                    })
                )
            )
        );
    }
};
<|endoftext|>