<commit_before>"use strict";

var assert = require('assert');

var add = function(x,y) { return x + y; };

var Game = function() {

    this._pins = [];

    this.roll = function(n) {
	this._pins.push(n);
    };

    this._scoreSpare = function(i) {
	return this._scoreFrame(i) + this._pins[i+2];
    };

    this._scoreFrame = function(i) {
	return this._pins[i] + this._pins[i+1];
    };

    this._isSpare = function(i) {
	return this._pins[i] + this._pins[i+1] === 10;
    };

    this._isStrike = function(i) {
	return this._pins[i] === 10;
    };

    this._scoreStrike = function(i) {
	return 10 + this._pins[i+1] + this._pins[i+2];
    };

    this.score = function() {

	var sum = 0;

	var pinCount = 0;
	for (var i=0;i<10;++i) {
	    if (this._isStrike(i)) {
		sum += this._scoreStrike(pinCount);
		pinCount+=1;
	    }
	    else if (this._isSpare(pinCount)) {
		sum += this._scoreSpare(pinCount);
		pinCount+=2;
	    }
	    else {
		sum += this._scoreFrame(pinCount);
		pinCount+=2;
	    }
	}

	return sum;
    };

    return this;
};

describe('bowling game', function() {

    var rollMany = function(game, roll, n) {
	for (var i=0;i<n;++i) {
	    game.roll(roll);
	}
    };

    var rollSpare = function(game) {
	game.roll(5);
	game.roll(5);
    };

    it('rolling 20 gutter balls is a gutter game', function() {
	var game = new Game();
	rollMany(game, 0, 20);

	assert.equal(0, game.score());
    });

    it('hitting 20 single pins scores 20', function() {
	var game = new Game();
	rollMany(game, 1, 20);

	assert.equal(20, game.score());
    });

    it('handles spares', function() {
	var game = new Game();
	rollSpare(game);
	game.roll(3);

	// Rest is a gutter game
	rollMany(game, 0,17);

	assert.equal(16, game.score());
    });

    it('handles strikes', function() {
	var game = new Game();
	game.roll(10);
	game.roll(3);
	game.roll(3);

	rollMany(game, 0, 16);

	assert.equal(10 + 6 + 6, game.score());
    });
});
<commit_msg>can now bowl a perfect game<commit_after>"use strict";

var assert = require('assert');

var add = function(x,y) { return x + y; };

var Game = function() {

    this._pins = [];

    this.roll = function(n) {
	this._pins.push(n);
    };

    this._scoreSpare = function(i) {
	return this._scoreFrame(i) + this._pins[i+2];
    };

    this._scoreFrame = function(i) {
	return this._pins[i] + this._pins[i+1];
    };

    this._isSpare = function(i) {
	return this._pins[i] + this._pins[i+1] === 10;
    };

    this._isStrike = function(i) {
	return this._pins[i] === 10;
    };

    this._scoreStrike = function(i) {
	return 10 + this._pins[i+1] + this._pins[i+2];
    };

    this.score = function() {

	var sum = 0;

	var pinCount = 0;
	for (var i=0;i<10;++i) {
	    if (this._isStrike(i)) {
		sum += this._scoreStrike(pinCount);
		pinCount+=1;
	    }
	    else if (this._isSpare(pinCount)) {
		sum += this._scoreSpare(pinCount);
		pinCount+=2;
	    }
	    else {
		sum += this._scoreFrame(pinCount);
		pinCount+=2;
	    }
	}

	return sum;
    };

    return this;
};

describe('bowling game', function() {

    var rollMany = function(game, roll, n) {
	for (var i=0;i<n;++i) {
	    game.roll(roll);
	}
    };

    var rollSpare = function(game) {
	game.roll(5);
	game.roll(5);
    };

    it('rolling 20 gutter balls is a gutter game', function() {
	var game = new Game();
	rollMany(game, 0, 20);

	assert.equal(0, game.score());
    });

    it('hitting 20 single pins scores 20', function() {
	var game = new Game();
	rollMany(game, 1, 20);

	assert.equal(20, game.score());
    });

    it('handles spares', function() {
	var game = new Game();
	rollSpare(game);
	game.roll(3);

	// Rest is a gutter game
	rollMany(game, 0,17);

	assert.equal(16, game.score());
    });

    it('handles strikes', function() {
	var game = new Game();
	game.roll(10);
	game.roll(3);
	game.roll(3);

	rollMany(game, 0, 16);

	assert.equal(10 + 6 + 6, game.score());
    });

    it('handles a perfect game', function() {
	var game = new Game();
	rollMany(game, 10, 12);

	assert.equal(300, game.score());
    });
});
<|endoftext|>


<commit_before>(function () {
  'use strict';

  angular.module('ngDragToReorder', [])
    .factory('ngDragToReorder', function () {

      var _list = [];

      return {
        isSupported: draggable,
        setList: setList,
        getList: getList
      };

      function setList(array) {
        if (array && array.length) _list = array;
      }

      function getList() {
        return _list;
      }

      function draggable() {
        var div = document.createElement('div');
        return ('draggable' in div) || ('ondragstart' in div && 'ondrop' in div);
      }

    })
    .directive('dragToReorder', ['ngDragToReorder', '$parse', function (ngDragToReorder, $parse) {
      return {
        restrict: 'A',
        link: function (scope, element, attrs, ctrl) {

          if (!ngDragToReorder.isSupported()) return;

          ngDragToReorder.setList($parse(attrs.dragToReorder)((scope)));

          var el = element[0], list, stringIdx, int, item,
            newIdx, startIndex, target, offsetY, dragging = 'dtr-dragging', over = 'dtr-over',
            droppingAbove = 'dtr-dropping-above', droppingBelow = 'dtr-dropping-below', transition = 'dtr-transition',
            delay = 1000, loaded = false, above = [],  below = [], i, j;

          //console.log('list:', list);

          if (attrs.hasOwnProperty('dtrInit')) {
            attrs.$observe('dtrInit', function (val) {
              if (val && val !== 'false') {
                addListeners();
              } else if (loaded) {
                removeListeners();
              }
              loaded = true;
            });
          } else {
            addListeners();
          }

          if (attrs.hasOwnProperty('dtrTransitionTimeout')) {
            int = parseInt(attrs.dtrTransitionTimeout, 10);
            delay = int ? int : 1000;
          }

          function addListeners() {
            el.draggable = true;
            el.addEventListener('dragstart', dragStart, false);
            el.addEventListener('dragend', dragEnd, false);
            el.addEventListener('dragenter', dragEnter, false);
            el.addEventListener('dragleave', dragLeave, false);
            el.addEventListener('dragover', dragOver, false);
            el.addEventListener('drop', drop, false);
          }

          function removeListeners() {
            el.draggable = false;
            el.removeEventListener('dragstart', dragStart, false);
            el.removeEventListener('dragend', dragEnd, false);
            el.removeEventListener('dragenter', dragEnter, false);
            el.removeEventListener('dragleave', dragLeave, false);
            el.removeEventListener('dragover', dragOver, false);
            el.removeEventListener('drop', drop, false);
          }

          function drop(e) {
            e.preventDefault();
            if (e.stopPropagation) e.stopPropagation();
            startIndex = parseInt(e.dataTransfer.getData('text'), 10);
            this.classList.remove(over);
            if (this.classList.contains(droppingAbove)) {
              if (startIndex < scope.$index) {
                newIdx = scope.$index - 1;
              } else {
                newIdx = scope.$index;
              }
            } else {
              if (startIndex < scope.$index) {
                newIdx = scope.$index;
              } else {
                newIdx = scope.$index + 1;
              }
            }

            list = ngDragToReorder.getList();
            item = list.splice(startIndex, 1)[0];
            list.splice(newIdx, 0, item);

            ngDragToReorder.setList(list);

            scope.$apply(function () {
              scope.$emit('dragToReorder_drop', {
                item: item,
                newIndex: newIdx,
                prevIndex: startIndex,
                list: list
              });
            });

            this.classList.remove(over);
            this.classList.remove(droppingAbove);
            this.classList.remove(droppingBelow);
            if (this.previousElementSibling) {
              this.previousElementSibling.classList.remove(over);
              this.previousElementSibling.classList.remove(droppingAbove);
              this.previousElementSibling.classList.remove(droppingBelow);
            }
            if (this.nextElementSibling) {
              this.nextElementSibling.classList.remove(over);
              this.nextElementSibling.classList.remove(droppingAbove);
              this.nextElementSibling.classList.remove(droppingBelow);
            }

          }

          function dragStart(e) {
            e.dataTransfer.effectAllowed = 'move';
            // **IMPORTANT** In order for this to work in IE, 'text' MUST
            // not 'Text', ot 'text/plain' etc...
            // Also the value (idx) needs to be a string
            stringIdx = scope.$index.toString();
            e.dataTransfer.setData('text', stringIdx);
            this.classList.add(dragging);
            this.classList.add(transition);
            return false;
          }

          function dragEnd(e) {
            target = this;
            target.classList.remove(dragging);
            setTimeout(function () {
              target.classList.remove(transition)
            }, delay);
            cleanupClasses();
            return false;
          }

          function dragOver(e) {
            e.preventDefault();
            e.dataTransfer.dropEffect = 'move';
            offsetY = e.offsetY;
            if (offsetY < (this.offsetHeight / 2)) {
              this.classList.remove(droppingBelow);
              this.classList.add(droppingAbove);
              if (this.previousElementSibling)
                this.previousElementSibling.classList.add(droppingBelow);
              if (this.nextElementSibling)
                this.nextElementSibling.classList.remove(droppingAbove);
            } else {
              this.classList.remove(droppingAbove);
              this.classList.add(droppingBelow);
              if (this.previousElementSibling)
                this.previousElementSibling.classList.remove(droppingBelow);
              if (this.nextElementSibling)
                this.nextElementSibling.classList.add(droppingAbove);
            }
            return false;
          }

          function dragEnter(e) {
            e.preventDefault();
            if (!this.classList.contains(dragging))
              this.classList.add(over);
          }

          function dragLeave(e) {
            this.classList.remove(over);
            this.classList.remove(droppingAbove);
            this.classList.remove(droppingBelow);
            return false;
          }

          function cleanupClasses () {
            above = document.querySelectorAll('.' + droppingAbove);
            below = document.querySelectorAll('.' + droppingBelow);
            if (above.length) {
              for (i = 0; i < above.length; i++) {
                above[i].classList.remove(droppingAbove);
              }
            }
            if (below.length) {
              for (i = 0; i < below.length; i++) {
                below[i].classList.remove(droppingBelow);
              }
            }
          }

        }
      };

    }]);

})();
<commit_msg>trying to get git tag to work<commit_after>(function () {
  'use strict';

  angular.module('ngDragToReorder', [])
    .factory('ngDragToReorder', function () {

      var _list = [];

      return {
        isSupported: draggable,
        setList: setList,
        getList: getList
      };

      function setList(array) {
        if (array && array.length) _list = array;
      }

      function getList() {
        return _list;
      }

      function draggable() {
        var div = document.createElement('div');
        return ('draggable' in div) || ('ondragstart' in div && 'ondrop' in div);
      }

    })
    .directive('dragToReorder', ['ngDragToReorder', '$parse', function (ngDragToReorder, $parse) {
      return {
        restrict: 'A',
        link: function (scope, element, attrs, ctrl) {

          if (!ngDragToReorder.isSupported()) return;

          ngDragToReorder.setList($parse(attrs.dragToReorder)((scope)));

          var el = element[0], list, stringIdx, int, item,
            newIdx, startIndex, target, offsetY, dragging = 'dtr-dragging', over = 'dtr-over',
            droppingAbove = 'dtr-dropping-above', droppingBelow = 'dtr-dropping-below', transition = 'dtr-transition',
            delay = 1000, loaded = false, above = [],  below = [], i, j;

          //console.log('list:', list);

          if (attrs.hasOwnProperty('dtrInit')) {
            attrs.$observe('dtrInit', function (val) {
              if (val && val !== 'false') {
                addListeners();
              } else if (loaded) {
                removeListeners();
              }
              loaded = true;
            });
          } else {
            addListeners();
          }

          if (attrs.hasOwnProperty('dtrTransitionTimeout')) {
            int = parseInt(attrs.dtrTransitionTimeout, 10);
            delay = int ? int : 1000;
          }

          function addListeners() {
            el.draggable = true;
            el.addEventListener('dragstart', dragStart, false);
            el.addEventListener('dragend', dragEnd, false);
            el.addEventListener('dragenter', dragEnter, false);
            el.addEventListener('dragleave', dragLeave, false);
            el.addEventListener('dragover', dragOver, false);
            el.addEventListener('drop', drop, false);
          }

          function removeListeners() {
            el.draggable = false;
            el.removeEventListener('dragstart', dragStart, false);
            el.removeEventListener('dragend', dragEnd, false);
            el.removeEventListener('dragenter', dragEnter, false);
            el.removeEventListener('dragleave', dragLeave, false);
            el.removeEventListener('dragover', dragOver, false);
            el.removeEventListener('drop', drop, false);
          }

          function drop(e) {
            e.preventDefault();
            if (e.stopPropagation) e.stopPropagation();
            startIndex = parseInt(e.dataTransfer.getData('text'), 10);
            this.classList.remove(over);
            if (this.classList.contains(droppingAbove)) {
              if (startIndex < scope.$index) {
                newIdx = scope.$index - 1;
              } else {
                newIdx = scope.$index;
              }
            } else {
              if (startIndex < scope.$index) {
                newIdx = scope.$index;
              } else {
                newIdx = scope.$index + 1;
              }
            }

            list = ngDragToReorder.getList();
            item = list.splice(startIndex, 1)[0];
            list.splice(newIdx, 0, item);

            ngDragToReorder.setList(list);

            scope.$apply(function () {
              scope.$emit('dragToReorder_drop', {
                item: item,
                newIndex: newIdx,
                prevIndex: startIndex,
                list: list
              });
            });

            this.classList.remove(over);
            this.classList.remove(droppingAbove);
            this.classList.remove(droppingBelow);
            if (this.previousElementSibling) {
              this.previousElementSibling.classList.remove(over);
              this.previousElementSibling.classList.remove(droppingAbove);
              this.previousElementSibling.classList.remove(droppingBelow);
            }
            if (this.nextElementSibling) {
              this.nextElementSibling.classList.remove(over);
              this.nextElementSibling.classList.remove(droppingAbove);
              this.nextElementSibling.classList.remove(droppingBelow);
            }

          }

          function dragStart(e) {
            e.dataTransfer.effectAllowed = 'move';
            // **IMPORTANT** In order for this to work in IE, 'text' MUST
            // not 'Text', ot 'text/plain' etc...
            // Also the value (idx) needs to be a string
            stringIdx = scope.$index.toString();
            e.dataTransfer.setData('text', stringIdx);
            this.classList.add(dragging);
            this.classList.add(transition);
            return false;
          }

          function dragEnd(e) {
            target = this;
            target.classList.remove(dragging);
            setTimeout(function () {
              target.classList.remove(transition)
            }, delay);
            cleanupClasses();
            return false;
          }

          function dragOver(e) {
            e.preventDefault();
            e.dataTransfer.dropEffect = 'move';
            offsetY = e.offsetY;
            if (offsetY < (this.offsetHeight / 2)) {
              this.classList.remove(droppingBelow);
              this.classList.add(droppingAbove);
              if (this.previousElementSibling)
                this.previousElementSibling.classList.add(droppingBelow);
              if (this.nextElementSibling)
                this.nextElementSibling.classList.remove(droppingAbove);
            } else {
              this.classList.remove(droppingAbove);
              this.classList.add(droppingBelow);
              if (this.previousElementSibling)
                this.previousElementSibling.classList.remove(droppingBelow);
              if (this.nextElementSibling)
                this.nextElementSibling.classList.add(droppingAbove);
            }
            return false;
          }

          function dragEnter(e) {
            e.preventDefault();
            if (!this.classList.contains(dragging))
              this.classList.add(over);
          }

          function dragLeave(e) {
            this.classList.remove(over);
            this.classList.remove(droppingAbove);
            this.classList.remove(droppingBelow);
            return false;
          }

          function cleanupClasses () {
            above = document.querySelectorAll('.' + droppingAbove);
            below = document.querySelectorAll('.' + droppingBelow);
            if (above.length) {
              for (i = 0; i < above.length; i++) {
                above[i].classList.remove(droppingAbove);
              }
            }
            if (below.length) {
              for (i = 0; i < below.length; i++) {
                below[i].classList.remove(droppingBelow);
              }
            }
          }

        }
      };

    }]);

})();
<|endoftext|>


<commit_before>""
/**
 * Created by abraham on 2/17/15.
 */

Zsqrt = Math.sqrt;

Math.sqrt = function(parameter) {
    var z = Z(parameter);
    if (z)
        return z.sqrt();
    return Zsqrt(parameter);
};

function Z (a, b) {

    /*
     This construction accepts Strings of the formats - "Mod < Arg", "Re + jIm",
     and process accordingly
     */

    //FIELD DECLARATION - ALL FIELDS ARE CURRENTLY ARE PRIVATE
    //ACCESS WITH set AND/OR get METHODS
    var Re = 0;
    var Im = 0;
    var ZInRectForm = true;
    var argInRad = true;

    //INITIALIZATION PROCESSING
    if (typeof a === "number" && typeof b === "number") {
        Re = a;
        Im = b;
    } else if (typeof a === "string" && typeof b === "string") {
        Re = parseFloat(a);
        Im = parseFloat(b);
    } else if (a instanceof Z) {
        Re = a.getRe();
        Im = a.getIm();
        ZInRectForm = a.isInRectForm();
        argInRad = a.isOpInRad();
    }else{
    }

    this.isOpInRad = function () {
        return argInRad;
    };

    this.isInRectForm = function () {
        return ZInRectForm;
    };

    var processStr = function (a) {

    };

    var prcessStrNumArgMix;

    //this.getPolar = function () {};
    //this.getRect = function () {};
    this.doOpInPolForm = function () {
        ZInRectForm = false;
    };
    this.doOpInRectForm = function () {
        ZInRectForm = true;
    };
    this.toString = function () {

    };
    this.valueOf = function () {
        return this.toString();
    };
    this.negate =  function () {
        Re = -Re;
        Im = -Im;
    };
    this.doInverse = function () {
        this.doPow(-1);
    };
    this.getAbs = function () {
        return Math.sqrt(Math.pow(Re, 2) + Math.pow(Im, 2));

    };
    this.getMod = function () {
        return this.getAbs();
    };
    this.getArg = function () {
        if ( (Re * Im) > 0)
            if( Re > 0) {
                return argInRad ? Math.atan(Im/Re) : Math.atan(Im/Re*180/Number.PI);
            } else {
                return argInRad ? Math.PI + Math.atan(Im/Re) : 180 + Math.atan(Im/Re*180/Number.PI);
            }
        else
        if (Re > 0)
            return argInRad ? 2  * Math.PI + Math.atan(Im/Re) : 360 + Math.atan(Im/Re*180/Number.PI);
        else
            return argInRad ? Math.PI + Math.atan(Im/Re) : 180 + Math.atan(Im/Re*180/Number.PI);

    };
    this.getRe = function () {
        return Re;
    };
    this.getIm = function () {
        return Im;
    };
    this.divideBy = function (that) {
        var mod = this.getAbs() / that.getAbs();
        var arg = this.getArg() - that.getArg();
        Re = mod * Math.cos(arg);
        Im = mod * Math.sin(arg);
    };
    this.multiplyBy = function (that) {
        var mod = this.getAbs() * that.getAbs();
        var arg = this.getArg() + that.getArg();
        Re = mod * Math.cos(arg);
        Im = mod * Math.sin(arg);

    };
    this.addTo = function (that) {
        Re = this.getRe() + that.getRe();
        Im = this.getIm() + that.getIm();
    };
    //Not Recommended
    this.differenceFrom = function (that) {
        var _Re = this.getRe() - that.getRe();
        var _Im = this.getIm() - that.getIm();
        return new Z(_Re, _Im);
    };
    this.doPow = function(power) {
        var mod = Math.pow(this.getAbs(), power);
        var arg = this.getArg() * power;
        Re = mod * Math.cos(arg);
        Im = mod * Math.sin(arg);
    };
    this.doSqrt = function () {
        this.doPow(0.5);
    };

};

Z.prototype.sqrt = function (z) {
    Z.pow(z, 0.5);
    return z;
};

Z.prototype.add = function (z1, z2) {
    var _Re = z1.getRe() + z2.getRe();
    var _Im = z1.getIm() + z2.getIm();
    return new Z(_Re, _Im);
};
Z.prototype.substract = function (z1, z2) {
    var _Re = z1.getRe() - z2.getRe();
    var _Im = z1.getIm() - z2.getIm();
    return new Z(_Re, _Im);
};

Z.prototype.divide = function (z1,z2) {
    var mod = z1.getAbs() / z2.getAbs();
    var arg = z1.getArg() - z2.getArg();
    return new Z(mod * Math.cos(arg), mod * Math.sin(arg));
};
Z.prototype.multiply = function (z1,z2) {
    var mod = z1.getAbs() - z2.getAbs();
    var arg = z1.getArg() + z2.getArg();
    return new Z(mod * Math.cos(arg), mod * Math.sin(arg));
};
Z.prototype.pow = function(z, power) {
    var mod = Math.pow(z.getAbs(), power);
    var arg = z.getArg() * power;
    return new Z(mod * Math.cos(arg), mod * Math.sin(arg));
};<commit_msg>Corrected a few runtime errors. Implemented Z.toString()<commit_after>""
/**
 * Created by abraham on 2/17/15.
 */

Zsqrt = Math.sqrt;

Math.sqrt = function(parameter) {
    var z = Z(parameter);
    if (z)
        return z.sqrt();
    return Zsqrt(parameter);
};

function Z (a, b) {

    /*
     This construction accepts Strings of the formats - "Mod < Arg", "Re + jIm",
     and process accordingly
     */

    //FIELD DECLARATION - ALL FIELDS ARE CURRENTLY ARE PRIVATE
    //ACCESS WITH set AND/OR get METHODS
    var Re = 0;
    var Im = 0;
    var ZInRectForm = true;
    var argInRad = true;

    //INITIALIZATION PROCESSING
    if (typeof a === "number" && typeof b === "number") {
        Re = a;
        Im = b;
    } else if (typeof a === "string" && typeof b === "string") {
        Re = parseFloat(a);
        Im = parseFloat(b);
    } else if (a instanceof Z) {
        Re = a.getRe();
        Im = a.getIm();
        ZInRectForm = a.isInRectForm();
        argInRad = a.isOpInRad();
    }else{
    }

    this.isOpInRad = function () {
        return argInRad;
    };

    this.isInRectForm = function () {
        return ZInRectForm;
    };

    var processStr = function (a) {

    };

    var prcessStrNumArgMix;

    //this.getPolar = function () {};
    //this.getRect = function () {};
    this.doOpInPolForm = function () {
        ZInRectForm = false;
    };
    this.doOpInRectForm = function () {
        ZInRectForm = true;
    };
    this.toString = function () {
        return ZInRectForm? Re + " + j " +Im:  this.getAbs() + " < " + this.getArg();
    };
    this.valueOf = function () {
        return this.toString();
    };
    this.negate =  function () {
        Re = -Re;
        Im = -Im;
    };
    this.doInverse = function () {
        this.doPow(-1);
    };
    this.getAbs = function () {
        return Math.sqrt(Math.pow(Re, 2) + Math.pow(Im, 2));

    };
    this.getMod = function () {
        return this.getAbs();
    };
    this.getArg = function () {
        if ( (Re * Im) > 0)
            if( Re > 0) {
                return argInRad ? Math.atan(Im/Re) : Math.atan(Im/Re*180/Number.PI);
            } else {
                return argInRad ? Math.PI + Math.atan(Im/Re) : 180 + Math.atan(Im/Re*180/Number.PI);
            }
        else
        if (Re > 0)
            return argInRad ? 2  * Math.PI + Math.atan(Im/Re) : 360 + Math.atan(Im/Re*180/Number.PI);
        else
            return argInRad ? Math.PI + Math.atan(Im/Re) : 180 + Math.atan(Im/Re*180/Number.PI);

    };
    this.getRe = function () {
        return Re;
    };
    this.getIm = function () {
        return Im;
    };
    this.divideBy = function (that) {
        var mod = this.getAbs() / that.getAbs();
        var arg = this.getArg() - that.getArg();
        Re = mod * Math.cos(arg);
        Im = mod * Math.sin(arg);
    };
    this.multiplyBy = function (that) {
        var mod = this.getAbs() * that.getAbs();
        var arg = this.getArg() + that.getArg();
        Re = mod * Math.cos(arg);
        Im = mod * Math.sin(arg);

    };
    this.addTo = function (that) {
        Re = this.getRe() + that.getRe();
        Im = this.getIm() + that.getIm();
    };
    //Not Recommended
    this.differenceFrom = function (that) {
        var _Re = this.getRe() - that.getRe();
        var _Im = this.getIm() - that.getIm();
        return new Z(_Re, _Im);
    };
    this.doPow = function(power) {
        var mod = Math.pow(this.getAbs(), power);
        var arg = this.getArg() * power;
        Re = mod * Math.cos(arg);
        Im = mod * Math.sin(arg);
    };
    this.doSqrt = function () {
        this.doPow(0.5);
    };

};

Z.sqrt = function (z) {
    Z.pow(z, 0.5);
    return z;
};

Z.add = function (z1, z2) {
    var _Re = z1.getRe() + z2.getRe();
    var _Im = z1.getIm() + z2.getIm();
    return new Z(_Re, _Im);
};
Z.substract = function (z1, z2) {
    var _Re = z1.getRe() - z2.getRe();
    var _Im = z1.getIm() - z2.getIm();
    return new Z(_Re, _Im);
};

Z.divide = function (z1,z2) {
    var mod = z1.getAbs() / z2.getAbs();
    var arg = z1.getArg() - z2.getArg();
    return new Z(mod * Math.cos(arg), mod * Math.sin(arg));
};
Z.multiply = function (z1,z2) {
    var mod = z1.getAbs() * z2.getAbs();
    var arg = z1.getArg() + z2.getArg();
    return new Z(mod * Math.cos(arg), mod * Math.sin(arg));
};
Z.pow = function(z, power) {
    var mod = Math.pow(z.getAbs(), power);
    var arg = z.getArg() * power;
    return new Z(mod * Math.cos(arg), mod * Math.sin(arg));
};<|endoftext|>


<commit_before>import test from 'ava';
import {upgrade, pilot, condition, _processCard, _slackifyText} from '../lib/cardProcessor';

test('slackify - simple string', async t => {
	const testText = 'You may treat all 1- and 2-speed maneuvers as green maneuvers.';
	t.is(testText, _slackifyText(testText));
});

test('slackify - with strong', async t => {
	t.is(
		'*Attack:* Attack 1 ship Then cancel *all* dice results.',
		_slackifyText('Attack: Attack 1 ship Then cancel all dice results.')
	);
});

test('slackify - with em', async t => {
	t.is(
		'the _Nastah Pup_ Pilot',
		_slackifyText('the Nastah Pup Pilot')
	);
});

test('slackify - with b', async t => {
	t.is(
		'*Attack:* Attack 1 ship. If this attack hits',
		_slackifyText('Attack: Attack 1 ship. If this attack hits')
	);
});

test('slackify - with i', async t => {
	t.is(
		'While you are docked, the _Ghost_ can perform [Turret].',
		_slackifyText('While you are docked, the Ghost can perform [Turret].')
	);
});

test('slackify - with linebreak', async t => {
	t.is(
		'Roll 1 defense die.\nOn a [Evade] or [Focus]',
		_slackifyText('Roll 1 defense die.

On a [Evade] or [Focus]')
	);
});

test('slackify - complex string', async t => {
	t.is(
		'After you are *destroyed*, before you are [removed] from the "play area", you may *deploy* the _Nastah Pup_ Pilot.\nIt _cannot_ attack this round.',
		_slackifyText('After you are destroyed, before you are [removed] from the \"play area\", you may deploy the Nastah Pup Pilot.

It cannot attack this round.')
	);
});

test('process - basic', async t => {
	const testData = {
		name:   'R2 Astromech',
		id:     2,
		slot:   'Astromech',
		points: 1,
		text:   'You may treat all 1- and 2-speed maneuvers
 as green maneuvers.',
		image:  'upgrades/Astromech/r2-astromech.png',
		xws:    'r2astromech'
	};

	t.deepEqual(
		{
			name:   'R2 Astromech',
			key:    'r2 astromech',
			id:     2,
			slot:   'Astromech',
			points: 1,
			text:   'You may treat *all* 1- and 2-speed maneuvers\n as green maneuvers.',
			image:  'http://upgrades/Astromech/r2-astromech.png',
			xws:    'r2astromech'
		},
		_processCard(testData, 'http://')
	);
});

test('process - no text', async t => {
	const testData = {
		name:   'R2 Astromech',
		id:     2,
		slot:   'Astromech',
		points: 1,
		image:  'upgrades/Astromech/r2-astromech.png',
		xws:    'r2astromech'
	};

	t.is('', _processCard(testData, '')['text']);
});

test('process - no text', async t => {
	const testData = {
		name:   'Seismic Charges',
		id:     24,
		slot:   'Bomb',
		points: 2,
		text:   'some text',
		effect: 'other text',
		image:  'upgrades/Bomb/seismic-charges.png',
		xws:    'seismiccharges'
	};

	t.is('other *text*', _processCard(testData, '')['effect']);
});

test.todo('upgrade - basic');
test.todo('upgrade - has ship');
test.todo('upgrade - has conditions');
test.todo('upgrade - has size');
test.todo('upgrade - has grants');

test.todo('pilot - basic');
test.todo('pilot - has slots');
test.todo('pilot - has conditions');
test.todo('pilot - has ship_override');

test('condition - basic', async t => {
	const testData = {
		image:  'conditions/a-debt-to-pay.png',
		text:   `When attacking a ship that has the \"A Score to Settle\" Upgrade card, you may change 1 [Focus] result to a [Critical Hit] result.`,
		name:   'A Debt to Pay',
		xws:    'adebttopay',
		unique: true,
		id:     3
	};

	t.is('condition', condition(testData, '')['slot']);
});
<commit_msg>test: Start filling out the card processor tests<commit_after>import test from 'ava';
import {upgrade, pilot, condition, _processCard, _slackifyText} from '../lib/cardProcessor';

test('slackify - simple string', async t => {
	const testText = 'You may treat all 1- and 2-speed maneuvers as green maneuvers.';
	t.is(testText, _slackifyText(testText));
});

test('slackify - with strong', async t => {
	t.is(
		'*Attack:* Attack 1 ship Then cancel *all* dice results.',
		_slackifyText('Attack: Attack 1 ship Then cancel all dice results.')
	);
});

test('slackify - with em', async t => {
	t.is(
		'the _Nastah Pup_ Pilot',
		_slackifyText('the Nastah Pup Pilot')
	);
});

test('slackify - with b', async t => {
	t.is(
		'*Attack:* Attack 1 ship. If this attack hits',
		_slackifyText('Attack: Attack 1 ship. If this attack hits')
	);
});

test('slackify - with i', async t => {
	t.is(
		'While you are docked, the _Ghost_ can perform [Turret].',
		_slackifyText('While you are docked, the Ghost can perform [Turret].')
	);
});

test('slackify - with linebreak', async t => {
	t.is(
		'Roll 1 defense die.\nOn a [Evade] or [Focus]',
		_slackifyText('Roll 1 defense die.

On a [Evade] or [Focus]')
	);
});

test('slackify - complex string', async t => {
	t.is(
		'After you are *destroyed*, before you are [removed] from the \'play area\', you may *deploy* the _Nastah Pup_ Pilot.\nIt _cannot_ attack this round.',
		_slackifyText('After you are destroyed, before you are [removed] from the \'play area\', you may deploy the Nastah Pup Pilot.

It cannot attack this round.')
	);
});

test('process - basic', async t => {
	const testData = {
		name:   'R2 Astromech',
		id:     2,
		slot:   'Astromech',
		points: 1,
		text:   'You may treat all 1- and 2-speed maneuvers
 as green maneuvers.',
		image:  'upgrades/Astromech/r2-astromech.png',
		xws:    'r2astromech'
	};

	t.deepEqual(
		{
			name:   'R2 Astromech',
			key:    'r2 astromech',
			id:     2,
			slot:   'Astromech',
			points: 1,
			text:   'You may treat *all* 1- and 2-speed maneuvers\n as green maneuvers.',
			image:  'http://upgrades/Astromech/r2-astromech.png',
			xws:    'r2astromech'
		},
		_processCard(testData, 'http://')
	);
});

test('process - no text', async t => {
	const testData = {
		name:   'R2 Astromech',
		id:     2,
		slot:   'Astromech',
		points: 1,
		image:  'upgrades/Astromech/r2-astromech.png',
		xws:    'r2astromech'
	};

	t.is('', _processCard(testData, '')['text']);
});

test('process - no text', async t => {
	const testData = {
		name:   'Seismic Charges',
		id:     24,
		slot:   'Bomb',
		points: 2,
		text:   'some text',
		effect: 'other text',
		image:  'upgrades/Bomb/seismic-charges.png',
		xws:    'seismiccharges'
	};

	t.is('other *text*', _processCard(testData, '')['effect']);
});

test('upgrade - basic', async t => {
	const testData = {
		name:   'Crack Shot',
		id:     141,
		slot:   'Elite',
		points: 1,
		text:   'When attacking a ship inside your firing arc',
		image:  'upgrades/Elite/crack-shot.png',
		xws:    'crackshot'
	};

	t.deepEqual(
		{
			name:   'Crack Shot',
			key:    'crack shot',
			id:     141,
			slot:   'elite',
			points: 1,
			text:   'When attacking a ship inside your firing arc',
			image:  'http://upgrades/Elite/crack-shot.png',
			xws:    'crackshot',
		},
		upgrade(testData, 'http://')
	);
});
test.todo('upgrade - has ship');
test.todo('upgrade - has conditions');
test.todo('upgrade - has size');
test.todo('upgrade - has grants');

test.todo('pilot - basic');
test.todo('pilot - has slots');
test.todo('pilot - has conditions');
test.todo('pilot - has ship_override');

test('condition - basic', async t => {
	const testData = {
		image:  'conditions/a-debt-to-pay.png',
		text:   `When attacking a ship that has the \"A Score to Settle\" Upgrade card, you may change 1 [Focus] result to a [Critical Hit] result.`,
		text:   `When attacking a ship that has the \'A Score to Settle\' Upgrade card, you may change 1 [Focus] result to a [Critical Hit] result.`,
		name:   'A Debt to Pay',
		xws:    'adebttopay',
		unique: true,
		id:     3
	};

	t.is('condition', condition(testData, '')['slot']);
});
<|endoftext|>


<commit_before>/**
 * Gulp script build tasks.
 */

'use strict';

var path = require('path'),
    gulp = require('gulp-help')(require('gulp')),
    conf = require('./conf'),
    source = require('vinyl-source-stream'),
<% if (bower) { -%>
    tsify = require('tsify'),
    browserify = require('browserify'),
<% } -%>
    runSequence = require('run-sequence'),
    tsConf = require('./../tsconfig.json').compilerOptions,
    $ = require('gulp-load-plugins')();

/* Initialize TS Project */
var tsProject = $.typescript.createProject(conf.paths.tsconfig_json);

/* Concat all source, test and typings TS files  */
var tsFiles = [].concat(conf.paths.src + '/' + conf.path_pattern.ts, conf.paths.test + '/' + conf.path_pattern.ts, conf.paths.typings.browser);

<% if (bower) { -%>
/**
 * Gulp minify js task.
 * Streamify and uglify, the output of the gulp bower task.
 * Rename uglified js file.
 * Notify created file size with the name.
 * Report errors.
 */
gulp.task('build-bower',['bower'], function () {
  return gulp.src(conf.paths.bower + conf.paths.bundle)
    .pipe($.streamify($.uglify()))
    .pipe($.streamify($.rename(conf.files.BOWER_MIN_JS)))
    .pipe(gulp.dest(conf.paths.bower))
    .pipe($.notify({
      "message": conf.files.BOWER_MIN_JS + " file size ",
      "onLast": true
    }))
    .pipe($.size())
    .on('error', conf.errorHandler(conf.errors.title.TYPESCRIPT));
});
<% } -%>

<% if (bower) { -%>
/**
 * Gulp bower task.
 * Clean bower directory.
 * Browserify will generate single .js file with the source map support(debug:true option will add sourceMap support).
 * Tsify to compile the sources.
 * Notify created file size with the name.
 * Report errors.
 */
gulp.task('bower', ['clean-bower'], function () {
  var bundler = browserify({
    basedir: './',
    debug: true
  })
    .add([].concat(conf.paths.src + conf.paths.main, conf.paths.typings.browser))
    .plugin(tsify);

  return bundler.bundle()
    .pipe(source(conf.files.BOWER_JS))
    .pipe(gulp.dest(conf.paths.bower))
    .pipe($.notify({
      "message": conf.files.BOWER_JS + " file size ",
      "onLast": true
    }))
    .pipe($.size())
    .on('error', conf.errorHandler(conf.errors.title.TYPESCRIPT));
});
<% } -%>

/**
 * Gulp npm task.
 * Clean lib directory.
 * Typescript compiler will generate all .js files and d.ts references for the source files.
 * Report errors.
 */
gulp.task('npm',['clean-lib'], function () {
  return gulp.src([].concat(conf.paths.src + '/' + conf.path_pattern.ts, conf.paths.typings.browser))
    .pipe($.tsc(tsConf))
    .pipe(gulp.dest(conf.paths.lib))
    .on('error', conf.errorHandler(conf.errors.title.TYPESCRIPT));
});

/**
 * Gulp temporary scripts generation task for coverage.
 * Typescript compiler will generate all .js files and maps references for the source files.
 * Report errors.
 */
gulp.task('tmp-scripts', function() {
  var res = gulp.src(tsFiles, {
      base: '.'
    })
    .pipe($.sourcemaps.init())
    .pipe($.typescript(tsProject))
    .on('error', conf.errorHandler(conf.errors.title.TYPESCRIPT));
  return res.js
    .pipe($.sourcemaps.write('.', {
      // Return relative source map root directories per file.
      includeContent: false,
      sourceRoot: function (file) {
        var sourceFile = path.join(file.cwd, file.sourceMap.file);
        return path.relative(path.dirname(sourceFile), file.cwd);
      }
    }))
    .pipe(gulp.dest('.'));
});

/**
 * Gulp watch temporary scripts task for error checking.
 * Typescript compiler will generate all .js files and maps references for the source files.
 * Report errors.
 */
gulp.task('tmp-watch-scripts',['clean-js-tmp'], function() {
  var res = gulp.src(tsFiles, {
      base: '.'
    })
    .pipe($.typescript(tsProject))
    .on('error', conf.errorHandler(conf.errors.title.TYPESCRIPT));
  return res.js
    .pipe(gulp.dest(conf.paths.jsTmp));
});

/**
 * Gulp nsp scripts task.
 * Run node Security check.
 * @param done - done callback function.
 */
gulp.task('nsp', function (done) {
  $.nsp({
    package: path.resolve('package.json')
  }, done);
});

<% if (styles) { -%>
/**
 * Gulp build css task.
 * Clean css temporary directory and css files in distribution directories -> run compile sccs and generate minified file -> copy css files.
 * @param done - done callback function.
 */
gulp.task('build-css',function(done) {
  runSequence(['clean-css-tmp','clean-css'],<% if (scss) { -%> 'compile-scss-min'<% } else { -%> 'min-css'<% } -%>, 'copy-css',done);
});
<% } -%>

/**
 * Gulp build scripts task.
 * Clean build -> show tslint errors and update tsconfig.json in parallel -> run npm and bower in parallel -> minify js -> inject bower js build to html ->
 * build css -> inject minified css build to html
 * @param done - done callback function.
 */
gulp.task('build-scripts',function(done) {
  runSequence('nsp','clean-build',['tslint', 'tsconfig-update'],<% if (bower) { -%> ['npm', 'build-bower'], 'inject-js'<% } else { -%> 'npm'<% } -%>,<% if (styles) { -%> 'build-css', 'inject-css',<% } -%> done);
});
<commit_msg>Updated scripts.js in gulp generator folder.<commit_after>/**
 * Gulp script build tasks.
 */

'use strict';

var path = require('path'),
    gulp = require('gulp-help')(require('gulp')),
    conf = require('./conf'),
    source = require('vinyl-source-stream'),
<% if (bower) { -%>
    tsify = require('tsify'),
    browserify = require('browserify'),
<% } -%>
    runSequence = require('run-sequence'),
    tsConf = require('./../tsconfig.json').compilerOptions,
    $ = require('gulp-load-plugins')();

/* Initialize TS Project */
var tsProject = $.typescript.createProject(conf.paths.tsconfig_json);

/* Concat all source, test and typings TS files  */
var tsFiles = [].concat(path.join(conf.paths.src, conf.path_pattern.ts), path.join(conf.paths.test, conf.path_pattern.ts), conf.paths.typings.browser);

<% if (bower) { -%>
/**
 * Gulp minify js task.
 * Streamify and uglify, the output of the gulp bower task.
 * Rename uglified js file.
 * Notify created file size with the name.
 * Report errors.
 */
gulp.task('build-bower',['bower'], function () {
  return gulp.src(conf.paths.bower + conf.paths.bundle)
    .pipe($.streamify($.uglify()))
    .pipe($.streamify($.rename(conf.files.BOWER_MIN_JS)))
    .pipe(gulp.dest(conf.paths.bower))
    .pipe($.notify({
      "message": conf.files.BOWER_MIN_JS + " file size ",
      "onLast": true
    }))
    .pipe($.size())
    .on('error', conf.errorHandler(conf.errors.title.TYPESCRIPT));
});
<% } -%>

<% if (bower) { -%>
/**
 * Gulp bower task.
 * Clean bower directory.
 * Browserify will generate single .js file with the source map support(debug:true option will add sourceMap support).
 * Tsify to compile the sources.
 * Notify created file size with the name.
 * Report errors.
 */
gulp.task('bower', ['clean-bower'], function () {
  var bundler = browserify({
    basedir: './',
    debug: true
  })
    .add([].concat(conf.paths.src + conf.paths.main, conf.paths.typings.browser))
    .plugin(tsify);

  return bundler.bundle()
    .pipe(source(conf.files.BOWER_JS))
    .pipe(gulp.dest(conf.paths.bower))
    .pipe($.notify({
      "message": conf.files.BOWER_JS + " file size ",
      "onLast": true
    }))
    .pipe($.size())
    .on('error', conf.errorHandler(conf.errors.title.TYPESCRIPT));
});
<% } -%>

/**
 * Gulp npm task.
 * Clean lib directory.
 * Typescript compiler will generate all .js files and d.ts references for the source files.
 * Report errors.
 */
gulp.task('npm',['clean-lib'], function () {
  return gulp.src([].concat(path.join(conf.paths.src, conf.path_pattern.ts), conf.paths.typings.browser))
    .pipe($.tsc(tsConf))
    .pipe(gulp.dest(conf.paths.lib))
    .on('error', conf.errorHandler(conf.errors.title.TYPESCRIPT));
});

/**
 * Gulp temporary scripts generation task for coverage.
 * Typescript compiler will generate all .js files and maps references for the source files.
 * Report errors.
 */
gulp.task('tmp-scripts', function() {
  var res = gulp.src(tsFiles, {
      base: '.'
    })
    .pipe($.sourcemaps.init())
    .pipe($.typescript(tsProject))
    .on('error', conf.errorHandler(conf.errors.title.TYPESCRIPT));
  return res.js
    .pipe($.sourcemaps.write('.', {
      // Return relative source map root directories per file.
      includeContent: false,
      sourceRoot: function (file) {
        var sourceFile = path.join(file.cwd, file.sourceMap.file);
        return path.relative(path.dirname(sourceFile), file.cwd);
      }
    }))
    .pipe(gulp.dest('.'));
});

/**
 * Gulp watch temporary scripts task for error checking.
 * Typescript compiler will generate all .js files and maps references for the source files.
 * Report errors.
 */
gulp.task('tmp-watch-scripts',['clean-js-tmp'], function() {
  var res = gulp.src(tsFiles, {
      base: '.'
    })
    .pipe($.typescript(tsProject))
    .on('error', conf.errorHandler(conf.errors.title.TYPESCRIPT));
  return res.js
    .pipe(gulp.dest(conf.paths.jsTmp));
});

/**
 * Gulp nsp scripts task.
 * Run node Security check.
 * @param done - done callback function.
 */
gulp.task('nsp', function (done) {
  $.nsp({
    package: path.resolve('package.json')
  }, done);
});

<% if (styles) { -%>
/**
 * Gulp build css task.
 * Clean css temporary directory and css files in distribution directories -> run compile sccs and generate minified file -> copy css files.
 * @param done - done callback function.
 */
gulp.task('build-css',function(done) {
  runSequence(['clean-css-tmp','clean-css'],<% if (scss) { -%> 'compile-scss-min'<% } else { -%> 'min-css'<% } -%>, 'copy-css',done);
});
<% } -%>

/**
 * Gulp build scripts task.
 * Clean build -> show tslint errors and update tsconfig.json in parallel -> run npm and bower in parallel -> minify js -> inject bower js build to html ->
 * build css -> inject minified css build to html
 * @param done - done callback function.
 */
gulp.task('build-scripts',function(done) {
  runSequence('nsp','clean-build',['tslint', 'tsconfig-update'],<% if (bower) { -%> ['npm', 'build-bower'], 'inject-js'<% } else { -%> 'npm'<% } -%>,<% if (styles) { -%> 'build-css', 'inject-css',<% } -%> done);
});
<|endoftext|>


<commit_before>const clipboardy = require('clipboardy');

function waitForOncoprint(timeout) {
    browser.pause(100); // give oncoprint time to disappear
    browser.waitUntil(()=>{
        return !browser.isExisting(".oncoprintLoadingIndicator") // wait for loading indicator to hide, and
            && browser.isExisting('#oncoprintDiv svg rect')// as a proxy for oncoprint being rendered, wait for an svg rectangle to appear in the legend
            && (browser.getCssProperty(".oncoprintContainer", "opacity").value === 1); // oncoprint has faded in
    }, timeout);
}

function getTextInOncoprintLegend() {
    return browser.getText("#oncoprintDiv .oncoprint-legend-div svg");
}

function setOncoprintMutationsMenuOpen(open) {
    const mutationColorMenuButton = "#mutationColorDropdown";
    const mutationColorMenuDropdown = "div.oncoprint__controls__mutation_color_menu";
    browser.moveToObject("div.oncoprint__controls");
    browser.waitForVisible(mutationColorMenuButton);
    browser.waitUntil(()=>{
        if (open === browser.isVisible(mutationColorMenuDropdown)) {
            return true;
        } else {
            browser.click(mutationColorMenuButton);
            return false;
        }
    }, 10000, `Couldn't ${open ? "open" : "close"} Mutations menu in Oncoprint`, 2000);
}

function goToUrlAndSetLocalStorage(url) {
    if (!useExternalFrontend) {
        browser.url(url);
    } else {
        var urlparam = useLocalDist? 'localdist' : 'localdev';
        var prefix = (url.indexOf("?") > 0)? '&' : '?';
        browser.url(`${url}${prefix}${urlparam}=true`);
    }
    browser.setViewportSize({ height: 1600, width: 1000 });

    // move mouse out of the way
    browser.moveToObject("body", 0, 0);
}

function sessionServiceIsEnabled() {
    return browser.execute(function() {
        return window.frontendConfig.serverConfig.sessionServiceEnabled;
    }).value;
}

function waitForNumberOfStudyCheckboxes(expectedNumber, text) {
    browser.waitUntil(()=>{
        var ret = browser.elements('[data-test="cancerTypeListContainer"] > ul > ul').value.length === expectedNumber;
        if (text && ret) {
            ret = browser.isExisting('[data-test="cancerTypeListContainer"] > ul > ul > ul > li:nth-child(2) > label > span');
            if (ret) {
                ret = (browser.getText('[data-test="cancerTypeListContainer"] > ul > ul > ul > li:nth-child(2) > label > span') === text);
            }
        }
        return ret;
    }, 10000);
}

function getNthOncoprintTrackOptionsElements(n) {
    // n is one-indexed

    const button_selector = "#oncoprintDiv .oncoprintjs__track_options__toggle_btn_img.nth-"+n;
    const dropdown_selector = "#oncoprintDiv .oncoprintjs__track_options__dropdown.nth-"+n;

    return {
        button: $(button_selector),
        button_selector,
        dropdown: $(dropdown_selector),
        dropdown_selector
    };
}

const useExternalFrontend = !process.env.FRONTEND_TEST_DO_NOT_LOAD_EXTERNAL_FRONTEND;

const useLocalDist = process.env.FRONTEND_TEST_USE_LOCAL_DIST;

function waitForNetworkQuiet(timeout){
    browser.waitUntil(()=>{
        return browser.execute(function(){
            return window.ajaxQuiet === true;
        }).value == true
    }, timeout);
}

function toStudyViewSummaryTab() {
    var summaryTab = "#studyViewTabs a.tabAnchor_summary";
    var summaryContent = "[data-test='summary-tab-content']";
    if (!browser.isVisible(summaryContent)) {
        browser.waitForVisible(summaryTab, 10000);
        browser.click(summaryTab);
        browser.waitForVisible(summaryContent, 10000);
    }
}

function toStudyViewClinicalDataTab() {
    var clinicalDataTab = "#studyViewTabs a.tabAnchor_clinicalData";
    var clinicalDataContent = "[data-test='clinical-data-tab-content']";
    if (!browser.isVisible(clinicalDataContent)) {
        browser.waitForVisible(clinicalDataTab, 10000);
        browser.click(clinicalDataTab);
        browser.waitForVisible(clinicalDataContent, 10000);
    }
}

function waitForStudyViewSelectedInfo() {
    browser.waitForVisible("[data-test='selected-info']", 5000);
    // pause to wait the animation finished
    browser.pause(2000);
}

function getTextFromElement(element) {
    return browser.element(element).getText().trim();
}

function getNumberOfStudyViewCharts() {
    return browser.elements('div.react-grid-item').value.length;
}

function setInputText(selector, text){
    browser.setValue(selector, '\uE003'.repeat(browser.getValue(selector).length) + text);
}

function pasteToElement(elementSelector, text){

    clipboardy.writeSync(text);
    browser.setValue(elementSelector, ["Shift","Insert"]);

}

module.exports = {
    waitForOncoprint: waitForOncoprint,
    goToUrlAndSetLocalStorage: goToUrlAndSetLocalStorage,
    useExternalFrontend: useExternalFrontend,
    sessionServiceIsEnabled: sessionServiceIsEnabled,
    waitForNumberOfStudyCheckboxes: waitForNumberOfStudyCheckboxes,
    waitForNetworkQuiet:waitForNetworkQuiet,
    getTextInOncoprintLegend: getTextInOncoprintLegend,
    toStudyViewSummaryTab: toStudyViewSummaryTab,
    toStudyViewClinicalDataTab: toStudyViewClinicalDataTab,
    waitForStudyViewSelectedInfo: waitForStudyViewSelectedInfo,
    getTextFromElement: getTextFromElement,
    getNumberOfStudyViewCharts: getNumberOfStudyViewCharts,
    setOncoprintMutationsMenuOpen: setOncoprintMutationsMenuOpen,
    getNthOncoprintTrackOptionsElements: getNthOncoprintTrackOptionsElements,
    setInputText: setInputText,
    pasteToElement: pasteToElement
};

<commit_msg>e2e incr timeout studyCheckBoxes<commit_after>const clipboardy = require('clipboardy');

function waitForOncoprint(timeout) {
    browser.pause(100); // give oncoprint time to disappear
    browser.waitUntil(()=>{
        return !browser.isExisting(".oncoprintLoadingIndicator") // wait for loading indicator to hide, and
            && browser.isExisting('#oncoprintDiv svg rect')// as a proxy for oncoprint being rendered, wait for an svg rectangle to appear in the legend
            && (browser.getCssProperty(".oncoprintContainer", "opacity").value === 1); // oncoprint has faded in
    }, timeout);
}

function getTextInOncoprintLegend() {
    return browser.getText("#oncoprintDiv .oncoprint-legend-div svg");
}

function setOncoprintMutationsMenuOpen(open) {
    const mutationColorMenuButton = "#mutationColorDropdown";
    const mutationColorMenuDropdown = "div.oncoprint__controls__mutation_color_menu";
    browser.moveToObject("div.oncoprint__controls");
    browser.waitForVisible(mutationColorMenuButton);
    browser.waitUntil(()=>{
        if (open === browser.isVisible(mutationColorMenuDropdown)) {
            return true;
        } else {
            browser.click(mutationColorMenuButton);
            return false;
        }
    }, 10000, `Couldn't ${open ? "open" : "close"} Mutations menu in Oncoprint`, 2000);
}

function goToUrlAndSetLocalStorage(url) {
    if (!useExternalFrontend) {
        browser.url(url);
    } else {
        var urlparam = useLocalDist? 'localdist' : 'localdev';
        var prefix = (url.indexOf("?") > 0)? '&' : '?';
        browser.url(`${url}${prefix}${urlparam}=true`);
    }
    browser.setViewportSize({ height: 1600, width: 1000 });

    // move mouse out of the way
    browser.moveToObject("body", 0, 0);
}

function sessionServiceIsEnabled() {
    return browser.execute(function() {
        return window.frontendConfig.serverConfig.sessionServiceEnabled;
    }).value;
}

function waitForNumberOfStudyCheckboxes(expectedNumber, text) {
    browser.waitUntil(()=>{
        var ret = browser.elements('[data-test="cancerTypeListContainer"] > ul > ul').value.length === expectedNumber;
        if (text && ret) {
            ret = browser.isExisting('[data-test="cancerTypeListContainer"] > ul > ul > ul > li:nth-child(2) > label > span');
            if (ret) {
                ret = (browser.getText('[data-test="cancerTypeListContainer"] > ul > ul > ul > li:nth-child(2) > label > span') === text);
            }
        }
        return ret;
    }, 30000);
}

function getNthOncoprintTrackOptionsElements(n) {
    // n is one-indexed

    const button_selector = "#oncoprintDiv .oncoprintjs__track_options__toggle_btn_img.nth-"+n;
    const dropdown_selector = "#oncoprintDiv .oncoprintjs__track_options__dropdown.nth-"+n;

    return {
        button: $(button_selector),
        button_selector,
        dropdown: $(dropdown_selector),
        dropdown_selector
    };
}

const useExternalFrontend = !process.env.FRONTEND_TEST_DO_NOT_LOAD_EXTERNAL_FRONTEND;

const useLocalDist = process.env.FRONTEND_TEST_USE_LOCAL_DIST;

function waitForNetworkQuiet(timeout){
    browser.waitUntil(()=>{
        return browser.execute(function(){
            return window.ajaxQuiet === true;
        }).value == true
    }, timeout);
}

function toStudyViewSummaryTab() {
    var summaryTab = "#studyViewTabs a.tabAnchor_summary";
    var summaryContent = "[data-test='summary-tab-content']";
    if (!browser.isVisible(summaryContent)) {
        browser.waitForVisible(summaryTab, 10000);
        browser.click(summaryTab);
        browser.waitForVisible(summaryContent, 10000);
    }
}

function toStudyViewClinicalDataTab() {
    var clinicalDataTab = "#studyViewTabs a.tabAnchor_clinicalData";
    var clinicalDataContent = "[data-test='clinical-data-tab-content']";
    if (!browser.isVisible(clinicalDataContent)) {
        browser.waitForVisible(clinicalDataTab, 10000);
        browser.click(clinicalDataTab);
        browser.waitForVisible(clinicalDataContent, 10000);
    }
}

function waitForStudyViewSelectedInfo() {
    browser.waitForVisible("[data-test='selected-info']", 5000);
    // pause to wait the animation finished
    browser.pause(2000);
}

function getTextFromElement(element) {
    return browser.element(element).getText().trim();
}

function getNumberOfStudyViewCharts() {
    return browser.elements('div.react-grid-item').value.length;
}

function setInputText(selector, text){
    browser.setValue(selector, '\uE003'.repeat(browser.getValue(selector).length) + text);
}

function pasteToElement(elementSelector, text){

    clipboardy.writeSync(text);
    browser.setValue(elementSelector, ["Shift","Insert"]);

}

module.exports = {
    waitForOncoprint: waitForOncoprint,
    goToUrlAndSetLocalStorage: goToUrlAndSetLocalStorage,
    useExternalFrontend: useExternalFrontend,
    sessionServiceIsEnabled: sessionServiceIsEnabled,
    waitForNumberOfStudyCheckboxes: waitForNumberOfStudyCheckboxes,
    waitForNetworkQuiet:waitForNetworkQuiet,
    getTextInOncoprintLegend: getTextInOncoprintLegend,
    toStudyViewSummaryTab: toStudyViewSummaryTab,
    toStudyViewClinicalDataTab: toStudyViewClinicalDataTab,
    waitForStudyViewSelectedInfo: waitForStudyViewSelectedInfo,
    getTextFromElement: getTextFromElement,
    getNumberOfStudyViewCharts: getNumberOfStudyViewCharts,
    setOncoprintMutationsMenuOpen: setOncoprintMutationsMenuOpen,
    getNthOncoprintTrackOptionsElements: getNthOncoprintTrackOptionsElements,
    setInputText: setInputText,
    pasteToElement: pasteToElement
};

<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015-2016 Institute of Applied and Experimental Physics (http://www.utef.cvut.cz/)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.*
 */

state = {
	histogram: null,
	binbars: null,
	binsize: 1,
	svg: null,
	cont: null,
	hist: null,
	zoom: null,
	xscale: null,
	yscale: null,
	since: 0,
	finished: null,
	threshold: 10,
	ws: null,
	cfgpropwid: {},
	autosave: null
}

function clamp(v, mi, mx) {
	if (v < mi)
		return mi;
	else if (v > mx)
		return mx;
	return v;
}

function endTime() {
	return state.finished ? state.finished : (new Date()).getTime() / 1000;
}

SIPrefixes = ["", "k", "M", "G", "T"]

function SIFormat(x) {
	x = Math.round(x);
	var i;
	for (i = 0; i < SIPrefixes.length && x > 1000; i++)
		x /= 1000;

	ret = i == 0 ? x : x.toFixed(1) + SIPrefixes[i];
	while (ret[0] == "0")
		ret = ret.substring(1);

	return ret + " ";
}

function getBarWidth() {
	return ($(state.svg[0][0]).width() / state.histogram.length * state.binsize);
}

function getBarHeight() {
	var max = d3.max(state.histogram);
	if (max == 0)
		return 10;
	return $(state.svg[0][0]).height() / max;
}

function lpad(s, t, p) {
	s = String(s);
	while (s.length < t)
		s = p + s;
	return s;
}

function updateTimer() {
	var diff = Math.round(endTime() - state.since);
	diff = Math.max(0, diff); // Should not happen, but just in case...
	var secs = diff % 60;
	var mins = Math.floor(diff / 60) % 60;
	var hrs = Math.floor(diff / 3600);
	$("#timer").text(lpad(hrs, 2, "0") + ":" + lpad(mins, 2, "0") + ":" + lpad(secs, 2, "0"));
}

function update() {
	var barw = getBarWidth();

	var binned = new Array(state.histogram.length / state.binsize).fill(0);

	for (var i = 0; i < state.histogram.length; i++) {
		if (i > state.threshold)
			binned[Math.floor(i / state.binsize)] += state.histogram[i];
	}

	var cpm = binned.reduce(function(i, v) { return i + v; }) /
		(endTime() - state.since) * 60;
	$("#cpm").text(cpm.toFixed(2) + " CPM")

	var maxh = d3.max(binned);
	var perh = maxh == 0 ? 10 : $(state.svg[0][0]).height() / maxh;

	var tickcount = 10;
	var tstep = d3.max(binned) / tickcount;
	var tvals = [0];
	for (var i = 1; i < tickcount; i++)
		tvals.push(tvals[tvals.length - 1] + tstep);

	state.xscale.range([0, barw * binned.length]);

	state.yscale.domain([d3.max(binned), 0])
				.range([0, $(state.svg[0][0]).height()]);
	var yaxis = d3.svg.axis()
					.tickValues(tvals)
					.scale(state.yscale)
					.orient("left")
					.tickFormat(SIFormat)
					.tickSize(-($(state.svg[0][0]).width() - 20))
					.outerTickSize(0);

	var xaxis = d3.svg.axis()
				.scale(state.xscale)
				.orient("top")
				.outerTickSize(0);

	state.svg.selectAll(".x.axis")
		.call(xaxis);
	state.svg.selectAll(".y.axis")
		.call(yaxis);

	for (var i = 0; i < binned.length; i++) {
		if (binned[i] > 0) {
			if (!state.binbars[i])
				state.binbars[i] = state.svg.select(".wrap").append("rect").attr("class", "bar");
			var x = state.xscale(i * state.binsize);
			var w = barw * state.zoom.scale() - Math.floor(barw * state.zoom.scale() * 0.15)
			// Beyond the edge, drop the bar entirely
			if (x + w < 0 || y > $(state.svg[0][0]).width()) {
				state.binbars[i].remove();
				state.binbars[i] = null;
				continue;
			}
			// Partially occluded, note that we don't care about occlusion
			// on the right border, as there are no axis we could be covering
			if (x < 0) {
				w = w + x;
				x = 0;
			}
			var y = Math.floor(state.yscale(binned[i]))
			var h = $(state.svg[0][0]).height() - y;
			state.binbars[i].data([{
				x: x,
				w: w,
				y: y,
				h: h,
			}]);
		} else if (state.binbars[i]) {
			state.binbars[i].remove();
			state.binbars[i] = null;
		}
	}

	var bs = state.svg.selectAll(".bar");

	bs.attr("width", function(d) { return d.w; })
		.attr("x", function(d) { return d.x; });

	bs.attr("height", function(d) { return d.h + "px"; })
		.attr("y", function(d) { return d.y; });

	d3.select(".pane")
		.attr("width", $(state.svg[0][0]).width() + "px")
		.attr("height", $(state.svg[0][0]).height() + "px");
}

function commandSender(cmd) {
	return function () { state.ws.send(JSON.stringify({"command": cmd}))}
}

function download(fname, data) {
	var el = document.createElement("a");
	el.setAttribute("download", fname)
	el.setAttribute("href", "data:text/html:charset=utf-8," +
								encodeURIComponent(data));
	el.style.display = "none";
	document.body.appendChild(el);
	el.click();
	document.body.removeChild(el);
}

function downloadTXT() {
	var startlabel = (new Date(state.since * 1000)).toISOString()
	var data = "-_-\n";
	data += "from: " + startlabel + "\n";
	data += "to: " + (new Date(endTime() * 1000)).toISOString() + "\n";
	data += "---\n";
	for (var i = 0; i < state.histogram.length; i++) {
		data += state.histogram[i] + "\n";
	}
	download("data-" + startlabel.substring(0, 19) + ".txt", data);
}

function init() {
	state.svg = d3.select("body").append("svg");

	state.xscale = d3.scale.linear()
			.domain([0, state.histogram.length])
			.range([0, getBarWidth() * state.histogram.length]);
	state.yscale = d3.scale.linear();

	state.zoom = d3.behavior.zoom().x(state.xscale).center(null)
				.scaleExtent([1, 100]).on("zoom", function() {
		var tr = state.zoom.translate();
		var tx = tr[0];

		tx = clamp(tx,
				getBarWidth() * state.histogram.length / state.binsize -
				$(state.svg[0][0]).width() * state.zoom.scale(), 0);

		state.zoom.translate([tx, tr[1]]);

		update();
	});

	state.svg.append("g").attr("class", "y axis");
	state.svg.append("g").attr("class", "x axis");

	state.cont = state.svg.append("g").attr("class", "wrap");
	state.hist = state.cont.selectAll(".bar");

	// Needs to be on the end (top)
	state.svg.append("rect")
		.attr("class", "pane")
		.call(state.zoom);

	state.binsize = $("#binsize").val();
	$("#binsize").change(function() {
		state.binsize = $("#binsize").val();

		state.svg.selectAll(".bar").remove();

		state.binbars = new Array(state.histogram.length / state.binsize).fill(null);
		update();
	}).trigger("change");
	$("#threshold").change(function() {
		state.threshold = $(this).val();
		update();
	}).trigger("change");
	$("#csv").click(downloadTXT);
	$("#autosave").change(function(ev) {
		clearInterval(state.autosave);
		var v = parseInt($(ev.target).val());
		if (isNaN(v)) {
			state.autosave = null;
		} else {
			setInterval(function() {
				downloadTXT();
				commandSender("clear")();
			}, v * 1000);
		}
	});
}

function initRemote(data) {
	data["configprops"].forEach(function (c, i) {
		var id = "config-" + c.id;
		$("" + c.name + "").appendTo("#control");
		if (c.from == 0 && c.to == 1) {
			var ctl = $("<input type=\"checkbox\" id=\"" + id + "\"></input>").appendTo("#control");
		} else {
			var ctl = $("<input type=\"number\" id=\"" + id + "\" min=" + c.from + " max=" + c.to + ">")
				.appendTo("#control");
		}
		ctl.change(function() {
			state.ws.send(JSON.stringify({"command": "set", "id": c.id,
											"value": this.value == "on" ?
														(this.checked ? 1 : 0) :
														parseInt(this.value)}));
		})
		state.cfgpropwid[c.id] = ctl;
	});
	$("#download").click(function () {
		var stored =
			$("

")
				.appendTo($("body"))
				.css("display", "none")
				.html(JSON.stringify(
							{hist: state.histogram,
							 since: state.since,
							 finished: (new Date()).getTime / 1000}));

		// Save the current binsize
		$("#binsize option:selected").attr("selected", true);

		download("spectrum.html", document.documentElement.innerHTML);

		stored.remove();
	})
	$("#clear").click(commandSender("clear"));

	setInterval(update, 100);
	setInterval(updateTimer, 1000);
	$("#clear").attr("disabled", null);

	state.ws = new WebSocket("ws://" + location.hostname + ":" + location.port + "/ws");
	state.ws.onopen = function() {
		state.ws.send(JSON.stringify({"csrf": data["csrf"]}));
	}
	state.ws.onmessage = function(msg) {
		var d = $.parseJSON(msg.data);
		if (d.v) {
			state.histogram[d.v] += 1;
		} else if (d.c) {
			console.log("Config received!");
		} else if (d.h) {
			console.log("History received!");
			state.histogram = d.h;
			state.since = d.since;
		} else if (d.props) {
			console.log("Configuration properties received!");
			for (var k in d.props) {
				var el = state.cfgpropwid[k];
				if (el.prop("type") == "checkbox") {
					el.prop("checked", !!d.props[k]);
				} else {
					el.val(d.props[k]);
				}
			}
		} else {
			console.log("WTF? " + d);
		}
	}
}

$(document).ready(function() {
	var stored = $("#stored");
	if (stored.length) {
		$("svg").remove();
		var sdata = JSON.parse(stored.html());
		state.histogram = sdata.hist;
		state.since = sdata.since;
		state.finished = sdata.finished;
		init();
		$(["#clear"]).each(function(i, v) {
			$(v).prop("disabled", true);
		});
		update();
	} else {
		$.get("metadata.json", function(data) {
			state.histogram = new Array(data["channels"]).fill(1);
			init();
			initRemote(data);
		})
	}
})
<commit_msg>py: ieapspect-web: Use window.requestAnimationFrame<commit_after>/*
 * Copyright (C) 2015-2016 Institute of Applied and Experimental Physics (http://www.utef.cvut.cz/)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.*
 */

state = {
	histogram: null,
	binbars: null,
	binsize: 1,
	svg: null,
	cont: null,
	hist: null,
	zoom: null,
	xscale: null,
	yscale: null,
	since: 0,
	finished: null,
	threshold: 10,
	ws: null,
	cfgpropwid: {},
	autosave: null,
	lastFrame: 0,
}

function clamp(v, mi, mx) {
	if (v < mi)
		return mi;
	else if (v > mx)
		return mx;
	return v;
}

function endTime() {
	return state.finished ? state.finished : (new Date()).getTime() / 1000;
}

SIPrefixes = ["", "k", "M", "G", "T"]

function SIFormat(x) {
	x = Math.round(x);
	var i;
	for (i = 0; i < SIPrefixes.length && x > 1000; i++)
		x /= 1000;

	ret = i == 0 ? x : x.toFixed(1) + SIPrefixes[i];
	while (ret[0] == "0")
		ret = ret.substring(1);

	return ret + " ";
}

function getBarWidth() {
	return ($(state.svg[0][0]).width() / state.histogram.length * state.binsize);
}

function getBarHeight() {
	var max = d3.max(state.histogram);
	if (max == 0)
		return 10;
	return $(state.svg[0][0]).height() / max;
}

function lpad(s, t, p) {
	s = String(s);
	while (s.length < t)
		s = p + s;
	return s;
}

function updateTimer() {
	var diff = Math.round(endTime() - state.since);
	diff = Math.max(0, diff); // Should not happen, but just in case...
	var secs = diff % 60;
	var mins = Math.floor(diff / 60) % 60;
	var hrs = Math.floor(diff / 3600);
	$("#timer").text(lpad(hrs, 2, "0") + ":" + lpad(mins, 2, "0") + ":" + lpad(secs, 2, "0"));
}

function update() {
	var barw = getBarWidth();

	var binned = new Array(state.histogram.length / state.binsize).fill(0);

	for (var i = 0; i < state.histogram.length; i++) {
		if (i > state.threshold)
			binned[Math.floor(i / state.binsize)] += state.histogram[i];
	}

	var cpm = binned.reduce(function(i, v) { return i + v; }) /
		(endTime() - state.since) * 60;
	$("#cpm").text(cpm.toFixed(2) + " CPM")

	var maxh = d3.max(binned);
	var perh = maxh == 0 ? 10 : $(state.svg[0][0]).height() / maxh;

	var tickcount = 10;
	var tstep = d3.max(binned) / tickcount;
	var tvals = [0];
	for (var i = 1; i < tickcount; i++)
		tvals.push(tvals[tvals.length - 1] + tstep);

	state.xscale.range([0, barw * binned.length]);

	state.yscale.domain([d3.max(binned), 0])
				.range([0, $(state.svg[0][0]).height()]);
	var yaxis = d3.svg.axis()
					.tickValues(tvals)
					.scale(state.yscale)
					.orient("left")
					.tickFormat(SIFormat)
					.tickSize(-($(state.svg[0][0]).width() - 20))
					.outerTickSize(0);

	var xaxis = d3.svg.axis()
				.scale(state.xscale)
				.orient("top")
				.outerTickSize(0);

	state.svg.selectAll(".x.axis")
		.call(xaxis);
	state.svg.selectAll(".y.axis")
		.call(yaxis);

	for (var i = 0; i < binned.length; i++) {
		if (binned[i] > 0) {
			if (!state.binbars[i])
				state.binbars[i] = state.svg.select(".wrap").append("rect").attr("class", "bar");
			var x = state.xscale(i * state.binsize);
			var w = barw * state.zoom.scale() - Math.floor(barw * state.zoom.scale() * 0.15)
			// Beyond the edge, drop the bar entirely
			if (x + w < 0 || y > $(state.svg[0][0]).width()) {
				state.binbars[i].remove();
				state.binbars[i] = null;
				continue;
			}
			// Partially occluded, note that we don't care about occlusion
			// on the right border, as there are no axis we could be covering
			if (x < 0) {
				w = w + x;
				x = 0;
			}
			var y = Math.floor(state.yscale(binned[i]))
			var h = $(state.svg[0][0]).height() - y;
			state.binbars[i].data([{
				x: x,
				w: w,
				y: y,
				h: h,
			}]);
		} else if (state.binbars[i]) {
			state.binbars[i].remove();
			state.binbars[i] = null;
		}
	}

	var bs = state.svg.selectAll(".bar");

	bs.attr("width", function(d) { return d.w; })
		.attr("x", function(d) { return d.x; });

	bs.attr("height", function(d) { return d.h + "px"; })
		.attr("y", function(d) { return d.y; });

	d3.select(".pane")
		.attr("width", $(state.svg[0][0]).width() + "px")
		.attr("height", $(state.svg[0][0]).height() + "px");
}

function updateLoop(timestamp) {
	// See https://remysharp.com/2015/07/13/optimising-a-canvas-animation for
	// reference
	window.requestAnimationFrame(updateLoop);

	var delta = timestamp - state.lastFrame;
	if (delta < 50) {
		return;
	}
	state.lastFrame = timestamp;

	update();
}

function commandSender(cmd) {
	return function () { state.ws.send(JSON.stringify({"command": cmd}))}
}

function download(fname, data) {
	var el = document.createElement("a");
	el.setAttribute("download", fname)
	el.setAttribute("href", "data:text/html:charset=utf-8," +
								encodeURIComponent(data));
	el.style.display = "none";
	document.body.appendChild(el);
	el.click();
	document.body.removeChild(el);
}

function downloadTXT() {
	var startlabel = (new Date(state.since * 1000)).toISOString()
	var data = "-_-\n";
	data += "from: " + startlabel + "\n";
	data += "to: " + (new Date(endTime() * 1000)).toISOString() + "\n";
	data += "---\n";
	for (var i = 0; i < state.histogram.length; i++) {
		data += state.histogram[i] + "\n";
	}
	download("data-" + startlabel.substring(0, 19) + ".txt", data);
}

function init() {
	state.svg = d3.select("body").append("svg");

	state.xscale = d3.scale.linear()
			.domain([0, state.histogram.length])
			.range([0, getBarWidth() * state.histogram.length]);
	state.yscale = d3.scale.linear();

	state.zoom = d3.behavior.zoom().x(state.xscale).center(null)
				.scaleExtent([1, 100]).on("zoom", function() {
		var tr = state.zoom.translate();
		var tx = tr[0];

		tx = clamp(tx,
				getBarWidth() * state.histogram.length / state.binsize -
				$(state.svg[0][0]).width() * state.zoom.scale(), 0);

		state.zoom.translate([tx, tr[1]]);

		update();
	});

	state.svg.append("g").attr("class", "y axis");
	state.svg.append("g").attr("class", "x axis");

	state.cont = state.svg.append("g").attr("class", "wrap");
	state.hist = state.cont.selectAll(".bar");

	// Needs to be on the end (top)
	state.svg.append("rect")
		.attr("class", "pane")
		.call(state.zoom);

	state.binsize = $("#binsize").val();
	$("#binsize").change(function() {
		state.binsize = $("#binsize").val();

		state.svg.selectAll(".bar").remove();

		state.binbars = new Array(state.histogram.length / state.binsize).fill(null);
		update();
	}).trigger("change");
	$("#threshold").change(function() {
		state.threshold = $(this).val();
		update();
	}).trigger("change");
	$("#csv").click(downloadTXT);
	$("#autosave").change(function(ev) {
		clearInterval(state.autosave);
		var v = parseInt($(ev.target).val());
		if (isNaN(v)) {
			state.autosave = null;
		} else {
			setInterval(function() {
				downloadTXT();
				commandSender("clear")();
			}, v * 1000);
		}
	});
}

function initRemote(data) {
	data["configprops"].forEach(function (c, i) {
		var id = "config-" + c.id;
		$("" + c.name + "").appendTo("#control");
		if (c.from == 0 && c.to == 1) {
			var ctl = $("<input type=\"checkbox\" id=\"" + id + "\"></input>").appendTo("#control");
		} else {
			var ctl = $("<input type=\"number\" id=\"" + id + "\" min=" + c.from + " max=" + c.to + ">")
				.appendTo("#control");
		}
		ctl.change(function() {
			state.ws.send(JSON.stringify({"command": "set", "id": c.id,
											"value": this.value == "on" ?
														(this.checked ? 1 : 0) :
														parseInt(this.value)}));
		})
		state.cfgpropwid[c.id] = ctl;
	});
	$("#download").click(function () {
		var stored =
			$("

")
				.appendTo($("body"))
				.css("display", "none")
				.html(JSON.stringify(
							{hist: state.histogram,
							 since: state.since,
							 finished: (new Date()).getTime / 1000}));

		// Save the current binsize
		$("#binsize option:selected").attr("selected", true);

		download("spectrum.html", document.documentElement.innerHTML);

		stored.remove();
	})
	$("#clear").click(commandSender("clear"));

	updateLoop();
	setInterval(updateTimer, 1000);
	$("#clear").attr("disabled", null);

	state.ws = new WebSocket("ws://" + location.hostname + ":" + location.port + "/ws");
	state.ws.onopen = function() {
		state.ws.send(JSON.stringify({"csrf": data["csrf"]}));
	}
	state.ws.onmessage = function(msg) {
		var d = $.parseJSON(msg.data);
		if (d.v) {
			state.histogram[d.v] += 1;
		} else if (d.c) {
			console.log("Config received!");
		} else if (d.h) {
			console.log("History received!");
			state.histogram = d.h;
			state.since = d.since;
		} else if (d.props) {
			console.log("Configuration properties received!");
			for (var k in d.props) {
				var el = state.cfgpropwid[k];
				if (el.prop("type") == "checkbox") {
					el.prop("checked", !!d.props[k]);
				} else {
					el.val(d.props[k]);
				}
			}
		} else {
			console.log("WTF? " + d);
		}
	}
}

$(document).ready(function() {
	var stored = $("#stored");
	if (stored.length) {
		$("svg").remove();
		var sdata = JSON.parse(stored.html());
		state.histogram = sdata.hist;
		state.since = sdata.since;
		state.finished = sdata.finished;
		init();
		$(["#clear"]).each(function(i, v) {
			$(v).prop("disabled", true);
		});
		update();
	} else {
		$.get("metadata.json", function(data) {
			state.histogram = new Array(data["channels"]).fill(1);
			init();
			initRemote(data);
		})
	}
})
<|endoftext|>


<commit_before>import { Meteor } from 'meteor/meteor';
import { SimpleSchema } from 'meteor/aldeed:simple-schema';

const Keywords = new Meteor.Collection('keywords');

Keywords.schema = new SimpleSchema({
	_id: {
		type: String,
		optional: true,
	},

	wordpressId: {
		type: Number,
		optional: true,
	},

	title: {
		type: String,
	},

	slug: {
		type: String,
		max: 200,
		optional: true,
		autoform: {
			type: 'hidden',
			label: false,
		},
	},

	description: {
		type: String,
		optional: true,
	},

	descriptionRaw: {
		type: Object,
		optional: true,
	},

	type: {
		type: String,
		optional: true,
		defaultValue: 'word',
	},

	count: {
		type: Number,
		optional: true,
	},

	work: {
		type: Object,
		blackbox: true,

	},

	subwork: {
		type: Object,
		blackbox: true,

	},

	lineFrom: {
		type: Number,
		optional: true,
	},

	lineTo: {
		type: Number,
		optional: true,
	},

	lineLetter: {
		type: String,
		optional: true,
	},

	nLines: {
		type: Number,
		optional: true,
	},
	tenantId: {
		type: String,
		optional: true,
	},
});

Keywords.attachSchema(Keywords.schema);
Keywords.friendlySlugs('title');

Keywords.attachBehaviour('timestampable', {
	createdAt: 'created',
	createdBy: 'createdBy',
	updatedAt: 'updated',
	updatedBy: 'updatedBy'
});

export default Keywords;
<commit_msg>updated snapshot tests<commit_after>import { Meteor } from 'meteor/meteor';
import { SimpleSchema } from 'meteor/aldeed:simple-schema';

const Keywords = new Meteor.Collection('keywords');

Keywords.schema = new SimpleSchema({
	_id: {
		type: String,
		optional: true,
	},

	wordpressId: {
		type: Number,
		optional: true,
	},

	title: {
		type: String,
	},

	slug: {
		type: String,
		max: 200,
		optional: true,
		autoform: {
			type: 'hidden',
			label: false,
		},
	},

	description: {
		type: String,
		optional: true,
	},

	descriptionRaw: {
		type: Object,
		optional: true,
	},

	type: {
		type: String,
		optional: true,
		defaultValue: 'word',
	},

	count: {
		type: Number,
		optional: true,
	},

	work: {
		type: Object,
		optional: true,
	},

	'work._id': {
		type: String,
		optional: true,
	},

	'work.title': {
		type: String,
		optional: true,
	},

	'work.order': {
		type: Number,
		optional: true,
	},

	'work.subworks': {
		type: [Object],
		optional: true,
	},

	'work.friendlySlugs': {
		type: Object,
		optional: true,
	},

	'work.slug': {
		type: String,
		optional: true,
	},

	'work.updated': {
		type: Date,
		optional: true,
	},

	'work.tenantID': {
		type: String,
		optional: true,
	},

	subwork: {
		type: Object,
		optional: true,
	},

	'subwork.n': {
		type: Number,
		optional: true,
	},

	'subwork.title': {
		type: String,
		optional: true,
	},

	'subwork.slug': {
		type: String,
		optional: true,
	},

	'subwork.comments': {
		type: [Object],
		optional: true,
	},

	lineFrom: {
		type: Number,
		optional: true,
	},

	lineTo: {
		type: Number,
		optional: true,
	},

	lineLetter: {
		type: String,
		optional: true,
	},

	nLines: {
		type: Number,
		optional: true,
	},
	tenantId: {
		type: String,
		optional: true,
	},
});

Keywords.attachSchema(Keywords.schema);
Keywords.friendlySlugs('title');

Keywords.attachBehaviour('timestampable', {
	createdAt: 'created',
	createdBy: 'createdBy',
	updatedAt: 'updated',
	updatedBy: 'updatedBy'
});

export default Keywords;
<|endoftext|>