<commit_before>var PROMPTER = function(options){
	//if no options, great we'll do it all by ourself.
	var options = options || {};
		options.text = options.text || {};
		options.caret = options.caret || {};
		options.keys = options.keys || {
			//setup the default keys.
			// x = 88, y = 89, 32 = space, 38 = up
			88: "flipHorizontal",
			89: "flipVertical",
			32: "stopStart",
			39: "getScript",
			85: "decreaseFontSize", //not yet implemented
			73: "increaseFontSize", //not yet implemented
			187: 'increaseSpeed',
			189: 'decreaseSpeed',
			27: 'resetScript',
			//development
			83: 'scriptify',
		};

		options.script = options.script || {};

	//first, what text are we showing the user?
	var script = scriptify("Lorem <<ipsum>> dolor {sit} amet, [*** consectetur adipiscing elit. ***] Sed augue sem,"+
	"porta id pharetra sit amet, dignissim egestas orci. Duis iaculis luctus"+
	"vehicula. Fusce orci sapien, pharetra ac molestie eu, tincidunt sed ipsum. "+
	"Integer sit amet dolor eu est viverra ultrices. Suspendisse potenti. "+
	"Nullam semper neque quis odio laoreet consectetur egestas eros pretium. "+
	"Praesent accumsan dictum pretium. Morbi tristique nulla sit amet nisl "+
	"lacinia et cursus neque molestie. ");

	//orietation is for tablet devices.
	var orientation = options.orientation || 90;

	//default width & height, get monkeyed with when orientation changes.
	var width = options.width || 900;
	var height = options.height || 650;

	//initial text settings, can be changed anytime. classes are base, information, notice, important
	var text =  {base:{},information:{},notice:{},important:{}};
		text.base.x = 5;
		text.base.height = 44;
		text.base.font = "bold "+ text.base.height +"px sans-serif";
		text.base.baseline = options.text.baseline || "top";
		text.base.style = "#fff";
		text.base.background = "#000";

		text.information.x = 5;
		text.information.height = 44;
		text.information.font = "bold "+ text.information.height +"px sans-serif";
		text.information.baseline = options.text.baseline || "top";
		text.information.style = "yellow";
		text.information.background = "#000";

		text.notice.x = 5;
		text.notice.height = 44;
		text.notice.font = "bold "+ text.notice.height +"px sans-serif";
		text.notice.baseline = options.text.baseline || "top";
		text.notice.style = "pink";
		text.notice.background = "#000";

		text.important.x = 5;
		text.important.height = 44;
		text.important.font = "bold "+ text.important.height +"px sans-serif";
		text.important.baseline = options.text.baseline || "top";
		text.important.style = "#000";
		text.important.background = "#fff";

	//caret/guidline setup.
	var caret = options.caret || {};
		caret.color = "yellow";
		caret.x = 10;
		caret.y = 200;
		caret.height = 40;
		caret.width = 40;
		caret.visible = options.caret.visible || true;

	//key setup
	var keys = options.keys;

	//if there was a container element specified, use it, otherwise make our own
	var containerElement = options.container || function(){
		var c = document.createElement('div');
		c.setAttribute('id', 'prompterContainer');
		document.body.appendChild(c);
		return c;
	}();

	//motion stuff
	var paused = true;

	//we need to know if our overlay or script setup has changed and needs a redraw (ie, its stale)
	var overlayStale = true;
	var scriptStale = true;

	//we're going to use two canvases, one for the script text, and one for the overlay.

	// script canvas element stuff.
	var scriptCanvas = options.scriptCanvas || function(){
		var c = document.createElement('canvas');
		c.setAttribute('id', 'scriptCanvas');
		c.setAttribute('class', 'scriptCan');
		c.setAttribute('height', height);
		c.setAttribute('width', width);
		containerElement.appendChild(c);
		return c;
	}();

	var scriptContext = scriptCanvas.getContext('2d');

	// overlay canvas element stuff.
	var overlayCanvas = options.overlayCanvas || function(){
		var c = document.createElement('canvas');
		c.setAttribute('id', 'overlayCanvas');
		c.setAttribute('class', 'overlayCan');
		c.setAttribute('height', height);
		c.setAttribute('width', width);
		containerElement.appendChild(c);
		return c;
	}();

	var overlayContext = overlayCanvas.getContext('2d');

	//drawing/refreshing stuff here

	//position of top of text while scrolling
	var scrollPosY = 0;
	var scrollSpeed = -1;

	//draw is where the magic happens.
	var draw = function(){

		if(overlayStale == true){
			//clean the slate if something has changed.
			overlayContext.clearRect(0,0,width, height);

			//if we're using a caret, draw it.
			if(caret.visible === true) {
				drawCaret();
			}

			//we've redrawn/refigured the overlay, so its no longer stale
			overlayStale = false;
		}

		if(scriptStale === true){
			scriptText = getLines(width - caret.width - 10, script);
			scriptStale = false;
		}

		//clear the canvas
		scriptContext.clearRect(0,0,width, height);

		var lineOffsetY = 0;

		for(var line in scriptText){
			var scrollY =  scrollPosY + lineOffsetY;
			var currentX = text.base.x + caret.width;

			//only show lines that are on the screen
			if(scrollY > 0 - text.base.height){

				for(var word in scriptText[line]){

					//setup text:

					scriptContext.font = text[scriptText[line][word].class].font;
					scriptContext.fillStyle = text[scriptText[line][word].class].style;
					scriptContext.textBaseline = text[scriptText[line][word].class].baseline;

					/*
					 * need to implement BG coloring.
					 */
					scriptContext.fillText(scriptText[line][word].word, currentX, scrollY);
					currentX+=scriptText[line][word].width;
				}
				//scriptContext.fillText(scriptText[line], text.x + caret.width, scrollY);

			}
			if(scrollY > height){
				continue;
			}
			lineOffsetY += text.base.height;

		}

		//set the starting position for the next frame
		if(!paused){
			scrollPosY += scrollSpeed;
		}

	};

	//all of our bindable control functions:
	var controls = {
		flipHorizontal: function(){
			overlayContext.scale(-1,1);
			overlayContext.translate(-overlayCanvas.width,0);
			overlayStale = true;

			scriptContext.scale(-1,1);
			scriptContext.translate(-scriptCanvas.width,0);
		},
		flipVertical: function(){
			overlayContext.scale(1,-1);
			overlayContext.translate(0,-overlayCanvas.height);
			overlayStale = true;

			scriptContext.scale(1,-1);
		   	scriptContext.translate(0,-scriptCanvas.height);

		},
		stopStart: function(){
			paused = !paused;
		},
		getScript: function(){
			getRemoteScript(options.script.url, true);
		},
		increaseSpeed: function(){
			scrollSpeed -= 1;
		},
		decreaseSpeed: function(){
			scrollSpeed += 1;
		},
		resetScript: function(){
			scrollPosY = 0;
			paused = true;
			scrollSpeed = -1;
		},
		scriptify: function(){
			console.log(scriptify(script));

		},
	}

	//utility stuff here
	function getXMLHttp() {
	  var xmlHttp;

	  try {
	    //good browsers
	    xmlHttp = new XMLHttpRequest();
	  }
	  catch(e){
	    //e'rthing else
	    try {
	      xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
	    }
	    catch(e){
	      try{
	        xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
	      }
	      catch(e){
	        alert("Your browser does not support AJAX!");
	        return false;
	      }
	    }
	  }
	  return xmlHttp;
	}

	function drawCaret(){
		//draw a caret
		overlayContext.fillStyle = caret.color;
		overlayContext.beginPath();
		overlayContext.moveTo(caret.x, caret.y - (caret.height/2));
		overlayContext.lineTo(caret.x + caret.width, caret.y);
		overlayContext.lineTo(caret.x, caret.y + (caret.height/2));
		overlayContext.fill();
	}

	function getLines(width, script){

		//seperate obj to keep track of lines.
		var lines = {};

		//seomthing to keep track of line width
		var lineWidth = 0;

		//counter
		var i = 0;

		for(word in script){
			lines[i] = lines[i] || {};
			lines[i][word] = script[word];

			scriptContext.font = text[script[word].class].font || text.base.font;
			scriptContext.fillStyle = text[script[word].class].style;
			scriptContext.textBaseline = text[script[word].class].baseline;

			lines[i][word].width = scriptContext.measureText(script[word].word).width
			lineWidth+= lines[i][word].width;
			//if we are close to the edge, newline.
			if(lineWidth >= width - 20){
				i++;
				lineWidth = 0;
			}
		}
		console.log(lines);
		return lines;
	}

	/*
	 * convert the script string to an object that we can more easily process into a preetty script.
	 */
	function scriptify(scriptString){

		var script = {};

		//split the string on spaces
		var tempArray = scriptString.split(/( |<<|>>|\[|\]|\{|\})/im);

		//set the default world class.
		var wordClass = "base";

		for(var i = 0 ; i < tempArray.length; i++){
			/*determine the script class
			* << starts INFO >>: Yellow Text
			*
			* {is NOTICE} : Invert, no Line break
			*
			* [IMPORTANT] : Implies Invert & Line Break
			*
			*/

			//check for an open << tag
			if(tempArray[i].search(/<{2}/im) >= 0){
 				wordClass = "information";
			}

			//check for an open { tag
			if(tempArray[i].search(/\{/img) >= 0){
 				wordClass = "notice";
			}

			//check for an open [ tag
			if(tempArray[i].search(/\[/img) >= 0){
 				wordClass = "important";
			}

			if(tempArray[i] != ""){
				script[i] = {
					'word': "" + tempArray[i],
					'class': wordClass,
				}
			}

			//if we recieve an end tag, go back to base class
			if(tempArray[i].search(/\}|>>|\]/) >= 0){
 				wordClass = "base";
			}

		}

		//return the processed object
		return script;
	}

	function setFontSize(size){
		text.height = size || text.height;
		text.font = "bold "+ text.height +"px sans-serif";
	}

	//remote text fetching, set use to true or set with setText
	function getRemoteScript(url, use){
		var xmlHttp = getXMLHttp();

		xmlHttp.onreadystatechange = function(){
		  if(xmlHttp.readyState == 4) {
		    if(use === true){
		    	script = xmlHttp.responseText;
		    }
		    else{
   			    return xmlHttp.responseText;
		    }
		  }
		}

		xmlHttp.open("GET", url, true);
		xmlHttp.send(null);
	}

	//view stuff here

	//key event stuff here
	//the commands we'll use. none by default.

	var handleKeyPress = function(e){
		e = e || window.event;
		key = e.keyCode;
		if(typeof controls[keys[key]] === 'function'){
			controls[keys[key]]();
		}
		//for dev purposes, tell us the keycodes.
		console.log(e.keyCode);
	};

	//initialization code.
	function init(){
		document.onkeydown = handleKeyPress;

		window.setInterval(draw, 30);
	};

	init();

	return this;
	/*return {
		init: init,
		bindKey: bindKey,
		flipHorizontal: flipHorizontal,
		flipVertical: flipVertical,
		stopStart: stopStart,
		caret: caret,
		draw: draw,
		increaseFontSize: function(){setFontSize(text.height + 1); scriptStale = true;},
		decreaseFontSize: function(){setFontSize(text.height -1); scriptStale = true;},
		setText: function(text){script = text;},
		getRemoteScript: getRemoteScript,
	};*/

}
<commit_msg>working again<commit_after>var PROMPTER = function(options){
	//if no options, great we'll do it all by ourself.
	var options = options || {};
		options.text = options.text || {};
		options.caret = options.caret || {};
		options.keys = options.keys || {
			//setup the default keys.
			// x = 88, y = 89, 32 = space, 38 = up
			88: "flipHorizontal",
			89: "flipVertical",
			32: "stopStart",
			39: "getScript",
			85: "decreaseFontSize", //not yet implemented
			73: "increaseFontSize", //not yet implemented
			187: 'increaseSpeed',
			189: 'decreaseSpeed',
			27: 'resetScript',
			//development
			83: 'scriptify',
		};

		options.script = options.script || {};

		//first, what text are we showing the user?
		var script = scriptify("Lorem <<ipsum>> dolor {sit} amet, [*** consectetur adipiscing elit. ***] Sed augue sem,"+
		"porta id pharetra sit amet, dignissim egestas orci. Duis iaculis luctus"+
		"vehicula. Fusce orci sapien, pharetra ac molestie eu, tincidunt sed ipsum. "+
		"Integer sit amet dolor eu est viverra ultrices. Suspendisse potenti. "+
		"Nullam semper neque quis odio laoreet consectetur egestas eros pretium. "+
		"Praesent accumsan dictum pretium. Morbi tristique nulla sit amet nisl "+
		"lacinia et cursus neque molestie. ");

	//orietation is for tablet devices.
	var orientation = options.orientation || 90;

	//default width & height, get monkeyed with when orientation changes.
	var width = options.width || 900;
	var height = options.height || 650;

	//initial text settings, can be changed anytime. classes are base, information, notice, important
	var text =  {base:{},information:{},notice:{},important:{}};
		text.base.x = 5;
		text.base.height = 64;
		text.base.font = "bold "+ text.base.height +"px sans-serif";
		text.base.baseline = options.text.baseline || "top";
		text.base.style = "#fff";
		text.base.background = "#000";

		text.information.x = 5;
		text.information.height = 64;
		text.information.font = "bold "+ text.information.height +"px sans-serif";
		text.information.baseline = options.text.baseline || "top";
		text.information.style = "yellow";
		text.information.background = "#000";

		text.notice.x = 5;
		text.notice.height = 64;
		text.notice.font = "bold "+ text.notice.height +"px sans-serif";
		text.notice.baseline = options.text.baseline || "top";
		text.notice.style = "pink";
		text.notice.background = "#000";

		text.important.x = 5;
		text.important.height = 64;
		text.important.font = "bold "+ text.important.height +"px sans-serif";
		text.important.baseline = options.text.baseline || "top";
		text.important.style = "#000";
		text.important.background = "#fff";

	//caret/guidline setup.
	var caret = options.caret || {};
		caret.color = "yellow";
		caret.x = 10;
		caret.y = 200;
		caret.height = 40;
		caret.width = 40;
		caret.visible = options.caret.visible || true;

	//key setup
	var keys = options.keys;

	//if there was a container element specified, use it, otherwise make our own
	var containerElement = options.container || function(){
		var c = document.createElement('div');
		c.setAttribute('id', 'prompterContainer');
		document.body.appendChild(c);
		return c;
	}();

	//motion stuff
	var paused = true;

	//we need to know if our overlay or script setup has changed and needs a redraw (ie, its stale)
	var overlayStale = true;
	var scriptStale = true;

	//we're going to use two canvases, one for the script text, and one for the overlay.

	// script canvas element stuff.
	var scriptCanvas = options.scriptCanvas || function(){
		var c = document.createElement('canvas');
		c.setAttribute('id', 'scriptCanvas');
		c.setAttribute('class', 'scriptCan');
		c.setAttribute('height', height);
		c.setAttribute('width', width);
		containerElement.appendChild(c);
		return c;
	}();

	var scriptContext = scriptCanvas.getContext('2d');

	// overlay canvas element stuff.
	var overlayCanvas = options.overlayCanvas || function(){
		var c = document.createElement('canvas');
		c.setAttribute('id', 'overlayCanvas');
		c.setAttribute('class', 'overlayCan');
		c.setAttribute('height', height);
		c.setAttribute('width', width);
		containerElement.appendChild(c);
		return c;
	}();

	var overlayContext = overlayCanvas.getContext('2d');

	//drawing/refreshing stuff here

	//position of top of text while scrolling
	var scrollPosY = 0;
	var scrollSpeed = -1;

	//draw is where the magic happens.
	var draw = function(){

		if(overlayStale == true){
			//clean the slate if something has changed.
			overlayContext.clearRect(0,0,width, height);

			//if we're using a caret, draw it.
			if(caret.visible === true) {
				drawCaret();
			}

			//we've redrawn/refigured the overlay, so its no longer stale
			overlayStale = false;
		}

		if(scriptStale === true){
			scriptText = getLines(width - caret.width - 10, script);
			scriptStale = false;
		}

		//clear the canvas
		scriptContext.clearRect(0,0,width, height);

		var lineOffsetY = 0;

		for(var line in scriptText){
			var scrollY =  scrollPosY + lineOffsetY;
			var currentX = text.base.x + caret.width;

			//only show lines that are on the screen
			if(scrollY > 0 - (text.base.height * 1.25) && scrollY < height + text.base.height){

				for(var word in scriptText[line]){
					//if the text has a background, draw it:
					scriptContext.fillStyle = text[scriptText[line][word].class].background;
					scriptContext.fillRect(currentX, scrollY + (text[scriptText[line][word].class].height*0.1), scriptText[line][word].width,(text[scriptText[line][word].class].height*1.1));

					//setup text:
					scriptContext.font = text[scriptText[line][word].class].font;
					scriptContext.fillStyle = text[scriptText[line][word].class].style;
					scriptContext.textBaseline = text[scriptText[line][word].class].baseline;

					//draw the text
					scriptContext.fillText(scriptText[line][word].word, currentX, scrollY);
					currentX+=scriptText[line][word].width;
				}
				//scriptContext.fillText(scriptText[line], text.x + caret.width, scrollY);

			}
			if(scrollY > height){
				continue;
			}
			lineOffsetY += (text.base.height*1.1);

		}

		//set the starting position for the next frame
		if(!paused){
			scrollPosY += scrollSpeed;
		}

	};

	//all of our bindable control functions:
	var controls = {
		flipHorizontal: function(){
			overlayContext.scale(-1,1);
			overlayContext.translate(-overlayCanvas.width,0);
			overlayStale = true;

			scriptContext.scale(-1,1);
			scriptContext.translate(-scriptCanvas.width,0);
		},
		flipVertical: function(){
			overlayContext.scale(1,-1);
			overlayContext.translate(0,-overlayCanvas.height);
			overlayStale = true;

			scriptContext.scale(1,-1);
		   	scriptContext.translate(0,-scriptCanvas.height);

		},
		stopStart: function(){
			paused = !paused;
		},
		getScript: function(){
			var temp = getRemoteScript(options.script.url, true);
			console.log(temp);
			script = scriptify(temp);
		},
		increaseSpeed: function(){
			scrollSpeed -= 1;
		},
		decreaseSpeed: function(){
			scrollSpeed += 1;
		},
		resetScript: function(){
			scrollPosY = 0;
			paused = true;
			scrollSpeed = -1;
		},

	}

	//utility stuff here
	function getXMLHttp() {
	  var xmlHttp;

	  try {
	    //good browsers
	    xmlHttp = new XMLHttpRequest();
	  }
	  catch(e){
	    //e'rthing else
	    try {
	      xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
	    }
	    catch(e){
	      try{
	        xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
	      }
	      catch(e){
	        alert("Your browser does not support AJAX!");
	        return false;
	      }
	    }
	  }
	  return xmlHttp;
	}

	function drawCaret(){
		//draw a caret
		overlayContext.fillStyle = caret.color;
		overlayContext.beginPath();
		overlayContext.moveTo(caret.x, caret.y - (caret.height/2));
		overlayContext.lineTo(caret.x + caret.width, caret.y);
		overlayContext.lineTo(caret.x, caret.y + (caret.height/2));
		overlayContext.fill();
	}

	function getLines(width, script){
		//seperate obj to keep track of lines.
		var lines = {};

		//seomthing to keep track of line width
		var lineWidth = 0;

		//counters
		var i = 0;
		var lastClass = "base";

		for(word in script){
			//we need a way to start a new line
			var carriageReturn = function(){
				i++;
				lineWidth = 0;
			}

			//what kind of text are we working with here?
			var wordClass = script[word].class;

			//if the class changes to from important to something else or vice versa, newline
			if(lastClass != "important" && wordClass === 'important'){
				carriageReturn();
			}
			if(lastClass === "important" && wordClass != "important"){
				carriageReturn();
			}

			lines[i] = lines[i] || {};
			lines[i][word] = script[word];

			//we've got to measeure the text with the appropriate font.
			scriptContext.font = text[wordClass].font || text.base.font;
			scriptContext.fillStyle = text[wordClass].style;
			scriptContext.textBaseline = text[wordClass].baseline;

			//measure and store width
			lines[i][word].width = scriptContext.measureText(script[word].word).width
			lineWidth+= lines[i][word].width;

			//if we are close to the edge, newline.
			if(lineWidth >= width - (0.25*width)){
				carriageReturn();
			}

			lastClass = wordClass;
		}
		console.log(lines);
		return lines;
	}

	/*
	 * convert the script string to an object that we can more easily process into a preetty script.
	 */
	function scriptify(scriptString){

		scriptString = scriptString || "canvas prompter ... http://www.github.com/jakecarpenter";

		var script = {};

		//split the string on spaces
		var tempArray = scriptString.split(/( |<<|>>|\[|\]|\{|\})/im);

		//set the default world class.
		var wordClass = "base";

		for(var i = 0 ; i < tempArray.length; i++){
			/*determine the script class
			* << starts INFO >>: Yellow Text
			*
			* {is NOTICE} : Invert, no Line break
			*
			* [IMPORTANT] : Implies Invert & Line Break
			*
			*/

			//check for an open << tag
			if(tempArray[i].search(/<{2}/im) >= 0){
 				wordClass = "information";
			}

			//check for an open { tag
			if(tempArray[i].search(/\{/img) >= 0){
 				wordClass = "notice";
			}

			//check for an open [ tag
			if(tempArray[i].search(/\[/img) >= 0){
 				wordClass = "important";
			}

			//add the class and word to the line
			if(tempArray[i] != ""){
				script[i] = {
					'word': "" + tempArray[i],
					'class': wordClass,
				}
			}

			//if we recieve an end tag, go back to base class
			if(tempArray[i].search(/\}|>>|\]/) >= 0){
 				wordClass = "base";
			}

		}

		//return the processed object
		return script;
	}

	function setFontSize(size){
		text.height = size || text.height;
		text.font = "bold "+ text.height +"px sans-serif";
	}

	//remote text fetching, set use to true or set with setText
	function getRemoteScript(url, use){
		var xmlHttp = getXMLHttp();

		xmlHttp.onreadystatechange = function(){
		  if(xmlHttp.readyState == 4) {
		    if(use === true){
		    	script = xmlHttp.responseText;
		    }
		    else{
   			    return xmlHttp.responseText;
		    }
		  }
		}

		xmlHttp.open("GET", url, true);
		xmlHttp.send(null);

		//since we are getting a new script, we need to tell the render the old one is stale.
		scriptStale = true;
	}

	//view stuff here

	//key event stuff here
	//the commands we'll use. none by default.

	var handleKeyPress = function(e){
		e = e || window.event;
		key = e.keyCode;
		if(typeof controls[keys[key]] === 'function'){
			controls[keys[key]]();
		}
		//for dev purposes, tell us the keycodes.
		console.log(e.keyCode);
	};

	//initialization code.
	function init(){
		document.onkeydown = handleKeyPress;

		window.setInterval(draw, 30);
	};

	init();

	return this;
	/*return {
		init: init,
		bindKey: bindKey,
		flipHorizontal: flipHorizontal,
		flipVertical: flipVertical,
		stopStart: stopStart,
		caret: caret,
		draw: draw,
		increaseFontSize: function(){setFontSize(text.height + 1); scriptStale = true;},
		decreaseFontSize: function(){setFontSize(text.height -1); scriptStale = true;},
		setText: function(text){script = text;},
		getRemoteScript: getRemoteScript,
	};*/

}
<|endoftext|>


<commit_before>import React from 'react'
import cx from 'classnames'
import { indexOf, isEmpty } from 'lodash'
import { getKeyCode, keyMap } from '../util/textInput'
var { array, func, object } = React.PropTypes

export default class KeyControlledList extends React.Component {

  static propTypes = {
    onChange: func.isRequired,
    items: array,
    selected: object
  }

  constructor (props) {
    super(props)
    let { items, selected } = props
    let index = indexOf(items, selected)
    this.state = {selectedIndex: index !== -1 ? index : 0}
  }

  componentWillReceiveProps (nextProps) {
    var max = nextProps.items.length - 1
    if (this.state.selectedIndex > max) {
      this.setState({selectedIndex: max})
    }
  }

  changeSelection = delta => {
    if (isEmpty(this.props.items)) return

    var i = this.state.selectedIndex
    var length = this.props.items.length

    i += delta
    if (i < 0) i += length
    i = i % length

    this.setState({selectedIndex: i})
  }

  // this method is called from other components
  handleKeys = event => {
    switch (getKeyCode(event)) {
      case keyMap.UP:
        this.changeSelection(-1)
        return true
      case keyMap.DOWN:
        this.changeSelection(1)
        return true
      case keyMap.TAB:
      case keyMap.ENTER:
        if (!isEmpty(this.props.items)) {
          var choice = this.props.items[this.state.selectedIndex]
          this.change(choice, event)
        }
        return true
    }
  }

  change = (choice, event) => {
    event.preventDefault()
    this.props.onChange(choice, event)
  }

  // FIXME use more standard props e.g. {label, value} instead of {id, name}, or
  // provide an API for configuring them
  render () {
    let { selectedIndex } = this.state
    let { items, onChange, ...props } = this.props
    return

      {items.map((c, i) =>

           this.change(c, event)}>{c.name}

)}

  }
}

export class GenericKeyControlledList extends React.Component {

  static propTypes = {
    onChange: func,
    children: array,
    selected: object
  }

  constructor (props) {
    super(props)
    let { children, selected } = props
    let index = indexOf(children, selected)
    this.state = {selectedIndex: index !== -1 ? index : 0}
  }

  componentWillReceiveProps (nextProps) {
    var max = nextProps.children.length - 1
    if (this.state.selectedIndex > max) {
      this.setState({selectedIndex: max})
    }
  }

  changeSelection = delta => {
    if (isEmpty(this.props.children)) return

    var i = this.state.selectedIndex
    var length = this.props.children.length

    i += delta
    if (i < 0) i += length
    i = i % length

    this.setState({selectedIndex: i})
  }

  // this method is called from other components
  handleKeys = event => {
    switch (getKeyCode(event)) {
      case keyMap.UP:
        this.changeSelection(-1)
        return true
      case keyMap.DOWN:
        this.changeSelection(1)
        return true
      case keyMap.TAB:
      case keyMap.ENTER:
        if (!isEmpty(this.props.children)) {
          var choice = this.props.children[this.state.selectedIndex]
          this.change(choice, event)
        }
        return true
    }
  }

  change = (choice, event) => {
    event.preventDefault()
    this.props.onChange(choice, event)
  }

  // FIXME use more standard props e.g. {label, value} instead of {id, name}, or
  // provide an API for configuring them
  render () {
    let { selectedIndex } = this.state
    let { children, onChange, ...props } = this.props

    const childrenSelected = children.map((child, i) => {
      if (selectedIndex !== i) return child
      return {
        ...child, props: {
          ...child.props,
          className: child.props.className + ' active'
        }
      }
    })

    return

      {childrenSelected}

  }
}
<commit_msg>make KeyControlledList depend on GenericKeyControlledList<commit_after>import React from 'react'
import { indexOf, isEmpty } from 'lodash'
import { find } from 'lodash/fp'
import { getKeyCode, keyMap } from '../util/textInput'
var { array, func, object } = React.PropTypes

export default class KeyControlledList extends React.Component {

  static propTypes = {
    onChange: func.isRequired,
    items: array,
    selected: object
  }

  // this method is called from other components
  handleKeys = event => {
    this.refs.gkcl.handleKeys(event)
  }

  change = (choice, event) => {
    event.preventDefault()
    this.props.onChange(choice, event)
  }

  onChangeExtractingItem = (choice, event) => {
    const item = find(i => i.id === choice.key, this.props.items)
    this.change(item, event)
  }

  // FIXME use more standard props e.g. {label, value} instead of {id, name}, or
  // provide an API for configuring them
  render () {
    let { items, onChange, ...props } = this.props
    return <GenericKeyControlledList ref='gkcl'
      onChange={this.onChangeExtractingItem} {...props}>
      {items.map((c, i) =>

           this.change(c, event)}>{c.name}

)}
    </GenericKeyControlledList>
  }
}

export class GenericKeyControlledList extends React.Component {

  static propTypes = {
    onChange: func,
    children: array,
    selected: object
  }

  constructor (props) {
    super(props)
    let { children, selected } = props
    let index = indexOf(children, selected)
    this.state = {selectedIndex: index !== -1 ? index : 0}
  }

  componentWillReceiveProps (nextProps) {
    var max = nextProps.children.length - 1
    if (this.state.selectedIndex > max) {
      this.setState({selectedIndex: max})
    }
  }

  changeSelection = delta => {
    if (isEmpty(this.props.children)) return

    var i = this.state.selectedIndex
    var length = this.props.children.length

    i += delta
    if (i < 0) i += length
    i = i % length

    this.setState({selectedIndex: i})
  }

  // this method is called from other components
  handleKeys = event => {
    switch (getKeyCode(event)) {
      case keyMap.UP:
        this.changeSelection(-1)
        return true
      case keyMap.DOWN:
        this.changeSelection(1)
        return true
      case keyMap.TAB:
      case keyMap.ENTER:
        if (!isEmpty(this.props.children)) {
          var choice = this.props.children[this.state.selectedIndex]
          this.change(choice, event)
        }
        return true
    }
  }

  change = (choice, event) => {
    event.preventDefault()
    this.props.onChange(choice, event)
  }

  // FIXME use more standard props e.g. {label, value} instead of {id, name}, or
  // provide an API for configuring them
  render () {
    let { selectedIndex } = this.state
    let { children, onChange, ...props } = this.props

    const childrenSelected = children.map((child, i) => {
      if (selectedIndex !== i) return child
      return {
        ...child, props: {
          ...child.props,
          className: child.props.className + ' active'
        }
      }
    })

    return

      {childrenSelected}

  }
}
<|endoftext|>


<commit_before>import {
  compose,
  createStore,
  applyMiddleware,
  combineReducers,
} from 'redux';
import { routerReducer } from 'react-router-redux'
import thunkMiddleware from 'redux-thunk';
import createLogger from 'redux-logger';
import reducers from '../reducers';

const loggerMiddleware = createLogger();
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

export default function configureStore (preloadedState) {
  const store = compose (
    composeEnhancers(
      applyMiddleware(thunkMiddleware, loggerMiddleware),
    ),
  )(createStore)(combineReducers({reducers, routing : routerReducer}));

  return store;
}<commit_msg>add i18n wiring on redux store<commit_after>import {
  compose,
  createStore,
  applyMiddleware,
  combineReducers,
} from 'redux';
import {
  loadTranslations,
  setLocale,
  syncTranslationWithStore,
  i18nReducer } from 'react-redux-i18n';

import { routerReducer } from 'react-router-redux';
import thunkMiddleware from 'redux-thunk';
import createLogger from 'redux-logger';
import reducers from '../reducers';
import translations from '../i18n';

const loggerMiddleware = createLogger();
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

export default function configureStore (preloadedState) {
  const store = compose (
    composeEnhancers(
      applyMiddleware(thunkMiddleware, loggerMiddleware),
    ),
  )(createStore)(combineReducers({
    reducers,
    i18n    : i18nReducer,
    routing : routerReducer}));

    syncTranslationWithStore(store);
    store.dispatch(loadTranslations(translations));
    store.dispatch(setLocale('en'));

  return store;
}<|endoftext|>


<commit_before>import {h} from 'preact';

import About from '../About';
import Resume from '../Resume';
import Style from './style.scss';
import Projects from '../Projects';
import Achivements from '../Achivements';
import Experiences from '../Experiences';

const MainContent = ({projects, experiences, about,resume}) => (

		<About about={about}/>
		<Experiences experiences={experiences}/>
		<Projects projects={projects}/>
		<Resume resume={resume}/>

);

export default MainContent;
<commit_msg>add Skills component to main<commit_after>import {h} from 'preact';

import About from '../About';
import Resume from '../Resume';
import Style from './style.scss';
import Projects from '../Projects';
import Achivements from '../Achivements';
import Skills from '../Skills';
import Experiences from '../Experiences';

const MainContent = ({projects, experiences, about,resume}) => (

		<About about={about}/>
		<Skills/>
		<Experiences experiences={experiences}/>
		<Projects projects={projects}/>
		<Resume resume={resume}/>

);

export default MainContent;
<|endoftext|>


<commit_before>/*
Copyright 2017 Vector Creations Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import dis from '../dispatcher';
import {Store} from 'flux/utils';
import MatrixClientPeg from '../MatrixClientPeg';
import sdk from '../index';
import Modal from '../Modal';
import { _t } from '../languageHandler';

const INITIAL_STATE = {
    // Whether we're joining the currently viewed room
    joining: false,
    // Any error that has occurred during joining
    joinError: null,
    // The room ID of the room currently being viewed
    roomId: null,

    // The event to scroll to when the room is first viewed
    initialEventId: null,
    // Whether to highlight the initial event
    isInitialEventHighlighted: false,

    // The room alias of the room (or null if not originally specified in view_room)
    roomAlias: null,
    // Whether the current room is loading
    roomLoading: false,
    // Any error that has occurred during loading
    roomLoadError: null,

    forwardingEvent: null,
};

/**
 * A class for storing application state for RoomView. This is the RoomView's interface
*  with a subset of the js-sdk.
 *  ```
 */
class RoomViewStore extends Store {
    constructor() {
        super(dis);

        // Initialise state
        this._state = INITIAL_STATE;
    }

    _setState(newState) {
        this._state = Object.assign(this._state, newState);
        this.__emitChange();
    }

    __onDispatch(payload) {
        switch (payload.action) {
            // view_room:
            //      - room_alias:   '#somealias:matrix.org'
            //      - room_id:      '!roomid123:matrix.org'
            //      - event_id:     '$213456782:matrix.org'
            //      - event_offset: 100
            //      - highlighted:  true
            case 'view_room':
                this._viewRoom(payload);
                break;
            case 'view_room_error':
                this._viewRoomError(payload);
                break;
            case 'will_join':
                this._setState({
                    joining: true,
                });
                break;
            case 'cancel_join':
                this._setState({
                    joining: false,
                });
                break;
            // join_room:
            //      - opts: options for joinRoom
            case 'join_room':
                this._joinRoom(payload);
                break;
            case 'joined_room':
                this._joinedRoom(payload);
                break;
            case 'join_room_error':
                this._joinRoomError(payload);
                break;
            case 'on_logged_out':
                this.reset();
                break;
            case 'forward_event':
                this._setState({
                    forwardingEvent: payload.event,
                });
                break;
        }
    }

    _viewRoom(payload) {
        if (payload.room_id) {
            const newState = {
                roomId: payload.room_id,
                roomAlias: payload.room_alias,
                initialEventId: payload.event_id,
                isInitialEventHighlighted: payload.highlighted,
                forwardingEvent: null,
                roomLoading: false,
                roomLoadError: null,
                // should peek by default
                shouldPeek: payload.should_peek === undefined ? true : payload.should_peek,
            };

            if (payload.joined) {
                newState.joining = false;
            }

            if (this._state.forwardingEvent) {
                dis.dispatch({
                    action: 'send_event',
                    room_id: newState.roomId,
                    event: this._state.forwardingEvent,
                });
            }

            this._setState(newState);
        } else if (payload.room_alias) {
            // Resolve the alias and then do a second dispatch with the room ID acquired
            this._setState({
                roomId: null,
                initialEventId: null,
                initialEventPixelOffset: null,
                isInitialEventHighlighted: null,
                roomAlias: payload.room_alias,
                roomLoading: true,
                roomLoadError: null,
            });
            MatrixClientPeg.get().getRoomIdForAlias(payload.room_alias).done(
            (result) => {
                dis.dispatch({
                    action: 'view_room',
                    room_id: result.room_id,
                    event_id: payload.event_id,
                    highlighted: payload.highlighted,
                    room_alias: payload.room_alias,
                });
            }, (err) => {
                dis.dispatch({
                    action: 'view_room_error',
                    room_id: null,
                    room_alias: payload.room_alias,
                    err: err,
                });
            });
        }
    }

    _viewRoomError(payload) {
        this._setState({
            roomId: payload.room_id,
            roomAlias: payload.room_alias,
            roomLoading: false,
            roomLoadError: payload.err,
        });
    }

    _joinRoom(payload) {
        this._setState({
            joining: true,
        });
        MatrixClientPeg.get().joinRoom(
            this._state.roomAlias || this._state.roomId, payload.opts,
        ).done(() => {
            dis.dispatch({
                action: 'joined_room',
            });
        }, (err) => {
            dis.dispatch({
                action: 'join_room_error',
                err: err,
            });
            const msg = err.message ? err.message : JSON.stringify(err);
            const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
            Modal.createTrackedDialog('Failed to join room', '', ErrorDialog, {
                title: _t("Failed to join room"),
                description: msg,
            });
        });
    }

    _joinedRoom(payload) {
        this._setState({
            joining: false,
        });
    }

    _joinRoomError(payload) {
        this._setState({
            joining: false,
            joinError: payload.err,
        });
    }

    _updateScrollState(payload) {
        // Clobber existing scroll state for the given room ID
        const newScrollStateMap = this._state.scrollStateMap;
        newScrollStateMap[payload.room_id] = payload.scroll_state;
        this._setState({
            scrollStateMap: newScrollStateMap,
        });
    }

    reset() {
        this._state = Object.assign({}, INITIAL_STATE);
    }

    // The room ID of the room currently being viewed
    getRoomId() {
        return this._state.roomId;
    }

    // The event to scroll to when the room is first viewed
    getInitialEventId() {
        return this._state.initialEventId;
    }

    // The offset to display the initial event at (see scrollStateMap)
    getInitialEventPixelOffset() {
        return this._state.initialEventPixelOffset;
    }

    // Whether to highlight the initial event
    isInitialEventHighlighted() {
        return this._state.isInitialEventHighlighted;
    }

    // The room alias of the room (or null if not originally specified in view_room)
    getRoomAlias() {
        return this._state.roomAlias;
    }

    // Whether the current room is loading (true whilst resolving an alias)
    isRoomLoading() {
        return this._state.roomLoading;
    }

    // Any error that has occurred during loading
    getRoomLoadError() {
        return this._state.roomLoadError;
    }

    // Whether we're joining the currently viewed room
    isJoining() {
        return this._state.joining;
    }

    // Any error that has occurred during joining
    getJoinError() {
        return this._state.joinError;
    }

    // The mxEvent if one is about to be forwarded
    getForwardingEvent() {
        return this._state.forwardingEvent;
    }

    shouldPeek() {
        return this._state.shouldPeek;
    }
}

let singletonRoomViewStore = null;
if (!singletonRoomViewStore) {
    singletonRoomViewStore = new RoomViewStore();
}
module.exports = singletonRoomViewStore;
<commit_msg>Remove now unused stuff from RoomViewStore<commit_after>/*
Copyright 2017 Vector Creations Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import dis from '../dispatcher';
import {Store} from 'flux/utils';
import MatrixClientPeg from '../MatrixClientPeg';
import sdk from '../index';
import Modal from '../Modal';
import { _t } from '../languageHandler';

const INITIAL_STATE = {
    // Whether we're joining the currently viewed room
    joining: false,
    // Any error that has occurred during joining
    joinError: null,
    // The room ID of the room currently being viewed
    roomId: null,

    // The event to scroll to when the room is first viewed
    initialEventId: null,
    // Whether to highlight the initial event
    isInitialEventHighlighted: false,

    // The room alias of the room (or null if not originally specified in view_room)
    roomAlias: null,
    // Whether the current room is loading
    roomLoading: false,
    // Any error that has occurred during loading
    roomLoadError: null,

    forwardingEvent: null,
};

/**
 * A class for storing application state for RoomView. This is the RoomView's interface
*  with a subset of the js-sdk.
 *  ```
 */
class RoomViewStore extends Store {
    constructor() {
        super(dis);

        // Initialise state
        this._state = INITIAL_STATE;
    }

    _setState(newState) {
        this._state = Object.assign(this._state, newState);
        this.__emitChange();
    }

    __onDispatch(payload) {
        switch (payload.action) {
            // view_room:
            //      - room_alias:   '#somealias:matrix.org'
            //      - room_id:      '!roomid123:matrix.org'
            //      - event_id:     '$213456782:matrix.org'
            //      - event_offset: 100
            //      - highlighted:  true
            case 'view_room':
                this._viewRoom(payload);
                break;
            case 'view_room_error':
                this._viewRoomError(payload);
                break;
            case 'will_join':
                this._setState({
                    joining: true,
                });
                break;
            case 'cancel_join':
                this._setState({
                    joining: false,
                });
                break;
            // join_room:
            //      - opts: options for joinRoom
            case 'join_room':
                this._joinRoom(payload);
                break;
            case 'joined_room':
                this._joinedRoom(payload);
                break;
            case 'join_room_error':
                this._joinRoomError(payload);
                break;
            case 'on_logged_out':
                this.reset();
                break;
            case 'forward_event':
                this._setState({
                    forwardingEvent: payload.event,
                });
                break;
        }
    }

    _viewRoom(payload) {
        if (payload.room_id) {
            const newState = {
                roomId: payload.room_id,
                roomAlias: payload.room_alias,
                initialEventId: payload.event_id,
                isInitialEventHighlighted: payload.highlighted,
                forwardingEvent: null,
                roomLoading: false,
                roomLoadError: null,
                // should peek by default
                shouldPeek: payload.should_peek === undefined ? true : payload.should_peek,
            };

            if (payload.joined) {
                newState.joining = false;
            }

            if (this._state.forwardingEvent) {
                dis.dispatch({
                    action: 'send_event',
                    room_id: newState.roomId,
                    event: this._state.forwardingEvent,
                });
            }

            this._setState(newState);
        } else if (payload.room_alias) {
            // Resolve the alias and then do a second dispatch with the room ID acquired
            this._setState({
                roomId: null,
                initialEventId: null,
                initialEventPixelOffset: null,
                isInitialEventHighlighted: null,
                roomAlias: payload.room_alias,
                roomLoading: true,
                roomLoadError: null,
            });
            MatrixClientPeg.get().getRoomIdForAlias(payload.room_alias).done(
            (result) => {
                dis.dispatch({
                    action: 'view_room',
                    room_id: result.room_id,
                    event_id: payload.event_id,
                    highlighted: payload.highlighted,
                    room_alias: payload.room_alias,
                });
            }, (err) => {
                dis.dispatch({
                    action: 'view_room_error',
                    room_id: null,
                    room_alias: payload.room_alias,
                    err: err,
                });
            });
        }
    }

    _viewRoomError(payload) {
        this._setState({
            roomId: payload.room_id,
            roomAlias: payload.room_alias,
            roomLoading: false,
            roomLoadError: payload.err,
        });
    }

    _joinRoom(payload) {
        this._setState({
            joining: true,
        });
        MatrixClientPeg.get().joinRoom(
            this._state.roomAlias || this._state.roomId, payload.opts,
        ).done(() => {
            dis.dispatch({
                action: 'joined_room',
            });
        }, (err) => {
            dis.dispatch({
                action: 'join_room_error',
                err: err,
            });
            const msg = err.message ? err.message : JSON.stringify(err);
            const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
            Modal.createTrackedDialog('Failed to join room', '', ErrorDialog, {
                title: _t("Failed to join room"),
                description: msg,
            });
        });
    }

    _joinedRoom(payload) {
        this._setState({
            joining: false,
        });
    }

    _joinRoomError(payload) {
        this._setState({
            joining: false,
            joinError: payload.err,
        });
    }

    reset() {
        this._state = Object.assign({}, INITIAL_STATE);
    }

    // The room ID of the room currently being viewed
    getRoomId() {
        return this._state.roomId;
    }

    // The event to scroll to when the room is first viewed
    getInitialEventId() {
        return this._state.initialEventId;
    }

    // Whether to highlight the initial event
    isInitialEventHighlighted() {
        return this._state.isInitialEventHighlighted;
    }

    // The room alias of the room (or null if not originally specified in view_room)
    getRoomAlias() {
        return this._state.roomAlias;
    }

    // Whether the current room is loading (true whilst resolving an alias)
    isRoomLoading() {
        return this._state.roomLoading;
    }

    // Any error that has occurred during loading
    getRoomLoadError() {
        return this._state.roomLoadError;
    }

    // Whether we're joining the currently viewed room
    isJoining() {
        return this._state.joining;
    }

    // Any error that has occurred during joining
    getJoinError() {
        return this._state.joinError;
    }

    // The mxEvent if one is about to be forwarded
    getForwardingEvent() {
        return this._state.forwardingEvent;
    }

    shouldPeek() {
        return this._state.shouldPeek;
    }
}

let singletonRoomViewStore = null;
if (!singletonRoomViewStore) {
    singletonRoomViewStore = new RoomViewStore();
}
module.exports = singletonRoomViewStore;
<|endoftext|>


<commit_before>jQuery(document).ready( function($) {
	console.log("START");

	var spoilersArr = ["the", "here", "li"];
	var target = $(".stream");

	if (target.length > 0) {

		// Get all tweets loaded on document ready
		var loadedTweets = $("li[data-item-type]").toArray();
		for (var i=0; i<loadedTweets.length; i++) {
			hideTweet(loadedTweets[i]);
		}

		function hideTweet(tweetNode) {
			tweetNode = $(tweetNode);

			// Adblocker was hiding 'tweets', but program was detecting spoilers within
			// These 'tweets' had height 0, so this weeds those out
			if (tweetNode.height() <= 0) return;

			var tweetText = tweetNode.find("p").text();

			toHide = false;
			for (var i=0; i<spoilersArr.length; i++) {
				// if tweet text contains a spoiler
				if (tweetText.indexOf( spoilersArr[i] ) > -1) {
					// tweetNode should be hidden
					toHide = true;
				}
			}

			if (toHide) {
				// Spoiler overlay
				newDiv = $(document.createElement("div")).css({
					'position': 'absolute',
					'top': 0,
					'left': 0,
					'background-color': 'white',
					'width': '100%',
					'height': '99%',
					'z-index': 1
				});

				lineHeight = tweetNode.height() * 0.9;

				// Spoiler text
				newDiv.append($('<p/>').text('Spoiler!').css({
					'position': 'absolute',
					'top': 0,
					'left': 0,
					'background-color': 'white',
					'width': '100%',
					'height': '100%',
					'font-size': 40,
					'text-align': 'center',
					'line-height': lineHeight.toString() + 'px',
					'font-family': 'Copperplate',
					'color': 'red'
				}));

				// Absolutely positioned element needs a positioned ancestor
				// This does not break any of twitter's formatting (far as I have seen)
				tweetNode.css({
					'position': 'relative'
				})

				newDiv.click(function() {
					$(this).hide()
				});

				tweetNode.append(newDiv);
			}
		}

		// Get tweets loaded on scroll
		var tweetObs = new MutationSummary({
			callback: newTweetsCallback,
			rootNode: target[0],
			queries: [{
				element: "li[data-item-type]"
			}]
		});

		function newTweetsCallback(summaries) {
			summaries[0].added.forEach( function(node) {
				hideTweet(node);
			});
		}
	}

});
<commit_msg>Added hand cursor when hovering over spoiler overlay<commit_after>jQuery(document).ready( function($) {
	console.log("START");

	var spoilersArr = ["the", "here", "li"];
	var target = $(".stream");

	if (target.length > 0) {

		// Get all tweets loaded on document ready
		var loadedTweets = $("li[data-item-type]").toArray();
		for (var i=0; i<loadedTweets.length; i++) {
			hideTweet(loadedTweets[i]);
		}

		function hideTweet(tweetNode) {
			tweetNode = $(tweetNode);

			// Adblocker was hiding 'tweets', but program was detecting spoilers within
			// These 'tweets' had height 0, so this weeds those out
			if (tweetNode.height() <= 0) return;

			var tweetText = tweetNode.find("p").text();

			toHide = false;
			for (var i=0; i<spoilersArr.length; i++) {
				// if tweet text contains a spoiler
				if (tweetText.indexOf( spoilersArr[i] ) > -1) {
					// tweetNode should be hidden
					toHide = true;
				}
			}

			if (toHide) {
				// Spoiler overlay
				newDiv = $(document.createElement("div")).css({
					'position': 'absolute',
					'top': 0,
					'left': 0,
					'background-color': 'white',
					'width': '100%',
					'height': '99%',
					'z-index': 1,
					'cursor': 'pointer'
				});

				lineHeight = tweetNode.height() * 0.9;

				// Spoiler text
				newDiv.append($('<p/>').text('Spoiler!').css({
					'position': 'absolute',
					'top': 0,
					'left': 0,
					'background-color': 'white',
					'width': '100%',
					'height': '100%',
					'font-size': 40,
					'text-align': 'center',
					'line-height': lineHeight.toString() + 'px',
					'font-family': 'Copperplate',
					'color': 'red'
				}));

				// Absolutely positioned element needs a positioned ancestor
				// This does not break any of twitter's formatting (far as I have seen)
				tweetNode.css({
					'position': 'relative'
				})

				newDiv.click(function() {
					$(this).hide()
				});

				tweetNode.append(newDiv);
			}
		}

		// Get tweets loaded on scroll
		var tweetObs = new MutationSummary({
			callback: newTweetsCallback,
			rootNode: target[0],
			queries: [{
				element: "li[data-item-type]"
			}]
		});

		function newTweetsCallback(summaries) {
			summaries[0].added.forEach( function(node) {
				hideTweet(node);
			});
		}
	}

});
<|endoftext|>


<commit_before>
import React from "react"

import * as S from "./styled"

const PostLoading = () => {
    return (
        <S.PostCard>
            <S.LoadingBox>
                <S.WhiteSpace>
                    <S.HeadOne></S.HeadOne>
                    <S.HeadTwo></S.HeadTwo>
                    <S.HeadThree></S.HeadThree>
                    <S.HeadFour></S.HeadFour>
                </S.WhiteSpace>
                <S.WhiteSpace>
                    <S.BodyOne></S.BodyOne>
                    <S.BodyTwo></S.BodyTwo>
                    <S.BodyThree></S.BodyThree>
                    <S.BodyFour></S.BodyFour>
                    <S.BodyFive></S.BodyFive>
                </S.WhiteSpace>
            </S.LoadingBox>
        </S.PostCard>
    )
}

export default PostLoading
<commit_msg>fix: postloadin style<commit_after>
import React from "react"

import * as S from "./styled"

const PostLoading = () => {
    return (
        <S.PostCard>
            <S.LoadingBox>
                <S.WhiteSpace>
                    <S.HeadOne></S.HeadOne>
                    <S.HeadTwo></S.HeadTwo>
                    <S.HeadThree></S.HeadThree>
                    <S.HeadFour></S.HeadFour>
                </S.WhiteSpace>
                <S.WhiteSpace>
                    <S.BodyOne></S.BodyOne>
                    <S.BodyTwo></S.BodyTwo>
                    <S.BodyThree></S.BodyThree>
                    {/* <S.BodyFour></S.BodyFour>
                    <S.BodyFive></S.BodyFive> */}
                </S.WhiteSpace>
            </S.LoadingBox>
        </S.PostCard>
    )
}

export default PostLoading
<|endoftext|>


<commit_before>define(["require", "dojo/_base/lang", "dojo/_base/declare", "dojo/on", "dojo/Deferred", "dojo/when", "../Controller"],
	function(require, lang, declare, on, Deferred, when, Controller, View){
	// module:
	//		dojox/app/controllers/Load
	// summary:
	//		Bind "load" event on dojox/app application instance.
	//		Load child view and sub children at one time.

	return declare("dojox.app.controllers.Load", Controller, {

		constructor: function(app, events){
			// summary:
			//		bind "load" event on application instance.
			//
			// app:
			//		dojox/app application instance.
			// events:
			//		{event : handler}
			this.events = {
				"init": this.init,
				"load": this.load
			};
		},

		init: function(event){
			// when the load controller received "init", before the lifecycle really starts we create the root view
			// if any. This used to be done in main.js but must be done in Load to be able to create custom
			// views from the Load controller.
			//create and start child. return Deferred
			when(this.createView(event.parent, null, event.app, {
					templateString: event.templateString,
					definition: event.definition
			}, null, event.type), function(newView){
				when(newView.start(), event.callback);
			});
		},

		load: function(event){
			// summary:
			//		Response to dojox/app "loadArray" event.
			//
			// example:
			//		Use trigger() to trigger "loadArray" event, and this function will response the event. For example:
			//		|	this.trigger("load", {"parent":parent, "viewId":viewId, "viewArray":viewArray, "callback":function(){...}});
			//
			// event: Object
			//		LoadArray event parameter. It should be like this: {"parent":parent, "viewId":viewId, "viewArray":viewArray, "callback":function(){...}}
			// returns:
			//		A dojo/Deferred object.
			//		The return value cannot return directly.
			//		If the caller need to use the return value, pass callback function in event parameter and process return value in callback function.

			this.app.log("in app/controllers/Load event.viewId="+event.viewId+" event =", event);
			var parent = event.parent || this.app;
			var views = event.viewId || "";
			viewArray = [];
			// create an array from the diff views in event.viewId (they are separated by +)
			var parts = views.split('+');
			while(parts.length > 0){
				var viewId = parts.shift();
				viewArray.push(viewId);
			}

			var params = event.params || "";
			var def;
			if(viewArray && viewArray.length > 0){
				// loop thru the array calling loadView for each item in the array
				for(var i=0; i<viewArray.length-1; i++){
					var newEvent = lang.clone(event);
					newEvent.callback = null;  // skip callback until after last view is loaded.
					newEvent.viewId = viewArray[i];
					this.loadView(newEvent);
				}
				// for last view leave the callback to be notified
				var newEvent = lang.clone(event);
				newEvent.viewId = viewArray[i];
				def = this.loadView(newEvent);
				return def;
			}else{
				var def = this.loadView(event);
				return def;
			}
		},

		loadView: function(event){
			// summary:
			//		Response to dojox/app "load" event.
			//
			// example:
			//		Use trigger() to trigger "load" event, and this function will response the event. For example:
			//		|	this.trigger("load", {"parent":parent, "viewId":viewId, "callback":function(){...}});
			//
			// event: Object
			//		Load event parameter. It should be like this: {"parent":parent, "viewId":viewId, "callback":function(){...}}
			// returns:
			//		A dojo/Deferred object.
			//		The return value cannot return directly.
			//		If the caller need to use the return value, pass callback function in event parameter and process return value in callback function.

			var parent = event.parent || this.app;
			var viewId = event.viewId || "";
			var parts = viewId.split(',');
			var childId = parts.shift();
			var subIds = parts.join(",");
			var params = event.params || "";

			var def = this.loadChild(parent, childId, subIds, params);
			// call Load event callback
			if(event.callback){
				when(def, event.callback);
			}
			return def;
		},

		createChild: function(parent, childId, subIds, params){
			// summary:
			//		Create a view instance if not already loaded by calling createView. This is typically a
			//		dojox/app/View.
			//
			// parent: Object
			//		parent of the view.
			// childId: String
			//		view id need to be loaded.
			// subIds: String
			//		sub views' id of this view.
			// returns:
			//		If view exist, return the view object.
			//		Otherwise, create the view and return a dojo.Deferred instance.

			var id = parent.id + '_' + childId;

			// check for possible default params if no params were provided
			if(!params && parent.views[childId].defaultParams){
				params = parent.views[childId].defaultParams;
			}
			var view = parent.children[id];
			if(view){
				// set params to new value before returning
				if(params){
					view.params = params;
				}
				return view;
			}
			var def = new Deferred();
			// create and start child. return Deferred
			when(this.createView(parent, id, childId, null, params, parent.views[childId].type), function(newView){
				parent.children[id] = newView;
				when(newView.start(), function(view){
					def.resolve(view);
				});
			});
			return def;
		},

		createView: function(parent, id, name, mixin, params, type){
			// summary:
			//		Create a dojox/app/View instance. Can be overridden to create different type of views.
			// parent: Object
			//		parent of this view.
			// id: String
			//		view id.
			// name: String
			//		view name.
			// mixin: String
			//		additional property to be mixed into the view (templateString, definition...)
			// params: Object
			//		params of this view.
			// type: String
			//		the MID of the View. If not provided "dojox/app/View".
			// returns:
			//		A dojo/Deferred instance which will be resolved when the view will be instantiated.
			// tags:
			//		protected
			var def = new Deferred();
			var app = this.app;
			require([type?type:"../View"], function(View){
				var newView = new View(lang.mixin({
					"app": app,
					"id": id,
					"name": name,
					"parent": parent
				}, { "params": params }, mixin));
				def.resolve(newView);
			});
			return def;
		},

		loadChild: function(parent, childId, subIds, params){
			// summary:
			//		Load child and sub children views recursively.
			//
			// parent: Object
			//		parent of this view.
			// childId: String
			//		view id need to be loaded.
			// subIds: String
			//		sub views' id of this view.
			// params: Object
			//		params of this view.
			// returns:
			//		A dojo/Deferred instance which will be resolved when all views loaded.

			//TODO: Can this be called with a viewId or default view which includes multiple views with a "+"?  Need to handle that!

			if(!parent){
				throw Error("No parent for Child '" + childId + "'.");
			}

			if(!childId){
				var parts = parent.defaultView ? parent.defaultView.split(",") : "default";
				childId = parts.shift();
				subIds = parts.join(',');
			}

			var loadChildDeferred = new Deferred();
			var createPromise;
			try{
				createPromise = this.createChild(parent, childId, subIds, params);
			}catch(ex){
				loadChildDeferred.reject("load child '"+childId+"' error.");
				return loadChildDeferred.promise;
			}
			when(createPromise, lang.hitch(this, function(child){
				// if no subIds and current view has default view, load the default view.
				if(!subIds && child.defaultView){
					subIds = child.defaultView;
				}

				var parts = subIds.split(',');
				childId = parts.shift();
				subIds = parts.join(',');
				if(childId){
					var subLoadDeferred = this.loadChild(child, childId, subIds, params);
					when(subLoadDeferred, function(){
						loadChildDeferred.resolve();
					},
					function(){
						loadChildDeferred.reject("load child '"+childId+"' error.");
					});
				}else{
					loadChildDeferred.resolve();
				}
			}),
			function(){
				loadChildDeferred.reject("load child '"+childId+"' error.")
			});
			return loadChildDeferred.promise; // dojo/Deferred.promise
		}
	});
});
<commit_msg>3rd parameter is a name not the app<commit_after>define(["require", "dojo/_base/lang", "dojo/_base/declare", "dojo/on", "dojo/Deferred", "dojo/when", "../Controller"],
	function(require, lang, declare, on, Deferred, when, Controller, View){
	// module:
	//		dojox/app/controllers/Load
	// summary:
	//		Bind "load" event on dojox/app application instance.
	//		Load child view and sub children at one time.

	return declare("dojox.app.controllers.Load", Controller, {

		constructor: function(app, events){
			// summary:
			//		bind "load" event on application instance.
			//
			// app:
			//		dojox/app application instance.
			// events:
			//		{event : handler}
			this.events = {
				"init": this.init,
				"load": this.load
			};
		},

		init: function(event){
			// when the load controller received "init", before the lifecycle really starts we create the root view
			// if any. This used to be done in main.js but must be done in Load to be able to create custom
			// views from the Load controller.
			//create and start child. return Deferred
			when(this.createView(event.parent, null, null, {
					templateString: event.templateString,
					definition: event.definition
			}, null, event.type), function(newView){
				when(newView.start(), event.callback);
			});
		},

		load: function(event){
			// summary:
			//		Response to dojox/app "loadArray" event.
			//
			// example:
			//		Use trigger() to trigger "loadArray" event, and this function will response the event. For example:
			//		|	this.trigger("load", {"parent":parent, "viewId":viewId, "viewArray":viewArray, "callback":function(){...}});
			//
			// event: Object
			//		LoadArray event parameter. It should be like this: {"parent":parent, "viewId":viewId, "viewArray":viewArray, "callback":function(){...}}
			// returns:
			//		A dojo/Deferred object.
			//		The return value cannot return directly.
			//		If the caller need to use the return value, pass callback function in event parameter and process return value in callback function.

			this.app.log("in app/controllers/Load event.viewId="+event.viewId+" event =", event);
			var parent = event.parent || this.app;
			var views = event.viewId || "";
			viewArray = [];
			// create an array from the diff views in event.viewId (they are separated by +)
			var parts = views.split('+');
			while(parts.length > 0){
				var viewId = parts.shift();
				viewArray.push(viewId);
			}

			var params = event.params || "";
			var def;
			if(viewArray && viewArray.length > 0){
				// loop thru the array calling loadView for each item in the array
				for(var i=0; i<viewArray.length-1; i++){
					var newEvent = lang.clone(event);
					newEvent.callback = null;  // skip callback until after last view is loaded.
					newEvent.viewId = viewArray[i];
					this.loadView(newEvent);
				}
				// for last view leave the callback to be notified
				var newEvent = lang.clone(event);
				newEvent.viewId = viewArray[i];
				def = this.loadView(newEvent);
				return def;
			}else{
				var def = this.loadView(event);
				return def;
			}
		},

		loadView: function(event){
			// summary:
			//		Response to dojox/app "load" event.
			//
			// example:
			//		Use trigger() to trigger "load" event, and this function will response the event. For example:
			//		|	this.trigger("load", {"parent":parent, "viewId":viewId, "callback":function(){...}});
			//
			// event: Object
			//		Load event parameter. It should be like this: {"parent":parent, "viewId":viewId, "callback":function(){...}}
			// returns:
			//		A dojo/Deferred object.
			//		The return value cannot return directly.
			//		If the caller need to use the return value, pass callback function in event parameter and process return value in callback function.

			var parent = event.parent || this.app;
			var viewId = event.viewId || "";
			var parts = viewId.split(',');
			var childId = parts.shift();
			var subIds = parts.join(",");
			var params = event.params || "";

			var def = this.loadChild(parent, childId, subIds, params);
			// call Load event callback
			if(event.callback){
				when(def, event.callback);
			}
			return def;
		},

		createChild: function(parent, childId, subIds, params){
			// summary:
			//		Create a view instance if not already loaded by calling createView. This is typically a
			//		dojox/app/View.
			//
			// parent: Object
			//		parent of the view.
			// childId: String
			//		view id need to be loaded.
			// subIds: String
			//		sub views' id of this view.
			// returns:
			//		If view exist, return the view object.
			//		Otherwise, create the view and return a dojo.Deferred instance.

			var id = parent.id + '_' + childId;

			// check for possible default params if no params were provided
			if(!params && parent.views[childId].defaultParams){
				params = parent.views[childId].defaultParams;
			}
			var view = parent.children[id];
			if(view){
				// set params to new value before returning
				if(params){
					view.params = params;
				}
				return view;
			}
			var def = new Deferred();
			// create and start child. return Deferred
			when(this.createView(parent, id, childId, null, params, parent.views[childId].type), function(newView){
				parent.children[id] = newView;
				when(newView.start(), function(view){
					def.resolve(view);
				});
			});
			return def;
		},

		createView: function(parent, id, name, mixin, params, type){
			// summary:
			//		Create a dojox/app/View instance. Can be overridden to create different type of views.
			// parent: Object
			//		parent of this view.
			// id: String
			//		view id.
			// name: String
			//		view name.
			// mixin: String
			//		additional property to be mixed into the view (templateString, definition...)
			// params: Object
			//		params of this view.
			// type: String
			//		the MID of the View. If not provided "dojox/app/View".
			// returns:
			//		A dojo/Deferred instance which will be resolved when the view will be instantiated.
			// tags:
			//		protected
			var def = new Deferred();
			var app = this.app;
			require([type?type:"../View"], function(View){
				var newView = new View(lang.mixin({
					"app": app,
					"id": id,
					"name": name,
					"parent": parent
				}, { "params": params }, mixin));
				def.resolve(newView);
			});
			return def;
		},

		loadChild: function(parent, childId, subIds, params){
			// summary:
			//		Load child and sub children views recursively.
			//
			// parent: Object
			//		parent of this view.
			// childId: String
			//		view id need to be loaded.
			// subIds: String
			//		sub views' id of this view.
			// params: Object
			//		params of this view.
			// returns:
			//		A dojo/Deferred instance which will be resolved when all views loaded.

			//TODO: Can this be called with a viewId or default view which includes multiple views with a "+"?  Need to handle that!

			if(!parent){
				throw Error("No parent for Child '" + childId + "'.");
			}

			if(!childId){
				var parts = parent.defaultView ? parent.defaultView.split(",") : "default";
				childId = parts.shift();
				subIds = parts.join(',');
			}

			var loadChildDeferred = new Deferred();
			var createPromise;
			try{
				createPromise = this.createChild(parent, childId, subIds, params);
			}catch(ex){
				loadChildDeferred.reject("load child '"+childId+"' error.");
				return loadChildDeferred.promise;
			}
			when(createPromise, lang.hitch(this, function(child){
				// if no subIds and current view has default view, load the default view.
				if(!subIds && child.defaultView){
					subIds = child.defaultView;
				}

				var parts = subIds.split(',');
				childId = parts.shift();
				subIds = parts.join(',');
				if(childId){
					var subLoadDeferred = this.loadChild(child, childId, subIds, params);
					when(subLoadDeferred, function(){
						loadChildDeferred.resolve();
					},
					function(){
						loadChildDeferred.reject("load child '"+childId+"' error.");
					});
				}else{
					loadChildDeferred.resolve();
				}
			}),
			function(){
				loadChildDeferred.reject("load child '"+childId+"' error.")
			});
			return loadChildDeferred.promise; // dojo/Deferred.promise
		}
	});
});
<|endoftext|>


<commit_before>/*
Copyright 2017 Vector Creations Ltd
Copyright 2017, 2018 New Vector Ltd
Copyright 2019 The Matrix.org Foundation C.I.C.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import dis from '../dispatcher/dispatcher';
import {Store} from 'flux/utils';
import {MatrixClientPeg} from '../MatrixClientPeg';
import * as sdk from '../index';
import Modal from '../Modal';
import { _t } from '../languageHandler';
import { getCachedRoomIDForAlias, storeRoomAliasInCache } from '../RoomAliasCache';

const INITIAL_STATE = {
    // Whether we're joining the currently viewed room (see isJoining())
    joining: false,
    // Any error that has occurred during joining
    joinError: null,
    // The room ID of the room currently being viewed
    roomId: null,

    // The event to scroll to when the room is first viewed
    initialEventId: null,
    // Whether to highlight the initial event
    isInitialEventHighlighted: false,

    // The room alias of the room (or null if not originally specified in view_room)
    roomAlias: null,
    // Whether the current room is loading
    roomLoading: false,
    // Any error that has occurred during loading
    roomLoadError: null,

    forwardingEvent: null,

    quotingEvent: null,
    matrixClientIsReady: false,
};

/**
 * A class for storing application state for RoomView. This is the RoomView's interface
*  with a subset of the js-sdk.
 *  ```
 */
class RoomViewStore extends Store {
    constructor() {
        super(dis);

        // Initialise state
        this._state = INITIAL_STATE;
        if (MatrixClientPeg.get()) {
            this._state.matrixClientIsReady = MatrixClientPeg.get().isInitialSyncComplete();
        }
    }

    _setState(newState) {
        // If values haven't changed, there's nothing to do.
        // This only tries a shallow comparison, so unchanged objects will slip
        // through, but that's probably okay for now.
        let stateChanged = false;
        for (const key of Object.keys(newState)) {
            if (this._state[key] !== newState[key]) {
                stateChanged = true;
                break;
            }
        }
        if (!stateChanged) {
            return;
        }

        this._state = Object.assign(this._state, newState);
        this.__emitChange();
    }

    __onDispatch(payload) {
        switch (payload.action) {
            // view_room:
            //      - room_alias:   '#somealias:matrix.org'
            //      - room_id:      '!roomid123:matrix.org'
            //      - event_id:     '$213456782:matrix.org'
            //      - event_offset: 100
            //      - highlighted:  true
            case 'view_room':
                this._viewRoom(payload);
                break;
            case 'view_my_groups':
            case 'view_group':
                this._setState({
                    roomId: null,
                    roomAlias: null,
                });
                break;
            case 'view_room_error':
                this._viewRoomError(payload);
                break;
            case 'will_join':
                this._setState({
                    joining: true,
                });
                break;
            case 'cancel_join':
                this._setState({
                    joining: false,
                });
                break;
            // join_room:
            //      - opts: options for joinRoom
            case 'join_room':
                this._joinRoom(payload);
                break;
            case 'join_room_error':
                this._joinRoomError(payload);
                break;
            case 'join_room_ready':
                this._setState({ shouldPeek: false });
                break;
            case 'on_client_not_viable':
            case 'on_logged_out':
                this.reset();
                break;
            case 'forward_event':
                this._setState({
                    forwardingEvent: payload.event,
                });
                break;
            case 'reply_to_event':
                // If currently viewed room does not match the room in which we wish to reply then change rooms
                // this can happen when performing a search across all rooms
                if (payload.event && payload.event.getRoomId() !== this._state.roomId) {
                    dis.dispatch({
                        action: 'view_room',
                        room_id: payload.event.getRoomId(),
                        replyingToEvent: payload.event,
                    });
                } else {
                    this._setState({
                        replyingToEvent: payload.event,
                    });
                }
                break;
            case 'open_room_settings': {
                const RoomSettingsDialog = sdk.getComponent("dialogs.RoomSettingsDialog");
                Modal.createTrackedDialog('Room settings', '', RoomSettingsDialog, {
                    roomId: payload.room_id || this._state.roomId,
                }, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true);
                break;
            }
            case 'sync_state':
                this._setState({
                    matrixClientIsReady: MatrixClientPeg.get() && MatrixClientPeg.get().isInitialSyncComplete(),
                });
                break;
        }
    }

    async _viewRoom(payload) {
        if (payload.room_id) {
            const newState = {
                roomId: payload.room_id,
                roomAlias: payload.room_alias,
                initialEventId: payload.event_id,
                isInitialEventHighlighted: payload.highlighted,
                forwardingEvent: null,
                roomLoading: false,
                roomLoadError: null,
                // should peek by default
                shouldPeek: payload.should_peek === undefined ? true : payload.should_peek,
                // have we sent a join request for this room and are waiting for a response?
                joining: payload.joining || false,
                // Reset replyingToEvent because we don't want cross-room because bad UX
                replyingToEvent: null,
                // pull the user out of Room Settings
                isEditingSettings: false,
            };

            // Allow being given an event to be replied to when switching rooms but sanity check its for this room
            if (payload.replyingToEvent && payload.replyingToEvent.getRoomId() === payload.room_id) {
                newState.replyingToEvent = payload.replyingToEvent;
            }

            if (this._state.forwardingEvent) {
                dis.dispatch({
                    action: 'send_event',
                    room_id: newState.roomId,
                    event: this._state.forwardingEvent,
                });
            }

            this._setState(newState);

            if (payload.auto_join) {
                this._joinRoom(payload);
            }
        } else if (payload.room_alias) {
            // Try the room alias to room ID navigation cache first to avoid
            // blocking room navigation on the homeserver.
            let roomId = getCachedRoomIDForAlias(payload.room_alias);
            if (!roomId) {
                // Room alias cache miss, so let's ask the homeserver. Resolve the alias
                // and then do a second dispatch with the room ID acquired.
                this._setState({
                    roomId: null,
                    initialEventId: null,
                    initialEventPixelOffset: null,
                    isInitialEventHighlighted: null,
                    roomAlias: payload.room_alias,
                    roomLoading: true,
                    roomLoadError: null,
                });
                try {
                    const result = await MatrixClientPeg.get().getRoomIdForAlias(payload.room_alias);
                    storeRoomAliasInCache(payload.room_alias, result.room_id);
                    roomId = result.room_id;
                } catch (err) {
                    dis.dispatch({
                        action: 'view_room_error',
                        room_id: null,
                        room_alias: payload.room_alias,
                        err,
                    });
                    return;
                }
            }

            dis.dispatch({
                action: 'view_room',
                room_id: roomId,
                event_id: payload.event_id,
                highlighted: payload.highlighted,
                room_alias: payload.room_alias,
                auto_join: payload.auto_join,
                oob_data: payload.oob_data,
            });
        }
    }

    _viewRoomError(payload) {
        this._setState({
            roomId: payload.room_id,
            roomAlias: payload.room_alias,
            roomLoading: false,
            roomLoadError: payload.err,
        });
    }

    _joinRoom(payload) {
        this._setState({
            joining: true,
        });
        MatrixClientPeg.get().joinRoom(
            this._state.roomAlias || this._state.roomId, payload.opts,
        ).then(() => {
            // We do *not* clear the 'joining' flag because the Room object and/or our 'joined' member event may not
            // have come down the sync stream yet, and that's the point at which we'd consider the user joined to the
            // room.
            dis.dispatch({ action: 'join_room_ready' });
        }, (err) => {
            dis.dispatch({
                action: 'join_room_error',
                err: err,
            });
            let msg = err.message ? err.message : JSON.stringify(err);
            // XXX: We are relying on the error message returned by browsers here.
            // This isn't great, but it does generalize the error being shown to users.
            if (msg && msg.startsWith("CORS request rejected")) {
                msg = _t("There was an error joining the room");
            }
            if (err.errcode === 'M_INCOMPATIBLE_ROOM_VERSION') {
                msg = <div>
                    {_t("Sorry, your homeserver is too old to participate in this room.")}<br />
                    {_t("Please contact your homeserver administrator.")}
                </div>;
            }
            const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
            Modal.createTrackedDialog('Failed to join room', '', ErrorDialog, {
                title: _t("Failed to join room"),
                description: msg,
            });
        });
    }

    _joinRoomError(payload) {
        this._setState({
            joining: false,
            joinError: payload.err,
        });
    }

    reset() {
        this._state = Object.assign({}, INITIAL_STATE);
    }

    // The room ID of the room currently being viewed
    getRoomId() {
        return this._state.roomId;
    }

    // The event to scroll to when the room is first viewed
    getInitialEventId() {
        return this._state.initialEventId;
    }

    // Whether to highlight the initial event
    isInitialEventHighlighted() {
        return this._state.isInitialEventHighlighted;
    }

    // The room alias of the room (or null if not originally specified in view_room)
    getRoomAlias() {
        return this._state.roomAlias;
    }

    // Whether the current room is loading (true whilst resolving an alias)
    isRoomLoading() {
        return this._state.roomLoading;
    }

    // Any error that has occurred during loading
    getRoomLoadError() {
        return this._state.roomLoadError;
    }

    // True if we're expecting the user to be joined to the room currently being
    // viewed. Note that this is left true after the join request has finished,
    // since we should still consider a join to be in progress until the room
    // & member events come down the sync.
    //
    // This flag remains true after the room has been sucessfully joined,
    // (this store doesn't listen for the appropriate member events)
    // so you should always observe the joined state from the member event
    // if a room object is present.
    // ie. The correct logic is:
    // if (room) {
    //     if (myMember.membership == 'joined') {
    //         // user is joined to the room
    //     } else {
    //         // Not joined
    //     }
    // } else {
    //     if (RoomViewStore.isJoining()) {
    //         // show spinner
    //     } else {
    //         // show join prompt
    //     }
    // }
    isJoining() {
        return this._state.joining;
    }

    // Any error that has occurred during joining
    getJoinError() {
        return this._state.joinError;
    }

    // The mxEvent if one is about to be forwarded
    getForwardingEvent() {
        return this._state.forwardingEvent;
    }

    // The mxEvent if one is currently being replied to/quoted
    getQuotingEvent() {
        return this._state.replyingToEvent;
    }

    shouldPeek() {
        return this._state.shouldPeek && this._state.matrixClientIsReady;
    }
}

let singletonRoomViewStore = null;
if (!singletonRoomViewStore) {
    singletonRoomViewStore = new RoomViewStore();
}
export default singletonRoomViewStore;
<commit_msg>add logging when we fail to join a room<commit_after>/*
Copyright 2017 Vector Creations Ltd
Copyright 2017, 2018 New Vector Ltd
Copyright 2019 The Matrix.org Foundation C.I.C.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import dis from '../dispatcher/dispatcher';
import {Store} from 'flux/utils';
import {MatrixClientPeg} from '../MatrixClientPeg';
import * as sdk from '../index';
import Modal from '../Modal';
import { _t } from '../languageHandler';
import { getCachedRoomIDForAlias, storeRoomAliasInCache } from '../RoomAliasCache';

const INITIAL_STATE = {
    // Whether we're joining the currently viewed room (see isJoining())
    joining: false,
    // Any error that has occurred during joining
    joinError: null,
    // The room ID of the room currently being viewed
    roomId: null,

    // The event to scroll to when the room is first viewed
    initialEventId: null,
    // Whether to highlight the initial event
    isInitialEventHighlighted: false,

    // The room alias of the room (or null if not originally specified in view_room)
    roomAlias: null,
    // Whether the current room is loading
    roomLoading: false,
    // Any error that has occurred during loading
    roomLoadError: null,

    forwardingEvent: null,

    quotingEvent: null,
    matrixClientIsReady: false,
};

/**
 * A class for storing application state for RoomView. This is the RoomView's interface
*  with a subset of the js-sdk.
 *  ```
 */
class RoomViewStore extends Store {
    constructor() {
        super(dis);

        // Initialise state
        this._state = INITIAL_STATE;
        if (MatrixClientPeg.get()) {
            this._state.matrixClientIsReady = MatrixClientPeg.get().isInitialSyncComplete();
        }
    }

    _setState(newState) {
        // If values haven't changed, there's nothing to do.
        // This only tries a shallow comparison, so unchanged objects will slip
        // through, but that's probably okay for now.
        let stateChanged = false;
        for (const key of Object.keys(newState)) {
            if (this._state[key] !== newState[key]) {
                stateChanged = true;
                break;
            }
        }
        if (!stateChanged) {
            return;
        }

        this._state = Object.assign(this._state, newState);
        this.__emitChange();
    }

    __onDispatch(payload) {
        switch (payload.action) {
            // view_room:
            //      - room_alias:   '#somealias:matrix.org'
            //      - room_id:      '!roomid123:matrix.org'
            //      - event_id:     '$213456782:matrix.org'
            //      - event_offset: 100
            //      - highlighted:  true
            case 'view_room':
                this._viewRoom(payload);
                break;
            case 'view_my_groups':
            case 'view_group':
                this._setState({
                    roomId: null,
                    roomAlias: null,
                });
                break;
            case 'view_room_error':
                this._viewRoomError(payload);
                break;
            case 'will_join':
                this._setState({
                    joining: true,
                });
                break;
            case 'cancel_join':
                this._setState({
                    joining: false,
                });
                break;
            // join_room:
            //      - opts: options for joinRoom
            case 'join_room':
                this._joinRoom(payload);
                break;
            case 'join_room_error':
                this._joinRoomError(payload);
                break;
            case 'join_room_ready':
                this._setState({ shouldPeek: false });
                break;
            case 'on_client_not_viable':
            case 'on_logged_out':
                this.reset();
                break;
            case 'forward_event':
                this._setState({
                    forwardingEvent: payload.event,
                });
                break;
            case 'reply_to_event':
                // If currently viewed room does not match the room in which we wish to reply then change rooms
                // this can happen when performing a search across all rooms
                if (payload.event && payload.event.getRoomId() !== this._state.roomId) {
                    dis.dispatch({
                        action: 'view_room',
                        room_id: payload.event.getRoomId(),
                        replyingToEvent: payload.event,
                    });
                } else {
                    this._setState({
                        replyingToEvent: payload.event,
                    });
                }
                break;
            case 'open_room_settings': {
                const RoomSettingsDialog = sdk.getComponent("dialogs.RoomSettingsDialog");
                Modal.createTrackedDialog('Room settings', '', RoomSettingsDialog, {
                    roomId: payload.room_id || this._state.roomId,
                }, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true);
                break;
            }
            case 'sync_state':
                this._setState({
                    matrixClientIsReady: MatrixClientPeg.get() && MatrixClientPeg.get().isInitialSyncComplete(),
                });
                break;
        }
    }

    async _viewRoom(payload) {
        if (payload.room_id) {
            const newState = {
                roomId: payload.room_id,
                roomAlias: payload.room_alias,
                initialEventId: payload.event_id,
                isInitialEventHighlighted: payload.highlighted,
                forwardingEvent: null,
                roomLoading: false,
                roomLoadError: null,
                // should peek by default
                shouldPeek: payload.should_peek === undefined ? true : payload.should_peek,
                // have we sent a join request for this room and are waiting for a response?
                joining: payload.joining || false,
                // Reset replyingToEvent because we don't want cross-room because bad UX
                replyingToEvent: null,
                // pull the user out of Room Settings
                isEditingSettings: false,
            };

            // Allow being given an event to be replied to when switching rooms but sanity check its for this room
            if (payload.replyingToEvent && payload.replyingToEvent.getRoomId() === payload.room_id) {
                newState.replyingToEvent = payload.replyingToEvent;
            }

            if (this._state.forwardingEvent) {
                dis.dispatch({
                    action: 'send_event',
                    room_id: newState.roomId,
                    event: this._state.forwardingEvent,
                });
            }

            this._setState(newState);

            if (payload.auto_join) {
                this._joinRoom(payload);
            }
        } else if (payload.room_alias) {
            // Try the room alias to room ID navigation cache first to avoid
            // blocking room navigation on the homeserver.
            let roomId = getCachedRoomIDForAlias(payload.room_alias);
            if (!roomId) {
                // Room alias cache miss, so let's ask the homeserver. Resolve the alias
                // and then do a second dispatch with the room ID acquired.
                this._setState({
                    roomId: null,
                    initialEventId: null,
                    initialEventPixelOffset: null,
                    isInitialEventHighlighted: null,
                    roomAlias: payload.room_alias,
                    roomLoading: true,
                    roomLoadError: null,
                });
                try {
                    const result = await MatrixClientPeg.get().getRoomIdForAlias(payload.room_alias);
                    storeRoomAliasInCache(payload.room_alias, result.room_id);
                    roomId = result.room_id;
                } catch (err) {
                    dis.dispatch({
                        action: 'view_room_error',
                        room_id: null,
                        room_alias: payload.room_alias,
                        err,
                    });
                    return;
                }
            }

            dis.dispatch({
                action: 'view_room',
                room_id: roomId,
                event_id: payload.event_id,
                highlighted: payload.highlighted,
                room_alias: payload.room_alias,
                auto_join: payload.auto_join,
                oob_data: payload.oob_data,
            });
        }
    }

    _viewRoomError(payload) {
        this._setState({
            roomId: payload.room_id,
            roomAlias: payload.room_alias,
            roomLoading: false,
            roomLoadError: payload.err,
        });
    }

    _joinRoom(payload) {
        this._setState({
            joining: true,
        });
        MatrixClientPeg.get().joinRoom(
            this._state.roomAlias || this._state.roomId, payload.opts,
        ).then(() => {
            // We do *not* clear the 'joining' flag because the Room object and/or our 'joined' member event may not
            // have come down the sync stream yet, and that's the point at which we'd consider the user joined to the
            // room.
            dis.dispatch({ action: 'join_room_ready' });
        }, (err) => {
            dis.dispatch({
                action: 'join_room_error',
                err: err,
            });
            let msg = err.message ? err.message : JSON.stringify(err);
            console.log("Failed to join room:", msg);
            // XXX: We are relying on the error message returned by browsers here.
            // This isn't great, but it does generalize the error being shown to users.
            if (msg && msg.startsWith("CORS request rejected")) {
                msg = _t("There was an error joining the room");
            }
            if (err.errcode === 'M_INCOMPATIBLE_ROOM_VERSION') {
                msg = <div>
                    {_t("Sorry, your homeserver is too old to participate in this room.")}<br />
                    {_t("Please contact your homeserver administrator.")}
                </div>;
            }
            const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
            Modal.createTrackedDialog('Failed to join room', '', ErrorDialog, {
                title: _t("Failed to join room"),
                description: msg,
            });
        });
    }

    _joinRoomError(payload) {
        this._setState({
            joining: false,
            joinError: payload.err,
        });
    }

    reset() {
        this._state = Object.assign({}, INITIAL_STATE);
    }

    // The room ID of the room currently being viewed
    getRoomId() {
        return this._state.roomId;
    }

    // The event to scroll to when the room is first viewed
    getInitialEventId() {
        return this._state.initialEventId;
    }

    // Whether to highlight the initial event
    isInitialEventHighlighted() {
        return this._state.isInitialEventHighlighted;
    }

    // The room alias of the room (or null if not originally specified in view_room)
    getRoomAlias() {
        return this._state.roomAlias;
    }

    // Whether the current room is loading (true whilst resolving an alias)
    isRoomLoading() {
        return this._state.roomLoading;
    }

    // Any error that has occurred during loading
    getRoomLoadError() {
        return this._state.roomLoadError;
    }

    // True if we're expecting the user to be joined to the room currently being
    // viewed. Note that this is left true after the join request has finished,
    // since we should still consider a join to be in progress until the room
    // & member events come down the sync.
    //
    // This flag remains true after the room has been sucessfully joined,
    // (this store doesn't listen for the appropriate member events)
    // so you should always observe the joined state from the member event
    // if a room object is present.
    // ie. The correct logic is:
    // if (room) {
    //     if (myMember.membership == 'joined') {
    //         // user is joined to the room
    //     } else {
    //         // Not joined
    //     }
    // } else {
    //     if (RoomViewStore.isJoining()) {
    //         // show spinner
    //     } else {
    //         // show join prompt
    //     }
    // }
    isJoining() {
        return this._state.joining;
    }

    // Any error that has occurred during joining
    getJoinError() {
        return this._state.joinError;
    }

    // The mxEvent if one is about to be forwarded
    getForwardingEvent() {
        return this._state.forwardingEvent;
    }

    // The mxEvent if one is currently being replied to/quoted
    getQuotingEvent() {
        return this._state.replyingToEvent;
    }

    shouldPeek() {
        return this._state.shouldPeek && this._state.matrixClientIsReady;
    }
}

let singletonRoomViewStore = null;
if (!singletonRoomViewStore) {
    singletonRoomViewStore = new RoomViewStore();
}
export default singletonRoomViewStore;
<|endoftext|>