<commit_before>class Pathi extends Chara{
	static getText(){
		//キャラ説明
		return "ジャマ―タイプ：
敵の妨害をするのが得意";
	}
	constructor(aX,aY,aTeam,aOperationNum){
		let tData=Pathi.getCharaData();
		super(aX,aY,aTeam,tData,aOperationNum);
		this.data=tData;
	}
	static getCharaData(){
		return {NAME:"パティ",
						HP:90,
						MP:5,
						ATK:25,
						DEF:25,
						SPD:22,
						MOV:2,
						TYPE:"heart",
						IMAGE:1437010001,
						DECK:Pathi.getDeck()
		}
	}
	static getDeck(){
		return [
			{NUMBER:801,
				TEXT:"周囲１２マスにいる相手の攻撃力を２下げ、威力２のダメージ",
				RANGE:[["circumference",2]],
				POWER:2,
				DELAY:0,
				MAGIC:0,
				SUPPORT_Be_Myself:[],
				SUPPORT_Af_Myself:[],
				SUPPORT_Be_Enemy:[],
				SUPPORT_Af_Enemy:[{effect:"atk",value:-2}],
				SUPPORT_Otherwise:[],
				M_ATTACK:0,
				F_ATTACK:false,
		    E_ATTACK:true,
				ANIMATION:[0]
			},
			{NUMBER:802,
				TEXT:"周囲１２マスにいる相手の防御力を２下げ、威力２のダメージ",
				RANGE:[["circumference",2]],
				POWER:2,
				DELAY:0,
				MAGIC:0,
				SUPPORT_Be_Myself:[],
				SUPPORT_Af_Myself:[],
				SUPPORT_Be_Enemy:[],
				SUPPORT_Af_Enemy:[{effect:"def",value:-2}],
				SUPPORT_Otherwise:[],
				M_ATTACK:0,
				F_ATTACK:false,
		    E_ATTACK:true,
				ANIMATION:[0]
			},
			{NUMBER:803,
				TEXT:"周囲１２マスにいる相手のスピードを２下げ、威力２のダメージ",
				RANGE:[["circumference",2]],
				POWER:2,
				DELAY:0,
				MAGIC:0,
				SUPPORT_Be_Myself:[],
				SUPPORT_Af_Myself:[],
				SUPPORT_Be_Enemy:[],
				SUPPORT_Af_Enemy:[{effect:"spd",value:-2}],
				SUPPORT_Otherwise:[],
				M_ATTACK:0,
				F_ATTACK:false,
		    E_ATTACK:true,
				ANIMATION:[0]
			},
			{NUMBER:804,
				TEXT:"周囲１２マスにいる相手のDELAYを２５上げ、威力２のダメージ",
				RANGE:[["circumference",2]],
				POWER:2,
				DELAY:0,
				MAGIC:0,
				SUPPORT_Be_Myself:[],
				SUPPORT_Af_Myself:[],
				SUPPORT_Be_Enemy:[],
				SUPPORT_Af_Enemy:[{effect:"delay",value:-25}],
				SUPPORT_Otherwise:[],
				M_ATTACK:0,
				F_ATTACK:false,
		    E_ATTACK:true,
				ANIMATION:[17]
			},
			{NUMBER:805,
				TEXT:"隣のマスにいる相手のDELAYを５０上げ、威力２のダメージ（消費MP２）",
				RANGE:[["distance",1]],
				POWER:2,
				DELAY:0,
				MAGIC:2,
				SUPPORT_Be_Myself:[],
				SUPPORT_Af_Myself:[],
				SUPPORT_Be_Enemy:[],
				SUPPORT_Af_Enemy:[{effect:"delay",value:50}],
				SUPPORT_Otherwise:[],
				M_ATTACK:0,
				F_ATTACK:false,
		    E_ATTACK:true,
				ANIMATION:[17]
			},
			{NUMBER:101,
				TEXT:"隣のマスに威力５のダメージ",
				RANGE:[["distance",1]],
				POWER:5,
				DELAY:0,
				MAGIC:0,
				SUPPORT_Be_Myself:[],
				SUPPORT_Af_Myself:[],
				SUPPORT_Be_Enemy:[],
				SUPPORT_Af_Enemy:[],
				SUPPORT_Otherwise:[],
				M_ATTACK:0,
				F_ATTACK:false,
		    E_ATTACK:true,
				ANIMATION:[4]
			},
			{NUMBER:7,
				TEXT:"シャッフルしてもう一度行動",
				RANGE:[],
				POWER:0,
				DELAY:0,
				MAGIC:1,
				SUPPORT_Be_Myself:[],
				SUPPORT_Af_Myself:[{effect:"getTurn"}],
				SUPPORT_Be_Enemy:[],
				SUPPORT_Af_Enemy:[],
				SUPPORT_Otherwise:[{effect:"shuffle"}],
				M_ATTACK:0,
				F_ATTACK:false,
				E_ATTACK:true,
				ANIMATION:[0]
			},
			{NUMBER:808,
				TEXT:"相手全体のDELAYを２５上げ、威力１のダメージ",
				RANGE:[["enemy"]],
				POWER:1,
				DELAY:0,
				MAGIC:0,
				SUPPORT_Be_Myself:[],
				SUPPORT_Af_Myself:[],
				SUPPORT_Be_Enemy:[],
				SUPPORT_Af_Enemy:[{effect:"delay",value:25}],
				SUPPORT_Otherwise:[],
				M_ATTACK:0,
				F_ATTACK:false,
		    E_ATTACK:true,
				ANIMATION:[17]
			},
			{NUMBER:102,
				TEXT:"２マス隣に威力４のダメージ",
				RANGE:[["distance",2]],
				POWER:4,
				DELAY:0,
				MAGIC:0,
				SUPPORT_Be_Myself:[],
				SUPPORT_Af_Myself:[],
				SUPPORT_Be_Enemy:[],
				SUPPORT_Af_Enemy:[],
				SUPPORT_Otherwise:[],
				M_ATTACK:0,
				F_ATTACK:false,
		    E_ATTACK:true,
				ANIMATION:[0]
			},
			{NUMBER:501,
				TEXT:"MPを３回復 (タイプ一致時４回復)",
				RANGE:[],
				POWER:0,
				DELAY:0,
				MAGIC:0,
				SUPPORT_Be_Myself:[{effect:"mp",value:3,additionOfMatchingType:1}],
				SUPPORT_Af_Myself:[],
				SUPPORT_Be_Enemy:[],
				SUPPORT_Af_Enemy:[],
				SUPPORT_Otherwise:[],
				M_ATTACK:0,
				F_ATTACK:false,
		    E_ATTACK:true,
				ANIMATION:[15]
			},
			{NUMBER:612,
				TEXT:"スピード4アップ　その後DELAYを50下げる（タイプ一致時6アップ）（消費MP２）",
				RANGE:[],
				POWER:0,
				DELAY:0,
				MAGIC:2,
				SUPPORT_Be_Myself:[],
				SUPPORT_Af_Myself:[{effect:"spd",value:4,additionOfMatchingType:2},{effect:"delay",value:50}],
				SUPPORT_Be_Enemy:[],
				SUPPORT_Af_Enemy:[],
				SUPPORT_Otherwise:[],
				M_ATTACK:0,
				F_ATTACK:false,
		    E_ATTACK:true,
				ANIMATION:[0]
			},
			{NUMBER:112,
				TEXT:"自分に威力５の回復（消費MP３）",
				RANGE:[],
				POWER:0,
				DELAY:0,
				MAGIC:3,
				SUPPORT_Be_Myself:[],
				SUPPORT_Af_Myself:[],
				SUPPORT_Be_Enemy:[],
				SUPPORT_Af_Enemy:[],
				SUPPORT_Otherwise:[],
				M_ATTACK:-5,
				F_ATTACK:false,
		    E_ATTACK:true,
				ANIMATION:[7]
			},
			{NUMBER:113,
				TEXT:"味方に威力３の回復（消費MP４）",
				RANGE:[["ally"]],
				POWER:-3,
				DELAY:0,
				MAGIC:4,
				SUPPORT_Be_Myself:[],
				SUPPORT_Af_Myself:[],
				SUPPORT_Be_Enemy:[],
				SUPPORT_Af_Enemy:[],
				SUPPORT_Otherwise:[],
				M_ATTACK:0,
				F_ATTACK:true,
		    E_ATTACK:false,
				ANIMATION:[7]
			},
			{NUMBER:814,
				TEXT:"相手全体のDELAYを５０上げ、威力３のダメージ　その後シャッフルする",
				RANGE:[["enemy"]],
				POWER:3,
				DELAY:0,
				MAGIC:0,
				SUPPORT_Be_Myself:[],
				SUPPORT_Af_Myself:[],
				SUPPORT_Be_Enemy:[],
				SUPPORT_Af_Enemy:[{effect:"delay",value:50}],
				SUPPORT_Otherwise:[{effect:"revers"},{effect:"shuffle"}],
				M_ATTACK:0,
				F_ATTACK:false,
		    E_ATTACK:true,
				ANIMATION:[17]
			},
			{NUMBER:0,
					TEXT:"スカ　何も起こらない",
					RANGE:[],
					POWER:0,
					DELAY:0,
					MAGIC:0,
					SUPPORT_Be_Myself:[],
					SUPPORT_Af_Myself:[],
					SUPPORT_Be_Enemy:[],
					SUPPORT_Af_Enemy:[],
					SUPPORT_Otherwise:[{effect:"revers"}],
					M_ATTACK:0,
					F_ATTACK:false,
					E_ATTACK:true,
					ANIMATION:[0]
				},]
	}
}
<commit_msg>パティのスキルバグ修正<commit_after>class Pathi extends Chara{
	static getText(){
		//キャラ説明
		return "ジャマ―タイプ：
敵の妨害をするのが得意";
	}
	constructor(aX,aY,aTeam,aOperationNum){
		let tData=Pathi.getCharaData();
		super(aX,aY,aTeam,tData,aOperationNum);
		this.data=tData;
	}
	static getCharaData(){
		return {NAME:"パティ",
						HP:90,
						MP:5,
						ATK:25,
						DEF:25,
						SPD:22,
						MOV:2,
						TYPE:"heart",
						IMAGE:1437010001,
						DECK:Pathi.getDeck()
		}
	}
	static getDeck(){
		return [
			{NUMBER:801,
				TEXT:"周囲１２マスにいる相手の攻撃力を２下げ、威力２のダメージ",
				RANGE:[["circumference",2]],
				POWER:2,
				DELAY:0,
				MAGIC:0,
				SUPPORT_Be_Myself:[],
				SUPPORT_Af_Myself:[],
				SUPPORT_Be_Enemy:[],
				SUPPORT_Af_Enemy:[{effect:"atk",value:-2}],
				SUPPORT_Otherwise:[],
				M_ATTACK:0,
				F_ATTACK:false,
		    E_ATTACK:true,
				ANIMATION:[0]
			},
			{NUMBER:802,
				TEXT:"周囲１２マスにいる相手の防御力を２下げ、威力２のダメージ",
				RANGE:[["circumference",2]],
				POWER:2,
				DELAY:0,
				MAGIC:0,
				SUPPORT_Be_Myself:[],
				SUPPORT_Af_Myself:[],
				SUPPORT_Be_Enemy:[],
				SUPPORT_Af_Enemy:[{effect:"def",value:-2}],
				SUPPORT_Otherwise:[],
				M_ATTACK:0,
				F_ATTACK:false,
		    E_ATTACK:true,
				ANIMATION:[0]
			},
			{NUMBER:803,
				TEXT:"周囲１２マスにいる相手のスピードを２下げ、威力２のダメージ",
				RANGE:[["circumference",2]],
				POWER:2,
				DELAY:0,
				MAGIC:0,
				SUPPORT_Be_Myself:[],
				SUPPORT_Af_Myself:[],
				SUPPORT_Be_Enemy:[],
				SUPPORT_Af_Enemy:[{effect:"spd",value:-2}],
				SUPPORT_Otherwise:[],
				M_ATTACK:0,
				F_ATTACK:false,
		    E_ATTACK:true,
				ANIMATION:[0]
			},
			{NUMBER:804,
				TEXT:"周囲１２マスにいる相手のDELAYを２５上げ、威力２のダメージ",
				RANGE:[["circumference",2]],
				POWER:2,
				DELAY:0,
				MAGIC:0,
				SUPPORT_Be_Myself:[],
				SUPPORT_Af_Myself:[],
				SUPPORT_Be_Enemy:[],
				SUPPORT_Af_Enemy:[{effect:"delay",value:25}],
				SUPPORT_Otherwise:[],
				M_ATTACK:0,
				F_ATTACK:false,
		    E_ATTACK:true,
				ANIMATION:[17]
			},
			{NUMBER:805,
				TEXT:"隣のマスにいる相手のDELAYを５０上げ、威力２のダメージ（消費MP２）",
				RANGE:[["distance",1]],
				POWER:2,
				DELAY:0,
				MAGIC:2,
				SUPPORT_Be_Myself:[],
				SUPPORT_Af_Myself:[],
				SUPPORT_Be_Enemy:[],
				SUPPORT_Af_Enemy:[{effect:"delay",value:50}],
				SUPPORT_Otherwise:[],
				M_ATTACK:0,
				F_ATTACK:false,
		    E_ATTACK:true,
				ANIMATION:[17]
			},
			{NUMBER:101,
				TEXT:"隣のマスに威力５のダメージ",
				RANGE:[["distance",1]],
				POWER:5,
				DELAY:0,
				MAGIC:0,
				SUPPORT_Be_Myself:[],
				SUPPORT_Af_Myself:[],
				SUPPORT_Be_Enemy:[],
				SUPPORT_Af_Enemy:[],
				SUPPORT_Otherwise:[],
				M_ATTACK:0,
				F_ATTACK:false,
		    E_ATTACK:true,
				ANIMATION:[4]
			},
			{NUMBER:7,
				TEXT:"シャッフルしてもう一度行動",
				RANGE:[],
				POWER:0,
				DELAY:0,
				MAGIC:1,
				SUPPORT_Be_Myself:[],
				SUPPORT_Af_Myself:[{effect:"getTurn"}],
				SUPPORT_Be_Enemy:[],
				SUPPORT_Af_Enemy:[],
				SUPPORT_Otherwise:[{effect:"shuffle"}],
				M_ATTACK:0,
				F_ATTACK:false,
				E_ATTACK:true,
				ANIMATION:[0]
			},
			{NUMBER:808,
				TEXT:"相手全体のDELAYを２５上げ、威力１のダメージ",
				RANGE:[["enemy"]],
				POWER:1,
				DELAY:0,
				MAGIC:0,
				SUPPORT_Be_Myself:[],
				SUPPORT_Af_Myself:[],
				SUPPORT_Be_Enemy:[],
				SUPPORT_Af_Enemy:[{effect:"delay",value:25}],
				SUPPORT_Otherwise:[],
				M_ATTACK:0,
				F_ATTACK:false,
		    E_ATTACK:true,
				ANIMATION:[17]
			},
			{NUMBER:102,
				TEXT:"２マス隣に威力４のダメージ",
				RANGE:[["distance",2]],
				POWER:4,
				DELAY:0,
				MAGIC:0,
				SUPPORT_Be_Myself:[],
				SUPPORT_Af_Myself:[],
				SUPPORT_Be_Enemy:[],
				SUPPORT_Af_Enemy:[],
				SUPPORT_Otherwise:[],
				M_ATTACK:0,
				F_ATTACK:false,
		    E_ATTACK:true,
				ANIMATION:[0]
			},
			{NUMBER:501,
				TEXT:"MPを３回復 (タイプ一致時４回復)",
				RANGE:[],
				POWER:0,
				DELAY:0,
				MAGIC:0,
				SUPPORT_Be_Myself:[{effect:"mp",value:3,additionOfMatchingType:1}],
				SUPPORT_Af_Myself:[],
				SUPPORT_Be_Enemy:[],
				SUPPORT_Af_Enemy:[],
				SUPPORT_Otherwise:[],
				M_ATTACK:0,
				F_ATTACK:false,
		    E_ATTACK:true,
				ANIMATION:[15]
			},
			{NUMBER:612,
				TEXT:"スピード4アップ　その後DELAYを50下げる（タイプ一致時6アップ）（消費MP２）",
				RANGE:[],
				POWER:0,
				DELAY:0,
				MAGIC:2,
				SUPPORT_Be_Myself:[],
				SUPPORT_Af_Myself:[{effect:"spd",value:4,additionOfMatchingType:2},{effect:"delay",value:-50}],
				SUPPORT_Be_Enemy:[],
				SUPPORT_Af_Enemy:[],
				SUPPORT_Otherwise:[],
				M_ATTACK:0,
				F_ATTACK:false,
		    E_ATTACK:true,
				ANIMATION:[0]
			},
			{NUMBER:112,
				TEXT:"自分に威力５の回復（消費MP３）",
				RANGE:[],
				POWER:0,
				DELAY:0,
				MAGIC:3,
				SUPPORT_Be_Myself:[],
				SUPPORT_Af_Myself:[],
				SUPPORT_Be_Enemy:[],
				SUPPORT_Af_Enemy:[],
				SUPPORT_Otherwise:[],
				M_ATTACK:-5,
				F_ATTACK:false,
		    E_ATTACK:true,
				ANIMATION:[7]
			},
			{NUMBER:113,
				TEXT:"味方に威力３の回復（消費MP４）",
				RANGE:[["ally"]],
				POWER:-3,
				DELAY:0,
				MAGIC:4,
				SUPPORT_Be_Myself:[],
				SUPPORT_Af_Myself:[],
				SUPPORT_Be_Enemy:[],
				SUPPORT_Af_Enemy:[],
				SUPPORT_Otherwise:[],
				M_ATTACK:0,
				F_ATTACK:true,
		    E_ATTACK:false,
				ANIMATION:[7]
			},
			{NUMBER:814,
				TEXT:"相手全体のDELAYを５０上げ、威力３のダメージ　その後シャッフルする",
				RANGE:[["enemy"]],
				POWER:3,
				DELAY:0,
				MAGIC:0,
				SUPPORT_Be_Myself:[],
				SUPPORT_Af_Myself:[],
				SUPPORT_Be_Enemy:[],
				SUPPORT_Af_Enemy:[{effect:"delay",value:50}],
				SUPPORT_Otherwise:[{effect:"revers"},{effect:"shuffle"}],
				M_ATTACK:0,
				F_ATTACK:false,
		    E_ATTACK:true,
				ANIMATION:[17]
			},
			{NUMBER:0,
					TEXT:"スカ　何も起こらない",
					RANGE:[],
					POWER:0,
					DELAY:0,
					MAGIC:0,
					SUPPORT_Be_Myself:[],
					SUPPORT_Af_Myself:[],
					SUPPORT_Be_Enemy:[],
					SUPPORT_Af_Enemy:[],
					SUPPORT_Otherwise:[{effect:"revers"}],
					M_ATTACK:0,
					F_ATTACK:false,
					E_ATTACK:true,
					ANIMATION:[0]
				},]
	}
}
<|endoftext|>


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

Elm.fullscreen = function(module) {
    var style = document.createElement('style');
    style.type = 'text/css';
    style.innerHTML = "html,head,body { padding:0; margin:0; }" +
        "body { font-family: calibri, helvetica, arial, sans-serif; }";
    document.head.appendChild(style);
    var container = document.createElement('div');
    document.body.appendChild(container);
    return init(ElmRuntime.Display.FULLSCREEN, container, module);
};

Elm.byId = function(id, module) {
    var container = document.getElementById(id);
    var tag = container.tagName;
    if (tag !== 'DIV') {
        throw new Error('Elm.byId must be given a div, not a ' + tag + '.');
    }
    while (container.hasChildNodes()) {
        container.removeChild(container.lastChild);
    }
    return init(ElmRuntime.Display.COMPONENT, container, module);
};

Elm.worker = function(module) {
    return init(ElmRuntime.Display.NONE, {}, module);
};

function init(display, container, module) {
  // defining state needed for an instance of the Elm RTS
  var signalGraph = null;
  var inputs = [];
  var visualModel = null;

  function notify(id, v) {
    var timestep = Date.now();
    var hasListener = false;
    for (var i = inputs.length; i--; ) {
      // must maintain the order of this stmt to avoid having the ||
      // short-circuiting the necessary work of recv
      hasListener = inputs[i].recv(timestep, id, v) || hasListener;
    }
    return hasListener;
  }

  container.offsetX = 0;
  container.offsetY = 0;

  // create the actual RTS. Any impure modules will attach themselves to this
  // object. This permits many Elm programs to be embedded per document.
  var elm = { notify:notify,
              node:container,
              display:display,
              id:ElmRuntime.guid(),
              inputs:inputs
  };

  // Set up methods to communicate with Elm program from JS.
  function send(name, value) {
      if (typeof value === 'undefined') return function(v) { return send(name,v); };
      var e = document.createEvent('Event');
      e.initEvent(name + '_' + elm.id, true, true);
      e.value = value;
      document.dispatchEvent(e);
  }
  function recv(name, handler) {
      document.addEventListener(name + '_' + elm.id, handler);
  }

  recv('log', function(e) {console.log(e.value)});
  recv('title', function(e) {document.title = e.value});
  recv('redirect', function(e) {
    if (e.value.length > 0) { window.location = e.value; }
  });

  // If graphics are not enabled, escape early, skip over setting up DOM stuff.
  if (display === ElmRuntime.Display.NONE) {
      return { send : send, recv : recv };
  }

  var Render = ElmRuntime.use(ElmRuntime.Render.Element);

  // evaluate the given module and extract its 'main' value.
  signalGraph = module(elm).main;

  // make sure the signal graph is actually a signal, extract the visual model,
  // and filter out any unused inputs.
  var Signal = Elm.Signal(elm);
  if (!('recv' in signalGraph)) signalGraph = Signal.constant(signalGraph);
  visualModel = signalGraph.value;
  inputs = ElmRuntime.filterDeadInputs(inputs);

   // Add the visualModel to the DOM
  container.appendChild(Render.render(visualModel));
  if (elm.Native.Window) elm.Native.Window.resizeIfNeeded();

  // set up updates so that the DOM is adjusted as necessary.
  var update = Render.update;
  function domUpdate(value) {
      ElmRuntime.draw(function(_) {
              update(container.firstChild, visualModel, value);
              visualModel = value;
              if (elm.Native.Window) elm.Native.Window.resizeIfNeeded();
          });
      return value;
  }

  signalGraph = A2(Signal.lift, domUpdate, signalGraph);

  return { send : send, recv : recv, node : container };
};

}());<commit_msg>Actually build the module when using `Elm.worker`<commit_after>
(function() {
'use strict';

Elm.fullscreen = function(module) {
    var style = document.createElement('style');
    style.type = 'text/css';
    style.innerHTML = "html,head,body { padding:0; margin:0; }" +
        "body { font-family: calibri, helvetica, arial, sans-serif; }";
    document.head.appendChild(style);
    var container = document.createElement('div');
    document.body.appendChild(container);
    return init(ElmRuntime.Display.FULLSCREEN, container, module);
};

Elm.byId = function(id, module) {
    var container = document.getElementById(id);
    var tag = container.tagName;
    if (tag !== 'DIV') {
        throw new Error('Elm.byId must be given a div, not a ' + tag + '.');
    }
    while (container.hasChildNodes()) {
        container.removeChild(container.lastChild);
    }
    return init(ElmRuntime.Display.COMPONENT, container, module);
};

Elm.worker = function(module) {
    return init(ElmRuntime.Display.NONE, {}, module);
};

function init(display, container, module) {
  // defining state needed for an instance of the Elm RTS
  var signalGraph = null;
  var inputs = [];
  var visualModel = null;

  function notify(id, v) {
    var timestep = Date.now();
    var hasListener = false;
    for (var i = inputs.length; i--; ) {
      // must maintain the order of this stmt to avoid having the ||
      // short-circuiting the necessary work of recv
      hasListener = inputs[i].recv(timestep, id, v) || hasListener;
    }
    return hasListener;
  }

  container.offsetX = 0;
  container.offsetY = 0;

  // create the actual RTS. Any impure modules will attach themselves to this
  // object. This permits many Elm programs to be embedded per document.
  var elm = { notify:notify,
              node:container,
              display:display,
              id:ElmRuntime.guid(),
              inputs:inputs
  };

  // Set up methods to communicate with Elm program from JS.
  function send(name, value) {
      if (typeof value === 'undefined') return function(v) { return send(name,v); };
      var e = document.createEvent('Event');
      e.initEvent(name + '_' + elm.id, true, true);
      e.value = value;
      document.dispatchEvent(e);
  }
  function recv(name, handler) {
      document.addEventListener(name + '_' + elm.id, handler);
  }

  recv('log', function(e) {console.log(e.value)});
  recv('title', function(e) {document.title = e.value});
  recv('redirect', function(e) {
    if (e.value.length > 0) { window.location = e.value; }
  });

  // If graphics are not enabled, escape early, skip over setting up DOM stuff.
  if (display === ElmRuntime.Display.NONE) {
      module(elm);
      return { send : send, recv : recv };
  }

  var Render = ElmRuntime.use(ElmRuntime.Render.Element);

  // evaluate the given module and extract its 'main' value.
  signalGraph = module(elm).main;

  // make sure the signal graph is actually a signal, extract the visual model,
  // and filter out any unused inputs.
  var Signal = Elm.Signal(elm);
  if (!('recv' in signalGraph)) signalGraph = Signal.constant(signalGraph);
  visualModel = signalGraph.value;
  inputs = ElmRuntime.filterDeadInputs(inputs);

   // Add the visualModel to the DOM
  container.appendChild(Render.render(visualModel));
  if (elm.Native.Window) elm.Native.Window.resizeIfNeeded();

  // set up updates so that the DOM is adjusted as necessary.
  var update = Render.update;
  function domUpdate(value) {
      ElmRuntime.draw(function(_) {
              update(container.firstChild, visualModel, value);
              visualModel = value;
              if (elm.Native.Window) elm.Native.Window.resizeIfNeeded();
          });
      return value;
  }

  signalGraph = A2(Signal.lift, domUpdate, signalGraph);

  return { send : send, recv : recv, node : container };
};

}());<|endoftext|>


<commit_before>var e = module.exports = {};
var bu;

var bot;
e.init = (Tbot, blargutil) => {
    bot = Tbot;
    bu = blargutil;

    e.category = bu.CommandType.ADMIN;
};

e.requireCtx = require;

e.isCommand = true;
e.hidden = false;
e.usage = 'modlog [disable]';
e.info = 'Enables the modlog and sets it to the current channel. Doing \`modlog disable\` will disable it. '
    + 'When an admin does a moderation command (ban, unban, mute, unmute, and kick), the incident will be logged. '
    + 'The admin will then be encouraged to do \`reason <case number> <reason>\` to specify why '
    + 'the action took place.'
    + '\nBans and unbans are logged regardless of whether the \`ban\` or \`unban\` commands are used.';
e.longinfo = `
Enables the modlog and sets it to the current channel. Doing modlog disable will disable it.
        When an admin does a moderation command (ban, unban, mute, unmute, and kick), the incident will be logged.
        The admin will then be encouraged to do reason <case number> <reason> to specify why
        the action took place.

Bans and unbans are logged regardless of whether the ban or unban commands are used.

`;

e.execute = (msg, words) => {

    if (words[1] == 'disable') {
        bu.guildSettings.remove(msg.channel.guild.id, 'modlog').then(() => {
            bu.sendMessageToDiscord(msg.channel.id, 'Modlog disabled!');
        });
    } else {
        bu.guildSettings.set(msg.channel.guild.id, 'modlog', msg.channel.id).then(() => {
            bu.sendMessageToDiscord(msg.channel.id, 'Modlog channel set!');
        });
    }
};<commit_msg>modlog clear<commit_after>var e = module.exports = {};
var bu;

var bot;
e.init = (Tbot, blargutil) => {
    bot = Tbot;
    bu = blargutil;

    e.category = bu.CommandType.ADMIN;
};

e.requireCtx = require;

e.isCommand = true;
e.hidden = false;
e.usage = 'modlog [disable | clear [number to clear]]';
e.info = 'Enables the modlog and sets it to the current channel. Doing \`modlog disable\` will disable it. Doing \`modlog clear [number]\` will clear the specified number of cases from the modlog. Leaving \`number\` blank will clear all cases.'
    + 'When an admin does a moderation command (ban, unban, mute, unmute, and kick), the incident will be logged. '
    + 'The admin will then be encouraged to do \`reason <case number> <reason>\` to specify why '
    + 'the action took place.'
    + '\nBans and unbans are logged regardless of whether the \`ban\` or \`unban\` commands are used.';
e.longinfo = `
Enables the modlog and sets it to the current channel. Doing modlog disable will disable it. Doing modlog disable will disable it. Doing modlog clear [number] will clear the specified number of cases from the modlog. Leaving number blank will clear all cases.
        When an admin does a moderation command (ban, unban, mute, unmute, and kick), the incident will be logged.
        The admin will then be encouraged to do reason <case number> <reason> to specify why
        the action took place.

Bans and unbans are logged regardless of whether the ban or unban commands are used.

`;

e.execute = (msg, words) => {
    if (words[1]) {
        switch (words[1].toLowerCase()) {
            case 'disable':
                bu.guildSettings.remove(msg.channel.guild.id, 'modlog').then(() => {
                    bu.sendMessageToDiscord(msg.channel.id, 'Modlog disabled!');
                });
                break;
            case 'clear':
                var limit = 0;
                if (words[2]) {
                    limit = parseInt(words[2]);
                    if (isNaN(limit)) {
                        bu.send(msg.channel.id, 'Invalid number of cases to clear');
                        return;
                    }
                }
                bu.db.query('select * from modlog where guildid = '
                    + bu.db.escape(msg.channel.guild.id) +
                    (limit > 0 ? ' order by caseid desc limit ' + bu.db.escape(limit) : '')
                    , (err, rows) => {
                        if (rows && rows.length > 0) {
                            var messages = [];
                            for (var i = 0; i < rows.length; i++) {
                                messages.push(rows[i].msgid);
                            }
                            bu.guildSettings.get(msg.channel.guild.id, 'modlog').then(channelid => {
                                bot.deleteMessages(channelid, messages);
                            });
                            bu.db.query('delete from modlog where guildid = '
                                + bu.db.escape(msg.channel.guild.id) +
                                (limit > 0 ? ' order by caseid desc limit ' + bu.db.escape(limit) : ''), () => {
                                    bu.send(msg.channel.id, 'Cleared ' + (limit > 0 ? limit : 'all') + ' cases from the modlog.');
                                });
                        }
                    });
                break;
        }
    } else {
        bu.guildSettings.set(msg.channel.guild.id, 'modlog', msg.channel.id).then(() => {
            bu.sendMessageToDiscord(msg.channel.id, 'Modlog channel set!');
        });
    }
};<|endoftext|>


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

describes.endtoend(
  'amp-bind',
  {
    fixture: 'amp-bind/bind-brightcove.html',
  },
  async (env) => {
    let controller;

    beforeEach(async () => {
      controller = env.controller;
    });

    describe('with <amp-brightcove>', () => {
      it('should support binding to data-account', async () => {
        const button = await controller.findElement('#brightcoveButton');
        const iframe = await controller.findElement('#brightcove iframe');

        await expect(
          controller.getElementProperty(iframe, 'src')
        ).to.not.contain('bound');

        await controller.click(button);
        await expect(controller.getElementProperty(iframe, 'src')).to.contain(
          'bound'
        );
      });
    });
  }
);
<commit_msg>✅ Remove a couple unnecessary `async`s from `amp-bind` e2e test (#35671)<commit_after>/**
 * Copyright 2019 The AMP HTML Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

describes.endtoend(
  'amp-bind',
  {
    fixture: 'amp-bind/bind-brightcove.html',
  },
  (env) => {
    let controller;

    beforeEach(() => {
      controller = env.controller;
    });

    describe('with <amp-brightcove>', () => {
      it('should support binding to data-account', async () => {
        const button = await controller.findElement('#brightcoveButton');
        const iframe = await controller.findElement('#brightcove iframe');

        await expect(
          controller.getElementProperty(iframe, 'src')
        ).to.not.contain('bound');

        await controller.click(button);
        await expect(controller.getElementProperty(iframe, 'src')).to.contain(
          'bound'
        );
      });
    });
  }
);
<|endoftext|>


<commit_before>import React from 'react';
import ReactQuill from 'react-quill';
import Quill from 'quill';

// This style will be lazy loaded by LazyRichTextEditor
import 'react-quill/dist/quill.snow.css';

const Block = Quill.import('blots/block');
Block.tagName = 'div';
Quill.register(Block);

const RichTextEditor = ({ ...rest }) => <ReactQuill {...rest} />;

export default RichTextEditor;
<commit_msg>Update Quill styles<commit_after>import React from 'react';
import ReactQuill from 'react-quill';
import Quill from 'quill';

// This style will be lazy loaded by LazyRichTextEditor
import 'design-system/_sass/react-styles/quill/_snow.scss';

const Block = Quill.import('blots/block');
Block.tagName = 'div';
Quill.register(Block);

const RichTextEditor = ({ ...rest }) => <ReactQuill {...rest} />;

export default RichTextEditor;
<|endoftext|>


<commit_before>/*
 * Wegas
 * http://wegas.albasim.ch
 *
 * Copyright (c) 2013, 2014, 2015 School of Business and Engineering Vaud, Comem
 * Licensed under the MIT License
 */
tinymce.PluginManager.add('dynamic_toolbar', function(editor) {
    var first = true;
    function showHideToolbar() {
        var toolbar = editor.theme.panel.find('toolbar');
        var resizeY = 0;
        if (first) {
            this.active(false);
            first = false;
        } else {
            this.active(!this.active());
        }
        var state = this.active(),
            i,
            barToHide = editor.settings.hidden_tootlbar;

        if (!barToHide) {
            barToHide = [2];
        }
        if (state) {
            for (i = 0; i < barToHide.length; i += 1) {
                toolbar[barToHide[i] - 1].show();
                resizeY += 26;
            }
        } else {
            for (i = 0; i < barToHide.length; i += 1) {
                toolbar[barToHide[i] - 1].hide();
                resizeY -= 26;
            }
        }
        if (editor.settings.inline) {
            editor.theme.panel.resizeBy(0, resizeY);
            editor.theme.panel.moveRel(editor.getBody(), 'tl-bl');
        }
    }

    editor.addButton('addToolbarButton', {
        image: 'wegas-editor/images/more.png',
        title: 'More options',
        onclick: showHideToolbar,
        onPostRender: showHideToolbar
    });
});
<commit_msg>TinyMCE fix toolbar position<commit_after>/*
 * Wegas
 * http://wegas.albasim.ch
 *
 * Copyright (c) 2013, 2014, 2015 School of Business and Engineering Vaud, Comem
 * Licensed under the MIT License
 */
tinymce.PluginManager.add('dynamic_toolbar', function(editor) {
    var first = true;
    function showHideToolbar() {
        var toolbar = editor.theme.panel.find('toolbar');
        var resizeY = 0;
        if (first) {
            this.active(false);
            first = false;
        } else {
            this.active(!this.active());
        }
        var state = this.active(),
            i,
            barToHide = editor.settings.hidden_tootlbar;

        if (!barToHide) {
            barToHide = [2];
        }
        if (state) {
            for (i = 0; i < barToHide.length; i += 1) {
                toolbar[barToHide[i] - 1].show();
                resizeY += 26;
            }
        } else {
            for (i = 0; i < barToHide.length; i += 1) {
                toolbar[barToHide[i] - 1].hide();
                resizeY -= 26;
            }
        }
        if (editor.settings.inline) {
            editor.theme.panel.resizeBy(0, resizeY);
            editor.theme.panel.moveBy(0, -resizeY);
        }
    }

    editor.addButton('addToolbarButton', {
        image: 'wegas-editor/images/more.png',
        title: 'More options',
        onclick: showHideToolbar,
        onPostRender: showHideToolbar
    });
});
<|endoftext|>


<commit_before>import crosstab from 'crosstab';

import React from 'react';
import Router from 'react-router';

import Deactivated from './components/Deactivated.react.js';
import Login from './components/Login.react.js';
import Main from './components/Main.react';
import JoinGroup from './components/JoinGroup.react';

import LoginStore from './stores/LoginStore';
import LoginActionCreators from './actions/LoginActionCreators';

const DefaultRoute = Router.DefaultRoute;
const Route = Router.Route;
const RouteHandler = Router.RouteHandler;

const ActorInitEvent = 'concurrentActorInit';

if (crosstab.supported) {
  crosstab.on(ActorInitEvent, (msg) => {
    if (msg.origin !== crosstab.id && window.location.pathname !== '/deactivated') {
      window.location.assign('/deactivated');
    }
  });
}

window.jsAppLoaded = () => {
  if (crosstab.supported && window.location.pathname !== '/deactivated') {
    crosstab.broadcast(ActorInitEvent, {});
    window.messenger = new window.actor.ActorApp();
  }

  const App = React.createClass({
    render() {
      return <RouteHandler/>;
    }
  });

  const routes = (
    <Route handler={App} name="app" path="/">
      <Route handler={Main} name="main" path="/"/>
      <Route handler={JoinGroup} name="join" path="/join/:token"/>
      <Route handler={Login} name="login" path="/auth"/>
      <Route handler={Deactivated} name="deactivated" path="/deactivated"/>
      <DefaultRoute handler={Main}/>
    </Route>
  );

  const router = Router.run(routes, Router.HistoryLocation, function (Handler) {
    React.render(<Handler/>, document.getElementById('actor-web-app'));
  });

  if (LoginStore.isLoggedIn()) {
    LoginActionCreators.setLoggedIn(router, {redirect: false});
  }
};
<commit_msg>fix(web): fixed actor init if crosstab isn't supported<commit_after>import crosstab from 'crosstab';

import React from 'react';
import Router from 'react-router';

import Deactivated from './components/Deactivated.react.js';
import Login from './components/Login.react.js';
import Main from './components/Main.react';
import JoinGroup from './components/JoinGroup.react';

import LoginStore from './stores/LoginStore';
import LoginActionCreators from './actions/LoginActionCreators';

const DefaultRoute = Router.DefaultRoute;
const Route = Router.Route;
const RouteHandler = Router.RouteHandler;

const ActorInitEvent = 'concurrentActorInit';

if (crosstab.supported) {
  crosstab.on(ActorInitEvent, (msg) => {
    if (msg.origin !== crosstab.id && window.location.pathname !== '/deactivated') {
      window.location.assign('/deactivated');
    }
  });
}

window.jsAppLoaded = () => {
  if (window.location.pathname !== '/deactivated') {
    if (crosstab.supported) {
      crosstab.broadcast(ActorInitEvent, {});
    }

    window.messenger = new window.actor.ActorApp();
  }

  const App = React.createClass({
    render() {
      return <RouteHandler/>;
    }
  });

  const routes = (
    <Route handler={App} name="app" path="/">
      <Route handler={Main} name="main" path="/"/>
      <Route handler={JoinGroup} name="join" path="/join/:token"/>
      <Route handler={Login} name="login" path="/auth"/>
      <Route handler={Deactivated} name="deactivated" path="/deactivated"/>
      <DefaultRoute handler={Main}/>
    </Route>
  );

  const router = Router.run(routes, Router.HistoryLocation, function (Handler) {
    React.render(<Handler/>, document.getElementById('actor-web-app'));
  });

  if (LoginStore.isLoggedIn()) {
    LoginActionCreators.setLoggedIn(router, {redirect: false});
  }
};
<|endoftext|>


<commit_before>var pendingPermissions = []
var grantedPermissions = []
var nextPermissionId = 1

/*
All permission requests are given to the renderer on each change,
it will figure out what updates to make
*/
function sendPermissionsToRenderer () {
  // remove properties that can't be serialized over IPC
  sendIPCToWindow(mainWindow, 'updatePermissions', pendingPermissions.concat(grantedPermissions).map(p => {
    return {
      permissionId: p.permissionId,
      tabId: p.tabId,
      permission: p.permission,
      details: p.details,
      granted: p.granted
    }
  }))
}

function removePermissionsForContents (contents) {
  pendingPermissions = pendingPermissions.filter(perm => perm.contents !== contents)
  grantedPermissions = grantedPermissions.filter(perm => perm.contents !== contents)

  sendPermissionsToRenderer()
}

/*
Was permission already granted for this tab and URL?
*/
function isPermissionGrantedForContents (requestContents, requestPermission, requestDetails) {
  var requestOrigin = new URL(requestDetails.requestingUrl).hostname

  for (var i = 0; i < grantedPermissions.length; i++) {
    var grantedOrigin = new URL(grantedPermissions[i].details.requestingUrl).hostname

    if (requestContents === grantedPermissions[i].contents && requestOrigin === grantedOrigin) {
      if (requestPermission === 'notifications' && grantedPermissions[i].permission === 'notifications') {
        return true
      }

      if (requestPermission === 'media' && grantedPermissions[i].permission === 'media') {
        // type 1: from permissionCheckHandler
        // request has a single media type
        if (requestDetails.mediaType && grantedPermissions[i].details.mediaTypes.includes(requestDetails.mediaType)) {
          return true
        }
        // type 2: from a permissionRequestHandler
        // request has multiple media types
        // TODO existing granted permissions should be merged together (i.e. if there is an existing permission for audio, and another for video, a new request for audio+video should be approved, but it currently won't be)
        if (requestDetails.mediaTypes && requestDetails.mediaTypes.every(type => grantedPermissions[i].details.mediaTypes.includes(type))) {
          return true
        }
      }
    }
  }
  return false
}

/*
Is there already a pending request of the given type for this tab+url?
 */
function hasPendingRequestForContents (contents, permission, details) {
  var requestOrigin = new URL(details.requestingUrl).hostname

  for (var i = 0; i < pendingPermissions.length; i++) {
    var pendingOrigin = new URL(pendingPermissions[i].details.requestingUrl).hostname

    if (contents === pendingPermissions[i].contents && requestOrigin === pendingOrigin && permission === pendingPermissions[i].permission) {
      return true
    }
  }
  return false
}

function pagePermissionRequestHandler (webContents, permission, callback, details) {
  if (!details.isMainFrame) {
    // not supported for now to simplify the UI
    callback(false)
    return
  }

  if (!details.requestingUrl) {
    callback(false)
    return
  }

  if (permission === 'fullscreen' || permission === 'clipboard-sanitized-write') {
    callback(true)
    return
  }

  /*
  Geolocation requires a Google API key (https://www.electronjs.org/docs/api/environment-variables#google_api_key), so it is disabled.
  Other permissions aren't supported for now to simplify the UI
  */
  if (['media', 'notifications'].includes(permission)) {
    /*
    If permission was previously granted for this page, new requests should be allowed
    */
    if (isPermissionGrantedForContents(webContents, permission, details)) {
      callback(true)
    } else if (permission === 'notifications' && hasPendingRequestForContents(webContents, permission, details)) {
      /*
      Sites sometimes make a new request for each notification, which can generate multiple requests if the first one wasn't approved.
      TODO this isn't entirely correct (some requests will be rejected when they should be pending) - correct solution is to show a single button to approve all requests in the UI.
      */
      callback(false)
    } else {
      pendingPermissions.push({
        permissionId: nextPermissionId,
        tabId: getViewIDFromWebContents(webContents),
        contents: webContents,
        permission: permission,
        details: details,
        callback: callback
      })

      sendPermissionsToRenderer()

      nextPermissionId++
    }

    /*
    Once this view is closed or navigated to a new page, these permissions should be revoked
    */
    webContents.on('did-start-navigation', function (e, url, isInPlace, isMainFrame, frameProcessId, frameRoutingId) {
      if (isMainFrame && !isInPlace) {
        removePermissionsForContents(webContents)
      }
    })
    webContents.once('destroyed', function () {
      // check whether the app is shutting down to avoid an electron crash (TODO remove this)
      if (mainWindow) {
        removePermissionsForContents(webContents)
      }
    })
  } else {
    callback(false)
  }
}

function pagePermissionCheckHandler (webContents, permission, requestingOrigin, details) {
  if (!details.isMainFrame) {
    return false
  }
  // starting in Electron 13, this will sometimes be called with no URL. TODO figure out why
  if (!details.requestingUrl) {
    return false
  }

  if (permission === 'clipboard-sanitized-write') {
    return true
  }

  return isPermissionGrantedForContents(webContents, permission, details)
}

app.once('ready', function () {
  session.defaultSession.setPermissionRequestHandler(pagePermissionRequestHandler)
  session.defaultSession.setPermissionCheckHandler(pagePermissionCheckHandler)
})

app.on('session-created', function (session) {
  session.setPermissionRequestHandler(pagePermissionRequestHandler)
  session.setPermissionCheckHandler(pagePermissionCheckHandler)
})

ipc.on('permissionGranted', function (e, permissionId) {
  for (var i = 0; i < pendingPermissions.length; i++) {
    if (permissionId && pendingPermissions[i].permissionId === permissionId) {
      pendingPermissions[i].granted = true
      pendingPermissions[i].callback(true)
      grantedPermissions.push(pendingPermissions[i])
      pendingPermissions.splice(i, 1)

      sendPermissionsToRenderer()
      break
    }
  }
})
<commit_msg>track permission requests by origin instead of contents<commit_after>var pendingPermissions = []
var grantedPermissions = []
var nextPermissionId = 1

/*
All permission requests are given to the renderer on each change,
it will figure out what updates to make
*/
function sendPermissionsToRenderer () {
  // remove properties that can't be serialized over IPC
  sendIPCToWindow(mainWindow, 'updatePermissions', pendingPermissions.concat(grantedPermissions).map(p => {
    return {
      permissionId: p.permissionId,
      tabId: p.tabId,
      origin: p.origin,
      permission: p.permission,
      details: p.details,
      granted: p.granted
    }
  }))
}

function removePermissionsForContents (contents) {
  pendingPermissions = pendingPermissions.filter(perm => perm.contents !== contents)
  grantedPermissions = grantedPermissions.filter(perm => perm.contents !== contents)

  sendPermissionsToRenderer()
}

/*
Was permission already granted for this origin?
*/
function isPermissionGrantedForOrigin (requestOrigin, requestPermission, requestDetails) {
  for (var i = 0; i < grantedPermissions.length; i++) {
    if (requestOrigin === grantedPermissions[i].origin) {
      if (requestPermission === 'notifications' && grantedPermissions[i].permission === 'notifications') {
        return true
      }

      if (requestPermission === 'media' && grantedPermissions[i].permission === 'media') {
        // type 1: from permissionCheckHandler
        // request has a single media type
        if (requestDetails.mediaType && grantedPermissions[i].details.mediaTypes.includes(requestDetails.mediaType)) {
          return true
        }
        // type 2: from a permissionRequestHandler
        // request has multiple media types
        // TODO existing granted permissions should be merged together (i.e. if there is an existing permission for audio, and another for video, a new request for audio+video should be approved, but it currently won't be)
        if (requestDetails.mediaTypes && requestDetails.mediaTypes.every(type => grantedPermissions[i].details.mediaTypes.includes(type))) {
          return true
        }
      }
    }
  }
  return false
}

/*
Is there already a pending request of the given type for this origin?
 */
function hasPendingRequestForOrigin (requestOrigin, permission, details) {
  for (var i = 0; i < pendingPermissions.length; i++) {
    if (requestOrigin === pendingPermissions[i].origin && permission === pendingPermissions[i].permission) {
      return true
    }
  }
  return false
}

function pagePermissionRequestHandler (webContents, permission, callback, details) {
  if (!details.isMainFrame) {
    // not supported for now to simplify the UI
    callback(false)
    return
  }

  if (!details.requestingUrl) {
    callback(false)
    return
  }

  if (permission === 'fullscreen' || permission === 'clipboard-sanitized-write') {
    callback(true)
    return
  }

  const requestOrigin = new URL(details.requestingUrl).hostname

  /*
  Geolocation requires a Google API key (https://www.electronjs.org/docs/api/environment-variables#google_api_key), so it is disabled.
  Other permissions aren't supported for now to simplify the UI
  */
  if (['media', 'notifications'].includes(permission)) {
    /*
    If permission was previously granted for this origin in a different tab, new requests should be allowed
    */
    if (isPermissionGrantedForOrigin(requestOrigin, permission, details)) {
      callback(true)

      if (!grantedPermissions.some(grant => grant.contents === webContents && grant.permission === permission)) {
        grantedPermissions.push({
          permissionId: nextPermissionId,
          tabId: getViewIDFromWebContents(webContents),
          contents: webContents,
          origin: requestOrigin,
          permission: permission,
          details: details,
          granted: true
        })

        sendPermissionsToRenderer()
        nextPermissionId++
      }
    } else if (permission === 'notifications' && hasPendingRequestForOrigin(requestOrigin, permission, details)) {
      /*
      Sites sometimes make a new request for each notification, which can generate multiple requests if the first one wasn't approved.
      TODO this isn't entirely correct (some requests will be rejected when they should be pending) - correct solution is to show a single button to approve all requests in the UI.
      */
      callback(false)
    } else {
      pendingPermissions.push({
        permissionId: nextPermissionId,
        tabId: getViewIDFromWebContents(webContents),
        contents: webContents,
        origin: requestOrigin,
        permission: permission,
        details: details,
        callback: callback
      })

      sendPermissionsToRenderer()
      nextPermissionId++
    }

    /*
    Once this view is closed or navigated to a new page, these permissions should be revoked
    */
    webContents.on('did-start-navigation', function (e, url, isInPlace, isMainFrame, frameProcessId, frameRoutingId) {
      if (isMainFrame && !isInPlace) {
        removePermissionsForContents(webContents)
      }
    })
    webContents.once('destroyed', function () {
      // check whether the app is shutting down to avoid an electron crash (TODO remove this)
      if (mainWindow) {
        removePermissionsForContents(webContents)
      }
    })
  } else {
    callback(false)
  }
}

function pagePermissionCheckHandler (webContents, permission, requestingOrigin, details) {
  if (!details.isMainFrame && requestingOrigin !== details.embeddingOrigin) {
    return false
  }

  if (permission === 'clipboard-sanitized-write') {
    return true
  }

  return isPermissionGrantedForOrigin(new URL(requestingOrigin).hostname, permission, details)
}

app.once('ready', function () {
  session.defaultSession.setPermissionRequestHandler(pagePermissionRequestHandler)
  session.defaultSession.setPermissionCheckHandler(pagePermissionCheckHandler)
})

app.on('session-created', function (session) {
  session.setPermissionRequestHandler(pagePermissionRequestHandler)
  session.setPermissionCheckHandler(pagePermissionCheckHandler)
})

ipc.on('permissionGranted', function (e, permissionId) {
  for (var i = 0; i < pendingPermissions.length; i++) {
    if (permissionId && pendingPermissions[i].permissionId === permissionId) {
      pendingPermissions[i].granted = true
      pendingPermissions[i].callback(true)
      grantedPermissions.push(pendingPermissions[i])
      pendingPermissions.splice(i, 1)

      sendPermissionsToRenderer()
      break
    }
  }
})
<|endoftext|>


<commit_before>/* global ListContainer */
var LabNotebook = function($selector){
	this.$list = $selector;
	this.$list.addClass("hidden"); // TODO: find a way to inherit while executing the constructor
	this.init();
};

LabNotebook.prototype = Object.create(ListContainer.prototype);

LabNotebook.prototype.init = function(){
	$('.create-note').click(this.addNoteField.bind(this));
	$('body').click(function(e) {
		if (!$(e.target).is('.create-note, input, .note-creator')) {
			this.removeInput();
		}
	}.bind(this));
};

LabNotebook.prototype.addNoteField = function(e){
	var field = $("
<input class='form-control' type='text' name='newNote' placeholder='New Note'><button id='add' class='btn btn-default'>Add</button><button id='cancel' class='btn btn-default'>Cancel</button>
");
	$('.labenotebook').append(field);

	$('.note-creator #add').click(function(){
		var text = field.find('input').val();
		this.add(text);
		this.removeInput();
	}.bind(this));

	$('.note-creator #cancel').click(function(e){
		this.removeInput();
	}.bind(this));
};

LabNotebook.prototype.removeInput = function(){
	this.$list.find('input').parent().remove();
};

LabNotebook.prototype.addButtonToLast = function(chainNum) {
       this.$list.children().last().append("<button id='"+chainNum+"' class='btn btn-default btn-return' >Change my choice</button>");
}<commit_msg>made adding notebook entry scroll to the bottom when clicked<commit_after>/* global ListContainer */
var LabNotebook = function($selector){
	this.$list = $selector;
	this.$list.addClass("hidden"); // TODO: find a way to inherit while executing the constructor
	this.init();
};

LabNotebook.prototype = Object.create(ListContainer.prototype);

LabNotebook.prototype.init = function(){
	$('.create-note').click(this.addNoteField.bind(this));
	$('body').click(function(e) {
		if (!$(e.target).is('.create-note, input, .note-creator')) {
			this.removeInput();
		}
	}.bind(this));
};

LabNotebook.prototype.addNoteField = function(e){
	var field = $("
<input class='form-control' type='text' name='newNote' placeholder='New Note'><button id='add' class='btn btn-default'>Add</button><button id='cancel' class='btn btn-default'>Cancel</button>
");
	$('.labenotebook').append(field);
	$('.labenotebook').scrollTop($('.labenotebook')[0].scrollHeight);
	$('.note-creator #add').click(function(){
		var text = field.find('input').val();
		this.add(text);
		this.removeInput();
	}.bind(this));

	$('.note-creator #cancel').click(function(e){
		this.removeInput();
	}.bind(this));
};

LabNotebook.prototype.removeInput = function(){
	this.$list.find('input').parent().remove();
};

LabNotebook.prototype.addButtonToLast = function(chainNum) {
       this.$list.children().last().append("<button id='"+chainNum+"' class='btn btn-default btn-return' >Change my choice</button>");
}<|endoftext|>


<commit_before>import Random from 'random-seed'

import Iterator from './iterator'
import {addToSet} from './helpers'

const MAX_PRIORITY = 2147483647 // max 32 bit signed int (unboxed in v8)

export default class MarkerIndex {
  constructor (seed) {
    this.random = new Random(seed)
    this.root = null
    this.startNodesById = {}
    this.endNodesById = {}
    this.iterator = new Iterator(this)
    this.exclusiveMarkers = new Set()
  }

  dump (filterSet) {
    return this.iterator.dump(filterSet)
  }

  getRange (markerId) {
    return [this.getStart(markerId), this.getEnd(markerId)]
  }

  getStart (markerId) {
    return this.getNodeOffset(this.startNodesById[markerId])
  }

  getEnd (markerId) {
    return this.getNodeOffset(this.endNodesById[markerId])
  }

  insert (markerId, startOffset, endOffset) {
    let startNode = this.iterator.insertMarkerStart(markerId, startOffset, endOffset)
    let endNode = this.iterator.insertMarkerEnd(markerId, startOffset, endOffset)

    startNode.startMarkerIds.add(markerId)
    endNode.endMarkerIds.add(markerId)

    startNode.priority = this.random(MAX_PRIORITY)
    this.bubbleNodeUp(startNode)

    endNode.priority = this.random(MAX_PRIORITY)
    this.bubbleNodeUp(endNode)

    this.startNodesById[markerId] = startNode
    this.endNodesById[markerId] = endNode
  }

  setExclusive (markerId, exclusive) {
    if (exclusive) {
      this.exclusiveMarkers.add(markerId)
    } else {
      this.exclusiveMarkers.remove(markerId)
    }
  }

  isExclusive (markerId) {
    return this.exclusiveMarkers.has(markerId)
  }

  delete (markerId) {
    let startNode = this.startNodesById[markerId]
    let endNode = this.endNodesById[markerId]

    let node = startNode
    while (node) {
      node.rightMarkerIds.delete(markerId)
      node = node.parent
    }

    node = endNode
    while (node) {
      node.leftMarkerIds.delete(markerId)
      node = node.parent
    }

    startNode.startMarkerIds.delete(markerId)
    endNode.endMarkerIds.delete(markerId)

    if (!startNode.isMarkerEndpoint()) {
      this.deleteNode(startNode)
    }

    if (endNode !== startNode && !endNode.isMarkerEndpoint()) {
      this.deleteNode(endNode)
    }

    delete this.startNodesById[markerId]
    delete this.endNodesById[markerId]
  }

  splice (start, oldExtent, newExtent) {
    if (!this.root || oldExtent === 0 && newExtent === 0) return

    let isInsertion = oldExtent === 0
    let startNode = this.iterator.insertSpliceBoundary(start, false)
    let endNode = this.iterator.insertSpliceBoundary(start + oldExtent, isInsertion)

    startNode.priority = -2
    this.bubbleNodeUp(startNode)
    endNode.priority = -1
    this.bubbleNodeUp(endNode)

    if (isInsertion) {
      for (let markerId of startNode.startMarkerIds) {
        if (this.exclusiveMarkers.has(markerId)) {
          startNode.startMarkerIds.delete(markerId)
          endNode.startMarkerIds.add(markerId)
          endNode.leftMarkerIds.delete(markerId)
          this.startNodesById[markerId] = endNode
        }
      }
      for (let markerId of startNode.endMarkerIds) {
        if (!this.exclusiveMarkers.has(markerId) || endNode.startMarkerIds.has(markerId)) {
          startNode.endMarkerIds.delete(markerId)
          endNode.endMarkerIds.add(markerId)
          if (!endNode.startMarkerIds.has(markerId)) {
            endNode.leftMarkerIds.add(markerId)
          }
          this.endNodesById[markerId] = endNode
        }
      }
    } else if (endNode.left) {
      let startMarkerIds = new Set()
      let endMarkerIds = new Set()
      this.getStartingAndEndingsMarkerWithinSubtree(endNode.left, startMarkerIds, endMarkerIds)

      for (let markerId of startMarkerIds) {
        endNode.startMarkerIds.add(markerId)
        this.startNodesById[markerId] = endNode
      }

      for (let markerId of endMarkerIds) {
        endNode.endMarkerIds.add(markerId)
        if (!startMarkerIds.has(markerId)) {
          endNode.leftMarkerIds.add(markerId)
        }
        this.endNodesById[markerId] = endNode
      }

      endNode.left = null
    }

    endNode.leftExtent += (newExtent - oldExtent)

    if (endNode.leftExtent === 0) {
      for (let markerId of endNode.startMarkerIds) {
        startNode.startMarkerIds.add(markerId)
        endNode.leftMarkerIds.add(markerId)
        this.startNodesById[markerId] = startNode
      }
      for (let markerId of endNode.endMarkerIds) {
        startNode.endMarkerIds.add(markerId)
        endNode.leftMarkerIds.delete(markerId)
        this.endNodesById[markerId] = startNode
      }
      this.deleteNode(endNode)
    } else if (endNode.isMarkerEndpoint()) {
      endNode.priority = this.random(MAX_PRIORITY)
      this.bubbleNodeDown(endNode)
    } else {
      this.deleteNode(endNode)
    }

    if (startNode.isMarkerEndpoint()) {
      startNode.priority = this.random(MAX_PRIORITY)
      this.bubbleNodeDown(startNode)
    } else {
      this.deleteNode(startNode)
    }
  }

  findIntersecting (start, end = start) {
    let intersecting = new Set()
    this.iterator.findIntersecting(start, end, intersecting)
    return intersecting
  }

  findContaining (start, end = start) {
    let containing = new Set()
    this.iterator.findContaining(start, containing)
    if (end !== start) {
      let containingEnd = new Set()
      this.iterator.findContaining(end, containingEnd)
      for (let markerId of containing) {
        if (!containingEnd.has(markerId)) containing.delete(markerId)
      }
    }
    return containing
  }

  findContainedIn (start, end) {
    let containedIn = new Set()
    this.iterator.findContainedIn(start, end, containedIn)
    return containedIn
  }

  findStartingIn (start, end) {
    let startingIn = new Set()
    this.iterator.findStartingIn(start, end, startingIn)
    return startingIn
  }

  findEndingIn (start, end) {
    let endingIn = new Set()
    this.iterator.findEndingIn(start, end, endingIn)
    return endingIn
  }

  findStartingAt (offset) {
    return this.findStartingIn(offset, offset)
  }

  findEndingAt (offset) {
    return this.findEndingIn(offset, offset)
  }

  getNodeOffset (node) {
    let offset = node.leftExtent
    while (node.parent) {
      if (node.parent.right === node) {
        offset += node.parent.leftExtent
      }
      node = node.parent
    }
    return offset
  }

  deleteNode (node) {
    node.priority = Infinity
    this.bubbleNodeDown(node)
    if (node.parent) {
      if (node.parent.left === node) {
        node.parent.left = null
      } else {
        node.parent.right = null
      }
    } else {
      this.root = null
    }
  }

  bubbleNodeUp (node) {
    while (node.parent && node.priority < node.parent.priority) {
      if (node === node.parent.left) {
        this.rotateNodeRight(node)
      } else {
        this.rotateNodeLeft(node)
      }
    }
  }

  bubbleNodeDown (node) {
    while (true) {
      let leftChildPriority = node.left ? node.left.priority : Infinity
      let rightChildPriority = node.right ? node.right.priority : Infinity

      if (leftChildPriority < rightChildPriority && leftChildPriority < node.priority) {
        this.rotateNodeRight(node.left)
      } else if (rightChildPriority < node.priority) {
        this.rotateNodeLeft(node.right)
      } else {
        break
      }
    }
  }

  rotateNodeLeft (pivot) {
    let root = pivot.parent

    if (root.parent) {
      if (root.parent.left === root) {
        root.parent.left = pivot
      } else {
        root.parent.right = pivot
      }
    } else {
      this.root = pivot
    }
    pivot.parent = root.parent

    root.right = pivot.left
    if (root.right) {
      root.right.parent = root
    }

    pivot.left = root
    pivot.left.parent = pivot

    pivot.leftExtent = root.leftExtent + pivot.leftExtent

    addToSet(pivot.rightMarkerIds, root.rightMarkerIds)

    for (let markerId of pivot.leftMarkerIds) {
      if (root.leftMarkerIds.has(markerId)) {
        root.leftMarkerIds.delete(markerId)
      } else {
        pivot.leftMarkerIds.delete(markerId)
        root.rightMarkerIds.add(markerId)
      }
    }
  }

  rotateNodeRight (pivot) {
    let root = pivot.parent

    if (root.parent) {
      if (root.parent.left === root) {
        root.parent.left = pivot
      } else {
        root.parent.right = pivot
      }
    } else {
      this.root = pivot
    }
    pivot.parent = root.parent

    root.left = pivot.right
    if (root.left) {
      root.left.parent = root
    }

    pivot.right = root
    pivot.right.parent = pivot

    root.leftExtent = root.leftExtent - pivot.leftExtent

    for (let markerId of root.leftMarkerIds) {
      if (!pivot.startMarkerIds.has(markerId)) { // don't do this when pivot is at offset 0
        pivot.leftMarkerIds.add(markerId)
      }
    }

    for (let markerId of pivot.rightMarkerIds) {
      if (root.rightMarkerIds.has(markerId)) {
        root.rightMarkerIds.delete(markerId)
      } else {
        pivot.rightMarkerIds.delete(markerId)
        root.leftMarkerIds.add(markerId)
      }
    }
  }

  getStartingAndEndingsMarkerWithinSubtree (node, startMarkerIds, endMarkerIds) {
    addToSet(startMarkerIds, node.startMarkerIds)
    addToSet(endMarkerIds, node.endMarkerIds)
    if (node.left) {
      this.getStartingAndEndingsMarkerWithinSubtree(node.left, startMarkerIds, endMarkerIds)
    }
    if (node.right) {
      this.getStartingAndEndingsMarkerWithinSubtree(node.right, startMarkerIds, endMarkerIds)
    }
  }
}
<commit_msg>Call correct delete method on Set<commit_after>import Random from 'random-seed'

import Iterator from './iterator'
import {addToSet} from './helpers'

const MAX_PRIORITY = 2147483647 // max 32 bit signed int (unboxed in v8)

export default class MarkerIndex {
  constructor (seed) {
    this.random = new Random(seed)
    this.root = null
    this.startNodesById = {}
    this.endNodesById = {}
    this.iterator = new Iterator(this)
    this.exclusiveMarkers = new Set()
  }

  dump (filterSet) {
    return this.iterator.dump(filterSet)
  }

  getRange (markerId) {
    return [this.getStart(markerId), this.getEnd(markerId)]
  }

  getStart (markerId) {
    return this.getNodeOffset(this.startNodesById[markerId])
  }

  getEnd (markerId) {
    return this.getNodeOffset(this.endNodesById[markerId])
  }

  insert (markerId, startOffset, endOffset) {
    let startNode = this.iterator.insertMarkerStart(markerId, startOffset, endOffset)
    let endNode = this.iterator.insertMarkerEnd(markerId, startOffset, endOffset)

    startNode.startMarkerIds.add(markerId)
    endNode.endMarkerIds.add(markerId)

    startNode.priority = this.random(MAX_PRIORITY)
    this.bubbleNodeUp(startNode)

    endNode.priority = this.random(MAX_PRIORITY)
    this.bubbleNodeUp(endNode)

    this.startNodesById[markerId] = startNode
    this.endNodesById[markerId] = endNode
  }

  setExclusive (markerId, exclusive) {
    if (exclusive) {
      this.exclusiveMarkers.add(markerId)
    } else {
      this.exclusiveMarkers.delete(markerId)
    }
  }

  isExclusive (markerId) {
    return this.exclusiveMarkers.has(markerId)
  }

  delete (markerId) {
    let startNode = this.startNodesById[markerId]
    let endNode = this.endNodesById[markerId]

    let node = startNode
    while (node) {
      node.rightMarkerIds.delete(markerId)
      node = node.parent
    }

    node = endNode
    while (node) {
      node.leftMarkerIds.delete(markerId)
      node = node.parent
    }

    startNode.startMarkerIds.delete(markerId)
    endNode.endMarkerIds.delete(markerId)

    if (!startNode.isMarkerEndpoint()) {
      this.deleteNode(startNode)
    }

    if (endNode !== startNode && !endNode.isMarkerEndpoint()) {
      this.deleteNode(endNode)
    }

    delete this.startNodesById[markerId]
    delete this.endNodesById[markerId]
  }

  splice (start, oldExtent, newExtent) {
    if (!this.root || oldExtent === 0 && newExtent === 0) return

    let isInsertion = oldExtent === 0
    let startNode = this.iterator.insertSpliceBoundary(start, false)
    let endNode = this.iterator.insertSpliceBoundary(start + oldExtent, isInsertion)

    startNode.priority = -2
    this.bubbleNodeUp(startNode)
    endNode.priority = -1
    this.bubbleNodeUp(endNode)

    if (isInsertion) {
      for (let markerId of startNode.startMarkerIds) {
        if (this.exclusiveMarkers.has(markerId)) {
          startNode.startMarkerIds.delete(markerId)
          endNode.startMarkerIds.add(markerId)
          endNode.leftMarkerIds.delete(markerId)
          this.startNodesById[markerId] = endNode
        }
      }
      for (let markerId of startNode.endMarkerIds) {
        if (!this.exclusiveMarkers.has(markerId) || endNode.startMarkerIds.has(markerId)) {
          startNode.endMarkerIds.delete(markerId)
          endNode.endMarkerIds.add(markerId)
          if (!endNode.startMarkerIds.has(markerId)) {
            endNode.leftMarkerIds.add(markerId)
          }
          this.endNodesById[markerId] = endNode
        }
      }
    } else if (endNode.left) {
      let startMarkerIds = new Set()
      let endMarkerIds = new Set()
      this.getStartingAndEndingsMarkerWithinSubtree(endNode.left, startMarkerIds, endMarkerIds)

      for (let markerId of startMarkerIds) {
        endNode.startMarkerIds.add(markerId)
        this.startNodesById[markerId] = endNode
      }

      for (let markerId of endMarkerIds) {
        endNode.endMarkerIds.add(markerId)
        if (!startMarkerIds.has(markerId)) {
          endNode.leftMarkerIds.add(markerId)
        }
        this.endNodesById[markerId] = endNode
      }

      endNode.left = null
    }

    endNode.leftExtent += (newExtent - oldExtent)

    if (endNode.leftExtent === 0) {
      for (let markerId of endNode.startMarkerIds) {
        startNode.startMarkerIds.add(markerId)
        endNode.leftMarkerIds.add(markerId)
        this.startNodesById[markerId] = startNode
      }
      for (let markerId of endNode.endMarkerIds) {
        startNode.endMarkerIds.add(markerId)
        endNode.leftMarkerIds.delete(markerId)
        this.endNodesById[markerId] = startNode
      }
      this.deleteNode(endNode)
    } else if (endNode.isMarkerEndpoint()) {
      endNode.priority = this.random(MAX_PRIORITY)
      this.bubbleNodeDown(endNode)
    } else {
      this.deleteNode(endNode)
    }

    if (startNode.isMarkerEndpoint()) {
      startNode.priority = this.random(MAX_PRIORITY)
      this.bubbleNodeDown(startNode)
    } else {
      this.deleteNode(startNode)
    }
  }

  findIntersecting (start, end = start) {
    let intersecting = new Set()
    this.iterator.findIntersecting(start, end, intersecting)
    return intersecting
  }

  findContaining (start, end = start) {
    let containing = new Set()
    this.iterator.findContaining(start, containing)
    if (end !== start) {
      let containingEnd = new Set()
      this.iterator.findContaining(end, containingEnd)
      for (let markerId of containing) {
        if (!containingEnd.has(markerId)) containing.delete(markerId)
      }
    }
    return containing
  }

  findContainedIn (start, end) {
    let containedIn = new Set()
    this.iterator.findContainedIn(start, end, containedIn)
    return containedIn
  }

  findStartingIn (start, end) {
    let startingIn = new Set()
    this.iterator.findStartingIn(start, end, startingIn)
    return startingIn
  }

  findEndingIn (start, end) {
    let endingIn = new Set()
    this.iterator.findEndingIn(start, end, endingIn)
    return endingIn
  }

  findStartingAt (offset) {
    return this.findStartingIn(offset, offset)
  }

  findEndingAt (offset) {
    return this.findEndingIn(offset, offset)
  }

  getNodeOffset (node) {
    let offset = node.leftExtent
    while (node.parent) {
      if (node.parent.right === node) {
        offset += node.parent.leftExtent
      }
      node = node.parent
    }
    return offset
  }

  deleteNode (node) {
    node.priority = Infinity
    this.bubbleNodeDown(node)
    if (node.parent) {
      if (node.parent.left === node) {
        node.parent.left = null
      } else {
        node.parent.right = null
      }
    } else {
      this.root = null
    }
  }

  bubbleNodeUp (node) {
    while (node.parent && node.priority < node.parent.priority) {
      if (node === node.parent.left) {
        this.rotateNodeRight(node)
      } else {
        this.rotateNodeLeft(node)
      }
    }
  }

  bubbleNodeDown (node) {
    while (true) {
      let leftChildPriority = node.left ? node.left.priority : Infinity
      let rightChildPriority = node.right ? node.right.priority : Infinity

      if (leftChildPriority < rightChildPriority && leftChildPriority < node.priority) {
        this.rotateNodeRight(node.left)
      } else if (rightChildPriority < node.priority) {
        this.rotateNodeLeft(node.right)
      } else {
        break
      }
    }
  }

  rotateNodeLeft (pivot) {
    let root = pivot.parent

    if (root.parent) {
      if (root.parent.left === root) {
        root.parent.left = pivot
      } else {
        root.parent.right = pivot
      }
    } else {
      this.root = pivot
    }
    pivot.parent = root.parent

    root.right = pivot.left
    if (root.right) {
      root.right.parent = root
    }

    pivot.left = root
    pivot.left.parent = pivot

    pivot.leftExtent = root.leftExtent + pivot.leftExtent

    addToSet(pivot.rightMarkerIds, root.rightMarkerIds)

    for (let markerId of pivot.leftMarkerIds) {
      if (root.leftMarkerIds.has(markerId)) {
        root.leftMarkerIds.delete(markerId)
      } else {
        pivot.leftMarkerIds.delete(markerId)
        root.rightMarkerIds.add(markerId)
      }
    }
  }

  rotateNodeRight (pivot) {
    let root = pivot.parent

    if (root.parent) {
      if (root.parent.left === root) {
        root.parent.left = pivot
      } else {
        root.parent.right = pivot
      }
    } else {
      this.root = pivot
    }
    pivot.parent = root.parent

    root.left = pivot.right
    if (root.left) {
      root.left.parent = root
    }

    pivot.right = root
    pivot.right.parent = pivot

    root.leftExtent = root.leftExtent - pivot.leftExtent

    for (let markerId of root.leftMarkerIds) {
      if (!pivot.startMarkerIds.has(markerId)) { // don't do this when pivot is at offset 0
        pivot.leftMarkerIds.add(markerId)
      }
    }

    for (let markerId of pivot.rightMarkerIds) {
      if (root.rightMarkerIds.has(markerId)) {
        root.rightMarkerIds.delete(markerId)
      } else {
        pivot.rightMarkerIds.delete(markerId)
        root.leftMarkerIds.add(markerId)
      }
    }
  }

  getStartingAndEndingsMarkerWithinSubtree (node, startMarkerIds, endMarkerIds) {
    addToSet(startMarkerIds, node.startMarkerIds)
    addToSet(endMarkerIds, node.endMarkerIds)
    if (node.left) {
      this.getStartingAndEndingsMarkerWithinSubtree(node.left, startMarkerIds, endMarkerIds)
    }
    if (node.right) {
      this.getStartingAndEndingsMarkerWithinSubtree(node.right, startMarkerIds, endMarkerIds)
    }
  }
}
<|endoftext|>


<commit_before>/*global define*/
define([
        '../Core/DeveloperError',
        '../Core/destroyObject',
        '../Core/Color',
        '../Core/Cartesian2',
        '../Core/Cartesian3',
        '../Scene/BillboardCollection',
        '../Scene/HorizontalOrigin',
        '../Scene/VerticalOrigin',
        '../Renderer/TextureAtlasBuilder'
    ], function(
        DeveloperError,
        destroyObject,
        Color,
        Cartesian2,
        Cartesian3,
        BillboardCollection,
        HorizontalOrigin,
        VerticalOrigin,
        TextureAtlasBuilder) {
    "use strict";

    //Callback to create a callback so that we close over all of the proper values.
    function textureReady(dynamicObject, billboardCollection, textureValue) {
        return function(imageIndex) {
            //By the time the texture was loaded, the billboard might already be
            //gone or have been assigned a different texture.  Look it up again
            //and check.
            var currentIndex = dynamicObject._billboardVisualizerIndex;
            if (typeof currentIndex !== 'undefined') {
                var cbBillboard = billboardCollection.get(currentIndex);
                if (cbBillboard._visualizerUrl === textureValue) {
                    cbBillboard._visualizerTextureAvailable = true;
                    cbBillboard.setImageIndex(imageIndex);
                }
            }
        };
    }

    /**
     * A DynamicObject visualizer which maps the DynamicBillboard instance
     * in DynamicObject.billboard to a Billboard primitive.
     * @alias DynamicBillboardVisualizer
     * @constructor
     *
     * @param {Scene} scene The scene the primitives will be rendered in.
     * @param {DynamicObjectCollection} [dynamicObjectCollection] The dynamicObjectCollection to visualize.
     *
     * @exception {DeveloperError} scene is required.
     *
     * @see DynamicBillboard
     * @see Scene
     * @see DynamicObject
     * @see DynamicObjectCollection
     * @see CompositeDynamicObjectCollection
     * @see VisualizerCollection
     * @see DynamicConeVisualizer
     * @see DynamicConeVisualizerUsingCustomSensor
     * @see DynamicLabelVisualizer
     * @see DynamicPointVisualizer
     * @see DynamicPolygonVisualizer
     * @see DynamicPolylineVisualizer
     * @see DynamicPyramidVisualizer
     *
     */
    var DynamicBillboardVisualizer = function(scene, dynamicObjectCollection) {
        if (typeof scene === 'undefined') {
            throw new DeveloperError('scene is required.');
        }

        this._scene = scene;
        this._unusedIndexes = [];
        this._dynamicObjectCollection = undefined;

        var billboardCollection = this._billboardCollection = new BillboardCollection();
        var atlas = this._textureAtlas = scene.getContext().createTextureAtlas();
        this._textureAtlasBuilder = new TextureAtlasBuilder(atlas);
        billboardCollection.setTextureAtlas(atlas);
        scene.getPrimitives().add(billboardCollection);
        this.setDynamicObjectCollection(dynamicObjectCollection);
    };

    /**
     * Returns the scene being used by this visualizer.
     *
     * @returns {Scene} The scene being used by this visualizer.
     */
    DynamicBillboardVisualizer.prototype.getScene = function() {
        return this._scene;
    };

    /**
     * Gets the DynamicObjectCollection being visualized.
     *
     * @returns {DynamicObjectCollection} The DynamicObjectCollection being visualized.
     */
    DynamicBillboardVisualizer.prototype.getDynamicObjectCollection = function() {
        return this._dynamicObjectCollection;
    };

    /**
     * Sets the DynamicObjectCollection to visualize.
     *
     * @param dynamicObjectCollection The DynamicObjectCollection to visualizer.
     */
    DynamicBillboardVisualizer.prototype.setDynamicObjectCollection = function(dynamicObjectCollection) {
        var oldCollection = this._dynamicObjectCollection;
        if (oldCollection !== dynamicObjectCollection) {
            if (typeof oldCollection !== 'undefined') {
                oldCollection.objectsRemoved.removeEventListener(DynamicBillboardVisualizer.prototype._onObjectsRemoved);
                this.removeAllPrimitives();
            }
            this._dynamicObjectCollection = dynamicObjectCollection;
            if (typeof dynamicObjectCollection !== 'undefined') {
                dynamicObjectCollection.objectsRemoved.addEventListener(DynamicBillboardVisualizer.prototype._onObjectsRemoved, this);
            }
        }
    };

    /**
     * Updates all of the primitives created by this visualizer to match their
     * DynamicObject counterpart at the given time.
     *
     * @param {JulianDate} time The time to update to.
     *
     * @exception {DeveloperError} time is required.
     */
    DynamicBillboardVisualizer.prototype.update = function(time) {
        if (typeof time === 'undefined') {
            throw new DeveloperError('time is requied.');
        }
        if (typeof this._dynamicObjectCollection !== 'undefined') {
            var dynamicObjects = this._dynamicObjectCollection.getObjects();
            for ( var i = 0, len = dynamicObjects.length; i < len; i++) {
                this._updateObject(time, dynamicObjects[i]);
            }
        }
    };

    /**
     * Removes all primitives from the scene.
     */
    DynamicBillboardVisualizer.prototype.removeAllPrimitives = function() {
        if (typeof this._dynamicObjectCollection !== 'undefined') {
            this._unusedIndexes = [];
            this._billboardCollection.removeAll();
            var dynamicObjects = this._dynamicObjectCollection.getObjects();
            for ( var i = dynamicObjects.length - 1; i > -1; i--) {
                dynamicObjects[i]._billboardVisualizerIndex = undefined;
            }
        }
    };

    /**
     * Returns true if this object was destroyed; otherwise, false.
     *

     * If this object was destroyed, it should not be used; calling any function other than
     * isDestroyed will result in a {@link DeveloperError} exception.
     *
     * @memberof DynamicBillboardVisualizer
     *
     * @return {Boolean} True if this object was destroyed; otherwise, false.
     *
     * @see DynamicBillboardVisualizer#destroy
     */
    DynamicBillboardVisualizer.prototype.isDestroyed = function() {
        return false;
    };

    /**
     * Destroys the WebGL resources held by this object.  Destroying an object allows for deterministic
     * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
     *

     * Once an object is destroyed, it should not be used; calling any function other than
     * isDestroyed will result in a {@link DeveloperError} exception.  Therefore,
     * assign the return value (undefined) to the object as done in the example.
     *
     * @memberof DynamicBillboardVisualizer
     *
     * @return {undefined}
     *
     * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
     *
     * @see DynamicBillboardVisualizer#isDestroyed
     *
     * @example
     * visualizer = visualizer && visualizer.destroy();
     */
    DynamicBillboardVisualizer.prototype.destroy = function() {
        this.removeAllPrimitives();
        this._scene.getPrimitives().remove(this._billboardCollection);
        return destroyObject(this);
    };

    var position;
    var color;
    var eyeOffset;
    var pixelOffset;
    DynamicBillboardVisualizer.prototype._updateObject = function(time, dynamicObject) {
        var dynamicBillboard = dynamicObject.billboard;
        if (typeof dynamicBillboard === 'undefined') {
            return;
        }

        var positionProperty = dynamicObject.position;
        if (typeof positionProperty === 'undefined') {
            return;
        }

        var textureProperty = dynamicBillboard.image;
        if (typeof textureProperty === 'undefined') {
            return;
        }

        var billboard;
        var showProperty = dynamicBillboard.show;
        var billboardVisualizerIndex = dynamicObject._billboardVisualizerIndex;
        var show = dynamicObject.isAvailable(time) && (typeof showProperty === 'undefined' || showProperty.getValue(time));

        if (!show) {
            //don't bother creating or updating anything else
            if (typeof billboardVisualizerIndex !== 'undefined') {
                billboard = this._billboardCollection.get(billboardVisualizerIndex);
                billboard.setShow(false);
                billboard.setImageIndex(-1);
                billboard._visualizerUrl = undefined;
                billboard._visualizerTextureAvailable = false;
                dynamicObject._billboardVisualizerIndex = undefined;
                this._unusedIndexes.push(billboardVisualizerIndex);
            }
            return;
        }

        if (typeof billboardVisualizerIndex === 'undefined') {
            var unusedIndexes = this._unusedIndexes;
            var length = unusedIndexes.length;
            if (length > 0) {
                billboardVisualizerIndex = unusedIndexes.pop();
                billboard = this._billboardCollection.get(billboardVisualizerIndex);
            } else {
                billboardVisualizerIndex = this._billboardCollection.getLength();
                billboard = this._billboardCollection.add();
            }
            dynamicObject._billboardVisualizerIndex = billboardVisualizerIndex;
            billboard.dynamicObject = dynamicObject;
            billboard._visualizerUrl = undefined;
            billboard._visualizerTextureAvailable = false;

            // CZML_TODO Determine official defaults
            billboard.setColor(Color.WHITE);
            billboard.setEyeOffset(Cartesian3.ZERO);
            billboard.setPixelOffset(Cartesian2.ZERO);
            billboard.setScale(1.0);
            billboard.setHorizontalOrigin(HorizontalOrigin.CENTER);
            billboard.setVerticalOrigin(VerticalOrigin.CENTER);
        } else {
            billboard = this._billboardCollection.get(billboardVisualizerIndex);
        }

        var textureValue = textureProperty.getValue(time);
        if (textureValue !== billboard._visualizerUrl) {
            billboard._visualizerUrl = textureValue;
            billboard._visualizerTextureAvailable = false;
            this._textureAtlasBuilder.addTextureFromUrl(textureValue, textureReady(dynamicObject, this._billboardCollection, textureValue));
        }

        billboard.setShow(billboard._visualizerTextureAvailable);
        if (!billboard._visualizerTextureAvailable) {
            return;
        }

        position = positionProperty.getValueCartesian(time, position);
        if (position !== 'undefined') {
            billboard.setPosition(position);
        }

        var property = dynamicBillboard.color;

        if (typeof property !== 'undefined') {
            color = property.getValue(time, color);
            if (typeof color !== 'undefined') {
                billboard.setColor(color);
            }
        }

        property = dynamicBillboard.eyeOffset;
        if (typeof property !== 'undefined') {
            eyeOffset = property.getValue(time, eyeOffset);
            if (typeof eyeOffset !== 'undefined') {
                billboard.setEyeOffset(eyeOffset);
            }
        }

        property = dynamicBillboard.pixelOffset;
        if (typeof property !== 'undefined') {
            pixelOffset = property.getValue(time, pixelOffset);
            if (typeof pixelOffset !== 'undefined') {
                billboard.setPixelOffset(pixelOffset);
            }
        }

        property = dynamicBillboard.scale;
        if (typeof property !== 'undefined') {
            var scale = property.getValue(time);
            if (typeof scale !== 'undefined') {
                billboard.setScale(scale);
            }
        }

        property = dynamicBillboard.horizontalOrigin;
        if (typeof property !== 'undefined') {
            var horizontalOrigin = property.getValue(time);
            if (typeof horizontalOrigin !== 'undefined') {
                billboard.setHorizontalOrigin(horizontalOrigin);
            }
        }

        property = dynamicBillboard.verticalOrigin;
        if (typeof property !== 'undefined') {
            var verticalOrigin = property.getValue(time);
            if (typeof verticalOrigin !== 'undefined') {
                billboard.setVerticalOrigin(verticalOrigin);
            }
        }
    };

    DynamicBillboardVisualizer.prototype._onObjectsRemoved = function(dynamicObjectCollection, dynamicObjects) {
        var thisBillboardCollection = this._billboardCollection;
        var thisUnusedIndexes = this._unusedIndexes;
        for ( var i = dynamicObjects.length - 1; i > -1; i--) {
            var dynamicObject = dynamicObjects[i];
            var billboardVisualizerIndex = dynamicObject._billboardVisualizerIndex;
            if (typeof billboardVisualizerIndex !== 'undefined') {
                var billboard = thisBillboardCollection.get(billboardVisualizerIndex);
                billboard.setShow(false);
                billboard.setImageIndex(-1);
                billboard._visualizerUrl = undefined;
                billboard._visualizerTextureAvailable = false;
                dynamicObject._billboardVisualizerIndex = undefined;
                thisUnusedIndexes.push(billboardVisualizerIndex);
            }
        }
    };

    return DynamicBillboardVisualizer;
});<commit_msg>Fix missing `typeof`<commit_after>/*global define*/
define([
        '../Core/DeveloperError',
        '../Core/destroyObject',
        '../Core/Color',
        '../Core/Cartesian2',
        '../Core/Cartesian3',
        '../Scene/BillboardCollection',
        '../Scene/HorizontalOrigin',
        '../Scene/VerticalOrigin',
        '../Renderer/TextureAtlasBuilder'
    ], function(
        DeveloperError,
        destroyObject,
        Color,
        Cartesian2,
        Cartesian3,
        BillboardCollection,
        HorizontalOrigin,
        VerticalOrigin,
        TextureAtlasBuilder) {
    "use strict";

    //Callback to create a callback so that we close over all of the proper values.
    function textureReady(dynamicObject, billboardCollection, textureValue) {
        return function(imageIndex) {
            //By the time the texture was loaded, the billboard might already be
            //gone or have been assigned a different texture.  Look it up again
            //and check.
            var currentIndex = dynamicObject._billboardVisualizerIndex;
            if (typeof currentIndex !== 'undefined') {
                var cbBillboard = billboardCollection.get(currentIndex);
                if (cbBillboard._visualizerUrl === textureValue) {
                    cbBillboard._visualizerTextureAvailable = true;
                    cbBillboard.setImageIndex(imageIndex);
                }
            }
        };
    }

    /**
     * A DynamicObject visualizer which maps the DynamicBillboard instance
     * in DynamicObject.billboard to a Billboard primitive.
     * @alias DynamicBillboardVisualizer
     * @constructor
     *
     * @param {Scene} scene The scene the primitives will be rendered in.
     * @param {DynamicObjectCollection} [dynamicObjectCollection] The dynamicObjectCollection to visualize.
     *
     * @exception {DeveloperError} scene is required.
     *
     * @see DynamicBillboard
     * @see Scene
     * @see DynamicObject
     * @see DynamicObjectCollection
     * @see CompositeDynamicObjectCollection
     * @see VisualizerCollection
     * @see DynamicConeVisualizer
     * @see DynamicConeVisualizerUsingCustomSensor
     * @see DynamicLabelVisualizer
     * @see DynamicPointVisualizer
     * @see DynamicPolygonVisualizer
     * @see DynamicPolylineVisualizer
     * @see DynamicPyramidVisualizer
     *
     */
    var DynamicBillboardVisualizer = function(scene, dynamicObjectCollection) {
        if (typeof scene === 'undefined') {
            throw new DeveloperError('scene is required.');
        }

        this._scene = scene;
        this._unusedIndexes = [];
        this._dynamicObjectCollection = undefined;

        var billboardCollection = this._billboardCollection = new BillboardCollection();
        var atlas = this._textureAtlas = scene.getContext().createTextureAtlas();
        this._textureAtlasBuilder = new TextureAtlasBuilder(atlas);
        billboardCollection.setTextureAtlas(atlas);
        scene.getPrimitives().add(billboardCollection);
        this.setDynamicObjectCollection(dynamicObjectCollection);
    };

    /**
     * Returns the scene being used by this visualizer.
     *
     * @returns {Scene} The scene being used by this visualizer.
     */
    DynamicBillboardVisualizer.prototype.getScene = function() {
        return this._scene;
    };

    /**
     * Gets the DynamicObjectCollection being visualized.
     *
     * @returns {DynamicObjectCollection} The DynamicObjectCollection being visualized.
     */
    DynamicBillboardVisualizer.prototype.getDynamicObjectCollection = function() {
        return this._dynamicObjectCollection;
    };

    /**
     * Sets the DynamicObjectCollection to visualize.
     *
     * @param dynamicObjectCollection The DynamicObjectCollection to visualizer.
     */
    DynamicBillboardVisualizer.prototype.setDynamicObjectCollection = function(dynamicObjectCollection) {
        var oldCollection = this._dynamicObjectCollection;
        if (oldCollection !== dynamicObjectCollection) {
            if (typeof oldCollection !== 'undefined') {
                oldCollection.objectsRemoved.removeEventListener(DynamicBillboardVisualizer.prototype._onObjectsRemoved);
                this.removeAllPrimitives();
            }
            this._dynamicObjectCollection = dynamicObjectCollection;
            if (typeof dynamicObjectCollection !== 'undefined') {
                dynamicObjectCollection.objectsRemoved.addEventListener(DynamicBillboardVisualizer.prototype._onObjectsRemoved, this);
            }
        }
    };

    /**
     * Updates all of the primitives created by this visualizer to match their
     * DynamicObject counterpart at the given time.
     *
     * @param {JulianDate} time The time to update to.
     *
     * @exception {DeveloperError} time is required.
     */
    DynamicBillboardVisualizer.prototype.update = function(time) {
        if (typeof time === 'undefined') {
            throw new DeveloperError('time is requied.');
        }
        if (typeof this._dynamicObjectCollection !== 'undefined') {
            var dynamicObjects = this._dynamicObjectCollection.getObjects();
            for ( var i = 0, len = dynamicObjects.length; i < len; i++) {
                this._updateObject(time, dynamicObjects[i]);
            }
        }
    };

    /**
     * Removes all primitives from the scene.
     */
    DynamicBillboardVisualizer.prototype.removeAllPrimitives = function() {
        if (typeof this._dynamicObjectCollection !== 'undefined') {
            this._unusedIndexes = [];
            this._billboardCollection.removeAll();
            var dynamicObjects = this._dynamicObjectCollection.getObjects();
            for ( var i = dynamicObjects.length - 1; i > -1; i--) {
                dynamicObjects[i]._billboardVisualizerIndex = undefined;
            }
        }
    };

    /**
     * Returns true if this object was destroyed; otherwise, false.
     *

     * If this object was destroyed, it should not be used; calling any function other than
     * isDestroyed will result in a {@link DeveloperError} exception.
     *
     * @memberof DynamicBillboardVisualizer
     *
     * @return {Boolean} True if this object was destroyed; otherwise, false.
     *
     * @see DynamicBillboardVisualizer#destroy
     */
    DynamicBillboardVisualizer.prototype.isDestroyed = function() {
        return false;
    };

    /**
     * Destroys the WebGL resources held by this object.  Destroying an object allows for deterministic
     * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
     *

     * Once an object is destroyed, it should not be used; calling any function other than
     * isDestroyed will result in a {@link DeveloperError} exception.  Therefore,
     * assign the return value (undefined) to the object as done in the example.
     *
     * @memberof DynamicBillboardVisualizer
     *
     * @return {undefined}
     *
     * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
     *
     * @see DynamicBillboardVisualizer#isDestroyed
     *
     * @example
     * visualizer = visualizer && visualizer.destroy();
     */
    DynamicBillboardVisualizer.prototype.destroy = function() {
        this.removeAllPrimitives();
        this._scene.getPrimitives().remove(this._billboardCollection);
        return destroyObject(this);
    };

    var position;
    var color;
    var eyeOffset;
    var pixelOffset;
    DynamicBillboardVisualizer.prototype._updateObject = function(time, dynamicObject) {
        var dynamicBillboard = dynamicObject.billboard;
        if (typeof dynamicBillboard === 'undefined') {
            return;
        }

        var positionProperty = dynamicObject.position;
        if (typeof positionProperty === 'undefined') {
            return;
        }

        var textureProperty = dynamicBillboard.image;
        if (typeof textureProperty === 'undefined') {
            return;
        }

        var billboard;
        var showProperty = dynamicBillboard.show;
        var billboardVisualizerIndex = dynamicObject._billboardVisualizerIndex;
        var show = dynamicObject.isAvailable(time) && (typeof showProperty === 'undefined' || showProperty.getValue(time));

        if (!show) {
            //don't bother creating or updating anything else
            if (typeof billboardVisualizerIndex !== 'undefined') {
                billboard = this._billboardCollection.get(billboardVisualizerIndex);
                billboard.setShow(false);
                billboard.setImageIndex(-1);
                billboard._visualizerUrl = undefined;
                billboard._visualizerTextureAvailable = false;
                dynamicObject._billboardVisualizerIndex = undefined;
                this._unusedIndexes.push(billboardVisualizerIndex);
            }
            return;
        }

        if (typeof billboardVisualizerIndex === 'undefined') {
            var unusedIndexes = this._unusedIndexes;
            var length = unusedIndexes.length;
            if (length > 0) {
                billboardVisualizerIndex = unusedIndexes.pop();
                billboard = this._billboardCollection.get(billboardVisualizerIndex);
            } else {
                billboardVisualizerIndex = this._billboardCollection.getLength();
                billboard = this._billboardCollection.add();
            }
            dynamicObject._billboardVisualizerIndex = billboardVisualizerIndex;
            billboard.dynamicObject = dynamicObject;
            billboard._visualizerUrl = undefined;
            billboard._visualizerTextureAvailable = false;

            // CZML_TODO Determine official defaults
            billboard.setColor(Color.WHITE);
            billboard.setEyeOffset(Cartesian3.ZERO);
            billboard.setPixelOffset(Cartesian2.ZERO);
            billboard.setScale(1.0);
            billboard.setHorizontalOrigin(HorizontalOrigin.CENTER);
            billboard.setVerticalOrigin(VerticalOrigin.CENTER);
        } else {
            billboard = this._billboardCollection.get(billboardVisualizerIndex);
        }

        var textureValue = textureProperty.getValue(time);
        if (textureValue !== billboard._visualizerUrl) {
            billboard._visualizerUrl = textureValue;
            billboard._visualizerTextureAvailable = false;
            this._textureAtlasBuilder.addTextureFromUrl(textureValue, textureReady(dynamicObject, this._billboardCollection, textureValue));
        }

        billboard.setShow(billboard._visualizerTextureAvailable);
        if (!billboard._visualizerTextureAvailable) {
            return;
        }

        position = positionProperty.getValueCartesian(time, position);
        if (typeof position !== 'undefined') {
            billboard.setPosition(position);
        }

        var property = dynamicBillboard.color;

        if (typeof property !== 'undefined') {
            color = property.getValue(time, color);
            if (typeof color !== 'undefined') {
                billboard.setColor(color);
            }
        }

        property = dynamicBillboard.eyeOffset;
        if (typeof property !== 'undefined') {
            eyeOffset = property.getValue(time, eyeOffset);
            if (typeof eyeOffset !== 'undefined') {
                billboard.setEyeOffset(eyeOffset);
            }
        }

        property = dynamicBillboard.pixelOffset;
        if (typeof property !== 'undefined') {
            pixelOffset = property.getValue(time, pixelOffset);
            if (typeof pixelOffset !== 'undefined') {
                billboard.setPixelOffset(pixelOffset);
            }
        }

        property = dynamicBillboard.scale;
        if (typeof property !== 'undefined') {
            var scale = property.getValue(time);
            if (typeof scale !== 'undefined') {
                billboard.setScale(scale);
            }
        }

        property = dynamicBillboard.horizontalOrigin;
        if (typeof property !== 'undefined') {
            var horizontalOrigin = property.getValue(time);
            if (typeof horizontalOrigin !== 'undefined') {
                billboard.setHorizontalOrigin(horizontalOrigin);
            }
        }

        property = dynamicBillboard.verticalOrigin;
        if (typeof property !== 'undefined') {
            var verticalOrigin = property.getValue(time);
            if (typeof verticalOrigin !== 'undefined') {
                billboard.setVerticalOrigin(verticalOrigin);
            }
        }
    };

    DynamicBillboardVisualizer.prototype._onObjectsRemoved = function(dynamicObjectCollection, dynamicObjects) {
        var thisBillboardCollection = this._billboardCollection;
        var thisUnusedIndexes = this._unusedIndexes;
        for ( var i = dynamicObjects.length - 1; i > -1; i--) {
            var dynamicObject = dynamicObjects[i];
            var billboardVisualizerIndex = dynamicObject._billboardVisualizerIndex;
            if (typeof billboardVisualizerIndex !== 'undefined') {
                var billboard = thisBillboardCollection.get(billboardVisualizerIndex);
                billboard.setShow(false);
                billboard.setImageIndex(-1);
                billboard._visualizerUrl = undefined;
                billboard._visualizerTextureAvailable = false;
                dynamicObject._billboardVisualizerIndex = undefined;
                thisUnusedIndexes.push(billboardVisualizerIndex);
            }
        }
    };

    return DynamicBillboardVisualizer;
});<|endoftext|>