<commit_before>var torque = require('../');
var cartocss = require('./cartocss_render');
var Profiler = require('../profiler');
var carto = global.carto || require('carto');

  var TAU = Math.PI * 2;
  var DEFAULT_CARTOCSS = [
    '#layer {',
    '  marker-fill: #662506;',
    '  marker-width: 4;',
    '  [value > 1] { marker-fill: #FEE391; }',
    '  [value > 2] { marker-fill: #FEC44F; }',
    '  [value > 3] { marker-fill: #FE9929; }',
    '  [value > 4] { marker-fill: #EC7014; }',
    '  [value > 5] { marker-fill: #CC4C02; }',
    '  [value > 6] { marker-fill: #993404; }',
    '  [value > 7] { marker-fill: #662506; }',
    '}'
  ].join('\n');

  var COMP_OP_TO_CANVAS = {
    "src": 'source-over',
    "src-over": 'source-over',
    "dst-over": 'destination-over',
    "src-in": 'source-in',
    "dst-in": 'destination-in',
    "src-out": 'source-out',
    "dst-out": 'destination-out',
    "src-atop": 'source-atop',
    "dst-atop": 'destination-atop',
    "xor": 'xor',
    "darken": 'darken',
    "lighten": 'lighten'
  }

  function compop2canvas(compop) {
    return COMP_OP_TO_CANVAS[compop] || compop;
  }

  //
  // this renderer just render points depending of the value
  //
  function PointRenderer(canvas, options) {
    if (!canvas) {
      throw new Error("canvas can't be undefined");
    }
    this.options = options;
    this._canvas = canvas;
    this._ctx = canvas.getContext('2d');
    this._sprites = []; // sprites per layer
    this._shader = null;
    this.setCartoCSS(this.options.cartocss || DEFAULT_CARTOCSS);
    this.TILE_SIZE = 256;
    this._icons = {};
    this._forcePoints = false;
  }

  torque.extend(PointRenderer.prototype, torque.Event, {

    clearCanvas: function() {
      var canvas = this._canvas;
      var color = this._Map['-torque-clear-color']
      // shortcut for the default value
      if (color  === "rgba(255, 255, 255, 0)" || !color) {
        this._canvas.width = this._canvas.width;
      } else {
        var ctx = this._ctx;
        ctx.setTransform(1, 0, 0, 1, 0, 0);
        var compop = this._Map['comp-op']
        ctx.globalCompositeOperation = compop2canvas(compop);
        ctx.fillStyle = color;
        ctx.fillRect(0, 0, canvas.width, canvas.height);
      }
    },

    setCanvas: function(canvas) {
      this._canvas = canvas;
      this._ctx = canvas.getContext('2d');
    },

    //
    // sets the cartocss style to render stuff
    //
    setCartoCSS: function(cartocss) {
      // clean sprites
      this.setShader(new carto.RendererJS().render(cartocss));
    },

    setShader: function(shader) {
      // clean sprites
      this._sprites = [];
      this._shader = shader;
      this._Map = this._shader.getDefault().getStyle({}, { zoom: 0 });
    },

    clearSpriteCache: function() {
      this._sprites = [];
    },

    //
    // generate sprite based on cartocss style
    //
    generateSprite: function(shader, value, shaderVars) {
      var self = this;
      var prof = Profiler.metric('torque.renderer.point.generateSprite').start();
      var st = shader.getStyle({
        value: value
      }, shaderVars);

      var pointSize = st['marker-width'];
      if (!pointSize) {
        return null;
      }

      if (st['marker-opacity'] === 0 && !st['marker-line-opacity']) {
        return null;
      }

      var canvas = this._createCanvas();
      // take into account the exterior ring to calculate the size
      var canvasSize = (st['marker-line-width'] || 0) + pointSize*2;
      var ctx = canvas.getContext('2d');
      var w = ctx.width = canvas.width = ctx.height = canvas.height = Math.ceil(canvasSize);
      ctx.translate(w/2, w/2);

      var img_names = this._shader.getImageURLs();
      this._preloadIcons(img_names);
      if(img_names.length>0 && this._icons.itemsToLoad==0) {
        var img_name = st["marker-file"] || st["point-file"];
        var img = this._icons[img_name];
        img.w = st['marker-width'] || img.width;
        img.h = st['marker-width'] || st['marker-height'];
        cartocss.renderSprite(ctx, img);
      }
      else {
        var mt = st['marker-type'];
        if (mt && mt === 'rectangle') {
          cartocss.renderRectangle(ctx, st);
        } else {
          cartocss.renderPoint(ctx, st);
        }
      }
      prof.end(true);
      if (torque.flags.sprites_to_images) {
        var i = this._createImage();
        i.src = canvas.toDataURL();
        return i;
      }
      return canvas;
    },

    //
    // renders all the layers (and frames for each layer) from cartocss
    //
    renderTile: function(tile, key) {
      var prof = Profiler.metric('torque.renderer.point.renderLayers').start();
      var layers = this._shader.getLayers();
      for(var i = 0, n = layers.length; i < n; ++i ) {
        var layer = layers[i];
        if (layer.name() !== "Map") {
          var sprites = this._sprites[i] || (this._sprites[i] = {});
          // frames for each layer
          for(var fr = 0; fr < layer.frames().length; ++fr) {
            var frame = layer.frames()[fr];
            var fr_sprites = sprites[frame] || (sprites[frame] = []);
            this._renderTile(tile, key - frame, frame, fr_sprites, layer);
          }
        }
      }
      prof.end(true);
    },

    _createCanvas: function() {
      return this.options.canvasClass
        ? new this.options.canvasClass()
        : document.createElement('canvas');
    },

    _createImage: function() {
      return this.options.imageClass
        ? new this.options.imageClass()
        : new Image();
    },

    //
    // renders a tile in the canvas for key defined in
    // the torque tile
    //
    _renderTile: function(tile, key, frame_offset, sprites, shader, shaderVars) {
      if(!this._canvas) return;

      var prof = Profiler.metric('torque.renderer.point.renderTile').start();
      var ctx = this._ctx;
      var blendMode = compop2canvas(shader.eval('comp-op')) || this.options.blendmode;
      if(blendMode) {
        ctx.globalCompositeOperation = blendMode;
      }
      if (this.options.cumulative && key > tile.maxDate) {
        //TODO: precache because this tile is not going to change
        key = tile.maxDate;
      }
      var tileMax = this.options.resolution * (this.TILE_SIZE/this.options.resolution - 1)
      var activePixels = tile.timeCount[key];
      if(activePixels) {
        var pixelIndex = tile.timeIndex[key];
        for(var p = 0; p < activePixels; ++p) {
          var posIdx = tile.renderDataPos[pixelIndex + p];
          var c = tile.renderData[pixelIndex + p];
          if(c) {
           var sp = sprites[c];
           if(sp === undefined) {
             sp = sprites[c] = this.generateSprite(shader, c, torque.extend({ zoom: tile.z, 'frame-offset': frame_offset }, shaderVars));
           }
           if (sp) {
             var x = tile.x[posIdx]- (sp.width >> 1);
             var y = tileMax - tile.y[posIdx]; // flip mercator
             ctx.drawImage(sp, x, y - (sp.height >> 1));
           }
          }
        }
      }
      prof.end(true);
    },

    setBlendMode: function(b) {
      this.options.blendmode = b;
    },

    /**
     * get active points for a step in active zoom
     * returns a list of bounding boxes [[sw, ne] , [], []] where ne is a {lat: .., lon: ...} obj
     * empty list if there is no active pixels
     */
    getActivePointsBBox: function(tile, step) {
      var positions = [];
      var mercator = new torque.Mercator();

      var tileMax = this.options.resolution * (this.TILE_SIZE/this.options.resolution - 1);
      //this.renderer.renderTile(tile, this.key, pos.x, pos.y);
      var activePixels = tile.timeCount[step];
      var pixelIndex = tile.timeIndex[step];
      for(var p = 0; p < activePixels; ++p) {
        var posIdx = tile.renderDataPos[pixelIndex + p];
        var c = tile.renderData[pixelIndex + p];
        if (c) {
         var x = tile.x[posIdx];
         var y = tileMax - tile.y[posIdx]; // flip mercator
         positions.push(mercator.tilePixelBBox(
           tile.coord.x,
           tile.coord.y,
           tile.coord.z,
           x, y
         ));
        }
      }
      return positions;
    },

    // return the value for x, y (tile coordinates)
    // null for no value
    getValueFor: function(tile, step, px, py) {
      var mercator = new torque.Mercator();
      var res = this.options.resolution;
      var res2 = res >> 1;

      var tileMax = this.options.resolution * (this.TILE_SIZE/this.options.resolution - 1);
      //this.renderer.renderTile(tile, this.key, pos.x, pos.y);
      var activePixels = tile.timeCount[step];
      var pixelIndex = tile.timeIndex[step];
      for(var p = 0; p < activePixels; ++p) {
        var posIdx = tile.renderDataPos[pixelIndex + p];
        var c = tile.renderData[pixelIndex + p];
        if (c) {
         var x = tile.x[posIdx];
         var y = tileMax - tile.y[posIdx];
         var dx = px + res2 - x;
         var dy = py + res2 - y;
         if (dx >= 0 && dx < res && dy >= 0 && dy < res) {
           return {
             value: c,
             bbox: mercator.tilePixelBBox(
               tile.coord.x,
               tile.coord.y,
               tile.coord.z,
               x - res2, y - res2, res
             )
           }
         }
        }
      }
      return null;
    },
    _preloadIcons: function(img_names){
      var self = this;
      if(img_names.length > 0 && !this._forcePoints){
        if(Object.keys(this._icons).length === 0){
          for (var i = 0; i<img_names.length; i++){
            var new_img = this._createImage();
            this._icons[img_names[i]] = null;
            if (typeof self._icons.itemsToLoad === 'undefined'){
              this._icons.itemsToLoad = img_names.length;
            }
            new_img.onload = function(e){
              self._icons[this.src] = this;
              if(Object.keys(self._icons).length === img_names.length + 1){
                self._icons.itemsToLoad--;
                if(self._icons.itemsToLoad === 0){
                  self.clearSpriteCache();
                  self.fire("allIconsLoaded");
                }
              }
            };
            new_img.onerror = function(){
              self._forcePoints = true;
              self.clearSpriteCache();
              console.error("Couldn't get marker-file " + this.src);
            };
            new_img.src = img_names[i];
          }

        }
      }

  }
});

  // exports public api
module.exports = PointRenderer;
<commit_msg>style<commit_after>var torque = require('../');
var cartocss = require('./cartocss_render');
var Profiler = require('../profiler');
var carto = global.carto || require('carto');

  var TAU = Math.PI * 2;
  var DEFAULT_CARTOCSS = [
    '#layer {',
    '  marker-fill: #662506;',
    '  marker-width: 4;',
    '  [value > 1] { marker-fill: #FEE391; }',
    '  [value > 2] { marker-fill: #FEC44F; }',
    '  [value > 3] { marker-fill: #FE9929; }',
    '  [value > 4] { marker-fill: #EC7014; }',
    '  [value > 5] { marker-fill: #CC4C02; }',
    '  [value > 6] { marker-fill: #993404; }',
    '  [value > 7] { marker-fill: #662506; }',
    '}'
  ].join('\n');

  var COMP_OP_TO_CANVAS = {
    "src": 'source-over',
    "src-over": 'source-over',
    "dst-over": 'destination-over',
    "src-in": 'source-in',
    "dst-in": 'destination-in',
    "src-out": 'source-out',
    "dst-out": 'destination-out',
    "src-atop": 'source-atop',
    "dst-atop": 'destination-atop',
    "xor": 'xor',
    "darken": 'darken',
    "lighten": 'lighten'
  }

  function compop2canvas(compop) {
    return COMP_OP_TO_CANVAS[compop] || compop;
  }

  //
  // this renderer just render points depending of the value
  //
  function PointRenderer(canvas, options) {
    if (!canvas) {
      throw new Error("canvas can't be undefined");
    }
    this.options = options;
    this._canvas = canvas;
    this._ctx = canvas.getContext('2d');
    this._sprites = []; // sprites per layer
    this._shader = null;
    this.setCartoCSS(this.options.cartocss || DEFAULT_CARTOCSS);
    this.TILE_SIZE = 256;
    this._icons = {};
    this._forcePoints = false;
  }

  torque.extend(PointRenderer.prototype, torque.Event, {

    clearCanvas: function() {
      var canvas = this._canvas;
      var color = this._Map['-torque-clear-color']
      // shortcut for the default value
      if (color  === "rgba(255, 255, 255, 0)" || !color) {
        this._canvas.width = this._canvas.width;
      } else {
        var ctx = this._ctx;
        ctx.setTransform(1, 0, 0, 1, 0, 0);
        var compop = this._Map['comp-op']
        ctx.globalCompositeOperation = compop2canvas(compop);
        ctx.fillStyle = color;
        ctx.fillRect(0, 0, canvas.width, canvas.height);
      }
    },

    setCanvas: function(canvas) {
      this._canvas = canvas;
      this._ctx = canvas.getContext('2d');
    },

    //
    // sets the cartocss style to render stuff
    //
    setCartoCSS: function(cartocss) {
      // clean sprites
      this.setShader(new carto.RendererJS().render(cartocss));
    },

    setShader: function(shader) {
      // clean sprites
      this._sprites = [];
      this._shader = shader;
      this._Map = this._shader.getDefault().getStyle({}, { zoom: 0 });
    },

    clearSpriteCache: function() {
      this._sprites = [];
    },

    //
    // generate sprite based on cartocss style
    //
    generateSprite: function(shader, value, shaderVars) {
      var self = this;
      var prof = Profiler.metric('torque.renderer.point.generateSprite').start();
      var st = shader.getStyle({
        value: value
      }, shaderVars);

      var pointSize = st['marker-width'];
      if (!pointSize) {
        return null;
      }

      if (st['marker-opacity'] === 0 && !st['marker-line-opacity']) {
        return null;
      }

      var canvas = this._createCanvas();
      // take into account the exterior ring to calculate the size
      var canvasSize = (st['marker-line-width'] || 0) + pointSize*2;
      var ctx = canvas.getContext('2d');
      var w = ctx.width = canvas.width = ctx.height = canvas.height = Math.ceil(canvasSize);
      ctx.translate(w/2, w/2);

      var img_names = this._shader.getImageURLs();
      this._preloadIcons(img_names);
      if (img_names.length > 0 && this._icons.itemsToLoad === 0) {
        var img_name = st["marker-file"] || st["point-file"];
        var img = this._icons[img_name];
        img.w = st['marker-width'] || img.width;
        img.h = st['marker-width'] || st['marker-height'];
        cartocss.renderSprite(ctx, img);
      }
      else {
        var mt = st['marker-type'];
        if (mt && mt === 'rectangle') {
          cartocss.renderRectangle(ctx, st);
        } else {
          cartocss.renderPoint(ctx, st);
        }
      }
      prof.end(true);
      if (torque.flags.sprites_to_images) {
        var i = this._createImage();
        i.src = canvas.toDataURL();
        return i;
      }
      return canvas;
    },

    //
    // renders all the layers (and frames for each layer) from cartocss
    //
    renderTile: function(tile, key) {
      var prof = Profiler.metric('torque.renderer.point.renderLayers').start();
      var layers = this._shader.getLayers();
      for(var i = 0, n = layers.length; i < n; ++i ) {
        var layer = layers[i];
        if (layer.name() !== "Map") {
          var sprites = this._sprites[i] || (this._sprites[i] = {});
          // frames for each layer
          for(var fr = 0; fr < layer.frames().length; ++fr) {
            var frame = layer.frames()[fr];
            var fr_sprites = sprites[frame] || (sprites[frame] = []);
            this._renderTile(tile, key - frame, frame, fr_sprites, layer);
          }
        }
      }
      prof.end(true);
    },

    _createCanvas: function() {
      return this.options.canvasClass
        ? new this.options.canvasClass()
        : document.createElement('canvas');
    },

    _createImage: function() {
      return this.options.imageClass
        ? new this.options.imageClass()
        : new Image();
    },

    //
    // renders a tile in the canvas for key defined in
    // the torque tile
    //
    _renderTile: function(tile, key, frame_offset, sprites, shader, shaderVars) {
      if (!this._canvas) return;

      var prof = Profiler.metric('torque.renderer.point.renderTile').start();
      var ctx = this._ctx;
      var blendMode = compop2canvas(shader.eval('comp-op')) || this.options.blendmode;
      if (blendMode) {
        ctx.globalCompositeOperation = blendMode;
      }
      if (this.options.cumulative && key > tile.maxDate) {
        //TODO: precache because this tile is not going to change
        key = tile.maxDate;
      }
      var tileMax = this.options.resolution * (this.TILE_SIZE/this.options.resolution - 1)
      var activePixels = tile.timeCount[key];
      if (activePixels) {
        var pixelIndex = tile.timeIndex[key];
        for(var p = 0; p < activePixels; ++p) {
          var posIdx = tile.renderDataPos[pixelIndex + p];
          var c = tile.renderData[pixelIndex + p];
          if (c) {
           var sp = sprites[c];
           if (sp === undefined) {
             sp = sprites[c] = this.generateSprite(shader, c, torque.extend({ zoom: tile.z, 'frame-offset': frame_offset }, shaderVars));
           }
           if (sp) {
             var x = tile.x[posIdx]- (sp.width >> 1);
             var y = tileMax - tile.y[posIdx]; // flip mercator
             ctx.drawImage(sp, x, y - (sp.height >> 1));
           }
          }
        }
      }
      prof.end(true);
    },

    setBlendMode: function(b) {
      this.options.blendmode = b;
    },

    /**
     * get active points for a step in active zoom
     * returns a list of bounding boxes [[sw, ne] , [], []] where ne is a {lat: .., lon: ...} obj
     * empty list if there is no active pixels
     */
    getActivePointsBBox: function(tile, step) {
      var positions = [];
      var mercator = new torque.Mercator();

      var tileMax = this.options.resolution * (this.TILE_SIZE/this.options.resolution - 1);
      //this.renderer.renderTile(tile, this.key, pos.x, pos.y);
      var activePixels = tile.timeCount[step];
      var pixelIndex = tile.timeIndex[step];
      for(var p = 0; p < activePixels; ++p) {
        var posIdx = tile.renderDataPos[pixelIndex + p];
        var c = tile.renderData[pixelIndex + p];
        if (c) {
         var x = tile.x[posIdx];
         var y = tileMax - tile.y[posIdx]; // flip mercator
         positions.push(mercator.tilePixelBBox(
           tile.coord.x,
           tile.coord.y,
           tile.coord.z,
           x, y
         ));
        }
      }
      return positions;
    },

    // return the value for x, y (tile coordinates)
    // null for no value
    getValueFor: function(tile, step, px, py) {
      var mercator = new torque.Mercator();
      var res = this.options.resolution;
      var res2 = res >> 1;

      var tileMax = this.options.resolution * (this.TILE_SIZE/this.options.resolution - 1);
      //this.renderer.renderTile(tile, this.key, pos.x, pos.y);
      var activePixels = tile.timeCount[step];
      var pixelIndex = tile.timeIndex[step];
      for(var p = 0; p < activePixels; ++p) {
        var posIdx = tile.renderDataPos[pixelIndex + p];
        var c = tile.renderData[pixelIndex + p];
        if (c) {
         var x = tile.x[posIdx];
         var y = tileMax - tile.y[posIdx];
         var dx = px + res2 - x;
         var dy = py + res2 - y;
         if (dx >= 0 && dx < res && dy >= 0 && dy < res) {
           return {
             value: c,
             bbox: mercator.tilePixelBBox(
               tile.coord.x,
               tile.coord.y,
               tile.coord.z,
               x - res2, y - res2, res
             )
           }
         }
        }
      }
      return null;
    },
    _preloadIcons: function(img_names){
      var self = this;
      if (img_names.length > 0 && !this._forcePoints){
        if (Object.keys(this._icons).length === 0){
          for (var i = 0; i<img_names.length; i++){
            var new_img = this._createImage();
            this._icons[img_names[i]] = null;
            if (typeof self._icons.itemsToLoad === 'undefined'){
              this._icons.itemsToLoad = img_names.length;
            }
            new_img.onload = function(e){
              self._icons[this.src] = this;
              if (Object.keys(self._icons).length === img_names.length + 1){
                self._icons.itemsToLoad--;
                if (self._icons.itemsToLoad === 0){
                  self.clearSpriteCache();
                  self.fire("allIconsLoaded");
                }
              }
            };
            new_img.onerror = function(){
              self._forcePoints = true;
              self.clearSpriteCache();
              console.error("Couldn't get marker-file " + this.src);
            };
            new_img.src = img_names[i];
          }

        }
      }

  }
});

  // exports public api
module.exports = PointRenderer;
<|endoftext|>


<commit_before>import Ember from 'ember';

const FLAG_ATHLETE = 1;
const FLAG_OBJECT = 2;
const FLAG_SIMULTANEOUS = 4;
const FLAG_ISOLATED = 8;
const FLAG_SAME = 16;
const FLAG_OPPOSITE = 32;

export default Ember.Component.extend({
	i18n: Ember.inject.service(),

	types: Ember.computed(function () {
		return [
			{
				key: 'skill',
				title: this.get('skill').get('sport').get('skillLabel')
			},
			{
				key: 'composite',
				title: this.get('i18n').t('composite'),
				help: this.get('i18n').t('composite.explanation', {skills: this.get('skill').get('sport').get('skillPluralLabel')}),
			},
			{
				key: 'multiple',
				title: this.get('i18n').t('multiple'),
				help: this.get('i18n').t('multiple.explanation')
			}
		];
	}),

	synchronizations: Ember.computed(function () {
		return [
			{
				key: 'simultaneous',
				title: this.get('i18n').t('simultaneous')
			},
			{
				key: 'isolated',
				title: this.get('i18n').t('isolated')
			}
		];
	}),

	directions: Ember.computed(function () {
		return [
			{
				key: 'same',
				title: this.get('i18n').t('same')
			},
			{
				key: 'opposite',
				title: this.get('i18n').t('opposite')
			}
		];
	}),

	type: Ember.computed('types', function() {
		return this.get('types')[0];
	}),

	isNonFixedObject: Ember.computed('skill.object', function() {
		let object = this.get('skill').get('object');
		if (!object.get('id')) {
			return false;
		}
		return !object.get('fixed');
	}),

	rotation: Ember.computed(function () {
		return {
			longitudinal: {
				athlete: false,
				object: false,
				sync: null,
				direction: null
			},
			latitudinal: {
				athlete: false,
				object: false,
				sync: null,
				direction: null
			},
			transversal: {
				athlete: false,
				object: false,
				sync: null,
				direction: null
			}
		};
	}),

	calculateRotationsFlag(rotation) {
		let flags = 0;
		if (rotation.athlete) {
			flags |= FLAG_ATHLETE;
		}

		if (rotation.object) {
			flags |= FLAG_OBJECT;
		}

		if (rotation.sync && rotation.sync.key === 'simultaneous') {
			flags |= FLAG_SIMULTANEOUS;
		} else if (rotation.sync && rotation.sync.key === 'isolated') {
			flags |= FLAG_ISOLATED;
		}

		if (rotation.direction && rotation.direction.key === 'same') {
			flags |= FLAG_SAME;
		} else if (rotation.direction && rotation.direction.key === 'opposite') {
			flags |= FLAG_OPPOSITE;
		}

		return flags;
	},

	loadRotations(flags) {
		let rotation = {};
		rotation.athlete = (flags & FLAG_ATHLETE) === FLAG_ATHLETE;
		rotation.object = (flags & FLAG_OBJECT) === FLAG_OBJECT;

		if ((flags & FLAG_SIMULTANEOUS) === FLAG_SIMULTANEOUS) {
			rotation.sync = this.get('synchronizations')[0];
		} else if ((flags & FLAG_ISOLATED) === FLAG_ISOLATED) {
			rotation.sync = this.get('synchronizations')[1];
		}

		if ((flags & FLAG_SAME) === FLAG_SAME) {
			rotation.direction = this.get('directions')[0];
		} else if ((flags & FLAG_OPPOSITE) === FLAG_OPPOSITE) {
			rotation.direction = this.get('directions')[1];
		}

		return rotation;
	},

	didReceiveAttrs() {
    	this._super(...arguments);

		let skill = this.get('skill');
		if (!skill.get('isNew')) {
			skill.set('versionComment', '');
		}
		this.get('rotation').longitudinal = this.loadRotations(skill.get('longitudinalFlags'));
		this.get('rotation').latitudinal = this.loadRotations(skill.get('latitudinalFlags'));
		this.get('rotation').transversal = this.loadRotations(skill.get('transversalFlags'));
	},

	searchGroups(group, term) {
		return group.get('title').toLowerCase().indexOf(term.toLowerCase());
	},

	searchSkills(skill, term) {
		if (skill !== null && skill.get) {
			return skill.get('name').toLowerCase().indexOf(term.toLowerCase());
		}
		return false;
	},

	actions: {
		save() {
			let skill = this.get('skill');

			// set type
			if (this.get('type').key === 'composite') {
				skill.set('isComposite', true);
			} else if (this.get('type').key === 'multiple') {
				skill.set('isMultiple', true);
			}

			// calculate rotation flags
			skill.set('longitudinalFlags', this.calculateRotationsFlag(this.get('rotation').longitudinal));
			skill.set('latitudinalFlags', this.calculateRotationsFlag(this.get('rotation').latitudinal));
			skill.set('transversalFlags', this.calculateRotationsFlag(this.get('rotation').transversal));
			this.sendAction('save', this.get('skill'));
		},

		changeVariationOf(variationOf) {
			let skill = this.get('skill');
			skill.set('variationOf', variationOf);
			skill.get('parents').pushObject(variationOf);
		},

		changeParts(selection) {
			let skill = this.get('skill');
			skill.set('parts', selection);
			skill.set('parents', selection);
		},

		handleKeydown(dropdown, e) {
			// prevent submitting the form
			if (e.keyCode === 13) {
				e.preventDefault();
			}
	  	},

		sequenceAdded(data) {
			let skill = this.get('skill');
			skill.set('filename', data.filename);
		},

		sequenceRemoved() {
			let skill = this.get('skill');
			skill.set('sequence_delete', true);
		}
	}
});
<commit_msg>Bugfix for loading rotation flags properly<commit_after>import Ember from 'ember';

const FLAG_ATHLETE = 1;
const FLAG_OBJECT = 2;
const FLAG_SIMULTANEOUS = 4;
const FLAG_ISOLATED = 8;
const FLAG_SAME = 16;
const FLAG_OPPOSITE = 32;

export default Ember.Component.extend({
	i18n: Ember.inject.service(),

	types: Ember.computed(function () {
		return [
			{
				key: 'skill',
				title: this.get('skill').get('sport').get('skillLabel')
			},
			{
				key: 'composite',
				title: this.get('i18n').t('composite'),
				help: this.get('i18n').t('composite.explanation', {skills: this.get('skill').get('sport').get('skillPluralLabel')}),
			},
			{
				key: 'multiple',
				title: this.get('i18n').t('multiple'),
				help: this.get('i18n').t('multiple.explanation')
			}
		];
	}),

	synchronizations: Ember.computed(function () {
		return [
			{
				key: 'simultaneous',
				title: this.get('i18n').t('simultaneous')
			},
			{
				key: 'isolated',
				title: this.get('i18n').t('isolated')
			}
		];
	}),

	directions: Ember.computed(function () {
		return [
			{
				key: 'same',
				title: this.get('i18n').t('same')
			},
			{
				key: 'opposite',
				title: this.get('i18n').t('opposite')
			}
		];
	}),

	type: Ember.computed('types', function() {
		return this.get('types')[0];
	}),

	isNonFixedObject: Ember.computed('skill.object', function() {
		let object = this.get('skill').get('object');
		if (!object.get('id')) {
			return false;
		}
		return !object.get('fixed');
	}),

	rotation: Ember.computed(function () {
		return {
			longitudinal: {
				athlete: false,
				object: false,
				sync: null,
				direction: null
			},
			latitudinal: {
				athlete: false,
				object: false,
				sync: null,
				direction: null
			},
			transversal: {
				athlete: false,
				object: false,
				sync: null,
				direction: null
			}
		};
	}),

	calculateRotationsFlag(rotation) {
		let flags = 0;
		if (rotation.athlete) {
			flags |= FLAG_ATHLETE;
		}

		if (rotation.object) {
			flags |= FLAG_OBJECT;
		}

		if (rotation.sync && rotation.sync.key === 'simultaneous') {
			flags |= FLAG_SIMULTANEOUS;
		} else if (rotation.sync && rotation.sync.key === 'isolated') {
			flags |= FLAG_ISOLATED;
		}

		if (rotation.direction && rotation.direction.key === 'same') {
			flags |= FLAG_SAME;
		} else if (rotation.direction && rotation.direction.key === 'opposite') {
			flags |= FLAG_OPPOSITE;
		}

		return flags;
	},

	loadRotations(flags) {
		let rotation = {};
		rotation.athlete = (flags & FLAG_ATHLETE) === FLAG_ATHLETE;
		rotation.object = (flags & FLAG_OBJECT) === FLAG_OBJECT;

		if ((flags & FLAG_SIMULTANEOUS) === FLAG_SIMULTANEOUS) {
			rotation.sync = this.get('synchronizations')[0];
		} else if ((flags & FLAG_ISOLATED) === FLAG_ISOLATED) {
			rotation.sync = this.get('synchronizations')[1];
		}

		if ((flags & FLAG_SAME) === FLAG_SAME) {
			rotation.direction = this.get('directions')[0];
		} else if ((flags & FLAG_OPPOSITE) === FLAG_OPPOSITE) {
			rotation.direction = this.get('directions')[1];
		}

		return rotation;
	},

	didReceiveAttrs() {
    	this._super(...arguments);

		let skill = this.get('skill');
		if (!skill.get('isNew')) {
			skill.set('versionComment', '');
		}
		Ember.set(this.get('rotation'), 'longitudinal', this.loadRotations(skill.get('longitudinalFlags')));
		Ember.set(this.get('rotation'), 'latitudinal', this.loadRotations(skill.get('latitudinalFlags')));
		Ember.set(this.get('rotation'), 'transversal', this.loadRotations(skill.get('transversalFlags')));
	},

	searchGroups(group, term) {
		return group.get('title').toLowerCase().indexOf(term.toLowerCase());
	},

	searchSkills(skill, term) {
		if (skill !== null && skill.get) {
			return skill.get('name').toLowerCase().indexOf(term.toLowerCase());
		}
		return false;
	},

	actions: {
		save() {
			let skill = this.get('skill');

			// set type
			if (this.get('type').key === 'composite') {
				skill.set('isComposite', true);
			} else if (this.get('type').key === 'multiple') {
				skill.set('isMultiple', true);
			}

			// calculate rotation flags
			skill.set('longitudinalFlags', this.calculateRotationsFlag(this.get('rotation').longitudinal));
			skill.set('latitudinalFlags', this.calculateRotationsFlag(this.get('rotation').latitudinal));
			skill.set('transversalFlags', this.calculateRotationsFlag(this.get('rotation').transversal));
			this.sendAction('save', this.get('skill'));
		},

		changeVariationOf(variationOf) {
			let skill = this.get('skill');
			skill.set('variationOf', variationOf);
			skill.get('parents').pushObject(variationOf);
		},

		changeParts(selection) {
			let skill = this.get('skill');
			skill.set('parts', selection);
			skill.set('parents', selection);
		},

		handleKeydown(dropdown, e) {
			// prevent submitting the form
			if (e.keyCode === 13) {
				e.preventDefault();
			}
	  	},

		sequenceAdded(data) {
			let skill = this.get('skill');
			skill.set('filename', data.filename);
		},

		sequenceRemoved() {
			let skill = this.get('skill');
			skill.set('sequence_delete', true);
		}
	}
});
<|endoftext|>


<commit_before>exports.definition = {
	config: {
		"columns": {
			name: 'TEXT',
			nickname: 'TEXT',

			// column values can have types with keywords
			fighterId: 'INTEGER PRIMARY KEY AUTOINCREMENT'
		},

		"adapter": {
			"type": "sql_new",

			// The table name inside the sqlite database to use for
			// models and collections based on this definition.
			"collection_name": "fighters",

			// idAttribute tells Alloy/Backbone to use this column in
			// my table as its unique identifier field. Without
			// specifying this, Alloy's default behavior is to create
			// and "alloy_id" field which will uniquely identify your
			// rows in the table with a text GUID.
			"idAttribute": "fighterId"
		}
	}
}<commit_msg>swicth to "sql" adapter<commit_after>exports.definition = {
	config: {
		"columns": {
			name: 'TEXT',
			nickname: 'TEXT',

			// column values can have types with keywords
			fighterId: 'INTEGER PRIMARY KEY AUTOINCREMENT'
		},

		"adapter": {
			"type": "sql",

			// The table name inside the sqlite database to use for
			// models and collections based on this definition.
			"collection_name": "fighters",

			// idAttribute tells Alloy/Backbone to use this column in
			// my table as its unique identifier field. Without
			// specifying this, Alloy's default behavior is to create
			// and "alloy_id" field which will uniquely identify your
			// rows in the table with a text GUID.
			"idAttribute": "fighterId"
		}
	}
}<|endoftext|>


<commit_before>import ViewTemplate from 'templates/view.jade';
import TagsTemplate from 'templates/tags.jade';
import 'stylesheets/dicom_viewer.styl';
import {getTags} from 'js/tags.js';

import dicomParser from 'dicom-parser';
import vtkImageSlice from 'vtk.js/Sources/Rendering/Core/ImageSlice';
import vtkImageData from 'vtk.js/Sources/Common/DataModel/ImageData';
import vtkDataArray from 'vtk.js/Sources/Common/Core/DataArray';
import vtkImageMapper from 'vtk.js/Sources/Rendering/Core/ImageMapper';
import vtkInteractorStyleImage from 'vtk.js/Sources/Interaction/Style/InteractorStyleImage';
import vtkOpenGLRenderWindow from 'vtk.js/Sources/Rendering/OpenGL/RenderWindow';
import vtkRenderer from 'vtk.js/Sources/Rendering/Core/Renderer';
import vtkRenderWindow from 'vtk.js/Sources/Rendering/Core/RenderWindow';
import vtkRenderWindowInteractor from 'vtk.js/Sources/Rendering/Core/RenderWindowInteractor';

import naturalSort from 'javascript-natural-sort';
naturalSort.insensitive = true;

girder.wrap(girder.views.ItemView, 'render', function (render) {
  this.once('g:rendered', function () {
    $('.g-item-header').after('

');
    new girder.views.DicomView({
      el: $('#g-dicom-view'),
      parentView: this,
      item: this.model
    });
  }, this);
  render.call(this);
});

girder.views.DicomView = girder.View.extend({
  events: {
    'click #dicom-zoom-in': function (event) {
      event.preventDefault();
      this.camera.zoom(9 / 8);
      this.iren.render();
    },
    'click #dicom-zoom-out': function (event) {
      event.preventDefault();
      this.camera.zoom(8 / 9);
      this.iren.render();
    },
    'click #dicom-reset-zoom': function (event) {
      event.preventDefault();
      this.autoZoom();
      this.iren.render();
    },
    'click #dicom-first': function (event) {
      event.preventDefault();
      this.setIndex(0);
    },
    'click #dicom-previous': function (event) {
      event.preventDefault();
      if (this.index === 0) {
        this.setIndex(this.files.length - 1);
      } else {
        this.setIndex(this.index - 1);
      }
    },
    'click #dicom-next': function (event) {
      event.preventDefault();
      if (this.index === this.files.length - 1) {
        this.setIndex(0);
      } else {
        this.setIndex(this.index + 1);
      }
    },
    'click #dicom-last': function (event) {
      event.preventDefault();
      this.setIndex(this.files.length - 1);
    },
    'click #dicom-auto-levels': function (event) {
      event.preventDefault();
      this.autoLevels();
      this.iren.render();
    },
    'input #dicom-slider': _.debounce(function (event) {
      this.setIndex(event.target.value);
    }, 10)
  },

  initialize: function (settings) {
    this.item = settings.item;
    this.files = [];
    this.index = 0;
    this.first = true;
    this.imageData = null;
    this.xhr = null;
    this.imageCache = {};
    this.tagCache = {};
    this.render();
    this.loadFileList();
  },

  setIndex: function (index) {
    if (index < 0 || index >= this.files.length) {
      return;
    }
    this.index = index;
    document.getElementById("dicom-slider").value = index;
    this.loadFile(this.files[index]);
  },

  loadFileList: function () {
    girder.restRequest({
      path: '/item/' + this.item.get('_id') + '/dicom',
      data: {
        limit: 0
      }
    }).done(_.bind(function (resp) {
      this.handleFileList(resp);
    }, this));
  },

  handleFileList: function (files) {
    if (files.length > 0 && files[0].size > 1024 * 1024 * 2) {
      // don't automatically download huge files
      return;
    }
    // files = files.sort((a, b) => naturalSort(a.name, b.name));
    this.files = files;
    var slider = document.getElementById("dicom-slider");
    slider.min = 0;
    slider.max = this.files.length - 1;
    slider.step = 1;
    this.setIndex(0);
  },

  loadFile: function (file) {
    if (file.name in this.imageCache) {
      this.showCached(file);
      return;
    }
    console.log(file.name);
    if (this.xhr) {
      this.xhr.abort();
    }
    const xhr = new XMLHttpRequest();
    xhr.open('GET', girder.apiRoot + '/file/' + file._id + '/download', true);
    xhr.responseType = 'arraybuffer';
    xhr.onload = _.bind(function (event) {
      try {
        const byteArray = new Uint8Array(xhr.response);
        const dataSet = dicomParser.parseDicom(byteArray);
        const imageData = createImageData(dataSet);
        this.imageCache[file.name] = imageData;
        this.tagCache[file.name] = getTags(dataSet);
        this.showCached(file);
      }
      catch (e) {
        console.log(e);
      }
    }, this);
    xhr.send();
    this.xhr = xhr;
  },

  showCached: function (file) {
    document.getElementById('dicom-filename').innerHTML = file.name;
    document.getElementById('g-dicom-tags').innerHTML = TagsTemplate({
      tags: this.tagCache[file.name]
    });
    this.setImageData(this.imageCache[file.name]);
  },

  render: function () {
    this.$el.html(ViewTemplate());
    return this;
  },

  autoLevels: function () {
    if (!this.imageData) {
      return;
    }
    const range = this.imageData.getPointData().getScalars().getRange();
    const ww = range[1] - range[0];
    const wc = (range[0] + range[1]) / 2;
    this.actor.getProperty().setColorWindow(ww);
    this.actor.getProperty().setColorLevel(wc);
  },

  autoZoom: function () {
    if (!this.imageData) {
      return;
    }
    this.ren.resetCamera();
    this.camera.zoom(1.44);
  },

  setImageData: function (imageData) {
    this.imageData = imageData;

    if (this.first) {
      this.first = false;
      this.initVtk(imageData);
      this.autoLevels();
      this.autoZoom();
    } else {
      const mapper = vtkImageMapper.newInstance();
      mapper.setInputData(imageData);
      this.actor.setMapper(mapper);
    }

    this.iren.render();
  },

  initVtk: function (imageData) {
    document.getElementById('g-dicom-view').style.display = 'block';
    const container = document.getElementById('g-dicom-container');

    const ren = vtkRenderer.newInstance();
    ren.setBackground(0.33, 0.33, 0.33);

    const renWin = vtkRenderWindow.newInstance();
    renWin.addRenderer(ren);

    const glWin = vtkOpenGLRenderWindow.newInstance();
    glWin.setContainer(container);
    glWin.setSize(512, 512);
    renWin.addView(glWin);

    const iren = vtkRenderWindowInteractor.newInstance();
    const style = vtkInteractorStyleImage.newInstance();
    iren.setInteractorStyle(style);
    iren.setView(glWin);

    const actor = vtkImageSlice.newInstance();
    ren.addActor(actor);

    const mapper = vtkImageMapper.newInstance();
    actor.setMapper(mapper);
    mapper.setInputData(imageData);

    const camera = ren.getActiveCameraAndResetIfCreated();

    iren.initialize();
    iren.bindEvents(container, document);
    iren.start();

    this.actor = actor;
    this.ren = ren;
    this.iren = iren;
    this.camera = camera;
  }

});

function createImageData(dataSet) {
  const rows = dataSet.uint16('x00280010');
  const cols = dataSet.uint16('x00280011');
  const rowSpacing = dataSet.floatString('x00280030', 0);
  const colSpacing = dataSet.floatString('x00280030', 1);

  const imageData = vtkImageData.newInstance();
  imageData.setOrigin(0, 0, 0);
  imageData.setSpacing(colSpacing, rowSpacing, 1);
  imageData.setExtent(0, cols - 1, 0, rows - 1, 0, 0);

  const values = createPixelBuffer(dataSet);
  const dataArray = vtkDataArray.newInstance({values: values});
  imageData.getPointData().addArray(dataArray);

  return imageData;
}

function createPixelBuffer(dataSet) {
  const buffer = dataSet.byteArray.buffer;
  const dataOffset = dataSet.elements.x7fe00010.dataOffset;
  const pixelRepresentation = dataSet.uint16('x00280103');
  const bitsAllocated = dataSet.uint16('x00280100');
  const bitsStored = dataSet.uint16('x00280101');
  const rows = dataSet.uint16('x00280010');
  const cols = dataSet.uint16('x00280011');
  const numPixels = rows * cols;
  const pmi = dataSet.string('x00280004');
  if (pmi !== 'MONOCHROME1' && pmi !== 'MONOCHROME2') {
    throw 'unsupported photometric interpretation';
  }
  // construct proper array type
  let array;
  if (pixelRepresentation === 0 && bitsAllocated === 8) {
    array = new Uint8Array(buffer, dataOffset, numPixels);
  } else if (pixelRepresentation === 0 && bitsAllocated === 16) {
    array = new Uint16Array(buffer, dataOffset, numPixels);
  } else if (pixelRepresentation === 1 && bitsAllocated === 8) {
    array = new Int8Array(buffer, dataOffset, numPixels);
  } else if (pixelRepresentation === 1 && bitsAllocated === 16) {
    array = new Int16Array(buffer, dataOffset, numPixels);
  } else {
    throw 'unrecognized image format';
  }
  // copy values to float buffer
  const values = new Float32Array(array.length);
  if (pixelRepresentation === 0) { // unsigned
    const mask = (1 << bitsStored) - 1;
    for (let i = 0; i < values.length; i++) {
      values[i] = (array[i] & mask) / mask;
    }
  } else {
    for (let i = 0; i < values.length; i++) {
      values[i] = array[i];
    }
  }
  // scale values to be 0 to 1
  let lo = values[0];
  let hi = values[0];
  for (let i = 0; i < values.length; i++) {
    if (values[i] < lo) {
      lo = values[i];
    }
    if (values[i] > hi) {
      hi = values[i];
    }
  }
  for (let i = 0; i < values.length; i++) {
    values[i] = (values[i] - lo) / (hi - lo);
  }
  if (pmi === 'MONOCHROME1') {
    // invert values
    for (let i = 0; i < values.length; i++) {
      values[i] = 1 - values[i];
    }
  }
  return values;
}
<commit_msg>don't scale values<commit_after>import ViewTemplate from 'templates/view.jade';
import TagsTemplate from 'templates/tags.jade';
import 'stylesheets/dicom_viewer.styl';
import {getTags} from 'js/tags.js';

import dicomParser from 'dicom-parser';
import vtkImageSlice from 'vtk.js/Sources/Rendering/Core/ImageSlice';
import vtkImageData from 'vtk.js/Sources/Common/DataModel/ImageData';
import vtkDataArray from 'vtk.js/Sources/Common/Core/DataArray';
import vtkImageMapper from 'vtk.js/Sources/Rendering/Core/ImageMapper';
import vtkInteractorStyleImage from 'vtk.js/Sources/Interaction/Style/InteractorStyleImage';
import vtkOpenGLRenderWindow from 'vtk.js/Sources/Rendering/OpenGL/RenderWindow';
import vtkRenderer from 'vtk.js/Sources/Rendering/Core/Renderer';
import vtkRenderWindow from 'vtk.js/Sources/Rendering/Core/RenderWindow';
import vtkRenderWindowInteractor from 'vtk.js/Sources/Rendering/Core/RenderWindowInteractor';

import naturalSort from 'javascript-natural-sort';
naturalSort.insensitive = true;

girder.wrap(girder.views.ItemView, 'render', function (render) {
  this.once('g:rendered', function () {
    $('.g-item-header').after('

');
    new girder.views.DicomView({
      el: $('#g-dicom-view'),
      parentView: this,
      item: this.model
    });
  }, this);
  render.call(this);
});

girder.views.DicomView = girder.View.extend({
  events: {
    'click #dicom-zoom-in': function (event) {
      event.preventDefault();
      this.camera.zoom(9 / 8);
      this.iren.render();
    },
    'click #dicom-zoom-out': function (event) {
      event.preventDefault();
      this.camera.zoom(8 / 9);
      this.iren.render();
    },
    'click #dicom-reset-zoom': function (event) {
      event.preventDefault();
      this.autoZoom();
      this.iren.render();
    },
    'click #dicom-first': function (event) {
      event.preventDefault();
      this.setIndex(0);
    },
    'click #dicom-previous': function (event) {
      event.preventDefault();
      if (this.index === 0) {
        this.setIndex(this.files.length - 1);
      } else {
        this.setIndex(this.index - 1);
      }
    },
    'click #dicom-next': function (event) {
      event.preventDefault();
      if (this.index === this.files.length - 1) {
        this.setIndex(0);
      } else {
        this.setIndex(this.index + 1);
      }
    },
    'click #dicom-last': function (event) {
      event.preventDefault();
      this.setIndex(this.files.length - 1);
    },
    'click #dicom-auto-levels': function (event) {
      event.preventDefault();
      this.autoLevels();
      this.iren.render();
    },
    'input #dicom-slider': _.debounce(function (event) {
      this.setIndex(event.target.value);
    }, 10)
  },

  initialize: function (settings) {
    this.item = settings.item;
    this.files = [];
    this.index = 0;
    this.first = true;
    this.imageData = null;
    this.xhr = null;
    this.imageCache = {};
    this.tagCache = {};
    this.render();
    this.loadFileList();
  },

  setIndex: function (index) {
    if (index < 0 || index >= this.files.length) {
      return;
    }
    this.index = index;
    document.getElementById("dicom-slider").value = index;
    this.loadFile(this.files[index]);
  },

  loadFileList: function () {
    girder.restRequest({
      path: '/item/' + this.item.get('_id') + '/dicom',
      data: {
        filters: 'dummy' // don't need the dicom tags, just want the sorted results
      }
    }).done(_.bind(function (resp) {
      this.handleFileList(resp);
    }, this));
  },

  handleFileList: function (files) {
    if (files.length > 0 && files[0].size > 1024 * 1024 * 2) {
      // don't automatically download huge files
      return;
    }
    // files = files.sort((a, b) => naturalSort(a.name, b.name));
    this.files = files;
    var slider = document.getElementById("dicom-slider");
    slider.min = 0;
    slider.max = this.files.length - 1;
    slider.step = 1;
    this.setIndex(0);
  },

  loadFile: function (file) {
    if (file.name in this.imageCache) {
      this.showCached(file);
      return;
    }
    console.log(file.name);
    if (this.xhr) {
      this.xhr.abort();
    }
    const xhr = new XMLHttpRequest();
    xhr.open('GET', girder.apiRoot + '/file/' + file._id + '/download', true);
    xhr.responseType = 'arraybuffer';
    xhr.onload = _.bind(function (event) {
      try {
        const byteArray = new Uint8Array(xhr.response);
        const dataSet = dicomParser.parseDicom(byteArray);
        const imageData = createImageData(dataSet);
        this.imageCache[file.name] = imageData;
        this.tagCache[file.name] = getTags(dataSet);
        this.showCached(file);
      }
      catch (e) {
        console.log(e);
      }
    }, this);
    xhr.send();
    this.xhr = xhr;
  },

  showCached: function (file) {
    document.getElementById('dicom-filename').innerHTML = file.name;
    document.getElementById('g-dicom-tags').innerHTML = TagsTemplate({
      tags: this.tagCache[file.name]
    });
    this.setImageData(this.imageCache[file.name]);
  },

  render: function () {
    this.$el.html(ViewTemplate());
    return this;
  },

  autoLevels: function () {
    if (!this.imageData) {
      return;
    }
    const range = this.imageData.getPointData().getScalars().getRange();
    const ww = range[1] - range[0];
    const wc = (range[0] + range[1]) / 2;
    this.actor.getProperty().setColorWindow(ww);
    this.actor.getProperty().setColorLevel(wc);
  },

  autoZoom: function () {
    if (!this.imageData) {
      return;
    }
    this.ren.resetCamera();
    this.camera.zoom(1.44);
  },

  setImageData: function (imageData) {
    this.imageData = imageData;

    if (this.first) {
      this.first = false;
      this.initVtk(imageData);
      this.autoLevels();
      this.autoZoom();
    } else {
      const mapper = vtkImageMapper.newInstance();
      mapper.setInputData(imageData);
      this.actor.setMapper(mapper);
    }

    this.iren.render();
  },

  initVtk: function (imageData) {
    document.getElementById('g-dicom-view').style.display = 'block';
    const container = document.getElementById('g-dicom-container');

    const ren = vtkRenderer.newInstance();
    ren.setBackground(0.33, 0.33, 0.33);

    const renWin = vtkRenderWindow.newInstance();
    renWin.addRenderer(ren);

    const glWin = vtkOpenGLRenderWindow.newInstance();
    glWin.setContainer(container);
    glWin.setSize(512, 512);
    renWin.addView(glWin);

    const iren = vtkRenderWindowInteractor.newInstance();
    const style = vtkInteractorStyleImage.newInstance();
    iren.setInteractorStyle(style);
    iren.setView(glWin);

    const actor = vtkImageSlice.newInstance();
    ren.addActor(actor);

    const mapper = vtkImageMapper.newInstance();
    actor.setMapper(mapper);
    mapper.setInputData(imageData);

    const camera = ren.getActiveCameraAndResetIfCreated();

    iren.initialize();
    iren.bindEvents(container, document);
    iren.start();

    this.actor = actor;
    this.ren = ren;
    this.iren = iren;
    this.camera = camera;
  }

});

function createImageData(dataSet) {
  const rows = dataSet.uint16('x00280010');
  const cols = dataSet.uint16('x00280011');
  const rowSpacing = dataSet.floatString('x00280030', 0);
  const colSpacing = dataSet.floatString('x00280030', 1);

  const imageData = vtkImageData.newInstance();
  imageData.setOrigin(0, 0, 0);
  imageData.setSpacing(colSpacing, rowSpacing, 1);
  imageData.setExtent(0, cols - 1, 0, rows - 1, 0, 0);

  const values = createPixelBuffer(dataSet);
  const dataArray = vtkDataArray.newInstance({values: values});
  imageData.getPointData().addArray(dataArray);

  return imageData;
}

function createPixelBuffer(dataSet) {
  const buffer = dataSet.byteArray.buffer;
  const dataOffset = dataSet.elements.x7fe00010.dataOffset;
  const pixelRepresentation = dataSet.uint16('x00280103');
  const bitsAllocated = dataSet.uint16('x00280100');
  const bitsStored = dataSet.uint16('x00280101');
  const rows = dataSet.uint16('x00280010');
  const cols = dataSet.uint16('x00280011');
  const numPixels = rows * cols;
  const pmi = dataSet.string('x00280004');
  if (pmi !== 'MONOCHROME1' && pmi !== 'MONOCHROME2') {
    throw 'unsupported photometric interpretation';
  }
  // construct proper array type
  let array;
  if (pixelRepresentation === 0 && bitsAllocated === 8) {
    array = new Uint8Array(buffer, dataOffset, numPixels);
  } else if (pixelRepresentation === 0 && bitsAllocated === 16) {
    array = new Uint16Array(buffer, dataOffset, numPixels);
  } else if (pixelRepresentation === 1 && bitsAllocated === 8) {
    array = new Int8Array(buffer, dataOffset, numPixels);
  } else if (pixelRepresentation === 1 && bitsAllocated === 16) {
    array = new Int16Array(buffer, dataOffset, numPixels);
  } else {
    throw 'unrecognized image format';
  }
  // copy values to float buffer
  const values = new Float32Array(array.length);
  if (pixelRepresentation === 0) { // unsigned
    const mask = (1 << bitsStored) - 1;
    for (let i = 0; i < values.length; i++) {
      values[i] = (array[i] & mask) / mask;
    }
  } else {
    for (let i = 0; i < values.length; i++) {
      values[i] = array[i];
    }
  }
  if (pmi === 'MONOCHROME1') {
    // invert values
    for (let i = 0; i < values.length; i++) {
      values[i] = 1 - values[i];
    }
  }
  return values;
}
<|endoftext|>


<commit_before>/**
 *
 * weatherunderground adapter
 *
 * Adapter loading the json forecast of weatherunderground
 *
 * note: you need an account and an api key to get the forecast. This is free for non excess usage: 500 requests/d
 * see: http://www.wunderground.com/weather/api/d/pricing
 *
 * register for a key:
 * http://www.wunderground.com/weather/api/d/questionnaire.html?plan=a&level=0&history=undefined
 *
 * see http://www.wunderground.com/weather/api/d/docs?d=data/hourly
 * for reference of the possible values from hourly WU forecast
 *
 */

"use strict";

var utils = require(__dirname + '/lib/utils'); // Get common adapter utils

var request = require('request');

var adapter = utils.adapter({
    name: 'weatherunderground',

    unload: function (callback) {
        adapter.log.info("adapter weatherunderground is unloading");
    },
    discover: function (callback) {
        adapter.log.info("adapter weatherunderground discovered");
    },
    install: function (callback) {
        adapter.log.info("adapter weatherunderground installed");
    },
    uninstall: function (callback) {
        adapter.log.info("adapter weatherunderground UN-installed");
    },
    objectChange: function (id, obj) {
        adapter.log.info('objectChange ' + id + ' ' + JSON.stringify(obj));
    },
    stateChange: function (id, state) {
        adapter.log.info('stateChange ' + id + ' ' + JSON.stringify(state));
    },
    ready: function () {
        adapter.log.info("Adapter weatherunderground got 'Ready' Signal - starting scheduler to look for forecasts");
        adapter.log.info("adapter weatherunderground initializing objects");
        checkWeatherVariables();
        //getWuForecastData();
    }
});

function getWuForecastData() {

    //debug

    var url = "http://api.wunderground.com/api/" + adapter.config.apikey + "/hourly/q/" + adapter.config.location + ".json";

    request({url: url, json: true}, function(error, response, body) {
        if (!error && response.statusCode === 200) {
            var qpf_sum = 0;
            var pop_max = 0;
            var uvi_sum = 0;

            for (var i = 0; i < 24; i++) {
                try {
                    //adapter.log.info("WU-Response Body: " + JSON.stringify(body.hourly_forecast[0].FCTTIME)); // Print the json response
                    //var forecast = JSON.parse(body.jsonData);
                    //adapter.log.info("version: " + body.response.version);
                    // see http://www.wunderground.com/weather/api/d/docs?d=resources/phrase-glossary for infos about properties and codes
                    adapter.setState("forecast." + i + "h.time", {ack: true, val: body.hourly_forecast[i].FCTTIME.pretty});
                    adapter.setState("forecast." + i + "h.temp", {ack: true, val: body.hourly_forecast[i].temp.metric});
                    adapter.setState("forecast." + i + "h.fctcode", {ack: true, val: body.hourly_forecast[i].fctcode}); //forecast description number -> see link above
                    adapter.setState("forecast." + i + "h.sky", {ack: true, val: body.hourly_forecast[i].sky}); //?
                    adapter.setState("forecast." + i + "h.wspd", {ack: true, val: body.hourly_forecast[i].wspd.metric}); // windspeed in kmh
                    adapter.setState("forecast." + i + "h.wdir", {ack: true, val: body.hourly_forecast[i].wdir.degrees}); //wind dir in degrees
                    adapter.setState("forecast." + i + "h.uvi", {ack: true, val: body.hourly_forecast[i].uvi}); //UV Index -> wikipedia
                    adapter.setState("forecast." + i + "h.humidity", {ack: true, val: body.hourly_forecast[i].humidity});
                    adapter.setState("forecast." + i + "h.heatindex", {ack: true, val: body.hourly_forecast[i].heatindex.metric}); // -> wikipedia
                    adapter.setState("forecast." + i + "h.feelslike", {ack: true, val: body.hourly_forecast[i].feelslike.metric}); // -> wikipedia
                    adapter.setState("forecast." + i + "h.qpf", {ack: true, val: body.hourly_forecast[i].qpf.metric}); // Quantitative precipitation forecast
                    adapter.setState("forecast." + i + "h.snow", {ack: true, val: body.hourly_forecast[i].snow.metric});
                    adapter.setState("forecast." + i + "h.pop", {ack: true, val: body.hourly_forecast[i].pop}); // probability of Precipitation
                    adapter.setState("forecast." + i + "h.mslp", {ack: true, val: body.hourly_forecast[i].mslp.metric}); // mean sea level pressure

                    qpf_sum += Number(body.hourly_forecast[i].qpf.metric);
                    uvi_sum += Number(body.hourly_forecast[i].uvi);
                    if (Number(body.hourly_forecast[i].uvi) > pop_max) {
                        pop_max = Number(body.hourly_forecast[i].uvi);
                    }

                    // 6h
                    if (i == 5) {
                        adapter.setState("forecast.6h.sum.qpf", {ack: true, val: qpf_sum});
                        adapter.setState("forecast.6h.sum.pop", {ack: true, val: pop_max});
                        adapter.setState("forecast.6h.sum.uvi", {ack: true, val: uvi_sum/6});
                    }
                    // 12h
                    if (i == 11) {
                        adapter.setState("forecast.12h.sum.qpf", {ack: true, val: qpf_sum});
                        adapter.setState("forecast.12h.sum.pop", {ack: true, val: pop_max});
                        adapter.setState("forecast.12h.sum.uvi", {ack: true, val: uvi_sum/12});
                    }
                    // 24h
                    if (i == 23) {
                        adapter.setState("forecast.24h.sum.qpf", {ack: true, val: qpf_sum});
                        adapter.setState("forecast.24h.sum.pop", {ack: true, val: pop_max});
                        adapter.setState("forecast.24h.sum.uvi", {ack: true, val: uvi_sum/24});
                    }
                } catch (error) {
                    adapter.log.error("Could not parse Forecast-Data: " + error);
                }
            }
            adapter.log.info("all forecast values set");
        } else
        {
            // ERROR
            adapter.log.error("Wunderground reported an error: " + error);
        }
    });
}

function checkWeatherVariables() {
    adapter.log.info("init forecast objects");

    adapter.setObject('forecast', {
        type: 'channel',
        role: 'forecast',
        common: {name: 'weatherunderground 24h forecast'},
        native: {location: adapter.config.location}
    });

    for (var h=0; h < 24; h++) {
        adapter.setObject('forecast.' + h + 'h', {
            type: 'channel',
            role: 'forecast',
            common: {name: 'in ' + h + 'h'},
            native: {location: adapter.config.location},
            children: ["temp"]
        });
    }

    /*
    ['time','temp','fctcode','sky','wspd','wdir','uvi','humidity','heatindex','feelslike','qpf','snow','pop','mslp'].map( function(v) {
        for (var i = 0; i < 24; i++) {
            var name = "forecast." + i + "h." + v;
            var obj = {type: 'state', parent: 'forecast.' + i +'h', common: {name: v, read: true, write: false, type: 'number', unit: ''}, native: {id: name}};
            adapter.setObjectNotExists(name, obj);
            adapter.log.debug("created object: " + name);
            //}
        }
    });

    ['forecast.6h.sum.qpf','forecast.12h.sum.qpf','forecast.24h.sum.qpf',
        'forecast.6h.sum.pop', 'forecast.12h.sum.pop', 'forecast.24h.sum.pop',
        'forecast.6h.sum.uvi','forecast.12h.sum.uvi','forecast.24h.sum.uvi'].map( function(name) {
            var obj = {
                type: 'state',
                common: {name: name, read: true, write: true, type: 'number'},
                native: {id: name}
            };
            adapter.setObjectNotExists(name, obj);
            adapter.log.debug("created object: " + name);
        });
        */
}

<commit_msg>initial version<commit_after>/**
 *
 * weatherunderground adapter
 *
 * Adapter loading the json forecast of weatherunderground
 *
 * note: you need an account and an api key to get the forecast. This is free for non excess usage: 500 requests/d
 * see: http://www.wunderground.com/weather/api/d/pricing
 *
 * register for a key:
 * http://www.wunderground.com/weather/api/d/questionnaire.html?plan=a&level=0&history=undefined
 *
 * see http://www.wunderground.com/weather/api/d/docs?d=data/hourly
 * for reference of the possible values from hourly WU forecast
 *
 */

"use strict";

var utils = require(__dirname + '/lib/utils'); // Get common adapter utils

var request = require('request');

var adapter = utils.adapter({
    name: 'weatherunderground',

    unload: function (callback) {
        adapter.log.info("adapter weatherunderground is unloading");
    },
    discover: function (callback) {
        adapter.log.info("adapter weatherunderground discovered");
    },
    install: function (callback) {
        adapter.log.info("adapter weatherunderground installed");
    },
    uninstall: function (callback) {
        adapter.log.info("adapter weatherunderground UN-installed");
    },
    objectChange: function (id, obj) {
        adapter.log.info('objectChange ' + id + ' ' + JSON.stringify(obj));
    },
    stateChange: function (id, state) {
        adapter.log.info('stateChange ' + id + ' ' + JSON.stringify(state));
    },
    ready: function () {
        adapter.log.info("Adapter weatherunderground got 'Ready' Signal - starting scheduler to look for forecasts");
        adapter.log.info("adapter weatherunderground initializing objects");
        checkWeatherVariables();
        //getWuForecastData();
    }
});

function getWuForecastData() {

    //debug

    var url = "http://api.wunderground.com/api/" + adapter.config.apikey + "/hourly/q/" + adapter.config.location + ".json";

    request({url: url, json: true}, function(error, response, body) {
        if (!error && response.statusCode === 200) {
            var qpf_sum = 0;
            var pop_max = 0;
            var uvi_sum = 0;

            for (var i = 0; i < 24; i++) {
                try {
                    //adapter.log.info("WU-Response Body: " + JSON.stringify(body.hourly_forecast[0].FCTTIME)); // Print the json response
                    //var forecast = JSON.parse(body.jsonData);
                    //adapter.log.info("version: " + body.response.version);
                    // see http://www.wunderground.com/weather/api/d/docs?d=resources/phrase-glossary for infos about properties and codes
                    adapter.setState("forecast." + i + "h.time", {ack: true, val: body.hourly_forecast[i].FCTTIME.pretty});
                    adapter.setState("forecast." + i + "h.temp", {ack: true, val: body.hourly_forecast[i].temp.metric});
                    adapter.setState("forecast." + i + "h.fctcode", {ack: true, val: body.hourly_forecast[i].fctcode}); //forecast description number -> see link above
                    adapter.setState("forecast." + i + "h.sky", {ack: true, val: body.hourly_forecast[i].sky}); //?
                    adapter.setState("forecast." + i + "h.wspd", {ack: true, val: body.hourly_forecast[i].wspd.metric}); // windspeed in kmh
                    adapter.setState("forecast." + i + "h.wdir", {ack: true, val: body.hourly_forecast[i].wdir.degrees}); //wind dir in degrees
                    adapter.setState("forecast." + i + "h.uvi", {ack: true, val: body.hourly_forecast[i].uvi}); //UV Index -> wikipedia
                    adapter.setState("forecast." + i + "h.humidity", {ack: true, val: body.hourly_forecast[i].humidity});
                    adapter.setState("forecast." + i + "h.heatindex", {ack: true, val: body.hourly_forecast[i].heatindex.metric}); // -> wikipedia
                    adapter.setState("forecast." + i + "h.feelslike", {ack: true, val: body.hourly_forecast[i].feelslike.metric}); // -> wikipedia
                    adapter.setState("forecast." + i + "h.qpf", {ack: true, val: body.hourly_forecast[i].qpf.metric}); // Quantitative precipitation forecast
                    adapter.setState("forecast." + i + "h.snow", {ack: true, val: body.hourly_forecast[i].snow.metric});
                    adapter.setState("forecast." + i + "h.pop", {ack: true, val: body.hourly_forecast[i].pop}); // probability of Precipitation
                    adapter.setState("forecast." + i + "h.mslp", {ack: true, val: body.hourly_forecast[i].mslp.metric}); // mean sea level pressure

                    qpf_sum += Number(body.hourly_forecast[i].qpf.metric);
                    uvi_sum += Number(body.hourly_forecast[i].uvi);
                    if (Number(body.hourly_forecast[i].uvi) > pop_max) {
                        pop_max = Number(body.hourly_forecast[i].uvi);
                    }

                    // 6h
                    if (i == 5) {
                        adapter.setState("forecast.6h.sum.qpf", {ack: true, val: qpf_sum});
                        adapter.setState("forecast.6h.sum.pop", {ack: true, val: pop_max});
                        adapter.setState("forecast.6h.sum.uvi", {ack: true, val: uvi_sum/6});
                    }
                    // 12h
                    if (i == 11) {
                        adapter.setState("forecast.12h.sum.qpf", {ack: true, val: qpf_sum});
                        adapter.setState("forecast.12h.sum.pop", {ack: true, val: pop_max});
                        adapter.setState("forecast.12h.sum.uvi", {ack: true, val: uvi_sum/12});
                    }
                    // 24h
                    if (i == 23) {
                        adapter.setState("forecast.24h.sum.qpf", {ack: true, val: qpf_sum});
                        adapter.setState("forecast.24h.sum.pop", {ack: true, val: pop_max});
                        adapter.setState("forecast.24h.sum.uvi", {ack: true, val: uvi_sum/24});
                    }
                } catch (error) {
                    adapter.log.error("Could not parse Forecast-Data: " + error);
                }
            }
            adapter.log.info("all forecast values set");
        } else
        {
            // ERROR
            adapter.log.error("Wunderground reported an error: " + error);
        }
    });
}

function checkWeatherVariables() {
    adapter.log.info("init forecast objects");

    adapter.setObjectNotExists('forecast', {
        type: 'channel',
        role: 'forecast',
        common: {name: 'weatherunderground 24h forecast'},
        native: {location: adapter.config.location}
    });

    for (var h=0; h < 24; h++) {
        var id = "forecast." + h + "h.";
        adapter.setObjectNotExists('forecast.' + h + 'h', {
            type: 'channel',
            role: 'forecast',
            common: {name: 'in ' + h + 'h'},
            native: {location: adapter.config.location}
        });
        adapter.setObjectNotExists(id + 'temp', {
            type: 'state',
            common: {name: 'Temperature', type: 'number', role: 'value.temperature', unit: 'C', read: true, write: false},
            native: {id: id + 'temp'}
        });
    }

    /*
    ['time','temp','fctcode','sky','wspd','wdir','uvi','humidity','heatindex','feelslike','qpf','snow','pop','mslp'].map( function(v) {
        for (var i = 0; i < 24; i++) {
            var name = "forecast." + i + "h." + v;
            var obj = {type: 'state', parent: 'forecast.' + i +'h', common: {name: v, read: true, write: false, type: 'number', unit: ''}, native: {id: name}};
            adapter.setObjectNotExists(name, obj);
            adapter.log.debug("created object: " + name);
            //}
        }
    });

    ['forecast.6h.sum.qpf','forecast.12h.sum.qpf','forecast.24h.sum.qpf',
        'forecast.6h.sum.pop', 'forecast.12h.sum.pop', 'forecast.24h.sum.pop',
        'forecast.6h.sum.uvi','forecast.12h.sum.uvi','forecast.24h.sum.uvi'].map( function(name) {
            var obj = {
                type: 'state',
                common: {name: name, read: true, write: true, type: 'number'},
                native: {id: name}
            };
            adapter.setObjectNotExists(name, obj);
            adapter.log.debug("created object: " + name);
        });
        */
}

<|endoftext|>


<commit_before>import { logError, deepAccess } from '../src/utils.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {BANNER, VIDEO} from '../src/mediaTypes.js';
import {config} from '../src/config.js';
import { Renderer } from '../src/Renderer.js';
import { INSTREAM, OUTSTREAM } from '../src/video.js';

const ENDPOINT = `https://d.vidoomy.com/api/rtbserver/prebid/`;
const BIDDER_CODE = 'vidoomy';
const GVLID = 380;

const COOKIE_SYNC_JSON = 'https://vpaid.vidoomy.com/sync/urls.json';

const isBidRequestValid = bid => {
  if (!bid.params) {
    logError(BIDDER_CODE + ': bid.params should be non-empty');
    return false;
  }

  if (!+bid.params.pid) {
    logError(BIDDER_CODE + ': bid.params.pid should be non-empty Number');
    return false;
  }

  if (!+bid.params.id) {
    logError(BIDDER_CODE + ': bid.params.id should be non-empty Number');
    return false;
  }

  if (bid.params && bid.params.mediaTypes && bid.params.mediaTypes.video && bid.params.mediaTypes.video.context === INSTREAM && !bid.params.mediaTypes.video.playerSize) {
    logError(BIDDER_CODE + ': bid.params.mediaType.video should have a playerSize property to tell player size when is INSTREAM');
    return false;
  }

  return true;
};

const isBidResponseValid = bid => {
  if (!bid.requestId || !bid.cpm || !bid.ttl || !bid.currency) {
    return false;
  }
  switch (bid.mediaType) {
    case BANNER:
      return Boolean(bid.width && bid.height && bid.ad);
    case VIDEO:
      return Boolean(bid.vastUrl);
    default:
      return false;
  }
}

const buildRequests = (validBidRequests, bidderRequest) => {
  const serverRequests = validBidRequests.map(bid => {
    let adType = BANNER;
    let w, h;
    if (bid.mediaTypes && bid.mediaTypes[BANNER] && bid.mediaTypes[BANNER].sizes) {
      [w, h] = bid.mediaTypes[BANNER].sizes[0];
      adType = BANNER;
    } else if (bid.mediaTypes && bid.mediaTypes[VIDEO] && bid.mediaTypes[VIDEO].playerSize) {
      [w, h] = bid.mediaTypes[VIDEO].playerSize;
      adType = VIDEO;
    }

    const aElement = document.createElement('a');
    aElement.href = (bidderRequest.refererInfo && bidderRequest.refererInfo.referer) || top.location.href;
    const hostname = aElement.hostname

    const videoContext = deepAccess(bid, 'mediaTypes.video.context');

    const queryParams = [];
    queryParams.push(['id', bid.params.id]);
    queryParams.push(['adtype', adType]);
    queryParams.push(['w', w]);
    queryParams.push(['h', h]);
    queryParams.push(['pos', parseInt(bid.params.position) || 1]);
    queryParams.push(['ua', navigator.userAgent]);
    queryParams.push(['l', navigator.language && navigator.language.indexOf('-') !== -1 ? navigator.language.split('-')[0] : '']);
    queryParams.push(['dt', /Mobi/.test(navigator.userAgent) ? 2 : 1]);
    queryParams.push(['pid', bid.params.pid]);
    queryParams.push(['requestId', bid.bidId]);
    queryParams.push(['d', getDomainWithoutSubdomain(hostname)]);
    queryParams.push(['sp', encodeURIComponent(aElement.href)]);
    if (bidderRequest.gdprConsent) {
      queryParams.push(['gdpr', bidderRequest.gdprConsent.gdprApplies]);
      queryParams.push(['gdprcs', bidderRequest.gdprConsent.consentString]);
    }
    queryParams.push(['usp', bidderRequest.uspConsent || '']);
    queryParams.push(['coppa', !!config.getConfig('coppa')]);

    const rawQueryParams = queryParams.map(qp => qp.join('=')).join('&');

    const xhr = new XMLHttpRequest();
    xhr.open('GET', COOKIE_SYNC_JSON)
    xhr.addEventListener('load', function () {
      const macro = Macro({
        gpdr: bidderRequest.gdprConsent ? bidderRequest.gdprConsent.gdprApplies : '0',
        gpdr_consent: bidderRequest.gdprConsent ? bidderRequest.gdprConsent.consentString : '',
      });
      JSON.parse(this.responseText).filter(Boolean).forEach(url => {
        firePixel(macro.replace(url))
      })
    })
    xhr.send()

    const url = `${ENDPOINT}?${rawQueryParams}`;
    return {
      method: 'GET',
      url: url,
      data: {videoContext}
    }
  });
  return serverRequests;
};

const render = (bid) => {
  bid.ad = bid.vastUrl;
  var obj = {
    vastTimeout: 5000,
    maxAllowedVastTagRedirects: 3,
    allowVpaid: true,
    autoPlay: true,
    preload: true,
    mute: true,
  }
  window.outstreamPlayer(bid, bid.adUnitCode, obj);
}

const interpretResponse = (serverResponse, bidRequest) => {
  try {
    let responseBody = serverResponse.body;
    if (responseBody.mediaType === 'video') {
      responseBody.ad = responseBody.vastUrl;
      const videoContext = bidRequest.data.videoContext;

      if (videoContext === OUTSTREAM) {
        try {
          const renderer = Renderer.install({
            id: bidRequest.bidId,
            adunitcode: bidRequest.tagId,
            loaded: false,
            config: responseBody.mediaType,
            url: responseBody.meta.rendererUrl
          });
          renderer.setRender(render);

          responseBody.renderer = renderer;
        } catch (e) {
          responseBody.ad = responseBody.vastUrl;
          logError(BIDDER_CODE + ': error while installing renderer to show outstream ad');
        }
      }
    }
    const bid = {
      vastUrl: responseBody.vastUrl,
      ad: responseBody.ad,
      renderer: responseBody.renderer,
      mediaType: responseBody.mediaType,
      requestId: responseBody.requestId,
      cpm: responseBody.cpm,
      currency: responseBody.currency,
      width: responseBody.width,
      height: responseBody.height,
      creativeId: responseBody.creativeId,
      netRevenue: responseBody.netRevenue,
      ttl: responseBody.ttl,
      meta: {
        mediaType: responseBody.meta.mediaType,
        rendererUrl: responseBody.meta.rendererUrl,
        advertiserDomains: responseBody.meta.advertiserDomains,
        advertiserId: responseBody.meta.advertiserId,
        advertiserName: responseBody.meta.advertiserName,
        agencyId: responseBody.meta.agencyId,
        agencyName: responseBody.meta.agencyName,
        brandId: responseBody.meta.brandId,
        brandName: responseBody.meta.brandName,
        dchain: responseBody.meta.dchain,
        networkId: responseBody.meta.networkId,
        networkName: responseBody.meta.networkName,
        primaryCatId: responseBody.meta.primaryCatId,
        secondaryCatIds: responseBody.meta.secondaryCatIds
      }
    };

    const bids = [];

    if (isBidResponseValid(bid)) {
      bids.push(bid);
    } else {
      logError(BIDDER_CODE + ': server returns invalid response');
    }

    return bids;
  } catch (e) {
    logError(BIDDER_CODE + ': error parsing server response to Prebid format');
    return [];
  }
};

export const spec = {
  code: BIDDER_CODE,
  supportedMediaTypes: [BANNER, VIDEO],
  isBidRequestValid,
  buildRequests,
  interpretResponse,
  gvlid: GVLID,
};

registerBidder(spec);

function firePixel(url) {
  const img = document.createElement('img');
  img.width = 1;
  img.height = 1;
  img.src = url;
  document.body.appendChild(img);
  setTimeout(() => {
    img.remove();
  }, 10000)
}

function normalizeKey (x) {
  return x.replace(/_/g, '').toLowerCase();
}

function Macro (obj) {
  const macros = {};
  for (const key in obj) {
    macros[normalizeKey(key)] = obj[key];
  }

  const set = (key, value) => {
    macros[normalizeKey(key)] = typeof value === 'function' ? value : String(value);
  };

  return {
    set,
    setAll (obj) {
      for (const key in obj) {
        macros[normalizeKey(key)] = set(obj[key]);
      }
    },
    replace (string, extraMacros = {}) {
      const allMacros = {
        ...macros,
        ...extraMacros,
      };
      const regexes = [
        /{{\s*([a-zA-Z0-9_]+)\s*}}/g,
        /\$\$\s*([a-zA-Z0-9_]+)\s*\$\$/g,
        /\[\s*([a-zA-Z0-9_]+)\s*\]/g,
        /\{\s*([a-zA-Z0-9_]+)\s*\}/g,
      ];
      regexes.forEach(regex => {
        string = string.replace(regex, (str, x) => {
          x = normalizeKey(x);
          let value = allMacros[x];
          value = typeof value === 'function' ? value(allMacros) : value;
          return !value && value !== 0 ? '' : value;
        });
      });
      return string;
    },
  };
}

function getDomainWithoutSubdomain (hostname) {
  const parts = hostname.split('.');
  const newParts = [];
  for (let i = parts.length - 1; i >= 0; i--) {
    newParts.push(parts[i]);
    if (newParts.length !== 1 && parts[i].length > 3) {
      break;
    }
  }
  return newParts.reverse().join('.');
}
<commit_msg>fix(vidoomyBidAdapter): ensure cookie sync is triggered once (#7534)<commit_after>import { logError, deepAccess } from '../src/utils.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {BANNER, VIDEO} from '../src/mediaTypes.js';
import {config} from '../src/config.js';
import { Renderer } from '../src/Renderer.js';
import { INSTREAM, OUTSTREAM } from '../src/video.js';

const ENDPOINT = `https://d.vidoomy.com/api/rtbserver/prebid/`;
const BIDDER_CODE = 'vidoomy';
const GVLID = 380;

const COOKIE_SYNC_JSON = 'https://vpaid.vidoomy.com/sync/urls.json';

const isBidRequestValid = bid => {
  if (!bid.params) {
    logError(BIDDER_CODE + ': bid.params should be non-empty');
    return false;
  }

  if (!+bid.params.pid) {
    logError(BIDDER_CODE + ': bid.params.pid should be non-empty Number');
    return false;
  }

  if (!+bid.params.id) {
    logError(BIDDER_CODE + ': bid.params.id should be non-empty Number');
    return false;
  }

  if (bid.params && bid.params.mediaTypes && bid.params.mediaTypes.video && bid.params.mediaTypes.video.context === INSTREAM && !bid.params.mediaTypes.video.playerSize) {
    logError(BIDDER_CODE + ': bid.params.mediaType.video should have a playerSize property to tell player size when is INSTREAM');
    return false;
  }

  return true;
};

const isBidResponseValid = bid => {
  if (!bid.requestId || !bid.cpm || !bid.ttl || !bid.currency) {
    return false;
  }
  switch (bid.mediaType) {
    case BANNER:
      return Boolean(bid.width && bid.height && bid.ad);
    case VIDEO:
      return Boolean(bid.vastUrl);
    default:
      return false;
  }
}

const buildRequests = (validBidRequests, bidderRequest) => {
  const serverRequests = validBidRequests.map(bid => {
    let adType = BANNER;
    let w, h;
    if (bid.mediaTypes && bid.mediaTypes[BANNER] && bid.mediaTypes[BANNER].sizes) {
      [w, h] = bid.mediaTypes[BANNER].sizes[0];
      adType = BANNER;
    } else if (bid.mediaTypes && bid.mediaTypes[VIDEO] && bid.mediaTypes[VIDEO].playerSize) {
      [w, h] = bid.mediaTypes[VIDEO].playerSize;
      adType = VIDEO;
    }

    const aElement = document.createElement('a');
    aElement.href = (bidderRequest.refererInfo && bidderRequest.refererInfo.referer) || top.location.href;
    const hostname = aElement.hostname

    const videoContext = deepAccess(bid, 'mediaTypes.video.context');

    const queryParams = [];
    queryParams.push(['id', bid.params.id]);
    queryParams.push(['adtype', adType]);
    queryParams.push(['w', w]);
    queryParams.push(['h', h]);
    queryParams.push(['pos', parseInt(bid.params.position) || 1]);
    queryParams.push(['ua', navigator.userAgent]);
    queryParams.push(['l', navigator.language && navigator.language.indexOf('-') !== -1 ? navigator.language.split('-')[0] : '']);
    queryParams.push(['dt', /Mobi/.test(navigator.userAgent) ? 2 : 1]);
    queryParams.push(['pid', bid.params.pid]);
    queryParams.push(['requestId', bid.bidId]);
    queryParams.push(['d', getDomainWithoutSubdomain(hostname)]);
    queryParams.push(['sp', encodeURIComponent(aElement.href)]);
    if (bidderRequest.gdprConsent) {
      queryParams.push(['gdpr', bidderRequest.gdprConsent.gdprApplies]);
      queryParams.push(['gdprcs', bidderRequest.gdprConsent.consentString]);
    }
    queryParams.push(['usp', bidderRequest.uspConsent || '']);
    queryParams.push(['coppa', !!config.getConfig('coppa')]);

    const rawQueryParams = queryParams.map(qp => qp.join('=')).join('&');

    cookieSync(bidderRequest)

    const url = `${ENDPOINT}?${rawQueryParams}`;
    return {
      method: 'GET',
      url: url,
      data: {videoContext}
    }
  });
  return serverRequests;
};

const render = (bid) => {
  bid.ad = bid.vastUrl;
  var obj = {
    vastTimeout: 5000,
    maxAllowedVastTagRedirects: 3,
    allowVpaid: true,
    autoPlay: true,
    preload: true,
    mute: true,
  }
  window.outstreamPlayer(bid, bid.adUnitCode, obj);
}

const interpretResponse = (serverResponse, bidRequest) => {
  try {
    let responseBody = serverResponse.body;
    if (responseBody.mediaType === 'video') {
      responseBody.ad = responseBody.vastUrl;
      const videoContext = bidRequest.data.videoContext;

      if (videoContext === OUTSTREAM) {
        try {
          const renderer = Renderer.install({
            id: bidRequest.bidId,
            adunitcode: bidRequest.tagId,
            loaded: false,
            config: responseBody.mediaType,
            url: responseBody.meta.rendererUrl
          });
          renderer.setRender(render);

          responseBody.renderer = renderer;
        } catch (e) {
          responseBody.ad = responseBody.vastUrl;
          logError(BIDDER_CODE + ': error while installing renderer to show outstream ad');
        }
      }
    }
    const bid = {
      vastUrl: responseBody.vastUrl,
      ad: responseBody.ad,
      renderer: responseBody.renderer,
      mediaType: responseBody.mediaType,
      requestId: responseBody.requestId,
      cpm: responseBody.cpm,
      currency: responseBody.currency,
      width: responseBody.width,
      height: responseBody.height,
      creativeId: responseBody.creativeId,
      netRevenue: responseBody.netRevenue,
      ttl: responseBody.ttl,
      meta: {
        mediaType: responseBody.meta.mediaType,
        rendererUrl: responseBody.meta.rendererUrl,
        advertiserDomains: responseBody.meta.advertiserDomains,
        advertiserId: responseBody.meta.advertiserId,
        advertiserName: responseBody.meta.advertiserName,
        agencyId: responseBody.meta.agencyId,
        agencyName: responseBody.meta.agencyName,
        brandId: responseBody.meta.brandId,
        brandName: responseBody.meta.brandName,
        dchain: responseBody.meta.dchain,
        networkId: responseBody.meta.networkId,
        networkName: responseBody.meta.networkName,
        primaryCatId: responseBody.meta.primaryCatId,
        secondaryCatIds: responseBody.meta.secondaryCatIds
      }
    };

    const bids = [];

    if (isBidResponseValid(bid)) {
      bids.push(bid);
    } else {
      logError(BIDDER_CODE + ': server returns invalid response');
    }

    return bids;
  } catch (e) {
    logError(BIDDER_CODE + ': error parsing server response to Prebid format');
    return [];
  }
};

export const spec = {
  code: BIDDER_CODE,
  supportedMediaTypes: [BANNER, VIDEO],
  isBidRequestValid,
  buildRequests,
  interpretResponse,
  gvlid: GVLID,
};

registerBidder(spec);

let cookieSynced = false;
function cookieSync(bidderRequest) {
  if (cookieSynced) return;
  const xhr = new XMLHttpRequest();
  xhr.open('GET', COOKIE_SYNC_JSON)
  xhr.addEventListener('load', function () {
    const macro = Macro({
      gpdr: bidderRequest.gdprConsent ? bidderRequest.gdprConsent.gdprApplies : '0',
      gpdr_consent: bidderRequest.gdprConsent ? bidderRequest.gdprConsent.consentString : '',
    });
    JSON.parse(this.responseText).filter(Boolean).forEach(url => {
      firePixel(macro.replace(url))
    })
  })
  xhr.send()
  cookieSynced = true;
}

function firePixel(url) {
  const img = document.createElement('img');
  img.width = 1;
  img.height = 1;
  img.src = url;
  document.body.appendChild(img);
  setTimeout(() => {
    img.remove();
  }, 10000)
}

function normalizeKey (x) {
  return x.replace(/_/g, '').toLowerCase();
}

function Macro (obj) {
  const macros = {};
  for (const key in obj) {
    macros[normalizeKey(key)] = obj[key];
  }

  const set = (key, value) => {
    macros[normalizeKey(key)] = typeof value === 'function' ? value : String(value);
  };

  return {
    set,
    setAll (obj) {
      for (const key in obj) {
        macros[normalizeKey(key)] = set(obj[key]);
      }
    },
    replace (string, extraMacros = {}) {
      const allMacros = {
        ...macros,
        ...extraMacros,
      };
      const regexes = [
        /{{\s*([a-zA-Z0-9_]+)\s*}}/g,
        /\$\$\s*([a-zA-Z0-9_]+)\s*\$\$/g,
        /\[\s*([a-zA-Z0-9_]+)\s*\]/g,
        /\{\s*([a-zA-Z0-9_]+)\s*\}/g,
      ];
      regexes.forEach(regex => {
        string = string.replace(regex, (str, x) => {
          x = normalizeKey(x);
          let value = allMacros[x];
          value = typeof value === 'function' ? value(allMacros) : value;
          return !value && value !== 0 ? '' : value;
        });
      });
      return string;
    },
  };
}

function getDomainWithoutSubdomain (hostname) {
  const parts = hostname.split('.');
  const newParts = [];
  for (let i = parts.length - 1; i >= 0; i--) {
    newParts.push(parts[i]);
    if (newParts.length !== 1 && parts[i].length > 3) {
      break;
    }
  }
  return newParts.reverse().join('.');
}
<|endoftext|>