<commit_before>/*
 * @license
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

foam.CLASS({
  package: 'foam.graphics',
  name: 'Matrix',

  properties: [
    {
      name: 'a',
      class: 'Simple'
    },
    {
      name: 'b',
      class: 'Simple'
    },
    {
      name: 'c',
      class: 'Simple'
    },
    {
      name: 'd',
      class: 'Simple'
    },
    {
      name: 'e',
      class: 'Simple'
    },
    {
      name: 'f',
      class: 'Simple'
    },
    {
      name: 'g',
      class: 'Simple'
    },
    {
      name: 'h',
      class: 'Simple'
    },
    {
      name: 'i',
      class: 'Simple'
    }
  ],

  methods: [
    function initArgs() {
      this.a = 1; this.b = 0; this.c = 0;
      this.d = 0; this.e = 1; this.f = 0;
      this.g = 0; this.h = 0; this.i = 1;
    },

    function mul(m) {
      var a = this.a, b = this.b, c = this.c,
          d = this.d, e = this.e, f = this.f,
          g = this.g, h = this.h, i = this.i;

      this.a = a * m.a + b * m.d + c * m.g;
      this.b = a * m.b + b * m.e + c * m.h;
      this.c = a * m.c + b * m.f + c * m.i;

      this.d = d * m.a + e * m.d + f * m.g;
      this.e = d * m.b + e * m.e + f * m.h;
      this.f = d * m.c + e * m.f + f * m.i;

      this.g = g * m.a + h * m.d + i * m.g;
      this.h = g * m.b + h * m.e + i * m.h;
      this.i = g * m.c + h * m.f + i * m.i;

      return this;
    },

    function invert() {
      // a b c    a d g
      // d e f -> b e h
      // g h i    c f i
      var tmp = this.b;
      this.b = this.d;
      this.d = tmp;

      tmp = this.c;
      this.c = this.g;
      this.g = tmp;

      tmp = this.f;
      this.f = this.h;
      this.h = tmp;
      return this;
    }
  ]
});

// TODO: Perhaps these methods should be moved to matrix.
foam.CLASS({
  package: 'foam.graphics',
  name: 'Transform',

  requires: [
    'foam.graphics.Matrix'
  ],

  properties: [
    {
      name: 'matrix',
      factory: function() {
        return this.Matrix.create();
      },
    },
    {
      name: 'm2',
      factory: function() {
        return this.Matrix.create();
      }
    }
  ],

  methods: [
    function reset() {
      this.matrix = this.Matrix.create();
    },

    function translate(dx, dy) {
      //      var m = this.Matrix.create()
      var m = this.m2;
      m.a = 1; m.b = 0; m.c = dx;
      m.d = 0; m.e = 1; m.f = dy;
      m.g = 0; m.h = 0; m.i = 1;
      this.matrix.mul(m);
    },

    function skew(x, y) {
      //      var m = this.Matrix.create();
      var m = this.m2;
      m.a = 1; m.b = x; m.c = 0;
      m.d = y; m.e = 1; m.f = 0;
      m.g = 0; m.h = 0; m.i = 1;
      this.matrix.mul(m);
    },

    function affine(m) {
      this.matrix.mul(m);
    },

    function scale(x, y) {
      //      var m = this.Matrix.create();
      var m = this.m2;
      m.a = x; m.b = 0; m.c = 0;
      m.d = 0; m.e = y; m.f = 0;
      m.g = 0; m.h = 0; m.i = 1;
      this.matrix.mul(m);
    },

    function rotate(a) {
      //      var m = this.Matrix.create();
      var m = this.m2;
      m.a = Math.cos(a);  m.b = Math.sin(a); m.c = 0;
      m.d = -Math.sin(a); m.e = Math.cos(a); m.f = 0;
      m.g = 0;            m.h = 0;           m.i = 1;
      this.matrix.mul(m);
    }
  ]
});

foam.CLASS({
  package: 'foam.graphics',
  name: 'CView',

  topics: ['invalidated'],

  requires: [
    'foam.graphics.Transform'
  ],

  properties: [
    {
      name: 'rotation',
      class: 'Float'
    },
    {
      name: 'originX',
      class: 'Float'
    },
    {
      name: 'originY',
      class: 'Float'
    },
    {
      name: 'scaleX',
      class: 'Float',
      defaultValue: 1
    },
    {
      name: 'scaleY',
      class: 'Float',
      defaultValue: 1
    },
    {
      name: 'skewX',
      class: 'Float'
    },
    {
      name: 'skewY',
      class: 'Float'
    },
    {
      name: 'x',
      class: 'Float'
    },
    {
      name: 'y',
      class: 'Float'
    },
    {
      name: 'alpha',
      class: 'Float',
      defaultValue: 1
    },
    {
      name: 'strokeStyle',
      defaultValue: 'black'
    },
    {
      name: 'fillStyle',
      defaultValue: 'black'
    },
    {
      name: 'shadowColor'
    },
    {
      name: 'shadowBlur'
    },
    {
      name: 'children',
      factory: function() { return []; },
      postSet: function(o, n) {
        for ( var i = 0 ; o && i < o.length ; i++ ) this.removeChild_(o[i]);
        for ( var i = 0 ; n && i < n.length ; i++ ) this.addChild_(n[i]);
      }
    },
    {
      name: 'state',
      defaultValue: 'initial'
    },
    {
      name: 'transform',
      expression: function(originX, originY, rotation, scaleX, scaleY, skewX, skewY, x, y) {
        var t = this.Transform.create()
        t.translate(x, y);
        t.rotate(rotation);
        t.skew(skewX, skewY);
        t.scale(scaleX, scaleY);
        t.translate(-originX, -originY);

        return t;
      }
    }
  ],

  listeners: [
    {
      name: 'onChildUpdate',
      code: function() {
        this.invalidated.publish();
      }
    }
  ],

  methods: [
    function initCView() {
      this.propertyChange.subscribe(function() {
        this.invalidated.publish();
      }.bind(this));
    },

    function maybeInitCView() {
      if ( this.state == 'initial' ) {
        this.initCView();
        this.state = 'initailized'
      }
    },

    function paint(x) {
      this.maybeInitCView();
      x.save();
      x.globalAlpha *= this.alpha;
      x.strokeStyle = this.strokeStyle.toCanvasStyle ?
        this.strokeStyle.toCanvasStyle(x) : this.strokeStyle;
      x.fillStyle = this.fillStyle.toCanvasStyle ?
        this.fillStyle.toCanvasStyle(x) : this.fillStyle;
      this.doTransform(x);
      if ( this.shadowColor && this.shadowBlur ) {
        x.shadowColor = this.shadowColor;
        x.shadowBlur  = this.shadowBlur;
      }
      this.paintSelf(x);
      this.paintChildren(x);
      x.restore();
    },

    function doTransform(x) {
      var t = this.transform.matrix;
      x.transform(t.a, t.d, t.b, t.e, t.c, t.f);
    },

    function paintChildren(x) {
      for ( var i = 0 ; i < this.children.length ; i++ ) {
        this.children[i].paint(x);
      }
    },

    function addChild_(c) {
      c.invalidated.subscribe(this.onChildUpdate);
      return c;
    },

    function removeChild_(c) {
      c.invalidated.unsubscribe(this.onChildUpdate);
      return c;
    },

    function addChildren() {
      for ( var i = 0 ; i < arguments.length ; i++ )
        this.children.push(arguments[i]);
    },

    function paintSelf(x) {}
  ]
});

foam.CLASS({
  package: 'foam.graphics',
  name: 'Box',
  extends: 'foam.graphics.CView',

  properties: [
    {
      name: 'width',
      class: 'Float'
    },
    {
      name: 'height',
      class: 'Float'
    },
    {
      name: 'fill',
      class: 'Boolean',
      defaultValue: false
    },
    {
      name: 'stroke',
      class: 'Boolean',
      defaultValue: true
    }
  ],

  methods: [
    function paintSelf(x) {
      x.beginPath();
      x.rect(0, 0, this.width, this.height);
      if ( this.stroke ) x.stroke();
      if ( this.fill ) x.fill();
    }
  ]
});

foam.CLASS({
  package: 'foam.graphics',
  name: 'Arc',
  extends: 'foam.graphics.CView',

  properties: [
    {
      name: 'radius',
      class: 'Float'
    },
    {
      name: 'start',
      class: 'Float'
    },
    {
      name: 'end',
      class: 'Float'
    },
    {
      name: 'arcWidth',
      class: 'Float'
    },
    {
      name: 'fill',
      class: 'Boolean',
      defaultValue: false
    },
    {
      name: 'stroke',
      class: 'Boolean',
      defaultValue: true
    }
  ],

  methods: [
    function paintSelf(x) {
      x.beginPath();
      x.arc(0, 0, this.radius, this.start, this.end);

      if ( this.fill ) {
        x.fill();
      }
      if ( this.stroke ) {
        x.lineWidth = this.arcWidth;
        x.stroke();
      }
    }
  ]
});

foam.CLASS({
  package: 'foam.graphics',
  name: 'Circle',
  extends: 'foam.graphics.Arc',

  properties: [
    {
      name: 'start',
      defaultValue: 0
    },
    {
      name: 'end',
      defaultValue: 2*Math.PI
    }
  ]
});

foam.CLASS({
  package: 'foam.graphics',
  name: 'Canvas',

  imports: [
    '$'
  ],

  properties: [
    {
      name: 'id'
    },
    {
      name: 'width',
    },
    {
      name: 'height',
    },
    {
      name: 'element',
      factory: function() {
        var e = this.$(this.id);
        e.width = this.width;
        e.height = this.height;
        return e;
      },
      postSet: function(_, e) {
        e.width = this.width;
        e.height = this.height;
      }
    },
    {
      name: 'context',
      factory: function() {
        return this.element.getContext('2d');
      }
    },
    {
      name: 'cview',
      postSet: function(o, n) {
        o && o.invalidated.unsubscribe(this.paint);
        n && n.invalidated.subscribe(this.paint);
        this.paint();
      }
    }
  ],

  listeners: [
    {
      name: 'paint',
      isFramed: true,
      code: function() {
        this.erase();
        this.cview && this.cview.paint(this.context);
      }
    }
  ],

  templates: [
    {
      name: 'toHTML',
      template: '<canvas id="<%= this.id %>"></canvas>'
    }
  ],

  methods: [
    function erase() {
      this.element.width = this.element.width;
    }
  ]
});

foam.CLASS({
  package: 'foam.graphics',
  name: 'Gradient',

  properties: [
    'x0', 'y0', 'r0',
    'x1', 'y1', 'r1',
    {
      name: 'radial',
      class: 'Boolean',
      defaultValue: false
    },
    {
      name: 'colors',
      factory: function() {
        return [];
      }
    }
  ],

  methods: [
    function toCanvasStyle(x) {
      var t = this;
      var g = this.radial ?
        x.createRadialGradient(t.x0, t.y0, t.r0, t.x1, t.y1, t.r1) :
        x.createLinearGradient(t.x0, t.y0, t.x1, t.y1) ;

      for ( var i = 0 ; i < t.colors.length ; i++ )
        g.addColorStop(t.colors[i][0], t.colors[i][1]);

      return g;
    }
  ]
});
<commit_msg>Code cleanup.<commit_after>/*
 * @license
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

foam.CLASS({
  package: 'foam.graphics',
  name: 'Matrix',

  properties: [
    {
      name: 'a',
      class: 'Simple'
    },
    {
      name: 'b',
      class: 'Simple'
    },
    {
      name: 'c',
      class: 'Simple'
    },
    {
      name: 'd',
      class: 'Simple'
    },
    {
      name: 'e',
      class: 'Simple'
    },
    {
      name: 'f',
      class: 'Simple'
    },
    {
      name: 'g',
      class: 'Simple'
    },
    {
      name: 'h',
      class: 'Simple'
    },
    {
      name: 'i',
      class: 'Simple'
    }
  ],

  methods: [
    function initArgs() {
      this.a = 1; this.b = 0; this.c = 0;
      this.d = 0; this.e = 1; this.f = 0;
      this.g = 0; this.h = 0; this.i = 1;
    },

    function mul(m) {
      var a = this.a, b = this.b, c = this.c,
          d = this.d, e = this.e, f = this.f,
          g = this.g, h = this.h, i = this.i;

      this.a = a * m.a + b * m.d + c * m.g;
      this.b = a * m.b + b * m.e + c * m.h;
      this.c = a * m.c + b * m.f + c * m.i;

      this.d = d * m.a + e * m.d + f * m.g;
      this.e = d * m.b + e * m.e + f * m.h;
      this.f = d * m.c + e * m.f + f * m.i;

      this.g = g * m.a + h * m.d + i * m.g;
      this.h = g * m.b + h * m.e + i * m.h;
      this.i = g * m.c + h * m.f + i * m.i;

      return this;
    },

    function invert() {
      // a b c    a d g
      // d e f -> b e h
      // g h i    c f i
      var tmp = this.b;
      this.b = this.d;
      this.d = tmp;

      tmp = this.c;
      this.c = this.g;
      this.g = tmp;

      tmp = this.f;
      this.f = this.h;
      this.h = tmp;
      return this;
    }
  ]
});

// TODO: Perhaps these methods should be moved to matrix.
foam.CLASS({
  package: 'foam.graphics',
  name: 'Transform',

  requires: [
    'foam.graphics.Matrix'
  ],

  properties: [
    {
      name: 'matrix',
      factory: function() {
        return this.Matrix.create();
      },
    },
    {
      name: 'm2',
      factory: function() {
        return this.Matrix.create();
      }
    }
  ],

  methods: [
    function reset() {
      this.matrix = this.Matrix.create();
    },

    function translate(dx, dy) {
      //      var m = this.Matrix.create()
      var m = this.m2;
      m.a = 1; m.b = 0; m.c = dx;
      m.d = 0; m.e = 1; m.f = dy;
      m.g = 0; m.h = 0; m.i = 1;
      this.matrix.mul(m);
    },

    function skew(x, y) {
      //      var m = this.Matrix.create();
      var m = this.m2;
      m.a = 1; m.b = x; m.c = 0;
      m.d = y; m.e = 1; m.f = 0;
      m.g = 0; m.h = 0; m.i = 1;
      this.matrix.mul(m);
    },

    function affine(m) {
      this.matrix.mul(m);
    },

    function scale(x, y) {
      //      var m = this.Matrix.create();
      var m = this.m2;
      m.a = x; m.b = 0; m.c = 0;
      m.d = 0; m.e = y; m.f = 0;
      m.g = 0; m.h = 0; m.i = 1;
      this.matrix.mul(m);
    },

    function rotate(a) {
      //      var m = this.Matrix.create();
      var m = this.m2;
      m.a = Math.cos(a);  m.b = Math.sin(a); m.c = 0;
      m.d = -Math.sin(a); m.e = Math.cos(a); m.f = 0;
      m.g = 0;            m.h = 0;           m.i = 1;
      this.matrix.mul(m);
    }
  ]
});

foam.CLASS({
  package: 'foam.graphics',
  name: 'CView',

  topics: [ 'invalidated' ],

  requires: [ 'foam.graphics.Transform' ],

  properties: [
    {
      name: 'rotation',
      class: 'Float'
    },
    {
      name: 'originX',
      class: 'Float'
    },
    {
      name: 'originY',
      class: 'Float'
    },
    {
      name: 'scaleX',
      class: 'Float',
      defaultValue: 1
    },
    {
      name: 'scaleY',
      class: 'Float',
      defaultValue: 1
    },
    {
      name: 'skewX',
      class: 'Float'
    },
    {
      name: 'skewY',
      class: 'Float'
    },
    {
      name: 'x',
      class: 'Float'
    },
    {
      name: 'y',
      class: 'Float'
    },
    {
      name: 'alpha',
      class: 'Float',
      defaultValue: 1
    },
    {
      name: 'strokeStyle',
      defaultValue: 'black'
    },
    {
      name: 'fillStyle',
      defaultValue: 'black'
    },
    {
      name: 'shadowColor'
    },
    {
      name: 'shadowBlur'
    },
    {
      name: 'children',
      factory: function() { return []; },
      postSet: function(o, n) {
        for ( var i = 0 ; o && i < o.length ; i++ ) this.removeChild_(o[i]);
        for ( var i = 0 ; n && i < n.length ; i++ ) this.addChild_(n[i]);
      }
    },
    {
      name: 'state',
      defaultValue: 'initial'
    },
    {
      name: 'transform',
      expression: function(originX, originY, rotation, scaleX, scaleY, skewX, skewY, x, y) {
        var t = this.Transform.create()
        t.translate(x, y);
        t.rotate(rotation);
        t.skew(skewX, skewY);
        t.scale(scaleX, scaleY);
        t.translate(-originX, -originY);

        return t;
      }
    }
  ],

  methods: [
    function initCView() {
      this.propertyChange.subscribe(function() {
        this.invalidated.publish();
      }.bind(this));
    },

    function maybeInitCView() {
      if ( this.state == 'initial' ) {
        this.initCView();
        this.state = 'initailized'
      }
    },

    function paint(x) {
      this.maybeInitCView();
      x.save();
      x.globalAlpha *= this.alpha;
      x.strokeStyle = this.strokeStyle.toCanvasStyle ?
        this.strokeStyle.toCanvasStyle(x) : this.strokeStyle;
      x.fillStyle = this.fillStyle.toCanvasStyle ?
        this.fillStyle.toCanvasStyle(x) : this.fillStyle;
      this.doTransform(x);
      if ( this.shadowColor && this.shadowBlur ) {
        x.shadowColor = this.shadowColor;
        x.shadowBlur  = this.shadowBlur;
      }
      this.paintSelf(x);
      this.paintChildren(x);
      x.restore();
    },

    function doTransform(x) {
      var t = this.transform.matrix;
      x.transform(t.a, t.d, t.b, t.e, t.c, t.f);
    },

    function paintChildren(x) {
      for ( var i = 0 ; i < this.children.length ; i++ ) {
        this.children[i].paint(x);
      }
    },

    function addChild_(c) {
      c.invalidated.subscribe(this.onChildUpdate);
      return c;
    },

    function removeChild_(c) {
      c.invalidated.unsubscribe(this.onChildUpdate);
      return c;
    },

    function addChildren() {
      for ( var i = 0 ; i < arguments.length ; i++ )
        this.children.push(arguments[i]);
    },

    function paintSelf(x) {}
  ],

  listeners: [
    function onChildUpdate() { this.invalidated.publish(); }
  ]
});

foam.CLASS({
  package: 'foam.graphics',
  name: 'Box',
  extends: 'foam.graphics.CView',

  properties: [
    {
      name: 'width',
      class: 'Float'
    },
    {
      name: 'height',
      class: 'Float'
    },
    {
      name: 'fill',
      class: 'Boolean',
      defaultValue: false
    },
    {
      name: 'stroke',
      class: 'Boolean',
      defaultValue: true
    }
  ],

  methods: [
    function paintSelf(x) {
      x.beginPath();
      x.rect(0, 0, this.width, this.height);
      if ( this.stroke ) x.stroke();
      if ( this.fill ) x.fill();
    }
  ]
});

foam.CLASS({
  package: 'foam.graphics',
  name: 'Arc',
  extends: 'foam.graphics.CView',

  properties: [
    {
      name: 'radius',
      class: 'Float'
    },
    {
      name: 'start',
      class: 'Float'
    },
    {
      name: 'end',
      class: 'Float'
    },
    {
      name: 'arcWidth',
      class: 'Float'
    },
    {
      name: 'fill',
      class: 'Boolean',
      defaultValue: false
    },
    {
      name: 'stroke',
      class: 'Boolean',
      defaultValue: true
    }
  ],

  methods: [
    function paintSelf(x) {
      x.beginPath();
      x.arc(0, 0, this.radius, this.start, this.end);

      if ( this.fill ) {
        x.fill();
      }
      if ( this.stroke ) {
        x.lineWidth = this.arcWidth;
        x.stroke();
      }
    }
  ]
});

foam.CLASS({
  package: 'foam.graphics',
  name: 'Circle',
  extends: 'foam.graphics.Arc',

  properties: [
    {
      name: 'start',
      defaultValue: 0
    },
    {
      name: 'end',
      defaultValue: 2*Math.PI
    }
  ]
});

foam.CLASS({
  package: 'foam.graphics',
  name: 'Canvas',

  imports: [ '$' ],

  properties: [
    {
      name: 'id'
    },
    {
      name: 'width',
    },
    {
      name: 'height',
    },
    {
      name: 'element',
      factory: function() {
        var e = this.$(this.id);
        e.width = this.width;
        e.height = this.height;
        return e;
      },
      postSet: function(_, e) {
        e.width = this.width;
        e.height = this.height;
      }
    },
    {
      name: 'context',
      factory: function() {
        return this.element.getContext('2d');
      }
    },
    {
      name: 'cview',
      postSet: function(o, n) {
        o && o.invalidated.unsubscribe(this.paint);
        n && n.invalidated.subscribe(this.paint);
        this.paint();
      }
    }
  ],

  methods: [
    function erase() {
      this.element.width = this.element.width;
    }
  ],

  listeners: [
    {
      name: 'paint',
      isFramed: true,
      code: function() {
        this.erase();
        this.cview && this.cview.paint(this.context);
      }
    }
  ],

  templates: [
    {
      name: 'toHTML',
      template: '<canvas id="<%= this.id %>"></canvas>'
    }
  ]
});

foam.CLASS({
  package: 'foam.graphics',
  name: 'Gradient',

  properties: [
    'x0', 'y0', 'r0',
    'x1', 'y1', 'r1',
    {
      name: 'radial',
      class: 'Boolean',
      defaultValue: false
    },
    {
      name: 'colors',
      factory: function() {
        return [];
      }
    }
  ],

  methods: [
    function toCanvasStyle(x) {
      var t = this;
      var g = this.radial ?
        x.createRadialGradient(t.x0, t.y0, t.r0, t.x1, t.y1, t.r1) :
        x.createLinearGradient(t.x0, t.y0, t.x1, t.y1) ;

      for ( var i = 0 ; i < t.colors.length ; i++ )
        g.addColorStop(t.colors[i][0], t.colors[i][1]);

      return g;
    }
  ]
});
<|endoftext|>


<commit_before>'use strict';

const co = require('co');
const cheerio = require('cheerio');

const ph = require('./helpers/phantom.js');
const db = require('./db.js');
const parse = require('./parser.js');
const format = require('./format.js');
const helpers = require('./helpers/helpers.js');
const db_helpers = require('./helpers/db_helpers.js');

const CONFIG = require('./config/config.json');
const collection = db.get('data');
let USER_ID = null;
let logs_written = 0;
let URL;
let instance;

module.exports = exports = {};

exports.work = function(user_id) {
    if (!user_id) {
        throw Error('No user ID supplied.');
    }

    USER_ID = user_id;
    URL = CONFIG.url + USER_ID;
    scrape();
};

function scrape() {
    co(function*() {
        const html = yield * getPageContent(URL);
        const $ = cheerio.load(html);
        if (!isUserPageOpen($)) {
            helpers.terminate('Cannot scrape page', 'Profile is either hidden, not existing or deleted');
        }
        const user_data = collectUserData($);

        const user_updates = yield * checkUserDataForUpdates(user_data);
        if (user_updates) {
            yield collection.insert(user_data);
            logs_written++;
        }

        const formatted_data = prepareConsoleOutput(user_data, user_updates);
        helpers.clearConsole();
        process.stdout.write(formatted_data);
    })
    .then(() => {
        const timeout = CONFIG.interval * 1000;
        setTimeout(scrape, timeout);
    })
    .catch(err => {
        console.log(err);
    });
}

function* getPageContent(url) {
    if (!instance) {
        instance = yield * ph.initPhantomInstance();
    }
    process.stdout.write('\nFetching data...');
    const html = yield * ph.fetchPageContent(url, instance, false);

    return html;
}

function isUserPageOpen($) {
    const is_hidden_or_deleted = ($ => {
        if ($('#page_current_info').length === 0 || $('.profile_deleted_text').length > 0) {
            return true;
        }

        return false;
    })($);

    if ($('#profile').length > 0 && !is_hidden_or_deleted) {
        return true;
    }

    return false;
}

function collectUserData($) {
    const data = {
        user_id: USER_ID,
        timestamp: new Date()
    };

    CONFIG.parse_map.forEach(item => {
        const parsed = parse(item.type, $, item);
        data[parsed.key] = parsed.value;
    });

    const detailed_info = parse('detailedProfileInformation', $);
    const counters = parse('counters', $);
    const content_counters = parse('contentCounters', $);
    Object.assign(data, detailed_info, counters, content_counters);
    data['Last seen'] = format('lastSeenTime', data['Last seen']);

    return data;
}

function* checkUserDataForUpdates(data) {
    let count = yield collection.count({
        user_id: USER_ID
    });

    if (!count) {
        // No entries for this USER_ID yet
        return data;
    }

    const last_document = yield * db_helpers.getLastUserDocument(collection, USER_ID);
    const updates = getDiff(last_document, data);

    return updates;
}

function getDiff(last_document, data) {
    let updates = {};

    let excluded = CONFIG.keys_to_exclude_when_looking_for_updates.concat(['timestamp']);
    let keys = Object.keys(data).filter(k => excluded.indexOf(k) === -1);
    for (let k of keys) {
        if (data[k] !== last_document[k]) {
            updates[k] = {
                old: last_document[k],
                current: data[k]
            };
        }
    }

    filterUpdates(updates);
    if (Object.keys(updates).length) {
        return updates;
    }

    return null;
}

function filterUpdates(updates) {
    CONFIG.keys_to_hide_from_updates.forEach(k => delete updates[k]);
}

function prepareConsoleOutput(data, updates) {
    let formatted_data = format('dataForConsole', data, logs_written);
    if (updates && updates !== data) {
        formatted_data += format('updatesForConsole', updates);
    }

    return formatted_data;
}
<commit_msg>Add logging to scrape(). Replace process.exit(), in case !isUserPageOpen($) is true, with return, to retry scraping after timeout.<commit_after>'use strict';

const co = require('co');
const cheerio = require('cheerio');

const ph = require('./helpers/phantom.js');
const db = require('./db.js');
const parse = require('./parser.js');
const format = require('./format.js');
const helpers = require('./helpers/helpers.js');
const db_helpers = require('./helpers/db_helpers.js');
const logger = require('./logger.js');

const CONFIG = require('./config/config.json');
const collection = db.get('data');
let USER_ID = null;
let logs_written = 0;
let retry_count = 0;
let URL;
let instance;

module.exports = exports = {};

exports.work = function(user_id) {
    if (!user_id) {
        throw Error('No user ID supplied.');
    }

    USER_ID = user_id;
    URL = CONFIG.url + USER_ID;
    scrape();
};

function scrape() {
    logger.debug('Function call. scrape()', {args: [].slice.call(arguments)});
    logger.info('Start scraping', {user_id: USER_ID});
    co(function*() {
        logger.info('Fetch HTML', {user_id: USER_ID});
        const html = yield * getPageContent(URL);
        const $ = cheerio.load(html);
        if (!isUserPageOpen($)) {
            logger.error('Cannot scrape page. !isUserPageOpen($) == true', {user_id: USER_ID, url: URL});
            logger.info('Scrape round skipped. Retry after timeout', {user_id: USER_ID});
            helpers.clearConsole();

            let message = '';
            message += `Attempt #${++retry_count}\n`;
            message += 'Cannot scrape page.';
            message += 'Profile is either hidden, not existing or deleted\n';
            message += `Please check that USER_ID=${USER_ID} and URL=${URL} are correct.\n`;
            message += 'Retry after timeout.';

            console.log(message);
            return;
            // helpers.terminate('Cannot scrape page', 'Profile is either hidden, not existing or deleted');
        }
        retry_count = 0;

        logger.info('Extract user data from fetched HTML', {user_id: USER_ID});
        const user_data = collectUserData($);

        logger.info('Check if new data has updates', {user_id: USER_ID, new_user_data: user_data});
        const user_updates = yield * checkUserDataForUpdates(user_data);
        if (user_updates) {
            logger.info('Write user data to DB', {user_id: USER_ID, data: user_data, updates: user_updates});
            yield collection.insert(user_data);
            logs_written++;
        }

        logger.info('Format data for console output', {user_id: USER_ID, data: user_data});
        const formatted_data = prepareConsoleOutput(user_data, user_updates);
        helpers.clearConsole();
        logger.debug('Write formatted data to console', {user_id: USER_ID, formatted_data: formatted_data});
        process.stdout.write(formatted_data);
    })
    .then(() => {
        const timeout = CONFIG.interval * 1000;
        logger.debug('Set timeout for next scrape() call.', {user_id: USER_ID, timeout: timeout});
        if (retry_count) {
            console.log(`Timeout ${timeout / 1000} seconds`);
        }
        setTimeout(scrape, timeout);
    })
    .catch(err => {
        logger.error('Caught exception in scrape()', {user_id: USER_ID, exception: err});
        console.log(err);
    });
}

function* getPageContent(url) {
    if (!instance) {
        instance = yield * ph.initPhantomInstance();
    }
    console.log('\nFetching data...');
    const html = yield * ph.fetchPageContent(url, instance, false);

    return html;
}

function isUserPageOpen($) {
    const is_hidden_or_deleted = ($ => {
        if ($('#page_current_info').length === 0 || $('.profile_deleted_text').length > 0) {
            return true;
        }

        return false;
    })($);

    if ($('#profile').length > 0 && !is_hidden_or_deleted) {
        return true;
    }

    return false;
}

function collectUserData($) {
    const data = {
        user_id: USER_ID,
        timestamp: new Date()
    };

    CONFIG.parse_map.forEach(item => {
        const parsed = parse(item.type, $, item);
        data[parsed.key] = parsed.value;
    });

    const detailed_info = parse('detailedProfileInformation', $);
    const counters = parse('counters', $);
    const content_counters = parse('contentCounters', $);
    Object.assign(data, detailed_info, counters, content_counters);
    data['Last seen'] = format('lastSeenTime', data['Last seen']);

    return data;
}

function* checkUserDataForUpdates(data) {
    let count = yield collection.count({
        user_id: USER_ID
    });

    if (!count) {
        // No entries for this USER_ID yet
        return data;
    }

    const last_document = yield * db_helpers.getLastUserDocument(collection, USER_ID);
    const updates = getDiff(last_document, data);

    return updates;
}

function getDiff(last_document, data) {
    let updates = {};

    let excluded = CONFIG.keys_to_exclude_when_looking_for_updates.concat(['timestamp']);
    let keys = Object.keys(data).filter(k => excluded.indexOf(k) === -1);
    for (let k of keys) {
        if (data[k] !== last_document[k]) {
            updates[k] = {
                old: last_document[k],
                current: data[k]
            };
        }
    }

    filterUpdates(updates);
    if (Object.keys(updates).length) {
        return updates;
    }

    return null;
}

function filterUpdates(updates) {
    CONFIG.keys_to_hide_from_updates.forEach(k => delete updates[k]);
}

function prepareConsoleOutput(data, updates) {
    let formatted_data = format('dataForConsole', data, logs_written);
    if (updates && updates !== data) {
        formatted_data += format('updatesForConsole', updates);
    }

    return formatted_data;
}
<|endoftext|>


<commit_before>'use strict';

var GetIntrinsic = require('../GetIntrinsic');

var $TypeError = GetIntrinsic('%TypeError%');
var $SyntaxError = GetIntrinsic('%SyntaxError%');

var has = require('has');

var predicates = {
  // https://ecma-international.org/ecma-262/6.0/#sec-property-descriptor-specification-type
  'Property Descriptor': function isPropertyDescriptor(ES, Desc) {
    if (ES.Type(Desc) !== 'Object') {
      return false;
    }
    var allowed = {
      '[[Configurable]]': true,
      '[[Enumerable]]': true,
      '[[Get]]': true,
      '[[Set]]': true,
      '[[Value]]': true,
      '[[Writable]]': true
    };

    for (var key in Desc) { // eslint-disable-line
      if (has(Desc, key) && !allowed[key]) {
        return false;
      }
    }

    var isData = has(Desc, '[[Value]]');
    var IsAccessor = has(Desc, '[[Get]]') || has(Desc, '[[Set]]');
    if (isData && IsAccessor) {
      throw new $TypeError('Property Descriptors may not be both accessor and data descriptors');
    }
    return true;
  }
};

module.exports = function assertRecord(ES, recordType, argumentName, value) {
  var predicate = predicates[recordType];
  if (typeof predicate !== 'function') {
    throw new $SyntaxError('unknown record type: ' + recordType);
  }
  if (!predicate(ES, value)) {
    throw new $TypeError(argumentName + ' must be a ' + recordType);
  }
  console.log(predicate(ES, value), value);
};
<commit_msg>[Fix] `helpers/assertRecord`: remove `console.log`<commit_after>'use strict';

var GetIntrinsic = require('../GetIntrinsic');

var $TypeError = GetIntrinsic('%TypeError%');
var $SyntaxError = GetIntrinsic('%SyntaxError%');

var has = require('has');

var predicates = {
  // https://ecma-international.org/ecma-262/6.0/#sec-property-descriptor-specification-type
  'Property Descriptor': function isPropertyDescriptor(ES, Desc) {
    if (ES.Type(Desc) !== 'Object') {
      return false;
    }
    var allowed = {
      '[[Configurable]]': true,
      '[[Enumerable]]': true,
      '[[Get]]': true,
      '[[Set]]': true,
      '[[Value]]': true,
      '[[Writable]]': true
    };

    for (var key in Desc) { // eslint-disable-line
      if (has(Desc, key) && !allowed[key]) {
        return false;
      }
    }

    var isData = has(Desc, '[[Value]]');
    var IsAccessor = has(Desc, '[[Get]]') || has(Desc, '[[Set]]');
    if (isData && IsAccessor) {
      throw new $TypeError('Property Descriptors may not be both accessor and data descriptors');
    }
    return true;
  }
};

module.exports = function assertRecord(ES, recordType, argumentName, value) {
  var predicate = predicates[recordType];
  if (typeof predicate !== 'function') {
    throw new $SyntaxError('unknown record type: ' + recordType);
  }
  if (!predicate(ES, value)) {
    throw new $TypeError(argumentName + ' must be a ' + recordType);
  }
};
<|endoftext|>


<commit_before>/* eslint-disable */
const getQueryString = (teamA, teamB) => {
    const selectTeamA = [4, 3, 2, 1, 0]
        .map(i => `pma${i}.hero_id`)
        .join();

    const joinTeamA = [0, 1, 2, 3, 4]
        .map(
            i => `
            JOIN player_matches pmA${i}
            ON pmA${i}.match_id = matches.match_id

            ${
            teamA[i] ?
                `AND pmA${i}.hero_id = ${teamA[i]}` : teamA.length > 0 ?
                    `AND pmA${i}.hero_id NOT IN (${teamA})` : ""
            }

            ${
            !teamA[i] && i > 0 && !teamA[i - 1] ?
                `AND pmA${i}.hero_id > pmA${i - 1}.hero_id` : ""
            }
        `
        )
        .join(" ");

    const joinTeamAConditions = [1, 2, 3, 4]
        .map(
            i => `
            AND ( ( pmA0.player_slot < 5 ) = ( pmA${i}.player_Slot < 5 ) )
        `
        )
        .join(" ");

    const selectTeamB = [0, 1, 2, 3, 4].map(i => `pmB${i}.hero_id`).join();

    const joinTeamB = [0, 1, 2, 3, 4]
        .map(
            i => `
            JOIN player_matches pmB${i}
            ON pmB${i}.match_id = matches.match_id

            ${
            teamB[i] ?
                `AND pmB${i}.hero_id = ${teamB[i]}` : teamB.length > 0 ?
                    `AND pmB${i}.hero_id NOT IN (${teamB})` : ""
            }

            ${
            !teamB[i] && i > 0 && !teamB[i - 1] ?
                `AND pmB${i}.hero_id > pmB${i - 1}.hero_id` : ""
            }
        `
        )
        .join(" ");

    const joinTeamBConditions = [0, 1, 2, 3, 4]
        .map(
            i => `
            AND ( ( pmA0.player_slot < 5 ) = ( pmB${i}.player_Slot > 5 ) )
        `
        )
        .join(" ");

    return `SELECT matches.match_id, matches.start_time,
            ARRAY[${selectTeamA}] team_composition_a, ARRAY[${selectTeamB}] team_composition_b FROM matches
            ${joinTeamA} ${joinTeamAConditions} ${joinTeamB} ${joinTeamBConditions}`;
};

export default getQueryString;

/* EXAMPLE QUERY */
/*
/*
/*
SELECT
matches.match_id,
matches.start_time,
array[pma4.hero_id, pma3.hero_id, pma2.hero_id, pma1.hero_id, pma0.hero_id]
team_composition_a,
array[pmb0.hero_id, pmb1.hero_id, pmb2.hero_id, pmb3.hero_id, pmb4.hero_id]
team_composition_b
FROM   matches
       join player_matches pmA0
         ON pmA0.match_id = matches.match_id
            AND pmA0.hero_id = 5
       join player_matches pmA1
         ON pmA1.match_id = matches.match_id
            AND pmA1.hero_id = 4
       join player_matches pmA2
         ON pmA2.match_id = matches.match_id
            AND pmA2.hero_id NOT IN ( 5, 4 )
       join player_matches pmA3
         ON pmA3.match_id = matches.match_id
            AND pmA3.hero_id NOT IN ( 5, 4 )
            AND pmA3.hero_id > pmA2.hero_id
       join player_matches pmA4
         ON pmA4.match_id = matches.match_id
            AND pmA4.hero_id NOT IN ( 5, 4 )
            AND pmA4.hero_id > pmA3.hero_id
            AND ( ( pmA0.player_slot < 5 ) = ( pmA1.player_slot < 5 ) )
            AND ( ( pmA0.player_slot < 5 ) = ( pmA2.player_slot < 5 ) )
            AND ( ( pmA0.player_slot < 5 ) = ( pmA3.player_slot < 5 ) )
            AND ( ( pmA0.player_slot < 5 ) = ( pmA4.player_slot < 5 ) )
       join player_matches pmB0
         ON pmB0.match_id = matches.match_id
            AND pmB0.hero_id = 6
       join player_matches pmB1
         ON pmB1.match_id = matches.match_id
            AND pmB1.hero_id = 7
       join player_matches pmB2
         ON pmB2.match_id = matches.match_id
            AND pmB2.hero_id NOT IN ( 6, 7 )
       join player_matches pmB3
         ON pmB3.match_id = matches.match_id
            AND pmB3.hero_id NOT IN ( 6, 7 )
            AND pmB3.hero_id > pmB2.hero_id
       join player_matches pmB4
         ON pmB4.match_id = matches.match_id
            AND pmB4.hero_id NOT IN ( 6, 7 )
            AND pmB4.hero_id > pmB3.hero_id
            AND ( ( pmA0.player_slot < 5 ) = ( pmB0.player_slot > 5 ) )
            AND ( ( pmA0.player_slot < 5 ) = ( pmB1.player_slot > 5 ) )
            AND ( ( pmA0.player_slot < 5 ) = ( pmB2.player_slot > 5 ) )
            AND ( ( pmA0.player_slot < 5 ) = ( pmB3.player_slot > 5 ) )
            AND ( ( pmA0.player_slot < 5 ) = ( pmB4.player_slot > 5 ) )
*/<commit_msg>order by start time<commit_after>/* eslint-disable */
const getQueryString = (teamA, teamB) => {
    const selectTeamA = [4, 3, 2, 1, 0]
        .map(i => `pma${i}.hero_id`)
        .join();

    const joinTeamA = [0, 1, 2, 3, 4]
        .map(
            i => `
            JOIN player_matches pmA${i}
            ON pmA${i}.match_id = matches.match_id

            ${
            teamA[i] ?
                `AND pmA${i}.hero_id = ${teamA[i]}` : teamA.length > 0 ?
                    `AND pmA${i}.hero_id NOT IN (${teamA})` : ""
            }

            ${
            !teamA[i] && i > 0 && !teamA[i - 1] ?
                `AND pmA${i}.hero_id > pmA${i - 1}.hero_id` : ""
            }
        `
        )
        .join(" ");

    const joinTeamAConditions = [1, 2, 3, 4]
        .map(
            i => `
            AND ( ( pmA0.player_slot < 5 ) = ( pmA${i}.player_Slot < 5 ) )
        `
        )
        .join(" ");

    const selectTeamB = [0, 1, 2, 3, 4].map(i => `pmB${i}.hero_id`).join();

    const joinTeamB = [0, 1, 2, 3, 4]
        .map(
            i => `
            JOIN player_matches pmB${i}
            ON pmB${i}.match_id = matches.match_id

            ${
            teamB[i] ?
                `AND pmB${i}.hero_id = ${teamB[i]}` : teamB.length > 0 ?
                    `AND pmB${i}.hero_id NOT IN (${teamB})` : ""
            }

            ${
            !teamB[i] && i > 0 && !teamB[i - 1] ?
                `AND pmB${i}.hero_id > pmB${i - 1}.hero_id` : ""
            }
        `
        )
        .join(" ");

    const joinTeamBConditions = [0, 1, 2, 3, 4]
        .map(
            i => `
            AND ( ( pmA0.player_slot < 5 ) = ( pmB${i}.player_Slot > 5 ) )
        `
        )
        .join(" ");

    return `SELECT matches.match_id, matches.start_time,
            ARRAY[${selectTeamA}] team_composition_a, ARRAY[${selectTeamB}] team_composition_b FROM matches
            ${joinTeamA} ${joinTeamAConditions} ${joinTeamB} ${joinTeamBConditions} ORDER BY matches.start_time DESC`;
};

export default getQueryString;

/* EXAMPLE QUERY */
/*
/*
/*
SELECT
matches.match_id,
matches.start_time,
array[pma4.hero_id, pma3.hero_id, pma2.hero_id, pma1.hero_id, pma0.hero_id]
team_composition_a,
array[pmb0.hero_id, pmb1.hero_id, pmb2.hero_id, pmb3.hero_id, pmb4.hero_id]
team_composition_b
FROM   matches
       join player_matches pmA0
         ON pmA0.match_id = matches.match_id
            AND pmA0.hero_id = 5
       join player_matches pmA1
         ON pmA1.match_id = matches.match_id
            AND pmA1.hero_id = 4
       join player_matches pmA2
         ON pmA2.match_id = matches.match_id
            AND pmA2.hero_id NOT IN ( 5, 4 )
       join player_matches pmA3
         ON pmA3.match_id = matches.match_id
            AND pmA3.hero_id NOT IN ( 5, 4 )
            AND pmA3.hero_id > pmA2.hero_id
       join player_matches pmA4
         ON pmA4.match_id = matches.match_id
            AND pmA4.hero_id NOT IN ( 5, 4 )
            AND pmA4.hero_id > pmA3.hero_id
            AND ( ( pmA0.player_slot < 5 ) = ( pmA1.player_slot < 5 ) )
            AND ( ( pmA0.player_slot < 5 ) = ( pmA2.player_slot < 5 ) )
            AND ( ( pmA0.player_slot < 5 ) = ( pmA3.player_slot < 5 ) )
            AND ( ( pmA0.player_slot < 5 ) = ( pmA4.player_slot < 5 ) )
       join player_matches pmB0
         ON pmB0.match_id = matches.match_id
            AND pmB0.hero_id = 6
       join player_matches pmB1
         ON pmB1.match_id = matches.match_id
            AND pmB1.hero_id = 7
       join player_matches pmB2
         ON pmB2.match_id = matches.match_id
            AND pmB2.hero_id NOT IN ( 6, 7 )
       join player_matches pmB3
         ON pmB3.match_id = matches.match_id
            AND pmB3.hero_id NOT IN ( 6, 7 )
            AND pmB3.hero_id > pmB2.hero_id
       join player_matches pmB4
         ON pmB4.match_id = matches.match_id
            AND pmB4.hero_id NOT IN ( 6, 7 )
            AND pmB4.hero_id > pmB3.hero_id
            AND ( ( pmA0.player_slot < 5 ) = ( pmB0.player_slot > 5 ) )
            AND ( ( pmA0.player_slot < 5 ) = ( pmB1.player_slot > 5 ) )
            AND ( ( pmA0.player_slot < 5 ) = ( pmB2.player_slot > 5 ) )
            AND ( ( pmA0.player_slot < 5 ) = ( pmB3.player_slot > 5 ) )
            AND ( ( pmA0.player_slot < 5 ) = ( pmB4.player_slot > 5 ) )
*/<|endoftext|>


<commit_before>import {observable, computed} from 'mobx';
import $ from 'jquery'

function nextHour(date) {
  date.setHours(date.getHours() + Math.floor(date.getMinutes() / 60))
  date.setMinutes(0)
  date.setSeconds(0)
  date.setMilliseconds(0)
  return date;
}

export default class JobForm {
  scheduled = true
  @observable submitError = ''
  @observable submitStatus = ''
  @observable fields = []
  defaultFields = [
    {
      name: 'name',
      label: 'Name',
      description: 'Unique name for this job',
      sync: this.syncNameField,
      value: '',
      defaultValue: '',
      error: '',
      valid: false,
      type: 'textinput',
      advanced: false,
    },
    {
      name: 'schedule',
      label: 'Schedule',
      description: 'ISO8601 job schedule',
      sync: this.syncScheduleField,
      error: '',
      value: '',
      defaultValue: 'R/' + nextHour(new Date()).toISOString() + '/PT24H',
      valid: true,
      type: 'textinput',
      advanced: false,
    },
    {
      name: 'parents',
      label: 'Parent Jobs',
      description: 'Job parents',
      sync: this.syncParentsField,
      error: '',
      value: '',
      defaultValue: '',
      valid: false,
      type: 'multiselect',
      advanced: false,
      getOptions: this.getParentsOptions,
    },
    {
      name: 'command',
      label: 'Command',
      description: 'Job command (i.e., `sh -c \'command\'`)',
      sync: this.syncNonEmptyStringField,
      value: '',
      defaultValue: '',
      error: '',
      valid: false,
      type: 'textinput',
      advanced: false,
    },
    {
      name: 'container.image',
      label: 'Container Image',
      description: 'Container image name',
      sync: this.syncContainerName,
      value: '',
      defaultValue: '',
      error: '',
      valid: true,
      type: 'textinput',
      advanced: true,
    },
    {
      name: 'container.network',
      label: 'Container Network',
      description: 'Container network type',
      sync: this.syncContainerNetwork,
      value: 'BRIDGE',
      defaultValue: 'BRIDGE',
      error: '',
      valid: true,
      type: 'select',
      advanced: true,
      getOptions: this.getContainerNetworkOptions,
    },
    {
      name: 'container.networkName',
      label: 'Container Network Name',
      description: 'Container network name',
      sync: this.syncStringField,
      value: '',
      defaultValue: '',
      error: '',
      valid: true,
      type: 'textinput',
      advanced: true,
    },
    {
      name: 'container.type',
      label: 'Container Type',
      description: 'Container type',
      sync: this.syncContainerType,
      value: 'DOCKER',
      defaultValue: 'DOCKER',
      error: '',
      valid: true,
      type: 'select',
      advanced: true,
      getOptions: this.getContainerTypeOptions,
    },
    {
      name: 'cpus',
      label: 'CPUs',
      description: 'CPU shares (ex: 0.1)',
      sync: this.syncNonzeroRealField,
      value: null,
      defaultValue: null,
      error: '',
      valid: true,
      type: 'numberinput',
      advanced: true,
    },
    {
      name: 'mem',
      label: 'Memory',
      description: 'Memory limit in GiB',
      sync: this.syncNonzeroRealField,
      value: null,
      defaultValue: null,
      error: '',
      valid: true,
      type: 'numberinput',
      advanced: true,
    },
    {
      name: 'disk',
      label: 'Disk',
      description: 'Disk space in GiB',
      sync: this.syncNonzeroRealField,
      value: null,
      defaultValue: null,
      error: '',
      valid: true,
      type: 'numberinput',
      advanced: true,
    },
    {
      name: 'concurrent',
      label: 'Concurrent',
      description: 'A job which may execute concurrently (i.e., multiple instances)',
      sync: this.syncBooleanField,
      value: false,
      defaultValue: false,
      error: '',
      valid: true,
      type: 'checkbox',
      advanced: true,
    },
    {
      name: 'owner',
      label: 'Owner',
      description: 'Comma-separated list of job owners (i.e., `peter@aol.com`)',
      sync: this.syncStringField,
      value: '',
      defaultValue: '',
      error: '',
      valid: true,
      type: 'textinput',
      advanced: true,
    },
    {
      name: 'ownerName',
      label: 'Owner Name',
      description: 'Own name',
      sync: this.syncStringField,
      value: '',
      defaultValue: '',
      error: '',
      valid: true,
      type: 'textinput',
      advanced: true,
    },
  ]

  getField(name) {
    for (let i = 0; i < this.fields.length; i++) {
      if (this.fields[i].name === name) {
        return this.fields[i]
      }
    }
  }

  @computed get submitDisabled() {
    var hasError = false
    this.fields.some(v => {
      if (!this.scheduled && v.name === 'schedule') {
        return false
      }
      if (this.scheduled && v.name === 'parents') {
        return false
      }
      if (v.name === 'command' &&
          this.getField('container.image').value &&
          this.getField('container.image').valid) {
        return false
      }
      if (!v.valid) {
        hasError = true
        return true
      }
    })
    return hasError ? 'disabled' : ''
  }

  getParentsOptions(jobSummaryStore) {
    var options = []
    jobSummaryStore.jobNames.forEach(j => {
      options.push({label: j, value: j})
    })
    return options
  }

  getContainerNetworkOptions(jobSummaryStore) {
    return [
      {label: 'BRIDGE', value: 'BRIDGE'},
      {label: 'HOST', value: 'HOST'},
      {label: 'USER', value: 'USER'},
    ]
  }

  getContainerTypeOptions(jobSummaryStore) {
    return [
      {label: 'DOCKER', value: 'DOCKER'},
    ]
    var options = []
    jobSummaryStore.jobNames.forEach(j => {
      options.push({label: j, value: j})
    })
    return options
  }

  syncNameField(event, field) {
    let value = event.target.value
    if (!value) {
      field.valid = false
      field.error = 'Name must not be empty'
    } else if (!value.match(/^[\w.-]+$/)) {
      field.valid = false
      field.error = 'Name must match regex /^[\w.-]+$/'
    } else {
      field.valid = true
      field.error = ''
    }
    field.value = value
  }

  syncScheduleField(event, field) {
    let value = event.target.value
    if (!value) {
      field.valid = false
      field.error = 'Schedule must not be empty'
    } else if (!value.match(/^R\d*\/([\+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24\:?00)([\.,]\d+(?!:))?)?(\17[0-5]\d([\.,]\d+)?)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?\/P(?=\w*\d)(?:\d+Y|Y)?(?:\d+M|M)?(?:\d+W|W)?(?:\d+D|D)?(?:T(?:\d+H|H)?(?:\d+M|M)?(?:\d+(?:\.\d{1,2})?S|S)?)?$/)) {
      field.valid = false
      field.error = 'Schedule must match ISO8601 format'
    } else {
      field.valid = true
      field.error = ''
    }
    field.value = value
  }

  syncNonEmptyStringField(event, field) {
    let value = event.target.value
    if (!value) {
      field.valid = false
      field.error = field.label + ' must not be empty'
    } else {
      field.valid = true
      field.error = ''
    }
    field.value = value
  }

  syncStringField(event, field) {
    let value = event.target.value
    field.valid = true
    field.error = ''
    field.value = value
  }

  syncParentsField(value, field) {
    if (!value) {
      field.valid = false
      field.error = field.label + ' must not be empty'
    } else {
      field.valid = true
      field.error = ''
    }
    field.value = value
  }

  syncNonzeroRealField(event, field) {
    let value = event.target.value
    if (isNaN(value)) {
      field.valid = false
      field.error = field.label + ' must contain real number'
    } else if (value && value <= 0) {
      field.valid = false
      field.error = field.label + ' must be >=0'
    } else {
      field.valid = true
      field.error = ''
    }
    field.value = value
  }

  syncContainerName(event, field) {
    let value = event.target.value
    if (value) {
      if (!value.match(/^[a-z0-9\/:]+$/)) {
        field.valid = false
        field.error = field.label + ' must match regex /^[a-z0-9\/:]+$/'
      } else {
        field.valid = true
        field.error = ''
      }
    }
    field.value = value
  }

  syncContainerNetwork(value, field) {
    field.valid = true
    field.value = value
  }

  syncContainerType(value, field) {
    field.valid = true
    field.value = value
  }

  syncBooleanField(event, field) {
    let checked = event.target.checked
    field.valid = true
    field.value = checked
  }

  reset() {
    this.fields = $.extend(true, [], this.defaultFields)
    this.submitError = ''
    this.submitStatus = ''
    if ($('#scheduled-job-editor-form')[0]) {
      $('#scheduled-job-editor-form')[0].reset()
    }
    if ($('#dependent-job-editor-form')[0]) {
      $('#dependent-job-editor-form')[0].reset()
    }
  }

  constructor(scheduled) {
    this.scheduled = scheduled
    this.reset()
  }

  onSubmit(event) {
    var _this = this
    var btn = $(event.currentTarget).button('loading')
    var url = ''
    if (_this.scheduled) {
      url = 'v1/scheduler/iso8601'
    } else {
      url = 'v1/scheduler/dependency'
    }
    $.ajax({
      type: 'POST',
      url: url,
      data: JSON.stringify(_this.toJS()),
      dataType: 'json',
      contentType: 'application/json; charset=utf-8',
    }).done(function(resp) {
      setTimeout(function() {
        btn.button('reset')
        $('#scheduled-job-editor-modal').modal('hide')
        $('#dependent-job-editor-modal').modal('hide')
        _this.reset()
      }, 500)
    }).fail(function(resp) {
      setTimeout(function() {
        btn.button('reset')
        let result = resp.responseJSON
        if (result instanceof Object) {
          _this.submitError = result.message
          _this.submitStatus = resp.status + ': ' + resp.statusText
        } else {
          _this.submitError = resp.responseText
          _this.submitStatus = resp.status + ': ' + resp.statusText
        }
      }, 500)
    })
  }

  toJS() {
    let obj = Object.create(null)
    this.fields.forEach(v => {
      if (this.scheduled && v.name === 'parents') {
        return
      } else if (!this.scheduled && v.name === 'schedule') {
        return
      }
      if (v.value || v.defaultValue) {
        if (!v.value) {
          v.value = v.defaultValue
        }
        if (v.name === 'parents') {
          obj[v.name] = v.value.split(',')
        } else {
          if (v.name.split('.').length > 1) {
            // handle nested values
            var splat = v.name.split('.').reverse()
            if (splat[splat.length - 1] == 'container') {
              // check that there's actually an image name defined
              if (!this.getField('container.image').value) {
                // skip if there's no image
                return
              }
            }
            var unflatten = function(obj, remaining) {
              var n = remaining.pop()
              if (remaining.length > 0) {
                if (!(n in obj)) {
                  obj[n] = Object.create(null)
                }
                unflatten(obj[n], remaining)
              } else {
                // this is the final value, store it
                obj[n] = v.value
              }
            }
            unflatten(obj, splat)
          } else {
            obj[v.name] = v.value
          }
        }
      }
    })
    return obj
  }
}
<commit_msg>Update UI to allow periods in container image name (#786)<commit_after>import {observable, computed} from 'mobx';
import $ from 'jquery'

function nextHour(date) {
  date.setHours(date.getHours() + Math.floor(date.getMinutes() / 60))
  date.setMinutes(0)
  date.setSeconds(0)
  date.setMilliseconds(0)
  return date;
}

export default class JobForm {
  scheduled = true
  @observable submitError = ''
  @observable submitStatus = ''
  @observable fields = []
  defaultFields = [
    {
      name: 'name',
      label: 'Name',
      description: 'Unique name for this job',
      sync: this.syncNameField,
      value: '',
      defaultValue: '',
      error: '',
      valid: false,
      type: 'textinput',
      advanced: false,
    },
    {
      name: 'schedule',
      label: 'Schedule',
      description: 'ISO8601 job schedule',
      sync: this.syncScheduleField,
      error: '',
      value: '',
      defaultValue: 'R/' + nextHour(new Date()).toISOString() + '/PT24H',
      valid: true,
      type: 'textinput',
      advanced: false,
    },
    {
      name: 'parents',
      label: 'Parent Jobs',
      description: 'Job parents',
      sync: this.syncParentsField,
      error: '',
      value: '',
      defaultValue: '',
      valid: false,
      type: 'multiselect',
      advanced: false,
      getOptions: this.getParentsOptions,
    },
    {
      name: 'command',
      label: 'Command',
      description: 'Job command (i.e., `sh -c \'command\'`)',
      sync: this.syncNonEmptyStringField,
      value: '',
      defaultValue: '',
      error: '',
      valid: false,
      type: 'textinput',
      advanced: false,
    },
    {
      name: 'container.image',
      label: 'Container Image',
      description: 'Container image name',
      sync: this.syncContainerName,
      value: '',
      defaultValue: '',
      error: '',
      valid: true,
      type: 'textinput',
      advanced: true,
    },
    {
      name: 'container.network',
      label: 'Container Network',
      description: 'Container network type',
      sync: this.syncContainerNetwork,
      value: 'BRIDGE',
      defaultValue: 'BRIDGE',
      error: '',
      valid: true,
      type: 'select',
      advanced: true,
      getOptions: this.getContainerNetworkOptions,
    },
    {
      name: 'container.networkName',
      label: 'Container Network Name',
      description: 'Container network name',
      sync: this.syncStringField,
      value: '',
      defaultValue: '',
      error: '',
      valid: true,
      type: 'textinput',
      advanced: true,
    },
    {
      name: 'container.type',
      label: 'Container Type',
      description: 'Container type',
      sync: this.syncContainerType,
      value: 'DOCKER',
      defaultValue: 'DOCKER',
      error: '',
      valid: true,
      type: 'select',
      advanced: true,
      getOptions: this.getContainerTypeOptions,
    },
    {
      name: 'cpus',
      label: 'CPUs',
      description: 'CPU shares (ex: 0.1)',
      sync: this.syncNonzeroRealField,
      value: null,
      defaultValue: null,
      error: '',
      valid: true,
      type: 'numberinput',
      advanced: true,
    },
    {
      name: 'mem',
      label: 'Memory',
      description: 'Memory limit in GiB',
      sync: this.syncNonzeroRealField,
      value: null,
      defaultValue: null,
      error: '',
      valid: true,
      type: 'numberinput',
      advanced: true,
    },
    {
      name: 'disk',
      label: 'Disk',
      description: 'Disk space in GiB',
      sync: this.syncNonzeroRealField,
      value: null,
      defaultValue: null,
      error: '',
      valid: true,
      type: 'numberinput',
      advanced: true,
    },
    {
      name: 'concurrent',
      label: 'Concurrent',
      description: 'A job which may execute concurrently (i.e., multiple instances)',
      sync: this.syncBooleanField,
      value: false,
      defaultValue: false,
      error: '',
      valid: true,
      type: 'checkbox',
      advanced: true,
    },
    {
      name: 'owner',
      label: 'Owner',
      description: 'Comma-separated list of job owners (i.e., `peter@aol.com`)',
      sync: this.syncStringField,
      value: '',
      defaultValue: '',
      error: '',
      valid: true,
      type: 'textinput',
      advanced: true,
    },
    {
      name: 'ownerName',
      label: 'Owner Name',
      description: 'Own name',
      sync: this.syncStringField,
      value: '',
      defaultValue: '',
      error: '',
      valid: true,
      type: 'textinput',
      advanced: true,
    },
  ]

  getField(name) {
    for (let i = 0; i < this.fields.length; i++) {
      if (this.fields[i].name === name) {
        return this.fields[i]
      }
    }
  }

  @computed get submitDisabled() {
    var hasError = false
    this.fields.some(v => {
      if (!this.scheduled && v.name === 'schedule') {
        return false
      }
      if (this.scheduled && v.name === 'parents') {
        return false
      }
      if (v.name === 'command' &&
          this.getField('container.image').value &&
          this.getField('container.image').valid) {
        return false
      }
      if (!v.valid) {
        hasError = true
        return true
      }
    })
    return hasError ? 'disabled' : ''
  }

  getParentsOptions(jobSummaryStore) {
    var options = []
    jobSummaryStore.jobNames.forEach(j => {
      options.push({label: j, value: j})
    })
    return options
  }

  getContainerNetworkOptions(jobSummaryStore) {
    return [
      {label: 'BRIDGE', value: 'BRIDGE'},
      {label: 'HOST', value: 'HOST'},
      {label: 'USER', value: 'USER'},
    ]
  }

  getContainerTypeOptions(jobSummaryStore) {
    return [
      {label: 'DOCKER', value: 'DOCKER'},
    ]
    var options = []
    jobSummaryStore.jobNames.forEach(j => {
      options.push({label: j, value: j})
    })
    return options
  }

  syncNameField(event, field) {
    let value = event.target.value
    if (!value) {
      field.valid = false
      field.error = 'Name must not be empty'
    } else if (!value.match(/^[\w.-]+$/)) {
      field.valid = false
      field.error = 'Name must match regex /^[\w.-]+$/'
    } else {
      field.valid = true
      field.error = ''
    }
    field.value = value
  }

  syncScheduleField(event, field) {
    let value = event.target.value
    if (!value) {
      field.valid = false
      field.error = 'Schedule must not be empty'
    } else if (!value.match(/^R\d*\/([\+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24\:?00)([\.,]\d+(?!:))?)?(\17[0-5]\d([\.,]\d+)?)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?\/P(?=\w*\d)(?:\d+Y|Y)?(?:\d+M|M)?(?:\d+W|W)?(?:\d+D|D)?(?:T(?:\d+H|H)?(?:\d+M|M)?(?:\d+(?:\.\d{1,2})?S|S)?)?$/)) {
      field.valid = false
      field.error = 'Schedule must match ISO8601 format'
    } else {
      field.valid = true
      field.error = ''
    }
    field.value = value
  }

  syncNonEmptyStringField(event, field) {
    let value = event.target.value
    if (!value) {
      field.valid = false
      field.error = field.label + ' must not be empty'
    } else {
      field.valid = true
      field.error = ''
    }
    field.value = value
  }

  syncStringField(event, field) {
    let value = event.target.value
    field.valid = true
    field.error = ''
    field.value = value
  }

  syncParentsField(value, field) {
    if (!value) {
      field.valid = false
      field.error = field.label + ' must not be empty'
    } else {
      field.valid = true
      field.error = ''
    }
    field.value = value
  }

  syncNonzeroRealField(event, field) {
    let value = event.target.value
    if (isNaN(value)) {
      field.valid = false
      field.error = field.label + ' must contain real number'
    } else if (value && value <= 0) {
      field.valid = false
      field.error = field.label + ' must be >=0'
    } else {
      field.valid = true
      field.error = ''
    }
    field.value = value
  }

  syncContainerName(event, field) {
    let value = event.target.value
    if (value) {
      if (!value.match(/^[a-z0-9\/\.:]+$/)) {
        field.valid = false
        field.error = field.label + ' must match regex /^[a-z0-9\/\.:]+$/'
      } else {
        field.valid = true
        field.error = ''
      }
    }
    field.value = value
  }

  syncContainerNetwork(value, field) {
    field.valid = true
    field.value = value
  }

  syncContainerType(value, field) {
    field.valid = true
    field.value = value
  }

  syncBooleanField(event, field) {
    let checked = event.target.checked
    field.valid = true
    field.value = checked
  }

  reset() {
    this.fields = $.extend(true, [], this.defaultFields)
    this.submitError = ''
    this.submitStatus = ''
    if ($('#scheduled-job-editor-form')[0]) {
      $('#scheduled-job-editor-form')[0].reset()
    }
    if ($('#dependent-job-editor-form')[0]) {
      $('#dependent-job-editor-form')[0].reset()
    }
  }

  constructor(scheduled) {
    this.scheduled = scheduled
    this.reset()
  }

  onSubmit(event) {
    var _this = this
    var btn = $(event.currentTarget).button('loading')
    var url = ''
    if (_this.scheduled) {
      url = 'v1/scheduler/iso8601'
    } else {
      url = 'v1/scheduler/dependency'
    }
    $.ajax({
      type: 'POST',
      url: url,
      data: JSON.stringify(_this.toJS()),
      dataType: 'json',
      contentType: 'application/json; charset=utf-8',
    }).done(function(resp) {
      setTimeout(function() {
        btn.button('reset')
        $('#scheduled-job-editor-modal').modal('hide')
        $('#dependent-job-editor-modal').modal('hide')
        _this.reset()
      }, 500)
    }).fail(function(resp) {
      setTimeout(function() {
        btn.button('reset')
        let result = resp.responseJSON
        if (result instanceof Object) {
          _this.submitError = result.message
          _this.submitStatus = resp.status + ': ' + resp.statusText
        } else {
          _this.submitError = resp.responseText
          _this.submitStatus = resp.status + ': ' + resp.statusText
        }
      }, 500)
    })
  }

  toJS() {
    let obj = Object.create(null)
    this.fields.forEach(v => {
      if (this.scheduled && v.name === 'parents') {
        return
      } else if (!this.scheduled && v.name === 'schedule') {
        return
      }
      if (v.value || v.defaultValue) {
        if (!v.value) {
          v.value = v.defaultValue
        }
        if (v.name === 'parents') {
          obj[v.name] = v.value.split(',')
        } else {
          if (v.name.split('.').length > 1) {
            // handle nested values
            var splat = v.name.split('.').reverse()
            if (splat[splat.length - 1] == 'container') {
              // check that there's actually an image name defined
              if (!this.getField('container.image').value) {
                // skip if there's no image
                return
              }
            }
            var unflatten = function(obj, remaining) {
              var n = remaining.pop()
              if (remaining.length > 0) {
                if (!(n in obj)) {
                  obj[n] = Object.create(null)
                }
                unflatten(obj[n], remaining)
              } else {
                // this is the final value, store it
                obj[n] = v.value
              }
            }
            unflatten(obj, splat)
          } else {
            obj[v.name] = v.value
          }
        }
      }
    })
    return obj
  }
}
<|endoftext|>


<commit_before>/*
 * @license
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

foam.CLASS({
  package: 'foam.graphics',
  name: 'Matrix',

  properties: [
    {
      name: 'a',
      class: 'Simple'
    },
    {
      name: 'b',
      class: 'Simple'
    },
    {
      name: 'c',
      class: 'Simple'
    },
    {
      name: 'd',
      class: 'Simple'
    },
    {
      name: 'e',
      class: 'Simple'
    },
    {
      name: 'f',
      class: 'Simple'
    },
    {
      name: 'g',
      class: 'Simple'
    },
    {
      name: 'h',
      class: 'Simple'
    },
    {
      name: 'i',
      class: 'Simple'
    }
  ],

  methods: [
    function initArgs() {
      this.a = 1; this.b = 0; this.c = 0;
      this.d = 0; this.e = 1; this.f = 0;
      this.g = 0; this.h = 0; this.i = 1;
    },

    function mul(m) {
      var a = this.a, b = this.b, c = this.c,
          d = this.d, e = this.e, f = this.f,
          g = this.g, h = this.h, i = this.i;

      this.a = a * m.a + b * m.d + c * m.g;
      this.b = a * m.b + b * m.e + c * m.h;
      this.c = a * m.c + b * m.f + c * m.i;

      this.d = d * m.a + e * m.d + f * m.g;
      this.e = d * m.b + e * m.e + f * m.h;
      this.f = d * m.c + e * m.f + f * m.i;

      this.g = g * m.a + h * m.d + i * m.g;
      this.h = g * m.b + h * m.e + i * m.h;
      this.i = g * m.c + h * m.f + i * m.i;

      return this;
    },

    function invert() {
      // a b c    a d g
      // d e f -> b e h
      // g h i    c f i
      var tmp = this.b;
      this.b = this.d;
      this.d = tmp;

      tmp = this.c;
      this.c = this.g;
      this.g = tmp;

      tmp = this.f;
      this.f = this.h;
      this.h = tmp;
      return this;
    }
  ]
});

// TODO: Perhaps these methods should be moved to matrix.
foam.CLASS({
  package: 'foam.graphics',
  name: 'Transform',

  requires: [
    'foam.graphics.Matrix'
  ],

  properties: [
    {
      name: 'matrix',
      factory: function() {
        return this.Matrix.create();
      },
    },
    {
      name: 'm2',
      factory: function() {
        return this.Matrix.create();
      }
    }
  ],

  methods: [
    function reset() {
      this.matrix.initArgs();
      return this;
    },

    function affine(m) {
      this.matrix.mul(m);
      return this;
    },

    function translate(dx, dy) {
      if ( ! dx && ! dy ) return;
      var m = this.m2;
      m.a = 1; m.b = 0; m.c = dx;
      m.d = 0; m.e = 1; m.f = dy;
      m.g = 0; m.h = 0; m.i = 1;
      this.matrix.mul(m);
    },

    function skew(x, y) {
      if ( ! x && ! y ) return;
      var m = this.m2;
      m.a = 1; m.b = x; m.c = 0;
      m.d = y; m.e = 1; m.f = 0;
      m.g = 0; m.h = 0; m.i = 1;
      this.matrix.mul(m);
    },

    function scale(x, y) {
      if ( x === 1 && y === 1 ) return;
      var m = this.m2;
      m.a = x; m.b = 0; m.c = 0;
      m.d = 0; m.e = y; m.f = 0;
      m.g = 0; m.h = 0; m.i = 1;
      this.matrix.mul(m);
    },

    function rotate(a) {
      if ( ! a ) return;
      var m = this.m2;
      m.a = Math.cos(a);  m.b = Math.sin(a); m.c = 0;
      m.d = -Math.sin(a); m.e = Math.cos(a); m.f = 0;
      m.g = 0;            m.h = 0;           m.i = 1;
      this.matrix.mul(m);
    }
  ]
});

foam.CLASS({
  package: 'foam.graphics',
  name: 'CView',

  requires: [
    'foam.graphics.Canvas',
    'foam.graphics.Transform'
  ],

  topics: [ 'invalidated' ],

  properties: [
    {
      name: 'rotation',
      class: 'Float'
    },
    {
      name: 'originX',
      class: 'Float'
    },
    {
      name: 'originY',
      class: 'Float'
    },
    {
      name: 'scaleX',
      class: 'Float',
      value: 1
    },
    {
      name: 'scaleY',
      class: 'Float',
      value: 1
    },
    {
      name: 'skewX',
      class: 'Float'
    },
    {
      name: 'skewY',
      class: 'Float'
    },
    {
      name: 'x',
      class: 'Float'
    },
    {
      name: 'y',
      class: 'Float'
    },
    {
      name: 'alpha',
      class: 'Float',
      value: 1
    },
    {
      name: 'border'
    },
    {
      name: 'color'
    },
    {
      name: 'shadowColor'
    },
    {
      name: 'shadowBlur'
    },
    {
      name: 'children',
      factory: function() { return []; },
      postSet: function(o, n) {
        for ( var i = 0 ; o && i < o.length ; i++ ) this.removeChild_(o[i]);
        for ( var i = 0 ; n && i < n.length ; i++ ) this.addChild_(n[i]);
      }
    },
    {
      name: 'state',
      value: 'initial'
    },
    {
      name: 'transform_',
      factory: function() { return this.Transform.create(); }
    },
    {
      name: 'transform',
      getter: function() {
        var t = this.transform_.reset();

        t.translate(this.x, this.y);
        t.rotate(this.rotation);
        t.skew(this.skewX, this.skewY);
        t.scale(this.scaleX, this.scaleY);
        t.translate(-this.originX, -this.originY);

        return t;
      }
    }
  ],

  methods: [
    function initCView() {
/*
      this.propertyChange.sub(function() {
        this.pub('invalidated');
      }.bind(this));
*/
    },

    function maybeInitCView() {
      if ( this.state == 'initial' ) {
        this.initCView();
        this.state = 'initailized'
      }
    },

    function paint(x) {
      this.maybeInitCView();
      x.save();
      x.globalAlpha *= this.alpha;
      x.strokeStyle = ( this.border && this.border.toCanvasStyle ) ?
        this.border.toCanvasStyle(x) : this.border;
      x.fillStyle = ( this.color && this.color.toCanvasStyle ) ?
        this.color.toCanvasStyle(x) : this.color;
      this.doTransform(x);
      if ( this.shadowColor && this.shadowBlur ) {
        x.shadowColor = this.shadowColor;
        x.shadowBlur  = this.shadowBlur;
      }
      this.paintSelf(x);
      this.paintChildren(x);
      x.restore();
    },

    function doTransform(x) {
      var t = this.transform.matrix;
      x.transform(t.a, t.d, t.b, t.e, t.c, t.f);
    },

    function paintChildren(x) {
      for ( var i = 0 ; i < this.children.length ; i++ ) {
        this.children[i].paint(x);
      }
    },

    function addChild_(c) {
      c.invalidated.sub(this.onChildUpdate);
      return c;
    },

    function removeChild_(c) {
      c.invalidated.unsub(this.onChildUpdate);
      return c;
    },

    function addChildren() {
      for ( var i = 0 ; i < arguments.length ; i++ ) {
        this.children.push(arguments[i]);
        // this.addChild_(arguments[i]);
      }
    },

    function paintSelf(x) {},

    // TODO: Replace with toE() when/if ready.
    function toHTML() {
      return this.Canvas.create({
        width: this.x + this.width || this.r * 2,
        height: this.y + this.height || this.r * 2,
        cview: this
      }).toHTML();
    }
  ],

  listeners: [
    {
      name: 'onChildUpdate',
      isFramed: true,
      code: function onChildUpdate() { this.pub('invalidated'); }
    }
  ]
});

foam.CLASS({
  package: 'foam.graphics',
  name: 'Box',
  extends: 'foam.graphics.CView',

  properties: [
    {
      name: 'width',
      class: 'Float'
    },
    {
      name: 'height',
      class: 'Float'
    },
    {
      name: 'border',
      value: 'black'
    }
  ],

  methods: [
    function paintSelf(x) {
      x.beginPath();
      x.rect(0, 0, this.width, this.height);
      if ( this.border ) x.stroke();
      if ( this.color ) x.fill();
    }
  ]
});

foam.CLASS({
  package: 'foam.graphics',
  name: 'Arc',
  extends: 'foam.graphics.CView',

  properties: [
    {
      name: 'radius',
      class: 'Float'
    },
    {
      name: 'start',
      class: 'Float'
    },
    {
      name: 'end',
      class: 'Float'
    },
    {
      name: 'arcWidth',
      class: 'Float'
    },
    {
      name: 'border',
      value: 'black'
    }
  ],

  methods: [
    function paintSelf(x) {
      x.beginPath();
      x.arc(0, 0, this.radius, this.start, this.end);

      if ( this.color ) x.fill();

      if ( this.border ) {
        x.lineWidth = this.arcWidth;
        x.stroke();
      }
    }
  ]
});

foam.CLASS({
  package: 'foam.graphics',
  name: 'Circle',
  extends: 'foam.graphics.Arc',

  properties: [
    {
      name: 'start',
      value: 0
    },
    {
      name: 'end',
      value: 2*Math.PI
    }
  ],

  methods: [
    function intersects(c) {
      var r = this.radius + c.radius;
      if ( this.border ) r += this.arcWidth/2-1;
      if ( c.border    ) r += c.arcWidth/2-1;
      return foam.math.distance(this.x-c.x, this.y-c.y) <= r;
    }
  ]
});

foam.CLASS({
  package: 'foam.graphics',
  name: 'Canvas',

  imports: [ 'getElementById' ],

  properties: [
    {
      name: 'id',
      factory: function() { return 'v' + this.$UID; }
    },
    {
      name: 'width',
    },
    {
      name: 'height',
    },
    {
      name: 'element',
      factory: function() {
        var e = this.getElementById(this.id);
        e.width = this.width;
        e.height = this.height;
        return e;
      },
      postSet: function(_, e) {
        e.width = this.width;
        e.height = this.height;
      }
    },
    {
      name: 'context',
      factory: function() {
        return this.element.getContext('2d');
      }
    },
    {
      name: 'cview',
      postSet: function(o, n) {
        o && o.invalidated.unsub(this.paint);
        n && n.invalidated.sub(this.paint);
        this.paint();
      }
    }
  ],

  methods: [
    function erase() {
      this.element.width = this.element.width;
    }
  ],

  listeners: [
    {
      name: 'paint',
      isFramed: true,
      code: function() {
        this.erase();
        this.cview && this.cview.paint(this.context);
      }
    }
  ],

  templates: [
    {
      name: 'toHTML',
      template: '<canvas id="<%= this.id %>"></canvas>'
    }
  ]
});

foam.CLASS({
  package: 'foam.graphics',
  name: 'Gradient',

  properties: [
    'x0', 'y0', 'r0',
    'x1', 'y1', 'r1',
    {
      name: 'radial',
      class: 'Boolean',
      value: false
    },
    {
      name: 'colors',
      factory: function() {
        return [];
      }
    }
  ],

  methods: [
    function toCanvasStyle(x) {
      var t = this;
      var g = this.radial ?
        x.createRadialGradient(t.x0, t.y0, t.r0, t.x1, t.y1, t.r1) :
        x.createLinearGradient(t.x0, t.y0, t.x1, t.y1) ;

      for ( var i = 0 ; i < t.colors.length ; i++ )
        g.addColorStop(t.colors[i][0], t.colors[i][1]);

      return g;
    }
  ]
});

// TODO: add configurable repaint strategy. Ex. explicit, on property change, on child change
<commit_msg>Faster Matrix.mul().<commit_after>/*
 * @license
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

foam.CLASS({
  package: 'foam.graphics',
  name: 'Matrix',

  properties: [
    {
      name: 'a',
      class: 'Simple'
    },
    {
      name: 'b',
      class: 'Simple'
    },
    {
      name: 'c',
      class: 'Simple'
    },
    {
      name: 'd',
      class: 'Simple'
    },
    {
      name: 'e',
      class: 'Simple'
    },
    {
      name: 'f',
      class: 'Simple'
    },
    {
      name: 'g',
      class: 'Simple'
    },
    {
      name: 'h',
      class: 'Simple'
    },
    {
      name: 'i',
      class: 'Simple'
    }
  ],

  methods: [
    function initArgs() {
      this.a = 1; this.b = 0; this.c = 0;
      this.d = 0; this.e = 1; this.f = 0;
      this.g = 0; this.h = 0; this.i = 1;
    },

    function mul(m) {
      var a = this.a, b = this.b, c = this.c,
          d = this.d, e = this.e, f = this.f,
          g = this.g, h = this.h, i = this.i;

      var ma = m.a, mb = m.b, mc = m.c,
          md = m.d, me = m.e, mf = m.f,
          mg = m.g, mh = m.h, mi = m.i;

      this.a = a * ma + b * md + c * mg;
      this.b = a * mb + b * me + c * mh;
      this.c = a * mc + b * mf + c * mi;

      this.d = d * ma + e * md + f * mg;
      this.e = d * mb + e * me + f * mh;
      this.f = d * mc + e * mf + f * mi;

      this.g = g * ma + h * md + i * mg;
      this.h = g * mb + h * me + i * mh;
      this.i = g * mc + h * mf + i * mi;

      return this;
    },

    function invert() {
      // a b c    a d g
      // d e f -> b e h
      // g h i    c f i
      var tmp = this.b;
      this.b = this.d;
      this.d = tmp;

      tmp = this.c;
      this.c = this.g;
      this.g = tmp;

      tmp = this.f;
      this.f = this.h;
      this.h = tmp;
      return this;
    }
  ]
});

// TODO: Perhaps these methods should be moved to matrix.
foam.CLASS({
  package: 'foam.graphics',
  name: 'Transform',

  requires: [
    'foam.graphics.Matrix'
  ],

  properties: [
    {
      name: 'matrix',
      factory: function() {
        return this.Matrix.create();
      },
    },
    {
      name: 'm2',
      factory: function() {
        return this.Matrix.create();
      }
    }
  ],

  methods: [
    function reset() {
      this.matrix.initArgs();
      return this;
    },

    function affine(m) {
      this.matrix.mul(m);
      return this;
    },

    function translate(dx, dy) {
      if ( ! dx && ! dy ) return;
      var m = this.m2;
      m.a = 1; m.b = 0; m.c = dx;
      m.d = 0; m.e = 1; m.f = dy;
      m.g = 0; m.h = 0; m.i = 1;
      this.matrix.mul(m);
    },

    function skew(x, y) {
      if ( ! x && ! y ) return;
      var m = this.m2;
      m.a = 1; m.b = x; m.c = 0;
      m.d = y; m.e = 1; m.f = 0;
      m.g = 0; m.h = 0; m.i = 1;
      this.matrix.mul(m);
    },

    function scale(x, y) {
      if ( x === 1 && y === 1 ) return;
      var m = this.m2;
      m.a = x; m.b = 0; m.c = 0;
      m.d = 0; m.e = y; m.f = 0;
      m.g = 0; m.h = 0; m.i = 1;
      this.matrix.mul(m);
    },

    function rotate(a) {
      if ( ! a ) return;
      var m = this.m2;
      m.a = Math.cos(a);  m.b = Math.sin(a); m.c = 0;
      m.d = -Math.sin(a); m.e = Math.cos(a); m.f = 0;
      m.g = 0;            m.h = 0;           m.i = 1;
      this.matrix.mul(m);
    }
  ]
});

foam.CLASS({
  package: 'foam.graphics',
  name: 'CView',

  requires: [
    'foam.graphics.Canvas',
    'foam.graphics.Transform'
  ],

  topics: [ 'invalidated' ],

  properties: [
    {
      name: 'rotation',
      class: 'Float'
    },
    {
      name: 'originX',
      class: 'Float'
    },
    {
      name: 'originY',
      class: 'Float'
    },
    {
      name: 'scaleX',
      class: 'Float',
      value: 1
    },
    {
      name: 'scaleY',
      class: 'Float',
      value: 1
    },
    {
      name: 'skewX',
      class: 'Float'
    },
    {
      name: 'skewY',
      class: 'Float'
    },
    {
      name: 'x',
      class: 'Float'
    },
    {
      name: 'y',
      class: 'Float'
    },
    {
      name: 'alpha',
      class: 'Float',
      value: 1
    },
    {
      name: 'border'
    },
    {
      name: 'color'
    },
    {
      name: 'shadowColor'
    },
    {
      name: 'shadowBlur'
    },
    {
      name: 'children',
      factory: function() { return []; },
      postSet: function(o, n) {
        for ( var i = 0 ; o && i < o.length ; i++ ) this.removeChild_(o[i]);
        for ( var i = 0 ; n && i < n.length ; i++ ) this.addChild_(n[i]);
      }
    },
    {
      name: 'state',
      value: 'initial'
    },
    {
      name: 'transform_',
      factory: function() { return this.Transform.create(); }
    },
    {
      name: 'transform',
      getter: function() {
        var t = this.transform_.reset();

        t.translate(this.x, this.y);
        t.rotate(this.rotation);
        t.skew(this.skewX, this.skewY);
        t.scale(this.scaleX, this.scaleY);
        t.translate(-this.originX, -this.originY);

        return t;
      }
    }
  ],

  methods: [
    function initCView() {
/*
      this.propertyChange.sub(function() {
        this.pub('invalidated');
      }.bind(this));
*/
    },

    function maybeInitCView() {
      if ( this.state == 'initial' ) {
        this.initCView();
        this.state = 'initailized'
      }
    },

    function paint(x) {
      this.maybeInitCView();
      x.save();
      x.globalAlpha *= this.alpha;
      x.strokeStyle = ( this.border && this.border.toCanvasStyle ) ?
        this.border.toCanvasStyle(x) : this.border;
      x.fillStyle = ( this.color && this.color.toCanvasStyle ) ?
        this.color.toCanvasStyle(x) : this.color;
      this.doTransform(x);
      if ( this.shadowColor && this.shadowBlur ) {
        x.shadowColor = this.shadowColor;
        x.shadowBlur  = this.shadowBlur;
      }
      this.paintSelf(x);
      this.paintChildren(x);
      x.restore();
    },

    function doTransform(x) {
      var t = this.transform.matrix;
      x.transform(t.a, t.d, t.b, t.e, t.c, t.f);
    },

    function paintChildren(x) {
      for ( var i = 0 ; i < this.children.length ; i++ ) {
        this.children[i].paint(x);
      }
    },

    function addChild_(c) {
      c.invalidated.sub(this.onChildUpdate);
      return c;
    },

    function removeChild_(c) {
      c.invalidated.unsub(this.onChildUpdate);
      return c;
    },

    function addChildren() {
      for ( var i = 0 ; i < arguments.length ; i++ ) {
        this.children.push(arguments[i]);
        // this.addChild_(arguments[i]);
      }
    },

    function paintSelf(x) {},

    // TODO: Replace with toE() when/if ready.
    function toHTML() {
      return this.Canvas.create({
        width: this.x + this.width || this.r * 2,
        height: this.y + this.height || this.r * 2,
        cview: this
      }).toHTML();
    }
  ],

  listeners: [
    {
      name: 'onChildUpdate',
      isFramed: true,
      code: function onChildUpdate() { this.pub('invalidated'); }
    }
  ]
});

foam.CLASS({
  package: 'foam.graphics',
  name: 'Box',
  extends: 'foam.graphics.CView',

  properties: [
    {
      name: 'width',
      class: 'Float'
    },
    {
      name: 'height',
      class: 'Float'
    },
    {
      name: 'border',
      value: 'black'
    }
  ],

  methods: [
    function paintSelf(x) {
      x.beginPath();
      x.rect(0, 0, this.width, this.height);
      if ( this.border ) x.stroke();
      if ( this.color ) x.fill();
    }
  ]
});

foam.CLASS({
  package: 'foam.graphics',
  name: 'Arc',
  extends: 'foam.graphics.CView',

  properties: [
    {
      name: 'radius',
      class: 'Float'
    },
    {
      name: 'start',
      class: 'Float'
    },
    {
      name: 'end',
      class: 'Float'
    },
    {
      name: 'arcWidth',
      class: 'Float'
    },
    {
      name: 'border',
      value: 'black'
    }
  ],

  methods: [
    function paintSelf(x) {
      x.beginPath();
      x.arc(0, 0, this.radius, this.start, this.end);

      if ( this.color ) x.fill();

      if ( this.border ) {
        x.lineWidth = this.arcWidth;
        x.stroke();
      }
    }
  ]
});

foam.CLASS({
  package: 'foam.graphics',
  name: 'Circle',
  extends: 'foam.graphics.Arc',

  properties: [
    {
      name: 'start',
      value: 0
    },
    {
      name: 'end',
      value: 2*Math.PI
    }
  ],

  methods: [
    function intersects(c) {
      var r = this.radius + c.radius;
      if ( this.border ) r += this.arcWidth/2-1;
      if ( c.border    ) r += c.arcWidth/2-1;
      return foam.math.distance(this.x-c.x, this.y-c.y) <= r;
    }
  ]
});

foam.CLASS({
  package: 'foam.graphics',
  name: 'Canvas',

  imports: [ 'getElementById' ],

  properties: [
    {
      name: 'id',
      factory: function() { return 'v' + this.$UID; }
    },
    {
      name: 'width',
    },
    {
      name: 'height',
    },
    {
      name: 'element',
      factory: function() {
        var e = this.getElementById(this.id);
        e.width = this.width;
        e.height = this.height;
        return e;
      },
      postSet: function(_, e) {
        e.width = this.width;
        e.height = this.height;
      }
    },
    {
      name: 'context',
      factory: function() {
        return this.element.getContext('2d');
      }
    },
    {
      name: 'cview',
      postSet: function(o, n) {
        o && o.invalidated.unsub(this.paint);
        n && n.invalidated.sub(this.paint);
        this.paint();
      }
    }
  ],

  methods: [
    function erase() {
      this.element.width = this.element.width;
    }
  ],

  listeners: [
    {
      name: 'paint',
      isFramed: true,
      code: function() {
        this.erase();
        this.cview && this.cview.paint(this.context);
      }
    }
  ],

  templates: [
    {
      name: 'toHTML',
      template: '<canvas id="<%= this.id %>"></canvas>'
    }
  ]
});

foam.CLASS({
  package: 'foam.graphics',
  name: 'Gradient',

  properties: [
    'x0', 'y0', 'r0',
    'x1', 'y1', 'r1',
    {
      name: 'radial',
      class: 'Boolean',
      value: false
    },
    {
      name: 'colors',
      factory: function() {
        return [];
      }
    }
  ],

  methods: [
    function toCanvasStyle(x) {
      var t = this;
      var g = this.radial ?
        x.createRadialGradient(t.x0, t.y0, t.r0, t.x1, t.y1, t.r1) :
        x.createLinearGradient(t.x0, t.y0, t.x1, t.y1) ;

      for ( var i = 0 ; i < t.colors.length ; i++ )
        g.addColorStop(t.colors[i][0], t.colors[i][1]);

      return g;
    }
  ]
});

// TODO: add configurable repaint strategy. Ex. explicit, on property change, on child change
<|endoftext|>


<commit_before>/* eslint-disable import/prefer-default-export */
import { Navigation } from 'react-native-navigation';

import SideMenu from './modules/_global/SideMenu';
import Movies from './modules/movies/Movies';

export function registerScreens(store, Provider) {
	Navigation.registerComponent('movieapp.Movies', () => Movies, store, Provider);
	Navigation.registerComponent('movieapp.SideMenu', () => SideMenu);
}
<commit_msg>Register MoviesList component<commit_after>/* eslint-disable import/prefer-default-export */
import { Navigation } from 'react-native-navigation';

import SideMenu from './modules/_global/SideMenu';
import Movies from './modules/movies/Movies';
import MoviesList from './modules/movies/MoviesList';

export function registerScreens(store, Provider) {
	Navigation.registerComponent('movieapp.Movies', () => Movies, store, Provider);
	Navigation.registerComponent('movieapp.MoviesList', () => MoviesList, store, Provider);
	Navigation.registerComponent('movieapp.SideMenu', () => SideMenu);
}
<|endoftext|>


<commit_before>var Context = process.binding('evals').Context,
    // TODO: Remove .Script when bumping req'd node version to >= 5.0
    Script = process.binding('evals').NodeScript || process.binding('evals').Script;

exports.javascript = function(element, code, filename) {
  var document = element.ownerDocument,
      window = document.parentWindow;

  if (window) {
    var ctx = window.__scriptContext;
    if (!ctx) {
      window.__scriptContext = ctx = new Context();
      ctx.__proto__ = window;
    }
    var tracelimitbak = Error.stackTraceLimit;
    Error.stackTraceLimit = 100;
    try {
      Script.runInContext(code, ctx, filename);
    }
    catch(e) {
      document.trigger(
        'error', 'Running ' + filename + ' failed.',
        {error: e, filename: filename}
      );
    }
    Error.stackTraceLimit = tracelimitbak;
  }
};
<commit_msg>process.binding('evals') replaced by 'vm' usage<commit_after>var vm = require('vm');

exports.javascript = function(element, code, filename) {
  var document = element.ownerDocument,
      window = document.parentWindow;

  if (window) {
    var ctx = window.__scriptContext;
    if (!ctx) {
      window.__scriptContext = ctx = vm.createContext({});
      ctx.__proto__ = window;
    }
    var tracelimitbak = Error.stackTraceLimit;
    Error.stackTraceLimit = 100;
    try {
      vm.Script.runInContext(code, ctx, filename);
    }
    catch(e) {
      document.trigger(
        'error', 'Running ' + filename + ' failed.',
        {error: e, filename: filename}
      );
    }
    Error.stackTraceLimit = tracelimitbak;
  }
};
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import Dropzone from 'react-dropzone';
import ApplicationTextField from './ApplicationTextField';
import SchoolInputField from './SchoolInputField';
import ResumeUploadProgressIndicator from './ResumeUploadProgressIndicator';

class ApplicationForm extends Component {

    // componentDidMount() {
    //      this.props.fetchApplication();
    // }

    toggleItem(item) {
        this.props.toggleApplicationFieldValue(item);
    }

    render () {
        const applicationForm = this.props.application.applicationForm;
        const isLoading = this.props.application.loading;
        const isGithubLinked = this.props.user.me && (this.props.user.me.github_user_id !== null);
        let dropzoneRef;
        return (
            <Dropzone
                ref={(node) => { dropzoneRef = node; }}
                disableClick
                multiple={false}
                accept="application/pdf"
                onDrop={this.props.onResumeDrop.bind(this)}
                style={{border: '1px solid red', height: '100%'}}
            >

First Name<ApplicationTextField field="first_name"/>

Last Name<ApplicationTextField field="last_name"/>

Major<ApplicationTextField field="major"/>

LinkedIn
                        {
                            applicationForm.has_no_linkedin
                                ? You indicated you don't have a Linedin <button onClick={this.toggleItem.bind(this,'has_no_linkedin')}>i do!</button>
                                :

                                    <ApplicationTextField field="linkedin"/>
                                    <button onClick={this.toggleItem.bind(this,'has_no_linkedin')}>i don't have one</button>

                        }

                    {/*If a user signs up with a github (isGithubLinked), they can't change their github username, nor can they opt out of providing their username*/}

Github
                        {
                            applicationForm.has_no_github && !isGithubLinked
                                ? You indicated you don't have a Github <button onClick={this.toggleItem.bind(this,'has_no_github')}>i do!</button>
                                :

                                    <ApplicationTextField field="github" disabled={isGithubLinked}/>
                                    {
                                        isGithubLinked
                                            ? You signed up with github, so you can't change the username
                                            : <button onClick={this.toggleItem.bind(this,'has_no_github')}>i don't have a github</button>
                                    }

                        }

                    Drag your resume anywhere on the page or....
                    <button type="button" onClick={() => { dropzoneRef.open() }}>Open File Dialog</button>
                    <ResumeUploadProgressIndicator/>

                    { applicationForm.resume_uploaded ?
You've uploaded {applicationForm.resume_filename}
 : null }

                    school<SchoolInputField/>

                        TODO:
                        Graduation Year (dropdown??)
                        Gender (what format?)
                        Race (dropdown??)
                        Is this your first hackathon (yesno picker or checkbox??)

                    <button disabled={isLoading} onClick={()=>{this.props.saveApplication()}}>save</button>

            </Dropzone>

        );
    }
}

//now the redux integration layer
import {
    fetchApplication,
    saveApplication,
    onResumeDrop,
    toggleApplicationFieldValue
} from '../../actions/application';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'
function mapStateToProps (state) {
    return {
        user: state.user,
        application: state.application
    };
}

const mapDispatchToProps = (dispatch) => {
    return bindActionCreators({
        fetchApplication,
        saveApplication,
        onResumeDrop,
        toggleApplicationFieldValue
    }, dispatch)
};

export default connect(mapStateToProps, mapDispatchToProps)(ApplicationForm);
<commit_msg>add in some application todos<commit_after>import React, { Component } from 'react';
import Dropzone from 'react-dropzone';
import ApplicationTextField from './ApplicationTextField';
import SchoolInputField from './SchoolInputField';
import ResumeUploadProgressIndicator from './ResumeUploadProgressIndicator';

class ApplicationForm extends Component {

    // componentDidMount() {
    //      this.props.fetchApplication();
    // }

    toggleItem(item) {
        this.props.toggleApplicationFieldValue(item);
    }

    render () {
        const applicationForm = this.props.application.applicationForm;
        const isLoading = this.props.application.loading;
        const isGithubLinked = this.props.user.me && (this.props.user.me.github_user_id !== null);
        let dropzoneRef;
        return (
            <Dropzone
                ref={(node) => { dropzoneRef = node; }}
                disableClick
                multiple={false}
                accept="application/pdf"
                onDrop={this.props.onResumeDrop.bind(this)}
                style={{border: '1px solid red', height: '100%'}}
            >

First Name<ApplicationTextField field="first_name"/>

Last Name<ApplicationTextField field="last_name"/>

Major<ApplicationTextField field="major"/>

                    {/*TODO: move Linkedin to its own component? */}

LinkedIn
                        {
                            applicationForm.has_no_linkedin
                                ? You indicated you don't have a Linedin <button onClick={this.toggleItem.bind(this,'has_no_linkedin')}>i do!</button>
                                :

                                    <ApplicationTextField field="linkedin"/>
                                    <button onClick={this.toggleItem.bind(this,'has_no_linkedin')}>i don't have one</button>

                        }

                    {/*TODO: move Github to its own component? */}
                    {/*If a user signs up with a github (isGithubLinked), they can't change their github username, nor can they opt out of providing their username*/}

Github
                        {
                            applicationForm.has_no_github && !isGithubLinked
                                ? You indicated you don't have a Github <button onClick={this.toggleItem.bind(this,'has_no_github')}>i do!</button>
                                :

                                    <ApplicationTextField field="github" disabled={isGithubLinked}/>
                                    {
                                        isGithubLinked
                                            ? You signed up with github, so you can't change the username
                                            : <button onClick={this.toggleItem.bind(this,'has_no_github')}>i don't have a github</button>
                                    }

                        }

                    Drag your resume anywhere on the page or....
                    <button type="button" onClick={() => { dropzoneRef.open() }}>Open File Dialog</button>
                    <ResumeUploadProgressIndicator/>

                    { applicationForm.resume_uploaded ?
You've uploaded {applicationForm.resume_filename}
 : null }

                    school<SchoolInputField/>

                        TODO:
                        Graduation Year (dropdown??)
                        Gender (what format?)
                        Race (dropdown??)
                        Is this your first hackathon (yesno picker or radio??)

                    <button disabled={isLoading} onClick={()=>{this.props.saveApplication()}}>save</button>
                    Does the server say your application is completed? {applicationForm.completed? ' yes! ': 'no...'}

            </Dropzone>

        );
    }
}

//now the redux integration layer
import {
    fetchApplication,
    saveApplication,
    onResumeDrop,
    toggleApplicationFieldValue
} from '../../actions/application';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'
function mapStateToProps (state) {
    return {
        user: state.user,
        application: state.application
    };
}

const mapDispatchToProps = (dispatch) => {
    return bindActionCreators({
        fetchApplication,
        saveApplication,
        onResumeDrop,
        toggleApplicationFieldValue
    }, dispatch)
};

export default connect(mapStateToProps, mapDispatchToProps)(ApplicationForm);
<|endoftext|>


<commit_before><commit_msg>Fixed Bug-1<commit_after><|endoftext|>