<commit_before>//= require codemirror
//= require codemirror/modes/ruby
//= require codemirror/modes/sass
//= require codemirror/modes/shell
//= require codemirror/modes/sql
//= require codemirror/modes/slim
//= require codemirror/modes/nginx
//= require codemirror/modes/markdown
//= require codemirror/modes/javascript
//= require codemirror/modes/http
//= require codemirror/modes/htmlmixed
//= require codemirror/modes/haml
//= require codemirror/modes/xml
//= require codemirror/modes/css
//= require codemirror/modes/yaml
//= require codemirror/modes/slim
//= require codemirror/modes/php
//= require summernote

// Gives us a capitalize method
String.prototype.capitalize = function() {
  return this.charAt(0).toUpperCase() + this.slice(1);
};

var Helpy = Helpy || {};
Helpy.admin = function(){

  $(".alert").delay(2000).slideUp(500, function(){
      $(".alert").alert('close');
  });

  $('div.sortable').sortable({
    items: '.item',
    axis: 'y',
    cursor: 'move',
    sort: function(event, ui) {
      ui.item.addClass('active-item-shadow');
    },
    stop: function(event, ui) {
      ui.item.removeClass('active-item-shadow');
      ui.item.effect('highlight');
    },
    update: function(event, ui) {
      var obj = ui.item.data('obj');
      var obj_id = ui.item.data('obj-id');
      var position = ui.item.index();
      $.ajax({
        type: 'POST',
        url: '/admin/shared/update_order',
        dataType: 'json',
        data: {object: obj, obj_id: obj_id, row_order_position: position }
      });
    }
  });

  // $('.settings-link').off().on('click', function(){
  //   // Clean up any select-styled links
  //   $('.settings-link').removeClass('active-settings-link');
  //
  //   // Hide and show the grid/panels
  //   $('.settings-grid').addClass('hidden');
  //   $('.settings-panel').removeClass('hidden');
  //
  //   var $this = $(this);
  //   var showthis = $this.data('target');
  //   $('a[data-target=' + showthis + ']').addClass('active-settings-link');
  //   $('.settings-section').addClass('hidden');
  //   $('.settings-section.' + showthis).removeClass('hidden');
  //   $('.agent-header').addClass('hidden');
  //   $('h2#setting-header').text('Settings: ' + $this.text().capitalize());
  //   return false;
  // });

  // You have to delegate this to the document or it does not work reliably
  // See http://stackoverflow.com/questions/18545941/jquery-on-submit-event
  $(document).off('submit','form.new-group-form').on('submit','form.new-group-form', function(){
      var $field = $('#new_group');
      var newGroup = $field.val();
      var $newItem = $('
');
      var $newLink = $('

' + newGroup + '

');
      $('.new-tag').before($newItem.append($newLink));
      $('#new_group').val('');
      $('.link-tag').off().click();
      return false;
  });

  $('.pick-a-color').minicolors({
    theme: 'bootstrap'
  });

  // $('.reports-menu-toggle').off().on('click', function(){
  //   var $reports_nav = $('.reports-nav');
  //   if ($reports_nav.is(":visible")) {
  //     $reports_nav.addClass('hidden-xs').addClass('hidden-sm');
  //   } else {
  //     $reports_nav.removeClass('hidden-xs').removeClass('hidden-sm');
  //   }
  //
  // });

  $('.bs-toggle').bootstrapSwitch();

  // Onboarding flow
  $('.panel-link').off().on('click', function(){
    $('.onboard-panel').addClass('hidden');
    $('#panel-' + $(this).data('panel')).removeClass('hidden');
    $('li.step-' + ($(this).data('panel')-1)).html("").addClass('filled-circle');
    $('li.step-' + $(this).data('panel')).addClass('active-step');
  });

  $('input.send-email').off().on('change', function(){
    var chosen = $("input.send-email:radio:checked").val();
    if (chosen === 'true') {
      $('.smtp-settings').removeClass('hidden');
    } else {
      $('.smtp-settings').addClass('hidden');
    }
  });

  $('.settings-section.email select').off().on('change', function(){
    var chosen = $(".settings-section.email select").val();
      $('.imap-settings').addClass('hidden');
      $('.pop3-settings').addClass('hidden');
    if (chosen == 'pop3' ){
      $('.pop3-settings').removeClass('hidden');
    }
    if (chosen == 'imap' ){
      $('.imap-settings').removeClass('hidden');
    }
  });

  $("#new_doc select, #edit_doc select").focusout(function(){
    if($(this).val() === ""){
      $("div.select").removeClass("has-success").addClass("has-error");
      $("select").next().removeClass("glyphicon-ok").addClass("glyphicon-remove");
      $("div.select .glyphicon-ok").remove();
      $("can't be blank").insertAfter("div.select .glyphicon-remove");
      $('input[type="submit"]').prop('disabled', true);
    }
    else{
     $('input[type="submit"]').prop('disabled', false);
    }
  });

  $('.settings-uploader').off().on('change', function(){
    var $this = $(this);
    var f = $this.val().split("\\");
    var filename = f[f.length-1];
    $('.hidden-header-logo').val("/uploads/logos/" + filename);
  });

  $('.favicon-uploader').off().on('change', function(){
    var $this = $(this);
    var f = $this.val().split("\\");
    var filename = f[f.length-1];
    $('.hidden-favicon').val("/uploads/logos/" + filename);
  });

  // Start logging history from first pageload
  // note: breaks back button so removed
  // Helpy.logHistory();

  $('.input-group.date').datepicker({
    format: "yyyy-mm-dd",
    orientation: "auto left",
    autoclose: true,
    todayHighlight: true
  });

  Helpy.initShortcuts();

  $('.change-user-modal').on('shown.bs.modal', function() {
    $('#user_search').focus();
  });

  // Highlight the last clicked view
  $('.nav-item').off().on('click', function(){
    var $this = $(this);
    $('.nav-item').removeClass('nav-active');
    $this.addClass('nav-active');
  });

  // Highlight the last clicked view
  $('.nav-item').on('mouseover', function(){
    var $this = $(this);
    $this.addClass('nav-over');
  });
  // Highlight the last clicked view
  $('.nav-item').on('mouseout', function(){
    var $this = $(this);
    $this.removeClass('nav-over');
  });

  Helpy.ticketMenu();

};

Helpy.ticketMenu = function() {
  // Show/hide ticket menu
  $('.show-ticket-menu').on('click', function(){
    var $ticketNav = $('#admin-left-nav');

    if ($ticketNav.hasClass('open')) {
      $ticketNav.removeClass('open').addClass('hidden-xs').addClass('hidden-sm').removeClass('left-dropdown');
    } else {
      $ticketNav.addClass('open');
      $ticketNav.removeClass('hidden-xs').removeClass('hidden-sm').addClass('left-dropdown');
    }
  });

  $('.show-ticket-menu.open').on('click', function(){
    var $ticketNav = $('#admin-left-nav');

    $ticketNav.removeClass('open');
    $ticketNav.addClass('hidden-xs');
  });
};

Helpy.showPanel = function(panel) {
  var currentPanel = panel-1;
  $('.onboard-panel').addClass('hidden');
  $('#panel-' + panel).removeClass('hidden');
  $('li.step-' + currentPanel).html("").addClass('filled-circle');
  $('li.step-' + panel).addClass('active-step');
  return true;
};

window.closeModal = function() {
  $('#modal').modal('hide');
};

Helpy.showGrid = function() {
  // Clean up any select-styled links
  $('.settings-link').removeClass('active-settings-link');

  // Hide and show the grid/panels
  $('.settings-grid').removeClass('hidden');
  $('.settings-panel').addClass('hidden');

  $('h2#setting-header').text('Settings');

};

// Enables correct URL in browser, history
Helpy.logHistory = function() {
  $('a').on('click', function(){
    var url = $(this).attr('href');
    console.log("Clicked: " + url);
    history.pushState(null, '', url);
  });

  $(window).off().on("popstate", function(){
    console.log("Popstate fired: " + location.href);
    $.getScript(location.href);
  });
};

var search_user = function(post_id) {
  $('#change-user-modal-' + post_id + ' .search_form').submit();
};

$(document).on('page:change', Helpy.admin);
<commit_msg>Closes #842<commit_after>//= require codemirror
//= require codemirror/modes/ruby
//= require codemirror/modes/sass
//= require codemirror/modes/shell
//= require codemirror/modes/sql
//= require codemirror/modes/slim
//= require codemirror/modes/nginx
//= require codemirror/modes/markdown
//= require codemirror/modes/javascript
//= require codemirror/modes/http
//= require codemirror/modes/htmlmixed
//= require codemirror/modes/haml
//= require codemirror/modes/xml
//= require codemirror/modes/css
//= require codemirror/modes/yaml
//= require codemirror/modes/slim
//= require codemirror/modes/php
//= require summernote

// Gives us a capitalize method
String.prototype.capitalize = function() {
  return this.charAt(0).toUpperCase() + this.slice(1);
};

var Helpy = Helpy || {};
Helpy.admin = function(){

  $(".alert").delay(2000).slideUp(500, function(){
      $(".alert").alert('close');
  });

  $('div.sortable').sortable({
    items: '.item',
    axis: 'y',
    cursor: 'move',
    sort: function(event, ui) {
      ui.item.addClass('active-item-shadow');
    },
    stop: function(event, ui) {
      ui.item.removeClass('active-item-shadow');
      ui.item.effect('highlight');
    },
    update: function(event, ui) {
      var obj = ui.item.data('obj');
      var obj_id = ui.item.data('obj-id');
      var position = ui.item.index();
      $.ajax({
        type: 'POST',
        url: '/admin/shared/update_order',
        dataType: 'json',
        data: {object: obj, obj_id: obj_id, row_order_position: position }
      });
    }
  });

  // $('.settings-link').off().on('click', function(){
  //   // Clean up any select-styled links
  //   $('.settings-link').removeClass('active-settings-link');
  //
  //   // Hide and show the grid/panels
  //   $('.settings-grid').addClass('hidden');
  //   $('.settings-panel').removeClass('hidden');
  //
  //   var $this = $(this);
  //   var showthis = $this.data('target');
  //   $('a[data-target=' + showthis + ']').addClass('active-settings-link');
  //   $('.settings-section').addClass('hidden');
  //   $('.settings-section.' + showthis).removeClass('hidden');
  //   $('.agent-header').addClass('hidden');
  //   $('h2#setting-header').text('Settings: ' + $this.text().capitalize());
  //   return false;
  // });

  // You have to delegate this to the document or it does not work reliably
  // See http://stackoverflow.com/questions/18545941/jquery-on-submit-event
  $(document).off('submit','form.new-group-form').on('submit','form.new-group-form', function(){
      var $field = $('#new_group');
      var newGroup = $field.val();
      var $newItem = $('
');
      var $newLink = $('

' + newGroup + '

');
      $('.new-tag').before($newItem.append($newLink));
      $('#new_group').val('');
      $('.link-tag').off().click();
      return false;
  });

  $('.pick-a-color').minicolors({
    theme: 'bootstrap'
  });

  // $('.reports-menu-toggle').off().on('click', function(){
  //   var $reports_nav = $('.reports-nav');
  //   if ($reports_nav.is(":visible")) {
  //     $reports_nav.addClass('hidden-xs').addClass('hidden-sm');
  //   } else {
  //     $reports_nav.removeClass('hidden-xs').removeClass('hidden-sm');
  //   }
  //
  // });

  $('.bs-toggle').bootstrapSwitch({
    onText: '    ',
    offText: '    '
  });

  // Onboarding flow
  $('.panel-link').off().on('click', function(){
    $('.onboard-panel').addClass('hidden');
    $('#panel-' + $(this).data('panel')).removeClass('hidden');
    $('li.step-' + ($(this).data('panel')-1)).html("").addClass('filled-circle');
    $('li.step-' + $(this).data('panel')).addClass('active-step');
  });

  $('input.send-email').off().on('change', function(){
    var chosen = $("input.send-email:radio:checked").val();
    if (chosen === 'true') {
      $('.smtp-settings').removeClass('hidden');
    } else {
      $('.smtp-settings').addClass('hidden');
    }
  });

  $('.settings-section.email select').off().on('change', function(){
    var chosen = $(".settings-section.email select").val();
      $('.imap-settings').addClass('hidden');
      $('.pop3-settings').addClass('hidden');
    if (chosen == 'pop3' ){
      $('.pop3-settings').removeClass('hidden');
    }
    if (chosen == 'imap' ){
      $('.imap-settings').removeClass('hidden');
    }
  });

  $("#new_doc select, #edit_doc select").focusout(function(){
    if($(this).val() === ""){
      $("div.select").removeClass("has-success").addClass("has-error");
      $("select").next().removeClass("glyphicon-ok").addClass("glyphicon-remove");
      $("div.select .glyphicon-ok").remove();
      $("can't be blank").insertAfter("div.select .glyphicon-remove");
      $('input[type="submit"]').prop('disabled', true);
    }
    else{
     $('input[type="submit"]').prop('disabled', false);
    }
  });

  $('.settings-uploader').off().on('change', function(){
    var $this = $(this);
    var f = $this.val().split("\\");
    var filename = f[f.length-1];
    $('.hidden-header-logo').val("/uploads/logos/" + filename);
  });

  $('.favicon-uploader').off().on('change', function(){
    var $this = $(this);
    var f = $this.val().split("\\");
    var filename = f[f.length-1];
    $('.hidden-favicon').val("/uploads/logos/" + filename);
  });

  // Start logging history from first pageload
  // note: breaks back button so removed
  // Helpy.logHistory();

  $('.input-group.date').datepicker({
    format: "yyyy-mm-dd",
    orientation: "auto left",
    autoclose: true,
    todayHighlight: true
  });

  Helpy.initShortcuts();

  $('.change-user-modal').on('shown.bs.modal', function() {
    $('#user_search').focus();
  });

  // Highlight the last clicked view
  $('.nav-item').off().on('click', function(){
    var $this = $(this);
    $('.nav-item').removeClass('nav-active');
    $this.addClass('nav-active');
  });

  // Highlight the last clicked view
  $('.nav-item').on('mouseover', function(){
    var $this = $(this);
    $this.addClass('nav-over');
  });
  // Highlight the last clicked view
  $('.nav-item').on('mouseout', function(){
    var $this = $(this);
    $this.removeClass('nav-over');
  });

  Helpy.ticketMenu();

};

Helpy.ticketMenu = function() {
  // Show/hide ticket menu
  $('.show-ticket-menu').on('click', function(){
    var $ticketNav = $('#admin-left-nav');

    if ($ticketNav.hasClass('open')) {
      $ticketNav.removeClass('open').addClass('hidden-xs').addClass('hidden-sm').removeClass('left-dropdown');
    } else {
      $ticketNav.addClass('open');
      $ticketNav.removeClass('hidden-xs').removeClass('hidden-sm').addClass('left-dropdown');
    }
  });

  $('.show-ticket-menu.open').on('click', function(){
    var $ticketNav = $('#admin-left-nav');

    $ticketNav.removeClass('open');
    $ticketNav.addClass('hidden-xs');
  });
};

Helpy.showPanel = function(panel) {
  var currentPanel = panel-1;
  $('.onboard-panel').addClass('hidden');
  $('#panel-' + panel).removeClass('hidden');
  $('li.step-' + currentPanel).html("").addClass('filled-circle');
  $('li.step-' + panel).addClass('active-step');
  return true;
};

window.closeModal = function() {
  $('#modal').modal('hide');
};

Helpy.showGrid = function() {
  // Clean up any select-styled links
  $('.settings-link').removeClass('active-settings-link');

  // Hide and show the grid/panels
  $('.settings-grid').removeClass('hidden');
  $('.settings-panel').addClass('hidden');

  $('h2#setting-header').text('Settings');

};

// Enables correct URL in browser, history
Helpy.logHistory = function() {
  $('a').on('click', function(){
    var url = $(this).attr('href');
    console.log("Clicked: " + url);
    history.pushState(null, '', url);
  });

  $(window).off().on("popstate", function(){
    console.log("Popstate fired: " + location.href);
    $.getScript(location.href);
  });
};

var search_user = function(post_id) {
  $('#change-user-modal-' + post_id + ' .search_form').submit();
};

$(document).on('page:change', Helpy.admin);
<|endoftext|>


<commit_before>/* eslint-env mocha */

import * as conversion from '../../src/conversion'

describe('conversion', function () {
  describe('extractDirAndName', () =>
    it('returns the remote path and name', function () {
      let [path, name] = conversion.extractDirAndName('foo')
      path.should.equal('/')
      name.should.equal('foo');
      [path, name] = conversion.extractDirAndName('foo/bar')
      path.should.equal('/foo')
      name.should.equal('bar');
      [path, name] = conversion.extractDirAndName('foo/bar/baz')
      path.should.equal('/foo/bar')
      name.should.equal('baz')
    })
  )
})
<commit_msg>conversion: Prefer should(obj) to obj.should()<commit_after>/* @flow */
/* eslint-env mocha */

import should from 'should'

import * as conversion from '../../src/conversion'

describe('conversion', function () {
  describe('extractDirAndName', () =>
    it('returns the remote path and name', function () {
      let [path, name] = conversion.extractDirAndName('foo')
      should(path).equal('/')
      should(name).equal('foo');
      [path, name] = conversion.extractDirAndName('foo/bar')
      should(path).equal('/foo')
      should(name).equal('bar');
      [path, name] = conversion.extractDirAndName('foo/bar/baz')
      should(path).equal('/foo/bar')
      should(name).equal('baz')
    })
  )
})
<|endoftext|>


<commit_before>(function(){
  "use strict";
  var POLLING_INTERVAL = 3 * 1000;
  var POLLING_URL = "/polling/alerts";

  $(function(){
    var alert = new Vue({
      el: "#alert",
      data: {
        "alerts": []
      },

      created: function(){
        var self = this;
        var fetch = function(){
          self.fetchAlertsData().then(function(alerts){
            self.alerts = alerts;
          })["catch"](function(xhr){
            if(xhr.status === 401) {
              clearInterval(timer);
            }
          });
        };
        fetch();
        var timer = setInterval(fetch, POLLING_INTERVAL);
      },

      computed: {
        alertsCount: {
          $get: function(){ return this.alerts.length; }
        },
        hasAlerts: {
          $get: function(){ return this.alertsCount > 0; }
        }
      },

      methods: {
        fetchAlertsData: function() {
          return new Promise(function(resolve, reject) {
            $.getJSON(POLLING_URL, resolve).fail(reject);
          });
        }
      }
    });
  });
})();
<commit_msg>Stop polling if fluentd-ui server is unreachable<commit_after>(function(){
  "use strict";
  var POLLING_INTERVAL = 3 * 1000;
  var POLLING_URL = "/polling/alerts";

  $(function(){
    var alert = new Vue({
      el: "#alert",
      data: {
        "alerts": []
      },

      created: function(){
        var self = this;
        var fetch = function(){
          self.fetchAlertsData().then(function(alerts){
            self.alerts = alerts;
          })["catch"](function(xhr){
            if(xhr.status === 401) {
              clearInterval(timer); // signed out
            }
            if(xhr.status === 0) {
              clearInterval(timer); // server unreachable (maybe down)
            }
          });
        };
        fetch();
        var timer = setInterval(fetch, POLLING_INTERVAL);
      },

      computed: {
        alertsCount: {
          $get: function(){ return this.alerts.length; }
        },
        hasAlerts: {
          $get: function(){ return this.alertsCount > 0; }
        }
      },

      methods: {
        fetchAlertsData: function() {
          return new Promise(function(resolve, reject) {
            $.getJSON(POLLING_URL, resolve).fail(reject);
          });
        }
      }
    });
  });
})();
<|endoftext|>


<commit_before>const expect = require('chai').expect;
const sinon = require('sinon');
const del = require('del');
const path = require('path');

const CleanObsoleteChunks = require('./../../index');

describe('CleanObsoleteChunks', () => {
  describe('instance', () => {
    let inst;

    beforeEach(() => {
      inst = new CleanObsoleteChunks();
    });

    describe(`method _saveChunkConfig(chunk)`, () => {
      describe('in order to save chunks versions', () => {
        it(`SHOULD add or update its deep chunkVersions[chunk.name] property`, () => {
          let chunkName = 'testChunkName';
          expect(inst.chunkVersions).to.not.have.property(chunkName);
          let chunk = {
            name: chunkName,
            files: ['test-file-name1'],
            hash: 'hash1'
          };
          inst._saveChunkConfig(chunk);
          expect(inst.chunkVersions).to.have.property(chunk.name);
          let oldValue = inst.chunkVersions[chunk.name];
          let updatedChunk = Object.assign(chunk, {files: ['test-file-name2'], hash: 'hash2'});
          inst._saveChunkConfig(updatedChunk);
          expect(oldValue).to.not.be.equal(inst.chunkVersions[updatedChunk.name]);
        });

        it(
          `SHOULD set its deep chunkVersions[chunk.name]['hash'] property with chunk.hash value`,
          () => {
            let testHash = '7df8ue0ucsd0asdwd20';
            let chunk = {
              name: 'test',
              files: ['test'],
              hash: testHash
            };
            inst._saveChunkConfig(chunk);
            expect(inst.chunkVersions[chunk.name]['hash']).to.be.equal(testHash);
          }
        );

        it(
          `SHOULD set its deep chunkVersions[chunk.name]['files'] property with chunk.files value`,
          () => {
            let files = ['file1', 'file2', 'file3'];
            let chunk = {
              name: 'test',
              files: files,
              hash: 'hash'
            };
            inst._saveChunkConfig(chunk);
            expect(inst.chunkVersions[chunk.name]['files']).to.be.equal(files);
          }
        );
      });

    });

    describe(`method _getObsoleteFiles(compilation)`, () => {
      describe(`in order to get obsolete chunk files`, () => {
        it(`SHOULD call _getChunkObsoleteFiles(chunk) for each chunk in compilation.chunks`,
          () => {
            let compilation = {
              chunks: ['chunk1', 'chunk2', 'chunk3']
            };
            let _getChunkObsoleteFiles = sinon.stub(inst, '_getChunkObsoleteFiles');
            expect(_getChunkObsoleteFiles.notCalled).to.be.true;
            inst._getObsoleteFiles(compilation);
            expect(_getChunkObsoleteFiles.callCount).to.be.equal(3);
            expect(_getChunkObsoleteFiles.args[0][0]).to.be.equal(compilation.chunks[0]);
            expect(_getChunkObsoleteFiles.args[1][0]).to.be.equal(compilation.chunks[1]);
            expect(_getChunkObsoleteFiles.args[2][0]).to.be.equal(compilation.chunks[2]);
          });

        it(`SHOULD return concatenated array of obsolete files of all of the chunks (if any)`,
          () => {
            let compilation = {
              chunks: ['chunk1', 'chunk2', 'chunk3']
            };
            let _getChunkObsoleteFiles = sinon.stub(inst, '_getChunkObsoleteFiles');
            _getChunkObsoleteFiles.onFirstCall().returns(['file1', 'file2']);
            _getChunkObsoleteFiles.onSecondCall().returns(['file3', 'file4', 'file5']);
            _getChunkObsoleteFiles.onThirdCall().returns(['file6']);
            expect(inst._getObsoleteFiles(compilation)).to.be.deep.equal(
              ['file1', 'file2', 'file3', 'file4', 'file5', 'file6']
            );
          });

        it(`SHOULD return empty array if there are no chunks`, () => {
          let compilation = {
            chunks: []
          };
          expect(inst._getObsoleteFiles(compilation)).to.be.deep.equal([]);
        });

        it(`SHOULD return empty array if there are no obsolete files in all of the chunks`, () => {
          let compilation = {
            chunks: ['chunk1', 'chunk2', 'chunk3']
          };
          let _getChunkObsoleteFiles = sinon.stub(inst, '_getChunkObsoleteFiles').returns([]);
          expect(inst._getObsoleteFiles(compilation)).to.be.deep.equal([]);
        });

      });
    });

    describe(`method _getChunkObsoleteFiles(chunk)`, () => {
      describe(`in order to get obsolete chunk files`, () => {
        it(`SHOULD return empty array if there is no chunkVersions[chunk.name]`, () => {
          let chunk = {
            name: 'test'
          };
          expect(inst._getChunkObsoleteFiles(chunk)).to.be.deep.equal([]);
        });

        it(`SHOULD return empty array if chunk hasn't been changed`, () => {
          let oldChunk = {
            name: 'test',
            hash: 'abcde'
          };
          inst.chunkVersions[oldChunk.name] = oldChunk;
          let newChunk = Object.assign({}, oldChunk);
          expect(inst._getChunkObsoleteFiles(newChunk)).to.be.deep.equal([]);
        });

        it(`SHOULD return only obsolete files if chunk has been changed`, () => {
          let oldChunk = {
            name: 'test',
            hash: 'hash1',
            files: ['file1(old-name)', 'file2(old-name)', 'file3(old-name)']
          };
          inst.chunkVersions[oldChunk.name] = {
            hash: oldChunk.hash,
            files: oldChunk.files
          };
          let newChunk = Object.assign(oldChunk, {
            hash: 'hash2',
            files: ['file1(old-name)', 'file2(old-name)', 'file3(new-name)']
          });
          expect(inst._getChunkObsoleteFiles(newChunk)).to.be.deep.equal(['file3(old-name)']);
        });

        it(`SHOULD call _saveChunkConfig(chunk) method in any case`,
          () => {
            let chunk = {
              name: 'test',
              hash: 'abcde'
            };
            let _saveChunkConfig = sinon.stub(inst, '_saveChunkConfig');

            // 1) if there is no chunkVersions[chunk.name]
            expect(_saveChunkConfig.called).to.be.false;
            inst._getChunkObsoleteFiles(chunk);
            expect(_saveChunkConfig.callCount).to.be.equal(1);
            expect(_saveChunkConfig.args[0][0]).to.be.deep.equal(chunk);
            _saveChunkConfig.reset();

            // 2) otherwise
            inst.chunkVersions[chunk.name] = chunk;
            expect(_saveChunkConfig.called).to.be.false;
            inst._getChunkObsoleteFiles(chunk);
            expect(_saveChunkConfig.callCount).to.be.equal(1);
            expect(_saveChunkConfig.args[0][0]).to.be.deep.equal(chunk);
          });

      });
    });

    describe(`method apply(compiler)`, () => {
      describe('in order to remove obsolete chunks files in webpack watch mode', () => {
        it(
          `SHOULD get hooked on compiler 'after-emit' event and pass _removeObsoleteFiles method
          as a callback`,
          () => {
            let compiler = {
              plugin: sinon.stub()
            };
            sinon.stub(inst._removeObsoleteFiles, 'bind').returns(inst._removeObsoleteFiles);
            inst.apply(compiler);
            expect(compiler.plugin.callCount).to.be.equal(1);
            expect(compiler.plugin.args[0][0]).to.be.equal('after-emit');
            expect(compiler.plugin.args[0][1]).to.be.equal(inst._removeObsoleteFiles);
            expect(inst._removeObsoleteFiles.bind.called).to.be.true;
            expect(inst._removeObsoleteFiles.bind.args[0][0]).to.be.equal(inst);
            expect(inst._removeObsoleteFiles.bind.args[0][1]).to.be.equal(compiler);
          });
      });
    });

    describe(`method _removeObsoleteFiles(compiler, compilation, done)`, () => {
      describe(`in order to remove obsolete chunks files in webpack watch mode`, () => {
        let done;
        let compilation;
        let compiler;
        let _getObsoleteFiles;
        beforeEach(() => {
          compilation = Math.random();
          _getObsoleteFiles = sinon.stub(inst, '_getObsoleteFiles').returns([]);
          done = sinon.stub();
          sinon.stub(del, 'sync');
        });

        afterEach(() => {
          del.sync.restore();
        });

        it(`SHOULD call _getObsoleteFiles(compilation) method`, () => {
          expect(_getObsoleteFiles.notCalled).to.be.true;
          inst._removeObsoleteFiles(compiler, compilation, done);
          expect(_getObsoleteFiles.callCount).to.be.equal(1);
          expect(_getObsoleteFiles.args[0][0]).to.be.equal(compilation);
        });

        it(`SHOULD call del.sync(filePath) for each obsolete file`, () => {
          compiler = {
            outputPath: 'absolute/path/to/output/folder/'
          };
          let obsoleteFiles = ['obsolete-file1', 'obsolete-file2', 'obsolete-file3'];
          _getObsoleteFiles.returns(obsoleteFiles);
          expect(del.sync.notCalled).to.be.true;
          inst._removeObsoleteFiles(compiler, compilation, done);
          expect(del.sync.callCount).to.be.equal(obsoleteFiles.length);
          expect(del.sync.args[0][0]).to.be.equal(path.join(compiler.outputPath, obsoleteFiles[0]));
          expect(del.sync.args[1][0]).to.be.equal(path.join(compiler.outputPath, obsoleteFiles[1]));
          expect(del.sync.args[2][0]).to.be.equal(path.join(compiler.outputPath, obsoleteFiles[2]));
        });

        it(`SHOULD call done() callback at the end`, () => {
          expect(done.notCalled).to.be.true;
          inst._removeObsoleteFiles(compiler, compilation, done);
          expect(done.callCount).to.be.equal(1);
        });
      });
    });

  });
});<commit_msg>code style edits<commit_after>const expect = require('chai').expect;
const sinon = require('sinon');
const del = require('del');
const path = require('path');

const CleanObsoleteChunks = require('./../../index');

describe('CleanObsoleteChunks', () => {
  describe('instance', () => {
    let inst;

    beforeEach(() => {
      inst = new CleanObsoleteChunks();
    });

    describe(`method _saveChunkConfig(chunk)`, () => {
      describe('in order to save chunks versions', () => {
        it(`SHOULD add or update its deep chunkVersions[chunk.name] property`, () => {
          let chunkName = 'testChunkName';
          expect(inst.chunkVersions).to.not.have.property(chunkName);
          let chunk = {
            name: chunkName,
            files: ['test-file-name1'],
            hash: 'hash1'
          };
          inst._saveChunkConfig(chunk);
          expect(inst.chunkVersions).to.have.property(chunk.name);
          let oldValue = inst.chunkVersions[chunk.name];
          let updatedChunk = Object.assign(chunk, {files: ['test-file-name2'], hash: 'hash2'});
          inst._saveChunkConfig(updatedChunk);
          expect(oldValue).to.not.be.equal(inst.chunkVersions[updatedChunk.name]);
        });

        it(
          `SHOULD set its deep chunkVersions[chunk.name]['hash'] property with chunk.hash value`,
          () => {
            let testHash = '7df8ue0ucsd0asdwd20';
            let chunk = {
              name: 'test',
              files: ['test'],
              hash: testHash
            };
            inst._saveChunkConfig(chunk);
            expect(inst.chunkVersions[chunk.name]['hash']).to.be.equal(testHash);
          }
        );

        it(
          `SHOULD set its deep chunkVersions[chunk.name]['files'] property with chunk.files value`,
          () => {
            let files = ['file1', 'file2', 'file3'];
            let chunk = {
              name: 'test',
              files: files,
              hash: 'hash'
            };
            inst._saveChunkConfig(chunk);
            expect(inst.chunkVersions[chunk.name]['files']).to.be.equal(files);
          }
        );
      });

    });

    describe(`method _getObsoleteFiles(compilation)`, () => {
      describe(`in order to get obsolete chunk files`, () => {
        it(`SHOULD call _getChunkObsoleteFiles(chunk) for each chunk in compilation.chunks`,
          () => {
            let compilation = {
              chunks: ['chunk1', 'chunk2', 'chunk3']
            };
            let _getChunkObsoleteFiles = sinon.stub(inst, '_getChunkObsoleteFiles');
            expect(_getChunkObsoleteFiles.notCalled).to.be.true;
            inst._getObsoleteFiles(compilation);
            expect(_getChunkObsoleteFiles.callCount).to.be.equal(3);
            expect(_getChunkObsoleteFiles.args[0][0]).to.be.equal(compilation.chunks[0]);
            expect(_getChunkObsoleteFiles.args[1][0]).to.be.equal(compilation.chunks[1]);
            expect(_getChunkObsoleteFiles.args[2][0]).to.be.equal(compilation.chunks[2]);
          });

        it(`SHOULD return concatenated array of obsolete files of all of the chunks (if any)`,
          () => {
            let compilation = {
              chunks: ['chunk1', 'chunk2', 'chunk3']
            };
            let _getChunkObsoleteFiles = sinon.stub(inst, '_getChunkObsoleteFiles');
            _getChunkObsoleteFiles.onFirstCall().returns(['file1', 'file2']);
            _getChunkObsoleteFiles.onSecondCall().returns(['file3', 'file4', 'file5']);
            _getChunkObsoleteFiles.onThirdCall().returns(['file6']);
            expect(inst._getObsoleteFiles(compilation)).to.be.deep.equal(
              ['file1', 'file2', 'file3', 'file4', 'file5', 'file6']
            );
          });

        it(`SHOULD return empty array if there are no chunks`, () => {
          let compilation = {
            chunks: []
          };
          expect(inst._getObsoleteFiles(compilation)).to.be.deep.equal([]);
        });

        it(`SHOULD return empty array if there are no obsolete files in all of the chunks`, () => {
          let compilation = {
            chunks: ['chunk1', 'chunk2', 'chunk3']
          };
          sinon.stub(inst, '_getChunkObsoleteFiles').returns([]);
          expect(inst._getObsoleteFiles(compilation)).to.be.deep.equal([]);
        });

      });
    });

    describe(`method _getChunkObsoleteFiles(chunk)`, () => {
      describe(`in order to get obsolete chunk files`, () => {
        it(`SHOULD return empty array if there is no chunkVersions[chunk.name]`, () => {
          let chunk = {
            name: 'test'
          };
          expect(inst._getChunkObsoleteFiles(chunk)).to.be.deep.equal([]);
        });

        it(`SHOULD return empty array if chunk hasn't been changed`, () => {
          let oldChunk = {
            name: 'test',
            hash: 'abcde'
          };
          inst.chunkVersions[oldChunk.name] = oldChunk;
          let newChunk = Object.assign({}, oldChunk);
          expect(inst._getChunkObsoleteFiles(newChunk)).to.be.deep.equal([]);
        });

        it(`SHOULD return only obsolete files if chunk has been changed`, () => {
          let oldChunk = {
            name: 'test',
            hash: 'hash1',
            files: ['file1(old-name)', 'file2(old-name)', 'file3(old-name)']
          };
          inst.chunkVersions[oldChunk.name] = {
            hash: oldChunk.hash,
            files: oldChunk.files
          };
          let newChunk = Object.assign(oldChunk, {
            hash: 'hash2',
            files: ['file1(old-name)', 'file2(old-name)', 'file3(new-name)']
          });
          expect(inst._getChunkObsoleteFiles(newChunk)).to.be.deep.equal(['file3(old-name)']);
        });

        it(`SHOULD call _saveChunkConfig(chunk) method in any case`,
          () => {
            let chunk = {
              name: 'test',
              hash: 'abcde'
            };
            let _saveChunkConfig = sinon.stub(inst, '_saveChunkConfig');

            // 1) if there is no chunkVersions[chunk.name]
            expect(_saveChunkConfig.called).to.be.false;
            inst._getChunkObsoleteFiles(chunk);
            expect(_saveChunkConfig.callCount).to.be.equal(1);
            expect(_saveChunkConfig.args[0][0]).to.be.deep.equal(chunk);
            _saveChunkConfig.reset();

            // 2) otherwise
            inst.chunkVersions[chunk.name] = chunk;
            expect(_saveChunkConfig.called).to.be.false;
            inst._getChunkObsoleteFiles(chunk);
            expect(_saveChunkConfig.callCount).to.be.equal(1);
            expect(_saveChunkConfig.args[0][0]).to.be.deep.equal(chunk);
          });

      });
    });

    describe(`method apply(compiler)`, () => {
      describe('in order to remove obsolete chunks files in webpack watch mode', () => {
        it(
          `SHOULD get hooked on compiler 'after-emit' event and pass _removeObsoleteFiles method
          as a callback`,
          () => {
            let compiler = {
              plugin: sinon.stub()
            };
            sinon.stub(inst._removeObsoleteFiles, 'bind').returns(inst._removeObsoleteFiles);
            inst.apply(compiler);
            expect(compiler.plugin.callCount).to.be.equal(1);
            expect(compiler.plugin.args[0][0]).to.be.equal('after-emit');
            expect(compiler.plugin.args[0][1]).to.be.equal(inst._removeObsoleteFiles);
            expect(inst._removeObsoleteFiles.bind.called).to.be.true;
            expect(inst._removeObsoleteFiles.bind.args[0][0]).to.be.equal(inst);
            expect(inst._removeObsoleteFiles.bind.args[0][1]).to.be.equal(compiler);
          });
      });
    });

    describe(`method _removeObsoleteFiles(compiler, compilation, done)`, () => {
      describe(`in order to remove obsolete chunks files in webpack watch mode`, () => {
        let done;
        let compilation;
        let compiler;
        let _getObsoleteFiles;
        beforeEach(() => {
          compilation = Math.random();
          _getObsoleteFiles = sinon.stub(inst, '_getObsoleteFiles').returns([]);
          done = sinon.stub();
          sinon.stub(del, 'sync');
        });

        afterEach(() => {
          del.sync.restore();
        });

        it(`SHOULD call _getObsoleteFiles(compilation) method`, () => {
          expect(_getObsoleteFiles.notCalled).to.be.true;
          inst._removeObsoleteFiles(compiler, compilation, done);
          expect(_getObsoleteFiles.callCount).to.be.equal(1);
          expect(_getObsoleteFiles.args[0][0]).to.be.equal(compilation);
        });

        it(`SHOULD call del.sync(filePath) for each obsolete file`, () => {
          compiler = {
            outputPath: 'absolute/path/to/output/folder/'
          };
          let obsoleteFiles = ['obsolete-file1', 'obsolete-file2', 'obsolete-file3'];
          _getObsoleteFiles.returns(obsoleteFiles);
          expect(del.sync.notCalled).to.be.true;
          inst._removeObsoleteFiles(compiler, compilation, done);
          expect(del.sync.callCount).to.be.equal(obsoleteFiles.length);
          expect(del.sync.args[0][0]).to.be.equal(path.join(compiler.outputPath, obsoleteFiles[0]));
          expect(del.sync.args[1][0]).to.be.equal(path.join(compiler.outputPath, obsoleteFiles[1]));
          expect(del.sync.args[2][0]).to.be.equal(path.join(compiler.outputPath, obsoleteFiles[2]));
        });

        it(`SHOULD call done() callback at the end`, () => {
          expect(done.notCalled).to.be.true;
          inst._removeObsoleteFiles(compiler, compilation, done);
          expect(done.callCount).to.be.equal(1);
        });
      });
    });

  });
});<|endoftext|>


<commit_before>//sort table
$(function(){
  'use strict';

  // add custom parser to make the stars column to sort according to attr.
  $.tablesorter.addParser({
    id: 'star_scores', // called later when init the tablesorter
    is: function() {
      return false; // return false so this parser is not auto detected
    },
    format: function(s, table, cell, cellIndex) {
      var $cell = $(cell);
      if (cellIndex === 1) {
        return $cell.attr('data-score') || s;
      }
      return s;
    },
    parsed: false,
    type: 'numeric' // Setting type of data...
  });

  $('table').tablesorter({
    headers: {
      1 : { sorter: 'star_scores' } // Telling it to use custom parser...
    },
    sortList: [[0,0]],
  });
});

//ToolTip
$(function () {
  'use strict';
  $("[data-toggle='tooltip']").tooltip();
});

//Hide empty columns
$(document).ready(function() {
  'use strict';
  if (window.chrome && (window.location.protocol === 'file:') )  {
    $('#browseralert').modal();
  }

  $('#sortable_table tr th').each(function(i) {
    //select all tds in this column
    var tds = $(this).parents('table')
    .find('tr td:nth-child(' + (i + 1) + ')');
    //check if all the cells in this column are empty
    //
    if ($(this).hasClass('chart-column')) {
    } else {
      if ($(this).text().trim() === '') {
        //hide header
        $(this).hide();
        //hide cells
        tds.hide();
      }
    }
  });
});

$(function(){
  $( "#sortable_table" ).on( "click", "td", function( event ) {
      if ($(this).hasClass('success') || $(this).hasClass('danger')){
        var title = $(this).attr('title');
        var val = title.replace(/[ \/]/g, '');
        addData(this, val);
      } else if ($(this).hasClass('plot_btn')){
        addData(this, 'all');
      }
  });
});
<commit_msg>use event delegation for plot_btn<commit_after>//sort table
$(function(){
  'use strict';

  // add custom parser to make the stars column to sort according to attr.
  $.tablesorter.addParser({
    id: 'star_scores', // called later when init the tablesorter
    is: function() {
      return false; // return false so this parser is not auto detected
    },
    format: function(s, table, cell, cellIndex) {
      var $cell = $(cell);
      if (cellIndex === 1) {
        return $cell.attr('data-score') || s;
      }
      return s;
    },
    parsed: false,
    type: 'numeric' // Setting type of data...
  });

  $('table').tablesorter({
    headers: {
      1 : { sorter: 'star_scores' } // Telling it to use custom parser...
    },
    sortList: [[0,0]],
  });
});

//ToolTip
$(function () {
  'use strict';
  $("[data-toggle='tooltip']").tooltip();
});

//Hide empty columns
$(document).ready(function() {
  'use strict';
  if (window.chrome && (window.location.protocol === 'file:') )  {
    $('#browseralert').modal();
  }

  $('#sortable_table tr th').each(function(i) {
    //select all tds in this column
    var tds = $(this).parents('table')
    .find('tr td:nth-child(' + (i + 1) + ')');
    //check if all the cells in this column are empty
    //
    if ($(this).hasClass('chart-column')) {
    } else {
      if ($(this).text().trim() === '') {
        //hide header
        $(this).hide();
        //hide cells
        tds.hide();
      }
    }
  });
});

$(function(){
  $( "#sortable_table" ).on( "click", "td, .plot_btn", function( event ) {
      if ($(this).hasClass('success') || $(this).hasClass('danger')){
        var title = $(this).attr('title');
        var val = title.replace(/[ \/]/g, '');
        addData(this, val);
      } else if ($(this).hasClass('plot_btn')){
        addData(this, 'all');
      }
  });
});
<|endoftext|>


<commit_before>$(function() {

  $('#start-draw').on('click', function(e) {
    var size = "size=" + "640x480&";
    if (map.streetView.pano === null) {
      alert('Please switch to Street View first!');
    } else {
      var pano = "pano=" + map.streetView.location.pano + "&";
      var heading = "heading=" + map.streetView.pov.heading + "&";
      var pitch = "pitch=" + map.streetView.pov.pitch + "&";         // var zoom = "zoom=" + map.streetView.location.pov.zoom + "&";

      var url = "http://maps.googleapis.com/maps/api/streetview?" + size + pano + heading + pitch;
      $('#street-view-image').attr("src", url);
      $('.metro').hide();
      $('.draw').show();
    }
  });

  var sketchPad = createSketchpad();

  startDrawing(sketchPad);

  // Default lat and long for map on load
  var latitude = 41.881487;
  var longitude = -87.631219;

  // Set options for base google map
  var mapOptions = {
    center: new google.maps.LatLng(latitude, longitude),
    zoom: 12
  };

  // Set options for marker clusterer map
  var clusterOptions = {
    gridSize: 70,
    maxZoom: 14
  };

  var chartData;
  var dates;

  convertToX = function(date, dates) {
    return dates.indexOf(date);
  };

  resetChart = function(numDates) {
    chartData = [];
    for (var i = 0; i < numDates; i++) {
      chartData.push({
        x: i,
        report: 0,
        patch: 0
      });
    }
  };

  insertData = function(hole, data, dates) {
    completeX = convertToX(hole.completion_date, dates);
    createdX = convertToX(hole.creation_date, dates);
    var numComplete = 0;
    var numCreate = 0;
    if (completeX > -1 && completeX < dates.length) {
      numComplete = ++data[completeX].patch;
    }
    if (createdX > -1 && createdX < dates.length) {
      numCreate = ++data[createdX].report;
    }
    return [numCreate, numComplete];
  };

  var map = initialize(mapOptions);

  var mc = new MarkerClusterer(map, [], clusterOptions);

  // function MyOverlay(options) {
  //   this.setValues(options);
  //   this.markerLayer = $('
').addClass('overlay');
  // }

  // // MyOverlay is derived from google.maps.OverlayView
  // MyOverlay.prototype = new google.maps.OverlayView();

  // MyOverlay.prototype.onAdd = function() {
  //   var $pane = $(this.getPanes().overlayImage); // Pane 4
  //   $pane.append(this.markerLayer);
  // };

  // MyOverlay.prototype.onRemove = function() {
  //   this.markerLayer.remove();
  // };

  // MyOverlay.prototype.draw = function() {
  //   var projection = this.getProjection();
  //   var zoom = this.getMap().getZoom();
  //   var fragment = document.createDocumentFragment();

  //   this.markerLayer.empty(); // Empty any previous rendered markers

  //   // Now append the entire fragment from memory onto the DOM
  //   this.markerLayer.append(fragment);
  // };

  // var OverlayMap = new MyOverlay({
  //   map: map
  // });

  // Search
  var infowindow = new google.maps.InfoWindow();
  var marker = "";
  var unfilled_markers = [];
  var filled_markers = [];
  var geocoder = new google.maps.Geocoder();

  function makeInfoWindowEvent(map, infowindow, contentString, marker) {
    google.maps.event.addListener(marker, 'click', function() {
      infowindow.setContent(contentString);
      infowindow.open(map, marker);
    });
  };

  function clearMarkers() {
    for (var i = 0; i < unfilled_markers.length; i++) {
      unfilled_markers[i].setMap(null);
    }
    for (var i = 0; i < filled_markers.length; i++) {
      filled_markers[i].setMap(null);
    }
  }

  function makeReportEvent(map, infowindow, event) {
    var lat = parseFloat(event.latLng.lat());
    var lng = parseFloat(event.latLng.lng());
    var latlng = new google.maps.LatLng(lat, lng);

    geocoder.geocode({
      'latLng': latlng
    }, function(results, status) {
      var address = String("'" + results[0].formatted_address + "'");
      var dropDownForm = "
Submit a report for this location
\
                <form id='reportSubmit' enctype='multipart/form-data'>\
                  <input type='hidden' name='latitude' value=" + lat + ">\
                  <input type='hidden' name='longitude' value=" + lng + ">\
                  <input type='hidden' name='address' value=" + address + ">\
                  Where is the pothole located? \
                  <select name='attribute'>\
                    <option value='CURB'>Curb Lane</option>\
                    <option value='CROSS'>Crosswalk</option>\
                    <option value='INTERSEC'>Intersection</option>\
                    <option value='TRAFFIC'>Traffic Lane</option>\
                  </select>\
                  Write a description of the pothole below (optional):\
                  <textarea name='description' cols='40' rows='4' maxLength='500' placeholder='Description here...'></textarea>\
                  Upload an image of this pothole (optional):\
                  <input type='file' name='picture'>\
                  Phone # to receive text updates about your request (optional):\
                  <input type='text' pattern='[0-9]{3}-[0-9]{3}-[0-9]{4}' name='phone' placeholder='###-###-####'>\

\

<input type='submit' value='Submit'>\
                </form>"

      infowindow.setContent(dropDownForm);
      infowindow.setPosition(event.latLng);
      infowindow.open(map);
    });
  }

  $('#map-canvas').on('submit', '#reportSubmit', function(event) {
    event.preventDefault();
    infowindow.setContent("");
    var form = $(this).serializeArray();

    $.post('/submitReport', form, function(data, textStatus, xhr) {
      if (xhr.status === 200) {
        infowindow.setContent("Your request has been submitted");
        setTimeout(function() {
          infowindow.close();
        }, 3000);
      } else {
        infowindow.setContent("Unable to process your request");
        setTimeout(function() {
          infowindow.close();
        }, 4000);
      }
    }, 'json');

  });

  getDatesBetween = function(startDate, endDate) {
    var start = new Date(startDate);
    var end = new Date(endDate);
    var between = [];
    while (start <= end) {
      start.setDate(start.getDate() + 1);
      between.push(new Date(start));
    }

    return between.map(function(date) {
      return date.getFullYear() + "-" + ("0" + (date.getMonth() + 1)).slice(-2) + "-" + ("0" + date.getDate()).slice(-2);
    });
  };

  //Using sprites
  var red = new google.maps.MarkerImage("/assets/sprite.png", new google.maps.Size(16, 16), new google.maps.Point(0, 0));
  var green = new google.maps.MarkerImage("/assets/sprite.png", new google.maps.Size(16, 16), new google.maps.Point(0, 26));

  puttingtheMarkers = function(data, dates) {
    console.log(data);
    if (data === null || data.length === 0) {
      alert("There is no data for this period. Please choose another date");
    } else {
      clearMarkers();
      resetChart(dates.length);
      var yMax = 0;
      var y2Max = 0;
      for (var i = 0; i < data.length; i++) {
        var counts = insertData(data[i], chartData, dates);
        yMax = Math.max(yMax, counts[0]);
        y2Max = Math.max(y2Max, counts[1]);
        if (data[i].completion_date === null) {
          marker = new google.maps.Marker({
            position: new google.maps.LatLng(data[i].latitude, data[i].longitude),
            map: map,
            // icon: '/assets/red_MarkerA.png',
            icon: red,
            optimized: true
          });
          makeInfoWindowEvent(map, infowindow, "Reported on: " + data[i].creation_date + "
" + "Street Address: " + data[i].street_address, marker);
          unfilled_markers.push(marker);
        } else {
          marker = new google.maps.Marker({
            position: new google.maps.LatLng(data[i].latitude, data[i].longitude),
            map: map,
            // icon: '/assets/green_MarkerA.png',
            icon: green,
            optimized: true
          });
          makeInfoWindowEvent(map, infowindow, "Reported on: " + data[i].creation_date + "
" + "Completed Date: " + data[i].completion_date + "
" + "Street Address: " + data[i].street_address, marker);
          filled_markers.push(marker);
        }
      }

      mc.addMarkers(unfilled_markers.concat(filled_markers));

      angular.element(document.getElementById('chart')).scope().$apply(function(scope) {
        scope.dates = dates.map(function(date) {
          return date.substring(5, 10);
        });
        scope.options.axes.y.max = Math.max(yMax, y2Max);
        scope.options.axes.y2.max = Math.max(yMax, y2Max);
        scope.data = chartData;
      });
    }
  };

  getPotholesByDate = function(startDate, endDate) {
    var dates = getDatesBetween(startDate, endDate);
    filled_markers = [];
    unfilled_markers = [];
    mc.clearMarkers();

    $.ajax({
      url: "/potholes.json",
      data: {
        all_dates: dates
      },
      success: function(data) {
        puttingtheMarkers(data, dates);
      },
      dataType: "json"
    });
  };

  // Remove all markers from map if you zoom out too far, put them back when zoomed back in
  // google.maps.event.addListener(map, 'zoom_changed', function(){
  //   if (map.zoom < 7) {
  //     mc.clearMarkers();
  //   } else if (map.zoom >= 7 && mc.a.length === 0) {
  //     mc.addMarkers(unfilled_markers.concat(filled_markers));
  //   }
  // });

  // Open report form when map is clicked if zoomed in far enough
  // If report form is already open, close it on next map click
  google.maps.event.addListener(map, 'click', function(event) {
    if (map.zoom > 14 && infowindow.getContent()) {
      infowindow.close();
      infowindow.setContent("");
    } else if (map.zoom > 14) {
      makeReportEvent(map, infowindow, event);
    } else {
      infowindow.close();
    }
  });

  $("#date-pick").on('submit', function(event) {
    event.preventDefault();
    getPotholesByDate($('#start-date').val(), $('#end-date').val());
  });

  $("#filled").on('click', function() {

    if ($("#filled").prop("checked")) {
      for (var i = 0; i < filled_markers.length; ++i) {
        filled_markers[i].setVisible(false);
      }
    } else {
      for (var i = 0; i < filled_markers.length; ++i) {
        filled_markers[i].setVisible(true);
      }
    }
  });

  function search(e) {
    var code = (e.keyCode ? e.keyCode : e.which);
    var target = document.getElementById('pac-input');
    if (code == 13) { //Enter keycode
      e.preventDefault();
      geocoder.geocode({
          'address': target.value
        },
        function(results, status) {
          if (status == google.maps.GeocoderStatus.OK) {
            var loc = results[0].geometry.location;
            map.setZoom(17);
            map.panTo(new google.maps.LatLng(loc.lat(), loc.lng()));
          } else {
            alert("Not found: " + status);
          }
        }
      );

    }
  };

  $("#pac-input").bind("keypress", {}, search);

});
<commit_msg>trying to make memcache work<commit_after>$(function() {

  $('#start-draw').on('click', function(e) {
    var size = "size=" + "640x480&";
    if (map.streetView.pano === null) {
      alert('Please switch to Street View first!');
    } else {
      var pano = "pano=" + map.streetView.location.pano + "&";
      var heading = "heading=" + map.streetView.pov.heading + "&";
      var pitch = "pitch=" + map.streetView.pov.pitch + "&";         // var zoom = "zoom=" + map.streetView.location.pov.zoom + "&";

      var url = "http://maps.googleapis.com/maps/api/streetview?" + size + pano + heading + pitch;
      $('#street-view-image').attr("src", url);
      $('.metro').hide();
      $('.draw').show();
    }
  });

  var sketchPad = createSketchpad();

  startDrawing(sketchPad);

  // Default lat and long for map on load
  var latitude = 41.881487;
  var longitude = -87.631219;

  // Set options for base google map
  var mapOptions = {
    center: new google.maps.LatLng(latitude, longitude),
    zoom: 12
  };

  // Set options for marker clusterer map
  var clusterOptions = {
    gridSize: 70,
    maxZoom: 14
  };

  var chartData;
  var dates;

  convertToX = function(date, dates) {
    return dates.indexOf(date);
  };

  resetChart = function(numDates) {
    chartData = [];
    for (var i = 0; i < numDates; i++) {
      chartData.push({
        x: i,
        report: 0,
        patch: 0
      });
    }
  };

  insertData = function(hole, data, dates) {
    completeX = convertToX(hole.completion_date, dates);
    createdX = convertToX(hole.creation_date, dates);
    var numComplete = 0;
    var numCreate = 0;
    if (completeX > -1 && completeX < dates.length) {
      numComplete = ++data[completeX].patch;
    }
    if (createdX > -1 && createdX < dates.length) {
      numCreate = ++data[createdX].report;
    }
    return [numCreate, numComplete];
  };

  var map = initialize(mapOptions);

  var mc = new MarkerClusterer(map, [], clusterOptions);

  // function MyOverlay(options) {
  //   this.setValues(options);
  //   this.markerLayer = $('
').addClass('overlay');
  // }

  // // MyOverlay is derived from google.maps.OverlayView
  // MyOverlay.prototype = new google.maps.OverlayView();

  // MyOverlay.prototype.onAdd = function() {
  //   var $pane = $(this.getPanes().overlayImage); // Pane 4
  //   $pane.append(this.markerLayer);
  // };

  // MyOverlay.prototype.onRemove = function() {
  //   this.markerLayer.remove();
  // };

  // MyOverlay.prototype.draw = function() {
  //   var projection = this.getProjection();
  //   var zoom = this.getMap().getZoom();
  //   var fragment = document.createDocumentFragment();

  //   this.markerLayer.empty(); // Empty any previous rendered markers

  //   // Now append the entire fragment from memory onto the DOM
  //   this.markerLayer.append(fragment);
  // };

  // var OverlayMap = new MyOverlay({
  //   map: map
  // });

  // Search
  var infowindow = new google.maps.InfoWindow();
  var marker = "";
  var unfilled_markers = [];
  var filled_markers = [];
  var geocoder = new google.maps.Geocoder();

  function makeInfoWindowEvent(map, infowindow, contentString, marker) {
    google.maps.event.addListener(marker, 'click', function() {
      infowindow.setContent(contentString);
      infowindow.open(map, marker);
    });
  };

  function clearMarkers() {
    for (var i = 0; i < unfilled_markers.length; i++) {
      unfilled_markers[i].setMap(null);
    }
    for (var i = 0; i < filled_markers.length; i++) {
      filled_markers[i].setMap(null);
    }
  }

  function makeReportEvent(map, infowindow, event) {
    var lat = parseFloat(event.latLng.lat());
    var lng = parseFloat(event.latLng.lng());
    var latlng = new google.maps.LatLng(lat, lng);

    geocoder.geocode({
      'latLng': latlng
    }, function(results, status) {
      var address = String("'" + results[0].formatted_address + "'");
      var dropDownForm = "
Submit a report for this location
\
                <form id='reportSubmit' enctype='multipart/form-data'>\
                  <input type='hidden' name='latitude' value=" + lat + ">\
                  <input type='hidden' name='longitude' value=" + lng + ">\
                  <input type='hidden' name='address' value=" + address + ">\
                  Where is the pothole located? \
                  <select name='attribute'>\
                    <option value='CURB'>Curb Lane</option>\
                    <option value='CROSS'>Crosswalk</option>\
                    <option value='INTERSEC'>Intersection</option>\
                    <option value='TRAFFIC'>Traffic Lane</option>\
                  </select>\
                  Write a description of the pothole below (optional):\
                  <textarea name='description' cols='40' rows='4' maxLength='500' placeholder='Description here...'></textarea>\
                  Upload an image of this pothole (optional):\
                  <input type='file' name='picture'>\
                  Phone # to receive text updates about your request (optional):\
                  <input type='text' pattern='[0-9]{3}-[0-9]{3}-[0-9]{4}' name='phone' placeholder='###-###-####'>\

\

<input type='submit' value='Submit'>\
                </form>"

      infowindow.setContent(dropDownForm);
      infowindow.setPosition(event.latLng);
      infowindow.open(map);
    });
  }

  $('#map-canvas').on('submit', '#reportSubmit', function(event) {
    event.preventDefault();
    infowindow.setContent("");
    var form = $(this).serializeArray();

    $.post('/submitReport', form, function(data, textStatus, xhr) {
      if (xhr.status === 200) {
        infowindow.setContent("Your request has been submitted");
        setTimeout(function() {
          infowindow.close();
        }, 3000);
      } else {
        infowindow.setContent("Unable to process your request");
        setTimeout(function() {
          infowindow.close();
        }, 4000);
      }
    }, 'json');

  });

  getDatesBetween = function(startDate, endDate) {
    var start = new Date(startDate);
    var end = new Date(endDate);
    var between = [];
    while (start <= end) {
      start.setDate(start.getDate() + 1);
      between.push(new Date(start));
    }

    return between.map(function(date) {
      return date.getFullYear() + "-" + ("0" + (date.getMonth() + 1)).slice(-2) + "-" + ("0" + date.getDate()).slice(-2);
    });
  };

  //Using sprites
  var red = new google.maps.MarkerImage("/assets/sprite.png", new google.maps.Size(16, 16), new google.maps.Point(0, 0));
  var green = new google.maps.MarkerImage("/assets/sprite.png", new google.maps.Size(16, 16), new google.maps.Point(0, 26));

  puttingtheMarkers = function(data, dates) {
    console.log(data);
    temp = []
    for (key in data) {
      if (data.hasOwnProperty(key)) {
        var val = data[key];
        temp.push(val);
      }
    }
    console.log(temp);

    if (data === null || data.length === 0) {
      alert("There is no data for this period. Please choose another date");
    } else {
      clearMarkers();
      resetChart(dates.length);
      var yMax = 0;
      var y2Max = 0;
      for (var i = 0; i < data.length; i++) {
        var counts = insertData(data[i], chartData, dates);
        yMax = Math.max(yMax, counts[0]);
        y2Max = Math.max(y2Max, counts[1]);
        if (data[i].completion_date === null) {
          marker = new google.maps.Marker({
            position: new google.maps.LatLng(data[i].latitude, data[i].longitude),
            map: map,
            // icon: '/assets/red_MarkerA.png',
            icon: red,
            optimized: true
          });
          makeInfoWindowEvent(map, infowindow, "Reported on: " + data[i].creation_date + "
" + "Street Address: " + data[i].street_address, marker);
          unfilled_markers.push(marker);
        } else {
          marker = new google.maps.Marker({
            position: new google.maps.LatLng(data[i].latitude, data[i].longitude),
            map: map,
            // icon: '/assets/green_MarkerA.png',
            icon: green,
            optimized: true
          });
          makeInfoWindowEvent(map, infowindow, "Reported on: " + data[i].creation_date + "
" + "Completed Date: " + data[i].completion_date + "
" + "Street Address: " + data[i].street_address, marker);
          filled_markers.push(marker);
        }
      }

      mc.addMarkers(unfilled_markers.concat(filled_markers));

      angular.element(document.getElementById('chart')).scope().$apply(function(scope) {
        scope.dates = dates.map(function(date) {
          return date.substring(5, 10);
        });
        scope.options.axes.y.max = Math.max(yMax, y2Max);
        scope.options.axes.y2.max = Math.max(yMax, y2Max);
        scope.data = chartData;
      });
    }
  };

  getPotholesByDate = function(startDate, endDate) {
    var dates = getDatesBetween(startDate, endDate);
    filled_markers = [];
    unfilled_markers = [];
    mc.clearMarkers();

    $.ajax({
      url: "/potholes.json",
      data: {
        all_dates: dates
      },
      success: function(data) {
        puttingtheMarkers(data, dates);
      },
      dataType: "json"
    });
  };

  // Remove all markers from map if you zoom out too far, put them back when zoomed back in
  // google.maps.event.addListener(map, 'zoom_changed', function(){
  //   if (map.zoom < 7) {
  //     mc.clearMarkers();
  //   } else if (map.zoom >= 7 && mc.a.length === 0) {
  //     mc.addMarkers(unfilled_markers.concat(filled_markers));
  //   }
  // });

  // Open report form when map is clicked if zoomed in far enough
  // If report form is already open, close it on next map click
  google.maps.event.addListener(map, 'click', function(event) {
    if (map.zoom > 14 && infowindow.getContent()) {
      infowindow.close();
      infowindow.setContent("");
    } else if (map.zoom > 14) {
      makeReportEvent(map, infowindow, event);
    } else {
      infowindow.close();
    }
  });

  $("#date-pick").on('submit', function(event) {
    event.preventDefault();
    getPotholesByDate($('#start-date').val(), $('#end-date').val());
  });

  $("#filled").on('click', function() {

    if ($("#filled").prop("checked")) {
      for (var i = 0; i < filled_markers.length; ++i) {
        filled_markers[i].setVisible(false);
      }
    } else {
      for (var i = 0; i < filled_markers.length; ++i) {
        filled_markers[i].setVisible(true);
      }
    }
  });

  function search(e) {
    var code = (e.keyCode ? e.keyCode : e.which);
    var target = document.getElementById('pac-input');
    if (code == 13) { //Enter keycode
      e.preventDefault();
      geocoder.geocode({
          'address': target.value
        },
        function(results, status) {
          if (status == google.maps.GeocoderStatus.OK) {
            var loc = results[0].geometry.location;
            map.setZoom(17);
            map.panTo(new google.maps.LatLng(loc.lat(), loc.lng()));
          } else {
            alert("Not found: " + status);
          }
        }
      );

    }
  };

  $("#pac-input").bind("keypress", {}, search);

});
<|endoftext|>


<commit_before>const webpackConfig = require('../../build/webpack.test.conf')

module.exports = config => {
  config.set({
    browsers: [ 'ChromeHeadless' ],
    frameworks: [ 'jasmine', 'jasmine-matchers' ],
    client: {
      jasmine: { random: false },
    },
    reporters: [ 'spec', 'coverage' ],
    files: [ './index.js' ],
    preprocessors: {
      './index.js': [ 'webpack', 'sourcemap' ],
    },
    webpack: webpackConfig,
    webpackMiddleware: {
      noInfo: true,
    },
    coverageReporter: {
      dir: './coverage',
      reporters: [
        { type: 'lcov', subdir: '.' },
        { type: 'text-summary' },
      ],
    },
  })
}
<commit_msg>fix automatic testing<commit_after>const webpackConfig = require('../../build/webpack.test.conf')

module.exports = config => {
  config.set({
    files: [ './index.js' ],
    browsers: [ 'CustomChrome' ],
    customLaunchers: {
      CustomChrome: {
        // `ChromeHeadless` without any flags used to be fine,
        // but it is not now for some unknown reason.
        // Adding `--no-sandbox` flag solves the issue, which
        // I know is insecure, but since we are only running
        // tests, it should be no problem.
        base: 'ChromeHeadless',
        flags: [ '--no-sandbox' ],
      },
    },
    preprocessors: {
      './index.js': [ 'webpack', 'sourcemap' ],
    },
    webpack: webpackConfig,
    webpackMiddleware: {
      noInfo: true,
    },
    frameworks: [ 'jasmine', 'jasmine-matchers' ],
    client: {
      jasmine: { random: false },
    },
    reporters: [ 'spec', 'coverage' ],
    coverageReporter: {
      dir: './coverage',
      reporters: [
        { type: 'lcov', subdir: '.' },
        { type: 'text-summary' },
      ],
    },
  })
}
<|endoftext|>


<commit_before>function nextPost() {
  $(".js-next").on("click", function(event) {
    event.preventDefault();
    var nextId = parseInt($(".js-next").attr("data-id")) + 1;
    $.get("/posts/" + nextId + ".json", function(post) {
      // get post
      $(".postTitle").text(post.title);
      $(".postContent").text(post.content);
      // re-set the id to current on the link
      $(".js-next").attr("data-id", post.id);
      renderComments(post.comments);
    });
  });
}

function renderComments(comments) {
  $('.comments').empty()
  $.each(comments, function(index, comment){
    if (comment.user != null) {
      $('.comments').append(
        "
" + comment['user'].first_name + " " + comment['user'].last_name + " says:
",
        "
" + comment.content + "
"
      );
    }
  });
}
<commit_msg>update flash message when there are no more post pages<commit_after>function nextPost() {
  $(".js-next").on("click", function(event) {
    event.preventDefault();
    var nextId = parseInt($(".js-next").attr("data-id")) + 1;
    $.get("/posts/" + nextId + ".json", function(post) {
      // get post
      $(".postTitle").text(post.title);
      $(".postContent").text(post.content);
      // re-set the id to current on the link
      $(".js-next").attr("data-id", post.id);
      renderComments(post.comments);
    }).fail(function() {
      $('#flash').append("
Sorry, there are more posts on the way!
");
    });
  });
}

function renderComments(comments) {
  $('.comments').empty()
  $.each(comments, function(index, comment){
    if (comment.user != null) {
      $('.comments').append(
        "
" + comment['user'].first_name + " " + comment['user'].last_name + " says:
",
        "
" + comment.content + "
"
      );
    }
  });
}
<|endoftext|>


<commit_before>App.RESTSerializer = DS.RESTSerializer.extend({
  init: function() {
    this._super();

    this.map('App.Contact', {
      phoneNumbers: {embedded: 'always'}
    });
  }
});

App.Adapter = DS.RESTAdapter.extend({
  bulkCommit: false,
  serializer: App.RESTSerializer.create()
});

App.Store = DS.Store.extend({
  revision: 11,
  adapter: App.Adapter.create()
});
<commit_msg>Mappings are preferably declared on the adapter<commit_after>App.Adapter = DS.RESTAdapter.extend({
  bulkCommit: false
});

App.Adapter.map('App.Contact', {
  phoneNumbers: {embedded: 'always'}
});

App.Store = DS.Store.extend({
  revision: 11,
  adapter: App.Adapter.create()
});
<|endoftext|>


<commit_before>// # Place all the behaviors and hooks related to the matching controller here.
// # All this logic will automatically be available in application.js.
// # You can use CoffeeScript in this file: http://coffeescript.org/

$(function() {

  function initialize() {
    var lat = $('#map-canvas').data('lat');
    var lng = $('#map-canvas').data('lng');
    var zoom_level = $('#map-canvas').data('zoom');
    var visuals = $('#map-canvas').data('visuals');

    var mapCanvas = document.getElementById('map-canvas');
    var mapOptions = {
      center: new google.maps.LatLng(lat, lng),
      zoom: zoom_level,
      mapTypeId: google.maps.MapTypeId.TERRAIN
    };

    map = new google.maps.Map(mapCanvas, mapOptions); //global

    function addMarker(visual) {
      // debugger
      var image = {
        url: visual.thumbnail_url,
        // This marker is 20 pixels wide by 32 pixels tall.
        size: new google.maps.Size(200, 200),
        // The origin for this image is 0,0.
        origin: new google.maps.Point(0,0),
        // The anchor for this image is the base of the flagpole at 0,32.
        anchor: new google.maps.Point(0, 80)
        // radius: new google.maps.Radius(100)
      };

      var shape = {
        coords: [1, 1, 1, 20, 18, 20, 18 , 1],
        type: 'poly'
      };

      var marker = new google.maps.Marker({
        position: new google.maps.LatLng(visual.lat, visual.lng),
        icon: image,
        map: map,
        shape: shape,
        zIndex: visual.id,
        animation: google.maps.Animation.DROP
      });
    };

    for (var i = 0, visual; visual = visuals[i]; i++) {
      addMarker(visual);
    };

  }

  google.maps.event.addDomListener(window, 'load', initialize);
  // if( typeof google !== 'undefined' ) {
  //   google.maps.event.addDomListener(window, 'load', initialize);
  // }

});

<commit_msg>saving<commit_after>// # Place all the behaviors and hooks related to the matching controller here.
// # All this logic will automatically be available in application.js.
// # You can use CoffeeScript in this file: http://coffeescript.org/

$(function() {

  function initialize() {
    var lat = $('#map-canvas').data('lat');
    var lng = $('#map-canvas').data('lng');
    var zoom_level = $('#map-canvas').data('zoom');
    var visuals = $('#map-canvas').data('visuals');

    var mapCanvas = document.getElementById('map-canvas');
    var mapOptions = {
      center: new google.maps.LatLng(lat, lng),
      zoom: zoom_level,
      mapTypeId: google.maps.MapTypeId.TERRAIN
    };

    map = new google.maps.Map(mapCanvas, mapOptions); //global

    function addMarker(visual) {
      // debugger
      var image = {
        url: visual.thumbnail_url,
        // This marker is 20 pixels wide by 32 pixels tall.
        size: new google.maps.Size(200, 200),
        // The origin for this image is 0,0.
        origin: new google.maps.Point(0,0),
        // The anchor for this image is the base of the flagpole at 0,32.
        anchor: new google.maps.Point(0, 80)
        // radius: new google.maps.Radius(100)
      };

      var shape = {
        coords: [1, 1, 1, 20, 18, 20, 18 , 1],
        type: 'poly'
      };

      var marker = new google.maps.Marker({
        position: new google.maps.LatLng(visual.lat, visual.lng),
        icon: image,
        map: map,
        shape: shape,
        zIndex: visual.id,
        animation: google.maps.Animation.DROP
      });
    };

    for (var i = 0, visual; visual = visuals[i]; i++) {
      addMarker(visual);
    };

  }

  if( typeof google !== 'undefined' ) {
    google.maps.event.addDomListener(window, 'load', initialize);
  }

});

<|endoftext|>


<commit_before>import Clipboard from "clipboard";

function addCopy(selector, textFunction, tooltip = "Copy to clipboard") {
    const $el = $(selector).data("placement", "top")
        .attr("title", tooltip)
        .tooltip();
    const clip = new Clipboard(selector, {
        text: textFunction,
    });
    clip.on("success", e => {
        $el.attr("title", "Copied!")
            .tooltip("fixTitle")
            .tooltip("show")
            .attr("title", tooltip)
            .tooltip("fixTitle");
    });
    clip.on("error", e => {
        $el.attr("title", "Sorry, something went wrong")
            .tooltip("fixTitle")
            .tooltip("show")
            .attr("title", tooltip)
            .tooltip("fixTitle");
    });
}

/*
 * Returns the brightness of an rgb-color
 * from: http:// www.w3.org/WAI/ER/WD-AERT/#color-contrast
 */
function brightness(rgb) {
    return rgb.r * 0.299 + rgb.g * 0.587 + rgb.b * 0.114;
}

/**
 * Download data with polyfill for browsers that don't support the download attr
 * @param  {[type]}  data                    data to download
 * @param  {[type]}  fileName                filename
 * @param  {String}  [fileType="text/plain"] filetype eg text/csv
 * @return {Promise.<string>}                Promice that returns when downloaded
 */
function downloadData(data, fileName, fileType="text/plain") {
    if ("download" in document.createElement("a")) {
        return new Promise(function (resolve, reject) {
            downloadDataByLink(`data:${fileType};base64,${btoa(data)}`, fileName);
            resolve(fileName);
        });
    } else {
        return downloadDataByForm(data, fileName, fileType);
    }
}

/**
 * Triggers a file download in the browser using a hidden
 * form and a server round trip. Returns a Promise that resolves when
 * the file starts downloading
 *
 * @param {string} data The text you want in the file
 * @param {string} fileName The requested file name
 * @return {Promise.<string>}
 */
function downloadDataByForm(data, fileName, fileType=null) {
    return new Promise(function (resolve, reject) {
        let nonce = Math.random();
        $("form.download").remove();
        $("body").append("<form class='download' method='post' action='/download'></form>");
        let $downloadForm = $("form.download").append("<input type='hidden' name='filename' value='" + fileName + "'/>");
        $downloadForm.append("<input type='hidden' name='data' class='data'/>");
        if (fileType !== null) {
            $downloadForm.append(`<input type='hidden' name='filetype' value='${fileType}'/>`);
        }
        $downloadForm.append("<input type='hidden' name='nonce' value='" + nonce + "'/>");
        // The x-www-form-urlencoded spec replaces newlines with \n\r
        $downloadForm.find(".data").val(data.replace(/\n\r/g, "\n"));
        let downloadTimer = setInterval(function checkCookie() {
            if (document.cookie.indexOf(nonce) !== -1) {
                clearInterval(downloadTimer);
                resolve(fileName);
            }
        }, 100);
        $downloadForm.submit();
    });
}

/**
 * Triggers a file download in the browser using a hidden
 * link and a data url.
 *
 * The download attribute doesn't work in IE and Safari:
 * http://caniuse.com/#feat=download
 *
 * @param <String> dataURL The dataURL of the data
 * @param <String> filename The requested file name
 */
function downloadDataByLink(dataURL, fileName) {
    $("a.downloadLink").remove();
    $("body").append("");
    let $downloadLink = $("a.downloadLink").attr("href", dataURL);
    $downloadLink[0].click();
}

// a promise based get function
// from http://www.html5rocks.com/en/tutorials/es6/promises/
function get(url) {
    // Return a new promise.
    return new Promise(function (resolve, reject) {
    // Do the usual XHR stuff
        let req = new XMLHttpRequest();
        req.open("GET", url);

        req.onload = function () {
            // This is called even on 404 etc
            // so check the status
            if (req.status === 200) {
                // Resolve the promise with the response text
                resolve(req.response);
            } else {
                // Otherwise reject with the status text
                // which will hopefully be a meaningful error
                reject(Error(req.statusText));
            }
        };

        // Handle network errors
        req.onerror = function () {
            reject(Error("Network Error"));
        };

        // Make the request
        req.send();
    });
}

function getJSON(url) {
    return get(url).then(JSON.parse);
}

/*
 * Returns the readable text color based on the brightness of a given backgroud color
 */
function getReadableColorFor(color) {
    let textColor;
    try {
        textColor = brightness(d3.rgb(color)) < 125 ? "#eee" : "#000";
    } catch (err) {
        textColor = "#000";
    }
    return textColor;
}

// highlights the background color
// of the given element for 2 seconds
function highlight(element) {
    $(element).addClass("flash");
    setTimeout(function () {
        $(element).removeClass("flash");
    }, 2000);
}

/**
 * Takes an iterator and puts all values in an array
 */
function iteratorToArray(iterator) {
    let vals = [],
        v;
    v = iterator.next();
    while (!v.done) {
        vals.push(v.value);
        v = iterator.next();
    }
    return vals;
}

/*
 * Logs a message as exception to Google Analytics
 */
function logErrorToGoogle(errorMessage) {
    logToGoogle("Global", "Exception", errorMessage);
}

/*
 * Logs data to Google Analytics
 */
function logToGoogle(page, action, name, value) {
    if (typeof(_gaq) !== "undefined") {
        if (name === undefined) {
            _gaq.push(["_trackEvent", page, action]);
        } else if (value === undefined) {
            _gaq.push(["_trackEvent", page, action, name]);
        } else {
            _gaq.push(["_trackEvent", page, action, name, value]);
        }
    }
}

/* function for error handling.
 * first parameter is the error that gets logged to the console
 * second parameter is optional message to display to the user
 */
function showError(errorMessage, userMessage) {
    if (errorMessage !== null) {
        logErrorToGoogle(errorMessage);
        if (typeof console !== "undefined") {
            /* eslint-disable no-console */
            console.error(errorMessage);
            /* eslint-enable no-console */
        }
    }
    if (userMessage) {
        let msg = $("
<button type='button' class='close' data-dismiss='alert'>×</button>Oh snap! " + userMessage + "
");
        $("#messages").append(msg);
        msg.show("normal");
    }
}

/* display the message variable in an info alert
 */
function showInfo(message) {
    let msg = $("
<button type='button' class='close' data-dismiss='alert'>×</button>Heads up! " + message + "
");
    $("#messages").append(msg);
    msg.show("normal");
}

/**
 * Hash function for strings from
 * http://stackoverflow.com/a/15710692/865696
 */
function stringHash(s) {
    return s.split("").reduce(function (a, b) {
        let r = ((a << 5) - a) + b.charCodeAt(0);
        return r & r;
    }, 0);
}

/**
 * Change string to title cases
 * https://stackoverflow.com/a/196991
 */
function stringTitleize(s){
    return s.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
}

/**
 * Converts a javascript array of array to RFC 4180 complient CSV
 *
 * Each record is located on a separate line, delimited by a line break (CRLF)
 *
 * Fields containing line breaks (CRLF), double quotes, and commas should be
 * enclosed in double-quotes; If double-quotes are used to enclose fields, then
 * a double-quote appearing inside a field must be escaped by preceding it with
 * another double quote.
 *
 * Prepending `data:text/csv,` makes a valid data url
 * @param  {[[string]]} grid [description]
 * @return {string}      The csv string
 */
function toCSVString(grid) {
    return grid.map(line =>
        line.map(cell => {
            let content = cell.toString();
            if (content.includes(",") || content.includes("\"") ||
                content.includes("\n") || content.includes("\r")) {
                return `"${content.replace(/"/g, "\"\"")}"`;
            } else {
                return content;
            }
        }).join(",")
    ).join("\n\r");
}

/**
 * Triggers the image export modal.
 *
 * If an svgSelector is present, sends the SVG-code to the server to convert
 * it to a PNG. The server return a data URL containing the PNG data.
 * In an canvasSelector is present, uses html2canvas to convert it to a PNG.
 * A modal dialog is shown containing the image and buttons
 * to download the image as PNG and SVG if present.
 *
 * @param <String> svgSelector The DOM selector of the SVG
 * @param <String> canvasSelector The DOM selector of the canvas
 * @param <String> baseFileName The requested file name
 */
function triggerDownloadModal(svgSelector, canvasSelector, baseFileName) {
    let $buttons = $("#save-as-modal .buttons"),
        $image = $("#save-as-modal .image"),
        $element,
        svg;

    // Reset the modal and show it
    $buttons.html("
Please wait while we create your image
");
    $image.html("
Loading preview...
" +
        "
" +
        "

");
    $("#save-as-modal").modal();

    // Generate the image
    if (svgSelector) {
        // Send the SVG code to the server for png conversion
        $element = $(svgSelector);
        svg = $element.wrap("

").parent().html();
        $element.unwrap();
        $.post("/convert", {image: svg}, showImage);
    }
    if (canvasSelector) {
        // Use html2canvas to convert canvas to dataURL
        html2canvas($(canvasSelector), {
            onrendered: function (canvas) {
                showImage(canvas.toDataURL());
            },
        });
    }

    if (window.fullScreenApi.isFullScreen()) {
        window.fullScreenApi.cancelFullScreen();
    }

    // Show the image and add buttons
    function showImage(dataURL) {
        $image.html("");
        $buttons.empty();
        if (svgSelector) {
            $buttons.append("<button id='download-svg' class='btn btn-primary btn-animate'> Download as SVG</button>");
            $("#download-svg").click(function () {
                downloadDataByForm(svg, baseFileName + ".svg");
            });
        }
        $buttons.append("<button id='download-png' class='btn btn-primary btn-animate'> Download as PNG</button>");
        $("#download-png").click(function () {
            downloadDataByLink($("#save-as-modal .image img").attr("src"), baseFileName + ".png");
        });
    }
}

export {addCopy, brightness, downloadData, downloadDataByForm, downloadDataByLink, get, getJSON, getReadableColorFor, highlight, iteratorToArray, logErrorToGoogle, logToGoogle, showError, showInfo, stringHash, stringTitleize, toCSVString, triggerDownloadModal};
<commit_msg>clean(sequences): Add a fraction to percentage function to utils<commit_after>import Clipboard from "clipboard";

function addCopy(selector, textFunction, tooltip = "Copy to clipboard") {
    const $el = $(selector).data("placement", "top")
        .attr("title", tooltip)
        .tooltip();
    const clip = new Clipboard(selector, {
        text: textFunction,
    });
    clip.on("success", e => {
        $el.attr("title", "Copied!")
            .tooltip("fixTitle")
            .tooltip("show")
            .attr("title", tooltip)
            .tooltip("fixTitle");
    });
    clip.on("error", e => {
        $el.attr("title", "Sorry, something went wrong")
            .tooltip("fixTitle")
            .tooltip("show")
            .attr("title", tooltip)
            .tooltip("fixTitle");
    });
}

/*
 * Returns the brightness of an rgb-color
 * from: http:// www.w3.org/WAI/ER/WD-AERT/#color-contrast
 */
function brightness(rgb) {
    return rgb.r * 0.299 + rgb.g * 0.587 + rgb.b * 0.114;
}

/**
 * Download data with polyfill for browsers that don't support the download attr
 * @param  {[type]}  data                    data to download
 * @param  {[type]}  fileName                filename
 * @param  {String}  [fileType="text/plain"] filetype eg text/csv
 * @return {Promise.<string>}                Promice that returns when downloaded
 */
function downloadData(data, fileName, fileType="text/plain") {
    if ("download" in document.createElement("a")) {
        return new Promise(function (resolve, reject) {
            downloadDataByLink(`data:${fileType};base64,${btoa(data)}`, fileName);
            resolve(fileName);
        });
    } else {
        return downloadDataByForm(data, fileName, fileType);
    }
}

/**
 * Triggers a file download in the browser using a hidden
 * form and a server round trip. Returns a Promise that resolves when
 * the file starts downloading
 *
 * @param {string} data The text you want in the file
 * @param {string} fileName The requested file name
 * @return {Promise.<string>}
 */
function downloadDataByForm(data, fileName, fileType=null) {
    return new Promise(function (resolve, reject) {
        let nonce = Math.random();
        $("form.download").remove();
        $("body").append("<form class='download' method='post' action='/download'></form>");
        let $downloadForm = $("form.download").append("<input type='hidden' name='filename' value='" + fileName + "'/>");
        $downloadForm.append("<input type='hidden' name='data' class='data'/>");
        if (fileType !== null) {
            $downloadForm.append(`<input type='hidden' name='filetype' value='${fileType}'/>`);
        }
        $downloadForm.append("<input type='hidden' name='nonce' value='" + nonce + "'/>");
        // The x-www-form-urlencoded spec replaces newlines with \n\r
        $downloadForm.find(".data").val(data.replace(/\n\r/g, "\n"));
        let downloadTimer = setInterval(function checkCookie() {
            if (document.cookie.indexOf(nonce) !== -1) {
                clearInterval(downloadTimer);
                resolve(fileName);
            }
        }, 100);
        $downloadForm.submit();
    });
}

/**
 * Triggers a file download in the browser using a hidden
 * link and a data url.
 *
 * The download attribute doesn't work in IE and Safari:
 * http://caniuse.com/#feat=download
 *
 * @param <String> dataURL The dataURL of the data
 * @param <String> filename The requested file name
 */
function downloadDataByLink(dataURL, fileName) {
    $("a.downloadLink").remove();
    $("body").append("");
    let $downloadLink = $("a.downloadLink").attr("href", dataURL);
    $downloadLink[0].click();
}

// a promise based get function
// from http://www.html5rocks.com/en/tutorials/es6/promises/
function get(url) {
    // Return a new promise.
    return new Promise(function (resolve, reject) {
    // Do the usual XHR stuff
        let req = new XMLHttpRequest();
        req.open("GET", url);

        req.onload = function () {
            // This is called even on 404 etc
            // so check the status
            if (req.status === 200) {
                // Resolve the promise with the response text
                resolve(req.response);
            } else {
                // Otherwise reject with the status text
                // which will hopefully be a meaningful error
                reject(Error(req.statusText));
            }
        };

        // Handle network errors
        req.onerror = function () {
            reject(Error("Network Error"));
        };

        // Make the request
        req.send();
    });
}

function getJSON(url) {
    return get(url).then(JSON.parse);
}

/*
 * Returns the readable text color based on the brightness of a given backgroud color
 */
function getReadableColorFor(color) {
    let textColor;
    try {
        textColor = brightness(d3.rgb(color)) < 125 ? "#eee" : "#000";
    } catch (err) {
        textColor = "#000";
    }
    return textColor;
}

// highlights the background color
// of the given element for 2 seconds
function highlight(element) {
    $(element).addClass("flash");
    setTimeout(function () {
        $(element).removeClass("flash");
    }, 2000);
}

/**
 * Takes an iterator and puts all values in an array
 */
function iteratorToArray(iterator) {
    let vals = [],
        v;
    v = iterator.next();
    while (!v.done) {
        vals.push(v.value);
        v = iterator.next();
    }
    return vals;
}

/*
 * Logs a message as exception to Google Analytics
 */
function logErrorToGoogle(errorMessage) {
    logToGoogle("Global", "Exception", errorMessage);
}

/*
 * Logs data to Google Analytics
 */
function logToGoogle(page, action, name, value) {
    if (typeof(_gaq) !== "undefined") {
        if (name === undefined) {
            _gaq.push(["_trackEvent", page, action]);
        } else if (value === undefined) {
            _gaq.push(["_trackEvent", page, action, name]);
        } else {
            _gaq.push(["_trackEvent", page, action, name, value]);
        }
    }
}

/* function for error handling.
 * first parameter is the error that gets logged to the console
 * second parameter is optional message to display to the user
 */
function showError(errorMessage, userMessage) {
    if (errorMessage !== null) {
        logErrorToGoogle(errorMessage);
        if (typeof console !== "undefined") {
            /* eslint-disable no-console */
            console.error(errorMessage);
            /* eslint-enable no-console */
        }
    }
    if (userMessage) {
        let msg = $("
<button type='button' class='close' data-dismiss='alert'>×</button>Oh snap! " + userMessage + "
");
        $("#messages").append(msg);
        msg.show("normal");
    }
}

/* display the message variable in an info alert
 */
function showInfo(message) {
    let msg = $("
<button type='button' class='close' data-dismiss='alert'>×</button>Heads up! " + message + "
");
    $("#messages").append(msg);
    msg.show("normal");
}

/**
 * Hash function for strings from
 * http://stackoverflow.com/a/15710692/865696
 */
function stringHash(s) {
    return s.split("").reduce(function (a, b) {
        let r = ((a << 5) - a) + b.charCodeAt(0);
        return r & r;
    }, 0);
}

/**
 * Change string to title cases
 * https://stackoverflow.com/a/196991
 */
function stringTitleize(s){
    return s.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
}

/**
 * Converts a javascript array of array to RFC 4180 complient CSV
 *
 * Each record is located on a separate line, delimited by a line break (CRLF)
 *
 * Fields containing line breaks (CRLF), double quotes, and commas should be
 * enclosed in double-quotes; If double-quotes are used to enclose fields, then
 * a double-quote appearing inside a field must be escaped by preceding it with
 * another double quote.
 *
 * Prepending `data:text/csv,` makes a valid data url
 * @param  {[[string]]} grid [description]
 * @return {string}      The csv string
 */
function toCSVString(grid) {
    return grid.map(line =>
        line.map(cell => {
            let content = cell.toString();
            if (content.includes(",") || content.includes("\"") ||
                content.includes("\n") || content.includes("\r")) {
                return `"${content.replace(/"/g, "\"\"")}"`;
            } else {
                return content;
            }
        }).join(",")
    ).join("\n\r");
}

/**
 * Convert a number to a percentage,
 * 0.1 => "10%".
 * @param  {Number} number     [description]
 * @param  {Number} [digits=0] [description]
 * @return {string}            [description]
 */
function numberToPercent(number, digits=0) {
    return (100*number).toFixed(digits)+"%";
}

/**
 * Triggers the image export modal.
 *
 * If an svgSelector is present, sends the SVG-code to the server to convert
 * it to a PNG. The server return a data URL containing the PNG data.
 * In an canvasSelector is present, uses html2canvas to convert it to a PNG.
 * A modal dialog is shown containing the image and buttons
 * to download the image as PNG and SVG if present.
 *
 * @param <String> svgSelector The DOM selector of the SVG
 * @param <String> canvasSelector The DOM selector of the canvas
 * @param <String> baseFileName The requested file name
 */
function triggerDownloadModal(svgSelector, canvasSelector, baseFileName) {
    let $buttons = $("#save-as-modal .buttons"),
        $image = $("#save-as-modal .image"),
        $element,
        svg;

    // Reset the modal and show it
    $buttons.html("
Please wait while we create your image
");
    $image.html("
Loading preview...
" +
        "
" +
        "

");
    $("#save-as-modal").modal();

    // Generate the image
    if (svgSelector) {
        // Send the SVG code to the server for png conversion
        $element = $(svgSelector);
        svg = $element.wrap("

").parent().html();
        $element.unwrap();
        $.post("/convert", {image: svg}, showImage);
    }
    if (canvasSelector) {
        // Use html2canvas to convert canvas to dataURL
        html2canvas($(canvasSelector), {
            onrendered: function (canvas) {
                showImage(canvas.toDataURL());
            },
        });
    }

    if (window.fullScreenApi.isFullScreen()) {
        window.fullScreenApi.cancelFullScreen();
    }

    // Show the image and add buttons
    function showImage(dataURL) {
        $image.html("");
        $buttons.empty();
        if (svgSelector) {
            $buttons.append("<button id='download-svg' class='btn btn-primary btn-animate'> Download as SVG</button>");
            $("#download-svg").click(function () {
                downloadDataByForm(svg, baseFileName + ".svg");
            });
        }
        $buttons.append("<button id='download-png' class='btn btn-primary btn-animate'> Download as PNG</button>");
        $("#download-png").click(function () {
            downloadDataByLink($("#save-as-modal .image img").attr("src"), baseFileName + ".png");
        });
    }
}

export {addCopy, brightness, downloadData, downloadDataByForm, downloadDataByLink, get, getJSON, getReadableColorFor, highlight, iteratorToArray, logErrorToGoogle, logToGoogle, showError, showInfo, stringHash, stringTitleize, toCSVString, numberToPercent, triggerDownloadModal};
<|endoftext|>