<commit_before>var AnalyzeCommentView = require("../views/analyze-comment");
var display = require("../util/display");
var eb = require("../eventBus");
var template = require("../tmpl/analyze-global");
var CommentModel = require("../models/comment");
var Thorax = require("thorax");

var NUMBER_OF_REPRESENTATIVE_COMMENTS_TO_SHOW = 5;

var sortRepness = function(a, b) {
  return b.get("repness") - a.get("repness");
};

var sortMostAgrees = function(a, b) {
  return b.get("A") - a.get("A");
};
var sortMostDisagrees = function(a, b) {
  return b.get("D") - a.get("D");
};

var sortMostStars = function(a, b) {
  return b.get("stars") - a.get("stars");
};
var el_carouselSelector = "#carousel";

module.exports = Thorax.CollectionView.extend({
    name: "analyze-global-view",
    template: template,
    itemView: AnalyzeCommentView,
    visibleTids: null,
    events: {
      "click #sortStar": "sortStar",
      "click #sortAgree": "sortAgree",
      "click #sortDisagree": "sortDisagree",
      "keyup input": "updateSearch",
      "propertychange input": "updateSearch",
      submit: function(e) {
        e.preventDefault();
      },
      "rendered:collection": function() {
        this.selectFirst();
        console.log('rendered:collection');
      },
      rendered: function() {
        var that = this;
        var items = this.$(".query_result_item");
        items.on("mouseover", function() {
            $(this).addClass("hover");
        });
        items.on("mouseout", function() {
            $(this).removeClass("hover");
        });
      }
    },
    selectSortModes: function(chosenButtonSelector) {
      this.$("#sortAgree,#sortDisagree,#sortStar").removeClass("enabled");
      this.$(chosenButtonSelector).addClass("enabled");
    },
    selectFirst: function() {
      var first = this.collection.first();
      if (first) {
        eb.trigger(eb.commentSelected, first.get("tid"));
      }
    },
    itemFilter: function(model, index) {
      if (this.visibleTids && this.visibleTids.indexOf(model.get("tid")) === -1) {
        return false;
      }
      if (this.searchEnabled) {
        if (_.isString(this.searchString)) {
          var tokens = this.searchString.split(/\s+/);
          // match all space separated word fragments
          var txt = model.get("txt").toLowerCase();
          for (var i = 0; i < tokens.length; i++) {
            var token = tokens[i].toLowerCase();

            var shouldNegateToken = token[0] === "-";
            if (shouldNegateToken) {
              token = token.slice(1);
            }
            var tokenPresent = txt.indexOf(token) >= 0;
            if (!token.length) {
              // a "-" followed by nothing should not count as present.
              tokenPresent = false;
            }
            if (
              (!tokenPresent && !shouldNegateToken) ||
              (tokenPresent && shouldNegateToken)) {
              return false;
            }
          }
          return true;
        }
      }
      return true;
    },
  searchEnabled: true,
  sortEnabled: true,
  sortAgree: function(e) {
    this.collection.comparator = sortMostAgrees;
    this.collection.sort();
    this.selectFirst();
    this.selectSortModes("#sortAgree");
  },
  sortDisagree: function(e) {
    this.collection.comparator = sortMostDisagrees;
    this.collection.sort();
    this.selectFirst();
    this.selectSortModes("#sortDisagree");
  },
  sortStar: function(e) {
    alert("coming soon!");
  },

  sortRepness: function(e) {
    // There are no buttons associated with this.
    this.collection.comparator = sortRepness;
    this.collection.sort();
  },
  useCarousel: function() {
      return !this.isIE8 && display.xs();
  },
  hideCarousel: function() {
    this.$("#carousel").hide();
  },
  showCarousel: function() {
    this.$("#carousel").show();
  },
  updateSearch: function(e) {
    this.searchString = e.target.value;
    this.deselectComments();
    this.updateFilter();
    // this.selectFirst();
  },
  deselectComments: function() {
    eb.trigger(eb.commentSelected, false);
  },
  renderWithCarousel: function() {

    $(el_carouselSelector).html("");
    // $(el_carouselSelector).css("overflow", "hidden");

    // $(el_carouselSelector).append("

");
    $(el_carouselSelector).append("

");

    var results = $("#smallWindow");
    results.addClass("owl-carousel");
    // results.css('background-color', 'yellow');

    if (results.data('owlCarousel')) {
      results.data('owlCarousel').destroy();
    }
    results.owlCarousel({
      items : NUMBER_OF_REPRESENTATIVE_COMMENTS_TO_SHOW, //3 items above 1000px browser width
      // itemsDesktop : [1000,5], //5 items between 1000px and 901px
      // itemsDesktopSmall : [900,3], // betweem 900px and 601px
      // itemsTablet: [600,2], //2 items between 600 and 0
      // itemsMobile : false // itemsMobile disabled - inherit from itemsTablet option
       singleItem : true,
       // autoHeight : true,
       afterMove: (function() {return function() {
          var tid = indexToTid[this.currentItem];
          setTimeout(function() {
              eb.trigger(eb.commentSelected, tid);
          }, 100);

      }}())
    });
    var indexToTid = this.collection.pluck("tid");

    _.each(this.collection.first(NUMBER_OF_REPRESENTATIVE_COMMENTS_TO_SHOW), function(c) {
      results.data('owlCarousel').addItem(
        "
" +
          "
" +
            "Agrees: " + c.get("A") +
            " Disagrees: " + c.get("D") +
          "
" +
          c.get("txt") +
        "
");
    });
    // Auto-select the first comment.
    $(el_carouselSelector).find(".query_result_item").first().trigger("click");
  },
  initialize: function(options) {
    var that = this;

    var getTidsForGroup = options.getTidsForGroup;

    this.fetcher = options.fetcher;
    if (!this.useCarousel()) {
      $(el_carouselSelector).html("");
    }

    eb.on(eb.commentSelected, function(tid) {
      that.collection.each(function(model) {
        if (model.get("tid") === tid) {
          model.set("selected", true);
        } else {
          model.set("selected", false);
        }
      });
    });

    eb.on(eb.clusterClicked, function(gid) {
      that.collection.firstFetchPromise.then(function() {
        if (gid === false) {
          that.$("#commentSearch").show();
          that.$("#commentSort").show();
          that.$("#groupStats").hide();
          that.sortEnabled = true;
          that.searchEnabled = true;
          that.visibleTids = null;
          that.sortAgree();
          that.updateFilter();
          if (that.useCarousel()) {
            that.renderWithCarousel();
          }
          that.selectFirst();
        } else {
          that.$("#commentSearch").hide();
          that.$("#commentSort").hide();
          that.$("#groupStats").show();
          that.sortEnabled = false;
          that.searchEnabled = false;
          getTidsForGroup(gid, NUMBER_OF_REPRESENTATIVE_COMMENTS_TO_SHOW).then(function(o) {
            that.visibleTids = o.tids;
            that.collection.updateRepness(o.tidToR);
            that.sortRepness();
            that.updateFilter();
            if (that.useCarousel()) {
              that.renderWithCarousel();
            }
            that.selectFirst();
          });
        }
      });
    });
  }
});<commit_msg>fix search with extra whitespace<commit_after>var AnalyzeCommentView = require("../views/analyze-comment");
var display = require("../util/display");
var eb = require("../eventBus");
var template = require("../tmpl/analyze-global");
var CommentModel = require("../models/comment");
var Thorax = require("thorax");

var NUMBER_OF_REPRESENTATIVE_COMMENTS_TO_SHOW = 5;

var sortRepness = function(a, b) {
  return b.get("repness") - a.get("repness");
};

var sortMostAgrees = function(a, b) {
  return b.get("A") - a.get("A");
};
var sortMostDisagrees = function(a, b) {
  return b.get("D") - a.get("D");
};

var sortMostStars = function(a, b) {
  return b.get("stars") - a.get("stars");
};
var el_carouselSelector = "#carousel";

module.exports = Thorax.CollectionView.extend({
    name: "analyze-global-view",
    template: template,
    itemView: AnalyzeCommentView,
    visibleTids: null,
    events: {
      "click #sortStar": "sortStar",
      "click #sortAgree": "sortAgree",
      "click #sortDisagree": "sortDisagree",
      "keyup input": "updateSearch",
      "propertychange input": "updateSearch",
      submit: function(e) {
        e.preventDefault();
      },
      "rendered:collection": function() {
        this.selectFirst();
        console.log('rendered:collection');
      },
      rendered: function() {
        var that = this;
        var items = this.$(".query_result_item");
        items.on("mouseover", function() {
            $(this).addClass("hover");
        });
        items.on("mouseout", function() {
            $(this).removeClass("hover");
        });
      }
    },
    selectSortModes: function(chosenButtonSelector) {
      this.$("#sortAgree,#sortDisagree,#sortStar").removeClass("enabled");
      this.$(chosenButtonSelector).addClass("enabled");
    },
    selectFirst: function() {
      var first = this.collection.first();
      if (first) {
        eb.trigger(eb.commentSelected, first.get("tid"));
      }
    },
    itemFilter: function(model, index) {
      var searchString = this.searchString;
      if (!_.isString(searchString) || /^\s*$/.exec(searchString)) {
        return true;
      }
      searchString = searchString
        .replace(/\s+/g, " ")
        .replace(/\s+$/,"")
        .replace(/^\s+/,"");

      if (this.visibleTids && this.visibleTids.indexOf(model.get("tid")) === -1) {
        return false;
      }
      var isMatch = true;
      if (this.searchEnabled) {
        if (_.isString(searchString)) {
          var tokens = searchString.split(/\s+/);
          // match all space separated word fragments
          var txt = model.get("txt").toLowerCase();
          for (var i = 0; i < tokens.length; i++) {
            var token = tokens[i].toLowerCase();

            var shouldNegateToken = token[0] === "-";
            if (shouldNegateToken) {
              token = token.slice(1);
            }
            var tokenPresent = txt.indexOf(token) >= 0;
            if (!token.length) {
              // a "-" followed by nothing should not count as present.
              tokenPresent = false;
            }
            if (
              (!tokenPresent && !shouldNegateToken) ||
              (tokenPresent && shouldNegateToken)) {
              isMatch = false;
              break;
            }
          }
        }
      }
      return isMatch;
    },
  searchEnabled: true,
  sortEnabled: true,
  sortAgree: function(e) {
    this.collection.comparator = sortMostAgrees;
    this.collection.sort();
    this.selectFirst();
    this.selectSortModes("#sortAgree");
  },
  sortDisagree: function(e) {
    this.collection.comparator = sortMostDisagrees;
    this.collection.sort();
    this.selectFirst();
    this.selectSortModes("#sortDisagree");
  },
  sortStar: function(e) {
    alert("coming soon!");
  },

  sortRepness: function(e) {
    // There are no buttons associated with this.
    this.collection.comparator = sortRepness;
    this.collection.sort();
  },
  useCarousel: function() {
      return !this.isIE8 && display.xs();
  },
  hideCarousel: function() {
    this.$("#carousel").hide();
  },
  showCarousel: function() {
    this.$("#carousel").show();
  },
  updateSearch: function(e) {
    this.searchString = e.target.value;
    this.deselectComments();
    this.updateFilter();
    // this.selectFirst();
  },
  deselectComments: function() {
    eb.trigger(eb.commentSelected, false);
  },
  renderWithCarousel: function() {

    $(el_carouselSelector).html("");
    // $(el_carouselSelector).css("overflow", "hidden");

    // $(el_carouselSelector).append("

");
    $(el_carouselSelector).append("

");

    var results = $("#smallWindow");
    results.addClass("owl-carousel");
    // results.css('background-color', 'yellow');

    if (results.data('owlCarousel')) {
      results.data('owlCarousel').destroy();
    }
    results.owlCarousel({
      items : NUMBER_OF_REPRESENTATIVE_COMMENTS_TO_SHOW, //3 items above 1000px browser width
      // itemsDesktop : [1000,5], //5 items between 1000px and 901px
      // itemsDesktopSmall : [900,3], // betweem 900px and 601px
      // itemsTablet: [600,2], //2 items between 600 and 0
      // itemsMobile : false // itemsMobile disabled - inherit from itemsTablet option
       singleItem : true,
       // autoHeight : true,
       afterMove: (function() {return function() {
          var tid = indexToTid[this.currentItem];
          setTimeout(function() {
              eb.trigger(eb.commentSelected, tid);
          }, 100);

      }}())
    });
    var indexToTid = this.collection.pluck("tid");

    _.each(this.collection.first(NUMBER_OF_REPRESENTATIVE_COMMENTS_TO_SHOW), function(c) {
      results.data('owlCarousel').addItem(
        "
" +
          "
" +
            "Agrees: " + c.get("A") +
            " Disagrees: " + c.get("D") +
          "
" +
          c.get("txt") +
        "
");
    });
    // Auto-select the first comment.
    $(el_carouselSelector).find(".query_result_item").first().trigger("click");
  },
  initialize: function(options) {
    var that = this;

    var getTidsForGroup = options.getTidsForGroup;

    this.fetcher = options.fetcher;
    if (!this.useCarousel()) {
      $(el_carouselSelector).html("");
    }

    eb.on(eb.commentSelected, function(tid) {
      that.collection.each(function(model) {
        if (model.get("tid") === tid) {
          model.set("selected", true);
        } else {
          model.set("selected", false);
        }
      });
    });

    eb.on(eb.clusterClicked, function(gid) {
      that.collection.firstFetchPromise.then(function() {
        if (gid === false) {
          that.$("#commentSearch").show();
          that.$("#commentSort").show();
          that.$("#groupStats").hide();
          that.sortEnabled = true;
          that.searchEnabled = true;
          that.visibleTids = null;
          that.sortAgree();
          that.updateFilter();
          if (that.useCarousel()) {
            that.renderWithCarousel();
          }
          that.selectFirst();
        } else {
          that.$("#commentSearch").hide();
          that.$("#commentSort").hide();
          that.$("#groupStats").show();
          that.sortEnabled = false;
          that.searchEnabled = false;
          getTidsForGroup(gid, NUMBER_OF_REPRESENTATIVE_COMMENTS_TO_SHOW).then(function(o) {
            that.visibleTids = o.tids;
            that.collection.updateRepness(o.tidToR);
            that.sortRepness();
            that.updateFilter();
            if (that.useCarousel()) {
              that.renderWithCarousel();
            }
            that.selectFirst();
          });
        }
      });
    });
  }
});<|endoftext|>


<commit_before>import $ from 'jquery';
import baseVw from '../baseVw';
import app from '../../app';
import loadTemplate from '../../utils/loadTemplate';
import { capitalize } from '../../utils/string';
import ListingCard from '../ListingCard';
import UserCard from '../UserCard';
import PageControls from '../components/PageControls';
import ListingCardModel from '../../models/listing/ListingShort';
import ResultsCol from '../../collections/Results';
import { recordEvent } from '../../utils/metrics';

export default class extends baseVw {
  constructor(options = {}) {
    super(options);
    this.options = options;

    this.searchUrl = options.searchUrl;
    if (!this.searchUrl) {
      throw new Error('Please provide a search provider URL.');
    }

    this.serverPage = this.options.serverPage || 0;
    this.pageSize = this.options.pageSize || 24;
    this.reportsUrl = this.options.reportsUrl || '';
    this.viewType = this.options.viewType || 'grid';

    this.cardViews = [];
    this.pageCollections = {};
    // if an initial collection was passed in, add it
    if (options.initCol) this.pageCollections[this.serverPage] = (options.initCol);
    this.firstRender = true;
  }

  className() {
    return 'searchResults flexColRow gutterV';
  }

  events() {
    return {
      'click .js-reset': 'clickResetBtn',
    };
  }

  clickResetBtn() {
    this.trigger('resetSearch');
  }

  createCardView(model) {
    // models can be listings or nodes
    if (model instanceof ListingCardModel) {
      const vendor = model.get('vendor') || {};
      vendor.avatar = vendor.avatarHashes;
      const base = vendor.handle ?
        `@${vendor.handle}` : vendor.peerID;

      const options = {
        listingBaseUrl: `${base}/store/`,
        reportsUrl: this.reportsUrl,
        model,
        vendor,
        onStore: false,
        viewType: this.viewType,
      };

      return this.createChild(ListingCard, options);
    }

    return this.createChild(UserCard, { model });
  }

  renderCards(models) {
    const resultsFrag = document.createDocumentFragment();
    const end = this.pageSize * (Number(this.serverPage) + 1) - (this.pageSize - models.length);
    const total = models.total;
    let start = 0;
    if (total) start = this.pageSize * Number(this.serverPage) + 1;

    models.forEach(model => {
      const cardVw = this.createCardView(model);

      if (cardVw) {
        this.cardViews.push(cardVw);
        cardVw.render().$el.appendTo(resultsFrag);
      }
    });

    this.$el.toggleClass('noResults', total < 1);

    this.$resultsGrid.html(resultsFrag);
    // update the page controls
    // this.$displayText.html(app.polyglot.t('search.displaying', { start, end, total }));
    this.pageControls.setState({ start, end, total });
    // hide the loading spinner
    this.$el.removeClass('loading');
    /*
    // disabled until an enter handler to added to the listing card
    // move focus to the first result. The timeout orders it after other operations.
    setTimeout(() => {
      this.$resultsGrid.find('.listingCard').filter(':first').focus();
    });
    */
  }

  loadPage(page = this.serverPage, size = this.pageSize) {
    this.removeCardViews();
    // get the new page
    const url = new URL(this.searchUrl);
    const params = new URLSearchParams(url.search);
    params.set('p', page);
    params.set('ps', size);
    const newURL = `${url.origin}${url.pathname}?${params.toString()}`;
    this.trigger('loadingPage');

    // if page exists, reuse it
    if (this.pageCollections[page]) {
      this.renderCards(this.pageCollections[page]);
      // update the address bar
      app.router.navigate(`search?providerQ=${encodeURIComponent(newURL)}`,
        { replace: this.firstRender });
    } else {
      // show the loading spinner
      this.$el.addClass('loading');

      const newPageCol = new ResultsCol();
      this.pageCollections[page] = newPageCol;

      if (this.newPageFetch) this.newPageFetch.abort();

      this.newPageFetch = newPageCol.fetch({
        url: newURL,
      })
          .done(() => {
            this.renderCards(newPageCol);
            // update the address bar
            app.router.navigate(`search?providerQ=${encodeURIComponent(newURL)}`,
              { replace: this.firstRender });
          })
          .fail((xhr) => {
            if (xhr.statusText !== 'abort') this.trigger('searchError', xhr);
          });
    }
  }

  clickPagePrev() {
    this.serverPage--;
    this.loadPage(this.serverPage);
    recordEvent('Discover_PrevPage');
  }

  clickPageNext() {
    this.serverPage++;
    this.loadPage(this.serverPage);
    recordEvent('Discover_NextPage');
  }

  removeCardViews() {
    this.cardViews.forEach(vw => vw.remove());
    this.cardViews = [];
  }

  remove() {
    this.removeCardViews();
    if (this.newPageFetch) this.newPageFetch.abort();
    super.remove();
  }

  render() {
    loadTemplate('search/results.html', (t) => {
      this.$el.html(t({
        viewTypeClass: this.viewType === 'grid' ?
          '' : `listingsGrid${capitalize(this.viewType)}View`,
        viewType: this.viewType,
      }));

      this.$resultsGrid = this.$('.js-resultsGrid');
      this.$displayText = this.$('.js-displayingText');
      this.removeCardViews();

      if (this.pageControls) this.pageControls.remove();
      this.pageControls = this.createChild(PageControls);
      this.listenTo(this.pageControls, 'clickNext', this.clickPageNext);
      this.listenTo(this.pageControls, 'clickPrev', this.clickPagePrev);
      this.$('.js-pageControlsContainer').html(this.pageControls.render().el);

      this.loadPage();
    });
    this.firstRender = false;

    return this;
  }
}
<commit_msg>cleanup<commit_after>import baseVw from '../baseVw';
import app from '../../app';
import loadTemplate from '../../utils/loadTemplate';
import { capitalize } from '../../utils/string';
import ListingCard from '../ListingCard';
import UserCard from '../UserCard';
import PageControls from '../components/PageControls';
import ListingCardModel from '../../models/listing/ListingShort';
import ResultsCol from '../../collections/Results';
import { recordEvent } from '../../utils/metrics';

export default class extends baseVw {
  constructor(options = {}) {
    super(options);
    this.options = options;

    this.searchUrl = options.searchUrl;
    if (!this.searchUrl) {
      throw new Error('Please provide a search provider URL.');
    }

    this.serverPage = this.options.serverPage || 0;
    this.pageSize = this.options.pageSize || 24;
    this.reportsUrl = this.options.reportsUrl || '';
    this.viewType = this.options.viewType || 'grid';

    this.cardViews = [];
    this.pageCollections = {};
    // if an initial collection was passed in, add it
    if (options.initCol) this.pageCollections[this.serverPage] = (options.initCol);
    this.firstRender = true;
  }

  className() {
    return 'searchResults flexColRow gutterV';
  }

  events() {
    return {
      'click .js-reset': 'clickResetBtn',
    };
  }

  clickResetBtn() {
    this.trigger('resetSearch');
  }

  createCardView(model) {
    // models can be listings or nodes
    if (model instanceof ListingCardModel) {
      const vendor = model.get('vendor') || {};
      vendor.avatar = vendor.avatarHashes;
      const base = vendor.handle ?
        `@${vendor.handle}` : vendor.peerID;

      const options = {
        listingBaseUrl: `${base}/store/`,
        reportsUrl: this.reportsUrl,
        model,
        vendor,
        onStore: false,
        viewType: this.viewType,
      };

      return this.createChild(ListingCard, options);
    }

    return this.createChild(UserCard, { model });
  }

  renderCards(models) {
    const resultsFrag = document.createDocumentFragment();
    const end = this.pageSize * (Number(this.serverPage) + 1) - (this.pageSize - models.length);
    const total = models.total;
    let start = 0;
    if (total) start = this.pageSize * Number(this.serverPage) + 1;

    models.forEach(model => {
      const cardVw = this.createCardView(model);

      if (cardVw) {
        this.cardViews.push(cardVw);
        cardVw.render().$el.appendTo(resultsFrag);
      }
    });

    this.$el.toggleClass('noResults', total < 1);

    this.$resultsGrid.html(resultsFrag);
    // update the page controls
    // this.$displayText.html(app.polyglot.t('search.displaying', { start, end, total }));
    this.pageControls.setState({ start, end, total });
    // hide the loading spinner
    this.$el.removeClass('loading');
    /*
    // disabled until an enter handler to added to the listing card
    // move focus to the first result. The timeout orders it after other operations.
    setTimeout(() => {
      this.$resultsGrid.find('.listingCard').filter(':first').focus();
    });
    */
  }

  loadPage(page = this.serverPage, size = this.pageSize) {
    this.removeCardViews();
    // get the new page
    const url = new URL(this.searchUrl);
    const params = new URLSearchParams(url.search);
    params.set('p', page);
    params.set('ps', size);
    const newURL = `${url.origin}${url.pathname}?${params.toString()}`;
    this.trigger('loadingPage');

    // if page exists, reuse it
    if (this.pageCollections[page]) {
      this.renderCards(this.pageCollections[page]);
      // update the address bar
      app.router.navigate(`search?providerQ=${encodeURIComponent(newURL)}`,
        { replace: this.firstRender });
    } else {
      // show the loading spinner
      this.$el.addClass('loading');

      const newPageCol = new ResultsCol();
      this.pageCollections[page] = newPageCol;

      if (this.newPageFetch) this.newPageFetch.abort();

      this.newPageFetch = newPageCol.fetch({
        url: newURL,
      })
          .done(() => {
            this.renderCards(newPageCol);
            // update the address bar
            app.router.navigate(`search?providerQ=${encodeURIComponent(newURL)}`,
              { replace: this.firstRender });
          })
          .fail((xhr) => {
            if (xhr.statusText !== 'abort') this.trigger('searchError', xhr);
          });
    }
  }

  clickPagePrev() {
    this.serverPage--;
    this.loadPage(this.serverPage);
    recordEvent('Discover_PrevPage');
  }

  clickPageNext() {
    this.serverPage++;
    this.loadPage(this.serverPage);
    recordEvent('Discover_NextPage');
  }

  removeCardViews() {
    this.cardViews.forEach(vw => vw.remove());
    this.cardViews = [];
  }

  remove() {
    this.removeCardViews();
    if (this.newPageFetch) this.newPageFetch.abort();
    super.remove();
  }

  render() {
    loadTemplate('search/results.html', (t) => {
      this.$el.html(t({
        viewTypeClass: this.viewType === 'grid' ?
          '' : `listingsGrid${capitalize(this.viewType)}View`,
        viewType: this.viewType,
      }));

      this.$resultsGrid = this.$('.js-resultsGrid');
      this.$displayText = this.$('.js-displayingText');
      this.removeCardViews();

      if (this.pageControls) this.pageControls.remove();
      this.pageControls = this.createChild(PageControls);
      this.listenTo(this.pageControls, 'clickNext', this.clickPageNext);
      this.listenTo(this.pageControls, 'clickPrev', this.clickPagePrev);
      this.$('.js-pageControlsContainer').html(this.pageControls.render().el);

      this.loadPage();
    });
    this.firstRender = false;

    return this;
  }
}
<|endoftext|>


<commit_before>const path = require('path');
const webpack = require('webpack');
const HtmlPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');

function configCommon(config, params)
{
    // Common configuration

    config.output.filename = 'bundle.js';
    config.performance.hints = false;

    config.entry.push('normalize.css');
    config.entry.push('event-source-polyfill');

    config.module.rules.push({
        test: /\.js$/,
        include: params.sourceDir,
        loader: 'babel-loader',
    });

    config.module.rules.push({
        test: /(\.svg|\.png)$/,
        loader: 'url-loader',
        options: {
            name: '[name].[ext]',
            limit: 1024
        }
    });
}

function configApp(config, params)
{
    // App target configuration

    config.output.path = params.outputDir;

    config.entry.push('./src/style.less');
    config.entry.push('./src/index.js');

    config.plugins.push(new HtmlPlugin({
        template: path.join(params.sourceDir, 'index.html')
    }));

    if (params.analyze)
    {
        config.plugins.push(new BundleAnalyzerPlugin({
           analyzerMode: 'static',
           reportFilename: path.join('stats', 'bundle-size.html'),
           openAnalyzer: false,
        }));
    }
}

function configTests(config, params)
{
    // Tests target configuration

    config.output.path = path.join(params.outputDir, 'tests');

    config.entry.push('qunit/qunit/qunit.css');
    config.entry.push('./src/tests/index.js');

    config.plugins.push(new HtmlPlugin({
        template: path.join(params.sourceDir, 'tests', 'index.html')
    }));
}

function configSandbox(config, params)
{
    // Sandbox target configuration

    config.output.path = path.join(params.outputDir, 'sandbox');

    config.entry.push('./src/style.less');
    config.entry.push('./src/sandbox/index.js');

    config.plugins.push(new HtmlPlugin({
        template: path.join(params.sourceDir, 'index.html')
    }));
}

function configDebug(config)
{
    // Debug configuration

    config.devtool = 'source-map';
    config.mode = 'none';

    config.plugins.push(new webpack.LoaderOptionsPlugin({
        debug: true
    }));

    config.module.rules.push({
        test: /\.(css|less)$/,
        use: ['style-loader', 'css-loader', 'less-loader']
    });
}

function configRelease(config)
{
    // Release configuration

    config.mode = 'production';
    config.node.process = false;

    config.plugins.push(new webpack.LoaderOptionsPlugin({
        minimize: true
    }));

    const styleExtractor = new ExtractTextPlugin({
        filename: 'bundle.css'
    });

    config.plugins.push(styleExtractor);

    config.module.rules.push({
        test: /\.(css|less)$/,
        use: styleExtractor.extract({
            use: ['css-loader', 'less-loader'],
            fallback: 'style-loader'
        })
    });
}

function makeBuildParams(env)
{
    const buildType = env.release ? 'release' : 'debug';
    const enableTests = !!env.tests;
    const analyze = !!env.analyze;

    const sourceDir = path.join(__dirname, 'src');
    const outputDir = path.join(__dirname, 'build', buildType);
    const rootDir = path.dirname(__dirname);

    return {
        buildType,
        enableTests,
        analyze,
        rootDir,
        sourceDir,
        outputDir,
    };
}

function makeTarget(configTarget, params)
{
    const config = {
        entry: [],
        output: {},
        plugins: [],
        module: { rules: [] },
        resolve: { alias: {} },
        node: {},
        performance: {},
    };

    configCommon(config, params);
    configTarget(config, params);

    (params.buildType === 'release' ? configRelease : configDebug)(config, params);

    return config;
}

module.exports = function(env)
{
    const params = makeBuildParams(env || {});
    const allTargets = [makeTarget(configApp, params)];

    if (params.enableTests)
    {
        allTargets.push(makeTarget(configTests, params));
        allTargets.push(makeTarget(configSandbox, params));
    }

    return allTargets;
};
<commit_msg>webui/webpack.config.js: emit error if bundle size is too large<commit_after>const path = require('path');
const webpack = require('webpack');
const HtmlPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');

function configCommon(config, params)
{
    // Common configuration

    config.output.filename = 'bundle.js';
    config.performance.hints = false;

    config.entry.push('normalize.css');
    config.entry.push('event-source-polyfill');

    config.module.rules.push({
        test: /\.js$/,
        include: params.sourceDir,
        loader: 'babel-loader',
    });

    config.module.rules.push({
        test: /(\.svg|\.png)$/,
        loader: 'url-loader',
        options: {
            name: '[name].[ext]',
            limit: 1024
        }
    });
}

function configApp(config, params)
{
    // App target configuration

    config.output.path = params.outputDir;

    if (params.buildType === 'release')
    {
        const limit = 300 * 1024;
        config.performance.hints = 'error';
        config.performance.maxEntrypointSize = limit;
        config.performance.maxAssetSize = limit;
    }

    config.entry.push('./src/style.less');
    config.entry.push('./src/index.js');

    config.plugins.push(new HtmlPlugin({
        template: path.join(params.sourceDir, 'index.html')
    }));

    if (params.analyze)
    {
        config.plugins.push(new BundleAnalyzerPlugin({
           analyzerMode: 'static',
           reportFilename: path.join('stats', 'bundle-size.html'),
           openAnalyzer: false,
        }));
    }
}

function configTests(config, params)
{
    // Tests target configuration

    config.output.path = path.join(params.outputDir, 'tests');

    config.entry.push('qunit/qunit/qunit.css');
    config.entry.push('./src/tests/index.js');

    config.plugins.push(new HtmlPlugin({
        template: path.join(params.sourceDir, 'tests', 'index.html')
    }));
}

function configSandbox(config, params)
{
    // Sandbox target configuration

    config.output.path = path.join(params.outputDir, 'sandbox');

    config.entry.push('./src/style.less');
    config.entry.push('./src/sandbox/index.js');

    config.plugins.push(new HtmlPlugin({
        template: path.join(params.sourceDir, 'index.html')
    }));
}

function configDebug(config)
{
    // Debug configuration

    config.devtool = 'source-map';
    config.mode = 'none';

    config.plugins.push(new webpack.LoaderOptionsPlugin({
        debug: true
    }));

    config.module.rules.push({
        test: /\.(css|less)$/,
        use: ['style-loader', 'css-loader', 'less-loader']
    });
}

function configRelease(config)
{
    // Release configuration

    config.mode = 'production';
    config.node.process = false;

    config.plugins.push(new webpack.LoaderOptionsPlugin({
        minimize: true
    }));

    const styleExtractor = new ExtractTextPlugin({
        filename: 'bundle.css'
    });

    config.plugins.push(styleExtractor);

    config.module.rules.push({
        test: /\.(css|less)$/,
        use: styleExtractor.extract({
            use: ['css-loader', 'less-loader'],
            fallback: 'style-loader'
        })
    });
}

function makeBuildParams(env)
{
    const buildType = env.release ? 'release' : 'debug';
    const enableTests = !!env.tests;
    const analyze = !!env.analyze;

    const sourceDir = path.join(__dirname, 'src');
    const outputDir = path.join(__dirname, 'build', buildType);
    const rootDir = path.dirname(__dirname);

    return {
        buildType,
        enableTests,
        analyze,
        rootDir,
        sourceDir,
        outputDir,
    };
}

function makeTarget(configTarget, params)
{
    const config = {
        entry: [],
        output: {},
        plugins: [],
        module: { rules: [] },
        resolve: { alias: {} },
        node: {},
        performance: {},
    };

    configCommon(config, params);
    configTarget(config, params);

    (params.buildType === 'release' ? configRelease : configDebug)(config, params);

    return config;
}

module.exports = function(env)
{
    const params = makeBuildParams(env || {});
    const allTargets = [makeTarget(configApp, params)];

    if (params.enableTests)
    {
        allTargets.push(makeTarget(configTests, params));
        allTargets.push(makeTarget(configSandbox, params));
    }

    return allTargets;
};
<|endoftext|>


<commit_before>angular
  .module('mage')
  .directive('leaflet', leaflet);

function leaflet() {
  var directive = {
    restrict: "A",
    replace: true,
    template: '

',
    controller: LeafletController,
    bindToController: true
  };

  return directive;
}

LeafletController.$inject = ['$rootScope', '$scope', '$interval', 'MapService', 'LocalStorageService'];

function LeafletController($rootScope, $scope, $interval, MapService, LocalStorageService) {
  var layers = {};
  var temporalLayers = [];
  var spiderfyState = null;
  var currentLocation = null;
  var locationLayer = L.locationMarker([0,0], {color: '#136AEC'});
  var map = L.map("map", {
    center: [0,0],
    zoom: 3,
    minZoom: 0,
    maxZoom: 18
  });

  // toolbar  and controls config
  var sidebar = L.control.sidebar('side-bar', {closeButton: false});
  map.addControl(new L.Control.MageFeature({
    onClick: function(latlng) {
      $scope.$emit('observation:create', latlng);
    }
  }));
  map.addControl(new L.Control.MageUserLocation({
    onLocation: onLocation,
    stopLocation: stopLocation
  }));
  map.addControl(new L.Control.MageListTools({
    onClick: function() {
      sidebar.toggle();
    }
  }));
  map.addControl(sidebar);
  sidebar.show();

  var layerControl = L.control.groupedLayers();
  layerControl.addTo(map);
  map.on('baselayerchange', function(baseLayer) {
    var layer = layers[baseLayer.name];
    MapService.selectBaseLayer(layer);
  });

  map.on('overlayadd', function(overlay, name) {
    var layer = layers[overlay.name];
    MapService.overlayAdded(layer);
  });

  function onLocation(location, broadcast) {
    // no need to do anything if the location has not changed
    if (currentLocation &&
        (currentLocation.lat === location.latlng.lat &&
         currentLocation.lng === location.latlng.lng &&
         currentLocation.accuracy === location.accuracy)) {
      return;
    }
    currentLocation = location;

    map.fitBounds(location.bounds);
    locationLayer.setLatLng(location.latlng).setAccuracy(location.accuracy);

    if (!map.hasLayer(locationLayer)) {
      map.addLayer(locationLayer);
    }

    if (broadcast) MapService.onLocation(location);
  }

  function stopLocation() {
    if (map.hasLayer(locationLayer)) {
      map.removeLayer(locationLayer);
    }

    currentLocation = null;
  }

  // setup my listeners
  var listener = {
    onLayerRemoved: onLayerRemoved,
    onLayersChanged: onLayersChanged,
    onFeaturesChanged: onFeaturesChanged,
    onFeatureZoom: onFeatureZoom,
    onPoll: onPoll
  };
  MapService.addListener(listener);

  $scope.$on('$destroy', function() {
    MapService.removeListener(listener);
  });

  function createMarker(marker) {
    // cannot create another marker with the same id
    if (layers[marker.layerId]) return;

    var options = marker.options || {};

    if (marker.geometry && marker.geometry.type === 'Point') {
      var latlng = [0, 0];
      if (marker.geometry.coordinates) {
        latlng = [marker.geometry.coordinates[1], marker.geometry.coordinates[0]];
      } else {
        latlng = map.getCenter();
      }

      var layer = L.marker(latlng, {
        draggable: options.draggable,
        icon: L.AwesomeMarkers.newDivIcon({
          icon: 'plus',
          color: 'cadetblue'
        })
      });

      if (_.isFunction(options.onDragEnd)) {
        layer.on('dragend', function() {
          options.onDragEnd(layer.getLatLng());
        });
      }

      if (options.selected) layer.addTo(map);
      layers[marker.layerId] = {layer: layer};
    }
  }

  function updateMarker(marker) {
    var layer = layers[marker.id];
    if (marker.geometry && marker.geometry.type === 'Point') {
      layer.setLatLng([marker.geometry.coordinates[1], marker.geometry.coordinates[0]]);
    }
  }

  // TODO move into leaflet ***REMOVED***, this and map clip both use it
  function createRasterLayer(layerInfo) {
    var baseLayer = null;
    var options = {};
    if (layerInfo.format == 'XYZ' || layerInfo.format == 'TMS') {
      options = { tms: layerInfo.format == 'TMS', maxZoom: 18}
      layerInfo.layer = new L.TileLayer(layerInfo.url, options);
    } else if (layerInfo.format == 'WMS') {
      options = {
        layers: layerInfo.wms.layers,
        version: layerInfo.wms.version,
        format: layerInfo.wms.format,
        transparent: layerInfo.wms.transparent
      };

      if (layerInfo.wms.styles) options.styles = layerInfo.wms.styles;
      layerInfo.layer = new L.TileLayer.WMS(layerInfo.url, options);
    }

    layers[layerInfo.name] = layerInfo;

    if (layerInfo.base) {
      layerControl.addBaseLayer(layerInfo.layer, layerInfo.name);
    } else {
      layerControl.addOverlay(layerInfo.layer, layerInfo.name, 'Static Layers');
    }

    if (layerInfo.options && layerInfo.options.selected) layerInfo.layer.addTo(map);
  }

  function colorForFeature(feature, options) {
    var age = Date.now() - moment(feature.properties[options.property]).valueOf();
    var bucket = _.find(options.colorBuckets, function(bucket) { return age > bucket.min && age <= bucket.max; });
    return bucket ? bucket.color : null;
  }

  function createGeoJsonForLayer(json, layerInfo) {
    var popup = layerInfo.options.popup;

    var geojson = L.geoJson(json, {
      onEachFeature: function (feature, layer) {
        if (popup) {
          if (_.isFunction(popup.html)) {
            var options = {autoPan: false};
            if (popup.closeButton != null) options.closeButton = popup.closeButton;
            layer.bindPopup(popup.html(feature), options);
          }

          if (_.isFunction(popup.onOpen)) {
            layer.on('popupopen', function() {
              popup.onOpen(feature);
            });
          }

          if (_.isFunction(popup.onClose)) {
            layer.on('popupclose', function() {
              popup.onClose(feature);
            });
          }
        }

        if (layerInfo.options.showAccuracy) {
          layer.on('popupopen', function() {
            layer.setAccuracy(layer.feature.properties.accuracy);
          });

          layer.on('popupclose', function() {
            layer.setAccuracy(null);
          });
        }

        layerInfo.featureIdToLayer[feature.id] = layer;
      },
      pointToLayer: function (feature, latlng) {
        if (layerInfo.options.temporal) {
          // TODO temporal layers should be fixed width as well, ie use fixedWidthMarker cl***REMOVED***
          var options = {
            color: colorForFeature(feature, layerInfo.options.temporal)
          };
          if (feature.style && feature.style.iconUrl) options.iconUrl = feature.style.iconUrl + '?access_token=' + LocalStorageService.getToken();

          return L.locationMarker(latlng, options);
        } else {
          var options = {};
          if (feature.style && feature.style.iconUrl) options.iconUrl = feature.style.iconUrl + '?access_token=' + LocalStorageService.getToken()
          return L.fixedWidthMarker(latlng, options);
        }
      },
      style: function(feature) {
        return feature.style;
      }
    });

    return geojson;
  }

  function createGeoJsonLayer(data) {
    var layerInfo = {
      type: 'geojson',
      featureIdToLayer: {},
      options: data.options
    };

    var geojson = createGeoJsonForLayer(data.geojson, layerInfo);
    if (data.options.cluster) {
      layerInfo.layer = L.markerClusterGroup().addLayer(geojson);
      layerInfo.layer.on('spiderfied', function(e) {
        if (spiderfyState) {
          spiderfyState.layer.openPopup();
        }
      });
    } else {
      layerInfo.layer = geojson;
    }

    layers[data.name] = layerInfo;
    if (data.options.temporal) temporalLayers.push(layerInfo);

    layerControl.addOverlay(layerInfo.layer, data.name, data.group);
    if (data.options.selected) layerInfo.layer.addTo(map);
  }

  function onLayersChanged(changed) {
    _.each(changed.added, function(added) {
      switch(added.type) {
        case 'Feature':
          createMarker(added);
          break;
        case 'Imagery':
          createRasterLayer(added);
          break;
        case 'geojson':
          createGeoJsonLayer(added);
          break;
      }
    });

    _.each(changed.updated, function(updated) {
      switch(updated.type) {
        case 'Feature':
          updateMarker(updated);
          break;
      }
    });

    _.each(changed.removed, function(removed) {
      var layer = layers[removed.layerId];
      if (layer) {
        map.removeLayer(layer.layer);
        delete layer.layer;
        delete layer;
        delete layers[removed.layerId];
      }
    });
  }

  function onFeaturesChanged(changed) {
    var featureLayer = layers[changed.name];

    _.each(changed.added, function(feature) {
      if (featureLayer.options.cluster) {
        featureLayer.layer.addLayer(createGeoJsonForLayer(feature, featureLayer));
      } else {
        featureLayer.layer.addData(feature);
      }

    });

    _.each(changed.updated, function(feature) {
      var layer = featureLayer.featureIdToLayer[feature.id];

      // Copy over the updated feature data
      if (layer.feature) {
        layer.feature = feature;
      }

      // Set the icon
      if (layer.feature && layer.feature.iconUrl !== feature.iconUrl) {
        layer.setIcon(L.urlDivIcon({
            feature: feature,
            token: LocalStorageService.getToken()
          })
        );
      }

      if (featureLayer.options.temporal) {
        var color = colorForFeature(feature, featureLayer.options.temporal);
        layer.setColor(color);
      }

      // Set the lat/lng
      if (feature.geometry.coordinates) {
        layer.setLatLng(L.GeoJSON.coordsToLatLng(feature.geometry.coordinates));
        if (featureLayer.options.showAccuracy && layer.getAccuracy()) {
          layer.setAccuracy(layer.feature.properties.accuracy);
        }
      }
    });

    _.each(changed.removed, function(feature) {
      var layer = featureLayer.featureIdToLayer[feature.id];
      featureLayer.layer.removeLayer(layer);
    });
  }

  function openPopup(layer, options) {
    options = options || {};
    var zoom = options.zoomToLocation ? 17: map.getZoom();
    var bounds = map.getBounds();
    if (options.zoomToLocation && (!bounds.contains(layer.getLatLng()) || zoom != map.getZoom())) {
      map.once('moveend', function() {
        layer.openPopup();
      });

      map.setView(layer.getLatLng(), zoom);
    } else {
      layer.openPopup();
    }
  }

  function onFeatureZoom(zoom) {
    var featureLayer = layers[zoom.name];
    var layer = featureLayer.featureIdToLayer[zoom.feature.id];
    if (!map.hasLayer(featureLayer.layer)) return;

    if (featureLayer.options.cluster) {
      if (map.getZoom() < 17) {
        map.once('zoomend', function() {
          featureLayer.layer.zoomToShowLayer(layer, function() {
            openPopup(layer);
          });
        });
        map.setZoom(17);
      } else {
        featureLayer.layer.zoomToShowLayer(layer, function() {
          openPopup(layer);
        });
      }
    } else {
      openPopup(layer, {zoomToLocation: true});
    }
  }

  function onPoll() {
    adjustTemporalLayers();
  }

  function adjustTemporalLayers() {
    _.each(temporalLayers, function(temporalLayer) {
      _.each(temporalLayer.featureIdToLayer, function(layer) {
        var color = colorForFeature(layer.feature, temporalLayer.options.temporal);
        layer.setColor(color);
      });
    });
  }

  function onLayerRemoved(layer) {
    var layerInfo = layers[layer.name];
    if (layerInfo) {
      map.removeLayer(layerInfo.layer);
      layerControl.removeLayer(layerInfo.layer);
      delete layers[layer.name];
    }
  }
}
<commit_msg>Center map every time on location follow<commit_after>angular
  .module('mage')
  .directive('leaflet', leaflet);

function leaflet() {
  var directive = {
    restrict: "A",
    replace: true,
    template: '

',
    controller: LeafletController,
    bindToController: true
  };

  return directive;
}

LeafletController.$inject = ['$rootScope', '$scope', '$interval', 'MapService', 'LocalStorageService'];

function LeafletController($rootScope, $scope, $interval, MapService, LocalStorageService) {
  var layers = {};
  var temporalLayers = [];
  var spiderfyState = null;
  var currentLocation = null;
  var locationLayer = L.locationMarker([0,0], {color: '#136AEC'});
  var map = L.map("map", {
    center: [0,0],
    zoom: 3,
    minZoom: 0,
    maxZoom: 18
  });

  // toolbar  and controls config
  var sidebar = L.control.sidebar('side-bar', {closeButton: false});
  map.addControl(new L.Control.MageFeature({
    onClick: function(latlng) {
      $scope.$emit('observation:create', latlng);
    }
  }));
  map.addControl(new L.Control.MageUserLocation({
    onLocation: onLocation,
    stopLocation: stopLocation
  }));
  map.addControl(new L.Control.MageListTools({
    onClick: function() {
      sidebar.toggle();
    }
  }));
  map.addControl(sidebar);
  sidebar.show();

  var layerControl = L.control.groupedLayers();
  layerControl.addTo(map);
  map.on('baselayerchange', function(baseLayer) {
    var layer = layers[baseLayer.name];
    MapService.selectBaseLayer(layer);
  });

  map.on('overlayadd', function(overlay, name) {
    var layer = layers[overlay.name];
    MapService.overlayAdded(layer);
  });

  function onLocation(location, broadcast) {
    // no need to do anything if the location has not changed
    if (currentLocation &&
        (currentLocation.lat === location.latlng.lat &&
         currentLocation.lng === location.latlng.lng &&
         currentLocation.accuracy === location.accuracy)) {
      return;
    }
    currentLocation = location;

    map.fitBounds(location.bounds);
    locationLayer.setLatLng(location.latlng).setAccuracy(location.accuracy);

    if (!map.hasLayer(locationLayer)) {
      map.addLayer(locationLayer);
    }

    if (broadcast) MapService.onLocation(location);
  }

  function stopLocation() {
    if (map.hasLayer(locationLayer)) {
      map.removeLayer(locationLayer);
    }

    currentLocation = null;
  }

  // setup my listeners
  var listener = {
    onLayerRemoved: onLayerRemoved,
    onLayersChanged: onLayersChanged,
    onFeaturesChanged: onFeaturesChanged,
    onFeatureZoom: onFeatureZoom,
    onPoll: onPoll
  };
  MapService.addListener(listener);

  $scope.$on('$destroy', function() {
    MapService.removeListener(listener);
  });

  function createMarker(marker) {
    // cannot create another marker with the same id
    if (layers[marker.layerId]) return;

    var options = marker.options || {};

    if (marker.geometry && marker.geometry.type === 'Point') {
      var latlng = [0, 0];
      if (marker.geometry.coordinates) {
        latlng = [marker.geometry.coordinates[1], marker.geometry.coordinates[0]];
      } else {
        latlng = map.getCenter();
      }

      var layer = L.marker(latlng, {
        draggable: options.draggable,
        icon: L.AwesomeMarkers.newDivIcon({
          icon: 'plus',
          color: 'cadetblue'
        })
      });

      if (_.isFunction(options.onDragEnd)) {
        layer.on('dragend', function() {
          options.onDragEnd(layer.getLatLng());
        });
      }

      if (options.selected) layer.addTo(map);
      layers[marker.layerId] = {layer: layer};
    }
  }

  function updateMarker(marker) {
    var layer = layers[marker.id];
    if (marker.geometry && marker.geometry.type === 'Point') {
      layer.setLatLng([marker.geometry.coordinates[1], marker.geometry.coordinates[0]]);
    }
  }

  // TODO move into leaflet ***REMOVED***, this and map clip both use it
  function createRasterLayer(layerInfo) {
    var baseLayer = null;
    var options = {};
    if (layerInfo.format == 'XYZ' || layerInfo.format == 'TMS') {
      options = { tms: layerInfo.format == 'TMS', maxZoom: 18}
      layerInfo.layer = new L.TileLayer(layerInfo.url, options);
    } else if (layerInfo.format == 'WMS') {
      options = {
        layers: layerInfo.wms.layers,
        version: layerInfo.wms.version,
        format: layerInfo.wms.format,
        transparent: layerInfo.wms.transparent
      };

      if (layerInfo.wms.styles) options.styles = layerInfo.wms.styles;
      layerInfo.layer = new L.TileLayer.WMS(layerInfo.url, options);
    }

    layers[layerInfo.name] = layerInfo;

    if (layerInfo.base) {
      layerControl.addBaseLayer(layerInfo.layer, layerInfo.name);
    } else {
      layerControl.addOverlay(layerInfo.layer, layerInfo.name, 'Static Layers');
    }

    if (layerInfo.options && layerInfo.options.selected) layerInfo.layer.addTo(map);
  }

  function colorForFeature(feature, options) {
    var age = Date.now() - moment(feature.properties[options.property]).valueOf();
    var bucket = _.find(options.colorBuckets, function(bucket) { return age > bucket.min && age <= bucket.max; });
    return bucket ? bucket.color : null;
  }

  function createGeoJsonForLayer(json, layerInfo) {
    var popup = layerInfo.options.popup;

    var geojson = L.geoJson(json, {
      onEachFeature: function (feature, layer) {
        if (popup) {
          if (_.isFunction(popup.html)) {
            var options = {autoPan: false};
            if (popup.closeButton != null) options.closeButton = popup.closeButton;
            layer.bindPopup(popup.html(feature), options);
          }

          if (_.isFunction(popup.onOpen)) {
            layer.on('popupopen', function() {
              popup.onOpen(feature);
            });
          }

          if (_.isFunction(popup.onClose)) {
            layer.on('popupclose', function() {
              popup.onClose(feature);
            });
          }
        }

        if (layerInfo.options.showAccuracy) {
          layer.on('popupopen', function() {
            layer.setAccuracy(layer.feature.properties.accuracy);
          });

          layer.on('popupclose', function() {
            layer.setAccuracy(null);
          });
        }

        layerInfo.featureIdToLayer[feature.id] = layer;
      },
      pointToLayer: function (feature, latlng) {
        if (layerInfo.options.temporal) {
          // TODO temporal layers should be fixed width as well, ie use fixedWidthMarker cl***REMOVED***
          var options = {
            color: colorForFeature(feature, layerInfo.options.temporal)
          };
          if (feature.style && feature.style.iconUrl) options.iconUrl = feature.style.iconUrl + '?access_token=' + LocalStorageService.getToken();

          return L.locationMarker(latlng, options);
        } else {
          var options = {};
          if (feature.style && feature.style.iconUrl) options.iconUrl = feature.style.iconUrl + '?access_token=' + LocalStorageService.getToken()
          return L.fixedWidthMarker(latlng, options);
        }
      },
      style: function(feature) {
        return feature.style;
      }
    });

    return geojson;
  }

  function createGeoJsonLayer(data) {
    var layerInfo = {
      type: 'geojson',
      featureIdToLayer: {},
      options: data.options
    };

    var geojson = createGeoJsonForLayer(data.geojson, layerInfo);
    if (data.options.cluster) {
      layerInfo.layer = L.markerClusterGroup().addLayer(geojson);
      layerInfo.layer.on('spiderfied', function(e) {
        if (spiderfyState) {
          spiderfyState.layer.openPopup();
        }
      });
    } else {
      layerInfo.layer = geojson;
    }

    layers[data.name] = layerInfo;
    if (data.options.temporal) temporalLayers.push(layerInfo);

    layerControl.addOverlay(layerInfo.layer, data.name, data.group);
    if (data.options.selected) layerInfo.layer.addTo(map);
  }

  function onLayersChanged(changed) {
    _.each(changed.added, function(added) {
      switch(added.type) {
        case 'Feature':
          createMarker(added);
          break;
        case 'Imagery':
          createRasterLayer(added);
          break;
        case 'geojson':
          createGeoJsonLayer(added);
          break;
      }
    });

    _.each(changed.updated, function(updated) {
      switch(updated.type) {
        case 'Feature':
          updateMarker(updated);
          break;
      }
    });

    _.each(changed.removed, function(removed) {
      var layer = layers[removed.layerId];
      if (layer) {
        map.removeLayer(layer.layer);
        delete layer.layer;
        delete layer;
        delete layers[removed.layerId];
      }
    });
  }

  function onFeaturesChanged(changed) {
    var featureLayer = layers[changed.name];

    _.each(changed.added, function(feature) {
      if (featureLayer.options.cluster) {
        featureLayer.layer.addLayer(createGeoJsonForLayer(feature, featureLayer));
      } else {
        featureLayer.layer.addData(feature);
      }

    });

    _.each(changed.updated, function(feature) {
      var layer = featureLayer.featureIdToLayer[feature.id];

      // Copy over the updated feature data
      if (layer.feature) {
        layer.feature = feature;
      }

      // Set the icon
      if (layer.feature && layer.feature.iconUrl !== feature.iconUrl) {
        layer.setIcon(L.urlDivIcon({
            feature: feature,
            token: LocalStorageService.getToken()
          })
        );
      }

      if (featureLayer.options.temporal) {
        var color = colorForFeature(feature, featureLayer.options.temporal);
        layer.setColor(color);
      }

      // Set the lat/lng
      if (feature.geometry.coordinates) {
        layer.setLatLng(L.GeoJSON.coordsToLatLng(feature.geometry.coordinates));
        if (featureLayer.options.showAccuracy && layer.getAccuracy()) {
          layer.setAccuracy(layer.feature.properties.accuracy);
        }
      }
    });

    _.each(changed.removed, function(feature) {
      var layer = featureLayer.featureIdToLayer[feature.id];
      featureLayer.layer.removeLayer(layer);
    });
  }

  function openPopup(layer, options) {
    options = options || {};
    if (options.zoomToLocation) { // && (!bounds.contains(layer.getLatLng()) || zoom != map.getZoom())) {
      map.once('moveend', function() {
        layer.openPopup();
      });

      map.setView(layer.getLatLng(),  options.zoomToLocation ? 17: map.getZoom());
    } else {
      layer.openPopup();
    }
  }

  function onFeatureZoom(zoom) {
    var featureLayer = layers[zoom.name];
    var layer = featureLayer.featureIdToLayer[zoom.feature.id];
    if (!map.hasLayer(featureLayer.layer)) return;

    if (featureLayer.options.cluster) {
      if (map.getZoom() < 17) {
        map.once('zoomend', function() {
          featureLayer.layer.zoomToShowLayer(layer, function() {
            openPopup(layer);
          });
        });
        map.setZoom(17);
      } else {
        featureLayer.layer.zoomToShowLayer(layer, function() {
          openPopup(layer);
        });
      }
    } else {
      openPopup(layer, {zoomToLocation: true});
    }
  }

  function onPoll() {
    adjustTemporalLayers();
  }

  function adjustTemporalLayers() {
    _.each(temporalLayers, function(temporalLayer) {
      _.each(temporalLayer.featureIdToLayer, function(layer) {
        var color = colorForFeature(layer.feature, temporalLayer.options.temporal);
        layer.setColor(color);
      });
    });
  }

  function onLayerRemoved(layer) {
    var layerInfo = layers[layer.name];
    if (layerInfo) {
      map.removeLayer(layerInfo.layer);
      layerControl.removeLayer(layerInfo.layer);
      delete layers[layer.name];
    }
  }
}
<|endoftext|>


<commit_before>exports.task = function (done) {
  var errorCount = 0;
  var karmaConfig = {
    logLevel: 'warn',
    singleRun: true,
    autoWatch: false,
    browsers : argv.browsers ? argv.browsers.trim().split(',') : ['Chrome'],
    configFile: root + '/config/karma.conf.js'
  };

  /**
   * For each cycle of testings (unminified, minified, minified w/ jQuery)
   * interrogate the exitCode to capture the exitCode...
   * Then report any errors that may manifest [e.g.  in the minified tests]
   */
  function captureError(next) {
    return function(exitCode) {
      if (exitCode != 0) {
        gutil.log(gutil.colors.red("Karma exited with the following exit code: " + exitCode));
        errorCount++;
      }
      next();
    };
  }

  gutil.log('Running unit tests on unminified source.');
  buildJs(true);
  karma.start(karmaConfig, captureError(testMinified));

  function testMinified() {
    gutil.log('Running unit tests on minified source.');
    process.env.KARMA_TEST_COMPRESSED = true;
    karma.start(karmaConfig, captureError(testMinifiedJquery));
  }

  function testMinifiedJquery() {
    gutil.log('Running unit tests on minified source w/ jquery.');
    process.env.KARMA_TEST_COMPRESSED = true;
    process.env.KARMA_TEST_JQUERY = true;
    karma.start(karmaConfig, clearEnv);
  }

  function clearEnv() {
    process.env.KARMA_TEST_COMPRESSED = undefined;
    process.env.KARMA_TEST_JQUERY = undefined;

    if (errorCount > 0) { process.exit(errorCount); }
    done();
  }
};
<commit_msg>Update karma.js<commit_after>exports.task = function (done) {
  var errorCount = 0;
  var karmaConfig = {
    logLevel: 'warn',
    singleRun: true,
    autoWatch: false,
    browsers : argv.browsers ? argv.browsers.trim().split(',') : ['Chrome'],
    configFile: root + '/config/karma.conf.js'
  };

  /**
   * For each cycle of testings (unminified, minified, minified w/ jQuery)
   * capgture the exitCode to update the error count...
   * Then report any errors that may manifest [e.g.  in the minified tests]
   */
  function captureError(next) {
    return function(exitCode) {
      if (exitCode != 0) {
        gutil.log(gutil.colors.red("Karma exited with the following exit code: " + exitCode));
        errorCount++;
      }
      next();
    };
  }

  gutil.log('Running unit tests on unminified source.');
  buildJs(true);
  karma.start(karmaConfig, captureError(testMinified));

  function testMinified() {
    gutil.log('Running unit tests on minified source.');
    process.env.KARMA_TEST_COMPRESSED = true;
    karma.start(karmaConfig, captureError(testMinifiedJquery));
  }

  function testMinifiedJquery() {
    gutil.log('Running unit tests on minified source w/ jquery.');
    process.env.KARMA_TEST_COMPRESSED = true;
    process.env.KARMA_TEST_JQUERY = true;
    karma.start(karmaConfig, clearEnv);
  }

  function clearEnv() {
    process.env.KARMA_TEST_COMPRESSED = undefined;
    process.env.KARMA_TEST_JQUERY = undefined;

    if (errorCount > 0) { process.exit(errorCount); }
    done();
  }
};
<|endoftext|>


<commit_before>angular.module('coderace.race', ['ui.codemirror'])

.controller('raceController', function ($scope, $rootScope, Race, socket){
  var master = false;
  $scope.room = false; // need to revert to false. only done for testing --- tito
  $scope.opponentLeft = false;
  $scope.opponentPassed = false;

  var getCookies = function(){
    var pairs = document.cookie.split(";");
    var cookies = {};
    for (var i=0; i<pairs.length; i++){
      pairs
      var pair = pairs[i].trim().split("=");
      cookies[pair[0]] = unescape(pair[1]);
    }
    return cookies;
  }

  var cookies = getCookies();
  var facebookId = document.facebookId = cookies.userID;
  var facebookDisplayName = document.facebookDisplayName = cookies.displayName;
  var startTime;
  var challengeId;

  // countdown timer
  function timer(){
    //Call them like these at the start.
    socket.emit('startAnimate', { avatar : 'kakashi', animation: 'stance' });
    socket.emit('startAnimate', { avatar : 'naruto', animation: 'stance' });

    $scope.counter = 5;
    $scope.countComplete = true;
    var countDown = setInterval(function() {
    $scope.counter--;
      $scope.$apply();
      if($scope.counter === 0){
        clearInterval(countDown);
        $scope.countComplete = false;
        $('#waitingOverlay').css('display', 'none');
        codeMirror();
        startTime = Date.now();
        var matchId = $scope.room
        var matchRef = Race.dataRef.child('Matches/' + matchId);
        matchRef.update({'startTime': startTime});
        matchRef.update({'challengeId': challengeId});
      }
    }, 1000);
  }

  function codeMirror(){
    // codemirror options
    $scope.editorOptions = {
      mode: 'javascript',
      theme: '3024-night',
      lineNumbers: true,
      lineWrapping: true,
      showCursorWhenSelecting: true,
      autofocus: false,
      keyMap: 'sublime',
      autoCloseBrackets: true,
      tabSize: 2,
      extraKeys: {"Ctrl-Space": "autocomplete"},
      gutters: ['CodeMirror-lint-markers'],
      lint: true,
      scrollbarStyle: "null"
    };
    // codemirror autocomplete
    function autocomplete(){
      CodeMirror.showHint({hint: CodeMirror.hint.anyword});
    }
    $scope.$apply();
  }

  function getRandomArbitrary(min, max) {
    return Math.floor(Math.random() * (max - min) + min);
  }//generate a random number

//challengeInputs is set the in the outer scope so it can be used in $scope.evaluate.
  var challengeInputs;

  //when the data is loaded in the factory, load the data in the html and set the challenge inputs.
  $scope.$on('Race:ready', function (event, data) {
    $scope.code = data.startingCode;
    $scope.question = data.question;
    $scope.functionName = data.functionName;
    $scope.$apply(); //push the newly acquired data to the dom.

    //set challenge inputs to be used later by the worker.
    challengeInputs = {
      inputs: data.inputs,
      answers: data.answers,
      functionName: data.functionName
    };
  });

  $scope.evaluate = function(code) {

    //When the user firsts click the submit button, set all values to their defaults, in case they are submitting on a second or third try, the wrong test results wont show up.
    $scope.dataLoaded = false;
    $scope.error = undefined;
    $scope.tests = undefined;
    $scope.passed = undefined;
    $scope.responseText = undefined;

    var matchId = $scope.room

    var storeMatchResults = function(){
      Race.dataRef.child('Matches/' + matchId).update({
        winnerId: facebookId,
        endTime: Date.now(),
      })
    }

    var storeGhost = function(){
      var matchRef = Race.dataRef.child('Matches/' + matchId);
      var matchesRef = matchRef.parent();

      matchRef.once('value', function(snapshot){
        var matchObj = snapshot.val();

        var winnerId = matchObj.winnerId;
        var winnerTypingDataRef = matchRef.child('players/' + winnerId);
        winnerTypingDataRef.once('value', function(snapshot){
          var ghostData = snapshot.val();
          var ghostsRef = Race.dataRef.child('Challenges/' + challengeId + '/Ghosts');
          var ghostIdRef = ghostsRef.push();
          ghostIdRef.set({
            typingData: ghostData,
            gameDuration: matchObj.endTime - matchObj.startTime,
          });
        })
      })
    }

    //render the results from the worker in the dom.
    var renderCodeResponse = function(codeResponse) {
      $scope.dataLoaded = true; //set this to true to stop the spinner.
      $scope.validResponse = codeResponse.valid;
      $scope.error = codeResponse.error;
      $scope.tests = codeResponse.tests;
      $scope.passed = codeResponse.passed;
      $scope.responseText = codeResponse.passed ? "correct!" : "incorrect";
      if(codeResponse.passed){
        storeMatchResults();
        storeGhost();
        socket.emit('knockOut', {facebookId: facebookId});
      }

      $scope.$apply(); //apply the scope to the dom once the worker has responded with results.
    };

    //Execute the worker on the client side
    if (window.Worker) { //verify that the browser has worker capability.
      var evalWorker = new Worker("client/evalWorker.js");

      angular.extend(challengeInputs, {code: code}); //use the extend function add the input code string to the challenge inputs.

      //launch the worker with the challenge inputs.
      evalWorker.postMessage(challengeInputs);

      //used to test for timeouts 5 seconds after the worker starts.
      var workerComplete = false;

      //if the code input by the user is invalid, this worker will trigger.
      evalWorker.onerror = function(error) {
        console.log(error);
        evalWorker.terminate(); //kill the worker.
        workerComplete = true; //the worker is done.
        var codeResponse = { //the code is invalid. create response to send to the dom.
          valid: false,
          error: error.message,
          passed: false
        };
        renderCodeResponse(codeResponse); //render the dom with the correct code response.
      };

      evalWorker.onmessage = function(codeResponse) { //when the worker sends back its response, update the dom.
        if(codeResponse.data.passed === true){ //tell the opponent that this users code passed.
          socket.emit('passed');
        }
        workerComplete = true; //don't execute the timeout function.
        renderCodeResponse(codeResponse.data); //pass the codeResponse object to the dom to be rendered.
      }

      //check for worker timeout.
      setTimeout(function() {
        if (workerComplete === false){ //the worker has not completed after 5 seconds.
          evalWorker.terminate(); //terminate the worker.
          renderCodeResponse({
            valid: false,
            error: "Code is taking longer than 5 seconds to process",
            passed: false
          });
        }
      }, 5000);
    }
  }

  $scope.typing = function(code){
    socket.emit('typing', {
      code: code,
      facebookId: facebookId,
      startTime: startTime,
      challengeId: challengeId,
    });

    //How I call it when they are attacking
    //Bug is kakashi attacks twice
    socket.emit('startAnimate', { avatar : 'kakashi', animation: 'attack' });
    socket.emit('startAnimate', { avatar : 'naruto', animation: 'attack' });
  };

  $scope.$on('$destroy', function(){
    socket.disconnect();
  });

  if(!socket.connected){
    socket.connect();
  }

  if(true){
    socket.emit('start', {
      username: facebookDisplayName.split(' ')[0],
      gameType: 'ghostMatch',
    })
  } else if(false){
    socket.emit('start', {
      username: facebookDisplayName.split(' ')[0],
      gameType: 'randomPlayerVsPlayer'
    });
  }

  socket.on('opponentLeft', function(){
    $scope.opponentLeft = true;
  });
  socket.on('typing', function(data) {
    $scope.competitorCode = data.code;
  })
  socket.on('roomJoined', function(matchData){
    $scope.room = matchData.matchId;
    timer();
    $scope.opponent = master ?
      matchData.player2: matchData.player1;
  });
  socket.on('passed', function(){
    $scope.opponentPassed = true;
  });
  socket.on('master', function(){
    master = true;
    Race.getLength();
    $scope.$on('GotLength', function(event,data){
      var challengesObject;
      Race.dataRef.child('Challenges').on('value', function(snapshot){
        challengesObject = snapshot.val();
      });

      challengeIdsArray = Object.keys(challengesObject);
      var randomIndex = getRandomArbitrary(0, challengeIdsArray.length);

      Race.getData(challengeIdsArray[randomIndex], function(problem){
        challengeId = challengeIdsArray[randomIndex];
        problem.challengeId = challengeId
        socket.emit('problem', problem);
      });
    });
  });
  socket.on('problem', function(problem){
    challengeId = problem.challengeId;
    setTimeout(function(){
      Race.setProblem(problem);
    }, 0, problem);
  });

  //Everything below here is for the animation
  var stance = {
    'kakashi' : ['stance/Position1.png', 'stance/Position2.png', 'stance/Position3.png', 'stance/Position4.png', 'stance/Position5.png',  'stance/Position6.png'],
    'naruto' : ['stance/Position1.png', 'stance/Position2.png', 'stance/Position3.png', 'stance/Position4.png', 'stance/Position5.png', 'stance/Position6.png']
  }

  var attack = {
    'kakashi' : ['attacks/attack1/Position1.png', 'attacks/attack1/Position2.png', 'attacks/attack1/Position3.png', 'attacks/attack1/Position4.png', 'attacks/attack1/Position5.png', 'attacks/attack1/Position6.png', 'attacks/attack1/Position7.png', 'attacks/attack1/Position8.png', 'attacks/attack1/Position9.png', 'attacks/attack1/Position10.png', 'attacks/attack1/Position11.png', 'attacks/attack1/Position12.png', 'attacks/attack1/Position13.png'],
    'naruto' : ['attacks/attack1/Position1.png', 'attacks/attack1/Position2.png', 'attacks/attack1/Position3.png', 'attacks/attack1/Position4.png', 'attacks/attack1/Position5.png', 'attacks/attack1/Position6.png', 'attacks/attack1/Position7.png', 'attacks/attack1/Position8.png', 'attacks/attack1/Position9.png', 'attacks/attack1/Position10.png', 'attacks/attack1/Position11.png']
  }

  var img = document.getElementsByTagName('IMG');
  var index = 0;
  var index2 = 0;
  var attacking = false;
  var running = false;
  var avatarPath = "img/kakashi/";
  var avatarPath2 = "img/naruto/"

  var thread, thread2;

  socket.on('animate', function(data){
    var elem = img[2];
    var elem2 = img[3];

    function kakashiStanceImg(){
      index++;

      if (index >= stance.kakashi.length) index = 0;

      elem.src = avatarPath + stance.kakashi[index];
    }

    function narutoStanceImg(){
      index2++;

      if (index2 >= stance.naruto.length) index2 = 0;

      elem2.src = avatarPath2 + stance.naruto[index2];
    }

    function kakashiAttackImg(){
      index++;

      if (index >= attack.kakashi.length) {
        switchImg('stance', 'kakashi')
      }
      else elem.src = avatarPath + attack.kakashi[index];
    }

    function narutoAttackImg(){
      index2++;

      if (index2 >= attack.naruto.length) {
        switchImg('stance', 'naruto');
      }
      else elem2.src = avatarPath2 + attack.naruto[index];
    }

    function switchImg(animation, avatar, duration, attacking, running){
      duration = duration || 100;
      index = -1;
      index2 = -1;
      attacking = attacking || false;
      running = running || false;

      if (avatar === 'kakashi') {
        clearInterval(thread);

        if (animation === 'stance') thread = setInterval(kakashiStanceImg, duration);
        else if (animation === 'attack') thread = setInterval(kakashiAttackImg, duration);
      }
      else if (avatar === 'naruto') {
        clearInterval(thread2);

        if (animation === 'stance') thread2 = setInterval(narutoStanceImg, duration);
        else if (animation === 'attack') thread2 = setInterval(narutoAttackImg, duration);
      }
    }

    switchImg(data.action[1], data.action[0]);

    //Call the animation like this:
    //switchImg(animationStrin, data[character]);
  })
});

<commit_msg>[fix] Connect typing from server to animation<commit_after>angular.module('coderace.race', ['ui.codemirror'])

.controller('raceController', function ($scope, $rootScope, Race, socket){
  var master = false;
  $scope.room = false; // need to revert to false. only done for testing --- tito
  $scope.opponentLeft = false;
  $scope.opponentPassed = false;

  var getCookies = function(){
    var pairs = document.cookie.split(";");
    var cookies = {};
    for (var i=0; i<pairs.length; i++){
      pairs
      var pair = pairs[i].trim().split("=");
      cookies[pair[0]] = unescape(pair[1]);
    }
    return cookies;
  }

  var cookies = getCookies();
  var facebookId = document.facebookId = cookies.userID;
  var facebookDisplayName = document.facebookDisplayName = cookies.displayName;
  var startTime;
  var challengeId;

  // countdown timer
  function timer(){
    //Call them like these at the start.
    socket.emit('startAnimate', { avatar : 'kakashi', animation: 'stance' });
    socket.emit('startAnimate', { avatar : 'naruto', animation: 'stance' });

    $scope.counter = 5;
    $scope.countComplete = true;
    var countDown = setInterval(function() {
    $scope.counter--;
      $scope.$apply();
      if($scope.counter === 0){
        clearInterval(countDown);
        $scope.countComplete = false;
        $('#waitingOverlay').css('display', 'none');
        codeMirror();
        startTime = Date.now();
        var matchId = $scope.room
        var matchRef = Race.dataRef.child('Matches/' + matchId);
        matchRef.update({'startTime': startTime});
        matchRef.update({'challengeId': challengeId});
      }
    }, 1000);
  }

  function codeMirror(){
    // codemirror options
    $scope.editorOptions = {
      mode: 'javascript',
      theme: '3024-night',
      lineNumbers: true,
      lineWrapping: true,
      showCursorWhenSelecting: true,
      autofocus: false,
      keyMap: 'sublime',
      autoCloseBrackets: true,
      tabSize: 2,
      extraKeys: {"Ctrl-Space": "autocomplete"},
      gutters: ['CodeMirror-lint-markers'],
      lint: true,
      scrollbarStyle: "null"
    };
    // codemirror autocomplete
    function autocomplete(){
      CodeMirror.showHint({hint: CodeMirror.hint.anyword});
    }
    $scope.$apply();
  }

  function getRandomArbitrary(min, max) {
    return Math.floor(Math.random() * (max - min) + min);
  }//generate a random number

//challengeInputs is set the in the outer scope so it can be used in $scope.evaluate.
  var challengeInputs;

  //when the data is loaded in the factory, load the data in the html and set the challenge inputs.
  $scope.$on('Race:ready', function (event, data) {
    $scope.code = data.startingCode;
    $scope.question = data.question;
    $scope.functionName = data.functionName;
    $scope.$apply(); //push the newly acquired data to the dom.

    //set challenge inputs to be used later by the worker.
    challengeInputs = {
      inputs: data.inputs,
      answers: data.answers,
      functionName: data.functionName
    };
  });

  $scope.evaluate = function(code) {

    //When the user firsts click the submit button, set all values to their defaults, in case they are submitting on a second or third try, the wrong test results wont show up.
    $scope.dataLoaded = false;
    $scope.error = undefined;
    $scope.tests = undefined;
    $scope.passed = undefined;
    $scope.responseText = undefined;

    var matchId = $scope.room

    var storeMatchResults = function(){
      Race.dataRef.child('Matches/' + matchId).update({
        winnerId: facebookId,
        endTime: Date.now(),
      })
    }

    var storeGhost = function(){
      var matchRef = Race.dataRef.child('Matches/' + matchId);
      var matchesRef = matchRef.parent();

      matchRef.once('value', function(snapshot){
        var matchObj = snapshot.val();

        var winnerId = matchObj.winnerId;
        var winnerTypingDataRef = matchRef.child('players/' + winnerId);
        winnerTypingDataRef.once('value', function(snapshot){
          var ghostData = snapshot.val();
          var ghostsRef = Race.dataRef.child('Challenges/' + challengeId + '/Ghosts');
          var ghostIdRef = ghostsRef.push();
          ghostIdRef.set({
            typingData: ghostData,
            gameDuration: matchObj.endTime - matchObj.startTime,
          });
        })
      })
    }

    //render the results from the worker in the dom.
    var renderCodeResponse = function(codeResponse) {
      $scope.dataLoaded = true; //set this to true to stop the spinner.
      $scope.validResponse = codeResponse.valid;
      $scope.error = codeResponse.error;
      $scope.tests = codeResponse.tests;
      $scope.passed = codeResponse.passed;
      $scope.responseText = codeResponse.passed ? "correct!" : "incorrect";
      if(codeResponse.passed){
        storeMatchResults();
        storeGhost();
        socket.emit('knockOut', {facebookId: facebookId});
      }

      $scope.$apply(); //apply the scope to the dom once the worker has responded with results.
    };

    //Execute the worker on the client side
    if (window.Worker) { //verify that the browser has worker capability.
      var evalWorker = new Worker("client/evalWorker.js");

      angular.extend(challengeInputs, {code: code}); //use the extend function add the input code string to the challenge inputs.

      //launch the worker with the challenge inputs.
      evalWorker.postMessage(challengeInputs);

      //used to test for timeouts 5 seconds after the worker starts.
      var workerComplete = false;

      //if the code input by the user is invalid, this worker will trigger.
      evalWorker.onerror = function(error) {
        console.log(error);
        evalWorker.terminate(); //kill the worker.
        workerComplete = true; //the worker is done.
        var codeResponse = { //the code is invalid. create response to send to the dom.
          valid: false,
          error: error.message,
          passed: false
        };
        renderCodeResponse(codeResponse); //render the dom with the correct code response.
      };

      evalWorker.onmessage = function(codeResponse) { //when the worker sends back its response, update the dom.
        if(codeResponse.data.passed === true){ //tell the opponent that this users code passed.
          socket.emit('passed');
        }
        workerComplete = true; //don't execute the timeout function.
        renderCodeResponse(codeResponse.data); //pass the codeResponse object to the dom to be rendered.
      }

      //check for worker timeout.
      setTimeout(function() {
        if (workerComplete === false){ //the worker has not completed after 5 seconds.
          evalWorker.terminate(); //terminate the worker.
          renderCodeResponse({
            valid: false,
            error: "Code is taking longer than 5 seconds to process",
            passed: false
          });
        }
      }, 5000);
    }
  }

  $scope.typing = function(code){
    socket.emit('typing', {
      code: code,
      facebookId: facebookId,
      startTime: startTime,
      challengeId: challengeId,
    });

    //How I call it when they are attacking
    //Bug is kakashi attacks twice

  };

  $scope.$on('$destroy', function(){
    socket.disconnect();
  });

  if(!socket.connected){
    socket.connect();
  }

  if(true){
    socket.emit('start', {
      username: facebookDisplayName.split(' ')[0],
      gameType: 'ghostMatch',
    })
  } else if(false){
    socket.emit('start', {
      username: facebookDisplayName.split(' ')[0],
      gameType: 'randomPlayerVsPlayer'
    });
  }

  socket.on('opponentLeft', function(){
    $scope.opponentLeft = true;
  });
  socket.on('typing', function(data) {
    $scope.competitorCode = data.code;
  })
  socket.on('roomJoined', function(matchData){
    $scope.room = matchData.matchId;
    timer();
    $scope.opponent = master ?
      matchData.player2: matchData.player1;
  });
  socket.on('passed', function(){
    $scope.opponentPassed = true;
  });
  socket.on('master', function(){
    master = true;
    Race.getLength();
    $scope.$on('GotLength', function(event,data){
      var challengesObject;
      Race.dataRef.child('Challenges').on('value', function(snapshot){
        challengesObject = snapshot.val();
      });

      challengeIdsArray = Object.keys(challengesObject);
      var randomIndex = getRandomArbitrary(0, challengeIdsArray.length);

      Race.getData(challengeIdsArray[randomIndex], function(problem){
        challengeId = challengeIdsArray[randomIndex];
        problem.challengeId = challengeId
        socket.emit('problem', problem);
      });
    });
  });
  socket.on('problem', function(problem){
    challengeId = problem.challengeId;
    setTimeout(function(){
      Race.setProblem(problem);
    }, 0, problem);
  });

  //Everything below here is for the animation
  var stance = {
    'kakashi' : ['stance/Position1.png', 'stance/Position2.png', 'stance/Position3.png', 'stance/Position4.png', 'stance/Position5.png',  'stance/Position6.png'],
    'naruto' : ['stance/Position1.png', 'stance/Position2.png', 'stance/Position3.png', 'stance/Position4.png', 'stance/Position5.png', 'stance/Position6.png']
  }

  var attack = {
    'kakashi' : ['attacks/attack1/Position1.png', 'attacks/attack1/Position2.png', 'attacks/attack1/Position3.png', 'attacks/attack1/Position4.png', 'attacks/attack1/Position5.png', 'attacks/attack1/Position6.png', 'attacks/attack1/Position7.png', 'attacks/attack1/Position8.png', 'attacks/attack1/Position9.png', 'attacks/attack1/Position10.png', 'attacks/attack1/Position11.png', 'attacks/attack1/Position12.png', 'attacks/attack1/Position13.png'],
    'naruto' : ['attacks/attack1/Position1.png', 'attacks/attack1/Position2.png', 'attacks/attack1/Position3.png', 'attacks/attack1/Position4.png', 'attacks/attack1/Position5.png', 'attacks/attack1/Position6.png', 'attacks/attack1/Position7.png', 'attacks/attack1/Position8.png', 'attacks/attack1/Position9.png', 'attacks/attack1/Position10.png', 'attacks/attack1/Position11.png']
  }

  var img = document.getElementsByTagName('IMG');
  var index = 0;
  var index2 = 0;
  var attacking = false;
  var running = false;
  var avatarPath = "img/kakashi/";
  var avatarPath2 = "img/naruto/"

  var thread, thread2;

  socket.on('animate', function(data){

    if(data.moveType === 'normalAttack'){
      if(data.facebookId === facebookId){
        switchImg('attack', 'naruto');
      } else{
        switchImg('attack', 'kakashi');
      }
    }
    else if(data.moveType === 'knockOut'){
      if(data.facebookId === facebookId){
        // call KO & Death Function
      } else{
        // call KO & Death Function
      }
    }

    var elem = img[2];
    var elem2 = img[3];

    function kakashiStanceImg(){
      index++;

      if (index >= stance.kakashi.length) index = 0;

      elem.src = avatarPath + stance.kakashi[index];
    }

    function narutoStanceImg(){
      index2++;

      if (index2 >= stance.naruto.length) index2 = 0;

      elem2.src = avatarPath2 + stance.naruto[index2];
    }

    function kakashiAttackImg(){
      index++;

      if (index >= attack.kakashi.length) {
        switchImg('stance', 'kakashi')
      }
      else elem.src = avatarPath + attack.kakashi[index];
    }

    function narutoAttackImg(){
      index2++;

      if (index2 >= attack.naruto.length) {
        switchImg('stance', 'naruto');
      }
      else elem2.src = avatarPath2 + attack.naruto[index];
    }

    function switchImg(animation, avatar, duration, attacking, running){
      duration = duration || 100;
      index = -1;
      index2 = -1;
      attacking = attacking || false;
      running = running || false;

      if (avatar === 'kakashi') {
        clearInterval(thread);

        if (animation === 'stance') thread = setInterval(kakashiStanceImg, duration);
        else if (animation === 'attack') thread = setInterval(kakashiAttackImg, duration);
      }
      else if (avatar === 'naruto') {
        clearInterval(thread2);

        if (animation === 'stance') thread2 = setInterval(narutoStanceImg, duration);
        else if (animation === 'attack') thread2 = setInterval(narutoAttackImg, duration);
      }
    }

    switchImg(data.action[1], data.action[0]);

    //Call the animation like this:
    //switchImg(animationStrin, data[character]);
  })
});

<|endoftext|>