<commit_before>const moment = require('moment')

const jll = require('./jiraloglist')

describe('createTrackingObject', () => {
  const response = require('./testResponse.json')
  const result = require('./trackingObject.json')

  test('should not error with predefined data', () => {
    expect(() => jll.createTrackingObject(response, moment('2017-03-01'), 1)).not.toThrow()
  })

  describe('patricks worklog', () => {
    const tracking = jll.createTrackingObject(response, moment('2017-03-01'), 1)
    const data = tracking['patrick.krassler']

    test('should have worked on 3 issues', () => {
      expect(Object.keys(data.issues).length).toBe(3)
    })
    test('should have a total of 5h', () => {
      expect(data.timeSpent).toBe(5 * 60 * 60)
    })
    test('should have worked 45m on CMD-2840', () => {
      expect(data.issues['CMD-2840'].timeSpent).toBe(45 * 60)
    })
    test('should have worked 1h 15m on CMD-2839', () => {
      expect(data.issues['CMD-2839'].timeSpent).toBe(75 * 60)
    })
    test('should have worked 3h on CMD-2707', () => {
      expect(data.issues['CMD-2707'].timeSpent).toBe(3 * 60 * 60)
    })
    test('should create correct tracking object', () => {
      expect(tracking).toEqual(result)
    })
  })

  describe('generateSlackMessage', () => {
    const msg = jll.generateSlackMessage(result, moment('2017-03-01'), 1)

    test('should have a message text', () => {
      expect(msg.text).toBe('WORKLOG for: 2017-03-01 to 2017-03-02')
    })
    test('should have a list of user log entries', () => {
      expect(msg.attachments).toHaveLength(5)
    })
    describe('attachments', () => {
      attachment = msg.attachments[0]
      test('should contain user info', () => {
        expect(attachment.title).toBe('daniel.paul logged 6.3h')
      })
      test('should list issues worked on', () => {
        expect(attachment.fields).toHaveLength(3)
      })
    })
    test('should contain user info', () => {
    })
  })
})
<commit_msg>fix lint issue<commit_after>const moment = require('moment')

const jll = require('./jiraloglist')

describe('createTrackingObject', () => {
  const response = require('./testResponse.json')
  const result = require('./trackingObject.json')

  test('should not error with predefined data', () => {
    expect(() => jll.createTrackingObject(response, moment('2017-03-01'), 1)).not.toThrow()
  })

  describe('patricks worklog', () => {
    const tracking = jll.createTrackingObject(response, moment('2017-03-01'), 1)
    const data = tracking['patrick.krassler']

    test('should have worked on 3 issues', () => {
      expect(Object.keys(data.issues).length).toBe(3)
    })
    test('should have a total of 5h', () => {
      expect(data.timeSpent).toBe(5 * 60 * 60)
    })
    test('should have worked 45m on CMD-2840', () => {
      expect(data.issues['CMD-2840'].timeSpent).toBe(45 * 60)
    })
    test('should have worked 1h 15m on CMD-2839', () => {
      expect(data.issues['CMD-2839'].timeSpent).toBe(75 * 60)
    })
    test('should have worked 3h on CMD-2707', () => {
      expect(data.issues['CMD-2707'].timeSpent).toBe(3 * 60 * 60)
    })
    test('should create correct tracking object', () => {
      expect(tracking).toEqual(result)
    })
  })

  describe('generateSlackMessage', () => {
    const msg = jll.generateSlackMessage(result, moment('2017-03-01'), 1)

    test('should have a message text', () => {
      expect(msg.text).toBe('WORKLOG for: 2017-03-01 to 2017-03-02')
    })
    test('should have a list of user log entries', () => {
      expect(msg.attachments).toHaveLength(5)
    })
    describe('attachments', () => {
      const attachment = msg.attachments[0]
      test('should contain user info', () => {
        expect(attachment.title).toBe('daniel.paul logged 6.3h')
      })
      test('should list issues worked on', () => {
        expect(attachment.fields).toHaveLength(3)
      })
    })
    test('should contain user info', () => {
    })
  })
})
<|endoftext|>


<commit_before>// From https://gist.github.com/1343518, modified to not load showdown
(function boom(){

  [].forEach.call( document.querySelectorAll('[data-markdown]'), function  fn(elem){

    // strip leading whitespace so it isn't evaluated as code
    var text      = elem.innerHTML.replace(/\n\s*\n/g,'\n'),
        // set indentation level so your markdown can be indented within your HTML
        leadingws = text.match(/^\n?(\s*)/)[1].length,
        regex     = new RegExp('\\n?\\s{' + leadingws + '}','g'),
        md        = text.replace(regex,'\n'),
        html      = (new Showdown.converter()).makeHtml(md);

    // here, have sum HTML
    elem.innerHTML = html;

  });

}());<commit_msg>self-execute data-markdown<commit_after>// From https://gist.github.com/1343518, modified to not load showdown
(function(){

  [].forEach.call( document.querySelectorAll('[data-markdown]'), function  fn(elem){

    // strip leading whitespace so it isn't evaluated as code
    var text      = elem.innerHTML.replace(/\n\s*\n/g,'\n'),
        // set indentation level so your markdown can be indented within your HTML
        leadingws = text.match(/^\n?(\s*)/)[1].length,
        regex     = new RegExp('\\n?\\s{' + leadingws + '}','g'),
        md        = text.replace(regex,'\n'),
        html      = (new Showdown.converter()).makeHtml(md);

    // here, have sum HTML
    elem.innerHTML = html;

  });

})();<|endoftext|>


<commit_before>/**
  If you want to add a new sharing source to Discourse, you can do so like this:

  ```javascript
    import Sharing from 'discourse/lib/sharing';

    Sharing.addSource({

      // This id must be present in the `share_links` site setting too
      id: 'twitter',

      // The icon that will be displayed
      iconClass: 'fa-twitter-square',

      // A callback for generating the remote link from the `link` and `title`
      generateUrl: function(link, title) {
        return "http://twitter.com/intent/tweet?url=" + encodeURIComponent(link) + "&text=" + encodeURIComponent(title);
      },

      // If true, opens in a popup of `popupHeight` size. If false it's opened in a new tab
      shouldOpenInPopup: true,
      popupHeight: 265
    });
  ```
**/

var _sources = [];

export default {
  addSource: function (source) {
    _sources.push(source);
  },

  activeSources: function() {
    var enabled = Discourse.SiteSettings.share_links.split('|');
    return _sources.filter(function(s) {
      return enabled.indexOf(s.id) !== -1;
    });
  }
};
<commit_msg>FIX: Return sharing sources in the same order as the site setting<commit_after>/**
  If you want to add a new sharing source to Discourse, you can do so like this:

  ```javascript
    import Sharing from 'discourse/lib/sharing';

    Sharing.addSource({

      // This id must be present in the `share_links` site setting too
      id: 'twitter',

      // The icon that will be displayed
      iconClass: 'fa-twitter-square',

      // A callback for generating the remote link from the `link` and `title`
      generateUrl: function(link, title) {
        return "http://twitter.com/intent/tweet?url=" + encodeURIComponent(link) + "&text=" + encodeURIComponent(title);
      },

      // If true, opens in a popup of `popupHeight` size. If false it's opened in a new tab
      shouldOpenInPopup: true,
      popupHeight: 265
    });
  ```
**/

var _sources = {};

export default {
  addSource(source) {
    _sources[source.id] = source;
  },

  activeSources() {
    const enabled = Discourse.SiteSettings.share_links.split('|');
    return enabled.map(s => _sources[s]).compact();
  }
};
<|endoftext|>


<commit_before>(function($) {

  var filters;

  $.filters = filters = {
    options: {
      regional: {
        datePicker: {
          dateFormat: 'mm/dd/yy'
        }
      }
    },

    append: function(field_label, field_name, field_type, field_value, field_operator, field_options, index) {
      var value_name = 'f[' +  field_name + '][' + index + '][v]';
      var operator_name = 'f[' +  field_name + '][' + index + '][o]';
      switch(field_type) {
        case 'boolean':
          var control = '<select class="input-small" name="' + value_name + '">' +
            '<option value="_discard">...</option>' +
            '<option value="true"' + (field_value == "true" ? 'selected="selected"' : '') + '>' + RailsAdmin.I18n.t("true") + '</option>' +
            '<option value="false"' + (field_value == "false" ? 'selected="selected"' : '') + '>' + RailsAdmin.I18n.t("false") + '</option>' +
            '<option disabled="disabled">---------</option>' +
            '<option ' + (field_value == "_present" ? 'selected="selected"' : '') + ' value="_present">' + RailsAdmin.I18n.t("is_present") + '</option>' +
            '<option ' + (field_value == "_blank"   ? 'selected="selected"' : '') + ' value="_blank"  >' + RailsAdmin.I18n.t("is_blank") + '</option>' +
          '</select>';
          break;
        case 'date':
        case 'datetime':
        case 'timestamp':
          var control = '<select class="switch-additionnal-fieldsets input-small" name="' + operator_name + '">' +
            '<option ' + (field_operator == "default"   ? 'selected="selected"' : '') + ' data-additional-fieldset="default" value="default">' + RailsAdmin.I18n.t("date") + '</option>' +
            '<option ' + (field_operator == "between"   ? 'selected="selected"' : '') + ' data-additional-fieldset="between" value="between">' + RailsAdmin.I18n.t("between_and_") + '</option>' +
            '<option ' + (field_operator == "today"   ? 'selected="selected"' : '') + ' value="today">' + RailsAdmin.I18n.t("today") + '</option>' +
            '<option ' + (field_operator == "yesterday"   ? 'selected="selected"' : '') + ' value="yesterday">' + RailsAdmin.I18n.t("yesterday") + '</option>' +
            '<option ' + (field_operator == "this_week"   ? 'selected="selected"' : '') + ' value="this_week">' + RailsAdmin.I18n.t("this_week") + '</option>' +
            '<option ' + (field_operator == "last_week"   ? 'selected="selected"' : '') + ' value="last_week">' + RailsAdmin.I18n.t("last_week") + '</option>' +
            '<option disabled="disabled">---------</option>' +
            '<option ' + (field_operator == "_not_null" ? 'selected="selected"' : '') + ' value="_not_null">' + RailsAdmin.I18n.t("is_present") + '</option>' +
            '<option ' + (field_operator == "_null"     ? 'selected="selected"' : '') + ' value="_null" >' + RailsAdmin.I18n.t("is_blank") + '</option>' +
          '</select>'
          var additional_control =
          '<input class="date additional-fieldset default input-small" style="display:' + ((!field_operator || field_operator == "default") ? 'inline-block' : 'none') + ';" type="text" name="' + value_name + '[]" value="' + (field_value[0] || '') + '" /> ' +
          '<input placeholder="-∞" class="date additional-fieldset between input-small" style="display:' + ((field_operator == "between") ? 'inline-block' : 'none') + ';" type="text" name="' + value_name + '[]" value="' + (field_value[1] || '') + '" /> ' +
          '<input placeholder="∞" class="date additional-fieldset between input-small" style="display:' + ((field_operator == "between") ? 'inline-block' : 'none') + ';" type="text" name="' + value_name + '[]" value="' + (field_value[2] || '') + '" />';
          break;
        case 'enum':
          var multiple_values = ((field_value instanceof Array) ? true : false)
          var control = '<select style="display:' + (multiple_values ? 'none' : 'inline-block') + '" ' + (multiple_values ? '' : 'name="' + value_name + '"') + ' data-name="' + value_name + '" class="select-single input-small">' +
              '<option value="_discard">...</option>' +
              '<option ' + (field_value == "_present" ? 'selected="selected"' : '') + ' value="_present">' + RailsAdmin.I18n.t("is_present") + '</option>' +
              '<option ' + (field_value == "_blank"   ? 'selected="selected"' : '') + ' value="_blank">' + RailsAdmin.I18n.t("is_blank") + '</option>' +
              '<option disabled="disabled">---------</option>' +
              field_options +
            '</select>' +
            '<select multiple="multiple" style="display:' + (multiple_values ? 'inline-block' : 'none') + '" ' + (multiple_values ? 'name="' + value_name + '[]"' : '') + ' data-name="' + value_name + '[]" class="select-multiple input-small">' +
              field_options +
            '</select> ' +
            '';
        break;
        case 'string':
        case 'text':
        case 'belongs_to_association':
          var control = '<select class="switch-additionnal-fieldsets input-small" value="' + field_operator + '" name="' + operator_name + '">' +
            '<option data-additional-fieldset="additional-fieldset"'  + (field_operator == "like"        ? 'selected="selected"' : '') + ' value="like">' + RailsAdmin.I18n.t("contains") + '</option>' +
            '<option data-additional-fieldset="additional-fieldset"'  + (field_operator == "is"          ? 'selected="selected"' : '') + ' value="is">' + RailsAdmin.I18n.t("is_exactly") + '</option>' +
            '<option data-additional-fieldset="additional-fieldset"'  + (field_operator == "starts_with" ? 'selected="selected"' : '') + ' value="starts_with">' + RailsAdmin.I18n.t("starts_with") + '</option>' +
            '<option data-additional-fieldset="additional-fieldset"'  + (field_operator == "ends_with"   ? 'selected="selected"' : '') + ' value="ends_with">' + RailsAdmin.I18n.t("ends_with") + '</option>' +
            '<option disabled="disabled">---------</option>' +
            '<option ' + (field_operator == "_not_null"    ? 'selected="selected"' : '') + ' value="_present">' + RailsAdmin.I18n.t("is_present") + '</option>' +
            '<option ' + (field_operator == "_null"      ? 'selected="selected"' : '') + ' value="_blank">' + RailsAdmin.I18n.t("is_blank") + '</option>' +
          '</select>'
          var additional_control = '<input class="additional-fieldset input-small" style="display:' + (field_operator == "_blank" || field_operator == "_present" ? 'none' : 'inline-block') + ';" type="text" name="' + value_name + '" value="' + field_value + '" /> ';
          break;
        case 'integer':
        case 'decimal':
        case 'float':
          var control = '<select class="switch-additionnal-fieldsets input-small" name="' + operator_name + '">' +
            '<option ' + (field_operator == "default"   ? 'selected="selected"' : '') + ' data-additional-fieldset="default" value="default">' + RailsAdmin.I18n.t("Number ...") + '</option>' +
            '<option ' + (field_operator == "between"   ? 'selected="selected"' : '') + ' data-additional-fieldset="between" value="between">' + RailsAdmin.I18n.t("Between ... and ...") + '</option>' +
            '<option disabled="disabled">---------</option>' +
            '<option ' + (field_operator == "_not_null" ? 'selected="selected"' : '') + ' value="_not_null">' + RailsAdmin.I18n.t("is_present") +'</option>' +
            '<option ' + (field_operator == "_null"     ? 'selected="selected"' : '') + ' value="_null" >' + RailsAdmin.I18n.t("is_blank") + '</option>' +
          '</select>'
          var additional_control =
          '<input class="additional-fieldset default input-small" style="display:' + ((!field_operator || field_operator == "default") ? 'inline-block' : 'none') + ';" type="' + field_type + '" name="' + value_name + '[]" value="' + (field_value[0] || '') + '" /> ' +
          '<input placeholder="-∞" class="additional-fieldset between input-small" style="display:' + ((field_operator == "between") ? 'inline-block' : 'none') + ';" type="' + field_type + '" name="' + value_name + '[]" value="' + (field_value[1] || '') + '" /> ' +
          '<input placeholder="∞" class="additional-fieldset between input-small" style="display:' + ((field_operator == "between") ? 'inline-block' : 'none') + ';" type="' + field_type + '" name="' + value_name + '[]" value="' + (field_value[2] || '') + '" />';
          break;
        default:
          var control = '<input type="text" class="input-small" name="' + value_name + '" value="' + field_value + '"/> ';
          break;
      }

      var content = '
' +
        ' ' + field_label + ' ' +
        control + " " +
        (additional_control || '') +
      '
 ';
      $('#filters_box').append(content);
      $('#filters_box .date').datepicker(this.options.regional.datePicker);
      $("hr.filters_box:hidden").show('slow');
    }
  }

  $(document).on('click', "#filters a", function(e) {
    e.preventDefault();
    $.filters.append(
      $(this).data('field-label'),
      $(this).data('field-name'),
      $(this).data('field-type'),
      $(this).data('field-value'),
      $(this).data('field-operator'),
      $(this).data('field-options'),
      $.now().toString().slice(6,11)
    );
  });

  $(document).on('click', "#filters_box .delete", function(e) {
    e.preventDefault();
    form = $(this).parents('form');
    $(this).parents('.filter').remove();
    !$("#filters_box").children().length && $("hr.filters_box:visible").hide('slow');
  });

  $(document).on('click', "#filters_box .switch-select", function(e) {
    e.preventDefault();
    var selected_select = $(this).siblings('select:visible');
    var not_selected_select = $(this).siblings('select:hidden');
    not_selected_select.attr('name', not_selected_select.data('name')).show('slow');
    selected_select.attr('name', null).hide('slow');
    $(this).find('i').toggleClass("icon-plus icon-minus")
  });

  $(document).on('change', "#filters_box .switch-additionnal-fieldsets", function(e) {
    var selected_option = $(this).find('option:selected');
    if(klass = $(selected_option).data('additional-fieldset')) {
      $(this).siblings('.additional-fieldset:not(.' + klass + ')').hide('slow');
      $(this).siblings('.' + klass).show('slow');
    } else {
      $(this).siblings('.additional-fieldset').hide('slow');
    }
  });
})( jQuery );
<commit_msg>really fix it<commit_after>(function($) {

  var filters;

  $.filters = filters = {
    options: {
      regional: {
        datePicker: {
          dateFormat: 'mm/dd/yy'
        }
      }
    },

    append: function(field_label, field_name, field_type, field_value, field_operator, field_options, index) {
      var value_name = 'f[' +  field_name + '][' + index + '][v]';
      var operator_name = 'f[' +  field_name + '][' + index + '][o]';
      switch(field_type) {
        case 'boolean':
          var control = '<select class="input-small" name="' + value_name + '">' +
            '<option value="_discard">...</option>' +
            '<option value="true"' + (field_value == "true" ? 'selected="selected"' : '') + '>' + RailsAdmin.I18n.t("true") + '</option>' +
            '<option value="false"' + (field_value == "false" ? 'selected="selected"' : '') + '>' + RailsAdmin.I18n.t("false") + '</option>' +
            '<option disabled="disabled">---------</option>' +
            '<option ' + (field_value == "_present" ? 'selected="selected"' : '') + ' value="_present">' + RailsAdmin.I18n.t("is_present") + '</option>' +
            '<option ' + (field_value == "_blank"   ? 'selected="selected"' : '') + ' value="_blank"  >' + RailsAdmin.I18n.t("is_blank") + '</option>' +
          '</select>';
          break;
        case 'date':
        case 'datetime':
        case 'timestamp':
          var control = '<select class="switch-additionnal-fieldsets input-small" name="' + operator_name + '">' +
            '<option ' + (field_operator == "default"   ? 'selected="selected"' : '') + ' data-additional-fieldset="default" value="default">' + RailsAdmin.I18n.t("date") + '</option>' +
            '<option ' + (field_operator == "between"   ? 'selected="selected"' : '') + ' data-additional-fieldset="between" value="between">' + RailsAdmin.I18n.t("between_and_") + '</option>' +
            '<option ' + (field_operator == "today"   ? 'selected="selected"' : '') + ' value="today">' + RailsAdmin.I18n.t("today") + '</option>' +
            '<option ' + (field_operator == "yesterday"   ? 'selected="selected"' : '') + ' value="yesterday">' + RailsAdmin.I18n.t("yesterday") + '</option>' +
            '<option ' + (field_operator == "this_week"   ? 'selected="selected"' : '') + ' value="this_week">' + RailsAdmin.I18n.t("this_week") + '</option>' +
            '<option ' + (field_operator == "last_week"   ? 'selected="selected"' : '') + ' value="last_week">' + RailsAdmin.I18n.t("last_week") + '</option>' +
            '<option disabled="disabled">---------</option>' +
            '<option ' + (field_operator == "_not_null" ? 'selected="selected"' : '') + ' value="_not_null">' + RailsAdmin.I18n.t("is_present") + '</option>' +
            '<option ' + (field_operator == "_null"     ? 'selected="selected"' : '') + ' value="_null" >' + RailsAdmin.I18n.t("is_blank") + '</option>' +
          '</select>'
          var additional_control =
          '<input class="date additional-fieldset default input-small" style="display:' + ((!field_operator || field_operator == "default") ? 'inline-block' : 'none') + ';" type="text" name="' + value_name + '[]" value="' + (field_value[0] || '') + '" /> ' +
          '<input placeholder="-∞" class="date additional-fieldset between input-small" style="display:' + ((field_operator == "between") ? 'inline-block' : 'none') + ';" type="text" name="' + value_name + '[]" value="' + (field_value[1] || '') + '" /> ' +
          '<input placeholder="∞" class="date additional-fieldset between input-small" style="display:' + ((field_operator == "between") ? 'inline-block' : 'none') + ';" type="text" name="' + value_name + '[]" value="' + (field_value[2] || '') + '" />';
          break;
        case 'enum':
          var multiple_values = ((field_value instanceof Array) ? true : false)
          var control = '<select style="display:' + (multiple_values ? 'none' : 'inline-block') + '" ' + (multiple_values ? '' : 'name="' + value_name + '"') + ' data-name="' + value_name + '" class="select-single input-small">' +
              '<option value="_discard">...</option>' +
              '<option ' + (field_value == "_present" ? 'selected="selected"' : '') + ' value="_present">' + RailsAdmin.I18n.t("is_present") + '</option>' +
              '<option ' + (field_value == "_blank"   ? 'selected="selected"' : '') + ' value="_blank">' + RailsAdmin.I18n.t("is_blank") + '</option>' +
              '<option disabled="disabled">---------</option>' +
              field_options +
            '</select>' +
            '<select multiple="multiple" style="display:' + (multiple_values ? 'inline-block' : 'none') + '" ' + (multiple_values ? 'name="' + value_name + '[]"' : '') + ' data-name="' + value_name + '[]" class="select-multiple input-small">' +
              field_options +
            '</select> ' +
            '';
        break;
        case 'string':
        case 'text':
        case 'belongs_to_association':
          var control = '<select class="switch-additionnal-fieldsets input-small" value="' + field_operator + '" name="' + operator_name + '">' +
            '<option data-additional-fieldset="additional-fieldset"'  + (field_operator == "like"        ? 'selected="selected"' : '') + ' value="like">' + RailsAdmin.I18n.t("contains") + '</option>' +
            '<option data-additional-fieldset="additional-fieldset"'  + (field_operator == "is"          ? 'selected="selected"' : '') + ' value="is">' + RailsAdmin.I18n.t("is_exactly") + '</option>' +
            '<option data-additional-fieldset="additional-fieldset"'  + (field_operator == "starts_with" ? 'selected="selected"' : '') + ' value="starts_with">' + RailsAdmin.I18n.t("starts_with") + '</option>' +
            '<option data-additional-fieldset="additional-fieldset"'  + (field_operator == "ends_with"   ? 'selected="selected"' : '') + ' value="ends_with">' + RailsAdmin.I18n.t("ends_with") + '</option>' +
            '<option disabled="disabled">---------</option>' +
            '<option ' + (field_operator == "_not_null"    ? 'selected="selected"' : '') + ' value="_not_null">' + RailsAdmin.I18n.t("is_present") + '</option>' +
            '<option ' + (field_operator == "_null"      ? 'selected="selected"' : '') + ' value="_null">' + RailsAdmin.I18n.t("is_blank") + '</option>' +
          '</select>'
          var additional_control = '<input class="additional-fieldset input-small" style="display:' + (field_operator == "_blank" || field_operator == "_present" ? 'none' : 'inline-block') + ';" type="text" name="' + value_name + '" value="' + field_value + '" /> ';
          break;
        case 'integer':
        case 'decimal':
        case 'float':
          var control = '<select class="switch-additionnal-fieldsets input-small" name="' + operator_name + '">' +
            '<option ' + (field_operator == "default"   ? 'selected="selected"' : '') + ' data-additional-fieldset="default" value="default">' + RailsAdmin.I18n.t("Number ...") + '</option>' +
            '<option ' + (field_operator == "between"   ? 'selected="selected"' : '') + ' data-additional-fieldset="between" value="between">' + RailsAdmin.I18n.t("Between ... and ...") + '</option>' +
            '<option disabled="disabled">---------</option>' +
            '<option ' + (field_operator == "_not_null" ? 'selected="selected"' : '') + ' value="_not_null">' + RailsAdmin.I18n.t("is_present") +'</option>' +
            '<option ' + (field_operator == "_null"     ? 'selected="selected"' : '') + ' value="_null" >' + RailsAdmin.I18n.t("is_blank") + '</option>' +
          '</select>'
          var additional_control =
          '<input class="additional-fieldset default input-small" style="display:' + ((!field_operator || field_operator == "default") ? 'inline-block' : 'none') + ';" type="' + field_type + '" name="' + value_name + '[]" value="' + (field_value[0] || '') + '" /> ' +
          '<input placeholder="-∞" class="additional-fieldset between input-small" style="display:' + ((field_operator == "between") ? 'inline-block' : 'none') + ';" type="' + field_type + '" name="' + value_name + '[]" value="' + (field_value[1] || '') + '" /> ' +
          '<input placeholder="∞" class="additional-fieldset between input-small" style="display:' + ((field_operator == "between") ? 'inline-block' : 'none') + ';" type="' + field_type + '" name="' + value_name + '[]" value="' + (field_value[2] || '') + '" />';
          break;
        default:
          var control = '<input type="text" class="input-small" name="' + value_name + '" value="' + field_value + '"/> ';
          break;
      }

      var content = '
' +
        ' ' + field_label + ' ' +
        control + " " +
        (additional_control || '') +
      '
 ';
      $('#filters_box').append(content);
      $('#filters_box .date').datepicker(this.options.regional.datePicker);
      $("hr.filters_box:hidden").show('slow');
    }
  }

  $(document).on('click', "#filters a", function(e) {
    e.preventDefault();
    $.filters.append(
      $(this).data('field-label'),
      $(this).data('field-name'),
      $(this).data('field-type'),
      $(this).data('field-value'),
      $(this).data('field-operator'),
      $(this).data('field-options'),
      $.now().toString().slice(6,11)
    );
  });

  $(document).on('click', "#filters_box .delete", function(e) {
    e.preventDefault();
    form = $(this).parents('form');
    $(this).parents('.filter').remove();
    !$("#filters_box").children().length && $("hr.filters_box:visible").hide('slow');
  });

  $(document).on('click', "#filters_box .switch-select", function(e) {
    e.preventDefault();
    var selected_select = $(this).siblings('select:visible');
    var not_selected_select = $(this).siblings('select:hidden');
    not_selected_select.attr('name', not_selected_select.data('name')).show('slow');
    selected_select.attr('name', null).hide('slow');
    $(this).find('i').toggleClass("icon-plus icon-minus")
  });

  $(document).on('change', "#filters_box .switch-additionnal-fieldsets", function(e) {
    var selected_option = $(this).find('option:selected');
    if(klass = $(selected_option).data('additional-fieldset')) {
      $(this).siblings('.additional-fieldset:not(.' + klass + ')').hide('slow');
      $(this).siblings('.' + klass).show('slow');
    } else {
      $(this).siblings('.additional-fieldset').hide('slow');
    }
  });
})( jQuery );
<|endoftext|>


<commit_before>// @flow
import React, { Component } from 'react';
import { observer, inject } from 'mobx-react';
import { defineMessages, intlShape } from 'react-intl';
import WalletTransactionsList from '../../../components/wallet/transactions/WalletTransactionsList';
// import WalletTransactionsSearch from '../../components/wallet/summary/WalletTransactionsSearch';
import WalletNoTransactions from '../../../components/wallet/transactions/WalletNoTransactions';
import VerticalFlexContainer from '../../../components/layout/VerticalFlexContainer';
import type { InjectedProps } from '../../../types/injectedPropsType';

const messages = defineMessages({
  noTransactions: {
    id: 'wallet.transactions.no.transactions',
    defaultMessage: '!!!No transactions',
    description: 'Message shown when wallet has no transactions yet.'
  },
  noTransactionsFound: {
    id: 'wallet.transactions.no.transactions.found',
    defaultMessage: '!!!No transactions found',
    description: 'Message shown when wallet transaction search returns zero results.'
  }
});

@inject('stores', 'actions') @observer
export default class WalletTransactionsPage extends Component {

  static defaultProps = { actions: null, stores: null };
  props: InjectedProps;

  static contextTypes = {
    intl: intlShape.isRequired,
  };

  // _handleSearchInputChange = (value: string, event: Object) => {
  //   this.props.actions.ada.transactions.filterTransactions({ searchTerm: event.target.value });
  // };

  render() {
    const { intl } = this.context;
    const actions = this.props.actions;
    const { /* transactions, */ wallets } = this.props.stores.etc;
    const activeWallet = wallets.active;
    // const {
    //   searchOptions,
    //   searchRequest,
    //   hasAny,
    //   totalAvailable,
    //   filtered,
    // } = transactions;

    // Faked missing data and methods
    const searchOptions = { searchTerm: '', searchLimit: 1000, searchSkip: 5 };
    const searchRequest = { isExecutingFirstTime: false };
    const hasAny = false;
    const totalAvailable = 0;
    const filtered = [];

    // Guard against potential null values
    if (!searchOptions || !activeWallet) return null;

    const { searchLimit, searchTerm } = searchOptions;
    const wasSearched = searchTerm !== '';
    let walletTransactions = null;
    // let transactionSearch = null;
    const noTransactionsLabel = intl.formatMessage(messages.noTransactions);
    const noTransactionsFoundLabel = intl.formatMessage(messages.noTransactionsFound);

    // if (wasSearched || hasAny) {
    //   transactionSearch = (
    //

    //       <WalletTransactionsSearch
    //         searchTerm={searchTerm}
    //         onChange={this._handleSearchInputChange}
    //       />
    //

    //   );
    // }

    if (searchRequest.isExecutingFirstTime || hasAny) {
      walletTransactions = (
        <WalletTransactionsList
          transactions={filtered}
          isLoadingTransactions={searchRequest.isExecutingFirstTime}
          hasMoreToLoad={totalAvailable > searchLimit}
          onLoadMore={actions.ada.transactions.loadMoreTransactions.trigger}
          assuranceMode={activeWallet.assuranceMode}
          walletId={activeWallet.id}
        />
      );
    } else if (wasSearched && !hasAny) {
      walletTransactions = <WalletNoTransactions label={noTransactionsFoundLabel} />;
    } else if (!hasAny) {
      walletTransactions = <WalletNoTransactions label={noTransactionsLabel} />;
    }

    return (
      <VerticalFlexContainer>
        {/* transactionSearch */}
        {walletTransactions}
      </VerticalFlexContainer>
    );
  }

}
<commit_msg>[DAEF-533] enable transactions page for etc<commit_after>// @flow
import React, { Component } from 'react';
import { observer, inject } from 'mobx-react';
import { defineMessages, intlShape } from 'react-intl';
import WalletTransactionsList from '../../../components/wallet/transactions/WalletTransactionsList';
// import WalletTransactionsSearch from '../../components/wallet/summary/WalletTransactionsSearch';
import WalletNoTransactions from '../../../components/wallet/transactions/WalletNoTransactions';
import VerticalFlexContainer from '../../../components/layout/VerticalFlexContainer';
import type { InjectedProps } from '../../../types/injectedPropsType';

const messages = defineMessages({
  noTransactions: {
    id: 'wallet.transactions.no.transactions',
    defaultMessage: '!!!No transactions',
    description: 'Message shown when wallet has no transactions yet.'
  },
  noTransactionsFound: {
    id: 'wallet.transactions.no.transactions.found',
    defaultMessage: '!!!No transactions found',
    description: 'Message shown when wallet transaction search returns zero results.'
  }
});

@inject('stores', 'actions') @observer
export default class WalletTransactionsPage extends Component {

  static defaultProps = { actions: null, stores: null };
  props: InjectedProps;

  static contextTypes = {
    intl: intlShape.isRequired,
  };

  // _handleSearchInputChange = (value: string, event: Object) => {
  //   this.props.actions.ada.transactions.filterTransactions({ searchTerm: event.target.value });
  // };

  render() {
    const { intl } = this.context;
    const actions = this.props.actions;
    const { transactions, wallets } = this.props.stores.etc;
    const activeWallet = wallets.active;
    const {
      searchOptions,
      searchRequest,
      hasAny,
      totalAvailable,
      filtered,
    } = transactions;

    // Guard against potential null values
    if (!searchOptions || !activeWallet) return null;

    const { searchLimit, searchTerm } = searchOptions;
    const wasSearched = searchTerm !== '';
    let walletTransactions = null;
    // let transactionSearch = null;
    const noTransactionsLabel = intl.formatMessage(messages.noTransactions);
    const noTransactionsFoundLabel = intl.formatMessage(messages.noTransactionsFound);

    // if (wasSearched || hasAny) {
    //   transactionSearch = (
    //

    //       <WalletTransactionsSearch
    //         searchTerm={searchTerm}
    //         onChange={this._handleSearchInputChange}
    //       />
    //

    //   );
    // }

    if (searchRequest.isExecutingFirstTime || hasAny) {
      walletTransactions = (
        <WalletTransactionsList
          transactions={filtered}
          isLoadingTransactions={searchRequest.isExecutingFirstTime}
          hasMoreToLoad={totalAvailable > searchLimit}
          onLoadMore={actions.ada.transactions.loadMoreTransactions.trigger}
          assuranceMode={activeWallet.assuranceMode}
          walletId={activeWallet.id}
        />
      );
    } else if (wasSearched && !hasAny) {
      walletTransactions = <WalletNoTransactions label={noTransactionsFoundLabel} />;
    } else if (!hasAny) {
      walletTransactions = <WalletNoTransactions label={noTransactionsLabel} />;
    }

    return (
      <VerticalFlexContainer>
        {/* transactionSearch */}
        {walletTransactions}
      </VerticalFlexContainer>
    );
  }

}
<|endoftext|>


<commit_before>import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types';
import IconButton from './icon_button';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { isIOS } from '../is_mobile';

const messages = defineMessages({
  toggle_visible: { id: 'media_gallery.toggle_visible', defaultMessage: 'Toggle visibility' },
});

class Item extends React.PureComponent {

  static propTypes = {
    attachment: ImmutablePropTypes.map.isRequired,
    index: PropTypes.number.isRequired,
    size: PropTypes.number.isRequired,
    onClick: PropTypes.func.isRequired,
    autoPlayGif: PropTypes.bool.isRequired,
  };

  handleClick = (e) => {
    const { index, onClick } = this.props;

    if (e.button === 0) {
      e.preventDefault();
      onClick(index);
    }

    e.stopPropagation();
  }

  render () {
    const { attachment, index, size } = this.props;

    let width  = 50;
    let height = 100;
    let top    = 'auto';
    let left   = 'auto';
    let bottom = 'auto';
    let right  = 'auto';

    if (size === 1) {
      width = 100;
    }

    if (size === 4 || (size === 3 && index > 0)) {
      height = 50;
    }

    if (size === 2) {
      if (index === 0) {
        right = '2px';
      } else {
        left = '2px';
      }
    } else if (size === 3) {
      if (index === 0) {
        right = '2px';
      } else if (index > 0) {
        left = '2px';
      }

      if (index === 1) {
        bottom = '2px';
      } else if (index > 1) {
        top = '2px';
      }
    } else if (size === 4) {
      if (index === 0 || index === 2) {
        right = '2px';
      }

      if (index === 1 || index === 3) {
        left = '2px';
      }

      if (index < 2) {
        bottom = '2px';
      } else {
        top = '2px';
      }
    }

    let thumbnail = '';

    if (attachment.get('type') === 'image') {
      const previewUrl = attachment.get('preview_url');
      const previewWidth = attachment.getIn(['meta', 'small', 'width']);

      const originalUrl = attachment.get('url');
      const originalWidth = attachment.getIn(['meta', 'original', 'width']);

      const srcSet = `${originalUrl} ${originalWidth}w, ${previewUrl} ${previewWidth}w`;
      const sizes = `(min-width: 1025px) ${320 * (width / 100)}px, ${width}vw`;

      thumbnail = (

      );
    } else if (attachment.get('type') === 'gifv') {
      const autoPlay = !isIOS() && this.props.autoPlayGif;

      thumbnail = (
        <div className={`media-gallery__gifv ${autoPlay ? 'autoplay' : ''}`}>
          <video
            className='media-gallery__item-gifv-thumbnail'
            role='application'
            src={attachment.get('url')}
            onClick={this.handleClick}
            autoPlay={autoPlay}
            loop
            muted
          />

          GIF

      );
    }

    return (
      <div className='media-gallery__item' key={attachment.get('id')} style={{ left: left, top: top, right: right, bottom: bottom, width: `${width}%`, height: `${height}%` }}>
        {thumbnail}

    );
  }

}

@injectIntl
export default class MediaGallery extends React.PureComponent {

  static propTypes = {
    sensitive: PropTypes.bool,
    media: ImmutablePropTypes.list.isRequired,
    height: PropTypes.number.isRequired,
    onOpenMedia: PropTypes.func.isRequired,
    intl: PropTypes.object.isRequired,
    autoPlayGif: PropTypes.bool.isRequired,
  };

  state = {
    visible: !this.props.sensitive,
  };

  handleOpen = () => {
    this.setState({ visible: !this.state.visible });
  }

  handleClick = (index) => {
    this.props.onOpenMedia(this.props.media, index);
  }

  render () {
    const { media, intl, sensitive } = this.props;

    let children;

    if (!this.state.visible) {
      let warning;

      if (sensitive) {
        warning = <FormattedMessage id='status.sensitive_warning' defaultMessage='Sensitive content' />;
      } else {
        warning = <FormattedMessage id='status.media_hidden' defaultMessage='Media hidden' />;
      }

      children = (

          {warning}
          <FormattedMessage id='status.sensitive_toggle' defaultMessage='Click to view' />

      );
    } else {
      const size = media.take(4).size;
      children = media.take(4).map((attachment, i) => <Item key={attachment.get('id')} onClick={this.handleClick} attachment={attachment} autoPlayGif={this.props.autoPlayGif} index={i} size={size} />);
    }

    return (
      <div className='media-gallery' style={{ height: `${this.props.height}px` }}>
        <div className={`spoiler-button ${this.state.visible ? 'spoiler-button--visible' : ''}`}>
          <IconButton title={intl.formatMessage(messages.toggle_visible)} icon={this.state.visible ? 'eye' : 'eye-slash'} overlay onClick={this.handleOpen} />

        {children}

    );
  }

}
<commit_msg>Use srcSet only when know width (#4112)<commit_after>import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types';
import IconButton from './icon_button';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { isIOS } from '../is_mobile';

const messages = defineMessages({
  toggle_visible: { id: 'media_gallery.toggle_visible', defaultMessage: 'Toggle visibility' },
});

class Item extends React.PureComponent {

  static propTypes = {
    attachment: ImmutablePropTypes.map.isRequired,
    index: PropTypes.number.isRequired,
    size: PropTypes.number.isRequired,
    onClick: PropTypes.func.isRequired,
    autoPlayGif: PropTypes.bool.isRequired,
  };

  handleClick = (e) => {
    const { index, onClick } = this.props;

    if (e.button === 0) {
      e.preventDefault();
      onClick(index);
    }

    e.stopPropagation();
  }

  render () {
    const { attachment, index, size } = this.props;

    let width  = 50;
    let height = 100;
    let top    = 'auto';
    let left   = 'auto';
    let bottom = 'auto';
    let right  = 'auto';

    if (size === 1) {
      width = 100;
    }

    if (size === 4 || (size === 3 && index > 0)) {
      height = 50;
    }

    if (size === 2) {
      if (index === 0) {
        right = '2px';
      } else {
        left = '2px';
      }
    } else if (size === 3) {
      if (index === 0) {
        right = '2px';
      } else if (index > 0) {
        left = '2px';
      }

      if (index === 1) {
        bottom = '2px';
      } else if (index > 1) {
        top = '2px';
      }
    } else if (size === 4) {
      if (index === 0 || index === 2) {
        right = '2px';
      }

      if (index === 1 || index === 3) {
        left = '2px';
      }

      if (index < 2) {
        bottom = '2px';
      } else {
        top = '2px';
      }
    }

    let thumbnail = '';

    if (attachment.get('type') === 'image') {
      const previewUrl = attachment.get('preview_url');
      const previewWidth = attachment.getIn(['meta', 'small', 'width']);

      const originalUrl = attachment.get('url');
      const originalWidth = attachment.getIn(['meta', 'original', 'width']);

      const hasSize = typeof originalWidth === 'number' && typeof previewWidth === 'number';

      const srcSet = hasSize && `${originalUrl} ${originalWidth}w, ${previewUrl} ${previewWidth}w`;
      const sizes = hasSize && `(min-width: 1025px) ${320 * (width / 100)}px, ${width}vw`;

      thumbnail = (

      );
    } else if (attachment.get('type') === 'gifv') {
      const autoPlay = !isIOS() && this.props.autoPlayGif;

      thumbnail = (
        <div className={`media-gallery__gifv ${autoPlay ? 'autoplay' : ''}`}>
          <video
            className='media-gallery__item-gifv-thumbnail'
            role='application'
            src={attachment.get('url')}
            onClick={this.handleClick}
            autoPlay={autoPlay}
            loop
            muted
          />

          GIF

      );
    }

    return (
      <div className='media-gallery__item' key={attachment.get('id')} style={{ left: left, top: top, right: right, bottom: bottom, width: `${width}%`, height: `${height}%` }}>
        {thumbnail}

    );
  }

}

@injectIntl
export default class MediaGallery extends React.PureComponent {

  static propTypes = {
    sensitive: PropTypes.bool,
    media: ImmutablePropTypes.list.isRequired,
    height: PropTypes.number.isRequired,
    onOpenMedia: PropTypes.func.isRequired,
    intl: PropTypes.object.isRequired,
    autoPlayGif: PropTypes.bool.isRequired,
  };

  state = {
    visible: !this.props.sensitive,
  };

  handleOpen = () => {
    this.setState({ visible: !this.state.visible });
  }

  handleClick = (index) => {
    this.props.onOpenMedia(this.props.media, index);
  }

  render () {
    const { media, intl, sensitive } = this.props;

    let children;

    if (!this.state.visible) {
      let warning;

      if (sensitive) {
        warning = <FormattedMessage id='status.sensitive_warning' defaultMessage='Sensitive content' />;
      } else {
        warning = <FormattedMessage id='status.media_hidden' defaultMessage='Media hidden' />;
      }

      children = (

          {warning}
          <FormattedMessage id='status.sensitive_toggle' defaultMessage='Click to view' />

      );
    } else {
      const size = media.take(4).size;
      children = media.take(4).map((attachment, i) => <Item key={attachment.get('id')} onClick={this.handleClick} attachment={attachment} autoPlayGif={this.props.autoPlayGif} index={i} size={size} />);
    }

    return (
      <div className='media-gallery' style={{ height: `${this.props.height}px` }}>
        <div className={`spoiler-button ${this.state.visible ? 'spoiler-button--visible' : ''}`}>
          <IconButton title={intl.formatMessage(messages.toggle_visible)} icon={this.state.visible ? 'eye' : 'eye-slash'} overlay onClick={this.handleOpen} />

        {children}

    );
  }

}
<|endoftext|>


<commit_before>"use strict";

const _debug = require("debug");
const debug = _debug("core:facebook-wit");

const VERIFY_TOKEN = "zcI9G14kJo";

exports.register = function (server, options, next) {
  debug("Registering...");

  //TODO run on https
  server.route({
    path: '/webhooks/facebook',
    method: 'GET',
    handler: function (request, reply) {
      if (request.query['hub.mode'] === 'subscribe' &&
        request.query['hub.verify_token'] === VERIFY_TOKEN) {
        console.log("Validating webhook");
        reply(request.query['hub.challenge']).code(200);
      } else {
        console.error("Failed validation. Make sure the validation tokens match.");
        reply("").code(403);
      }
    }
  });

  //TODO remove, test only, short circuit facebook
  server.route({
    path: '/webhooks/test',
    method: 'POST',
    handler: function (request, reply)
    {
      require("./wit")(request.payload, reply);
    }
  });

  debug("Complete !");
  next();
};

exports.register.attributes = {
  pkg: require('./package.json')
};
<commit_msg>add facebook message reception<commit_after>"use strict";

const _debug = require("debug");
const debug = _debug("core:facebook-wit");

const VERIFY_TOKEN = "zcI9G14kJo";

exports.register = function (server, options, next) {
  debug("Registering...");

  //server owner validation
  server.route({
    path: '/webhooks/facebook',
    method: 'GET',
    handler: function (request, reply) {
      if (request.query['hub.mode'] === 'subscribe' &&
        request.query['hub.verify_token'] === VERIFY_TOKEN) {
        console.log("Validating webhook");
        reply(request.query['hub.challenge']).code(200);
      } else {
        console.error("Failed validation. Make sure the validation tokens match.");
        reply("").code(403);
      }
    }
  });

  //messages endpoint
  server.route({
    path: '/webhooks/facebook',
    method: 'POST',
    handler: function (request, reply) {
      var data = request.payload;

      // Make sure this is a page subscription
      if (data.object === 'page') {

        // Iterate over each entry - there may be multiple if batched
        data.entry.forEach(function (entry) {
          var pageID = entry.id;
          var timeOfEvent = entry.time;

          // Iterate over each messaging event
          entry.messaging.forEach(function (event) {
            if (event.message) {
              debug("Received fb msg: ", event);
              //require("./wit")(request.payload, reply);
            } else {
              debug("Webhook received unknown event: ", event);
            }
          });
        });

        reply("").code(200);
      }
    }
  });

  debug("Complete !");
  next();
};

exports.register.attributes = {
  pkg: require('./package.json')
};
<|endoftext|>


<commit_before>import { module } from 'qunit';
import startApp from '../helpers/start-app';
import destroyApp from '../helpers/destroy-app';

export default function(name, options = {}) {
  module(name, {
    beforeEach() {
      this.application = startApp();

      if (options.beforeEach) {
        options.beforeEach.apply(this, arguments);
      }
    },

    afterEach() {
      destroyApp(this.application);

      if (options.afterEach) {
        options.afterEach.apply(this, arguments);
      }
    }
  });
}
<commit_msg>Ensure Ember.Test.adapter is removed after acceptance tests.<commit_after>import Ember from 'ember';
import { module } from 'qunit';
import startApp from '../helpers/start-app';
import destroyApp from '../helpers/destroy-app';

export default function(name, options = {}) {
  module(name, {
    beforeEach() {
      this.application = startApp();

      if (options.beforeEach) {
        options.beforeEach.apply(this, arguments);
      }
    },

    afterEach() {
      destroyApp(this.application);

      if (options.afterEach) {
        options.afterEach.apply(this, arguments);
      }

      Ember.Test.adapter = null;
    }
  });
}
<|endoftext|>


<commit_before>/**
 * milli @@version
 *
 * @author Alistair Dutton (christian@cjohansen.no)
 */
(function (context) {
    function argsToArray(args) {
        return Array.prototype.slice.call(args, 0);
    }

    function Stub(method, url, defaultRequestContentType, defaultResponseContentType) {
        this.entity = function (body, contentType) {
            stub.criteria.body = body;
            stub.criteria.contentType = contentType;
            return this;
        };

        this.body = function (body) {
            stub.criteria.body = body;
            if (!stub.criteria.contentType && defaultRequestContentType) {
                stub.criteria.contentType = defaultRequestContentType;
            }
            return this;
        };

        this.contentType = function (contentType) {
            stub.criteria.contentType = contentType;
            return this;
        };

        this.header = function (name, value) {
            stub.criteria.headers = stub.criteria.headers || {};
            stub.criteria.headers[name] = value;
            return this;
        };

        this.param = function (name, value) {
            stub.criteria.query = stub.criteria.query || {};
            stub.criteria.query[name] = value;
            return this;
        };

        this.respondWith = function (status) {
            return new StubRespondWith(stub, status, defaultResponseContentType);
        };

        this.capture = function (captureId) {
            stub.capture = captureId;
            return this;
        };

        var stub = {
            criteria: {},
            respondWith: {}
        };

        if (method) {
            stub.criteria.method = method;
        }

        stub.criteria.url = url;
    }

    function StubRespondWith(stub, status, defaultContentType) {
        if (typeof status === 'object') {
            // 'status' is actually an entire respondWith object - typically sent in from an 'ignored call'.
            stub.respondWith = status;
        } else {
            stub.respondWith.status = status;
        }

        this.entity = function (body, contentType) {
            stub.respondWith.body = body;
            stub.respondWith.contentType = contentType;
            return this;
        };

        this.body = function (body) {
            stub.respondWith.body = body;
            if (!stub.respondWith.contentType && defaultContentType) {
                stub.respondWith.contentType = defaultContentType;
            }
            return this;
        };

        this.contentType = function (contentType) {
            stub.respondWith.contentType = contentType;
            return this;
        };

        this.header = function (name, value) {
            stub.respondWith.headers = stub.respondWith.headers || {};
            stub.respondWith.headers[name] = value;
            return this;
        };

        this.times = function (times) {
            stub.times = times;
            return this;
        };

        this.afterWaiting = function (afterWaitingMilliseconds) {
            stub.respondWith.wait = afterWaitingMilliseconds;
            return this;
        };

        this.clone = function () {
            return new StubRespondWith(JSON.parse(JSON.stringify(stub)), status, defaultContentType);
        };

        this.priority = function (priority) {
            stub.priority = priority;
            return this;
        };

        this.vanilliRequestBody = stub;
    }

    function Milli() {

        var vanilliPort,
            self = this,
            asyncStubs = [],
            Promiser;

        function sendToVanilli(method, url, data, doneOrSync, successBuilder, errorBuilder) {
            var xhr = new XMLHttpRequest(),
                syncMode = ((typeof doneOrSync === 'boolean') && !doneOrSync),
                callbackMode = (typeof doneOrSync === 'function'),
                promiseMode = !callbackMode && !syncMode,
                deferred = promiseMode ? Promiser.defer() : null;

            function reactToCompletedResponse() {
                function success(result) {
                    if (syncMode) {
                        return result;
                    } else if (promiseMode) {
                        deferred.resolve(result);
                    } else {
                        doneOrSync(result);
                    }
                }

                function failure(error) {
                    if (syncMode) {
                        throw error;
                    } else if (promiseMode) {
                        deferred.reject(error);
                    } else {
                        doneOrSync(error);
                    }
                }

                if (xhr.readyState === 4) {
                    if (xhr.status === 200) {
                        var result = successBuilder(xhr);

                        if (result instanceof Error) {
                            failure(result);
                        } else {
                            return success(result);
                        }
                    } else {
                        failure(errorBuilder(xhr));
                    }
                }
            }

            xhr.open(method, url, !syncMode);
            if ((method === "POST") || (method === 'PUT')) {
                xhr.setRequestHeader('Content-Type', 'application/json');
            }

            if (!syncMode) {
                xhr.onreadystatechange = reactToCompletedResponse;
            }

            xhr.send(data);

            if (syncMode) {
                return reactToCompletedResponse();
            } else {
                if (promiseMode) {
                    return deferred.promise;
                }
            }
        }

        function sendStubs(stubs, done) {
            var body = JSON.stringify(
                stubs.map(function (stub) {
                    return stub.vanilliRequestBody;
                }));

            stubs.length = 0;
            var result = sendToVanilli("POST", "http://localhost:" + vanilliPort + "/_vanilli/stubs", body,
                done,
                function (xhr) {
                    return JSON.parse(xhr.responseText);
                },
                function (xhr) {
                    if (xhr.status === 400) {
                        return new Error("One or more stubs were invalid. " + xhr.responseText);
                    } else {
                        return new Error("Could not add stubs. " + xhr.responseText);
                    }
                });

            return result;
        }

        function clearStubs(done) {
            return sendToVanilli("DELETE", "http://localhost:" + vanilliPort + "/_vanilli/stubs", null,
                done,
                function () {
                    return null;
                },
                function (xhr) {
                    return new Error("Could not clear stubs. " + xhr.responseText);
                });
        }

        function verify(done) {
            return sendToVanilli("GET", "http://localhost:" + vanilliPort + "/_vanilli/stubs/verification", null,
                done,
                function (xhr) {
                    var response = JSON.parse(xhr.responseText);
                    if (response.errors.length === 0) {
                        return null;
                    } else {
                        return new Error("Vanilli expectations were not met:\n\n" + response.errors.join("\n"));
                    }
                },
                function (xhr) {
                    return new Error("Could not verify expectations. " + xhr.responseText);
                });
        }

        function getCapture(captureId, done) {
            return sendToVanilli("GET", "http://localhost:" + vanilliPort + "/_vanilli/captures/" + captureId, null,
                done,
                function (xhr) {
                    return JSON.parse(xhr.responseText);
                },
                function (xhr) {
                    if (xhr.status === 404) {
                        return new Error("Capture could not be found. " + xhr.responseText);
                    } else {
                        return new Error("Capture could not be retrieved. " + xhr.responseText);
                    }
                });
        }

        function buildStubsFrom(stubDefinitions) {
            var stubs = [];

            if (stubDefinitions.length === 0) {
                throw new Error("Stub content must be specified.");
            }

            stubDefinitions.forEach(function (stub) {
                if (stub instanceof Stub) {
                    throw new Error("Stub " + JSON.stringify(stub) + " is incomplete - make sure you use a call to 'respondWith' to complete the stub.");
                }

                if (!(stub instanceof StubRespondWith)) {
                    throw new Error("Argument " + JSON.stringify(stub) + " is not a stub - it is a " + typeof stub + ".");
                }

                stubs.push(stub);
            });

            return stubs;
        }

        self.configure = function (config) {
            if (!config) {
                throw new Error("Config must be specified.");
            }

            if (!config.port) {
                throw new Error("config.port must be specified.");
            }

            vanilliPort = config.port;
            Promiser = config.promiser;
            asyncStubs.length = 0;
        };

        self.stub = function () {
            buildStubsFrom(argsToArray(arguments)).forEach(function (stub) {
                asyncStubs.push(stub);
            });

            return self;
        };

        self.allow = function () {
            var stubs = buildStubsFrom(argsToArray(arguments));

            stubs.forEach(function (stub) {
                if (!isNaN(stub.vanilliRequestBody.times)) {
                    throw new Error("Stubs cannot be specified with 'times' - use an expectation instead.");
                }
            });

            return sendStubs(stubs, false);
        };

        self.expect = function () {
            var expectations = buildStubsFrom(argsToArray(arguments).map(
                function (stub) {
                    return expectRequest(stub);
                }));

            return sendStubs(expectations, false);
        };

        self.run = function (next) {
            sendStubs(asyncStubs, function (err) {
                asyncStubs.length = 0;

                if (err instanceof Error) {
                    throw err;
                } else {
                    next();
                }
            });
        };

        self.clearStubs = function (done) {
            return clearStubs(done);
        };

        self.clearStubsSync = function () {
            return clearStubs(false);
        };

        self.getCapture = function (captureId, done) {
            return getCapture(captureId, done);
        };

        self.getCaptureSync = function (captureId) {
            return getCapture(captureId, false);
        };

        self.verifyExpectations = function (done) {
            return verify(done);
        };

        self.verifyExpectationsSync = function () {
            return verify(false);
        };

        self.ignoreCallsTo = function () {
            function setResponseContentTypeOn(respondWith, resource) {
                if (!resource.defaultResponse.contentType) {
                    if (resource.produces) {
                        respondWith.contentType(resource.produces);
                    } else {
                        throw new Error("Either a resource.produces or resource.defaultResponse.contentType must be specified.");
                    }
                }
            }

            function createIgnoresFrom(objectOrArray) {
                if (Array.isArray(objectOrArray)) {
                    return objectOrArray.map(createIgnoresFrom);

                } else {
                    var urlOrResource = objectOrArray,
                        substitutionData = {},
                        stubRespondWith;

                    substitutionData[matchAnyPlaceholderSubtitution] = "[\\s\\S]+?";

                    if (urlOrResource.defaultResponse) {
                        stubRespondWith = context.onRequest(null, urlOrResource, substitutionData).respondWith(urlOrResource.defaultResponse);

                        if (typeof urlOrResource.defaultResponse.body !== 'undefined') {
                            setResponseContentTypeOn(stubRespondWith, urlOrResource);
                        }
                    } else {
                        stubRespondWith = context.onRequest(null, urlOrResource, substitutionData).respondWith(200);
                    }

                    return stubRespondWith.priority(100);
                }
            }

            function flatten(arrayOfThings) {
                return arrayOfThings.reduce(function (accumulation, thing) {
                    if (Array.isArray(thing)) {
                        thing.forEach(function (item) {
                            accumulation.push(item);
                        });
                    } else {
                        accumulation.push(thing);
                    }
                    return accumulation;
                }, []);
            }

            return sendStubs(
                buildStubsFrom(
                    flatten(
                        createIgnoresFrom(
                            argsToArray(arguments)))),
                    false);
        };
    }

    var matchAnyPlaceholderSubtitution = "*";

    context.onRequest = function (method, urlOrResource, substitutionData) {
        var substitutedPlaceholders = {};

        function substituteTemplatePlaceholders(uriTemplate, substitutionData) {
            return uriTemplate.replace(/:[a-zA-Z][0-9a-zA-Z]+/g, function (placeholder) {
                var paramName = placeholder.substr(1),
                    paramValue = substitutionData[paramName] || substitutionData["*"];

                if (paramValue === undefined) {
                    throw new Error("Could not find substitution for placeholder '" + placeholder + "'.");
                }

                substitutedPlaceholders[paramName] = paramValue;

                return paramValue;
            });
        }

        function addLeftoverSubstitutionsAsQueryParameters(stub, substitutionData) {
            if (substitutionData) {
                for (var paramName in substitutionData) {
                    if (substitutionData.hasOwnProperty(paramName) &&
                        (!substitutedPlaceholders[paramName] && (paramName !== matchAnyPlaceholderSubtitution))) {
                        stub.param(paramName, substitutionData[paramName]);
                    }
                }
            }
        }

        if (!urlOrResource) {
            throw new Error("The stub url must be specified.");
        }

        var stub, url;

        if (typeof urlOrResource !== 'string') {
            url = substituteTemplatePlaceholders(urlOrResource.url, substitutionData || {});
            stub = new Stub(method, url, urlOrResource.consumes, urlOrResource.produces);
        } else {
            url = substituteTemplatePlaceholders(urlOrResource, substitutionData || {});
            stub = new Stub(method, url);
        }

        addLeftoverSubstitutionsAsQueryParameters(stub, substitutionData);

        return stub;
    };

    context.onGet = function (urlOrResource, substitutionData) {
        return context.onRequest('GET', urlOrResource, substitutionData);
    };

    context.onDelete = function (urlOrResource, substitutionData) {
        return context.onRequest('DELETE', urlOrResource, substitutionData);
    };

    context.onPut = function (urlOrResource, substitutionData) {
        return context.onRequest('PUT', urlOrResource, substitutionData);
    };

    context.onPost = function (urlOrResource, substitutionData) {
        return context.onRequest('POST', urlOrResource, substitutionData);
    };

    context.expectRequest = function (stub, times) {
        function convertToExpectation(stub) {
            if (isNaN(stub.vanilliRequestBody.times)) {
                stub.vanilliRequestBody.times = isNaN(times) ? 1 : times;
            }
            stub.vanilliRequestBody.expect = true;

            return stub;
        }

        return convertToExpectation(stub.clone());
    };

    context.milli = new Milli();
})(window);<commit_msg>Change the right file this time...<commit_after>/**
 * milli @@version
 */
(function (context) {
    function argsToArray(args) {
        return Array.prototype.slice.call(args, 0);
    }

    function Stub(method, url, defaultRequestContentType, defaultResponseContentType) {
        this.entity = function (body, contentType) {
            stub.criteria.body = body;
            stub.criteria.contentType = contentType;
            return this;
        };

        this.body = function (body) {
            stub.criteria.body = body;
            if (!stub.criteria.contentType && defaultRequestContentType) {
                stub.criteria.contentType = defaultRequestContentType;
            }
            return this;
        };

        this.contentType = function (contentType) {
            stub.criteria.contentType = contentType;
            return this;
        };

        this.header = function (name, value) {
            stub.criteria.headers = stub.criteria.headers || {};
            stub.criteria.headers[name] = value;
            return this;
        };

        this.param = function (name, value) {
            stub.criteria.query = stub.criteria.query || {};
            stub.criteria.query[name] = value;
            return this;
        };

        this.respondWith = function (status) {
            return new StubRespondWith(stub, status, defaultResponseContentType);
        };

        this.capture = function (captureId) {
            stub.capture = captureId;
            return this;
        };

        var stub = {
            criteria: {},
            respondWith: {}
        };

        if (method) {
            stub.criteria.method = method;
        }

        stub.criteria.url = url;
    }

    function StubRespondWith(stub, status, defaultContentType) {
        if (typeof status === 'object') {
            // 'status' is actually an entire respondWith object - typically sent in from an 'ignored call'.
            stub.respondWith = status;
        } else {
            stub.respondWith.status = status;
        }

        this.entity = function (body, contentType) {
            stub.respondWith.body = body;
            stub.respondWith.contentType = contentType;
            return this;
        };

        this.body = function (body) {
            stub.respondWith.body = body;
            if (!stub.respondWith.contentType && defaultContentType) {
                stub.respondWith.contentType = defaultContentType;
            }
            return this;
        };

        this.contentType = function (contentType) {
            stub.respondWith.contentType = contentType;
            return this;
        };

        this.header = function (name, value) {
            stub.respondWith.headers = stub.respondWith.headers || {};
            stub.respondWith.headers[name] = value;
            return this;
        };

        this.times = function (times) {
            stub.times = times;
            return this;
        };

        this.afterWaiting = function (afterWaitingMilliseconds) {
            stub.respondWith.wait = afterWaitingMilliseconds;
            return this;
        };

        this.clone = function () {
            return new StubRespondWith(JSON.parse(JSON.stringify(stub)), status, defaultContentType);
        };

        this.priority = function (priority) {
            stub.priority = priority;
            return this;
        };

        this.vanilliRequestBody = stub;
    }

    function Milli() {

        var vanilliPort,
            self = this,
            asyncStubs = [],
            Promiser;

        function sendToVanilli(method, url, data, doneOrSync, successBuilder, errorBuilder) {
            var xhr = new XMLHttpRequest(),
                syncMode = ((typeof doneOrSync === 'boolean') && !doneOrSync),
                callbackMode = (typeof doneOrSync === 'function'),
                promiseMode = !callbackMode && !syncMode,
                deferred = promiseMode ? Promiser.defer() : null;

            function reactToCompletedResponse() {
                function success(result) {
                    if (syncMode) {
                        return result;
                    } else if (promiseMode) {
                        deferred.resolve(result);
                    } else {
                        doneOrSync(result);
                    }
                }

                function failure(error) {
                    if (syncMode) {
                        throw error;
                    } else if (promiseMode) {
                        deferred.reject(error);
                    } else {
                        doneOrSync(error);
                    }
                }

                if (xhr.readyState === 4) {
                    if (xhr.status === 200) {
                        var result = successBuilder(xhr);

                        if (result instanceof Error) {
                            failure(result);
                        } else {
                            return success(result);
                        }
                    } else {
                        failure(errorBuilder(xhr));
                    }
                }
            }

            xhr.open(method, url, !syncMode);
            if ((method === "POST") || (method === 'PUT')) {
                xhr.setRequestHeader('Content-Type', 'application/json');
            }

            if (!syncMode) {
                xhr.onreadystatechange = reactToCompletedResponse;
            }

            xhr.send(data);

            if (syncMode) {
                return reactToCompletedResponse();
            } else {
                if (promiseMode) {
                    return deferred.promise;
                }
            }
        }

        function sendStubs(stubs, done) {
            var body = JSON.stringify(
                stubs.map(function (stub) {
                    return stub.vanilliRequestBody;
                }));

            stubs.length = 0;
            var result = sendToVanilli("POST", "http://localhost:" + vanilliPort + "/_vanilli/stubs", body,
                done,
                function (xhr) {
                    return JSON.parse(xhr.responseText);
                },
                function (xhr) {
                    if (xhr.status === 400) {
                        return new Error("One or more stubs were invalid. " + xhr.responseText);
                    } else {
                        return new Error("Could not add stubs. " + xhr.responseText);
                    }
                });

            return result;
        }

        function clearStubs(done) {
            return sendToVanilli("DELETE", "http://localhost:" + vanilliPort + "/_vanilli/stubs", null,
                done,
                function () {
                    return null;
                },
                function (xhr) {
                    return new Error("Could not clear stubs. " + xhr.responseText);
                });
        }

        function verify(done) {
            return sendToVanilli("GET", "http://localhost:" + vanilliPort + "/_vanilli/stubs/verification", null,
                done,
                function (xhr) {
                    var response = JSON.parse(xhr.responseText);
                    if (response.errors.length === 0) {
                        return null;
                    } else {
                        return new Error("Vanilli expectations were not met:\n\n" + response.errors.join("\n"));
                    }
                },
                function (xhr) {
                    return new Error("Could not verify expectations. " + xhr.responseText);
                });
        }

        function getCapture(captureId, done) {
            return sendToVanilli("GET", "http://localhost:" + vanilliPort + "/_vanilli/captures/" + captureId, null,
                done,
                function (xhr) {
                    return JSON.parse(xhr.responseText);
                },
                function (xhr) {
                    if (xhr.status === 404) {
                        return new Error("Capture could not be found. " + xhr.responseText);
                    } else {
                        return new Error("Capture could not be retrieved. " + xhr.responseText);
                    }
                });
        }

        function buildStubsFrom(stubDefinitions) {
            var stubs = [];

            if (stubDefinitions.length === 0) {
                throw new Error("Stub content must be specified.");
            }

            stubDefinitions.forEach(function (stub) {
                if (stub instanceof Stub) {
                    throw new Error("Stub " + JSON.stringify(stub) + " is incomplete - make sure you use a call to 'respondWith' to complete the stub.");
                }

                if (!(stub instanceof StubRespondWith)) {
                    throw new Error("Argument " + JSON.stringify(stub) + " is not a stub - it is a " + typeof stub + ".");
                }

                stubs.push(stub);
            });

            return stubs;
        }

        self.configure = function (config) {
            if (!config) {
                throw new Error("Config must be specified.");
            }

            if (!config.port) {
                throw new Error("config.port must be specified.");
            }

            vanilliPort = config.port;
            Promiser = config.promiser;
            asyncStubs.length = 0;
        };

        self.stub = function () {
            buildStubsFrom(argsToArray(arguments)).forEach(function (stub) {
                asyncStubs.push(stub);
            });

            return self;
        };

        self.allow = function () {
            var stubs = buildStubsFrom(argsToArray(arguments));

            stubs.forEach(function (stub) {
                if (!isNaN(stub.vanilliRequestBody.times)) {
                    throw new Error("Stubs cannot be specified with 'times' - use an expectation instead.");
                }
            });

            return sendStubs(stubs, false);
        };

        self.expect = function () {
            var expectations = buildStubsFrom(argsToArray(arguments).map(
                function (stub) {
                    return expectRequest(stub);
                }));

            return sendStubs(expectations, false);
        };

        self.run = function (next) {
            sendStubs(asyncStubs, function (err) {
                asyncStubs.length = 0;

                if (err instanceof Error) {
                    throw err;
                } else {
                    next();
                }
            });
        };

        self.clearStubs = function (done) {
            return clearStubs(done);
        };

        self.clearStubsSync = function () {
            return clearStubs(false);
        };

        self.getCapture = function (captureId, done) {
            return getCapture(captureId, done);
        };

        self.getCaptureSync = function (captureId) {
            return getCapture(captureId, false);
        };

        self.verifyExpectations = function (done) {
            return verify(done);
        };

        self.verifyExpectationsSync = function () {
            return verify(false);
        };

        self.ignoreCallsTo = function () {
            function setResponseContentTypeOn(respondWith, resource) {
                if (!resource.defaultResponse.contentType) {
                    if (resource.produces) {
                        respondWith.contentType(resource.produces);
                    } else {
                        throw new Error("Either a resource.produces or resource.defaultResponse.contentType must be specified.");
                    }
                }
            }

            function createIgnoresFrom(objectOrArray) {
                if (Array.isArray(objectOrArray)) {
                    return objectOrArray.map(createIgnoresFrom);

                } else {
                    var urlOrResource = objectOrArray,
                        substitutionData = {},
                        stubRespondWith;

                    substitutionData[matchAnyPlaceholderSubtitution] = "[\\s\\S]+?";

                    if (urlOrResource.defaultResponse) {
                        stubRespondWith = context.onRequest(null, urlOrResource, substitutionData).respondWith(urlOrResource.defaultResponse);

                        if (typeof urlOrResource.defaultResponse.body !== 'undefined') {
                            setResponseContentTypeOn(stubRespondWith, urlOrResource);
                        }
                    } else {
                        stubRespondWith = context.onRequest(null, urlOrResource, substitutionData).respondWith(200);
                    }

                    return stubRespondWith.priority(100);
                }
            }

            function flatten(arrayOfThings) {
                return arrayOfThings.reduce(function (accumulation, thing) {
                    if (Array.isArray(thing)) {
                        thing.forEach(function (item) {
                            accumulation.push(item);
                        });
                    } else {
                        accumulation.push(thing);
                    }
                    return accumulation;
                }, []);
            }

            return sendStubs(
                buildStubsFrom(
                    flatten(
                        createIgnoresFrom(
                            argsToArray(arguments)))),
                    false);
        };
    }

    var matchAnyPlaceholderSubtitution = "*";

    context.onRequest = function (method, urlOrResource, substitutionData) {
        var substitutedPlaceholders = {};

        function substituteTemplatePlaceholders(uriTemplate, substitutionData) {
            return uriTemplate.replace(/:[a-zA-Z][0-9a-zA-Z]+/g, function (placeholder) {
                var paramName = placeholder.substr(1),
                    paramValue = substitutionData[paramName] || substitutionData["*"];

                if (paramValue === undefined) {
                    throw new Error("Could not find substitution for placeholder '" + placeholder + "'.");
                }

                substitutedPlaceholders[paramName] = paramValue;

                return paramValue;
            });
        }

        function addLeftoverSubstitutionsAsQueryParameters(stub, substitutionData) {
            if (substitutionData) {
                for (var paramName in substitutionData) {
                    if (substitutionData.hasOwnProperty(paramName) &&
                        (!substitutedPlaceholders[paramName] && (paramName !== matchAnyPlaceholderSubtitution))) {
                        stub.param(paramName, substitutionData[paramName]);
                    }
                }
            }
        }

        if (!urlOrResource) {
            throw new Error("The stub url must be specified.");
        }

        var stub, url;

        if (typeof urlOrResource !== 'string') {
            url = substituteTemplatePlaceholders(urlOrResource.url, substitutionData || {});
            stub = new Stub(method, url, urlOrResource.consumes, urlOrResource.produces);
        } else {
            url = substituteTemplatePlaceholders(urlOrResource, substitutionData || {});
            stub = new Stub(method, url);
        }

        addLeftoverSubstitutionsAsQueryParameters(stub, substitutionData);

        return stub;
    };

    context.onGet = function (urlOrResource, substitutionData) {
        return context.onRequest('GET', urlOrResource, substitutionData);
    };

    context.onDelete = function (urlOrResource, substitutionData) {
        return context.onRequest('DELETE', urlOrResource, substitutionData);
    };

    context.onPut = function (urlOrResource, substitutionData) {
        return context.onRequest('PUT', urlOrResource, substitutionData);
    };

    context.onPost = function (urlOrResource, substitutionData) {
        return context.onRequest('POST', urlOrResource, substitutionData);
    };

    context.expectRequest = function (stub, times) {
        function convertToExpectation(stub) {
            if (isNaN(stub.vanilliRequestBody.times)) {
                stub.vanilliRequestBody.times = isNaN(times) ? 1 : times;
            }
            stub.vanilliRequestBody.expect = true;

            return stub;
        }

        return convertToExpectation(stub.clone());
    };

    context.milli = new Milli();
})(window);<|endoftext|>


<commit_before>"use strict";

// ---------------------------------------------------------------------------------------
// Database of party names and colors

// Dictionary of raw party data:
// - Key is the canonical short name.
// - Value is an array of aliases.
// - The first alias is the canonical long name.
const PARTY_DATA = {
    Con: ["Conservative", "C"],
    Lab: ["Labour", "Lab Co-op", "L"],
    LD: ["Liberal Democrat"],
    DUP: ["Democratic Unionist Party"],
    SNP: ["Scottish National Party"],
    PC: ["Plaid Cymru"],
    SDLP: ["Social Democratic and Labour Party"],
    Green: ["Green Party", "GRN"],
    Alliance: ["Alliance Party of Northern Ireland", "APNI"],
    UUP: ["Ulster Unionist Party"],
    UKIP: ["UK Independence Party"],
    SF: ["Sinn Féin", "Sinn Fein"], // Abstentionist, doesn't sit in the Commons
    Speaker: ["Speaker"] // Speaker of the House, traditionally non-partisan
};

const SHORT_NAME_MAP = {}; // Map of anything -> short name
const LONG_NAME_MAP = {}; // Map of anything -> long name

for (let short_name in PARTY_DATA) {
    const aliases = PARTY_DATA[short_name];
    const long_name = aliases[0];
    SHORT_NAME_MAP[short_name.toLowerCase()] = short_name;
    LONG_NAME_MAP[short_name.toLowerCase()] = long_name;
    for (let alias of aliases) {
        SHORT_NAME_MAP[alias.toLowerCase()] = short_name;
        LONG_NAME_MAP[alias.toLowerCase()] = long_name;
    }
}

/**
 * Get the canonical short version of any party name or synonym
 */
export function party_name(party) {
    return SHORT_NAME_MAP[party.toLowerCase()] || "Other";
}

/**
 * Get the canonical long version of any party name or synonym
 */
export function party_long_name(party) {
    return LONG_NAME_MAP[party.toLowerCase()] || "Other";
}

/**
 * Returns true for parties that vote normally in the House of Commons.
 * Only returns false for some special cases (Sinn Féin and the Speaker).
 */
export function party_takes_seat(party) {
    party = party_name(party);
    return party !== "SF" && party !== "Speaker";
}

// ---------------------------------------------------------------------------------------
// Basic calculations with raw election data
//
// Raw election data is a CSV-like array of arrays.
// The first row contains column headers (e.g. party names).
// The remaining rows contain voting data, one row per constituency.
// The first column is assumed to be the constituency name.

/**
 * Count the total number of votes cast for each party
 * @param data - table of per-constituency votes
 * @return map of party name -> seat count
 */
export function get_votes(data) {
    var keys = data[0];
    var votes = []
    var i, j;
    for (i = 1; i < keys.length; i++) {
        votes[i] = 0;
    }
    for (i = 1; i < data.length; i++) {
        var row = data[i];
        for (j = 1; j < row.length; j++) {
            votes[j] += row[j];
        }
    }
    var vote_map = {};
    for (i = 1; i < keys.length; i++) {
        vote_map[keys[i]] = votes[i];
    }
    return vote_map;
}

/**
 * Count the number of seats won by each party
 * @param data - table of per-constituency votes
 * @return map of party name -> seat count
 */
export function get_seats(data) {
    var keys = data[0];
    var seats = []
    var i, j;
    for (i = 1; i < keys.length; i++) {
        seats[i] = 0;
    }
    for (i = 1; i < data.length; i++) {
        var row = data[i];
        var max_indices = [];
        var max_value = 0;
        for (j = 1; j < row.length; j++) {
            if (row[j] > max_value) {
                max_indices = [j];
                max_value = row[j];
            } else if (row[j] == max_value) {
                max_indices.push(j);
            }
        }
        // Tied seats are divided evenly (in real life it's resolved by a coin toss)
        for (let winner of max_indices) {
            seats[winner] += 1.0 / max_indices.length;
        }
    }
    var seat_map = {};
    for (i = 1; i < keys.length; i++) {
        seat_map[keys[i]] = seats[i];
    }
    return seat_map;
}

/**
 * Given a party -> seats map, remove abstentionist parties (i.e. those who don't sit)
 * @param seat_map - map of party name -> seat count
 * @return map of party name -> seat count
 */
export function remove_unused_seats(seat_map) {
    var result = {};
    for (let party in seat_map) {
        result[party] = party_takes_seat(party) ? seat_map[party] : 0;
    }
    return result;
}

/**
 * Get votes or seats for the given parties, in order.
 * Parties not listed will be counted as "Other".
 * @param votes_or_seats - map of party name -> seat count or vote count
 * @param parties - list of party names
 * @return list of votes or seats for the corresponding parties
 */
export function count_by_party(votes_or_seats, parties) {
    var count = {};
    for (let party in votes_or_seats) {
        if (parties.indexOf(party) >= 0) {
            count[party] = (count[party] || 0) + votes_or_seats[party];
        } else {
            count.Other = (count.Other || 0) +  votes_or_seats[party];
        }
    }
    var result = [];
    for (let party of parties) {
        result.push(count[party]);
    }
    return result;
}

// ---------------------------------------------------------------------------------------
// Tactical voting

/**
 * Given raw data and a voting bloc, transforms the data as if the bloc voted tactically.
 * We assume that all bloc votes go to the biggest party in each constituency.
 * @param data - table of party votes per constituency
 * @return a new table of party votes per constituency
 */
export function adjust_data_with_tactics(data, bloc) {
    const keys = data[0];
    var result = [keys];
    const party_indices = [];
    for (let party of bloc) {
        party_indices.push(keys.indexOf(party));
    }
    for (let i = 1; i < data.length; i++) {
        const row = data[i];
        let total = 0;
        let biggest_vote = -1;
        let biggest_party = -1;
        for (let j of party_indices) {
            let vote = row[j];
            if (vote > biggest_vote) {
                biggest_vote = vote;
                biggest_party = j;
            }
            total += vote;
        }
        const new_row = row.slice();
        for (let j of party_indices) {
            new_row[j] = (j == biggest_party) ? total : 0;
        }
        result.push(new_row);
    }
    return result;
}

// ---------------------------------------------------------------------------------------
// Swingometer

/**
 * Scale the given votes so that the total vote counts is 1
 * @param votes - map of party name -> vote count
 * @return new map with normalized counts
 */
export function normalize_votes(votes) {
    let total = 0;
    for (let party in votes) {
        total += votes[party];
    }
    let result = {};
    for (let party in votes) {
        result[party] = votes[party] / total;
    }
    return result;
}

/**
 * Calculate the vote swing percentage for each party
 * @param prev - map of party name -> vote count
 * @param next - map of party name -> vote count
 * @return map of party name -> swing (in range 0 to 1)
 */
export function get_swing(prev, next) {
    prev = normalize_votes(prev);
    next = normalize_votes(next);
    let swing = {};
    for (let party in next) {
        swing[party] = next[party] - prev[party];
    }
    return swing;
}

/**
 * Given old election data and a new poll, return a forecast for the new election.
 * We use a simple uniform national swing model.
 * @param data - table of party votes per constituency
 * @param poll - map of party name -> vote count
 * @return a new table of party votes per constituency
 */
export function adjust_data_with_poll(data, poll) {
    const votes = get_votes(data);
    const swing_map = get_swing(votes, poll);
    const keys = data[0];
    let swing = [];
    for (let i = 1; i < keys.length; i++) {
        swing[i] = swing_map[keys[i]] || swing_map.Other || 0;
    }
    var result = [keys];
    for (let i = 1; i < data.length; i++) {
        const row = data[i];
        let row_total = 0;
        for (let j = 1; j < row.length; j++) {
            row_total += row[j];
        }
        const new_row = row.slice();
        for (let j = 1; j < row.length; j++) {
            new_row[j] += swing[j] * row_total;
        }
        result.push(new_row);
    }
    return result;
}
<commit_msg>This is what I get for not writing a test<commit_after>"use strict";

// ---------------------------------------------------------------------------------------
// Database of party names and colors

// Dictionary of raw party data:
// - Key is the canonical short name.
// - Value is an array of aliases.
// - The first alias is the canonical long name.
const PARTY_DATA = {
    Con: ["Conservative", "C"],
    Lab: ["Labour", "Lab Co-op", "L"],
    LD: ["Liberal Democrat"],
    DUP: ["Democratic Unionist Party"],
    SNP: ["Scottish National Party"],
    PC: ["Plaid Cymru"],
    SDLP: ["Social Democratic and Labour Party"],
    Green: ["Green Party", "GRN"],
    Alliance: ["Alliance Party of Northern Ireland", "APNI"],
    UUP: ["Ulster Unionist Party"],
    UKIP: ["UK Independence Party"],
    SF: ["Sinn Féin", "Sinn Fein"], // Abstentionist, doesn't sit in the Commons
    Speaker: ["Speaker"] // Speaker of the House, traditionally non-partisan
};

const SHORT_NAME_MAP = {}; // Map of anything -> short name
const LONG_NAME_MAP = {}; // Map of anything -> long name

for (let short_name in PARTY_DATA) {
    const aliases = PARTY_DATA[short_name];
    const long_name = aliases[0];
    SHORT_NAME_MAP[short_name.toLowerCase()] = short_name;
    LONG_NAME_MAP[short_name.toLowerCase()] = long_name;
    for (let alias of aliases) {
        SHORT_NAME_MAP[alias.toLowerCase()] = short_name;
        LONG_NAME_MAP[alias.toLowerCase()] = long_name;
    }
}

/**
 * Get the canonical short version of any party name or synonym
 */
export function party_name(party) {
    return SHORT_NAME_MAP[party.toLowerCase()] || "Other";
}

/**
 * Get the canonical long version of any party name or synonym
 */
export function party_long_name(party) {
    return LONG_NAME_MAP[party.toLowerCase()] || "Other";
}

/**
 * Returns true for parties that vote normally in the House of Commons.
 * Only returns false for some special cases (Sinn Féin and the Speaker).
 */
export function party_takes_seat(party) {
    party = party_name(party);
    return party !== "SF" && party !== "Speaker";
}

// ---------------------------------------------------------------------------------------
// Basic calculations with raw election data
//
// Raw election data is a CSV-like array of arrays.
// The first row contains column headers (e.g. party names).
// The remaining rows contain voting data, one row per constituency.
// The first column is assumed to be the constituency name.

/**
 * Count the total number of votes cast for each party
 * @param data - table of per-constituency votes
 * @return map of party name -> seat count
 */
export function get_votes(data) {
    var keys = data[0];
    var votes = []
    var i, j;
    for (i = 1; i < keys.length; i++) {
        votes[i] = 0;
    }
    for (i = 1; i < data.length; i++) {
        var row = data[i];
        for (j = 1; j < row.length; j++) {
            votes[j] += row[j];
        }
    }
    var vote_map = {};
    for (i = 1; i < keys.length; i++) {
        vote_map[keys[i]] = votes[i];
    }
    return vote_map;
}

/**
 * Count the number of seats won by each party
 * @param data - table of per-constituency votes
 * @return map of party name -> seat count
 */
export function get_seats(data) {
    var keys = data[0];
    var seats = []
    var i, j;
    for (i = 1; i < keys.length; i++) {
        seats[i] = 0;
    }
    for (i = 1; i < data.length; i++) {
        var row = data[i];
        var max_indices = [];
        var max_value = 0;
        for (j = 1; j < row.length; j++) {
            if (row[j] > max_value) {
                max_indices = [j];
                max_value = row[j];
            } else if (row[j] == max_value) {
                max_indices.push(j);
            }
        }
        // Tied seats are divided evenly (in real life it's resolved by a coin toss)
        for (let winner of max_indices) {
            seats[winner] += 1.0 / max_indices.length;
        }
    }
    var seat_map = {};
    for (i = 1; i < keys.length; i++) {
        seat_map[keys[i]] = seats[i];
    }
    return seat_map;
}

/**
 * Given a party -> seats map, remove abstentionist parties (i.e. those who don't sit)
 * @param seat_map - map of party name -> seat count
 * @return map of party name -> seat count
 */
export function remove_unused_seats(seat_map) {
    var result = {};
    for (let party in seat_map) {
        result[party] = party_takes_seat(party) ? seat_map[party] : 0;
    }
    return result;
}

/**
 * Get votes or seats for the given parties, in order.
 * Parties not listed will be counted as "Other".
 * @param votes_or_seats - map of party name -> seat count or vote count
 * @param parties - list of party names
 * @return list of votes or seats for the corresponding parties
 */
export function count_by_party(votes_or_seats, parties) {
    var count = {};
    for (let party in votes_or_seats) {
        if (parties.indexOf(party) >= 0) {
            count[party] = (count[party] || 0) + votes_or_seats[party];
        } else {
            count.Other = (count.Other || 0) +  votes_or_seats[party];
        }
    }
    var result = [];
    for (let party of parties) {
        result.push(count[party]);
    }
    return result;
}

// ---------------------------------------------------------------------------------------
// Tactical voting

/**
 * Given raw data and a voting bloc, transforms the data as if the bloc voted tactically.
 * We assume that all bloc votes go to the biggest party in each constituency.
 * @param data - table of party votes per constituency
 * @return a new table of party votes per constituency
 */
export function adjust_data_with_tactics(data, bloc) {
    const keys = data[0];
    var result = [keys];
    const party_indices = [];
    for (let party of bloc) {
        party_indices.push(keys.indexOf(party));
    }
    for (let i = 1; i < data.length; i++) {
        const row = data[i];
        let total = 0;
        let biggest_vote = -1;
        let biggest_party = -1;
        for (let j of party_indices) {
            let vote = row[j];
            if (vote > biggest_vote) {
                biggest_vote = vote;
                biggest_party = j;
            }
            total += vote;
        }
        const new_row = row.slice();
        for (let j of party_indices) {
            new_row[j] = (j == biggest_party) ? total : 0;
        }
        result.push(new_row);
    }
    return result;
}

// ---------------------------------------------------------------------------------------
// Swingometer

/**
 * Scale the given votes so that the total vote counts is 1
 * @param votes - map of party name -> vote count
 * @return new map with normalized counts
 */
export function normalize_votes(votes) {
    let total = 0;
    for (let party in votes) {
        total += votes[party];
    }
    let result = {};
    for (let party in votes) {
        result[party] = votes[party] / total;
    }
    return result;
}

/**
 * Calculate the vote swing percentage for each party
 * @param prev - map of party name -> vote count
 * @param next - map of party name -> vote count
 * @return map of party name -> swing (in range 0 to 1)
 */
export function get_swing(prev, next) {
    prev = normalize_votes(prev);
    next = normalize_votes(next);
    let swing = {};
    for (let party in next) {
        swing[party] = next[party] - prev[party];
    }
    return swing;
}

/**
 * Given old election data and a new poll, return a forecast for the new election.
 * We use a simple uniform national swing model.
 * @param data - table of party votes per constituency
 * @param poll - map of party name -> vote count
 * @return a new table of party votes per constituency
 */
export function adjust_data_with_poll(data, poll) {
    const votes = get_votes(data);
    const swing_map = get_swing(votes, poll);
    const keys = data[0];
    let swing = [];
    for (let i = 1; i < keys.length; i++) {
        let s = swing_map[keys[i]];
        if (s === undefined) s = swing_map.Other;
        if (s === undefined) s = 0;
        swing[i] = s;
    }
    var result = [keys];
    for (let i = 1; i < data.length; i++) {
        const row = data[i];
        let row_total = 0;
        for (let j = 1; j < row.length; j++) {
            row_total += row[j];
        }
        const new_row = row.slice();
        for (let j = 1; j < row.length; j++) {
            new_row[j] += swing[j] * row_total;
        }
        result.push(new_row);
    }
    return result;
}
<|endoftext|>