<commit_before>
$(function(){
	play();
	$('#play_game').click(function(){
		play();
	});
});

var bk_table = [];
var myScore = 0;
function play(){
	createUI();
	initNumber(bk_table);
}

//初始化界面
function createUI(){
	//bk_cell init
	for(var i = 0; i < 4; i++){
		bk_table[i] = [];
		for(var j = 0; j <4; j++){
			bk_table[i][j] = 0;
			var bk_cell = $('#bk_cell_'+ i + '_' + j);
			bk_cell.css('top', getTop(i, j));
			bk_cell.css('left', getLeft(i, j));
		}
	}
}

//点击paly，产生随机数，展示随机数
function initNumber(bk_table){
	var xy1 = createPosition(bk_table);
	var number1 = createNumber();

	var xy2 = createPosition(bk_table);
	var number2 = createNumber();

	bk_table[xy1.x][xy1.y] = number1;
	bk_table[xy2.x][xy2.y] = number2;

	showNumber(bk_table);
}

function moveNumber(){

}

//------------------------------------------------
function createNumber(){
	return Math.random() *10 > 5 ? 4:2;
}

//-----------------------------------------------
function createPosition(table){
	var x = Math.floor(Math.random() *4);
	var y = Math.floor(Math.random() *4);
	var i = 0;
	while(i < 100){
		if(table[x][y] === 0)
			return {x:x, y:y};
		x = Math.floor(Math.random() *4);
		y = Math.floor(Math.random() *4);
		i++;
	}

	for(i = 0; i < 4; i++){
		for(var j = 0; j < 4; j++){
			if(table[i][j] === 0)
				return {x:i, y:j};
		}
	}

	return false;
}

//-----------------------------------------------
function showNumber(table){
	$('.fr_cell').remove();
	for(var i = 0; i < 4; i++){
		for(var j = 0; j < 4; j++){
			var fr_cell_str = '
' + '
';
			$('#main_ui_cell').append(fr_cell_str);
			var fr_cell = $('#fr_cell_'+ i + '_' + j);
			if(table[i][j] == 0){
				fr_cell.css('width', '10px');
				fr_cell.css('height', '10px');
				fr_cell.css('top', getTop(i, j) + 50);
				fr_cell.css('left', getLeft(i, j) + 50);
			}else{
				fr_cell.css('width', '100px');
				fr_cell.css('height', '100px');
				fr_cell.css('top', getTop(i, j));
				fr_cell.css('left', getLeft(i, j));
				fr_cell.css('backgroundColor', getBackColor(table[i][j])); //设置背景色
				fr_cell.css('color', getTextColor(table[i][j])); //设置文字颜色
				fr_cell.text(table[i][j]);
			}
		}
	}
}

//-------------------------------------------
function getBackColor(number){
	switch(number){
		case 2: return '#EEE4DA'; break;
		case 4: return '#EEE4DA'; break;
		case 8: return '#F3B079'; break;
		case 16: return '#EB8E53'; break;
		case 32: return '#FB795F'; break;
		case 64: return '#EB573B'; break;
		case 128: return '#00C6E5'; break;
		case 256: return '#EDCA62'; break;
		case 512: return '#FCD209'; break;
		case 1024: return '#E12D2D'; break;
		case 2048: return '#4CB649'; break;
		default: break;
	}
}

//------------------------------------------
function getTextColor(number){
	if(number <= 4)
		return '#665C52';

	return '#FFF';
}

//------------------------------------------
function getTop(i , j){
	return i*110 + 10;
}

//------------------------------------------
function getLeft(i, j){
	return j*110 + 10;
}

function getKeyDowm(e){
	switch(e.which){
		case 37:
			//left
		 	//整体向左移 并合并相同的数字，i不变
		 	for(var i = 0; i < 4; i++){
		 		for(var j = 3; j > 0; j--){
		 			if(bk_table[i][j-1] === 0 || (bk_table[i][j] === bk_table[i][j-1])){
		 				bk_table[i][j-1] = bk_table[i][j] + bk_table[i][j-1];
		 				myScore = myScore + bk_table[i][j-1];
		 				bk_table[i][j] = 0;
		 			}
		 		}
		 	}
		 	//生成新的数字和UI
		 	$('#my_score').text(myScore);
		 	initNumber(bk_table);
		 	break;
		case 38:
			//top
		 	//j = 0不移，其余都移动 j不变
		 	for(var j = 0; j < 4; j++){
		 		for(var i = 3; i > 0 ; i--){
			 		if(bk_table[i-1][j] === 0 || (bk_table[i][j] === bk_table[i-1][j])){
			 			bk_table[i-1][j] = bk_table[i][j] + bk_table[i-1][j];
			 			myScore = myScore + bk_table[i-1][j];
			 			bk_table[i][j] = 0;
			 		}
		 		}
		 	}
		 	$('#my_score').text(myScore);
		 	initNumber(bk_table);
		 	break;
		case 39:
			//right
			//整体向右移 并合并相同的数字，i不变
		 	for(var i = 0; i < 4; i++){
		 		for(var j = 0; j < 3; j++){
		 			if(bk_table[i][j+1] === 0 || (bk_table[i][j] === bk_table[i][j+1])){
		 				bk_table[i][j+1] = bk_table[i][j] + bk_table[i][j+1];
		 				myScore = myScore + bk_table[i][j+1];
		 				bk_table[i][j] = 0;
		 			}
		 		}
		 	}
		 	//生成新的数字和UI
		 	$('#my_score').text(myScore);
		 	initNumber(bk_table);
		 	break;
		case 40:
			//bottom
			for(var j = 0; j < 4; j++){
		 		for(var i = 0; i < 3 ; i++){
			 		if(bk_table[i+1][j] === 0 || (bk_table[i][j] === bk_table[i+1][j])){
			 			bk_table[i+1][j] = bk_table[i][j] + bk_table[i+1][j];
			 			myScore = myScore + bk_table[i+1][j];
			 			bk_table[i][j] = 0;
			 		}
		 		}
		 	}
		 	$('#my_score').text(myScore);
		 	initNumber(bk_table);
			break;
		default:
			break;
	}
}

document.onkeydown = getKeyDowm;

// function hasSpace(){
// 	for(var i = 0; i < 4; i++){
// 		for(var j = 0; j < 4; j++){
// 			if(bk_table[i][j] === 0)
// 				return true;
// 		}
// 	}

// 	return false;
// }

// function canMoveLeft(){
// 	for(var i = 0; i < 4; i++){
// 		for(var j = 0; j < 4; j++){

// 		}
// 	}
// }<commit_msg>socore init<commit_after>
$(function(){
	play();
	$('#play_game').click(function(){
		play();
	});
});

var bk_table = [];
var myScore = 0;
function play(){
	myScore = 0;
	$('#my_score').text(0);
	createUI();
	initNumber(bk_table);
}

//初始化界面
function createUI(){
	//bk_cell init
	for(var i = 0; i < 4; i++){
		bk_table[i] = [];
		for(var j = 0; j <4; j++){
			bk_table[i][j] = 0;
			var bk_cell = $('#bk_cell_'+ i + '_' + j);
			bk_cell.css('top', getTop(i, j));
			bk_cell.css('left', getLeft(i, j));
		}
	}
}

//点击paly，产生随机数，展示随机数
function initNumber(bk_table){
	var xy1 = createPosition(bk_table);
	var number1 = createNumber();
	//产生一个数
	bk_table[xy1.x][xy1.y] = number1;

	// var xy2 = createPosition(bk_table);
	// var number2 = createNumber();
	// bk_table[xy2.x][xy2.y] = number2;

	showNumber(bk_table);
}

function createNumber(){
	return Math.random() *10 > 5 ? 4:2;
}

function createPosition(table){
	var x = Math.floor(Math.random() *4);
	var y = Math.floor(Math.random() *4);
	var i = 0;
	while(i < 300){
		if(table[x][y] === 0)
			return {x:x, y:y};
		x = Math.floor(Math.random() *4);
		y = Math.floor(Math.random() *4);
		i++;
	}

	for(i = 0; i < 4; i++){
		for(var j = 0; j < 4; j++){
			if(table[i][j] === 0)
				return {x:i, y:j};
		}
	}

	return false;
}

function showNumber(table){
	$('.fr_cell').remove();
	for(var i = 0; i < 4; i++){
		for(var j = 0; j < 4; j++){
			var fr_cell_str = '
' + '
';
			$('#main_ui_cell').append(fr_cell_str);
			var fr_cell = $('#fr_cell_'+ i + '_' + j);
			if(table[i][j] == 0){
				fr_cell.css('width', '10px');
				fr_cell.css('height', '10px');
				fr_cell.css('top', getTop(i, j) + 50);
				fr_cell.css('left', getLeft(i, j) + 50);
			}else{
				fr_cell.css('width', '100px');
				fr_cell.css('height', '100px');
				fr_cell.css('top', getTop(i, j));
				fr_cell.css('left', getLeft(i, j));
				fr_cell.css('backgroundColor', getBackColor(table[i][j])); //设置背景色
				fr_cell.css('color', getTextColor(table[i][j])); //设置文字颜色
				fr_cell.text(table[i][j]);
			}
		}
	}
}

function getBackColor(number){
	switch(number){
		case 2: return '#EEE4DA'; break;
		case 4: return '#EEE4DA'; break;
		case 8: return '#F3B079'; break;
		case 16: return '#EB8E53'; break;
		case 32: return '#FB795F'; break;
		case 64: return '#EB573B'; break;
		case 128: return '#00C6E5'; break;
		case 256: return '#EDCA62'; break;
		case 512: return '#FCD209'; break;
		case 1024: return '#E12D2D'; break;
		case 2048: return '#4CB649'; break;
		default: break;
	}
}

function getTextColor(number){
	if(number <= 4)
		return '#665C52';

	return '#FFF';
}

function getTop(i , j){
	return i*110 + 10;
}

function getLeft(i, j){
	return j*110 + 10;
}

function getKeyDowm(e){
	switch(e.which){
		case 37:
			//left
		 	//整体向左移 并合并相同的数字，i不变
		 	for(var i = 0; i < 4; i++){
		 		for(var j = 3; j > 0; j--){
		 			if(bk_table[i][j-1] === 0 || (bk_table[i][j] === bk_table[i][j-1])){
		 				bk_table[i][j-1] = bk_table[i][j] + bk_table[i][j-1];
		 				myScore = myScore + bk_table[i][j-1];
		 				bk_table[i][j] = 0;
		 			}
		 		}
		 	}
		 	//生成新的数字和UI
		 	$('#my_score').text(myScore);
		 	initNumber(bk_table);
		 	break;
		case 38:
			//top
		 	//j = 0不移，其余都移动 j不变
		 	for(var j = 0; j < 4; j++){
		 		for(var i = 3; i > 0 ; i--){
			 		if(bk_table[i-1][j] === 0 || (bk_table[i][j] === bk_table[i-1][j])){
			 			bk_table[i-1][j] = bk_table[i][j] + bk_table[i-1][j];
			 			myScore = myScore + bk_table[i-1][j];
			 			bk_table[i][j] = 0;
			 		}
		 		}
		 	}
		 	$('#my_score').text(myScore);
		 	initNumber(bk_table);
		 	break;
		case 39:
			//right
			//整体向右移 并合并相同的数字，i不变
		 	for(var i = 0; i < 4; i++){
		 		for(var j = 0; j < 3; j++){
		 			if(bk_table[i][j+1] === 0 || (bk_table[i][j] === bk_table[i][j+1])){
		 				bk_table[i][j+1] = bk_table[i][j] + bk_table[i][j+1];
		 				myScore = myScore + bk_table[i][j+1];
		 				bk_table[i][j] = 0;
		 			}
		 		}
		 	}
		 	//生成新的数字和UI
		 	$('#my_score').text(myScore);
		 	initNumber(bk_table);
		 	break;
		case 40:
			//bottom
			for(var j = 0; j < 4; j++){
		 		for(var i = 0; i < 3 ; i++){
			 		if(bk_table[i+1][j] === 0 || (bk_table[i][j] === bk_table[i+1][j])){
			 			bk_table[i+1][j] = bk_table[i][j] + bk_table[i+1][j];
			 			myScore = myScore + bk_table[i+1][j];
			 			bk_table[i][j] = 0;
			 		}
		 		}
		 	}
		 	$('#my_score').text(myScore);
		 	initNumber(bk_table);
			break;
		default:
			break;
	}
}

document.onkeydown = getKeyDowm;

<|endoftext|>


<commit_before>var http = require('http');
http.createServer(function (req, res) {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('Hello Travis!\n') // this will FAIL travis ci lint
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');<commit_msg>fixed linting issue<commit_after>var http = require('http');
http.createServer(function (req, res) {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('Hello Travis!\n');
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');<|endoftext|>


<commit_before>// DecentCMS (c) 2014 Bertrand Le Roy, under MIT. See LICENSE.txt for licensing details.
'use strict';

/**
 * @description
 * A view engine where templates are JavaScript functions.
 */
var codeViewEngine = {
  service: 'view-engine',
  feature: 'code-view-engine',
  extension: 'js',
  /**
   * @description
   * Loads the rendering function from the provided path.
   * @param {string} templatePath The path to the JavaScript file.
   * @param {Function} done The callback function to call when the template is loaded.
   * @returns {function} The template function.
   */
  load: function (templatePath, done) {
    done(require(templatePath));
  }
};

module.exports = codeViewEngine;<commit_msg>Name load function on code view engine.<commit_after>// DecentCMS (c) 2014 Bertrand Le Roy, under MIT. See LICENSE.txt for licensing details.
'use strict';

/**
 * @description
 * A view engine where templates are JavaScript functions.
 */
var codeViewEngine = {
  service: 'view-engine',
  feature: 'code-view-engine',
  extension: 'js',
  /**
   * @description
   * Loads the rendering function from the provided path.
   * @param {string} templatePath The path to the JavaScript file.
   * @param {Function} done The callback function to call when the template is loaded.
   * @returns {function} The template function.
   */
  load: function loadCodeTemplate(templatePath, done) {
    done(require(templatePath));
  }
};

module.exports = codeViewEngine;<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

(() => {
  "use strict";

  const MARKUP = `

      <button id="play-pause-button"></button>
      <input id="progress" type="range" value="0" min="0" max="100" step="1"></input>

        #1
         / #2

      <button id="volume-switch"></button>
      <input id="volume-level" type="range" value="100" min="0" max="100" step="1"></input>

  `;

  // States.
  const BUFFERING = "buffering";
  const ENDED = "ended";
  const ERRORED = "errored";
  const PAUSED = "paused";
  const PLAYING = "playing";

  // State transitions.
  const TRANSITIONS = {
    buffer: {
      "paused": BUFFERING,
    },
    end: {
      "playing": ENDED,
      "paused": ENDED,
    },
    error: {
      "buffering": ERRORED,
      "playing": ERRORED,
      "paused": ERRORED,
    },
    pause: {
      "buffering": PAUSED,
      "playing": PAUSED,
    },
    play: {
      "buffering": PLAYING,
      "ended": PLAYING,
      "paused": PLAYING,
    },
  };

  function camelCase(str) {
    const rdashes = /-(.)/g;
    return str.replace(rdashes, (str, p1) => {
      return p1.toUpperCase();
    });
  }

  function formatTime(time, showHours = false) {
    // Format the duration as "h:mm:ss" or "m:ss"
    time = Math.round(time / 1000);

    const hours = Math.floor(time / 3600);
    const mins  = Math.floor((time % 3600) / 60);
    const secs  = Math.floor(time % 60);

    const formattedHours = hours || showHours ?
      `${hours.toString().padStart(2, "0")}:` :
      "";

    return `${formattedHours}${mins.toString().padStart(2, "0")}:${secs.toString().padStart(2, "0")}`;
  }

  class MediaControls {
    constructor() {
      // Get the instance of the shadow root where these controls live.
      this.controls = document.servoGetMediaControls("@@@id@@@");
      // Get the instance of the host of these controls.
      this.media = this.controls.host;

      // Create root element and load markup.
      this.root = document.createElement("div");
      this.root.classList.add("root");
      this.root.innerHTML = MARKUP;
      this.controls.appendChild(this.root);

      // Import elements.
      this.elements = {};
      [
        "duration",
        "play-pause-button",
        "position-duration-box",
        "position-text",
        "progress",
        "volume-switch",
        "volume-level"
      ].forEach(id => {
        this.elements[camelCase(id)] = this.controls.getElementById(id);
      });

      // Init position duration box.
      const positionTextNode = this.elements.positionText;
      const durationSpan = this.elements.duration;
      const durationFormat = durationSpan.textContent;
      const positionFormat = positionTextNode.textContent;

      durationSpan.classList.add("duration");
      durationSpan.setAttribute("role", "none");

      Object.defineProperties(this.elements.positionDurationBox, {
        durationSpan: {
          value: durationSpan,
        },
        position: {
          get: () => {
            return positionTextNode.textContent;
          },
          set: (v) => {
            positionTextNode.textContent = positionFormat.replace("#1", v);
          },
        },
        duration: {
          get: () => {
            return durationSpan.textContent;
          },
          set: (v) => {
            durationSpan.textContent = v ? durationFormat.replace("#2", v) : "";
          },
        },
        show:{
          value: (currentTime, duration) => {
            const self = this.elements.positionDurationBox;
            if (self.position != currentTime) {
              self.position = currentTime;
            }
            if (self.duration != duration) {
              self.duration = duration;
            }
            self.classList.remove("hidden");
          },
        },
      });

      // Add event listeners.
      this.mediaEvents = [
        "play", "pause", "ended", "volumechange", "loadeddata",
        "loadstart", "timeupdate", "progress", "playing",
        "waiting", "canplay", "canplaythrough", "seeking",
        "seeked", "emptied", "loadedmetadata", "error", "suspend"];
      this.mediaEvents.forEach(event => {
        this.media.addEventListener(event, this);
      });

      this.controlEvents = [
        { el: this.elements.playPauseButton, type: "click"},
        { el: this.elements.volumeSwitch, type: "click" },
        { el: this.elements.volumeLevel, type: "input" },
      ];
      this.controlEvents.forEach(({ el, type }) => {
        el.addEventListener(type, this);
      });

      // Create state transitions.
      //
      // It exposes one method per transition. i.e. this.pause(), this.play(), etc.
      // For each transition, we check that the transition is possible and call
      // the `onStateChange` handler.
      for (let name in TRANSITIONS) {
        if (!TRANSITIONS.hasOwnProperty(name)) {
          continue;
        }
        this[name] = () => {
          const from = this.state;

          // Checks if the transition is valid in the current state.
          if (!TRANSITIONS[name][from]) {
            const error = `Transition "${name}" invalid for the current state "${from}"`;
            console.error(error);
            throw new Error(error);
          }

          const to = TRANSITIONS[name][from];

          if (from == to) {
            return;
          }

          // Transition to the next state.
          this.state = to;
          this.onStateChange(from);
        };
      }

      // Set initial state.
      this.state = this.media.paused ? PAUSED : PLAYING;
      this.onStateChange(null);
    }

    // State change handler
    onStateChange(from) {
      this.render(from);
    }

    render(from = this.state) {
      const isAudioOnly = this.media.localName == "audio";
      if (!isAudioOnly) {
        // XXX This should ideally use clientHeight/clientWidth,
        //     but for some reason I couldn't figure out yet,
        //     using it breaks layout.
        this.root.style.height = this.media.videoHeight;
        this.root.style.width = this.media.videoWidth;
      }

      // Error
      if (this.state == ERRORED) {
        //XXX render errored state
        return;
      }

      if (this.state != from) {
        // Play/Pause button.
        const playPauseButton = this.elements.playPauseButton;
        playPauseButton.classList.remove(from);
        playPauseButton.classList.add(this.state);
      }

      // Progress.
      const positionPercent = this.media.currentTime / this.media.duration * 100;
      if (Number.isFinite(positionPercent)) {
        this.elements.progress.value = positionPercent;
      } else {
        this.elements.progress.value = 0;
      }

      // Current time and duration.
      let currentTime = formatTime(0);
      let duration = formatTime(0);
      if (!isNaN(this.media.currentTime) && !isNaN(this.media.duration)) {
        currentTime = formatTime(Math.round(this.media.currentTime * 1000));
        duration = formatTime(Math.round(this.media.duration * 1000));
      }
      this.elements.positionDurationBox.show(currentTime, duration);

      // Volume.
      const volumeSwitchClass = this.media.muted || this.media.volume == 0 ? "muted" : "volumeup";
      if (!this.elements.volumeSwitch.classList.contains(volumeSwitchClass)) {
        this.elements.volumeSwitch.classList = "";
        this.elements.volumeSwitch.classList.add(volumeSwitchClass);
      }
      const volumeLevelValue = Math.round((this.media.muted ? 0 : this.media.volume) * 100);
      if (this.elements.volumeLevel.value != volumeLevelValue) {
        this.elements.volumeLevel.value = volumeLevelValue;
      }
    }

    handleEvent(event) {
      if (!event.isTrusted) {
        console.warn(`Drop untrusted event ${event.type}`);
        return;
      }

      if (this.mediaEvents.includes(event.type)) {
        this.onMediaEvent(event);
      } else {
        this.onControlEvent(event);
      }
    }

    onControlEvent(event) {
      switch (event.type) {
        case "click":
          switch (event.currentTarget) {
            case this.elements.playPauseButton:
              this.playOrPause();
              break;
            case this.elements.volumeSwitch:
              this.muteUnmute();
              break;
          }
          break;
        case "input":
          switch (event.currentTarget) {
            case this.elements.volumeLevel:
              this.changeVolume();
              break;
          }
          break;
        break;
          throw new Error(`Unknown event ${event.type}`);
      }
    }

    // HTMLMediaElement event handler
    onMediaEvent(event) {
      switch (event.type) {
        case "ended":
          this.end();
          break;
        case "play":
        case "pause":
          // Transition to PLAYING or PAUSED state.
          this[event.type]();
          break;
        case "volumechange":
        case "timeupdate":
        case "resize":
          this.render();
          break;
        case "loadedmetadata":
          break;
      }
    }

    /* Media actions */

    playOrPause() {
      switch(this.state) {
        case PLAYING:
          this.media.pause();
          break;
        case BUFFERING:
        case ENDED:
        case PAUSED:
          this.media.play();
          break;
        default:
          throw new Error(`Invalid state ${this.state}`);
      }
    }

    muteUnmute() {
      this.media.muted = !this.media.muted;
    }

    changeVolume() {
      const volume = parseInt(this.elements.volumeLevel.value);
      if (!isNaN(volume)) {
        this.media.volume = volume / 100;
      }
    }
  }

  new MediaControls();
})();
<commit_msg>Naming and minor JS nits fixed<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

(() => {
  "use strict";

  const MARKUP = `

      <button id="play-pause-button"></button>
      <input id="progress" type="range" value="0" min="0" max="100" step="1"></input>

        #1
         / #2

      <button id="volume-switch"></button>
      <input id="volume-level" type="range" value="100" min="0" max="100" step="1"></input>

  `;

  // States.
  const BUFFERING = "buffering";
  const ENDED = "ended";
  const ERRORED = "errored";
  const PAUSED = "paused";
  const PLAYING = "playing";

  // State transitions.
  const TRANSITIONS = {
    buffer: {
      "paused": BUFFERING,
    },
    end: {
      "playing": ENDED,
      "paused": ENDED,
    },
    error: {
      "buffering": ERRORED,
      "playing": ERRORED,
      "paused": ERRORED,
    },
    pause: {
      "buffering": PAUSED,
      "playing": PAUSED,
    },
    play: {
      "buffering": PLAYING,
      "ended": PLAYING,
      "paused": PLAYING,
    },
  };

  function camelCase(str) {
    const rdashes = /-(.)/g;
    return str.replace(rdashes, (str, p1) => {
      return p1.toUpperCase();
    });
  }

  function formatTime(time, showHours = false) {
    // Format the duration as "h:mm:ss" or "m:ss"
    time = Math.round(time / 1000);

    const hours = Math.floor(time / 3600);
    const mins  = Math.floor((time % 3600) / 60);
    const secs  = Math.floor(time % 60);

    const formattedHours = hours || showHours ?
      `${hours.toString().padStart(2, "0")}:` :
      "";

    return `${formattedHours}${mins.toString().padStart(2, "0")}:${secs.toString().padStart(2, "0")}`;
  }

  class MediaControls {
    constructor() {
      // Get the instance of the shadow root where these controls live.
      this.controls = document.servoGetMediaControls("@@@id@@@");
      // Get the instance of the host of these controls.
      this.media = this.controls.host;

      // Create root element and load markup.
      this.root = document.createElement("div");
      this.root.classList.add("root");
      this.root.innerHTML = MARKUP;
      this.controls.appendChild(this.root);

      // Import elements.
      this.elements = {};
      [
        "duration",
        "play-pause-button",
        "position-duration-box",
        "position-text",
        "progress",
        "volume-switch",
        "volume-level"
      ].forEach(id => {
        this.elements[camelCase(id)] = this.controls.getElementById(id);
      });

      // Init position duration box.
      const positionTextNode = this.elements.positionText;
      const durationSpan = this.elements.duration;
      const durationFormat = durationSpan.textContent;
      const positionFormat = positionTextNode.textContent;

      durationSpan.classList.add("duration");
      durationSpan.setAttribute("role", "none");

      Object.defineProperties(this.elements.positionDurationBox, {
        durationSpan: {
          value: durationSpan,
        },
        position: {
          get: () => {
            return positionTextNode.textContent;
          },
          set: (v) => {
            positionTextNode.textContent = positionFormat.replace("#1", v);
          },
        },
        duration: {
          get: () => {
            return durationSpan.textContent;
          },
          set: (v) => {
            durationSpan.textContent = v ? durationFormat.replace("#2", v) : "";
          },
        },
        show:{
          value: (currentTime, duration) => {
            const self = this.elements.positionDurationBox;
            if (self.position != currentTime) {
              self.position = currentTime;
            }
            if (self.duration != duration) {
              self.duration = duration;
            }
            self.classList.remove("hidden");
          },
        },
      });

      // Add event listeners.
      this.mediaEvents = [
        "play", "pause", "ended", "volumechange", "loadeddata",
        "loadstart", "timeupdate", "progress", "playing",
        "waiting", "canplay", "canplaythrough", "seeking",
        "seeked", "emptied", "loadedmetadata", "error", "suspend"];
      this.mediaEvents.forEach(event => {
        this.media.addEventListener(event, this);
      });

      this.controlEvents = [
        { el: this.elements.playPauseButton, type: "click"},
        { el: this.elements.volumeSwitch, type: "click" },
        { el: this.elements.volumeLevel, type: "input" },
      ];
      this.controlEvents.forEach(({ el, type }) => {
        el.addEventListener(type, this);
      });

      // Create state transitions.
      //
      // It exposes one method per transition. i.e. this.pause(), this.play(), etc.
      // For each transition, we check that the transition is possible and call
      // the `onStateChange` handler.
      for (let name in TRANSITIONS) {
        if (!TRANSITIONS.hasOwnProperty(name)) {
          continue;
        }
        this[name] = () => {
          const from = this.state;

          // Checks if the transition is valid in the current state.
          if (!TRANSITIONS[name][from]) {
            const error = `Transition "${name}" invalid for the current state "${from}"`;
            console.error(error);
            throw new Error(error);
          }

          const to = TRANSITIONS[name][from];

          if (from == to) {
            return;
          }

          // Transition to the next state.
          this.state = to;
          this.onStateChange(from);
        };
      }

      // Set initial state.
      this.state = this.media.paused ? PAUSED : PLAYING;
      this.onStateChange(null);
    }

    // State change handler
    onStateChange(from) {
      this.render(from);
    }

    render(from = this.state) {
      const isAudioOnly = this.media.localName == "audio";
      if (!isAudioOnly) {
        // XXX This should ideally use clientHeight/clientWidth,
        //     but for some reason I couldn't figure out yet,
        //     using it breaks layout.
        this.root.style.height = this.media.videoHeight;
        this.root.style.width = this.media.videoWidth;
      }

      // Error
      if (this.state == ERRORED) {
        //XXX render errored state
        return;
      }

      if (this.state != from) {
        // Play/Pause button.
        const playPauseButton = this.elements.playPauseButton;
        playPauseButton.classList.remove(from);
        playPauseButton.classList.add(this.state);
      }

      // Progress.
      const positionPercent = this.media.currentTime / this.media.duration * 100;
      if (Number.isFinite(positionPercent)) {
        this.elements.progress.value = positionPercent;
      } else {
        this.elements.progress.value = 0;
      }

      // Current time and duration.
      let currentTime = formatTime(0);
      let duration = formatTime(0);
      if (!isNaN(this.media.currentTime) && !isNaN(this.media.duration)) {
        currentTime = formatTime(Math.round(this.media.currentTime * 1000));
        duration = formatTime(Math.round(this.media.duration * 1000));
      }
      this.elements.positionDurationBox.show(currentTime, duration);

      // Volume.
      this.elements.volumeSwitch.className = this.media.muted || !this.media.volume ? "muted" : "volumeup";
      const volumeLevelValue = this.media.muted ? 0 : Math.round(this.media.volume * 100);
      if (this.elements.volumeLevel.value != volumeLevelValue) {
        this.elements.volumeLevel.value = volumeLevelValue;
      }
    }

    handleEvent(event) {
      if (!event.isTrusted) {
        console.warn(`Drop untrusted event ${event.type}`);
        return;
      }

      if (this.mediaEvents.includes(event.type)) {
        this.onMediaEvent(event);
      } else {
        this.onControlEvent(event);
      }
    }

    onControlEvent(event) {
      switch (event.type) {
        case "click":
          switch (event.currentTarget) {
            case this.elements.playPauseButton:
              this.playOrPause();
              break;
            case this.elements.volumeSwitch:
              this.toggleMuted();
              break;
          }
          break;
        case "input":
          switch (event.currentTarget) {
            case this.elements.volumeLevel:
              this.changeVolume();
              break;
          }
          break;
        break;
          throw new Error(`Unknown event ${event.type}`);
      }
    }

    // HTMLMediaElement event handler
    onMediaEvent(event) {
      switch (event.type) {
        case "ended":
          this.end();
          break;
        case "play":
        case "pause":
          // Transition to PLAYING or PAUSED state.
          this[event.type]();
          break;
        case "volumechange":
        case "timeupdate":
        case "resize":
          this.render();
          break;
        case "loadedmetadata":
          break;
      }
    }

    /* Media actions */

    playOrPause() {
      switch(this.state) {
        case PLAYING:
          this.media.pause();
          break;
        case BUFFERING:
        case ENDED:
        case PAUSED:
          this.media.play();
          break;
        default:
          throw new Error(`Invalid state ${this.state}`);
      }
    }

    toggleMuted() {
      this.media.muted = !this.media.muted;
    }

    changeVolume() {
      const volume = parseInt(this.elements.volumeLevel.value);
      if (!isNaN(volume)) {
        this.media.volume = volume / 100;
      }
    }
  }

  new MediaControls();
})();
<|endoftext|>


<commit_before>import { Observable } from 'rxjs/Observable';
import { combineEpics } from 'redux-observable';
import {
  EntityDataModelApi
} from 'loom-data';

import * as actionTypes from './TopUtilizersActionTypes';
import * as actionFactory from './TopUtilizersActionFactory';

function getAssociationsEpic(action$) {
  return action$
    .ofType(actionTypes.GET_ASSOCIATIONS_REQUEST)
    .mergeMap((action) => {
      console.log('associations request, ', action);
      return Observable
        .from(
          //Plug in associations api here
          EntityDataModelApi.getAllEntityTypes()
        )
        .mergeMap((results) => {
          return Observable
            .of(
              actionFactory.getAssociationsSuccess(results)
            );
        })
        .catch((err) => {
          actionFactory.getAssociationsFailure(err);
        });
    });
}

function getAllEntityTypesEpic(action$) {
  return action$
    .ofType(actionTypes.GET_ENTITY_TYPES_REQUEST)
    .mergeMap((action) => {
      return Observable
        .from(
          EntityDataModelApi.getAllEntityTypes()
        )
        .mergeMap((results) => {
          return Observable
            .of(
              actionFactory.getAllEntityTypesSuccess(results)
            );
        })
        .catch((err) => {
          actionFactory.getAllEntityTypesFailure(err);
        });
    });
}

function submitQueryEpic(action$, state) {
  return action$
    .ofType(actionTypes.SUBMIT_TOP_UTILIZERS_REQUEST)
    .mergeMap((action) => {
      const topUtilizersState = state.getState().get('topUtilizers');
      const topUtilizersDetailsObj = topUtilizersState.get('topUtilizersDetailsList').toJS();
      const topUtilizersDetailsList = Object.values(topUtilizersDetailsObj);
      return Observable
        .from(
          // API.findTopUtilizers(state.get('entitySetId'), 100, topUtilizersDetailsList)
        )
        .mergeMap((results) => {
          return Observable
            .of(
              actionFactory.onSubmitSuccess(results)
            );
        })
        .catch((err) => {
          actionFactory.onSubmitFailure();
        });
    });
}

export default combineEpics(
  getAssociationsEpic,
  getAllEntityTypesEpic,
  submitQueryEpic
);
<commit_msg>results.entities<commit_after>import { Observable } from 'rxjs/Observable';
import { combineEpics } from 'redux-observable';
import {
  EntityDataModelApi
} from 'loom-data';

import * as actionTypes from './TopUtilizersActionTypes';
import * as actionFactory from './TopUtilizersActionFactory';

function getAssociationsEpic(action$) {
  return action$
    .ofType(actionTypes.GET_ASSOCIATIONS_REQUEST)
    .mergeMap((action) => {
      console.log('associations request, ', action);
      return Observable
        .from(
          //Plug in associations api here
          EntityDataModelApi.getAllEntityTypes()
        )
        .mergeMap((results) => {
          return Observable
            .of(
              actionFactory.getAssociationsSuccess(results)
            );
        })
        .catch((err) => {
          actionFactory.getAssociationsFailure(err);
        });
    });
}

function getAllEntityTypesEpic(action$) {
  return action$
    .ofType(actionTypes.GET_ENTITY_TYPES_REQUEST)
    .mergeMap((action) => {
      return Observable
        .from(
          EntityDataModelApi.getAllEntityTypes()
        )
        .mergeMap((results) => {
          return Observable
            .of(
              actionFactory.getAllEntityTypesSuccess(results)
            );
        })
        .catch((err) => {
          actionFactory.getAllEntityTypesFailure(err);
        });
    });
}

function submitQueryEpic(action$, state) {
  return action$
    .ofType(actionTypes.SUBMIT_TOP_UTILIZERS_REQUEST)
    .mergeMap((action) => {
      const topUtilizersState = state.getState().get('topUtilizers');
      const topUtilizersDetailsObj = topUtilizersState.get('topUtilizersDetailsList').toJS();
      const topUtilizersDetailsList = Object.values(topUtilizersDetailsObj);
      return Observable
        .from(
          // API.findTopUtilizers(state.get('entitySetId'), 100, topUtilizersDetailsList)
        )
        .mergeMap((results) => {
          return Observable
            .of(
              actionFactory.onSubmitSuccess(results.entities)
            );
        })
        .catch((err) => {
          actionFactory.onSubmitFailure();
        });
    });
}

export default combineEpics(
  getAssociationsEpic,
  getAllEntityTypesEpic,
  submitQueryEpic
);
<|endoftext|>


<commit_before>'use strict';

angular.module('mean.cooking').controller('CookingController', ['$scope', '$stateParams', '$location', 'Global', 'Cooking',
	function ($scope, $stateParams, $location, Global, Cooking) {
    $scope.global = Global;

    $scope.create = function() {
        var cooking = new Cooking({
            title: this.title,
            content: this.content
        });
        cooking.$save(function(response) {
            $location.path('cookings/' + response._id);
        });

        this.title = '';
        this.content = '';
    };

    $scope.remove = function(cooking) {
        if (cooking) {
            cooking.$remove();

            for (var i in $scope.cookings) {
                if ($scope.cookings[i] === cooking) {
                    $scope.cookings.splice(i, 1);
                }
            }
        }
        else {
            $scope.cooking.$remove();
            $location.path('cookings');
        }
    };

    $scope.update = function() {
        var cooking = $scope.cooking;
        if (!cooking.updated) {
            cooking.updated = [];
        }
        cooking.updated.push(new Date().getTime());

        cooking.$update(function() {
            $location.path('cookings/' + cooking._id);
        });
    };

    $scope.find = function() {
        Cooking.query(function(cookings) {
            $scope.cookings = cookings;
        });
    };

    $scope.findOne = function() {
        Cooking.get({
            cookingId: $stateParams.cookingId
        }, function(cooking) {
            $scope.cooking = cooking;
        });
    };
}]);<commit_msg>description<commit_after>'use strict';

angular.module('mean.cooking').controller('CookingController', ['$scope', '$stateParams', '$location', 'Global', 'Cooking',
	function ($scope, $stateParams, $location, Global, Cooking) {
    $scope.global = Global;

    $scope.create = function() {
        var cooking = new Cooking({
            title: this.title,
            description: this.description
        });
        cooking.$save(function(response) {
            $location.path('cookings/' + response._id);
        });

        this.title = '';
        this.description = '';
    };

    $scope.remove = function(cooking) {
        if (cooking) {
            cooking.$remove();

            for (var i in $scope.cookings) {
                if ($scope.cookings[i] === cooking) {
                    $scope.cookings.splice(i, 1);
                }
            }
        }
        else {
            $scope.cooking.$remove();
            $location.path('cookings');
        }
    };

    $scope.update = function() {
        var cooking = $scope.cooking;
        if (!cooking.updated) {
            cooking.updated = [];
        }
        cooking.updated.push(new Date().getTime());

        cooking.$update(function() {
            $location.path('cookings/' + cooking._id);
        });
    };

    $scope.find = function() {
        Cooking.query(function(cookings) {
            $scope.cookings = cookings;
        });
    };

    $scope.findOne = function() {
        Cooking.get({
            cookingId: $stateParams.cookingId
        }, function(cooking) {
            $scope.cooking = cooking;
        });
    };
}]);<|endoftext|>


<commit_before>// ==UserScript==
// @name Chatception
// @namespace http://keyboardfire.com/
// @version 0.2
// @description A tiny widget that allows you to quickly chat in different Stack Exchange chatrooms from a single page.
// @grant none
// @copyright MIT
// @match *://chat.stackoverflow.com/*
// @match *://chat.stackexchange.com/*
// @match *://chat.meta.stackexchange.com/*
// ==/UserScript==

window.addEventListener('load', function() {

var MSG_LIST_WIDTH = 500,
    MSG_LIST_HEIGHT = 300,
    MSG_LIST_MAX = 100;

var roomsList = document.getElementById('my-rooms');

[].slice.apply(roomsList.getElementsByTagName('li')).forEach(initRoom);

function initRoom(room) {
    var msgWrapper = document.createElement('div');
    var msgList = document.createElement('table');
    msgList.className = 'msgList';
    msgList.style.width = '100%';
    msgWrapper.style.position = 'absolute';
    msgWrapper.style.top = '-' + (MSG_LIST_HEIGHT / 2) + 'px';
    msgWrapper.style.left = '-' + MSG_LIST_WIDTH + 'px';
    msgWrapper.style.width = MSG_LIST_WIDTH + 'px';
    msgWrapper.style.height = MSG_LIST_HEIGHT + 'px';
    msgWrapper.style.backgroundColor = 'white';
    msgWrapper.style.color = 'black';
    msgWrapper.style.border = '1px solid black';
    msgWrapper.style.zIndex = '999';
    msgWrapper.style.overflowY = 'auto';
    msgWrapper.style.display = 'none';
    msgWrapper.appendChild(msgList);
    room.appendChild(msgWrapper);

    var sendMsg = function() {
        $.post('http://' + location.host + '/chats/' + room.id.slice(5) + '/messages/new', {
            text: sendInput.value,
            fkey: fkey().fkey
        });
        sendInput.value = '';
    };

    var sendMsgWidget = document.createElement('tr');
    var sendInputTd = document.createElement('td');
    var sendInput = document.createElement('input');
    sendInput.className = 'sendInput';
    sendInput.style.width = '100%';
    sendInput.addEventListener('keyup', function(e) {
        if (e.keyCode === 13) {
            sendMsg();
        }
    });
    sendInputTd.style.width = '100%';
    sendInputTd.colSpan = 2;
    sendInputTd.appendChild(sendInput);
    sendMsgWidget.appendChild(sendInputTd);

    var sendButtonTd = document.createElement('td');
    var sendButton = document.createElement('button');
    sendButton.textContent = 'Send';
    sendButton.addEventListener('click', sendMsg);
    sendButtonTd.appendChild(sendButton);
    sendMsgWidget.appendChild(sendButtonTd);
    msgList.appendChild(sendMsgWidget);

    room.addEventListener('mouseenter', function() {
        msgWrapper.style.display = 'block';
    });
    room.addEventListener('mouseleave', function() {
        msgWrapper.style.display = 'none';
    });

    handleEvents(getEvents(room.id.slice(5)));
}

var observer = new MutationObserver(function(mutations) {
    [].slice.apply(roomsList.children).forEach(function(room) {
        if (room.children.length < 4) initRoom(room);
    });
});
observer.observe(roomsList, {
    childList: true
});

var sock = getSock();
sock.onmessage = function(e) {
    var data = JSON.parse(e.data);
    handleEvents([].concat.apply([], Object.keys(data).map(function(k) {
        return data[k]['e'];
    })).filter(function(x) { return x; }));
}

function handleEvents(events) {
    if (!events) return;

    events.forEach(function(msg) {
        var room = document.getElementById('room-' + msg['room_id']);
        var msgList = room ? room.getElementsByClassName('msgList')[0] : null;

        if (!msgList) return;

        if (msg['event_type'] === 1) {
            var msgRow = document.createElement('tr');
            msgRow.style.height = 'auto';

            var msgUser = document.createElement('td');
            msgUser.textContent = msg['user_name'];
            msgUser.style.padding = '5px';
            msgUser.style.maxWidth = Math.floor(MSG_LIST_WIDTH * 0.15) + 'px';
            msgUser.style.overflow = 'hidden';
            var msgActions = document.createElement('div');
            var actionReply = document.createElement('button');
            actionReply.textContent = 'reply';
            actionReply.addEventListener('click', function() {
                var sendInput = msgList.getElementsByClassName('sendInput')[0];
                sendInput.value = ':' + msg['message_id'] + ' ' + sendInput.value;
                sendInput.focus();
            });
            msgActions.appendChild(actionReply);
            var actionDelete = document.createElement('button');
            actionDelete.textContent = 'delete';
            actionDelete.addEventListener('click', function() {
                $.post('http://' + location.host + '/messages/' + msg['message_id'] + '/delete', {
                    fkey: fkey().fkey
                });
            });
            msgActions.appendChild(actionDelete);
            var actionEdit = document.createElement('button');
            actionEdit.textContent = 'edit';
            actionEdit.addEventListener('click', function() {
                $.post('http://' + location.host + '/messages/' + msg['message_id'], {
                    fkey: fkey().fkey,
                    text: prompt('Edit to what?', msg['content'])
                });
            });
            msgActions.appendChild(actionEdit);
            msgActions.style.display = 'none';
            msgActions.style.position = 'absolute';
            msgActions.style.zIndex = '1000';
            msgUser.addEventListener('mouseenter', function() {
                msgActions.style.display = 'block';
            });
            msgUser.addEventListener('mouseleave', function() {
                msgActions.style.display = 'none';
            });
            msgUser.appendChild(msgActions);
            msgRow.appendChild(msgUser);

            var msgContent = document.createElement('td');
            msgContent.innerHTML = msg['content'] === undefined ?
                '(removed)' : msg['content'];
            msgContent.id = 'chatception-msg' + msg['message_id'];
            msgContent.style.padding = '5px';
            msgContent.style.width = '100%';
            msgRow.appendChild(msgContent);

            var msgDate = document.createElement('td');
            var msgDateLink = document.createElement('a');
            msgDateLink.href = 'http://' + location.host + '/transcript/message/' + msg['message_id'] + '#' + msg['message_id'];
            var d = new Date(msg['time_stamp'] * 1000);
            var days = Math.floor(new Date().getTime() / (3600*24*1000)) - Math.floor(d.getTime() / (3600*24*1000));
            if (days) {
                msgDateLink.textContent = days + 'd ago';
            } else {
                msgDateLink.textContent = d.getHours() + ':' +
                    (d.getMinutes() < 10 ? '0' : '') + d.getMinutes();
            }
            msgDate.className = 'timestamp';
            msgDate.setAttribute('data-timestamp', (+d) / 1000);
            msgDate.style.padding = '5px';
            msgDate.appendChild(msgDateLink);
            msgRow.appendChild(msgDate);

            msgList.insertBefore(msgRow, msgList.firstChild.nextSibling);
        } else if (msg['event_type'] === 2) {
            var msgContent = document.getElementById('chatception-msg' + msg['message_id']);
            if (msgContent) {
                msgContent.innerHTML = msg['content'];
            }
        } else if (msg['event_type'] === 10) {
            var msgContent = document.getElementById('chatception-msg' + msg['message_id']);
            if (msgContent) {
                msgContent.innerHTML = '(removed)';
            }
        }

        while (msgList.children.length > MSG_LIST_MAX) {
            msgList.removeChild(msgList.lastChild);
        }
    });
};

function getSock() { return new WebSocket(JSON.parse($.ajax({type: 'POST', url: 'http://' + location.host + '/ws-auth', data: {roomid: CHAT.CURRENT_ROOM_ID, fkey: fkey().fkey}, async: false}).responseText)['url'] + '?l=' + JSON.parse($.ajax({type: 'POST', url: 'http://' + location.host + '/chats/' + CHAT.CURRENT_ROOM_ID + '/events', data: {fkey: fkey().fkey}, async: false}).responseText)['time']); }
function getEvents(roomid) { return JSON.parse($.ajax({type: 'POST', url: 'http://' + location.host + '/chats/' + roomid + '/events', data: {fkey: fkey().fkey, mode: 'Messages', msgCount: 10, since: 0}, async: false}).responseText)['events']; }

});
<commit_msg>chrome mutationobserver support<commit_after>// ==UserScript==
// @name Chatception
// @namespace http://keyboardfire.com/
// @version 0.2
// @description A tiny widget that allows you to quickly chat in different Stack Exchange chatrooms from a single page.
// @grant none
// @copyright MIT
// @match *://chat.stackoverflow.com/*
// @match *://chat.stackexchange.com/*
// @match *://chat.meta.stackexchange.com/*
// ==/UserScript==

window.addEventListener('load', function() {

var MSG_LIST_WIDTH = 500,
    MSG_LIST_HEIGHT = 300,
    MSG_LIST_MAX = 100;

var roomsList = document.getElementById('my-rooms');

[].slice.apply(roomsList.getElementsByTagName('li')).forEach(initRoom);

function initRoom(room) {
    var msgWrapper = document.createElement('div');
    var msgList = document.createElement('table');
    msgList.className = 'msgList';
    msgList.style.width = '100%';
    msgWrapper.style.position = 'absolute';
    msgWrapper.style.top = '-' + (MSG_LIST_HEIGHT / 2) + 'px';
    msgWrapper.style.left = '-' + MSG_LIST_WIDTH + 'px';
    msgWrapper.style.width = MSG_LIST_WIDTH + 'px';
    msgWrapper.style.height = MSG_LIST_HEIGHT + 'px';
    msgWrapper.style.backgroundColor = 'white';
    msgWrapper.style.color = 'black';
    msgWrapper.style.border = '1px solid black';
    msgWrapper.style.zIndex = '999';
    msgWrapper.style.overflowY = 'auto';
    msgWrapper.style.display = 'none';
    msgWrapper.appendChild(msgList);
    room.appendChild(msgWrapper);

    var sendMsg = function() {
        $.post('http://' + location.host + '/chats/' + room.id.slice(5) + '/messages/new', {
            text: sendInput.value,
            fkey: fkey().fkey
        });
        sendInput.value = '';
    };

    var sendMsgWidget = document.createElement('tr');
    var sendInputTd = document.createElement('td');
    var sendInput = document.createElement('input');
    sendInput.className = 'sendInput';
    sendInput.style.width = '100%';
    sendInput.addEventListener('keyup', function(e) {
        if (e.keyCode === 13) {
            sendMsg();
        }
    });
    sendInputTd.style.width = '100%';
    sendInputTd.colSpan = 2;
    sendInputTd.appendChild(sendInput);
    sendMsgWidget.appendChild(sendInputTd);

    var sendButtonTd = document.createElement('td');
    var sendButton = document.createElement('button');
    sendButton.textContent = 'Send';
    sendButton.addEventListener('click', sendMsg);
    sendButtonTd.appendChild(sendButton);
    sendMsgWidget.appendChild(sendButtonTd);
    msgList.appendChild(sendMsgWidget);

    room.addEventListener('mouseenter', function() {
        msgWrapper.style.display = 'block';
    });
    room.addEventListener('mouseleave', function() {
        msgWrapper.style.display = 'none';
    });

    handleEvents(getEvents(room.id.slice(5)));
}

MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
var observer = new MutationObserver(function(mutations) {
    [].slice.apply(roomsList.children).forEach(function(room) {
        if (room.children.length < 4) initRoom(room);
    });
});
observer.observe(roomsList, {
    childList: true
});

var sock = getSock();
sock.onmessage = function(e) {
    var data = JSON.parse(e.data);
    handleEvents([].concat.apply([], Object.keys(data).map(function(k) {
        return data[k]['e'];
    })).filter(function(x) { return x; }));
}

function handleEvents(events) {
    if (!events) return;

    events.forEach(function(msg) {
        var room = document.getElementById('room-' + msg['room_id']);
        var msgList = room ? room.getElementsByClassName('msgList')[0] : null;

        if (!msgList) return;

        if (msg['event_type'] === 1) {
            var msgRow = document.createElement('tr');
            msgRow.style.height = 'auto';

            var msgUser = document.createElement('td');
            msgUser.textContent = msg['user_name'];
            msgUser.style.padding = '5px';
            msgUser.style.maxWidth = Math.floor(MSG_LIST_WIDTH * 0.15) + 'px';
            msgUser.style.overflow = 'hidden';
            var msgActions = document.createElement('div');
            var actionReply = document.createElement('button');
            actionReply.textContent = 'reply';
            actionReply.addEventListener('click', function() {
                var sendInput = msgList.getElementsByClassName('sendInput')[0];
                sendInput.value = ':' + msg['message_id'] + ' ' + sendInput.value;
                sendInput.focus();
            });
            msgActions.appendChild(actionReply);
            var actionDelete = document.createElement('button');
            actionDelete.textContent = 'delete';
            actionDelete.addEventListener('click', function() {
                $.post('http://' + location.host + '/messages/' + msg['message_id'] + '/delete', {
                    fkey: fkey().fkey
                });
            });
            msgActions.appendChild(actionDelete);
            var actionEdit = document.createElement('button');
            actionEdit.textContent = 'edit';
            actionEdit.addEventListener('click', function() {
                $.post('http://' + location.host + '/messages/' + msg['message_id'], {
                    fkey: fkey().fkey,
                    text: prompt('Edit to what?', msg['content'])
                });
            });
            msgActions.appendChild(actionEdit);
            msgActions.style.display = 'none';
            msgActions.style.position = 'absolute';
            msgActions.style.zIndex = '1000';
            msgUser.addEventListener('mouseenter', function() {
                msgActions.style.display = 'block';
            });
            msgUser.addEventListener('mouseleave', function() {
                msgActions.style.display = 'none';
            });
            msgUser.appendChild(msgActions);
            msgRow.appendChild(msgUser);

            var msgContent = document.createElement('td');
            msgContent.innerHTML = msg['content'] === undefined ?
                '(removed)' : msg['content'];
            msgContent.id = 'chatception-msg' + msg['message_id'];
            msgContent.style.padding = '5px';
            msgContent.style.width = '100%';
            msgRow.appendChild(msgContent);

            var msgDate = document.createElement('td');
            var msgDateLink = document.createElement('a');
            msgDateLink.href = 'http://' + location.host + '/transcript/message/' + msg['message_id'] + '#' + msg['message_id'];
            var d = new Date(msg['time_stamp'] * 1000);
            var days = Math.floor(new Date().getTime() / (3600*24*1000)) - Math.floor(d.getTime() / (3600*24*1000));
            if (days) {
                msgDateLink.textContent = days + 'd ago';
            } else {
                msgDateLink.textContent = d.getHours() + ':' +
                    (d.getMinutes() < 10 ? '0' : '') + d.getMinutes();
            }
            msgDate.className = 'timestamp';
            msgDate.setAttribute('data-timestamp', (+d) / 1000);
            msgDate.style.padding = '5px';
            msgDate.appendChild(msgDateLink);
            msgRow.appendChild(msgDate);

            msgList.insertBefore(msgRow, msgList.firstChild.nextSibling);
        } else if (msg['event_type'] === 2) {
            var msgContent = document.getElementById('chatception-msg' + msg['message_id']);
            if (msgContent) {
                msgContent.innerHTML = msg['content'];
            }
        } else if (msg['event_type'] === 10) {
            var msgContent = document.getElementById('chatception-msg' + msg['message_id']);
            if (msgContent) {
                msgContent.innerHTML = '(removed)';
            }
        }

        while (msgList.children.length > MSG_LIST_MAX) {
            msgList.removeChild(msgList.lastChild);
        }
    });
};

function getSock() { return new WebSocket(JSON.parse($.ajax({type: 'POST', url: 'http://' + location.host + '/ws-auth', data: {roomid: CHAT.CURRENT_ROOM_ID, fkey: fkey().fkey}, async: false}).responseText)['url'] + '?l=' + JSON.parse($.ajax({type: 'POST', url: 'http://' + location.host + '/chats/' + CHAT.CURRENT_ROOM_ID + '/events', data: {fkey: fkey().fkey}, async: false}).responseText)['time']); }
function getEvents(roomid) { return JSON.parse($.ajax({type: 'POST', url: 'http://' + location.host + '/chats/' + roomid + '/events', data: {fkey: fkey().fkey, mode: 'Messages', msgCount: 10, since: 0}, async: false}).responseText)['events']; }

});
<|endoftext|>


<commit_before>(function(){
    app = app || angular.module(module || 'psfs', []);
    /**
     * Message Service
     * @type {*[]}
     */
    var messageService = ['$rootScope', '$log', function($rootScope, $log) {
        var config = {
            debug: true
        };
        return {
            'send': function(message, data) {
                if(config.debug) {
                    $log.debug('Event: ' + message);
                    if(!angular.isUndefined(data) && null !== data) {
                        $log.debug(data);
                    }
                }
                $rootScope.$broadcast(message, data);
            },
            $config: function($config) {
                if(angular.isObject($config)) {
                    angular.forEach($config, function(value, key) {
                        config[key] = value;
                    });
                }
            }
        };
    }];
    app.service('$msgSrv', messageService);
    var entitySrv = ['$log', function($log) {
        var entity, id;

        function getLabelField(item) {
            if (item) {
                if('__name__' in item) return '__name__';
                if('label' in item) return 'label';
                if('Label' in item) return 'Label';
                if('name' in item) return 'name';
                if('Name' in item) return 'Name';
                if('title' in item) return 'title';
                if('Title' in item) return 'Title';
            }
            return '';
        }

        function getLabel(item)
        {
            if (item) {
                var label = getLabelField(item);
                if(label in item) {
                    return item[label];
                }
            }
            return '';
        }

        function getPkField(fields) {
            var pk = null, sep = '';
            fields = fields || {};
            for(var i in fields) {
                var field = fields[i];
                if(field.pk) {
                    if(null === pk) pk = '';
                    pk += sep + field.name;
                    sep = '__|__';
                }
            }
            return pk;
        }

        function getId(item, fields)
        {
            var pk = getPkField(fields);
            if (null !== pk) {
                if (item[pk]) {
                    return item[pk];
                } else if('__pk' in item){
                    return item['__pk'];
                } else if(null !== pk.match(/__\|__/)){
                    var pks = pk.split('__|__'), complexPk = '', sep = '';
                    for(var i in pks) {
                        var _pk = pks[i];
                        if (item[_pk]) {
                            complexPk += sep + item[_pk];
                            sep = '__|__';
                        } else {
                            throw new Error('Unidentified element');
                        }
                    }
                    return complexPk;
                } else {
                    throw new Error('Unidentified element');
                }
            } else if('__pk' in item){
                return item['__pk'];
            } else {
                throw new Error('Null object!!!');
            }
        }

        function setEntity(entity) {
            this.entity = entity;
        }

        function setId(id) {
            this.id = id;
        }

        return {
            getLabel: getLabel,
            getLabelField: getLabelField,
            getId: getId,
            setEntity: setEntity,
            getPkField: getPkField,
            setId: setId
        };
    }];
    app.service('$apiSrv', entitySrv);
    /**
     * Message Service
     * @type {*[]}
     */
    var httpService = ['$rootScope', '$log', '$http', '$msgSrv',
        function($rootScope, $log, $http, $msgSrv) {
        var srvConfig = {
            psfsToken: null,
            psfsTokenUrl: null,
            userToken: null,
            debug: true,
            lang: angular.element('html').attr('lang') || 'es'
        };

        /**
         *
         * @param $method string
         * @param $url string
         * @param $data object
         * @returns object
         * @private
         */
        function __prepare($method, $url, $data) {
            var config = {
                method: $method,
                url: $url,
                headers: {
                    'Access-Control-Allow-Origin': '*',
                    'Access-Control-Allow-Methods': 'GET,POST,PUT,DELETE,OPTIONS',
                    'Access-Control-Allow-Headers': '*',
                    'Content-Type': 'application/json',
                    'X-API-SEC-TOKEN': srvConfig.psfsToken,
                    'X-API-LANG': srvConfig.lang
                }
            };
            if(srvConfig.userToken) {
                config.headers['Authorization'] = 'Bearer ' + srvConfig.userToken;
            }
            if(!angular.isUndefined($data) && angular.isObject($data)) {
                if($method === 'GET') {
                    config.params = $data;
                } else {
                    config.data = $data;
                }
            }
            if('headers' in srvConfig) {
                angular.merge(config['headers'], srvConfig['headers']);
            }
            return config;
        }

        /**
         * @param $promise $http
         * @param $method string
         * @param $url string
         * @returns {*}
         * @private
         */
        function __return($promise, $method, $url) {
            return $promise.finally(function() {
                if(srvConfig.debug) {
                    $log.debug($url + ' request finished');
                }
                $msgSrv.send('request.' + $method.toLowerCase() + '.finished');
                $msgSrv.send('request.finished');
                return true;
            });
        }

        /**
         * @param $method string
         * @param $url string
         * @param $data object
         * @returns promise
         * @private
         */
        function __call($method, $url, $data) {
            var config = __prepare($method, $url, $data);

            $msgSrv.$config({
                debug: srvConfig.debug
            });

            if(srvConfig.debug) {
                $log.debug($url + ' request started');
            }
            $msgSrv.send('request.started');
            $msgSrv.send('request.' + $method.toLowerCase() + '.started');
            return __return($http(config), $method, $url);
        }

        function __upload($url, $data) {
            var config = __prepare('POST', $url, $data);
            config.headers['Content-Type'] = undefined;
            config.transformRequest = angular.identity;

            $msgSrv.$config({
                debug: srvConfig.debug
            });

            if(srvConfig.debug) {
                $log.debug($url + ' request started');
            }
            $msgSrv.send('request.started');
            $msgSrv.send('request.upload.started');
            return __return($http(config), 'upload', $url);
        }

        /**
         * @param $method
         * @param $url
         * @param $data
         * @returns {*}
         * @private
         */
        function __download($method, $url, $data) {
            var config = __prepare($method, $url, $data);
            config.headers['Content-Type'] = 'blob';
            config.headers['Accept'] = 'blob';
            config.headers['Access-Control-Expose-Headers'] = 'Filename';
            config.responseType = "blob";
            config.transformRequest = angular.identity;
            config.transformResponse = angular.identity;

            $msgSrv.$config({
                debug: srvConfig.debug
            });

            if(srvConfig.debug) {
                $log.debug($url + ' request started');
            }
            $msgSrv.send('request.started');
            $msgSrv.send('request.download.started');

            return __return($http(config)
                .then(function(response) {
                    var headers = response.headers(),
                        fileName = headers['fileName'] || 'noname';
                    if('noname' === fileName && 'filename' in headers) {
                        fileName = headers['filename'];
                    }
                    if('noname' === fileName && 'content-disposition' in headers) {
                        fileName = headers['content-disposition'].split(/filename\=/ig).slice(-1).pop().replace(/(\"|\')/ig, '');
                    }
                    if('noname' === fileName) {
                        var cType = headers['content-type'].split('/').slice(-1).pop();
                        fileName += '.' + cType;
                    }
                    var anchor = angular.element('<a/>');
                    var blob = new Blob([response.data], { type: headers['content-type'] });                    anchor.attr({
                        href: window.URL.createObjectURL(blob),
                        target: '_blank',
                        download: fileName
                    })[0].click();
                }), 'download', $url)
                .catch(function(error) {
                    $log.error(error);
                });
        }

        return {
            $get: function(url, query) {
                return __call('GET', url, query);
            },
            $post: function(url, data) {
                return __call('POST', url, data);
            },
            $put: function(url, data) {
                return __call('PUT', url, data);
            },
            $delete: function(url) {
                return __call('DELETE', url, null);
            },
            $upload: function(url, data) {
                return __upload(url, data);
            },
            $download: function(method, url, queryData) {
                method = method || 'GET';
                var promise;
                switch(method.toUpperCase()) {
                    default:
                    case 'GET':
                        promise = __download('GET', url, queryData);
                        break;
                    case 'POST':
                        promise = __download('POST', url, queryData);
                        break;
                }
                return promise;
            },
            $config: function($config) {
                if(angular.isObject($config)) {
                    angular.forEach($config, function(value, key) {
                        if(key in srvConfig && (angular.isArray(srvConfig[key]) || angular.isObject(srvConfig[key]))) {
                            angular.merge(srvConfig[key], value);
                        } else {
                            srvConfig[key] = value;
                        }
                    });
                }
            }
        };
    }];
    app.service('$httpSrv', httpService);
})();
var $httpSrv;
function loadCallService() {
    try {
        $httpSrv = angular.element(document.body).injector().get('$httpSrv');
    } catch (err) {
        setTimeout(loadCallService, 100);
    }
}

loadCallService();<commit_msg>Fixed js error<commit_after>(function(){
    app = app || angular.module(module || 'psfs', []);
    /**
     * Message Service
     * @type {*[]}
     */
    var messageService = ['$rootScope', '$log', function($rootScope, $log) {
        var config = {
            debug: true
        };
        return {
            'send': function(message, data) {
                if(config.debug) {
                    $log.debug('Event: ' + message);
                    if(!angular.isUndefined(data) && null !== data) {
                        $log.debug(data);
                    }
                }
                $rootScope.$broadcast(message, data);
            },
            $config: function($config) {
                if(angular.isObject($config)) {
                    angular.forEach($config, function(value, key) {
                        config[key] = value;
                    });
                }
            }
        };
    }];
    app.service('$msgSrv', messageService);
    var entitySrv = ['$log', function($log) {
        var entity, id;

        function getLabelField(item) {
            if (item) {
                if('__name__' in item) return '__name__';
                if('label' in item) return 'label';
                if('Label' in item) return 'Label';
                if('name' in item) return 'name';
                if('Name' in item) return 'Name';
                if('title' in item) return 'title';
                if('Title' in item) return 'Title';
            }
            return '';
        }

        function getLabel(item)
        {
            if (item) {
                var label = getLabelField(item);
                if(label in item) {
                    return item[label];
                }
            }
            return '';
        }

        function getPkField(fields) {
            var pk = null, sep = '';
            fields = fields || {};
            for(var i in fields) {
                var field = fields[i];
                if(field.pk) {
                    if(null === pk) pk = '';
                    pk += sep + field.name;
                    sep = '__|__';
                }
            }
            return pk;
        }

        function getId(item, fields)
        {
            var pk = getPkField(fields);
            if (null !== pk) {
                if (item[pk]) {
                    return item[pk];
                } else if('__pk' in item){
                    return item['__pk'];
                } else if(null !== pk.match(/__\|__/)){
                    var pks = pk.split('__|__'), complexPk = '', sep = '';
                    for(var i in pks) {
                        var _pk = pks[i];
                        if (item[_pk]) {
                            complexPk += sep + item[_pk];
                            sep = '__|__';
                        } else {
                            throw new Error('Unidentified element');
                        }
                    }
                    return complexPk;
                } else {
                    throw new Error('Unidentified element');
                }
            } else if('__pk' in item){
                return item['__pk'];
            } else {
                throw new Error('Null object!!!');
            }
        }

        function setEntity(entity) {
            this.entity = entity;
        }

        function setId(id) {
            this.id = id;
        }

        return {
            getLabel: getLabel,
            getLabelField: getLabelField,
            getId: getId,
            setEntity: setEntity,
            getPkField: getPkField,
            setId: setId
        };
    }];
    app.service('$apiSrv', entitySrv);
    /**
     * Message Service
     * @type {*[]}
     */
    var httpService = ['$rootScope', '$log', '$http', '$msgSrv',
        function($rootScope, $log, $http, $msgSrv) {
        var srvConfig = {
            psfsToken: null,
            psfsTokenUrl: null,
            userToken: null,
            debug: true,
            lang: document.getElementsByTagName('HTML')[0].lang || 'es'
        };

        /**
         *
         * @param $method string
         * @param $url string
         * @param $data object
         * @returns object
         * @private
         */
        function __prepare($method, $url, $data) {
            var config = {
                method: $method,
                url: $url,
                headers: {
                    'Access-Control-Allow-Origin': '*',
                    'Access-Control-Allow-Methods': 'GET,POST,PUT,DELETE,OPTIONS',
                    'Access-Control-Allow-Headers': '*',
                    'Content-Type': 'application/json',
                    'X-API-SEC-TOKEN': srvConfig.psfsToken,
                    'X-API-LANG': srvConfig.lang
                }
            };
            if(srvConfig.userToken) {
                config.headers['Authorization'] = 'Bearer ' + srvConfig.userToken;
            }
            if(!angular.isUndefined($data) && angular.isObject($data)) {
                if($method === 'GET') {
                    config.params = $data;
                } else {
                    config.data = $data;
                }
            }
            if('headers' in srvConfig) {
                angular.merge(config['headers'], srvConfig['headers']);
            }
            return config;
        }

        /**
         * @param $promise $http
         * @param $method string
         * @param $url string
         * @returns {*}
         * @private
         */
        function __return($promise, $method, $url) {
            return $promise.finally(function() {
                if(srvConfig.debug) {
                    $log.debug($url + ' request finished');
                }
                $msgSrv.send('request.' + $method.toLowerCase() + '.finished');
                $msgSrv.send('request.finished');
                return true;
            });
        }

        /**
         * @param $method string
         * @param $url string
         * @param $data object
         * @returns promise
         * @private
         */
        function __call($method, $url, $data) {
            var config = __prepare($method, $url, $data);

            $msgSrv.$config({
                debug: srvConfig.debug
            });

            if(srvConfig.debug) {
                $log.debug($url + ' request started');
            }
            $msgSrv.send('request.started');
            $msgSrv.send('request.' + $method.toLowerCase() + '.started');
            return __return($http(config), $method, $url);
        }

        function __upload($url, $data) {
            var config = __prepare('POST', $url, $data);
            config.headers['Content-Type'] = undefined;
            config.transformRequest = angular.identity;

            $msgSrv.$config({
                debug: srvConfig.debug
            });

            if(srvConfig.debug) {
                $log.debug($url + ' request started');
            }
            $msgSrv.send('request.started');
            $msgSrv.send('request.upload.started');
            return __return($http(config), 'upload', $url);
        }

        /**
         * @param $method
         * @param $url
         * @param $data
         * @returns {*}
         * @private
         */
        function __download($method, $url, $data) {
            var config = __prepare($method, $url, $data);
            config.headers['Content-Type'] = 'blob';
            config.headers['Accept'] = 'blob';
            config.headers['Access-Control-Expose-Headers'] = 'Filename';
            config.responseType = "blob";
            config.transformRequest = angular.identity;
            config.transformResponse = angular.identity;

            $msgSrv.$config({
                debug: srvConfig.debug
            });

            if(srvConfig.debug) {
                $log.debug($url + ' request started');
            }
            $msgSrv.send('request.started');
            $msgSrv.send('request.download.started');

            return __return($http(config)
                .then(function(response) {
                    var headers = response.headers(),
                        fileName = headers['fileName'] || 'noname';
                    if('noname' === fileName && 'filename' in headers) {
                        fileName = headers['filename'];
                    }
                    if('noname' === fileName && 'content-disposition' in headers) {
                        fileName = headers['content-disposition'].split(/filename\=/ig).slice(-1).pop().replace(/(\"|\')/ig, '');
                    }
                    if('noname' === fileName) {
                        var cType = headers['content-type'].split('/').slice(-1).pop();
                        fileName += '.' + cType;
                    }
                    var anchor = angular.element('<a/>');
                    var blob = new Blob([response.data], { type: headers['content-type'] });                    anchor.attr({
                        href: window.URL.createObjectURL(blob),
                        target: '_blank',
                        download: fileName
                    })[0].click();
                }), 'download', $url)
                .catch(function(error) {
                    $log.error(error);
                });
        }

        return {
            $get: function(url, query) {
                return __call('GET', url, query);
            },
            $post: function(url, data) {
                return __call('POST', url, data);
            },
            $put: function(url, data) {
                return __call('PUT', url, data);
            },
            $delete: function(url) {
                return __call('DELETE', url, null);
            },
            $upload: function(url, data) {
                return __upload(url, data);
            },
            $download: function(method, url, queryData) {
                method = method || 'GET';
                var promise;
                switch(method.toUpperCase()) {
                    default:
                    case 'GET':
                        promise = __download('GET', url, queryData);
                        break;
                    case 'POST':
                        promise = __download('POST', url, queryData);
                        break;
                }
                return promise;
            },
            $config: function($config) {
                if(angular.isObject($config)) {
                    angular.forEach($config, function(value, key) {
                        if(key in srvConfig && (angular.isArray(srvConfig[key]) || angular.isObject(srvConfig[key]))) {
                            angular.merge(srvConfig[key], value);
                        } else {
                            srvConfig[key] = value;
                        }
                    });
                }
            }
        };
    }];
    app.service('$httpSrv', httpService);
})();
var $httpSrv;
function loadCallService() {
    try {
        $httpSrv = angular.element(document.body).injector().get('$httpSrv');
    } catch (err) {
        setTimeout(loadCallService, 100);
    }
}

loadCallService();<|endoftext|>


<commit_before>/**
 * param {TODO} expression for the Code that will be used for rendering
 * @constructor
 */
function Renderer(api, expression) {
    this.api = api;
    this.expression = expression;
    this.rebuild();
}

//////////////////////////////////////////////////////////////////////
// Members of the prototype objects

/**
 * List of uniforms that are required in cpg-prog
 * @type {Object}
 */
Renderer.prototype.cpguniforms;

/**
 * Source code of vertex shader
 * @type {string}
 */
Renderer.prototype.vertexShaderCode;

/**
 * Source code of fragment shader, contains code
 * @type {string}
 */
Renderer.prototype.fragmentShaderCode;

/** @type {ShaderProgram} */
Renderer.prototype.shaderProgram;

/** @type {CindyJS.pluginApi} */
Renderer.prototype.api;

/** @type {CindyJS.anyval} */
Renderer.prototype.expression;

/** @type {CanvasWrapper} */
Renderer.prototype.canvaswrapper

/** @type {Object.<TextureReader>} */
Renderer.prototype.texturereaders

Renderer.prototype.rebuild = function() {
    let cb = new CodeBuilder(this.api);
    let cpg = cb.generateColorPlotProgram(this.expression);
    this.cpguniforms = cpg.uniforms;
    this.texturereaders = cpg.texturereaders;

    this.fragmentShaderCode =
        cgl_resources["standardFragmentHeader"] + cpg.code;
    this.vertexShaderCode = cgl_resources["vshader"];
    this.shaderProgram = new ShaderProgram(gl, this.vertexShaderCode, this.fragmentShaderCode);

    /*
     *    gl.bindBuffer(gl.ARRAY_BUFFER, this.ssArrayBuffer);
      gl.bufferData(
        gl.ARRAY_BUFFER, new Float32Array([
          1,  1,  1, 1,
          -1,  1, 0, 1,
          1, -1,  1, 0,
          -1, -1, 0, 0]),
        gl.STATIC_DRAW);
      this.textureQuadProgram = new ShaderProgram(
        gl, c3d_resources.texq_vert, c3d_resources.texq_frag);
      this.textureQuadAttrib = gl.getAttribLocation(l
        this.textureQuadProgram.handle, "aPos");*/
    var posBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer);

    var vertices = new Float32Array([-1, -1, 0, 1, -1, 0, -1, 1, 0, 1, 1, 0]);

    var aPosLoc = gl.getAttribLocation(this.shaderProgram.handle, "aPos");
    gl.enableVertexAttribArray(aPosLoc);

    var aTexLoc = gl.getAttribLocation(this.shaderProgram.handle, "aTexCoord");
    gl.enableVertexAttribArray(aTexLoc);

    var texCoords = new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]);

    var texCoordOffset = vertices.byteLength;

    gl.bufferData(gl.ARRAY_BUFFER, texCoordOffset + texCoords.byteLength, gl.STATIC_DRAW);
    gl.bufferSubData(gl.ARRAY_BUFFER, 0, vertices);
    gl.bufferSubData(gl.ARRAY_BUFFER, texCoordOffset, texCoords);
    gl.vertexAttribPointer(aPosLoc, 3, gl.FLOAT, false, 0, 0);
    gl.vertexAttribPointer(aTexLoc, 2, gl.FLOAT, false, 0, texCoordOffset);
}

/**
 * @param {Array.<number>} m
 * @return {Array.<number>}
 */
function transpose3(m) {
    return [
        m[0], m[3], m[6],
        m[1], m[4], m[7],
        m[2], m[5], m[8]
    ];
};

/**
 * sets uniform transformMatrix such that it represents an affine trafo with (0,0)->a, (1,0)->b, (0,1)->c
 */
Renderer.prototype.setTransformMatrix = function(a, b, c) {
    let m = [
        b.x - a.x, c.x - a.x, a.x,
        b.y - a.y, c.y - a.y, a.y,
        0, 0, 1
    ];
    if (this.shaderProgram.uniform.hasOwnProperty('transformMatrix'))
        this.shaderProgram.uniform["transformMatrix"](transpose3(m));
}

Renderer.prototype.setUniforms = function() {
    for (let uname in this.cpguniforms) {
        let val = this.api.evaluateAndVal(this.cpguniforms[uname].expr);
        let t = this.cpguniforms[uname].type;

        if (!issubtypeof(guessTypeOfValue(val), t)) {
            console.log("type of " + uname + " changed; forcing rebuild");
            this.rebuild();
        }

        //@TODO: handle other types as well

        let setter = this.shaderProgram.uniform[uname];

        if (setter === undefined) continue;
        switch (t) {
            case type.complex:
                setter([val['value']['real'], val['value']['imag']]);
                break;
            case type.bool:
                if (val['value'])
                    setter([1]);
                else
                    setter([0]);
                break;
            case type.int:
            case type.float:
                setter([val['value']['real']]);
                break;
            case type.vec2:
            case type.vec3:
            case type.vec4:
                {
                    let n = 0;
                    if (t === type.vec2) n = 2;
                    if (t === type.vec3) n = 3;
                    if (t === type.vec4) n = 4;
                    let v = [];
                    for (let i = 0; i < n; i++) v.push(val['value'][i]['value']['real']);
                    setter(v);
                }
                break;
                //TODO: other non-quadratic matrices
            case type.mat2:
            case type.mat3:
            case type.mat4:
                {
                    let l = 0;
                    if (t === type.mat2) l = 2;
                    if (t === type.mat3) l = 3;
                    if (t === type.mat4) l = 4;
                    let m = [];
                    for (let i = 0; i < l; i++)
                        for (let j = 0; j < l; j++) m.push(val['value'][j]['value'][i]['value']['real']);
                    setter(m);
                }
                break;
            case type.vec2complex:
                setter([val['value'][0]['value']['real'], val['value'][0]['value']['imag'], val['value'][1]['value']['real'], val['value'][1]['value']['imag']]);
                break;
            case type.mat2complex:
                let rm = Array(16);
                for (let i = 0; i < 2; i++)
                    for (let j = 0; j < 2; j++) {
                        let tval = val['value'][i]['value'][j]['value'];
                        rm[4 * (2 * j) + 2 * i] = tval['real'];
                        rm[4 * (2 * j + 1) + 2 * i + 1] = tval['real'];
                        rm[4 * (2 * j + 1) + 2 * i] = -tval['imag'];
                        rm[4 * (2 * j) + 2 * i + 1] = tval['imag'];
                    }
                setter(rm);
                break;
            default:
                console.error("Don't know how to set uniform" + uname + ", which has the type " + typeToString(t) + ", to " + val);
                break;
        }
    }
    if (this.shaderProgram.uniform.hasOwnProperty('rnd_'))
        this.shaderProgram.uniform['rnd_']([Math.random()]);

};

/**
 * Activates, loads textures and sets corresponding sampler uniforms
 */
Renderer.prototype.loadTextures = function() {
    let cnt = 0;
    for (let t in this.texturereaders)
        for (let i in this.texturereaders[t]) {
            gl.activeTexture(gl.TEXTURE0 + cnt);

            let tr = this.texturereaders[t][i];
            let tname = tr.name;

            let properties = {
                interpolate: tr.modifs.hasOwnProperty("interpolate") ? tr.api.evaluateAndVal(tr.modifs['interpolate'])['value'] : true,
                mipmap: tr.modifs.hasOwnProperty("mipmap") ? tr.api.evaluateAndVal(tr.modifs['mipmap'])['value'] : false,
                repeat: tr.modifs.hasOwnProperty("repeat") ? tr.api.evaluateAndVal(tr.modifs['repeat'])['value'] : false
            };
            let cw = tr.returnCanvaswrapper(properties);

            cw.reloadIfRequired();
            cw.bindTexture();
            this.shaderProgram.uniform['_sampler' + tname]([cnt]);
            this.shaderProgram.uniform['_ratio' + tname]([cw.sizeX / cw.sizeY]);
            this.shaderProgram.uniform['_cropfact' + tname]([cw.sizeX / cw.sizeXP, cw.sizeY / cw.sizeYP]);
            this.shaderProgram.uniform['_repeat' + tname]([properties.repeat]);
            this.shaderProgram.uniform['_mipmap' + tname]([properties.mipmap]);

            cnt++;
        }
}

/**
 * runs shaderProgram on gl. Will render to texture in canvaswrapper
 * or if argument canvaswrapper is not given, then to glcanvas
 */
Renderer.prototype.render = function(a, b, sizeX, sizeY, canvaswrapper) {
    let alpha = sizeY / sizeX;
    let n = {
        x: -(b.y - a.y) * alpha,
        y: (b.x - a.x) * alpha
    };
    let c = {
        x: a.x + n.x,
        y: a.y + n.y
    };
    //let d = {x: b.x + n.x, y: b.y + n.y};

    enlargeCanvasIfRequired(sizeX, sizeY)
    if (canvaswrapper)
        gl.viewport(0, 0, sizeX, sizeY);
    else
        gl.viewport(0, glcanvas.height - sizeY, sizeX, sizeY);

    this.shaderProgram.use(gl);
    this.setTransformMatrix(a, b, c);
    this.setUniforms();
    this.loadTextures();

    if (canvaswrapper) {
        canvaswrapper.bindFramebuffer(); //render to texture stored in canvaswrapper
        canvaswrapper.generation = ++canvaswrapper.canvas.generation;
    } else
        gl.bindFramebuffer(gl.FRAMEBUFFER, null); //render to glcanvas
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
    gl.flush(); //renders stuff to canvaswrapper

    /* render on glcanvas
	gl.bindFramebuffer(gl.FRAMEBUFFER, null);
	gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
	gl.flush();
  */
}
<commit_msg>CindyGL: Minor buxfix for #414<commit_after>/**
 * param {TODO} expression for the Code that will be used for rendering
 * @constructor
 */
function Renderer(api, expression) {
    this.api = api;
    this.expression = expression;
    this.rebuild();
}

//////////////////////////////////////////////////////////////////////
// Members of the prototype objects

/**
 * List of uniforms that are required in cpg-prog
 * @type {Object}
 */
Renderer.prototype.cpguniforms;

/**
 * Source code of vertex shader
 * @type {string}
 */
Renderer.prototype.vertexShaderCode;

/**
 * Source code of fragment shader, contains code
 * @type {string}
 */
Renderer.prototype.fragmentShaderCode;

/** @type {ShaderProgram} */
Renderer.prototype.shaderProgram;

/** @type {CindyJS.pluginApi} */
Renderer.prototype.api;

/** @type {CindyJS.anyval} */
Renderer.prototype.expression;

/** @type {CanvasWrapper} */
Renderer.prototype.canvaswrapper

/** @type {Object.<TextureReader>} */
Renderer.prototype.texturereaders

Renderer.prototype.rebuild = function() {
    let cb = new CodeBuilder(this.api);
    let cpg = cb.generateColorPlotProgram(this.expression);
    this.cpguniforms = cpg.uniforms;
    this.texturereaders = cpg.texturereaders;

    this.fragmentShaderCode =
        cgl_resources["standardFragmentHeader"] + cpg.code;
    this.vertexShaderCode = cgl_resources["vshader"];
    this.shaderProgram = new ShaderProgram(gl, this.vertexShaderCode, this.fragmentShaderCode);

    /*
     *    gl.bindBuffer(gl.ARRAY_BUFFER, this.ssArrayBuffer);
      gl.bufferData(
        gl.ARRAY_BUFFER, new Float32Array([
          1,  1,  1, 1,
          -1,  1, 0, 1,
          1, -1,  1, 0,
          -1, -1, 0, 0]),
        gl.STATIC_DRAW);
      this.textureQuadProgram = new ShaderProgram(
        gl, c3d_resources.texq_vert, c3d_resources.texq_frag);
      this.textureQuadAttrib = gl.getAttribLocation(l
        this.textureQuadProgram.handle, "aPos");*/
    var posBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer);

    var vertices = new Float32Array([-1, -1, 0, 1, -1, 0, -1, 1, 0, 1, 1, 0]);

    var aPosLoc = gl.getAttribLocation(this.shaderProgram.handle, "aPos");
    gl.enableVertexAttribArray(aPosLoc);

    var aTexLoc = gl.getAttribLocation(this.shaderProgram.handle, "aTexCoord");
    gl.enableVertexAttribArray(aTexLoc);

    var texCoords = new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]);

    var texCoordOffset = vertices.byteLength;

    gl.bufferData(gl.ARRAY_BUFFER, texCoordOffset + texCoords.byteLength, gl.STATIC_DRAW);
    gl.bufferSubData(gl.ARRAY_BUFFER, 0, vertices);
    gl.bufferSubData(gl.ARRAY_BUFFER, texCoordOffset, texCoords);
    gl.vertexAttribPointer(aPosLoc, 3, gl.FLOAT, false, 0, 0);
    gl.vertexAttribPointer(aTexLoc, 2, gl.FLOAT, false, 0, texCoordOffset);
}

/**
 * @param {Array.<number>} m
 * @return {Array.<number>}
 */
function transpose3(m) {
    return [
        m[0], m[3], m[6],
        m[1], m[4], m[7],
        m[2], m[5], m[8]
    ];
};

/**
 * sets uniform transformMatrix such that it represents an affine trafo with (0,0)->a, (1,0)->b, (0,1)->c
 */
Renderer.prototype.setTransformMatrix = function(a, b, c) {
    let m = [
        b.x - a.x, c.x - a.x, a.x,
        b.y - a.y, c.y - a.y, a.y,
        0, 0, 1
    ];
    if (this.shaderProgram.uniform.hasOwnProperty('transformMatrix'))
        this.shaderProgram.uniform["transformMatrix"](transpose3(m));
}

Renderer.prototype.setUniforms = function() {
    for (let uname in this.cpguniforms) {
        let val = this.api.evaluateAndVal(this.cpguniforms[uname].expr);
        let t = this.cpguniforms[uname].type;

        if (!issubtypeof(guessTypeOfValue(val), t)) {
            console.log("Type of " + uname + " changed; forcing rebuild.");
            this.rebuild();
            this.shaderProgram.use(gl);
            this.setUniforms();
            return;
        }

        //@TODO: handle other types as well

        let setter = this.shaderProgram.uniform[uname];

        if (setter === undefined) continue;
        switch (t) {
            case type.complex:
                setter([val['value']['real'], val['value']['imag']]);
                break;
            case type.bool:
                if (val['value'])
                    setter([1]);
                else
                    setter([0]);
                break;
            case type.int:
            case type.float:
                setter([val['value']['real']]);
                break;
            case type.vec2:
            case type.vec3:
            case type.vec4:
                {
                    let n = 0;
                    if (t === type.vec2) n = 2;
                    if (t === type.vec3) n = 3;
                    if (t === type.vec4) n = 4;
                    let v = [];
                    for (let i = 0; i < n; i++) v.push(val['value'][i]['value']['real']);
                    setter(v);
                }
                break;
                //TODO: other non-quadratic matrices
            case type.mat2:
            case type.mat3:
            case type.mat4:
                {
                    let l = 0;
                    if (t === type.mat2) l = 2;
                    if (t === type.mat3) l = 3;
                    if (t === type.mat4) l = 4;
                    let m = [];
                    for (let i = 0; i < l; i++)
                        for (let j = 0; j < l; j++) m.push(val['value'][j]['value'][i]['value']['real']);
                    setter(m);
                }
                break;
            case type.vec2complex:
                setter([val['value'][0]['value']['real'], val['value'][0]['value']['imag'], val['value'][1]['value']['real'], val['value'][1]['value']['imag']]);
                break;
            case type.mat2complex:
                let rm = Array(16);
                for (let i = 0; i < 2; i++)
                    for (let j = 0; j < 2; j++) {
                        let tval = val['value'][i]['value'][j]['value'];
                        rm[4 * (2 * j) + 2 * i] = tval['real'];
                        rm[4 * (2 * j + 1) + 2 * i + 1] = tval['real'];
                        rm[4 * (2 * j + 1) + 2 * i] = -tval['imag'];
                        rm[4 * (2 * j) + 2 * i + 1] = tval['imag'];
                    }
                setter(rm);
                break;
            default:
                console.error("Don't know how to set uniform" + uname + ", which has the type " + typeToString(t) + ", to " + val);
                break;
        }
    }
    if (this.shaderProgram.uniform.hasOwnProperty('rnd_'))
        this.shaderProgram.uniform['rnd_']([Math.random()]);

};

/**
 * Activates, loads textures and sets corresponding sampler uniforms
 */
Renderer.prototype.loadTextures = function() {
    let cnt = 0;
    for (let t in this.texturereaders)
        for (let i in this.texturereaders[t]) {
            gl.activeTexture(gl.TEXTURE0 + cnt);

            let tr = this.texturereaders[t][i];
            let tname = tr.name;

            let properties = {
                interpolate: tr.modifs.hasOwnProperty("interpolate") ? tr.api.evaluateAndVal(tr.modifs['interpolate'])['value'] : true,
                mipmap: tr.modifs.hasOwnProperty("mipmap") ? tr.api.evaluateAndVal(tr.modifs['mipmap'])['value'] : false,
                repeat: tr.modifs.hasOwnProperty("repeat") ? tr.api.evaluateAndVal(tr.modifs['repeat'])['value'] : false
            };
            let cw = tr.returnCanvaswrapper(properties);

            cw.reloadIfRequired();
            cw.bindTexture();
            this.shaderProgram.uniform['_sampler' + tname]([cnt]);
            this.shaderProgram.uniform['_ratio' + tname]([cw.sizeX / cw.sizeY]);
            this.shaderProgram.uniform['_cropfact' + tname]([cw.sizeX / cw.sizeXP, cw.sizeY / cw.sizeYP]);
            this.shaderProgram.uniform['_repeat' + tname]([properties.repeat]);
            this.shaderProgram.uniform['_mipmap' + tname]([properties.mipmap]);

            cnt++;
        }
}

/**
 * runs shaderProgram on gl. Will render to texture in canvaswrapper
 * or if argument canvaswrapper is not given, then to glcanvas
 */
Renderer.prototype.render = function(a, b, sizeX, sizeY, canvaswrapper) {
    let alpha = sizeY / sizeX;
    let n = {
        x: -(b.y - a.y) * alpha,
        y: (b.x - a.x) * alpha
    };
    let c = {
        x: a.x + n.x,
        y: a.y + n.y
    };
    //let d = {x: b.x + n.x, y: b.y + n.y};

    enlargeCanvasIfRequired(sizeX, sizeY)
    if (canvaswrapper)
        gl.viewport(0, 0, sizeX, sizeY);
    else
        gl.viewport(0, glcanvas.height - sizeY, sizeX, sizeY);

    this.shaderProgram.use(gl);
    this.setUniforms();
    this.setTransformMatrix(a, b, c);
    this.loadTextures();

    if (canvaswrapper) {
        canvaswrapper.bindFramebuffer(); //render to texture stored in canvaswrapper
        canvaswrapper.generation = ++canvaswrapper.canvas.generation;
    } else
        gl.bindFramebuffer(gl.FRAMEBUFFER, null); //render to glcanvas
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
    gl.flush(); //renders stuff to canvaswrapper

    /* render on glcanvas
	gl.bindFramebuffer(gl.FRAMEBUFFER, null);
	gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
	gl.flush();
  */
}
<|endoftext|>


<commit_before><commit_msg>Moved type extraction logic to a seperate function<commit_after><|endoftext|>