<commit_before>/**
ItemViews
*/

Lizard.Views = {};

Lizard.Views.Layer = Backbone.Marionette.ItemView.extend({
  tagName: 'li',
  template: '#layeritem-template',
  initialize: function() {
    this.model.bind('change', this.render);
  },
  events: {
    'click .icon-circle-arrow-up': 'moveUp',
    'click .icon-circle-arrow-down': 'moveDown',
    'click .indicator': 'toggleVisibility'
  },
  moveUp: function(event) {
    console.log('moveUp!', this.model.get('display_name'));
  },
  moveDown: function(event) {
    console.log('moveDown!', this.model.get('display_name'));
  },
  toggleVisibility: function(event) {
    console.log('Toggle!', this.model.get('display_name'));
  },
  onBeforeRender: function(model) {
    // console.log('onBeforeRender', model);
  }
});

Lizard.Views.InfoModal = Backbone.Marionette.ItemView.extend({
  template: '#info-modal-template',
  initialize: function() {
    console.log('Lizard.Views.Info initializing');
  }
});

Lizard.Views.Filter = Backbone.Marionette.ItemView.extend({
  _modelBinder: undefined,
  initialize: function(){
    // console.log('FilterView.initialize()');
    this._modelBinder = new Backbone.ModelBinder();
    this.model.on('reset', this.render, this);
  },
  onRender: function() {
    // console.log('filterview onRender()');
    var bindings = {state: 'span.state'};
    this._modelBinder.bind(this.model, this.el, bindings);
  },
  tagName: 'li',
  template: '#filterview-template',
  events: {
    'click input': 'toggle'
  },
  toggle: function(e) {
    uuid = this.model.get('id');
    type = 'logicalgroups';
    Lizard.Utils.Favorites.toggleSelected(uuid, type);
  },
});

Lizard.Views.Location = Backbone.Marionette.ItemView.extend({
  _modelBinder: undefined,
  initialize: function(){
    // console.log('LocationView.initialize()');
    this._modelBinder = new Backbone.ModelBinder();
  },
  onRender: function() {
    // console.log('locationview onRender()');
    var bindings = {state: 'span.state'};
    this._modelBinder.bind(this.model, this.el, bindings);
    this._modelBinder._onElChanged(this);
  },
  tagName: 'li',
  template: '#locationview-template',
  events: {
    'click input': 'toggle'
  },
  toggle: function(e) {

  },
});

Lizard.Views.Parameter = Backbone.Marionette.ItemView.extend({

  _modelBinder: undefined,
  initialize: function(){
    this._modelBinder = new Backbone.ModelBinder();
  },
  onRender: function() {
    var bindings = {state: 'span.state'};
    this._modelBinder.bind(this.model, this.el, bindings);
  },
  tagName: 'li',
  className: '',
  template: '#parameterview-template',
  events: {
    'click input': 'toggle'
  },
  toggle: function(e) {
    if(this.model.get('selected') === false) {
      this.model.set('selected', true);
    } else {
      this.model.set('selected', false);
    }
  },
  modelEvents: {
    'change:hidden': 'modelChanged'
  },
  modelChanged: function() {
    if (this.model.get('hidden') === true ) {
      this.$el._addClass("hidden");
    } else {
      this.$el._removeClass("hidden");
    }
  },
});

Lizard.Views.WidgetView = Backbone.Marionette.ItemView.extend({
  initialize: function(){
    // console.log('Lizard.Views.WidgetView initializing');
    that = this;
  },
  tagName: 'li',
  className: 'new',
  template: '#widgetview-template',
  attributes: {
    "data-col": "1", // << this needs to be dynamic!
    "data-row": "1",
    "data-sizex": "2",
    "data-sizey": "2"
  },
  events: {
    'click .icon-cog': 'configureWidget'
  },
  configureWidget: function(e) {
    // alert('test');
    // console.log(this.model.attributes.label + ' of ' + this.model.attributes.title);
    e.preventDefault();
    var template = _.template( $("#widget-configuration").html(), {} );
    this.$el.html( template );
  },
  modelEvents: {
    'change': 'modelChanged'
  },
  collectionEvents: {
    'add': 'modelAdded'
  },
  modelChanged: function() {
    console.log('modelChanged()');
  },
  modelAdded: function() {
    console.log('modelAdded()');
  }

});

Lizard.Views.FavoriteView = Backbone.Marionette.ItemView.extend({
  template:function(model){
    return _.template($('#favorite-item-template').html(), {
      name: model.data.name,
      uuid: "fav" + model.data.timeserie
    }, {variable: 'favorite'});
  },
  onRender: function(){
    if (!window.mapCanvas){
      this.$el.find('.goto').toggle('hidden');
    }
  },
  tagName: 'li',
  events:{
    "click .goto": 'goTo',
    "click .favstar" : 'removeFav'
  },
  removeFav: function(){
    this.model.destroy({wait:true});
  },
  goTo: function(e){
    if (window.mapCanvas){
      var location = locationCollection.get(this.model.attributes.location);
      var point = location.attributes.point_geometry;
      window.mapCanvas.panTo(new L.LatLng(point[1], point[0]));
    }
  }
});

/**
CollectionViews
*/

Lizard.Views.FilterCollection = Backbone.Marionette.CollectionView.extend({
  collection: filterCollection,
  tagName: 'ul',
  itemView: Lizard.Views.Filter,

  initialize: function(){
     this.collection.fetch({
        cache: true
      });
      this.listenTo(this.collection, 'reset', this.render, this);
  }
});

Lizard.Views.LocationCollection = Backbone.Marionette.CollectionView.extend({
  collection: locationCollection,
  tagName: 'ul',
  itemView: Lizard.Views.Location,

  initialize: function(){
      this.collection.fetch({
        cache: true
      });
      this.listenTo(this.collection, 'reset', this.render, this);
  }
});

Lizard.Views.ParameterCollection = Backbone.Marionette.CollectionView.extend({
  collection: parameterCollection,
  tagName: 'ul',

  itemView: Lizard.Views.Parameter,

  initialize: function(){
      this.collection.fetch({
        cache: true
      });
      this.listenTo(this.collection, 'reset', this.render, this);
  }
});

Lizard.Views.WidgetCollectionView = Backbone.Marionette.CollectionView.extend({
  // Creates the Gridster UL element
  collection: new Lizard.Collections.Widget(),
  tagName: 'ul',
  className: 'gridster',
  itemView: Lizard.Views.WidgetView,

  initialize: function(){
    this.listenTo(this.collection, 'reset', this.render, this);
  },
  onShow: function() {
    var self = this;
    var gridster = $('.gridster').gridster({
        widget_margins: [10, 10],
        widget_base_dimensions: [140, 140],
        draggable: {
          stop: function(event, ui) {
            console.log('Syncing dashboard: ', gridster.serialize());
            $('.top-right').notify({message: {text: 'Saving your dashboard layout!'}}).show();
          }
        }
    }).data('gridster');

    _.each(self.collection.models, function(model) {
      var jg = new JustGage({
        id: model.attributes.gaugeId,
        value: getRandomInt(650, 980),
        min: 350,
        max: 980,
        title: model.attributes.title,
        label: model.attributes.label
      });
      setInterval(function() { // <-- commented during development...
        jg.refresh(getRandomInt(350,980));
      }, getRandomInt(2000,5000));
    });
  }
});

Lizard.Views.FavoriteCollection = Backbone.Marionette.CollectionView.extend({
  collection: favoriteCollection,
  tagName: 'ul',
  itemView: Lizard.Views.FavoriteView,
  initialize: function() {
    this.collection.fetch();
  }
});

// Lizard.Views.LayerCollection = Backbone.Marionette.CollectionView.extend({
//   collection: layerCollection,
//   itemView: Lizard.Views.Layer,
//   initialize: function() {
//     this.collection.fetch();

/* LAYER VIEWS */
Lizard.Views.Layer = Backbone.Marionette.ItemView.extend({
  tagName: 'li',
  className: 'drawer-item',
  template: '#layeritem-template',
  initialize: function () {
    this.model.bind('change', this.render);
  },
  onBeforeRender: function () {
    this.el.setAttribute("id", this.model.attributes.display_name);
  },
  events: {
    'click .layer-item .indicator': 'toggleVisibility'
  },
  toggleVisibility: function () {
    if(this.model.attributes.visibility) {
      this.model.set({ visibility: false });
      window.mapCanvas.removeLayer(this.model.attributes.lyr);
    } else {
      this.model.set({ visibility: true });
      var lyr = L.tileLayer.wms(this.model.attributes.wms_url, {
        zIndex: 100 - this.$el.index(),
        layers: this.model.attributes.layer_name,
        format: this.model.attributes.format,
        transparent: this.model.attributes.transparent,
        opacity: this.model.attributes.opacity,
        attribution: 'DDSC'
      });
      this.model.set({lyr: lyr});
      window.mapCanvas.addLayer(lyr);
    }
  },
  updateOrder: function() {

    console.log($(this.model.attributes.display_name).index());
  }
});

Lizard.Views.LayerList = Backbone.Marionette.CollectionView.extend({
  initialize: function () {
    this.collection.fetch();
  },
  collection: layerCollection,
  tagName: 'ol',
  className: 'ui-sortable drawer-group',
  itemView: Lizard.Views.Layer,
  onDomRefresh: function () {
    $('.drawer-group').sortable({
      'forcePlaceholderSize': true,
      'handle': '.handle',
      'axis': 'y',
      update: function (event, ui) {
        model = layerCollection.where({display_name: ui.item[0].id})[0];
        model.attributes.lyr.setZIndex(100 - ui.item.index())
      }
    });
    $('.drawer-group').disableSelection();
  }
});

/* MENU VIEWS */

Lizard.Views.Menu = Backbone.Marionette.ItemView.extend({
	model: new Lizard.Models.Account(),
	tagName: 'a',
	template: '#login-template',
    attributes: {
		'href': '#'},

	events: {
		'click #login': 'doLogin',
		'click #logout': 'doLogout'
	},

	initialize: function(){
		console.log('initialize LoginView');
		var that = this;
		this.model.fetch({
			success: function(model, response, data){
				if (model.attributes.authenticated === true){
					that.template = '#loggedin-template';
					that.render();
				}
			}
		});
	},

	doLogin: function(e){
		// Redirect to the Single Sign On server.
		e.preventDefault();
		url = settings.login_token_url;
		$.getJSON(url, function(json) {
			window.location=json.login_url;
		});
	},

	doLogout: function(e){
		// Redirect to the Single Sign On server.
		e.preventDefault();
		url = settings.logout_token_url;
		$.getJSON(url, function(json) {
			window.location=json.logout_url;
		});
	}

});

// Instantiate the Views
var filtercollectionview = new Lizard.Views.FilterCollection();
var favoritecollectionview = new Lizard.Views.FavoriteCollection();
var locationcollectionview = new Lizard.Views.LocationCollection();
var parametercollectionview = new Lizard.Views.ParameterCollection();
var layercollectionview = new Lizard.Views.LayerCollection();
var widgetcollectionview = new Lizard.Views.WidgetCollectionView();

widgetcollectionview.collection.add([
  new Lizard.Models.Widget({col:3,row:5,size_x:2,size_y:2,gaugeId:1,title:'Amstel',label:'Verplaatsing (m/s)'}),
  new Lizard.Models.Widget({col:1,row:1,size_x:2,size_y:2,gaugeId:2,title:'Waternet',label:'Debiet (m3)'}),
  new Lizard.Models.Widget({col:3,row:3,size_x:2,size_y:2,gaugeId:3,title:'Rijn',label:'Volume (m3)'}),
  new Lizard.Models.Widget({col:3,row:1,size_x:2,size_y:2,gaugeId:4,title:'Dijk 22',label:'Sulfiet'}),
  new Lizard.Models.Widget({col:3,row:1,size_x:2,size_y:2,gaugeId:5,title:'Dijk 23',label:'Temperatuur (c)'}),
  new Lizard.Models.Widget({col:3,row:1,size_x:2,size_y:2,gaugeId:6,title:'Dijk 24',label:'Druk'})
]);
<commit_msg>broke something, fixed now<commit_after>/**
ItemViews
*/

Lizard.Views = {};

Lizard.Views.Layer = Backbone.Marionette.ItemView.extend({
  tagName: 'li',
  template: '#layeritem-template',
  initialize: function() {
    this.model.bind('change', this.render);
  },
  events: {
    'click .icon-circle-arrow-up': 'moveUp',
    'click .icon-circle-arrow-down': 'moveDown',
    'click .indicator': 'toggleVisibility'
  },
  moveUp: function(event) {
    console.log('moveUp!', this.model.get('display_name'));
  },
  moveDown: function(event) {
    console.log('moveDown!', this.model.get('display_name'));
  },
  toggleVisibility: function(event) {
    console.log('Toggle!', this.model.get('display_name'));
  },
  onBeforeRender: function(model) {
    // console.log('onBeforeRender', model);
  }
});

Lizard.Views.InfoModal = Backbone.Marionette.ItemView.extend({
  template: '#info-modal-template',
  initialize: function() {
    console.log('Lizard.Views.Info initializing');
  }
});

Lizard.Views.Filter = Backbone.Marionette.ItemView.extend({
  _modelBinder: undefined,
  initialize: function(){
    // console.log('FilterView.initialize()');
    this._modelBinder = new Backbone.ModelBinder();
    this.model.on('reset', this.render, this);
  },
  onRender: function() {
    // console.log('filterview onRender()');
    var bindings = {state: 'span.state'};
    this._modelBinder.bind(this.model, this.el, bindings);
  },
  tagName: 'li',
  template: '#filterview-template',
  events: {
    'click input': 'toggle'
  },
  toggle: function(e) {
    uuid = this.model.get('id');
    type = 'logicalgroups';
    Lizard.Utils.Favorites.toggleSelected(uuid, type);
  },
});

Lizard.Views.Location = Backbone.Marionette.ItemView.extend({
  _modelBinder: undefined,
  initialize: function(){
    // console.log('LocationView.initialize()');
    this._modelBinder = new Backbone.ModelBinder();
  },
  onRender: function() {
    // console.log('locationview onRender()');
    var bindings = {state: 'span.state'};
    this._modelBinder.bind(this.model, this.el, bindings);
    this._modelBinder._onElChanged(this);
  },
  tagName: 'li',
  template: '#locationview-template',
  events: {
    'click input': 'toggle'
  },
  toggle: function(e) {

  },
});

Lizard.Views.Parameter = Backbone.Marionette.ItemView.extend({

  _modelBinder: undefined,
  initialize: function(){
    this._modelBinder = new Backbone.ModelBinder();
  },
  onRender: function() {
    var bindings = {state: 'span.state'};
    this._modelBinder.bind(this.model, this.el, bindings);
  },
  tagName: 'li',
  className: '',
  template: '#parameterview-template',
  events: {
    'click input': 'toggle'
  },
  toggle: function(e) {
    if(this.model.get('selected') === false) {
      this.model.set('selected', true);
    } else {
      this.model.set('selected', false);
    }
  },
  modelEvents: {
    'change:hidden': 'modelChanged'
  },
  modelChanged: function() {
    if (this.model.get('hidden') === true ) {
      this.$el._addClass("hidden");
    } else {
      this.$el._removeClass("hidden");
    }
  },
});

Lizard.Views.WidgetView = Backbone.Marionette.ItemView.extend({
  initialize: function(){
    // console.log('Lizard.Views.WidgetView initializing');
    that = this;
  },
  tagName: 'li',
  className: 'new',
  template: '#widgetview-template',
  attributes: {
    "data-col": "1", // << this needs to be dynamic!
    "data-row": "1",
    "data-sizex": "2",
    "data-sizey": "2"
  },
  events: {
    'click .icon-cog': 'configureWidget'
  },
  configureWidget: function(e) {
    // alert('test');
    // console.log(this.model.attributes.label + ' of ' + this.model.attributes.title);
    e.preventDefault();
    var template = _.template( $("#widget-configuration").html(), {} );
    this.$el.html( template );
  },
  modelEvents: {
    'change': 'modelChanged'
  },
  collectionEvents: {
    'add': 'modelAdded'
  },
  modelChanged: function() {
    console.log('modelChanged()');
  },
  modelAdded: function() {
    console.log('modelAdded()');
  }

});

Lizard.Views.FavoriteView = Backbone.Marionette.ItemView.extend({
  template:function(model){
    return _.template($('#favorite-item-template').html(), {
      name: model.data.name,
      uuid: "fav" + model.data.timeserie
    }, {variable: 'favorite'});
  },
  onRender: function(){
    if (!window.mapCanvas){
      this.$el.find('.goto').toggle('hidden');
    }
  },
  tagName: 'li',
  events:{
    "click .goto": 'goTo',
    "click .favstar" : 'removeFav'
  },
  removeFav: function(){
    this.model.destroy({wait:true});
  },
  goTo: function(e){
    if (window.mapCanvas){
      var location = locationCollection.get(this.model.attributes.location);
      var point = location.attributes.point_geometry;
      window.mapCanvas.panTo(new L.LatLng(point[1], point[0]));
    }
  }
});

/**
CollectionViews
*/

Lizard.Views.FilterCollection = Backbone.Marionette.CollectionView.extend({
  collection: filterCollection,
  tagName: 'ul',
  itemView: Lizard.Views.Filter,

  initialize: function(){
     this.collection.fetch({
        cache: true
      });
      this.listenTo(this.collection, 'reset', this.render, this);
  }
});

Lizard.Views.LocationCollection = Backbone.Marionette.CollectionView.extend({
  collection: locationCollection,
  tagName: 'ul',
  itemView: Lizard.Views.Location,

  initialize: function(){
      this.collection.fetch({
        cache: true
      });
      this.listenTo(this.collection, 'reset', this.render, this);
  }
});

Lizard.Views.ParameterCollection = Backbone.Marionette.CollectionView.extend({
  collection: parameterCollection,
  tagName: 'ul',

  itemView: Lizard.Views.Parameter,

  initialize: function(){
      this.collection.fetch({
        cache: true
      });
      this.listenTo(this.collection, 'reset', this.render, this);
  }
});

Lizard.Views.WidgetCollectionView = Backbone.Marionette.CollectionView.extend({
  // Creates the Gridster UL element
  collection: new Lizard.Collections.Widget(),
  tagName: 'ul',
  className: 'gridster',
  itemView: Lizard.Views.WidgetView,

  initialize: function(){
    this.listenTo(this.collection, 'reset', this.render, this);
  },
  onShow: function() {
    var self = this;
    var gridster = $('.gridster').gridster({
        widget_margins: [10, 10],
        widget_base_dimensions: [140, 140],
        draggable: {
          stop: function(event, ui) {
            console.log('Syncing dashboard: ', gridster.serialize());
            $('.top-right').notify({message: {text: 'Saving your dashboard layout!'}}).show();
          }
        }
    }).data('gridster');

    _.each(self.collection.models, function(model) {
      var jg = new JustGage({
        id: model.attributes.gaugeId,
        value: getRandomInt(650, 980),
        min: 350,
        max: 980,
        title: model.attributes.title,
        label: model.attributes.label
      });
      setInterval(function() { // <-- commented during development...
        jg.refresh(getRandomInt(350,980));
      }, getRandomInt(2000,5000));
    });
  }
});

Lizard.Views.FavoriteCollection = Backbone.Marionette.CollectionView.extend({
  collection: favoriteCollection,
  tagName: 'ul',
  itemView: Lizard.Views.FavoriteView,
  initialize: function() {
    this.collection.fetch();
  }
});

Lizard.Views.LayerCollection = Backbone.Marionette.CollectionView.extend({
  collection: layerCollection,
  itemView: Lizard.Views.Layer,
  initialize: function() {
    this.collection.fetch();
  }
});

/* LAYER VIEWS */
Lizard.Views.Layer = Backbone.Marionette.ItemView.extend({
  tagName: 'li',
  className: 'drawer-item',
  template: '#layeritem-template',
  initialize: function () {
    this.model.bind('change', this.render);
  },
  onBeforeRender: function () {
    this.el.setAttribute("id", this.model.attributes.display_name);
  },
  events: {
    'click .layer-item .indicator': 'toggleVisibility'
  },
  toggleVisibility: function () {
    if(this.model.attributes.visibility) {
      this.model.set({ visibility: false });
      window.mapCanvas.removeLayer(this.model.attributes.lyr);
    } else {
      this.model.set({ visibility: true });
      var lyr = L.tileLayer.wms(this.model.attributes.wms_url, {
        zIndex: 100 - this.$el.index(),
        layers: this.model.attributes.layer_name,
        format: this.model.attributes.format,
        transparent: this.model.attributes.transparent,
        opacity: this.model.attributes.opacity,
        attribution: 'DDSC'
      });
      this.model.set({lyr: lyr});
      window.mapCanvas.addLayer(lyr);
    }
  },
  updateOrder: function() {

    console.log($(this.model.attributes.display_name).index());
  }
});

Lizard.Views.LayerList = Backbone.Marionette.CollectionView.extend({
  initialize: function () {
    this.collection.fetch();
  },
  collection: layerCollection,
  tagName: 'ol',
  className: 'ui-sortable drawer-group',
  itemView: Lizard.Views.Layer,
  onDomRefresh: function () {
    $('.drawer-group').sortable({
      'forcePlaceholderSize': true,
      'handle': '.handle',
      'axis': 'y',
      update: function (event, ui) {
        model = layerCollection.where({display_name: ui.item[0].id})[0];
        model.attributes.lyr.setZIndex(100 - ui.item.index())
      }
    });
    $('.drawer-group').disableSelection();
  }
});

/* MENU VIEWS */

Lizard.Views.Menu = Backbone.Marionette.ItemView.extend({
	model: new Lizard.Models.Account(),
	tagName: 'a',
	template: '#login-template',
    attributes: {
		'href': '#'},

	events: {
		'click #login': 'doLogin',
		'click #logout': 'doLogout'
	},

	initialize: function(){
		console.log('initialize LoginView');
		var that = this;
		this.model.fetch({
			success: function(model, response, data){
				if (model.attributes.authenticated === true){
					that.template = '#loggedin-template';
					that.render();
				}
			}
		});
	},

	doLogin: function(e){
		// Redirect to the Single Sign On server.
		e.preventDefault();
		url = settings.login_token_url;
		$.getJSON(url, function(json) {
			window.location=json.login_url;
		});
	},

	doLogout: function(e){
		// Redirect to the Single Sign On server.
		e.preventDefault();
		url = settings.logout_token_url;
		$.getJSON(url, function(json) {
			window.location=json.logout_url;
		});
	}

});

// Instantiate the Views
var filtercollectionview = new Lizard.Views.FilterCollection();
var favoritecollectionview = new Lizard.Views.FavoriteCollection();
var locationcollectionview = new Lizard.Views.LocationCollection();
var parametercollectionview = new Lizard.Views.ParameterCollection();
var layercollectionview = new Lizard.Views.LayerCollection();
var widgetcollectionview = new Lizard.Views.WidgetCollectionView();

widgetcollectionview.collection.add([
  new Lizard.Models.Widget({col:3,row:5,size_x:2,size_y:2,gaugeId:1,title:'Amstel',label:'Verplaatsing (m/s)'}),
  new Lizard.Models.Widget({col:1,row:1,size_x:2,size_y:2,gaugeId:2,title:'Waternet',label:'Debiet (m3)'}),
  new Lizard.Models.Widget({col:3,row:3,size_x:2,size_y:2,gaugeId:3,title:'Rijn',label:'Volume (m3)'}),
  new Lizard.Models.Widget({col:3,row:1,size_x:2,size_y:2,gaugeId:4,title:'Dijk 22',label:'Sulfiet'}),
  new Lizard.Models.Widget({col:3,row:1,size_x:2,size_y:2,gaugeId:5,title:'Dijk 23',label:'Temperatuur (c)'}),
  new Lizard.Models.Widget({col:3,row:1,size_x:2,size_y:2,gaugeId:6,title:'Dijk 24',label:'Druk'})
]);
<|endoftext|>


<commit_before>(function() {
	/**
	 * Main script for the game AD. Uses canvas to display the game board.
	 */

	// Creation of the 4 arrows to move the rows and columns.
	var d = document, createElement = 'createElement',
		arrow = d[createElement]('canvas'),
		controlSize = 100,
		controlThird = controlSize / 3,
		controlTwoThird = controlThird * 2,
		controlHalf = controlSize / 2,
		controls = [
			[
				d[createElement]('img'),
				[controlThird, controlThird],
				[controlTwoThird, controlThird],
				[controlHalf, controlTwoThird]
			],
			[
				d[createElement]('img'),
				[controlTwoThird, controlThird],
				[controlTwoThird, controlTwoThird],
				[controlThird, controlHalf]
			],
			[
				d[createElement]('img'),
				[controlThird, controlTwoThird],
				[controlTwoThird, controlTwoThird],
				[controlHalf, controlThird]
			],
			[
				d[createElement]('img'),
				[controlThird, controlThird],
				[controlThird, controlTwoThird],
				[controlTwoThird, controlHalf]
			]
		],
		ctx,
		c,
		und = undefined,
		// attributes strings
		cellSize = 'cellSize',
		gridWidth = 'gridWidth',
		grid = 'grid',

		// Methods
		ad,
		_init,
		_createInformationsTable,
		_getInformation,
		_updateInformation,
		_randomInt,
		_generateColors,
		_generateGrid,
		_displayGrid,
		_clickEvent,
		_onControl,
		_setEvents,
		_detectBlobs;

	if (!arrow.getContext) {
		throw 'This browser does not support the use of canvas';
	}

	arrow.width = arrow.height = controlSize;
	ctx = arrow.getContext('2d');

	ctx.fillStyle = '#888';
	ctx.strokeRect(0, 0, controlSize, controlSize);
	for (c = 0; c < 4; c++) {
		ctx.clearRect(1, 1, controlSize - 2, controlSize - 2);
		ctx.beginPath();
		ctx.moveTo(controls[c][1][0], controls[c][1][1]);
		ctx.lineTo(controls[c][2][0], controls[c][2][1]);
		ctx.lineTo(controls[c][3][0], controls[c][3][1]);
		ctx.fill();
		controls[c][0].src = arrow.toDataURL("image/png").replace("image/png", "image/octet-stream");
	}

	/**
	 * Game construct
	 */
	ad = function(parent, options) {
		var tmp, size, canvas;
		if (parent.tagName != 'DIV') {
			tmp = B.create('div');
			parent.parentNode.replaceChild(tmp, parent);
			parent = tmp;
		}

		this.parent = parent;
		options = options || {};
		size = options.size||300;
		canvas = B.create('canvas', {width: size, height: size}, this.parent);

		this.ctx = canvas.getContext('2d');
		this._controlButtons = [],
		_init.apply(this, [options.nbCellsSide||4, options.nbColors||4]);
	};

	_createInformationsTable = function() {
		this.infos = {blobs: ['Blobs', 0], moves: ['Moves', 0], colors: ['Colors', this.colors.length]};

		Object.keys(this.infos).forEach(function(key) {
			this.infos[key][1] = B.create('span', {text: this.infos[key][1]}, B.create('div', {text: this.infos[key][0] + ': '}, this.parent));
		}.bind(this));
	};

	_getInformation = function(info) {
		return this.infos[info] ? parseInt(this.infos[info][1].innerHTML) : false;
	};

	_updateInformation = function(info, value) {
		if (!this.infos[info]) return false;

		this.infos[info][1].innerHTML = value;
	};

	/**
	 * Method to init the game
	 * Private method
	 */
	_init = function(gridWidth, nbColors) {
		_generateColors.apply(this, [nbColors]);
		_generateGrid.apply(this, [gridWidth, nbColors]);
		_displayGrid.apply(this, [true]);
		_createInformationsTable.apply(this);
		_detectBlobs.apply(this);
		_setEvents.apply(this);
	};

	/**
	 * Method to generate a random integer
	 *
	 * Private method
	 */
	_randomInt = function(maxExcluded) {
		return 0 | Math.random() * maxExcluded;
	};

	/**
	 * Generate an array of colors. A color is an array of 3 integer(0, 255).
	 * The first color is randomly generated, the last one is the opposite of
	 * the first one. The others are calculated with a lerp computation.
	 */
	_generateColors = function(nb) {
		var colors = [],
			c,
			last = nb - 1,
			percentColor,
			_generateRandomColor,
			_lerp;

		_generateRandomColor = function() {
			return [
				_randomInt(256),
				_randomInt(256),
				_randomInt(256)
			]
		};

		_lerp = function(val1, val2, ratio) {
			return 0 | (Math.min(val1, val2) + Math.abs(val1 - val2) * ratio);
		};

		colors[0] = _generateRandomColor();
		colors[last] = [];

		for (c = 0; c < 3; c++) {
			colors[last].push((colors[0][c] + 128) % 256);
		}

		for (c = 1; c < last; c++) {
			percentColor = c / last;
			colors[c] = [
				_lerp(colors[0][0], colors[last][0], percentColor),
				_lerp(colors[0][2], colors[last][1], percentColor),
				_lerp(colors[0][1], colors[last][2], percentColor)
			];
		}

		this.colors = colors;
		this.nbColors = colors.length;
	};

	/**
	 * Generate the grid from a width (the grid is a square) and a number of
	 * colors.
	 * The returned grid is a 2 dimensions array containing, for each cell, an
	 * integer corresponding to the cell's color index (0, nbColors - 1)
	 */
	_generateGrid = function(width, nbColors) {
		var x, y;

		this[grid] = [];
		this[gridWidth] = width;

		for (x = 0; x < width; x++) {
			this[grid][x] = [];
			for (y = 0; y < width; y++) {
				this[grid][x][y] = _randomInt(nbColors);
			}
		}
	};

	/**
	 * Display the grid in the canvas
	 */
	_displayGrid = function(init) {
		var canvasWidth = this.ctx.canvas.clientWidth,
			x, y,
			_displayCell,
			_displayControl;

		this[cellSize] = canvasWidth / (this[gridWidth] + 2);

		_displayCell = function(x, y) {
			var color = this.colors[this[grid][x][y]];
			this.ctx.fillStyle = 'rgb(' + color[0] + ',' + color[1] + ',' + color[2] + ')';
			this.ctx.fillRect((x + 1) * this[cellSize], (y + 1) * this[cellSize], this[cellSize], this[cellSize]);
		};

		_displayControl = function(contX, contY, ctrls, c, tmp) {
			for (c = 0; c < ctrls.length; c++) {
				this._controlButtons.push([contX, contY]);
				this.ctx.drawImage(controls[ctrls[c]][0], contX, contY, this[cellSize], this[cellSize]);
				tmp = contX;
				contX = contY;
				contY = tmp;
			}
		};

		for (x = 0; x < this[gridWidth]; x++) {
			if (init) {
				// control buttons
				_displayControl.apply(this, [(x + 1) * this[cellSize], 0, [0, 3]]);
				_displayControl.apply(this, [canvasWidth - this[cellSize], (x + 1) * this[cellSize], [1, 2]]);
			}

			for (y = 0; y < this[gridWidth]; y++) {
				_displayCell.apply(this, [x, y]);
			}
		}
	};

	/**
	 * Method to check if the player clicked on a control.
	 */
	_onControl = function(x, y) {
		var b, nbButtons = this._controlButtons.length;

		for (b = 0; b < nbButtons; b++) {
			if (
				this._controlButtons[b][0] <= x
				&& x <= this._controlButtons[b][0] + this[cellSize]
				&& this._controlButtons[b][1] <= y
				&& y <= this._controlButtons[b][1] + this[cellSize]
			) {
				break;
			}
		}

		return b == nbButtons ? null : b;
	};

	_clickEvent = function(e){
		var button = _onControl.apply(this, [e.layerX, e.layerY]),
			_shiftDown, _shiftRight, _shiftLeft, _shiftUp,
			row, nbBlobs;
		if (button == null) return;

		/**
		 * Methods to move a row or column in the 4 directions
		 */
		_shiftDown = function(col) {
			var last = this[grid][col][this[grid][col].length - 1];
			this[grid][col].pop();
			this[grid][col].unshift(last);
		};
		_shiftRight = function(row) {
			var old1, old2 = -1, r, last = this[grid].length - 1;
			for (r in this[grid]) {
				old1 = this[grid][r][row];
				this[grid][r][row] = ~old2 ? old2 : this[grid][(r + last) % this[grid].length][row];
				old2 = old1;
			}
		};
		_shiftUp = function(col) {
			var first = this[grid][col][0];
			this[grid][col].shift();
			this[grid][col].push(first);
		};
		_shiftLeft = function(row) {
			var r, first = this[grid][0][row];
			for (r = 0; r < this[gridWidth]; r++) {
				this[grid][r][row] = this[grid][(parseInt(r) + 1) % this[grid].length][row];
			}
			this[grid][this[grid].length - 1][row] = first;
		};

		row = 0|button/4;
		switch (button % 4) {
			case 0:
				_shiftDown.apply(this, [row]);
				break;
			case 1:
				_shiftRight.apply(this, [row]);
				break;
			case 2:
				_shiftLeft.apply(this, [row]);
				break;
			case 3:
				_shiftUp.apply(this, [row]);
				break;
		};

		nbBlobs = _detectBlobs.apply(this);
		_updateInformation.apply(this, ['moves', parseInt(_getInformation.apply(this, ['moves'])) + 1]);
		_displayGrid.apply(this, [false]);

		// Winning condition
		if (nbBlobs == this.nbColors) {
		}
	};

	/**
	 * Method to set the click event on the game, to move the controls.
	 */
	_setEvents = function() {
		B.addEvent(this.parent, 'click', _clickEvent.bind(this));
	};

	/**
	 * Method to detect the blobs (like in picture processing) in the board.
	 * Returns the number of blobs of the board.
	 */
	_detectBlobs = function() {
		var blobs, nbBlobs = 0,
			x, y,
			nbCells = this[gridWidth] * this[gridWidth],
			_setBlobsAndPropagate;

		blobs = new Array(nbCells);

		_setBlobsAndPropagate = function(x, y, id) {
			var current = this[grid][x][y],
				currentIndex = y * this[gridWidth] + x,
				north = currentIndex - this[gridWidth],
				south = currentIndex + this[gridWidth],
				east = currentIndex + 1,
				west = currentIndex - 1;
			blobs[currentIndex] = id;

			if (north > 0 && blobs[north] == und && this[grid][x][y-1] == current) {
				_setBlobsAndPropagate.apply(this, [x, y-1, id]);
			}
			if (east%this[gridWidth] > currentIndex%this[gridWidth] && blobs[east] == und && this[grid][x+1][y] == current) {
				_setBlobsAndPropagate.apply(this, [x+1, y, id]);
			}
			if (south < nbCells && blobs[south] == und && this[grid][x][y+1] == current) {
				_setBlobsAndPropagate.apply(this, [x, y+1, id]);
			}
			if (Math.abs(west%this[gridWidth]) < currentIndex%this[gridWidth] && blobs[west] == und && this[grid][x-1][y] == current) {
				_setBlobsAndPropagate.apply(this, [x-1, y, id]);
			}
		};

		for (x = 0; x < this[gridWidth]; x++) {
			for (y = 0; y < this[gridWidth]; y++) {
				if (blobs[y * this[gridWidth] + x] != und) {
					continue;
				}

				_setBlobsAndPropagate.apply(this, [x, y, nbBlobs]);
				nbBlobs++;
			}
		}

		_updateInformation.apply(this, ['blobs', nbBlobs]);
		return nbBlobs;
	};

	window.Ad = ad;
})();
<commit_msg>if the player won, disable the click event<commit_after>(function() {
	/**
	 * Main script for the game AD. Uses canvas to display the game board.
	 */

	// Creation of the 4 arrows to move the rows and columns.
	var d = document, createElement = 'createElement',
		arrow = d[createElement]('canvas'),
		controlSize = 100,
		controlThird = controlSize / 3,
		controlTwoThird = controlThird * 2,
		controlHalf = controlSize / 2,
		controls = [
			[
				d[createElement]('img'),
				[controlThird, controlThird],
				[controlTwoThird, controlThird],
				[controlHalf, controlTwoThird]
			],
			[
				d[createElement]('img'),
				[controlTwoThird, controlThird],
				[controlTwoThird, controlTwoThird],
				[controlThird, controlHalf]
			],
			[
				d[createElement]('img'),
				[controlThird, controlTwoThird],
				[controlTwoThird, controlTwoThird],
				[controlHalf, controlThird]
			],
			[
				d[createElement]('img'),
				[controlThird, controlThird],
				[controlThird, controlTwoThird],
				[controlTwoThird, controlHalf]
			]
		],
		ctx,
		c,
		und = undefined,
		// attributes strings
		cellSize = 'cellSize',
		gridWidth = 'gridWidth',
		grid = 'grid',

		// Methods
		ad,
		_init,
		_createInformationsTable,
		_getInformation,
		_updateInformation,
		_randomInt,
		_generateColors,
		_generateGrid,
		_displayGrid,
		_clickEvent,
		_onControl,
		_setEvents,
		_detectBlobs;

	if (!arrow.getContext) {
		throw 'This browser does not support the use of canvas';
	}

	arrow.width = arrow.height = controlSize;
	ctx = arrow.getContext('2d');

	ctx.fillStyle = '#888';
	ctx.strokeRect(0, 0, controlSize, controlSize);
	for (c = 0; c < 4; c++) {
		ctx.clearRect(1, 1, controlSize - 2, controlSize - 2);
		ctx.beginPath();
		ctx.moveTo(controls[c][1][0], controls[c][1][1]);
		ctx.lineTo(controls[c][2][0], controls[c][2][1]);
		ctx.lineTo(controls[c][3][0], controls[c][3][1]);
		ctx.fill();
		controls[c][0].src = arrow.toDataURL("image/png").replace("image/png", "image/octet-stream");
	}

	/**
	 * Game construct
	 */
	ad = function(parent, options) {
		var tmp, size, canvas;
		if (parent.tagName != 'DIV') {
			tmp = B.create('div');
			parent.parentNode.replaceChild(tmp, parent);
			parent = tmp;
		}

		this.parent = parent;
		options = options || {};
		size = options.size||300;
		canvas = B.create('canvas', {width: size, height: size}, this.parent);

		this.ctx = canvas.getContext('2d');
		this._controlButtons = [],
		_init.apply(this, [options.nbCellsSide||4, options.nbColors||4]);
	};

	_createInformationsTable = function() {
		this.infos = {blobs: ['Blobs', 0], moves: ['Moves', 0], colors: ['Colors', this.colors.length]};

		Object.keys(this.infos).forEach(function(key) {
			this.infos[key][1] = B.create('span', {text: this.infos[key][1]}, B.create('div', {text: this.infos[key][0] + ': '}, this.parent));
		}.bind(this));
	};

	_getInformation = function(info) {
		return this.infos[info] ? parseInt(this.infos[info][1].innerHTML) : false;
	};

	_updateInformation = function(info, value) {
		if (!this.infos[info]) return false;

		this.infos[info][1].innerHTML = value;
	};

	/**
	 * Method to init the game
	 * Private method
	 */
	_init = function(gridWidth, nbColors) {
		_generateColors.apply(this, [nbColors]);
		_generateGrid.apply(this, [gridWidth, nbColors]);
		_displayGrid.apply(this, [true]);
		_createInformationsTable.apply(this);
		_detectBlobs.apply(this);
		_setEvents.apply(this);
	};

	/**
	 * Method to generate a random integer
	 *
	 * Private method
	 */
	_randomInt = function(maxExcluded) {
		return 0 | Math.random() * maxExcluded;
	};

	/**
	 * Generate an array of colors. A color is an array of 3 integer(0, 255).
	 * The first color is randomly generated, the last one is the opposite of
	 * the first one. The others are calculated with a lerp computation.
	 */
	_generateColors = function(nb) {
		var colors = [],
			c,
			last = nb - 1,
			percentColor,
			_generateRandomColor,
			_lerp;

		_generateRandomColor = function() {
			return [
				_randomInt(256),
				_randomInt(256),
				_randomInt(256)
			]
		};

		_lerp = function(val1, val2, ratio) {
			return 0 | (Math.min(val1, val2) + Math.abs(val1 - val2) * ratio);
		};

		colors[0] = _generateRandomColor();
		colors[last] = [];

		for (c = 0; c < 3; c++) {
			colors[last].push((colors[0][c] + 128) % 256);
		}

		for (c = 1; c < last; c++) {
			percentColor = c / last;
			colors[c] = [
				_lerp(colors[0][0], colors[last][0], percentColor),
				_lerp(colors[0][2], colors[last][1], percentColor),
				_lerp(colors[0][1], colors[last][2], percentColor)
			];
		}

		this.colors = colors;
		this.nbColors = colors.length;
	};

	/**
	 * Generate the grid from a width (the grid is a square) and a number of
	 * colors.
	 * The returned grid is a 2 dimensions array containing, for each cell, an
	 * integer corresponding to the cell's color index (0, nbColors - 1)
	 */
	_generateGrid = function(width, nbColors) {
		var x, y;

		this[grid] = [];
		this[gridWidth] = width;

		for (x = 0; x < width; x++) {
			this[grid][x] = [];
			for (y = 0; y < width; y++) {
				this[grid][x][y] = _randomInt(nbColors);
			}
		}
	};

	/**
	 * Display the grid in the canvas
	 */
	_displayGrid = function(init) {
		var canvasWidth = this.ctx.canvas.clientWidth,
			x, y,
			_displayCell,
			_displayControl;

		this[cellSize] = canvasWidth / (this[gridWidth] + 2);

		_displayCell = function(x, y) {
			var color = this.colors[this[grid][x][y]];
			this.ctx.fillStyle = 'rgb(' + color[0] + ',' + color[1] + ',' + color[2] + ')';
			this.ctx.fillRect((x + 1) * this[cellSize], (y + 1) * this[cellSize], this[cellSize], this[cellSize]);
		};

		_displayControl = function(contX, contY, ctrls, c, tmp) {
			for (c = 0; c < ctrls.length; c++) {
				this._controlButtons.push([contX, contY]);
				this.ctx.drawImage(controls[ctrls[c]][0], contX, contY, this[cellSize], this[cellSize]);
				tmp = contX;
				contX = contY;
				contY = tmp;
			}
		};

		for (x = 0; x < this[gridWidth]; x++) {
			if (init) {
				// control buttons
				_displayControl.apply(this, [(x + 1) * this[cellSize], 0, [0, 3]]);
				_displayControl.apply(this, [canvasWidth - this[cellSize], (x + 1) * this[cellSize], [1, 2]]);
			}

			for (y = 0; y < this[gridWidth]; y++) {
				_displayCell.apply(this, [x, y]);
			}
		}
	};

	/**
	 * Method to check if the player clicked on a control.
	 */
	_onControl = function(x, y) {
		var b, nbButtons = this._controlButtons.length;

		for (b = 0; b < nbButtons; b++) {
			if (
				this._controlButtons[b][0] <= x
				&& x <= this._controlButtons[b][0] + this[cellSize]
				&& this._controlButtons[b][1] <= y
				&& y <= this._controlButtons[b][1] + this[cellSize]
			) {
				break;
			}
		}

		return b == nbButtons ? null : b;
	};

	_clickEvent = function(e){
		var button = _onControl.apply(this, [e.layerX, e.layerY]),
			_shiftDown, _shiftRight, _shiftLeft, _shiftUp,
			row, nbBlobs;
		if (button == null) return;

		/**
		 * Methods to move a row or column in the 4 directions
		 */
		_shiftDown = function(col) {
			var last = this[grid][col][this[grid][col].length - 1];
			this[grid][col].pop();
			this[grid][col].unshift(last);
		};
		_shiftRight = function(row) {
			var old1, old2 = -1, r, last = this[grid].length - 1;
			for (r in this[grid]) {
				old1 = this[grid][r][row];
				this[grid][r][row] = ~old2 ? old2 : this[grid][(r + last) % this[grid].length][row];
				old2 = old1;
			}
		};
		_shiftUp = function(col) {
			var first = this[grid][col][0];
			this[grid][col].shift();
			this[grid][col].push(first);
		};
		_shiftLeft = function(row) {
			var r, first = this[grid][0][row];
			for (r = 0; r < this[gridWidth]; r++) {
				this[grid][r][row] = this[grid][(parseInt(r) + 1) % this[grid].length][row];
			}
			this[grid][this[grid].length - 1][row] = first;
		};

		row = 0|button/4;
		switch (button % 4) {
			case 0:
				_shiftDown.apply(this, [row]);
				break;
			case 1:
				_shiftRight.apply(this, [row]);
				break;
			case 2:
				_shiftLeft.apply(this, [row]);
				break;
			case 3:
				_shiftUp.apply(this, [row]);
				break;
		};

		nbBlobs = _detectBlobs.apply(this);
		_updateInformation.apply(this, ['moves', parseInt(_getInformation.apply(this, ['moves'])) + 1]);
		_displayGrid.apply(this, [false]);

		// Winning condition
		if (nbBlobs == this.nbColors) {
			B.removeEvent(this.parent, 'click', _clickEvent.bind(this));
		}
	};

	/**
	 * Method to set the click event on the game, to move the controls.
	 */
	_setEvents = function() {
		B.addEvent(this.parent, 'click', _clickEvent.bind(this));
	};

	/**
	 * Method to detect the blobs (like in picture processing) in the board.
	 * Returns the number of blobs of the board.
	 */
	_detectBlobs = function() {
		var blobs, nbBlobs = 0,
			x, y,
			nbCells = this[gridWidth] * this[gridWidth],
			_setBlobsAndPropagate;

		blobs = new Array(nbCells);

		_setBlobsAndPropagate = function(x, y, id) {
			var current = this[grid][x][y],
				currentIndex = y * this[gridWidth] + x,
				north = currentIndex - this[gridWidth],
				south = currentIndex + this[gridWidth],
				east = currentIndex + 1,
				west = currentIndex - 1;
			blobs[currentIndex] = id;

			if (north > 0 && blobs[north] == und && this[grid][x][y-1] == current) {
				_setBlobsAndPropagate.apply(this, [x, y-1, id]);
			}
			if (east%this[gridWidth] > currentIndex%this[gridWidth] && blobs[east] == und && this[grid][x+1][y] == current) {
				_setBlobsAndPropagate.apply(this, [x+1, y, id]);
			}
			if (south < nbCells && blobs[south] == und && this[grid][x][y+1] == current) {
				_setBlobsAndPropagate.apply(this, [x, y+1, id]);
			}
			if (Math.abs(west%this[gridWidth]) < currentIndex%this[gridWidth] && blobs[west] == und && this[grid][x-1][y] == current) {
				_setBlobsAndPropagate.apply(this, [x-1, y, id]);
			}
		};

		for (x = 0; x < this[gridWidth]; x++) {
			for (y = 0; y < this[gridWidth]; y++) {
				if (blobs[y * this[gridWidth] + x] != und) {
					continue;
				}

				_setBlobsAndPropagate.apply(this, [x, y, nbBlobs]);
				nbBlobs++;
			}
		}

		_updateInformation.apply(this, ['blobs', nbBlobs]);
		return nbBlobs;
	};

	window.Ad = ad;
})();
<|endoftext|>


<commit_before>import Ember from 'ember';
import { module, test } from 'qunit';
import Store from 'gatemedia-data/utils/store';

module('Store basics');

test('modelFor retrieves model from key', function (assert) {
  var store = Store.create({
    container: Ember.Object.create({
      lookupFactory: function (key) {
        return '%@:factory'.fmt(key);
      }
    })
  });

  assert.equal(store.modelFor('stuff'), 'model:stuff:factory', 'modelFor return simply named model factory');
  assert.equal(store.modelFor('test-stuff'), 'model:test-stuff:factory', 'modelFor return complex named model factory');
});

module('Store', {

  beforeEach: function () {
    var Model = Ember.Object.extend({
      meta: null,

      init: function () {
        this._super();
        this.set('meta', Ember.Object.create({
            isNew: 'untouched',
            isDirty: 'untouched',
            isDeleted: 'untouched'
          })
        );
      },

      cacheReset: 0,
      resetCaches: function () {
        this.incrementProperty('cacheReset');
      },

      dirty: function () {
        this.set('meta.isDirty', true);
      }
    });
    Model.reopenClass({

      eachRelation: function (callback, binding) {
        this.relations.forEach(function (name) {
          callback.call(binding || this, name, {
            type: name.singularize(),
            options: {}
          });
        }, this);
      }
    });

    this.store = Store.create({
      container: Ember.Object.create({
        lookupFactory: function (key) {
          switch (key) {
          case 'model:user':
            return Model.extend({ '__modelFor__': key }).reopenClass({ relations: [ 'posts' ] });
          case 'model:post':
            return Model.extend({ '__modelFor__': key }).reopenClass({ relations: [ 'comments' ]  });
          case 'model:comment':
            return Model.extend({ '__modelFor__': key }).reopenClass({ relations: [] });
          default:
            return Model.extend({ '__modelFor__': key }).reopenClass({ relations: [] });
          }
        }
      })
    });
  }
});

test('createRecord expects data with ID', function (assert) {
  var self = this;
  assert.throws(function () {
    self.store.createRecord('stuff');
  },
  new Error('Assertion Failed: Missing record id'),
  'Missing data ID fails');

  assert.throws(function () {
    self.store.createRecord('stuff', { stuff: 'Hi' });
  },
  new Error('Assertion Failed: Missing record id'),
  'Missing data ID fails');
});

test('createRecord works & cache created instance if not embedded', function (assert) {
  var record = this.store.createRecord('stuff', {
    id: 42,
    stuff: 'Hi'
  });
  assert.equal(record.get('__modelFor__'), 'model:stuff', 'Record belongs to expected model');
  assert.equal(record.get('meta.isNew'), 'untouched', 'Record has meta.isNew untouched');
  assert.equal(record.get('meta.isDirty'), 'untouched', 'Record has meta.isDirty untouched');
  assert.equal(record.get('meta.isDeleted'), 'untouched', 'Record has meta.isDeleted untouched');
  assert.deepEqual(record.get('_data'), {
    id: 42,
    stuff: 'Hi'
  }, 'Record attributes are stored raw');
  assert.deepEqual(Ember.keys(this.store._cache.stuff), ['42'], 'Record is cached');

  this.store.createRecord('stuff', {
    id: 36,
    stuff: 'Hi world'
  }, {
    _embeddedContainer: 'something'
  });
  assert.deepEqual(Ember.keys(this.store._cache.stuff), ['42'], 'Last record was not cached');

  this.store.createRecord('stuff', {
    id: 75,
    stuff: 'Hello'
  });
  assert.deepEqual(Ember.keys(this.store._cache.stuff), ['42','75'], 'Last record was cached');
});

test('createRecord accepts extra data', function (assert) {
  var record = this.store.createRecord('stuff', {
    id: 42,
    stuff: 'Hi'
  }, {
    extra: 'Yeah'
  });
  assert.deepEqual(record.get('_data'), {
    id: 42,
    stuff: 'Hi'
  }, 'Record attributes are stored raw');
  assert.equal(record.get('extra'), 'Yeah', 'Extra data are set as record properties');
});

test('instanciate returns a record which is marked new & dirty', function (assert) {
  var done = assert.async();
  var record = this.store.instanciate('stuff', {
    id: 48
  });

  assert.equal(record.get('meta.isNew'), true, 'Record has meta.isNew false');
  assert.equal(record.get('meta.isDirty'), 'untouched', 'Record has meta.isDirty untouched');
  assert.equal(record.get('meta.isDeleted'), 'untouched', 'Record has meta.isDeleted untouched');
  Ember.run.next(this, function () {
    assert.equal(record.get('meta.isDirty'), true, 'Record has meta.isDirty true');

    done();
  });
});

test('load instanciate new record but meta.isNew is false', function (assert) {
  var record = this.store.load('stuff', {
    id: 42,
    stuff: 'Hop'
  });
  assert.equal(record.get('__modelFor__'), 'model:stuff', 'Record belongs to expected model');
  assert.equal(record.get('meta.isNew'), false, 'Record has meta.isNew false');
  assert.equal(record.get('meta.isDirty'), 'untouched', 'Record has meta.isDirty untouched');
  assert.equal(record.get('meta.isDeleted'), 'untouched', 'Record has meta.isDeleted untouched');
  assert.deepEqual(record.get('_data'), {
    id: 42,
    stuff: 'Hop'
  }, 'Record attributes are stored raw');
  assert.equal(record.get('cacheReset'), 1, 'Record caches are reset');
  assert.deepEqual(Ember.keys(this.store._cache.stuff), ['42'], 'Record is cached');
});

test('loadMany instanciate new records', function (assert) {
  var records = this.store.loadMany('stuff', [{
    id: 12,
    stuff: 'Hop'
  }, {
    id: 34,
    stuff: 'Yo'
  }]);
  assert.equal(records.length, 2, '2 records were created');
  records.forEach(function (record) {
    assert.equal(record.get('__modelFor__'), 'model:stuff', 'Record belongs to expected model');
    assert.equal(record.get('meta.isNew'), false, 'Record has meta.isNew false');
  });
  assert.deepEqual(Ember.keys(this.store._cache.stuff), ['12','34'], 'Record is cached');
});

test('sideLoad loads extra data', function (assert) {
  var data = {
    'user': {
      'id': 42,
      'first_name': 'John',
      'last_name': 'Doe',
      'post_ids': [ 1, 2, 3 ]
    },
    'comments': [{
      'id': 11,
      'text': 'Why not...'
    }, {
      'id': 12,
      'text': 'Incredible!'
    }, {
      'id': 13,
      'text': 'You think so?'
    }],
    'posts': [{
      'id': 1,
      'title': 'My first post',
      'comment_ids': [ 11 ]
    }, {
      'id': 2,
      'title': 'My second post',
      'comment_ids': []
    }, {
      'id': 3,
      'title': 'Yet another post',
      'comment_ids': [ 12, 13 ]
    }]
  };
  this.store.load('user', data);

  assert.ok(!Ember.isNone(this.store._cache.user), 'User records cache is defined');
  assert.deepEqual(Ember.keys(this.store._cache.user), [ '42' ], '1 user record cached');
  assert.ok(!Ember.isNone(this.store._cache.post), 'Post records cache is defined');
  assert.deepEqual(Ember.keys(this.store._cache.post), [ '1', '2', '3' ], '3 post records cached');
  assert.ok(!Ember.isNone(this.store._cache.comment), 'Comment records cache is defined');
  assert.deepEqual(Ember.keys(this.store._cache.comment), [ '11', '12', '13' ], '3 comment records cached');
});

module('Store find', {
  setup: function () {
    var Model = Ember.Object.extend({
      meta: null,

      init: function () {
        this._super();
        this.set('meta', Ember.Object.create({
            isNew: 'untouched',
            isDirty: 'untouched',
            isDeleted: 'untouched'
          })
        );
      },

      cacheReset: 0,
      resetCaches: function () {
        this.incrementProperty('cacheReset');
      },

      dirty: function () {
        this.set('meta.isDirty', true);
      }
    });

    this.store = Store.create({
      container: Ember.Object.create({
        lookupFactory: function (key) {
          return Model.extend({
            '__modelFor__': key
          });
        }
      }),
      adapter: Ember.Object.create({
        calls: [],

        find: function (model, request, result) {
          this.calls.push({
            model: model,
            request: request,
            result: result
          });
        }
      })
    });
  }
});

test('find one can run async', function (assert) {
  var result = this.store.find('stuff', 1);

  assert.ok(Ember.canInvoke(result, 'then'), 'find result is a promise');
  checkFindOne.call(this, assert, 1, true);
});

test('find one (string key) can run async', function (assert) {
  var result = this.store.find('stuff', '1');

  assert.ok(Ember.canInvoke(result, 'then'), 'find result is a promise');
  checkFindOne.call(this, assert, '1', true);
});

test('find one can run sync', function (assert) {
  var result = this.store.find('stuff', 1, null, { sync: true });

  assert.ok(!Ember.canInvoke(result, 'then'), 'find result is not a promise');
  checkFindOne.call(this, assert, 1, false);
});

test('find one (string key) can run sync', function (assert) {
  var result = this.store.find('stuff', '1', null, { sync: true });

  assert.ok(!Ember.canInvoke(result, 'then'), 'find result is not a promise');
  checkFindOne.call(this, assert, '1', false);
});

function checkFindOne (assert, id, async) {
  assert.equal(this.store.adapter.calls.length, 1, 'adapter has been called once');
  var call = this.store.adapter.calls[0];
  assert.deepEqual(call.model, { key: 'stuff', ids: id, parent: async ? undefined : null },
    'adapter.find has been called with expected model args');
  assert.deepEqual(call.request.findMany, false, 'adapter.find has been called for a single entity');
  assert.equal(call.request.async, async, 'adapter.find has been called sync');
  assert.deepEqual(call.request.options, async ? {} : { sync: true }, 'adapter.find has been called with expected options');
}

test('find many can run async', function (assert) {
  var result = this.store.find('stuff', [ 1, 2, 3 ]);

  assert.ok(Ember.canInvoke(result, 'then'), 'find result is a promise');
  checkFindMany.call(this, assert, [ 1, 2, 3 ], true);
});

test('find many can run sync', function (assert) {
  var result = this.store.find('stuff', [ 1, 2, 3 ], null, { sync: true });

  assert.ok(!Ember.canInvoke(result, 'then'), 'find result is not a promise');
  checkFindMany.call(this, assert, [ 1, 2, 3 ], false);
});

test('find many (filter) can run async', function (assert) {
  var result = this.store.find('stuff', { matching: 'bam' });

  assert.ok(Ember.canInvoke(result, 'then'), 'find result is a promise');
  checkFindMany.call(this, assert, [], true, {
    "params": {
      "matching": "bam"
    }
  });
});

test('find many (filter) can run sync', function (assert) {
  var result = this.store.find('stuff', { matching: 'bam' }, null, { sync: true });

  assert.ok(!Ember.canInvoke(result, 'then'), 'find result is not a promise');
  checkFindMany.call(this, assert, [], false, {
    "params": {
      "matching": "bam"
    }
  });
});

function checkFindMany (assert, ids, async, options) {
  assert.equal(this.store.adapter.calls.length, 1, 'adapter has been called once');
  var call = this.store.adapter.calls[0];
  assert.deepEqual(call.model, { key: 'stuff', ids: ids, parent: async ? undefined : null },
    'adapter.find has been called with expected model args');
  assert.deepEqual(call.request.findMany, true, 'adapter.find has been called for many entities');
  assert.equal(call.request.async, async, 'adapter.find has been called ' + (async ? 'async' : 'sync'));
  assert.deepEqual(call.request.options, Ember.merge(async ? {} : {
    sync: true
  }, options), 'adapter.find has been called with expected options');
}
<commit_msg>Track log changes in UT<commit_after>import Ember from 'ember';
import { module, test } from 'qunit';
import Store from 'gatemedia-data/utils/store';

module('Store basics');

test('modelFor retrieves model from key', function (assert) {
  var store = Store.create({
    container: Ember.Object.create({
      lookupFactory: function (key) {
        return '%@:factory'.fmt(key);
      }
    })
  });

  assert.equal(store.modelFor('stuff'), 'model:stuff:factory', 'modelFor return simply named model factory');
  assert.equal(store.modelFor('test-stuff'), 'model:test-stuff:factory', 'modelFor return complex named model factory');
});

module('Store', {

  beforeEach: function () {
    var Model = Ember.Object.extend({
      meta: null,

      init: function () {
        this._super();
        this.set('meta', Ember.Object.create({
            isNew: 'untouched',
            isDirty: 'untouched',
            isDeleted: 'untouched'
          })
        );
      },

      cacheReset: 0,
      resetCaches: function () {
        this.incrementProperty('cacheReset');
      },

      dirty: function () {
        this.set('meta.isDirty', true);
      }
    });
    Model.reopenClass({

      eachRelation: function (callback, binding) {
        this.relations.forEach(function (name) {
          callback.call(binding || this, name, {
            type: name.singularize(),
            options: {}
          });
        }, this);
      }
    });

    this.store = Store.create({
      container: Ember.Object.create({
        lookupFactory: function (key) {
          switch (key) {
          case 'model:user':
            return Model.extend({ '__modelFor__': key }).reopenClass({ relations: [ 'posts' ] });
          case 'model:post':
            return Model.extend({ '__modelFor__': key }).reopenClass({ relations: [ 'comments' ]  });
          case 'model:comment':
            return Model.extend({ '__modelFor__': key }).reopenClass({ relations: [] });
          default:
            return Model.extend({ '__modelFor__': key }).reopenClass({ relations: [] });
          }
        }
      })
    });
  }
});

test('createRecord expects data with ID', function (assert) {
  var self = this;
  assert.throws(function () {
    self.store.createRecord('stuff');
  },
  new Error('Assertion Failed: Missing record id (stuff)'),
  'Missing data ID fails');

  assert.throws(function () {
    self.store.createRecord('stuff', { stuff: 'Hi' });
  },
  new Error('Assertion Failed: Missing record id (stuff)'),
  'Missing data ID fails');
});

test('createRecord works & cache created instance if not embedded', function (assert) {
  var record = this.store.createRecord('stuff', {
    id: 42,
    stuff: 'Hi'
  });
  assert.equal(record.get('__modelFor__'), 'model:stuff', 'Record belongs to expected model');
  assert.equal(record.get('meta.isNew'), 'untouched', 'Record has meta.isNew untouched');
  assert.equal(record.get('meta.isDirty'), 'untouched', 'Record has meta.isDirty untouched');
  assert.equal(record.get('meta.isDeleted'), 'untouched', 'Record has meta.isDeleted untouched');
  assert.deepEqual(record.get('_data'), {
    id: 42,
    stuff: 'Hi'
  }, 'Record attributes are stored raw');
  assert.deepEqual(Ember.keys(this.store._cache.stuff), ['42'], 'Record is cached');

  this.store.createRecord('stuff', {
    id: 36,
    stuff: 'Hi world'
  }, {
    _embeddedContainer: 'something'
  });
  assert.deepEqual(Ember.keys(this.store._cache.stuff), ['42'], 'Last record was not cached');

  this.store.createRecord('stuff', {
    id: 75,
    stuff: 'Hello'
  });
  assert.deepEqual(Ember.keys(this.store._cache.stuff), ['42','75'], 'Last record was cached');
});

test('createRecord accepts extra data', function (assert) {
  var record = this.store.createRecord('stuff', {
    id: 42,
    stuff: 'Hi'
  }, {
    extra: 'Yeah'
  });
  assert.deepEqual(record.get('_data'), {
    id: 42,
    stuff: 'Hi'
  }, 'Record attributes are stored raw');
  assert.equal(record.get('extra'), 'Yeah', 'Extra data are set as record properties');
});

test('instanciate returns a record which is marked new & dirty', function (assert) {
  var done = assert.async();
  var record = this.store.instanciate('stuff', {
    id: 48
  });

  assert.equal(record.get('meta.isNew'), true, 'Record has meta.isNew false');
  assert.equal(record.get('meta.isDirty'), 'untouched', 'Record has meta.isDirty untouched');
  assert.equal(record.get('meta.isDeleted'), 'untouched', 'Record has meta.isDeleted untouched');
  Ember.run.next(this, function () {
    assert.equal(record.get('meta.isDirty'), true, 'Record has meta.isDirty true');

    done();
  });
});

test('load instanciate new record but meta.isNew is false', function (assert) {
  var record = this.store.load('stuff', {
    id: 42,
    stuff: 'Hop'
  });
  assert.equal(record.get('__modelFor__'), 'model:stuff', 'Record belongs to expected model');
  assert.equal(record.get('meta.isNew'), false, 'Record has meta.isNew false');
  assert.equal(record.get('meta.isDirty'), 'untouched', 'Record has meta.isDirty untouched');
  assert.equal(record.get('meta.isDeleted'), 'untouched', 'Record has meta.isDeleted untouched');
  assert.deepEqual(record.get('_data'), {
    id: 42,
    stuff: 'Hop'
  }, 'Record attributes are stored raw');
  assert.equal(record.get('cacheReset'), 1, 'Record caches are reset');
  assert.deepEqual(Ember.keys(this.store._cache.stuff), ['42'], 'Record is cached');
});

test('loadMany instanciate new records', function (assert) {
  var records = this.store.loadMany('stuff', [{
    id: 12,
    stuff: 'Hop'
  }, {
    id: 34,
    stuff: 'Yo'
  }]);
  assert.equal(records.length, 2, '2 records were created');
  records.forEach(function (record) {
    assert.equal(record.get('__modelFor__'), 'model:stuff', 'Record belongs to expected model');
    assert.equal(record.get('meta.isNew'), false, 'Record has meta.isNew false');
  });
  assert.deepEqual(Ember.keys(this.store._cache.stuff), ['12','34'], 'Record is cached');
});

test('sideLoad loads extra data', function (assert) {
  var data = {
    'user': {
      'id': 42,
      'first_name': 'John',
      'last_name': 'Doe',
      'post_ids': [ 1, 2, 3 ]
    },
    'comments': [{
      'id': 11,
      'text': 'Why not...'
    }, {
      'id': 12,
      'text': 'Incredible!'
    }, {
      'id': 13,
      'text': 'You think so?'
    }],
    'posts': [{
      'id': 1,
      'title': 'My first post',
      'comment_ids': [ 11 ]
    }, {
      'id': 2,
      'title': 'My second post',
      'comment_ids': []
    }, {
      'id': 3,
      'title': 'Yet another post',
      'comment_ids': [ 12, 13 ]
    }]
  };
  this.store.load('user', data);

  assert.ok(!Ember.isNone(this.store._cache.user), 'User records cache is defined');
  assert.deepEqual(Ember.keys(this.store._cache.user), [ '42' ], '1 user record cached');
  assert.ok(!Ember.isNone(this.store._cache.post), 'Post records cache is defined');
  assert.deepEqual(Ember.keys(this.store._cache.post), [ '1', '2', '3' ], '3 post records cached');
  assert.ok(!Ember.isNone(this.store._cache.comment), 'Comment records cache is defined');
  assert.deepEqual(Ember.keys(this.store._cache.comment), [ '11', '12', '13' ], '3 comment records cached');
});

module('Store find', {
  setup: function () {
    var Model = Ember.Object.extend({
      meta: null,

      init: function () {
        this._super();
        this.set('meta', Ember.Object.create({
            isNew: 'untouched',
            isDirty: 'untouched',
            isDeleted: 'untouched'
          })
        );
      },

      cacheReset: 0,
      resetCaches: function () {
        this.incrementProperty('cacheReset');
      },

      dirty: function () {
        this.set('meta.isDirty', true);
      }
    });

    this.store = Store.create({
      container: Ember.Object.create({
        lookupFactory: function (key) {
          return Model.extend({
            '__modelFor__': key
          });
        }
      }),
      adapter: Ember.Object.create({
        calls: [],

        find: function (model, request, result) {
          this.calls.push({
            model: model,
            request: request,
            result: result
          });
        }
      })
    });
  }
});

test('find one can run async', function (assert) {
  var result = this.store.find('stuff', 1);

  assert.ok(Ember.canInvoke(result, 'then'), 'find result is a promise');
  checkFindOne.call(this, assert, 1, true);
});

test('find one (string key) can run async', function (assert) {
  var result = this.store.find('stuff', '1');

  assert.ok(Ember.canInvoke(result, 'then'), 'find result is a promise');
  checkFindOne.call(this, assert, '1', true);
});

test('find one can run sync', function (assert) {
  var result = this.store.find('stuff', 1, null, { sync: true });

  assert.ok(!Ember.canInvoke(result, 'then'), 'find result is not a promise');
  checkFindOne.call(this, assert, 1, false);
});

test('find one (string key) can run sync', function (assert) {
  var result = this.store.find('stuff', '1', null, { sync: true });

  assert.ok(!Ember.canInvoke(result, 'then'), 'find result is not a promise');
  checkFindOne.call(this, assert, '1', false);
});

function checkFindOne (assert, id, async) {
  assert.equal(this.store.adapter.calls.length, 1, 'adapter has been called once');
  var call = this.store.adapter.calls[0];
  assert.deepEqual(call.model, { key: 'stuff', ids: id, parent: async ? undefined : null },
    'adapter.find has been called with expected model args');
  assert.deepEqual(call.request.findMany, false, 'adapter.find has been called for a single entity');
  assert.equal(call.request.async, async, 'adapter.find has been called sync');
  assert.deepEqual(call.request.options, async ? {} : { sync: true }, 'adapter.find has been called with expected options');
}

test('find many can run async', function (assert) {
  var result = this.store.find('stuff', [ 1, 2, 3 ]);

  assert.ok(Ember.canInvoke(result, 'then'), 'find result is a promise');
  checkFindMany.call(this, assert, [ 1, 2, 3 ], true);
});

test('find many can run sync', function (assert) {
  var result = this.store.find('stuff', [ 1, 2, 3 ], null, { sync: true });

  assert.ok(!Ember.canInvoke(result, 'then'), 'find result is not a promise');
  checkFindMany.call(this, assert, [ 1, 2, 3 ], false);
});

test('find many (filter) can run async', function (assert) {
  var result = this.store.find('stuff', { matching: 'bam' });

  assert.ok(Ember.canInvoke(result, 'then'), 'find result is a promise');
  checkFindMany.call(this, assert, [], true, {
    "params": {
      "matching": "bam"
    }
  });
});

test('find many (filter) can run sync', function (assert) {
  var result = this.store.find('stuff', { matching: 'bam' }, null, { sync: true });

  assert.ok(!Ember.canInvoke(result, 'then'), 'find result is not a promise');
  checkFindMany.call(this, assert, [], false, {
    "params": {
      "matching": "bam"
    }
  });
});

function checkFindMany (assert, ids, async, options) {
  assert.equal(this.store.adapter.calls.length, 1, 'adapter has been called once');
  var call = this.store.adapter.calls[0];
  assert.deepEqual(call.model, { key: 'stuff', ids: ids, parent: async ? undefined : null },
    'adapter.find has been called with expected model args');
  assert.deepEqual(call.request.findMany, true, 'adapter.find has been called for many entities');
  assert.equal(call.request.async, async, 'adapter.find has been called ' + (async ? 'async' : 'sync'));
  assert.deepEqual(call.request.options, Ember.merge(async ? {} : {
    sync: true
  }, options), 'adapter.find has been called with expected options');
}
<|endoftext|>


<commit_before>'use strict';

var express = require('express');
var router = express.Router();
var util = require('util');
var request = require('request');
var lxc = require('../lxc');
var doapi = require('../doapi')();

var label2runner = {};
var workers = [];
var isCheckingWorkers = false;

var dopletNewID = 0;

var checkDroplet = function(id, time){
	time = time || 30000;
	doapi.dropletInfo(id, function(data){
		var worker = JSON.parse(data)['droplet'];
		if(worker.status == 'active'){
			workers.push(makeWokerObj(worker));
			isCheckingWorkers = false;
			return ch;
		}else if(worker.status == 'new'){
			setTimeout(function(){
				checkDroplet(id)
			}, time);
		}
	});
};

var workerCreate = function(){
	doapi.dropletCreate({
		name: 'clworker'+(Math.random()*100).toString().replace('.',''),
		image: '17375637'
	}, function(data){
		data = JSON.parse(data);
		doapi.dropletSetTag('clworker', data.droplet.id, function(data){
			console.log('Set tag data:', data)
			dopletNewID = data.droplet.id;
			setTimeout(function(){checkDroplet(dropletNewId)}, 10000);
		});
	});
};

var workerDestroy = function(worker){
	worker = worker || workers.pop();
	doapi.dropletDestroy(worker.id, function(){});
};

var checkWorkersBalance = function(){
	if(isCheckingWorkers) return false;

	isCheckingWorkers = true;
	if(!workers){
		return workerCreate();
	}
	if(!workers[workers.length-1].availrunners.length){
		return workerCreate();
	}
	if(!workers[workers.length-1].availrunners.length && !workers[workers.length-2].availrunners.length){

		workerDestroy();
	}
	isCheckingWorkers = false;
};

var start

var ramPercentUsed = function(ip, callback){

	return lxc.exec(
		"python3 -c \"a=`head /proc/meminfo|grep MemAvail|grep -Po '\\d+'`;t=`head /proc/meminfo|grep MemTotal|grep -Po '\\d+'`;print(round(((t-a)/t)*100, 2))\"",
		ip,
		callback
	);
};

var runnerFree = function(runner){
	lxc.stop(runner.name, runner.worker.ip);
	runner.worker.usedrunner--;
	if(runner.hasOwnProperty('timeout')){
		clearTimeout(runner.timeout);
	}
	delete label2runner[runner.label];

	startRunners(runner.worker);
};

var lxcTimeout = function(runner, time){
	time = time || 60000 // 900000; // 15 minutes

	if(runner.hasOwnProperty('timeout')){
		clearTimeout(runner.timeout);
	}

	return runner.timeout = setTimeout(function(){
		runnerFree(runner)
	}, time);
};

var runner = function(req, res, runner){

	var httpOptions = {
		url: 'http://' + runner.worker.ip,
		headers: {
			Host: runner.name
		},
		body: JSON.stringify({
			code: req.body.code
		})
	};

	return request.post(httpOptions, function(error, response, body){
		// console.log('runner response:', arguments)
		body = JSON.parse(body);

		body['ip'] = getAvailrunner(runner).label;
		lxcTimeout(runner);
		return res.json(body);

	});
};

var makeWokerObj = function(worker){
	worker.networks.v4.forEach(function(value){
		worker[value.type+'IP'] = value.ip_address;
	});
	worker.availrunners = [];
	worker.ip = worker.privateIP;
	worker.usedrunner = 0;
	worker.index = workers.length,
	worker.getRunner = function(){
		if(this.availrunners === 0) return false;
		var runner = this.availrunners.pop();
		this.usedrunner++;
		label2runner[runner.label] = runner;

		return runner;
	}
	return worker;
};

var initWorkers = function(){
	doapi.dropletsByTag('clworker', function(data){
		data = JSON.parse(data);
		data['droplets'].forEach(function(value){
			startRunners(workers[workers.push(makeWokerObj(value))-1]);
		});
	});
};

var getAvailrunner = function(runner){
	var i = -1;
	while(workers[++i].availrunners.length && ( runner && workers[i].index < runner.worker.index )){
		if(runner) runnerFree(runner);
		return workers[i].getRunner();
	}
	if(runner) return runner;

};

var startRunners = function(worker, stopPercent){
	console.log('starting runners on', worker.name)
	stopPercent = stopPercent || 15;
	ramPercentUsed(worker.ip, function(usedMemPercent){
		if(usedMemPercent < stopPercent ){
			var name = 'crunner-'+(Math.random()*100).toString().replace('.','');
			return lxc.startEphemeral(name, 'crunner0', worker.ip, function(data){
				if( !data.ip ) return setTimeout(startRunners(worker),0);
				console.log('started runner')

				worker.availrunners.push({
					ip: data.ip,
					name: name,
					worker: worker,
					label: worker.name + ':' + name
				});
				return setTimeout(startRunners(worker, stopPercent), 0);
			});
		}else{
			checkWorkersBalance();
			console.log('using', usedMemPercent, 'percent memory, stopping runner creation!', worker.availrunners.length, 'created');
		}
	});
};

initWorkers();

// router.get('/start/:name', function(req, res, next){
// 	return lxc.start(req.params.name, function(data){
// 		if(!data){
// 			return res.json({
// 				status: 500,
// 				name: req.params.name,
// 				message: data
// 			});
// 		}else{
// 			res.json({});
// 		}
// 	});
// });

// router.get('/live/:template/:name', function(req, res, next){
// 	return lxc.startEphemeral(req.params.name, req.params.template, function (data) {
// 		console.log('live', arguments);
// 		return res.json(data);
// 	});
// });

// router.get('/clone/:template/:name', function(req, res, next){
// 	return lxc.clone(req.params.name, req.params.template, function(data){
// 		console.log('clone', arguments);
// 		if( data.match(/Created runner/) ){
// 			return res.json({status: 200});
// 		}else{
// 			return res.json({status: 500, message: data});
// 		}
// 	});
// });

// router.get('/destroy/:name', function(req, res, next){
// 	return lxc.destroy(req.params.name, function(data){
// 		console.log('destroy', arguments);
// 		if(data){
// 			return res.json({status: 500, message: data});
// 		}else{
// 			return res.json({status: 200});
// 		}
// 	});
// });

// router.get('/info/:name', function(req, res, next){
// 	return lxc.info(req.params.name, function(data){
// 		return res.json(data);
// 	});
// });

// router.get('/list', function(req, res, next) {
// 	return lxc.list(workers.clworker0.ip, function(data){
// 		return res.json(data);
// 	});
// });

router.get('/stop/:name', function(req, res, next){
	return lxc.stop(req.params.name, function(data){
		console.log('stop', arguments);
		if(data){
			return res.json({status: 500, name: req.params.name, message: data});
		}else{
			return res.json({status: 200});
		}
	});
});

router.get('/liststuff', function(req, res, next){
	var obj = util.inspect(workers, {depth: 4});
	res.send(obj);
});

router.post('/run/:ip?', function doRun(req, res, next){

	var runner = label2runner[req.params.ip] || getAvailrunner();

	return runner(req, res, runner);
});

module.exports = router;
<commit_msg>stuff<commit_after>'use strict';

var express = require('express');
var router = express.Router();
var util = require('util');
var request = require('request');
var lxc = require('../lxc');
var doapi = require('../doapi')();

var label2runner = {};
var workers = [];
var isCheckingWorkers = false;

var dopletNewID = 0;

var checkDroplet = function(id, time){
	time = time || 30000;
	doapi.dropletInfo(id, function(data){
		var worker = JSON.parse(data)['droplet'];
		if(worker.status == 'active'){
			workers.push(makeWokerObj(worker));
			isCheckingWorkers = false;
			return ch;
		}else if(worker.status == 'new'){
			setTimeout(function(){
				checkDroplet(id)
			}, time);
		}
	});
};

var workerCreate = function(){
	doapi.dropletCreate({
		name: 'clworker'+(Math.random()*100).toString().replace('.',''),
		image: '17375637'
	}, function(data){
		data = JSON.parse(data);
		dopletNewID = data.droplet.id;
		doapi.dropletSetTag('clworker', data.droplet.id, function(data){
			console.log('Set tag data:', data)
			setTimeout(function(){checkDroplet(dropletNewId)}, 10000);
		});
	});
};

var workerDestroy = function(worker){
	worker = worker || workers.pop();
	doapi.dropletDestroy(worker.id, function(){});
};

var checkWorkersBalance = function(){
	if(isCheckingWorkers) return false;

	isCheckingWorkers = true;
	if(!workers){
		return workerCreate();
	}
	if(!workers[workers.length-1].availrunners.length){
		return workerCreate();
	}
	if(!workers[workers.length-1].availrunners.length && !workers[workers.length-2].availrunners.length){

		workerDestroy();
	}
	isCheckingWorkers = false;
};

var start

var ramPercentUsed = function(ip, callback){

	return lxc.exec(
		"python3 -c \"a=`head /proc/meminfo|grep MemAvail|grep -Po '\\d+'`;t=`head /proc/meminfo|grep MemTotal|grep -Po '\\d+'`;print(round(((t-a)/t)*100, 2))\"",
		ip,
		callback
	);
};

var runnerFree = function(runner){
	lxc.stop(runner.name, runner.worker.ip);
	runner.worker.usedrunner--;
	if(runner.hasOwnProperty('timeout')){
		clearTimeout(runner.timeout);
	}
	delete label2runner[runner.label];

	startRunners(runner.worker);
};

var lxcTimeout = function(runner, time){
	time = time || 60000 // 900000; // 15 minutes

	if(runner.hasOwnProperty('timeout')){
		clearTimeout(runner.timeout);
	}

	return runner.timeout = setTimeout(function(){
		runnerFree(runner)
	}, time);
};

var runner = function(req, res, runner){

	var httpOptions = {
		url: 'http://' + runner.worker.ip,
		headers: {
			Host: runner.name
		},
		body: JSON.stringify({
			code: req.body.code
		})
	};

	return request.post(httpOptions, function(error, response, body){
		// console.log('runner response:', arguments)
		body = JSON.parse(body);

		body['ip'] = getAvailrunner(runner).label;
		lxcTimeout(runner);
		return res.json(body);

	});
};

var makeWokerObj = function(worker){
	worker.networks.v4.forEach(function(value){
		worker[value.type+'IP'] = value.ip_address;
	});
	worker.availrunners = [];
	worker.ip = worker.privateIP;
	worker.usedrunner = 0;
	worker.index = workers.length,
	worker.getRunner = function(){
		if(this.availrunners === 0) return false;
		var runner = this.availrunners.pop();
		this.usedrunner++;
		label2runner[runner.label] = runner;

		return runner;
	}
	return worker;
};

var initWorkers = function(){
	doapi.dropletsByTag('clworker', function(data){
		data = JSON.parse(data);
		data['droplets'].forEach(function(value){
			startRunners(workers[workers.push(makeWokerObj(value))-1]);
		});
	});
};

var getAvailrunner = function(runner){
	var i = -1;
	while(workers[++i].availrunners.length && ( runner && workers[i].index < runner.worker.index )){
		if(runner) runnerFree(runner);
		return workers[i].getRunner();
	}
	if(runner) return runner;

};

var startRunners = function(worker, stopPercent){
	console.log('starting runners on', worker.name)
	stopPercent = stopPercent || 15;
	ramPercentUsed(worker.ip, function(usedMemPercent){
		if(usedMemPercent < stopPercent ){
			var name = 'crunner-'+(Math.random()*100).toString().replace('.','');
			return lxc.startEphemeral(name, 'crunner0', worker.ip, function(data){
				if( !data.ip ) return setTimeout(startRunners(worker),0);
				console.log('started runner')

				worker.availrunners.push({
					ip: data.ip,
					name: name,
					worker: worker,
					label: worker.name + ':' + name
				});
				return setTimeout(startRunners(worker, stopPercent), 0);
			});
		}else{
			checkWorkersBalance();
			console.log('using', usedMemPercent, 'percent memory, stopping runner creation!', worker.availrunners.length, 'created');
		}
	});
};

initWorkers();

// router.get('/start/:name', function(req, res, next){
// 	return lxc.start(req.params.name, function(data){
// 		if(!data){
// 			return res.json({
// 				status: 500,
// 				name: req.params.name,
// 				message: data
// 			});
// 		}else{
// 			res.json({});
// 		}
// 	});
// });

// router.get('/live/:template/:name', function(req, res, next){
// 	return lxc.startEphemeral(req.params.name, req.params.template, function (data) {
// 		console.log('live', arguments);
// 		return res.json(data);
// 	});
// });

// router.get('/clone/:template/:name', function(req, res, next){
// 	return lxc.clone(req.params.name, req.params.template, function(data){
// 		console.log('clone', arguments);
// 		if( data.match(/Created runner/) ){
// 			return res.json({status: 200});
// 		}else{
// 			return res.json({status: 500, message: data});
// 		}
// 	});
// });

// router.get('/destroy/:name', function(req, res, next){
// 	return lxc.destroy(req.params.name, function(data){
// 		console.log('destroy', arguments);
// 		if(data){
// 			return res.json({status: 500, message: data});
// 		}else{
// 			return res.json({status: 200});
// 		}
// 	});
// });

// router.get('/info/:name', function(req, res, next){
// 	return lxc.info(req.params.name, function(data){
// 		return res.json(data);
// 	});
// });

// router.get('/list', function(req, res, next) {
// 	return lxc.list(workers.clworker0.ip, function(data){
// 		return res.json(data);
// 	});
// });

router.get('/stop/:name', function(req, res, next){
	return lxc.stop(req.params.name, function(data){
		console.log('stop', arguments);
		if(data){
			return res.json({status: 500, name: req.params.name, message: data});
		}else{
			return res.json({status: 200});
		}
	});
});

router.get('/liststuff', function(req, res, next){
	var obj = util.inspect(workers, {depth: 4});
	res.send(obj);
});

router.post('/run/:ip?', function doRun(req, res, next){

	var runner = label2runner[req.params.ip] || getAvailrunner();

	return runner(req, res, runner);
});

module.exports = router;
<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash');

/**
 * KongNode.js
 *
 * @description :: TODO: You might write a short summary of how this model works and what it represents here.
 * @docs        :: http://sailsjs.org/#!documentation/models
 */
var defaultModel = _.merge(_.cloneDeep(require('../base/Model')), {
    tableName: "konga_settings",
    autoPK: false,
    attributes: {
        id: {
            type: 'integer',
            primaryKey: true,
            unique: true,
            autoIncrement: true
        },
        data: {
            type: 'json'
        },
    },
    seedData: [
        {
            "data": {
                signup_enable: true,
                signup_require_activation: false,
                info_polling_interval: 5000,
                email_default_sender_name: 'KONGA',
                email_default_sender: 'konga@konga.test',
                email_notifications: false,
                default_transport: 'sendmail',
                notify_when: {
                    node_down: {
                        title: "A node is down or unresponsive",
                        description: "Health checks must be enabled for the nodes that need to be monitored.",
                        active: false
                    },
                    api_down: {
                        title: "An API is down or unresponsive",
                        description: "Health checks must be enabled for the APIs that need to be monitored.",
                        active: false
                    }
                },

                integrations : [
                    {
                        id   : "slack",
                        name : "Slack",
                        image : "slack_rbg.png",
                        config : {
                            enabled : false,
                            fields : [{
                                id : "slack_webhook_url",
                                name : "Slack Webhook URL",
                                type : "text",
                                required : true,
                                value : ""
                            }],
                            slack_webhook_url : ""
                        }
                    }
                ],

                user_permissions: {
                    apis: {
                        create: false,
                        read: true,
                        update: false,
                        delete: false
                    },
                    consumers: {
                        create: false,
                        read: true,
                        update: false,
                        delete: false
                    },
                    plugins: {
                        create: false,
                        read: true,
                        update: false,
                        delete: false
                    },
                    upstreams: {
                        create: false,
                        read: true,
                        update: false,
                        delete: false
                    },
                    certificates: {
                        create: false,
                        read: true,
                        update: false,
                        delete: false
                    },
                    connections: {
                        create: false,
                        read: true,
                        update: false,
                        delete: false
                    },
                    users: {
                        create: false,
                        read: true,
                        update: false,
                        delete: false
                    }
                }
            }
        },
    ]
});

var mongoModel = function () {
    var obj = _.cloneDeep(defaultModel)
    delete obj.autoPK
    delete obj.attributes.id
    return obj;
}

module.exports = sails.config.models.connection == 'mongo' ? mongoModel() : defaultModel
<commit_msg>fix typo<commit_after>'use strict';

var _ = require('lodash');

/**
 * KongNode.js
 *
 * @description :: TODO: You might write a short summary of how this model works and what it represents here.
 * @docs        :: http://sailsjs.org/#!documentation/models
 */
var defaultModel = _.merge(_.cloneDeep(require('../base/Model')), {
    tableName: "konga_settings",
    autoPK: false,
    attributes: {
        id: {
            type: 'integer',
            primaryKey: true,
            unique: true,
            autoIncrement: true
        },
        data: {
            type: 'json'
        },
    },
    seedData: [
        {
            "data": {
                signup_enable: true,
                signup_require_activation: false,
                info_polling_interval: 5000,
                email_default_sender_name: 'KONGA',
                email_default_sender: 'konga@konga.test',
                email_notifications: false,
                default_transport: 'sendmail',
                notify_when: {
                    node_down: {
                        title: "A node is down or unresponsive",
                        description: "Health checks must be enabled for the nodes that need to be monitored.",
                        active: false
                    },
                    api_down: {
                        title: "An API is down or unresponsive",
                        description: "Health checks must be enabled for the APIs that need to be monitored.",
                        active: false
                    }
                },

                integrations : [
                    {
                        id   : "slack",
                        name : "Slack",
                        image : "slack_rgb.png",
                        config : {
                            enabled : false,
                            fields : [{
                                id : "slack_webhook_url",
                                name : "Slack Webhook URL",
                                type : "text",
                                required : true,
                                value : ""
                            }],
                            slack_webhook_url : ""
                        }
                    }
                ],

                user_permissions: {
                    apis: {
                        create: false,
                        read: true,
                        update: false,
                        delete: false
                    },
                    consumers: {
                        create: false,
                        read: true,
                        update: false,
                        delete: false
                    },
                    plugins: {
                        create: false,
                        read: true,
                        update: false,
                        delete: false
                    },
                    upstreams: {
                        create: false,
                        read: true,
                        update: false,
                        delete: false
                    },
                    certificates: {
                        create: false,
                        read: true,
                        update: false,
                        delete: false
                    },
                    connections: {
                        create: false,
                        read: true,
                        update: false,
                        delete: false
                    },
                    users: {
                        create: false,
                        read: true,
                        update: false,
                        delete: false
                    }
                }
            }
        },
    ]
});

var mongoModel = function () {
    var obj = _.cloneDeep(defaultModel)
    delete obj.autoPK
    delete obj.attributes.id
    return obj;
}

module.exports = sails.config.models.connection == 'mongo' ? mongoModel() : defaultModel
<|endoftext|>