<commit_before>/*eslint-env mocha*/
/*global assert*/ // chai

define(['BlinkForms', 'BIC'], function (Forms) {

  var originalOptions = { a: 'alpha', b: 'beta', g: 'gamma' };
  var choiceElements = [ 'selectc', 'selecte', 'multic', 'multie', 'multif',
      'selectf', 'multig', 'selecth' ];
  var multiWithOther = [ 'multic', 'multie', 'multif', 'multig' ];
  var collapsed = [ 'selectc', 'multic', 'selectf' ];

  suite('2: options', function () {
    var $page = $('[data-role=page]'),
      $content = $page.find('[data-role=content]');

    /**
     * execute once before everything else in this suite
     */
    suiteSetup(function () {
      $content.empty();
      delete Forms.current;
    });

    suite('Form', function () {

      test('BlinkForms global is an Object', function () {
        assert($.isPlainObject(Forms), 'BlinkForms is a JavaScript object');
      });

      test('initialise with form.json', function (done) {
        var form;

        Forms.getDefinition('form1', 'add').then(function (def) {
          Forms.initialize(def);
          form = Forms.current;
          assert.equal($.type(form), 'object');
          assert.equal(form.get('name'), 'form1');
          assert.equal(form.get('label'), 'Form 1');
          done();
        }, function () {
          assert.fail(true, false, 'getDefinition failed!');
          done();
        });
      });

      test('render form for jQuery Mobile', function () {
        var form = Forms.current;

        $content.append(form.$form);

        $.mobile.page({}, $page);
        $page.trigger('pagecreate');
        $page.show();
      });

      test('select-1 collapsed API -> UI bindings', function () {
        var form = Forms.current,
          element = form.getElement('selectc'),
          $fieldset = element.attributes._view.$el,
          $span = $fieldset.find('.ui-btn-text'),
          $select = $fieldset.find('select'),
          $other;

        element.val('');
        $other = $fieldset.find('input[type = text]');
        assert.equal($select.val(), 'select one...', 'form element value is "select one"..."');
        assert.equal($other.length, 0, 'other box absent');

        element.val('a');
        $other = $fieldset.find('input[type = text]');
        assert.equal($select.val(), 'a', 'form element value = "a"');
        assert.equal($span.text(), 'alpha', 'jQM shows "alpha"');
        assert.equal($other.length, 0, 'other box absent');

        element.val('b');
        $other = $fieldset.find('input[type = text]');
        assert.equal($select.val(), 'b', 'form element value = "b"');
        assert.equal($span.text(), 'beta', 'jQM shows "beta"');
        assert.equal($other.length, 0, 'other box absent');

        element.val('other');
        $other = $fieldset.find('input[type = text]');
        assert.equal($select.val(), 'other', 'form element value = "other"');
        assert.equal($other.length, 1, 'other box exists');
        assert.equal($other.val(), '', 'other box is empty');

        // Include this test here to make the other box dissapear
        // Then test what happens when an 'other' value is directly inserted
        // This pattern repeated in all test suites below for coverage
        element.val('');
        $other = $fieldset.find('input[type = text]');
        assert.equal($select.val(), 'select one...', 'form element value is "select one"..."');
        assert.equal($other.length, 0, 'other box absent');

        element.val('cat');
        $other = $fieldset.find('input[type = text]');
        assert.equal($select.val(), 'other', 'form element value = "other"');
        assert.equal($other.length, 1, 'other box exists');
        assert.equal($other.val(), 'cat', 'other box is has value "cat"');
      });

      test('select-1 collapsed UI -> API bindings');

      test('select-1 expanded API -> UI bindings', function () {
        var form = Forms.current,
        element = form.getElement('selecte'),
        $fieldset = element.attributes._view.$el,
        $selected,
        $other;

        element.val('');
        $selected = $fieldset.find('input[type = radio]:checked');
        $other = $fieldset.find('input[type = text]');
        assert.equal($selected.length, 0, 'no form element selected');
        assert.equal($other.length, 0, 'other box absent');

        element.val('a');
        $selected = $fieldset.find('input[type = radio]:checked').val();
        $other = $fieldset.find('input[type = text]');
        assert.equal($selected, 'a', 'form element value = "a"');
        assert.equal($other.length, 0, 'other box absent');

        element.val('b');
        $selected = $fieldset.find('input[type = radio]:checked').val();
        $other = $fieldset.find('input[type = text]');
        assert.equal($selected, 'b', 'form element value = "b"');
        assert.equal($other.length, 0, 'other box absent');

        element.val('other');
        $selected = $fieldset.find('input[type = radio]:checked').val();
        $other = $fieldset.find('input[type = text]');
        assert.equal($selected, 'other', 'form element value = "other"');
        assert.equal($other.length, 1, 'other box exists');
        assert.equal($other.val(), '', 'other box is empty');

        element.val('');
        $selected = $fieldset.find('input[type = radio]:checked');
        $other = $fieldset.find('input[type = text]');
        assert.equal($selected.length, 0, 'no form element selected');
        assert.equal($other.length, 0, 'other box absent');

        element.val('cat');
        $selected = $fieldset.find('input[type = radio]:checked').val();
        $other = $fieldset.find('input[type = text]');
        assert.equal($selected, 'other', 'form element value = "other"');
        assert.equal($other.length, 1, 'other box exists');
        assert.equal($other.val(), 'cat', 'other box is "cat"');
      });

      test('select-1 expanded UI -> API bindings', function () {
        // UI -> API bindings
        var form = Forms.current,
        element = form.getElement('selecte'),
        $fieldset = element.attributes._view.$el;

        $fieldset.find('label:contains(gamma)').trigger('click');
        assert.equal(element.val(), 'g', 'model.value = "g"');
      });

      test('multi+other bindings between model and "other" input', function () {
        var form = Forms.current;
        multiWithOther.forEach(function (name) {
          var value;
          var element = form.getElement(name);
          var $view = element.get('_view').$el;
          var $other;
          var $checked;

          element.val('');
          $other = $view.find('input[type=text]');
          $checked = $view.find(':checked');
          assert.notOk(element.val(), name + ': model value is falsey');
          assert.lengthOf($checked, 0, name + ': nothing checked');
          assert.lengthOf($other, 0, name + ': other box absent');

          value = ['a'];
          element.val(value);
          $other = $view.find('input[type=text]');
          $checked = $view.find(':checked');
          assert.deepEqual(element.val(), value, name + ': model value correct');
          assert.lengthOf($checked, 1, name + ': 1 checked');
          assert.lengthOf($other, 0, name + ': other box absent');
          assert.deepEqual($checked.map(function () {
            return $(this).val();
          }).get(), value, name + ': model value correct');

          value = ['a', 'b'];
          element.val(value);
          $other = $view.find('input[type=text]');
          $checked = $view.find(':checked');
          assert.deepEqual(element.val(), value, name + ': model value correct');
          assert.lengthOf($checked, 2, name + ': 2 checked');
          assert.lengthOf($other, 0, name + ': other box absent');
          assert.deepEqual($checked.map(function () {
            return $(this).val();
          }).get(), value, name + ': model value correct');

          value = [''];
          element.val(value);
          $other = $view.find('input[type=text]');
          $checked = $view.find(':checked');
          assert.deepEqual(element.val(), value, name + ': model value correct');
          assert.lengthOf($checked, 1, name + ': 1 checked');
          assert.lengthOf($other, 1, name + ': other box present');
          assert.equal($other.val(), '', name + ': other box empty');

          value = ['a', ''];
          element.val(value);
          $other = $view.find('input[type=text]');
          $checked = $view.find(':checked');
          assert.deepEqual(element.val(), value, name + ': model value correct');
          assert.lengthOf($checked, 2, name + ': 2 checked');
          assert.lengthOf($other, 1, name + ': other box present');
          assert.equal($other.val(), '', name + ': other box empty');
          assert.deepEqual($checked.map(function () {
            return $(this).val();
          }).get(), ['a', 'other'], name + ': model value correct');

          value = ['123'];
          element.val(value);
          $other = $view.find('input[type=text]');
          $checked = $view.find(':checked');
          assert.deepEqual(element.val(), value, name + ': model value correct');
          assert.lengthOf($checked, 1, name + ': 1 checked');
          assert.lengthOf($other, 1, name + ': other box present');
          assert.equal($other.val(), '123', name + ': other box shows "123"');
          assert.deepEqual($checked.map(function () {
            return $(this).val();
          }).get(), ['other'], name + ': model value correct');

          value = ['a', '123'];
          element.val(value);
          $other = $view.find('input[type=text]');
          $checked = $view.find(':checked');
          assert.deepEqual(element.val(), value, name + ': model value correct');
          assert.lengthOf($checked, 2, name + ': 2 checked');
          assert.lengthOf($other, 1, name + ': other box present');
          assert.equal($other.val(), '123', name + ': other box shows "123"');
          assert.deepEqual($checked.map(function () {
            return $(this).val();
          }).get(), ['a', 'other'], name + ': model value correct');

          element.val('');
          $other = $view.find('input[type=text]');
          $checked = $view.find(':checked');
          assert.notOk(element.val(), name + ': model value is falsey');
          assert.lengthOf($checked, 0, name + ': nothing checked');
          assert.lengthOf($other, 0, name + ': other box absent');

          if (collapsed.indexOf(name) !== -1) {
            assert.notEqual($view.find('select').data("role"), "none", name + ': data-role is not none');
          }
        });
      });

      test('Select-F native collapsed', function () {
        var form = Forms.current,
          element = form.getElement('selectf'),
          $fieldset = element.attributes._view.$el,
          $select = $fieldset.find('select');

        element.val('');
        assert.equal(element.val(), '', 'model.value is ""');

        element.val('a');
        assert.equal(element.val(), 'a', 'model.value = "a"');

        element.val('b');
        assert.equal(element.val(), 'b', 'model.value = "b"');

        assert.equal($select.data("role"), "none", "data-role is not none");

      });

      test('Multi-F native collapsed', function () {
        var form = Forms.current,
          element = form.getElement('multif'),
          $fieldset = element.attributes._view.$el,
          $select = $fieldset.find('select');

        element.val([]);
        assert.deepEqual(element.val(), [], 'model.value is []');

        element.val(['a']);
        assert.deepEqual(element.val(), ['a'], 'model.value = ["a"]');

        element.val(['a', 'b']);
        assert.deepEqual(element.val(), ['a', 'b'], 'model.value = ["a", "b"]');

        assert.equal($select.data("role"), "none", "data-role is not none");

      });

      test('Select-H native collapsed', function () {
        var form = Forms.current,
          element = form.getElement('selecth'),
          $fieldset = element.attributes._view.$el,
          $select = $fieldset.find('select'),
          $other;

        element.val('');
        assert.equal(element.val(), '', 'model.value is ""');

        element.val('other');
        assert.equal(element.val(), 'other', 'model.value = "other"');

        element.val('other');
        $other = $fieldset.find('input[type = text]');
        $other.val("test");
        $other.change();
        assert.equal(element.val(), 'test', 'model.value = "test"');

        assert.equal($select.data("role"), "none", "data-role is not none");

      });

      test('boolean 0/1', function (done) {
        var form = Forms.current,
          element = form.getElement('boolean'),
          $fieldset = element.attributes._view.$el;

        element.val('');
        assert.equal(element.val(), '', 'model.value is ""');
        assert.equal($fieldset.find('ui-btn-active').length, 0, 'jQM is blank');

        element.val(0);
        assert.equal(element.val(), 0, 'model.value = 0');

        setTimeout(function () {
          element.val(1);
          assert.equal(element.val(), 1, 'model.value = 1');

          setTimeout(function () {
            $fieldset.find('div.ui-slider-switch').trigger('mousedown').trigger('mouseup').trigger('click');
            assert.equal(element.val(), 0, 'model.value = 0');
            done();
          }, 500);

        }, 500);

      });

      test('boolean n/y', function (done) {
        var form = Forms.current,
          element = form.getElement('question'),
          $fieldset = element.attributes._view.$el;

        element.val('');
        assert.equal(element.val(), '', 'model.value is ""');
        assert.equal($fieldset.find('ui-btn-active').length, 0, 'jQM is blank');

        element.val('n');
        assert.equal(element.val(), 'n', 'model.value = "n"');

        setTimeout(function () {
          element.val('y');
          assert.equal(element.val(), 'y', 'model.value = "y"');

          setTimeout(function () {
            $fieldset.find('div.ui-slider-switch').trigger('mousedown').trigger('mouseup').trigger('click');
            assert.equal(element.val(), 'n', 'model.value = "n"');
            done();
          }, 500);
        }, 500);

      });

      test('elements have original a|b|g options', function () {
        var form = Forms.current;
        choiceElements.forEach(function (name) {
          var element = form.getElement(name);
          var currentOptions = element.get('options');
          assert.deepEqual(currentOptions, originalOptions, name + ' ' + JSON.stringify(currentOptions));
        });
      });

    }); // END: suite('Form', ...)

  }); // END: suite('1', ...)

});

/*
TODO
SelectC ...chose one bugs out
*/
<commit_msg>FORMS-125 # further tidy up for tests<commit_after>/*eslint-env mocha*/
/*global assert*/ // chai

define(['BlinkForms', 'BIC'], function (Forms) {

  var originalOptions = { a: 'alpha', b: 'beta', g: 'gamma' };
  var choiceElements = [ 'selectc', 'selecte', 'multic', 'multie', 'multif',
      'selectf', 'multig', 'selecth' ];
  var multiWithOther = [ 'multic', 'multie', 'multif', 'multig' ];
  var selectWithOther = [ 'selectc', 'selecte', 'selectf', 'selecth' ];

  suite('2: options', function () {
    var $page = $('[data-role=page]'),
      $content = $page.find('[data-role=content]');

    /**
     * execute once before everything else in this suite
     */
    suiteSetup(function () {
      $content.empty();
      delete Forms.current;
    });

    suite('Form', function () {

      test('BlinkForms global is an Object', function () {
        assert($.isPlainObject(Forms), 'BlinkForms is a JavaScript object');
      });

      test('initialise with form.json', function (done) {
        var form;

        Forms.getDefinition('form1', 'add').then(function (def) {
          Forms.initialize(def);
          form = Forms.current;
          assert.equal($.type(form), 'object');
          assert.equal(form.get('name'), 'form1');
          assert.equal(form.get('label'), 'Form 1');
          done();
        }, function () {
          assert.fail(true, false, 'getDefinition failed!');
          done();
        });
      });

      test('render form for jQuery Mobile', function () {
        var form = Forms.current;

        $content.append(form.$form);

        $.mobile.page({}, $page);
        $page.trigger('pagecreate');
        $page.show();
      });

      test('select+other bindings between model and "other" input', function () {
        var form = Forms.current;
        selectWithOther.forEach(function (name) {
          var value;
          var element = form.getElement(name);
          var $view = element.get('_view').$el;
          var $other;
          var $checked;

          element.val('');
          $other = $view.find('input[type=text]');
          assert.notOk(element.val(), name + ': model value is falsey');
          // assert.lengthOf($selected, 0, name + ': nothing checked');
          assert.lengthOf($other, 0, name + ': other box absent');

          value = 'a';
          element.val(value);
          $other = $view.find('input[type=text]');
          $checked = $view.find(':checked');
          assert.equal(element.val(), value, name + ': model value correct');
          assert.lengthOf($checked, 1, name + ': 1 checked');
          assert.lengthOf($other, 0, name + ': other box absent');
          assert.equal($checked.val(), value, name + ': model value correct');

          value = '123';
          element.val(value);
          $other = $view.find('input[type=text]');
          $checked = $view.find(':checked');
          assert.deepEqual(element.val(), value, name + ': model value correct');
          assert.lengthOf($checked, 1, name + ': 1 checked');
          assert.lengthOf($other, 1, name + ': other box present');
          assert.equal($other.val(), '123', name + ': other box shows "123"');
          assert.equal($checked.val(), 'other', name + ': model value correct');

          element.val('');
          $other = $view.find('input[type=text]');
          assert.notOk(element.val(), name + ': model value is falsey');
          // assert.lengthOf($selected, 0, name + ': nothing checked');
          assert.lengthOf($other, 0, name + ': other box absent');

          if (element.get('nativeMenu')) {
            assert.equal($view.find('select').data("role"), "none", name + ': data-role is not none');
          } else {
            assert.notEqual($view.find('select').data("role"), "none", name + ': data-role is not none');
          }
        });
      });

      test('multi+other bindings between model and "other" input', function () {
        var form = Forms.current;
        multiWithOther.forEach(function (name) {
          var value;
          var element = form.getElement(name);
          var $view = element.get('_view').$el;
          var $other;
          var $checked;

          element.val('');
          $other = $view.find('input[type=text]');
          $checked = $view.find(':checked');
          assert.notOk(element.val(), name + ': model value is falsey');
          assert.lengthOf($checked, 0, name + ': nothing checked');
          assert.lengthOf($other, 0, name + ': other box absent');

          value = ['a'];
          element.val(value);
          $other = $view.find('input[type=text]');
          $checked = $view.find(':checked');
          assert.deepEqual(element.val(), value, name + ': model value correct');
          assert.lengthOf($checked, 1, name + ': 1 checked');
          assert.lengthOf($other, 0, name + ': other box absent');
          assert.deepEqual($checked.map(function () {
            return $(this).val();
          }).get(), value, name + ': model value correct');

          value = ['a', 'b'];
          element.val(value);
          $other = $view.find('input[type=text]');
          $checked = $view.find(':checked');
          assert.deepEqual(element.val(), value, name + ': model value correct');
          assert.lengthOf($checked, 2, name + ': 2 checked');
          assert.lengthOf($other, 0, name + ': other box absent');
          assert.deepEqual($checked.map(function () {
            return $(this).val();
          }).get(), value, name + ': model value correct');

          value = [''];
          element.val(value);
          $other = $view.find('input[type=text]');
          $checked = $view.find(':checked');
          assert.deepEqual(element.val(), value, name + ': model value correct');
          assert.lengthOf($checked, 1, name + ': 1 checked');
          assert.lengthOf($other, 1, name + ': other box present');
          assert.equal($other.val(), '', name + ': other box empty');

          value = ['a', ''];
          element.val(value);
          $other = $view.find('input[type=text]');
          $checked = $view.find(':checked');
          assert.deepEqual(element.val(), value, name + ': model value correct');
          assert.lengthOf($checked, 2, name + ': 2 checked');
          assert.lengthOf($other, 1, name + ': other box present');
          assert.equal($other.val(), '', name + ': other box empty');
          assert.deepEqual($checked.map(function () {
            return $(this).val();
          }).get(), ['a', 'other'], name + ': model value correct');

          value = ['123'];
          element.val(value);
          $other = $view.find('input[type=text]');
          $checked = $view.find(':checked');
          assert.deepEqual(element.val(), value, name + ': model value correct');
          assert.lengthOf($checked, 1, name + ': 1 checked');
          assert.lengthOf($other, 1, name + ': other box present');
          assert.equal($other.val(), '123', name + ': other box shows "123"');
          assert.deepEqual($checked.map(function () {
            return $(this).val();
          }).get(), ['other'], name + ': model value correct');

          value = ['a', '123'];
          element.val(value);
          $other = $view.find('input[type=text]');
          $checked = $view.find(':checked');
          assert.deepEqual(element.val(), value, name + ': model value correct');
          assert.lengthOf($checked, 2, name + ': 2 checked');
          assert.lengthOf($other, 1, name + ': other box present');
          assert.equal($other.val(), '123', name + ': other box shows "123"');
          assert.deepEqual($checked.map(function () {
            return $(this).val();
          }).get(), ['a', 'other'], name + ': model value correct');

          element.val('');
          $other = $view.find('input[type=text]');
          $checked = $view.find(':checked');
          assert.notOk(element.val(), name + ': model value is falsey');
          assert.lengthOf($checked, 0, name + ': nothing checked');
          assert.lengthOf($other, 0, name + ': other box absent');

          if (element.get('nativeMenu')) {
            assert.equal($view.find('select').data("role"), "none", name + ': data-role is not none');
          } else {
            assert.notEqual($view.find('select').data("role"), "none", name + ': data-role is not none');
          }
        });
      });

      test('Multi-F native collapsed', function () {
        var form = Forms.current,
          element = form.getElement('multif'),
          $fieldset = element.attributes._view.$el,
          $select = $fieldset.find('select');

        element.val([]);
        assert.deepEqual(element.val(), [], 'model.value is []');

        element.val(['a']);
        assert.deepEqual(element.val(), ['a'], 'model.value = ["a"]');

        element.val(['a', 'b']);
        assert.deepEqual(element.val(), ['a', 'b'], 'model.value = ["a", "b"]');

        assert.equal($select.data("role"), "none", "data-role is not none");

      });

      test('boolean 0/1', function (done) {
        var form = Forms.current,
          element = form.getElement('boolean'),
          $fieldset = element.attributes._view.$el;

        element.val('');
        assert.equal(element.val(), '', 'model.value is ""');
        assert.equal($fieldset.find('ui-btn-active').length, 0, 'jQM is blank');

        element.val(0);
        assert.equal(element.val(), 0, 'model.value = 0');

        setTimeout(function () {
          element.val(1);
          assert.equal(element.val(), 1, 'model.value = 1');

          setTimeout(function () {
            $fieldset.find('div.ui-slider-switch').trigger('mousedown').trigger('mouseup').trigger('click');
            assert.equal(element.val(), 0, 'model.value = 0');
            done();
          }, 500);

        }, 500);

      });

      test('boolean n/y', function (done) {
        var form = Forms.current,
          element = form.getElement('question'),
          $fieldset = element.attributes._view.$el;

        element.val('');
        assert.equal(element.val(), '', 'model.value is ""');
        assert.equal($fieldset.find('ui-btn-active').length, 0, 'jQM is blank');

        element.val('n');
        assert.equal(element.val(), 'n', 'model.value = "n"');

        setTimeout(function () {
          element.val('y');
          assert.equal(element.val(), 'y', 'model.value = "y"');

          setTimeout(function () {
            $fieldset.find('div.ui-slider-switch').trigger('mousedown').trigger('mouseup').trigger('click');
            assert.equal(element.val(), 'n', 'model.value = "n"');
            done();
          }, 500);
        }, 500);

      });

      test('elements have original a|b|g options', function () {
        var form = Forms.current;
        choiceElements.forEach(function (name) {
          var element = form.getElement(name);
          var currentOptions = element.get('options');
          assert.deepEqual(currentOptions, originalOptions, name + ' ' + JSON.stringify(currentOptions));
        });
      });

    }); // END: suite('Form', ...)

  }); // END: suite('1', ...)

});

/*
TODO
SelectC ...chose one bugs out
*/
<|endoftext|>


<commit_before>#!/usr/bin/env node

var Blynk = require('blynk-library');
var robot = require("robotjs");

if (!process.argv[2]) {
  console.log("Please specify auth token.");
  process.exit(1);
}

var blynk = new Blynk.Blynk(process.argv[2]);

// Mouse control
var dx = 0,
    dy = 0;
var tmr = 0;

robot.setMouseDelay(2);

function mouseMover() {
  var mousePos = robot.getMousePos();
  mousePos.x += dx;
  mousePos.y += dy;
  robot.moveMouse(mousePos.x, mousePos.y);
}

function deadZone(d) {
  var sign = d?d<0?-1:1:0;
  return (Math.abs(d) < 5) ? 0 : d - 5*sign;
}

var v1 = new blynk.VirtualPin(1);
v1.on('write', function(param) {
  dx = parseInt(param[0]);
  dy = -parseInt(param[1]);
  dx = deadZone(dx);
  dy = deadZone(dy);
  if ((dx != 0 || dy != 0) && tmr == 0) {
    tmr = setInterval(mouseMover, 50);
    mouseMover();
  } else if (dx == 0 && dy == 0 && tmr != 0) {
    clearInterval(tmr);
    tmr = 0;
  }
});

var v2 = new blynk.VirtualPin(2);
v2.on('write', function(param) {
  if (param[0]==1) {
    robot.mouseToggle("down", "left");
  } else {
    robot.mouseToggle("up", "left");
  }
});

var v3 = new blynk.VirtualPin(3);
v3.on('write', function(param) {
  if (param[0]==1) {
    robot.mouseToggle("down", "right");
  } else {
    robot.mouseToggle("up", "right");
  }
});

// Keyboard

var key_mapping = {
  "page_up"         : 5,
  "page_down"       : 6,

  "audio_vol_up"    : 7,
  "audio_vol_down"  : 8,

  "audio_play"      : 9,
  "audio_next"      : 10,
};

// Generate virtual pins to control different keys
Object.keys(key_mapping).forEach(function(key) {
  var vpin_num = key_mapping[key];
  var vpin = new blynk.VirtualPin(vpin_num);
  vpin.on('write', function(param) {
    if (param[0] == 1) {
      robot.keyToggle(key, "down");
    } else {
      robot.keyToggle(key, "up");
    }
  });
});
<commit_msg>Updated example<commit_after>#!/usr/bin/env node

var Blynk = require('blynk-library');
var robot = require("robotjs");

if (!process.argv[2]) {
  console.log("Please specify auth token.");
  process.exit(1);
}

var blynk = new Blynk.Blynk(process.argv[2]);

// Mouse control
var delta_x = 0,
    delta_y = 0;
var cur_x = 0,
    cur_y = 0;
var tmr = 0;

robot.setMouseDelay(2);

function deadZone(d) {
  var sign = d?d<0?-1:1:0;
  return (Math.abs(d) < 50) ? 0 : (d - 50*sign)/10;
}

var v1 = new blynk.VirtualPin(1);
v1.on('write', function(param) {
  delta_x = deadZone(parseInt(param[0]));
  delta_y = deadZone(-parseInt(param[1]));
  if ((delta_x != 0 || delta_y != 0) && tmr == 0) {
    var mousePos = robot.getMousePos();
    cur_x = mousePos.x;
    cur_y = mousePos.y;
    tmr = setInterval(function() {
      robot.moveMouse(cur_x += delta_x, cur_y += delta_y);
    }, 30);
  } else if (delta_x == 0 && delta_y == 0 && tmr != 0) {
    clearInterval(tmr);
    tmr = 0;
  }
});

// Mouse left button
var v2 = new blynk.VirtualPin(2);
v2.on('write', function(param) {
  if (param[0]==1) {
    robot.mouseToggle("down", "left");
  } else {
    robot.mouseToggle("up", "left");
  }
});

// Mouse right button
var v3 = new blynk.VirtualPin(3);
v3.on('write', function(param) {
  if (param[0]==1) {
    robot.mouseToggle("down", "right");
  } else {
    robot.mouseToggle("up", "right");
  }
});

// Keyboard

var key_mapping = {
  "page_up"         : 5,
  "page_down"       : 6,

  "audio_vol_up"    : 7,
  "audio_vol_down"  : 8,

  "audio_play"      : 9,
  "audio_next"      : 10,
};

// Generate virtual pins to control different keys
Object.keys(key_mapping).forEach(function(key_name) {
  var vpin_num = key_mapping[key_name];
  var vpin = new blynk.VirtualPin(vpin_num);
  vpin.on('write', function(param) {
    if (param[0] == 1) {
      robot.keyToggle(key_name, "down");
    } else {
      robot.keyToggle(key_name, "up");
    }
  });
});
<|endoftext|>


<commit_before>/*********************************************************************************
*     File Name           :     view.js
*     Created By          :     Jone Casper
*     Creation Date       :     [2014-03-05 18:36]
*     Last Modified       :     [2014-04-09 02:27]
*     Description         :     Backchart view for amcharts
**********************************************************************************/
(function(root, name, factory) {
	"use strict";
	if (typeof define === 'function' && define.amd) {
		define(['jquery','../backchart.base/view','backbone',"underscore","AmCharts","../backchart.base/logger"], function($, base, Backbone, _, amcharts, logger) {
			amcharts = amcharts || window.AmCharts;
			return factory($, Backbone, _, base, amcharts, logger);
		});
	}else if(typeof module !== 'undefined' && module.exports && typeof require === 'function'){
		var $ = require("jquery"),
		base = require("../backchart.base/view"),
		Backbone = require("backbone"),
		_ = require("underscore"),
		amcharts = require("AmCharts") || window.AmCharts;
		var logger = require("../backchart.base/logger");
		module.exports = factory($, Backbone, _, base, amcharts, logger);
	}else{
		var namespaces = name.split("."),
			//scope = (root.jQuery || root.ender || root.$ || root || this),
			scope = root || this;
		for (var i=0; i<namespaces.length; i++) {
			var p = namespaces[i],
				ex = scope[p];
			scope = scope[p] = (i === namespaces.length - 1) ?
				factory(
					(root.jQuery || window.jQuery),
					(root.Backbone|| window.Backbone),
					(root._ || window._),
					root.backchart.base.view,
					(root.amcharts|| window.AmCharts)
				):
				(ex || {});
		}
	}
}(this, "backchart.amcharts.view", function($, Backbone, _, baseview, AmCharts) {
	/**
	 * Backchart amcharts view
	 * @module amcharts/view
	 * @requires jquery
	 * @requires base/view
	 * @requires backbone
	 * @requires AmCharts
     * @this {Backbone.View}
     */
	var backchartAmchartsView = baseview.extend(
		/**
		 * @lends module:amcharts/view.prototype
		 */
		{
		graphsFieldsForConflictTest:["valueField", "alphaField", "bulletField", "bulletSizeField", "colorField", "fillColorsField", "labelColorField", "lineColorField"],
        /**
         * Merge collection ways
         *
         * simple : default, use index to merge
         * category : use category as key to merge data
         */
        mergeData : "simple",
        /**
         * Key field in all kinds of charts' data
         * These field will be attached to main configuration of chart
         */
        keyFields :{
            "serial" :["categoryField"],
            "pie" :["titleField","valueField"]
        },
		constructor: function() {
			/**
			 * The default Graphs Options for all collections
			 * You renderOptions have the high priority and can cover the default configuration
			 * @type {object}
			 * @property {string} balloonText default balloonText
			 * @property {string} valueField default value field
             * @default {balloonText: "[[category]]: [[value]]",valueField: "value"}
			 */
			this.defaultGraphsOptions = {
				balloonText: "[[category]]: [[value]]",
				valueField: "value"
			};
			/**
			 * Allow one or more Axes displayed in chart. Default is false and use the [defaultAxesOptions]{@link amcharts/view:defaultAxesOption} if no axes configuration in collections' renderOptions which had been bound to the view and not hidden.
			 * If exist axes configuration in any collection's renderOptions and multiValueAxes is false, we will use the first collection's setting when creating the options.
			 * @type {boolean}
             * @default false
			 */
			this.multiValueAxes = false;
			/**
			 * The default Axes Options for all collections
			 * @type {object}
			 */
            this.defaultAxesOptions = {
            };
            /**
             * The default categoryAxis when the type of value is Date
             * @type {object}
             * @default {equalSpacing: true}
             */
            this.defaultDateAxesOptions = {
                equalSpacing: true
            };
			 /**
			  * The default make chart Options, value is "serial"
			  * @type {object}
              * @default {type:"serial"}
			  */
			this.defaultOptions = {
				 type:"serial"
			 };

			 baseview.apply(this, arguments);
		},
        /**
         * initialize
         *
         * @param defaultOptions
         * @param defaultGraphsOptions
         * @param defaultAxesOptions
         * @return {Backbone.View}
         */
		initialize: function(defaultOptions, defaultGraphsOptions, defaultAxesOptions){
			baseview.prototype.initialize.apply(this, arguments);
			this.defaultOptions = $.extend(true, {}, this.defaultOptions, defaultOptions);
			this.defaultGraphsOptions = $.extend(true, {}, this.defaultGraphsOptions, defaultGraphsOptions);
			this.defaultAxesOptions = $.extend(true, {}, this.defaultAxesOptions, defaultAxesOptions);
			this.__rendered = false;
			return this;
		},
		/**
		 * @function onCollection
		 * @memberof module:amcharts/view
         * @desc inherited from [base/view:onCollection]{@link base/view:onCollection} and defined a special fixed formated renderOptions
		 * @param {(backchart.base.collection|Backbone.Collection)} collection
		 * @param {Object} renderOptions render chart options which based what chart library you used.
		 * @param {Object} [renderOptions.graph] same as the AmGraph configration.If you set valueField
		 * @param {Object} [renderOptions.axes] same as the ValueAxis or GaugeAxis configration
		 * @example
		 * 	   var chart = new AmCharts.AmSerialChart();
		 * 	   var graph = new AmCharts.AmGraph();
		 * 	   graph.type = 'column';
		 * 	   graph.fillAlphas = 1;
		 *
		 *     //you renderOptions
		 *     var renderOptions = {
		 *         graph : {
		 *			   type : 'column',
		 *			   fillAlphas : 1
		 *		   }
		 *	   }
		 * @param {Object} options inherited the options on [base/collection.onCollection]{@link base/view:onCollection}
		 *
		 */

		__mergeToDataProvider: function(key, data, dataProvider, graphFields){
			var me = this;

            var conflicts = {};
			for(var k=data.length-1; k>=0; k--){
				var d = data[k],
                    pindex = -1;
                if (me.mergeData === "simple"){
                    pindex = k;
                }else if (me.mergeData === "category"){
				    for (var i=dataProvider.length-1; i>=0; i--){
					    if (
                            (dataProvider[i][key] instanceof Date && d[key] instanceof Date && dataProvider[i][key].valueOf() === d[key].valueOf()) ||
                            dataProvider[i][key] === d[key]
                        ){
                            pindex = i;
                            break;
                        }
                    }
                }
                if (pindex>=0 && d){
                    //clone a new object and resolve conflicts
                    for (var gk in graphFields){
                        var thisField = graphFields[gk];
                        if (d[thisField] && typeof dataProvider[pindex][thisField] !== "undefined"){
                            var resolveName = conflicts[thisField];
                            if (!resolveName){
                                resolveName = _.uniqueId(thisField);
                                conflicts[thisField] = resolveName;
                            }
                            d[resolveName] = d[thisField];
                            delete d[thisField];
                        }
                    }
                    dataProvider[pindex] = $.extend(dataProvider[pindex], d);
                }else{
                    dataProvider.push(d);
                }
            }
            for (var ck in conflicts){
                for (var gh in graphFields){
                    if (graphFields[gh] === ck){
                        graphFields[gh] = conflicts[ck];
                    }
                }
            }
			return graphFields;
		},
		_createGraph: function(renderOptions){
			var me = this;
			var graphs = [],
			    valueAxes = [],
				dataProvider = [];

            if (!renderOptions.type){
                renderOptions.type = "serial";
            }

            //get default key filed in data
            var keyFields = me.keyFields[renderOptions.type] || [];
            var defaultKeyFieldsName = {};
            for (var k=0; k<keyFields.length; k++){
                defaultKeyFieldsName[keyFields[k]] = renderOptions[keyFields[k]] || ("_" + keyFields[k]);
            }

			me.eachCollection(function(uid, collection, renderOption){
				var _data = [],
				    _gph = $.extend(true ,{}, me.defaultGraphsOptions, renderOption.graph);

                var testModel = collection.model ? new collection.model() : null,
				    graphFields = {},
                    keyFieldAttrMap = {};

                //get fileds for testing confliction
				for (var f=me.graphsFieldsForConflictTest.length-1; f>=0; f--){
					var testField = me.graphsFieldsForConflictTest[f];
                    var definedFD = collection[testField] || (testModel ? testModel[testField] : null);
				    if (definedFD){
				   		graphFields[testField] = definedFD;
					}
				}
                for (var k=0; k<keyFields.length; k++){
                    var kf = keyFields[k];
                    var thisFiledAttrName = testModel[kf] || collection[kf] || renderOptions[kf];
                    if (thisFiledAttrName){
                        keyFieldAttrMap[kf] = thisFiledAttrName;
                        renderOptions[kf] = defaultKeyFieldsName[kf];
                    }
                }

				var coverDateFormat = false;
				collection.each(function(model){
					if (collection.isVisibled(model)){
						var _d = _.clone(model.attributes);

                        for (var kf in defaultKeyFieldsName){
                            var attrname = keyFieldAttrMap[kf];
                            if (attrname){
                                _d[defaultKeyFieldsName[kf]] = model.get(attrname);
                            }
                        }

						if (renderOptions.type === "serial" && (model.dataDateFormat || renderOptions.dataDateFormat)){
							if (!renderOptions.categoryAxis || typeof renderOptions.categoryAxis.parseDates === "undefined"){
								console.warn("One model's category field is Date Object, but not set categoryAxis.parseDates");
							}else{
								var dataDateFormat = model.dataDateFormat || renderOptions.dataDateFormat;
                                if (typeof _d[keyFields[0]] === "undefined"){
                                    //console.warn("One model's categoryField is undefined!!");
                                    return;
                                }
								_d[keyFields[0]] = AmCharts.stringToDate(_d[keyFields[0]], dataDateFormat);
                                coverDateFormat = true;
							}
						}
				    	_data.push(_d);
					}
				});

				//merge data to dataProvider configuration
                if (!!!dataProvider.length){
                    dataProvider = _data;
                }else{
                    graphFields = me.__mergeToDataProvider(keyFields[0], _data, dataProvider, graphFields);
                }
                _gph = $.extend(_gph, graphFields);

				/**
				 * We already cover the date
				 */
				if (coverDateFormat){
                    renderOptions.categoryAxis = $.extend(true, {}, renderOptions.categoryAxis, me.defaultDateAxesOptions);
				}

				if (renderOption.axes){
					if (me.multiValueAxes === true){
						var newValueAxes = $.extend(true, {}, me.defaultAxesOptions, renderOption.axes);
						newValueAxes.id = _.uniqueId("_axes");
						valueAxes.push(newValueAxes);
						_gph.valueAxis = newValueAxes.id;
					}else if (!!!valueAxes.length){
						valueAxes.push($.extend(true, {}, me.defaultAxesOptions, renderOption.axes));
					}else{
						console.info("We has ignored one valueAxes configuration because the multiValueAxes option is false.");
					}
				}
				graphs.push(_gph);
			});

			renderOptions.dataProvider = dataProvider;
			renderOptions.valueAxes = valueAxes;
			renderOptions.graphs = graphs;
			return renderOptions;
		},
        /**
		 * The collection options transform and push
         *
         * @param {Object} renderOptions the render options
         * @return {Object} options
         */
		transformOptions: function(renderOptions){
			var me = this;
			//create dataProvider and unified the valueFiled
			renderOptions = me._createGraph(renderOptions);
			return renderOptions;
		},
		/**
		 * The render function
		 *
		 * @fires module:amcharts/view#beforeValidateData
		 * @fires module:amcharts/view#beforeRender
		 * @fires module:base/view#rendered
		 * @return {Object} this instance
		 */
		render: function(force){
			var me = this;
			var rret = baseview.prototype.renderBefore.apply(me, arguments);
			if (!rret){
				return false;
			}
			if (!me.el.id){
				me.el.id = _.uniqueId(me._viewPrefix || "_bcname");
			}
			var renderOptions = _.clone(me.defaultOptions);
		    renderOptions = me.transformOptions(renderOptions);

			me.__cOptions = renderOptions;
            if (me.Chart && !force){
			    me.trigger("beforeValidateData", me, me.el.id, renderOptions.dataProvider);
                me.Chart.dataProvider = renderOptions.dataProvider;
                me.Chart.validateData();
                return;
            }else{
			    me.trigger("beforeRender", me, me.el.id, renderOptions);
			    me.$container.empty().append(me.$el.empty());
			    me.elFillParents();
			    me.Chart = AmCharts.makeChart(me.el.id, $.extend(true, {}, renderOptions));
                me.Chart.write(me.el.id);
			    return baseview.prototype.renderAfter.apply(me, [me, me.el, me.Chart, renderOptions]);
            }
		},
		/**
		 * A event triggered before render chart.
		 * @event module:amcharts/view#beforeRender
		 * @type {object}
		 * @property {Backbone.View} view view instance
		 * @property {string} elId Element's ID
		 * @property {object} renderOptions options for rendering
		 */
        /**
		 * A event triggered before chart data change.
		 * @event module:amcharts/view#beforeValidateData
		 * @type {object}
		 * @property {Backbone.View} view view instance
		 * @property {string} elId Element's ID
		 * @property {object} dataProvider dataProvider options
		 */

		/**
		 * Implement [getChartOptions]{@link base/view:getChartOptions}
		 */
		getChartOption: function(){
			return this.__cOptions;
		}

	});
	return backchartAmchartsView;
}));

<commit_msg>disabled 'date covered feature'<commit_after>/*********************************************************************************
*     File Name           :     view.js
*     Created By          :     Jone Casper
*     Creation Date       :     [2014-03-05 18:36]
*     Last Modified       :     [2014-04-09 02:27]
*     Description         :     Backchart view for amcharts
**********************************************************************************/
(function(root, name, factory) {
	"use strict";
	if (typeof define === 'function' && define.amd) {
		define(['jquery','../backchart.base/view','backbone',"underscore","AmCharts","../backchart.base/logger"], function($, base, Backbone, _, amcharts, logger) {
			amcharts = amcharts || window.AmCharts;
			return factory($, Backbone, _, base, amcharts, logger);
		});
	}else if(typeof module !== 'undefined' && module.exports && typeof require === 'function'){
		var $ = require("jquery"),
		base = require("../backchart.base/view"),
		Backbone = require("backbone"),
		_ = require("underscore"),
		amcharts = require("AmCharts") || window.AmCharts;
		var logger = require("../backchart.base/logger");
		module.exports = factory($, Backbone, _, base, amcharts, logger);
	}else{
		var namespaces = name.split("."),
			//scope = (root.jQuery || root.ender || root.$ || root || this),
			scope = root || this;
		for (var i=0; i<namespaces.length; i++) {
			var p = namespaces[i],
				ex = scope[p];
			scope = scope[p] = (i === namespaces.length - 1) ?
				factory(
					(root.jQuery || window.jQuery),
					(root.Backbone|| window.Backbone),
					(root._ || window._),
					root.backchart.base.view,
					(root.amcharts|| window.AmCharts)
				):
				(ex || {});
		}
	}
}(this, "backchart.amcharts.view", function($, Backbone, _, baseview, AmCharts) {
	/**
	 * Backchart amcharts view
	 * @module amcharts/view
	 * @requires jquery
	 * @requires base/view
	 * @requires backbone
	 * @requires AmCharts
     * @this {Backbone.View}
     */
	var backchartAmchartsView = baseview.extend(
		/**
		 * @lends module:amcharts/view.prototype
		 */
		{
		graphsFieldsForConflictTest:["valueField", "alphaField", "bulletField", "bulletSizeField", "colorField", "fillColorsField", "labelColorField", "lineColorField"],
        /**
         * Merge collection ways
         *
         * simple : default, use index to merge
         * category : use category as key to merge data
         */
        mergeData : "simple",
        /**
         * Key field in all kinds of charts' data
         * These field will be attached to main configuration of chart
         */
        keyFields :{
            "serial" :["categoryField"],
            "pie" :["titleField","valueField"]
        },
		constructor: function() {
			/**
			 * The default Graphs Options for all collections
			 * You renderOptions have the high priority and can cover the default configuration
			 * @type {object}
			 * @property {string} balloonText default balloonText
			 * @property {string} valueField default value field
             * @default {balloonText: "[[category]]: [[value]]",valueField: "value"}
			 */
			this.defaultGraphsOptions = {
				balloonText: "[[category]]: [[value]]",
				valueField: "value"
			};
			/**
			 * Allow one or more Axes displayed in chart. Default is false and use the [defaultAxesOptions]{@link amcharts/view:defaultAxesOption} if no axes configuration in collections' renderOptions which had been bound to the view and not hidden.
			 * If exist axes configuration in any collection's renderOptions and multiValueAxes is false, we will use the first collection's setting when creating the options.
			 * @type {boolean}
             * @default false
			 */
			this.multiValueAxes = false;
			/**
			 * The default Axes Options for all collections
			 * @type {object}
			 */
            this.defaultAxesOptions = {
            };
            /**
             * The default categoryAxis when the type of value is Date
             * @type {object}
             * @default {equalSpacing: true}
             */
            this.defaultDateAxesOptions = {
                equalSpacing: true
            };
			 /**
			  * The default make chart Options, value is "serial"
			  * @type {object}
              * @default {type:"serial"}
			  */
			this.defaultOptions = {
				 type:"serial"
			 };

			 baseview.apply(this, arguments);
		},
        /**
         * initialize
         *
         * @param defaultOptions
         * @param defaultGraphsOptions
         * @param defaultAxesOptions
         * @return {Backbone.View}
         */
		initialize: function(defaultOptions, defaultGraphsOptions, defaultAxesOptions){
			baseview.prototype.initialize.apply(this, arguments);
			this.defaultOptions = $.extend(true, {}, this.defaultOptions, defaultOptions);
			this.defaultGraphsOptions = $.extend(true, {}, this.defaultGraphsOptions, defaultGraphsOptions);
			this.defaultAxesOptions = $.extend(true, {}, this.defaultAxesOptions, defaultAxesOptions);
			this.__rendered = false;
			return this;
		},
		/**
		 * @function onCollection
		 * @memberof module:amcharts/view
         * @desc inherited from [base/view:onCollection]{@link base/view:onCollection} and defined a special fixed formated renderOptions
		 * @param {(backchart.base.collection|Backbone.Collection)} collection
		 * @param {Object} renderOptions render chart options which based what chart library you used.
		 * @param {Object} [renderOptions.graph] same as the AmGraph configration.If you set valueField
		 * @param {Object} [renderOptions.axes] same as the ValueAxis or GaugeAxis configration
		 * @example
		 * 	   var chart = new AmCharts.AmSerialChart();
		 * 	   var graph = new AmCharts.AmGraph();
		 * 	   graph.type = 'column';
		 * 	   graph.fillAlphas = 1;
		 *
		 *     //you renderOptions
		 *     var renderOptions = {
		 *         graph : {
		 *			   type : 'column',
		 *			   fillAlphas : 1
		 *		   }
		 *	   }
		 * @param {Object} options inherited the options on [base/collection.onCollection]{@link base/view:onCollection}
		 *
		 */

		__mergeToDataProvider: function(key, data, dataProvider, graphFields){
			var me = this;

            var conflicts = {};
			for(var k=data.length-1; k>=0; k--){
				var d = data[k],
                    pindex = -1;
                if (me.mergeData === "simple"){
                    pindex = k;
                }else if (me.mergeData === "category"){
				    for (var i=dataProvider.length-1; i>=0; i--){
					    if (
                            (dataProvider[i][key] instanceof Date && d[key] instanceof Date && dataProvider[i][key].valueOf() === d[key].valueOf()) ||
                            dataProvider[i][key] === d[key]
                        ){
                            pindex = i;
                            break;
                        }
                    }
                }
                if (pindex>=0 && d){
                    //clone a new object and resolve conflicts
                    for (var gk in graphFields){
                        var thisField = graphFields[gk];
                        if (d[thisField] && typeof dataProvider[pindex][thisField] !== "undefined"){
                            var resolveName = conflicts[thisField];
                            if (!resolveName){
                                resolveName = _.uniqueId(thisField);
                                conflicts[thisField] = resolveName;
                            }
                            d[resolveName] = d[thisField];
                            delete d[thisField];
                        }
                    }
                    dataProvider[pindex] = $.extend(dataProvider[pindex], d);
                }else{
                    dataProvider.push(d);
                }
            }
            for (var ck in conflicts){
                for (var gh in graphFields){
                    if (graphFields[gh] === ck){
                        graphFields[gh] = conflicts[ck];
                    }
                }
            }
			return graphFields;
		},
		_createGraph: function(renderOptions){
			var me = this;
			var graphs = [],
			    valueAxes = [],
				dataProvider = [];

            if (!renderOptions.type){
                renderOptions.type = "serial";
            }

            //get default key filed in data
            var keyFields = me.keyFields[renderOptions.type] || [];
            var defaultKeyFieldsName = {};
            for (var k=0; k<keyFields.length; k++){
                defaultKeyFieldsName[keyFields[k]] = renderOptions[keyFields[k]] || ("_" + keyFields[k]);
            }

			me.eachCollection(function(uid, collection, renderOption){
				var _data = [],
				    _gph = $.extend(true ,{}, me.defaultGraphsOptions, renderOption.graph);

                var testModel = collection.model ? new collection.model() : null,
				    graphFields = {},
                    keyFieldAttrMap = {};

                //get fileds for testing confliction
				for (var f=me.graphsFieldsForConflictTest.length-1; f>=0; f--){
					var testField = me.graphsFieldsForConflictTest[f];
                    var definedFD = collection[testField] || (testModel ? testModel[testField] : null);
				    if (definedFD){
				   		graphFields[testField] = definedFD;
					}
				}
                for (var k=0; k<keyFields.length; k++){
                    var kf = keyFields[k];
                    var thisFiledAttrName = testModel[kf] || collection[kf] || renderOptions[kf];
                    if (thisFiledAttrName){
                        keyFieldAttrMap[kf] = thisFiledAttrName;
                        renderOptions[kf] = defaultKeyFieldsName[kf];
                    }
                }

				collection.each(function(model){
					if (collection.isVisibled(model)){
						var _d = _.clone(model.attributes);

                        for (var kf in defaultKeyFieldsName){
                            var attrname = keyFieldAttrMap[kf];
                            if (attrname){
                                _d[defaultKeyFieldsName[kf]] = model.get(attrname);
                            }
                        }

				    	_data.push(_d);
					}
				});

				//merge data to dataProvider configuration
                if (!!!dataProvider.length){
                    dataProvider = _data;
                }else{
                    graphFields = me.__mergeToDataProvider(keyFields[0], _data, dataProvider, graphFields);
                }
                _gph = $.extend(_gph, graphFields);

				if (renderOption.axes){
					if (me.multiValueAxes === true){
						var newValueAxes = $.extend(true, {}, me.defaultAxesOptions, renderOption.axes);
						newValueAxes.id = _.uniqueId("_axes");
						valueAxes.push(newValueAxes);
						_gph.valueAxis = newValueAxes.id;
					}else if (!!!valueAxes.length){
						valueAxes.push($.extend(true, {}, me.defaultAxesOptions, renderOption.axes));
					}else{
						console.info("We has ignored one valueAxes configuration because the multiValueAxes option is false.");
					}
				}
				graphs.push(_gph);
			});

			renderOptions.dataProvider = dataProvider;
			renderOptions.valueAxes = valueAxes;
			renderOptions.graphs = graphs;
			return renderOptions;
		},
        /**
		 * The collection options transform and push
         *
         * @param {Object} renderOptions the render options
         * @return {Object} options
         */
		transformOptions: function(renderOptions){
			var me = this;
			//create dataProvider and unified the valueFiled
			renderOptions = me._createGraph(renderOptions);
			return renderOptions;
		},
		/**
		 * The render function
		 *
		 * @fires module:amcharts/view#beforeValidateData
		 * @fires module:amcharts/view#beforeRender
		 * @fires module:base/view#rendered
		 * @return {Object} this instance
		 */
		render: function(force){
			var me = this;
			var rret = baseview.prototype.renderBefore.apply(me, arguments);
			if (!rret){
				return false;
			}
			if (!me.el.id){
				me.el.id = _.uniqueId(me._viewPrefix || "_bcname");
			}
			var renderOptions = _.clone(me.defaultOptions);
		    renderOptions = me.transformOptions(renderOptions);

			me.__cOptions = renderOptions;
            if (me.Chart && !force){
			    me.trigger("beforeValidateData", me, me.el.id, renderOptions.dataProvider);
                me.Chart.dataProvider = renderOptions.dataProvider;
                me.Chart.validateData();
                return;
            }else{
			    me.trigger("beforeRender", me, me.el.id, renderOptions);
			    me.$container.empty().append(me.$el.empty());
			    me.elFillParents();
			    me.Chart = AmCharts.makeChart(me.el.id, $.extend(true, {}, renderOptions));
                me.Chart.write(me.el.id);
			    return baseview.prototype.renderAfter.apply(me, [me, me.el, me.Chart, renderOptions]);
            }
		},
		/**
		 * A event triggered before render chart.
		 * @event module:amcharts/view#beforeRender
		 * @type {object}
		 * @property {Backbone.View} view view instance
		 * @property {string} elId Element's ID
		 * @property {object} renderOptions options for rendering
		 */
        /**
		 * A event triggered before chart data change.
		 * @event module:amcharts/view#beforeValidateData
		 * @type {object}
		 * @property {Backbone.View} view view instance
		 * @property {string} elId Element's ID
		 * @property {object} dataProvider dataProvider options
		 */

		/**
		 * Implement [getChartOptions]{@link base/view:getChartOptions}
		 */
		getChartOption: function(){
			return this.__cOptions;
		}

	});
	return backchartAmchartsView;
}));

<|endoftext|>


<commit_before>/**
 * @file ScrollBar vendor
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

// Statics
import Direction from '../_statics/Direction';

// Vendors
import classNames from 'classnames';

// Styles
import './ScrollBar.scss';

// cache
const tempSize = {
    vertical: null,
    horizontal: null
};

export function getSize(direction = Direction.VERTICAL) {

    if (document == undefined || window == undefined) {
        return;
    }

    const isVertical = direction === Direction.VERTICAL;

    if (isVertical && tempSize.vertical !== null) {
        return tempSize.vertical;
    } else if (!isVertical && tempSize.horizontal != null) {
        return tempSize.horizontal;
    }

    const scrollDiv = document.createElement('div');
    scrollDiv.className = classNames('scroll-bar-for-measure', direction);
    document.body.appendChild(scrollDiv);

    let size = 0;
    if (isVertical) {
        size = scrollDiv.offsetWidth - scrollDiv.clientWidth;
        tempSize.vertical = size;
    } else {
        size = scrollDiv.offsetHeight - scrollDiv.clientHeight;
        tempSize.horizontal = size;
    }

    document.body.removeChild(scrollDiv);

    return size;

}

export function getHideHorizontalScrollBarStyle() {

    const size = getSize(Direction.HORIZONTAL);

    return size > 0 ? {
        marginBottom: -size,
        paddingBottom: 0
    } : {
        marginBottom: -20,
        paddingBottom: 20
    };

}

export default {
    getSize,
    getHideHorizontalScrollBarStyle
};
<commit_msg>update ScrollBar<commit_after>/**
 * @file ScrollBar vendor
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

// Statics
import Direction from '../_statics/Direction';

// Vendors
import classNames from 'classnames';

// Styles
import './ScrollBar.scss';

// cache
const tempSize = {
    vertical: null,
    horizontal: null
};

export function getSize(direction = Direction.VERTICAL) {

    if (document == undefined || window == undefined) {
        return;
    }

    const isVertical = direction === Direction.VERTICAL;

    if (isVertical && tempSize.vertical !== null) {
        return tempSize.vertical;
    } else if (!isVertical && tempSize.horizontal != null) {
        return tempSize.horizontal;
    }

    const scrollDiv = document.createElement('div');
    scrollDiv.className = classNames('scroll-bar-for-measure', direction);
    document.body.appendChild(scrollDiv);

    let size = 0;
    if (isVertical) {
        size = scrollDiv.offsetWidth - scrollDiv.clientWidth;
        tempSize.vertical = size;
    } else {
        size = scrollDiv.offsetHeight - scrollDiv.clientHeight;
        tempSize.horizontal = size;
    }

    document.body.removeChild(scrollDiv);

    return size;

}

export function getHideHorizontalScrollBarStyle() {

    const size = getSize(Direction.HORIZONTAL);

    return size > 0 ? {
        paddingBottom: 0,
        marginBottom: -size
    } : {
        paddingBottom: 20,
        marginBottom: -20
    };

}

export default {
    getSize,
    getHideHorizontalScrollBarStyle
};
<|endoftext|>


<commit_before>// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require_tree .
//= require highcharts
//= require highcharts/highcharts-more<commit_msg>Forgot to require underscore in application.js<commit_after>// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require_tree .
//= require highcharts
//= require highcharts/highcharts-more
//= require underscore<|endoftext|>


<commit_before>var React = require('react');
var resolveAsyncState = require('../helpers/resolveAsyncState');

/**
 * A mixin for route handler component classes that fetch at least
 * part of their state asynchronously. Classes that use it should
 * declare a static `getInitialAsyncState` method that fetches state
 * for a component after it mounts. This function is given three
 * arguments: 1) the current route params, 2) the current query and
 * 3) a function that can be used to set state as it is received.
 *
 * Much like the familiar `getInitialState` method, `getInitialAsyncState`
 * should return a hash of key/value pairs to use in the component's
 * state. The difference is that the values may be promises. As these
 * values resolve, the component's state is updated. You should only
 * ever need to use the setState function for doing things like
 * streaming data and/or updating progress.
 *
 * Example:
 *
 *   var User = React.createClass({
 *
 *     statics: {
 *
 *       getInitialAsyncState: function (params, query, setState) {
 *         // Return a hash with keys named after the state variables
 *         // you want to set, as you normally do in getInitialState,
 *         // except the values may be immediate values or promises.
 *         // The state is automatically updated as promises resolve.
 *         return {
 *           user: getUserByID(params.userID) // may be a promise
 *         };
 *
 *         // Or, use the setState function to stream data!
 *         var buffer = '';
 *
 *         return {
 *
 *           // Same as above, the stream state variable is set to the
 *           // value returned by this promise when it resolves.
 *           stream: getStreamingData(params.userID, function (chunk) {
 *             buffer += chunk;
 *
 *             // Notify of progress.
 *             setState({
 *               streamBuffer: buffer
 *             });
 *           })
 *
 *         };
 *       }
 *
 *     },
 *
 *     getInitialState: function () {
 *       return {
 *         user: null,        // Receives a value when getUserByID resolves.
 *         stream: null,      // Receives a value when getStreamingData resolves.
 *         streamBuffer: ''   // Used to track data as it loads.
 *       };
 *     },
 *
 *     render: function () {
 *       if (!this.state.user)
 *         return <LoadingUser/>;
 *
 *       return (
 *

 *
Welcome {this.state.user.name}!

 *
So far, you've received {this.state.streamBuffer.length} data!

 *

 *       );
 *     }
 *
 *   });
 *
 * When testing, use the `initialAsyncState` prop to simulate asynchronous
 * data fetching. When this prop is present, no attempt is made to retrieve
 * additional state via `getInitialAsyncState`.
 */
var AsyncState = {

  propTypes: {
    initialAsyncState: React.PropTypes.object
  },

  getInitialState: function () {
    return this.props.initialAsyncState || null;
  },

  updateAsyncState: function (state) {
    if (this.isMounted())
      this.setState(state);
  },

  componentDidMount: function () {
    if (this.props.initialAsyncState || !this.constructor.getInitialAsyncState)
      return;

    resolveAsyncState(
      this.constructor.getInitialAsyncState(this.props.params, this.props.query, this.updateAsyncState),
      this.updateAsyncState
    );
  }

};

module.exports = AsyncState;
<commit_msg>Clarify type check<commit_after>var React = require('react');
var resolveAsyncState = require('../helpers/resolveAsyncState');

/**
 * A mixin for route handler component classes that fetch at least
 * part of their state asynchronously. Classes that use it should
 * declare a static `getInitialAsyncState` method that fetches state
 * for a component after it mounts. This function is given three
 * arguments: 1) the current route params, 2) the current query and
 * 3) a function that can be used to set state as it is received.
 *
 * Much like the familiar `getInitialState` method, `getInitialAsyncState`
 * should return a hash of key/value pairs to use in the component's
 * state. The difference is that the values may be promises. As these
 * values resolve, the component's state is updated. You should only
 * ever need to use the setState function for doing things like
 * streaming data and/or updating progress.
 *
 * Example:
 *
 *   var User = React.createClass({
 *
 *     statics: {
 *
 *       getInitialAsyncState: function (params, query, setState) {
 *         // Return a hash with keys named after the state variables
 *         // you want to set, as you normally do in getInitialState,
 *         // except the values may be immediate values or promises.
 *         // The state is automatically updated as promises resolve.
 *         return {
 *           user: getUserByID(params.userID) // may be a promise
 *         };
 *
 *         // Or, use the setState function to stream data!
 *         var buffer = '';
 *
 *         return {
 *
 *           // Same as above, the stream state variable is set to the
 *           // value returned by this promise when it resolves.
 *           stream: getStreamingData(params.userID, function (chunk) {
 *             buffer += chunk;
 *
 *             // Notify of progress.
 *             setState({
 *               streamBuffer: buffer
 *             });
 *           })
 *
 *         };
 *       }
 *
 *     },
 *
 *     getInitialState: function () {
 *       return {
 *         user: null,        // Receives a value when getUserByID resolves.
 *         stream: null,      // Receives a value when getStreamingData resolves.
 *         streamBuffer: ''   // Used to track data as it loads.
 *       };
 *     },
 *
 *     render: function () {
 *       if (!this.state.user)
 *         return <LoadingUser/>;
 *
 *       return (
 *

 *
Welcome {this.state.user.name}!

 *
So far, you've received {this.state.streamBuffer.length} data!

 *

 *       );
 *     }
 *
 *   });
 *
 * When testing, use the `initialAsyncState` prop to simulate asynchronous
 * data fetching. When this prop is present, no attempt is made to retrieve
 * additional state via `getInitialAsyncState`.
 */
var AsyncState = {

  propTypes: {
    initialAsyncState: React.PropTypes.object
  },

  getInitialState: function () {
    return this.props.initialAsyncState || null;
  },

  updateAsyncState: function (state) {
    if (this.isMounted())
      this.setState(state);
  },

  componentDidMount: function () {
    if (this.props.initialAsyncState || typeof this.constructor.getInitialAsyncState !== 'function')
      return;

    resolveAsyncState(
      this.constructor.getInitialAsyncState(this.props.params, this.props.query, this.updateAsyncState),
      this.updateAsyncState
    );
  }

};

module.exports = AsyncState;
<|endoftext|>


<commit_before>if (enyo && enyo.version) {
	enyo.version.canvas = "2.4.0-pre.1";
}
<commit_msg>Update version string to 2.4.0-pre.2<commit_after>if (enyo && enyo.version) {
	enyo.version.canvas = "2.4.0-pre.2";
}
<|endoftext|>


<commit_before>module.exports = function (app, options) {
  var models = app.models;

  //use loopbacks standard API to disable all methods that JSON API
  //does not support.
  var isStatic = true;

  if (options.hideIrrelevantMethods !== false) {
    options.debug('Disable methods not supported by `jsonapi`. (Set `options.hideIrrelevantMethods = true` to reenable)');
    Object.keys(models).forEach(function (model) {
      ['upsert', 'exists', 'findOne', 'count', 'createChangeStream', 'updateAll'].forEach(function (method) {
        models[model].disableRemoteMethod(method, isStatic);
      });
    });
    options.debug('`upsert`, `exists`, `findOne`, `count`, `createChangeStream` and `updateAll` disabled for all models');
  }
};
<commit_msg>Update removeRemoteMethods.js<commit_after>module.exports = function (app, options) {
  var models = app.models;

  //use loopbacks standard API to disable all methods that JSON API
  //does not support.
  var isStatic = true;

  if (options.hideIrrelevantMethods !== false) {
    options.debug('Disable methods not supported by `jsonapi`. (Set `options.hideIrrelevantMethods = false` to re-enable)');
    Object.keys(models).forEach(function (model) {
      ['upsert', 'exists', 'findOne', 'count', 'createChangeStream', 'updateAll'].forEach(function (method) {
        models[model].disableRemoteMethod(method, isStatic);
      });
    });
    options.debug('`upsert`, `exists`, `findOne`, `count`, `createChangeStream` and `updateAll` disabled for all models');
  }
};
<|endoftext|>


<commit_before>import getMetaInfo from '../shared/getMetaInfo'
import generateServerInjector from './generateServerInjector'

export default function _inject (options = {}) {
  /**
   * Converts the state of the meta info object such that each item
   * can be compiled to a tag string on the server
   *
   * @this {Object} - Vue instance - ideally the root component
   * @return {Object} - server meta info with `toString` methods
   */
  return function inject () {
    // get meta info with sensible defaults
    const info = getMetaInfo(options)(this.$root)

    // generate server injectors
    for (let key in info) {
      if (info.hasOwnProperty(key) && key !== 'titleTemplate') {
        info[key] = generateServerInjector(options)(key, info[key])
      }
    }

    return info
  }
}
<commit_msg>generateServerInjector(): excluding titleChunk<commit_after>import getMetaInfo from '../shared/getMetaInfo'
import generateServerInjector from './generateServerInjector'

export default function _inject (options = {}) {
  /**
   * Converts the state of the meta info object such that each item
   * can be compiled to a tag string on the server
   *
   * @this {Object} - Vue instance - ideally the root component
   * @return {Object} - server meta info with `toString` methods
   */
  return function inject () {
    // get meta info with sensible defaults
    const info = getMetaInfo(options)(this.$root)

    // generate server injectors
    for (let key in info) {
      if (info.hasOwnProperty(key) && key !== 'titleTemplate' && key !== 'titleChunk') {
        info[key] = generateServerInjector(options)(key, info[key])
      }
    }

    return info
  }
}
<|endoftext|>


<commit_before>// Copyright (c) 2012 Titanium I.T. LLC. All rights reserved. See LICENSE.txt for details.
/*global desc, task, jake, fail, complete, directory*/

(function() {
	"use strict";

	if (!process.env.loose) console.log("For more forgiving test settings, use 'loose=true'");

	var fs = require("fs");
	var shell = require("shelljs");

	var browserify = require("browserify");
	var lint = require("./build/util/lint_runner.js");
	var nodeunit = require("./build/util/nodeunit_runner.js");
	var karma = require("./build/util/karma_runner.js");
	var versionChecker = require("./build/util/version_checker.js");
	var runServer = require("./src/_run_server.js");
	var path = require("path");

	var REQUIRED_BROWSERS = [
		"IE 8.0.0 (Windows 7)",
		"IE 9.0.0 (Windows 7)",
		"Firefox 32.0.0 (Mac OS X 10.8)",
		"Chrome 37.0.2062 (Mac OS X 10.8.5)",
		"Safari 6.1.6 (Mac OS X 10.8.5)",
		"Mobile Safari 7.0.0 (iOS 7.1)"
	];

	var GENERATED_DIR = "generated";
	var TEMP_TESTFILE_DIR = GENERATED_DIR + "/test";
	var BUILD_DIR = GENERATED_DIR + "/build";
	var BUILD_CLIENT_DIR = BUILD_DIR + "/client";

	directory(TEMP_TESTFILE_DIR);
	directory(BUILD_DIR);
	directory(BUILD_CLIENT_DIR);

	desc("Delete all generated files");
	task("clean", [], function() {
		jake.rmRf(GENERATED_DIR);
	});

	desc("Build and test");
	task("default", ["lint", "test"], function() {
		console.log("\n\nOK");
	});

	desc("Start Karma server for testing");
	task("karma", function() {
		karma.serve("build/karma.conf.js", complete, fail);
	}, {async: true});

	desc("Start WeeWikiPaint server for manual testing");
	task("run", ["build"], function() {
		console.log("Running server. Press Ctrl-C to stop.");
		runServer.runInteractively();
		// We never call complete() because we want the task to hang until the user presses 'Ctrl-C'.
	}, {async: true});

	desc("Lint everything");
	task("lint", ["lintNode", "lintClient"]);

	task("lintNode", ["nodeVersion"], function() {
		var passed = lint.validateFileList(nodeLintFiles(), nodeLintOptions(), {});
		if (!passed) fail("Lint failed");
	});

	task("lintClient", function() {
		var passed = lint.validateFileList(clientLintFiles(), clientLintOptions(), clientGlobals());
		if (!passed) fail("Lint failed");
	});

	desc("Test everything");
	task("test", ["testServer", "testClient", "testSmoke"]);

	desc("Test server code");
	task("testServer", ["nodeVersion", TEMP_TESTFILE_DIR], function() {
		nodeunit.runTests(serverTestFiles(), complete, fail);
	}, {async: true});

	desc("Test client code");
	task("testClient", [], function() {
		karma.runTests(REQUIRED_BROWSERS, complete, fail);
	}, {async: true});

	desc("End-to-end smoke tests");
	task("testSmoke", ["build"], function() {
		nodeunit.runTests(smokeTestFiles(), complete, fail);
	}, {async: true});

	desc("Bundle and build code");
	task("build", [BUILD_CLIENT_DIR], function() {
		shell.rm("-rf", BUILD_CLIENT_DIR + "/*");
		shell.cp("-R", "src/client/*.html", "src/client/*.css", "src/client/images", "src/client/vendor", BUILD_CLIENT_DIR);

		console.log("Bundling client files with Browserify...");
		var b = browserify();
		b.require("./src/client/client.js", {expose: "./client.js"} );
		b.require("./src/client/html_element.js", {expose: "./html_element.js"} );
		b.bundle({ debug: true }, function(err, bundle) {
			if (err) fail(err);
			fs.writeFileSync(BUILD_CLIENT_DIR + "/bundle.js", bundle);
			complete();
		});
	}, {async: true});

	desc("Deploy to Heroku");
	task("deploy", function() {
		console.log("To deploy to production:");
		console.log("1. Integrate ('jake integrate')");

		// Correction: Use "git push heroku integration:master" to deploy from integration branch.
		// Thanks to Jüri A, http://www.letscodejavascript.com/v3/comments/live/32#comment-798947003 .
		console.log("2. 'git push heroku integration:master' (or 'git push staging integration:master')");
		console.log("3. 'jake test'");
		console.log();
		console.log("To deploy latest code to staging server:");
		console.log("1. Make sure 'git status' is clean.");
		console.log("2. 'git push staging master");
		console.log("3. Visit http://wwp-staging.herokuapp.com/");
	});

//	desc("Ensure correct version of Node is present.");
	task("nodeVersion", [], function() {
		var deployedVersion = "v" + require("./package.json").engines.node;
		versionChecker.check("Node", !process.env.loose, deployedVersion, process.version, fail);
	});

	desc("Integration checklist");
	task("integrate", ["default"], function() {
		console.log("1. Make sure 'git status' is clean.");
		console.log("2. Build on the integration box.");
		console.log("   a. Walk over to integration box.");
		console.log("   b. 'git pull'");
		console.log("   c. 'npm rebuild'");
		console.log("   d. Check status for files that need to be .gitignore'd");
		console.log("   e. 'jake'");
		console.log("   f. If jake fails, stop! Try again after fixing the issue.");
		console.log("3. 'git checkout integration'");
		console.log("4. 'git merge master --no-ff --log'");
		console.log("5. 'git checkout master'");
	});

	desc("End-of-episode checklist");
	task("episode", [], function() {
		console.log("1. Save recording.");
		console.log("2. Double-check sound and framing.");
		console.log("3. Commit source code.");
		console.log("4. Check Windows compatibility");
		console.log("   a. Switch to Windows VM");
		console.log("   b. 'git pull'");
		console.log("   c. 'npm rebuild'");
		console.log("   d. Check status for files that need to be .gitignore'd");
		console.log("   e. 'jake'");
		console.log("5. Tag episode: 'git tag -a episodeXX -m \"End of episode XX\"'");
	});

	function serverTestFiles() {
		var testFiles = new jake.FileList();
		testFiles.include("src/server/**/_*_test.js");
		testFiles = testFiles.toArray();
		return testFiles;
	}

	function smokeTestFiles() {
		var testFiles = new jake.FileList();
		testFiles.include("src/_*_test.js");
		testFiles = testFiles.toArray();
		return testFiles;
	}

	function nodeLintFiles() {
		var javascriptFiles = new jake.FileList();
		javascriptFiles.include("*.js");
		javascriptFiles.include("build/util/*.js");
		javascriptFiles.include("src/server/**/*.js");
		javascriptFiles.include("src/*.js");
		return javascriptFiles.toArray();
	}

	function clientLintFiles() {
		var javascriptFiles = new jake.FileList();
		javascriptFiles.include("src/client/*.js");
		return javascriptFiles.toArray();
	}

	function nodeLintOptions() {
		var options = sharedLintOptions();
		options.node = true;
		return options;
	}

	function clientLintOptions() {
		var options = sharedLintOptions();
		options.browser = true;
		return options;
	}

	function sharedLintOptions() {
		return {
			bitwise: true,
			curly: false,
			eqeqeq: true,
			forin: true,
			immed: true,
			latedef: false,
			newcap: true,
			noarg: true,
			noempty: true,
			nonew: true,
			regexp: true,
			undef: true,
			strict: true,
			trailing: true
		};
	}

	function clientGlobals() {
		return {
			// Browserify
			require: false,
			module: false,
			exports: false,

			// Mocha / expect.js
			describe: false,
			it: false,
			expect: false,
			dump: false,
			beforeEach: false,
			afterEach: false,

			// Browser
			console: false
		};
	}

}());
<commit_msg>Upgraded Mac Safari 6.1.6 --> 6.2.0<commit_after>// Copyright (c) 2012 Titanium I.T. LLC. All rights reserved. See LICENSE.txt for details.
/*global desc, task, jake, fail, complete, directory*/

(function() {
	"use strict";

	if (!process.env.loose) console.log("For more forgiving test settings, use 'loose=true'");

	var fs = require("fs");
	var shell = require("shelljs");

	var browserify = require("browserify");
	var lint = require("./build/util/lint_runner.js");
	var nodeunit = require("./build/util/nodeunit_runner.js");
	var karma = require("./build/util/karma_runner.js");
	var versionChecker = require("./build/util/version_checker.js");
	var runServer = require("./src/_run_server.js");
	var path = require("path");

	var REQUIRED_BROWSERS = [
		"IE 8.0.0 (Windows 7)",
		"IE 9.0.0 (Windows 7)",
		"Firefox 32.0.0 (Mac OS X 10.8)",
		"Chrome 37.0.2062 (Mac OS X 10.8.5)",
		"Safari 6.2.0 (Mac OS X 10.8.5)",
		"Mobile Safari 7.0.0 (iOS 7.1)"
	];

	var GENERATED_DIR = "generated";
	var TEMP_TESTFILE_DIR = GENERATED_DIR + "/test";
	var BUILD_DIR = GENERATED_DIR + "/build";
	var BUILD_CLIENT_DIR = BUILD_DIR + "/client";

	directory(TEMP_TESTFILE_DIR);
	directory(BUILD_DIR);
	directory(BUILD_CLIENT_DIR);

	desc("Delete all generated files");
	task("clean", [], function() {
		jake.rmRf(GENERATED_DIR);
	});

	desc("Build and test");
	task("default", ["lint", "test"], function() {
		console.log("\n\nOK");
	});

	desc("Start Karma server for testing");
	task("karma", function() {
		karma.serve("build/karma.conf.js", complete, fail);
	}, {async: true});

	desc("Start WeeWikiPaint server for manual testing");
	task("run", ["build"], function() {
		console.log("Running server. Press Ctrl-C to stop.");
		runServer.runInteractively();
		// We never call complete() because we want the task to hang until the user presses 'Ctrl-C'.
	}, {async: true});

	desc("Lint everything");
	task("lint", ["lintNode", "lintClient"]);

	task("lintNode", ["nodeVersion"], function() {
		var passed = lint.validateFileList(nodeLintFiles(), nodeLintOptions(), {});
		if (!passed) fail("Lint failed");
	});

	task("lintClient", function() {
		var passed = lint.validateFileList(clientLintFiles(), clientLintOptions(), clientGlobals());
		if (!passed) fail("Lint failed");
	});

	desc("Test everything");
	task("test", ["testServer", "testClient", "testSmoke"]);

	desc("Test server code");
	task("testServer", ["nodeVersion", TEMP_TESTFILE_DIR], function() {
		nodeunit.runTests(serverTestFiles(), complete, fail);
	}, {async: true});

	desc("Test client code");
	task("testClient", [], function() {
		karma.runTests(REQUIRED_BROWSERS, complete, fail);
	}, {async: true});

	desc("End-to-end smoke tests");
	task("testSmoke", ["build"], function() {
		nodeunit.runTests(smokeTestFiles(), complete, fail);
	}, {async: true});

	desc("Bundle and build code");
	task("build", [BUILD_CLIENT_DIR], function() {
		shell.rm("-rf", BUILD_CLIENT_DIR + "/*");
		shell.cp("-R", "src/client/*.html", "src/client/*.css", "src/client/images", "src/client/vendor", BUILD_CLIENT_DIR);

		console.log("Bundling client files with Browserify...");
		var b = browserify();
		b.require("./src/client/client.js", {expose: "./client.js"} );
		b.require("./src/client/html_element.js", {expose: "./html_element.js"} );
		b.bundle({ debug: true }, function(err, bundle) {
			if (err) fail(err);
			fs.writeFileSync(BUILD_CLIENT_DIR + "/bundle.js", bundle);
			complete();
		});
	}, {async: true});

	desc("Deploy to Heroku");
	task("deploy", function() {
		console.log("To deploy to production:");
		console.log("1. Integrate ('jake integrate')");

		// Correction: Use "git push heroku integration:master" to deploy from integration branch.
		// Thanks to Jüri A, http://www.letscodejavascript.com/v3/comments/live/32#comment-798947003 .
		console.log("2. 'git push heroku integration:master' (or 'git push staging integration:master')");
		console.log("3. 'jake test'");
		console.log();
		console.log("To deploy latest code to staging server:");
		console.log("1. Make sure 'git status' is clean.");
		console.log("2. 'git push staging master");
		console.log("3. Visit http://wwp-staging.herokuapp.com/");
	});

//	desc("Ensure correct version of Node is present.");
	task("nodeVersion", [], function() {
		var deployedVersion = "v" + require("./package.json").engines.node;
		versionChecker.check("Node", !process.env.loose, deployedVersion, process.version, fail);
	});

	desc("Integration checklist");
	task("integrate", ["default"], function() {
		console.log("1. Make sure 'git status' is clean.");
		console.log("2. Build on the integration box.");
		console.log("   a. Walk over to integration box.");
		console.log("   b. 'git pull'");
		console.log("   c. 'npm rebuild'");
		console.log("   d. Check status for files that need to be .gitignore'd");
		console.log("   e. 'jake'");
		console.log("   f. If jake fails, stop! Try again after fixing the issue.");
		console.log("3. 'git checkout integration'");
		console.log("4. 'git merge master --no-ff --log'");
		console.log("5. 'git checkout master'");
	});

	desc("End-of-episode checklist");
	task("episode", [], function() {
		console.log("1. Save recording.");
		console.log("2. Double-check sound and framing.");
		console.log("3. Commit source code.");
		console.log("4. Check Windows compatibility");
		console.log("   a. Switch to Windows VM");
		console.log("   b. 'git pull'");
		console.log("   c. 'npm rebuild'");
		console.log("   d. Check status for files that need to be .gitignore'd");
		console.log("   e. 'jake'");
		console.log("5. Tag episode: 'git tag -a episodeXX -m \"End of episode XX\"'");
	});

	function serverTestFiles() {
		var testFiles = new jake.FileList();
		testFiles.include("src/server/**/_*_test.js");
		testFiles = testFiles.toArray();
		return testFiles;
	}

	function smokeTestFiles() {
		var testFiles = new jake.FileList();
		testFiles.include("src/_*_test.js");
		testFiles = testFiles.toArray();
		return testFiles;
	}

	function nodeLintFiles() {
		var javascriptFiles = new jake.FileList();
		javascriptFiles.include("*.js");
		javascriptFiles.include("build/util/*.js");
		javascriptFiles.include("src/server/**/*.js");
		javascriptFiles.include("src/*.js");
		return javascriptFiles.toArray();
	}

	function clientLintFiles() {
		var javascriptFiles = new jake.FileList();
		javascriptFiles.include("src/client/*.js");
		return javascriptFiles.toArray();
	}

	function nodeLintOptions() {
		var options = sharedLintOptions();
		options.node = true;
		return options;
	}

	function clientLintOptions() {
		var options = sharedLintOptions();
		options.browser = true;
		return options;
	}

	function sharedLintOptions() {
		return {
			bitwise: true,
			curly: false,
			eqeqeq: true,
			forin: true,
			immed: true,
			latedef: false,
			newcap: true,
			noarg: true,
			noempty: true,
			nonew: true,
			regexp: true,
			undef: true,
			strict: true,
			trailing: true
		};
	}

	function clientGlobals() {
		return {
			// Browserify
			require: false,
			module: false,
			exports: false,

			// Mocha / expect.js
			describe: false,
			it: false,
			expect: false,
			dump: false,
			beforeEach: false,
			afterEach: false,

			// Browser
			console: false
		};
	}

}());
<|endoftext|>


<commit_before>'use strict';

const has = require('has');
const { Strategy } = require('passport');
const jwt = require('jsonwebtoken');
const { BannedError } = require('../errors');

/** @typedef {import('../models').User} User */

/**
 * @param {Record<string, string>} cookies
 */
function getCookieToken(cookies) {
  return cookies && cookies.uwsession;
}

/**
 * @param {import('qs').ParsedQs} query
 */
function getQueryToken(query) {
  return query && typeof query.token === 'string' ? query.token : null;
}

/**
 * @param {import('http').IncomingHttpHeaders} headers
 */
function getHeaderToken(headers) {
  if (headers.authorization) {
    const parts = headers.authorization.split(' ');
    if (parts[0].toLowerCase() === 'jwt') {
      return parts[1];
    }
  }
  return null;
}

/**
 * @param {unknown} obj
 * @returns {obj is { id: string }}
 */
function isUserIDToken(obj) {
  return typeof obj === 'object'
    && obj !== null
    && has(obj, 'id')
    && typeof obj.id === 'string';
}

/** @typedef {(claim: { id: string }) => Promise<User|null>} GetUserFn */

class JWTStrategy extends Strategy {
  /** @type {Buffer|string} */
  #secret

  /** @type {GetUserFn} */
  #getUser

  /**
   * @param {Buffer|string} secret
   * @param {GetUserFn} getUser
   */
  constructor(secret, getUser) {
    super();
    this.#secret = secret;
    this.#getUser = getUser;
  }

  /**
   * @param {import('express').Request} req
   */
  authenticate(req) {
    this.authenticateP(req).catch((err) => {
      this.error(err);
    });
  }

  /**
   * @param {import('express').Request} req
   * @private
   */
  async authenticateP(req) {
    const { bans } = req.uwave;

    const token = getQueryToken(req.query)
      || getHeaderToken(req.headers)
      || getCookieToken(req.cookies);
    if (!token) {
      return this.pass();
    }

    /** @type {unknown} */
    let value;
    try {
      value = jwt.verify(token, this.#secret);
    } catch (e) {
      return this.pass();
    }

    if (!isUserIDToken(value)) {
      return this.pass();
    }

    const user = await this.#getUser(value);
    if (!user) {
      return this.pass();
    }

    if (user.pendingActivation) {
      req.pendingUser = user;
      return this.pass();
    }

    if (await bans.isBanned(user)) {
      throw new BannedError();
    }

    return this.success(user);
  }
}

module.exports = JWTStrategy;
<commit_msg>Remove private fields that caused auth bugs<commit_after>'use strict';

const has = require('has');
const { Strategy } = require('passport');
const jwt = require('jsonwebtoken');
const { BannedError } = require('../errors');

/** @typedef {import('../models').User} User */

/**
 * @param {Record<string, string>} cookies
 */
function getCookieToken(cookies) {
  return cookies && cookies.uwsession;
}

/**
 * @param {import('qs').ParsedQs} query
 */
function getQueryToken(query) {
  return query && typeof query.token === 'string' ? query.token : null;
}

/**
 * @param {import('http').IncomingHttpHeaders} headers
 */
function getHeaderToken(headers) {
  if (headers.authorization) {
    const parts = headers.authorization.split(' ');
    if (parts[0].toLowerCase() === 'jwt') {
      return parts[1];
    }
  }
  return null;
}

/**
 * @param {unknown} obj
 * @returns {obj is { id: string }}
 */
function isUserIDToken(obj) {
  return typeof obj === 'object'
    && obj !== null
    && has(obj, 'id')
    && typeof obj.id === 'string';
}

/** @typedef {(claim: { id: string }) => Promise<User|null>} GetUserFn */

class JWTStrategy extends Strategy {
  /**
   * @param {Buffer|string} secret
   * @param {GetUserFn} getUser
   */
  constructor(secret, getUser) {
    super();
    /** @private */
    this.secret = secret;
    /** @private */
    this.getUser = getUser;
  }

  /**
   * @param {import('express').Request} req
   */
  authenticate(req) {
    this.authenticateP(req).catch((err) => {
      this.error(err);
    });
  }

  /**
   * @param {import('express').Request} req
   * @private
   */
  async authenticateP(req) {
    const { bans } = req.uwave;

    const token = getQueryToken(req.query)
      || getHeaderToken(req.headers)
      || getCookieToken(req.cookies);
    if (!token) {
      return this.pass();
    }

    /** @type {unknown} */
    let value;
    try {
      value = jwt.verify(token, this.secret);
    } catch (e) {
      return this.pass();
    }

    if (!isUserIDToken(value)) {
      return this.pass();
    }

    const user = await this.getUser(value);
    if (!user) {
      return this.pass();
    }

    if (user.pendingActivation) {
      req.pendingUser = user;
      return this.pass();
    }

    if (await bans.isBanned(user)) {
      throw new BannedError();
    }

    return this.success(user);
  }
}

module.exports = JWTStrategy;
<|endoftext|>


<commit_before>10
<commit_msg>Easy testing of version<commit_after>exports.number = 10;
exports.ensureAtLeast = function(n) {
  if (exports.number < n) {
    process.exit(1);
  }
  process.exit(0);
};
<|endoftext|>