<commit_before>App.addChild('MixPanel', {
  el: 'body',

  activate: function(){
    this.VISIT_MIN_TIME = 4000;
    this.user = null;
    this.controller = this.$el.data('controller') || this.$el.data('controller-name');
    this.action = this.$el.data('action');
    this.user = this.$el.data('user');
    this.id = this.$el.data('id');
    if(window.mixpanel){
      this.detectLogin();
      this.startTracking();
    }
  },

  projectProperties: function(){
    return this.$('#project-header').data('stats');
  },

  startTracking: function(){
    var self = this;

    this.trackOnPage('projects', 'show', function(){
      self.trackVisit('Visited project page', self.projectProperties());
      self.trackReminderClick();
      self.trackOnContributionStart();
    });

    this.trackOnPage('projects', 'edit', function(){
      $(window).on('hashchange', function() {
        if(window.location.hash == '#reports'){
          self.track('Project owner engaged with Catarse', _.extend(self.projectProperties(), { action: 'Visited reports' }));
        }
      });
    });

    this.trackOnPage('pages', 'show', function(){
      if(self.id == 'start'){
        self.track('Visited start page');
      }
    });

    this.trackPageVisit('projects', 'new', 'Visited new project page');
    this.trackPageVisit('projects', 'index', 'Visited home');
    this.trackPageVisit('explore', 'index', 'Explored projects');
    this.trackPageLoad('contributions', 'edit', 'Selected reward');
    this.trackPageLoad('contributions', 'show', 'Finished Contribution')
    this.trackContributions();
    this.trackPaymentChoice();
    this.trackTwitterShare();
    this.trackFacebookShare();
    this.trackFollowCategory();
    try {
      this.trackOnFacebookLike();
      this.trackOnFacebookComment();
    } catch(e) {
      console.log(e);
    }
  },

  trackFollowCategory: function(){
    var self = this;
    this.$('.category-follow .follow-btn').on('click', function(event){
      self.track('Engaged with Catarse', { ref: $(event.currentTarget).data('title'), action: 'click follow category' });
      return true;
    });
  },

  trackOnContributionStart: function(){
    var self = this;

    this.$('.card-reward').on('click', function(event){
      self.trackStartedContribution('Reward click');
    });

    this.$('#contribute_project_form').on('click', function(event){
      self.trackStartedContribution('Contribute button click');
    });
  },

  trackStartedContribution: function(action){
    this.track('Started contribution', _.extend(this.projectProperties(), { action: action }));
  },

  trackReminderClick: function(){
    var self = this;
    this.$('#reminder:not([data-method])').on('click', function(event){
      self.track('Engaged with Catarse', { ref: $(event.currentTarget).data('title'), action: 'click reminder' });
      return true;
    });
  },

  trackPageLoad: function(controller, action, text){
    var self = this;
    this.trackOnPage(controller, action, function(){
      self.track(text);
    });
  },

  trackContributions: function(){
    var from = '',
        self = this;
    this.trackOnPage('contributions', 'new', function(){
      if(window.location.href.indexOf('reward_id') > -1){
        from = 'Reward click';
      } else {
        from = 'Contribute button click';
      }
      self.track('Started contribution', _.extend(self.projectProperties(), {action: from}));
    });
  },

  trackPaymentChoice: function(){
    var self = this;
    this.$('#engine').on('click', '.back-payment-radio-button', function(){
      var choice = $('.back-payment-radio-button:checked').val();
      console.log("Clicked payment");
      if(choice === 'slip'){
        self.track('Payment chosen', _.extend(self.projectProperties(), {payment_choice: 'BoletoBancario'}));
      }
      if(choice === 'credit_card'){
        self.track('Payment chosen', _.extend(self.projectProperties(), {payment_choice: 'CartaoDeCredito'}));
      }
    });
  },

  trackPageVisit: function(controller, action, text){
    var self = this;
    this.trackOnPage(controller, action, function(){
      self.trackVisit(text);
    });
  },

  trackOnPage: function(controller, action, callback){
    if(this.controller == controller && this.action == action){
      callback();
    }
  },

  trackTwitterShare: function() {
    var self = this;

    this.$('#twitter_share_button').on('click', function(event){
      self.track('Engaged with Catarse', { ref: $(event.currentTarget).data('title'), action: 'share twitter' });
    });
  },

  trackFacebookShare: function() {
    var self = this;
    this.$('a#facebook_share').on('click', function(event){
      self.track('Engaged with Catarse', { ref: $(event.currentTarget).data('title'), action: 'share facebook' });
    });
  },

  trackOnFacebookComment: function() {
    var self = this;

    FB.Event.subscribe('comment.create', function(url, html_element){
      self.track('Engaged with Catarse', { ref: $(html_element).data('title'), action: 'comment facebook' });
    });
  },

  trackOnFacebookLike: function() {
    var self = this;

    FB.Event.subscribe('edge.create', function(url, html_element){
      self.track('Engaged with Catarse', { ref: $(html_element).data('title'), action: 'like facebook' });
    });
  },

  onLogin: function(){
    if(this.user.created_today){
      mixpanel.alias(this.user.user_id);
      this.track("Signed up");
    }
    else{
      this.track("Logged in");
    }
  },

  detectLogin: function(){
    if(this.user){ // User is logged in
      if(this.user.user_id != store.get('user_id')){ // First page load after login
        this.onLogin();
        store.set('user_id', this.user.user_id);
      }
    }
    else if(store.get('user_id')){ // Logout
      if(mixpanel && mixpanel.cookie){
        mixpanel.cookie.clear();
      }
      store.set('user_id', null);
    }
  },

  identifyUser: function(){
    if (this.user){
      mixpanel.name_tag(this.user.email);
      mixpanel.identify(this.user.user_id);
      mixpanel.people.set({
        "$email": this.user.email,
        "$created": this.user.created,
        "$last_login": this.user.last_login,
        "contributions": this.user.contributions
      });
    }
  },

  track: function(text, options){
    this.identifyUser();
    var opt             = options || {};
    var obj             = $(this);
    var ref             = (obj.attr('href') != undefined) ? obj.attr('href') : (opt.ref ? opt.ref : null);
    var usr             = this.user || {};
    var page            = {
      'page name':          document.title,
      'url':                window.location,
      'host':               window.location.host,
      'from_page':          this.$el.data('referral')
    };

    var opt = $.fn.extend(usr, opt, page, {project : ref});

    mixpanel.track(text, opt);
  },

  mixPanelEvent: function(target, event, text, options){
    var self = this;
    this.$(target).on(event, function(){
      self.track(text, options);
    });
  },

  trackVisit: function(eventName, options){
    var self = this;
    window.setTimeout(function(){
      self.track(eventName, options);
    }, this.VISIT_MIN_TIME);
  }
});
<commit_msg>Fixes project reports tracking [fix #101912232]<commit_after>App.addChild('MixPanel', {
  el: 'body',

  activate: function(){
    this.VISIT_MIN_TIME = 4000;
    this.user = null;
    this.controller = this.$el.data('controller') || this.$el.data('controller-name');
    this.action = this.$el.data('action');
    this.user = this.$el.data('user');
    this.id = this.$el.data('id');
    if(window.mixpanel){
      this.detectLogin();
      this.startTracking();
    }
  },

  projectProperties: function(){
    return this.$('#project-header').data('stats');
  },

  startTracking: function(){
    var self = this;

    this.trackOnPage('projects', 'show', function(){
      self.trackVisit('Visited project page', self.projectProperties());
      self.trackReminderClick();
      self.trackOnContributionStart();
    });

    this.trackProjectDashboard = function(){
      if(window.location.hash == '#reports'){
        self.track('Project owner engaged with Catarse', _.extend(self.projectProperties(), {action: 'Visited reports'}));
      }
    };

    this.trackOnPage('projects', 'edit', function(){
      $(window).on('hashchange', function() {
        self.trackProjectDashboard();
      });
    });

    this.trackOnPage('pages', 'show', function(){
      if(self.id == 'start'){
        self.track('Visited start page');
      }
    });

    this.trackPageVisit('projects', 'new', 'Visited new project page');
    this.trackPageVisit('projects', 'index', 'Visited home');
    this.trackPageVisit('explore', 'index', 'Explored projects');
    this.trackPageLoad('contributions', 'edit', 'Selected reward');
    this.trackPageLoad('contributions', 'show', 'Finished Contribution')
    this.trackProjectDashboard();
    this.trackContributions();
    this.trackPaymentChoice();
    this.trackTwitterShare();
    this.trackFacebookShare();
    this.trackFollowCategory();
    try {
      this.trackOnFacebookLike();
      this.trackOnFacebookComment();
    } catch(e) {
      console.log(e);
    }
  },

  trackFollowCategory: function(){
    var self = this;
    this.$('.category-follow .follow-btn').on('click', function(event){
      self.track('Engaged with Catarse', { ref: $(event.currentTarget).data('title'), action: 'click follow category' });
      return true;
    });
  },

  trackOnContributionStart: function(){
    var self = this;

    this.$('.card-reward').on('click', function(event){
      self.trackStartedContribution('Reward click');
    });

    this.$('#contribute_project_form').on('click', function(event){
      self.trackStartedContribution('Contribute button click');
    });
  },

  trackStartedContribution: function(action){
    this.track('Started contribution', _.extend(this.projectProperties(), { action: action }));
  },

  trackReminderClick: function(){
    var self = this;
    this.$('#reminder:not([data-method])').on('click', function(event){
      self.track('Engaged with Catarse', { ref: $(event.currentTarget).data('title'), action: 'click reminder' });
      return true;
    });
  },

  trackPageLoad: function(controller, action, text){
    var self = this;
    this.trackOnPage(controller, action, function(){
      self.track(text);
    });
  },

  trackContributions: function(){
    var from = '',
        self = this;
    this.trackOnPage('contributions', 'new', function(){
      if(window.location.href.indexOf('reward_id') > -1){
        from = 'Reward click';
      } else {
        from = 'Contribute button click';
      }
      self.track('Started contribution', _.extend(self.projectProperties(), {action: from}));
    });
  },

  trackPaymentChoice: function(){
    var self = this;
    this.$('#engine').on('click', '.back-payment-radio-button', function(){
      var choice = $('.back-payment-radio-button:checked').val();
      console.log("Clicked payment");
      if(choice === 'slip'){
        self.track('Payment chosen', _.extend(self.projectProperties(), {payment_choice: 'BoletoBancario'}));
      }
      if(choice === 'credit_card'){
        self.track('Payment chosen', _.extend(self.projectProperties(), {payment_choice: 'CartaoDeCredito'}));
      }
    });
  },

  trackPageVisit: function(controller, action, text){
    var self = this;
    this.trackOnPage(controller, action, function(){
      self.trackVisit(text);
    });
  },

  trackOnPage: function(controller, action, callback){
    if(this.controller == controller && this.action == action){
      callback();
    }
  },

  trackTwitterShare: function() {
    var self = this;

    this.$('#twitter_share_button').on('click', function(event){
      self.track('Engaged with Catarse', { ref: $(event.currentTarget).data('title'), action: 'share twitter' });
    });
  },

  trackFacebookShare: function() {
    var self = this;
    this.$('a#facebook_share').on('click', function(event){
      self.track('Engaged with Catarse', { ref: $(event.currentTarget).data('title'), action: 'share facebook' });
    });
  },

  trackOnFacebookComment: function() {
    var self = this;

    FB.Event.subscribe('comment.create', function(url, html_element){
      self.track('Engaged with Catarse', { ref: $(html_element).data('title'), action: 'comment facebook' });
    });
  },

  trackOnFacebookLike: function() {
    var self = this;

    FB.Event.subscribe('edge.create', function(url, html_element){
      self.track('Engaged with Catarse', { ref: $(html_element).data('title'), action: 'like facebook' });
    });
  },

  onLogin: function(){
    if(this.user.created_today){
      mixpanel.alias(this.user.user_id);
      this.track("Signed up");
    }
    else{
      this.track("Logged in");
    }
  },

  detectLogin: function(){
    if(this.user){ // User is logged in
      if(this.user.user_id != store.get('user_id')){ // First page load after login
        this.onLogin();
        store.set('user_id', this.user.user_id);
      }
    }
    else if(store.get('user_id')){ // Logout
      if(mixpanel && mixpanel.cookie){
        mixpanel.cookie.clear();
      }
      store.set('user_id', null);
    }
  },

  identifyUser: function(){
    if (this.user){
      mixpanel.name_tag(this.user.email);
      mixpanel.identify(this.user.user_id);
      mixpanel.people.set({
        "$email": this.user.email,
        "$created": this.user.created,
        "$last_login": this.user.last_login,
        "contributions": this.user.contributions
      });
    }
  },

  track: function(text, options){
    this.identifyUser();
    var opt             = options || {};
    var obj             = $(this);
    var ref             = (obj.attr('href') != undefined) ? obj.attr('href') : (opt.ref ? opt.ref : null);
    var usr             = this.user || {};
    var page            = {
      'page name':          document.title,
      'url':                window.location,
      'host':               window.location.host,
      'from_page':          this.$el.data('referral')
    };

    var opt = $.fn.extend(usr, opt, page, {project : ref});

    mixpanel.track(text, opt);
  },

  mixPanelEvent: function(target, event, text, options){
    var self = this;
    this.$(target).on(event, function(){
      self.track(text, options);
    });
  },

  trackVisit: function(eventName, options){
    var self = this;
    window.setTimeout(function(){
      self.track(eventName, options);
    }, this.VISIT_MIN_TIME);
  }
});
<|endoftext|>


<commit_before>Package.describe({summary: "Crater.io Theme"});

Package.onUse(function (api) {

  api.use(['telescope-lib', 'telescope-base', 'useraccounts:bootstrap'], ['client', 'server']);

  api.use(['templating', 'less'], 'client');

  api.addFiles([
    'lib/crater.js',
  ], ['client', 'server']);

  // Bootstrap - Base
  api.addFiles([
    'lib/client/stylesheets/lib/bootstrap/alerts.import.less',
    'lib/client/stylesheets/lib/bootstrap/badges.import.less',
    'lib/client/stylesheets/lib/bootstrap/bootstrap.import.less',
    'lib/client/stylesheets/lib/bootstrap/breadcrumbs.import.less',
    'lib/client/stylesheets/lib/bootstrap/button-groups.import.less',
    'lib/client/stylesheets/lib/bootstrap/buttons.import.less',
    'lib/client/stylesheets/lib/bootstrap/close.import.less',
    'lib/client/stylesheets/lib/bootstrap/code.import.less',
    'lib/client/stylesheets/lib/bootstrap/component-animations.import.less',
    'lib/client/stylesheets/lib/bootstrap/dropdowns.import.less',
    'lib/client/stylesheets/lib/bootstrap/forms.import.less',
    'lib/client/stylesheets/lib/bootstrap/glyphicons.import.less',
    'lib/client/stylesheets/lib/bootstrap/grid.import.less',
    'lib/client/stylesheets/lib/bootstrap/input-groups.import.less',
    'lib/client/stylesheets/lib/bootstrap/jumbotron.import.less',
    'lib/client/stylesheets/lib/bootstrap/labels.import.less',
    'lib/client/stylesheets/lib/bootstrap/list-group.import.less',
    'lib/client/stylesheets/lib/bootstrap/media.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins.import.less',
    'lib/client/stylesheets/lib/bootstrap/modals.import.less',
    'lib/client/stylesheets/lib/bootstrap/navbar.import.less',
    'lib/client/stylesheets/lib/bootstrap/navs.import.less',
    'lib/client/stylesheets/lib/bootstrap/normalize.import.less',
    'lib/client/stylesheets/lib/bootstrap/pager.import.less',
    'lib/client/stylesheets/lib/bootstrap/pagination.import.less',
    'lib/client/stylesheets/lib/bootstrap/panels.import.less',
    'lib/client/stylesheets/lib/bootstrap/popovers.import.less',
    'lib/client/stylesheets/lib/bootstrap/print.import.less',
    'lib/client/stylesheets/lib/bootstrap/progress-bars.import.less',
    'lib/client/stylesheets/lib/bootstrap/responsive-embed.import.less',
    'lib/client/stylesheets/lib/bootstrap/responsive-utilities.import.less',
    'lib/client/stylesheets/lib/bootstrap/scaffolding.import.less',
    'lib/client/stylesheets/lib/bootstrap/tables.import.less',
    'lib/client/stylesheets/lib/bootstrap/thumbnails.import.less',
    'lib/client/stylesheets/lib/bootstrap/tooltip.import.less',
    'lib/client/stylesheets/lib/bootstrap/type.import.less',
    'lib/client/stylesheets/lib/bootstrap/utilities.import.less',
    'lib/client/stylesheets/lib/bootstrap/variables.import.less',
    'lib/client/stylesheets/lib/bootstrap/wells.import.less'
  ], ['client']);

  // Bootstrap - Mixins
  api.addFiles([
    'lib/client/stylesheets/lib/bootstrap/mixins/alerts.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/background-variant.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/border-radius.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/buttons.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/center-block.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/clearfix.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/forms.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/gradients.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/grid-framework.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/grid.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/hide-text.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/image.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/labels.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/list-group.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/nav-divider.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/nav-vertical-align.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/opacity.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/pagination.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/panels.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/progress-bar.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/reset-filter.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/resize.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/responsive-visibility.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/size.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/tab-focus.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/table-row.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/text-emphasis.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/text-overflow.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/vendor-prefixes.import.less'
  ], ['client']);

  // Ionicons - Fonts
  api.addFiles([
    'lib/public/fonts/ionicons.eot',
    'lib/public/fonts/ionicons.svg',
    'lib/public/fonts/ionicons.ttf',
    'lib/public/fonts/ionicons.woff'
  ], ['client']);

  // Ionicons - CSS
  api.addFiles([
    'lib/client/stylesheets/lib/ionicons/_ionicons-font.import.less',
    'lib/client/stylesheets/lib/ionicons/_ionicons-icons.import.less',
    'lib/client/stylesheets/lib/ionicons/_ionicons-variables.import.less',
    'lib/client/stylesheets/lib/ionicons/ionicons.less'
  ], ['client']);

  // Custom CSS - Base
  api.addFiles([
    'lib/client/stylesheets/base/variables.import.less',
    'lib/client/stylesheets/base/mixins.import.less',
    'lib/client/stylesheets/base/type.import.less',
    'lib/client/stylesheets/base/mobile.import.less',
    'lib/client/stylesheets/crater.less'
  ], ['client']);

  // Custom CSS - Components
  api.addFiles([
    'lib/client/stylesheets/components/avatars.import.less',
    'lib/client/stylesheets/components/buttons.import.less',
    'lib/client/stylesheets/components/comment-item.import.less',
    'lib/client/stylesheets/components/forms.import.less',
    'lib/client/stylesheets/components/jumbotron.import.less',
    'lib/client/stylesheets/components/navs.import.less',
    'lib/client/stylesheets/components/newsletter-banner.import.less',
    'lib/client/stylesheets/components/post-detail.import.less',
    'lib/client/stylesheets/components/post-item.import.less',
    'lib/client/stylesheets/components/post-list.import.less',
    'lib/client/stylesheets/components/telescope.import.less',
    'lib/client/stylesheets/components/update-banner.import.less',
    'lib/client/stylesheets/components/user-profile.import.less'
  ], ['client']);

  // Custom CSS - Layout
  api.addFiles([
    'lib/client/stylesheets/layout/content.import.less',
    'lib/client/stylesheets/layout/footer.import.less',
    'lib/client/stylesheets/layout/header.import.less',
    'lib/client/stylesheets/layout/tabs.import.less'
  ], ['client']);

  // Images
  api.addFiles([
    'lib/public/images/crater.png'
  ], ['client']);

  // JavaScript
  api.addFiles([
    'lib/client/compatibility/bootstrap.js'
  ], ['client']);

  // Custom Templates
  api.addFiles([
    'lib/client/templates/comments/comment_item.html',
    'lib/client/templates/common/footer.html',
    'lib/client/templates/common/layout.html',
    'lib/client/templates/nav/nav.html',
    'lib/client/templates/nav/nav.js',
    'lib/client/templates/nav/admin_menu.html',
    'lib/client/templates/nav/categories_menu.html',
    'lib/client/templates/nav/notifications_menu.html',
    'lib/client/templates/nav/search.html',
    'lib/client/templates/nav/submit_button.html',
    'lib/client/templates/nav/tabs.html',
    'lib/client/templates/nav/tabs.js',
    'lib/client/templates/posts/modules/post_admin.html',
    'lib/client/templates/posts/modules/post_avatars.html',
    'lib/client/templates/posts/modules/post_avatars.js',
    'lib/client/templates/posts/modules/post_categories.html',
    'lib/client/templates/posts/modules/post_discuss.html',
    'lib/client/templates/posts/modules/post_domain.html',
    'lib/client/templates/posts/modules/post_domain.js',
    'lib/client/templates/posts/modules/post_info.html',
    'lib/client/templates/posts/modules/post_upvote.html',
    'lib/client/templates/posts/post_body.html',
    'lib/client/templates/posts/post_edit.html',
    'lib/client/templates/posts/posts_load_more.html',
    'lib/client/templates/users/user_profile.html',
    'lib/client/templates/users/user_profile.js',
    'lib/client/templates/users/profile/user_comments.html',
    'lib/client/templates/users/profile/user_downvoted_posts.html',
    'lib/client/templates/users/profile/user_info.html',
    'lib/client/templates/users/profile/user_posts.html',
    'lib/client/templates/users/profile/user_upvoted_posts.html'
  ], ['client']);

  // Custom Configuration
  api.export([
    'viewNav',
    'postHeading',
    'postMeta',
    'heroModules'
  ]);

});
<commit_msg>Try to export more from the theme package<commit_after>Package.describe({summary: "Crater.io Theme"});

Package.onUse(function (api) {

  api.use(['telescope-lib', 'telescope-base', 'useraccounts:bootstrap'], ['client', 'server']);

  api.use(['templating', 'less'], 'client');

  api.addFiles([
    'lib/crater.js',
  ], ['client', 'server']);

  // Bootstrap - Base
  api.addFiles([
    'lib/client/stylesheets/lib/bootstrap/alerts.import.less',
    'lib/client/stylesheets/lib/bootstrap/badges.import.less',
    'lib/client/stylesheets/lib/bootstrap/bootstrap.import.less',
    'lib/client/stylesheets/lib/bootstrap/breadcrumbs.import.less',
    'lib/client/stylesheets/lib/bootstrap/button-groups.import.less',
    'lib/client/stylesheets/lib/bootstrap/buttons.import.less',
    'lib/client/stylesheets/lib/bootstrap/close.import.less',
    'lib/client/stylesheets/lib/bootstrap/code.import.less',
    'lib/client/stylesheets/lib/bootstrap/component-animations.import.less',
    'lib/client/stylesheets/lib/bootstrap/dropdowns.import.less',
    'lib/client/stylesheets/lib/bootstrap/forms.import.less',
    'lib/client/stylesheets/lib/bootstrap/glyphicons.import.less',
    'lib/client/stylesheets/lib/bootstrap/grid.import.less',
    'lib/client/stylesheets/lib/bootstrap/input-groups.import.less',
    'lib/client/stylesheets/lib/bootstrap/jumbotron.import.less',
    'lib/client/stylesheets/lib/bootstrap/labels.import.less',
    'lib/client/stylesheets/lib/bootstrap/list-group.import.less',
    'lib/client/stylesheets/lib/bootstrap/media.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins.import.less',
    'lib/client/stylesheets/lib/bootstrap/modals.import.less',
    'lib/client/stylesheets/lib/bootstrap/navbar.import.less',
    'lib/client/stylesheets/lib/bootstrap/navs.import.less',
    'lib/client/stylesheets/lib/bootstrap/normalize.import.less',
    'lib/client/stylesheets/lib/bootstrap/pager.import.less',
    'lib/client/stylesheets/lib/bootstrap/pagination.import.less',
    'lib/client/stylesheets/lib/bootstrap/panels.import.less',
    'lib/client/stylesheets/lib/bootstrap/popovers.import.less',
    'lib/client/stylesheets/lib/bootstrap/print.import.less',
    'lib/client/stylesheets/lib/bootstrap/progress-bars.import.less',
    'lib/client/stylesheets/lib/bootstrap/responsive-embed.import.less',
    'lib/client/stylesheets/lib/bootstrap/responsive-utilities.import.less',
    'lib/client/stylesheets/lib/bootstrap/scaffolding.import.less',
    'lib/client/stylesheets/lib/bootstrap/tables.import.less',
    'lib/client/stylesheets/lib/bootstrap/thumbnails.import.less',
    'lib/client/stylesheets/lib/bootstrap/tooltip.import.less',
    'lib/client/stylesheets/lib/bootstrap/type.import.less',
    'lib/client/stylesheets/lib/bootstrap/utilities.import.less',
    'lib/client/stylesheets/lib/bootstrap/variables.import.less',
    'lib/client/stylesheets/lib/bootstrap/wells.import.less'
  ], ['client']);

  // Bootstrap - Mixins
  api.addFiles([
    'lib/client/stylesheets/lib/bootstrap/mixins/alerts.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/background-variant.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/border-radius.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/buttons.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/center-block.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/clearfix.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/forms.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/gradients.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/grid-framework.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/grid.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/hide-text.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/image.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/labels.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/list-group.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/nav-divider.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/nav-vertical-align.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/opacity.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/pagination.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/panels.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/progress-bar.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/reset-filter.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/resize.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/responsive-visibility.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/size.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/tab-focus.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/table-row.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/text-emphasis.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/text-overflow.import.less',
    'lib/client/stylesheets/lib/bootstrap/mixins/vendor-prefixes.import.less'
  ], ['client']);

  // Ionicons - Fonts
  api.addFiles([
    'lib/public/fonts/ionicons.eot',
    'lib/public/fonts/ionicons.svg',
    'lib/public/fonts/ionicons.ttf',
    'lib/public/fonts/ionicons.woff'
  ], ['client']);

  // Ionicons - CSS
  api.addFiles([
    'lib/client/stylesheets/lib/ionicons/_ionicons-font.import.less',
    'lib/client/stylesheets/lib/ionicons/_ionicons-icons.import.less',
    'lib/client/stylesheets/lib/ionicons/_ionicons-variables.import.less',
    'lib/client/stylesheets/lib/ionicons/ionicons.less'
  ], ['client']);

  // Custom CSS - Base
  api.addFiles([
    'lib/client/stylesheets/base/variables.import.less',
    'lib/client/stylesheets/base/mixins.import.less',
    'lib/client/stylesheets/base/type.import.less',
    'lib/client/stylesheets/base/mobile.import.less',
    'lib/client/stylesheets/crater.less'
  ], ['client']);

  // Custom CSS - Components
  api.addFiles([
    'lib/client/stylesheets/components/avatars.import.less',
    'lib/client/stylesheets/components/buttons.import.less',
    'lib/client/stylesheets/components/comment-item.import.less',
    'lib/client/stylesheets/components/forms.import.less',
    'lib/client/stylesheets/components/jumbotron.import.less',
    'lib/client/stylesheets/components/navs.import.less',
    'lib/client/stylesheets/components/newsletter-banner.import.less',
    'lib/client/stylesheets/components/post-detail.import.less',
    'lib/client/stylesheets/components/post-item.import.less',
    'lib/client/stylesheets/components/post-list.import.less',
    'lib/client/stylesheets/components/telescope.import.less',
    'lib/client/stylesheets/components/update-banner.import.less',
    'lib/client/stylesheets/components/user-profile.import.less'
  ], ['client']);

  // Custom CSS - Layout
  api.addFiles([
    'lib/client/stylesheets/layout/content.import.less',
    'lib/client/stylesheets/layout/footer.import.less',
    'lib/client/stylesheets/layout/header.import.less',
    'lib/client/stylesheets/layout/tabs.import.less'
  ], ['client']);

  // Images
  api.addFiles([
    'lib/public/images/crater.png'
  ], ['client']);

  // JavaScript
  api.addFiles([
    'lib/client/compatibility/bootstrap.js'
  ], ['client']);

  // Custom Templates
  api.addFiles([
    'lib/client/templates/comments/comment_item.html',
    'lib/client/templates/common/footer.html',
    'lib/client/templates/common/layout.html',
    'lib/client/templates/nav/nav.html',
    'lib/client/templates/nav/nav.js',
    'lib/client/templates/nav/admin_menu.html',
    'lib/client/templates/nav/categories_menu.html',
    'lib/client/templates/nav/notifications_menu.html',
    'lib/client/templates/nav/search.html',
    'lib/client/templates/nav/submit_button.html',
    'lib/client/templates/nav/tabs.html',
    'lib/client/templates/nav/tabs.js',
    'lib/client/templates/posts/modules/post_admin.html',
    'lib/client/templates/posts/modules/post_avatars.html',
    'lib/client/templates/posts/modules/post_avatars.js',
    'lib/client/templates/posts/modules/post_categories.html',
    'lib/client/templates/posts/modules/post_discuss.html',
    'lib/client/templates/posts/modules/post_domain.html',
    'lib/client/templates/posts/modules/post_domain.js',
    'lib/client/templates/posts/modules/post_info.html',
    'lib/client/templates/posts/modules/post_upvote.html',
    'lib/client/templates/posts/post_body.html',
    'lib/client/templates/posts/post_edit.html',
    'lib/client/templates/posts/posts_load_more.html',
    'lib/client/templates/users/user_profile.html',
    'lib/client/templates/users/user_profile.js',
    'lib/client/templates/users/profile/user_comments.html',
    'lib/client/templates/users/profile/user_downvoted_posts.html',
    'lib/client/templates/users/profile/user_info.html',
    'lib/client/templates/users/profile/user_posts.html',
    'lib/client/templates/users/profile/user_upvoted_posts.html'
  ], ['client']);

  // Custom Configuration
  api.export([
    'viewNav',
    'postHeading',
    'postModules',
    'postMeta',
    'heroModules'
  ]);

});
<|endoftext|>


<commit_before>require('./helpers/autoconfig')

var express = require('express')
var fs = require('fs-extra')
var morgan = require('morgan')
var bodyParser = require('body-parser')
var passport = require('passport')
var Strategy = require('passport-http').DigestStrategy
var db = require('./db')

var settings = require('./helpers/settings')

// var tile_cache = require('./helpers/tile-cache')
// tile_cache.pyramid_urls(-76.3605,-9.4491,-72.757,-6.1078, 1, 13)

var FormSubmissionMiddleware = require('openrosa-form-submission-middleware')
var ProcessSubmission = require('./middlewares/process-submission')
var SaveMedia = require('./middlewares/save-media')
var AppendGeoJSON = require('./middlewares/append-geojson')

var storage = require('./helpers/community-storage')
var forms = require('./controllers/forms')
var error = require('./controllers/error-handler')
var CommunityLands = require('./controllers/community-lands')
var MapFilter = require('./controllers/map-filter')
var Cms = require('./controllers/cms')

var TileLayers = require('./controllers/tile-layers')
var Bing = require('./controllers/bing-proxy')

var Backup = require('./controllers/backup')
var Tracks = require('./controllers/tracks')

// Configure the Digest strategy for use by Passport.
//
// The Digest strategy requires a `secret`function, which is used to look up
// user.  The function must invoke `cb` with the user object as well as the
// user's password as known by the server.  The password is used to compute a
// hash, and authentication will fail if the computed value does not match that
// of the request.  The user object will be set at `req.user` in route handlers
// after authentication.
passport.use(new Strategy({ qop: 'auth' },
  function (username, cb) {
    db.users.findByUsername(username, function (err, user) {
      if (err) { return cb(err) }
      if (!user) { return cb(null, false) }
      return cb(null, user, user.password)
    })
  }))

// Create a new Express application.
var app = express()
var path = require('path')

app.use(morgan('dev'))

app.use('/mapfilter', express.static(__dirname + '/mapfilter'))
app.use('/website', express.static(__dirname + '/website'))
app.use(bodyParser.urlencoded({limit: '500mb'}))
app.post('/website/save', bodyParser.urlencoded({extended: true}), Cms.save)
app.get('/mapfilter/json/mapfilter-config.json', MapFilter.config)
app.get('/mapfilter/json/mapfilter-locations.geojson', MapFilter.locations);
app.get('/mapfilter/filters', MapFilter.listFilters)
app.post('/mapfilter/filters/local', bodyParser.json(), MapFilter.saveFilter)
app.delete('/mapfilter/filters/local/:id', MapFilter.deleteFilter);

// Map Tiles
app.get('/bing-metadata/:url', Bing.metadata)
app.get('/bing-proxy/:url', Bing.prepare, Bing.proxy)
app.get('/map-tiles/:id*', TileLayers.getTile)

app.get('/tracks', Tracks.tracks)
app.get('/sounds/:file', Tracks.sounds)

app.use('/monitoring-files', express.static(settings.getRootPath()))

app.get('/',
  function (req, res) {
    res.redirect('/mapfilter')
  })

var handleError = function (err, res) {
  res.status(500)
  res.render('error', {
    message: err.message,
    error: err
  })
}

app.get('/map', function (req, res, next) {
  storage.getMap('Monitoring.geojson', function (err, data) {
    if (err) {
      handleError(err, res)
    } else {
      res.json(JSON.parse(data))
    }
  })
})

app.get('/json/Monitoring.json', function (req, res, next) {
  storage.getMap('Monitoring.geojson', function (err, data) {
    if (err) {
      handleError(err, res)
    } else {
      res.json(JSON.parse(data).features)
    }
  })
})

app.get('/formList', forms.index)

app.get('/forms', forms.index)
app.get('/forms/:id', forms.show)

app.get('/backup/latest', CommunityLands.Submissions.backup)
app.get('/backup/all', CommunityLands.Submissions.resync)
app.get('/backup/status', CommunityLands.Submissions.lastBackup)

app.get('/communitylands/latest', CommunityLands.Submissions.backup)
app.get('/communitylands/status', CommunityLands.Content.lastBackup)

app.get('/save/all', Backup.backup)
app.get('/save/status', Backup.lastBackup)

app.get('/tileLayers', TileLayers.listTileLayers)

app.post('/filters', bodyParser.json(), CommunityLands.saveFilter)

app.head('/submission',
  passport.authenticate('digest', { session: false }),
  FormSubmissionMiddleware())

app.post('/submission',
  passport.authenticate('digest', { session: false }),
  FormSubmissionMiddleware(),
  ProcessSubmission(),
  SaveMedia(),
  AppendGeoJSON(),
  forms.create)

app.use(error)

var port = settings.getPort()
app.listen(port)
console.log('Listening on port %s', port)
<commit_msg>Correct bodyParser deprecation for urlextended<commit_after>require('./helpers/autoconfig')

var express = require('express')
var fs = require('fs-extra')
var morgan = require('morgan')
var bodyParser = require('body-parser')
var passport = require('passport')
var Strategy = require('passport-http').DigestStrategy
var db = require('./db')

var settings = require('./helpers/settings')

// var tile_cache = require('./helpers/tile-cache')
// tile_cache.pyramid_urls(-76.3605,-9.4491,-72.757,-6.1078, 1, 13)

var FormSubmissionMiddleware = require('openrosa-form-submission-middleware')
var ProcessSubmission = require('./middlewares/process-submission')
var SaveMedia = require('./middlewares/save-media')
var AppendGeoJSON = require('./middlewares/append-geojson')

var storage = require('./helpers/community-storage')
var forms = require('./controllers/forms')
var error = require('./controllers/error-handler')
var CommunityLands = require('./controllers/community-lands')
var MapFilter = require('./controllers/map-filter')
var Cms = require('./controllers/cms')

var TileLayers = require('./controllers/tile-layers')
var Bing = require('./controllers/bing-proxy')

var Backup = require('./controllers/backup')
var Tracks = require('./controllers/tracks')

// Configure the Digest strategy for use by Passport.
//
// The Digest strategy requires a `secret`function, which is used to look up
// user.  The function must invoke `cb` with the user object as well as the
// user's password as known by the server.  The password is used to compute a
// hash, and authentication will fail if the computed value does not match that
// of the request.  The user object will be set at `req.user` in route handlers
// after authentication.
passport.use(new Strategy({ qop: 'auth' },
  function (username, cb) {
    db.users.findByUsername(username, function (err, user) {
      if (err) { return cb(err) }
      if (!user) { return cb(null, false) }
      return cb(null, user, user.password)
    })
  }))

// Create a new Express application.
var app = express()
var path = require('path')

app.use(morgan('dev'))

app.use('/mapfilter', express.static(__dirname + '/mapfilter'))
app.use('/website', express.static(__dirname + '/website'))
app.use(bodyParser.urlencoded({extended: true, limit: '500mb'}))
app.post('/website/save', bodyParser.urlencoded({extended: true}), Cms.save)
app.get('/mapfilter/json/mapfilter-config.json', MapFilter.config)
app.get('/mapfilter/json/mapfilter-locations.geojson', MapFilter.locations);
app.get('/mapfilter/filters', MapFilter.listFilters)
app.post('/mapfilter/filters/local', bodyParser.json(), MapFilter.saveFilter)
app.delete('/mapfilter/filters/local/:id', MapFilter.deleteFilter);

// Map Tiles
app.get('/bing-metadata/:url', Bing.metadata)
app.get('/bing-proxy/:url', Bing.prepare, Bing.proxy)
app.get('/map-tiles/:id*', TileLayers.getTile)

app.get('/tracks', Tracks.tracks)
app.get('/sounds/:file', Tracks.sounds)

app.use('/monitoring-files', express.static(settings.getRootPath()))

app.get('/',
  function (req, res) {
    res.redirect('/mapfilter')
  })

var handleError = function (err, res) {
  res.status(500)
  res.render('error', {
    message: err.message,
    error: err
  })
}

app.get('/map', function (req, res, next) {
  storage.getMap('Monitoring.geojson', function (err, data) {
    if (err) {
      handleError(err, res)
    } else {
      res.json(JSON.parse(data))
    }
  })
})

app.get('/json/Monitoring.json', function (req, res, next) {
  storage.getMap('Monitoring.geojson', function (err, data) {
    if (err) {
      handleError(err, res)
    } else {
      res.json(JSON.parse(data).features)
    }
  })
})

app.get('/formList', forms.index)

app.get('/forms', forms.index)
app.get('/forms/:id', forms.show)

app.get('/backup/latest', CommunityLands.Submissions.backup)
app.get('/backup/all', CommunityLands.Submissions.resync)
app.get('/backup/status', CommunityLands.Submissions.lastBackup)

app.get('/communitylands/latest', CommunityLands.Submissions.backup)
app.get('/communitylands/status', CommunityLands.Content.lastBackup)

app.get('/save/all', Backup.backup)
app.get('/save/status', Backup.lastBackup)

app.get('/tileLayers', TileLayers.listTileLayers)

app.post('/filters', bodyParser.json(), CommunityLands.saveFilter)

app.head('/submission',
  passport.authenticate('digest', { session: false }),
  FormSubmissionMiddleware())

app.post('/submission',
  passport.authenticate('digest', { session: false }),
  FormSubmissionMiddleware(),
  ProcessSubmission(),
  SaveMedia(),
  AppendGeoJSON(),
  forms.create)

app.use(error)

var port = settings.getPort()
app.listen(port)
console.log('Listening on port %s', port)
<|endoftext|>


<commit_before>/*
Decompressor derived from LZ-String, with unused code removed.
Copyright (c) 2013 Pieroxy <pieroxy@pieroxy.net>
Licensed under the WTFPL license.
*/
(function(input) {
  var f = String.fromCharCode,
      doc = document,
      /*length = input.length, */
      resetValue = 32,
      keyStrBase64 =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
      baseReverseDic = {},

  // Original decompress starts here.
	    dictionary = [],
			next,
			enlargeIn = 4,
			dictSize = 4,
			numBits = 3,
			entry = "",
			result = [],
      mpow = Math.pow,
			i,
			w,
			bits, resb, maxpower, power,
			c,
			data = {v:getNextValue(0), p:resetValue, i:1};

  for (i = 0; i < keyStrBase64.length ; i++) {
    baseReverseDic[keyStrBase64.charAt(i)] = i;
  }
  function getNextValue(i) { return baseReverseDic[input.charAt(i)]; }

  doc.scripts[0].remove();

	for (i = 0; i < 3; i += 1) {
		dictionary[i] = i;
	}

	bits = 0;
	maxpower = mpow(2,2);
	power=1;
	while (power!=maxpower) {
		resb = data.v & data.p;
		data.p >>= 1;
		if (data.p == 0) {
			data.p = resetValue;
			data.v = getNextValue(data.i++);
		}
		bits |= (resb>0 ? 1 : 0) * power;
		power <<= 1;
	}

	switch (next = bits) {
		case 0:
				bits = 0;
				maxpower = mpow(2,8);
				power=1;
				while (power!=maxpower) {
					resb = data.v & data.p;
					data.p >>= 1;
					if (data.p == 0) {
						data.p = resetValue;
						data.v = getNextValue(data.i++);
					}
					bits |= (resb>0 ? 1 : 0) * power;
					power <<= 1;
				}
			c = f(bits);
			break;
		case 1:
				bits = 0;
				maxpower = mpow(2,16);
				power=1;
				while (power!=maxpower) {
					resb = data.v & data.p;
					data.p >>= 1;
					if (data.p == 0) {
						data.p = resetValue;
						data.v = getNextValue(data.i++);
					}
					bits |= (resb>0 ? 1 : 0) * power;
					power <<= 1;
				}
			c = f(bits);
			break;
    /* Should not happen
		case 2:
			return "";
    */
	}
	dictionary[3] = c;
	w = c;
	result.push(c);
	while (true) {
    /* Should not happen
		if (data.i > length) {
			return "";
		}
    */

		bits = 0;
		maxpower = mpow(2,numBits);
		power=1;
		while (power!=maxpower) {
			resb = data.v & data.p;
			data.p >>= 1;
			if (data.p == 0) {
				data.p = resetValue;
				data.v = getNextValue(data.i++);
			}
			bits |= (resb>0 ? 1 : 0) * power;
			power <<= 1;
		}

		switch (c = bits) {
			case 0:
				bits = 0;
				maxpower = mpow(2,8);
				power=1;
				while (power!=maxpower) {
					resb = data.v & data.p;
					data.p >>= 1;
					if (data.p == 0) {
						data.p = resetValue;
						data.v = getNextValue(data.i++);
					}
					bits |= (resb>0 ? 1 : 0) * power;
					power <<= 1;
				}

				dictionary[dictSize++] = f(bits);
				c = dictSize-1;
				enlargeIn--;
				break;
			case 1:
				bits = 0;
				maxpower = mpow(2,16);
				power=1;
				while (power!=maxpower) {
					resb = data.v & data.p;
					data.p >>= 1;
					if (data.p == 0) {
						data.p = resetValue;
						data.v = getNextValue(data.i++);
					}
					bits |= (resb>0 ? 1 : 0) * power;
					power <<= 1;
				}
				dictionary[dictSize++] = f(bits);
				c = dictSize-1;
				enlargeIn--;
				break;
			case 2:
        /* when done, generate a document with the result. */
				doc.write(result.join(''));
        return;
		}

		if (enlargeIn == 0) {
			enlargeIn = mpow(2, numBits);
			numBits++;
		}

		if (dictionary[c]) {
			entry = dictionary[c];
		} else {
			if (c === dictSize) {
				entry = w + w.charAt(0);
			} /* Should not happen else {

				return null;
			} */
		}
		result.push(entry);

		// Add w+entry[0] to the dictionary.
		dictionary[dictSize++] = w + entry.charAt(0);
		enlargeIn--;

		w = entry;

		if (enlargeIn == 0) {
			enlargeIn = mpow(2, numBits);
			numBits++;
		}

	}

})('');
<commit_msg>Remove self-removal line.<commit_after>/*
Decompressor derived from LZ-String, with unused code removed.
Copyright (c) 2013 Pieroxy <pieroxy@pieroxy.net>
Licensed under the WTFPL license.
*/
(function(input) {
  var f = String.fromCharCode,
      doc = document,
      /*length = input.length, */
      resetValue = 32,
      keyStrBase64 =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
      baseReverseDic = {},

  // Original decompress starts here.
	    dictionary = [],
			next,
			enlargeIn = 4,
			dictSize = 4,
			numBits = 3,
			entry = "",
			result = [],
      mpow = Math.pow,
			i,
			w,
			bits, resb, maxpower, power,
			c,
			data = {v:getNextValue(0), p:resetValue, i:1};

  for (i = 0; i < keyStrBase64.length ; i++) {
    baseReverseDic[keyStrBase64.charAt(i)] = i;
  }
  function getNextValue(i) { return baseReverseDic[input.charAt(i)]; }

  // doc.scripts[doc.scripts.length - 1].remove();

	for (i = 0; i < 3; i += 1) {
		dictionary[i] = i;
	}

	bits = 0;
	maxpower = mpow(2,2);
	power=1;
	while (power!=maxpower) {
		resb = data.v & data.p;
		data.p >>= 1;
		if (data.p == 0) {
			data.p = resetValue;
			data.v = getNextValue(data.i++);
		}
		bits |= (resb>0 ? 1 : 0) * power;
		power <<= 1;
	}

	switch (next = bits) {
		case 0:
				bits = 0;
				maxpower = mpow(2,8);
				power=1;
				while (power!=maxpower) {
					resb = data.v & data.p;
					data.p >>= 1;
					if (data.p == 0) {
						data.p = resetValue;
						data.v = getNextValue(data.i++);
					}
					bits |= (resb>0 ? 1 : 0) * power;
					power <<= 1;
				}
			c = f(bits);
			break;
		case 1:
				bits = 0;
				maxpower = mpow(2,16);
				power=1;
				while (power!=maxpower) {
					resb = data.v & data.p;
					data.p >>= 1;
					if (data.p == 0) {
						data.p = resetValue;
						data.v = getNextValue(data.i++);
					}
					bits |= (resb>0 ? 1 : 0) * power;
					power <<= 1;
				}
			c = f(bits);
			break;
    /* Should not happen
		case 2:
			return "";
    */
	}
	dictionary[3] = c;
	w = c;
	result.push(c);
	while (true) {
    /* Should not happen
		if (data.i > length) {
			return "";
		}
    */

		bits = 0;
		maxpower = mpow(2,numBits);
		power=1;
		while (power!=maxpower) {
			resb = data.v & data.p;
			data.p >>= 1;
			if (data.p == 0) {
				data.p = resetValue;
				data.v = getNextValue(data.i++);
			}
			bits |= (resb>0 ? 1 : 0) * power;
			power <<= 1;
		}

		switch (c = bits) {
			case 0:
				bits = 0;
				maxpower = mpow(2,8);
				power=1;
				while (power!=maxpower) {
					resb = data.v & data.p;
					data.p >>= 1;
					if (data.p == 0) {
						data.p = resetValue;
						data.v = getNextValue(data.i++);
					}
					bits |= (resb>0 ? 1 : 0) * power;
					power <<= 1;
				}

				dictionary[dictSize++] = f(bits);
				c = dictSize-1;
				enlargeIn--;
				break;
			case 1:
				bits = 0;
				maxpower = mpow(2,16);
				power=1;
				while (power!=maxpower) {
					resb = data.v & data.p;
					data.p >>= 1;
					if (data.p == 0) {
						data.p = resetValue;
						data.v = getNextValue(data.i++);
					}
					bits |= (resb>0 ? 1 : 0) * power;
					power <<= 1;
				}
				dictionary[dictSize++] = f(bits);
				c = dictSize-1;
				enlargeIn--;
				break;
			case 2:
        /* when done, generate a document with the result. */
				doc.write(result.join(''));
        return;
		}

		if (enlargeIn == 0) {
			enlargeIn = mpow(2, numBits);
			numBits++;
		}

		if (dictionary[c]) {
			entry = dictionary[c];
		} else {
			if (c === dictSize) {
				entry = w + w.charAt(0);
			} /* Should not happen else {

				return null;
			} */
		}
		result.push(entry);

		// Add w+entry[0] to the dictionary.
		dictionary[dictSize++] = w + entry.charAt(0);
		enlargeIn--;

		w = entry;

		if (enlargeIn == 0) {
			enlargeIn = mpow(2, numBits);
			numBits++;
		}

	}

})('');
<|endoftext|>


<commit_before>'use strict';

module.exports = {
	db: process.env.MONGOHQ_URL || process.env.MONGOLAB_URI || 'mongodb://' + (process.env.DB_1_PORT_27017_TCP_ADDR || 'localhost') + '/betools',
	assets: {
		lib: {
			css: [
				'public/lib/bootstrap/dist/css/bootstrap.min.css',
				'public/lib/bootstrap/dist/css/bootstrap-theme.min.css',
				'public/lib/ng-table/dist/ng-table.min.css'
			],
			js: [
				'public/lib/angular/angular.min.js',
				'public/lib/angular-i18n/angular-locale_fr-fr.min.js',
				'public/lib/angular-resource/angular-resource.js',
				'public/lib/angular-cookies/angular-cookies.js',
				'public/lib/angular-animate/angular-animate.js',
				'public/lib/angular-touch/angular-touch.js',
				'public/lib/angular-sanitize/angular-sanitize.js',
				'public/lib/angular-ui-router/release/angular-ui-router.min.js',
				'public/lib/angular-ui-utils/ui-utils.min.js',
				'public/lib/angular-bootstrap/ui-bootstrap-tpls.min.js',
				'public/lib/ng-table/dist/ng-table.min.js'
			]
		},
		css: 'public/dist/application.min.css',
		js: 'public/dist/application.min.js'
	},
	facebook: {
		clientID: process.env.FACEBOOK_ID || 'APP_ID',
		clientSecret: process.env.FACEBOOK_SECRET || 'APP_SECRET',
		callbackURL: '/auth/facebook/callback'
	},
	twitter: {
		clientID: process.env.TWITTER_KEY || 'CONSUMER_KEY',
		clientSecret: process.env.TWITTER_SECRET || 'CONSUMER_SECRET',
		callbackURL: '/auth/twitter/callback'
	},
	google: {
		clientID: process.env.GOOGLE_ID || 'APP_ID',
		clientSecret: process.env.GOOGLE_SECRET || 'APP_SECRET',
		callbackURL: '/auth/google/callback'
	},
	linkedin: {
		clientID: process.env.LINKEDIN_ID || 'APP_ID',
		clientSecret: process.env.LINKEDIN_SECRET || 'APP_SECRET',
		callbackURL: '/auth/linkedin/callback'
	},
	github: {
		clientID: process.env.GITHUB_ID || 'APP_ID',
		clientSecret: process.env.GITHUB_SECRET || 'APP_SECRET',
		callbackURL: '/auth/github/callback'
	},
	mailer: {
		from: process.env.MAILER_FROM || 'MAILER_FROM',
		options: {
			service: process.env.MAILER_SERVICE_PROVIDER || 'MAILER_SERVICE_PROVIDER',
			auth: {
				user: process.env.MAILER_EMAIL_ID || 'MAILER_EMAIL_ID',
				pass: process.env.MAILER_PASSWORD || 'MAILER_PASSWORD'
			}
		}
	}
};
<commit_msg>Add parametrized mongo url.<commit_after>'use strict';

module.exports = {
    db: process.env.MONGOHQ_URL || process.env.MONGOLAB_URI || 'mongodb://' + process.env.BETOOLS_USER + process.env.BETOOLS_PWD + (process.env.DB_1_PORT_27017_TCP_ADDR || 'localhost') + '/betools?authSource=betools&ssl=true',

        assets: {
		lib: {
			css: [
				'public/lib/bootstrap/dist/css/bootstrap.min.css',
				'public/lib/bootstrap/dist/css/bootstrap-theme.min.css',
				'public/lib/ng-table/dist/ng-table.min.css'
			],
			js: [
				'public/lib/angular/angular.min.js',
				'public/lib/angular-i18n/angular-locale_fr-fr.min.js',
				'public/lib/angular-resource/angular-resource.js',
				'public/lib/angular-cookies/angular-cookies.js',
				'public/lib/angular-animate/angular-animate.js',
				'public/lib/angular-touch/angular-touch.js',
				'public/lib/angular-sanitize/angular-sanitize.js',
				'public/lib/angular-ui-router/release/angular-ui-router.min.js',
				'public/lib/angular-ui-utils/ui-utils.min.js',
				'public/lib/angular-bootstrap/ui-bootstrap-tpls.min.js',
				'public/lib/ng-table/dist/ng-table.min.js'
			]
		},
		css: 'public/dist/application.min.css',
		js: 'public/dist/application.min.js'
	},
	facebook: {
		clientID: process.env.FACEBOOK_ID || 'APP_ID',
		clientSecret: process.env.FACEBOOK_SECRET || 'APP_SECRET',
		callbackURL: '/auth/facebook/callback'
	},
	twitter: {
		clientID: process.env.TWITTER_KEY || 'CONSUMER_KEY',
		clientSecret: process.env.TWITTER_SECRET || 'CONSUMER_SECRET',
		callbackURL: '/auth/twitter/callback'
	},
	google: {
		clientID: process.env.GOOGLE_ID || 'APP_ID',
		clientSecret: process.env.GOOGLE_SECRET || 'APP_SECRET',
		callbackURL: '/auth/google/callback'
	},
	linkedin: {
		clientID: process.env.LINKEDIN_ID || 'APP_ID',
		clientSecret: process.env.LINKEDIN_SECRET || 'APP_SECRET',
		callbackURL: '/auth/linkedin/callback'
	},
	github: {
		clientID: process.env.GITHUB_ID || 'APP_ID',
		clientSecret: process.env.GITHUB_SECRET || 'APP_SECRET',
		callbackURL: '/auth/github/callback'
	},
	mailer: {
		from: process.env.MAILER_FROM || 'MAILER_FROM',
		options: {
			service: process.env.MAILER_SERVICE_PROVIDER || 'MAILER_SERVICE_PROVIDER',
			auth: {
				user: process.env.MAILER_EMAIL_ID || 'MAILER_EMAIL_ID',
				pass: process.env.MAILER_PASSWORD || 'MAILER_PASSWORD'
			}
		}
	}
};
<|endoftext|>


<commit_before>import React, {PropTypes} from 'react'
import styles from 'part:@sanity/components/edititem/popover-style'
import Button from 'part:@sanity/components/buttons/default'
import {debounce} from 'lodash'
import CloseIcon from 'part:@sanity/base/close-icon'
import scroll from 'scroll'
import ease from 'ease-component'
import Portal from './Portal'

export default class EditItemPopOver extends React.Component {
  static propTypes = {
    title: PropTypes.string,
    children: PropTypes.node.isRequired,
    className: PropTypes.string,
    onClose: PropTypes.func,
    isCreatingNewItem: PropTypes.bool,
    actions: PropTypes.arrayOf(PropTypes.shape({
      kind: PropTypes.string,
      title: PropTypes.string,
      handleClick: PropTypes.func
    })),
    fullWidth: PropTypes.bool,
    onNeedScroll: PropTypes.func,
    scrollContainer: PropTypes.node,
    scrollContainerId: PropTypes.string,
    isOpen: PropTypes.bool
  }

  static defaultProps = {
    onClose() {}, // eslint-disable-line
    scrollContainerId: 'Sanity_Default_DeskTool_Editor_ScrollContainer',
    actions: [],
    isOpen: true
  }

  constructor(props) {
    super()
    this.handleClose = this.handleClose.bind(this)
    this.handleClick = this.handleClick.bind(this)
    this.handleMouseDown = this.handleMouseDown.bind(this)
    this.repositionElement = this.repositionElement.bind(this)
    this.handleResize = debounce(this.handleResize.bind(this), 17) // 60fps
    this.resetPosition = this.resetPosition.bind(this)
    this.scrollOptions = {
      duration: 250,
      ease: ease.easeInOutQuart
    }
    this.state = {
      scrollContainer: null
    }
  }

  handleClose() {
    const {scrollContainer} = this.state
    if (scrollContainer) {
      scroll.top(scrollContainer, this.initialScrollTop, this.scrollOptions)
    }
    this.props.onClose()
  }

  handleClick(event) {
    // event.stopPropagation()
  }

  handleMouseDown(event) {
    event.stopPropagation()
  }

  repositionElement() {
    const {scrollContainer} = this.state
    const {rootRects} = this.state
    const rootElement = this._rootElement
    const portalModalElement = this._portalModalElement

    if (!rootElement || !portalModalElement || !rootRects) {
      return
    }

    const portalModalRects = portalModalElement.getClientRects()[0]
    const scrollTop = scrollContainer.scrollTop
    const width = portalModalRects.width
    const height = portalModalRects.height
    const left = rootRects.left
    const top = rootRects.top

    // we can use window since we don't support horizontal scrolling
    // and the backdrop is fixed
    const windowWidth = window.innerWidth
    const containerOffsetHeight = scrollContainer.offsetHeight

    const padding = 30

    const margin = 0

    // Scroll container when there is no space
    if ((containerOffsetHeight) < (top + height)) {
      let newScrollTop = (containerOffsetHeight - top - height - scrollTop - padding) * -1

      // If element is to big for screen, scroll top only top of the element
      if (height > containerOffsetHeight) {
        newScrollTop = top + scrollTop - (padding * 3)
      }

      scroll.top(scrollContainer, newScrollTop, this.scrollOptions)
    }

    // Need more bottom space
    if (scrollContainer.scrollHeight < (scrollTop + top + height)) {
      const extraPaddingBottom = Math.abs(scrollContainer.scrollHeight - scrollTop - height - top)
      scrollContainer.style.marginBottom = `${extraPaddingBottom}px`
      let newScrollTop = (containerOffsetHeight - top - height - scrollTop) * -1

      // If element is to big for screen, scroll top only top of the element
      if (height > containerOffsetHeight) {
        newScrollTop = top + scrollTop - (padding * 3)
      }
      scroll.top(scrollContainer, newScrollTop, this.scrollOptions)
    }

    // Reposition horizon
    if ((width + left - margin + padding) > windowWidth) {
      const diff = windowWidth - width - padding - left + margin
      portalModalElement.style.marginLeft = `${diff}px`
      this._arrowElement.style.transform = `translateX(${diff * -1}px)`
    } else {
      this.resetPosition()
    }
  }

  resetPosition() {
    this._portalModalElement.style.marginLeft = '0'
    this._arrowElement.style.transform = 'translateX(0px)'
  }

  handleResize() {

    // Uses the css the determine if it should reposition with an Media Query
    const computedStyle = window.getComputedStyle(this._rootElement, '::before')
    const contentValue = computedStyle.getPropertyValue('content')
    const shouldReposition = contentValue === '"shouldReposition"' || contentValue === 'shouldReposition' // Is quoted

    if (shouldReposition) {
      this.repositionElement()
    } else {
      this.resetPosition()
    }
  }

  componentDidMount() {
    const {scrollContainerId, scrollContainer} = this.props

    // Sets a scrollContainer with ID
    if (scrollContainerId) {
      this.setState({
        scrollContainer: document.getElementById(scrollContainerId)
      })
    }

    if (scrollContainer) {
      this.setState({
        scrollContainer: scrollContainer
      })
    }

  }

  componentWillUnmount() {
    const {scrollContainer} = this.state

    if (scrollContainer && this.initialScrollTop) {
      scroll.top(scrollContainer, this.initialScrollTop, this.scrollOptions)
    }
    window.removeEventListener('resize', this.handleResize)
    window.removeEventListener('keydown', this.handleKeyDown)
  }

  setRootElement = element => {
    this._rootElement = element
    if (element) {
      const rootRects = element.getClientRects()[0]
      this.setState({
        rootRects: rootRects
      })
    }
  }

  setPortalInnerElement = element => {
    this._portalInnerElement = element
  }

  setPortalModalElement = element => {
    this._portalModalElement = element
  }

  setArrowElement = element => {
    this._arrowElement = element
  }

  handleKeyDown = event => {
    if (event.key == 'Escape') {
      this.handleClose()
    }
  }

  handleBackdropClick = event => {
    this.handleClose()
    event.stopPropagation()
    event.preventDefault()
  }

  handleBackdropMouseDown = event => {
    event.stopPropagation()
    event.preventDefault()
  }

  handleModalClick = event => {
    event.preventDefault()
    event.stopPropagation()
  }
  handleModalMouseDown = event => {
    event.stopPropagation()
  }

  getScrollContainer = () => {
    return this.scrollContainer
  }

  renderPortal = (scrollContainer, rootRects) => {
    const {title, children, className, isCreatingNewItem, actions, fullWidth} = this.props
    const {top, left} = rootRects

    this.initialScrollTop = scrollContainer.scrollTop

    window.addEventListener('resize', this.handleResize)
    window.addEventListener('keydown', this.handleKeyDown)

    return (
      <Portal
        isOpen
        scrollContainer={scrollContainer}
        onOpen={this.handleResize}
        className={styles.portal}
      >

          <div
            className={styles.portalModal}
            ref={this.setPortalModalElement}
            style={{
              position: 'absolute',
              top: `${top + this.initialScrollTop}px`,
              left: `${left}px`
            }}
          >

            <button className={styles.close} type="button" onClick={this.handleClose}>
              <CloseIcon />
            </button>

                {
                  isCreatingNewItem && 'New '
                }
                {title}

              {children}

            {
              actions.length > 0 &&

                {
                  actions.map((action, i) => {
                    return (
                      <Button
                        key={i}
                        onClick={action.handleClick}
                        data-action-index={i}
                        kind={action.kind}
                        className={styles[`button_${action.kind}`] || styles.button}
                      >
                        {action.title}
                      </Button>
                    )
                  })
                }

            }

      </Portal>
    )
  }

  render() {
    const {isOpen} = this.props
    const {scrollContainer, rootRects} = this.state
    return (

        {
          scrollContainer && isOpen && this.renderPortal(scrollContainer, rootRects)
        }

    )
  }
}
<commit_msg>adding padding instead of margin when container is to small<commit_after>import React, {PropTypes} from 'react'
import styles from 'part:@sanity/components/edititem/popover-style'
import Button from 'part:@sanity/components/buttons/default'
import {debounce} from 'lodash'
import CloseIcon from 'part:@sanity/base/close-icon'
import scroll from 'scroll'
import ease from 'ease-component'
import Portal from './Portal'

export default class EditItemPopOver extends React.Component {
  static propTypes = {
    title: PropTypes.string,
    children: PropTypes.node.isRequired,
    className: PropTypes.string,
    onClose: PropTypes.func,
    isCreatingNewItem: PropTypes.bool,
    actions: PropTypes.arrayOf(PropTypes.shape({
      kind: PropTypes.string,
      title: PropTypes.string,
      handleClick: PropTypes.func
    })),
    fullWidth: PropTypes.bool,
    onNeedScroll: PropTypes.func,
    scrollContainer: PropTypes.node,
    scrollContainerId: PropTypes.string,
    isOpen: PropTypes.bool
  }

  static defaultProps = {
    onClose() {}, // eslint-disable-line
    scrollContainerId: 'Sanity_Default_DeskTool_Editor_ScrollContainer',
    actions: [],
    isOpen: true
  }

  constructor(props) {
    super()
    this.handleClose = this.handleClose.bind(this)
    this.handleClick = this.handleClick.bind(this)
    this.handleMouseDown = this.handleMouseDown.bind(this)
    this.repositionElement = this.repositionElement.bind(this)
    this.handleResize = debounce(this.handleResize.bind(this), 17) // 60fps
    this.resetPosition = this.resetPosition.bind(this)
    this.scrollOptions = {
      duration: 250,
      ease: ease.easeInOutQuart
    }
    this.state = {
      scrollContainer: null
    }
  }

  handleClose() {
    const {scrollContainer} = this.state
    if (scrollContainer) {
      scroll.top(scrollContainer, this.initialScrollTop, this.scrollOptions)
    }
    this.props.onClose()
  }

  handleClick(event) {
    // event.stopPropagation()
  }

  handleMouseDown(event) {
    event.stopPropagation()
  }

  repositionElement() {
    const {scrollContainer} = this.state
    const {rootRects} = this.state
    const rootElement = this._rootElement
    const portalModalElement = this._portalModalElement

    if (!rootElement || !portalModalElement || !rootRects) {
      return
    }

    const portalModalRects = portalModalElement.getClientRects()[0]
    const scrollTop = scrollContainer.scrollTop
    const width = portalModalRects.width
    const height = portalModalRects.height
    const left = rootRects.left
    const top = rootRects.top

    // we can use window since we don't support horizontal scrolling
    // and the backdrop is fixed
    const windowWidth = window.innerWidth
    const containerOffsetHeight = scrollContainer.offsetHeight

    const padding = 30

    const margin = 0

    // Scroll container when there is no space
    if ((containerOffsetHeight) < (top + height)) {
      let newScrollTop = (containerOffsetHeight - top - height - scrollTop - padding) * -1

      // If element is to big for screen, scroll top only top of the element
      if (height > containerOffsetHeight) {
        newScrollTop = top + scrollTop - (padding * 3)
      }

      scroll.top(scrollContainer, newScrollTop, this.scrollOptions)
    }

    // Need more bottom space
    if (scrollContainer.scrollHeight < (scrollTop + top + height)) {
      const extraPaddingBottom = Math.abs(scrollContainer.scrollHeight - scrollTop - height - top)
      scrollContainer.style.paddingBottom = `${extraPaddingBottom}px`
      let newScrollTop = (containerOffsetHeight - top - height - scrollTop) * -1

      // If element is to big for screen, scroll top only top of the element
      if (height > containerOffsetHeight) {
        newScrollTop = top + scrollTop - (padding * 3)
      }
      scroll.top(scrollContainer, newScrollTop, this.scrollOptions)
    }

    // Reposition horizon
    if ((width + left - margin + padding) > windowWidth) {
      const diff = windowWidth - width - padding - left + margin
      portalModalElement.style.marginLeft = `${diff}px`
      this._arrowElement.style.transform = `translateX(${diff * -1}px)`
    } else {
      this.resetPosition()
    }
  }

  resetPosition() {
    this._portalModalElement.style.marginLeft = '0'
    this._arrowElement.style.transform = 'translateX(0px)'
  }

  handleResize() {

    // Uses the css the determine if it should reposition with an Media Query
    const computedStyle = window.getComputedStyle(this._rootElement, '::before')
    const contentValue = computedStyle.getPropertyValue('content')
    const shouldReposition = contentValue === '"shouldReposition"' || contentValue === 'shouldReposition' // Is quoted

    if (shouldReposition) {
      this.repositionElement()
    } else {
      this.resetPosition()
    }
  }

  componentDidMount() {
    const {scrollContainerId, scrollContainer} = this.props

    // Sets a scrollContainer with ID
    if (scrollContainerId) {
      this.setState({
        scrollContainer: document.getElementById(scrollContainerId)
      })
    }

    if (scrollContainer) {
      this.setState({
        scrollContainer: scrollContainer
      })
    }

  }

  componentWillUnmount() {
    const {scrollContainer} = this.state

    if (scrollContainer && this.initialScrollTop) {
      scroll.top(scrollContainer, this.initialScrollTop, this.scrollOptions)
    }
    window.removeEventListener('resize', this.handleResize)
    window.removeEventListener('keydown', this.handleKeyDown)
  }

  setRootElement = element => {
    this._rootElement = element
    if (element) {
      const rootRects = element.getClientRects()[0]
      this.setState({
        rootRects: rootRects
      })
    }
  }

  setPortalInnerElement = element => {
    this._portalInnerElement = element
  }

  setPortalModalElement = element => {
    this._portalModalElement = element
  }

  setArrowElement = element => {
    this._arrowElement = element
  }

  handleKeyDown = event => {
    if (event.key == 'Escape') {
      this.handleClose()
    }
  }

  handleBackdropClick = event => {
    this.handleClose()
    event.stopPropagation()
    event.preventDefault()
  }

  handleBackdropMouseDown = event => {
    event.stopPropagation()
    event.preventDefault()
  }

  handleModalClick = event => {
    event.preventDefault()
    event.stopPropagation()
  }
  handleModalMouseDown = event => {
    event.stopPropagation()
  }

  getScrollContainer = () => {
    return this.scrollContainer
  }

  renderPortal = (scrollContainer, rootRects) => {
    const {title, children, className, isCreatingNewItem, actions, fullWidth} = this.props
    const {top, left} = rootRects

    this.initialScrollTop = scrollContainer.scrollTop

    window.addEventListener('resize', this.handleResize)
    window.addEventListener('keydown', this.handleKeyDown)

    return (
      <Portal
        isOpen
        scrollContainer={scrollContainer}
        onOpen={this.handleResize}
        className={styles.portal}
      >

          <div
            className={styles.portalModal}
            ref={this.setPortalModalElement}
            style={{
              position: 'absolute',
              top: `${top + this.initialScrollTop}px`,
              left: `${left}px`
            }}
          >

            <button className={styles.close} type="button" onClick={this.handleClose}>
              <CloseIcon />
            </button>

                {
                  isCreatingNewItem && 'New '
                }
                {title}

              {children}

            {
              actions.length > 0 &&

                {
                  actions.map((action, i) => {
                    return (
                      <Button
                        key={i}
                        onClick={action.handleClick}
                        data-action-index={i}
                        kind={action.kind}
                        className={styles[`button_${action.kind}`] || styles.button}
                      >
                        {action.title}
                      </Button>
                    )
                  })
                }

            }

      </Portal>
    )
  }

  render() {
    const {isOpen} = this.props
    const {scrollContainer, rootRects} = this.state
    return (

        {
          scrollContainer && isOpen && this.renderPortal(scrollContainer, rootRects)
        }

    )
  }
}
<|endoftext|>


<commit_before>/* global ListIssue */

import '~/boards/models/label';
import '~/boards/models/assignee';
import '~/boards/models/issue';
import '~/boards/models/list';
import Store from '~/boards/stores/modal_store';

describe('Modal store', () => {
  let issue;
  let issue2;

  beforeEach(() => {
    // Set up default state
    Store.store.issues = [];
    Store.store.selectedIssues = [];

    issue = new ListIssue({
      title: 'Testing',
      id: 1,
      iid: 1,
      confidential: false,
      labels: [],
      assignees: [],
    });
    issue2 = new ListIssue({
      title: 'Testing',
      id: 1,
      iid: 2,
      confidential: false,
      labels: [],
      assignees: [],
    });
    Store.store.issues.push(issue);
    Store.store.issues.push(issue2);
  });

  it('returns selected count', () => {
    expect(Store.selectedCount()).toBe(0);
  });

  it('toggles the issue as selected', () => {
    Store.toggleIssue(issue);

    expect(issue.selected).toBe(true);
    expect(Store.selectedCount()).toBe(1);
  });

  it('toggles the issue as un-selected', () => {
    Store.toggleIssue(issue);
    Store.toggleIssue(issue);

    expect(issue.selected).toBe(false);
    expect(Store.selectedCount()).toBe(0);
  });

  it('toggles all issues as selected', () => {
    Store.toggleAll();

    expect(issue.selected).toBe(true);
    expect(issue2.selected).toBe(true);
    expect(Store.selectedCount()).toBe(2);
  });

  it('toggles all issues as un-selected', () => {
    Store.toggleAll();
    Store.toggleAll();

    expect(issue.selected).toBe(false);
    expect(issue2.selected).toBe(false);
    expect(Store.selectedCount()).toBe(0);
  });

  it('toggles all if a single issue is selected', () => {
    Store.toggleIssue(issue);
    Store.toggleAll();

    expect(issue.selected).toBe(true);
    expect(issue2.selected).toBe(true);
    expect(Store.selectedCount()).toBe(2);
  });

  it('adds issue to selected array', () => {
    issue.selected = true;
    Store.addSelectedIssue(issue);

    expect(Store.selectedCount()).toBe(1);
  });

  it('removes issue from selected array', () => {
    Store.addSelectedIssue(issue);
    Store.removeSelectedIssue(issue);

    expect(Store.selectedCount()).toBe(0);
  });

  it('returns selected issue index if present', () => {
    Store.toggleIssue(issue);

    expect(Store.selectedIssueIndex(issue)).toBe(0);
  });

  it('returns -1 if issue is not selected', () => {
    expect(Store.selectedIssueIndex(issue)).toBe(-1);
  });

  it('finds the selected issue', () => {
    Store.toggleIssue(issue);

    expect(Store.findSelectedIssue(issue)).toBe(issue);
  });

  it('does not find a selected issue', () => {
    expect(Store.findSelectedIssue(issue)).toBe(undefined);
  });

  it('does not remove from selected issue if tab is not all', () => {
    Store.store.activeTab = 'selected';

    Store.toggleIssue(issue);
    Store.toggleIssue(issue);

    expect(Store.store.selectedIssues.length).toBe(1);
    expect(Store.selectedCount()).toBe(0);
  });

  it('gets selected issue array with only selected issues', () => {
    Store.toggleIssue(issue);
    Store.toggleIssue(issue2);
    Store.toggleIssue(issue2);

    expect(Store.getSelectedIssues().length).toBe(1);
  });
});
<commit_msg>Removes EE differences<commit_after>/* global ListIssue */

import '~/boards/models/label';
import '~/boards/models/assignee';
import '~/boards/models/issue';
import '~/boards/models/list';
import Store from '~/boards/stores/modal_store';

describe('Modal store', () => {
  let issue;
  let issue2;

  beforeEach(() => {
    // Set up default state
    Store.store.issues = [];
    Store.store.selectedIssues = [];

    issue = new ListIssue({
      title: 'Testing',
      id: 1,
      iid: 1,
      confidential: false,
      labels: [],
      assignees: [],
    });
    issue2 = new ListIssue({
      title: 'Testing',
      id: 2,
      iid: 2,
      confidential: false,
      labels: [],
      assignees: [],
    });
    Store.store.issues.push(issue);
    Store.store.issues.push(issue2);
  });

  it('returns selected count', () => {
    expect(Store.selectedCount()).toBe(0);
  });

  it('toggles the issue as selected', () => {
    Store.toggleIssue(issue);

    expect(issue.selected).toBe(true);
    expect(Store.selectedCount()).toBe(1);
  });

  it('toggles the issue as un-selected', () => {
    Store.toggleIssue(issue);
    Store.toggleIssue(issue);

    expect(issue.selected).toBe(false);
    expect(Store.selectedCount()).toBe(0);
  });

  it('toggles all issues as selected', () => {
    Store.toggleAll();

    expect(issue.selected).toBe(true);
    expect(issue2.selected).toBe(true);
    expect(Store.selectedCount()).toBe(2);
  });

  it('toggles all issues as un-selected', () => {
    Store.toggleAll();
    Store.toggleAll();

    expect(issue.selected).toBe(false);
    expect(issue2.selected).toBe(false);
    expect(Store.selectedCount()).toBe(0);
  });

  it('toggles all if a single issue is selected', () => {
    Store.toggleIssue(issue);
    Store.toggleAll();

    expect(issue.selected).toBe(true);
    expect(issue2.selected).toBe(true);
    expect(Store.selectedCount()).toBe(2);
  });

  it('adds issue to selected array', () => {
    issue.selected = true;
    Store.addSelectedIssue(issue);

    expect(Store.selectedCount()).toBe(1);
  });

  it('removes issue from selected array', () => {
    Store.addSelectedIssue(issue);
    Store.removeSelectedIssue(issue);

    expect(Store.selectedCount()).toBe(0);
  });

  it('returns selected issue index if present', () => {
    Store.toggleIssue(issue);

    expect(Store.selectedIssueIndex(issue)).toBe(0);
  });

  it('returns -1 if issue is not selected', () => {
    expect(Store.selectedIssueIndex(issue)).toBe(-1);
  });

  it('finds the selected issue', () => {
    Store.toggleIssue(issue);

    expect(Store.findSelectedIssue(issue)).toBe(issue);
  });

  it('does not find a selected issue', () => {
    expect(Store.findSelectedIssue(issue)).toBe(undefined);
  });

  it('does not remove from selected issue if tab is not all', () => {
    Store.store.activeTab = 'selected';

    Store.toggleIssue(issue);
    Store.toggleIssue(issue);

    expect(Store.store.selectedIssues.length).toBe(1);
    expect(Store.selectedCount()).toBe(0);
  });

  it('gets selected issue array with only selected issues', () => {
    Store.toggleIssue(issue);
    Store.toggleIssue(issue2);
    Store.toggleIssue(issue2);

    expect(Store.getSelectedIssues().length).toBe(1);
  });
});
<|endoftext|>