countly/countly.view.js

/* global countlyCommon, countlyEvent, $, Backbone, app, CountlyHelpers, _, Handlebars */

var initializeOnce = _.once(function() {
    return $.when(countlyEvent.initialize()).then(function() { });
});

/**
* Default Backbone View template from which all countly views should inherit.
* A countly view is defined as a page corresponding to a url fragment such
* as #/manage/apps. This interface defines common functions or properties
* the view object has. A view may override any function or property.
* @name countlyView
* @global
* @namespace countlyView
* @example <caption>Extending default view and overwriting its methods</caption>
*  window.DashboardView = countlyView.extend({
*       renderCommon:function () {
*           if(countlyGlobal["apps"][countlyCommon.ACTIVE_APP_ID]){
*               var type = countlyGlobal["apps"][countlyCommon.ACTIVE_APP_ID].type;
*               type = jQuery.i18n.map["management-applications.types."+type] || type;
*               $(this.el).html("<div id='no-app-type'><h1>"+jQuery.i18n.map["common.missing-type"]+": "+type+"</h1></div>");
*           }
*           else{
*               $(this.el).html("<div id='no-app-type'><h1>"+jQuery.i18n.map["management-applications.no-app-warning"]+"</h1></div>");
*           }
*       }
*   });
*/
window.countlyView = Backbone.View.extend({
    /**
    * Checking state of view, if it is loaded
    * @type {boolean}
    * @instance
    * @memberof countlyView
    */
    isLoaded: false,
    /**
    * Handlebar template
    * @type {object}
    * @instance
    * @memberof countlyView
    */
    template: null, //handlebars template of the view
    /**
    * Data to pass to Handlebar template when building it
    * @type {object}
    * @instance
    * @memberof countlyView
    */
    templateData: {}, //data to be used while rendering the template
    /**
    * Main container which contents to replace by compiled template
    * @type {jquery_object}
    * @instance
    * @memberof countlyView
    */
    el: $('#content'), //jquery element to render view into
    _myRequests: {}, //save requests called for this view
    /**
    * Initialize view, overwrite it with at least empty function if you are using some custom remote template
    * @memberof countlyView
    * @instance
    */
    initialize: function() { //compile view template
        this.template = Handlebars.compile($("#template-analytics-common").html());
    },
    _removeMyRequests: function() {
        for (var url in this._myRequests) {
            for (var data in this._myRequests[url]) {
                //4 means done, less still in progress
                if (parseInt(this._myRequests[url][data].readyState, 10) !== 4) {
                    this._myRequests[url][data].abort_reason = "app_remove_reqs";
                    this._myRequests[url][data].abort();
                }
            }
        }
        this._myRequests = {};
    },
    /**
    * This method is called when date is changed, default behavior is to call refresh method of the view
    * @memberof countlyView
    * @instance
    */
    dateChanged: function() { //called when user changes the date selected
        if (Backbone.history.fragment === "/") {
            this.refresh(true);
        }
        else {
            this.refresh();
        }
    },
    /**
    * This method is called when app is changed, default behavior is to reset preloaded data as events
    * @param {function=} callback  - callback function
    * @memberof countlyView
    * @instance
    */
    appChanged: function(callback) { //called when user changes selected app from the sidebar
        countlyEvent.reset();
        $.when(countlyEvent.initialize()).always(function() {
            if (callback) {
                callback();
            }
        });
    },
    /**
    * This method is called before calling render, load your data and remote template if needed here
    * @returns {boolean} true
    * @memberof countlyView
    * @instance
    * @example
    *beforeRender: function() {
    *   var self = this;
    *   return $.when(T.render('/density/templates/density.html', function(src){
    *       self.template = src;
    *   }), countlyDeviceDetails.initialize(), countlyTotalUsers.initialize("densities"), countlyDensity.initialize()).then(function () {});
    *}
    */
    beforeRender: function() {
        return true;
    },
    /**
    * This method is called after calling render method
    * @memberof countlyView
    * @instance
    */
    afterRender: function() {
        CountlyHelpers.makeSelectNative();
    },
    /**
    * Main render method, better not to over write it, but use {@link countlyView.renderCommon} instead
    * @returns {object} this
    * @memberof countlyView
    * @instance
    */
    render: function() { //backbone.js view render function
        // var currLink = Backbone.history.fragment;

        // // Reset any active views and dropdowns
        // $("#main-views-container").find(".main-view").removeClass("active");
        // $("#top-bar").find(".dropdown.active").removeClass("active");

        // // Activate the main view and dropdown based on the active view
        // if (/^\/custom/.test(currLink) === true) {
        //     $("#dashboards-main-view").addClass("active");
        //     $("#dashboard-selection").addClass("active");
        // }
        // else {
        //     $("#analytics-main-view").addClass("active");
        //     $("#app-navigation").addClass("active");
        // }

        $("#content-top").html("");
        this.el.html('');

        if (countlyCommon.ACTIVE_APP_ID) {
            var self = this;
            $.when(this.beforeRender(), initializeOnce()).fail(function(XMLHttpRequest, textStatus, errorThrown) {
                if (XMLHttpRequest && XMLHttpRequest.status === 0) {
                    // eslint-disable-next-line no-console
                    console.error("Check Your Network Connection");
                }
                else if (XMLHttpRequest && XMLHttpRequest.status === 404) {
                    // eslint-disable-next-line no-console
                    console.error("Requested URL not found: " + XMLHttpRequest.my_set_url + " with " + JSON.stringify(XMLHttpRequest.my_set_data));
                }
                else if (XMLHttpRequest && XMLHttpRequest.status === 500) {
                    // eslint-disable-next-line no-console
                    console.error("Internel Server Error: " + XMLHttpRequest.my_set_url + " with " + JSON.stringify(XMLHttpRequest.my_set_data));
                }
                else if ((XMLHttpRequest && typeof XMLHttpRequest.status === "undefined") || errorThrown) {
                    // eslint-disable-next-line no-console
                    console.error("Unknow Error: ");
                    if (XMLHttpRequest) {
                        // eslint-disable-next-line no-console
                        console.log(XMLHttpRequest.my_set_url + " with " + JSON.stringify(XMLHttpRequest.my_set_data) + "\n" + (XMLHttpRequest.responseText) + "\n");
                    }
                    // eslint-disable-next-line no-console
                    console.error(textStatus + "\n" + errorThrown);
                }
            })
                .always(function() {
                    if (app.activeView === self) {
                        self.isLoaded = true;
                        self.renderCommon();
                        self.afterRender();
                        app.pageScript();
                    }
                });
        }
        else {
            if (app.activeView === this) {
                this.isLoaded = true;
                this.renderCommon();
                this.afterRender();
                app.pageScript();
            }
        }

        /*
        Vue update - remove following
        if (countlyGlobal.member.member_image) {
            $('.member_image').html("");
            $('.member_image').css({'background-image': 'url(' + countlyGlobal.member.member_image + '?now=' + Date.now() + ')', 'background-size': '100%'});
        }
        else {
            var defaultAvatarSelector = countlyGlobal.member.created_at % 16 * 30;
            var name = countlyGlobal.member.full_name.split(" ");
            $('.member_image').css({'background-image': 'url("images/avatar-sprite.png")', 'background-position': defaultAvatarSelector + 'px', 'background-size': '510px 30px', 'text-align': 'center'});
            $('.member_image').html("");
            $('.member_image').prepend('<span style="text-style: uppercase;color: white;position: relative; top: 6px; font-size: 16px;">' + name[0][0] + name[name.length - 1][0] + '</span>');
        }
        // Top bar dropdowns are hidden by default, fade them in when view render is complete
        $("#top-bar").find(".dropdown").fadeIn(2000);
        */

        return this;
    },
    /**
    * Do all your rendering in this method
    * @param {boolean} isRefresh - render is called from refresh method, so do not need to do initialization
    * @memberof countlyView
    * @instance
    * @example
    *renderCommon:function (isRefresh) {
    *    //set initial data for template
    *    this.templateData = {
    *        "page-title":jQuery.i18n.map["density.title"],
    *        "logo-class":"densities",
    *        "chartHTML": chartHTML,
    *    };
    *
    *    if (!isRefresh) {
    *        //populate template with data and add to html
    *        $(this.el).html(this.template(this.templateData));
    *    }
    *}
    */
    renderCommon: function(/* isRefresh*/) {}, // common render function of the view
    /**
    * Called when view is refreshed, you can reload data here or call {@link countlyView.renderCommon} with parameter true for code reusability
    * @returns {boolean} true
    * @memberof countlyView
    * @instance
    * @example
    * refresh:function () {
    *    var self = this;
    *    //reload data from beforeRender method
    *    $.when(this.beforeRender()).then(function () {
    *        if (app.activeView != self) {
    *            return false;
    *        }
    *        //re render data again
    *        self.renderCommon(true);
    *
    *        //replace some parts manually from templateData
    *        var newPage = $("<div>" + self.template(self.templateData) + "</div>");
    *        $(self.el).find(".widget-content").replaceWith(newPage.find(".widget-content"));
    *        $(self.el).find(".dashboard-summary").replaceWith(newPage.find(".dashboard-summary"));
    *        $(self.el).find(".density-widget").replaceWith(newPage.find(".density-widget"));
    *    });
    *}
    */
    refresh: function() { // resfresh function for the view called every 10 seconds by default
        return true;
    },
    /**
    * This method is called when user is active after idle period
    * @memberof countlyView
    * @instance
    */
    restart: function() { // triggered when user is active after idle period
        this.refresh();
    },
    /**
    * This method is called when view is destroyed (user entered inactive state or switched to other view) you can clean up here if there is anything to be cleaned
    * @memberof countlyView
    * @instance
    */
    destroy: function() { }
});