<commit_before>// See: https://gist.github.com/xiaodaigh/6445698
// TODO Change to more selective solution
var swichTab = function(i) {
    tabs = $('.tabbable .nav.nav-tabs li');
    tabs.each(function() {
        $(this).removeClass('active')
    });
    $(tabs[i]).addClass('active');
    tabsContents = $('.tabbable .tab-content .tab-pane')
    tabsContents.each(function() {
        $(this).removeClass('active')
    });
    $(tabsContents[i]).addClass('active');
}

function initTour() {

    var steps = [{
            content: '
Welcome...
',
            target: [50, 120],
            nextButton: true,
            my: 'bottom left'
    }, {
        content: [
            '
Choose expression dataset to upload.
',
            '
It should contain a sinlge column with gene names ',
            'followed by the  expression profiles.
',
            '
If you want to test this app you can use ',
            '',
            'expression_example.csv.
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#datain_container'),
        my: 'bottom left',
        at: 'top center',
    }, {
        content: [
            '
Select input file separator
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#sep'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
Check if file has been loaded as expected.
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#datain_preview'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
Choose control samples
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#control_samples'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
Switch to the Characteristic Direction Analysis
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#workflow_panel'),
        my: 'top center',
        at: 'bottom center',
        teardown: function(tour, options) {
            swichTab(1);
        }
    }, {
        content: [
            '
Check input data summary
'
        ].join(''),
        nextButton: true,
        closeButton: true,
        target: $('#chdir_input_summary'),
        my: 'bottom center',
        at: 'bottom center'
    }, {
        content: [
            '
Set parameters.../p>'
        ].join(''),
        nextButton: true,
        closeButton: true,
        target: $('#chdir_parameters'),
        my: 'bottom center',
        at: 'bottom center'
    }, {
        content: [
            '
...and run chdir
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#run_chdir_container'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
When chdir analysis is finished ',
            'you can download the results...
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#chdir_downloads'),
        my: 'bottom center',
        at: 'bottom center'
    }, {
        content: [
            '
... and start PAE analysis
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#workflow_panel'),
        my: 'bottom center',
        at: 'bottom center',
        teardown: function(tour, options) {
            swichTab(2);
        }
    }, {
        content: [
            '
To run PAEA click Run Principle Angle Enrichment
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#run_paea_container'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
When PAEA is ready results will be shown below
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#pae_results'),
        my: 'top left',
        at: 'top left'
    }];

    var tour = new Tourist.Tour({
        steps: steps,
        tipClass: 'Bootstrap',
        tipOptions:{ showEffect: 'slidein' }
    });
    tour.start();
}

$(document).ready(function() {
    window.setTimeout(initTour, 1000);
})<commit_msg>Add PAEA download to the tour<commit_after>// See: https://gist.github.com/xiaodaigh/6445698
// TODO Change to more selective solution
var swichTab = function(i) {
    tabs = $('.tabbable .nav.nav-tabs li');
    tabs.each(function() {
        $(this).removeClass('active')
    });
    $(tabs[i]).addClass('active');
    tabsContents = $('.tabbable .tab-content .tab-pane')
    tabsContents.each(function() {
        $(this).removeClass('active')
    });
    $(tabsContents[i]).addClass('active');
}

function initTour() {

    var steps = [{
            content: '
Welcome...
',
            target: [50, 120],
            nextButton: true,
            my: 'bottom left'
    }, {
        content: [
            '
Choose expression dataset to upload.
',
            '
It should contain a sinlge column with gene names ',
            'followed by the  expression profiles.
',
            '
If you want to test this app you can use ',
            '',
            'expression_example.csv.
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#datain_container'),
        my: 'bottom left',
        at: 'top center',
    }, {
        content: [
            '
Select input file separator
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#sep'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
Check if file has been loaded as expected.
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#datain_preview'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
Choose control samples
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#control_samples'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
Switch to the Characteristic Direction Analysis
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#workflow_panel'),
        my: 'top center',
        at: 'bottom center',
        teardown: function(tour, options) {
            swichTab(1);
        }
    }, {
        content: [
            '
Check input data summary
'
        ].join(''),
        nextButton: true,
        closeButton: true,
        target: $('#chdir_input_summary'),
        my: 'bottom center',
        at: 'bottom center'
    }, {
        content: [
            '
Set parameters...
'
        ].join(''),
        nextButton: true,
        closeButton: true,
        target: $('#chdir_parameters'),
        my: 'bottom center',
        at: 'bottom center'
    }, {
        content: [
            '
...and run chdir
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#run_chdir_container'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
When chdir analysis is finished ',
            'you can download the results...
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#chdir_downloads'),
        my: 'bottom center',
        at: 'bottom center'
    }, {
        content: [
            '
... and start PAE analysis
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#workflow_panel'),
        my: 'bottom center',
        at: 'bottom center',
        teardown: function(tour, options) {
            swichTab(2);
        }
    }, {
        content: [
            '
To run PAEA click Run Principle Angle Enrichment
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#run_paea_container'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
When PAEA is ready results will be shown below
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#pae_results'),
        my: 'top left',
        at: 'top left'
    },  {
        content: [
            '
... and will be available for download/p>'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#paea_downloads'),
        my: 'top left',
        at: 'top left'
    }];

    var tour = new Tourist.Tour({
        steps: steps,
        tipClass: 'Bootstrap',
        tipOptions:{ showEffect: 'slidein' }
    });
    tour.start();
}

$(document).ready(function() {
    window.setTimeout(initTour, 1000);
})<|endoftext|>


<commit_before>(function (FMS, Backbone, _, $) {
    _.extend( FMS, {
        AroundView: FMS.LocatorView.extend({
            template: 'around',
            id: 'around-page',

            events: {
                'pagehide': 'destroy',
                'pagebeforeshow': 'beforeDisplay',
                'pageshow': 'afterDisplay',
                'vclick #locate_search': 'goSearch',
                'vclick #login-options': 'goLogin',
                'vclick #view-my-reports': 'goReports',
                'vclick #search': 'goSearch',
                'vclick #relocate': 'centerMapOnPosition',
                'vclick #cancel': 'onClickCancel',
                'vclick #confirm': 'onClickReport',
                'vclick #confirm-map': 'onClickReport',
                'vclick #mark-here': 'onClickMark',
                'vclick #reposition': 'onClickReposition',
                'vclick a.address': 'goAddress',
                'submit #postcodeForm': 'search'
            },

            render: function(){
                if ( !this.template ) {
                    console.log('no template to render');
                    return;
                }
                template = _.template( tpl.get( this.template ) );
                if ( this.model ) {
                    this.$el.html(template({ model: this.model.toJSON(), user: FMS.currentUser.toJSON() }));
                } else {
                    this.$el.html(template());
                }
                this.afterRender();
                return this;
            },

            beforeDisplay: function() {
                $('a[data-role="button"]').hide();
                $('#cancel').hide();
            },

            afterDisplay: function() {
                if ( FMS.isOffline ) {
                    this.navigate( 'offline' );
                } else if ( this.model && this.model.get('lat') ) {
                    var modelInfo = { coordinates: { latitude: this.model.get('lat'), longitude: this.model.get('lon') } };
                    this.setMapPosition(modelInfo);
                    this.displayButtons(true);
                    this.setReportPosition({ lat: this.model.get('lat'), lon: this.model.get('lon') }, true);
                    this.listenTo(FMS.locator, 'gps_current_position', this.positionUpdate);
                } else if ( FMS.currentPosition ) {
                    var info = { coordinates: FMS.currentPosition };
                    FMS.currentPosition = null;
                    this.setMapPosition(info);
                    this.displayButtons(false);
                    this.listenTo(FMS.locator, 'gps_current_position', this.positionUpdate);
                } else {
                    this.locate();
                }
            },

            setMapPosition: function( info ) {
                var coords = info.coordinates;
                fixmystreet.latitude = coords.latitude;
                fixmystreet.longitude = coords.longitude;

                if ( !fixmystreet.map ) {
                    show_map();
                } else {
                    FMS.currentPosition = coords;
                    var centre = this.projectCoords( coords );
                    fixmystreet.map.panTo(centre);
                }
            },

            gotLocation: function( info ) {
                this.finishedLocating();

                this.listenTo(FMS.locator, 'gps_current_position', this.positionUpdate);

                this.located = true;
                this.locateCount = 21;

                this.setMapPosition( info );

                FMS.locator.trackPosition();
                // FIXME: not sure why I need to do this
                fixmystreet.select_feature.deactivate();
                fixmystreet.select_feature.activate();
                fixmystreet.nav.activate();
            },

            positionUpdate: function( info ) {
                FMS.currentPosition = info.coordinates;
                var centre = this.projectCoords( info.coordinates );

                var point = new OpenLayers.Geometry.Point( centre.lon, centre.lat );

                fixmystreet.location.removeAllFeatures();
                    var x = new OpenLayers.Feature.Vector(
                        point,
                        {},
                        {
                            graphicZIndex: 3000,
                            graphicName: 'circle',
                            'externalGraphic': 'images/gps-marker.svg',
                            pointRadius: 16
                        }
                    );
                fixmystreet.location.addFeatures([ x ]);
            },

            centerMapOnPosition: function(e) {
                e.preventDefault();
                if ( !fixmystreet.map ) {
                    return;
                }

                // if there isn't a currentPosition then something
                // is up so we probably should not recenter
                if ( FMS.currentPosition ) {
                    fixmystreet.map.panTo(this.projectCoords( FMS.currentPosition ));
                }
            },

            failedLocation: function( details ) {
                this.finishedLocating();
                this.locateCount = 21;
                var msg = '';
                if ( details.msg ) {
                    msg = details.msg;
                } else {
                    msg = FMS.strings.location_problem;
                }
                $('#front-howto').html('
' + msg + '</msg>');
                $('#relocate').hide(); // TODO: not sure we want to do this
                $('#front-howto').show();
            },

            displayButtons: function(isLocationSet) {
                $('#relocate').show();
                if (isLocationSet) {
                    $('#cancel').addClass('ui-btn-left').show();
                    $('#confirm').addClass('ui-btn-right ui-btn-icon-right').show();
                    $('#confirm-map').show();
                    $('#view-my-reports').hide();
                    $('#login-options').hide();
                    $('#mark-here').hide();
                    $('#postcodeForm').hide();
                    fixmystreet.markers.setVisibility(false);
                    fixmystreet.select_feature.deactivate();
                    fixmystreet.bbox_strategy.deactivate();
                } else {
                    $('#cancel').hide().removeClass('ui-btn-left');
                    $('#confirm').hide().removeClass('ui-btn-right ui-btn-icon-right');
                    $('#confirm-map').hide();
                    $('#view-my-reports').show();
                    $('#login-options').show();
                    $('#mark-here').show();
                    $('#postcodeForm').show();
                    $('#reposition').hide();
                    fixmystreet.bbox_strategy.activate();
                    fixmystreet.report_location.setVisibility(false);
                    fixmystreet.markers.setVisibility(true);
                    fixmystreet.select_feature.activate();
                }
            },

            setReportPosition: function(lonlat, convertPosition) {
                var markers = fms_markers_list( [ [ lonlat.lat, lonlat.lon, 'green', 'location', '', 'location' ] ], convertPosition );
                fixmystreet.report_location.removeAllFeatures();
                fixmystreet.report_location.addFeatures( markers );
                fixmystreet.report_location.setVisibility(true);
            },

            onClickMark: function(e) {
                e.preventDefault();
                this.displayButtons(true);
                $('#reposition').hide();

                var lonlat = this.getCrossHairPosition();
                this.setReportPosition(lonlat, true);
            },

            onClickCancel: function(e) {
                e.preventDefault();
                fixmystreet.markers.removeAllFeatures();
                fixmystreet_activate_drag();
                this.displayButtons(false);
                if ( this.model.isPartial() ) {
                    FMS.clearCurrentDraft();
                } else {
                    this.model.set('lat', null);
                    this.model.set('lon', null);
                }
            },

            onClickReposition: function(e) {
                e.preventDefault();
                var lonlat = this.getCrossHairPosition();
                lonlat.transform(
                    new OpenLayers.Projection("EPSG:4326"),
                    fixmystreet.map.getProjectionObject()
                );
                fixmystreet.report_location.features[0].move(lonlat);
                $('#reposition').hide();
            },

           onClickReport: function() {
                var position = this.getCrossHairPosition();

                if ( FMS.isOffline ) {
                    this.stopListening(FMS.locator);
                    FMS.locator.stopTracking();
                    // these may be out of the area but lets just save them
                    // for now and they can be checked when we are online.
                    this.model.set('lat', position.lat );
                    this.model.set('lon', position.lon );
                    FMS.saveCurrentDraft();
                    this.navigate( 'offline' );
                } else {
                    this.listenTo(FMS.locator, 'gps_located', this.goPhoto);
                    this.listenTo(FMS.locator, 'gps_failed', this.noMap );
                    FMS.locator.check_location( { latitude: position.lat, longitude: position.lon } );
                }
            },

            search: function(e) {
                $('#pc').blur();
                // this is to stop form submission
                e.preventDefault();
                $('#front-howto').hide();
                this.clearValidationErrors();
                var pc = this.$('#pc').val();
                this.listenTo(FMS.locator, 'search_located', this.searchSuccess );
                this.listenTo(FMS.locator, 'search_failed', this.searchFail);

                FMS.locator.lookup(pc);
            },

            searchSuccess: function( info ) {
                this.stopListening(FMS.locator);
                var coords = info.coordinates;
                if ( fixmystreet.map ) {
                    fixmystreet.map.panTo(this.projectCoords( coords ));
                } else {
                    this.setMapPosition(info);
                }
            },

            goAddress: function(e) {
                $('#relocate').show();
                $('#front-howto').html('').hide();
                var t = $(e.target);
                var lat = t.attr('data-lat');
                var long = t.attr('data-long');

                var coords  = { latitude: lat, longitude: long };
                if ( fixmystreet.map ) {
                    fixmystreet.map.panTo(this.projectCoords( coords ));
                } else {
                    this.setMapPosition({ coordinates: coords });
                }
            },

            searchFail: function( details ) {
                // this makes sure any onscreen keyboard is dismissed
                $('#submit').focus();
                this.stopListening(FMS.locator);
                if ( details.msg ) {
                    this.validationError( 'pc', details.msg );
                } else if ( details.locations ) {
                    var multiple = '';
                    for ( var i = 0; i < details.locations.length; i++ ) {
                        var loc = details.locations[i];
                        var li = '
' + loc.address + '
';
                        multiple = multiple + li;
                    }
                    $('#front-howto').html('
Multiple matches found

' + multiple + '
');
                    $('.ui-page').trigger('create');
                    $('#relocate').hide();
                    $('#front-howto').show();
                } else {
                    this.validationError( 'pc', FMS.strings.location_problem );
                }
            },

            goPhoto: function(info) {
                this.stopListening(FMS.locator);
                FMS.locator.stopTracking();
                this.model.set('lat', info.coordinates.latitude );
                this.model.set('lon', info.coordinates.longitude );
                this.model.set('categories', info.details.category );
                if ( info.details.title_list ) {
                    this.model.set('title_list', info.details.title_list);
                }
                FMS.saveCurrentDraft();
                fixmystreet.nav.deactivate();

                this.navigate( 'photo' );
            },

            goSearch: function(e) {
                e.preventDefault();
                this.stopListening(FMS.locator);
                FMS.locator.stopTracking();
                this.navigate( 'search' );
            },

            goLogin: function(e) {
                e.preventDefault();
                this.stopListening(FMS.locator);
                FMS.locator.stopTracking();
                this.navigate( 'login' );
            },

            goReports: function(e) {
                e.preventDefault();
                this.stopListening(FMS.locator);
                FMS.locator.stopTracking();
                this.navigate( 'reports' );
            },

            getCrossHairPosition: function() {
                var cross = fixmystreet.map.getControlsByClass(
                "OpenLayers.Control.Crosshairs");

                var position = cross[0].getMapPosition();
                position.transform(
                    fixmystreet.map.getProjectionObject(),
                    new OpenLayers.Projection("EPSG:4326")
                );

                return position;
            },

            projectCoords: function( coords ) {
                var centre = new OpenLayers.LonLat( coords.longitude, coords.latitude );
                centre.transform(
                    new OpenLayers.Projection("EPSG:4326"),
                    fixmystreet.map.getProjectionObject()
                );

                return centre;
            }
        })
    });
})(FMS, Backbone, _, $);
<commit_msg>check map has been created before fiddling with the features on it<commit_after>(function (FMS, Backbone, _, $) {
    _.extend( FMS, {
        AroundView: FMS.LocatorView.extend({
            template: 'around',
            id: 'around-page',

            events: {
                'pagehide': 'destroy',
                'pagebeforeshow': 'beforeDisplay',
                'pageshow': 'afterDisplay',
                'vclick #locate_search': 'goSearch',
                'vclick #login-options': 'goLogin',
                'vclick #view-my-reports': 'goReports',
                'vclick #search': 'goSearch',
                'vclick #relocate': 'centerMapOnPosition',
                'vclick #cancel': 'onClickCancel',
                'vclick #confirm': 'onClickReport',
                'vclick #confirm-map': 'onClickReport',
                'vclick #mark-here': 'onClickMark',
                'vclick #reposition': 'onClickReposition',
                'vclick a.address': 'goAddress',
                'submit #postcodeForm': 'search'
            },

            render: function(){
                if ( !this.template ) {
                    console.log('no template to render');
                    return;
                }
                template = _.template( tpl.get( this.template ) );
                if ( this.model ) {
                    this.$el.html(template({ model: this.model.toJSON(), user: FMS.currentUser.toJSON() }));
                } else {
                    this.$el.html(template());
                }
                this.afterRender();
                return this;
            },

            beforeDisplay: function() {
                $('a[data-role="button"]').hide();
                $('#cancel').hide();
            },

            afterDisplay: function() {
                if ( FMS.isOffline ) {
                    this.navigate( 'offline' );
                } else if ( this.model && this.model.get('lat') ) {
                    var modelInfo = { coordinates: { latitude: this.model.get('lat'), longitude: this.model.get('lon') } };
                    this.setMapPosition(modelInfo);
                    this.displayButtons(true);
                    this.setReportPosition({ lat: this.model.get('lat'), lon: this.model.get('lon') }, true);
                    this.listenTo(FMS.locator, 'gps_current_position', this.positionUpdate);
                } else if ( FMS.currentPosition ) {
                    var info = { coordinates: FMS.currentPosition };
                    FMS.currentPosition = null;
                    this.setMapPosition(info);
                    this.displayButtons(false);
                    this.listenTo(FMS.locator, 'gps_current_position', this.positionUpdate);
                } else {
                    this.locate();
                }
            },

            setMapPosition: function( info ) {
                var coords = info.coordinates;
                fixmystreet.latitude = coords.latitude;
                fixmystreet.longitude = coords.longitude;

                if ( !fixmystreet.map ) {
                    show_map();
                } else {
                    FMS.currentPosition = coords;
                    var centre = this.projectCoords( coords );
                    fixmystreet.map.panTo(centre);
                }
            },

            gotLocation: function( info ) {
                this.finishedLocating();

                this.listenTo(FMS.locator, 'gps_current_position', this.positionUpdate);

                this.located = true;
                this.locateCount = 21;

                this.setMapPosition( info );

                FMS.locator.trackPosition();
                // FIXME: not sure why I need to do this
                fixmystreet.select_feature.deactivate();
                fixmystreet.select_feature.activate();
                fixmystreet.nav.activate();
            },

            positionUpdate: function( info ) {
                FMS.currentPosition = info.coordinates;
                var centre = this.projectCoords( info.coordinates );

                var point = new OpenLayers.Geometry.Point( centre.lon, centre.lat );

                fixmystreet.location.removeAllFeatures();
                    var x = new OpenLayers.Feature.Vector(
                        point,
                        {},
                        {
                            graphicZIndex: 3000,
                            graphicName: 'circle',
                            'externalGraphic': 'images/gps-marker.svg',
                            pointRadius: 16
                        }
                    );
                fixmystreet.location.addFeatures([ x ]);
            },

            centerMapOnPosition: function(e) {
                e.preventDefault();
                if ( !fixmystreet.map ) {
                    return;
                }

                // if there isn't a currentPosition then something
                // is up so we probably should not recenter
                if ( FMS.currentPosition ) {
                    fixmystreet.map.panTo(this.projectCoords( FMS.currentPosition ));
                }
            },

            failedLocation: function( details ) {
                this.finishedLocating();
                this.locateCount = 21;
                var msg = '';
                if ( details.msg ) {
                    msg = details.msg;
                } else {
                    msg = FMS.strings.location_problem;
                }
                $('#front-howto').html('
' + msg + '</msg>');
                $('#relocate').hide(); // TODO: not sure we want to do this
                $('#front-howto').show();
            },

            displayButtons: function(isLocationSet) {
                $('#relocate').show();
                if (isLocationSet) {
                    $('#cancel').addClass('ui-btn-left').show();
                    $('#confirm').addClass('ui-btn-right ui-btn-icon-right').show();
                    $('#confirm-map').show();
                    $('#view-my-reports').hide();
                    $('#login-options').hide();
                    $('#mark-here').hide();
                    $('#postcodeForm').hide();
                    if ( fixmystreet.map ) {
                        fixmystreet.markers.setVisibility(false);
                        fixmystreet.select_feature.deactivate();
                        fixmystreet.bbox_strategy.deactivate();
                    }
                } else {
                    $('#cancel').hide().removeClass('ui-btn-left');
                    $('#confirm').hide().removeClass('ui-btn-right ui-btn-icon-right');
                    $('#confirm-map').hide();
                    $('#view-my-reports').show();
                    $('#login-options').show();
                    $('#mark-here').show();
                    $('#postcodeForm').show();
                    $('#reposition').hide();
                    if ( fixmystreet.map ) {
                        fixmystreet.bbox_strategy.activate();
                        fixmystreet.report_location.setVisibility(false);
                        fixmystreet.markers.setVisibility(true);
                        fixmystreet.select_feature.activate();
                    }
                }
            },

            setReportPosition: function(lonlat, convertPosition) {
                var markers = fms_markers_list( [ [ lonlat.lat, lonlat.lon, 'green', 'location', '', 'location' ] ], convertPosition );
                fixmystreet.report_location.removeAllFeatures();
                fixmystreet.report_location.addFeatures( markers );
                fixmystreet.report_location.setVisibility(true);
            },

            onClickMark: function(e) {
                e.preventDefault();
                this.displayButtons(true);
                $('#reposition').hide();

                var lonlat = this.getCrossHairPosition();
                this.setReportPosition(lonlat, true);
            },

            onClickCancel: function(e) {
                e.preventDefault();
                fixmystreet.markers.removeAllFeatures();
                fixmystreet_activate_drag();
                this.displayButtons(false);
                if ( this.model.isPartial() ) {
                    FMS.clearCurrentDraft();
                } else {
                    this.model.set('lat', null);
                    this.model.set('lon', null);
                }
            },

            onClickReposition: function(e) {
                e.preventDefault();
                var lonlat = this.getCrossHairPosition();
                lonlat.transform(
                    new OpenLayers.Projection("EPSG:4326"),
                    fixmystreet.map.getProjectionObject()
                );
                fixmystreet.report_location.features[0].move(lonlat);
                $('#reposition').hide();
            },

           onClickReport: function() {
                var position = this.getCrossHairPosition();

                if ( FMS.isOffline ) {
                    this.stopListening(FMS.locator);
                    FMS.locator.stopTracking();
                    // these may be out of the area but lets just save them
                    // for now and they can be checked when we are online.
                    this.model.set('lat', position.lat );
                    this.model.set('lon', position.lon );
                    FMS.saveCurrentDraft();
                    this.navigate( 'offline' );
                } else {
                    this.listenTo(FMS.locator, 'gps_located', this.goPhoto);
                    this.listenTo(FMS.locator, 'gps_failed', this.noMap );
                    FMS.locator.check_location( { latitude: position.lat, longitude: position.lon } );
                }
            },

            search: function(e) {
                $('#pc').blur();
                // this is to stop form submission
                e.preventDefault();
                $('#front-howto').hide();
                this.clearValidationErrors();
                var pc = this.$('#pc').val();
                this.listenTo(FMS.locator, 'search_located', this.searchSuccess );
                this.listenTo(FMS.locator, 'search_failed', this.searchFail);

                FMS.locator.lookup(pc);
            },

            searchSuccess: function( info ) {
                this.stopListening(FMS.locator);
                var coords = info.coordinates;
                if ( fixmystreet.map ) {
                    fixmystreet.map.panTo(this.projectCoords( coords ));
                } else {
                    this.setMapPosition(info);
                }
            },

            goAddress: function(e) {
                $('#relocate').show();
                $('#front-howto').html('').hide();
                var t = $(e.target);
                var lat = t.attr('data-lat');
                var long = t.attr('data-long');

                var coords  = { latitude: lat, longitude: long };
                if ( fixmystreet.map ) {
                    fixmystreet.map.panTo(this.projectCoords( coords ));
                } else {
                    this.setMapPosition({ coordinates: coords });
                }
            },

            searchFail: function( details ) {
                // this makes sure any onscreen keyboard is dismissed
                $('#submit').focus();
                this.stopListening(FMS.locator);
                if ( details.msg ) {
                    this.validationError( 'pc', details.msg );
                } else if ( details.locations ) {
                    var multiple = '';
                    for ( var i = 0; i < details.locations.length; i++ ) {
                        var loc = details.locations[i];
                        var li = '
' + loc.address + '
';
                        multiple = multiple + li;
                    }
                    $('#front-howto').html('
Multiple matches found

' + multiple + '
');
                    $('.ui-page').trigger('create');
                    $('#relocate').hide();
                    $('#front-howto').show();
                } else {
                    this.validationError( 'pc', FMS.strings.location_problem );
                }
            },

            goPhoto: function(info) {
                this.stopListening(FMS.locator);
                FMS.locator.stopTracking();
                this.model.set('lat', info.coordinates.latitude );
                this.model.set('lon', info.coordinates.longitude );
                this.model.set('categories', info.details.category );
                if ( info.details.title_list ) {
                    this.model.set('title_list', info.details.title_list);
                }
                FMS.saveCurrentDraft();
                fixmystreet.nav.deactivate();

                this.navigate( 'photo' );
            },

            goSearch: function(e) {
                e.preventDefault();
                this.stopListening(FMS.locator);
                FMS.locator.stopTracking();
                this.navigate( 'search' );
            },

            goLogin: function(e) {
                e.preventDefault();
                this.stopListening(FMS.locator);
                FMS.locator.stopTracking();
                this.navigate( 'login' );
            },

            goReports: function(e) {
                e.preventDefault();
                this.stopListening(FMS.locator);
                FMS.locator.stopTracking();
                this.navigate( 'reports' );
            },

            getCrossHairPosition: function() {
                var cross = fixmystreet.map.getControlsByClass(
                "OpenLayers.Control.Crosshairs");

                var position = cross[0].getMapPosition();
                position.transform(
                    fixmystreet.map.getProjectionObject(),
                    new OpenLayers.Projection("EPSG:4326")
                );

                return position;
            },

            projectCoords: function( coords ) {
                var centre = new OpenLayers.LonLat( coords.longitude, coords.latitude );
                centre.transform(
                    new OpenLayers.Projection("EPSG:4326"),
                    fixmystreet.map.getProjectionObject()
                );

                return centre;
            }
        })
    });
})(FMS, Backbone, _, $);
<|endoftext|>


<commit_before>/* jshint -W097 */// jshint strict:true
/* jslint vars: true */
/* jslint browser:true */
/* jslint devel:true */
/* jshint browser:true */
/* jshint devel:true */
/* jshint jquery:true */
/* global io:false */
/* global jQuery:false */
/* global $:false */

/**
 *
 * @constructor
 * @param {Object} main
 * @returns {Logs}
 */
function Logs(main) {
    'use strict';

    var that = this;
    this.main = main;

    this.menuIcon = 'fa-file-text-o';
    this.logLimit = 2000; //const

    this.logLinesCount = 0;
    this.logLinesStart = 0;
    this.errorCount = 0;
    this.logSize = 0;
    this.logHosts = [];
    this.logFilterTimeout = null;
    this.logFilterHost = '';
    this.logFilterSeverity = '';
    this.logFilterMessage = '';

    this.logPauseList = [];
    this.logPauseMode = false;
    this.logPauseOverflow = false;
    this.logPauseCounterSpan = null;
    this.logPauseCounter = [];

    this.prepare = function () {

        $('#menu-logs-div').load("templates/log.html", function () {

            $('#log-outer').find('[data-i18n]').i18n();
            $('#log-outer').bootstrapTable();

            $('#log-filter-severity, #log-filter-host').on('change', function () {
                that.filter();
            });

            $('#log-filter-message').change(function () {
                if (that.logFilterTimeout) {
                    clearTimeout(that.logFilterTimeout);
                }
                that.logFilterTimeout = setTimeout(that.filter, 600);
            }).keyup(function (e) {
                if (e.which === 13) {
                    that.filter();
                } else {
                    $(this).trigger('change');
                }
            });

            $('#log-filter-message-clear').click(function () {
                var $log_filter = $('#log-filter-message');
                if ($log_filter.val() !== '') {
                    $log_filter.val('').trigger('change');
                }
            });

            $('#log-clear-on-disk').click(function () {
                that.main.confirmMessage($.i18n('logfileDeletedQuestion'), null, null, function (result) {
                    if (result) {
                        that.main.socket.emit('sendToHost', main.currentHost, 'delLogs', null, function (err) {
                            if (err) {
                                that.main.showError(err);
                            } else {
                                that.clear();
                            }
                        });
                    }
                });
            });

            $('#log-refresh').click(function () {
                that.clear();
            });

            $('#log-pause').click(function () {
                that.pause();
            });

            this.logPauseCounterSpan = $('#log-pause .fa');

            $('#log-clear').click(function () {
                that.clear(false);
            });

            new Clipboard('#log-copy').on('success', function (e) {
                alert($.i18n('log-copy-success'), 'success');
                e.clearSelection();
            }).on('error', function (e) {
                alert($.i18n('log-copy-error'), 'error');
            });

            if (that.main.isServiceWorker) {
                $('#log-subscribe').prop('disabled', false);
                if (main.isLogSubscribed) {
                    $('#log-subscribe').removeClass('btn-default').addClass('btn-primary').changeTooltip($.i18n('unsubscribeLog'));
                } else {
                    $('#log-subscribe').addClass('btn-default').removeClass('btn-primary').changeTooltip($.i18n('subscribeLog'));
                }
            }

        });

    };

    // -------------------------------- Logs ------------------------------------------------------------
    this.init = function () {
        if (!this.main.currentHost) {
            setTimeout(function () {
                that.init();
            }, 500);
            return;
        }

        $('#log-table').html('');

        this.main.socket.emit('sendToHost', this.main.currentHost, 'getLogs', 200, function (lines) {
            setTimeout(function () {
                var size = lines ? lines.pop() : -1;
                if (size !== -1) {
                    size = parseInt(size);
                    that.logSize = (size / (1024 * 1024)).toFixed(2);
                    $('#log-size').html($.i18n('logsize') + ': ' + (that.logSize + ' MB ').replace(/ /g, ' '));
                }

                that.errorCount = 0;
                $(".errorLogInfo").remove();

                for (var i = 0; i < lines.length; i++) {
                    if (!lines[i]) {
                        continue;
                    }
                    var message = {message: '', severity: 'debug', from: '', ts: ''};
                    // 2014-12-05 14:47:10.739 - info: iobroker  ERR! network In most cases you are behind a proxy or have bad network settings.npm ERR! network
                    if (lines[i][4] === '-' && lines[i][7] === '-') {
                        lines[i] = lines[i].replace(/(\[[0-9]+m)/g, '');
                        message.ts = lines[i].substring(0, 23);
                        lines[i] = lines[i].substring(27);

                        var pos = lines[i].indexOf(':');
                        message.severity = lines[i].substring(0, pos);
                        if (message.severity.charCodeAt(message.severity.length - 1) === 27) {
                            message.severity = message.severity.substring(0, message.severity.length - 1);
                        }
                        if (message.severity.charCodeAt(0) === 27) {
                            message.severity = message.severity.substring(1);
                        }

                        lines[i] = lines[i].substring(pos + 2);
                        pos = lines[i].indexOf(' ');
                        message.from = lines[i].substring(0, pos);
                        message.message = lines[i].substring(pos);
                    } else {
                        message.message = lines[i];
                    }
                    that.add(message);
                }
                restartFunctions('#log_error_list');

                that.logFilterHost = $('#log-filter-host').val();
                that.logFilterMessage = $('#log-filter-message').val();
                that.logFilterSeverity = $('#log-filter-severity').val();
            }, 0);
        });

        this.main.fillContent('#menu-logs-div');
    };

    this.add = function (message) {
        // remove instance name from text
        if (message.message.substring(0, message.from.length) === message.from) {
            message.message = message.message.substring(message.from.length + 1);
        }

        if (this.logPauseMode) {
            this.logPauseList.push(message);
            this.logPauseCounter++;

            if (this.logPauseCounter > this.logLimit) {
                if (!this.logPauseOverflow) {
                    $('#log-pause').switchClass('btn-default', 'btn-danger').changeTooltip($.i18n('mboLosing'));
                    this.logPauseOverflow = true;
                }
                this.logPauseList.shift();
            }
            this.logPauseCounterSpan.html(this.logPauseCounter);
            return;
        }
        //message = {message: msg, severity: level, from: this.namespace, ts: (new Date()).getTime()}
        if (this.logLinesCount >= this.logLimit) {
            var line = document.getElementById('log-line-' + (this.logLinesStart + 1));
            if (line) {
                line.outerHTML = '';
            }
            this.logLinesStart++;
        } else {
            this.logLinesCount++;
        }

        if (message.from && this.logHosts.indexOf(message.from) === -1) {
            this.logHosts.push(message.from);
            this.logHosts.sort();

            if ($('#log-filter-host')) {
                $('#log-filter-host')
                        .append('<option value="' + message.from.replace(/\./g, '-') + '" ' + ((message.from === this.logFilterHost) ? 'selected' : '') + '>' + message.from + '</option>')
                        .selectpicker('refresh');
            }
        }
        var visible = '';
        var from = message.from ? message.from.replace(/\./g, '-') : '';

        if (this.logFilterHost && this.logFilterHost !== from) {
            visible = 'display: none';
        }

        if (!visible && this.logFilterSeverity) {
            if (this.logFilterSeverity === 'info' && message.severity === 'debug') {
                visible = 'display: none';
            } else if (this.logFilterSeverity === 'warn' && message.severity !== 'warn' && message.severity !== 'error') {
                visible = 'display: none';
            } else if (this.logFilterSeverity === 'error' && message.severity !== 'error') {
                visible = 'display: none';
            }
        }

        if (!visible && this.logFilterMessage && message.message.indexOf(that.logFilterMessage) === -1) {
            visible = 'display: none';
        }

        if (message.severity === 'error') {
            $('a[href="#tab-log"]').addClass('errorLog');
        }

        var text = '
';
        text += '' + (message.from || '') + '	';
        text += '' + this.main.formatDate(message.ts) + '	';
        text += '' + $.i18n(message.severity) + '	';
        text += '' + message.message.substring(0, 200) + '
';

        if (message.severity === "error") {
            this.errorCount += 1;
            $('#log_error_count').text(this.errorCount);
            if (this.errorCount === 1) {
                $('#log_error_count').removeClass('bg-green').addClass('bg-red');
            }
            if (this.errorCount === 0) {
                $('#log_error_count').addClass('bg-green').removeClass('bg-red');
            }
            if (this.errorCount > 10) {
                $('#log_error_list li:nth-last-child(2)').remove();
            }
            var count = (message.from || '').length + message.message.length;
            $('#log_error_list')
                    .prepend('
 30 ? 'title="' + message.message.substring(0, 200) + '" data-toggle="tooltip"' : '') + '>' + (message.from || '') + ' - ' + message.message.substring(0, 20) + (count > 30 ? '...' : '') + '
');
        }

        $('#log-table').prepend(text);
    };

    this.filter = function () {
        if (that.logFilterTimeout) {
            clearTimeout(that.logFilterTimeout);
            that.logFilterTimeout = null;
        }
        var $logOuter = $('#log-outer');

        that.logFilterHost = $('#log-filter-host').val();
        that.logFilterMessage = $('#log-filter-message').val();
        that.logFilterSeverity = $('#log-filter-severity').val();

        if (that.logFilterSeverity === 'error') {
            $logOuter.find('.log-severity-debug').hide();
            $logOuter.find('.log-severity-info').hide();
            $logOuter.find('.log-severity-warn').hide();
            $logOuter.find('.log-severity-error').show();
        } else if (that.logFilterSeverity === 'warn') {
            $logOuter.find('.log-severity-debug').hide();
            $logOuter.find('.log-severity-info').hide();
            $logOuter.find('.log-severity-warn').show();
            $logOuter.find('.log-severity-error').show();
        } else if (that.logFilterSeverity === 'info') {
            $logOuter.find('.log-severity-debug').hide();
            $logOuter.find('.log-severity-info').show();
            $logOuter.find('.log-severity-warn').show();
            $logOuter.find('.log-severity-error').show();
        } else {
            $logOuter.find('.log-severity-debug').show();
            $logOuter.find('.log-severity-info').show();
            $logOuter.find('.log-severity-warn').show();
            $logOuter.find('.log-severity-error').show();
        }
        if (that.logFilterHost || that.logFilterMessage) {
            $logOuter.find('.log-line').each(function () {
                if (that.logFilterHost && !$(this).hasClass('log-from-' + that.logFilterHost)) {
                    $(this).hide();
                } else if (that.logFilterMessage && $(this).html().indexOf(that.logFilterMessage) === -1) {
                    $(this).hide();
                }
            });
        }
    };

    this.clear = function (isReload) {
        if (isReload === undefined) {
            isReload = true;
        }
        $('#log-table').html('');
        this.logLinesCount = 0;
        this.logLinesStart = 0;
        $('#menu-logs').removeClass('text-danger');

        if (isReload) {
            setTimeout(function () {
                that.init();
            }, 0);
        }
    };

    this.pause = function () {
        var $logPause = $('#log-pause');
        if (!this.logPauseMode) {
            $logPause
                    .addClass('active')
                    .find('.fa')
                    .removeClass('fa-pause')
                    .html('0');

            this.logPauseCounter = 0;
            this.logPauseMode = true;
        } else {
            this.logPauseMode = false;
            for (var i = 0; i < this.logPauseList.length; i++) {
                this.add(this.logPauseList[i]);
            }
            restartFunctions('#log_error_list');
            this.logPauseOverflow = false;
            this.logPauseList = [];
            this.logPauseCounter = 0;

            $logPause
                    .removeClass('active')
                    .removeClass('btn-danger')
                    .addClass('btn-default');
        }
    };
}<commit_msg>fix util.js usage and use utils.Adapter<commit_after>/* jshint -W097 */// jshint strict:true
/* jslint vars: true */
/* jslint browser:true */
/* jslint devel:true */
/* jshint browser:true */
/* jshint devel:true */
/* jshint jquery:true */
/* global io:false */
/* global jQuery:false */
/* global $:false */

/**
 *
 * @constructor
 * @param {Object} main
 * @returns {Logs}
 */
function Logs(main) {
    'use strict';

    var that = this;
    this.main = main;

    this.menuIcon = 'fa-file-text-o';
    this.logLimit = 2000; //const

    this.logLinesCount = 0;
    this.logLinesStart = 0;
    this.errorCount = 0;
    this.logSize = 0;
    this.logHosts = [];
    this.logFilterTimeout = null;
    this.logFilterHost = '';
    this.logFilterSeverity = '';
    this.logFilterMessage = '';

    this.logPauseList = [];
    this.logPauseMode = false;
    this.logPauseOverflow = false;
    this.logPauseCounterSpan = null;
    this.logPauseCounter = [];

    this.prepare = function () {

        $('#menu-logs-div').load("templates/log.html", function () {

            $('#log-outer').find('[data-i18n]').i18n();
            $('#log-outer').bootstrapTable();

            $('#log-filter-severity, #log-filter-host').on('change', function () {
                that.filter();
            });

            $('#log-filter-message').change(function () {
                if (that.logFilterTimeout) {
                    clearTimeout(that.logFilterTimeout);
                }
                that.logFilterTimeout = setTimeout(that.filter, 600);
            }).keyup(function (e) {
                if (e.which === 13) {
                    that.filter();
                } else {
                    $(this).trigger('change');
                }
            });

            $('#log-filter-message-clear').click(function () {
                var $log_filter = $('#log-filter-message');
                if ($log_filter.val() !== '') {
                    $log_filter.val('').trigger('change');
                }
            });

            $('#log-clear-on-disk').click(function () {
                that.main.confirmMessage($.i18n('logfileDeletedQuestion'), null, null, function (result) {
                    if (result) {
                        that.main.socket.emit('sendToHost', main.currentHost, 'delLogs', null, function (err) {
                            if (err) {
                                that.main.showError(err);
                            } else {
                                that.clear();
                            }
                        });
                    }
                });
            });

            $('#log-refresh').click(function () {
                that.clear();
            });

            $('#log-pause').click(function () {
                that.pause();
            });

            this.logPauseCounterSpan = $('#log-pause .fa');

            $('#log-clear').click(function () {
                that.clear(false);
            });

            new Clipboard('#log-copy').on('success', function (e) {
                alert($.i18n('log-copy-success'), 'success');
                e.clearSelection();
            }).on('error', function (e) {
                alert($.i18n('log-copy-error'), 'error');
            });

            if (that.main.isServiceWorker) {
                $('#log-subscribe').prop('disabled', false);
                if (main.isLogSubscribed) {
                    $('#log-subscribe').removeClass('btn-default').addClass('btn-primary').changeTooltip($.i18n('unsubscribeLog'));
                } else {
                    $('#log-subscribe').addClass('btn-default').removeClass('btn-primary').changeTooltip($.i18n('subscribeLog'));
                }
            }

        });

    };

    // -------------------------------- Logs ------------------------------------------------------------
    this.init = function () {
        if (!this.main.currentHost) {
            setTimeout(function () {
                that.init();
            }, 500);
            return;
        }

        $('#log-table').html('');

        this.main.socket.emit('sendToHost', this.main.currentHost, 'getLogs', 200, function (lines) {
            setTimeout(function () {
                var size = lines ? lines.pop() : -1;
                if (size !== -1) {
                    size = parseInt(size);
                    that.logSize = (size / (1024 * 1024)).toFixed(2);
                    $('#log-size').html($.i18n('logsize') + ': ' + (that.logSize + ' MB ').replace(/ /g, ' '));
                }

                that.errorCount = 0;
                $(".errorLogInfo").remove();

                for (var i = 0; i < lines.length; i++) {
                    if (!lines[i]) {
                        continue;
                    }
                    var message = {message: '', severity: 'debug', from: '', ts: ''};
                    // 2014-12-05 14:47:10.739 - info: iobroker  ERR! network In most cases you are behind a proxy or have bad network settings.npm ERR! network
                    if (lines[i][4] === '-' && lines[i][7] === '-') {
                        lines[i] = lines[i].replace(/(\[[0-9]+m)/g, '');
                        message.ts = lines[i].substring(0, 23);
                        lines[i] = lines[i].substring(27);

                        var pos = lines[i].indexOf(':');
                        message.severity = lines[i].substring(0, pos);
                        if (message.severity.charCodeAt(message.severity.length - 1) === 27) {
                            message.severity = message.severity.substring(0, message.severity.length - 1);
                        }
                        if (message.severity.charCodeAt(0) === 27) {
                            message.severity = message.severity.substring(1);
                        }

                        lines[i] = lines[i].substring(pos + 2);
                        pos = lines[i].indexOf(' ');
                        message.from = lines[i].substring(0, pos);
                        message.message = lines[i].substring(pos);
                    } else {
                        message.message = lines[i];
                    }
                    that.add(message);
                }
                restartFunctions('#log_error_list');

                that.logFilterHost = $('#log-filter-host').val();
                that.logFilterMessage = $('#log-filter-message').val();
                that.logFilterSeverity = $('#log-filter-severity').val();
            }, 0);
        });

        this.main.fillContent('#menu-logs-div');
    };

    this.add = function (message) {
        // remove instance name from text
        if (message.message.substring(0, message.from.length) === message.from) {
            message.message = message.message.substring(message.from.length + 1);
        }

        if (this.logPauseMode) {
            this.logPauseList.push(message);
            this.logPauseCounter++;

            if (this.logPauseCounter > this.logLimit) {
                if (!this.logPauseOverflow) {
                    $('#log-pause').switchClass('btn-default', 'btn-danger').changeTooltip($.i18n('mboLosing'));
                    this.logPauseOverflow = true;
                }
                this.logPauseList.shift();
            }
            this.logPauseCounterSpan.html(this.logPauseCounter);
            return;
        }
        //message = {message: msg, severity: level, from: this.namespace, ts: (new Date()).getTime()}
        if (this.logLinesCount >= this.logLimit) {
            var line = document.getElementById('log-line-' + (this.logLinesStart + 1));
            if (line) {
                line.outerHTML = '';
            }
            this.logLinesStart++;
        } else {
            this.logLinesCount++;
        }

        if (message.from && this.logHosts.indexOf(message.from) === -1) {
            this.logHosts.push(message.from);
            this.logHosts.sort();

            if ($('#log-filter-host')) {
                $('#log-filter-host')
                        .append('<option value="' + message.from.replace(/\./g, '-') + '" ' + ((message.from === this.logFilterHost) ? 'selected' : '') + '>' + message.from + '</option>')
                        .selectpicker('refresh');
            }
        }
        var visible = '';
        var from = message.from ? message.from.replace(/\./g, '-') : '';

        if (this.logFilterHost && this.logFilterHost !== from) {
            visible = 'display: none';
        }

        if (!visible && this.logFilterSeverity) {
            if (this.logFilterSeverity === 'info' && message.severity === 'debug') {
                visible = 'display: none';
            } else if (this.logFilterSeverity === 'warn' && message.severity !== 'warn' && message.severity !== 'error') {
                visible = 'display: none';
            } else if (this.logFilterSeverity === 'error' && message.severity !== 'error') {
                visible = 'display: none';
            }
        }

        if (!visible && this.logFilterMessage && message.message.indexOf(that.logFilterMessage) === -1) {
            visible = 'display: none';
        }

        if (message.severity === 'error') {
            $('a[href="#tab-log"]').addClass('errorLog');
        }

        var text = '
';
        text += '' + (message.from || '') + '	';
        text += '' + this.main.formatDate(message.ts) + '	';
        text += '' + $.i18n(message.severity) + '	';
        text += '' + message.message.substring(0, 200) + '
';

        if (message.severity === "error") {
            this.errorCount += 1;
            $('#log_error_count').text(this.errorCount);
            if (this.errorCount === 1) {
                $('#log_error_count').removeClass('bg-green').addClass('bg-red');
            }
            if (this.errorCount === 0) {
                $('#log_error_count').addClass('bg-green').removeClass('bg-red');
            }
            if (this.errorCount > 10) {
                $('#log_error_list li:nth-last-child(2)').remove();
            }
            var count = (message.from || '').length + message.message.length;
            $('#log_error_list')
                    .prepend('
 30 ? 'title="' + message.message.substring(0, 200) + '" data-toggle="tooltip"' : '') + '>' + (message.from || '') + ' - ' + message.message.substring(0, 20) + (count > 30 ? '...' : '') + '
');
        }

        $('#log-table').prepend(text);
    };

    this.filter = function () {
        if (that.logFilterTimeout) {
            clearTimeout(that.logFilterTimeout);
            that.logFilterTimeout = null;
        }
        var $logOuter = $('#log-outer');

        that.logFilterHost = $('#log-filter-host').val();
        that.logFilterMessage = $('#log-filter-message').val();
        that.logFilterSeverity = $('#log-filter-severity').val();

        if (that.logFilterSeverity === 'error') {
            $logOuter.find('.log-severity-debug').hide();
            $logOuter.find('.log-severity-info').hide();
            $logOuter.find('.log-severity-warn').hide();
            $logOuter.find('.log-severity-error').show();
        } else if (that.logFilterSeverity === 'warn') {
            $logOuter.find('.log-severity-debug').hide();
            $logOuter.find('.log-severity-info').hide();
            $logOuter.find('.log-severity-warn').show();
            $logOuter.find('.log-severity-error').show();
        } else if (that.logFilterSeverity === 'info') {
            $logOuter.find('.log-severity-debug').hide();
            $logOuter.find('.log-severity-info').show();
            $logOuter.find('.log-severity-warn').show();
            $logOuter.find('.log-severity-error').show();
        } else {
            $logOuter.find('.log-severity-debug').show();
            $logOuter.find('.log-severity-info').show();
            $logOuter.find('.log-severity-warn').show();
            $logOuter.find('.log-severity-error').show();
        }
        if (that.logFilterHost || that.logFilterMessage) {
            $logOuter.find('.log-line').each(function () {
                if (that.logFilterHost && !$(this).hasClass('log-from-' + that.logFilterHost)) {
                    $(this).hide();
                } else if (that.logFilterMessage && $(this).html().indexOf(that.logFilterMessage) === -1) {
                    $(this).hide();
                }
            });
        }
    };

    this.clear = function (isReload) {
        if (isReload === undefined) {
            isReload = true;
        }
        $('#log-table').html('');
        this.logLinesCount = 0;
        this.logLinesStart = 0;
        $('#menu-logs').removeClass('text-danger');

        if (isReload) {
            setTimeout(function () {
                that.init();
            }, 0);
        }
    };

    this.pause = function () {
        var $logPause = $('#log-pause');
        if (!this.logPauseMode) {
            $logPause
                    .addClass('active')
                    .find('.fa')
                    .removeClass('fa-pause')
                    .html('0');

            this.logPauseCounter = 0;
            this.logPauseMode = true;
        } else {
            this.logPauseMode = false;
            for (var i = 0; i < this.logPauseList.length; i++) {
                this.add(this.logPauseList[i]);
            }
            restartFunctions('#log_error_list');
            this.logPauseOverflow = false;
            this.logPauseList = [];
            this.logPauseCounter = 0;

            $logPause
                    .removeClass('active')
                    .removeClass('btn-danger')
                    .addClass('btn-default');
        }
    };
}
<|endoftext|>


<commit_before>var NanoEvents = require('nanoevents')
var merge = require('logux-sync/merge')

var Client = require('./client')

function storageKey (client, name) {
  return client.options.prefix + ':' + client.options.userId + ':' + name
}

function sendToTabs (client, event, data) {
  if (typeof localStorage === 'undefined') return
  localStorage.setItem(storageKey(client, event), JSON.stringify(data))
}

function getLeader (client) {
  var data = localStorage.getItem(storageKey(client, 'leader'))
  var json = []
  if (typeof data === 'string') json = JSON.parse(data)
  return json
}

function leaderPing (client) {
  sendToTabs(client, 'leader', [client.id, Date.now()])
}

function onDeadLeader (client) {
  if (client.state === 'sending') {
    setState(client, 'wait')
  } else if (client.state !== 'disconnected' && client.state !== 'wait') {
    setState(client, 'disconnected')
  }
  startElection(client)
}

function watchForLeader (client) {
  clearTimeout(client.watching)
  client.watching = setTimeout(function () {
    if (!isActiveLeader(client)) {
      onDeadLeader(client)
    } else {
      watchForLeader(client)
    }
  }, client.roleTimeout)
}

function setRole (client, role) {
  if (client.role !== role) {
    var sync = client.sync
    client.role = role

    clearTimeout(client.watching)
    if (role === 'leader') {
      localStorage.removeItem(storageKey(client, 'state'))
      client.leadership = setInterval(function () {
        if (!client.unloading) leaderPing(client)
      }, client.leaderPing)
      sync.connection.connect()
    } else {
      clearTimeout(client.elections)
      clearInterval(client.leadership)

      if (sync.state !== 'disconnected' && sync.state !== 'wait') {
        client.sync.connection.disconnect()
      }
    }

    if (role === 'follower') {
      var state = 'disconnected'
      var json = localStorage.getItem(storageKey(client, 'state'))
      if (json && json !== null) state = JSON.parse(json)
      if (state !== client.state) {
        client.state = state
        client.emitter.emit('state')
      }
    }

    client.emitter.emit('role')
  }
}

function isActiveLeader (client) {
  var leader = getLeader(client)
  return leader[1] && leader[1] >= Date.now() - client.leaderTimeout
}

function startElection (client) {
  leaderPing(client)
  setRole(client, 'candidate')
  client.elections = setTimeout(function () {
    var data = getLeader(client, 'leader')
    if (data[0] === client.id) {
      setRole(client, 'leader')
    } else {
      setRole(client, 'follower')
      watchForLeader(client)
    }
  }, client.electionDelay)
}

function setState (client, state) {
  client.state = state
  client.emitter.emit('state')
  sendToTabs(client, 'state', client.state)
}

/**
 * Low-level browser API for Logux.
 *
 * Instead of {@link Client}, this class work prevent conflicts
 * between Logux instances in different tabs on single browser.
 *
 * @param {object} options Client options.
 * @param {string} options.url Server URL.
 * @param {string} options.subprotocol Client subprotocol version
 *                                     in SemVer format.
 * @param {number|string|false} options.userId User ID. Pass `false` if no user.
 * @param {any} [options.credentials] Client credentials for authentication.
 * @param {string} [options.prefix="logux"] Prefix for `IndexedDB` database
 *                                          to run multiple Logux instances
 *                                          in the same browser.
 * @param {string|number} [options.nodeId] Unique client ID.
 *                                         Compacted UUID by default.
 * @param {number} [options.timeout=20000] Timeout in milliseconds
 *                                         to break connection.
 * @param {number} [options.ping=10000] Milliseconds since last message to test
 *                                      connection by sending ping.
 * @param {Store} [options.store] Store to save log data. `IndexedStore`
 *                                by default (if available)
 * @param {number} [options.minDelay=1000] Minimum delay between reconnections.
 * @param {number} [options.maxDelay=5000] Maximum delay between reconnections.
 * @param {number} [options.attempts=Infinity] Maximum reconnection attempts.
 * @param {bool} [options.allowDangerousProtocol=false] Do not show warning
 *                                                      when using 'ws://'
 *                                                      in production.
 *
 * @example
 * token = document.querySelector('meta[name=token]')
 *
 * import CrossTabClient from 'logux-client/cross-tab-client'
 * const app = new CrossTabClient({
 *   credentials: token.content,
 *   subprotocol: '1.0.0',
 *   url: 'wss://example.com:1337'
 * })
 * app.start()
 *
 * @extends Client
 * @class
 */
function CrossTabClient (options) {
  Client.call(this, options)

  this.emitter = new NanoEvents()

  /**
   * Current tab role. Only `leader` tab connects to server. `followers` just
   * listen to events from `leader`.
   * @type {"leader"|"follower"|"candidate"}
   *
   * @example
   * app.on('role', () => {
   *   console.log('Tab role:', app.role)
   * })
   */
  this.role = 'candidate'

  this.roleTimeout = 3000 + Math.floor(Math.random() * 1000)
  this.leaderTimeout = 5000
  this.leaderPing = 2000
  this.electionDelay = 1000

  /**
   * Leader tab synchronization state. It can differs
   * from `Client#sync.state` (because only the leader tab keeps connection).
   *
   * @type {"disconnected"|"wait"|"connecting"|"sending"|"synchronized"}
   *
   * @example
   * client.on('state', () => {
   *   if (sync.state === 'wait' && sync.state === 'sending') {
   *     showCloseWarning()
   *   }
   * })
   */
  this.state = this.sync.state

  var client = this

  this.sync.on('state', function () {
    if (client.role === 'leader') {
      setState(client, client.sync.state)
    }
  })

  this.log.on('add', function (action, meta) {
    client.emitter.emit('add', action, meta)
    if (meta.tab !== client.id) {
      sendToTabs(client, 'add', [client.id, action, meta])
    }
  })
  this.log.on('clean', function (action, meta) {
    client.emitter.emit('clean', action, meta)
    if (meta.tab !== client.id) {
      sendToTabs(client, 'clean', [client.id, action, meta])
    }
  })

  this.storageListener = function (e) {
    if (e.newValue === null) return

    var data
    if (e.key === storageKey(client, 'add')) {
      data = JSON.parse(e.newValue)
      if (data[0] !== client.id) {
        if (!data[2].tab || data[2].tab === client.id) {
          client.emitter.emit('add', data[1], data[2])
        }
      }
    } else if (e.key === storageKey(client, 'clean')) {
      data = JSON.parse(e.newValue)
      if (data[0] !== client.id) {
        if (!data[2].tab || data[2].tab === client.id) {
          client.emitter.emit('clean', data[1], data[2])
        }
      }
    } else if (e.key === storageKey(client, 'leader')) {
      data = JSON.parse(e.newValue)
      if (data.length === 0) {
        onDeadLeader(client)
      } else if (data[0] !== client.id && client.role !== 'candidate') {
        setRole(client, 'follower')
        watchForLeader(client)
      }
    } else if (e.key === storageKey(client, 'state')) {
      var state = JSON.parse(localStorage.getItem(e.key))
      if (client.state !== state) {
        client.state = state
        client.emitter.emit('state')
      }
    }
  }
  window.addEventListener('storage', this.storageListener)

  this.unloadListener = function () {
    if (client.role === 'leader') {
      client.unloading = true
      sendToTabs(client, 'leader', [])
    }
  }
  window.addEventListener('unload', this.unloadListener)
}

CrossTabClient.prototype = {

  start: function start () {
    if (typeof localStorage === 'undefined') {
      this.role = 'leader'
      this.emitter.emit('role')
      this.sync.connection.connect()
      return
    }

    if (isActiveLeader(this)) {
      setRole(this, 'follower')
      watchForLeader(this)
    } else {
      startElection(this)
    }
  },

  destroy: function destroy () {
    Client.prototype.destroy.call(this)

    clearTimeout(this.watching)
    clearTimeout(this.elections)
    clearInterval(this.leadership)
    window.removeEventListener('storage', this.storageListener)
    window.removeEventListener('unload', this.unloadListener)
  },

  clean: function clean () {
    if (typeof localStorage !== 'undefined') {
      localStorage.removeItem(storageKey(this, 'add'))
      localStorage.removeItem(storageKey(this, 'clean'))
      localStorage.removeItem(storageKey(this, 'state'))
      localStorage.removeItem(storageKey(this, 'leader'))
    }
    return Client.prototype.clean.call(this)
  },

  /**
   * Subscribe for synchronization events. It implements nanoevents API.
   * Supported events:
   *
   * * `add`: action has been added to log (by any tab).
   * * `clean`: action has been removed from log (by any tab).
   * * `role`: tab role has been changed.
   * * `state`: leader tab synchronization state has been changed.
   *
   * @param {"add"|"clean"|"role"|"state"} event The event name.
   * @param {listener} listener The listener function.
   *
   * @return {function} Unbind listener from event.
   *
   * @example
   * app.on('add', (action, meta) => {
   *   dispatch(action)
   * })
   */
  on: function on (event, listener) {
    return this.emitter.on(event, listener)
  },

  /**
   * Add one-time listener for synchronization events.
   * See {@link Client#on} for supported events.
   *
   * @param {"add"|"clean"|"role"|"state"} event The event name.
   * @param {listener} listener The listener function.
   *
   * @return {function} Unbind listener from event.
   *
   * @example
   * app.once('clean', () => {
   *   cleaningWork = true
   * })
   */
  once: function once (event, listener) {
    return this.emitter.once(event, listener)
  }

}

/**
 * Is leader tab connected to server.
 *
 * @name connected
 * @type {boolean}
 * @memberof CrossTabClient#
 */

CrossTabClient.prototype = merge(CrossTabClient.prototype, Client.prototype)

Object.defineProperty(CrossTabClient.prototype, 'connected', {
  get: function get () {
    return this.state !== 'disconnected' &&
           this.state !== 'wait' &&
           this.state !== 'connecting'
  }
})

module.exports = CrossTabClient
<commit_msg>Move listeners to normal class methods<commit_after>var NanoEvents = require('nanoevents')
var merge = require('logux-sync/merge')

var Client = require('./client')

function storageKey (client, name) {
  return client.options.prefix + ':' + client.options.userId + ':' + name
}

function sendToTabs (client, event, data) {
  if (typeof localStorage === 'undefined') return
  localStorage.setItem(storageKey(client, event), JSON.stringify(data))
}

function getLeader (client) {
  var data = localStorage.getItem(storageKey(client, 'leader'))
  var json = []
  if (typeof data === 'string') json = JSON.parse(data)
  return json
}

function leaderPing (client) {
  sendToTabs(client, 'leader', [client.id, Date.now()])
}

function onDeadLeader (client) {
  if (client.state === 'sending') {
    setState(client, 'wait')
  } else if (client.state !== 'disconnected' && client.state !== 'wait') {
    setState(client, 'disconnected')
  }
  startElection(client)
}

function watchForLeader (client) {
  clearTimeout(client.watching)
  client.watching = setTimeout(function () {
    if (!isActiveLeader(client)) {
      onDeadLeader(client)
    } else {
      watchForLeader(client)
    }
  }, client.roleTimeout)
}

function setRole (client, role) {
  if (client.role !== role) {
    var sync = client.sync
    client.role = role

    clearTimeout(client.watching)
    if (role === 'leader') {
      localStorage.removeItem(storageKey(client, 'state'))
      client.leadership = setInterval(function () {
        if (!client.unloading) leaderPing(client)
      }, client.leaderPing)
      sync.connection.connect()
    } else {
      clearTimeout(client.elections)
      clearInterval(client.leadership)

      if (sync.state !== 'disconnected' && sync.state !== 'wait') {
        client.sync.connection.disconnect()
      }
    }

    if (role === 'follower') {
      var state = 'disconnected'
      var json = localStorage.getItem(storageKey(client, 'state'))
      if (json && json !== null) state = JSON.parse(json)
      if (state !== client.state) {
        client.state = state
        client.emitter.emit('state')
      }
    }

    client.emitter.emit('role')
  }
}

function isActiveLeader (client) {
  var leader = getLeader(client)
  return leader[1] && leader[1] >= Date.now() - client.leaderTimeout
}

function startElection (client) {
  leaderPing(client)
  setRole(client, 'candidate')
  client.elections = setTimeout(function () {
    var data = getLeader(client, 'leader')
    if (data[0] === client.id) {
      setRole(client, 'leader')
    } else {
      setRole(client, 'follower')
      watchForLeader(client)
    }
  }, client.electionDelay)
}

function setState (client, state) {
  client.state = state
  client.emitter.emit('state')
  sendToTabs(client, 'state', client.state)
}

/**
 * Low-level browser API for Logux.
 *
 * Instead of {@link Client}, this class work prevent conflicts
 * between Logux instances in different tabs on single browser.
 *
 * @param {object} options Client options.
 * @param {string} options.url Server URL.
 * @param {string} options.subprotocol Client subprotocol version
 *                                     in SemVer format.
 * @param {number|string|false} options.userId User ID. Pass `false` if no user.
 * @param {any} [options.credentials] Client credentials for authentication.
 * @param {string} [options.prefix="logux"] Prefix for `IndexedDB` database
 *                                          to run multiple Logux instances
 *                                          in the same browser.
 * @param {string|number} [options.nodeId] Unique client ID.
 *                                         Compacted UUID by default.
 * @param {number} [options.timeout=20000] Timeout in milliseconds
 *                                         to break connection.
 * @param {number} [options.ping=10000] Milliseconds since last message to test
 *                                      connection by sending ping.
 * @param {Store} [options.store] Store to save log data. `IndexedStore`
 *                                by default (if available)
 * @param {number} [options.minDelay=1000] Minimum delay between reconnections.
 * @param {number} [options.maxDelay=5000] Maximum delay between reconnections.
 * @param {number} [options.attempts=Infinity] Maximum reconnection attempts.
 * @param {bool} [options.allowDangerousProtocol=false] Do not show warning
 *                                                      when using 'ws://'
 *                                                      in production.
 *
 * @example
 * token = document.querySelector('meta[name=token]')
 *
 * import CrossTabClient from 'logux-client/cross-tab-client'
 * const app = new CrossTabClient({
 *   credentials: token.content,
 *   subprotocol: '1.0.0',
 *   url: 'wss://example.com:1337'
 * })
 * app.start()
 *
 * @extends Client
 * @class
 */
function CrossTabClient (options) {
  Client.call(this, options)

  this.emitter = new NanoEvents()

  /**
   * Current tab role. Only `leader` tab connects to server. `followers` just
   * listen to events from `leader`.
   * @type {"leader"|"follower"|"candidate"}
   *
   * @example
   * app.on('role', () => {
   *   console.log('Tab role:', app.role)
   * })
   */
  this.role = 'candidate'

  this.roleTimeout = 3000 + Math.floor(Math.random() * 1000)
  this.leaderTimeout = 5000
  this.leaderPing = 2000
  this.electionDelay = 1000

  /**
   * Leader tab synchronization state. It can differs
   * from `Client#sync.state` (because only the leader tab keeps connection).
   *
   * @type {"disconnected"|"wait"|"connecting"|"sending"|"synchronized"}
   *
   * @example
   * client.on('state', () => {
   *   if (sync.state === 'wait' && sync.state === 'sending') {
   *     showCloseWarning()
   *   }
   * })
   */
  this.state = this.sync.state

  var client = this

  this.sync.on('state', function () {
    if (client.role === 'leader') {
      setState(client, client.sync.state)
    }
  })

  this.log.on('add', function (action, meta) {
    client.emitter.emit('add', action, meta)
    if (meta.tab !== client.id) {
      sendToTabs(client, 'add', [client.id, action, meta])
    }
  })
  this.log.on('clean', function (action, meta) {
    client.emitter.emit('clean', action, meta)
    if (meta.tab !== client.id) {
      sendToTabs(client, 'clean', [client.id, action, meta])
    }
  })

  this.onStorage = this.onStorage.bind(this)
  this.onUnload = this.onUnload.bind(this)
  window.addEventListener('storage', this.onStorage)
  window.addEventListener('unload', this.onUnload)
}

CrossTabClient.prototype = {

  start: function start () {
    if (typeof localStorage === 'undefined') {
      this.role = 'leader'
      this.emitter.emit('role')
      this.sync.connection.connect()
      return
    }

    if (isActiveLeader(this)) {
      setRole(this, 'follower')
      watchForLeader(this)
    } else {
      startElection(this)
    }
  },

  destroy: function destroy () {
    Client.prototype.destroy.call(this)

    clearTimeout(this.watching)
    clearTimeout(this.elections)
    clearInterval(this.leadership)
    window.removeEventListener('storage', this.onStorage)
    window.removeEventListener('unload', this.onUnload)
  },

  clean: function clean () {
    if (typeof localStorage !== 'undefined') {
      localStorage.removeItem(storageKey(this, 'add'))
      localStorage.removeItem(storageKey(this, 'clean'))
      localStorage.removeItem(storageKey(this, 'state'))
      localStorage.removeItem(storageKey(this, 'leader'))
    }
    return Client.prototype.clean.call(this)
  },

  /**
   * Subscribe for synchronization events. It implements nanoevents API.
   * Supported events:
   *
   * * `add`: action has been added to log (by any tab).
   * * `clean`: action has been removed from log (by any tab).
   * * `role`: tab role has been changed.
   * * `state`: leader tab synchronization state has been changed.
   *
   * @param {"add"|"clean"|"role"|"state"} event The event name.
   * @param {listener} listener The listener function.
   *
   * @return {function} Unbind listener from event.
   *
   * @example
   * app.on('add', (action, meta) => {
   *   dispatch(action)
   * })
   */
  on: function on (event, listener) {
    return this.emitter.on(event, listener)
  },

  /**
   * Add one-time listener for synchronization events.
   * See {@link Client#on} for supported events.
   *
   * @param {"add"|"clean"|"role"|"state"} event The event name.
   * @param {listener} listener The listener function.
   *
   * @return {function} Unbind listener from event.
   *
   * @example
   * app.once('clean', () => {
   *   cleaningWork = true
   * })
   */
  once: function once (event, listener) {
    return this.emitter.once(event, listener)
  },

  onStorage: function (e) {
    if (e.newValue === null) return

    var data
    if (e.key === storageKey(this, 'add')) {
      data = JSON.parse(e.newValue)
      if (data[0] !== this.id) {
        if (!data[2].tab || data[2].tab === this.id) {
          this.emitter.emit('add', data[1], data[2])
        }
      }
    } else if (e.key === storageKey(this, 'clean')) {
      data = JSON.parse(e.newValue)
      if (data[0] !== this.id) {
        if (!data[2].tab || data[2].tab === this.id) {
          this.emitter.emit('clean', data[1], data[2])
        }
      }
    } else if (e.key === storageKey(this, 'leader')) {
      data = JSON.parse(e.newValue)
      if (data.length === 0) {
        onDeadLeader(this)
      } else if (data[0] !== this.id && this.role !== 'candidate') {
        setRole(this, 'follower')
        watchForLeader(this)
      }
    } else if (e.key === storageKey(this, 'state')) {
      var state = JSON.parse(localStorage.getItem(e.key))
      if (this.state !== state) {
        this.state = state
        this.emitter.emit('state')
      }
    }
  },

  onUnload: function () {
    if (this.role === 'leader') {
      this.unloading = true
      sendToTabs(this, 'leader', [])
    }
  }

}

/**
 * Is leader tab connected to server.
 *
 * @name connected
 * @type {boolean}
 * @memberof CrossTabClient#
 */

CrossTabClient.prototype = merge(CrossTabClient.prototype, Client.prototype)

Object.defineProperty(CrossTabClient.prototype, 'connected', {
  get: function get () {
    return this.state !== 'disconnected' &&
           this.state !== 'wait' &&
           this.state !== 'connecting'
  }
})

module.exports = CrossTabClient
<|endoftext|>