<commit_before>const expect = require('chai').expect;
const G = require('@ali/g');
const Canvas = G.Canvas;
const Labels = require('../../../../src/component/label/index');

const div = document.createElement('div');
div.id = 'c1';
document.body.appendChild(div);

describe('Labels', function() {

  const newItems = [
    { x: 10, y: 20, text: '一' },
    { x: 10, y: 40, text: '二' },
    { x: 10, y: 60, text: '三' },
    { x: 10, y: 80, text: '四' },
    { x: 10, y: 100, text: '五' },
    { x: 10, y: 120, text: '六' },
    { x: 10, y: 140, text: '七' }
  ];
  const canvas = new Canvas({
    containerId: 'c1',
    width: 500,
    height: 500
  });
  const labels = canvas.addGroup(Labels, {
    items: [
      { x: 10, y: 20, text: '1' },
      { x: 10, y: 40, text: '2' },
      { x: 10, y: 60, text: '3' },
      { x: 10, y: 80, text: '4' },
      { x: 10, y: 100, text: '5', fontFamily: 'Arial', fontSize: 30, fill: 'red' }
    ],
    textStyle(value) {
      console.log(value);
      const style = {
        fill: '#f80',
        textAlign: 'center', // 文本对齐方向，可取值为： start middle end
        fontSize: 16, // 文本大小
        fontWeight: 'bold' // 文本粗细
      };
      if (value !== '5' && value !== '五') {
        style.rotate = 45;
      }
      return style;
    }
  });

  canvas.draw();

  it('labels create', function() {
    expect(labels.getCount()).to.equal(labels.get('items').length);
    expect(labels).to.have.property('__cfg');
    expect(labels.__cfg.textStyle).to.be.a('function');
  });

  it('labels x,y', function() {
    const item = labels.getFirst();
    expect(item.attr('x')).to.equal(10);
    expect(item.attr('y')).to.equal(20);
    expect(item.attr('rotate')).to.equal(45);
  });

  it('labels rotate', function() {
    const item = labels.getLast();
    console.log();
    expect(item.attr('rotate')).to.be.undefined;
  });

  it('labels add Labels By String', function() {
    labels.addLabel('21331');
    const item = labels.getLast();
    expect(item.attr('text')).to.equal('21331');
  });

  it('labels add Labels By Object', function() {
    labels.addLabel({
      text: 2122121,
      x: 22,
      y: 23
    });
    const item = labels.getLast();
    expect(item.attr('text')).to.equal('2122121');
  });

  it('labels getLabels', function() {
    const labelsGroup = labels.getLabels();
    expect(labelsGroup.length).to.equal(7);
  });

  it('labels add Labels is Null', function() {
    labels.addLabel({
      text: null,
      x: 22,
      y: 23
    });
    const item = labels.getLast();
    expect(item.attr('text')).to.equal('');
  });

  it('labels.setItems', function() {
    labels.setItems(newItems);
    expect(labels.getCount()).to.equal(7);
  });

  it('labels.changeLabel(null, newLabel)', function() {
    const label = null;
    labels.changeLabel(label, {
      x: 100,
      y: 20,
      text: '变化'
    });
    expect(labels.get('items')).to.deep.equal(newItems);
  });

  it('change label', function() {
    const label = labels.getFirst();
    labels.changeLabel(label, {
      x: 100,
      y: 20,
      text: '变化'
    });
    expect(label.attr('x')).to.equal(100);
    expect(label.attr('text')).to.equal('变化');
    expect(label.attr('rotate')).to.equal(45);
  });

  it('change label without rotate.', function() {
    const label = labels.get('children')[4];
    labels.changeLabel(label, {
      x: 100,
      y: 20,
      text: '变化'
    });
    expect(label.attr('x')).to.equal(100);
    expect(label.attr('text')).to.equal('变化');
    expect(label.attr('rotate')).to.equal(45);
  });
});

describe('格式化文本信息', function() {
  const canvas = new Canvas({
    containerId: 'c1',
    width: 500,
    height: 500
  });

  it('格式化文本信息', function() {
    const labels = canvas.addGroup(Labels, {
      items: [
        { x: 10, y: 20, text: '1' },
        { x: 10, y: 40, text: '2' }
      ],
      formatter(value) {
        return 'text--' + value;
      },
      textStyle: {
        fill: '#f80'
      }
    });
    canvas.draw();

    const firstItem = labels.getFirst();
    const lastItem = labels.getLast();
    expect(labels.getCount()).to.equal(labels.get('items').length);
    expect(firstItem.attr('text')).to.equal('text--1');
    expect(lastItem.attr('text')).to.equal('text--2');
    canvas.clear();
  });

  it('textStyle is a function', function() {
    const labels = canvas.addGroup(Labels, {
      items: [
        { x: 10, y: 20, text: 'CAT' },
        { x: 10, y: 40, text: 'DOG' }
      ],
      textStyle(text) {
        if (text === 'CAT') {
          return {
            fontSize: 24,
            fill: '#6556E2'
          };
        }
        return {
          fontSize: 12,
          fill: '#000'
        };
      }
    });
    canvas.draw();

    const firstItem = labels.getFirst();
    const lastItem = labels.getLast();
    expect(labels.getCount()).to.equal(labels.get('items').length);
    expect(firstItem.attr('fontSize')).to.equal(24);
    expect(firstItem.attr('fill')).to.equal('#6556E2');
    expect(lastItem.attr('fontSize')).to.equal(12);
    expect(lastItem.attr('fill')).to.equal('#000');
    canvas.destroy();
  });
});

describe('HTML 自定义 Labels', function() {

  it('html 渲染，htmlTemplate 是个字符串', function() {
    const canvas = new Canvas({
      containerId: 'c1',
      width: 500,
      height: 500
    });
    const labels = canvas.addGroup(Labels, {
      items: [
        { x: 10, y: 20, text: '1' },
        { x: 10, y: 40, text: '2' }
      ],
      textStyle: {
        fill: '#f80',
        textAlign: 'right'
      },
      htmlTemplate: '${text}',
      formatter(value) {
        return 'text--' + value;
      }
    });
    canvas.draw();
    const customDiv = labels.get('customDiv');
    const childNodes = customDiv.childNodes;
    const lastNode = childNodes[childNodes.length - 1];
    expect(labels.get('children')).to.be.empty;
    expect(customDiv).not.to.be.undefined;
    expect(lastNode.innerHTML).to.equal('text--2');
    expect(lastNode.style.right).to.equal('');
    expect(lastNode.style.left).to.equal('-21px');
    canvas.destroy();
  });
  describe('自定义 Labels，回调', function() {

    const canvas = new Canvas({
      containerId: 'c1',
      width: 500,
      height: 500
    });

    const labels = canvas.addGroup(Labels, {
      items: [
        { x: 10, y: 20, text: '1' },
        { x: 10, y: 40, text: '2' },
        { x: 10, y: 60, text: '3' },
        { x: 10, y: 80, text: '4' },
        { x: 10, y: 100, text: '5' },
        { x: 10, y: 120, text: '6' },
        { x: 10, y: 140, text: '7' },
        { x: 10, y: 160, text: '8' }
      ],
      htmlTemplate(value, item, index) {
        return item.x + '' + value + '' + index;
      }
    });
    canvas.draw();
    const customDiv = labels.get('customDiv');

    it('labels create', function() {
      expect(customDiv).not.to.be.undefined;
      expect(customDiv.childNodes.length).to.equal(labels.get('items').length);
    });

    it('labels x,y', function() {
      const item = customDiv.childNodes[0];
      expect(parseFloat(item.style.left)).to.equal(10);
      const second = customDiv.childNodes[1];
      expect(parseFloat(second.style.left)).not.to.equal(40);
    });

    it('labels getLabels', function() {
      const labelsGroup = labels.getLabels();
      expect(labelsGroup.length).to.equal(8);
    });

    it('labels add Labels By Object', function() {
      labels.addLabel({
        text: 2122121,
        x: 22,
        y: 23,
        textAlign: 'end'
      });
      labels.addLabel({
        text: 2122121,
        x: 22,
        y: 23,
        textAlign: 'center'
      });
      const childNodes = customDiv.childNodes;
      const lastNode = childNodes[childNodes.length - 1];
      expect(lastNode.innerHTML).to.equal('2221221219');
    });

    it('custom renderer', function() {
      const item = customDiv.childNodes[0];
      expect(item.innerHTML).to.equal('1010');
    });

    it('labels.setItems', function() {
      const items = [
        { x: 10, y: 20, text: '一' },
        { x: 10, y: 40, text: '二' },
        { x: 10, y: 60, text: '三' },
        { x: 10, y: 80, text: '四' },
        { x: 10, y: 100, text: '五' },
        { x: 10, y: 120, text: '六' },
        { x: 10, y: 140, text: '七' }
      ];
      labels.setItems(items);
      expect(customDiv.childNodes.length).to.equal(7);
    });

    it('change label', function() {
      const label = customDiv.childNodes[0];
      labels.changeLabel(label, {
        x: 100,
        y: 40,
        text: '变化'
      });
      expect(label.innerHTML).to.equal('100变化0');
    });

    it('remove', function() {
      labels.remove();
      expect(customDiv.childNodes.length).to.equal(0);
    });
  });
});
<commit_msg>修复测试用例，统一 dom 字体。<commit_after>const expect = require('chai').expect;
const G = require('@ali/g');
const Canvas = G.Canvas;
const Labels = require('../../../../src/component/label/index');

const div = document.createElement('div');
div.id = 'c1';
document.body.appendChild(div);

describe('Labels', function() {

  const newItems = [
    { x: 10, y: 20, text: '一' },
    { x: 10, y: 40, text: '二' },
    { x: 10, y: 60, text: '三' },
    { x: 10, y: 80, text: '四' },
    { x: 10, y: 100, text: '五' },
    { x: 10, y: 120, text: '六' },
    { x: 10, y: 140, text: '七' }
  ];
  const canvas = new Canvas({
    containerId: 'c1',
    width: 500,
    height: 500,
    pixelRatio: 2
  });
  const labels = canvas.addGroup(Labels, {
    items: [
      { x: 10, y: 20, text: '1' },
      { x: 10, y: 40, text: '2' },
      { x: 10, y: 60, text: '3' },
      { x: 10, y: 80, text: '4' },
      { x: 10, y: 100, text: '5', fontFamily: 'Arial', fontSize: 30, fill: 'red' }
    ],
    textStyle(value) {
      console.log(value);
      const style = {
        fill: '#f80',
        textAlign: 'center', // 文本对齐方向，可取值为： start middle end
        fontSize: 16, // 文本大小
        fontWeight: 'bold' // 文本粗细
      };
      if (value !== '5' && value !== '五') {
        style.rotate = 45;
      }
      return style;
    }
  });

  canvas.draw();

  it('labels create', function() {
    expect(labels.getCount()).to.equal(labels.get('items').length);
    expect(labels).to.have.property('__cfg');
    expect(labels.__cfg.textStyle).to.be.a('function');
  });

  it('labels x,y', function() {
    const item = labels.getFirst();
    expect(item.attr('x')).to.equal(10);
    expect(item.attr('y')).to.equal(20);
    expect(item.attr('rotate')).to.equal(45);
  });

  it('labels rotate', function() {
    const item = labels.getLast();
    console.log();
    expect(item.attr('rotate')).to.be.undefined;
  });

  it('labels add Labels By String', function() {
    labels.addLabel('21331');
    const item = labels.getLast();
    expect(item.attr('text')).to.equal('21331');
  });

  it('labels add Labels By Object', function() {
    labels.addLabel({
      text: 2122121,
      x: 22,
      y: 23
    });
    const item = labels.getLast();
    expect(item.attr('text')).to.equal('2122121');
  });

  it('labels getLabels', function() {
    const labelsGroup = labels.getLabels();
    expect(labelsGroup.length).to.equal(7);
  });

  it('labels add Labels is Null', function() {
    labels.addLabel({
      text: null,
      x: 22,
      y: 23
    });
    const item = labels.getLast();
    expect(item.attr('text')).to.equal('');
  });

  it('labels.setItems', function() {
    labels.setItems(newItems);
    expect(labels.getCount()).to.equal(7);
  });

  it('labels.changeLabel(null, newLabel)', function() {
    const label = null;
    labels.changeLabel(label, {
      x: 100,
      y: 20,
      text: '变化'
    });
    expect(labels.get('items')).to.deep.equal(newItems);
  });

  it('change label', function() {
    const label = labels.getFirst();
    labels.changeLabel(label, {
      x: 100,
      y: 20,
      text: '变化'
    });
    expect(label.attr('x')).to.equal(100);
    expect(label.attr('text')).to.equal('变化');
    expect(label.attr('rotate')).to.equal(45);
  });

  it('change label without rotate.', function() {
    const label = labels.get('children')[4];
    labels.changeLabel(label, {
      x: 100,
      y: 20,
      text: '变化'
    });
    expect(label.attr('x')).to.equal(100);
    expect(label.attr('text')).to.equal('变化');
    expect(label.attr('rotate')).to.equal(45);
  });
});

describe('格式化文本信息', function() {
  const canvas = new Canvas({
    containerId: 'c1',
    width: 500,
    height: 500,
    pixelRatio: 2
  });

  it('格式化文本信息', function() {
    const labels = canvas.addGroup(Labels, {
      items: [
        { x: 10, y: 20, text: '1' },
        { x: 10, y: 40, text: '2' }
      ],
      formatter(value) {
        return 'text--' + value;
      },
      textStyle: {
        fill: '#f80'
      }
    });
    canvas.draw();

    const firstItem = labels.getFirst();
    const lastItem = labels.getLast();
    expect(labels.getCount()).to.equal(labels.get('items').length);
    expect(firstItem.attr('text')).to.equal('text--1');
    expect(lastItem.attr('text')).to.equal('text--2');
    canvas.clear();
  });

  it('textStyle is a function', function() {
    const labels = canvas.addGroup(Labels, {
      items: [
        { x: 10, y: 20, text: 'CAT' },
        { x: 10, y: 40, text: 'DOG' }
      ],
      textStyle(text) {
        if (text === 'CAT') {
          return {
            fontSize: 24,
            fill: '#6556E2'
          };
        }
        return {
          fontSize: 12,
          fill: '#000'
        };
      }
    });
    canvas.draw();

    const firstItem = labels.getFirst();
    const lastItem = labels.getLast();
    expect(labels.getCount()).to.equal(labels.get('items').length);
    expect(firstItem.attr('fontSize')).to.equal(24);
    expect(firstItem.attr('fill')).to.equal('#6556E2');
    expect(lastItem.attr('fontSize')).to.equal(12);
    expect(lastItem.attr('fill')).to.equal('#000');
    canvas.destroy();
  });
});

describe('HTML 自定义 Labels', function() {

  it('html 渲染，htmlTemplate 是个字符串', function() {
    const canvas = new Canvas({
      containerId: 'c1',
      width: 500,
      height: 500,
      pixelRatio: 2
    });
    const labels = canvas.addGroup(Labels, {
      items: [
        { x: 80, y: 20, text: '1' },
        { x: 80, y: 40, text: '2' }
      ],
      textStyle: {
        fill: '#f80',
        textAlign: 'right'
      },
      htmlTemplate: '
${text}
',
      formatter(value) {
        return 'text--' + value;
      }
    });
    canvas.draw();
    const customDiv = labels.get('customDiv');
    const childNodes = customDiv.childNodes;
    const lastNode = childNodes[childNodes.length - 1];
    expect(labels.get('children')).to.be.empty;
    expect(customDiv).not.to.be.undefined;
    expect(lastNode.innerHTML).to.equal('
text--2
');
    expect(lastNode.style.right).to.equal('');
    expect(lastNode.style.left).to.equal('30px');
    canvas.destroy();
  });
  describe('自定义 Labels，回调', function() {

    const canvas = new Canvas({
      containerId: 'c1',
      width: 500,
      height: 500,
      pixelRatio: 2
    });

    const labels = canvas.addGroup(Labels, {
      items: [
        { x: 10, y: 20, text: '1' },
        { x: 10, y: 40, text: '2' },
        { x: 10, y: 60, text: '3' },
        { x: 10, y: 80, text: '4' },
        { x: 10, y: 100, text: '5' },
        { x: 10, y: 120, text: '6' },
        { x: 10, y: 140, text: '7' },
        { x: 10, y: 160, text: '8' }
      ],
      htmlTemplate(value, item, index) {
        return item.x + '' + value + '' + index;
      }
    });
    canvas.draw();
    const customDiv = labels.get('customDiv');

    it('labels create', function() {
      expect(customDiv).not.to.be.undefined;
      expect(customDiv.childNodes.length).to.equal(labels.get('items').length);
    });

    it('labels x,y', function() {
      const item = customDiv.childNodes[0];
      expect(parseFloat(item.style.left)).to.equal(10);
      const second = customDiv.childNodes[1];
      expect(parseFloat(second.style.left)).not.to.equal(40);
    });

    it('labels getLabels', function() {
      const labelsGroup = labels.getLabels();
      expect(labelsGroup.length).to.equal(8);
    });

    it('labels add Labels By Object', function() {
      labels.addLabel({
        text: 2122121,
        x: 22,
        y: 23,
        textAlign: 'end'
      });
      labels.addLabel({
        text: 2122121,
        x: 22,
        y: 23,
        textAlign: 'center'
      });
      const childNodes = customDiv.childNodes;
      const lastNode = childNodes[childNodes.length - 1];
      expect(lastNode.innerHTML).to.equal('2221221219');
    });

    it('custom renderer', function() {
      const item = customDiv.childNodes[0];
      expect(item.innerHTML).to.equal('1010');
    });

    it('labels.setItems', function() {
      const items = [
        { x: 10, y: 20, text: '一' },
        { x: 10, y: 40, text: '二' },
        { x: 10, y: 60, text: '三' },
        { x: 10, y: 80, text: '四' },
        { x: 10, y: 100, text: '五' },
        { x: 10, y: 120, text: '六' },
        { x: 10, y: 140, text: '七' }
      ];
      labels.setItems(items);
      expect(customDiv.childNodes.length).to.equal(7);
    });

    it('change label', function() {
      const label = customDiv.childNodes[0];
      labels.changeLabel(label, {
        x: 100,
        y: 40,
        text: '变化'
      });
      expect(label.innerHTML).to.equal('100变化0');
    });

    it('remove', function() {
      labels.remove();
      expect(customDiv.childNodes.length).to.equal(0);
    });
  });
});
<|endoftext|>


<commit_before>/**
 * File: Merlin Example Widget
 * This file holds an example merlin widget.
 *
 * Filename:
 * tc.gam.example_widget.js
 *
 * Dependencies:
 * - tc.gam.util.js
 * - tc.util.js
 * - tc.gam.app.js
 * - tc.gam.merlin.js
 */

/**
 * Variable: tc.gam.widgets
 * Container for widgets.
 */
tc.gam.widgets = tc.gam.widgets || {};

/**
 * Variable: tc.gam.widgets.example_widget.merlin_options
 * Define the merlin wizard options here.
 */
tc.gam.widgets.example_widget = function(dom) {
    return {
        name: 'merlin_app_id',
        dom: tc.jQ('.merlin.example-wizard'),
        next_button: tc.jQ('a.next-button'),
        back_button: tc.jQ('a.back-button'),  // Still not working or fully understood
        error_indicator: tc.jQ('.error-indicator'),
        progress_element: tc.jQ('.progress-element'),  // Still not working or fully understood
        magic: false, // Makes a mess
        first_step: 'step_id_1',
        data: {
            data_property: 'default-value',
            text_input: '',
            checkbox_input: '0',
            email_input: '',
            error_select: 'none'
        },
        use_hashchange: false,
        steps: {
            'step_id_1': {
                progress_selector: '.1',  // Still not working or fully understood
                selector: '.step.step-selector-1',
                prev_step: null,
                next_step: 'step_id_2',
				use_for_history: false, // Not sure what is used for.
                inputs: {
                    'input_textfield': {
                        selector: 'input.textfield-input',
                        validators: ['min-3', 'max-200', 'required'],
                        hint: 'This is a hint for a textfield...',
                        counter: {
                            selector: '.charlimit.charlimit-textfield-input',
                            limit: 200
                        },
                        handlers: {
                            focus: function(e, d) {
                                tc.jQ('.messages').append('
Focuses on the input field.
');
                            }
                        }
                    },
                    'input_checkbox': {
                        selector: 'input.checkbox-input'
                    }
                },
                init: function(merlin, dom) {
                    tc.jQ('.messages').append('
Starting Step 01.
');
                },
                finish: function(merlin, dom) {
                    // Add data from the inputs.
                    merlin.options.data = tc.jQ.extend(merlin.options.data, {
                        text_input: merlin.current_step.inputs.input_textfield.dom.val(),
                        checkbox_input: merlin.current_step.inputs.input_checkbox.dom.val()
                    });

                    tc.jQ('.messages').append('
Passed Step 01.
');
                    tc.jQ('.messages').append('
Data: ' + JSON.stringify(merlin.options.data) + '
');
                }
            },
            'step_id_2': {
                progress_selector: '.2', // Still not working or fully understood
                selector: '.step.step-selector-2',
                prev_step: 'step_id_1',
                next_step: 'step_id_3_success',
                inputs: {
                    'input_textfield_email': {
                        selector: 'input.textfield-input-email',
                        validators: ['email', 'required'],
                        hint: 'Please enter your email'
                    },
                    'error_select': {
                        selector: 'select.select-error'
                    }
                },
                init: function(merlin, dom) {
                    tc.jQ('.messages').append('
Passed Step 01.
');
                },
                finish: function(merlin, dom) {
                    // Add data from the inputs.
                    merlin.options.data = tc.jQ.extend(merlin.options.data, {
                        email_input: merlin.current_step.inputs.input_textfield_email.dom.val(),
                        error_select: merlin.current_step.inputs.error_select.dom.val()
                    });

                    tc.jQ('.messages').append('
Passed Step 02.
');
                    tc.jQ('.messages').append('
Data: ' + JSON.stringify(merlin.options.data) + '
');

                    if (merlin.options.data.error_select == 'error') {
                        // How to raise error in merline?
                        tc.jQ('.messages').append('
Raising error.
');
                    }
                    // This causes recursion
                    /*
                    if (merlin.options.data.error_select == 'redirect') {
                        merlin.show_step('step_id_1');
                        tc.jQ('.messages').append('
Redirecting.
');
                    }
                    */
                }
            },
            'step_id_3_success': {
                selector: '.step.step-id-3-success',
                init: function(merlin, dom) {
                    tc.jQ('.messages').append('
Init Step 03, Success.
');
                }
            },
            'step_id_4_error': {
                selector: '.step.step-id-4-error',
                init: function(merlin, dom) {
                    tc.jQ('.messages').append('
Init Step 04, Error.
');
                }
            }
        }
    };
};

/**
 * Function: jQuery ready
 *
 */
jQuery(document).ready(function(e) {
    // Create object for the page application, which is the container
    // for Features.  Features are functions that accept the app object.
    var app_page = {
        data: {},
        features: [],
        prevent_logging: false
    };

    // Push a feature.  This feature contains an app.component that is a
    // new merlin object.  This merlin object is defined above.
    app_page.features.push(function(app) {
        app.components.example = new tc.merlin(app, tc.gam.widgets.example_widget());
    });

    // Create app.
    tc.app(app_page);
});<commit_msg>Added some more inline documentation for merlin example.<commit_after>/**
 * File: Merlin Example Widget
 * This file holds an example merlin widget.
 *
 * Filename:
 * tc.gam.example_widget.js
 *
 * Dependencies:
 * - tc.gam.util.js
 * - tc.util.js
 * - tc.gam.app.js
 * - tc.gam.merlin.js
 */

/**
 * Variable: tc.gam.widgets
 * Container for widgets.  Not entirely necessary.
 */
tc.gam.widgets = tc.gam.widgets || {};

/**
 * Variable: tc.gam.widgets.example_widget
 * Define the merlin wizard options object here.
 */
tc.gam.widgets.example_widget =  {
    name: 'merlin_app_id',
    dom: tc.jQ('.merlin.example-wizard'),
    next_button: tc.jQ('a.next-button'),
    back_button: tc.jQ('a.back-button'),  // Still not working or fully understood
    error_indicator: tc.jQ('.error-indicator'),
    progress_element: tc.jQ('.progress-element'),  // Still not working or fully understood
    magic: false, // Makes a mess if true.
    first_step: 'step_id_1',
    data: {
        data_property: 'default-value',
        text_input: '',
        checkbox_input: '0',
        email_input: '',
        error_select: 'none'
    },
    use_hashchange: false, // Turning this to true seems to cause a repeat of steps
    steps: {
        'step_id_1': {
            progress_selector: '.1',  // Still not working or fully understood
            selector: '.step.step-selector-1',
            prev_step: null,
            next_step: 'step_id_2',
			use_for_history: false, // Not sure what is used for.
            inputs: {
                'input_textfield': {
                    selector: 'input.textfield-input',
                    validators: ['min-3', 'max-200', 'required'],
                    hint: 'This is a hint for a textfield...',
                    counter: {
                        selector: '.charlimit.charlimit-textfield-input',
                        limit: 200
                    },
                    handlers: {
                        focus: function(e, d) {
                            tc.jQ('.messages').append('
Focuses on the input field.
');
                        }
                    }
                },
                'input_checkbox': {
                    selector: 'input.checkbox-input'
                }
            },
            init: function(merlin, dom) {
                tc.jQ('.messages').append('
Starting Step 01.
');
            },
            finish: function(merlin, dom) {
                // Add data from the inputs.
                merlin.options.data = tc.jQ.extend(merlin.options.data, {
                    text_input: merlin.current_step.inputs.input_textfield.dom.val(),
                    checkbox_input: merlin.current_step.inputs.input_checkbox.dom.val()
                });

                tc.jQ('.messages').append('
Passed Step 01.
');
                tc.jQ('.messages').append('
Data: ' + JSON.stringify(merlin.options.data) + '
');
            }
        },
        'step_id_2': {
            progress_selector: '.2', // Still not working or fully understood
            selector: '.step.step-selector-2',
            prev_step: 'step_id_1',
            next_step: 'step_id_3_success',
            inputs: {
                'input_textfield_email': {
                    selector: 'input.textfield-input-email',
                    validators: ['email', 'required'],
                    hint: 'Please enter your email'
                },
                'error_select': {
                    selector: 'select.select-error'
                }
            },
            init: function(merlin, dom) {
                tc.jQ('.messages').append('
Passed Step 01.
');
            },
            finish: function(merlin, dom) {
                // Add data from the inputs.
                merlin.options.data = tc.jQ.extend(merlin.options.data, {
                    email_input: merlin.current_step.inputs.input_textfield_email.dom.val(),
                    error_select: merlin.current_step.inputs.error_select.dom.val()
                });

                tc.jQ('.messages').append('
Passed Step 02.
');
                tc.jQ('.messages').append('
Data: ' + JSON.stringify(merlin.options.data) + '
');

                if (merlin.options.data.error_select == 'error') {
                    // How to raise error in merline?
                    tc.jQ('.messages').append('
Raising error.
');
                }
                // This causes recursion
                /*
                if (merlin.options.data.error_select == 'redirect') {
                    merlin.show_step('step_id_1');
                    tc.jQ('.messages').append('
Redirecting.
');
                }
                */
            }
        },
        'step_id_3_success': {
            selector: '.step.step-id-3-success',
            init: function(merlin, dom) {
                tc.jQ('.messages').append('
Init Step 03, Success.
');
            }
        },
        'step_id_4_error': {
            selector: '.step.step-id-4-error',
            init: function(merlin, dom) {
                tc.jQ('.messages').append('
Init Step 04, Error.
');
            }
        }
    }
};

/**
 * Function: jQuery ready
 * When page is ready, start execution.
 */
jQuery(document).ready(function(e) {
    // Create object for the page application, which is the container
    // for Features.  Features are functions that accept the app object.
    var app_page = {
        data: {},
        features: [],
        prevent_logging: false
    };

    // Push a feature.  This feature contains an app.component that is a
    // new merlin object.  This merlin object is defined above.
    app_page.features.push(function(app) {
        // Add the example component (merlin example widget) here.
        app.components.example_component = new tc.merlin(app, tc.gam.widgets.example_widget);
    });

    // Create app.
    tc.app(app_page);
});<|endoftext|>


<commit_before>function initializeDetector(name, headline){
    var headWrapper = document.createElement('div')
    ,   title = document.createElement('h1')
    ,   drawTarget = document.createElement('div')
    ,   plotControlWrap = document.createElement('form')
    ,   plotControlTitle = document.createElement('h3')
    ,   plotControlMinLabel = document.createElement('label')
    ,   plotControlMaxLabel = document.createElement('label')
    ,   plotControlMin = document.createElement('input')
    ,   plotControlMax = document.createElement('input')
    ,   plotScale = document.createElement('select')
    ,   plotScaleLin = document.createElement('option')
    ,   plotScaleLog = document.createElement('option')
    ,   deckWrap = document.createElement('div')
    ,   sidebarWrap = document.createElement('div')
    ,   plotDeck
    ,   plotCard
    ,   xString
    ,   deckNavigator, deckOption
    //image has aspect ratio 3:2 and tries to be 80% of the window width, but not more than 80% of the window height
    ,   width = 0.70*this.offsetWidth
    ,   height = 2*width/3
    ,   i, subdetectorNav, subdetectorNavLabel

    this.name = name;
    //declare default views and units if none pre-defined
    if(!this.views)
        this.views = ['HV', 'Threshold', 'Rate'];
    if(!this.units)
        this.units = ['V', 'ADC Units', 'Hz'];

    //set up data store for detectors
    if(!window.currentData)
        window.currentData = {};
    for(i=0; i<this.views.length; i++){
        window.currentData[this.views[i]] = {};
    }

    //////////////////////
    //Build DOM
    //////////////////////
    //the DOM layout for a detector view is roughly:

    //-----------------------------------
    // h1 Title
    //-----------------------------------
    // input[radio] HV / Thresh / Rate
    //-----------------------------------
    // x-deck for detector views; each
    // card contains a Kinetic drawing
    // context
    //
    //-----------------------------------
    // x-deck navigation if required
    //-----------------------------------
    // form plot control widget
    //-----------------------------------

    headWrapper.setAttribute('id', this.id+'titleWrapper');
    headWrapper.setAttribute('class', 'subdetectorHeadlineWrap')
    this.appendChild(headWrapper);
    //top nav title
    title.setAttribute('id', this.id+'title');
    title.setAttribute('class', 'subdetectorTitle');
    document.getElementById(this.id+'titleWrapper').appendChild(title);
    document.getElementById(this.id+'title').innerHTML = headline;
    //state nav radio
    for(i=0; i<this.views.length; i++){
        subdetectorNav = document.createElement('input')
        subdetectorNav.setAttribute('id', this.id+'goto'+this.views[i]);
        subdetectorNav.setAttribute('class', 'subdetectorNavRadio');
        subdetectorNav.setAttribute('type', 'radio');
        subdetectorNav.setAttribute('name', this.id+'Nav');
        subdetectorNav.setAttribute('value', this.views[i]);
        subdetectorNav.onchange = this.trackView.bind(this);
        if(i==2) subdetectorNav.setAttribute('checked', true); //default to rate view
        document.getElementById(this.id+'titleWrapper').appendChild(subdetectorNav);
        subdetectorNavLabel = document.createElement('label');
        subdetectorNavLabel.setAttribute('id', this.id+'goto'+this.views[i]+'Label');
        subdetectorNavLabel.setAttribute('class', 'subdetectorNavLabel');
        subdetectorNavLabel.setAttribute('for', this.id+'goto'+this.views[i]);
        document.getElementById(this.id+'titleWrapper').appendChild(subdetectorNavLabel);
        document.getElementById(this.id+'goto'+this.views[i]+'Label').innerHTML = this.views[i];
    }

    //sidebar deck
    sidebarWrap.setAttribute('id', this.id+'sidebarWrap');
    sidebarWrap.setAttribute('class', 'sidebarWrap');
    sidebarWrap.setAttribute('style', 'height:'+height + parseInt(headWrapper.offsetHeight,10));
    this.appendChild(sidebarWrap);
    xString = '<x-deck id='+this.id+'SidebarDeck selected-index=1>'
    xString += '<x-card id='+this.id+'HVSideCard class="sidebarCard"><widget-HVcontrol id="HVcontrol" MIDAS='+this.MIDAS+' style="max-width:100%"></widget-HVcontrol></x-card>'
    xString += '<x-card id='+this.id+'RateSideCard class="sidebarCard"><widget-rateBar id="ratesAndThresholds" MIDAS='+this.MIDAS+'></widget-RateBar></x-card>'
    xString += '</x-deck>'
    sidebarWrap.innerHTML = xString;

    //plot deck wrapper:
    deckWrap.setAttribute('id', this.id+'DeckWrap');
    deckWrap.setAttribute('class', 'deckWrap')
    this.appendChild(deckWrap);

    //declaring x-tags from within other x-tags needs special treatment via innerHTML; must build HTML string and set it.
    xString = '<x-deck id="' + this.id + 'Deck" selected-index=0 style="height:'+height+'">';
    for(i=0; i<this.viewNames.length; i++){
        xString += '<x-card id="' + this.id+this.viewNames[i] + 'Card"></x-card>';
    }
    xString += '</x-deck>'
    deckWrap.innerHTML = xString;

    //plot buffers
    for(i=0; i<this.viewNames.length; i++){
        //divs to hold kinetic contexts
        drawTarget = document.createElement('div');
        drawTarget.setAttribute('id', this.id+this.viewNames[i]+'Draw');
        document.getElementById(this.id+this.viewNames[i] + 'Card').appendChild(drawTarget);
    }

    //plot control widget
    plotControlWrap.setAttribute('id', this.id+'PlotControl');
    plotControlWrap.setAttribute('class', 'plotControlWidget');
    this.appendChild(plotControlWrap);
    document.getElementById(this.id+'PlotControl').onchange = this.updatePlotParameters.bind(this);

    plotControlTitle.setAttribute('id', this.id+'PlotControlTitle');
    plotControlWrap.appendChild(plotControlTitle);
    document.getElementById(this.id + 'PlotControlTitle').innerHTML = 'Plot Control'

    //x-deck navigation
    if(this.viewNames.length > 1){
        deckNavigator = document.createElement('select');
        deckNavigator.id = this.id + 'viewSelect';
        for(i=0; i<this.viewNames.length; i++){
            deckOption = document.createElement('option');
            deckOption.innerHTML = this.viewNames[i];
            deckOption.value = i;
            deckNavigator.appendChild(deckOption);
        }
        deckNavigator.onchange = function(){
            var viewVal = selected(this.id+'viewSelect');

            document.getElementById(this.id+'Deck').shuffleTo(viewVal);
            this.displayIndex = viewVal;
            this.update();  //repaint right away
        }.bind(this)
        plotControlWrap.appendChild(deckNavigator);
    }

    plotControlMinLabel.setAttribute('id', this.id+'PlotControlMinLabel');
    plotControlWrap.appendChild(plotControlMinLabel)
    document.getElementById(this.id+'PlotControlMinLabel').innerHTML = 'Min: ';
    plotControlMin.setAttribute('id', this.id + 'PlotControlMin');
    plotControlMin.setAttribute('type', 'number');
    plotControlMin.setAttribute('step', 'any');
    plotControlWrap.appendChild(plotControlMin);

    plotControlMaxLabel.setAttribute('id', this.id+'PlotControlMaxLabel');
    plotControlWrap.appendChild(plotControlMaxLabel)
    document.getElementById(this.id+'PlotControlMaxLabel').innerHTML = 'Max: ';
    plotControlMax.setAttribute('id', this.id + 'PlotControlMax');
    plotControlMax.setAttribute('type', 'number');
    plotControlMax.setAttribute('step', 'any');
    plotControlWrap.appendChild(plotControlMax);

    plotScale.setAttribute('id', this.id+'PlotControlScale');
    plotControlWrap.appendChild(plotScale);

    plotScaleLin.setAttribute('id', this.id+'PlotScaleLin');
    plotScaleLin.setAttribute('value', 'lin');
    plotScale.appendChild(plotScaleLin);
    document.getElementById(this.id+'PlotScaleLin').innerHTML = 'Linear';

    plotScaleLog.setAttribute('id', this.id+'PlotScaleLog');
    plotScaleLog.setAttribute('value', 'log');
    plotScale.appendChild(plotScaleLog);
    document.getElementById(this.id+'PlotScaleLog').innerHTML = 'Log';

    ///////////////////////
    //State variables
    ///////////////////////
    this.currentView = 'Rate';
    this.currentUnit = 'Hz';
    this.displayIndex = 0;  //always start on the first card, guarnateed to exist.
    this.HVcrates = 0;
    detectHVcrates(this.MIDAS, this);
    //cells
    this.cells = {};
    this.summaryDepth = 0;

    ////////////////////////////
    //Drawing parameters
    ////////////////////////////
    this.frameLineWidth = 2;
    this.frameColor = '#000000';//'#999999';
    this.width = width;
    this.height = height;

    ///////////////////////////
    //Scale Parameters
    ///////////////////////////
    this.scale = 'ROOT Rainbow';
    this.min = {};
    this.max = {};
    this.scaleType = {};
    for(i=0; i<this.views.length; i++){
        this.min[this.views[i]] = canHas(localStorage.getItem(name+this.views[i]+'min'), 0);
        this.max[this.views[i]] = canHas(localStorage.getItem(name+this.views[i]+'max'), 3000);
        this.scaleType[this.views[i]] = canHas(localStorage.getItem(name+this.views[i]+'scaleType'), 'lin');
    }

    //if anything was in local storage, communicate this to the UI:
    plotControlMin.value = this.min[this.currentView];
    plotControlMax.value = this.max[this.currentView];
    plotScale.value = this.scaleType[this.currentView];

    ///////////////////////////
    //Tooltip state
    ///////////////////////////
    this.lastTTindex = -1;

    ////////////////////////////
    //Kinetic.js setup
    ////////////////////////////

    //indices for these arrays correspond to the x-card index on display
    this.stage = [];
    this.mainLayer = [];
    this.scaleLayer = [];
    this.tooltipLayer = [];
    this.TTbkg = [];
    this.text = [];

    for(i=0; i<this.viewNames.length; i++){

        //point kinetic at the div and set up the staging and layers:
        this.stage[i] = new Kinetic.Stage({
            container: this.id+this.viewNames[i]+'Draw',
            width: width,
            height: height
        });
        this.mainLayer[i] = new Kinetic.Layer();       //main rendering layer
        this.scaleLayer[i] = new Kinetic.Layer();      //layer for scales / legends
        this.tooltipLayer[i] = new Kinetic.Layer();    //layer for tooltip info

        //tooltip background:
        this.TTbkg[i] = new Kinetic.Rect({
            x:-1000,
            y:-1000,
            width:100,
            height:100,
            fill:'rgba(0,0,0,0.8)',
            stroke: 'rgba(0,0,0,0)',
            listening: false
        });
        this.tooltipLayer[i].add(this.TTbkg[i]);

        //tooltip text:
        this.text[i] = new Kinetic.Text({
            x: -1000,
            y: -1000,
            fontFamily: 'Arial',
            fontSize: 16,
            text: '',
            lineHeight: 1.2,
            fill: '#EEEEEE',
            listening: false
        });
        this.tooltipLayer[i].add(this.text[i]);
    }

    this.errorPattern = new Image();
    this.errorPattern.onload = function(){
        var key;

        for(key in this.cells){
            this.cells[key].setAttr('fillPatternImage', this.errorPattern);
        }

        this.mainLayer[this.displayIndex].draw();
    }.bind(this)
    this.errorPattern.src = 'static/img/static.gif'

    //let repopulate know that the detector would like to be updated every loop:
    if(!window.refreshTargets)
        window.refreshTargets = [];
    window.refreshTargets[window.refreshTargets.length] = this;

}

//stick the ODB equipment directory into its local slot:
function fetchODBEquipment(returnObj){
    if(!window.currentData.ODB)
        window.currentData.ODB = {};
    window.currentData.ODB.Equipment = returnObj;
}

//callback for fetching from the scalar service:
function parseRate(data){
    var key, subkey;

    if(!window.currentData.Rate)
        window.currentData.Rate = {};

    for(key in data){
        if (data.hasOwnProperty(key)) {
            for(subkey in data[key]){
                if(data[key].hasOwnProperty(subkey)){
                    window.currentData.Rate[subkey.toUpperCase().slice(0,10)] = data[key][subkey].TRIGREQ;
                }
            }
        }
    }
}

//similar function for the threshold service:
function parseThreshold(data){
    var key;
    if(!window.currentData.Threshold)
        window.currentData.Threshold = {};

    if(data['parameters']['thresholds']){
        for(key in data['parameters']['thresholds']){
            window.currentData.Threshold[key.toUpperCase().slice(0,10)] = data['parameters']['thresholds'][key];
        }
    }
}

//and again for HV:
function parseHV(data){
    var i, j;

    if(!window.currentData.HV)
        window.currentData.HV = {};

    if(!window.ODBEquipment)
        window.ODBEquipment = {};

    for(i=0; i<data.length; i++){
        window.ODBEquipment['HV-'+i] = data[i]; //keep the whole blob around for the benefit of the HV sidebar
        for(j=0; j<data[i].Settings.Names.length; j++){
            window.currentData.HV[data[i].Settings.Names[j].toUpperCase().slice(0,10)] = data[i].Variables.Measured[j];
        }
    }
}

//function to make a reasonable decision on how many decimal places to show, whether to to use
//sci. notation on an axis tick mark, where <min> and <max> are the axis minimum and maximum,
//<nTicks> is the number of tickmarks on the axis, and we are returning the label for the <n>th
//tick mark
function generateTickLabel(min, max, nTicks, n){
    var range = max - min,
        smallestPrecision = range / (nTicks-1),
        tickValue = min + (max-min)/(nTicks-1)*n;

    //tickmark needs to be labeled to enough precision to show the difference between subsequent ticks:
    smallestPrecision = Math.floor(Math.log(smallestPrecision) / Math.log(10));

    if(smallestPrecision < 0)
        return tickValue.toFixed(-smallestPrecision)

    tickValue = Math.floor(tickValue/Math.pow(10, smallestPrecision)) * Math.pow(10, smallestPrecision);
    return tickValue+'';

}

//determine how many HV frontends are in the ODB.  Frontends must be named HV-0, HV-1, HV-2....
function detectHVcrates(MIDAS, obj){
    var xmlhttp = new XMLHttpRequest();

    //once this is all dealt with, refresh the display immediately
    xmlhttp.onreadystatechange = function(target){
        var data, nCrates = 0;

        if (this.readyState != 4) return;

        data = JSON.parse(this.responseText.slice(this.responseText.indexOf('{'), this.responseText.lastIndexOf('}')+1 ) )

        while(data['HV-'+nCrates])
            nCrates++

        target.HVcrates = nCrates;
    }.bind(xmlhttp, obj)

    xmlhttp.withCredentials = true;
    //fire async
    xmlhttp.open('GET', 'http://' + MIDAS + '/?cmd=jcopy&odb=/Equipment&encoding=json-nokeys');
    xmlhttp.send();
}<commit_msg>sidebar CSS<commit_after>function initializeDetector(name, headline){
    var headWrapper = document.createElement('div')
    ,   title = document.createElement('h1')
    ,   drawTarget = document.createElement('div')
    ,   plotControlWrap = document.createElement('form')
    ,   plotControlTitle = document.createElement('h3')
    ,   plotControlMinLabel = document.createElement('label')
    ,   plotControlMaxLabel = document.createElement('label')
    ,   plotControlMin = document.createElement('input')
    ,   plotControlMax = document.createElement('input')
    ,   plotScale = document.createElement('select')
    ,   plotScaleLin = document.createElement('option')
    ,   plotScaleLog = document.createElement('option')
    ,   deckWrap = document.createElement('div')
    ,   sidebarWrap = document.createElement('div')
    ,   plotDeck
    ,   plotCard
    ,   xString
    ,   deckNavigator, deckOption
    //image has aspect ratio 3:2 and tries to be 80% of the window width, but not more than 80% of the window height
    ,   width = 0.70*this.offsetWidth
    ,   height = 2*width/3
    ,   i, subdetectorNav, subdetectorNavLabel

    this.name = name;
    //declare default views and units if none pre-defined
    if(!this.views)
        this.views = ['HV', 'Threshold', 'Rate'];
    if(!this.units)
        this.units = ['V', 'ADC Units', 'Hz'];

    //set up data store for detectors
    if(!window.currentData)
        window.currentData = {};
    for(i=0; i<this.views.length; i++){
        window.currentData[this.views[i]] = {};
    }

    //////////////////////
    //Build DOM
    //////////////////////
    //the DOM layout for a detector view is roughly:

    //-----------------------------------
    // h1 Title
    //-----------------------------------
    // input[radio] HV / Thresh / Rate
    //-----------------------------------
    // x-deck for detector views; each
    // card contains a Kinetic drawing
    // context
    //
    //-----------------------------------
    // x-deck navigation if required
    //-----------------------------------
    // form plot control widget
    //-----------------------------------

    headWrapper.setAttribute('id', this.id+'titleWrapper');
    headWrapper.setAttribute('class', 'subdetectorHeadlineWrap')
    this.appendChild(headWrapper);
    //top nav title
    title.setAttribute('id', this.id+'title');
    title.setAttribute('class', 'subdetectorTitle');
    document.getElementById(this.id+'titleWrapper').appendChild(title);
    document.getElementById(this.id+'title').innerHTML = headline;
    //state nav radio
    for(i=0; i<this.views.length; i++){
        subdetectorNav = document.createElement('input')
        subdetectorNav.setAttribute('id', this.id+'goto'+this.views[i]);
        subdetectorNav.setAttribute('class', 'subdetectorNavRadio');
        subdetectorNav.setAttribute('type', 'radio');
        subdetectorNav.setAttribute('name', this.id+'Nav');
        subdetectorNav.setAttribute('value', this.views[i]);
        subdetectorNav.onchange = this.trackView.bind(this);
        if(i==2) subdetectorNav.setAttribute('checked', true); //default to rate view
        document.getElementById(this.id+'titleWrapper').appendChild(subdetectorNav);
        subdetectorNavLabel = document.createElement('label');
        subdetectorNavLabel.setAttribute('id', this.id+'goto'+this.views[i]+'Label');
        subdetectorNavLabel.setAttribute('class', 'subdetectorNavLabel');
        subdetectorNavLabel.setAttribute('for', this.id+'goto'+this.views[i]);
        document.getElementById(this.id+'titleWrapper').appendChild(subdetectorNavLabel);
        document.getElementById(this.id+'goto'+this.views[i]+'Label').innerHTML = this.views[i];
    }

    //sidebar deck
console.log(headWrapper.offsetHeight)
    sidebarWrap.setAttribute('id', this.id+'sidebarWrap');
    sidebarWrap.setAttribute('class', 'sidebarWrap');
    sidebarWrap.setAttribute('style', 'height:'+ (height + parseInt(headWrapper.offsetHeight,10)) );
    this.appendChild(sidebarWrap);
    xString = '<x-deck id='+this.id+'SidebarDeck selected-index=1>'
    xString += '<x-card id='+this.id+'HVSideCard class="sidebarCard"><widget-HVcontrol id="HVcontrol" MIDAS='+this.MIDAS+' style="max-width:100%"></widget-HVcontrol></x-card>'
    xString += '<x-card id='+this.id+'RateSideCard class="sidebarCard"><widget-rateBar id="ratesAndThresholds" MIDAS='+this.MIDAS+'></widget-RateBar></x-card>'
    xString += '</x-deck>'
    sidebarWrap.innerHTML = xString;

    //plot deck wrapper:
    deckWrap.setAttribute('id', this.id+'DeckWrap');
    deckWrap.setAttribute('class', 'deckWrap')
    this.appendChild(deckWrap);

    //declaring x-tags from within other x-tags needs special treatment via innerHTML; must build HTML string and set it.
    xString = '<x-deck id="' + this.id + 'Deck" selected-index=0 style="height:'+height+'">';
    for(i=0; i<this.viewNames.length; i++){
        xString += '<x-card id="' + this.id+this.viewNames[i] + 'Card"></x-card>';
    }
    xString += '</x-deck>'
    deckWrap.innerHTML = xString;

    //plot buffers
    for(i=0; i<this.viewNames.length; i++){
        //divs to hold kinetic contexts
        drawTarget = document.createElement('div');
        drawTarget.setAttribute('id', this.id+this.viewNames[i]+'Draw');
        document.getElementById(this.id+this.viewNames[i] + 'Card').appendChild(drawTarget);
    }

    //plot control widget
    plotControlWrap.setAttribute('id', this.id+'PlotControl');
    plotControlWrap.setAttribute('class', 'plotControlWidget');
    this.appendChild(plotControlWrap);
    document.getElementById(this.id+'PlotControl').onchange = this.updatePlotParameters.bind(this);

    plotControlTitle.setAttribute('id', this.id+'PlotControlTitle');
    plotControlWrap.appendChild(plotControlTitle);
    document.getElementById(this.id + 'PlotControlTitle').innerHTML = 'Plot Control'

    //x-deck navigation
    if(this.viewNames.length > 1){
        deckNavigator = document.createElement('select');
        deckNavigator.id = this.id + 'viewSelect';
        for(i=0; i<this.viewNames.length; i++){
            deckOption = document.createElement('option');
            deckOption.innerHTML = this.viewNames[i];
            deckOption.value = i;
            deckNavigator.appendChild(deckOption);
        }
        deckNavigator.onchange = function(){
            var viewVal = selected(this.id+'viewSelect');

            document.getElementById(this.id+'Deck').shuffleTo(viewVal);
            this.displayIndex = viewVal;
            this.update();  //repaint right away
        }.bind(this)
        plotControlWrap.appendChild(deckNavigator);
    }

    plotControlMinLabel.setAttribute('id', this.id+'PlotControlMinLabel');
    plotControlWrap.appendChild(plotControlMinLabel)
    document.getElementById(this.id+'PlotControlMinLabel').innerHTML = 'Min: ';
    plotControlMin.setAttribute('id', this.id + 'PlotControlMin');
    plotControlMin.setAttribute('type', 'number');
    plotControlMin.setAttribute('step', 'any');
    plotControlWrap.appendChild(plotControlMin);

    plotControlMaxLabel.setAttribute('id', this.id+'PlotControlMaxLabel');
    plotControlWrap.appendChild(plotControlMaxLabel)
    document.getElementById(this.id+'PlotControlMaxLabel').innerHTML = 'Max: ';
    plotControlMax.setAttribute('id', this.id + 'PlotControlMax');
    plotControlMax.setAttribute('type', 'number');
    plotControlMax.setAttribute('step', 'any');
    plotControlWrap.appendChild(plotControlMax);

    plotScale.setAttribute('id', this.id+'PlotControlScale');
    plotControlWrap.appendChild(plotScale);

    plotScaleLin.setAttribute('id', this.id+'PlotScaleLin');
    plotScaleLin.setAttribute('value', 'lin');
    plotScale.appendChild(plotScaleLin);
    document.getElementById(this.id+'PlotScaleLin').innerHTML = 'Linear';

    plotScaleLog.setAttribute('id', this.id+'PlotScaleLog');
    plotScaleLog.setAttribute('value', 'log');
    plotScale.appendChild(plotScaleLog);
    document.getElementById(this.id+'PlotScaleLog').innerHTML = 'Log';

    ///////////////////////
    //State variables
    ///////////////////////
    this.currentView = 'Rate';
    this.currentUnit = 'Hz';
    this.displayIndex = 0;  //always start on the first card, guarnateed to exist.
    this.HVcrates = 0;
    detectHVcrates(this.MIDAS, this);
    //cells
    this.cells = {};
    this.summaryDepth = 0;

    ////////////////////////////
    //Drawing parameters
    ////////////////////////////
    this.frameLineWidth = 2;
    this.frameColor = '#000000';//'#999999';
    this.width = width;
    this.height = height;

    ///////////////////////////
    //Scale Parameters
    ///////////////////////////
    this.scale = 'ROOT Rainbow';
    this.min = {};
    this.max = {};
    this.scaleType = {};
    for(i=0; i<this.views.length; i++){
        this.min[this.views[i]] = canHas(localStorage.getItem(name+this.views[i]+'min'), 0);
        this.max[this.views[i]] = canHas(localStorage.getItem(name+this.views[i]+'max'), 3000);
        this.scaleType[this.views[i]] = canHas(localStorage.getItem(name+this.views[i]+'scaleType'), 'lin');
    }

    //if anything was in local storage, communicate this to the UI:
    plotControlMin.value = this.min[this.currentView];
    plotControlMax.value = this.max[this.currentView];
    plotScale.value = this.scaleType[this.currentView];

    ///////////////////////////
    //Tooltip state
    ///////////////////////////
    this.lastTTindex = -1;

    ////////////////////////////
    //Kinetic.js setup
    ////////////////////////////

    //indices for these arrays correspond to the x-card index on display
    this.stage = [];
    this.mainLayer = [];
    this.scaleLayer = [];
    this.tooltipLayer = [];
    this.TTbkg = [];
    this.text = [];

    for(i=0; i<this.viewNames.length; i++){

        //point kinetic at the div and set up the staging and layers:
        this.stage[i] = new Kinetic.Stage({
            container: this.id+this.viewNames[i]+'Draw',
            width: width,
            height: height
        });
        this.mainLayer[i] = new Kinetic.Layer();       //main rendering layer
        this.scaleLayer[i] = new Kinetic.Layer();      //layer for scales / legends
        this.tooltipLayer[i] = new Kinetic.Layer();    //layer for tooltip info

        //tooltip background:
        this.TTbkg[i] = new Kinetic.Rect({
            x:-1000,
            y:-1000,
            width:100,
            height:100,
            fill:'rgba(0,0,0,0.8)',
            stroke: 'rgba(0,0,0,0)',
            listening: false
        });
        this.tooltipLayer[i].add(this.TTbkg[i]);

        //tooltip text:
        this.text[i] = new Kinetic.Text({
            x: -1000,
            y: -1000,
            fontFamily: 'Arial',
            fontSize: 16,
            text: '',
            lineHeight: 1.2,
            fill: '#EEEEEE',
            listening: false
        });
        this.tooltipLayer[i].add(this.text[i]);
    }

    this.errorPattern = new Image();
    this.errorPattern.onload = function(){
        var key;

        for(key in this.cells){
            this.cells[key].setAttr('fillPatternImage', this.errorPattern);
        }

        this.mainLayer[this.displayIndex].draw();
    }.bind(this)
    this.errorPattern.src = 'static/img/static.gif'

    //let repopulate know that the detector would like to be updated every loop:
    if(!window.refreshTargets)
        window.refreshTargets = [];
    window.refreshTargets[window.refreshTargets.length] = this;

}

//stick the ODB equipment directory into its local slot:
function fetchODBEquipment(returnObj){
    if(!window.currentData.ODB)
        window.currentData.ODB = {};
    window.currentData.ODB.Equipment = returnObj;
}

//callback for fetching from the scalar service:
function parseRate(data){
    var key, subkey;

    if(!window.currentData.Rate)
        window.currentData.Rate = {};

    for(key in data){
        if (data.hasOwnProperty(key)) {
            for(subkey in data[key]){
                if(data[key].hasOwnProperty(subkey)){
                    window.currentData.Rate[subkey.toUpperCase().slice(0,10)] = data[key][subkey].TRIGREQ;
                }
            }
        }
    }
}

//similar function for the threshold service:
function parseThreshold(data){
    var key;
    if(!window.currentData.Threshold)
        window.currentData.Threshold = {};

    if(data['parameters']['thresholds']){
        for(key in data['parameters']['thresholds']){
            window.currentData.Threshold[key.toUpperCase().slice(0,10)] = data['parameters']['thresholds'][key];
        }
    }
}

//and again for HV:
function parseHV(data){
    var i, j;

    if(!window.currentData.HV)
        window.currentData.HV = {};

    if(!window.ODBEquipment)
        window.ODBEquipment = {};

    for(i=0; i<data.length; i++){
        window.ODBEquipment['HV-'+i] = data[i]; //keep the whole blob around for the benefit of the HV sidebar
        for(j=0; j<data[i].Settings.Names.length; j++){
            window.currentData.HV[data[i].Settings.Names[j].toUpperCase().slice(0,10)] = data[i].Variables.Measured[j];
        }
    }
}

//function to make a reasonable decision on how many decimal places to show, whether to to use
//sci. notation on an axis tick mark, where <min> and <max> are the axis minimum and maximum,
//<nTicks> is the number of tickmarks on the axis, and we are returning the label for the <n>th
//tick mark
function generateTickLabel(min, max, nTicks, n){
    var range = max - min,
        smallestPrecision = range / (nTicks-1),
        tickValue = min + (max-min)/(nTicks-1)*n;

    //tickmark needs to be labeled to enough precision to show the difference between subsequent ticks:
    smallestPrecision = Math.floor(Math.log(smallestPrecision) / Math.log(10));

    if(smallestPrecision < 0)
        return tickValue.toFixed(-smallestPrecision)

    tickValue = Math.floor(tickValue/Math.pow(10, smallestPrecision)) * Math.pow(10, smallestPrecision);
    return tickValue+'';

}

//determine how many HV frontends are in the ODB.  Frontends must be named HV-0, HV-1, HV-2....
function detectHVcrates(MIDAS, obj){
    var xmlhttp = new XMLHttpRequest();

    //once this is all dealt with, refresh the display immediately
    xmlhttp.onreadystatechange = function(target){
        var data, nCrates = 0;

        if (this.readyState != 4) return;

        data = JSON.parse(this.responseText.slice(this.responseText.indexOf('{'), this.responseText.lastIndexOf('}')+1 ) )

        while(data['HV-'+nCrates])
            nCrates++

        target.HVcrates = nCrates;
    }.bind(xmlhttp, obj)

    xmlhttp.withCredentials = true;
    //fire async
    xmlhttp.open('GET', 'http://' + MIDAS + '/?cmd=jcopy&odb=/Equipment&encoding=json-nokeys');
    xmlhttp.send();
}<|endoftext|>


<commit_before>CKEDITOR.dialog.add("selectStyle", function (editor) {

  var columns = 4;
  var htmlContent = "";
  htmlContent += "
";
  htmlContent += "

";

  //build out HTML
  (function () {
    var i = 0;

    for (var style in editor.config.autonumberStyles) {

          //If we're at column limit, start new row
      if (i % columns === 0) {
        htmlContent += "
";
      }

      //Setup for each style item
      var styleOption = [];
      styleOption += "<input type=\"radio\" name=\"styleChooser\" style=\"text-align: center;\"" +
      "id=\" style_label_" + i + "_" + CKEDITOR.tools.getNextNumber() + "\"" +
      "value=\"" + style + "\">" + style +
      "</input>";

      htmlContent += "" +
      styleOption +
      "	";

      //end the new row
      if (i % columns === columns - 1) {
        htmlContent += "
";
      }

      //increment counter
      i++;
    }

    //padding for if last row is short
    if (i < columns - 1) {
      for (; i < columns - 1; i++) {
        htmlContent += "	";
      }
      htmlContent += "
";
    }

    htmlContent += "

";

  })();

  return {
    title: "Style Selection",
    minWidth: 400,
    minHeight: 200,
    contents: [
      {
        id: "tab-style",
        label: "test",
        title: "test tab",
        elements: [
          {
            type: "html",
            html: htmlContent
          }
        ],
        style: "width: 100%; border-collapse: separate;"
      }

    ],
    onOk: function () {
      var style = this.getElement().getDocument().findOne("input:checked").getValue();
      if (editor.execCommand("setCurrentStyle", style)) {
        editor.execCommand("reapplyStyle");
      }
    },
    onShow: function () {
      var document = this.getElement().getDocument();

      //Set current style input as active
      var elementList = document.find("input[value='" +
              editor.config.autonumberCurrentStyle + "']");
      if (elementList.count() > 0) {
        var element = elementList.getItem(0);
        element.$.checked = true;
        element.focus();
      } else {
        document.find("input").getItem(0).$.checked = true;
      }
    }
  };
});
<commit_msg>Crazy refactor, prep for images in dialog<commit_after>CKEDITOR.dialog.add("selectStyle", function (editor) {

  var columns = 4;
  var htmlContent = "";
  htmlContent += "
";
  htmlContent += "

";

  var selectedStyle;

  var markActive = function (element) {
    element.setStyles({
      color: "red"
    });
  };

  var clearSelection = function (document) {
    var elementList = document.find("*[id*=\"style_\"]");
    for (var i = 0; i < elementList.count(); i++) {
      var node = elementList.getItem(i);
      node.removeStyle("color");
    }
  };

  var onSelect = CKEDITOR.tools.addFunction(function (ev, element) {
    //eslint-disable-next-line new-cap
    ev = new CKEDITOR.dom.event(ev);
    //eslint-disable-next-line new-cap
    element = new CKEDITOR.dom.element(element);
    selectedStyle = element.getAttribute("name");
    clearSelection(element.getDocument());
    markActive(element);
  });

  //build out HTML
  (function () {
    var i = 0;

    for (var style in editor.config.autonumberStyles) {

          //If we're at column limit, start new row
      if (i % columns === 0) {
        htmlContent += "
";
      }

      //Setup for each style item
      var styleOption = [];
      styleOption += "
" + style +
      "
";

      htmlContent += "" +
      styleOption +
      "	";

      //end the new row
      if (i % columns === columns - 1) {
        htmlContent += "
";
      }

      //increment counter
      i++;
    }

    //padding for if last row is short
    if (i < columns - 1) {
      for (; i < columns - 1; i++) {
        htmlContent += "	";
      }
      htmlContent += "
";
    }

    htmlContent += "

";

  })();

  return {
    title: "Style Selection",
    minWidth: 400,
    minHeight: 200,
    contents: [
      {
        id: "tab-style",
        label: "test",
        title: "test tab",
        elements: [
          {
            type: "html",
            html: htmlContent
          }
        ],
        style: "width: 100%; border-collapse: separate;"
      }

    ],
    onOk: function () {
      if (editor.execCommand("setCurrentStyle", selectedStyle)) {
        editor.execCommand("reapplyStyle");
      }
    },
    onShow: function () {
      var document = this.getElement().getDocument();

      clearSelection(document);

      //Set current style as active
      var element = document.findOne("*[name='" +
          editor.config.autonumberCurrentStyle + "']");

      if (element) {
        markActive(element);
      }
    }
  };
});
<|endoftext|>


<commit_before>#!/usr/bin/env node
// ---------------------------------------------------------------------------------------------------------------------
// This script is intended to execute all unit tests.
// ---------------------------------------------------------------------------------------------------------------------
/* eslint-env node, es6 */
// ---------------------------------------------------------------------------------------------------------------------
// This script is intended to execute all unit tests.
// ---------------------------------------------------------------------------------------------------------------------

require('shelljs/global');

// set directories and files for test and coverage report
var path = require('path'),

    NYC = require('nyc'),
    chalk = require('chalk'),
    recursive = require('recursive-readdir'),

    COV_REPORT_PATH = '.coverage',
    SPEC_SOURCE_DIR = path.join(__dirname, '..', 'test', 'unit');

module.exports = function (exit) {
    // banner line
    console.info(chalk.yellow.bold('Running unit tests using mocha on node...'));

    test('-d', COV_REPORT_PATH) && rm('-rf', COV_REPORT_PATH);
    mkdir('-p', COV_REPORT_PATH);

    var Mocha = require('mocha'),
        nyc = new NYC({
            reportDir: COV_REPORT_PATH,
            tempDirectory: COV_REPORT_PATH,
            reporter: ['text', 'lcov', 'text-summary']
        });

    nyc.wrap();
    // add all spec files to mocha
    recursive(SPEC_SOURCE_DIR, function (err, files) {
        if (err) {
            console.error(err);

            return exit(1);
        }

        var mocha = new Mocha({ timeout: 1000 * 60 });

        files.filter(function (file) { // extract all test files
            return (file.substr(-8) === '.test.js');
        }).forEach(mocha.addFile.bind(mocha));

        return mocha.run(function (runError) {
            runError && console.error(runError.stack || runError);

            nyc.writeCoverageFile();
            nyc.report();
            nyc.checkCoverage({
                statements: 90,
                branches: 90,
                functions: 90,
                lines: 90
            });

            exit(process.exitCode || runError ? 1 : 0);
        });
    });
};

// ensure we run this script exports if this is a direct stdin.tty run
!module.parent && module.exports(exit);
<commit_msg>Fixed broken builds :white_check_mark:<commit_after>#!/usr/bin/env node
// ---------------------------------------------------------------------------------------------------------------------
// This script is intended to execute all unit tests.
// ---------------------------------------------------------------------------------------------------------------------
/* eslint-env node, es6 */
// ---------------------------------------------------------------------------------------------------------------------
// This script is intended to execute all unit tests.
// ---------------------------------------------------------------------------------------------------------------------

require('shelljs/global');

// set directories and files for test and coverage report
var path = require('path'),

    NYC = require('nyc'),
    chalk = require('chalk'),
    recursive = require('recursive-readdir'),

    COV_REPORT_PATH = '.coverage',
    SPEC_SOURCE_DIR = path.join(__dirname, '..', 'test', 'unit');

module.exports = function (exit) {
    // banner line
    console.info(chalk.yellow.bold('Running unit tests using mocha on node...'));

    test('-d', COV_REPORT_PATH) && rm('-rf', COV_REPORT_PATH);
    mkdir('-p', COV_REPORT_PATH);

    var Mocha = require('mocha'),
        nyc = new NYC({
            hookRequire: true,
            reportDir: COV_REPORT_PATH,
            tempDirectory: COV_REPORT_PATH,
            reporter: ['text', 'lcov', 'text-summary']
        });

    nyc.wrap();
    // add all spec files to mocha
    recursive(SPEC_SOURCE_DIR, function (err, files) {
        if (err) {
            console.error(err);

            return exit(1);
        }

        var mocha = new Mocha({ timeout: 1000 * 60 });

        files.filter(function (file) { // extract all test files
            return (file.substr(-8) === '.test.js');
        }).forEach(mocha.addFile.bind(mocha));

        return mocha.run(function (runError) {
            runError && console.error(runError.stack || runError);

            nyc.reset();
            nyc.writeCoverageFile();
            nyc.report();
            nyc.checkCoverage({
                statements: 90,
                branches: 90,
                functions: 90,
                lines: 90
            });

            exit(process.exitCode || runError ? 1 : 0);
        });
    });
};

// ensure we run this script exports if this is a direct stdin.tty run
!module.parent && module.exports(exit);
<|endoftext|>


<commit_before>'use strict';

// Load the module dependencies
var config = require('../config'),
    path = require('path'),
    fs = require('fs'),
    http = require('http'),
    https = require('https'),
    cookieParser = require('cookie-parser'),
    passport = require('passport'),
    socketio = require('socket.io'),
    session = require('express-session'),
    MongoStore = require('connect-mongo')(session);
var global = require('./global');
var coreController = require('../../modules/core/server/controllers/core.server.controller');
// Define the Socket.io configuration method
module.exports = function (app, db) {
    var server;
    if (config.secure && config.secure.ssl === true) {
        // Load SSL key and certificate
        var privateKey = fs.readFileSync(path.resolve(config.secure.privateKey), 'utf8');
        var certificate = fs.readFileSync(path.resolve(config.secure.certificate), 'utf8');
        var options = {
            key: privateKey,
            cert: certificate,
            //  requestCert : true,
            //  rejectUnauthorized : true,
            secureProtocol: 'TLSv1_method',
            ciphers: [
                'ECDHE-RSA-AES128-GCM-SHA256',
                'ECDHE-ECDSA-AES128-GCM-SHA256',
                'ECDHE-RSA-AES256-GCM-SHA384',
                'ECDHE-ECDSA-AES256-GCM-SHA384',
                'DHE-RSA-AES128-GCM-SHA256',
                'ECDHE-RSA-AES128-SHA256',
                'DHE-RSA-AES128-SHA256',
                'ECDHE-RSA-AES256-SHA384',
                'DHE-RSA-AES256-SHA384',
                'ECDHE-RSA-AES256-SHA256',
                'DHE-RSA-AES256-SHA256',
                'HIGH',
                '!aNULL',
                '!eNULL',
                '!EXPORT',
                '!DES',
                '!RC4',
                '!MD5',
                '!PSK',
                '!SRP',
                '!CAMELLIA'
            ].join(':'),
            honorCipherOrder: true
        };

        // Create new HTTPS Server
        server = https.createServer(options, app);

    } else {
        // Create a new HTTP server
        server = http.createServer(app);
    }
    //server.listen(3000);
    // Create a new Socket.io server

    var io = socketio.listen(server,{origins:'*:*'});
    //io.set('origins', 'https://noti8.herokuapp.com');
    //io.set('origins', '*');
    // io.origins('*');

    global.io = io;
    // Create a MongoDB storage object
    var mongoStore = new MongoStore({
        mongooseConnection: db.connection,
        collection: config.sessionCollection
    });

    // Intercept Socket.io's handshake request
    io.use(function (socket, next) {
        // Use the 'cookie-parser' module to parse the request cookies
        cookieParser(config.sessionSecret)(socket.request, {}, function (err) {
            // Get the session id from the request cookies
            var sessionId = socket.request.signedCookies ? socket.request.signedCookies[config.sessionKey] : undefined;

            if (!sessionId) return next(new Error('sessionId was not found in socket.request'), false);

            // Use the mongoStorage instance to get the Express session information
            mongoStore.get(sessionId, function (err, session) {
                if (err) return next(err, false);
                if (!session) return next(new Error('session was not found for ' + sessionId), false);

                // Set the Socket.io session information
                socket.request.session = session;

                // Use Passport to populate the user details
                passport.initialize()(socket.request, {}, function () {
                    passport.session()(socket.request, {}, function () {
                        if (socket.request.user) {
                            next(null, true);
                        } else {
                            next(new Error('User is not authenticated'), false);
                        }
                    });
                });
            });
        });
    });

    // Add an event listener to the 'connection' event
    io.on('connection', function (socket) {
        config.files.server.sockets.forEach(function (socketConfiguration) {
            require(path.resolve(socketConfiguration))(io, socket);
        });
    });

    return server;
};
<commit_msg>dfdsf<commit_after>'use strict';

// Load the module dependencies
var config = require('../config'),
    path = require('path'),
    fs = require('fs'),
    http = require('http'),
    https = require('https'),
    cookieParser = require('cookie-parser'),
    passport = require('passport'),
    socketio = require('socket.io'),
    session = require('express-session'),
    MongoStore = require('connect-mongo')(session);
var global = require('./global');
var coreController = require('../../modules/core/server/controllers/core.server.controller');
// Define the Socket.io configuration method
module.exports = function (app, db) {
    var server;
    if (config.secure && config.secure.ssl === true) {
        // Load SSL key and certificate
        var privateKey = fs.readFileSync(path.resolve(config.secure.privateKey), 'utf8');
        var certificate = fs.readFileSync(path.resolve(config.secure.certificate), 'utf8');
        var options = {
            key: privateKey,
            cert: certificate,
            //  requestCert : true,
            //  rejectUnauthorized : true,
            secureProtocol: 'TLSv1_method',
            ciphers: [
                'ECDHE-RSA-AES128-GCM-SHA256',
                'ECDHE-ECDSA-AES128-GCM-SHA256',
                'ECDHE-RSA-AES256-GCM-SHA384',
                'ECDHE-ECDSA-AES256-GCM-SHA384',
                'DHE-RSA-AES128-GCM-SHA256',
                'ECDHE-RSA-AES128-SHA256',
                'DHE-RSA-AES128-SHA256',
                'ECDHE-RSA-AES256-SHA384',
                'DHE-RSA-AES256-SHA384',
                'ECDHE-RSA-AES256-SHA256',
                'DHE-RSA-AES256-SHA256',
                'HIGH',
                '!aNULL',
                '!eNULL',
                '!EXPORT',
                '!DES',
                '!RC4',
                '!MD5',
                '!PSK',
                '!SRP',
                '!CAMELLIA'
            ].join(':'),
            honorCipherOrder: true
        };

        // Create new HTTPS Server
        server = https.createServer(options, app);

    } else {
        // Create a new HTTP server
        server = http.createServer(app);
    }
    //server.listen(3000);
    // Create a new Socket.io server

    var io = socketio.listen(server,{origins:'*:*'});
    //io.set('origins', 'https://noti8.herokuapp.com');
    //io.set('origins', '*');
    // io.origins('*');

    global.io = io;
    // Create a MongoDB storage object
    var mongoStore = new MongoStore({
        mongooseConnection: db.connection,
        collection: config.sessionCollection
    });

    // Intercept Socket.io's handshake request
    io.use(function (socket, next) {
        // Use the 'cookie-parser' module to parse the request cookies
        cookieParser(config.sessionSecret)(socket.request, {}, function (err) {
            // Get the session id from the request cookies
            var sessionId = socket.request.signedCookies ? socket.request.signedCookies[config.sessionKey] : undefined;

            if (!sessionId) return next(new Error('sessionId was not found in socket.request'), false);

            // Use the mongoStorage instance to get the Express session information
            mongoStore.get(sessionId, function (err, session) {
                if (err) return next(err, false);
                if (!session) return next(new Error('session was not found for ' + sessionId), false);

                // Set the Socket.io session information
                socket.request.session = session;

                // Use Passport to populate the user details
                passport.initialize()(socket.request, {}, function () {
                    passport.session()(socket.request, {}, function () {
                        if (socket.request.user) {
                            next(null, true);
                        } else {
                            next(new Error('User is not authenticated'), false);
                        }
                    });
                });
            });
        });
    });

    // Add an event listener to the 'connection' event
    io.on('connection', function (socket) {
        config.files.server.sockets.forEach(function (socketConfiguration) {
            require(path.resolve(socketConfiguration))(io, socket);
        });
    });

    return server;
};
<|endoftext|>


<commit_before>/**
 * Wraps a DataChannel between two Peers.
 */
function DataConnection(peer, provider, options) {
  if (!(this instanceof DataConnection)) return new DataConnection(peer, provider, options);
  EventEmitter.call(this);

  // TODO: perhaps default serialization should be binary-utf8?
  this.options = util.extend({
    serialization: 'binary'
  }, options);

  // Connection is not open yet.
  this.open = false;
  this.type = 'data';
  this.peer = peer;
  this.provider = provider;

  this.id = this.options.connectionId || DataConnection._idPrefix + util.randomToken();

  this.label = this.options.label || this.id;
  this.metadata = this.options.metadata;
  this.serialization = this.options.serialization;
  this.reliable = this.options.reliable;

  Negotiator.startConnection(
    this,
    this.options._payload || {
      originator: true
    }
  );
}

util.inherits(DataConnection, EventEmitter);

DataConnection._idPrefix = 'dc_';

/** Called by the Negotiator when the DataChannel is ready. */
DataConnection.prototype.initialize = function(dc) {
  this._dc = dc;
  this._configureDataChannel();
}

DataConnection.prototype._configureDataChannel = function() {
  var self = this;
  if (util.supports.binary) {
    // Webkit doesn't support binary yet
    this._dc.binaryType = 'arraybuffer';
  }
  this._dc.onopen = function() {
    util.log('Data channel connection success');
    self.open = true;
    self.emit('open');
  }

  // Use the Reliable shim for non Firefox browsers
  if (!util.supports.reliable) {
    this._reliable = new Reliable(this._dc, util.debug);
  }

  if (this._reliable) {
    this._reliable.onmessage = function(msg) {
      self.emit('data', msg);
    };
  } else {
    this._dc.onmessage = function(e) {
      self._handleDataMessage(e);
    };
  }
  this._dc.onclose = function(e) {
    util.log('DataChannel closed for:', self.peer);
    self.close();
  };
}

// Handles a DataChannel message.
DataConnection.prototype._handleDataMessage = function(e) {
  var self = this;
  var data = e.data;
  var datatype = data.constructor;
  if (this.serialization === 'binary' || this.serialization === 'binary-utf8') {
    if (datatype === Blob) {
      // Datatype should never be blob
      util.blobToArrayBuffer(data, function(ab) {
        data = util.unpack(ab);
        self.emit('data', data);
      });
      return;
    } else if (datatype === ArrayBuffer) {
      data = util.unpack(data);
    } else if (datatype === String) {
      // String fallback for binary data for browsers that don't support binary yet
      var ab = util.binaryStringToArrayBuffer(data);
      data = util.unpack(ab);
    }
  } else if (this.serialization === 'json') {
    data = JSON.parse(data);
  }
  this.emit('data', data);
}

/**
 * Exposed functionality for users.
 */

/** Allows user to close connection. */
DataConnection.prototype.close = function() {
  if (!this.open) {
    return;
  }
  this.open = false;
  Negotiator.cleanup(this);
  this.emit('close');
}

/** Allows user to send data. */
DataConnection.prototype.send = function(data) {
  if (!this.open) {
    this.emit('error', new Error('Connection is not open. You should listen for the `open` event before sending messages.'));
  }
  if (this._reliable) {
    // Note: reliable shim sending will make it so that you cannot customize
    // serialization.
    this._reliable.send(data);
    return;
  }
  var self = this;
  if (this.serialization === 'none') {
    this._dc.send(data);
  } else if (this.serialization === 'json') {
    this._dc.send(JSON.stringify(data));
  } else {
    var utf8 = (this.serialization === 'binary-utf8');
    var blob = util.pack(data, utf8);
    // DataChannel currently only supports strings.
    if (!util.supports.binary) {
      util.blobToBinaryString(blob, function(str){
        self._dc.send(str);
      });
    } else {
      this._dc.send(blob);
    }
  }
}

DataConnection.prototype.handleMessage = function(message) {
  var payload = message.payload;

  switch (message.type) {
    case 'ANSWER':
      // Forward to negotiator
      Negotiator.handleSDP(message.type, this, payload.sdp);
      break;
    case 'CANDIDATE':
      Negotiator.handleCandidate(this, payload.candidate);
      break;
    default:
      util.warn('Unrecognized message type:', message.type, 'from peer:', this.peer);
      break;
  }
}
<commit_msg>default should be none to match message parsing<commit_after>/**
 * Wraps a DataChannel between two Peers.
 */
function DataConnection(peer, provider, options) {
  if (!(this instanceof DataConnection)) return new DataConnection(peer, provider, options);
  EventEmitter.call(this);

  // TODO: perhaps default serialization should be binary-utf8?
  this.options = util.extend({
    serialization: 'binary'
  }, options);

  // Connection is not open yet.
  this.open = false;
  this.type = 'data';
  this.peer = peer;
  this.provider = provider;

  this.id = this.options.connectionId || DataConnection._idPrefix + util.randomToken();

  this.label = this.options.label || this.id;
  this.metadata = this.options.metadata;
  this.serialization = this.options.serialization;
  this.reliable = this.options.reliable;

  Negotiator.startConnection(
    this,
    this.options._payload || {
      originator: true
    }
  );
}

util.inherits(DataConnection, EventEmitter);

DataConnection._idPrefix = 'dc_';

/** Called by the Negotiator when the DataChannel is ready. */
DataConnection.prototype.initialize = function(dc) {
  this._dc = dc;
  this._configureDataChannel();
}

DataConnection.prototype._configureDataChannel = function() {
  var self = this;
  if (util.supports.binary) {
    // Webkit doesn't support binary yet
    this._dc.binaryType = 'arraybuffer';
  }
  this._dc.onopen = function() {
    util.log('Data channel connection success');
    self.open = true;
    self.emit('open');
  }

  // Use the Reliable shim for non Firefox browsers
  if (!util.supports.reliable) {
    this._reliable = new Reliable(this._dc, util.debug);
  }

  if (this._reliable) {
    this._reliable.onmessage = function(msg) {
      self.emit('data', msg);
    };
  } else {
    this._dc.onmessage = function(e) {
      self._handleDataMessage(e);
    };
  }
  this._dc.onclose = function(e) {
    util.log('DataChannel closed for:', self.peer);
    self.close();
  };
}

// Handles a DataChannel message.
DataConnection.prototype._handleDataMessage = function(e) {
  var self = this;
  var data = e.data;
  var datatype = data.constructor;
  if (this.serialization === 'binary' || this.serialization === 'binary-utf8') {
    if (datatype === Blob) {
      // Datatype should never be blob
      util.blobToArrayBuffer(data, function(ab) {
        data = util.unpack(ab);
        self.emit('data', data);
      });
      return;
    } else if (datatype === ArrayBuffer) {
      data = util.unpack(data);
    } else if (datatype === String) {
      // String fallback for binary data for browsers that don't support binary yet
      var ab = util.binaryStringToArrayBuffer(data);
      data = util.unpack(ab);
    }
  } else if (this.serialization === 'json') {
    data = JSON.parse(data);
  }
  this.emit('data', data);
}

/**
 * Exposed functionality for users.
 */

/** Allows user to close connection. */
DataConnection.prototype.close = function() {
  if (!this.open) {
    return;
  }
  this.open = false;
  Negotiator.cleanup(this);
  this.emit('close');
}

/** Allows user to send data. */
DataConnection.prototype.send = function(data) {
  if (!this.open) {
    this.emit('error', new Error('Connection is not open. You should listen for the `open` event before sending messages.'));
  }
  if (this._reliable) {
    // Note: reliable shim sending will make it so that you cannot customize
    // serialization.
    this._reliable.send(data);
    return;
  }
  var self = this;
  if (this.serialization === 'json') {
    this._dc.send(JSON.stringify(data));
  } else if ('binary-utf8'.indexOf(this.serialization) !== -1) {
    var utf8 = (this.serialization === 'binary-utf8');
    var blob = util.pack(data, utf8);
    // DataChannel currently only supports strings.
    if (!util.supports.binary) {
      util.blobToBinaryString(blob, function(str){
        self._dc.send(str);
      });
    } else {
      this._dc.send(blob);
    }
  } else {
    this._dc.send(data);
  }
}

DataConnection.prototype.handleMessage = function(message) {
  var payload = message.payload;

  switch (message.type) {
    case 'ANSWER':
      // Forward to negotiator
      Negotiator.handleSDP(message.type, this, payload.sdp);
      break;
    case 'CANDIDATE':
      Negotiator.handleCandidate(this, payload.candidate);
      break;
    default:
      util.warn('Unrecognized message type:', message.type, 'from peer:', this.peer);
      break;
  }
}
<|endoftext|>


<commit_before>import $ from './vendor/jquery-3.2.1.min';

// import readInput from './modules/readInput';
import shortenTabUrl from './modules/shortenTabUrl';
import './vendor/jquery.qrcode.min';
import printPalette from './modules/getPalette';
import { storeColorPickerData, printNewHistoryColor, printHistoryColor } from './modules/colorHistory';
// readInput();
shortenTabUrl();

$(document).ready(function() {
    printHistoryColor(onColorClick);
});

$("#colorPicker").on("change", function(e) {
    var selectedColor = e.currentTarget.value;
    storeColorPickerData(selectedColor);
    printNewHistoryColor(selectedColor, onColorClick);
    printPalette(selectedColor.substring(1));
});

function onColorClick(selectedColor) {
    printPalette(selectedColor.substring(1));
}<commit_msg>Update App.js<commit_after>import $ from './vendor/jquery-3.2.1.min';

import shortenTabUrl from './modules/shortenTabUrl';
import './vendor/jquery.qrcode.min';
import printPalette from './modules/getPalette';
import { storeColorPickerData, printNewHistoryColor, printHistoryColor } from './modules/colorHistory';
shortenTabUrl();

$(document).ready(function() {
    printHistoryColor(onColorClick);
});

$("#colorPicker").on("change", function(e) {
    var selectedColor = e.currentTarget.value;
    storeColorPickerData(selectedColor);
    printNewHistoryColor(selectedColor, onColorClick);
    printPalette(selectedColor.substring(1));
});

function onColorClick(selectedColor) {
    printPalette(selectedColor.substring(1));
}
<|endoftext|>


<commit_before>define([
  'extensions/views/table',
  'modernizr'
],
function (TableView, Modernizr) {

  return TableView.extend({

    initialize: function () {
      this.$table = this.$('table');

      this.tableCollection = new this.collection.constructor(this.collection.models, this.collection.options);

      this.sort();

      this.listenTo(this.tableCollection, 'sort', this.renderSort);
      this.listenTo(this.tableCollection, 'reset', this.renderSort);

      this.listenTo(this.collection, 'sync', this.syncToTableCollection);

      this.listenTo(this.model, 'change:sort-by change:sort-order', function () { this.sort(); });

      TableView.prototype.initialize.apply(this, arguments);
    },

    events: {
      'click th a': 'sortCol'
    },

    syncToTableCollection: function () {
      this.tableCollection.reset(this.collection.toJSON());
    },

    renderSort: function () {
      if (this.$table.find('tbody')) {
        this.$table.find('tbody').remove();
      }
      $(this.renderBody(this.tableCollection)).appendTo(this.$table);

      var sortBy = this.model.get('sort-by'),
        sortOrder = this.model.get('sort-order');

      if (sortBy) {
        var ths = this.$('thead th'),
          th = this.$('thead th[data-key="' + sortBy + '"]');

        ths.removeClass('asc');
        ths.removeClass('desc');
        ths.attr('aria-sort', 'none');

        if (sortOrder === 'descending') {
          th.addClass('desc');
          th.attr('aria-sort', 'descending');
        } else {
          th.addClass('asc');
          th.attr('aria-sort', 'ascending');
        }
      }

      this.render();
    },

    sortCol: function (e) {
      e.preventDefault();

      var th = $(e.target).parent(),
        sorted = this.model.get('sort-by'),
        isDescending = this.model.get('sort-order') === 'descending',
        sortBy = th.attr('data-key');

      if (_.isArray(sortBy)) {
        sortBy = sortBy[0];
      }

      if (sorted === sortBy) {
        this.model.set('sort-order', isDescending ? 'ascending' : 'descending');
      } else {
        this.model.set({
          'sort-order': 'descending',
          'sort-by': sortBy
        });
      }
    },

    sort: function (sortBy, sortOrder) {

      sortBy = sortBy || this.model.get('sort-by');
      sortOrder = sortOrder || this.model.get('sort-order');

      this.tableCollection.comparator = function (a, b) {
        var firstVal = a.get(sortBy),
          firstTime = a.get('_timestamp') || a.get('_start_at'),
          secondVal = b.get(sortBy),
          secondTime = b.get('_timestamp') || b.get('_start_at'),
          nullValues = (firstVal !== null || secondVal !== null),
          ret;

        if (nullValues && firstVal < secondVal) {
          ret = -1;
        } else if (nullValues && firstVal > secondVal) {
          ret = 1;
        } else {
          if (nullValues) {
            if (firstVal === null) {
              ret = -1;
            }
            if (secondVal === null) {
              ret = 1;
            }
          } else {
            if (firstTime < secondTime) {
              ret = -1;
            } else if (firstTime > secondTime) {
              ret = 1;
            } else {
              ret = 0;
            }
          }
        }

        if (sortOrder === 'descending') {
          ret = -ret;
        }
        return ret;
      };

      this.tableCollection.sort();
    },

    render: function () {
      if (Modernizr.touch) {
        this.$table.addClass('touch-table');
      }

      this.$table.removeClass('floated-header');
      var headers = this.$table.find('thead th'),
          headerLinks = this.$table.find('thead th a'),
          body = this.$table.find('tbody td');

      headers.attr('width', '');
      if (headerLinks.length === 0) {
        headers.each(function () {
          $(this).attr('aria-sort', 'none');
          $(this).wrapInner('');
        });
      }
      body.attr('width', '');

      if (body.length > headers.length) {
        _.each(headers, function (th, index) {
          th.width = th.offsetWidth;
          body[index].width = th.offsetWidth;
        }, this);
        this.$('table').addClass('floated-header');
      }

    }

  });

});<commit_msg>Handle the case where no sort order is defined on model<commit_after>define([
  'extensions/views/table',
  'modernizr'
],
function (TableView, Modernizr) {

  return TableView.extend({

    initialize: function () {
      this.$table = this.$('table');

      this.tableCollection = new this.collection.constructor(this.collection.models, this.collection.options);

      this.sort();

      this.listenTo(this.tableCollection, 'sort', this.renderSort);
      this.listenTo(this.tableCollection, 'reset', this.renderSort);

      this.listenTo(this.collection, 'sync', this.syncToTableCollection);

      this.listenTo(this.model, 'change:sort-by change:sort-order', function () { this.sort(); });

      TableView.prototype.initialize.apply(this, arguments);
    },

    events: {
      'click th a': 'sortCol'
    },

    syncToTableCollection: function () {
      this.tableCollection.reset(this.collection.toJSON());
    },

    renderSort: function () {
      if (this.$table.find('tbody')) {
        this.$table.find('tbody').remove();
      }
      $(this.renderBody(this.tableCollection)).appendTo(this.$table);

      var sortBy = this.model.get('sort-by'),
        sortOrder = this.model.get('sort-order');

      if (sortBy) {
        var ths = this.$('thead th'),
          th = this.$('thead th[data-key="' + sortBy + '"]');

        ths.removeClass('asc');
        ths.removeClass('desc');
        ths.attr('aria-sort', 'none');

        if (sortOrder === 'descending') {
          th.addClass('desc');
          th.attr('aria-sort', 'descending');
        } else {
          th.addClass('asc');
          th.attr('aria-sort', 'ascending');
        }
      }

      this.render();
    },

    sortCol: function (e) {
      e.preventDefault();

      var th = $(e.target).parent(),
        sorted = this.model.get('sort-by'),
        isDescending = this.model.get('sort-order') === 'descending',
        sortBy = th.attr('data-key');

      if (_.isArray(sortBy)) {
        sortBy = sortBy[0];
      }

      if (sorted === sortBy) {
        this.model.set('sort-order', isDescending ? 'ascending' : 'descending');
      } else {
        this.model.set({
          'sort-order': 'descending',
          'sort-by': sortBy
        });
      }
    },

    sort: function (sortBy, sortOrder) {

      sortBy = sortBy || this.model.get('sort-by');
      sortOrder = sortOrder || this.model.get('sort-order') || 'descending';

      if (!sortBy) {
        return;
      }

      this.tableCollection.comparator = function (a, b) {
        var firstVal = a.get(sortBy),
          firstTime = a.get('_timestamp') || a.get('_start_at'),
          secondVal = b.get(sortBy),
          secondTime = b.get('_timestamp') || b.get('_start_at'),
          nullValues = (firstVal !== null || secondVal !== null),
          ret;

        if (nullValues && firstVal < secondVal) {
          ret = -1;
        } else if (nullValues && firstVal > secondVal) {
          ret = 1;
        } else {
          if (nullValues) {
            if (firstVal === null) {
              ret = -1;
            }
            if (secondVal === null) {
              ret = 1;
            }
          } else {
            if (firstTime < secondTime) {
              ret = -1;
            } else if (firstTime > secondTime) {
              ret = 1;
            } else {
              ret = 0;
            }
          }
        }

        if (sortOrder === 'descending') {
          ret = -ret;
        }
        return ret;
      };

      this.tableCollection.sort();
    },

    render: function () {
      if (Modernizr.touch) {
        this.$table.addClass('touch-table');
      }

      this.$table.removeClass('floated-header');
      var headers = this.$table.find('thead th'),
          headerLinks = this.$table.find('thead th a'),
          body = this.$table.find('tbody td');

      headers.attr('width', '');
      if (headerLinks.length === 0) {
        headers.each(function () {
          $(this).attr('aria-sort', 'none');
          $(this).wrapInner('');
        });
      }
      body.attr('width', '');

      if (body.length > headers.length) {
        _.each(headers, function (th, index) {
          th.width = th.offsetWidth;
          body[index].width = th.offsetWidth;
        }, this);
        this.$('table').addClass('floated-header');
      }

    }

  });

});<|endoftext|>