<commit_before>function Ship(r, f, p) {
  this.x = 0;
  this.y = 0;
  this.r = r;
  this.f = -f;
  this.p = p || 0;
  this.off = 0;
  this.tick = function(t, time) {
    this.off = Math.max(0, this.off - t);
    this.x = this.r * Math.sin(this.f * time / 1000 + this.p);
    this.y = this.r * Math.cos(this.f * time / 1000 + this.p);
  };
  this.remove = function() {
    this.uiRemove();
  };
}

function Bullet(speed) {
  this.x = 0;
  this.y = 0;
  this.tx = 0;
  this.ty = 0;
  this.vx = 0;
  this.vy = 0;
  this.speed = speed;
  this.shoot = function(from, to) {
    this.x = from.x;
    this.y = from.y;
    this.tx = to.x;
    this.ty = to.y;
    var dx = this.tx - this.x;
    var dy = this.ty - this.y;
    var d = M.length(dx, dy);
    this.vx = this.speed * dx / d;
    this.vy = this.speed * dy / d;
    return this;
  };
  this.tick = function(t, time) {
    var x = this.x + this.vx * t / 1000;
    var y = this.y + this.vy * t / 1000;
    if (Math.abs(x - this.tx) > Math.abs(this.x - this.tx)
        && Math.abs(y - this.ty) > Math.abs(this.y - this.ty)) {
      return true;
    }
    this.x = x;
    this.y = y;
  };
  this.remove = function() {
    this.uiRemove();
  };
}

var Asteroid = Bullet;

function Space() {

  var speed = 4;
  var acc = 0.00001;
  var explode = 6;
  var collide = 6;
  var off = 1200;

  var time = 0;

  var planet = {
    life : 0,
    down : 0,
    up : 0,
    next : 0
  };

  var next = {};
  var ships = [];
  var bullets = [];
  var asteroids = [];

  this.start = function() {
    time = 0;
    planet.life = 16, planet.up = 0, planet.down = 0;

    if (ships.length == 0) {
      ships.push(this.uiAddShip(new Ship(8, 2, 0)));
      ships.push(this.uiAddShip(new Ship(8, 2, Math.PI / 3 * 2)));
      ships.push(this.uiAddShip(new Ship(8, 2, Math.PI / 3 * 4)));
      this.uiAddPlanet(planet);
      this.uiAddNext(next);
    }

    for (var i = bullets.length - 1; i >= 0; i--) {
      var bullet = bullets[i];
      bullet.remove();
      bullets.splice(i, 1);
    }
    for (var j = asteroids.length - 1; j >= 0; j--) {
      var asteroid = asteroids[j];
      asteroid.remove();
      asteroids.splice(j, 1);
    }

    for (var s = ships.length - 1; s >= 0; s--) {
      var ship = ships[s];
      ship.off = 0;
    }

    next.uiNext(ships[planet.next]);
  };

  var randomAsteroid = new Randomize().spacing(function() {
    return M.random(2, 3) * 180 / (time / 1000 + 180) * 1000;
  }, 0).add(function() {
    var a = M.random(0, 2 * Math.PI);
    var r = M.random(120, 180);
    return new Asteroid(speed + time * acc).shoot({
      x : r * Math.sin(a),
      y : r * Math.cos(a)
    }, {
      x : 0,
      y : 0
    });
  });

  this.tick = function(t) {
    t = Math.min(t, 100);
    time += t;

    next.uiUpdate();

    for (var s = ships.length - 1; s >= 0; s--) {
      var ship = ships[s];
      ship.tick(t, time);
      ship.uiUpdate();
    }

    for (var j = asteroids.length - 1; j >= 0; j--) {
      var asteroid = asteroids[j];
      if (M.length(asteroid.x, asteroid.y) < collide || asteroid.tick(t, time)) {
        planet.life--;
        asteroid.remove();
        asteroids.splice(j, 1);
        planet.down++;
      }
      asteroid.uiUpdate();
    }

    for (var i = bullets.length - 1; i >= 0; i--) {
      var bullet = bullets[i];
      if (bullet.tick(t, time)) {
        for (var j = asteroids.length - 1; j >= 0; j--) {
          var asteroid = asteroids[j];
          if (M.length(asteroid.x - bullet.x, asteroid.y - bullet.y) < explode) {
            asteroid.remove();
            asteroids.splice(j, 1);
            planet.life = Math.min(16, planet.life + 1);
            planet.up++;
          }
        }
        this.uiExplode(bullet);
        bullet.remove();
        bullets.splice(i, 1);
      }
      bullet.uiUpdate();
    }

    var asteroid = randomAsteroid.test(t).random();
    asteroid && asteroids.push(this.uiAddAsteroid(asteroid()));

    planet.uiUpdate();

    if (planet.life == 0) {
      this.uiGameover();
    }
  };

  this.shoot = function(loc) {
    var ship = ships[planet.next];
    if (ship.off <= 0) {
      ship.off = off;
      var bullet = new Bullet(25).shoot(ship, loc);
      bullets.push(bullet);
      this.uiAddBullet(bullet);
      planet.next = ++planet.next % ships.length;
      next.uiNext(ships[planet.next]);
    }
  };
}

Cut(function(root, container) {
  Cut.Mouse(root, container);
  root.viewbox(150, 150).pin('handle', -0.5);

  var ui = {};

  root.on('viewport', function(width, height) {
    ui.bg.pin({
      resizeMode : 'out',
      resizeWidth : width,
      resizeHeight : height
    });
  });

  ui.bg = Cut.image('base:bg').pin('handle', 0.5).appendTo(root);

  ui.status = Cut.row().spacing(2).pin({
    offset : 1,
    align : -0.5,
    handle : 0
  }).appendTo(root);
  ui.up = Cut.string('base:d_').appendTo(ui.status);
  ui.down = Cut.string('base:d_').appendTo(ui.status);

  var space = new Space(root);

  space.uiAddPlanet = function(obj) {
    obj.ui = Cut.image('base:planet').pin('handle', 0.5).appendTo(root);
    obj.uiRemove = function() {
      this.ui.remove();
    };
    obj.uiUpdate = function() {
      this.ui.pin('alpha', Math.min(1, Math.max(0, this.life / 16)));
      ui.up.setValue((this.up > 0 ? '+' : '') + this.up);
      ui.down.setValue(-this.down);
    };
    return obj;
  };

  space.uiAddBullet = function(obj) {
    obj.ui = Cut.image('base:bullet').pin('handle', 0.5).appendTo(root);
    obj.uiRemove = function() {
      this.ui.remove();
    };
    obj.uiUpdate = function() {
      this.ui.pin({
        offsetX : this.x,
        offsetY : this.y
      });
    };
    return obj;
  };

  space.uiAddShip = function(obj) {
    obj.ui = Cut.image('base:ship').pin('handle', 0.5).appendTo(root);
    obj.uiRemove = function() {
      this.ui.remove();
    };
    obj.uiUpdate = function() {
      this.ui.pin('alpha', 1 / (this.off / 100 + 1));
      this.ui.pin({
        offsetX : this.x,
        offsetY : this.y
      });
    };
    return obj;
  };

  space.uiAddAsteroid = function(obj) {
    obj.ui = Cut.image('base:asteroid').pin('handle', 0.5).appendTo(root);
    obj.uiRemove = function() {
      this.ui.remove();
    };
    obj.uiUpdate = function() {
      this.ui.pin({
        offsetX : this.x,
        offsetY : this.y
      });
    };
    return obj;
  };

  space.uiAddNext = function(obj) {
    obj.ui = Cut.image('base:next').pin('align', 0.5);
    var _next;
    obj.uiNext = function(next) {
      this.ui.appendTo(next.ui);
      _next = next;
    };
    obj.uiUpdate = function() {
      _next && this.ui.pin('alpha', _next.off <= 0 ? 1 : 0.6);
    };
    return obj;
  };

  space.uiExplode = function(loc) {
    Cut.image('base:explosion').pin('handle', 0.5).pin({
      offsetX : loc.x,
      offsetY : loc.y
    }).appendTo(root).pin('scale', 0.1).tween(50).pin({
      scaleX : 1,
      scaleY : 1
    }).tween(200).pin('alpha', 0).then(function() {
      this.remove();
    });
  };

  space.uiGameover = function() {
    this.start();
  };

  root.tick(function(t) {
    space.tick(t);
  });

  root.on(Cut.Mouse.START, function(ev, point) {
    space.shoot(point);
  });

  space.start();

});

var M = Cut.Math;<commit_msg>doc orbit<commit_after>function Ship(r, f, p) {
  this.x = 0;
  this.y = 0;
  this.r = r; // radius
  this.f = -f; // rotation frequency
  this.p = p || 0; // rotation phase offset
  this.off = 0; // time to reload and ready
  this.tick = function(t, time) {
    this.off = Math.max(0, this.off - t);
    this.x = this.r * Math.sin(this.f * time / 1000 + this.p);
    this.y = this.r * Math.cos(this.f * time / 1000 + this.p);
  };
  this.remove = function() {
    this.uiRemove();
  };
}

function Bullet(speed) {
  this.x = 0;
  this.y = 0;
  this.tx = 0; // target
  this.ty = 0;
  this.vx = 0; // velocity vector
  this.vy = 0;
  this.speed = speed;
  this.shoot = function(from, to) {
    this.x = from.x;
    this.y = from.y;
    this.tx = to.x;
    this.ty = to.y;
    var dx = this.tx - this.x;
    var dy = this.ty - this.y;
    var d = M.length(dx, dy);
    this.vx = this.speed * dx / d;
    this.vy = this.speed * dy / d;
    return this;
  };
  this.tick = function(t, time) {
    var x = this.x + this.vx * t / 1000;
    var y = this.y + this.vy * t / 1000;
    if (Math.abs(x - this.tx) > Math.abs(this.x - this.tx)
        && Math.abs(y - this.ty) > Math.abs(this.y - this.ty)) {
      return true;
    }
    this.x = x;
    this.y = y;
  };
  this.remove = function() {
    this.uiRemove();
  };
}

var Asteroid = Bullet;

function Space() {

  var speed = 4; // asteroids speed
  var acc = 0.00001; // asteroids acceleration
  var explode = 6; // explode radius
  var collide = 6; // collision radius
  var off = 1200; // ship realod time

  var time = 0;

  var planet = {
    life : 0,
    down : 0,
    up : 0,
    next : 0
  };

  var next = {};
  var ships = [];
  var bullets = [];
  var asteroids = [];

  this.start = function() {
    time = 0;
    planet.life = 16, planet.up = 0, planet.down = 0;

    if (ships.length == 0) {
      ships.push(this.uiAddShip(new Ship(8, 2, 0)));
      ships.push(this.uiAddShip(new Ship(8, 2, Math.PI / 3 * 2)));
      ships.push(this.uiAddShip(new Ship(8, 2, Math.PI / 3 * 4)));
      this.uiAddPlanet(planet);
      this.uiAddNext(next);
    }

    for (var i = bullets.length - 1; i >= 0; i--) {
      var bullet = bullets[i];
      bullet.remove();
      bullets.splice(i, 1);
    }
    for (var j = asteroids.length - 1; j >= 0; j--) {
      var asteroid = asteroids[j];
      asteroid.remove();
      asteroids.splice(j, 1);
    }

    for (var s = ships.length - 1; s >= 0; s--) {
      var ship = ships[s];
      ship.off = 0;
    }

    next.uiNext(ships[planet.next]);
  };

  var randomAsteroid = new Randomize().spacing(function() {
    return M.random(2, 3) * 180 / (time / 1000 + 180) * 1000;
  }, 0).add(function() {
    var a = M.random(0, 2 * Math.PI);
    var r = M.random(120, 180);
    return new Asteroid(speed + time * acc).shoot({
      x : r * Math.sin(a),
      y : r * Math.cos(a)
    }, {
      x : 0,
      y : 0
    });
  });

  this.tick = function(t) {
    t = Math.min(t, 100);
    time += t;

    next.uiUpdate();

    for (var s = ships.length - 1; s >= 0; s--) {
      var ship = ships[s];
      ship.tick(t, time);
      ship.uiUpdate();
    }

    for (var j = asteroids.length - 1; j >= 0; j--) {
      var asteroid = asteroids[j];
      if (M.length(asteroid.x, asteroid.y) < collide || asteroid.tick(t, time)) {
        planet.life--;
        asteroid.remove();
        asteroids.splice(j, 1);
        planet.down++;
      }
      asteroid.uiUpdate();
    }

    for (var i = bullets.length - 1; i >= 0; i--) {
      var bullet = bullets[i];
      if (bullet.tick(t, time)) {
        for (var j = asteroids.length - 1; j >= 0; j--) {
          var asteroid = asteroids[j];
          if (M.length(asteroid.x - bullet.x, asteroid.y - bullet.y) < explode) {
            asteroid.remove();
            asteroids.splice(j, 1);
            planet.life = Math.min(16, planet.life + 1);
            planet.up++;
          }
        }
        this.uiExplode(bullet);
        bullet.remove();
        bullets.splice(i, 1);
      }
      bullet.uiUpdate();
    }

    var asteroid = randomAsteroid.test(t).random();
    asteroid && asteroids.push(this.uiAddAsteroid(asteroid()));

    planet.uiUpdate();

    if (planet.life == 0) {
      this.uiGameover();
    }
  };

  this.shoot = function(loc) {
    var ship = ships[planet.next];
    if (ship.off <= 0) {
      ship.off = off;
      var bullet = new Bullet(25).shoot(ship, loc);
      bullets.push(bullet);
      this.uiAddBullet(bullet);
      planet.next = ++planet.next % ships.length;
      next.uiNext(ships[planet.next]);
    }
  };
}

Cut(function(root, container) {
  Cut.Mouse(root, container);
  root.viewbox(150, 150).pin('handle', -0.5);

  var ui = {};

  root.on('viewport', function(width, height) {
    ui.bg.pin({
      resizeMode : 'out',
      resizeWidth : width,
      resizeHeight : height
    });
  });

  ui.bg = Cut.image('base:bg').pin('handle', 0.5).appendTo(root);

  ui.status = Cut.row().spacing(2).pin({
    offset : 1,
    align : -0.5,
    handle : 0
  }).appendTo(root);
  ui.up = Cut.string('base:d_').appendTo(ui.status);
  ui.down = Cut.string('base:d_').appendTo(ui.status);

  var space = new Space(root);

  space.uiAddPlanet = function(obj) {
    obj.ui = Cut.image('base:planet').pin('handle', 0.5).appendTo(root);
    obj.uiRemove = function() {
      this.ui.remove();
    };
    obj.uiUpdate = function() {
      this.ui.pin('alpha', Math.min(1, Math.max(0, this.life / 16)));
      ui.up.setValue((this.up > 0 ? '+' : '') + this.up);
      ui.down.setValue(-this.down);
    };
    return obj;
  };

  space.uiAddBullet = function(obj) {
    obj.ui = Cut.image('base:bullet').pin('handle', 0.5).appendTo(root);
    obj.uiRemove = function() {
      this.ui.remove();
    };
    obj.uiUpdate = function() {
      this.ui.pin({
        offsetX : this.x,
        offsetY : this.y
      });
    };
    return obj;
  };

  space.uiAddShip = function(obj) {
    obj.ui = Cut.image('base:ship').pin('handle', 0.5).appendTo(root);
    obj.uiRemove = function() {
      this.ui.remove();
    };
    obj.uiUpdate = function() {
      this.ui.pin('alpha', 1 / (this.off / 100 + 1));
      this.ui.pin({
        offsetX : this.x,
        offsetY : this.y
      });
    };
    return obj;
  };

  space.uiAddAsteroid = function(obj) {
    obj.ui = Cut.image('base:asteroid').pin('handle', 0.5).appendTo(root);
    obj.uiRemove = function() {
      this.ui.remove();
    };
    obj.uiUpdate = function() {
      this.ui.pin({
        offsetX : this.x,
        offsetY : this.y
      });
    };
    return obj;
  };

  space.uiAddNext = function(obj) {
    obj.ui = Cut.image('base:next').pin('align', 0.5);
    var _next;
    obj.uiNext = function(next) {
      this.ui.appendTo(next.ui);
      _next = next;
    };
    obj.uiUpdate = function() {
      _next && this.ui.pin('alpha', _next.off <= 0 ? 1 : 0.6);
    };
    return obj;
  };

  space.uiExplode = function(loc) {
    Cut.image('base:explosion').pin('handle', 0.5).pin({
      offsetX : loc.x,
      offsetY : loc.y
    }).appendTo(root).pin('scale', 0.1).tween(50).pin({
      scaleX : 1,
      scaleY : 1
    }).tween(200).pin('alpha', 0).then(function() {
      this.remove();
    });
  };

  space.uiGameover = function() {
    this.start();
  };

  root.tick(function(t) {
    space.tick(t);
  });

  root.on(Cut.Mouse.START, function(ev, point) {
    space.shoot(point);
  });

  space.start();

});

var M = Cut.Math;<|endoftext|>


<commit_before>$(document).ready(function() {
  "use strict";
  var GARMENTS = {
    // JavaScript doesn't have ints or else prices would be in cents.
    // I guessed the service (dry clean vs launder and press) as best as I could,
    // send in a pull request to correct it if the service is wrong.
    "bathing suit (per piece)": {
      "price": 5,
      "services": ["launder and press"],
      "descriptor": "piece"
    },
    "bed sheets": {
      "price": 15,
      "services": ["launder and press"],
      "descriptor": "sheet"
    },
    "blankets": {
      "price": 15,
      "services": ["launder and press", "wash and fold"],
      "descriptor": "blanket"
    },
    "blazers": {
      "price": 8,
      "services": ["dry clean"],
      "descriptor": "blazer"
    },
    "blouses": {
      "price": 7,
      "services": ["launder and press"],
      "descriptor": "blouse"
    },
    "coats": {
      "price": 15,
      "services": ["dry clean"],
      "descriptor": "coat"
    },
    "comforters": {
      "price": 30,
      "services": ["wash and fold"],
      "descriptor": "comforter"
    },
    "comforter covers": {
      "price": 30,
      "services": ["launder and press"],
      "descriptor": "cover"
    },
    "dresses (casual)": {
      "price": 11,
      "services": ["launder and press"],
      "descriptor": "dress"
    },
    "dresses (formal)": {
      "price": 15,
      "services": ["dry clean"],
      "descriptor": "dress"
    },
    "duvets": {
      "price": 30,
      "services": ["wash and fold"],
      "descriptor": "duvet"
    },
    "duvet covers": {
      "price": 30,
      "services": ["launder and press"],
      "descriptor": "cover"
    },
    "jackets (formal)": {
      "price": 8,
      "services": ["dry clean"],
      "descriptor": "jacket"
    },
    "napkins": {
      "price": 1,
      "services": ["launder and press"],
      "descriptor": "napkin"
    },
    "pants": {
      "price": 7,
      "services": ["launder and press"],
      "descriptor": "pant"
    },
    "pashminas": {
      "price": 7,
      "services": ["launder and press"],
      "descriptor": "pashmina"
    },
    "pillows": {
      "price": 10,
      "services": ["launder and press"],
      "descriptor": "pillow"
    },
    "pillowcases": {
      "price": 2,
      "services": ["launder and press"],
      "descriptor": "item"
    },
    "scarves": {
      "price": 7,
      "services": ["launder and press"],
      "descriptor": "scarf"
    },
    "shawls": {
      "price": 7,
      "services": ["launder and press"],
      "descriptor": "shawl"
    },
    "shirts": {
      "price": 2.5,
      "services": ["launder and press"],
      "descriptor": "shirt"
    },
    "skirts": {
      "price": 7,
      "services": ["launder and press"],
      "descriptor": "skirt"
    },
    "sweaters": {
      "price": 7,
      "services": ["dry clean"],
      "descriptor": "sweater"
    },
    "tablecloths": {
      "price": 30,
      "services": ["launder and press"],
      "descriptor": "item"
    },
    "ties": {
      "price": 5,
      "services": ["dry clean"],
      "descriptor": "tie"
    },
    "bath mats": {
      "price": 6,
      "services": ["wash and fold"],
      "descriptor": "mat"
    },
    "kitchen mats": {
      "price": 6,
      "services": ["wash and fold"],
      "descriptor": "mat"
    },
    "delicates" : {
      "price": 1,
      "services": ["hang dry"],
      "descriptor": "item"
    },
    "synthetics" : {
      "price": 2.5,
      "services": ["hang dry"],
      "descriptor": "item"
    },
    "wash and fold" : {
      "price": 1.5,
      "services": ["wash and fold"],
      "descriptor": "pound"
    },
  };
  var RUSH_FEE = 3.95;

  var GarmentModel = Backbone.Model.extend({
    defaults: {
      garment_type: '',
      cost: 0,
      quantity: 1,
      is_rush: false,
      hang_dry: false,
      descriptor: 'item',
      total_cost: 0,
    }
  });

  var GarmentCollection = Backbone.Collection.extend({
    model: GarmentModel,
    localStorage: new Backbone.LocalStorage('garments-manager'),

    initialize: function() {
      _.bind(this.sumCosts, this);
    },

    sumCosts: function() {
      return _.reduce(this.models, function(a,x){return a + x.get('total_cost');}, 0);
    }
  });

  var GarmentView = Backbone.View.extend({
    tagName: 'tr',
    className: 'garment',

    events: {
      'click .remove-garment': 'remove'
    },

    initialize: function() {
      _.bindAll(this, 'render', 'unrender', 'remove', 'edit');
      this.model.bind('change', this.render);
      this.model.bind('remove', this.unrender);
    },

    render: function() {
      var is_rush = this.model.get('is_rush') ? 'Yes' : 'No';
      var hang_dry = this.model.get('hang_dry') ? 'Yes' : 'No';
      this.$el.html(
        '' + this.model.get('garment_type') + '	' +
        '' + Math.round(this.model.get('quantity')) + '	' +
        '$' + Math.round10(this.model.get('cost'), -2).toFixed(2) + ' per ' + this.model.get('descriptor') + '	' +
        '' + is_rush + '	' +
        '' + hang_dry + '	' +
        '$' + Math.round10(this.model.get('total_cost'), -2).toFixed(2) + '	' +
        '<button class="btn btn-danger remove-garment"></button>	'
      );
      return this;
    },

    edit: function() {

    },

    unrender: function() {
      this.$el.remove();
    },

    remove: function() {
      this.model.destroy();
      tableView.updateTotal();
    }
  });

  var TableView = Backbone.View.extend({
    el: $('#calculation-table'),

    initialize: function() {
      var self = this;
      var $garmentType = $('#garment-type');
      var $garmentQuantity = $('#garment-quantity');
      var $rushOrder = $('#rush-order');
      var $hangDry = $('#hang-dry');
      var $addGarment = $('#add-garment');
      var $clearAll = $('#clear-items');
      var $toggleBoxes = $('input:checkbox');
      var $rushOrderLabel = $rushOrder.parent();

      _.bindAll(this, 'render');

      this.collection = new GarmentCollection();
      this.collection.bind('add', this.appendGarment);
      // Used to fetch from localStorage
      this.collection.fetch();
      this.updateTotal();
      this.render();

      // Populate garment choices
      var sortedGarments = [];
      for (var garment in GARMENTS) {
        sortedGarments.push(garment);
      }
      sortedGarments.sort();
      for (var i = 0; i < sortedGarments.length; i++) {
        var gar = sortedGarments[i];
        $garmentType.append(
          '<option value="' + gar + '">' +
            gar.toTitleCase() +
          '</option>'
          );
      }

      $garmentType.chosen();

      $rushOrderLabel.popover({
        'trigger' : 'hover click',
        'placement' : 'top',
        'content' : 'This garment cannot be rushed.'
      });
      $rushOrderLabel.popover('disable');

      $garmentType.on('change', function() {
        // Only wash and fold items can be rushed
        var garment = $(this).val();
        if ($.inArray('wash and fold', GARMENTS[garment].services) > -1) {
          $rushOrder.prop('checked', false);
          $rushOrder.prop('disabled', false);
          $rushOrderLabel.removeClass('disabled');
          $rushOrderLabel.removeClass('active');
          $rushOrder.next().html('No');
          $rushOrderLabel.popover('disable');
        } else {
          $rushOrder.prop('checked', false);
          $rushOrder.prop('disabled', true);
          $rushOrderLabel.addClass('disabled');
          $rushOrder.next().html('No');
          $rushOrderLabel.popover('enable');
        }
      });

      // TODO: Refactor this into an AppView to preserve the local scope
      //       and events of $el.
      $addGarment.on('click', function(event) {
        event.preventDefault();
        var garmentType = $garmentType.val();
        var garmentQuantity = parseInt($garmentQuantity.val());
        var garmentDescriptor = GARMENTS[garmentType].descriptor;
        var garmentCost = parseFloat(GARMENTS[garmentType].price);
        var isRush = $rushOrder.prop('checked');
        var hangDry = $hangDry.prop('checked');

        self.addGarment.bind(self)({
          garment_type: garmentType.toTitleCase(),
          cost: garmentCost,
          quantity: garmentQuantity,
          is_rush: isRush,
          hang_dry: hangDry,
          descriptor: garmentDescriptor,
          total_cost: garmentCost * garmentQuantity,
        });
      });

      $clearAll.on('click', function(event) {
        event.preventDefault();
        self.removeAll.bind(self)();
        self.unrenderAll.bind(self)();
        self.updateTotal();
      });

      // Make the toggle checkboxes change from "No" to "Yes"
      $toggleBoxes.on('change', function(event) {
        event.preventDefault();
        var $this = $(this);
        var newState = 'No';

        if ($this.prop('disabled')) {
          return;
        }

        if ($this.prop('checked')) {
          newState = 'Yes';
          $this.parent().addClass('active');
        } else {
          newState = 'No';
          $this.parent().removeClass('active');
        }
        $this.next().html(newState);
      });
    },

    updateTotal: function() {
      var totalCost = this.collection.sumCosts();
      // Check if there is at least one item that is being rushed
      var willRush = false;
      _.each(this.collection.models, function(garment) {
        if (garment.get('is_rush')) {
          willRush = true;
        }
      });
      if (willRush) {
        totalCost += RUSH_FEE;
        $('#rush-fee').html('$' + RUSH_FEE.toFixed(2));
      } else {
        $('#rush-fee').html('$0.00');
      }
      $('#grand-total').html('$' + totalCost.toFixed(2));
    },

    render: function() {
      var self = this;
      _(this.collection.models).each(function(garment){
        self.appendGarment(garment);
      }, this);
    },

    unrenderAll: function() {
      this.$el.html('');
    },

    addGarment: function(params) {
      this.collection.create(new GarmentModel(params));
      this.updateTotal();
    },

    appendGarment: function(garment) {
      var garmentView = new GarmentView({
        model: garment
      });
      var garmentEl = garmentView.render().$el;
      $('#calculation-table', this.el).append(garmentEl);
    },

    removeAll: function() {
      // This is done instead of this.collection.reset();
      // because we also need to delete the models in localStorage
      _.chain(this.collection.models).clone().each(function(model){
        model.destroy();
      });
    }
  });

  $('#popover-disclaimer').popover({
    'trigger' : 'hover click',
    'placement' : 'right',
    'content' : 'For informational purposes only; check rinse.com for the most updated prices.',
    'html' : true
  });

  var tableView = new TableView();

});

// Decimal rounding from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
(function() {
  /**
   * Decimal adjustment of a number.
   *
   * @param {String}  type  The type of adjustment.
   * @param {Number}  value The number.
   * @param {Integer} exp   The exponent (the 10 logarithm of the adjustment base).
   * @returns {Number} The adjusted value.
   */
  function decimalAdjust(type, value, exp) {
    // If the exp is undefined or zero...
    if (typeof exp === 'undefined' || +exp === 0) {
      return Math[type](value);
    }
    value = +value;
    exp = +exp;
    // If the value is not a number or the exp is not an integer...
    if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0)) {
      return NaN;
    }
    // Shift
    value = value.toString().split('e');
    value = Math[type](+(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp)));
    // Shift back
    value = value.toString().split('e');
    return +(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp));
  }

  // Decimal round
  if (!Math.round10) {
    Math.round10 = function(value, exp) {
      return decimalAdjust('round', value, exp);
    };
  }
  // Decimal floor
  if (!Math.floor10) {
    Math.floor10 = function(value, exp) {
      return decimalAdjust('floor', value, exp);
    };
  }
  // Decimal ceil
  if (!Math.ceil10) {
    Math.ceil10 = function(value, exp) {
      return decimalAdjust('ceil', value, exp);
    };
  }
})();

/*
  * To Title Case 2.1 – http://individed.com/code/to-title-case/
  * Copyright © 2008–2013 David Gouch. Licensed under the MIT License.
 */

String.prototype.toTitleCase = function(){
  var smallWords = /^(a|an|and|as|at|but|by|en|for|if|in|nor|of|on|or|per|the|to|vs?\.?|via)$/i;

  return this.replace(/[A-Za-z0-9\u00C0-\u00FF]+[^\s-]*/g, function(match, index, title){
    if (index > 0 && index + match.length !== title.length &&
      match.search(smallWords) > -1 && title.charAt(index - 2) !== ":" &&
      (title.charAt(index + match.length) !== '-' || title.charAt(index - 1) === '-') &&
      title.charAt(index - 1).search(/[^\s-]/) < 0) {
      return match.toLowerCase();
    }

    if (match.substr(1).search(/[A-Z]|\../) > -1) {
      return match;
    }

    return match.charAt(0).toUpperCase() + match.substr(1);
  });
};
<commit_msg>Show the user an error message if no garment was selected<commit_after>$(document).ready(function() {
  "use strict";
  var GARMENTS = {
    // JavaScript doesn't have ints or else prices would be in cents.
    // I guessed the service (dry clean vs launder and press) as best as I could,
    // send in a pull request to correct it if the service is wrong.
    "bathing suit (per piece)": {
      "price": 5,
      "services": ["launder and press"],
      "descriptor": "piece"
    },
    "bed sheets": {
      "price": 15,
      "services": ["launder and press"],
      "descriptor": "sheet"
    },
    "blankets": {
      "price": 15,
      "services": ["launder and press", "wash and fold"],
      "descriptor": "blanket"
    },
    "blazers": {
      "price": 8,
      "services": ["dry clean"],
      "descriptor": "blazer"
    },
    "blouses": {
      "price": 7,
      "services": ["launder and press"],
      "descriptor": "blouse"
    },
    "coats": {
      "price": 15,
      "services": ["dry clean"],
      "descriptor": "coat"
    },
    "comforters": {
      "price": 30,
      "services": ["wash and fold"],
      "descriptor": "comforter"
    },
    "comforter covers": {
      "price": 30,
      "services": ["launder and press"],
      "descriptor": "cover"
    },
    "dresses (casual)": {
      "price": 11,
      "services": ["launder and press"],
      "descriptor": "dress"
    },
    "dresses (formal)": {
      "price": 15,
      "services": ["dry clean"],
      "descriptor": "dress"
    },
    "duvets": {
      "price": 30,
      "services": ["wash and fold"],
      "descriptor": "duvet"
    },
    "duvet covers": {
      "price": 30,
      "services": ["launder and press"],
      "descriptor": "cover"
    },
    "jackets (formal)": {
      "price": 8,
      "services": ["dry clean"],
      "descriptor": "jacket"
    },
    "napkins": {
      "price": 1,
      "services": ["launder and press"],
      "descriptor": "napkin"
    },
    "pants": {
      "price": 7,
      "services": ["launder and press"],
      "descriptor": "pant"
    },
    "pashminas": {
      "price": 7,
      "services": ["launder and press"],
      "descriptor": "pashmina"
    },
    "pillows": {
      "price": 10,
      "services": ["launder and press"],
      "descriptor": "pillow"
    },
    "pillowcases": {
      "price": 2,
      "services": ["launder and press"],
      "descriptor": "item"
    },
    "scarves": {
      "price": 7,
      "services": ["launder and press"],
      "descriptor": "scarf"
    },
    "shawls": {
      "price": 7,
      "services": ["launder and press"],
      "descriptor": "shawl"
    },
    "shirts": {
      "price": 2.5,
      "services": ["launder and press"],
      "descriptor": "shirt"
    },
    "skirts": {
      "price": 7,
      "services": ["launder and press"],
      "descriptor": "skirt"
    },
    "sweaters": {
      "price": 7,
      "services": ["dry clean"],
      "descriptor": "sweater"
    },
    "tablecloths": {
      "price": 30,
      "services": ["launder and press"],
      "descriptor": "item"
    },
    "ties": {
      "price": 5,
      "services": ["dry clean"],
      "descriptor": "tie"
    },
    "bath mats": {
      "price": 6,
      "services": ["wash and fold"],
      "descriptor": "mat"
    },
    "kitchen mats": {
      "price": 6,
      "services": ["wash and fold"],
      "descriptor": "mat"
    },
    "delicates" : {
      "price": 1,
      "services": ["hang dry"],
      "descriptor": "item"
    },
    "synthetics" : {
      "price": 2.5,
      "services": ["hang dry"],
      "descriptor": "item"
    },
    "wash and fold" : {
      "price": 1.5,
      "services": ["wash and fold"],
      "descriptor": "pound"
    },
  };
  var RUSH_FEE = 3.95;

  var GarmentModel = Backbone.Model.extend({
    defaults: {
      garment_type: '',
      cost: 0,
      quantity: 1,
      is_rush: false,
      hang_dry: false,
      descriptor: 'item',
      total_cost: 0,
    }
  });

  var GarmentCollection = Backbone.Collection.extend({
    model: GarmentModel,
    localStorage: new Backbone.LocalStorage('garments-manager'),

    initialize: function() {
      _.bind(this.sumCosts, this);
    },

    sumCosts: function() {
      return _.reduce(this.models, function(a,x){return a + x.get('total_cost');}, 0);
    }
  });

  var GarmentView = Backbone.View.extend({
    tagName: 'tr',
    className: 'garment',

    events: {
      'click .remove-garment': 'remove'
    },

    initialize: function() {
      _.bindAll(this, 'render', 'unrender', 'remove', 'edit');
      this.model.bind('change', this.render);
      this.model.bind('remove', this.unrender);
    },

    render: function() {
      var is_rush = this.model.get('is_rush') ? 'Yes' : 'No';
      var hang_dry = this.model.get('hang_dry') ? 'Yes' : 'No';
      this.$el.html(
        '' + this.model.get('garment_type') + '	' +
        '' + Math.round(this.model.get('quantity')) + '	' +
        '$' + Math.round10(this.model.get('cost'), -2).toFixed(2) + ' per ' + this.model.get('descriptor') + '	' +
        '' + is_rush + '	' +
        '' + hang_dry + '	' +
        '$' + Math.round10(this.model.get('total_cost'), -2).toFixed(2) + '	' +
        '<button class="btn btn-danger remove-garment"></button>	'
      );
      return this;
    },

    edit: function() {

    },

    unrender: function() {
      this.$el.remove();
    },

    remove: function() {
      this.model.destroy();
      tableView.updateTotal();
    }
  });

  var TableView = Backbone.View.extend({
    el: $('#calculation-table'),

    initialize: function() {
      var self = this;
      var $garmentType = $('#garment-type');
      var $garmentQuantity = $('#garment-quantity');
      var $rushOrder = $('#rush-order');
      var $hangDry = $('#hang-dry');
      var $addGarment = $('#add-garment');
      var $clearAll = $('#clear-items');
      var $toggleBoxes = $('input:checkbox');
      var $rushOrderLabel = $rushOrder.parent();

      _.bindAll(this, 'render');

      this.collection = new GarmentCollection();
      this.collection.bind('add', this.appendGarment);
      // Used to fetch from localStorage
      this.collection.fetch();
      this.updateTotal();
      this.render();

      // Populate garment choices
      var sortedGarments = [];
      for (var garment in GARMENTS) {
        sortedGarments.push(garment);
      }
      sortedGarments.sort();
      for (var i = 0; i < sortedGarments.length; i++) {
        var gar = sortedGarments[i];
        $garmentType.append(
          '<option value="' + gar + '">' +
            gar.toTitleCase() +
          '</option>'
          );
      }

      $garmentType.chosen();

      $rushOrderLabel.popover({
        'trigger' : 'hover click',
        'placement' : 'top',
        'content' : 'This garment cannot be rushed.'
      });
      $rushOrderLabel.popover('disable');

      $addGarment.popover({
        'trigger' : 'manual',
        'placement' : 'top',
        'content' : 'No garment selected.'
      });
      $addGarment.popover('disable');

      $garmentType.on('change', function() {
        // Only wash and fold items can be rushed
        var garment = $(this).val();
        if ($.inArray('wash and fold', GARMENTS[garment].services) > -1) {
          $rushOrder.prop('checked', false);
          $rushOrder.prop('disabled', false);
          $rushOrderLabel.removeClass('disabled');
          $rushOrderLabel.removeClass('active');
          $rushOrder.next().html('No');
          $rushOrderLabel.popover('disable');
        } else {
          $rushOrder.prop('checked', false);
          $rushOrder.prop('disabled', true);
          $rushOrderLabel.addClass('disabled');
          $rushOrder.next().html('No');
          $rushOrderLabel.popover('enable');
        }
      });

      // TODO: Refactor this into an AppView to preserve the local scope
      //       and events of $el.
      $addGarment.on('click', function(event) {
        event.preventDefault();
        var garmentType = $garmentType.val();

        $addGarment.popover('hide').popover('disable');
        if (!garmentType) {
          $addGarment.popover('enable').popover('show');
          return;
        }

        var garmentQuantity = parseInt($garmentQuantity.val());
        var garmentDescriptor = GARMENTS[garmentType].descriptor;
        var garmentCost = parseFloat(GARMENTS[garmentType].price);
        var isRush = $rushOrder.prop('checked');
        var hangDry = $hangDry.prop('checked');

        self.addGarment.bind(self)({
          garment_type: garmentType.toTitleCase(),
          cost: garmentCost,
          quantity: garmentQuantity,
          is_rush: isRush,
          hang_dry: hangDry,
          descriptor: garmentDescriptor,
          total_cost: garmentCost * garmentQuantity,
        });
      });

      $clearAll.on('click', function(event) {
        event.preventDefault();
        self.removeAll.bind(self)();
        self.unrenderAll.bind(self)();
        self.updateTotal();
      });

      // Make the toggle checkboxes change from "No" to "Yes"
      $toggleBoxes.on('change', function(event) {
        event.preventDefault();
        var $this = $(this);
        var newState = 'No';

        if ($this.prop('disabled')) {
          return;
        }

        if ($this.prop('checked')) {
          newState = 'Yes';
          $this.parent().addClass('active');
        } else {
          newState = 'No';
          $this.parent().removeClass('active');
        }
        $this.next().html(newState);
      });
    },

    updateTotal: function() {
      var totalCost = this.collection.sumCosts();
      // Check if there is at least one item that is being rushed
      var willRush = false;
      _.each(this.collection.models, function(garment) {
        if (garment.get('is_rush')) {
          willRush = true;
        }
      });
      if (willRush) {
        totalCost += RUSH_FEE;
        $('#rush-fee').html('$' + RUSH_FEE.toFixed(2));
      } else {
        $('#rush-fee').html('$0.00');
      }
      $('#grand-total').html('$' + totalCost.toFixed(2));
    },

    render: function() {
      var self = this;
      _(this.collection.models).each(function(garment){
        self.appendGarment(garment);
      }, this);
    },

    unrenderAll: function() {
      this.$el.html('');
    },

    addGarment: function(params) {
      this.collection.create(new GarmentModel(params));
      this.updateTotal();
    },

    appendGarment: function(garment) {
      var garmentView = new GarmentView({
        model: garment
      });
      var garmentEl = garmentView.render().$el;
      $('#calculation-table', this.el).append(garmentEl);
    },

    removeAll: function() {
      // This is done instead of this.collection.reset();
      // because we also need to delete the models in localStorage
      _.chain(this.collection.models).clone().each(function(model){
        model.destroy();
      });
    }
  });

  $('#popover-disclaimer').popover({
    'trigger' : 'hover click',
    'placement' : 'right',
    'content' : 'For informational purposes only; check rinse.com for the most updated prices.',
    'html' : true
  });

  var tableView = new TableView();

});

// Decimal rounding from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
(function() {
  /**
   * Decimal adjustment of a number.
   *
   * @param {String}  type  The type of adjustment.
   * @param {Number}  value The number.
   * @param {Integer} exp   The exponent (the 10 logarithm of the adjustment base).
   * @returns {Number} The adjusted value.
   */
  function decimalAdjust(type, value, exp) {
    // If the exp is undefined or zero...
    if (typeof exp === 'undefined' || +exp === 0) {
      return Math[type](value);
    }
    value = +value;
    exp = +exp;
    // If the value is not a number or the exp is not an integer...
    if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0)) {
      return NaN;
    }
    // Shift
    value = value.toString().split('e');
    value = Math[type](+(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp)));
    // Shift back
    value = value.toString().split('e');
    return +(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp));
  }

  // Decimal round
  if (!Math.round10) {
    Math.round10 = function(value, exp) {
      return decimalAdjust('round', value, exp);
    };
  }
  // Decimal floor
  if (!Math.floor10) {
    Math.floor10 = function(value, exp) {
      return decimalAdjust('floor', value, exp);
    };
  }
  // Decimal ceil
  if (!Math.ceil10) {
    Math.ceil10 = function(value, exp) {
      return decimalAdjust('ceil', value, exp);
    };
  }
})();

/*
  * To Title Case 2.1 – http://individed.com/code/to-title-case/
  * Copyright © 2008–2013 David Gouch. Licensed under the MIT License.
 */

String.prototype.toTitleCase = function(){
  var smallWords = /^(a|an|and|as|at|but|by|en|for|if|in|nor|of|on|or|per|the|to|vs?\.?|via)$/i;

  return this.replace(/[A-Za-z0-9\u00C0-\u00FF]+[^\s-]*/g, function(match, index, title){
    if (index > 0 && index + match.length !== title.length &&
      match.search(smallWords) > -1 && title.charAt(index - 2) !== ":" &&
      (title.charAt(index + match.length) !== '-' || title.charAt(index - 1) === '-') &&
      title.charAt(index - 1).search(/[^\s-]/) < 0) {
      return match.toLowerCase();
    }

    if (match.substr(1).search(/[A-Z]|\../) > -1) {
      return match;
    }

    return match.charAt(0).toUpperCase() + match.substr(1);
  });
};
<|endoftext|>


<commit_before>import standardSqlFormatter from "xr/sqlFormatter/standardSqlFormatter";

describe("standardSqlFormatter", function() {
    it("formats simple SELECT query", function() {
        const result = standardSqlFormatter.format("SELECT count(*),Column1 FROM Table1;");
        expect(result).toBe(
            "SELECT\n" +
            "  count(*),\n" +
            "  Column1\n" +
            "FROM\n" +
            "  Table1;\n"
        );
    });

    it("formats SELECT query with WHERE, GROUP/ORDER BY", function() {
        const result = standardSqlFormatter.format(
            "SELECT count(*),'Column1','Testing', 'Testing Three' FROM Table1 WHERE Column1 = 'testing' " +
            "AND ( (Column2 = Column3 OR Column4 >= NOW()) ) GROUP BY Column1 ORDER BY Column3 DESC;"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  count(*),\n" +
            "  'Column1',\n" +
            "  'Testing',\n" +
            "  'Testing Three'\n" +
            "FROM\n" +
            "  Table1\n" +
            "WHERE\n" +
            "  Column1 = 'testing'\n" +
            "  AND (\n" +
            "    (\n" +
            "      Column2 = Column3\n" +
            "      OR Column4 >= NOW()\n" +
            "    )\n" +
            "  )\n" +
            "GROUP BY\n" +
            "  Column1\n" +
            "ORDER BY\n" +
            "  Column3 DESC;\n"
        );
    });

    it("formats LIMIT and OFFSET", function() {
        const result = standardSqlFormatter.format(
            "SELECT * FROM Table1 LIMIT 5,10 OFFSET 20;"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  *\n" +
            "FROM\n" +
            "  Table1\n" +
            "LIMIT\n" +
            "  5, 10 OFFSET 20;\n"
        );
    });

    it("formats SELECT query with SELECT query inside it", function() {
        const result = standardSqlFormatter.format(
            "SELECT *, SUM(*) AS sum FROM (SELECT * FROM Posts LIMIT 30) GROUP BY Category"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  *,\n" +
            "  SUM(*) AS sum\n" +
            "FROM\n" +
            "  (\n" +
            "    SELECT\n" +
            "      *\n" +
            "    FROM\n" +
            "      Posts\n" +
            "    LIMIT\n" +
            "      30\n" +
            "  )\n" +
            "GROUP BY\n" +
            "  Category\n"
        );
    });

    it("formats SELECT query with INNER JOIN", function() {
        const result = standardSqlFormatter.format(
            "SELECT customer_id.from, COUNT(order_id) AS total FROM customers " +
            "INNER JOIN orders ON customers.customer_id = orders.customer_id;"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  customer_id.from,\n" +
            "  COUNT(order_id) AS total\n" +
            "FROM\n" +
            "  customers\n" +
            "  INNER JOIN orders ON customers.customer_id = orders.customer_id;\n"
        );
    });

    it("formats SELECT query with different comments", function() {
        const result = standardSqlFormatter.format(
            "SELECT\n" +
            "/* This is a block comment\n" +
            "*/\n" +
            "* FROM\n" +
            "-- This is another comment\n" +
            "MyTable # One final comment\n" +
            "WHERE 1 = 2;"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  /* This is a block comment\n" +
            "  */\n" +
            "  *\n" +
            "FROM\n" +
            "  -- This is another comment\n" +
            "  MyTable # One final comment\n" +
            "WHERE\n" +
            "  1 = 2;\n"
        );
    });

    it("formats SELECT query with different variable types", function() {
        const result = standardSqlFormatter.format(
            "SELECT @variable, @'variable name';"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  @variable,\n" +
            "  @'variable name';\n"
        );
    });

    it("formats simple INSERT query", function() {
        const result = standardSqlFormatter.format(
            "INSERT INTO Customers (ID, MoneyBalance, Address, City) VALUES (12,-123.4, 'Skagen 2111','Stavanger');"
        );
        expect(result).toBe(
            "INSERT INTO Customers (ID, MoneyBalance, Address, City)\n" +
            "VALUES\n" +
            "  (\n" +
            "    12, -123.4, 'Skagen 2111', 'Stavanger'\n" +
            "  );\n"
        );
    });

    it("formats long INSERT query including SELECT, IF and INNER JOIN parts", function() {
        const result = standardSqlFormatter.format(
            "INSERT INTO `PREFIX_specific_price` (`id_product`, `id_shop`, `id_currency`, `id_country`, `id_group`, `priority`, " +
            "`price`, `from_quantity`, `reduction`,   `reduction_type`, `from`, `to`) (" +
            "SELECT dq.`id_product`, 1, 1,0,1, 0, 0.00, dq.`quantity`, IF(dq.`id_discount_type` = 2, dq.`value`, dq.`value` / 100)," +
            "IF (dq.`id_discount_type` = 2, 'amount', 'percentage'), '0000-00-00 00:00:00', '0000-00-00 00:00:00' " +
            "FROM `PREFIX_discount_quantity` dq INNER JOIN `PREFIX_product` p ON (p.`id_product` = dq.`id_product`));"
        );
        expect(result).toBe(
            "INSERT INTO `PREFIX_specific_price` (\n" +
            "  `id_product`, `id_shop`, `id_currency`,\n" +
            "  `id_country`, `id_group`, `priority`,\n" +
            "  `price`, `from_quantity`, `reduction`,\n" +
            "  `reduction_type`, `from`, `to`\n" +
            ") (\n" +
            "  SELECT\n" +
            "    dq.`id_product`,\n" +
            "    1,\n" +
            "    1,\n" +
            "    0,\n" +
            "    1,\n" +
            "    0,\n" +
            "    0.00,\n" +
            "    dq.`quantity`,\n" +
            "    IF(\n" +
            "      dq.`id_discount_type` = 2, dq.`value`,\n" +
            "      dq.`value` / 100\n" +
            "    ),\n" +
            "    IF (\n" +
            "      dq.`id_discount_type` = 2, 'amount',\n" +
            "      'percentage'\n" +
            "    ),\n" +
            "    '0000-00-00 00:00:00',\n" +
            "    '0000-00-00 00:00:00'\n" +
            "  FROM\n" +
            "    `PREFIX_discount_quantity` dq\n" +
            "    INNER JOIN `PREFIX_product` p ON (p.`id_product` = dq.`id_product`)\n" +
            ");\n"
        );
    });

    it("formats simple UPDATE query", function() {
        const result = standardSqlFormatter.format(
            "uPDATE Customers set ContactName='Alfred Schmidt', City='Hamburg' WHERE CustomerName='Alfreds Futterkiste';"
        );
        expect(result).toBe(
            "uPDATE\n" +
            "  Customers\n" +
            "set\n" +
            "  ContactName = 'Alfred Schmidt',\n" +
            "  City = 'Hamburg'\n" +
            "WHERE\n" +
            "  CustomerName = 'Alfreds Futterkiste';\n"
        );
    });

    it("formats simple DELETE query", function() {
        const result = standardSqlFormatter.format(
            "DELETE FROM Customers WHERE CustomerName='Alfred' AND Phone=5002132;"
        );
        expect(result).toBe(
            "DELETE FROM\n" +
            "  Customers\n" +
            "WHERE\n" +
            "  CustomerName = 'Alfred'\n" +
            "  AND Phone = 5002132;\n"
        );
    });

    it("formats simple DROP query", function() {
        const result = standardSqlFormatter.format(
            "DROP TABLE IF EXISTS admin_role;"
        );
        expect(result).toBe(
            "DROP\n" +
            "  TABLE IF EXISTS admin_role;\n"
        );
    });

    it("formats uncomplete query", function() {
        const result = standardSqlFormatter.format("SELECT count(");
        expect(result).toBe(
            "SELECT\n" +
            "  count(\n"
        );
    });

    it("formats query that ends with open comment", function() {
        const result = standardSqlFormatter.format("SELECT count(*)\n/*Comment");
        expect(result).toBe(
            "SELECT\n" +
            "  count(*)\n" +
            "  /*Comment\n"
        );
    });

    it("formats UPDATE query with AS part", function() {
        const result = standardSqlFormatter.format(
            "UPDATE customers SET totalorders = ordersummary.total  FROM ( SELECT * FROM bank) As ordersummary"
        );
        expect(result).toBe(
            "UPDATE\n" +
            "  customers\n" +
            "SET\n" +
            "  totalorders = ordersummary.total\n" +
            "FROM\n" +
            "  (\n" +
            "    SELECT\n" +
            "      *\n" +
            "    FROM\n" +
            "      bank\n" +
            "  ) As ordersummary\n"
        );
    });

    it("formats top-level and newline multi-word reserved words with inconsistent spacing", function() {
        const result = standardSqlFormatter.format("SELECT * FROM foo LEFT \t OUTER  \n JOIN bar ORDER \n BY blah");
        expect(result).toBe(
            "SELECT\n" +
            "  *\n" +
            "FROM\n" +
            "  foo\n" +
            "  LEFT OUTER JOIN bar\n" +
            "ORDER BY\n" +
            "  blah\n"
        );
    });

    it("formats single-char operators", function() {
        expect(standardSqlFormatter.format("foo = bar")).toBe("foo = bar\n");
        expect(standardSqlFormatter.format("foo < bar")).toBe("foo < bar\n");
        expect(standardSqlFormatter.format("foo > bar")).toBe("foo > bar\n");
        expect(standardSqlFormatter.format("foo + bar")).toBe("foo + bar\n");
        expect(standardSqlFormatter.format("foo - bar")).toBe("foo - bar\n");
        expect(standardSqlFormatter.format("foo * bar")).toBe("foo * bar\n");
        expect(standardSqlFormatter.format("foo / bar")).toBe("foo / bar\n");
        expect(standardSqlFormatter.format("foo % bar")).toBe("foo % bar\n");
    });

    it("formats multi-char operators", function() {
        expect(standardSqlFormatter.format("foo != bar")).toBe("foo != bar\n");
        expect(standardSqlFormatter.format("foo <> bar")).toBe("foo <> bar\n");
        expect(standardSqlFormatter.format("foo == bar")).toBe("foo == bar\n"); // N1QL
        expect(standardSqlFormatter.format("foo || bar")).toBe("foo || bar\n"); // Oracle, Postgres, N1QL string concat

        expect(standardSqlFormatter.format("foo <= bar")).toBe("foo <= bar\n");
        expect(standardSqlFormatter.format("foo >= bar")).toBe("foo >= bar\n");

        expect(standardSqlFormatter.format("foo !< bar")).toBe("foo !< bar\n");
        expect(standardSqlFormatter.format("foo !> bar")).toBe("foo !> bar\n");
    });

    it("formats logical operators", function() {
        expect(standardSqlFormatter.format("foo ALL bar")).toBe("foo ALL bar\n");
        expect(standardSqlFormatter.format("foo = ANY (1, 2, 3)")).toBe("foo = ANY (1, 2, 3)\n");
        expect(standardSqlFormatter.format("EXISTS bar")).toBe("EXISTS bar\n");
        expect(standardSqlFormatter.format("foo IN (1, 2, 3)")).toBe("foo IN (1, 2, 3)\n");
        expect(standardSqlFormatter.format("foo LIKE 'hello%'")).toBe("foo LIKE 'hello%'\n");
        expect(standardSqlFormatter.format("foo IS NULL")).toBe("foo IS NULL\n");
        expect(standardSqlFormatter.format("UNIQUE foo")).toBe("UNIQUE foo\n");
    });

    it("formats AND/OR operators", function() {
        expect(standardSqlFormatter.format("foo BETWEEN bar AND baz")).toBe("foo BETWEEN bar\nAND baz\n");
        expect(standardSqlFormatter.format("foo AND bar")).toBe("foo\nAND bar\n");
        expect(standardSqlFormatter.format("foo OR bar")).toBe("foo\nOR bar\n");
    });

    it("recognizes strings", function() {
        expect(standardSqlFormatter.format("\"foo JOIN bar\"")).toBe("\"foo JOIN bar\"\n");
        expect(standardSqlFormatter.format("'foo JOIN bar'")).toBe("'foo JOIN bar'\n");
        expect(standardSqlFormatter.format("`foo JOIN bar`")).toBe("`foo JOIN bar`\n");
        expect(standardSqlFormatter.format("[foo JOIN bar]")).toBe("[foo JOIN bar]\n");
    });

    it("recognizes escaped strings", function() {
        expect(standardSqlFormatter.format("\"foo \\\" JOIN bar\"")).toBe("\"foo \\\" JOIN bar\"\n");
        expect(standardSqlFormatter.format("'foo \\' JOIN bar'")).toBe("'foo \\' JOIN bar'\n");
        expect(standardSqlFormatter.format("`foo `` JOIN bar`")).toBe("`foo `` JOIN bar`\n");
        expect(standardSqlFormatter.format("[foo ]] JOIN bar]")).toBe("[foo ]] JOIN bar]\n");
    });
});
<commit_msg>XR-3710 Test group/order by in just one test<commit_after>import standardSqlFormatter from "xr/sqlFormatter/standardSqlFormatter";

describe("standardSqlFormatter", function() {
    it("formats simple SELECT query", function() {
        const result = standardSqlFormatter.format("SELECT count(*),Column1 FROM Table1;");
        expect(result).toBe(
            "SELECT\n" +
            "  count(*),\n" +
            "  Column1\n" +
            "FROM\n" +
            "  Table1;\n"
        );
    });

    it("formats SELECT query with complex WHERE", function() {
        const result = standardSqlFormatter.format(
            "SELECT count(*),'Column1','Testing', 'Testing Three' FROM Table1 WHERE Column1 = 'testing' " +
            "AND ( (Column2 = Column3 OR Column4 >= NOW()) );"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  count(*),\n" +
            "  'Column1',\n" +
            "  'Testing',\n" +
            "  'Testing Three'\n" +
            "FROM\n" +
            "  Table1\n" +
            "WHERE\n" +
            "  Column1 = 'testing'\n" +
            "  AND (\n" +
            "    (\n" +
            "      Column2 = Column3\n" +
            "      OR Column4 >= NOW()\n" +
            "    )\n" +
            "  );\n"
        );
    });

    it("formats GROUP BY and ORDER BY", function() {
        const result = standardSqlFormatter.format(
            "SELECT * FROM foo GROUP BY some_column ORDER BY other_column;"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  *\n" +
            "FROM\n" +
            "  foo\n" +
            "GROUP BY\n" +
            "  some_column\n" +
            "ORDER BY\n" +
            "  other_column;\n"
        );
    });

    it("formats LIMIT and OFFSET", function() {
        const result = standardSqlFormatter.format(
            "SELECT * FROM Table1 LIMIT 5,10 OFFSET 20;"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  *\n" +
            "FROM\n" +
            "  Table1\n" +
            "LIMIT\n" +
            "  5, 10 OFFSET 20;\n"
        );
    });

    it("formats SELECT query with SELECT query inside it", function() {
        const result = standardSqlFormatter.format(
            "SELECT *, SUM(*) AS sum FROM (SELECT * FROM Posts LIMIT 30) WHERE a > b"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  *,\n" +
            "  SUM(*) AS sum\n" +
            "FROM\n" +
            "  (\n" +
            "    SELECT\n" +
            "      *\n" +
            "    FROM\n" +
            "      Posts\n" +
            "    LIMIT\n" +
            "      30\n" +
            "  )\n" +
            "WHERE\n" +
            "  a > b\n"
        );
    });

    it("formats SELECT query with INNER JOIN", function() {
        const result = standardSqlFormatter.format(
            "SELECT customer_id.from, COUNT(order_id) AS total FROM customers " +
            "INNER JOIN orders ON customers.customer_id = orders.customer_id;"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  customer_id.from,\n" +
            "  COUNT(order_id) AS total\n" +
            "FROM\n" +
            "  customers\n" +
            "  INNER JOIN orders ON customers.customer_id = orders.customer_id;\n"
        );
    });

    it("formats SELECT query with different comments", function() {
        const result = standardSqlFormatter.format(
            "SELECT\n" +
            "/* This is a block comment\n" +
            "*/\n" +
            "* FROM\n" +
            "-- This is another comment\n" +
            "MyTable # One final comment\n" +
            "WHERE 1 = 2;"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  /* This is a block comment\n" +
            "  */\n" +
            "  *\n" +
            "FROM\n" +
            "  -- This is another comment\n" +
            "  MyTable # One final comment\n" +
            "WHERE\n" +
            "  1 = 2;\n"
        );
    });

    it("formats SELECT query with different variable types", function() {
        const result = standardSqlFormatter.format(
            "SELECT @variable, @'variable name';"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  @variable,\n" +
            "  @'variable name';\n"
        );
    });

    it("formats simple INSERT query", function() {
        const result = standardSqlFormatter.format(
            "INSERT INTO Customers (ID, MoneyBalance, Address, City) VALUES (12,-123.4, 'Skagen 2111','Stavanger');"
        );
        expect(result).toBe(
            "INSERT INTO Customers (ID, MoneyBalance, Address, City)\n" +
            "VALUES\n" +
            "  (\n" +
            "    12, -123.4, 'Skagen 2111', 'Stavanger'\n" +
            "  );\n"
        );
    });

    it("formats long INSERT query including SELECT, IF and INNER JOIN parts", function() {
        const result = standardSqlFormatter.format(
            "INSERT INTO `PREFIX_specific_price` (`id_product`, `id_shop`, `id_currency`, `id_country`, `id_group`, `priority`, " +
            "`price`, `from_quantity`, `reduction`,   `reduction_type`, `from`, `to`) (" +
            "SELECT dq.`id_product`, 1, 1,0,1, 0, 0.00, dq.`quantity`, IF(dq.`id_discount_type` = 2, dq.`value`, dq.`value` / 100)," +
            "IF (dq.`id_discount_type` = 2, 'amount', 'percentage'), '0000-00-00 00:00:00', '0000-00-00 00:00:00' " +
            "FROM `PREFIX_discount_quantity` dq INNER JOIN `PREFIX_product` p ON (p.`id_product` = dq.`id_product`));"
        );
        expect(result).toBe(
            "INSERT INTO `PREFIX_specific_price` (\n" +
            "  `id_product`, `id_shop`, `id_currency`,\n" +
            "  `id_country`, `id_group`, `priority`,\n" +
            "  `price`, `from_quantity`, `reduction`,\n" +
            "  `reduction_type`, `from`, `to`\n" +
            ") (\n" +
            "  SELECT\n" +
            "    dq.`id_product`,\n" +
            "    1,\n" +
            "    1,\n" +
            "    0,\n" +
            "    1,\n" +
            "    0,\n" +
            "    0.00,\n" +
            "    dq.`quantity`,\n" +
            "    IF(\n" +
            "      dq.`id_discount_type` = 2, dq.`value`,\n" +
            "      dq.`value` / 100\n" +
            "    ),\n" +
            "    IF (\n" +
            "      dq.`id_discount_type` = 2, 'amount',\n" +
            "      'percentage'\n" +
            "    ),\n" +
            "    '0000-00-00 00:00:00',\n" +
            "    '0000-00-00 00:00:00'\n" +
            "  FROM\n" +
            "    `PREFIX_discount_quantity` dq\n" +
            "    INNER JOIN `PREFIX_product` p ON (p.`id_product` = dq.`id_product`)\n" +
            ");\n"
        );
    });

    it("formats simple UPDATE query", function() {
        const result = standardSqlFormatter.format(
            "uPDATE Customers set ContactName='Alfred Schmidt', City='Hamburg' WHERE CustomerName='Alfreds Futterkiste';"
        );
        expect(result).toBe(
            "uPDATE\n" +
            "  Customers\n" +
            "set\n" +
            "  ContactName = 'Alfred Schmidt',\n" +
            "  City = 'Hamburg'\n" +
            "WHERE\n" +
            "  CustomerName = 'Alfreds Futterkiste';\n"
        );
    });

    it("formats simple DELETE query", function() {
        const result = standardSqlFormatter.format(
            "DELETE FROM Customers WHERE CustomerName='Alfred' AND Phone=5002132;"
        );
        expect(result).toBe(
            "DELETE FROM\n" +
            "  Customers\n" +
            "WHERE\n" +
            "  CustomerName = 'Alfred'\n" +
            "  AND Phone = 5002132;\n"
        );
    });

    it("formats simple DROP query", function() {
        const result = standardSqlFormatter.format(
            "DROP TABLE IF EXISTS admin_role;"
        );
        expect(result).toBe(
            "DROP\n" +
            "  TABLE IF EXISTS admin_role;\n"
        );
    });

    it("formats uncomplete query", function() {
        const result = standardSqlFormatter.format("SELECT count(");
        expect(result).toBe(
            "SELECT\n" +
            "  count(\n"
        );
    });

    it("formats query that ends with open comment", function() {
        const result = standardSqlFormatter.format("SELECT count(*)\n/*Comment");
        expect(result).toBe(
            "SELECT\n" +
            "  count(*)\n" +
            "  /*Comment\n"
        );
    });

    it("formats UPDATE query with AS part", function() {
        const result = standardSqlFormatter.format(
            "UPDATE customers SET totalorders = ordersummary.total  FROM ( SELECT * FROM bank) As ordersummary"
        );
        expect(result).toBe(
            "UPDATE\n" +
            "  customers\n" +
            "SET\n" +
            "  totalorders = ordersummary.total\n" +
            "FROM\n" +
            "  (\n" +
            "    SELECT\n" +
            "      *\n" +
            "    FROM\n" +
            "      bank\n" +
            "  ) As ordersummary\n"
        );
    });

    it("formats top-level and newline multi-word reserved words with inconsistent spacing", function() {
        const result = standardSqlFormatter.format("SELECT * FROM foo LEFT \t OUTER  \n JOIN bar ORDER \n BY blah");
        expect(result).toBe(
            "SELECT\n" +
            "  *\n" +
            "FROM\n" +
            "  foo\n" +
            "  LEFT OUTER JOIN bar\n" +
            "ORDER BY\n" +
            "  blah\n"
        );
    });

    it("formats single-char operators", function() {
        expect(standardSqlFormatter.format("foo = bar")).toBe("foo = bar\n");
        expect(standardSqlFormatter.format("foo < bar")).toBe("foo < bar\n");
        expect(standardSqlFormatter.format("foo > bar")).toBe("foo > bar\n");
        expect(standardSqlFormatter.format("foo + bar")).toBe("foo + bar\n");
        expect(standardSqlFormatter.format("foo - bar")).toBe("foo - bar\n");
        expect(standardSqlFormatter.format("foo * bar")).toBe("foo * bar\n");
        expect(standardSqlFormatter.format("foo / bar")).toBe("foo / bar\n");
        expect(standardSqlFormatter.format("foo % bar")).toBe("foo % bar\n");
    });

    it("formats multi-char operators", function() {
        expect(standardSqlFormatter.format("foo != bar")).toBe("foo != bar\n");
        expect(standardSqlFormatter.format("foo <> bar")).toBe("foo <> bar\n");
        expect(standardSqlFormatter.format("foo == bar")).toBe("foo == bar\n"); // N1QL
        expect(standardSqlFormatter.format("foo || bar")).toBe("foo || bar\n"); // Oracle, Postgres, N1QL string concat

        expect(standardSqlFormatter.format("foo <= bar")).toBe("foo <= bar\n");
        expect(standardSqlFormatter.format("foo >= bar")).toBe("foo >= bar\n");

        expect(standardSqlFormatter.format("foo !< bar")).toBe("foo !< bar\n");
        expect(standardSqlFormatter.format("foo !> bar")).toBe("foo !> bar\n");
    });

    it("formats logical operators", function() {
        expect(standardSqlFormatter.format("foo ALL bar")).toBe("foo ALL bar\n");
        expect(standardSqlFormatter.format("foo = ANY (1, 2, 3)")).toBe("foo = ANY (1, 2, 3)\n");
        expect(standardSqlFormatter.format("EXISTS bar")).toBe("EXISTS bar\n");
        expect(standardSqlFormatter.format("foo IN (1, 2, 3)")).toBe("foo IN (1, 2, 3)\n");
        expect(standardSqlFormatter.format("foo LIKE 'hello%'")).toBe("foo LIKE 'hello%'\n");
        expect(standardSqlFormatter.format("foo IS NULL")).toBe("foo IS NULL\n");
        expect(standardSqlFormatter.format("UNIQUE foo")).toBe("UNIQUE foo\n");
    });

    it("formats AND/OR operators", function() {
        expect(standardSqlFormatter.format("foo BETWEEN bar AND baz")).toBe("foo BETWEEN bar\nAND baz\n");
        expect(standardSqlFormatter.format("foo AND bar")).toBe("foo\nAND bar\n");
        expect(standardSqlFormatter.format("foo OR bar")).toBe("foo\nOR bar\n");
    });

    it("recognizes strings", function() {
        expect(standardSqlFormatter.format("\"foo JOIN bar\"")).toBe("\"foo JOIN bar\"\n");
        expect(standardSqlFormatter.format("'foo JOIN bar'")).toBe("'foo JOIN bar'\n");
        expect(standardSqlFormatter.format("`foo JOIN bar`")).toBe("`foo JOIN bar`\n");
        expect(standardSqlFormatter.format("[foo JOIN bar]")).toBe("[foo JOIN bar]\n");
    });

    it("recognizes escaped strings", function() {
        expect(standardSqlFormatter.format("\"foo \\\" JOIN bar\"")).toBe("\"foo \\\" JOIN bar\"\n");
        expect(standardSqlFormatter.format("'foo \\' JOIN bar'")).toBe("'foo \\' JOIN bar'\n");
        expect(standardSqlFormatter.format("`foo `` JOIN bar`")).toBe("`foo `` JOIN bar`\n");
        expect(standardSqlFormatter.format("[foo ]] JOIN bar]")).toBe("[foo ]] JOIN bar]\n");
    });
});
<|endoftext|>


<commit_before>import './App.css';
import '../node_modules/react-vis/dist/style.css';

import React, { Component } from 'react';
import {
  FlexibleXYPlot,
  HorizontalGridLines,
  LineSeries,
  VerticalGridLines,
  XAxis,
  YAxis,
} from 'react-vis';

import { fetchIssues } from './github.js';
import {
  countByCreationDay,
  buildSeriesDataFromDatesWithValues,
} from './series.js';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = { issues: null };
  }

  componentWillMount() {
    this.fetchNextIssues();
  }

  fetchNextIssues(
    url = `https://api.github.com/repos/${process.env
      .REACT_APP_REPOSITORY}/issues`
  ) {
    fetchIssues(url)
      .then(result => {
        const { issues, next } = result;
        this.setState((prevState, props) => {
          const prevIssues = prevState.issues;
          const totalIssues = prevIssues ? prevIssues.concat(issues) : issues;
          return {
            issues: totalIssues,
          };
        });
        if (next) {
          this.fetchNextIssues(next);
        }
      })
      .catch(error => console.error(error));
  }

  render() {
    const repositoryLink = `https://www.github.com/${process.env
      .REACT_APP_REPOSITORY}`;
    return (

issue-vis

Data source

        {process.env.REACT_APP_REPOSITORY}

Opened issues

        {this.renderOpenedIssues()}

    );
  }

  renderOpenedIssues() {
    const issues = this.state.issues;
    if (issues) {
      const issuesByCreationDay = buildSeriesDataFromDatesWithValues(
        countByCreationDay(issues)
      );
      return (
        <FlexibleXYPlot height={300} xType="time">
          <VerticalGridLines />
          <HorizontalGridLines />
          <XAxis />
          <YAxis />
          <LineSeries data={issuesByCreationDay} />
        </FlexibleXYPlot>
      );
    }
    return 'Fetching...';
  }
}

export default App;
<commit_msg>Extract timechart<commit_after>import './App.css';
import '../node_modules/react-vis/dist/style.css';

import React, { Component } from 'react';

import { fetchIssues } from './github.js';
import {
  countByCreationDay,
  buildSeriesDataFromDatesWithValues,
} from './series.js';
import { TimeChart } from './timechart.js';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = { issues: null };
  }

  componentWillMount() {
    this.fetchNextIssues();
  }

  fetchNextIssues(
    url = `https://api.github.com/repos/${process.env
      .REACT_APP_REPOSITORY}/issues`
  ) {
    fetchIssues(url)
      .then(result => {
        const { issues, next } = result;
        this.setState((prevState, props) => {
          const prevIssues = prevState.issues;
          const totalIssues = prevIssues ? prevIssues.concat(issues) : issues;
          return {
            issues: totalIssues,
          };
        });
        if (next) {
          this.fetchNextIssues(next);
        }
      })
      .catch(error => console.error(error));
  }

  render() {
    const repositoryLink = `https://www.github.com/${process.env
      .REACT_APP_REPOSITORY}`;
    return (

issue-vis

Data source

        {process.env.REACT_APP_REPOSITORY}

Opened issues

        {this.renderOpenedIssues()}

    );
  }

  renderOpenedIssues() {
    const issues = this.state.issues;
    if (issues) {
      const issuesByCreationDay = buildSeriesDataFromDatesWithValues(
        countByCreationDay(issues)
      );
      return <TimeChart data={issuesByCreationDay} />;
    }
    return 'Fetching...';
  }
}

export default App;
<|endoftext|>


<commit_before>describe('adming configure node', function() {
  it('should configure node en internalization', function() {
    browser.setLocation('admin/content');
    element(by.model('GLTranslate.indirect.appLanguage')).element(by.xpath(".//*[text()='English']")).click();
    expect(element(by.model('admin.node.header_title_homepage')).clear().sendKeys('TEXT1_EN'));
    expect(element(by.model('admin.node.presentation')).clear().sendKeys('TEXT2_EN'));
    element(by.css('[data-ng-click="updateNode(admin.node)"]')).click();
  });

  it('should configure node it internalization', function() {
    browser.setLocation('admin/content');
    element(by.model('GLTranslate.indirect.appLanguage')).element(by.xpath(".//*[text()='Italiano']")).click();
    expect(element(by.model('admin.node.header_title_homepage')).clear().sendKeys('TEXT1_IT'));
    expect(element(by.model('admin.node.presentation')).clear().sendKeys('TEXT2_IT'));
    element(by.css('[data-ng-click="updateNode(admin.node)"]')).click();
  });

  it('should configure node advanced settings', function() {
    browser.setLocation('admin/advanced_settings');

    // enable all receivers to postpone and delete tips
    element(by.model('admin.node.can_postpone_expiration')).click();
    element(by.model('admin.node.can_delete_submission')).click();

    element(by.css('[data-ng-click="updateNode(admin.node)"]')).click();
  });
});
<commit_msg>Fix tests switching the language back to english ad the end of the node configuration<commit_after>describe('adming configure node', function() {
  it('should configure node en internalization', function() {
    browser.setLocation('admin/content');
    element(by.model('GLTranslate.indirect.appLanguage')).element(by.xpath(".//*[text()='English']")).click();
    expect(element(by.model('admin.node.header_title_homepage')).clear().sendKeys('TEXT1_EN'));
    expect(element(by.model('admin.node.presentation')).clear().sendKeys('TEXT2_EN'));
    element(by.css('[data-ng-click="updateNode(admin.node)"]')).click();
  });

  it('should configure node it internalization', function() {
    browser.setLocation('admin/content');
    element(by.model('GLTranslate.indirect.appLanguage')).element(by.xpath(".//*[text()='Italiano']")).click();
    expect(element(by.model('admin.node.header_title_homepage')).clear().sendKeys('TEXT1_IT'));
    expect(element(by.model('admin.node.presentation')).clear().sendKeys('TEXT2_IT'));
    element(by.css('[data-ng-click="updateNode(admin.node)"]')).click();

    element(by.model('GLTranslate.indirect.appLanguage')).element(by.xpath(".//*[text()='English']")).click();
  });

  it('should configure node advanced settings', function() {
    browser.setLocation('admin/advanced_settings');

    // enable all receivers to postpone and delete tips
    element(by.model('admin.node.can_postpone_expiration')).click();
    element(by.model('admin.node.can_delete_submission')).click();

    element(by.css('[data-ng-click="updateNode(admin.node)"]')).click();
  });
});
<|endoftext|>


<commit_before>// this file must be run in gnode or similar, to have generators support

var test = require('gap')
  , localtunnel = require('localtunnel')

  , utils = require('./utils')
  , server = utils.createServer()
  , browser = require('co-wd').remote('ondemand.saucelabs.com', 80)
  , tests = require('bulk-require')(__dirname, [ '*-test.js' ])
  , tunnel

require('co')(function* () {
  var browsers = yield require('./utils/saucelabs')

  // TODO: run this in parallel
  browsers.forEach(function (browserConfig) {
    test('setup (' + browserConfig.name + ' ' + browserConfig.version + ')', function* () {
      yield browser.init(browserConfig)
      yield server.listen.bind(server, 0)
      tunnel = yield localtunnel.bind(localtunnel, server.address().port)
      server.url = tunnel.url
    })

    Object.keys(tests).forEach(function (key) {
      tests[key](server, browser)
    })

    test('teardown', function* () {
      yield browser.quit()
      tunnel.close()
      server.close()
    })
  })

})()
<commit_msg>set correct options in browser.init<commit_after>// this file must be run in gnode or similar, to have generators support

var test = require('gap')
  , localtunnel = require('localtunnel')

  , utils = require('./utils')
  , server = utils.createServer()
  , browser = require('co-wd').remote('ondemand.saucelabs.com', 80)
  , tests = require('bulk-require')(__dirname, [ '*-test.js' ])
  , tunnel
  , build = (new Date()).toJSON()

require('co')(function* () {
  var browsers = yield require('./utils/saucelabs')

  // TODO: run this in parallel
  browsers.forEach(function (browserConfig) {
    test('setup (' + browserConfig.name + ' ' + browserConfig.version + ')', function* () {
      yield browser.init({
          name: 'condor'
        , browserName: browserConfig.name
        , version: browserConfig.version
        , platform: browserConfig.platform
        , build: build
      })
      yield server.listen.bind(server, 0)
      tunnel = yield localtunnel.bind(localtunnel, server.address().port)
      server.url = tunnel.url
    })

    Object.keys(tests).forEach(function (key) {
      tests[key](server, browser)
    })

    test('teardown', function* () {
      yield browser.quit()
      tunnel.close()
      server.close()
    })
  })

})()
<|endoftext|>


<commit_before>"use strict";

class gsuiDrumrows {
	constructor() {
		const root = gsuiDrumrows.template.cloneNode( true ),
			reorder = new gsuiReorder( {
				rootElement: root,
				direction: "column",
				dataTransferType: "drumrow",
				itemSelector: ".gsuiDrumrow",
				handleSelector: ".gsuiDrumrow-grip",
				parentSelector: ".gsuiDrumrows",
				onchange: this._onreorderRows.bind( this ),
			} );

		this.rootElement = root;
		this._rows = new Map();
		this._lines = new Map();
		this._reorder = reorder;
		this._dragoverId =
		this._elDragover =
		this._elLinesParent = null;
		this._dispatch = GSUtils.dispatchEvent.bind( null, root, "gsuiDrumrows" );
		Object.seal( this );

		root.ondrop = this._ondropRows.bind( this );
		root.onclick = this._onclickRows.bind( this );
		root.ondragover = this._ondragoverRows.bind( this );
		root.ondragleave = this._ondragleaveRows.bind( this );
		root.onmousedown = this._onmousedownRows.bind( this );
		root.oncontextmenu = this._oncontextmenuRows.bind( this );
		root.onanimationend = this._onanimationendRows.bind( this );
	}

	// .........................................................................
	setLinesParent( el, childClass ) {
		this._elLinesParent = el;
		this._reorder.setShadowElement( el );
		this._reorder.setShadowChildClass( childClass );
	}
	setFontSize( fs ) {
		this.rootElement.style.fontSize =
		this._elLinesParent.style.fontSize = `${ fs }px`;
	}
	reorderDrumrows( obj ) {
		gsuiReorder.listReorder( this.rootElement, obj );
		gsuiReorder.listReorder( this._elLinesParent, obj );
	}
	playRow( id ) {
		const rect = document.createElement( "div" );

		rect.classList.add( "gsuiDrumrow-startCursor" );
		this._rows.get( id ).root.querySelector( ".gsuiDrumrow-waveWrap" ).append( rect );
	}
	stopRow( id ) {
		this._rows.get( id ).root.querySelectorAll( ".gsuiDrumrow-startCursor" )
			.forEach( el => el.remove() );
	}

	// .........................................................................
	add( id, elLine ) {
		const elRow = gsuiDrumrows.templateRow.cloneNode( true ),
			sliDetune = new gsuiSlider(),
			sliGain = new gsuiSlider(),
			html = {
				root: elRow,
				name: elRow.querySelector( ".gsuiDrumrow-name" ),
				gain: sliGain,
				detune: sliDetune,
			};

		elRow.dataset.id =
		elLine.dataset.id = id;
		sliDetune.options( { min: -12, max: 12, step: 1, value: 0, type: "linear-y", mousemoveSize: 400 } );
		sliGain.options( { min: 0, max: 1, step: .01, value: 1, type: "linear-y", mousemoveSize: 400 } );
		sliDetune.oninput = val => {
			this._namePrint( id, `pitch: ${ val > 0 ? `+${ val }` : val }` );
			this._dispatch( "liveChangeDrumrow", id, "detune", val );
		};
		sliGain.oninput = val => {
			this._namePrint( id, `gain: ${ val.toFixed( 2 ) }` );
			this._dispatch( "liveChangeDrumrow", id, "gain", val );
		};
		sliDetune.onchange = this._onchangeRowSlider.bind( this, id, "detune" );
		sliGain.onchange = this._onchangeRowSlider.bind( this, id, "gain" );
		sliDetune.oninputend = this._oninputendRowSlider.bind( this, id );
		sliGain.oninputend = this._oninputendRowSlider.bind( this, id );
		elRow.querySelector( ".gsuiDrumrow-detune" ).append( sliDetune.rootElement );
		elRow.querySelector( ".gsuiDrumrow-gain" ).append( sliGain.rootElement );
		this._rows.set( id, html );
		this._lines.set( id, elLine );
		this.rootElement.append( elRow );
		this._elLinesParent.append( elLine );
		sliDetune.attached();
		sliGain.attached();
	}
	remove( id ) {
		this._rows.get( id ).root.remove();
		this._lines.get( id ).remove();
		this._rows.delete( id );
		this._lines.delete( id );
	}
	change( id, prop, val ) {
		switch ( prop ) {
			case "name": this._changeName( id, val ); break;
			case "gain": this._changeGain( id, val ); break;
			case "order": this._changeOrder( id, val ); break;
			case "detune": this._changeDetune( id, val ); break;
			case "toggle": this._changeToggle( id, val ); break;
			case "pattern": this._changePattern( id, val ); break;
			case "duration": this._changeDuration( id, val ); break;
		}
	}
	_changeGain( id, val ) {
		this._rows.get( id ).gain.setValue( val );
	}
	_changeDetune( id, val ) {
		this._rows.get( id ).detune.setValue( val );
	}
	_changeName( id, name ) {
		const el = this._rows.get( id ).name;

		el.dataset.name =
		el.textContent = name;
	}
	_changeToggle( id, b ) {
		this._rows.get( id ).root.classList.toggle( "gsuiDrumrow-mute", !b );
		this._lines.get( id ).classList.toggle( "gsuiDrumrow-mute", !b );
	}
	_changeDuration( id, dur ) {
		this._rows.get( id ).root.querySelector( ".gsuiDrumrow-waveWrap" ).style.animationDuration = `${ dur * 2 }s`;
	}
	_changePattern( id, svg ) {
		const elWave = this._rows.get( id ).root.querySelector( ".gsuiDrumrow-waveWrap" );

		if ( elWave.firstChild ) {
			elWave.firstChild.remove();
		}
		if ( svg ) {
			svg.classList.add( "gsuiDrumrow-wave" );
			elWave.append( svg );
		}
	}
	_changeOrder( id, order ) {
		this._rows.get( id ).root.dataset.order =
		this._lines.get( id ).dataset.order = order;
	}

	// .........................................................................
	_namePrint( id, msg ) {
		const el = this._rows.get( id ).name;

		el.textContent = msg;
		el.classList.add( "gsuiDrumrow-nameInfo" );
	}
	_expandProps( id ) {
		this._rows.get( id ).root.classList.toggle( "gsuiDrumrow-open" );
		this._lines.get( id ).classList.toggle( "gsuiDrums-lineOpen" );
	}

	// events:
	// .........................................................................
	_oninputendRowSlider( id ) {
		const el = this._rows.get( id ).name;

		el.textContent = el.dataset.name;
		el.classList.remove( "gsuiDrumrow-nameInfo" );
	}
	_onchangeRowSlider( id, prop, val ) {
		this._dispatch( "change", "changeDrumrow", id, prop, val );
	}
	_onreorderRows( elRow ) {
		const rows = gsuiReorder.listComputeOrderChange( this.rootElement, {} );

		this._dispatch( "change", "reorderDrumrow", elRow.dataset.id, rows );
	}
	_onclickRows( e ) {
		if ( e.target !== this.rootElement ) {
			const id = e.target.closest( ".gsuiDrumrow" ).dataset.id;

			switch ( e.target.dataset.action ) {
				case "props": this._expandProps( id ); break;
				case "toggle": this._dispatch( "change", "toggleDrumrow", id ); break;
				case "delete": this._dispatch( "change", "removeDrumrow", id ); break;
			}
		}
	}
	_onmousedownRows( e ) {
		if ( ( e.button === 0 || e.button === 2 ) && e.target.classList.contains( "gsuiDrumrow-main" ) ) {
			this._dispatch(
				e.button === 0 ? "liveStartDrum" : "liveStopDrum",
				e.target.parentNode.dataset.id );
		}
	}
	_onanimationendRows( e ) {
		if ( e.target.classList.contains( "gsuiDrumrow-startCursor" ) ) {
			e.target.remove();
		}
	}
	_oncontextmenuRows( e ) {
		e.preventDefault();
		if ( e.target.dataset.action === "toggle" ) {
			this._dispatch( "change", "toggleOnlyDrumrow", e.target.closest( ".gsuiDrumrow" ).dataset.id );
		}
	}
	_ondropRows( e ) {
		if ( this._dragoverId ) {
			const [ patId ] = e.dataTransfer.getData( "pattern-buffer" ).split( ":" );

			if ( patId ) {
				this._dragoverId === Infinity
					? this._dispatch( "change", "addDrumrow", patId )
					: this._dispatch( "change", "changeDrumrowPattern", this._dragoverId, patId );
			}
		}
		this._ondragleaveRows();
	}
	_ondragleaveRows() {
		if ( this._elDragover ) {
			this._elDragover.classList.remove( "gsuiDrumrows-dragover" );
			this._elDragover =
			this._dragoverId = null;
		}
	}
	_ondragoverRows( e ) {
		if ( e.dataTransfer.types.includes( "pattern-buffer" ) ) {
			const tar = e.target,
				isParent = tar.classList.contains( "gsuiDrumrows" ),
				elDragover = isParent ? tar : tar.closest( ".gsuiDrumrow" );

			if ( elDragover !== this._elDragover ) {
				this._dragoverId = null;
				if ( isParent ) {
					this._dragoverId = Infinity;
				} else if ( elDragover ) {
					this._dragoverId = elDragover.dataset.id;
				}
				if ( this._elDragover ) {
					this._elDragover.classList.remove( "gsuiDrumrows-dragover" );
				}
				this._elDragover = elDragover;
				if ( elDragover ) {
					elDragover.classList.add( "gsuiDrumrows-dragover" );
				}
			}
		}
	}
}

gsuiDrumrows.template = document.querySelector( "#gsuiDrumrows-template" );
gsuiDrumrows.template.remove();
gsuiDrumrows.template.removeAttribute( "id" );

gsuiDrumrows.templateRow = document.querySelector( "#gsuiDrumrow-template" );
gsuiDrumrows.templateRow.remove();
gsuiDrumrows.templateRow.removeAttribute( "id" );

Object.freeze( gsuiDrumrows );
<commit_msg>gsuiDrumrows: drum props filter<commit_after>"use strict";

class gsuiDrumrows {
	constructor() {
		const root = gsuiDrumrows.template.cloneNode( true ),
			reorder = new gsuiReorder( {
				rootElement: root,
				direction: "column",
				dataTransferType: "drumrow",
				itemSelector: ".gsuiDrumrow",
				handleSelector: ".gsuiDrumrow-grip",
				parentSelector: ".gsuiDrumrows",
				onchange: this._onreorderRows.bind( this ),
			} );

		this.rootElement = root;
		this._rows = new Map();
		this._lines = new Map();
		this._reorder = reorder;
		this._dragoverId =
		this._elDragover =
		this._elLinesParent = null;
		this._dispatch = GSUtils.dispatchEvent.bind( null, root, "gsuiDrumrows" );
		Object.seal( this );

		root.ondrop = this._ondropRows.bind( this );
		root.onclick = this._onclickRows.bind( this );
		root.onchange = this._onchangeRows.bind( this );
		root.ondragover = this._ondragoverRows.bind( this );
		root.ondragleave = this._ondragleaveRows.bind( this );
		root.onmousedown = this._onmousedownRows.bind( this );
		root.oncontextmenu = this._oncontextmenuRows.bind( this );
		root.onanimationend = this._onanimationendRows.bind( this );
	}

	// .........................................................................
	setLinesParent( el, childClass ) {
		this._elLinesParent = el;
		this._reorder.setShadowElement( el );
		this._reorder.setShadowChildClass( childClass );
	}
	setFontSize( fs ) {
		this.rootElement.style.fontSize =
		this._elLinesParent.style.fontSize = `${ fs }px`;
	}
	reorderDrumrows( obj ) {
		gsuiReorder.listReorder( this.rootElement, obj );
		gsuiReorder.listReorder( this._elLinesParent, obj );
	}
	playRow( id ) {
		const rect = document.createElement( "div" );

		rect.classList.add( "gsuiDrumrow-startCursor" );
		this._rows.get( id ).root.querySelector( ".gsuiDrumrow-waveWrap" ).append( rect );
	}
	stopRow( id ) {
		this._rows.get( id ).root.querySelectorAll( ".gsuiDrumrow-startCursor" )
			.forEach( el => el.remove() );
	}
	setPropFilter( id, prop ) {
		this._rows.get( id ).root.querySelector( `.gsuiDrumrow-propRadio[value="${ prop }"]` ).checked = true;
	}

	// .........................................................................
	add( id, elLine ) {
		const elRow = gsuiDrumrows.templateRow.cloneNode( true ),
			sliDetune = new gsuiSlider(),
			sliGain = new gsuiSlider(),
			html = {
				root: elRow,
				name: elRow.querySelector( ".gsuiDrumrow-name" ),
				gain: sliGain,
				detune: sliDetune,
			};

		elRow.dataset.id =
		elLine.dataset.id = id;
		sliDetune.options( { min: -12, max: 12, step: 1, value: 0, type: "linear-y", mousemoveSize: 400 } );
		sliGain.options( { min: 0, max: 1, step: .01, value: 1, type: "linear-y", mousemoveSize: 400 } );
		sliDetune.oninput = val => {
			this._namePrint( id, `pitch: ${ val > 0 ? `+${ val }` : val }` );
			this._dispatch( "liveChangeDrumrow", id, "detune", val );
		};
		sliGain.oninput = val => {
			this._namePrint( id, `gain: ${ val.toFixed( 2 ) }` );
			this._dispatch( "liveChangeDrumrow", id, "gain", val );
		};
		sliDetune.onchange = this._onchangeRowSlider.bind( this, id, "detune" );
		sliGain.onchange = this._onchangeRowSlider.bind( this, id, "gain" );
		sliDetune.oninputend = this._oninputendRowSlider.bind( this, id );
		sliGain.oninputend = this._oninputendRowSlider.bind( this, id );
		elRow.querySelector( ".gsuiDrumrow-detune" ).append( sliDetune.rootElement );
		elRow.querySelector( ".gsuiDrumrow-gain" ).append( sliGain.rootElement );
		this._rows.set( id, html );
		this._lines.set( id, elLine );
		this.rootElement.append( elRow );
		this._elLinesParent.append( elLine );
		sliDetune.attached();
		sliGain.attached();
	}
	remove( id ) {
		this._rows.get( id ).root.remove();
		this._lines.get( id ).remove();
		this._rows.delete( id );
		this._lines.delete( id );
	}
	change( id, prop, val ) {
		switch ( prop ) {
			case "name": this._changeName( id, val ); break;
			case "gain": this._changeGain( id, val ); break;
			case "order": this._changeOrder( id, val ); break;
			case "detune": this._changeDetune( id, val ); break;
			case "toggle": this._changeToggle( id, val ); break;
			case "pattern": this._changePattern( id, val ); break;
			case "duration": this._changeDuration( id, val ); break;
		}
	}
	_changeGain( id, val ) {
		this._rows.get( id ).gain.setValue( val );
	}
	_changeDetune( id, val ) {
		this._rows.get( id ).detune.setValue( val );
	}
	_changeName( id, name ) {
		const el = this._rows.get( id ).name;

		el.dataset.name =
		el.textContent = name;
	}
	_changeToggle( id, b ) {
		this._rows.get( id ).root.classList.toggle( "gsuiDrumrow-mute", !b );
		this._lines.get( id ).classList.toggle( "gsuiDrumrow-mute", !b );
	}
	_changeDuration( id, dur ) {
		this._rows.get( id ).root.querySelector( ".gsuiDrumrow-waveWrap" ).style.animationDuration = `${ dur * 2 }s`;
	}
	_changePattern( id, svg ) {
		const elWave = this._rows.get( id ).root.querySelector( ".gsuiDrumrow-waveWrap" );

		if ( elWave.firstChild ) {
			elWave.firstChild.remove();
		}
		if ( svg ) {
			svg.classList.add( "gsuiDrumrow-wave" );
			elWave.append( svg );
		}
	}
	_changeOrder( id, order ) {
		this._rows.get( id ).root.dataset.order =
		this._lines.get( id ).dataset.order = order;
	}

	// .........................................................................
	_namePrint( id, msg ) {
		const el = this._rows.get( id ).name;

		el.textContent = msg;
		el.classList.add( "gsuiDrumrow-nameInfo" );
	}
	_expandProps( id ) {
		this._rows.get( id ).root.classList.toggle( "gsuiDrumrow-open" );
		this._lines.get( id ).classList.toggle( "gsuiDrums-lineOpen" );
	}

	// events:
	// .........................................................................
	_oninputendRowSlider( id ) {
		const el = this._rows.get( id ).name;

		el.textContent = el.dataset.name;
		el.classList.remove( "gsuiDrumrow-nameInfo" );
	}
	_onchangeRowSlider( id, prop, val ) {
		this._dispatch( "change", "changeDrumrow", id, prop, val );
	}
	_onreorderRows( elRow ) {
		const rows = gsuiReorder.listComputeOrderChange( this.rootElement, {} );

		this._dispatch( "change", "reorderDrumrow", elRow.dataset.id, rows );
	}
	_onchangeRows( e ) {
		const id = e.target.closest( ".gsuiDrumrow" ).dataset.id;

		this._dispatch( "propFilter", id, e.target.value );
	}
	_onclickRows( e ) {
		if ( e.target !== this.rootElement ) {
			const id = e.target.closest( ".gsuiDrumrow" ).dataset.id;

			switch ( e.target.dataset.action ) {
				case "props": this._expandProps( id ); break;
				case "toggle": this._dispatch( "change", "toggleDrumrow", id ); break;
				case "delete": this._dispatch( "change", "removeDrumrow", id ); break;
			}
		}
	}
	_onmousedownRows( e ) {
		if ( ( e.button === 0 || e.button === 2 ) && e.target.classList.contains( "gsuiDrumrow-main" ) ) {
			this._dispatch(
				e.button === 0 ? "liveStartDrum" : "liveStopDrum",
				e.target.parentNode.dataset.id );
		}
	}
	_onanimationendRows( e ) {
		if ( e.target.classList.contains( "gsuiDrumrow-startCursor" ) ) {
			e.target.remove();
		}
	}
	_oncontextmenuRows( e ) {
		e.preventDefault();
		if ( e.target.dataset.action === "toggle" ) {
			this._dispatch( "change", "toggleOnlyDrumrow", e.target.closest( ".gsuiDrumrow" ).dataset.id );
		} else if ( e.target.classList.contains( "gsuiDrumrow-propSpan" ) ) {
			this._dispatch( "propFilters", e.target.previousElementSibling.value );
		}
	}
	_ondropRows( e ) {
		if ( this._dragoverId ) {
			const [ patId ] = e.dataTransfer.getData( "pattern-buffer" ).split( ":" );

			if ( patId ) {
				this._dragoverId === Infinity
					? this._dispatch( "change", "addDrumrow", patId )
					: this._dispatch( "change", "changeDrumrowPattern", this._dragoverId, patId );
			}
		}
		this._ondragleaveRows();
	}
	_ondragleaveRows() {
		if ( this._elDragover ) {
			this._elDragover.classList.remove( "gsuiDrumrows-dragover" );
			this._elDragover =
			this._dragoverId = null;
		}
	}
	_ondragoverRows( e ) {
		if ( e.dataTransfer.types.includes( "pattern-buffer" ) ) {
			const tar = e.target,
				isParent = tar.classList.contains( "gsuiDrumrows" ),
				elDragover = isParent ? tar : tar.closest( ".gsuiDrumrow" );

			if ( elDragover !== this._elDragover ) {
				this._dragoverId = null;
				if ( isParent ) {
					this._dragoverId = Infinity;
				} else if ( elDragover ) {
					this._dragoverId = elDragover.dataset.id;
				}
				if ( this._elDragover ) {
					this._elDragover.classList.remove( "gsuiDrumrows-dragover" );
				}
				this._elDragover = elDragover;
				if ( elDragover ) {
					elDragover.classList.add( "gsuiDrumrows-dragover" );
				}
			}
		}
	}
}

gsuiDrumrows.template = document.querySelector( "#gsuiDrumrows-template" );
gsuiDrumrows.template.remove();
gsuiDrumrows.template.removeAttribute( "id" );

gsuiDrumrows.templateRow = document.querySelector( "#gsuiDrumrow-template" );
gsuiDrumrows.templateRow.remove();
gsuiDrumrows.templateRow.removeAttribute( "id" );

Object.freeze( gsuiDrumrows );
<|endoftext|>


<commit_before>'use strict';
const app = require('app');
const BrowserWindow = require('browser-window');
const Menu = require('menu');

// report crashes to the Electron project
// require('crash-reporter').start();

// adds debug features like hotkeys for triggering dev tools and reload
require('electron-debug')();

// prevent window being GC'd
let mainWindow;

function createMainWindow() {
  const win = new BrowserWindow({
    width: 600,
    height: 400,
    resizable: true,
    transparent: true,
    frame: true,
    // 'title-bar-style': 'hidden',
    'always-on-top': true
  });
  // win.maximize();

  win.loadUrl(`file://${__dirname}/renderer/index.html`);
  win.on('closed', onClosed);

  return win;
}

function onClosed() {
  // deref the window
  // for multiple windows store them in an array
  mainWindow = null;
}

function createMenu() {
  var template = [
    {
      label: require('app').getName(),
      submenu: [
        {
          label: 'Quit',
          accelerator: 'Command+Q',
          click: function() { app.quit(); }
        }
      ]
    }
  ];

  var menu = Menu.buildFromTemplate(template);
  Menu.setApplicationMenu(menu);
}

app.on('window-all-closed', function () {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

app.on('activate-with-no-open-windows', function () {
  if (!mainWindow) {
    mainWindow = createMainWindow();
  }
});

app.on('ready', function () {
  mainWindow = createMainWindow();
  createMenu();
});
<commit_msg>Add app menu to have full screen<commit_after>'use strict';
const app = require('app');
const BrowserWindow = require('browser-window');
const Menu = require('menu');

// report crashes to the Electron project
// require('crash-reporter').start();

// adds debug features like hotkeys for triggering dev tools and reload
require('electron-debug')();

// prevent window being GC'd
let mainWindow;

function createMainWindow() {
  const win = new BrowserWindow({
    width: 600,
    height: 400,
    resizable: true,
    transparent: true,
    frame: true,
    // 'title-bar-style': 'hidden',
    'always-on-top': true
  });
  // win.maximize();

  win.loadUrl(`file://${__dirname}/renderer/index.html`);
  win.on('closed', onClosed);

  return win;
}

function onClosed() {
  // deref the window
  // for multiple windows store them in an array
  mainWindow = null;
}

function createMenu() {
  var template = [
    {
      label: require('app').getName(),
      submenu: [
        {
          label: 'Quit',
          accelerator: 'Command+Q',
          click: function() { app.quit(); }
        }
      ]
    },
    {
      label: 'View',
      submenu: [
        {
          label: 'Full screen',
          accelerator: 'Command+F',
          click: function() { mainWindow.maximize(); }
        }
      ]
    }
  ];

  var menu = Menu.buildFromTemplate(template);
  Menu.setApplicationMenu(menu);
}

app.on('window-all-closed', function () {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

app.on('activate-with-no-open-windows', function () {
  if (!mainWindow) {
    mainWindow = createMainWindow();
  }
});

app.on('ready', function () {
  mainWindow = createMainWindow();
  createMenu();
});
<|endoftext|>