<commit_before>// Karma configuration
// Generated on Fri May 05 2017 08:10:00 GMT+0000 (UTC)

module.exports = function(config) {

  var extraFormPath = '../../../vendor/idci/extra-form-bundle/IDCI/Bundle/ExtraFormBundle/Resources/public/js/';

  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: 'Resources/public/js/',

    browserConsoleLogOptions: {
      level: 'log',
      format: '%b %T: %m',
      terminal: true
    },

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['jasmine'],

    // list of files / patterns to load in the browser
    files: [
      // extra form bundle dependencies
      extraFormPath + '/vendor/jquery-2.2.4.min.js',
      extraFormPath + 'vendor/codemirror.js',
      extraFormPath + 'vendor/codemirror-javascript-mode.js',
      extraFormPath + 'vendor/vue-prod.min.js',
      extraFormPath + 'vendor/vuex-2.1.2.js',
      extraFormPath + 'vendor/sortable.min.js',
      extraFormPath + 'vendor/vue-multiselect.min.js',
      extraFormPath + 'vendor/vue-draggable.min.js',
      extraFormPath + 'vendor/vue-resource-1.2.0.min.js',
      extraFormPath + 'editor/utils/utils.js',
      extraFormPath + 'editor/utils/JsonToTwigTransformer.js',
      extraFormPath + 'editor/mixins/option.vue.js',
      extraFormPath + 'editor/mixins/jsonOption.vue.js',
      extraFormPath + 'editor/mixins/http.vue.js',
      extraFormPath + 'editor/mixins/rawModal.vue.js',
      extraFormPath + 'editor/mixins/raw.vue.js',
      extraFormPath + 'editor/mixins/icon.vue.js',
      extraFormPath + 'editor/store/getters.vue.js',
      extraFormPath + 'editor/store/actions.vue.js',
      extraFormPath + 'editor/store/mutations.vue.js',
      extraFormPath + 'editor/components/common/modal.vue.js',
      extraFormPath + 'editor/components/common/options/textarea.vue.js',
      extraFormPath + 'editor/components/common/options/choice.vue.js',
      extraFormPath + 'editor/components/common/options/text.vue.js',
      extraFormPath + 'editor/components/common/options/number.vue.js',
      extraFormPath + 'editor/components/common/options/integer.vue.js',
      extraFormPath + 'editor/components/common/options/checkbox.vue.js',
      extraFormPath + 'editor/components/editor-simple/types-selectbox.vue.js',
      extraFormPath + 'editor/components/editor-simple/new-field.vue.js',
      extraFormPath + 'editor/components/editor-simple/new-field-constraint.vue.js',
      extraFormPath + 'editor/components/editor-simple/field-options.vue.js',
      extraFormPath + 'editor/components/editor-simple/field-constraint-options.vue.js',
      extraFormPath + 'editor/components/editor-simple/field-constraints.vue.js',
      extraFormPath + 'editor/components/editor-simple/field.vue.js',
      extraFormPath + 'editor/components/editor-advanced/new-field-constraint.vue.js',
      extraFormPath + 'editor/components/editor-advanced/field-constraint-options.vue.js',
      extraFormPath + 'editor/components/editor-advanced/field-constraints.vue.js',
      extraFormPath + 'editor/components/editor-advanced/field-options.vue.js',
      extraFormPath + 'editor/components/editor-advanced/field.vue.js',
      extraFormPath + 'editor/components/editor-advanced/extra-form-fields.vue.js',
      extraFormPath + 'editor/components/editor-advanced/extra-form-fields-configuration.vue.js',
      extraFormPath + 'editor/components/editor-advanced/configured-extra-form-type.vue.js',
      extraFormPath + 'editor/components/editor-advanced/base-extra-form-type.vue.js',
      extraFormPath + 'editor/components/editor-advanced/extra-form-types.vue.js',
      extraFormPath + 'editor/components/editor-simple/editor.vue.js',
      extraFormPath + 'editor/components/editor-advanced/editor.vue.js',
      extraFormPath + 'editor/components/editor-raw.vue.js',
      extraFormPath + 'editor/app.js',
      'vendor/polyfills.js',
      'vendor/lodash-3.10.1.min.js',
      'vendor/backbone-1.3.3.js',
      'vendor/joint-1.1.0.js',
      'editor/diagram/DiagramColors.js',
      'editor/diagram/factory/DiagramStepFactory.js',
      'editor/diagram/factory/DiagramPathFactory.js',
      'editor/diagram/shapes/EndOfPathLink.js',
      'editor/diagram/shapes/IntersectionOfPathLinks.js',
      'editor/diagram/shapes/PathLink.js',
      'editor/diagram/shapes/Step.js',
      'editor/diagram/DiagramUtils.js',
      'editor/diagram/Diagram.js',
      'editor/utils/MapUtils.js',
      'editor/mixins/path-type-options.vue.js',
      'editor/components/step-editor/options/conditional-destinations-collection.vue.js',
      'editor/components/step-editor/options/form-builder.vue.js',
      'editor/store/getters.vue.js',
      'editor/store/mutations/mutations.vue.js',
      'editor/store/mutations/pathEventActionMutations.vue.js',
      'editor/store/mutations/stepEventActionMutations.vue.js',
      'editor/store/actions.vue.js',
      'editor/components/step-editor-raw.vue.js',
      'editor/components/step-editor/diagram.vue.js',
      'editor/components/step-editor/map/map-configuration-button.vue.js',
      'editor/components/step-editor/map/map-options.vue.js',
      'editor/components/step-editor/map/map-configuration.vue.js',
      'editor/components/step-editor/step-event-actions/new-step-event-action.vue.js',
      'editor/components/step-editor/step-event-actions/step-event-action-configuration.vue.js',
      'editor/components/step-editor/step-event-actions/step-event-actions.vue.js',
      'editor/components/step-editor/path-event-actions/new-path-event-action.vue.js',
      'editor/components/step-editor/path-event-actions/path-event-action-configuration.vue.js',
      'editor/components/step-editor/path-event-actions/path-event-actions.vue.js',
      'editor/components/step-editor/step-types/new-step-type.vue.js',
      'editor/components/step-editor/step-types/step-type-options.vue.js',
      'editor/components/step-editor/step-types/step-types-configuration.vue.js',
      'editor/components/step-editor/path-types/path-type-required-options.vue.js',
      'editor/components/step-editor/path-types/new-path-type.vue.js',
      'editor/components/step-editor/path-types/path-type-options.vue.js',
      'editor/components/step-editor/path-types/path-types-configuration.vue.js',
      'editor/components/step-editor/step-editor.vue.js',
      'editor/app.js',
      'editor/load-extra-step-editors.js',

      'tests/**/*.js'
    ],

    // list of files to exclude
    exclude: [
    ],

    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
    },

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress'],

    // web server port
    port: 9876,

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['Chromium', 'Firefox'],

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: false,

    // Concurrency level
    // how many browser should be started simultaneous
    concurrency: Infinity
  })
};
<commit_msg>Add missing js file for tests<commit_after>// Karma configuration
// Generated on Fri May 05 2017 08:10:00 GMT+0000 (UTC)

module.exports = function(config) {

  var extraFormPath = '../../../vendor/idci/extra-form-bundle/IDCI/Bundle/ExtraFormBundle/Resources/public/js/';

  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: 'Resources/public/js/',

    browserConsoleLogOptions: {
      level: 'log',
      format: '%b %T: %m',
      terminal: true
    },

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['jasmine'],

    // list of files / patterns to load in the browser
    files: [
      // extra form bundle dependencies
      extraFormPath + '/vendor/jquery-2.2.4.min.js',
      extraFormPath + 'vendor/codemirror.js',
      extraFormPath + 'vendor/codemirror-javascript-mode.js',
      extraFormPath + 'vendor/vue-prod.min.js',
      extraFormPath + 'vendor/vuex-2.1.2.js',
      extraFormPath + 'vendor/sortable.min.js',
      extraFormPath + 'vendor/vue-multiselect.min.js',
      extraFormPath + 'vendor/vue-draggable.min.js',
      extraFormPath + 'vendor/vue-resource-1.2.0.min.js',
      extraFormPath + 'editor/utils/utils.js',
      extraFormPath + 'editor/utils/JsonToTwigTransformer.js',
      extraFormPath + 'editor/mixins/waitForIt.vue.js',
      extraFormPath + 'editor/mixins/option.vue.js',
      extraFormPath + 'editor/mixins/jsonOption.vue.js',
      extraFormPath + 'editor/mixins/http.vue.js',
      extraFormPath + 'editor/mixins/rawModal.vue.js',
      extraFormPath + 'editor/mixins/raw.vue.js',
      extraFormPath + 'editor/mixins/icon.vue.js',
      extraFormPath + 'editor/store/getters.vue.js',
      extraFormPath + 'editor/store/actions.vue.js',
      extraFormPath + 'editor/store/mutations.vue.js',
      extraFormPath + 'editor/components/common/modal.vue.js',
      extraFormPath + 'editor/components/common/options/textarea.vue.js',
      extraFormPath + 'editor/components/common/options/choice.vue.js',
      extraFormPath + 'editor/components/common/options/text.vue.js',
      extraFormPath + 'editor/components/common/options/number.vue.js',
      extraFormPath + 'editor/components/common/options/integer.vue.js',
      extraFormPath + 'editor/components/common/options/checkbox.vue.js',
      extraFormPath + 'editor/components/editor-simple/types-selectbox.vue.js',
      extraFormPath + 'editor/components/editor-simple/new-field.vue.js',
      extraFormPath + 'editor/components/editor-simple/new-field-constraint.vue.js',
      extraFormPath + 'editor/components/editor-simple/field-options.vue.js',
      extraFormPath + 'editor/components/editor-simple/field-constraint-options.vue.js',
      extraFormPath + 'editor/components/editor-simple/field-constraints.vue.js',
      extraFormPath + 'editor/components/editor-simple/field.vue.js',
      extraFormPath + 'editor/components/editor-advanced/new-field-constraint.vue.js',
      extraFormPath + 'editor/components/editor-advanced/field-constraint-options.vue.js',
      extraFormPath + 'editor/components/editor-advanced/field-constraints.vue.js',
      extraFormPath + 'editor/components/editor-advanced/field-options.vue.js',
      extraFormPath + 'editor/components/editor-advanced/field.vue.js',
      extraFormPath + 'editor/components/editor-advanced/extra-form-fields.vue.js',
      extraFormPath + 'editor/components/editor-advanced/extra-form-fields-configuration.vue.js',
      extraFormPath + 'editor/components/editor-advanced/configured-extra-form-type.vue.js',
      extraFormPath + 'editor/components/editor-advanced/base-extra-form-type.vue.js',
      extraFormPath + 'editor/components/editor-advanced/extra-form-types.vue.js',
      extraFormPath + 'editor/components/editor-simple/editor.vue.js',
      extraFormPath + 'editor/components/editor-advanced/editor.vue.js',
      extraFormPath + 'editor/components/editor-raw.vue.js',
      extraFormPath + 'editor/app.js',
      'vendor/polyfills.js',
      'vendor/lodash-3.10.1.min.js',
      'vendor/backbone-1.3.3.js',
      'vendor/joint-1.1.0.js',
      'editor/diagram/DiagramColors.js',
      'editor/diagram/factory/DiagramStepFactory.js',
      'editor/diagram/factory/DiagramPathFactory.js',
      'editor/diagram/shapes/EndOfPathLink.js',
      'editor/diagram/shapes/IntersectionOfPathLinks.js',
      'editor/diagram/shapes/PathLink.js',
      'editor/diagram/shapes/Step.js',
      'editor/diagram/DiagramUtils.js',
      'editor/diagram/Diagram.js',
      'editor/utils/MapUtils.js',
      'editor/mixins/path-type-options.vue.js',
      'editor/components/step-editor/options/conditional-destinations-collection.vue.js',
      'editor/components/step-editor/options/form-builder.vue.js',
      'editor/store/getters.vue.js',
      'editor/store/mutations/mutations.vue.js',
      'editor/store/mutations/pathEventActionMutations.vue.js',
      'editor/store/mutations/stepEventActionMutations.vue.js',
      'editor/store/actions.vue.js',
      'editor/components/step-editor-raw.vue.js',
      'editor/components/step-editor/diagram.vue.js',
      'editor/components/step-editor/map/map-configuration-button.vue.js',
      'editor/components/step-editor/map/map-options.vue.js',
      'editor/components/step-editor/map/map-configuration.vue.js',
      'editor/components/step-editor/step-event-actions/new-step-event-action.vue.js',
      'editor/components/step-editor/step-event-actions/step-event-action-configuration.vue.js',
      'editor/components/step-editor/step-event-actions/step-event-actions.vue.js',
      'editor/components/step-editor/path-event-actions/new-path-event-action.vue.js',
      'editor/components/step-editor/path-event-actions/path-event-action-configuration.vue.js',
      'editor/components/step-editor/path-event-actions/path-event-actions.vue.js',
      'editor/components/step-editor/step-types/new-step-type.vue.js',
      'editor/components/step-editor/step-types/step-type-options.vue.js',
      'editor/components/step-editor/step-types/step-types-configuration.vue.js',
      'editor/components/step-editor/path-types/path-type-required-options.vue.js',
      'editor/components/step-editor/path-types/new-path-type.vue.js',
      'editor/components/step-editor/path-types/path-type-options.vue.js',
      'editor/components/step-editor/path-types/path-types-configuration.vue.js',
      'editor/components/step-editor/step-editor.vue.js',
      'editor/app.js',
      'editor/load-extra-step-editors.js',

      'tests/**/*.js'
    ],

    // list of files to exclude
    exclude: [
    ],

    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
    },

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress'],

    // web server port
    port: 9876,

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['Chromium', 'Firefox'],

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: false,

    // Concurrency level
    // how many browser should be started simultaneous
    concurrency: Infinity
  })
};
<|endoftext|>


<commit_before>(function ($) {

  $.fn.calculate = function (stringOrFunction, opts) {
    var opts = $.extend({}, $.calculate.defaults(), opts || {});
    var api = new Api(this, opts);

    if (typeof stringOrFunction === 'string') {
      api.formula(stringOrFunction);
    }
    else {
      stringOrFunction.call(stringOrFunction, api);
    }

    return this;
  }

  function Api(base, opts) {
    this.base = $(base);
    this.details = null;
    this.opts = opts || {};
  }
  Api.prototype = {
    formula: function(val) {
      this.compile(val);
      this.updateEvents();
      this.run();
    },

    compile: function(formula) {
      this.details = parseFormula(formula);
      var tree = math.parse(this.details.rside);
      this.compiled = tree.compile(math);
    },

    updateEvents: function() {
      var that = this;
      Object.keys(this.details.operands).forEach(function(selector) {
        that.base.find(selector).on('change', function() {
          that.run();
        });
      });
    },

    run: function() {
      var that = this;
      this.base.each(function() {
        var singleBase = $(this);
        var values = readValues(that.details.operands, singleBase, that.opts.inputParser);
        var result = that.compiled.eval(values);
        var formattedResult = that.opts.outputFormatter(result);
        singleBase.find(that.details.resultSelector)
          .val(formattedResult)
          .trigger('change');
      });
    }
  }

  $.calculate = {
    inputParser: function(rawVal) { return rawVal },

    outputFormatter: function(result) { return result },

    options: function(opts) {
      if (opts.inputParser) {
        this.inputParser = opts.inputParser;
      }
      if (opts.outputFormatter) {
        this.outputFormatter = opts.outputFormatter;
      }
    },

    defaults: function() {
      return {
        inputParser: this.inputParser,
        outputFormatter: this.outputFormatter
      }
    }
  }

  // Receives a string such as '{{.foo}} = {{.bar}} - {{.baz}}'.
  // Returns an object such as:
  //   formula.operands => { '.bar': 'B', '.baz': 'C' }
  //   formula.resultSelector => '.foo'
  //   formula.rside => 'B - C'
  function parseFormula(str) {
    var formula = {
      operands: {},
    };

    var sides = str.match(/^\s*([^=\s]+)\s*=\s*(.*)$/);
    formula.rside = assignVariablesToSelectors(sides[2], formula.operands);
    formula.resultSelector = readFirstSelector(sides[1]);

    return formula;
  }

  var formulaSelectorsRegexp = '\{\{([^\}]+)\}\}';

  function assignVariablesToSelectors(formula, dict) {
    var re = new RegExp(formulaSelectorsRegexp, 'g');
    return formula.replace(re, function (_, capture) {
      var existing = dict[capture];
      if (existing) {
        return existing;
      }
      return dict[capture] = generateVarName();
    });
  }

  function readFirstSelector(formula) {
    var re = new RegExp(formulaSelectorsRegexp);
    return re.exec(formula)[1];
  }

  // Simply returning letters in order for now
  var generateVarName_count = 0;
  function generateVarName(index) {
    return 'X' + (generateVarName_count++);
  }

  // Receives:
  //   * operands: { '.foo': 'A', '.bar': 'B', '.baz': 'C' }
  //   * element: base for DOM search
  // Returns {'A': '23.5', 'B': '12.8', C: '3'}
  function readValues(operands, element, inputParser) {
    var values = {};
    var base = $(element);
    $.each(asPairs(operands), function() {
      var selector = this[0];
      var varname = this[1];
      var matches = base.find(selector);
      values[varname] = matches.toArray().reduce(function(sum, el) {
        var rawVal = $(el).val();
        return sum + inputParser(rawVal)*1;
      }, 0);
    });
    return values;
  }

  // Receive an object and return key/value pairs
  function asPairs(obj) {
    var pairs = [];
    Object.keys(obj).forEach(function (key) {
      var value = obj[key];
      pairs.push([key, value]);
    });
    return pairs;
  }

})(jQuery);
<commit_msg>Remove unused events<commit_after>(function ($) {

  $.fn.calculate = function (stringOrFunction, opts) {
    var opts = $.extend({}, $.calculate.defaults(), opts || {});
    var api = new Api(this, opts);

    if (typeof stringOrFunction === 'string') {
      api.formula(stringOrFunction);
    }
    else {
      stringOrFunction.call(stringOrFunction, api);
    }

    return this;
  }

  function Api(base, opts) {
    this.base = $(base);
    this.details = null;
    this.opts = opts || {};
    this.events = [];
  }
  Api.prototype = {
    formula: function(val) {
      this.compile(val);
      this.updateEvents();
      this.run();
    },

    compile: function(formula) {
      this.details = parseFormula(formula);
      var tree = math.parse(this.details.rside);
      this.compiled = tree.compile(math);
    },

    updateEvents: function() {
      this.removeEvents();
      this.setEvents();
    },

    removeEvents: function() {
      this.events.forEach(function(evt) {
        evt.query.off(evt.name, evt.handler);
      });
      this.events = [];
    },

    setEvents: function() {
      var that = this;
      Object.keys(this.details.operands).forEach(function(selector) {
        var evt = {
          query: that.base.find(selector),
          name: 'change',
          handler: function() { that.run(); }
        }
        evt.query.on(evt.name, evt.handler);
        that.events.push(evt);
      });
    },

    run: function() {
      var that = this;
      this.base.each(function() {
        var singleBase = $(this);
        var values = readValues(that.details.operands, singleBase, that.opts.inputParser);
        var result = that.compiled.eval(values);
        var formattedResult = that.opts.outputFormatter(result);
        singleBase.find(that.details.resultSelector)
          .val(formattedResult)
          .trigger('change');
      });
    }
  }

  $.calculate = {
    inputParser: function(rawVal) { return rawVal },

    outputFormatter: function(result) { return result },

    options: function(opts) {
      if (opts.inputParser) {
        this.inputParser = opts.inputParser;
      }
      if (opts.outputFormatter) {
        this.outputFormatter = opts.outputFormatter;
      }
    },

    defaults: function() {
      return {
        inputParser: this.inputParser,
        outputFormatter: this.outputFormatter
      }
    }
  }

  // Receives a string such as '{{.foo}} = {{.bar}} - {{.baz}}'.
  // Returns an object such as:
  //   formula.operands => { '.bar': 'B', '.baz': 'C' }
  //   formula.resultSelector => '.foo'
  //   formula.rside => 'B - C'
  function parseFormula(str) {
    var formula = {
      operands: {},
    };

    var sides = str.match(/^\s*([^=\s]+)\s*=\s*(.*)$/);
    formula.rside = assignVariablesToSelectors(sides[2], formula.operands);
    formula.resultSelector = readFirstSelector(sides[1]);

    return formula;
  }

  var formulaSelectorsRegexp = '\{\{([^\}]+)\}\}';

  function assignVariablesToSelectors(formula, dict) {
    var re = new RegExp(formulaSelectorsRegexp, 'g');
    return formula.replace(re, function (_, capture) {
      var existing = dict[capture];
      if (existing) {
        return existing;
      }
      return dict[capture] = generateVarName();
    });
  }

  function readFirstSelector(formula) {
    var re = new RegExp(formulaSelectorsRegexp);
    return re.exec(formula)[1];
  }

  // Simply returning letters in order for now
  var generateVarName_count = 0;
  function generateVarName(index) {
    return 'X' + (generateVarName_count++);
  }

  // Receives:
  //   * operands: { '.foo': 'A', '.bar': 'B', '.baz': 'C' }
  //   * element: base for DOM search
  // Returns {'A': '23.5', 'B': '12.8', C: '3'}
  function readValues(operands, element, inputParser) {
    var values = {};
    var base = $(element);
    $.each(asPairs(operands), function() {
      var selector = this[0];
      var varname = this[1];
      var matches = base.find(selector);
      values[varname] = matches.toArray().reduce(function(sum, el) {
        var rawVal = $(el).val();
        return sum + inputParser(rawVal)*1;
      }, 0);
    });
    return values;
  }

  // Receive an object and return key/value pairs
  function asPairs(obj) {
    var pairs = [];
    Object.keys(obj).forEach(function (key) {
      var value = obj[key];
      pairs.push([key, value]);
    });
    return pairs;
  }

})(jQuery);
<|endoftext|>


<commit_before>// Nothing
<commit_msg>Updates a flag to ensure loading and execution (#168)<commit_after>// Nothing particular
"use strict";

gpf.context().test.empty = true;
<|endoftext|>


<commit_before>'use strict';

describe('monomer library', function () {
  // start at the main page
  beforeEach(function () {
    browser.get('/');
  });

  // idea was to catch the message while initializing, but seems like it's too slow
  // it('should display the placeholder immediately upon loading', function () {
  //   var monomerLibraryHolder = element(by.css('.library-column'));
  //   expect(monomerLibraryHolder.all(by.xpath('./div')).get(0).isDisplayed()).toBeTruthy();
  //   expect(monomerLibraryHolder.all(by.xpath('./div')).get(1).isDisplayed()).toBeTruthy();
  // });

  it('should display all three main types', function () {
    var monomerLibraryHolder = element(by.css('.library-column'));
    var libraryTypeHolder = monomerLibraryHolder.element(by.id('libraryTypeButtons'));
    var typeButtons = libraryTypeHolder.all(by.tagName('button'));
    expect(typeButtons.count()).toBe(3);
    expect(typeButtons.get(0).getText()).toBe('RNA');
    expect(typeButtons.get(0).getAttribute('class')).toContain('activetype');
    expect(typeButtons.get(1).getText()).toBe('PEPTIDE');
    expect(typeButtons.get(2).getText()).toBe('CHEM');
  });

  it('should be able to search for monomers by typing in some text', function () {
    var monomerLibraryHolder = element(by.css('.library-column'));
    var searchInput = monomerLibraryHolder.element(by.css('.search-input'));

    // make sure the right number are showing up for RNA
    var searchViewDisplay = element(by.id('searchViewDisplay'));
    expect(searchViewDisplay.all(by.css('.monomer-directive')).count()).toBe(0);
    searchInput.sendKeys('a');
    expect(searchViewDisplay.all(by.css('.monomer-directive')).count()).toBe(51);
    searchInput.sendKeys('s');
    expect(searchViewDisplay.all(by.css('.monomer-directive')).count()).toBe(1);
    expect(searchViewDisplay.all(by.css('.monomer-directive')).get(0).element(by.tagName('span')).getText()).toBe('nasP');
    searchInput.clear();
    expect(searchViewDisplay.all(by.css('.monomer-directive')).count()).toBe(0);

    // make sure the right number are showing up for PEPTIDE
    var libraryTypeHolder = monomerLibraryHolder.element(by.id('libraryTypeButtons'));
    var typeButtons = libraryTypeHolder.all(by.tagName('button'));
    typeButtons.get(1).click();
    expect(searchViewDisplay.all(by.css('.monomer-directive')).count()).toBe(0);
    searchInput.sendKeys('a');
    expect(searchViewDisplay.all(by.css('.monomer-directive')).count()).toBe(77);
    searchInput.sendKeys('s');
    expect(searchViewDisplay.all(by.css('.monomer-directive')).count()).toBe(7);
    searchInput.sendKeys('u');
    expect(searchViewDisplay.all(by.css('.monomer-directive')).count()).toBe(1);
    expect(searchViewDisplay.all(by.css('.monomer-directive')).get(0).element(by.tagName('span')).getText()).toBe('Asu');
    searchInput.clear();
    expect(searchViewDisplay.all(by.css('.monomer-directive')).count()).toBe(0);

    // make sure the right number are showing up for CHEM
    typeButtons.get(2).click();
    expect(searchViewDisplay.all(by.css('.monomer-directive')).count()).toBe(0);
    searchInput.sendKeys('a');
    expect(searchViewDisplay.all(by.css('.monomer-directive')).count()).toBe(6);
    searchInput.sendKeys('lex');
    expect(searchViewDisplay.all(by.css('.monomer-directive')).count()).toBe(1);
    expect(searchViewDisplay.all(by.css('.monomer-directive')).get(0).element(by.tagName('span')).getText()).toBe('Alexa');
    searchInput.clear();
    expect(searchViewDisplay.all(by.css('.monomer-directive')).count()).toBe(0);
  });

  it('should be able to explore the RNA for a specific type of monomer', function () {
    var monomerLibraryHolder = element(by.css('.library-column'));
    var exploreViewDisplay = element(by.id('exploreViewDisplay'));

    // select the explore view
    expect(exploreViewDisplay.isDisplayed()).not.toBeTruthy();
    var viewSelector = monomerLibraryHolder.element(by.css('.view-selector'));
    viewSelector.all(by.tagName('button')).get(1).click();
    expect(exploreViewDisplay.isDisplayed()).toBeTruthy();

    // click on types until you reach one where there are elements displayed
    exploreViewDisplay.all(by.css('.category-holder')).get(0).element(by.tagName('button')).click();
    expect(exploreViewDisplay.all(by.css('.category-holder')).get(0).all(by.css('.monomer-directive')).count()).toBe(5);
    expect(exploreViewDisplay.all(by.css('.monomer-directive')).get(0).isDisplayed()).toBeTruthy();
    expect(exploreViewDisplay.all(by.css('.monomer-directive')).get(0).element(by.tagName('span')).getText()).toBe('A');

    // try the Modified ones
    expect(exploreViewDisplay.isDisplayed()).toBeTruthy();
    var categoryHolder = exploreViewDisplay.all(by.css('.category-holder')).get(1);
    categoryHolder.all(by.tagName('button')).get(0).click();
    // categoryHolder.all(by.css('.sub-category-holder')).get(0).element(by.tagName('button')).click();
    // expect(categoryHolder.all(by.css('.sub-category-holder')).get(0).all(by.css('.monomer-directive')).count()).toBe(10);
    // expect(categoryHolder.all(by.css('.sub-category-holder')).get(0).all(by.css('.monomer-directive')).get(4).isDisplayed()).toBeTruthy();
    // expect(categoryHolder.all(by.css('.sub-category-holder')).get(0).all(by.css('.monomer-directive')).get(4).element(by.tagName('span')).getText()).toBe('dG');
  });

  it('should be able to explore the PEPTIDES for a specific type of monomer', function () {
    var monomerLibraryHolder = element(by.css('.library-column'));
    var exploreViewDisplay = element(by.id('exploreViewDisplay'));
    var libraryTypeHolder = monomerLibraryHolder.element(by.id('libraryTypeButtons'));
    var typeButtons = libraryTypeHolder.all(by.tagName('button'));

    // select the explore view
    expect(exploreViewDisplay.isDisplayed()).not.toBeTruthy();
    var viewSelector = monomerLibraryHolder.element(by.css('.view-selector'));
    viewSelector.all(by.tagName('button')).get(1).click();
    expect(exploreViewDisplay.isDisplayed()).toBeTruthy();

    // go to the peptides
    typeButtons.get(1).click();

    // click on types until you reach one where there are elements displayed
    exploreViewDisplay.all(by.css('.category-holder')).get(0).element(by.xpath('./button')).click();
    exploreViewDisplay.all(by.css('.sub-category-holder')).get(0).element(by.tagName('button')).click();
    expect(exploreViewDisplay.all(by.css('.sub-category-holder')).get(0).all(by.css('.monomer-directive')).count()).toBe(20);
    expect(exploreViewDisplay.all(by.css('.sub-category-holder')).get(0).all(by.css('.monomer-directive')).get(4).isDisplayed()).toBeTruthy();
    expect(exploreViewDisplay.all(by.css('.sub-category-holder')).get(0).all(by.css('.monomer-directive')).get(4).element(by.tagName('span')).getText()).toBe('F');
  });

  it('should be able to explore the CHEMs for a specific type of monomer', function () {
    var monomerLibraryHolder = element(by.css('.library-column'));
    var exploreViewDisplay = element(by.id('exploreViewDisplay'));
    var libraryTypeHolder = monomerLibraryHolder.element(by.id('libraryTypeButtons'));
    var typeButtons = libraryTypeHolder.all(by.tagName('button'));

    // select the explore view
    expect(exploreViewDisplay.isDisplayed()).not.toBeTruthy();
    var viewSelector = monomerLibraryHolder.element(by.css('.view-selector'));
    viewSelector.all(by.tagName('button')).get(1).click();
    expect(exploreViewDisplay.isDisplayed()).toBeTruthy();

    // go to the chems
    typeButtons.get(2).click();

    // click on types until you reach one where there are elements displayed
    expect(exploreViewDisplay.all(by.css('.category-holder')).get(4).all(by.css('.monomer-directive')).get(0).isDisplayed()).not.toBeTruthy();
    exploreViewDisplay.all(by.css('.category-holder')).get(4).element(by.tagName('button')).click();
    expect(exploreViewDisplay.all(by.css('.category-holder')).get(4).all(by.css('.monomer-directive')).count()).toBe(1);
    expect(exploreViewDisplay.all(by.css('.category-holder')).get(4).all(by.css('.monomer-directive')).get(0).isDisplayed()).toBeTruthy();
    expect(exploreViewDisplay.all(by.css('.category-holder')).get(4).all(by.css('.monomer-directive')).get(0).element(by.tagName('span')).getText()).toBe('sDBL');
  });

});<commit_msg>now back to testing sugars<commit_after>'use strict';

describe('monomer library', function () {
  // start at the main page
  beforeEach(function () {
    browser.get('/');
  });

  // idea was to catch the message while initializing, but seems like it's too slow
  // it('should display the placeholder immediately upon loading', function () {
  //   var monomerLibraryHolder = element(by.css('.library-column'));
  //   expect(monomerLibraryHolder.all(by.xpath('./div')).get(0).isDisplayed()).toBeTruthy();
  //   expect(monomerLibraryHolder.all(by.xpath('./div')).get(1).isDisplayed()).toBeTruthy();
  // });

  it('should display all three main types', function () {
    var monomerLibraryHolder = element(by.css('.library-column'));
    var libraryTypeHolder = monomerLibraryHolder.element(by.id('libraryTypeButtons'));
    var typeButtons = libraryTypeHolder.all(by.tagName('button'));
    expect(typeButtons.count()).toBe(3);
    expect(typeButtons.get(0).getText()).toBe('RNA');
    expect(typeButtons.get(0).getAttribute('class')).toContain('activetype');
    expect(typeButtons.get(1).getText()).toBe('PEPTIDE');
    expect(typeButtons.get(2).getText()).toBe('CHEM');
  });

  it('should be able to search for monomers by typing in some text', function () {
    var monomerLibraryHolder = element(by.css('.library-column'));
    var searchInput = monomerLibraryHolder.element(by.css('.search-input'));

    // make sure the right number are showing up for RNA
    var searchViewDisplay = element(by.id('searchViewDisplay'));
    expect(searchViewDisplay.all(by.css('.monomer-directive')).count()).toBe(0);
    searchInput.sendKeys('a');
    expect(searchViewDisplay.all(by.css('.monomer-directive')).count()).toBe(51);
    searchInput.sendKeys('s');
    expect(searchViewDisplay.all(by.css('.monomer-directive')).count()).toBe(1);
    expect(searchViewDisplay.all(by.css('.monomer-directive')).get(0).element(by.tagName('span')).getText()).toBe('nasP');
    searchInput.clear();
    expect(searchViewDisplay.all(by.css('.monomer-directive')).count()).toBe(0);

    // make sure the right number are showing up for PEPTIDE
    var libraryTypeHolder = monomerLibraryHolder.element(by.id('libraryTypeButtons'));
    var typeButtons = libraryTypeHolder.all(by.tagName('button'));
    typeButtons.get(1).click();
    expect(searchViewDisplay.all(by.css('.monomer-directive')).count()).toBe(0);
    searchInput.sendKeys('a');
    expect(searchViewDisplay.all(by.css('.monomer-directive')).count()).toBe(77);
    searchInput.sendKeys('s');
    expect(searchViewDisplay.all(by.css('.monomer-directive')).count()).toBe(7);
    searchInput.sendKeys('u');
    expect(searchViewDisplay.all(by.css('.monomer-directive')).count()).toBe(1);
    expect(searchViewDisplay.all(by.css('.monomer-directive')).get(0).element(by.tagName('span')).getText()).toBe('Asu');
    searchInput.clear();
    expect(searchViewDisplay.all(by.css('.monomer-directive')).count()).toBe(0);

    // make sure the right number are showing up for CHEM
    typeButtons.get(2).click();
    expect(searchViewDisplay.all(by.css('.monomer-directive')).count()).toBe(0);
    searchInput.sendKeys('a');
    expect(searchViewDisplay.all(by.css('.monomer-directive')).count()).toBe(6);
    searchInput.sendKeys('lex');
    expect(searchViewDisplay.all(by.css('.monomer-directive')).count()).toBe(1);
    expect(searchViewDisplay.all(by.css('.monomer-directive')).get(0).element(by.tagName('span')).getText()).toBe('Alexa');
    searchInput.clear();
    expect(searchViewDisplay.all(by.css('.monomer-directive')).count()).toBe(0);
  });

  it('should be able to explore the RNA for a specific type of monomer', function () {
    var monomerLibraryHolder = element(by.css('.library-column'));
    var exploreViewDisplay = element(by.id('exploreViewDisplay'));

    // select the explore view
    expect(exploreViewDisplay.isDisplayed()).not.toBeTruthy();
    var viewSelector = monomerLibraryHolder.element(by.css('.view-selector'));
    viewSelector.all(by.tagName('button')).get(1).click();
    expect(exploreViewDisplay.isDisplayed()).toBeTruthy();

    // click on types until you reach one where there are elements displayed
    exploreViewDisplay.all(by.css('.category-holder')).get(0).element(by.tagName('button')).click();
    expect(exploreViewDisplay.all(by.css('.category-holder')).get(0).all(by.css('.monomer-directive')).count()).toBe(5);
    expect(exploreViewDisplay.all(by.css('.monomer-directive')).get(0).isDisplayed()).toBeTruthy();
    expect(exploreViewDisplay.all(by.css('.monomer-directive')).get(0).element(by.tagName('span')).getText()).toBe('A');

    // try the sugars
    exploreViewDisplay.all(by.css('.category-holder')).get(2).element(by.xpath('./button')).click();;
    exploreViewDisplay.all(by.css('.category-holder')).get(2).all(by.css('.sub-category-holder')).get(0).element(by.tagName('button')).click();
    expect(exploreViewDisplay.all(by.css('.category-holder')).get(2).all(by.css('.sub-category-holder')).get(0).all(by.css('.monomer-directive')).count()).toBe(6);
    expect(exploreViewDisplay.all(by.css('.category-holder')).get(2).all(by.css('.sub-category-holder')).get(0).all(by.css('.monomer-directive')).get(4).isDisplayed()).toBeTruthy();
    expect(exploreViewDisplay.all(by.css('.category-holder')).get(2).all(by.css('.sub-category-holder')).get(0).all(by.css('.monomer-directive')).get(4).element(by.tagName('span')).getText()).toBe('mR');
  });

  it('should be able to explore the PEPTIDES for a specific type of monomer', function () {
    var monomerLibraryHolder = element(by.css('.library-column'));
    var exploreViewDisplay = element(by.id('exploreViewDisplay'));
    var libraryTypeHolder = monomerLibraryHolder.element(by.id('libraryTypeButtons'));
    var typeButtons = libraryTypeHolder.all(by.tagName('button'));

    // select the explore view
    expect(exploreViewDisplay.isDisplayed()).not.toBeTruthy();
    var viewSelector = monomerLibraryHolder.element(by.css('.view-selector'));
    viewSelector.all(by.tagName('button')).get(1).click();
    expect(exploreViewDisplay.isDisplayed()).toBeTruthy();

    // go to the peptides
    typeButtons.get(1).click();

    // click on types until you reach one where there are elements displayed
    exploreViewDisplay.all(by.css('.category-holder')).get(0).element(by.xpath('./button')).click();
    exploreViewDisplay.all(by.css('.sub-category-holder')).get(0).element(by.tagName('button')).click();
    expect(exploreViewDisplay.all(by.css('.sub-category-holder')).get(0).all(by.css('.monomer-directive')).count()).toBe(20);
    expect(exploreViewDisplay.all(by.css('.sub-category-holder')).get(0).all(by.css('.monomer-directive')).get(4).isDisplayed()).toBeTruthy();
    expect(exploreViewDisplay.all(by.css('.sub-category-holder')).get(0).all(by.css('.monomer-directive')).get(4).element(by.tagName('span')).getText()).toBe('F');
  });

  it('should be able to explore the CHEMs for a specific type of monomer', function () {
    var monomerLibraryHolder = element(by.css('.library-column'));
    var exploreViewDisplay = element(by.id('exploreViewDisplay'));
    var libraryTypeHolder = monomerLibraryHolder.element(by.id('libraryTypeButtons'));
    var typeButtons = libraryTypeHolder.all(by.tagName('button'));

    // select the explore view
    expect(exploreViewDisplay.isDisplayed()).not.toBeTruthy();
    var viewSelector = monomerLibraryHolder.element(by.css('.view-selector'));
    viewSelector.all(by.tagName('button')).get(1).click();
    expect(exploreViewDisplay.isDisplayed()).toBeTruthy();

    // go to the chems
    typeButtons.get(2).click();

    // click on types until you reach one where there are elements displayed
    expect(exploreViewDisplay.all(by.css('.category-holder')).get(4).all(by.css('.monomer-directive')).get(0).isDisplayed()).not.toBeTruthy();
    exploreViewDisplay.all(by.css('.category-holder')).get(4).element(by.tagName('button')).click();
    expect(exploreViewDisplay.all(by.css('.category-holder')).get(4).all(by.css('.monomer-directive')).count()).toBe(1);
    expect(exploreViewDisplay.all(by.css('.category-holder')).get(4).all(by.css('.monomer-directive')).get(0).isDisplayed()).toBeTruthy();
    expect(exploreViewDisplay.all(by.css('.category-holder')).get(4).all(by.css('.monomer-directive')).get(0).element(by.tagName('span')).getText()).toBe('sDBL');
  });

});<|endoftext|>


<commit_before>(function($, createjs, io, window, undefined){

	// 定数定義
	var CONSTANT = {
		SIZE: {
			width: 1920,
			height: 1080
		},
		fps: 60,
		SCREEN: {
			LOADING: 0,
			TITLE:   1,
			TIMER:   2
		}
	};

	var manifest = [{}];

	var _SCREENSTATUS = CONSTANT.SCREEN.LOADING;
	var _SCREENSTATUS_OLD = null;

	var easel, Easel = function() {
		easel = this;
		this.stage = null;
		this.initalized();
	};
	Easel.prototype = {
		// 初期化
		initalized: function() {
			// 画面領域の設定(Retinaの対応)
			$("#game").attr({width: CONSTANT.SIZE.width, height: CONSTANT.SIZE.height}).css({width: CONSTANT.SIZE.width/2, height:CONSTANT.SIZE.height/2});
			// ステージの作成
			this.stage = new createjs.Stage($("#game").get(0));
			// 入力の受付
			createjs.Touch.enable(this.stage);
			// FPSの設定
			createjs.Ticker.setFPS(CONSTANT.fps);
			// ticker
			createjs.Ticker.addEventListener("tick", $.proxy(this.transitScreen, this));
		},
		transitScreen: function(event) {
			// 画面が変更されたら
			if ( _SCREENSTATUS != _SCREENSTATUS_OLD ) {
				// ローディング画面
				if ( _SCREENSTATUS == CONSTANT.SCREEN.LOADING ) {
					this.stage.removeAllChildren();
					this.stage.addChild(this.displayLoading());
				}
				// タイトル画面
				else if ( _SCREENSTATUS == CONSTANT.SCREEN.TITLE ) {
					this.stage.removeAllChildren();
					this.stage.addChild(this.displayTitle());
				}
				// タイマー
				else if ( _SCREENSTATUS == CONSTANT.SCREEN.TIMER ) {
					this.stage.removeAllChildren();
					this.stage.addChild(this.displayTimer());
				}
			}
			this.stage.update();
			_SCREENSTATUS_OLD = _SCREENSTATUS;
		},
		displayLoading: function() {

		},
		displayTitle: function() {
			var container = new createjs.Container();
			// 黒色の背景
			var back = new createjs.Shape();
			back.graphics.f("#000").r(0, 0, CONSTANT.SIZE.width, CONSTANT.SIZE.height);
			back.set({x: 0, y: 0});
			var text = new createjs.Text("よみこみかんりょう！", "48px PixelMplus12", "#fff");
			text.set({
				textAlign: "center",
				x: CONSTANT.SIZE.width/2,
				y :CONSTANT.SIZE.height/2 - text.getMeasuredHeight()/2,
			});
			var start = new createjs.Text("クリックでスタート", "48px PixelMplus12", "#fff");
			start.set({
				textAlign: "center",
				x: CONSTANT.SIZE.width/2,
				y :CONSTANT.SIZE.height/2 - text.getMeasuredHeight()/2 + 280,
			});
			createjs.Tween.get(start, {loop: true})
				.to({alpha: 1}, 300)
				.to({alpha: 0}, 300)
				.to({alpha: 1}, 300);

			container.addChild(back, start, text);
			container.addEventListener("click", function(event) {
				_SCREENSTATUS = CONSTANT.SCREEN.TIMER;
			});

			return container;
		},
		displayTimer: function() {
			var container = new createjs.Container();
			// 黒色の背景
			var back = new createjs.Shape();
			back.graphics.f("#000").r(0, 0, CONSTANT.SIZE.width, CONSTANT.SIZE.height);
			back.set({x: 0, y: 0});
			var text = new createjs.Text("残り時間", "72px PixelMplus12", "#FFF");
			text.set({
				textAlign: "center",
				x: CONSTANT.SIZE.width/2,
				y :CONSTANT.SIZE.height/2 - text.getMeasuredHeight()/2 - 200
			});
			var shadow = new createjs.Text("88:88:88.88", "Italic Bold 180px DSEG7 Classic Mini", "#222");
			shadow.set({
				textAlign: "center",
				x: CONSTANT.SIZE.width/2,
				y :CONSTANT.SIZE.height/2 - shadow.getMeasuredHeight()/2
			});
			var time = new createjs.Text(timer.getFormattedTimes(), "Italic Bold 180px DSEG7 Classic Mini", "#FFD032");
			time.set({
				textAlign: "center",
				x: CONSTANT.SIZE.width/2,
				y :CONSTANT.SIZE.height/2 - time.getMeasuredHeight()/2
			});

			createjs.Ticker.addEventListener("tick", function(evt) {
				if ( timer.countdown ) {
					timer.decreaseTimes(evt.delta/1000);
				}
				var color = timer.getTimeColor();
				time.set({text: timer.getFormattedTimes(), color: color });
			});

			container.addChild(back, shadow, text, time);
			return container;
		}
	};

	var timer, Timer = function() {
		timer = this;
		this.second = 0;
		this.delta = 0;
		this.countdown = false;
		this.countup = false;
		this.initalized();
	};
	Timer.prototype = {
		initalized: function() {
			this.second = 0;
		},
		decreaseTimes: function(delta) {
			if ( this.countup ) {
				delta *= -1;
			}
			this.second -= delta;
			if ( this.second < 0 ) {
				this.stopTimeover();
			}
		},
		getTimeColor: function() {
			var color = "#FFD032";
			if ( this.second == 0 ) {
				color = "#FF3B21";
			}
			return color;
		},
		getTimes: function() {
			return this.second;
		},
		getFormattedTimes: function() {
			var second = this.second;
			var times = {
				hour: this.fillZero(Math.floor(second/3600)),
				min:  this.fillZero(Math.floor(second/60%60)),
				sec:  this.fillZero(Math.floor(second%60)),
				msec: this.fillZero(Math.floor(second*100%100))
			};

			return [ times.hour, times.min, times.sec ].join(":") + "." + times.msec;
		},
		startCount: function() {
			this.countup = false;
			this.countdown = true;
		},
		stopCount: function() {
			this.countdown = false;
		},
		stopTimeover: function() {
			this.stopCount();
			this.resetCount(0);
			socketio.socket.emit("stop timer");
		},
		resetCount: function(time) {
			this.countdown = this.countup = false;
			this.second = time;
		},
		fillZero: function(num) {
			return (("0"+num).slice(-2));
		},
		setCountup: function() {
			this.countup = true;
		}
	};

	// Preload.js
	var preload, Preload = function() {
		preload = this;
		this.load = null;
		this.initalized();
	};
	Preload.prototype = {
		// 初期化
		initalized: function() {
			// Preload.js
			this.load = new createjs.LoadQueue();

			// 最大並列接続数
			this.load.setMaxConnections(6);

			// 読み込みの進行状況が変化した
			this.load.addEventListener("progress", this.handleProgress);
			// 1つのファイルを読み込み終わったら
			this.load.addEventListener("fileload", this.handleFileLoadComplete);
			// 全てのファイルを読み込み終わったら
			this.load.addEventListener("complete", this.handleComplete);

			// 読み込み開始
			this.load.loadManifest(manifest);
		},
		// 読み込み中
		handleProgress: function(event) {
			// 読み込み率を0.0~1.0で取得
			var progress = event.progress;
		},
		// ファイルごとの読み込み完了イベント
		handleProgress: function(event) {
			// 読み込んだファイル
			var result = event.result;
		},
		// 読み込み完了
		handleComplete: function() {
			_SCREENSTATUS = CONSTANT.SCREEN.TITLE;
		}
	}

	var socketio, SocketIO = function() {
		socketio = this;
		this.socket = io();
		this.initalized();
	};
	SocketIO.prototype = {
		initalized: function() {
			this.socket
				.on("start timer", $.proxy(this.startTimer, this))
				.on("stop timer", $.proxy(this.stopTimer, this))
				.on("set timer", $.proxy(this.resetTimer, this))
				.on("countup timer", $.proxy(this.countupTimer, this));
		},
		startTimer: function() {
			timer.startCount();
		},
		stopTimer: function() {
			timer.stopCount();
		},
		resetTimer: function(sec) {
			timer.resetCount(sec);
		},
		countupTimer: function() {
			timer.setCountup();
		}

	};

	$(document).ready(function(e){
		new Preload();
		new Timer();
		new Easel();
		new SocketIO();
	});

}(jQuery, createjs, io, window, undefined));
<commit_msg>Containerオブジェクトの要素の再参照の仕方がわかった<commit_after>(function($, createjs, io, window, undefined){

	// 定数定義
	var CONSTANT = {
		SIZE: {
			width: 1920,
			height: 1080
		},
		fps: 60,
		SCREEN: {
			LOADING: 0,
			TITLE:   1,
			TIMER:   2
		}
	};

	var manifest = [{}];

	var _SCREENSTATUS = CONSTANT.SCREEN.LOADING;
	var _SCREENSTATUS_OLD = null;

	var easel, Easel = function() {
		easel = this;
		this.stage = null;
		this.initalized();
	};
	Easel.prototype = {
		// 初期化
		initalized: function() {
			// 画面領域の設定(Retinaの対応)
			$("#game").attr({width: CONSTANT.SIZE.width, height: CONSTANT.SIZE.height}).css({width: CONSTANT.SIZE.width/2, height:CONSTANT.SIZE.height/2});
			// ステージの作成
			this.stage = new createjs.Stage($("#game").get(0));
			// 入力の受付
			createjs.Touch.enable(this.stage);
			// FPSの設定
			createjs.Ticker.setFPS(CONSTANT.fps);
			// ticker
			createjs.Ticker.addEventListener("tick", $.proxy(this.transitScreen, this));
		},
		transitScreen: function(event) {
			// 画面が変更されたら
			if ( _SCREENSTATUS != _SCREENSTATUS_OLD ) {
				// ローディング画面
				if ( _SCREENSTATUS == CONSTANT.SCREEN.LOADING ) {
					this.stage.removeAllChildren();
					this.stage.addChild(this.displayLoading());
				}
				// タイトル画面
				else if ( _SCREENSTATUS == CONSTANT.SCREEN.TITLE ) {
					this.stage.removeAllChildren();
					this.stage.addChild(this.displayTitle());
				}
				// タイマー
				else if ( _SCREENSTATUS == CONSTANT.SCREEN.TIMER ) {
					this.stage.removeAllChildren();
					this.stage.addChild(this.displayTimer());
				}
			}
			this.stage.update();
			_SCREENSTATUS_OLD = _SCREENSTATUS;
		},
		displayLoading: function() {

		},
		displayTitle: function() {
			var container = new createjs.Container();
			// 黒色の背景
			var back = new createjs.Shape();
			back.graphics.f("#000").r(0, 0, CONSTANT.SIZE.width, CONSTANT.SIZE.height);
			back.set({x: 0, y: 0});
			var text = new createjs.Text("よみこみかんりょう！", "48px PixelMplus12", "#fff");
			text.set({
				textAlign: "center",
				x: CONSTANT.SIZE.width/2,
				y :CONSTANT.SIZE.height/2 - text.getMeasuredHeight()/2,
			});
			var start = new createjs.Text("クリックでスタート", "48px PixelMplus12", "#fff");
			start.set({
				textAlign: "center",
				x: CONSTANT.SIZE.width/2,
				y :CONSTANT.SIZE.height/2 - text.getMeasuredHeight()/2 + 280,
			});
			createjs.Tween.get(start, {loop: true})
				.to({alpha: 1}, 300)
				.to({alpha: 0}, 300)
				.to({alpha: 1}, 300);

			container.addChild(back, start, text);
			container.addEventListener("click", function(event) {
				_SCREENSTATUS = CONSTANT.SCREEN.TIMER;
			});

			return container;
		},
		displayTimer: function() {
			var container = new createjs.Container();
			// 黒色の背景
			var back = new createjs.Shape();
			back.graphics.f("#000").r(0, 0, CONSTANT.SIZE.width, CONSTANT.SIZE.height);
			back.set({x: 0, y: 0});
			var text = new createjs.Text("残り時間", "72px PixelMplus12", "#FFF");
			text.set({
				textAlign: "center",
				x: CONSTANT.SIZE.width/2,
				y :CONSTANT.SIZE.height/2 - text.getMeasuredHeight()/2 - 300
			});
			//var shadow = new createjs.Container();
			var shadow = this.objectTime("88", "88", "88", "#111", 350);
			var times = timer.getFormattedTimes();
			var color = timer.getTimeColor();
			var time = this.objectTime(times.min, times.sec, times.msec, color, 350);
			console.log(time);
			createjs.Ticker.addEventListener("tick", function(evt) {
				if ( timer.countdown ) {
					timer.decreaseTimes(evt.delta/1000);
				}
				color = timer.getTimeColor();
				times = timer.getFormattedTimes();
				time.getChildByName("time").set({text: times.min+":"+times.sec, color: color});
				time.getChildByName("msec").set({text: times.msec, color: color});
			});

			container.addChild(back, shadow, text, time);
			return container;
		},
		objectTime: function(min, sec, msec, color, size) {
			var container = new createjs.Container();
			var time = new createjs.Text( min+":"+sec, "Italic Bold "+size+"px DSEG7 Classic Mini", color ).set({name: "time"});
			var msec = new createjs.Text( msec, "Italic Bold "+(size/2)+"px DSEG7 Classic Mini", color ).set({name: "msec"});;
			time.set({
				x: CONSTANT.SIZE.width/2  - time.getMeasuredWidth()/2 - msec.getMeasuredWidth()/2,
				y :CONSTANT.SIZE.height/2 - time.getMeasuredHeight()/2
			});
			msec.set({
				x: CONSTANT.SIZE.width/2  + time.getMeasuredWidth()/2 - msec.getMeasuredWidth()/2,
				y :CONSTANT.SIZE.height/2 - msec.getMeasuredHeight()/2 + (size/4)
			});
			container.addChild(time, msec);
			return container;
		}
	};

	var timer, Timer = function() {
		timer = this;
		this.second = 0;
		this.delta = 0;
		this.countdown = false;
		this.countup = false;
		this.initalized();
	};
	Timer.prototype = {
		initalized: function() {
			this.second = 0;
		},
		decreaseTimes: function(delta) {
			if ( this.countup ) {
				delta *= -1;
			}
			this.second -= delta;
			if ( this.second < 0 ) {
				this.stopTimeover();
			}
		},
		getTimeColor: function() {
			var color = "#FFD032";
			if ( this.second == 0 ) {
				color = "#FF3B21";
			}
			return color;
		},
		getTimes: function() {
			return this.second;
		},
		getFormattedTimes: function() {
			var second = this.second;
			var times = {
				min:  this.fillZero(Math.floor(second/60)),
				sec:  this.fillZero(Math.floor(second%60)),
				msec: this.fillZero(Math.floor(second*100%100))
			};

			return times;
		},
		startCount: function() {
			this.countup = false;
			this.countdown = true;
		},
		stopCount: function() {
			this.countdown = false;
		},
		stopTimeover: function() {
			this.stopCount();
			this.resetCount(0);
			socketio.socket.emit("stop timer");
		},
		resetCount: function(time) {
			this.countdown = this.countup = false;
			if ( time >= 6000 ) { time = 5999; }
			this.second = time;
		},
		fillZero: function(num) {
			return (("0"+num).slice(-2));
		},
		setCountup: function() {
			this.countup = true;
		}
	};

	// Preload.js
	var preload, Preload = function() {
		preload = this;
		this.load = null;
		this.initalized();
	};
	Preload.prototype = {
		// 初期化
		initalized: function() {
			// Preload.js
			this.load = new createjs.LoadQueue();

			// 最大並列接続数
			this.load.setMaxConnections(6);

			// 読み込みの進行状況が変化した
			this.load.addEventListener("progress", this.handleProgress);
			// 1つのファイルを読み込み終わったら
			this.load.addEventListener("fileload", this.handleFileLoadComplete);
			// 全てのファイルを読み込み終わったら
			this.load.addEventListener("complete", this.handleComplete);

			// 読み込み開始
			this.load.loadManifest(manifest);
		},
		// 読み込み中
		handleProgress: function(event) {
			// 読み込み率を0.0~1.0で取得
			var progress = event.progress;
		},
		// ファイルごとの読み込み完了イベント
		handleProgress: function(event) {
			// 読み込んだファイル
			var result = event.result;
		},
		// 読み込み完了
		handleComplete: function() {
			_SCREENSTATUS = CONSTANT.SCREEN.TITLE;
		}
	}

	var socketio, SocketIO = function() {
		socketio = this;
		this.socket = io();
		this.initalized();
	};
	SocketIO.prototype = {
		initalized: function() {
			this.socket
				.on("start timer", $.proxy(this.startTimer, this))
				.on("stop timer", $.proxy(this.stopTimer, this))
				.on("set timer", $.proxy(this.resetTimer, this))
				.on("countup timer", $.proxy(this.countupTimer, this));
		},
		startTimer: function() {
			timer.startCount();
		},
		stopTimer: function() {
			timer.stopCount();
		},
		resetTimer: function(sec) {
			timer.resetCount(sec);
		},
		countupTimer: function() {
			timer.setCountup();
		}

	};

	$(document).ready(function(e){
		new Preload();
		new Timer();
		new Easel();
		new SocketIO();
	});

}(jQuery, createjs, io, window, undefined));
<|endoftext|>


<commit_before>module.exports = {

	//---------------------------------------------------------------------
	// Action Name
	//
	// This is the name of the action displayed in the editor.
	//---------------------------------------------------------------------

	name: "Weather",

	//---------------------------------------------------------------------
	// Action Section
	//
	// This is the section the action will fall into.
	//---------------------------------------------------------------------

	section: "Other Stuff",

	//---------------------------------------------------------------------
	// Action Subtitle
	//
	// This function generates the subtitle displayed next to the name.
	//---------------------------------------------------------------------

	subtitle: function (data) {
		const info = ['Temperature', 'Weather Text', 'Date', 'City', 'Country', 'Region', 'Wind Speed', 'Wind Chill', 'Wind Direction', 'Humidity', 'Pressure', 'Atmosphere Visibility', 'Sunrise Time', 'Sunset Time'];
		return `${info[parseInt(data.info)]}`;
	},

	//---------------------------------------------------------------------
	// DBM Mods Manager Variables (Optional but nice to have!)
	//
	// These are variables that DBM Mods Manager uses to show information
	// about the mods for people to see in the list.
	//---------------------------------------------------------------------

	// Who made the mod (If not set, defaults to "DBM Mods")
	author: "EGGSY",

	// The version of the mod (Defaults to 1.0.0)
	version: "1.8.7", //Added in 1.8.7

	// A short description to show on the mod line for this mod (Must be on a single line)
	short_description: "Stores weather informations with node module.",

	// If it depends on any other mods by name, ex: WrexMODS if the mod uses something from WrexMods

	//---------------------------------------------------------------------

	//---------------------------------------------------------------------
	// Action Storage Function
	//
	// Stores the relevant variable info for the editor.
	//---------------------------------------------------------------------

	variableStorage: function (data, varType) {
		const type = parseInt(data.storage);
		if (type !== varType) return;
		const info = parseInt(data.info);
		let dataType = 'Unknown Weather Type';
		switch (info) {
			case 0:
				dataType = "Temperature";
				break;
			case 1:
				dataType = "Weather - Text";
				break;
			case 2:
				dataType = "Date";
				break;
			case 3:
				dataType = "Weather - City";
				break;
			case 4:
				dataType = "Weather - Country";
				break;
			case 5:
				dataType = "Weather - Region";
				break;
			case 6:
				dataType = "Wind Speed";
				break;
			case 7:
				dataType = "Wind Chill";
				break;
			case 8:
				dataType = "Wind Direction";
				break;
			case 9:
				dataType = "Atmosphere Humidity";
				break;
			case 10:
				dataType = "Atmosphere Pressure";
				break;
			case 11:
				dataType = "Atmosphere Visibility";
				break;
			case 12:
				dataType = "Weather - Sunrise";
				break;
			case 13:
				dataType = "Weather - Sunset";
				break;
		}
		return ([data.varName, dataType]);
	},

	//---------------------------------------------------------------------
	// Action Fields
	//
	// These are the fields for the action. These fields are customized
	// by creating elements with corresponding IDs in the HTML. These
	// are also the names of the fields stored in the action's JSON data.
	//---------------------------------------------------------------------

	fields: ["city", "degreeType", "info", "storage", "varName"],

	//---------------------------------------------------------------------
	// Command HTML
	//
	// This function returns a string containing the HTML used for
	// editting actions.
	//
	// The "isEvent" parameter will be true if this action is being used
	// for an event. Due to their nature, events lack certain information,
	// so edit the HTML to reflect this.
	//
	// The "data" parameter stores constants for select elements to use.
	// Each is an array: index 0 for commands, index 1 for events.
	// The names are: sendTargets, members, roles, channels,
	//                messages, servers, variables
	//---------------------------------------------------------------------

	html: function (isEvent, data) {
		return `

				Mod Info:

				Created by EGGSY!

		Source City:

		<input id="city" class="round" type="text">

	 	Degree Type:

	 	<select id="degreeType" class="round">
			<option value="F">F</option>
			<option value="C">C</option>
		</select>

		Source Info:

		<select id="info" class="round">
			<option value="0">Temperature</option>
			<option value="1">Weather Text</option>
			<option value="2">Date</option>
			<option value="3">City</option>
			<option value="4">Country</option>
			<option value="5">Region</option>
			<option value="6">Wind Speed</option>
			<option value="7">Wind Chill</option>
			<option value="8">Wind Direction</option>
			<option value="9">Humidity</option>
			<option value="10">Pressure</option>
			<option value="11">Atmosphere Visibility</option>
			<option value="12">Sunrise Time</option>
			<option value="13">Sunset Time</option>
		</select>

			Store In:

			<select id="storage" class="round" onchange="glob.variableChange(this, 'varNameContainer')">
				${data.variables[0]}
			</select>

			Variable Name:

			<input id="varName" class="round" type="text">

`
	},

	//---------------------------------------------------------------------
	// Action Editor Init Code
	//
	// When the HTML is first applied to the action editor, this code
	// is also run. This helps add modifications or setup reactionary
	// functions for the DOM elements.
	//---------------------------------------------------------------------

	init: function () {
		const { glob, document } = this;
		glob.variableChange(document.getElementById('storage'), 'varNameContainer');
	},

	//---------------------------------------------------------------------
	// Action Bot Function
	//
	// This is the function for the action within the Bot's Action class.
	// Keep in mind event calls won't have access to the "msg" parameter,
	// so be sure to provide checks for variable existance.
	//---------------------------------------------------------------------

	action: function (cache) {
		const data = cache.actions[cache.index];
		const info = parseInt(data.info);
		const city = this.evalMessage(data.city, cache);
		const degreeType2 = this.evalMessage(data.degreeType, cache);

		// Check if everything is ok:
		if (!city) return console.log("Please specify a city to get weather informations.");

		// Main code:
		const WrexMODS = this.getWrexMods(); // as always.
		const weather = WrexMODS.require('yahoo-weather'); // WrexMODS'll automatically try to install the module if you run it with CMD/PowerShell.

		weather(`${city}`, `${degreeType2}`).then(response => {
			switch (info) {
				case 0:
					result = response.item.condition.temp;
					break;
				case 1:
					result = response.item.condition.text;
					break;
				case 2:
					result = response.item.condition.date;
					break;
				case 3:
					result = response.location.city;
					break;
				case 4:
					result = response.location.country;
					break;
				case 5:
					result = response.location.region;
					break;
				case 6:
					result = response.wind.speed;
					break;
				case 7:
					result = response.wind.chill;
					break;
				case 8:
					result = response.wind.direction;
					break;
				case 9:
					result = response.atmosphere.humidity;
					break;
				case 10:
					result = response.atmosphere.pressure;
					break;
				case 11:
					result = response.atmosphere.visibility;
					break;
				case 12:
					result = response.astronomy.sunrise;
					break;
				case 13:
					result = response.astronomy.sunset;
					break;
				default:
					break;
			}
			if (result !== undefined) {
				const storage = parseInt(data.storage);
				const varName2 = this.evalMessage(data.varName, cache);
				this.storeValue(result, storage, varName2, cache);
			}
			this.callNextAction(cache);
		}).catch(err => {
			console.log("ERROR:", err)
			this.callNextAction(cache)
		});
	},

	//---------------------------------------------------------------------
	// Action Bot Mod
	//
	// Upon initialization of the bot, this code is run. Using the bot's
	// DBM namespace, one can add/modify existing functions if necessary.
	// In order to reduce conflictions between mods, be sure to alias
	// functions you wish to overwrite.
	//---------------------------------------------------------------------

	mod: function (DBM) { }

}; // End of module
<commit_msg>Deleting this old thing.<commit_after><|endoftext|>


<commit_before>var path = require('path');

// for prod builds, we have already done AoT and AoT writes to disk
// so read the JS file from disk
// for dev buids, we actually want to pass in .ts files since we
// don't have .js files on disk, they're exclusively in memory

function getEntryPoint() {
  if (process.env.IONIC_ENV === 'prod') {
    return '{{TMP}}/app/main.prod.js';
  }
  return '{{TMP}}/app/main.dev.js';
}

module.exports = {
  devtool: 'source-map',
  entry: getEntryPoint(),
  output: {
    path: '{{BUILD}}',
    filename: 'main.js'
  },

  resolve: {
    extensions: ['.js', '.json']
  },

  module: {
    loaders: [
      {
        test: /\.json$/,
        loader: 'json'
      }
    ]
  }
};<commit_msg>chore(webpack): add optimize plugins<commit_after>var path = require('path');
var webpack = require('webpack');

// for prod builds, we have already done AoT and AoT writes to disk
// so read the JS file from disk
// for dev buids, we actually want to pass in .ts files since we
// don't have .js files on disk, they're exclusively in memory

function getEntryPoint() {
  if (process.env.IONIC_ENV === 'prod') {
    return '{{TMP}}/app/main.prod.js';
  }
  return '{{TMP}}/app/main.dev.js';
}

function getPlugins() {
  if (process.env.IONIC_ENV === 'prod') {
    return [
      // This helps ensure the builds are consistent if source hasn't changed:
      new webpack.optimize.OccurrenceOrderPlugin(),
      // Try to dedupe duplicated modules, if any:
      // Add this back in when Angular fixes the issue: https://github.com/angular/angular-cli/issues/1587
      //new DedupePlugin()
    ];
  }
  return [];
}

module.exports = {
  entry: getEntryPoint(),
  output: {
    path: '{{BUILD}}',
    filename: 'main.js'
  },

  resolve: {
    extensions: ['.js', '.json']
  },

  module: {
    loaders: [
      {
        test: /\.json$/,
        loader: 'json'
      }
    ]
  },

  plugins: getPlugins(),

  // Some libraries import Node modules but don't use them in the browser.
  // Tell Webpack to provide empty mocks for them so importing them works.
  node: {
    fs: 'empty',
    net: 'empty',
    tls: 'empty'
  }
};<|endoftext|>


<commit_before>import Widget from "./base_widget.js";
import { build_summary_item } from "./utils";
frappe.provide('frappe.dashboards');
frappe.provide('frappe.dashboards.chart_sources');

export default class ChartWidget extends Widget {
	constructor(opts) {
		super(opts);
	}

	refresh() {
		this.make_chart();
	}

	customize() {
		this.setup_customize_actions();
	}

	set_body() {
		this.widget.addClass('dashboard-widget-box');
		if (this.width == "Full") {
			this.widget.addClass('full-width');
		}
		this.make_chart();
	}

	set_summary() {
		let $summary = $(`<div class="report-summary"></div>`).hide().prependTo(this.body);

		this.summary.forEach((summary) => {
			build_summary_item(summary).appendTo($summary);
		})
		this.summary.length && $summary.show();
	}

	make_chart() {
		this.body.empty();
		this.get_settings().then(() => {
			this.prepare_chart_object();
			// this.prepare_container();

			this.action_area.empty();
			this.prepare_chart_actions();
			// this.setup_refresh_button();
			this.setup_filter_button();
			if (this.chart_doc.timeseries && this.chart_doc.chart_type !== 'Custom') {
				this.render_time_series_filters();
			}

			this.fetch(this.filters).then(data => {
				this.summary = []
				if (this.chart_doc.chart_type == 'Report') {
					this.summary = data.report_summary;
					data = this.get_report_chart_data(data);
				}
				this.update_last_synced();
				this.data = data;
				// Delete existing chart when refreshing
				delete this.dashboardchart;
				this.render();
				this.set_summary();
			});
		});
	}

	setup_customize_actions() {
		this.action_area.empty();
		const buttons = $(`<button type="button" class="btn btn-xs btn-secondary btn-default selected">Resize</button>
					<button class="btn btn-secondary btn-light btn-danger btn-xs"></button>`);
		buttons.appendTo(this.action_area);
	}

	setup_refresh_button() {
		const refresh_button = $(`<button class="btn btn-secondary btn-light btn-default btn-xs"><i class="fa fa-refresh" aria-hidden="true"></i></button>`);
		refresh_button.appendTo(this.action_area);
		refresh_button.on('click', () => this.refresh());
	}

	render_time_series_filters() {
		let filters = [
			{
				label: this.chart_doc.timespan,
				options: ['Select Date Range', 'Last Year', 'Last Quarter', 'Last Month', 'Last Week'],
				action: (selected_item) => {
					this.selected_timespan = selected_item;

					if (this.selected_timespan === 'Select Date Range') {
						this.render_date_range_fields();
					} else {
						this.selected_from_date = null;
						this.selected_to_date = null;
						if (this.date_field_wrapper) this.date_field_wrapper.hide();
						this.fetch_and_update_chart();
					}
				}
			},
			{
				label: this.chart_doc.time_interval,
				options: ['Yearly', 'Quarterly', 'Monthly', 'Weekly', 'Daily'],
				action: (selected_item) => {
					this.selected_time_interval = selected_item;
					this.fetch_and_update_chart();
				}
			},
		];

		frappe.dashboard_utils.render_chart_filters(filters, 'chart-actions', this.action_area, 0);
	}

	fetch_and_update_chart() {
		this.args = {
			timespan: this.selected_timespan,
			time_interval: this.selected_time_interval,
			from_date: this.selected_from_date,
			to_date: this.selected_to_date
		};

		this.fetch(this.filters, true, this.args).then(data => {
			if (this.chart_doc.chart_type == 'Report') {
				data = this.get_report_chart_data(data);
			}

			this.update_chart_object();
			this.data = data;
			this.render();
		});
	}

	render_date_range_fields() {
		if (!this.date_field_wrapper || !this.date_field_wrapper.is(':visible')) {
			this.date_field_wrapper =
				$(`<div class="dashboard-date-field pull-right"></div>`)
					.appendTo(this.action_area);

			this.date_range_field = frappe.ui.form.make_control({
				df: {
					fieldtype: 'DateRange',
					fieldname: 'from_date',
					placeholder: 'Date Range',
					input_class: 'input-xs',
					reqd: 1,
					change: () => {
						let selected_date_range = this.date_range_field.get_value();
						this.selected_from_date = selected_date_range[0];
						this.selected_to_date = selected_date_range[1];

						if (selected_date_range && selected_date_range.length == 2) {
							this.fetch_and_update_chart();
						}
					}
				},
				parent: this.date_field_wrapper,
				render_input: 1
			});
		}
	}

	get_report_chart_data(result) {
		if (result.chart && this.chart_doc.is_custom) {
			return result.chart.data;
		} else {
			let y_fields = [];
			this.chart_doc.y_axis.map( field => {
				y_fields.push(field.y_field);
			});

			let chart_fields = {
				y_fields: y_fields,
				x_field: this.chart_doc.x_field,
				chart_type: this.chart_doc.type,
				color: this.chart_doc.color
			};
			let columns = result.columns.map((col)=> {
				return frappe.report_utils.prepare_field_from_column(col);
			});

			let data = frappe.report_utils.make_chart_options(columns, result, chart_fields).data;
			return data;
		}
	}

	prepare_chart_actions() {
		let actions = [
			{
				label: __("Refresh"),
				action: 'action-refresh',
				handler: () => {
					this.fetch_and_update_chart();
				}
			},
			{
				label: __("Edit..."),
				action: 'action-edit',
				handler: () => {
					frappe.set_route('Form', 'Dashboard Chart', this.chart_doc.name);
				}
			}
		];

		if (this.chart_doc.document_type) {
			actions.push({
				label: __("{0} List", [this.chart_doc.document_type]),
				action: 'action-list',
				handler: () => {
					frappe.set_route('List', this.chart_doc.document_type);
				}
			});
		} else if (this.chart_doc.chart_type === 'Report') {
			actions.push({
				label: __("{0} Report", [this.chart_doc.report_name]),
				action: 'action-list',
				handler: () => {
					frappe.set_route('query-report', this.chart_doc.report_name);
				}
			})
		}
		this.set_chart_actions(actions);
	}

	setup_filter_button() {
		this.is_document_type = this.chart_doc.chart_type!== 'Report' && this.chart_doc.chart_type!=='Custom';
		this.filter_button =
			$(`<div class="filter-chart btn btn-default btn-xs pull-right">${__("Filter")}</div>`);
		this.filter_button.appendTo(this.action_area);

		this.filter_button.on('click', () => {
			let fields;

			frappe.dashboard_utils.get_filters_for_chart_type(this.chart_doc)
				.then(filters => {
					if (!this.is_document_type) {
						if (!filters) {
							fields = [{
								fieldtype: "HTML",
								options: __("No Filters Set")
							}];
						} else {
							fields = filters.filter(f => {
								if (f.on_change && !f.reqd) {
									return false;
								}
								if (f.get_query || f.get_data) {
									f.read_only = 1;
								}
								return f.fieldname;
							});
						}
					} else {
						fields = [{
							fieldtype: 'HTML',
							fieldname: 'filter_area',
						}];
					}

					this.setup_filter_dialog(fields);
				});
		});
	}

	setup_filter_dialog(fields) {

		let me = this;
		let dialog = new frappe.ui.Dialog({
			title: __(`Set Filters for ${this.chart_doc.chart_name}`),
			fields: fields,
			primary_action: function() {
				let values = this.get_values();
				if (values) {
					this.hide();
					if (me.is_document_type) {
						me.filters = me.filter_group.get_filters();
					} else {
						me.filters = values;
					}
					me.fetch_and_update_chart();
				}
			},
			primary_action_label: "Set"
		});

		if (this.is_document_type) {
			this.create_filter_group_and_add_filters(dialog.get_field('filter_area').$wrapper);
		}

		dialog.show();
		dialog.set_values(this.filters);

	}

	create_filter_group_and_add_filters(parent) {
		this.filter_group = new frappe.ui.FilterGroup({
			parent: parent,
			doctype: this.chart_doc.document_type,
			on_change: () => {},
		});

		frappe.model.with_doctype(this.chart_doc.document_type, () => {
			this.filter_group.add_filters_to_filter_group(this.filters);
		});
	}

	set_chart_actions(actions) {
		this.chart_actions = $(`

				<button class="btn btn-default btn-xs"></button>

				${actions.map(action => `<li><a data-action="${action.action}">${action.label}</a></li>`).join('')}

		`);

		this.chart_actions.find("a[data-action]").each((i, o) => {
			const action = o.dataset.action;
			$(o).click(actions.find(a => a.action === action));
		});
		this.chart_actions.appendTo(this.action_area);
	}

	fetch(filters, refresh=false, args) {
		// this.chart_container.find('.chart-loading-state').removeClass('hide');
		let method = this.settings ? this.settings.method
			: 'frappe.desk.doctype.dashboard_chart.dashboard_chart.get';

		if (this.chart_doc.chart_type == 'Report') {
			args = {
				report_name: this.chart_doc.report_name,
				filters: filters,
			};
		} else {
			args = {
				chart_name: this.chart_doc.name,
				filters: filters,
				refresh: refresh ? 1 : 0,
				time_interval: args && args.time_interval? args.time_interval: null,
				timespan: args && args.timespan? args.timespan: null,
				from_date: args && args.from_date? args.from_date: null,
				to_date: args && args.to_date? args.to_date: null,
			};
		}
		return frappe.xcall(
			method,
			args
		);
	}

	render() {
		const chart_type_map = {
			'Line': 'line',
			'Bar': 'bar',
			'Percentage': 'percentage',
			'Pie': 'pie'
		};

		let colors = [];

		if (this.chart_doc.y_axis.length) {
			this.chart_doc.y_axis.map( field => {
				colors.push(field.color);
			});
		} else if (['Line', 'Bar'].includes(this.chart_doc.type)) {
			colors = [this.chart_doc.color || "light-blue"];
		}

		if (this.data) {
			let chart_args = {
				data: this.data,
				type: chart_type_map[this.chart_doc.type],
				colors: colors,
				axisOptions: {
					xIsSeries: this.chart_doc.timeseries,
					shortenYAxisNumbers: 1
				}
			};
			if (!this.dashboardchart) {
				this.dashboardchart = new frappe.Chart(this.body[0], chart_args);
			} else {
				this.dashboardchart.update(this.data);
			}
		}
	}

	update_last_synced() {
		let last_synced_text = __("Last synced {0}", [comment_when(this.chart_doc.last_synced_on)]);
		this.footer.html(last_synced_text);
	}

	update_chart_object() {
		frappe.db.get_doc("Dashboard Chart", this.chart_doc.name).then(doc => {
			this.chart_doc = doc;
			this.prepare_chart_object();
			this.update_last_synced();
		});
	}

	prepare_chart_object() {
		this.filters = this.filters || JSON.parse(this.chart_doc.filters_json || '[]');
	}

	get_settings() {
		return frappe.model.with_doc("Dashboard Chart", this.chart_name).then(chart_doc => {
			this.chart_doc = chart_doc;
			if (this.chart_doc.chart_type == 'Custom') {
				// custom source
				if (frappe.dashboards.chart_sources[this.chart_doc.source]) {
					this.settings = frappe.dashboards.chart_sources[this.chart_doc.source];
					return Promise.resolve();
				} else {
					const method = 'frappe.desk.doctype.dashboard_chart_source.dashboard_chart_source.get_config';
					return frappe.xcall(method, {name: this.chart_doc.source}).then(config => {
						frappe.dom.eval(config);
						this.settings = frappe.dashboards.chart_sources[this.chart_doc.source];
					});
				}
			} else if (this.chart_doc.chart_type == 'Report') {
				this.settings = {
					'method': 'frappe.desk.query_report.run'
				};
				return Promise.resolve();
			} else {
				return Promise.resolve();
			}
		});
	}
}<commit_msg>refactor: show summary only in full width<commit_after>import Widget from "./base_widget.js";
import { build_summary_item } from "./utils";
frappe.provide('frappe.dashboards');
frappe.provide('frappe.dashboards.chart_sources');

export default class ChartWidget extends Widget {
	constructor(opts) {
		super(opts);
	}

	refresh() {
		this.make_chart();
	}

	customize() {
		this.setup_customize_actions();
	}

	set_body() {
		this.widget.addClass('dashboard-widget-box');
		if (this.width == "Full") {
			this.widget.addClass('full-width');
		}
		this.make_chart();
	}

	set_summary() {
		let $summary = $(`<div class="report-summary"></div>`).hide().prependTo(this.body);

		this.summary.forEach((summary) => {
			build_summary_item(summary).appendTo($summary);
		})
		this.summary.length && $summary.show();
	}

	make_chart() {
		this.body.empty();
		this.get_settings().then(() => {
			this.prepare_chart_object();
			// this.prepare_container();

			this.action_area.empty();
			this.prepare_chart_actions();
			// this.setup_refresh_button();
			this.setup_filter_button();
			if (this.chart_doc.timeseries && this.chart_doc.chart_type !== 'Custom') {
				this.render_time_series_filters();
			}

			this.fetch(this.filters).then(data => {
				this.summary = []
				if (this.chart_doc.chart_type == 'Report') {
					this.summary = data.report_summary;
					data = this.get_report_chart_data(data);
				}
				this.update_last_synced();
				this.data = data;
				// Delete existing chart when refreshing
				delete this.dashboardchart;
				this.render();
				this.width == "Full" && this.set_summary();
			});
		});
	}

	setup_customize_actions() {
		this.action_area.empty();
		const buttons = $(`<button type="button" class="btn btn-xs btn-secondary btn-default selected">Resize</button>
					<button class="btn btn-secondary btn-light btn-danger btn-xs"></button>`);
		buttons.appendTo(this.action_area);
	}

	setup_refresh_button() {
		const refresh_button = $(`<button class="btn btn-secondary btn-light btn-default btn-xs"><i class="fa fa-refresh" aria-hidden="true"></i></button>`);
		refresh_button.appendTo(this.action_area);
		refresh_button.on('click', () => this.refresh());
	}

	render_time_series_filters() {
		let filters = [
			{
				label: this.chart_doc.timespan,
				options: ['Select Date Range', 'Last Year', 'Last Quarter', 'Last Month', 'Last Week'],
				action: (selected_item) => {
					this.selected_timespan = selected_item;

					if (this.selected_timespan === 'Select Date Range') {
						this.render_date_range_fields();
					} else {
						this.selected_from_date = null;
						this.selected_to_date = null;
						if (this.date_field_wrapper) this.date_field_wrapper.hide();
						this.fetch_and_update_chart();
					}
				}
			},
			{
				label: this.chart_doc.time_interval,
				options: ['Yearly', 'Quarterly', 'Monthly', 'Weekly', 'Daily'],
				action: (selected_item) => {
					this.selected_time_interval = selected_item;
					this.fetch_and_update_chart();
				}
			},
		];

		frappe.dashboard_utils.render_chart_filters(filters, 'chart-actions', this.action_area, 0);
	}

	fetch_and_update_chart() {
		this.args = {
			timespan: this.selected_timespan,
			time_interval: this.selected_time_interval,
			from_date: this.selected_from_date,
			to_date: this.selected_to_date
		};

		this.fetch(this.filters, true, this.args).then(data => {
			if (this.chart_doc.chart_type == 'Report') {
				data = this.get_report_chart_data(data);
			}

			this.update_chart_object();
			this.data = data;
			this.render();
		});
	}

	render_date_range_fields() {
		if (!this.date_field_wrapper || !this.date_field_wrapper.is(':visible')) {
			this.date_field_wrapper =
				$(`<div class="dashboard-date-field pull-right"></div>`)
					.appendTo(this.action_area);

			this.date_range_field = frappe.ui.form.make_control({
				df: {
					fieldtype: 'DateRange',
					fieldname: 'from_date',
					placeholder: 'Date Range',
					input_class: 'input-xs',
					reqd: 1,
					change: () => {
						let selected_date_range = this.date_range_field.get_value();
						this.selected_from_date = selected_date_range[0];
						this.selected_to_date = selected_date_range[1];

						if (selected_date_range && selected_date_range.length == 2) {
							this.fetch_and_update_chart();
						}
					}
				},
				parent: this.date_field_wrapper,
				render_input: 1
			});
		}
	}

	get_report_chart_data(result) {
		if (result.chart && this.chart_doc.is_custom) {
			return result.chart.data;
		} else {
			let y_fields = [];
			this.chart_doc.y_axis.map( field => {
				y_fields.push(field.y_field);
			});

			let chart_fields = {
				y_fields: y_fields,
				x_field: this.chart_doc.x_field,
				chart_type: this.chart_doc.type,
				color: this.chart_doc.color
			};
			let columns = result.columns.map((col)=> {
				return frappe.report_utils.prepare_field_from_column(col);
			});

			let data = frappe.report_utils.make_chart_options(columns, result, chart_fields).data;
			return data;
		}
	}

	prepare_chart_actions() {
		let actions = [
			{
				label: __("Refresh"),
				action: 'action-refresh',
				handler: () => {
					this.fetch_and_update_chart();
				}
			},
			{
				label: __("Edit..."),
				action: 'action-edit',
				handler: () => {
					frappe.set_route('Form', 'Dashboard Chart', this.chart_doc.name);
				}
			}
		];

		if (this.chart_doc.document_type) {
			actions.push({
				label: __("{0} List", [this.chart_doc.document_type]),
				action: 'action-list',
				handler: () => {
					frappe.set_route('List', this.chart_doc.document_type);
				}
			});
		} else if (this.chart_doc.chart_type === 'Report') {
			actions.push({
				label: __("{0} Report", [this.chart_doc.report_name]),
				action: 'action-list',
				handler: () => {
					frappe.set_route('query-report', this.chart_doc.report_name);
				}
			})
		}
		this.set_chart_actions(actions);
	}

	setup_filter_button() {
		this.is_document_type = this.chart_doc.chart_type!== 'Report' && this.chart_doc.chart_type!=='Custom';
		this.filter_button =
			$(`<div class="filter-chart btn btn-default btn-xs pull-right">${__("Filter")}</div>`);
		this.filter_button.appendTo(this.action_area);

		this.filter_button.on('click', () => {
			let fields;

			frappe.dashboard_utils.get_filters_for_chart_type(this.chart_doc)
				.then(filters => {
					if (!this.is_document_type) {
						if (!filters) {
							fields = [{
								fieldtype: "HTML",
								options: __("No Filters Set")
							}];
						} else {
							fields = filters.filter(f => {
								if (f.on_change && !f.reqd) {
									return false;
								}
								if (f.get_query || f.get_data) {
									f.read_only = 1;
								}
								return f.fieldname;
							});
						}
					} else {
						fields = [{
							fieldtype: 'HTML',
							fieldname: 'filter_area',
						}];
					}

					this.setup_filter_dialog(fields);
				});
		});
	}

	setup_filter_dialog(fields) {

		let me = this;
		let dialog = new frappe.ui.Dialog({
			title: __(`Set Filters for ${this.chart_doc.chart_name}`),
			fields: fields,
			primary_action: function() {
				let values = this.get_values();
				if (values) {
					this.hide();
					if (me.is_document_type) {
						me.filters = me.filter_group.get_filters();
					} else {
						me.filters = values;
					}
					me.fetch_and_update_chart();
				}
			},
			primary_action_label: "Set"
		});

		if (this.is_document_type) {
			this.create_filter_group_and_add_filters(dialog.get_field('filter_area').$wrapper);
		}

		dialog.show();
		dialog.set_values(this.filters);

	}

	create_filter_group_and_add_filters(parent) {
		this.filter_group = new frappe.ui.FilterGroup({
			parent: parent,
			doctype: this.chart_doc.document_type,
			on_change: () => {},
		});

		frappe.model.with_doctype(this.chart_doc.document_type, () => {
			this.filter_group.add_filters_to_filter_group(this.filters);
		});
	}

	set_chart_actions(actions) {
		this.chart_actions = $(`

				<button class="btn btn-default btn-xs"></button>

				${actions.map(action => `<li><a data-action="${action.action}">${action.label}</a></li>`).join('')}

		`);

		this.chart_actions.find("a[data-action]").each((i, o) => {
			const action = o.dataset.action;
			$(o).click(actions.find(a => a.action === action));
		});
		this.chart_actions.appendTo(this.action_area);
	}

	fetch(filters, refresh=false, args) {
		// this.chart_container.find('.chart-loading-state').removeClass('hide');
		let method = this.settings ? this.settings.method
			: 'frappe.desk.doctype.dashboard_chart.dashboard_chart.get';

		if (this.chart_doc.chart_type == 'Report') {
			args = {
				report_name: this.chart_doc.report_name,
				filters: filters,
			};
		} else {
			args = {
				chart_name: this.chart_doc.name,
				filters: filters,
				refresh: refresh ? 1 : 0,
				time_interval: args && args.time_interval? args.time_interval: null,
				timespan: args && args.timespan? args.timespan: null,
				from_date: args && args.from_date? args.from_date: null,
				to_date: args && args.to_date? args.to_date: null,
			};
		}
		return frappe.xcall(
			method,
			args
		);
	}

	render() {
		const chart_type_map = {
			'Line': 'line',
			'Bar': 'bar',
			'Percentage': 'percentage',
			'Pie': 'pie'
		};

		let colors = [];

		if (this.chart_doc.y_axis.length) {
			this.chart_doc.y_axis.map( field => {
				colors.push(field.color);
			});
		} else if (['Line', 'Bar'].includes(this.chart_doc.type)) {
			colors = [this.chart_doc.color || "light-blue"];
		}

		if (this.data) {
			let chart_args = {
				data: this.data,
				type: chart_type_map[this.chart_doc.type],
				colors: colors,
				axisOptions: {
					xIsSeries: this.chart_doc.timeseries,
					shortenYAxisNumbers: 1
				}
			};
			if (!this.dashboardchart) {
				this.dashboardchart = new frappe.Chart(this.body[0], chart_args);
			} else {
				this.dashboardchart.update(this.data);
			}
		}
	}

	update_last_synced() {
		let last_synced_text = __("Last synced {0}", [comment_when(this.chart_doc.last_synced_on)]);
		this.footer.html(last_synced_text);
	}

	update_chart_object() {
		frappe.db.get_doc("Dashboard Chart", this.chart_doc.name).then(doc => {
			this.chart_doc = doc;
			this.prepare_chart_object();
			this.update_last_synced();
		});
	}

	prepare_chart_object() {
		this.filters = this.filters || JSON.parse(this.chart_doc.filters_json || '[]');
	}

	get_settings() {
		return frappe.model.with_doc("Dashboard Chart", this.chart_name).then(chart_doc => {
			this.chart_doc = chart_doc;
			if (this.chart_doc.chart_type == 'Custom') {
				// custom source
				if (frappe.dashboards.chart_sources[this.chart_doc.source]) {
					this.settings = frappe.dashboards.chart_sources[this.chart_doc.source];
					return Promise.resolve();
				} else {
					const method = 'frappe.desk.doctype.dashboard_chart_source.dashboard_chart_source.get_config';
					return frappe.xcall(method, {name: this.chart_doc.source}).then(config => {
						frappe.dom.eval(config);
						this.settings = frappe.dashboards.chart_sources[this.chart_doc.source];
					});
				}
			} else if (this.chart_doc.chart_type == 'Report') {
				this.settings = {
					'method': 'frappe.desk.query_report.run'
				};
				return Promise.resolve();
			} else {
				return Promise.resolve();
			}
		});
	}
}<|endoftext|>


<commit_before>// Take on Pirates

var vehicle1 = {type: "Motorboat", capacity: 6, storedAt: "Ammunition Depot"};
var vehicle2 = {type: "Jet Ski", capacity: 1, storedAt: "Reef Dock"};
var vehicle3 = {type: "Submarine", capacity: 8, storedAt: "Underwater Outpost"};

var vehicles = [vehicle1, vehicle2, vehicle3];

var findVehicle= function(name, list){
  for(var i = 0; i <list.length; i++){
    if(list[i].type == name ){
    	return list[i].storedAt;
    }
  }
};

findVehicle("Submarine", vehicles);

vehicle1.capacity = 10;
vehicle2.submersible = false;
vehicle3.weapon = "Torpedoes";
vehicle1.submersible = false;
vehicle2.weapon = "Lasers";
vehicle3.capacity = 16;
vehicle1.weapon = "Rear-Mounted Slingshot";
vehicle3.submersible = true;

vehicle1["# of weapons"] = 1;
vehicle2["# of weapons"] = 4;
vehicle3["# of weapons"] = 8;

var superBlinders = [
  ["Firelight", 4000],
  ["Solar Death Ray", 6000],
  ["Supernova", 12000]
];

var lighthouseRock = {
  gateClosed: true,
  weaponBulbs: superBlinders,
  capacity: 30,
  secretPassageTo: "Underwater Outpost",
  numRangers: 0
};

function addRanger(location, name, skills, station) {
  location.numRangers++;
  location["ranger"+location.numRangers] ={
    name: name,
    skills: skills,
    station: station
  };

}

addRanger(lighthouseRock, "Rudy", "magnification burn", 2);
addRanger(lighthouseRock, "Jack", "uppercut launch", 3);
addRanger(lighthouseRock, "Rey", "bomb defusing", 1);
<commit_msg>added dontpanic function<commit_after>// Take on Pirates

var vehicle1 = {type: "Motorboat", capacity: 6, storedAt: "Ammunition Depot"};
var vehicle2 = {type: "Jet Ski", capacity: 1, storedAt: "Reef Dock"};
var vehicle3 = {type: "Submarine", capacity: 8, storedAt: "Underwater Outpost"};

var vehicles = [vehicle1, vehicle2, vehicle3];

var findVehicle= function(name, list){
  for(var i = 0; i <list.length; i++){
    if(list[i].type == name ){
    	return list[i].storedAt;
    }
  }
};

findVehicle("Submarine", vehicles);

vehicle1.capacity = 10;
vehicle2.submersible = false;
vehicle3.weapon = "Torpedoes";
vehicle1.submersible = false;
vehicle2.weapon = "Lasers";
vehicle3.capacity = 16;
vehicle1.weapon = "Rear-Mounted Slingshot";
vehicle3.submersible = true;

vehicle1["# of weapons"] = 1;
vehicle2["# of weapons"] = 4;
vehicle3["# of weapons"] = 8;

var superBlinders = [
  ["Firelight", 4000],
  ["Solar Death Ray", 6000],
  ["Supernova", 12000]
];

var lighthouseRock = {
  gateClosed: true,
  weaponBulbs: superBlinders,
  capacity: 30,
  secretPassageTo: "Underwater Outpost",
  numRangers: 3,
  ranger1: {name: "Rudy", skills: "magnification burn", station: 2},
  ranger2: {name: "Jack", skills: "uppercut launch", station: 3},
  ranger3: {name: "Rey", skills: "bomb defusing", station: 1}
};

function addRanger(location, name, skills, station) {
  location.numRangers++;
  location["ranger"+location.numRangers] ={
    name: name,
    skills: skills,
    station: station
  };

}

addRanger(lighthouseRock, "Rudy", "magnification burn", 2);
addRanger(lighthouseRock, "Jack", "uppercut launch", 3);
addRanger(lighthouseRock, "Rey", "bomb defusing", 1);

function dontPanic(location) {
  var list = "Avast, me hearties!\n" +
             "There be Pirates nearby! Stations!\n";

  // loop through the rangers and append to list
  for (var i = 1; i <= location.numRangers; i++) {
    var ranger = location["ranger"+i];
    var name = ranger.name;
    var superblinder = location.weaponBulbs[ranger.station-1][0];
    list += name + ", man the " + superblinder + "!\n";
  }

  alert(list);
}
<|endoftext|>


<commit_before>// tests/queryStuff.test.js
import buildQueryFromString from 'app/helpers/queryStuff'

describe('buildQueryFromString', () => {
	it('builds a query string with multiple keys into a query object', () => {
		let query = 'dept: Computer Science  dept: Asian Studies  name: Parallel  level: 300  year: $OR year:2013 year: 2014'
		let expectedResult = {
			depts: ['$AND', 'CSCI', 'ASIAN'],
			title: ['Parallel'],
			level: [300],
			year: ['$OR', 2013, 2014],
		}

		buildQueryFromString(query).should.eql(expectedResult)
	})

	it('builds a query string with variable-case keys into a query object', () => {
		let query = 'dept: ASIAN  Dept: Religion  title: "Japan*"  LEVEL: 200  year: 2014  semester: $OR  semester: 3  semester: 1'
		let expectedResult = {
			depts: ['$AND', 'ASIAN', 'REL'],
			title: ['"Japan*"'],
			level: [200],
			year: [2014],
			sem: ['$OR', 3, 1],
		}

		buildQueryFromString(query).should.eql(expectedResult)
	})

	it('builds a query string even with somewhat unconventional input', () => {
		let query = 'department: American Conversations  name: Independence  year: 2014  time: Tuesdays after 12'
		let expectedResult = {
			depts: ['AMCON'],
			title: ['Independence'],
			year: [2014],
			times: ['tuesdays after 12'],
		}

		buildQueryFromString(query).should.eql(expectedResult)
	})

	it('builds a query string while deduplicating synonyms of keys', () => {
		let query = 'ges: $AND  geneds: history of western culture gened: HBS  semester: Spring  year: 2014'
		let expectedResult = {
			gereqs: ['$AND', 'HWC', 'HBS'],
			sem: [3],
			year: [2014],
		}

		buildQueryFromString(query).should.eql(expectedResult)
	})

	it('builds a query string even with no keys', () => {
		let query = 'History of Asia'
		let expectedResult = {
			title: ['History of Asia'],
		}

		buildQueryFromString(query).should.eql(expectedResult)
	})
})
<commit_msg>Update path for testing<commit_after>// tests/queryStuff.test.js
import buildQueryFromString from '../app/helpers/queryStuff'

describe('buildQueryFromString', () => {
	it('builds a query string with multiple keys into a query object', () => {
		let query = 'dept: Computer Science  dept: Asian Studies  name: Parallel  level: 300  year: $OR year:2013 year: 2014'
		let expectedResult = {
			depts: ['$AND', 'CSCI', 'ASIAN'],
			title: ['Parallel'],
			level: [300],
			year: ['$OR', 2013, 2014],
		}

		buildQueryFromString(query).should.eql(expectedResult)
	})

	it('builds a query string with variable-case keys into a query object', () => {
		let query = 'dept: ASIAN  Dept: Religion  title: "Japan*"  LEVEL: 200  year: 2014  semester: $OR  semester: 3  semester: 1'
		let expectedResult = {
			depts: ['$AND', 'ASIAN', 'REL'],
			title: ['"Japan*"'],
			level: [200],
			year: [2014],
			sem: ['$OR', 3, 1],
		}

		buildQueryFromString(query).should.eql(expectedResult)
	})

	it('builds a query string even with somewhat unconventional input', () => {
		let query = 'department: American Conversations  name: Independence  year: 2014  time: Tuesdays after 12'
		let expectedResult = {
			depts: ['AMCON'],
			title: ['Independence'],
			year: [2014],
			times: ['tuesdays after 12'],
		}

		buildQueryFromString(query).should.eql(expectedResult)
	})

	it('builds a query string while deduplicating synonyms of keys', () => {
		let query = 'ges: $AND  geneds: history of western culture gened: HBS  semester: Spring  year: 2014'
		let expectedResult = {
			gereqs: ['$AND', 'HWC', 'HBS'],
			sem: [3],
			year: [2014],
		}

		buildQueryFromString(query).should.eql(expectedResult)
	})

	it('builds a query string even with no keys', () => {
		let query = 'History of Asia'
		let expectedResult = {
			title: ['History of Asia'],
		}

		buildQueryFromString(query).should.eql(expectedResult)
	})
})
<|endoftext|>