text: (function (w){
    "use strict";
  
    var app = window.app = _.extend({}, Backbone.Events);
    var b = app.b = utils._getBackgroundPage();
    var _baron;
  
    app.twitchApi = b.twitchApi;
    app.currentView = null;
    app.views = {};
    app.windowOpened = false;
  
    app.resetScroll = function (){
      app.container.scrollTop(0);
      _baron.update();
    }
  
    app.lazyload = function (){
      var collection = $('.lazy-container').filter(":visible").find(".lazy");
      collection.each(function (){
        var t = $(this);
        if ( t.visible(true) ) {
          t.attr('src', t.attr('data-original'));
          t.removeClass('lazy');
        }
      });
      setTimeout(function (){
        app.lazyload()
      }, 100);
    }
  
    app.setCurrentView = function (viewName){
      if ( app.views[viewName] ) {
        if ( app.curView && typeof app.curView.onhide == 'function' ) {
          app.curView.onhide();
        }
        $('.screen').hide();
        app.curView = app.views[viewName];
        $('#filterInput:visible, #searchInput:visible').focus();
        app.curView.$el.show();
        if ( app.curView.lazyrender ) {
          app.curView.lazyrender();
        }
        if ( app.curView.onshow ) {
          app.curView.onshow();
        }
        app.resetScroll();
      }
    }
  
    app.init = function (){
      app.container = $('#content');
      app.scroller = app.container.find(".scroller");
      app._scroller = document.querySelector(".scroller");
      app.scrollerBar = document.querySelector(".scroller__bar");
      app.preloader = $("<div id='preloader'><img src='../img/spinner.gif'/></div>");
      app.streamPreloader = $("<div id='preloader-stream'><img src='../img/spinner.gif'/></div>");
  
      i18n.setGetMessageFn(utils.i18n.getMessage);
      i18n.replace(document.body);
      i18n.observe(document.body);
  
      var views = app.views;
      this.router = new this.Router;
  
      app.scroller.on("scroll", function (){
        if ( app._scroller.scrollTop + app._scroller.offsetHeight >= app._scroller.scrollHeight - 100 ) {
          if ( app.curView && app.curView.loadNext && $("#filterInput").val().length == 0 ) {
            app.curView.loadNext();
          }
        }
      })
  
      views.menu = new Menu;
  
      views.topMenu = new TopMenu;
  
      _baron = baron({
        root    : '#content',
        scroller: '.scroller',
        bar     : '.scroller__bar',
        barOnCls: 'baron'
      }).autoUpdate();
  
      Backbone.history.start();
  
      this.router.on("all", function (r){
        var route = r.split(":")[1];
        console.log("ROUTE", route);
        if ( route ) {
          app.setCurrentView(route);
        }
      });
  
      views.notifications = new ChannelNotificationListView({
        el        : "#notifications-screen",
        collection: b.notifications
      })
  
      views.followedGames = new GameListView({
        el        : "#followed-game-screen",
        collection: b.followedgames,
        messages  : {
          noresults: {
            header: "No Channels Live",
            text  : "Nobody is streaming your favorites games right now"
          }
        }
      })
  
      views.gameLobby = new GameLobbyView({
        el   : "#gamelobby",
        route: "gameLobby",
        model: b.gameLobby
      })
  
      views.info = new InfoView({
        el: "#info-screen"
      });
  
      views.topstreams = new StreamListView({
        el        : "#topstreams-screen",
        collection: b.topstreams
      });
  
      views.following = new StreamListView({
        el        : "#stream-screen",
        collection: b.following,
        messages  : {
          noresults: {
            header: "No Channels Live",
            text  : "Nobody is streaming right now"
          }
        }
      });
  
      views.videos = new VideoListView({
        el        : "#video-screen",
        collection: b.videos
      });
  
      views.search = new SearchView({
        el        : "#search-screen",
        collection: b.search
      });
  
      views.browseGames = new GameListView({
        el        : "#browse-game-screen",
        collection: b.games
      });
  
      new UserView({
        model: b.user
      });
  
      views.settings = new SettingsView({
        collection: b.settings
      });
  
      new ContributorListView({
        el        : ".contributor-list",
        collection: b.contributors
      })
  
      $(window).on("popup-close", function (){
        app.scroller.scrollTop(0); //reset scroll on popup close for firefox
        b.bgApp.dispatcher.trigger("popup-close");
      });
  
      $(self).on("unload", function (){
        $(window).trigger("popup-close");
      })
  
  //    app.lazyload();
  
      $("body")
        .on('click', '*[data-route]', function (){
          var route = $(this).attr('data-route');
          window.location.hash = '#' + route;
        })
        .one('mouseenter', function (){
          app.lazyload();
        })
        .on('click', '.js-tab', function (e){
          var href = $(this).attr('data-href');
          utils.tabs.create({url: href});
          e.preventDefault();
        })
  
        .on('click', '.js-window', function (e){
          var windowOpts = JSON.parse($(this).attr('data-window-opts') || "{}");
          utils.windows.create($.extend({url: $(this).attr('data-href')}, windowOpts));
          e.preventDefault();
        })
  
      $('.tip').tooltip();
  
      console.log(window.location.href);
    };
  
    var DefaultView = Backbone.View.extend({
      app          : app,
      template     : "",
      render       : function (){
        this.$el.empty().html($(Handlebars.templates[this.template](this.model.toJSON())));
        return this;
      },
      onshow       : function (){
  
      },
      onhide       : function (){
  
      },
      initialize   : function (){
        $(self).unload(function (){
          this.undelegateEvents();
          this.stopListening();
        }.bind(this));
      },
      showPreloader: function (container){
        container = container || this.$el;
        container.append(this.app.streamPreloader);
      },
      hidePreloader: function (){
        this.app.streamPreloader.detach();
      }
    });
  
    var LazyRenderView = DefaultView.extend({
      isRendered: false,
      lazyrender: function (){
        if ( !this.isRendered ) {
          this.render();
          this.isRendered = true;
        }
      }
    })
  
    var UserView = DefaultView.extend({
      el        : "#user-info",
      events    : {
        "click #logout-btn": "logout",
        "click #login-btn" : "login"
      },
      template  : "user",
      initialize: function (){
        DefaultView.prototype.initialize.apply(this, arguments);
        this.listenTo(this.model, "change", this.render);
        this.render();
      },
      login     : function (){
        this.model.login();
      },
      logout    : function (){
        this.model.logout();
      }
    })
  
    var TopMenu = DefaultView.extend({
      el            : "#top-menu",
      events        : {
        "click #refresh-btn": "refresh",
        "keyup #filterInput": "filter"
      },
      show          : function (){
        this.$el.show();
      },
      hide          : function (){
        this.$el.hide();
      },
      initialize    : function (){
        DefaultView.prototype.initialize.apply(this, arguments);
        this.$filterInput = this.$("#filterInput");
        this.listenTo(this.app.router, "route", this.resetFilterVal);
      },
      resetFilterVal: function (){
        this.$filterInput.val("").keyup();
        this.filter();
      },
      filter        : function (){
        var fValue = this.$filterInput.val().toLowerCase();
        fValue = fValue.replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
        var rFilter = new RegExp(fValue);
        if ( this.app.curView ) {
          this.app.curView.$el.find(".js-filterable").each(function (i, e){
            $(e).toggle(!!$(e).text().toLowerCase().match(rFilter));
          });
          _baron.update();
        }
      },
      refresh       : function (){
        this.resetFilterVal();
        this.app.curView && this.app.curView.update && this.app.curView.update();
      }
    });
  
    var Menu = DefaultView.extend({
      el           : "#menu",
      events       : {
        "click .menu-btn": "selectTab"
      },
      initialize   : function (){
        DefaultView.prototype.initialize.apply(this, arguments);
        this.listenTo(this.app.router, "route", this.onRouteChange.bind(this));
      },
      onRouteChange: function (route, r1){
        console.log("_route", route, r1);
        if ( /user/i.test(Backbone.history.fragment) ) {
          this.$el.find(".menu-btn").removeClass("active");
        }
      },
      selectTab    : function (e){
        this.$el.find(".menu-btn").removeClass("active");
  //      this.$el.find(".menu-btn[data-route=" + route + "]").addClass("active");
        $(e.target).closest(".menu-btn").addClass("active");
      }
    });
  
    var InfoView = DefaultView.extend({});
  
    var ControlView = DefaultView.extend({
      template: "control"
    });
  
    var SettingsView = LazyRenderView.extend({
      el    : "#settings-screen",
      events: {
        "click input[data-id=\"customNotificationSound\"]": "uploadSound",
        "change input[type=\"range\"]"                    : "rangeHelper",
        "change input, select"                            : "serialize"
      },
  
      setDefaultTab: function (value){
        app.router.navigate(value, {trigger: true});
        $('#menu').find('[data-route="' + value + '"]').addClass("active");
      },
  
      changeThemeType: function (value){
        $("body").toggleClass("white-version", value !== "dark");
      },
  
      toggleSimpleView: function (value){
        $("body").toggleClass("simple-version", value);
      },
  
      onModelChange: function (model, options){
        var v = model.get("value");
  
        switch ( model.get("id") ) {
          case "notificationSound":
            this.playSound()
            break;
  
          case "simpleView":
            this.toggleSimpleView(model.get("value"))
            break;
  
          case "themeType":
            this.changeThemeType(model.get("value"));
            break;
  
          default:
            break;
        }
      },
  
      uploadSound: function (e){
        utils.tabs.create({url: utils.runtime.getURL("common/html/upload.html")});
      },
  
      playSound  : function (e){
        b.bgApp.playSound(b.settings.getNotificationSoundSource(), b.settings.get("notificationVolume").get("value") / 100);
      },
      rangeHelper: function (e){
        var t = $(e.target);
        t.siblings(".range-helper").find(".range-helper-value").html(t.val());
      },
      initialize : function (){
        LazyRenderView.prototype.initialize.apply(this, arguments);
        this.setDefaultTab(this.collection.get("defaultTab").get("value"));
        this.changeThemeType(this.collection.get("themeType").get("value"));
        this.toggleSimpleView(this.collection.get("simpleView").get("value"));
  
        this.$container = this.$el.find("#settings-container");
        this.listenTo(this.collection, "change", this.onModelChange);
      },
      serialize  : function (){
        var controls = [];
        this.$container.find('input, select').each(function (e){
          var t = $(this),
            value,
            type = t.attr('data-type'),
            controlId = t.attr('data-id');
  
          if ( type == 'radio' ) {
            if ( t.prop("checked") ) {
              value = t.val();
            } else {
              return;
            }
          }
          if ( type == 'checkbox' ) {
            value = t.prop('checked');
          }
          else if ( type == 'range' ) {
            value = parseInt(t.val(), 10);
          }
          else {
            value = t.val();
          }
          controls.push({id: controlId, value: value});
        });
        this.collection.set(controls, {add: false, remove: false});
      },
      render     : function (){
        var views = this.collection.map(function (control){
          return new ControlView({model: control}).render().$el;
        }, this);
        this.$container.empty().html(views);
        return this;
      }
    });
  
    var GameView = DefaultView.extend({
      template: "game",
      events  : {
        "contextmenu .stream": "showMenu"
      },
  
      unfollow: function (){
        var self = this;
        self.showPreloader(self.$el.find(".stream"));
        this.model.unfollow(function (){
          self.hidePreloader();
        });
      },
      follow  : function (){
        var self = this;
        self.showPreloader(self.$el.find(".stream"));
        self.model.follow(function (){
          self.hidePreloader();
        });
      },
  
      showMenu: function (e){
        var m = this.model.toJSON();
        var self = this;
        m.authorized = app.twitchApi.isAuthorized();
  
        var menu = new MenuView({
          model: m
        })
  
        menu.showMenu("contextgamemenu", {y: e.clientY, x: e.clientX});
  
        menu.$el
          .on('click', '.js-follow-game', function (){
            self.follow();
          })
          .on('click', '.js-unfollow-game', function (){
            self.unfollow();
          })
  
        return false;
      }
    });
  
    var MenuView = DefaultView.extend({
      template  : "",
      el        : "#context-menu",
      initialize: function (){
        DefaultView.prototype.initialize.apply(this, arguments);
        $("body").on("click", this.hide.bind(this));
      },
      hide      : function (){
        this.$el.hide();
      },
      showMenu  : function (tmpl, coords){
        var menuEl = $(this.el);
        var y = coords.y;
        var x = coords.x;
        var height = menuEl.height();
  
        if ( y + height > $(document.body).height() ) {
          y -= height;
        }
  
        menuEl
          .off()
          .empty()
          .html($(Handlebars.templates[tmpl](this.model)))
          .css({top: y, left: x})
          .show();
        return false;
      }
    })
  
    var VideoView = DefaultView.extend({
      template: "video"
    });
  
  
    var StreamView = DefaultView.extend({
      template  : "stream",
      menuEl    : '#context-menu',
      events    : {
        "contextmenu .stream": "showMenu",
        "click .stream"      : "openStream"
      },
      openStream: function (){
        this.model.openStream();
      },
      openChat  : function (){
        this.model.openChat();
      },
      showMenu  : function (e){
        var self = this;
        var m = this.model.toJSON();
        m.authorized = app.twitchApi.isAuthorized();
  
        var menu = new MenuView({
          model: m
        })
        menu.showMenu("contextstreammenu", {y: e.clientY, x: e.clientX});
  
        menu.$el
          .on('click', '.js-open-chat', function (){
            self.model.openChat();
          })
          .on('click', '.js-open-in-multitwitch', function (){
            self.model.openMultitwitch();
          })
          .on('click', '.js-open-stream', function (e){
            self.model.openStream($(e.target).attr("data-type"));
          })
          .on('click', '.js-follow', function (){
            self.follow();
          })
          .on('click', '.js-unfollow', function (){
            self.unfollow();
          })
  
        return false;
      },
      unfollow  : function (){
        var self = this;
        self.showPreloader(self.$el.find(".stream"));
        this.model.unfollow(function (){
          self.hidePreloader();
        });
      },
      follow    : function (){
        var self = this;
        self.showPreloader(self.$el.find(".stream"));
        self.model.follow(function (){
          self.hidePreloader();
        });
      },
      initialize: function (){
        DefaultView.prototype.initialize.apply(this, arguments);
        this.listenTo(this.model, "change", this.render);
      }
    });
  
    var ListView = LazyRenderView.extend({
      messages   : {
        "auth"           : {
          text: "__MSG_m73__"
        },
        "api"            : {
          text: "__MSG_m48__"
        },
        "novideo"        : {
          text: "__MSG_m49__"
        },
        "nosearchresults": {
          text: "__MSG_m50__"
        },
        "nosearchquery"  : {
          text: "No search query"
        }
      },
      itemView   : null, // single item view
      initialize : function (opts){
        LazyRenderView.prototype.initialize.apply(this, arguments);
        this.container = opts.container || this.$el.find(".screen-content");
        this.messages = $.extend({}, this.messages, opts.messages);
        this.listenTo(this.collection, "remove update sort reset", this.render.bind(this, null));
        this.listenTo(this.collection, "_error", this.showMessage.bind(this));
        this.listenTo(this.collection, "add addarray", this.render.bind(this));
      },
      showMessage: function (type){
        if ( this.messages[type] ) {
          var text = this.messages[type];
          this.container.empty().html(Handlebars.templates["screenmessage"](text));
        }
      },
      loadNext   : function (){
        if ( this.collection.pagination ) {
          this.collection.loadNext();
        }
      },
      update     : function (){
        this.container.empty().append(this.app.preloader);
        this.collection.update();
      },
      render     : function (models){
        var elementsToRender = [];
        if ( models ) {
          elementsToRender = Array.isArray(models) ? models : [models];
        } else {
          elementsToRender = this.collection;
          if ( !elementsToRender.length ) {
            return this.showMessage("noresults");
          }
        }
  
        var views = elementsToRender.map(function (item){
          return new this.itemView({model: item}).render().$el;
        }, this);
        if ( models ) {
          this.container.append($(document.createDocumentFragment()).html(views));
        } else {
          this.container.empty().html(views);
        }
      }
    });
  
    var ChannelNotificationView = DefaultView.extend({
      template   : "channelnotification",
      events     : {
        "click .channel-logo": "openProfile"
      },
      openProfile: function (){
        this.model.openProfilePage();
      }
    })
  
    var ChannelNotificationListView = ListView.extend({
      itemView      : ChannelNotificationView,
      events        : {
        "click .undo-message a"       : "undo",
        "change .screen-content input": "serialize",
        "click .dropdown-menu a"      : "toggle"
      },
      initialize    : function (){
        this.$undoMessage = this.$el.find(".undo-message");
        this.$selectMenuBtn = this.$el.find(".dropdown .btn");
        this.listenTo(this.collection, "add update remove reset", this.toggleDropdown.bind(this));
        this.toggleDropdown();
        ListView.prototype.initialize.apply(this, arguments);
      },
      toggleDropdown: function (){
        this.$selectMenuBtn.toggleClass("disabled", this.collection.length == 0);
      },
      update        : function (){
        this.$selectMenuBtn.addClass("disabled");
        this.$undoMessage.css({visibility: "hidden"});
        ListView.prototype.update.apply(this, arguments);
      },
  
      onhide   : function (){
        this.$undoMessage.css({visibility: "hidden"});
      },
      onshow   : function (){
        //update once on view show if not updated before
        if ( !this.collection.length ) {
          this.update();
        }
      },
      undo     : function (){
        this.collection.restore();
        this.$undoMessage.css({visibility: "hidden"});
      },
      toggle   : function (e){
        var type = $(e.currentTarget).data("notification-type");
        var val = $(e.currentTarget).data("notification-value") == "1" ? true : false;
        this.$el
          .find(".screen-content input[data-notification-type='" + type + "']")
          .prop("checked", val);
  
        this.$undoMessage.css({visibility: "visible"});
        this.collection.store();
        this.serialize();
      },
      serialize: function (){
        var attributes = [];
        this.$el.find(".screen-content [data-channel-id]")
          .map(function (i, e){
            e = $(e);
            attributes.push({
              _id             : parseInt(e.attr("data-channel-id")),
              notificationOpts: {
                desktop: e.find("input[data-notification-type='desktop']").prop("checked"),
                sound  : e.find("input[data-notification-type='sound']").prop("checked")
              }
            });
          })
        this.collection.set(attributes, {add: false, remove: false, silent: true});
        this.collection.saveToStorage();
      }
    })
  
    var GameListView = ListView.extend({
      itemView: GameView
    });
  
    var StreamListView = ListView.extend({
      itemView: StreamView
    });
  
    var SearchView = ListView.extend({
      events  : {
        "keyup #searchInput": "onkeyup",
        "click #search"     : "search"
      },
      itemView: StreamView,
      onkeyup : function (e){
        if ( e.keyCode == 13 ) {
          this.search();
        }
      },
      search  : function (){
        this.collection.query = this.$el.find("#searchInput").val();
        this.update();
      }
    });
  
    var VideoListView = ListView.extend({
      itemView : VideoView,
      setStream: function (channel){
        this.lazyrender();
        this.collection.channel = channel;
        this.update();
      }
    });
  
    var ExtendedGameView = DefaultView.extend({
      template          : "gameextended",
      events            : {
        "click .game-follow": "follow"
      },
      render            : function (){
        DefaultView.prototype.render.apply(this, arguments);
        this.toggleActiveButton(this.view);
      },
      setCurrentView    : function (game, view){
        this.view = view;
        this.toggleActiveButton(this.view);
      },
      toggleActiveButton: function (view){
        var $buttons = this.$el.find(".button").removeClass("active");
        if ( /streams$/i.exec(view) ) {
          $buttons.eq(0).addClass("active");
        }
  
        if ( /videos$/i.exec(view) ) {
          $buttons.eq(1).addClass("active");
        }
      },
      follow            : function (){
        this.$el.find(".game-follow").addClass("active");
        this.model.follow();
      },
      initialize        : function (){
        DefaultView.prototype.initialize.apply(this, arguments);
        this.listenTo(this.app.router, "route:gameLobby", this.setCurrentView.bind(this));
      }
    })
  
    var GameLobbyView = DefaultView.extend({
  
      initialize      : function (opts){
        DefaultView.prototype.initialize.apply(this, arguments);
        this.listenTo(this.app.router, "route:gameLobby", this.toggleActiveView.bind(this));
        this.listenTo(this.app.router, "route", this.toggle.bind(this));
  
        this.gameView = new ExtendedGameView({
          el   : "#gamelobby-game",
          model: this.model.game
        })
  
        this.streamView = new StreamListView({
          el        : "#gamelobby-streams",
          collection: b.gameLobby.streams
        });
  
        this.videoView = new VideoListView({
          el        : "#gamelobby-videos",
          collection: b.gameLobby.videos
        })
  
        this.gameView.render();
        this.videoView.render();
        this.streamView.render();
        this.listenTo(this.model.game, "change", this.update);
      },
      toggle          : function (r){
        if ( /gamelobby/i.exec(r) ) {
          $("#content").css({top: 154});
        } else {
          $("#content").css({top: 31});
        }
      },
      loadNext        : function (){
        this.videoView.loadNext();
        this.streamView.loadNext();
      },
      toggleActiveView: function (game, view){
        this.$el.find(".tabs [tab-id]").hide();
        this.$el.find(".tabs [tab-id='" + view + "']").show();
      },
      update          : function (){
        this.gameView.render();
        this.videoView.update();
        this.streamView.update();
      },
      setGame         : function (game){
        this.model.setGame(game);
      }
    })
  
    var ContributorView = DefaultView.extend({
      template: "contributor"
    });
  
    var ContributorListView = DefaultView.extend({
      initialize: function (){
        DefaultView.prototype.initialize.apply(this, arguments);
        this.listenTo(this.collection, "reset", this.render);
        this.render();
      },
      render    : function (){
        var views = this.collection.map(function (item){
          return new ContributorView({model: item}).render().$el;
        }, this);
  
        this.$el.empty().html(views);
      }
    });
  })(window);